X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fcore%2Fshutdown.c;h=4bb4b4d13e435e157a950907762d5902fce246c6;hp=105a604542b001cb760a66ff404b978e58f970d0;hb=02eaa78835fb994dbe609296d9b9d0c9b463cc1c;hpb=cee530bb23b78c0dfd18b0c2718cfe41286396df diff --git a/src/core/shutdown.c b/src/core/shutdown.c index 105a60454..4bb4b4d13 100644 --- a/src/core/shutdown.c +++ b/src/core/shutdown.c @@ -104,15 +104,14 @@ 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"); @@ -264,14 +263,8 @@ int main(int argc, char *argv[]) { 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) { @@ -280,28 +273,45 @@ int main(int argc, char *argv[]) { } } - sync(); + /* 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(); 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; + + if (!in_container) { + /* 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; + } } cmd = RB_AUTOBOOT; } 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_error("Exiting container."); + exit(0); + } + log_error("Failed to invoke reboot(): %m"); r = -errno;