chiark / gitweb /
log.h: new log_oom() -> int -ENOMEM, use it
[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 = mkdir_safe_label("/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                 return log_oom();
217
218         r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
219         if (r < 0) {
220                 log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
221                 free(t);
222                 return r;
223         }
224
225         fchmod(fileno(f), 0644);
226
227         fprintf(f,
228                 "USEC=%llu\n"
229                 "WARN_WALL=%i\n"
230                 "MODE=%s\n",
231                 (unsigned long long) c->usec,
232                 c->warn_wall,
233                 mode_to_string(c->mode));
234
235         if (c->dry_run)
236                 fputs("DRY_RUN=1\n", f);
237
238         if (!isempty(t))
239                 fprintf(f, "WALL_MESSAGE=%s\n", t);
240
241         free(t);
242
243         fflush(f);
244
245         if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
246                 log_error("Failed to write information about scheduled shutdowns: %m");
247                 r = -errno;
248
249                 unlink(temp_path);
250                 unlink("/run/systemd/shutdown/scheduled");
251         }
252
253         fclose(f);
254         free(temp_path);
255
256         return r;
257 }
258
259 static bool scheduled(struct sd_shutdown_command *c) {
260         return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
261 }
262
263 int main(int argc, char *argv[]) {
264         enum {
265                 FD_SOCKET,
266                 FD_WALL_TIMER,
267                 FD_NOLOGIN_TIMER,
268                 FD_SHUTDOWN_TIMER,
269                 _FD_MAX
270         };
271
272         int r = EXIT_FAILURE, n_fds;
273         union shutdown_buffer b;
274         struct pollfd pollfd[_FD_MAX];
275         bool exec_shutdown = false, unlink_nologin = false;
276         unsigned i;
277
278         if (getppid() != 1) {
279                 log_error("This program should be invoked by init only.");
280                 return EXIT_FAILURE;
281         }
282
283         if (argc > 1) {
284                 log_error("This program does not take arguments.");
285                 return EXIT_FAILURE;
286         }
287
288         log_set_target(LOG_TARGET_AUTO);
289         log_parse_environment();
290         log_open();
291
292         umask(0022);
293
294         n_fds = sd_listen_fds(true);
295         if (n_fds < 0) {
296                 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
297                 return EXIT_FAILURE;
298         }
299
300         if (n_fds != 1) {
301                 log_error("Need exactly one file descriptor.");
302                 return EXIT_FAILURE;
303         }
304
305         zero(b);
306         zero(pollfd);
307
308         pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
309         pollfd[FD_SOCKET].events = POLLIN;
310
311         for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
312                 pollfd[i].events = POLLIN;
313                 pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
314                 if (pollfd[i].fd < 0) {
315                         log_error("timerfd_create(): %m");
316                         goto finish;
317                 }
318         }
319
320         log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
321
322         sd_notify(false,
323                   "READY=1\n"
324                   "STATUS=Processing requests...");
325
326         for (;;) {
327                 int k;
328                 usec_t n;
329
330                 k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
331                 if (k < 0) {
332
333                         if (errno == EAGAIN || errno == EINTR)
334                                 continue;
335
336                         log_error("poll(): %m");
337                         goto finish;
338                 }
339
340                 /* Exit on idle */
341                 if (k == 0)
342                         break;
343
344                 n = now(CLOCK_REALTIME);
345
346                 if (pollfd[FD_SOCKET].revents) {
347
348                         k = read_packet(pollfd[FD_SOCKET].fd, &b);
349                         if (k < 0)
350                                 goto finish;
351                         else if (k > 0) {
352                                 struct itimerspec its;
353                                 char date[FORMAT_TIMESTAMP_MAX];
354
355                                 if (!scheduled(&b.command)) {
356                                         log_info("Shutdown canceled.");
357                                         break;
358                                 }
359
360                                 zero(its);
361                                 if (b.command.warn_wall) {
362                                         /* Send wall messages every so often */
363                                         timespec_store(&its.it_value, when_wall(n, b.command.usec));
364
365                                         /* Warn immediately if less than 15 minutes are left */
366                                         if (n < b.command.usec &&
367                                             n + 15*USEC_PER_MINUTE >= b.command.usec)
368                                                 warn_wall(n, &b.command);
369                                 }
370                                 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
371                                         log_error("timerfd_settime(): %m");
372                                         goto finish;
373                                 }
374
375                                 /* Disallow logins 5 minutes prior to shutdown */
376                                 zero(its);
377                                 timespec_store(&its.it_value, when_nologin(b.command.usec));
378                                 if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
379                                         log_error("timerfd_settime(): %m");
380                                         goto finish;
381                                 }
382
383                                 /* Shutdown after the specified time is reached */
384                                 zero(its);
385                                 timespec_store(&its.it_value, b.command.usec);
386                                 if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
387                                         log_error("timerfd_settime(): %m");
388                                         goto finish;
389                                 }
390
391                                 update_schedule_file(&b.command);
392
393                                 sd_notifyf(false,
394                                            "STATUS=Shutting down at %s (%s)...",
395                                            format_timestamp(date, sizeof(date), b.command.usec),
396                                            mode_to_string(b.command.mode));
397
398                                 log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
399                         }
400                 }
401
402                 if (pollfd[FD_WALL_TIMER].revents) {
403                         struct itimerspec its;
404
405                         warn_wall(n, &b.command);
406                         flush_fd(pollfd[FD_WALL_TIMER].fd);
407
408                         /* Restart timer */
409                         zero(its);
410                         timespec_store(&its.it_value, when_wall(n, b.command.usec));
411                         if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
412                                 log_error("timerfd_settime(): %m");
413                                 goto finish;
414                         }
415                 }
416
417                 if (pollfd[FD_NOLOGIN_TIMER].revents) {
418                         int e;
419
420                         log_info("Creating /run/nologin, blocking further logins...");
421
422                         e = write_one_line_file_atomic("/run/nologin", "System is going down.");
423                         if (e < 0)
424                                 log_error("Failed to create /run/nologin: %s", strerror(-e));
425                         else
426                                 unlink_nologin = true;
427
428                         flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
429                 }
430
431                 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
432                         exec_shutdown = true;
433                         goto finish;
434                 }
435         }
436
437         r = EXIT_SUCCESS;
438
439         log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
440
441 finish:
442
443         for (i = 0; i < _FD_MAX; i++)
444                 if (pollfd[i].fd >= 0)
445                         close_nointr_nofail(pollfd[i].fd);
446
447         if (unlink_nologin)
448                 unlink("/run/nologin");
449
450         unlink("/run/systemd/shutdown/scheduled");
451
452         if (exec_shutdown && !b.command.dry_run) {
453                 char sw[3];
454
455                 sw[0] = '-';
456                 sw[1] = b.command.mode;
457                 sw[2] = 0;
458
459                 execl(SYSTEMCTL_BINARY_PATH,
460                       "shutdown",
461                       sw,
462                       "now",
463                       (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
464                       (b.command.warn_wall ? NULL : "--no-wall"),
465                       NULL);
466
467                 log_error("Failed to execute /sbin/shutdown: %m");
468         }
469
470         sd_notify(false,
471                   "STATUS=Exiting...");
472
473         return r;
474 }