]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shutdownd.c
relicense to LGPLv2.1 (with exceptions)
[thirdparty/systemd.git] / src / shutdownd.c
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
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
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
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
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>
31 #include <stddef.h>
32
33 #include <systemd/sd-daemon.h>
34 #include <systemd/sd-shutdown.h>
35
36 #include "log.h"
37 #include "macro.h"
38 #include "util.h"
39 #include "utmp-wtmp.h"
40 #include "mkdir.h"
41
42 union shutdown_buffer {
43 struct sd_shutdown_command command;
44 char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
45 };
46
47 static int read_packet(int fd, union shutdown_buffer *_b) {
48 struct msghdr msghdr;
49 struct iovec iovec;
50 struct ucred *ucred;
51 union {
52 struct cmsghdr cmsghdr;
53 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
54 } control;
55 ssize_t n;
56 union shutdown_buffer b; /* We maintain our own copy here, in order not to corrupt the last message */
57
58 assert(fd >= 0);
59 assert(_b);
60
61 zero(iovec);
62 iovec.iov_base = &b;
63 iovec.iov_len = sizeof(b) - 1;
64
65 zero(control);
66 zero(msghdr);
67 msghdr.msg_iov = &iovec;
68 msghdr.msg_iovlen = 1;
69 msghdr.msg_control = &control;
70 msghdr.msg_controllen = sizeof(control);
71
72 n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
73 if (n <= 0) {
74 if (n == 0) {
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
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.");
111 return 0;
112 }
113
114 b.space[n] = 0;
115
116 *_b = b;
117 return 1;
118 }
119
120 static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
121 char date[FORMAT_TIMESTAMP_MAX];
122 const char *prefix;
123 char *l = NULL;
124
125 assert(c);
126 assert(c->warn_wall);
127
128 if (n >= c->usec)
129 return;
130
131 if (c->mode == SD_SHUTDOWN_HALT)
132 prefix = "The system is going down for system halt at ";
133 else if (c->mode == SD_SHUTDOWN_POWEROFF)
134 prefix = "The system is going down for power-off at ";
135 else if (c->mode == SD_SHUTDOWN_REBOOT)
136 prefix = "The system is going down for reboot at ";
137 else if (c->mode == SD_SHUTDOWN_KEXEC)
138 prefix = "The system is going down for kexec reboot at ";
139 else
140 assert_not_reached("Unknown mode!");
141
142 if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
143 prefix, format_timestamp(date, sizeof(date), c->usec)) < 0)
144 log_error("Failed to allocate wall message");
145 else {
146 utmp_wall(l, NULL);
147 free(l);
148 }
149 }
150
151 static usec_t when_wall(usec_t n, usec_t elapse) {
152
153 static const struct {
154 usec_t delay;
155 usec_t interval;
156 } table[] = {
157 { 10 * USEC_PER_MINUTE, USEC_PER_MINUTE },
158 { USEC_PER_HOUR, 15 * USEC_PER_MINUTE },
159 { 3 * USEC_PER_HOUR, 30 * USEC_PER_MINUTE }
160 };
161
162 usec_t left, sub;
163 unsigned i;
164
165 /* If the time is already passed, then don't announce */
166 if (n >= elapse)
167 return 0;
168
169 left = elapse - n;
170 for (i = 0; i < ELEMENTSOF(table); i++)
171 if (n + table[i].delay >= elapse) {
172 sub = ((left / table[i].interval) * table[i].interval);
173 break;
174 }
175
176 if (i >= ELEMENTSOF(table))
177 sub = ((left / USEC_PER_HOUR) * USEC_PER_HOUR);
178
179 return elapse > sub ? elapse - sub : 1;
180 }
181
182 static usec_t when_nologin(usec_t elapse) {
183 return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
184 }
185
186 static const char *mode_to_string(enum sd_shutdown_mode m) {
187 switch (m) {
188 case SD_SHUTDOWN_REBOOT:
189 return "reboot";
190 case SD_SHUTDOWN_POWEROFF:
191 return "poweroff";
192 case SD_SHUTDOWN_HALT:
193 return "halt";
194 case SD_SHUTDOWN_KEXEC:
195 return "kexec";
196 default:
197 return NULL;
198 }
199 }
200
201 static int update_schedule_file(struct sd_shutdown_command *c) {
202 int r;
203 FILE *f;
204 char *temp_path, *t;
205
206 assert(c);
207
208 r = safe_mkdir("/run/systemd/shutdown", 0755, 0, 0);
209 if (r < 0) {
210 log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
211 return r;
212 }
213
214 t = cescape(c->wall_message);
215 if (!t) {
216 log_error("Out of memory");
217 return -ENOMEM;
218 }
219
220 r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
221 if (r < 0) {
222 log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
223 free(t);
224 return r;
225 }
226
227 fchmod(fileno(f), 0644);
228
229 fprintf(f,
230 "USEC=%llu\n"
231 "WARN_WALL=%i\n"
232 "MODE=%s\n",
233 (unsigned long long) c->usec,
234 c->warn_wall,
235 mode_to_string(c->mode));
236
237 if (c->dry_run)
238 fputs("DRY_RUN=1\n", f);
239
240 if (!isempty(t))
241 fprintf(f, "WALL_MESSAGE=%s\n", t);
242
243 free(t);
244
245 fflush(f);
246
247 if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
248 log_error("Failed to write information about scheduled shutdowns: %m");
249 r = -errno;
250
251 unlink(temp_path);
252 unlink("/run/systemd/shutdown/scheduled");
253 }
254
255 fclose(f);
256 free(temp_path);
257
258 return r;
259 }
260
261 static bool scheduled(struct sd_shutdown_command *c) {
262 return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
263 }
264
265 int main(int argc, char *argv[]) {
266 enum {
267 FD_SOCKET,
268 FD_WALL_TIMER,
269 FD_NOLOGIN_TIMER,
270 FD_SHUTDOWN_TIMER,
271 _FD_MAX
272 };
273
274 int r = EXIT_FAILURE, n_fds;
275 union shutdown_buffer b;
276 struct pollfd pollfd[_FD_MAX];
277 bool exec_shutdown = false, unlink_nologin = false;
278 unsigned i;
279
280 if (getppid() != 1) {
281 log_error("This program should be invoked by init only.");
282 return EXIT_FAILURE;
283 }
284
285 if (argc > 1) {
286 log_error("This program does not take arguments.");
287 return EXIT_FAILURE;
288 }
289
290 log_set_target(LOG_TARGET_AUTO);
291 log_parse_environment();
292 log_open();
293
294 umask(0022);
295
296 n_fds = sd_listen_fds(true);
297 if (n_fds < 0) {
298 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
299 return EXIT_FAILURE;
300 }
301
302 if (n_fds != 1) {
303 log_error("Need exactly one file descriptor.");
304 return EXIT_FAILURE;
305 }
306
307 zero(b);
308 zero(pollfd);
309
310 pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
311 pollfd[FD_SOCKET].events = POLLIN;
312
313 for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
314 pollfd[i].events = POLLIN;
315 pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
316 if (pollfd[i].fd < 0) {
317 log_error("timerfd_create(): %m");
318 goto finish;
319 }
320 }
321
322 log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
323
324 sd_notify(false,
325 "READY=1\n"
326 "STATUS=Processing requests...");
327
328 for (;;) {
329 int k;
330 usec_t n;
331
332 k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
333 if (k < 0) {
334
335 if (errno == EAGAIN || errno == EINTR)
336 continue;
337
338 log_error("poll(): %m");
339 goto finish;
340 }
341
342 /* Exit on idle */
343 if (k == 0)
344 break;
345
346 n = now(CLOCK_REALTIME);
347
348 if (pollfd[FD_SOCKET].revents) {
349
350 k = read_packet(pollfd[FD_SOCKET].fd, &b);
351 if (k < 0)
352 goto finish;
353 else if (k > 0) {
354 struct itimerspec its;
355 char date[FORMAT_TIMESTAMP_MAX];
356
357 if (!scheduled(&b.command)) {
358 log_info("Shutdown canceled.");
359 break;
360 }
361
362 zero(its);
363 if (b.command.warn_wall) {
364 /* Send wall messages every so often */
365 timespec_store(&its.it_value, when_wall(n, b.command.usec));
366
367 /* Warn immediately if less than 15 minutes are left */
368 if (n < b.command.usec &&
369 n + 15*USEC_PER_MINUTE >= b.command.usec)
370 warn_wall(n, &b.command);
371 }
372 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
373 log_error("timerfd_settime(): %m");
374 goto finish;
375 }
376
377 /* Disallow logins 5 minutes prior to shutdown */
378 zero(its);
379 timespec_store(&its.it_value, when_nologin(b.command.usec));
380 if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
381 log_error("timerfd_settime(): %m");
382 goto finish;
383 }
384
385 /* Shutdown after the specified time is reached */
386 zero(its);
387 timespec_store(&its.it_value, b.command.usec);
388 if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
389 log_error("timerfd_settime(): %m");
390 goto finish;
391 }
392
393 update_schedule_file(&b.command);
394
395 sd_notifyf(false,
396 "STATUS=Shutting down at %s (%s)...",
397 format_timestamp(date, sizeof(date), b.command.usec),
398 mode_to_string(b.command.mode));
399
400 log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
401 }
402 }
403
404 if (pollfd[FD_WALL_TIMER].revents) {
405 struct itimerspec its;
406
407 warn_wall(n, &b.command);
408 flush_fd(pollfd[FD_WALL_TIMER].fd);
409
410 /* Restart timer */
411 zero(its);
412 timespec_store(&its.it_value, when_wall(n, b.command.usec));
413 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
414 log_error("timerfd_settime(): %m");
415 goto finish;
416 }
417 }
418
419 if (pollfd[FD_NOLOGIN_TIMER].revents) {
420 int e;
421
422 log_info("Creating /run/nologin, blocking further logins...");
423
424 e = write_one_line_file_atomic("/run/nologin", "System is going down.");
425 if (e < 0)
426 log_error("Failed to create /run/nologin: %s", strerror(-e));
427 else
428 unlink_nologin = true;
429
430 flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
431 }
432
433 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
434 exec_shutdown = true;
435 goto finish;
436 }
437 }
438
439 r = EXIT_SUCCESS;
440
441 log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
442
443 finish:
444
445 for (i = 0; i < _FD_MAX; i++)
446 if (pollfd[i].fd >= 0)
447 close_nointr_nofail(pollfd[i].fd);
448
449 if (unlink_nologin)
450 unlink("/run/nologin");
451
452 unlink("/run/systemd/shutdown/scheduled");
453
454 if (exec_shutdown && !b.command.dry_run) {
455 char sw[3];
456
457 sw[0] = '-';
458 sw[1] = b.command.mode;
459 sw[2] = 0;
460
461 execl(SYSTEMCTL_BINARY_PATH,
462 "shutdown",
463 sw,
464 "now",
465 (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
466 (b.command.warn_wall ? NULL : "--no-wall"),
467 NULL);
468
469 log_error("Failed to execute /sbin/shutdown: %m");
470 }
471
472 sd_notify(false,
473 "STATUS=Exiting...");
474
475 return r;
476 }