chiark / gitweb /
461a7261f43b675decbc99cbecf78f3722122d53
[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 #include "fileio.h"
42
43 union shutdown_buffer {
44         struct sd_shutdown_command command;
45         char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
46 };
47
48 static int read_packet(int fd, union shutdown_buffer *_b) {
49         struct ucred *ucred;
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         };
58         union {
59                 struct cmsghdr cmsghdr;
60                 uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
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         };
68
69         assert(fd >= 0);
70         assert(_b);
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 if (c->mode == SD_SHUTDOWN_NONE)
140                 prefix = "The system shutdown has been cancelled at ";
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" : "",
145                      prefix, format_timestamp(date, sizeof(date), c->usec)) < 0)
146                 log_error("Failed to allocate wall message");
147         else {
148                 utmp_wall(l, NULL);
149                 free(l);
150         }
151 }
152
153 _const_ static 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[] = {
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        },
163         };
164
165         usec_t left, sub;
166         unsigned i = ELEMENTSOF(table) - 1;
167
168         /* If the time is already passed, then don't announce */
169         if (n >= elapse)
170                 return 0;
171
172         left = elapse - n;
173         while (left < table[i].delay)
174                 i--;
175         sub = (left / table[i].interval) * table[i].interval;
176
177         assert(sub < elapse);
178         return elapse - sub;
179 }
180
181 static usec_t when_nologin(usec_t elapse) {
182         return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
183 }
184
185 static 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
200 static 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
207         r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
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);
214         if (!t)
215                 return log_oom();
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
258 static bool scheduled(struct sd_shutdown_command *c) {
259         return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
260 }
261
262 int main(int argc, char *argv[]) {
263         enum {
264                 FD_SOCKET,
265                 FD_WALL_TIMER,
266                 FD_NOLOGIN_TIMER,
267                 FD_SHUTDOWN_TIMER,
268                 _FD_MAX
269         };
270
271         int r = EXIT_FAILURE, n_fds;
272         union shutdown_buffer b = {};
273         struct pollfd pollfd[_FD_MAX] = {};
274         bool exec_shutdown = false, unlink_nologin = false;
275         unsigned i;
276
277         if (getppid() != 1) {
278                 log_error("This program should be invoked by init only.");
279                 return EXIT_FAILURE;
280         }
281
282         if (argc > 1) {
283                 log_error("This program does not take arguments.");
284                 return EXIT_FAILURE;
285         }
286
287         log_set_target(LOG_TARGET_AUTO);
288         log_parse_environment();
289         log_open();
290
291         umask(0022);
292
293         n_fds = sd_listen_fds(true);
294         if (n_fds < 0) {
295                 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
296                 return EXIT_FAILURE;
297         }
298
299         if (n_fds != 1) {
300                 log_error("Need exactly one file descriptor.");
301                 return EXIT_FAILURE;
302         }
303
304         pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
305         pollfd[FD_SOCKET].events = POLLIN;
306
307         for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
308                 pollfd[i].events = POLLIN;
309                 pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
310                 if (pollfd[i].fd < 0) {
311                         log_error("timerfd_create(): %m");
312                         goto finish;
313                 }
314         }
315
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
322         for (;;) {
323                 int k;
324                 usec_t n;
325
326                 k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
327                 if (k < 0) {
328
329                         if (errno == EAGAIN || errno == EINTR)
330                                 continue;
331
332                         log_error("poll(): %m");
333                         goto finish;
334                 }
335
336                 /* Exit on idle */
337                 if (k == 0)
338                         break;
339
340                 n = now(CLOCK_REALTIME);
341
342                 if (pollfd[FD_SOCKET].revents) {
343
344                         k = read_packet(pollfd[FD_SOCKET].fd, &b);
345                         if (k < 0)
346                                 goto finish;
347                         else if (k > 0) {
348                                 struct itimerspec its;
349                                 char date[FORMAT_TIMESTAMP_MAX];
350
351                                 if (!scheduled(&b.command)) {
352                                         log_info("Shutdown canceled.");
353                                         if (b.command.warn_wall)
354                                                 warn_wall(0, &b.command);
355                                         break;
356                                 }
357
358                                 zero(its);
359                                 if (b.command.warn_wall) {
360                                         /* Send wall messages every so often */
361                                         timespec_store(&its.it_value, when_wall(n, b.command.usec));
362
363                                         /* Warn immediately if less than 15 minutes are left */
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;
371                                 }
372
373                                 /* Disallow logins 5 minutes prior to shutdown */
374                                 zero(its);
375                                 timespec_store(&its.it_value, when_nologin(b.command.usec));
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);
383                                 timespec_store(&its.it_value, b.command.usec);
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
389                                 update_schedule_file(&b.command);
390
391                                 sd_notifyf(false,
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));
397                         }
398                 }
399
400                 if (pollfd[FD_WALL_TIMER].revents) {
401                         struct itimerspec its = {};
402
403                         warn_wall(n, &b.command);
404                         flush_fd(pollfd[FD_WALL_TIMER].fd);
405
406                         /* Restart timer */
407                         timespec_store(&its.it_value, when_wall(n, b.command.usec));
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) {
415                         int e;
416
417                         log_info("Creating /run/nologin, blocking further logins...");
418
419                         e = write_string_file_atomic("/run/nologin", "System is going down.");
420                         if (e < 0)
421                                 log_error("Failed to create /run/nologin: %s", strerror(-e));
422                         else
423                                 unlink_nologin = true;
424
425                         flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
426                 }
427
428                 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
429                         exec_shutdown = true;
430                         goto finish;
431                 }
432         }
433
434         r = EXIT_SUCCESS;
435
436         log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
437
438 finish:
439
440         for (i = 0; i < _FD_MAX; i++)
441                 if (pollfd[i].fd >= 0)
442                         close_nointr_nofail(pollfd[i].fd);
443
444         if (unlink_nologin)
445                 unlink("/run/nologin");
446
447         unlink("/run/systemd/shutdown/scheduled");
448
449         if (exec_shutdown && !b.command.dry_run) {
450                 char sw[3];
451
452                 sw[0] = '-';
453                 sw[1] = b.command.mode;
454                 sw[2] = 0;
455
456                 execl(SYSTEMCTL_BINARY_PATH,
457                       "shutdown",
458                       sw,
459                       "now",
460                       (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
461                       (b.command.warn_wall ? NULL : "--no-wall"),
462                       NULL);
463
464                 log_error("Failed to execute /sbin/shutdown: %m");
465         }
466
467         sd_notify(false,
468                   "STATUS=Exiting...");
469
470         return r;
471 }