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