[systemd-devel] [PATCH 3/3] shutdown: pivot_root to a tmpfs directory to properly umount root

harald at redhat.com harald at redhat.com
Thu May 5 03:48:01 PDT 2011


From: Harald Hoyer <harald at redhat.com>

umount /boot
mount a tmpfs on /boot
mount bind all needed dirs
pivot_root to /boot
umount all mountpoints as before
see root be umounted properly and all dm devices deconstructed
---
 src/shutdown.c |  154 +++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 142 insertions(+), 12 deletions(-)

diff --git a/src/shutdown.c b/src/shutdown.c
index a2f3b53..66813ed 100644
--- a/src/shutdown.c
+++ b/src/shutdown.c
@@ -24,6 +24,11 @@
 #include <sys/reboot.h>
 #include <linux/reboot.h>
 #include <sys/wait.h>
+#include <sys/types.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>
@@ -38,6 +43,7 @@
 
 #define TIMEOUT_USEC (5 * USEC_PER_SEC)
 #define FINALIZE_ATTEMPTS 50
+#define pivot_root(new_root,put_old) syscall(SYS_pivot_root,new_root,put_old)
 
 static bool ignore_proc(pid_t pid) {
         if (pid == 1)
@@ -192,11 +198,112 @@ finish:
         sigprocmask(SIG_SETMASK, &oldmask, NULL);
 }
 
+static bool prepare_new_root(void) {
+        int r = false;
+        const char *dirs[] = { "/boot/oldroot",
+                               "/boot/proc",
+                               "/boot/sys",
+                               "/boot/dev",
+                               "/boot/run",
+                               NULL };
+        const char **dir;
+        const char *msg;
+
+        umount("/boot");
+
+        msg = "Could not remount tmpfs on /boot";
+
+        if (mount("tmpfs", "/boot", "tmpfs", 0, "mode=0755") != 0)
+                goto out;
+
+        for (dir = &dirs[0]; *dir != NULL; dir++) {
+                asprintf((char **) &msg, "mkdir %s: %%m", *dir);
+                if (mkdir(*dir, 0755) != 0) {
+                        if (errno != EEXIST)
+                                goto out;
+                }
+                free((char *) msg);
+        }
+
+        log_info("mkdir in tmpfs");
+
+        msg = "Failed to mount bind /sys on /boot/sys";
+        if (mount("/sys", "/boot/sys", NULL, MS_BIND, NULL) != 0)
+                goto out;
+        msg = "Failed to mount bind /proc on /boot/proc";
+        if (mount("/proc", "/boot/proc", NULL, MS_BIND, NULL) != 0)
+                goto out;
+        msg = "Failed to mount bind /dev on /boot/dev";
+        if (mount("/dev", "/boot/dev", NULL, MS_BIND, NULL) != 0)
+                goto out;
+        msg = "Failed to mount bind /run on /boot/run";
+        if (mount("/run", "/boot/run", NULL, MS_BIND, NULL) != 0)
+                goto out;
+
+        msg = "Failed copying " SYSTEMD_SHUTDOWN_BINARY_PATH " to /boot";
+        if (install_bin(SYSTEMD_SHUTDOWN_BINARY_PATH, "/boot") != 0)
+                goto out;
+
+        msg = "Failed copying directory " SYSTEM_SHUTDOWN_PATH " to /boot/" SYSTEM_SHUTDOWN_PATH;
+        if (copy_execute_directory(SYSTEM_SHUTDOWN_PATH, "/boot") != 0)
+                goto out;
+
+        /* For debugging purposes, copy a small shutdown root
+          cp_a("/lib/systemd/shutdown-root", "/boot");
+        */
+
+        r = true;
+out:
+        if (!r)
+                log_error("%s: %m", msg);
+        return r;
+}
+
+static bool pivot_to_new_root(void) {
+        int fd;
+        int r = 0;
+        chdir("/boot");
+
+        /*
+           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 false;
+        }
+        if (mount(NULL, "/boot", NULL, MS_PRIVATE, NULL) != 0) {
+                log_error("Failed to make /boot private mount %m: ");
+                return false;
+        }
+
+        r = pivot_root(".", "oldroot");
+        if (r!=0) {
+                log_error("pivot failed: %m");
+                /* only chroot, if pivot root succeded */
+                return false;
+        }
+        chroot(".");
+
+        fd = open("dev/console", O_RDONLY);
+        dup2(fd, STDIN_FILENO);
+        close_nointr_nofail(fd);
+        fd = open("dev/console", O_WRONLY);
+        dup2(fd, STDOUT_FILENO);
+        close_nointr_nofail(fd);
+        fd = open("dev/console", O_WRONLY);
+        dup2(fd, STDERR_FILENO);
+        close_nointr_nofail(fd);
+        return true;
+}
+
 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 killed_everbody = false, in_container;
+        bool pivoted = false;
 
         log_parse_environment();
         log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
@@ -208,6 +315,17 @@ int main(int argc, char *argv[]) {
                 goto error;
         }
 
+        if (argc == 3) {
+                if (! streq(argv[2], "pivoted")) {
+                        log_error("Invalid number of arguments.");
+                        r = -EINVAL;
+                        goto error;
+                }
+                argc--;
+                argv[2] = NULL;
+                pivoted = true;
+        }
+
         if (argc != 2) {
                 log_error("Invalid number of arguments.");
                 r = -EINVAL;
@@ -230,6 +348,18 @@ int main(int argc, char *argv[]) {
                 goto error;
         }
 
+        if (!(pivoted || in_container)) {
+                char *new_argv[4];
+                new_argv[0] = strdup(argv[0]);
+                new_argv[1] = strdup(argv[1]);
+                new_argv[2] = strdup("pivoted");
+                new_argv[3] = NULL;
+                if (prepare_new_root() && pivot_to_new_root()) {
+                        execv(SYSTEMD_SHUTDOWN_BINARY_PATH, new_argv);
+                        log_error("Failed to execute shutdown binary: %m");
+                }
+        }
+
         /* lock us into memory */
         if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0)
                 log_warning("Cannot lock process memory: %m");
@@ -247,17 +377,6 @@ int main(int argc, char *argv[]) {
         for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
                 bool changed = false;
 
-                if (need_umount) {
-                        log_info("Unmounting file systems.");
-                        r = umount_all(&changed);
-                        if (r == 0)
-                                need_umount = false;
-                        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.");
                         r = swapoff_all(&changed);
@@ -269,6 +388,17 @@ int main(int argc, char *argv[]) {
                                 log_error("Failed to turn off swaps: %s", strerror(-r));
                 }
 
+                if (need_umount) {
+                        log_info("Unmounting file systems.");
+                        r = umount_all(&changed);
+                        if (r == 0)
+                                need_umount = false;
+                        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_loop_detach) {
                         log_info("Detaching loop devices.");
                         r = loopback_detach_all(&changed);
@@ -353,7 +483,7 @@ int main(int argc, char *argv[]) {
         log_error("Failed to invoke reboot(): %m");
         r = -errno;
 
-  error:
+error:
         log_error("Critical error while doing system shutdown: %s", strerror(-r));
 
         freeze();
-- 
1.7.3.4



More information about the systemd-devel mailing list