chiark / gitweb /
0497cd41a05ee8ed90c6042e22da3528dde3778a
[elogind.git] / src / shutdownd / 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 }