]> git.proxmox.com Git - systemd.git/blame - src/shutdownd/shutdownd.c
Imported Upstream version 218
[systemd.git] / src / shutdownd / shutdownd.c
CommitLineData
663996b3
MS
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
5eef597e
MP
33#include "systemd/sd-daemon.h"
34#include "systemd/sd-shutdown.h"
663996b3
MS
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
43union shutdown_buffer {
44 struct sd_shutdown_command command;
45 char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
46};
47
48static 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 = {
5eef597e
MP
55 .iov_base = &b,
56 .iov_len = sizeof(b) - 1,
663996b3
MS
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,
60f067b4
JS
64 .msg_iovlen = 1,
65 .msg_control = &control,
66 .msg_controllen = sizeof(control),
663996b3
MS
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
f47781d8 82 log_error_errno(errno, "recvmsg(): %m");
663996b3
MS
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
120static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
121 char date[FORMAT_TIMESTAMP_MAX];
122 const char *prefix;
60f067b4 123 _cleanup_free_ char *l = NULL;
663996b3
MS
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" : "",
60f067b4
JS
145 prefix, format_timestamp(date, sizeof(date), c->usec)) >= 0)
146 utmp_wall(l, NULL, NULL);
147 else
663996b3 148 log_error("Failed to allocate wall message");
663996b3
MS
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
179static usec_t when_nologin(usec_t elapse) {
180 return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
181}
182
183static 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
198static int update_schedule_file(struct sd_shutdown_command *c) {
199 int r;
60f067b4
JS
200 _cleanup_fclose_ FILE *f = NULL;
201 _cleanup_free_ char *t = NULL, *temp_path = NULL;
663996b3
MS
202
203 assert(c);
204
205 r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0);
f47781d8
MP
206 if (r < 0)
207 return log_error_errno(r, "Failed to create shutdown subdirectory: %m");
663996b3
MS
208
209 t = cescape(c->wall_message);
210 if (!t)
211 return log_oom();
212
213 r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
f47781d8
MP
214 if (r < 0)
215 return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m");
663996b3
MS
216
217 fchmod(fileno(f), 0644);
218
219 fprintf(f,
60f067b4 220 "USEC="USEC_FMT"\n"
663996b3
MS
221 "WARN_WALL=%i\n"
222 "MODE=%s\n",
60f067b4 223 c->usec,
663996b3
MS
224 c->warn_wall,
225 mode_to_string(c->mode));
226
227 if (c->dry_run)
228 fputs("DRY_RUN=1\n", f);
229
230 if (!isempty(t))
231 fprintf(f, "WALL_MESSAGE=%s\n", t);
232
663996b3
MS
233 fflush(f);
234
235 if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
f47781d8 236 log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m");
663996b3
MS
237 r = -errno;
238
239 unlink(temp_path);
240 unlink("/run/systemd/shutdown/scheduled");
241 }
242
663996b3
MS
243 return r;
244}
245
246static bool scheduled(struct sd_shutdown_command *c) {
247 return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
248}
249
250int main(int argc, char *argv[]) {
251 enum {
252 FD_SOCKET,
253 FD_WALL_TIMER,
254 FD_NOLOGIN_TIMER,
255 FD_SHUTDOWN_TIMER,
256 _FD_MAX
257 };
258
259 int r = EXIT_FAILURE, n_fds;
260 union shutdown_buffer b = {};
261 struct pollfd pollfd[_FD_MAX] = {};
262 bool exec_shutdown = false, unlink_nologin = false;
263 unsigned i;
264
265 if (getppid() != 1) {
266 log_error("This program should be invoked by init only.");
267 return EXIT_FAILURE;
268 }
269
270 if (argc > 1) {
271 log_error("This program does not take arguments.");
272 return EXIT_FAILURE;
273 }
274
275 log_set_target(LOG_TARGET_AUTO);
276 log_parse_environment();
277 log_open();
278
279 umask(0022);
280
281 n_fds = sd_listen_fds(true);
282 if (n_fds < 0) {
f47781d8 283 log_error_errno(r, "Failed to read listening file descriptors from environment: %m");
663996b3
MS
284 return EXIT_FAILURE;
285 }
286
287 if (n_fds != 1) {
288 log_error("Need exactly one file descriptor.");
289 return EXIT_FAILURE;
290 }
291
292 pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
293 pollfd[FD_SOCKET].events = POLLIN;
294
295 for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
296 pollfd[i].events = POLLIN;
297 pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
298 if (pollfd[i].fd < 0) {
f47781d8 299 log_error_errno(errno, "timerfd_create(): %m");
663996b3
MS
300 goto finish;
301 }
302 }
303
60f067b4 304 log_debug("systemd-shutdownd running as pid "PID_FMT, getpid());
663996b3
MS
305
306 sd_notify(false,
307 "READY=1\n"
308 "STATUS=Processing requests...");
309
310 for (;;) {
311 int k;
312 usec_t n;
313
314 k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
315 if (k < 0) {
316
317 if (errno == EAGAIN || errno == EINTR)
318 continue;
319
f47781d8 320 log_error_errno(errno, "poll(): %m");
663996b3
MS
321 goto finish;
322 }
323
324 /* Exit on idle */
325 if (k == 0)
326 break;
327
328 n = now(CLOCK_REALTIME);
329
330 if (pollfd[FD_SOCKET].revents) {
331
332 k = read_packet(pollfd[FD_SOCKET].fd, &b);
333 if (k < 0)
334 goto finish;
335 else if (k > 0) {
336 struct itimerspec its;
337 char date[FORMAT_TIMESTAMP_MAX];
338
339 if (!scheduled(&b.command)) {
340 log_info("Shutdown canceled.");
341 if (b.command.warn_wall)
342 warn_wall(0, &b.command);
343 break;
344 }
345
346 zero(its);
347 if (b.command.warn_wall) {
348 /* Send wall messages every so often */
349 timespec_store(&its.it_value, when_wall(n, b.command.usec));
350
351 /* Warn immediately if less than 15 minutes are left */
352 if (n < b.command.usec &&
353 n + 15*USEC_PER_MINUTE >= b.command.usec)
354 warn_wall(n, &b.command);
355 }
356 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
f47781d8 357 log_error_errno(errno, "timerfd_settime(): %m");
663996b3
MS
358 goto finish;
359 }
360
361 /* Disallow logins 5 minutes prior to shutdown */
362 zero(its);
363 timespec_store(&its.it_value, when_nologin(b.command.usec));
364 if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
f47781d8 365 log_error_errno(errno, "timerfd_settime(): %m");
663996b3
MS
366 goto finish;
367 }
368
369 /* Shutdown after the specified time is reached */
370 zero(its);
371 timespec_store(&its.it_value, b.command.usec);
372 if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
f47781d8 373 log_error_errno(errno, "timerfd_settime(): %m");
663996b3
MS
374 goto finish;
375 }
376
377 update_schedule_file(&b.command);
378
379 sd_notifyf(false,
380 "STATUS=Shutting down at %s (%s)...",
381 format_timestamp(date, sizeof(date), b.command.usec),
382 mode_to_string(b.command.mode));
383
384 log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
385 }
386 }
387
388 if (pollfd[FD_WALL_TIMER].revents) {
389 struct itimerspec its = {};
390
391 warn_wall(n, &b.command);
392 flush_fd(pollfd[FD_WALL_TIMER].fd);
393
394 /* Restart timer */
395 timespec_store(&its.it_value, when_wall(n, b.command.usec));
396 if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
f47781d8 397 log_error_errno(errno, "timerfd_settime(): %m");
663996b3
MS
398 goto finish;
399 }
400 }
401
402 if (pollfd[FD_NOLOGIN_TIMER].revents) {
403 int e;
404
405 log_info("Creating /run/nologin, blocking further logins...");
406
407 e = write_string_file_atomic("/run/nologin", "System is going down.");
408 if (e < 0)
f47781d8 409 log_error_errno(e, "Failed to create /run/nologin: %m");
663996b3
MS
410 else
411 unlink_nologin = true;
412
413 flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
414 }
415
416 if (pollfd[FD_SHUTDOWN_TIMER].revents) {
417 exec_shutdown = true;
418 goto finish;
419 }
420 }
421
422 r = EXIT_SUCCESS;
423
60f067b4 424 log_debug("systemd-shutdownd stopped as pid "PID_FMT, getpid());
663996b3
MS
425
426finish:
427
428 for (i = 0; i < _FD_MAX; i++)
60f067b4 429 safe_close(pollfd[i].fd);
663996b3
MS
430
431 if (unlink_nologin)
432 unlink("/run/nologin");
433
434 unlink("/run/systemd/shutdown/scheduled");
435
436 if (exec_shutdown && !b.command.dry_run) {
437 char sw[3];
438
439 sw[0] = '-';
440 sw[1] = b.command.mode;
441 sw[2] = 0;
442
443 execl(SYSTEMCTL_BINARY_PATH,
444 "shutdown",
445 sw,
446 "now",
447 (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
448 (b.command.warn_wall ? NULL : "--no-wall"),
449 NULL);
450
f47781d8 451 log_error_errno(errno, "Failed to execute /sbin/shutdown: %m");
663996b3
MS
452 }
453
454 sd_notify(false,
5eef597e 455 "STOPPING=\n"
663996b3
MS
456 "STATUS=Exiting...");
457
458 return r;
459}