chiark / gitweb /
include <poll.h> instead of <sys/poll.h>
[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 <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                 .iov_base = &b,
56                 .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                 .msg_iovlen = 1,
65                 .msg_control = &control,
66                 .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_errno(errno, "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         _cleanup_free_ 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                 utmp_wall(l, NULL, NULL);
147         else
148                 log_error("Failed to allocate wall message");
149 }
150
151 _const_ 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                 { 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        },
161         };
162
163         usec_t left, sub;
164         unsigned i = ELEMENTSOF(table) - 1;
165
166         /* If the time is already passed, then don't announce */
167         if (n >= elapse)
168                 return 0;
169
170         left = elapse - n;
171         while (left < table[i].delay)
172                 i--;
173         sub = (left / table[i].interval) * table[i].interval;
174
175         assert(sub < elapse);
176         return elapse - sub;
177 }
178
179 static usec_t when_nologin(usec_t elapse) {
180         return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
181 }
182
183 static 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
198 static int update_schedule_file(struct sd_shutdown_command *c) {
199         int r;
200         _cleanup_fclose_ FILE *f = NULL;
201         _cleanup_free_ char *t = NULL, *temp_path = NULL;
202
203         assert(c);
204
205         r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
206         if (r < 0)
207                 return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
208
209         t = cescape(c->wall_message);
210         if (!t)
211                 return log_oom();
212
213         r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
214         if (r < 0)
215                 return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
216
217         fchmod(fileno(f), 0644);
218
219         fprintf(f,
220                 "USEC="USEC_FMT"\n"
221                 "WARN_WALL=%i\n"
222                 "MODE=%s\n",
223                 c->usec,
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
233         fflush(f);
234
235         if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
236                 log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m");
237                 r = -errno;
238
239                 unlink(temp_path);
240                 unlink("/run/systemd/shutdown/scheduled");
241         }
242
243         return r;
244 }
245
246 static bool scheduled(struct sd_shutdown_command *c) {
247         return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
248 }
249
250 int main(int argc, char *argv[]) {
251         enum {
252                 FD_SOCKET,
253                 FD_WALL_TIMER,
254                 FD_NOLOGIN_TIMER,
255                 FD_SHUTDOWN_TIMER,
256                 _FD_MAX
257         };
258
259         int r = EXIT_FAILURE, n_fds;
260         union shutdown_buffer b = {};
261         struct pollfd pollfd[_FD_MAX] = {};
262         bool exec_shutdown = false, unlink_nologin = false;
263         unsigned i;
264
265         if (getppid() != 1) {
266                 log_error("This program should be invoked by init only.");
267                 return EXIT_FAILURE;
268         }
269
270         if (argc > 1) {
271                 log_error("This program does not take arguments.");
272                 return EXIT_FAILURE;
273         }
274
275         log_set_target(LOG_TARGET_AUTO);
276         log_parse_environment();
277         log_open();
278
279         umask(0022);
280
281         n_fds = sd_listen_fds(true);
282         if (n_fds < 0) {
283                 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
284                 return EXIT_FAILURE;
285         }
286
287         if (n_fds != 1) {
288                 log_error("Need exactly one file descriptor.");
289                 return EXIT_FAILURE;
290         }
291
292         pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
293         pollfd[FD_SOCKET].events = POLLIN;
294
295         for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
296                 pollfd[i].events = POLLIN;
297                 pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
298                 if (pollfd[i].fd < 0) {
299                         log_error_errno(errno, "timerfd_create(): %m");
300                         goto finish;
301                 }
302         }
303
304         log_debug("systemd-shutdownd running as pid "PID_FMT, getpid());
305
306         sd_notify(false,
307                   "READY=1\n"
308                   "STATUS=Processing requests...");
309
310         for (;;) {
311                 int k;
312                 usec_t n;
313
314                 k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
315                 if (k < 0) {
316
317                         if (errno == EAGAIN || errno == EINTR)
318                                 continue;
319
320                         log_error_errno(errno, "poll(): %m");
321                         goto finish;
322                 }
323
324                 /* Exit on idle */
325                 if (k == 0)
326                         break;
327
328                 n = now(CLOCK_REALTIME);
329
330                 if (pollfd[FD_SOCKET].revents) {
331
332                         k = read_packet(pollfd[FD_SOCKET].fd, &b);
333                         if (k < 0)
334                                 goto finish;
335                         else if (k > 0) {
336                                 struct itimerspec its;
337                                 char date[FORMAT_TIMESTAMP_MAX];
338
339                                 if (!scheduled(&b.command)) {
340                                         log_info("Shutdown canceled.");
341                                         if (b.command.warn_wall)
342                                                 warn_wall(0, &b.command);
343                                         break;
344                                 }
345
346                                 zero(its);
347                                 if (b.command.warn_wall) {
348                                         /* Send wall messages every so often */
349                                         timespec_store(&its.it_value, when_wall(n, b.command.usec));
350
351                                         /* Warn immediately if less than 15 minutes are left */
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) {
357                                         log_error_errno(errno, "timerfd_settime(): %m");
358                                         goto finish;
359                                 }
360
361                                 /* Disallow logins 5 minutes prior to shutdown */
362                                 zero(its);
363                                 timespec_store(&its.it_value, when_nologin(b.command.usec));
364                                 if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
365                                         log_error_errno(errno, "timerfd_settime(): %m");
366                                         goto finish;
367                                 }
368
369                                 /* Shutdown after the specified time is reached */
370                                 zero(its);
371                                 timespec_store(&its.it_value, b.command.usec);
372                                 if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
373                                         log_error_errno(errno, "timerfd_settime(): %m");
374                                         goto finish;
375                                 }
376
377                                 update_schedule_file(&b.command);
378
379                                 sd_notifyf(false,
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));
385                         }
386                 }
387
388                 if (pollfd[FD_WALL_TIMER].revents) {
389                         struct itimerspec its = {};
390
391                         warn_wall(n, &b.command);
392                         flush_fd(pollfd[FD_WALL_TIMER].fd);
393
394                         /* Restart timer */
395                         timespec_store(&its.it_value, when_wall(n, b.command.usec));
396                         if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
397                                 log_error_errno(errno, "timerfd_settime(): %m");
398                                 goto finish;
399                         }
400                 }
401
402                 if (pollfd[FD_NOLOGIN_TIMER].revents) {
403                         int e;
404
405                         log_info("Creating /run/nologin, blocking further logins...");
406
407                         e = write_string_file_atomic("/run/nologin", "System is going down.");
408                         if (e < 0)
409                                 log_error_errno(e, "Failed to create /run/nologin: %m");
410                         else
411                                 unlink_nologin = true;
412
413                         flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
414                 }
415
416                 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
417                         exec_shutdown = true;
418                         goto finish;
419                 }
420         }
421
422         r = EXIT_SUCCESS;
423
424         log_debug("systemd-shutdownd stopped as pid "PID_FMT, getpid());
425
426 finish:
427
428         for (i = 0; i < _FD_MAX; i++)
429                 safe_close(pollfd[i].fd);
430
431         if (unlink_nologin)
432                 unlink("/run/nologin");
433
434         unlink("/run/systemd/shutdown/scheduled");
435
436         if (exec_shutdown && !b.command.dry_run) {
437                 char sw[3];
438
439                 sw[0] = '-';
440                 sw[1] = b.command.mode;
441                 sw[2] = 0;
442
443                 execl(SYSTEMCTL_BINARY_PATH,
444                       "shutdown",
445                       sw,
446                       "now",
447                       (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
448                       (b.command.warn_wall ? NULL : "--no-wall"),
449                       NULL);
450
451                 log_error_errno(errno, "Failed to execute /sbin/shutdown: %m");
452         }
453
454         sd_notify(false,
455                   "STOPPING=\n"
456                   "STATUS=Exiting...");
457
458         return r;
459 }