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