diff options
Diffstat (limited to 'src')
l--------- | src/shutdownd/Makefile | 1 | ||||
-rw-r--r-- | src/shutdownd/shutdownd.c | 458 | ||||
-rw-r--r-- | src/systemd/sd-shutdown.h | 119 |
3 files changed, 0 insertions, 578 deletions
diff --git a/src/shutdownd/Makefile b/src/shutdownd/Makefile deleted file mode 120000 index d0b0e8e008..0000000000 --- a/src/shutdownd/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile
\ No newline at end of file diff --git a/src/shutdownd/shutdownd.c b/src/shutdownd/shutdownd.c deleted file mode 100644 index 8b857b55cb..0000000000 --- a/src/shutdownd/shutdownd.c +++ /dev/null @@ -1,458 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -#include <sys/socket.h> -#include <poll.h> -#include <sys/timerfd.h> -#include <errno.h> -#include <unistd.h> -#include <stddef.h> - -#include "systemd/sd-daemon.h" -#include "systemd/sd-shutdown.h" - -#include "log.h" -#include "macro.h" -#include "util.h" -#include "utmp-wtmp.h" -#include "mkdir.h" -#include "fileio.h" -#include "formats-util.h" - -union shutdown_buffer { - struct sd_shutdown_command command; - char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX]; -}; - -static int read_packet(int fd, union shutdown_buffer *_b) { - struct ucred *ucred; - ssize_t n; - - union shutdown_buffer b; /* We maintain our own copy here, in - * order not to corrupt the last message */ - struct iovec iovec = { - .iov_base = &b, - .iov_len = sizeof(b) - 1, - }; - union { - struct cmsghdr cmsghdr; - uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; - } control = {}; - struct msghdr msghdr = { - .msg_iov = &iovec, - .msg_iovlen = 1, - .msg_control = &control, - .msg_controllen = sizeof(control), - }; - - assert(fd >= 0); - assert(_b); - - n = recvmsg(fd, &msghdr, MSG_DONTWAIT); - if (n < 0) { - if (errno == EAGAIN || errno == EINTR) - return 0; - - log_error_errno(errno, "recvmsg(): %m"); - return -errno; - } - - cmsg_close_all(&msghdr); - - if (n == 0) { - log_error("Short read"); - return -EIO; - } - - if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) || - control.cmsghdr.cmsg_level != SOL_SOCKET || - control.cmsghdr.cmsg_type != SCM_CREDENTIALS || - control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) { - log_warning("Received message without credentials. Ignoring."); - return 0; - } - - ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr); - if (ucred->uid != 0) { - log_warning("Got request from unprivileged user. Ignoring."); - return 0; - } - - if ((size_t) n < offsetof(struct sd_shutdown_command, wall_message)) { - log_warning("Message has invalid size. Ignoring."); - return 0; - } - - if (b.command.mode != SD_SHUTDOWN_NONE && - b.command.mode != SD_SHUTDOWN_REBOOT && - b.command.mode != SD_SHUTDOWN_POWEROFF && - b.command.mode != SD_SHUTDOWN_HALT && - b.command.mode != SD_SHUTDOWN_KEXEC) { - log_warning("Message has invalid mode. Ignoring."); - return 0; - } - - b.space[n] = 0; - - *_b = b; - return 1; -} - -static void warn_wall(usec_t n, struct sd_shutdown_command *c) { - char date[FORMAT_TIMESTAMP_MAX]; - const char *prefix; - _cleanup_free_ char *l = NULL; - - assert(c); - assert(c->warn_wall); - - if (n >= c->usec) - return; - - if (c->mode == SD_SHUTDOWN_HALT) - prefix = "The system is going down for system halt at "; - else if (c->mode == SD_SHUTDOWN_POWEROFF) - prefix = "The system is going down for power-off at "; - else if (c->mode == SD_SHUTDOWN_REBOOT) - prefix = "The system is going down for reboot at "; - else if (c->mode == SD_SHUTDOWN_KEXEC) - prefix = "The system is going down for kexec reboot at "; - else if (c->mode == SD_SHUTDOWN_NONE) - prefix = "The system shutdown has been cancelled at "; - else - assert_not_reached("Unknown mode!"); - - if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "", - prefix, format_timestamp(date, sizeof(date), c->usec)) >= 0) - utmp_wall(l, NULL, NULL, NULL, NULL); - else - log_error("Failed to allocate wall message"); -} - -_const_ static usec_t when_wall(usec_t n, usec_t elapse) { - - static const struct { - usec_t delay; - usec_t interval; - } table[] = { - { 0, USEC_PER_MINUTE }, - { 10 * USEC_PER_MINUTE, 15 * USEC_PER_MINUTE }, - { USEC_PER_HOUR, 30 * USEC_PER_MINUTE }, - { 3 * USEC_PER_HOUR, USEC_PER_HOUR }, - }; - - usec_t left, sub; - unsigned i = ELEMENTSOF(table) - 1; - - /* If the time is already passed, then don't announce */ - if (n >= elapse) - return 0; - - left = elapse - n; - while (left < table[i].delay) - i--; - sub = (left / table[i].interval) * table[i].interval; - - assert(sub < elapse); - return elapse - sub; -} - -static usec_t when_nologin(usec_t elapse) { - return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1; -} - -static const char *mode_to_string(enum sd_shutdown_mode m) { - switch (m) { - case SD_SHUTDOWN_REBOOT: - return "reboot"; - case SD_SHUTDOWN_POWEROFF: - return "poweroff"; - case SD_SHUTDOWN_HALT: - return "halt"; - case SD_SHUTDOWN_KEXEC: - return "kexec"; - default: - return NULL; - } -} - -static int update_schedule_file(struct sd_shutdown_command *c) { - int r; - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *t = NULL, *temp_path = NULL; - - assert(c); - - r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0); - if (r < 0) - return log_error_errno(r, "Failed to create shutdown subdirectory: %m"); - - t = cescape(c->wall_message); - if (!t) - return log_oom(); - - r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path); - if (r < 0) - return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m"); - - fchmod(fileno(f), 0644); - - fprintf(f, - "USEC="USEC_FMT"\n" - "WARN_WALL=%i\n" - "MODE=%s\n", - c->usec, - c->warn_wall, - mode_to_string(c->mode)); - - if (c->dry_run) - fputs("DRY_RUN=1\n", f); - - if (!isempty(t)) - fprintf(f, "WALL_MESSAGE=%s\n", t); - - fflush(f); - - if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) { - log_error_errno(errno, "Failed to write information about scheduled shutdowns: %m"); - r = -errno; - - unlink(temp_path); - unlink("/run/systemd/shutdown/scheduled"); - } - - return r; -} - -static bool scheduled(struct sd_shutdown_command *c) { - return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE; -} - -int main(int argc, char *argv[]) { - enum { - FD_SOCKET, - FD_WALL_TIMER, - FD_NOLOGIN_TIMER, - FD_SHUTDOWN_TIMER, - _FD_MAX - }; - - int r = EXIT_FAILURE, n_fds; - union shutdown_buffer b = {}; - struct pollfd pollfd[_FD_MAX] = {}; - bool exec_shutdown = false, unlink_nologin = false; - unsigned i; - - if (getppid() != 1) { - log_error("This program should be invoked by init only."); - return EXIT_FAILURE; - } - - if (argc > 1) { - log_error("This program does not take arguments."); - return EXIT_FAILURE; - } - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - umask(0022); - - n_fds = sd_listen_fds(true); - if (n_fds < 0) { - log_error_errno(r, "Failed to read listening file descriptors from environment: %m"); - return EXIT_FAILURE; - } - - if (n_fds != 1) { - log_error("Need exactly one file descriptor."); - return EXIT_FAILURE; - } - - pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START; - pollfd[FD_SOCKET].events = POLLIN; - - for (i = FD_WALL_TIMER; i < _FD_MAX; i++) { - pollfd[i].events = POLLIN; - pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (pollfd[i].fd < 0) { - log_error_errno(errno, "timerfd_create(): %m"); - goto finish; - } - } - - log_debug("systemd-shutdownd running as pid "PID_FMT, getpid()); - - sd_notify(false, - "READY=1\n" - "STATUS=Processing requests..."); - - for (;;) { - int k; - usec_t n; - - k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0); - if (k < 0) { - - if (errno == EAGAIN || errno == EINTR) - continue; - - log_error_errno(errno, "poll(): %m"); - goto finish; - } - - /* Exit on idle */ - if (k == 0) - break; - - n = now(CLOCK_REALTIME); - - if (pollfd[FD_SOCKET].revents) { - - k = read_packet(pollfd[FD_SOCKET].fd, &b); - if (k < 0) - goto finish; - else if (k > 0) { - struct itimerspec its; - char date[FORMAT_TIMESTAMP_MAX]; - - if (!scheduled(&b.command)) { - log_info("Shutdown canceled."); - if (b.command.warn_wall) - warn_wall(0, &b.command); - break; - } - - zero(its); - if (b.command.warn_wall) { - /* Send wall messages every so often */ - timespec_store(&its.it_value, when_wall(n, b.command.usec)); - - /* Warn immediately if less than 15 minutes are left */ - if (n < b.command.usec && - n + 15*USEC_PER_MINUTE >= b.command.usec) - warn_wall(n, &b.command); - } - if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { - log_error_errno(errno, "timerfd_settime(): %m"); - goto finish; - } - - /* Disallow logins 5 minutes prior to shutdown */ - zero(its); - timespec_store(&its.it_value, when_nologin(b.command.usec)); - if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { - log_error_errno(errno, "timerfd_settime(): %m"); - goto finish; - } - - /* Shutdown after the specified time is reached */ - zero(its); - timespec_store(&its.it_value, b.command.usec); - if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { - log_error_errno(errno, "timerfd_settime(): %m"); - goto finish; - } - - update_schedule_file(&b.command); - - sd_notifyf(false, - "STATUS=Shutting down at %s (%s)...", - format_timestamp(date, sizeof(date), b.command.usec), - mode_to_string(b.command.mode)); - - log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode)); - } - } - - if (pollfd[FD_WALL_TIMER].revents) { - struct itimerspec its = {}; - - warn_wall(n, &b.command); - flush_fd(pollfd[FD_WALL_TIMER].fd); - - /* Restart timer */ - timespec_store(&its.it_value, when_wall(n, b.command.usec)); - if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) { - log_error_errno(errno, "timerfd_settime(): %m"); - goto finish; - } - } - - if (pollfd[FD_NOLOGIN_TIMER].revents) { - int e; - - log_info("Creating /run/nologin, blocking further logins..."); - - e = write_string_file_atomic("/run/nologin", "System is going down."); - if (e < 0) - log_error_errno(e, "Failed to create /run/nologin: %m"); - else - unlink_nologin = true; - - flush_fd(pollfd[FD_NOLOGIN_TIMER].fd); - } - - if (pollfd[FD_SHUTDOWN_TIMER].revents) { - exec_shutdown = true; - goto finish; - } - } - - r = EXIT_SUCCESS; - - log_debug("systemd-shutdownd stopped as pid "PID_FMT, getpid()); - -finish: - - for (i = 0; i < _FD_MAX; i++) - safe_close(pollfd[i].fd); - - if (unlink_nologin) - unlink("/run/nologin"); - - unlink("/run/systemd/shutdown/scheduled"); - - if (exec_shutdown && !b.command.dry_run) { - char sw[3]; - - sw[0] = '-'; - sw[1] = b.command.mode; - sw[2] = 0; - - execl(SYSTEMCTL_BINARY_PATH, - "shutdown", - sw, - "now", - (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message : - (b.command.warn_wall ? NULL : "--no-wall"), - NULL); - - log_error_errno(errno, "Failed to execute /sbin/shutdown: %m"); - } - - sd_notify(false, - "STOPPING=\n" - "STATUS=Exiting..."); - - return r; -} diff --git a/src/systemd/sd-shutdown.h b/src/systemd/sd-shutdown.h deleted file mode 100644 index 9ff377f4e4..0000000000 --- a/src/systemd/sd-shutdown.h +++ /dev/null @@ -1,119 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foosdshutdownhfoo -#define foosdshutdownhfoo - -/*** - This file is part of systemd. - - Copyright 2010 Lennart Poettering - - systemd is free software; you can redistribute it and/or modify it - under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2.1 of the License, or - (at your option) any later version. - - systemd is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with systemd; If not, see <http://www.gnu.org/licenses/>. -***/ - -/* Interface for scheduling and cancelling timed shutdowns. */ - -#include <inttypes.h> - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef _sd_packed_ -# define _sd_packed_ __attribute__((packed)) -#endif - -typedef enum sd_shutdown_mode { - SD_SHUTDOWN_NONE = 0, - SD_SHUTDOWN_REBOOT = 'r', - SD_SHUTDOWN_POWEROFF = 'P', - SD_SHUTDOWN_HALT = 'H', - SD_SHUTDOWN_KEXEC = 'K' -} sd_shutdown_mode_t; - -/* Calculate the size of the message as "offsetof(struct - * sd_shutdown_command, wall_message) + - * strlen(command.wall_message)" */ -struct sd_shutdown_command { - /* Microseconds after the epoch 1970 UTC */ - uint64_t usec; - - /* H, P, r, i.e. the switches usually passed to - * /usr/bin/shutdown to select whether to halt, power-off or - * reboot the machine */ - sd_shutdown_mode_t mode:8; - - /* If non-zero, don't actually shut down, just pretend */ - unsigned dry_run:1; - - /* If non-zero, send our wall message */ - unsigned warn_wall:1; - - /* The wall message to send around. Leave empty for the - * default wall message */ - char wall_message[]; -} _sd_packed_; - -/* The scheme is very simple: - * - * To schedule a shutdown, simply fill in and send a single - * AF_UNIX/SOCK_DGRAM datagram with the structure above suffixed with - * the wall message to the socket /run/systemd/shutdownd (leave an - * empty wall message for the default shutdown message). To calculate - * the size of the message, use "offsetof(struct sd_shutdown_command, - * wall_message) + strlen(command.wall_message)". - * - * To cancel a shutdown, do the same, but send a fully zeroed-out - * structure. - * - * To be notified about scheduled shutdowns, create an inotify watch - * on /run/shutdown/. Whenever a file called "scheduled" appears, a - * shutdown is scheduled. If it is removed, it is canceled. If it is - * replaced, the scheduled shutdown has been changed. The file contains - * a simple, environment-like block that contains information about - * the scheduled shutdown: - * - * USEC= - * encodes the time for the shutdown in usecs since the epoch UTC, - * formatted as a numeric string. - * - * WARN_WALL= - * is 1 if a wall message shall be sent - * - * DRY_RUN= - * is 1 if a dry-run shutdown is scheduled - * - * MODE= - * is the shutdown mode, one of "poweroff", "reboot", "halt", "kexec" - * - * WALL_MESSAGE= - * is the wall message to use, with all special characters escaped in C-style. - * - * Note that some fields might be missing if they do not apply. - * - * Note that the file is first written to a temporary file and then - * renamed, in order to provide atomic properties for readers: if the - * file exists under the name "scheduled", it is guaranteed to be fully - * written. A reader should ignore all files in that directory by any - * other name. - * - * Scheduled shutdowns are only accepted from privileged processes, - * but anyone may watch the directory and the file in it. - */ - -#ifdef __cplusplus -} -#endif - -#endif |