--- Makefile.am | 6 +- src/core/shutdown.c | 420 ----------------------------------------- src/power/Makefile | 28 +++ src/power/shutdown.c | 420 +++++++++++++++++++++++++++++++++++++++++ src/power/shutdownd.c | 461 ++++++++++++++++++++++++++++++++++++++++++++++ src/power/sleep.c | 219 ++++++++++++++++++++++ src/shutdownd/Makefile | 1 - src/shutdownd/shutdownd.c | 461 ---------------------------------------------- src/sleep/Makefile | 1 - src/sleep/sleep.c | 219 ---------------------- 10 files changed, 1131 insertions(+), 1105 deletions(-) delete mode 100644 src/core/shutdown.c create mode 100644 src/power/Makefile create mode 100644 src/power/shutdown.c create mode 100644 src/power/shutdownd.c create mode 100644 src/power/sleep.c delete mode 120000 src/shutdownd/Makefile delete mode 100644 src/shutdownd/shutdownd.c delete mode 120000 src/sleep/Makefile delete mode 100644 src/sleep/sleep.c
diff --git a/Makefile.am b/Makefile.am index ba63f68..52ec7ef 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2122,7 +2122,7 @@ systemd_update_done_LDADD = \ # ------------------------------------------------------------------------------ systemd_shutdownd_SOURCES = \ - src/shutdownd/shutdownd.c + src/power/shutdownd.c systemd_shutdownd_LDADD = \ libsystemd-label.la \ @@ -2136,7 +2136,7 @@ dist_doc_DATA += \ systemd_shutdown_SOURCES = \ src/core/umount.c \ src/core/umount.h \ - src/core/shutdown.c \ + src/power/shutdown.c \ src/core/mount-setup.c \ src/core/mount-setup.h \ src/core/killall.h \ @@ -2340,7 +2340,7 @@ systemd_sysctl_LDADD = \ # ------------------------------------------------------------------------------ systemd_sleep_SOURCES = \ - src/sleep/sleep.c + src/power/sleep.c systemd_sleep_LDADD = \ libsystemd-shared.la diff --git a/src/core/shutdown.c b/src/core/shutdown.c deleted file mode 100644 index 71f001a..0000000 --- a/src/core/shutdown.c +++ /dev/null @@ -1,420 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2010 ProFUSION embedded systems - - 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/mman.h> -#include <sys/types.h> -#include <sys/reboot.h> -#include <linux/reboot.h> -#include <sys/wait.h> -#include <sys/stat.h> -#include <sys/mount.h> -#include <sys/syscall.h> -#include <fcntl.h> -#include <dirent.h> -#include <errno.h> -#include <unistd.h> -#include <signal.h> -#include <stdbool.h> -#include <stdlib.h> -#include <string.h> -#include <getopt.h> - -#include "missing.h" -#include "log.h" -#include "fileio.h" -#include "umount.h" -#include "util.h" -#include "mkdir.h" -#include "virt.h" -#include "watchdog.h" -#include "killall.h" -#include "cgroup-util.h" -#include "def.h" -#include "switch-root.h" -#include "strv.h" - -#define FINALIZE_ATTEMPTS 50 - -static char* arg_verb; - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_LOG_LEVEL = 0x100, - ARG_LOG_TARGET, - ARG_LOG_COLOR, - ARG_LOG_LOCATION, - }; - - static const struct option options[] = { - { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, - { "log-target", required_argument, NULL, ARG_LOG_TARGET }, - { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, - { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, - {} - }; - - int c, r; - - assert(argc >= 1); - assert(argv); - - /* "-" prevents getopt from permuting argv[] and moving the verb away - * from argv[1]. Our interface to initrd promises it'll be there. */ - while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0) - switch (c) { - - case ARG_LOG_LEVEL: - r = log_set_max_level_from_string(optarg); - if (r < 0) - log_error("Failed to parse log level %s, ignoring.", optarg); - - break; - - case ARG_LOG_TARGET: - r = log_set_target_from_string(optarg); - if (r < 0) - log_error("Failed to parse log target %s, ignoring", optarg); - - break; - - case ARG_LOG_COLOR: - - if (optarg) { - r = log_show_color_from_string(optarg); - if (r < 0) - log_error("Failed to parse log color setting %s, ignoring", optarg); - } else - log_show_color(true); - - break; - - case ARG_LOG_LOCATION: - if (optarg) { - r = log_show_location_from_string(optarg); - if (r < 0) - log_error("Failed to parse log location setting %s, ignoring", optarg); - } else - log_show_location(true); - - break; - - case '\001': - if (!arg_verb) - arg_verb = optarg; - else - log_error("Excess arguments, ignoring"); - break; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option code."); - } - - if (!arg_verb) { - log_error("Verb argument missing."); - return -EINVAL; - } - - return 0; -} - -static int switch_root_initramfs(void) { - if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) - return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m"); - - if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) - return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m"); - - /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors. - * /run/initramfs/shutdown will take care of these. - * Also do not detach the old root, because /run/initramfs/shutdown needs to access it. - */ - return switch_root("/run/initramfs", "/oldroot", false, MS_BIND); -} - - -int main(int argc, char *argv[]) { - bool need_umount, need_swapoff, need_loop_detach, need_dm_detach; - bool in_container, use_watchdog = false; - _cleanup_free_ char *cgroup = NULL; - char *arguments[3]; - unsigned retries; - int cmd, r; - static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL}; - - log_parse_environment(); - r = parse_argv(argc, argv); - if (r < 0) - goto error; - - /* journald will die if not gone yet. The log target defaults - * to console, but may have been changed by command line options. */ - - log_close_console(); /* force reopen of /dev/console */ - log_open(); - - umask(0022); - - if (getpid() != 1) { - log_error("Not executed by init (PID 1)."); - r = -EPERM; - goto error; - } - - if (streq(arg_verb, "reboot")) - cmd = RB_AUTOBOOT; - else if (streq(arg_verb, "poweroff")) - cmd = RB_POWER_OFF; - else if (streq(arg_verb, "halt")) - cmd = RB_HALT_SYSTEM; - else if (streq(arg_verb, "kexec")) - cmd = LINUX_REBOOT_CMD_KEXEC; - else { - r = -EINVAL; - log_error("Unknown action '%s'.", arg_verb); - goto error; - } - - cg_get_root_path(&cgroup); - - use_watchdog = !!getenv("WATCHDOG_USEC"); - - /* lock us into memory */ - mlockall(MCL_CURRENT|MCL_FUTURE); - - log_info("Sending SIGTERM to remaining processes..."); - broadcast_signal(SIGTERM, true, true); - - log_info("Sending SIGKILL to remaining processes..."); - broadcast_signal(SIGKILL, true, false); - - in_container = detect_container(NULL) > 0; - - need_umount = !in_container; - need_swapoff = !in_container; - need_loop_detach = !in_container; - need_dm_detach = !in_container; - - /* Unmount all mountpoints, swaps, and loopback devices */ - for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) { - bool changed = false; - - if (use_watchdog) - watchdog_ping(); - - /* Let's trim the cgroup tree on each iteration so - that we leave an empty cgroup tree around, so that - container managers get a nice notify event when we - are down */ - if (cgroup) - cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false); - - if (need_umount) { - log_info("Unmounting file systems."); - r = umount_all(&changed); - if (r == 0) { - need_umount = false; - log_info("All filesystems unmounted."); - } else if (r > 0) - log_info("Not all file systems unmounted, %d left.", r); - else - log_error_errno(r, "Failed to unmount file systems: %m"); - } - - if (need_swapoff) { - log_info("Deactivating swaps."); - r = swapoff_all(&changed); - if (r == 0) { - need_swapoff = false; - log_info("All swaps deactivated."); - } else if (r > 0) - log_info("Not all swaps deactivated, %d left.", r); - else - log_error_errno(r, "Failed to deactivate swaps: %m"); - } - - if (need_loop_detach) { - log_info("Detaching loop devices."); - r = loopback_detach_all(&changed); - if (r == 0) { - need_loop_detach = false; - log_info("All loop devices detached."); - } else if (r > 0) - log_info("Not all loop devices detached, %d left.", r); - else - log_error_errno(r, "Failed to detach loop devices: %m"); - } - - if (need_dm_detach) { - log_info("Detaching DM devices."); - r = dm_detach_all(&changed); - if (r == 0) { - need_dm_detach = false; - log_info("All DM devices detached."); - } else if (r > 0) - log_info("Not all DM devices detached, %d left.", r); - else - log_error_errno(r, "Failed to detach DM devices: %m"); - } - - if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) { - if (retries > 0) - log_info("All filesystems, swaps, loop devices, DM devices detached."); - /* Yay, done */ - goto initrd_jump; - } - - /* If in this iteration we didn't manage to - * unmount/deactivate anything, we simply give up */ - if (!changed) { - log_info("Cannot finalize remaining%s%s%s%s continuing.", - need_umount ? " file systems," : "", - need_swapoff ? " swap devices," : "", - need_loop_detach ? " loop devices," : "", - need_dm_detach ? " DM devices," : ""); - goto initrd_jump; - } - - log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.", - retries + 1, - need_umount ? " file systems," : "", - need_swapoff ? " swap devices," : "", - need_loop_detach ? " loop devices," : "", - need_dm_detach ? " DM devices," : ""); - } - - log_error("Too many iterations, giving up."); - - initrd_jump: - - arguments[0] = NULL; - arguments[1] = arg_verb; - arguments[2] = NULL; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - if (!in_container && !in_initrd() && - access("/run/initramfs/shutdown", X_OK) == 0) { - r = switch_root_initramfs(); - if (r >= 0) { - argv[0] = (char*) "/shutdown"; - - setsid(); - make_console_stdio(); - - log_info("Successfully changed into root pivot.\n" - "Returning to initrd..."); - - execv("/shutdown", argv); - log_error_errno(errno, "Failed to execute shutdown binary: %m"); - } else - log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m"); - - } - - if (need_umount || need_swapoff || need_loop_detach || need_dm_detach) - log_error("Failed to finalize %s%s%s%s ignoring", - need_umount ? " file systems," : "", - need_swapoff ? " swap devices," : "", - need_loop_detach ? " loop devices," : "", - need_dm_detach ? " DM devices," : ""); - - /* The kernel will automaticall flush ATA disks and suchlike - * on reboot(), but the file systems need to be synce'd - * explicitly in advance. So let's do this here, but not - * needlessly slow down containers. */ - if (!in_container) - sync(); - - switch (cmd) { - - case LINUX_REBOOT_CMD_KEXEC: - - if (!in_container) { - /* We cheat and exec kexec to avoid doing all its work */ - pid_t pid; - - log_info("Rebooting with kexec."); - - pid = fork(); - if (pid < 0) - log_error_errno(errno, "Failed to fork: %m"); - else if (pid == 0) { - - const char * const args[] = { - KEXEC, "-e", NULL - }; - - /* Child */ - - execv(args[0], (char * const *) args); - _exit(EXIT_FAILURE); - } else - wait_for_terminate_and_warn("kexec", pid, true); - } - - cmd = RB_AUTOBOOT; - /* Fall through */ - - case RB_AUTOBOOT: - - if (!in_container) { - _cleanup_free_ char *param = NULL; - - if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) { - log_info("Rebooting with argument '%s'.", param); - syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); - } - } - - log_info("Rebooting."); - break; - - case RB_POWER_OFF: - log_info("Powering off."); - break; - - case RB_HALT_SYSTEM: - log_info("Halting system."); - break; - - default: - assert_not_reached("Unknown magic"); - } - - reboot(cmd); - if (errno == EPERM && in_container) { - /* If we are in a container, and we lacked - * CAP_SYS_BOOT just exit, this will kill our - * container for good. */ - log_info("Exiting container."); - exit(0); - } - - log_error_errno(errno, "Failed to invoke reboot(): %m"); - r = -errno; - - error: - log_emergency_errno(r, "Critical error while doing system shutdown: %m"); - - freeze(); -} diff --git a/src/power/Makefile b/src/power/Makefile new file mode 100644 index 0000000..9d07505 --- /dev/null +++ b/src/power/Makefile @@ -0,0 +1,28 @@ +# 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/>. + +# This file is a dirty trick to simplify compilation from within +# emacs. This file is not intended to be distributed. So, don't touch +# it, even better ignore it! + +all: + $(MAKE) -C .. + +clean: + $(MAKE) -C .. clean + +.PHONY: all clean diff --git a/src/power/shutdown.c b/src/power/shutdown.c new file mode 100644 index 0000000..71f001a --- /dev/null +++ b/src/power/shutdown.c @@ -0,0 +1,420 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 ProFUSION embedded systems + + 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/mman.h> +#include <sys/types.h> +#include <sys/reboot.h> +#include <linux/reboot.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/syscall.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <getopt.h> + +#include "missing.h" +#include "log.h" +#include "fileio.h" +#include "umount.h" +#include "util.h" +#include "mkdir.h" +#include "virt.h" +#include "watchdog.h" +#include "killall.h" +#include "cgroup-util.h" +#include "def.h" +#include "switch-root.h" +#include "strv.h" + +#define FINALIZE_ATTEMPTS 50 + +static char* arg_verb; + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_LOG_LEVEL = 0x100, + ARG_LOG_TARGET, + ARG_LOG_COLOR, + ARG_LOG_LOCATION, + }; + + static const struct option options[] = { + { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, + { "log-target", required_argument, NULL, ARG_LOG_TARGET }, + { "log-color", optional_argument, NULL, ARG_LOG_COLOR }, + { "log-location", optional_argument, NULL, ARG_LOG_LOCATION }, + {} + }; + + int c, r; + + assert(argc >= 1); + assert(argv); + + /* "-" prevents getopt from permuting argv[] and moving the verb away + * from argv[1]. Our interface to initrd promises it'll be there. */ + while ((c = getopt_long(argc, argv, "-", options, NULL)) >= 0) + switch (c) { + + case ARG_LOG_LEVEL: + r = log_set_max_level_from_string(optarg); + if (r < 0) + log_error("Failed to parse log level %s, ignoring.", optarg); + + break; + + case ARG_LOG_TARGET: + r = log_set_target_from_string(optarg); + if (r < 0) + log_error("Failed to parse log target %s, ignoring", optarg); + + break; + + case ARG_LOG_COLOR: + + if (optarg) { + r = log_show_color_from_string(optarg); + if (r < 0) + log_error("Failed to parse log color setting %s, ignoring", optarg); + } else + log_show_color(true); + + break; + + case ARG_LOG_LOCATION: + if (optarg) { + r = log_show_location_from_string(optarg); + if (r < 0) + log_error("Failed to parse log location setting %s, ignoring", optarg); + } else + log_show_location(true); + + break; + + case '\001': + if (!arg_verb) + arg_verb = optarg; + else + log_error("Excess arguments, ignoring"); + break; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option code."); + } + + if (!arg_verb) { + log_error("Verb argument missing."); + return -EINVAL; + } + + return 0; +} + +static int switch_root_initramfs(void) { + if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) + return log_error_errno(errno, "Failed to mount bind /run/initramfs on /run/initramfs: %m"); + + if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) + return log_error_errno(errno, "Failed to make /run/initramfs private mount: %m"); + + /* switch_root with MS_BIND, because there might still be processes lurking around, which have open file descriptors. + * /run/initramfs/shutdown will take care of these. + * Also do not detach the old root, because /run/initramfs/shutdown needs to access it. + */ + return switch_root("/run/initramfs", "/oldroot", false, MS_BIND); +} + + +int main(int argc, char *argv[]) { + bool need_umount, need_swapoff, need_loop_detach, need_dm_detach; + bool in_container, use_watchdog = false; + _cleanup_free_ char *cgroup = NULL; + char *arguments[3]; + unsigned retries; + int cmd, r; + static const char* const dirs[] = {SYSTEM_SHUTDOWN_PATH, NULL}; + + log_parse_environment(); + r = parse_argv(argc, argv); + if (r < 0) + goto error; + + /* journald will die if not gone yet. The log target defaults + * to console, but may have been changed by command line options. */ + + log_close_console(); /* force reopen of /dev/console */ + log_open(); + + umask(0022); + + if (getpid() != 1) { + log_error("Not executed by init (PID 1)."); + r = -EPERM; + goto error; + } + + if (streq(arg_verb, "reboot")) + cmd = RB_AUTOBOOT; + else if (streq(arg_verb, "poweroff")) + cmd = RB_POWER_OFF; + else if (streq(arg_verb, "halt")) + cmd = RB_HALT_SYSTEM; + else if (streq(arg_verb, "kexec")) + cmd = LINUX_REBOOT_CMD_KEXEC; + else { + r = -EINVAL; + log_error("Unknown action '%s'.", arg_verb); + goto error; + } + + cg_get_root_path(&cgroup); + + use_watchdog = !!getenv("WATCHDOG_USEC"); + + /* lock us into memory */ + mlockall(MCL_CURRENT|MCL_FUTURE); + + log_info("Sending SIGTERM to remaining processes..."); + broadcast_signal(SIGTERM, true, true); + + log_info("Sending SIGKILL to remaining processes..."); + broadcast_signal(SIGKILL, true, false); + + in_container = detect_container(NULL) > 0; + + need_umount = !in_container; + need_swapoff = !in_container; + need_loop_detach = !in_container; + need_dm_detach = !in_container; + + /* Unmount all mountpoints, swaps, and loopback devices */ + for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) { + bool changed = false; + + if (use_watchdog) + watchdog_ping(); + + /* Let's trim the cgroup tree on each iteration so + that we leave an empty cgroup tree around, so that + container managers get a nice notify event when we + are down */ + if (cgroup) + cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false); + + if (need_umount) { + log_info("Unmounting file systems."); + r = umount_all(&changed); + if (r == 0) { + need_umount = false; + log_info("All filesystems unmounted."); + } else if (r > 0) + log_info("Not all file systems unmounted, %d left.", r); + else + log_error_errno(r, "Failed to unmount file systems: %m"); + } + + if (need_swapoff) { + log_info("Deactivating swaps."); + r = swapoff_all(&changed); + if (r == 0) { + need_swapoff = false; + log_info("All swaps deactivated."); + } else if (r > 0) + log_info("Not all swaps deactivated, %d left.", r); + else + log_error_errno(r, "Failed to deactivate swaps: %m"); + } + + if (need_loop_detach) { + log_info("Detaching loop devices."); + r = loopback_detach_all(&changed); + if (r == 0) { + need_loop_detach = false; + log_info("All loop devices detached."); + } else if (r > 0) + log_info("Not all loop devices detached, %d left.", r); + else + log_error_errno(r, "Failed to detach loop devices: %m"); + } + + if (need_dm_detach) { + log_info("Detaching DM devices."); + r = dm_detach_all(&changed); + if (r == 0) { + need_dm_detach = false; + log_info("All DM devices detached."); + } else if (r > 0) + log_info("Not all DM devices detached, %d left.", r); + else + log_error_errno(r, "Failed to detach DM devices: %m"); + } + + if (!need_umount && !need_swapoff && !need_loop_detach && !need_dm_detach) { + if (retries > 0) + log_info("All filesystems, swaps, loop devices, DM devices detached."); + /* Yay, done */ + goto initrd_jump; + } + + /* If in this iteration we didn't manage to + * unmount/deactivate anything, we simply give up */ + if (!changed) { + log_info("Cannot finalize remaining%s%s%s%s continuing.", + need_umount ? " file systems," : "", + need_swapoff ? " swap devices," : "", + need_loop_detach ? " loop devices," : "", + need_dm_detach ? " DM devices," : ""); + goto initrd_jump; + } + + log_debug("After %u retries, couldn't finalize remaining %s%s%s%s trying again.", + retries + 1, + need_umount ? " file systems," : "", + need_swapoff ? " swap devices," : "", + need_loop_detach ? " loop devices," : "", + need_dm_detach ? " DM devices," : ""); + } + + log_error("Too many iterations, giving up."); + + initrd_jump: + + arguments[0] = NULL; + arguments[1] = arg_verb; + arguments[2] = NULL; + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + if (!in_container && !in_initrd() && + access("/run/initramfs/shutdown", X_OK) == 0) { + r = switch_root_initramfs(); + if (r >= 0) { + argv[0] = (char*) "/shutdown"; + + setsid(); + make_console_stdio(); + + log_info("Successfully changed into root pivot.\n" + "Returning to initrd..."); + + execv("/shutdown", argv); + log_error_errno(errno, "Failed to execute shutdown binary: %m"); + } else + log_error_errno(r, "Failed to switch root to \"/run/initramfs\": %m"); + + } + + if (need_umount || need_swapoff || need_loop_detach || need_dm_detach) + log_error("Failed to finalize %s%s%s%s ignoring", + need_umount ? " file systems," : "", + need_swapoff ? " swap devices," : "", + need_loop_detach ? " loop devices," : "", + need_dm_detach ? " DM devices," : ""); + + /* The kernel will automaticall flush ATA disks and suchlike + * on reboot(), but the file systems need to be synce'd + * explicitly in advance. So let's do this here, but not + * needlessly slow down containers. */ + if (!in_container) + sync(); + + switch (cmd) { + + case LINUX_REBOOT_CMD_KEXEC: + + if (!in_container) { + /* We cheat and exec kexec to avoid doing all its work */ + pid_t pid; + + log_info("Rebooting with kexec."); + + pid = fork(); + if (pid < 0) + log_error_errno(errno, "Failed to fork: %m"); + else if (pid == 0) { + + const char * const args[] = { + KEXEC, "-e", NULL + }; + + /* Child */ + + execv(args[0], (char * const *) args); + _exit(EXIT_FAILURE); + } else + wait_for_terminate_and_warn("kexec", pid, true); + } + + cmd = RB_AUTOBOOT; + /* Fall through */ + + case RB_AUTOBOOT: + + if (!in_container) { + _cleanup_free_ char *param = NULL; + + if (read_one_line_file(REBOOT_PARAM_FILE, ¶m) >= 0) { + log_info("Rebooting with argument '%s'.", param); + syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, param); + } + } + + log_info("Rebooting."); + break; + + case RB_POWER_OFF: + log_info("Powering off."); + break; + + case RB_HALT_SYSTEM: + log_info("Halting system."); + break; + + default: + assert_not_reached("Unknown magic"); + } + + reboot(cmd); + if (errno == EPERM && in_container) { + /* If we are in a container, and we lacked + * CAP_SYS_BOOT just exit, this will kill our + * container for good. */ + log_info("Exiting container."); + exit(0); + } + + log_error_errno(errno, "Failed to invoke reboot(): %m"); + r = -errno; + + error: + log_emergency_errno(r, "Critical error while doing system shutdown: %m"); + + freeze(); +} diff --git a/src/power/shutdownd.c b/src/power/shutdownd.c new file mode 100644 index 0000000..60a6468 --- /dev/null +++ b/src/power/shutdownd.c @@ -0,0 +1,461 @@ +/*-*- 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/types.h> +#include <sys/timerfd.h> +#include <assert.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.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" + +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); + 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/power/sleep.c b/src/power/sleep.c new file mode 100644 index 0000000..cc1ffa6 --- /dev/null +++ b/src/power/sleep.c @@ -0,0 +1,219 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2012 Lennart Poettering + Copyright 2013 Zbigniew Jędrzejewski-Szmek + + 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 <stdio.h> +#include <errno.h> +#include <string.h> +#include <getopt.h> + +#include "sd-id128.h" +#include "sd-messages.h" +#include "log.h" +#include "util.h" +#include "strv.h" +#include "fileio.h" +#include "build.h" +#include "sleep-config.h" +#include "def.h" + +static char* arg_verb = NULL; + +static int write_mode(char **modes) { + int r = 0; + char **mode; + + STRV_FOREACH(mode, modes) { + int k; + + k = write_string_file("/sys/power/disk", *mode); + if (k == 0) + return 0; + + log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", + *mode); + if (r == 0) + r = k; + } + + if (r < 0) + log_error_errno(r, "Failed to write mode to /sys/power/disk: %m"); + + return r; +} + +static int write_state(FILE **f, char **states) { + char **state; + int r = 0; + + STRV_FOREACH(state, states) { + int k; + + k = write_string_stream(*f, *state); + if (k == 0) + return 0; + log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", + *state); + if (r == 0) + r = k; + + fclose(*f); + *f = fopen("/sys/power/state", "we"); + if (!*f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); + } + + return r; +} + +static int execute(char **modes, char **states) { + + char *arguments[] = { + NULL, + (char*) "pre", + arg_verb, + NULL + }; + static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; + + int r; + _cleanup_fclose_ FILE *f = NULL; + + /* This file is opened first, so that if we hit an error, + * we can abort before modifying any state. */ + f = fopen("/sys/power/state", "we"); + if (!f) + return log_error_errno(errno, "Failed to open /sys/power/state: %m"); + + /* Configure the hibernation mode */ + r = write_mode(modes); + if (r < 0) + return r; + + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), + LOG_MESSAGE("Suspending system..."), + "SLEEP=%s", arg_verb, + NULL); + + r = write_state(&f, states); + if (r < 0) + return r; + + log_struct(LOG_INFO, + LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), + LOG_MESSAGE("System resumed."), + "SLEEP=%s", arg_verb, + NULL); + + arguments[1] = (char*) "post"; + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); + + return r; +} + +static void help(void) { + printf("%s COMMAND\n\n" + "Suspend the system, hibernate the system, or both.\n\n" + "Commands:\n" + " -h --help Show this help and exit\n" + " --version Print version string and exit\n" + " suspend Suspend the system\n" + " hibernate Hibernate the system\n" + " hybrid-sleep Both hibernate and suspend the system\n" + , program_invocation_short_name); +} + +static int parse_argv(int argc, char *argv[]) { + enum { + ARG_VERSION = 0x100, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + {} + }; + + int c; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) + switch(c) { + case 'h': + help(); + return 0; /* done */ + + case ARG_VERSION: + puts(PACKAGE_STRING); + puts(SYSTEMD_FEATURES); + return 0 /* done */; + + case '?': + return -EINVAL; + + default: + assert_not_reached("Unhandled option"); + } + + if (argc - optind != 1) { + log_error("Usage: %s COMMAND", + program_invocation_short_name); + return -EINVAL; + } + + arg_verb = argv[optind]; + + if (!streq(arg_verb, "suspend") && + !streq(arg_verb, "hibernate") && + !streq(arg_verb, "hybrid-sleep")) { + log_error("Unknown command '%s'.", arg_verb); + return -EINVAL; + } + + return 1 /* work to do */; +} + +int main(int argc, char *argv[]) { + _cleanup_strv_free_ char **modes = NULL, **states = NULL; + int r; + + log_set_target(LOG_TARGET_AUTO); + log_parse_environment(); + log_open(); + + r = parse_argv(argc, argv); + if (r <= 0) + goto finish; + + r = parse_sleep_config(arg_verb, &modes, &states); + if (r < 0) + goto finish; + + r = execute(modes, states); + +finish: + return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; +} diff --git a/src/shutdownd/Makefile b/src/shutdownd/Makefile deleted file mode 120000 index d0b0e8e..0000000 --- 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 60a6468..0000000 --- a/src/shutdownd/shutdownd.c +++ /dev/null @@ -1,461 +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/types.h> -#include <sys/timerfd.h> -#include <assert.h> -#include <string.h> -#include <errno.h> -#include <unistd.h> -#include <fcntl.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" - -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); - 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/sleep/Makefile b/src/sleep/Makefile deleted file mode 120000 index d0b0e8e..0000000 --- a/src/sleep/Makefile +++ /dev/null @@ -1 +0,0 @@ -../Makefile \ No newline at end of file diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c deleted file mode 100644 index cc1ffa6..0000000 --- a/src/sleep/sleep.c +++ /dev/null @@ -1,219 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright 2012 Lennart Poettering - Copyright 2013 Zbigniew Jędrzejewski-Szmek - - 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 <stdio.h> -#include <errno.h> -#include <string.h> -#include <getopt.h> - -#include "sd-id128.h" -#include "sd-messages.h" -#include "log.h" -#include "util.h" -#include "strv.h" -#include "fileio.h" -#include "build.h" -#include "sleep-config.h" -#include "def.h" - -static char* arg_verb = NULL; - -static int write_mode(char **modes) { - int r = 0; - char **mode; - - STRV_FOREACH(mode, modes) { - int k; - - k = write_string_file("/sys/power/disk", *mode); - if (k == 0) - return 0; - - log_debug_errno(k, "Failed to write '%s' to /sys/power/disk: %m", - *mode); - if (r == 0) - r = k; - } - - if (r < 0) - log_error_errno(r, "Failed to write mode to /sys/power/disk: %m"); - - return r; -} - -static int write_state(FILE **f, char **states) { - char **state; - int r = 0; - - STRV_FOREACH(state, states) { - int k; - - k = write_string_stream(*f, *state); - if (k == 0) - return 0; - log_debug_errno(k, "Failed to write '%s' to /sys/power/state: %m", - *state); - if (r == 0) - r = k; - - fclose(*f); - *f = fopen("/sys/power/state", "we"); - if (!*f) - return log_error_errno(errno, "Failed to open /sys/power/state: %m"); - } - - return r; -} - -static int execute(char **modes, char **states) { - - char *arguments[] = { - NULL, - (char*) "pre", - arg_verb, - NULL - }; - static const char* const dirs[] = {SYSTEM_SLEEP_PATH, NULL}; - - int r; - _cleanup_fclose_ FILE *f = NULL; - - /* This file is opened first, so that if we hit an error, - * we can abort before modifying any state. */ - f = fopen("/sys/power/state", "we"); - if (!f) - return log_error_errno(errno, "Failed to open /sys/power/state: %m"); - - /* Configure the hibernation mode */ - r = write_mode(modes); - if (r < 0) - return r; - - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_START), - LOG_MESSAGE("Suspending system..."), - "SLEEP=%s", arg_verb, - NULL); - - r = write_state(&f, states); - if (r < 0) - return r; - - log_struct(LOG_INFO, - LOG_MESSAGE_ID(SD_MESSAGE_SLEEP_STOP), - LOG_MESSAGE("System resumed."), - "SLEEP=%s", arg_verb, - NULL); - - arguments[1] = (char*) "post"; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, arguments); - - return r; -} - -static void help(void) { - printf("%s COMMAND\n\n" - "Suspend the system, hibernate the system, or both.\n\n" - "Commands:\n" - " -h --help Show this help and exit\n" - " --version Print version string and exit\n" - " suspend Suspend the system\n" - " hibernate Hibernate the system\n" - " hybrid-sleep Both hibernate and suspend the system\n" - , program_invocation_short_name); -} - -static int parse_argv(int argc, char *argv[]) { - enum { - ARG_VERSION = 0x100, - }; - - static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - {} - }; - - int c; - - assert(argc >= 0); - assert(argv); - - while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) - switch(c) { - case 'h': - help(); - return 0; /* done */ - - case ARG_VERSION: - puts(PACKAGE_STRING); - puts(SYSTEMD_FEATURES); - return 0 /* done */; - - case '?': - return -EINVAL; - - default: - assert_not_reached("Unhandled option"); - } - - if (argc - optind != 1) { - log_error("Usage: %s COMMAND", - program_invocation_short_name); - return -EINVAL; - } - - arg_verb = argv[optind]; - - if (!streq(arg_verb, "suspend") && - !streq(arg_verb, "hibernate") && - !streq(arg_verb, "hybrid-sleep")) { - log_error("Unknown command '%s'.", arg_verb); - return -EINVAL; - } - - return 1 /* work to do */; -} - -int main(int argc, char *argv[]) { - _cleanup_strv_free_ char **modes = NULL, **states = NULL; - int r; - - log_set_target(LOG_TARGET_AUTO); - log_parse_environment(); - log_open(); - - r = parse_argv(argc, argv); - if (r <= 0) - goto finish; - - r = parse_sleep_config(arg_verb, &modes, &states); - if (r < 0) - goto finish; - - r = execute(modes, states); - -finish: - return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS; -} -- 2.2.1.209.g41e5f3a _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel