X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fshutdown.c;h=31129b7697edf01ae5de5787b7c5b75816fe4e64;hp=1c2bf372b5aafe5dd3059e7c8312021dee0270b0;hb=3fa5dd6de798e17d93531bc900b8e2dc587c38f3;hpb=39d6464ce3ae529eabba68e7dc57259b7d4ee8ac diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 1c2bf372b..31129b769 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -39,12 +38,15 @@ #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" #define FINALIZE_ATTEMPTS 50 @@ -104,19 +106,18 @@ static int pivot_to_new_root(void) { return -errno; } - /* - In case some evil process made "/" MS_SHARED - It works for pivot_root, but the ref count for the root device - is not decreasing :-/ - */ - if (mount(NULL, "/", NULL, MS_PRIVATE, NULL) < 0) { - log_error("Failed to make \"/\" private mount %m"); - return -errno; - } + /* Work-around for a kernel bug: for some reason the kernel + * refuses switching root if any file systems are mounted + * MS_SHARED. Hence remount them MS_PRIVATE here as a + * work-around. + * + * https://bugzilla.redhat.com/show_bug.cgi?id=847418 */ + if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) + log_warning("Failed to make \"/\" private mount: %m"); if (pivot_root(".", "oldroot") < 0) { log_error("pivot failed: %m"); - /* only chroot if pivot root succeded */ + /* only chroot if pivot root succeeded */ return -errno; } @@ -131,11 +132,26 @@ static int pivot_to_new_root(void) { } int main(int argc, char *argv[]) { - int cmd, r; - unsigned retries; bool need_umount = true, need_swapoff = true, need_loop_detach = true, need_dm_detach = true; bool in_container, use_watchdog = false; + _cleanup_free_ char *line = NULL, *cgroup = NULL; char *arguments[3]; + unsigned retries; + int cmd, r; + + /* suppress shutdown status output if 'quiet' is used */ + r = proc_cmdline(&line); + if (r > 0) { + char *w, *state; + size_t l; + + FOREACH_WORD_QUOTED(w, l, line, state) { + if (l == 5 && memcmp(w, "quiet", 5) == 0) { + log_set_max_level(LOG_WARNING); + break; + } + } + } log_parse_environment(); log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */ @@ -171,16 +187,18 @@ int main(int argc, char *argv[]) { 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); + broadcast_signal(SIGTERM, true, true); log_info("Sending SIGKILL to remaining processes..."); - broadcast_signal(SIGKILL); + broadcast_signal(SIGKILL, true, false); if (in_container) { need_swapoff = false; @@ -195,34 +213,44 @@ int main(int argc, char *argv[]) { 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) + if (r == 0) { need_umount = false; - else if (r > 0) + log_info("All filesystems unmounted."); + } else if (r > 0) log_info("Not all file systems unmounted, %d left.", r); else log_error("Failed to unmount file systems: %s", strerror(-r)); } if (need_swapoff) { - log_info("Disabling swaps."); + log_info("Deactivating swaps."); r = swapoff_all(&changed); - if (r == 0) + if (r == 0) { need_swapoff = false; - else if (r > 0) - log_info("Not all swaps are turned off, %d left.", r); + log_info("All swaps deactivated."); + } else if (r > 0) + log_info("Not all swaps deactivated, %d left.", r); else - log_error("Failed to turn off swaps: %s", strerror(-r)); + log_error("Failed to deactivate swaps: %s", strerror(-r)); } if (need_loop_detach) { log_info("Detaching loop devices."); r = loopback_detach_all(&changed); - if (r == 0) + if (r == 0) { need_loop_detach = false; - else if (r > 0) + log_info("All loop devices detached."); + } else if (r > 0) log_info("Not all loop devices detached, %d left.", r); else log_error("Failed to detach loop devices: %s", strerror(-r)); @@ -231,10 +259,11 @@ int main(int argc, char *argv[]) { if (need_dm_detach) { log_info("Detaching DM devices."); r = dm_detach_all(&changed); - if (r == 0) + if (r == 0) { need_dm_detach = false; - else if (r > 0) - log_warning("Not all DM devices detached, %d left.", r); + log_info("All DM devices detached."); + } else if (r > 0) + log_info("Not all DM devices detached, %d left.", r); else log_error("Failed to detach DM devices: %s", strerror(-r)); } @@ -258,50 +287,100 @@ int main(int argc, char *argv[]) { if (retries >= FINALIZE_ATTEMPTS) log_error("Too many iterations, giving up."); + else + log_info("Storage is finalized."); arguments[0] = NULL; arguments[1] = argv[1]; arguments[2] = NULL; execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, arguments); - /* If we are in a container, just exit, this will kill our - * container for good. */ - if (in_container) { - log_error("Exiting container."); - exit(0); - } - - if (access("/run/initramfs/shutdown", X_OK) == 0) { + if (!in_container && !in_initrd() && + access("/run/initramfs/shutdown", X_OK) == 0) { if (prepare_new_root() >= 0 && pivot_to_new_root() >= 0) { + + log_info("Returning to initrd..."); + execv("/shutdown", argv); log_error("Failed to execute shutdown binary: %m"); } } - sync(); - - if (cmd == LINUX_REBOOT_CMD_KEXEC) { - /* We cheat and exec kexec to avoid doing all its work */ - pid_t pid = fork(); - - if (pid < 0) - log_error("Could not fork: %m. Falling back to normal reboot."); - else if (pid > 0) { - wait_for_terminate_and_warn("kexec", pid); - log_warning("kexec failed. Falling back to normal reboot."); - } else { - /* Child */ - const char *args[3] = { "/sbin/kexec", "-e", NULL }; - execv(args[0], (char * const *) args); - return EXIT_FAILURE; + /* 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("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); } 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("Failed to invoke reboot(): %m"); r = -errno;