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