]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shutdownd/shutdownd.c
shutdownd: clean up initialization of struct
[thirdparty/systemd.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 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 (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 _cleanup_free_ 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 utmp_wall(l, NULL, NULL);
147 else
148 log_error("Failed to allocate wall message");
149 }
150
151 _const_ 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 { 0, USEC_PER_MINUTE },
158 { 10 * USEC_PER_MINUTE, 15 * USEC_PER_MINUTE },
159 { USEC_PER_HOUR, 30 * USEC_PER_MINUTE },
160 { 3 * USEC_PER_HOUR, USEC_PER_HOUR },
161 };
162
163 usec_t left, sub;
164 unsigned i = ELEMENTSOF(table) - 1;
165
166 /* If the time is already passed, then don't announce */
167 if (n >= elapse)
168 return 0;
169
170 left = elapse - n;
171 while (left < table[i].delay)
172 i--;
173 sub = (left / table[i].interval) * table[i].interval;
174
175 assert(sub < elapse);
176 return elapse - sub;
177 }
178
179 static usec_t when_nologin(usec_t elapse) {
180 return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
181 }
182
183 static const char *mode_to_string(enum sd_shutdown_mode m) {
184 switch (m) {
185 case SD_SHUTDOWN_REBOOT:
186 return "reboot";
187 case SD_SHUTDOWN_POWEROFF:
188 return "poweroff";
189 case SD_SHUTDOWN_HALT:
190 return "halt";
191 case SD_SHUTDOWN_KEXEC:
192 return "kexec";
193 default:
194 return NULL;
195 }
196 }
197
198 static int update_schedule_file(struct sd_shutdown_command *c) {
199 int r;
200 _cleanup_fclose_ FILE *f = NULL;
201 _cleanup_free_ char *t = NULL, *temp_path = NULL;
202
203 assert(c);
204
205 r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
206 if (r < 0) {
207 log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
208 return r;
209 }
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 log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
218 return r;
219 }
220
221 fchmod(fileno(f), 0644);
222
223 fprintf(f,
224 "USEC="USEC_FMT"\n"
225 "WARN_WALL=%i\n"
226 "MODE=%s\n",
227 c->usec,
228 c->warn_wall,
229 mode_to_string(c->mode));
230
231 if (c->dry_run)
232 fputs("DRY_RUN=1\n", f);
233
234 if (!isempty(t))
235 fprintf(f, "WALL_MESSAGE=%s\n", t);
236
237 fflush(f);
238
239 if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
240 log_error("Failed to write information about scheduled shutdowns: %m");
241 r = -errno;
242
243 unlink(temp_path);
244 unlink("/run/systemd/shutdown/scheduled");
245 }
246
247 return r;
248 }
249
250 static bool scheduled(struct sd_shutdown_command *c) {
251 return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
252 }
253
254 int main(int argc, char *argv[]) {
255 enum {
256 FD_SOCKET,
257 FD_WALL_TIMER,
258 FD_NOLOGIN_TIMER,
259 FD_SHUTDOWN_TIMER,
260 _FD_MAX
261 };
262
263 int r = EXIT_FAILURE, n_fds;
264 union shutdown_buffer b = {};
265 struct pollfd pollfd[_FD_MAX] = {};
266 bool exec_shutdown = false, unlink_nologin = false;
267 unsigned i;
268
269 if (getppid() != 1) {
270 log_error("This program should be invoked by init only.");
271 return EXIT_FAILURE;
272 }
273
274 if (argc > 1) {
275 log_error("This program does not take arguments.");
276 return EXIT_FAILURE;
277 }
278
279 log_set_target(LOG_TARGET_AUTO);
280 log_parse_environment();
281 log_open();
282
283 umask(0022);
284
285 n_fds = sd_listen_fds(true);
286 if (n_fds < 0) {
287 log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
288 return EXIT_FAILURE;
289 }
290
291 if (n_fds != 1) {
292 log_error("Need exactly one file descriptor.");
293 return EXIT_FAILURE;
294 }
295
296 pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
297 pollfd[FD_SOCKET].events = POLLIN;
298
299 for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
300 pollfd[i].events = POLLIN;
301 pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
302 if (pollfd[i].fd < 0) {
303 log_error("timerfd_create(): %m");
304 goto finish;
305 }
306 }
307
308 log_debug("systemd-shutdownd running as pid "PID_FMT, getpid());
309
310 sd_notify(false,
311 "READY=1\n"
312 "STATUS=Processing requests...");
313
314 for (;;) {
315 int k;
316 usec_t n;
317
318 k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
319 if (k < 0) {
320
321 if (errno == EAGAIN || errno == EINTR)
322 continue;
323
324 log_error("poll(): %m");
325 goto finish;
326 }
327
328 /* Exit on idle */
329 if (k == 0)
330 break;
331
332 n = now(CLOCK_REALTIME);
333
334 if (pollfd[FD_SOCKET].revents) {
335
336 k = read_packet(pollfd[FD_SOCKET].fd, &b);
337 if (k < 0)
338 goto finish;
339 else if (k > 0) {
340 struct itimerspec its;
341 char date[FORMAT_TIMESTAMP_MAX];
342
343 if (!scheduled(&b.command)) {
344 log_info("Shutdown canceled.");
345 if (b.command.warn_wall)
346 warn_wall(0, &b.command);
347 break;
348 }
349
350 zero(its);
351 if (b.command.warn_wall) {
352 /* Send wall messages every so often */
353 timespec_store(&its.it_value, when_wall(n, b.command.usec));
354
355 /* Warn immediately if less than 15 minutes are left */
356 if (n < b.command.usec &&
357 n + 15*USEC_PER_MINUTE >= b.command.usec)
358 warn_wall(n, &b.command);
359 }
360 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
361 log_error("timerfd_settime(): %m");
362 goto finish;
363 }
364
365 /* Disallow logins 5 minutes prior to shutdown */
366 zero(its);
367 timespec_store(&its.it_value, when_nologin(b.command.usec));
368 if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
369 log_error("timerfd_settime(): %m");
370 goto finish;
371 }
372
373 /* Shutdown after the specified time is reached */
374 zero(its);
375 timespec_store(&its.it_value, b.command.usec);
376 if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
377 log_error("timerfd_settime(): %m");
378 goto finish;
379 }
380
381 update_schedule_file(&b.command);
382
383 sd_notifyf(false,
384 "STATUS=Shutting down at %s (%s)...",
385 format_timestamp(date, sizeof(date), b.command.usec),
386 mode_to_string(b.command.mode));
387
388 log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
389 }
390 }
391
392 if (pollfd[FD_WALL_TIMER].revents) {
393 struct itimerspec its = {};
394
395 warn_wall(n, &b.command);
396 flush_fd(pollfd[FD_WALL_TIMER].fd);
397
398 /* Restart timer */
399 timespec_store(&its.it_value, when_wall(n, b.command.usec));
400 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
401 log_error("timerfd_settime(): %m");
402 goto finish;
403 }
404 }
405
406 if (pollfd[FD_NOLOGIN_TIMER].revents) {
407 int e;
408
409 log_info("Creating /run/nologin, blocking further logins...");
410
411 e = write_string_file_atomic("/run/nologin", "System is going down.");
412 if (e < 0)
413 log_error("Failed to create /run/nologin: %s", strerror(-e));
414 else
415 unlink_nologin = true;
416
417 flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
418 }
419
420 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
421 exec_shutdown = true;
422 goto finish;
423 }
424 }
425
426 r = EXIT_SUCCESS;
427
428 log_debug("systemd-shutdownd stopped as pid "PID_FMT, getpid());
429
430 finish:
431
432 for (i = 0; i < _FD_MAX; i++)
433 safe_close(pollfd[i].fd);
434
435 if (unlink_nologin)
436 unlink("/run/nologin");
437
438 unlink("/run/systemd/shutdown/scheduled");
439
440 if (exec_shutdown && !b.command.dry_run) {
441 char sw[3];
442
443 sw[0] = '-';
444 sw[1] = b.command.mode;
445 sw[2] = 0;
446
447 execl(SYSTEMCTL_BINARY_PATH,
448 "shutdown",
449 sw,
450 "now",
451 (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
452 (b.command.warn_wall ? NULL : "--no-wall"),
453 NULL);
454
455 log_error("Failed to execute /sbin/shutdown: %m");
456 }
457
458 sd_notify(false,
459 "STOPPING=\n"
460 "STATUS=Exiting...");
461
462 return r;
463 }