chiark / gitweb /
journal: log user units for coredumps and show them in systemctl status
[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 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 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                 { 10 * USEC_PER_MINUTE, USEC_PER_MINUTE      },
160                 { USEC_PER_HOUR,        15 * USEC_PER_MINUTE },
161                 { 3 * USEC_PER_HOUR,    30 * USEC_PER_MINUTE }
162         };
163
164         usec_t left, sub;
165         unsigned i;
166
167         /* If the time is already passed, then don't announce */
168         if (n >= elapse)
169                 return 0;
170
171         left = elapse - n;
172         for (i = 0; i < ELEMENTSOF(table); i++)
173                 if (n + table[i].delay >= elapse) {
174                         sub = ((left / table[i].interval) * table[i].interval);
175                         break;
176                 }
177
178         if (i >= ELEMENTSOF(table))
179                 sub = ((left / USEC_PER_HOUR) * USEC_PER_HOUR);
180
181         return elapse > sub ? elapse - sub : 1;
182 }
183
184 static usec_t when_nologin(usec_t elapse) {
185         return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
186 }
187
188 static const char *mode_to_string(enum sd_shutdown_mode m) {
189         switch (m) {
190         case SD_SHUTDOWN_REBOOT:
191                 return "reboot";
192         case SD_SHUTDOWN_POWEROFF:
193                 return "poweroff";
194         case SD_SHUTDOWN_HALT:
195                 return "halt";
196         case SD_SHUTDOWN_KEXEC:
197                 return "kexec";
198         default:
199                 return NULL;
200         }
201 }
202
203 static int update_schedule_file(struct sd_shutdown_command *c) {
204         int r;
205         FILE *f;
206         char *temp_path, *t;
207
208         assert(c);
209
210         r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
211         if (r < 0) {
212                 log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
213                 return r;
214         }
215
216         t = cescape(c->wall_message);
217         if (!t)
218                 return log_oom();
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                                         if (b.command.warn_wall)
360                                                 warn_wall(0, &b.command);
361                                         break;
362                                 }
363
364                                 zero(its);
365                                 if (b.command.warn_wall) {
366                                         /* Send wall messages every so often */
367                                         timespec_store(&its.it_value, when_wall(n, b.command.usec));
368
369                                         /* Warn immediately if less than 15 minutes are left */
370                                         if (n < b.command.usec &&
371                                             n + 15*USEC_PER_MINUTE >= b.command.usec)
372                                                 warn_wall(n, &b.command);
373                                 }
374                                 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
375                                         log_error("timerfd_settime(): %m");
376                                         goto finish;
377                                 }
378
379                                 /* Disallow logins 5 minutes prior to shutdown */
380                                 zero(its);
381                                 timespec_store(&its.it_value, when_nologin(b.command.usec));
382                                 if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
383                                         log_error("timerfd_settime(): %m");
384                                         goto finish;
385                                 }
386
387                                 /* Shutdown after the specified time is reached */
388                                 zero(its);
389                                 timespec_store(&its.it_value, b.command.usec);
390                                 if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
391                                         log_error("timerfd_settime(): %m");
392                                         goto finish;
393                                 }
394
395                                 update_schedule_file(&b.command);
396
397                                 sd_notifyf(false,
398                                            "STATUS=Shutting down at %s (%s)...",
399                                            format_timestamp(date, sizeof(date), b.command.usec),
400                                            mode_to_string(b.command.mode));
401
402                                 log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
403                         }
404                 }
405
406                 if (pollfd[FD_WALL_TIMER].revents) {
407                         struct itimerspec its;
408
409                         warn_wall(n, &b.command);
410                         flush_fd(pollfd[FD_WALL_TIMER].fd);
411
412                         /* Restart timer */
413                         zero(its);
414                         timespec_store(&its.it_value, when_wall(n, b.command.usec));
415                         if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
416                                 log_error("timerfd_settime(): %m");
417                                 goto finish;
418                         }
419                 }
420
421                 if (pollfd[FD_NOLOGIN_TIMER].revents) {
422                         int e;
423
424                         log_info("Creating /run/nologin, blocking further logins...");
425
426                         e = write_one_line_file_atomic("/run/nologin", "System is going down.");
427                         if (e < 0)
428                                 log_error("Failed to create /run/nologin: %s", strerror(-e));
429                         else
430                                 unlink_nologin = true;
431
432                         flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
433                 }
434
435                 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
436                         exec_shutdown = true;
437                         goto finish;
438                 }
439         }
440
441         r = EXIT_SUCCESS;
442
443         log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
444
445 finish:
446
447         for (i = 0; i < _FD_MAX; i++)
448                 if (pollfd[i].fd >= 0)
449                         close_nointr_nofail(pollfd[i].fd);
450
451         if (unlink_nologin)
452                 unlink("/run/nologin");
453
454         unlink("/run/systemd/shutdown/scheduled");
455
456         if (exec_shutdown && !b.command.dry_run) {
457                 char sw[3];
458
459                 sw[0] = '-';
460                 sw[1] = b.command.mode;
461                 sw[2] = 0;
462
463                 execl(SYSTEMCTL_BINARY_PATH,
464                       "shutdown",
465                       sw,
466                       "now",
467                       (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
468                       (b.command.warn_wall ? NULL : "--no-wall"),
469                       NULL);
470
471                 log_error("Failed to execute /sbin/shutdown: %m");
472         }
473
474         sd_notify(false,
475                   "STATUS=Exiting...");
476
477         return r;
478 }