]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shutdownd/shutdownd.c
Use initalization instead of explicit zeroing
[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,
64 msghdr.msg_iovlen = 1,
65 msghdr.msg_control = &control,
66 msghdr.msg_controllen = sizeof(control),
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;
123 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" : "",
04ebb595 145 prefix, format_timestamp(date, sizeof(date), c->usec)) < 0)
30923233 146 log_error("Failed to allocate wall message");
9be9828c 147 else {
30923233
MS
148 utmp_wall(l, NULL);
149 free(l);
9be9828c
LP
150 }
151}
152
153static usec_t when_wall(usec_t n, usec_t elapse) {
154
155 static const struct {
156 usec_t delay;
157 usec_t interval;
158 } table[] = {
7a4b2eab
ZJS
159 { 0, USEC_PER_MINUTE },
160 { 10 * USEC_PER_MINUTE, 15 * USEC_PER_MINUTE },
161 { USEC_PER_HOUR, 30 * USEC_PER_MINUTE },
162 { 3 * USEC_PER_HOUR, USEC_PER_HOUR },
9be9828c
LP
163 };
164
165 usec_t left, sub;
7a4b2eab 166 unsigned i = ELEMENTSOF(table) - 1;
9be9828c
LP
167
168 /* If the time is already passed, then don't announce */
169 if (n >= elapse)
170 return 0;
171
172 left = elapse - n;
7a4b2eab
ZJS
173 while (left < table[i].delay)
174 i--;
175 sub = (left / table[i].interval) * table[i].interval;
9be9828c 176
7a4b2eab
ZJS
177 assert(sub < elapse);
178 return elapse - sub;
9be9828c
LP
179}
180
181static usec_t when_nologin(usec_t elapse) {
182 return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
183}
184
04ebb595
LP
185static const char *mode_to_string(enum sd_shutdown_mode m) {
186 switch (m) {
187 case SD_SHUTDOWN_REBOOT:
188 return "reboot";
189 case SD_SHUTDOWN_POWEROFF:
190 return "poweroff";
191 case SD_SHUTDOWN_HALT:
192 return "halt";
193 case SD_SHUTDOWN_KEXEC:
194 return "kexec";
195 default:
196 return NULL;
197 }
198}
199
200static int update_schedule_file(struct sd_shutdown_command *c) {
201 int r;
202 FILE *f;
203 char *temp_path, *t;
204
205 assert(c);
206
d2e54fae 207 r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
04ebb595
LP
208 if (r < 0) {
209 log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
210 return r;
211 }
212
213 t = cescape(c->wall_message);
0d0f0c50
SL
214 if (!t)
215 return log_oom();
04ebb595
LP
216
217 r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
218 if (r < 0) {
219 log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
220 free(t);
221 return r;
222 }
223
224 fchmod(fileno(f), 0644);
225
226 fprintf(f,
227 "USEC=%llu\n"
228 "WARN_WALL=%i\n"
229 "MODE=%s\n",
230 (unsigned long long) c->usec,
231 c->warn_wall,
232 mode_to_string(c->mode));
233
234 if (c->dry_run)
235 fputs("DRY_RUN=1\n", f);
236
237 if (!isempty(t))
238 fprintf(f, "WALL_MESSAGE=%s\n", t);
239
240 free(t);
241
242 fflush(f);
243
244 if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
245 log_error("Failed to write information about scheduled shutdowns: %m");
246 r = -errno;
247
248 unlink(temp_path);
249 unlink("/run/systemd/shutdown/scheduled");
250 }
251
252 fclose(f);
253 free(temp_path);
254
255 return r;
256}
257
258static bool scheduled(struct sd_shutdown_command *c) {
259 return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
260}
261
f6144808
LP
262int main(int argc, char *argv[]) {
263 enum {
264 FD_SOCKET,
9be9828c 265 FD_WALL_TIMER,
f6144808 266 FD_NOLOGIN_TIMER,
9be9828c 267 FD_SHUTDOWN_TIMER,
f6144808
LP
268 _FD_MAX
269 };
270
22f4096c 271 int r = EXIT_FAILURE, n_fds;
b92bea5d
ZJS
272 union shutdown_buffer b = {};
273 struct pollfd pollfd[_FD_MAX] = {};
04ebb595 274 bool exec_shutdown = false, unlink_nologin = false;
9be9828c 275 unsigned i;
f6144808
LP
276
277 if (getppid() != 1) {
278 log_error("This program should be invoked by init only.");
22f4096c 279 return EXIT_FAILURE;
f6144808
LP
280 }
281
282 if (argc > 1) {
283 log_error("This program does not take arguments.");
22f4096c 284 return EXIT_FAILURE;
f6144808
LP
285 }
286
4cfa2c99 287 log_set_target(LOG_TARGET_AUTO);
f6144808 288 log_parse_environment();
2396fb04 289 log_open();
f6144808 290
4c12626c
LP
291 umask(0022);
292
04ebb595
LP
293 n_fds = sd_listen_fds(true);
294 if (n_fds < 0) {
f6144808 295 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
22f4096c 296 return EXIT_FAILURE;
f6144808
LP
297 }
298
9be9828c 299 if (n_fds != 1) {
f6144808 300 log_error("Need exactly one file descriptor.");
22f4096c 301 return EXIT_FAILURE;
f6144808
LP
302 }
303
f6144808
LP
304 pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
305 pollfd[FD_SOCKET].events = POLLIN;
9be9828c 306
04ebb595 307 for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
9be9828c 308 pollfd[i].events = POLLIN;
04ebb595
LP
309 pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
310 if (pollfd[i].fd < 0) {
9be9828c 311 log_error("timerfd_create(): %m");
04ebb595 312 goto finish;
9be9828c
LP
313 }
314 }
315
f6144808
LP
316 log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
317
318 sd_notify(false,
319 "READY=1\n"
320 "STATUS=Processing requests...");
321
04ebb595 322 for (;;) {
f6144808 323 int k;
9be9828c 324 usec_t n;
f6144808 325
04ebb595
LP
326 k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
327 if (k < 0) {
f6144808
LP
328
329 if (errno == EAGAIN || errno == EINTR)
330 continue;
331
332 log_error("poll(): %m");
333 goto finish;
334 }
335
04ebb595
LP
336 /* Exit on idle */
337 if (k == 0)
338 break;
339
9be9828c
LP
340 n = now(CLOCK_REALTIME);
341
f6144808
LP
342 if (pollfd[FD_SOCKET].revents) {
343
04ebb595
LP
344 k = read_packet(pollfd[FD_SOCKET].fd, &b);
345 if (k < 0)
f6144808 346 goto finish;
04ebb595 347 else if (k > 0) {
f6144808 348 struct itimerspec its;
11620592 349 char date[FORMAT_TIMESTAMP_MAX];
f6144808 350
04ebb595
LP
351 if (!scheduled(&b.command)) {
352 log_info("Shutdown canceled.");
dfcc5c33
MS
353 if (b.command.warn_wall)
354 warn_wall(0, &b.command);
04ebb595
LP
355 break;
356 }
357
358 zero(its);
359 if (b.command.warn_wall) {
9be9828c 360 /* Send wall messages every so often */
04ebb595 361 timespec_store(&its.it_value, when_wall(n, b.command.usec));
f6144808 362
9be9828c 363 /* Warn immediately if less than 15 minutes are left */
04ebb595
LP
364 if (n < b.command.usec &&
365 n + 15*USEC_PER_MINUTE >= b.command.usec)
366 warn_wall(n, &b.command);
367 }
368 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
369 log_error("timerfd_settime(): %m");
370 goto finish;
9be9828c
LP
371 }
372
f6144808
LP
373 /* Disallow logins 5 minutes prior to shutdown */
374 zero(its);
04ebb595 375 timespec_store(&its.it_value, when_nologin(b.command.usec));
f6144808
LP
376 if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
377 log_error("timerfd_settime(): %m");
378 goto finish;
379 }
380
381 /* Shutdown after the specified time is reached */
382 zero(its);
04ebb595 383 timespec_store(&its.it_value, b.command.usec);
f6144808
LP
384 if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
385 log_error("timerfd_settime(): %m");
386 goto finish;
387 }
388
04ebb595
LP
389 update_schedule_file(&b.command);
390
f6144808 391 sd_notifyf(false,
04ebb595
LP
392 "STATUS=Shutting down at %s (%s)...",
393 format_timestamp(date, sizeof(date), b.command.usec),
394 mode_to_string(b.command.mode));
395
396 log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
f6144808
LP
397 }
398 }
399
9be9828c 400 if (pollfd[FD_WALL_TIMER].revents) {
b92bea5d 401 struct itimerspec its = {};
9be9828c 402
04ebb595 403 warn_wall(n, &b.command);
9be9828c
LP
404 flush_fd(pollfd[FD_WALL_TIMER].fd);
405
406 /* Restart timer */
04ebb595 407 timespec_store(&its.it_value, when_wall(n, b.command.usec));
9be9828c
LP
408 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
409 log_error("timerfd_settime(): %m");
410 goto finish;
411 }
412 }
413
414 if (pollfd[FD_NOLOGIN_TIMER].revents) {
f6144808
LP
415 int e;
416
db019b8d 417 log_info("Creating /run/nologin, blocking further logins...");
08e4b1c5 418
574d5f2d 419 e = write_string_file_atomic("/run/nologin", "System is going down.");
04ebb595 420 if (e < 0)
db019b8d 421 log_error("Failed to create /run/nologin: %s", strerror(-e));
f6144808
LP
422 else
423 unlink_nologin = true;
424
9be9828c 425 flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
f6144808
LP
426 }
427
9be9828c 428 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
f6144808
LP
429 exec_shutdown = true;
430 goto finish;
431 }
04ebb595 432 }
f6144808 433
22f4096c 434 r = EXIT_SUCCESS;
f6144808
LP
435
436 log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
437
438finish:
f6144808 439
9be9828c
LP
440 for (i = 0; i < _FD_MAX; i++)
441 if (pollfd[i].fd >= 0)
442 close_nointr_nofail(pollfd[i].fd);
f6144808 443
16061c20 444 if (unlink_nologin)
db019b8d 445 unlink("/run/nologin");
16061c20 446
04ebb595
LP
447 unlink("/run/systemd/shutdown/scheduled");
448
449 if (exec_shutdown && !b.command.dry_run) {
f6144808
LP
450 char sw[3];
451
452 sw[0] = '-';
04ebb595 453 sw[1] = b.command.mode;
f6144808
LP
454 sw[2] = 0;
455
08e4b1c5
LP
456 execl(SYSTEMCTL_BINARY_PATH,
457 "shutdown",
458 sw,
459 "now",
04ebb595
LP
460 (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
461 (b.command.warn_wall ? NULL : "--no-wall"),
08e4b1c5
LP
462 NULL);
463
f6144808
LP
464 log_error("Failed to execute /sbin/shutdown: %m");
465 }
466
f6144808
LP
467 sd_notify(false,
468 "STATUS=Exiting...");
469
470 return r;
471}