chiark / gitweb /
shutdown: don't pivot to /run/initramfs if already there
[elogind.git] / src / core / shutdown.c
index 105a604542b001cb760a66ff404b978e58f970d0..4bb4b4d13e435e157a950907762d5902fce246c6 100644 (file)
@@ -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;