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