]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shutdownd/shutdownd.c
util: replace close_nointr_nofail() by a more useful safe_close()
[thirdparty/systemd.git] / src / shutdownd / shutdownd.c
CommitLineData
f6144808
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
f6144808
LP
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 16 Lesser General Public License for more details.
f6144808 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
f6144808
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <sys/socket.h>
23#include <sys/poll.h>
24#include <sys/types.h>
25#include <sys/timerfd.h>
26#include <assert.h>
27#include <string.h>
28#include <errno.h>
29#include <unistd.h>
30#include <fcntl.h>
04ebb595 31#include <stddef.h>
f6144808 32
81527be1 33#include <systemd/sd-daemon.h>
04ebb595 34#include <systemd/sd-shutdown.h>
81527be1 35
f6144808
LP
36#include "log.h"
37#include "macro.h"
38#include "util.h"
9be9828c 39#include "utmp-wtmp.h"
04ebb595 40#include "mkdir.h"
a5c32cff 41#include "fileio.h"
f6144808 42
04ebb595
LP
43union shutdown_buffer {
44 struct sd_shutdown_command command;
45 char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
46};
47
48static int read_packet(int fd, union shutdown_buffer *_b) {
f6144808 49 struct ucred *ucred;
b92bea5d
ZJS
50 ssize_t n;
51
52 union shutdown_buffer b; /* We maintain our own copy here, in
53 * order not to corrupt the last message */
54 struct iovec iovec = {
55 iovec.iov_base = &b,
56 iovec.iov_len = sizeof(b) - 1,
57 };
f6144808
LP
58 union {
59 struct cmsghdr cmsghdr;
60 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
b92bea5d
ZJS
61 } control = {};
62 struct msghdr msghdr = {
63 .msg_iov = &iovec,
e062dec5
ZJS
64 .msg_iovlen = 1,
65 .msg_control = &control,
66 .msg_controllen = sizeof(control),
b92bea5d 67 };
f6144808
LP
68
69 assert(fd >= 0);
04ebb595 70 assert(_b);
f6144808 71
04ebb595
LP
72 n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
73 if (n <= 0) {
74 if (n == 0) {
f6144808
LP
75 log_error("Short read");
76 return -EIO;
77 }
78
79 if (errno == EAGAIN || errno == EINTR)
80 return 0;
81
82 log_error("recvmsg(): %m");
83 return -errno;
84 }
85
86 if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
87 control.cmsghdr.cmsg_level != SOL_SOCKET ||
88 control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
89 control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
90 log_warning("Received message without credentials. Ignoring.");
91 return 0;
92 }
93
94 ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
95 if (ucred->uid != 0) {
96 log_warning("Got request from unprivileged user. Ignoring.");
97 return 0;
98 }
99
04ebb595
LP
100 if ((size_t) n < offsetof(struct sd_shutdown_command, wall_message)) {
101 log_warning("Message has invalid size. Ignoring.");
102 return 0;
103 }
104
105 if (b.command.mode != SD_SHUTDOWN_NONE &&
106 b.command.mode != SD_SHUTDOWN_REBOOT &&
107 b.command.mode != SD_SHUTDOWN_POWEROFF &&
108 b.command.mode != SD_SHUTDOWN_HALT &&
109 b.command.mode != SD_SHUTDOWN_KEXEC) {
110 log_warning("Message has invalid mode. Ignoring.");
f6144808
LP
111 return 0;
112 }
113
04ebb595 114 b.space[n] = 0;
9be9828c 115
04ebb595 116 *_b = b;
f6144808
LP
117 return 1;
118}
119
04ebb595 120static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
30923233
MS
121 char date[FORMAT_TIMESTAMP_MAX];
122 const char *prefix;
e062dec5 123 _cleanup_free_ char *l = NULL;
9be9828c
LP
124
125 assert(c);
126 assert(c->warn_wall);
127
04ebb595 128 if (n >= c->usec)
08e4b1c5
LP
129 return;
130
04ebb595 131 if (c->mode == SD_SHUTDOWN_HALT)
30923233 132 prefix = "The system is going down for system halt at ";
04ebb595 133 else if (c->mode == SD_SHUTDOWN_POWEROFF)
30923233 134 prefix = "The system is going down for power-off at ";
04ebb595 135 else if (c->mode == SD_SHUTDOWN_REBOOT)
30923233 136 prefix = "The system is going down for reboot at ";
04ebb595
LP
137 else if (c->mode == SD_SHUTDOWN_KEXEC)
138 prefix = "The system is going down for kexec reboot at ";
dfcc5c33
MS
139 else if (c->mode == SD_SHUTDOWN_NONE)
140 prefix = "The system shutdown has been cancelled at ";
30923233
MS
141 else
142 assert_not_reached("Unknown mode!");
143
144 if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
e062dec5 145 prefix, format_timestamp(date, sizeof(date), c->usec)) >= 0)
9003d9b0 146 utmp_wall(l, NULL, NULL);
e062dec5
ZJS
147 else
148 log_error("Failed to allocate wall message");
9be9828c
LP
149}
150
44a6b1b6 151_const_ static usec_t when_wall(usec_t n, usec_t elapse) {
9be9828c
LP
152
153 static const struct {
154 usec_t delay;
155 usec_t interval;
156 } table[] = {
7a4b2eab
ZJS
157 { 0, USEC_PER_MINUTE },
158 { 10 * USEC_PER_MINUTE, 15 * USEC_PER_MINUTE },
159 { USEC_PER_HOUR, 30 * USEC_PER_MINUTE },
160 { 3 * USEC_PER_HOUR, USEC_PER_HOUR },
9be9828c
LP
161 };
162
163 usec_t left, sub;
7a4b2eab 164 unsigned i = ELEMENTSOF(table) - 1;
9be9828c
LP
165
166 /* If the time is already passed, then don't announce */
167 if (n >= elapse)
168 return 0;
169
170 left = elapse - n;
7a4b2eab
ZJS
171 while (left < table[i].delay)
172 i--;
173 sub = (left / table[i].interval) * table[i].interval;
9be9828c 174
7a4b2eab
ZJS
175 assert(sub < elapse);
176 return elapse - sub;
9be9828c
LP
177}
178
179static usec_t when_nologin(usec_t elapse) {
180 return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
181}
182
04ebb595
LP
183static const char *mode_to_string(enum sd_shutdown_mode m) {
184 switch (m) {
185 case SD_SHUTDOWN_REBOOT:
186 return "reboot";
187 case SD_SHUTDOWN_POWEROFF:
188 return "poweroff";
189 case SD_SHUTDOWN_HALT:
190 return "halt";
191 case SD_SHUTDOWN_KEXEC:
192 return "kexec";
193 default:
194 return NULL;
195 }
196}
197
198static int update_schedule_file(struct sd_shutdown_command *c) {
199 int r;
e062dec5
ZJS
200 _cleanup_fclose_ FILE *f = NULL;
201 _cleanup_free_ char *t = NULL, *temp_path = NULL;
04ebb595
LP
202
203 assert(c);
204
d2e54fae 205 r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
04ebb595
LP
206 if (r < 0) {
207 log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
208 return r;
209 }
210
211 t = cescape(c->wall_message);
0d0f0c50
SL
212 if (!t)
213 return log_oom();
04ebb595
LP
214
215 r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
216 if (r < 0) {
217 log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
04ebb595
LP
218 return r;
219 }
220
221 fchmod(fileno(f), 0644);
222
223 fprintf(f,
90b2de37 224 "USEC="USEC_FMT"\n"
04ebb595
LP
225 "WARN_WALL=%i\n"
226 "MODE=%s\n",
90b2de37 227 c->usec,
04ebb595
LP
228 c->warn_wall,
229 mode_to_string(c->mode));
230
231 if (c->dry_run)
232 fputs("DRY_RUN=1\n", f);
233
234 if (!isempty(t))
235 fprintf(f, "WALL_MESSAGE=%s\n", t);
236
04ebb595
LP
237 fflush(f);
238
239 if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
240 log_error("Failed to write information about scheduled shutdowns: %m");
241 r = -errno;
242
243 unlink(temp_path);
244 unlink("/run/systemd/shutdown/scheduled");
245 }
246
04ebb595
LP
247 return r;
248}
249
250static bool scheduled(struct sd_shutdown_command *c) {
251 return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
252}
253
f6144808
LP
254int main(int argc, char *argv[]) {
255 enum {
256 FD_SOCKET,
9be9828c 257 FD_WALL_TIMER,
f6144808 258 FD_NOLOGIN_TIMER,
9be9828c 259 FD_SHUTDOWN_TIMER,
f6144808
LP
260 _FD_MAX
261 };
262
22f4096c 263 int r = EXIT_FAILURE, n_fds;
b92bea5d
ZJS
264 union shutdown_buffer b = {};
265 struct pollfd pollfd[_FD_MAX] = {};
04ebb595 266 bool exec_shutdown = false, unlink_nologin = false;
9be9828c 267 unsigned i;
f6144808
LP
268
269 if (getppid() != 1) {
270 log_error("This program should be invoked by init only.");
22f4096c 271 return EXIT_FAILURE;
f6144808
LP
272 }
273
274 if (argc > 1) {
275 log_error("This program does not take arguments.");
22f4096c 276 return EXIT_FAILURE;
f6144808
LP
277 }
278
4cfa2c99 279 log_set_target(LOG_TARGET_AUTO);
f6144808 280 log_parse_environment();
2396fb04 281 log_open();
f6144808 282
4c12626c
LP
283 umask(0022);
284
04ebb595
LP
285 n_fds = sd_listen_fds(true);
286 if (n_fds < 0) {
f6144808 287 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
22f4096c 288 return EXIT_FAILURE;
f6144808
LP
289 }
290
9be9828c 291 if (n_fds != 1) {
f6144808 292 log_error("Need exactly one file descriptor.");
22f4096c 293 return EXIT_FAILURE;
f6144808
LP
294 }
295
f6144808
LP
296 pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
297 pollfd[FD_SOCKET].events = POLLIN;
9be9828c 298
04ebb595 299 for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
9be9828c 300 pollfd[i].events = POLLIN;
04ebb595
LP
301 pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
302 if (pollfd[i].fd < 0) {
9be9828c 303 log_error("timerfd_create(): %m");
04ebb595 304 goto finish;
9be9828c
LP
305 }
306 }
307
f6144808
LP
308 log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
309
310 sd_notify(false,
311 "READY=1\n"
312 "STATUS=Processing requests...");
313
04ebb595 314 for (;;) {
f6144808 315 int k;
9be9828c 316 usec_t n;
f6144808 317
04ebb595
LP
318 k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
319 if (k < 0) {
f6144808
LP
320
321 if (errno == EAGAIN || errno == EINTR)
322 continue;
323
324 log_error("poll(): %m");
325 goto finish;
326 }
327
04ebb595
LP
328 /* Exit on idle */
329 if (k == 0)
330 break;
331
9be9828c
LP
332 n = now(CLOCK_REALTIME);
333
f6144808
LP
334 if (pollfd[FD_SOCKET].revents) {
335
04ebb595
LP
336 k = read_packet(pollfd[FD_SOCKET].fd, &b);
337 if (k < 0)
f6144808 338 goto finish;
04ebb595 339 else if (k > 0) {
f6144808 340 struct itimerspec its;
11620592 341 char date[FORMAT_TIMESTAMP_MAX];
f6144808 342
04ebb595
LP
343 if (!scheduled(&b.command)) {
344 log_info("Shutdown canceled.");
dfcc5c33
MS
345 if (b.command.warn_wall)
346 warn_wall(0, &b.command);
04ebb595
LP
347 break;
348 }
349
350 zero(its);
351 if (b.command.warn_wall) {
9be9828c 352 /* Send wall messages every so often */
04ebb595 353 timespec_store(&its.it_value, when_wall(n, b.command.usec));
f6144808 354
9be9828c 355 /* Warn immediately if less than 15 minutes are left */
04ebb595
LP
356 if (n < b.command.usec &&
357 n + 15*USEC_PER_MINUTE >= b.command.usec)
358 warn_wall(n, &b.command);
359 }
360 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
361 log_error("timerfd_settime(): %m");
362 goto finish;
9be9828c
LP
363 }
364
f6144808
LP
365 /* Disallow logins 5 minutes prior to shutdown */
366 zero(its);
04ebb595 367 timespec_store(&its.it_value, when_nologin(b.command.usec));
f6144808
LP
368 if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
369 log_error("timerfd_settime(): %m");
370 goto finish;
371 }
372
373 /* Shutdown after the specified time is reached */
374 zero(its);
04ebb595 375 timespec_store(&its.it_value, b.command.usec);
f6144808
LP
376 if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
377 log_error("timerfd_settime(): %m");
378 goto finish;
379 }
380
04ebb595
LP
381 update_schedule_file(&b.command);
382
f6144808 383 sd_notifyf(false,
04ebb595
LP
384 "STATUS=Shutting down at %s (%s)...",
385 format_timestamp(date, sizeof(date), b.command.usec),
386 mode_to_string(b.command.mode));
387
388 log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
f6144808
LP
389 }
390 }
391
9be9828c 392 if (pollfd[FD_WALL_TIMER].revents) {
b92bea5d 393 struct itimerspec its = {};
9be9828c 394
04ebb595 395 warn_wall(n, &b.command);
9be9828c
LP
396 flush_fd(pollfd[FD_WALL_TIMER].fd);
397
398 /* Restart timer */
04ebb595 399 timespec_store(&its.it_value, when_wall(n, b.command.usec));
9be9828c
LP
400 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
401 log_error("timerfd_settime(): %m");
402 goto finish;
403 }
404 }
405
406 if (pollfd[FD_NOLOGIN_TIMER].revents) {
f6144808
LP
407 int e;
408
db019b8d 409 log_info("Creating /run/nologin, blocking further logins...");
08e4b1c5 410
574d5f2d 411 e = write_string_file_atomic("/run/nologin", "System is going down.");
04ebb595 412 if (e < 0)
db019b8d 413 log_error("Failed to create /run/nologin: %s", strerror(-e));
f6144808
LP
414 else
415 unlink_nologin = true;
416
9be9828c 417 flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
f6144808
LP
418 }
419
9be9828c 420 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
f6144808
LP
421 exec_shutdown = true;
422 goto finish;
423 }
04ebb595 424 }
f6144808 425
22f4096c 426 r = EXIT_SUCCESS;
f6144808
LP
427
428 log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
429
430finish:
f6144808 431
9be9828c 432 for (i = 0; i < _FD_MAX; i++)
03e334a1 433 safe_close(pollfd[i].fd);
f6144808 434
16061c20 435 if (unlink_nologin)
db019b8d 436 unlink("/run/nologin");
16061c20 437
04ebb595
LP
438 unlink("/run/systemd/shutdown/scheduled");
439
440 if (exec_shutdown && !b.command.dry_run) {
f6144808
LP
441 char sw[3];
442
443 sw[0] = '-';
04ebb595 444 sw[1] = b.command.mode;
f6144808
LP
445 sw[2] = 0;
446
08e4b1c5
LP
447 execl(SYSTEMCTL_BINARY_PATH,
448 "shutdown",
449 sw,
450 "now",
04ebb595
LP
451 (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
452 (b.command.warn_wall ? NULL : "--no-wall"),
08e4b1c5
LP
453 NULL);
454
f6144808
LP
455 log_error("Failed to execute /sbin/shutdown: %m");
456 }
457
f6144808
LP
458 sd_notify(false,
459 "STATUS=Exiting...");
460
461 return r;
462}