]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shutdownd/shutdownd.c
treewide: use log_*_errno whenever %m is in the format string
[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
73f860db
ZJS
33#include "systemd/sd-daemon.h"
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 = {
b748c759
TA
55 .iov_base = &b,
56 .iov_len = sizeof(b) - 1,
b92bea5d 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
56f64d95 82 log_error_errno(errno, "recvmsg(): %m");
f6144808
LP
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);
f647962d
MS
206 if (r < 0)
207 return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
04ebb595
LP
208
209 t = cescape(c->wall_message);
0d0f0c50
SL
210 if (!t)
211 return log_oom();
04ebb595
LP
212
213 r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
f647962d
MS
214 if (r < 0)
215 return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
04ebb595
LP
216
217 fchmod(fileno(f), 0644);
218
219 fprintf(f,
90b2de37 220 "USEC="USEC_FMT"\n"
04ebb595
LP
221 "WARN_WALL=%i\n"
222 "MODE=%s\n",
90b2de37 223 c->usec,
04ebb595
LP
224 c->warn_wall,
225 mode_to_string(c->mode));
226
227 if (c->dry_run)
228 fputs("DRY_RUN=1\n", f);
229
230 if (!isempty(t))
231 fprintf(f, "WALL_MESSAGE=%s\n", t);
232
04ebb595
LP
233 fflush(f);
234
235 if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
56f64d95 236 log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m");
04ebb595
LP
237 r = -errno;
238
239 unlink(temp_path);
240 unlink("/run/systemd/shutdown/scheduled");
241 }
242
04ebb595
LP
243 return r;
244}
245
246static bool scheduled(struct sd_shutdown_command *c) {
247 return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
248}
249
f6144808
LP
250int main(int argc, char *argv[]) {
251 enum {
252 FD_SOCKET,
9be9828c 253 FD_WALL_TIMER,
f6144808 254 FD_NOLOGIN_TIMER,
9be9828c 255 FD_SHUTDOWN_TIMER,
f6144808
LP
256 _FD_MAX
257 };
258
22f4096c 259 int r = EXIT_FAILURE, n_fds;
b92bea5d
ZJS
260 union shutdown_buffer b = {};
261 struct pollfd pollfd[_FD_MAX] = {};
04ebb595 262 bool exec_shutdown = false, unlink_nologin = false;
9be9828c 263 unsigned i;
f6144808
LP
264
265 if (getppid() != 1) {
266 log_error("This program should be invoked by init only.");
22f4096c 267 return EXIT_FAILURE;
f6144808
LP
268 }
269
270 if (argc > 1) {
271 log_error("This program does not take arguments.");
22f4096c 272 return EXIT_FAILURE;
f6144808
LP
273 }
274
4cfa2c99 275 log_set_target(LOG_TARGET_AUTO);
f6144808 276 log_parse_environment();
2396fb04 277 log_open();
f6144808 278
4c12626c
LP
279 umask(0022);
280
04ebb595
LP
281 n_fds = sd_listen_fds(true);
282 if (n_fds < 0) {
da927ba9 283 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
22f4096c 284 return EXIT_FAILURE;
f6144808
LP
285 }
286
9be9828c 287 if (n_fds != 1) {
f6144808 288 log_error("Need exactly one file descriptor.");
22f4096c 289 return EXIT_FAILURE;
f6144808
LP
290 }
291
f6144808
LP
292 pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
293 pollfd[FD_SOCKET].events = POLLIN;
9be9828c 294
04ebb595 295 for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
9be9828c 296 pollfd[i].events = POLLIN;
04ebb595
LP
297 pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
298 if (pollfd[i].fd < 0) {
56f64d95 299 log_error_errno(errno, "timerfd_create(): %m");
04ebb595 300 goto finish;
9be9828c
LP
301 }
302 }
303
de0671ee 304 log_debug("systemd-shutdownd running as pid "PID_FMT, getpid());
f6144808
LP
305
306 sd_notify(false,
307 "READY=1\n"
308 "STATUS=Processing requests...");
309
04ebb595 310 for (;;) {
f6144808 311 int k;
9be9828c 312 usec_t n;
f6144808 313
04ebb595
LP
314 k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
315 if (k < 0) {
f6144808
LP
316
317 if (errno == EAGAIN || errno == EINTR)
318 continue;
319
56f64d95 320 log_error_errno(errno, "poll(): %m");
f6144808
LP
321 goto finish;
322 }
323
04ebb595
LP
324 /* Exit on idle */
325 if (k == 0)
326 break;
327
9be9828c
LP
328 n = now(CLOCK_REALTIME);
329
f6144808
LP
330 if (pollfd[FD_SOCKET].revents) {
331
04ebb595
LP
332 k = read_packet(pollfd[FD_SOCKET].fd, &b);
333 if (k < 0)
f6144808 334 goto finish;
04ebb595 335 else if (k > 0) {
f6144808 336 struct itimerspec its;
11620592 337 char date[FORMAT_TIMESTAMP_MAX];
f6144808 338
04ebb595
LP
339 if (!scheduled(&b.command)) {
340 log_info("Shutdown canceled.");
dfcc5c33
MS
341 if (b.command.warn_wall)
342 warn_wall(0, &b.command);
04ebb595
LP
343 break;
344 }
345
346 zero(its);
347 if (b.command.warn_wall) {
9be9828c 348 /* Send wall messages every so often */
04ebb595 349 timespec_store(&its.it_value, when_wall(n, b.command.usec));
f6144808 350
9be9828c 351 /* Warn immediately if less than 15 minutes are left */
04ebb595
LP
352 if (n < b.command.usec &&
353 n + 15*USEC_PER_MINUTE >= b.command.usec)
354 warn_wall(n, &b.command);
355 }
356 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
56f64d95 357 log_error_errno(errno, "timerfd_settime(): %m");
04ebb595 358 goto finish;
9be9828c
LP
359 }
360
f6144808
LP
361 /* Disallow logins 5 minutes prior to shutdown */
362 zero(its);
04ebb595 363 timespec_store(&its.it_value, when_nologin(b.command.usec));
f6144808 364 if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
56f64d95 365 log_error_errno(errno, "timerfd_settime(): %m");
f6144808
LP
366 goto finish;
367 }
368
369 /* Shutdown after the specified time is reached */
370 zero(its);
04ebb595 371 timespec_store(&its.it_value, b.command.usec);
f6144808 372 if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
56f64d95 373 log_error_errno(errno, "timerfd_settime(): %m");
f6144808
LP
374 goto finish;
375 }
376
04ebb595
LP
377 update_schedule_file(&b.command);
378
f6144808 379 sd_notifyf(false,
04ebb595
LP
380 "STATUS=Shutting down at %s (%s)...",
381 format_timestamp(date, sizeof(date), b.command.usec),
382 mode_to_string(b.command.mode));
383
384 log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
f6144808
LP
385 }
386 }
387
9be9828c 388 if (pollfd[FD_WALL_TIMER].revents) {
b92bea5d 389 struct itimerspec its = {};
9be9828c 390
04ebb595 391 warn_wall(n, &b.command);
9be9828c
LP
392 flush_fd(pollfd[FD_WALL_TIMER].fd);
393
394 /* Restart timer */
04ebb595 395 timespec_store(&its.it_value, when_wall(n, b.command.usec));
9be9828c 396 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
56f64d95 397 log_error_errno(errno, "timerfd_settime(): %m");
9be9828c
LP
398 goto finish;
399 }
400 }
401
402 if (pollfd[FD_NOLOGIN_TIMER].revents) {
f6144808
LP
403 int e;
404
db019b8d 405 log_info("Creating /run/nologin, blocking further logins...");
08e4b1c5 406
574d5f2d 407 e = write_string_file_atomic("/run/nologin", "System is going down.");
04ebb595 408 if (e < 0)
da927ba9 409 log_error_errno(e, "Failed to create /run/nologin: %m");
f6144808
LP
410 else
411 unlink_nologin = true;
412
9be9828c 413 flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
f6144808
LP
414 }
415
9be9828c 416 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
f6144808
LP
417 exec_shutdown = true;
418 goto finish;
419 }
04ebb595 420 }
f6144808 421
22f4096c 422 r = EXIT_SUCCESS;
f6144808 423
de0671ee 424 log_debug("systemd-shutdownd stopped as pid "PID_FMT, getpid());
f6144808
LP
425
426finish:
f6144808 427
9be9828c 428 for (i = 0; i < _FD_MAX; i++)
03e334a1 429 safe_close(pollfd[i].fd);
f6144808 430
16061c20 431 if (unlink_nologin)
db019b8d 432 unlink("/run/nologin");
16061c20 433
04ebb595
LP
434 unlink("/run/systemd/shutdown/scheduled");
435
436 if (exec_shutdown && !b.command.dry_run) {
f6144808
LP
437 char sw[3];
438
439 sw[0] = '-';
04ebb595 440 sw[1] = b.command.mode;
f6144808
LP
441 sw[2] = 0;
442
08e4b1c5
LP
443 execl(SYSTEMCTL_BINARY_PATH,
444 "shutdown",
445 sw,
446 "now",
04ebb595
LP
447 (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
448 (b.command.warn_wall ? NULL : "--no-wall"),
08e4b1c5
LP
449 NULL);
450
56f64d95 451 log_error_errno(errno, "Failed to execute /sbin/shutdown: %m");
f6144808
LP
452 }
453
f6144808 454 sd_notify(false,
af4ec430 455 "STOPPING=\n"
f6144808
LP
456 "STATUS=Exiting...");
457
458 return r;
459}