[systemd-commits] Makefile.am src/core src/shutdown.c src/umount.c src/umount.h

Lennart Poettering lennart at kemper.freedesktop.org
Thu Apr 12 05:18:44 PDT 2012


 Makefile.am         |    6 
 src/core/shutdown.c |  492 +++++++++++++++++++++++++++++++++++++++
 src/core/umount.c   |  649 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/core/umount.h   |   33 ++
 src/shutdown.c      |  492 ---------------------------------------
 src/umount.c        |  649 ----------------------------------------------------
 src/umount.h        |   33 --
 7 files changed, 1177 insertions(+), 1177 deletions(-)

New commits:
commit 146de8175e11c877908f20f8ed1b5a3805455c0a
Author: Lennart Poettering <lennart at poettering.net>
Date:   Thu Apr 12 14:18:36 2012 +0200

    shutdown: move shutdown to core since it replaces PID 1

diff --git a/Makefile.am b/Makefile.am
index 829db7e..54cc509 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -946,9 +946,9 @@ pkginclude_HEADERS += \
 
 # ------------------------------------------------------------------------------
 systemd_shutdown_SOURCES = \
-	src/umount.c \
-	src/umount.h \
-	src/shutdown.c
+	src/core/umount.c \
+	src/core/umount.h \
+	src/core/shutdown.c
 
 systemd_shutdown_LDADD = \
 	libsystemd-label.la \
diff --git a/src/core/shutdown.c b/src/core/shutdown.c
new file mode 100644
index 0000000..cd478b0
--- /dev/null
+++ b/src/core/shutdown.c
@@ -0,0 +1,492 @@
+/*-*- 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/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>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "missing.h"
+#include "log.h"
+#include "umount.h"
+#include "util.h"
+#include "mkdir.h"
+#include "virt.h"
+#include "watchdog.h"
+
+#define TIMEOUT_USEC (5 * USEC_PER_SEC)
+#define FINALIZE_ATTEMPTS 50
+
+static bool ignore_proc(pid_t pid) {
+        char buf[PATH_MAX];
+        FILE *f;
+        char c;
+        size_t count;
+        uid_t uid;
+        int r;
+
+        /* We are PID 1, let's not commit suicide */
+        if (pid == 1)
+                return true;
+
+        r = get_process_uid(pid, &uid);
+        if (r < 0)
+                return true; /* not really, but better safe than sorry */
+
+        /* Non-root processes otherwise are always subject to be killed */
+        if (uid != 0)
+                return false;
+
+        snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid);
+        char_array_0(buf);
+
+        f = fopen(buf, "re");
+        if (!f)
+                return true; /* not really, but has the desired effect */
+
+        count = fread(&c, 1, 1, f);
+        fclose(f);
+
+        /* Kernel threads have an empty cmdline */
+        if (count <= 0)
+                return true;
+
+        /* Processes with argv[0][0] = '@' we ignore from the killing
+         * spree.
+         *
+         * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
+        if (count == 1 && c == '@')
+                return true;
+
+        return false;
+}
+
+static int killall(int sign) {
+        DIR *dir;
+        struct dirent *d;
+        unsigned int n_processes = 0;
+
+        dir = opendir("/proc");
+        if (!dir)
+                return -errno;
+
+        while ((d = readdir(dir))) {
+                pid_t pid;
+
+                if (parse_pid(d->d_name, &pid) < 0)
+                        continue;
+
+                if (ignore_proc(pid))
+                        continue;
+
+                if (kill(pid, sign) == 0)
+                        n_processes++;
+                else
+                        log_warning("Could not kill %d: %m", pid);
+        }
+
+        closedir(dir);
+
+        return n_processes;
+}
+
+static void wait_for_children(int n_processes, sigset_t *mask) {
+        usec_t until;
+
+        assert(mask);
+
+        until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
+        for (;;) {
+                struct timespec ts;
+                int k;
+                usec_t n;
+
+                for (;;) {
+                        pid_t pid = waitpid(-1, NULL, WNOHANG);
+
+                        if (pid == 0)
+                                break;
+
+                        if (pid < 0 && errno == ECHILD)
+                                return;
+
+                        if (n_processes > 0)
+                                if (--n_processes == 0)
+                                        return;
+                }
+
+                n = now(CLOCK_MONOTONIC);
+                if (n >= until)
+                        return;
+
+                timespec_store(&ts, until - n);
+
+                if ((k = sigtimedwait(mask, NULL, &ts)) != SIGCHLD) {
+
+                        if (k < 0 && errno != EAGAIN) {
+                                log_error("sigtimedwait() failed: %m");
+                                return;
+                        }
+
+                        if (k >= 0)
+                                log_warning("sigtimedwait() returned unexpected signal.");
+                }
+        }
+}
+
+static void send_signal(int sign) {
+        sigset_t mask, oldmask;
+        int n_processes;
+
+        assert_se(sigemptyset(&mask) == 0);
+        assert_se(sigaddset(&mask, SIGCHLD) == 0);
+        assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
+
+        if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
+                log_warning("kill(-1, SIGSTOP) failed: %m");
+
+        n_processes = killall(sign);
+
+        if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
+                log_warning("kill(-1, SIGCONT) failed: %m");
+
+        if (n_processes <= 0)
+                goto finish;
+
+        wait_for_children(n_processes, &mask);
+
+finish:
+        sigprocmask(SIG_SETMASK, &oldmask, NULL);
+}
+
+static void ultimate_send_signal(int sign) {
+        sigset_t mask, oldmask;
+        int r;
+
+        assert_se(sigemptyset(&mask) == 0);
+        assert_se(sigaddset(&mask, SIGCHLD) == 0);
+        assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
+
+        if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
+                log_warning("kill(-1, SIGSTOP) failed: %m");
+
+        r = kill(-1, sign);
+        if (r < 0 && errno != ESRCH)
+                log_warning("kill(-1, %s) failed: %m", signal_to_string(sign));
+
+        if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
+                log_warning("kill(-1, SIGCONT) failed: %m");
+
+        if (r < 0)
+                goto finish;
+
+        wait_for_children(0, &mask);
+
+finish:
+        sigprocmask(SIG_SETMASK, &oldmask, NULL);
+}
+
+static int prepare_new_root(void) {
+        static const char dirs[] =
+                "/run/initramfs/oldroot\0"
+                "/run/initramfs/proc\0"
+                "/run/initramfs/sys\0"
+                "/run/initramfs/dev\0"
+                "/run/initramfs/run\0";
+
+        const char *dir;
+
+        if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
+                log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
+                return -errno;
+        }
+
+        if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
+                log_error("Failed to make /run/initramfs private mount: %m");
+                return -errno;
+        }
+
+        NULSTR_FOREACH(dir, dirs)
+                if (mkdir_p(dir, 0755) < 0 && errno != EEXIST) {
+                        log_error("Failed to mkdir %s: %m", dir);
+                        return -errno;
+                }
+
+        if (mount("/sys", "/run/initramfs/sys", NULL, MS_BIND, NULL) < 0) {
+                log_error("Failed to mount bind /sys on /run/initramfs/sys: %m");
+                return -errno;
+        }
+
+        if (mount("/proc", "/run/initramfs/proc", NULL, MS_BIND, NULL) < 0) {
+                log_error("Failed to mount bind /proc on /run/initramfs/proc: %m");
+                return -errno;
+        }
+
+        if (mount("/dev", "/run/initramfs/dev", NULL, MS_BIND, NULL) < 0) {
+                log_error("Failed to mount bind /dev on /run/initramfs/dev: %m");
+                return -errno;
+        }
+
+        if (mount("/run", "/run/initramfs/run", NULL, MS_BIND, NULL) < 0) {
+                log_error("Failed to mount bind /run on /run/initramfs/run: %m");
+                return -errno;
+        }
+
+        return 0;
+}
+
+static int pivot_to_new_root(void) {
+        int fd;
+
+        chdir("/run/initramfs");
+
+        /*
+          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;
+        }
+
+        if (pivot_root(".", "oldroot") < 0) {
+                log_error("pivot failed: %m");
+                /* only chroot if pivot root succeded */
+                return -errno;
+        }
+
+        chroot(".");
+        log_info("Successfully changed into root pivot.");
+
+        fd = open("/dev/console", O_RDWR);
+        if (fd < 0)
+                log_error("Failed to open /dev/console: %m");
+        else {
+                make_stdio(fd);
+
+                /* Initialize the controlling terminal */
+                setsid();
+                ioctl(STDIN_FILENO, TIOCSCTTY, NULL);
+        }
+
+        return 0;
+}
+
+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, use_watchdog = false;
+
+        log_parse_environment();
+        log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
+        log_open();
+
+        umask(0022);
+
+        if (getpid() != 1) {
+                log_error("Not executed by init (pid 1).");
+                r = -EPERM;
+                goto error;
+        }
+
+        if (argc != 2) {
+                log_error("Invalid number of arguments.");
+                r = -EINVAL;
+                goto error;
+        }
+
+        in_container = detect_container(NULL) > 0;
+
+        if (streq(argv[1], "reboot"))
+                cmd = RB_AUTOBOOT;
+        else if (streq(argv[1], "poweroff"))
+                cmd = RB_POWER_OFF;
+        else if (streq(argv[1], "halt"))
+                cmd = RB_HALT_SYSTEM;
+        else if (streq(argv[1], "kexec"))
+                cmd = LINUX_REBOOT_CMD_KEXEC;
+        else {
+                log_error("Unknown action '%s'.", argv[1]);
+                r = -EINVAL;
+                goto error;
+        }
+
+        use_watchdog = !!getenv("WATCHDOG_USEC");
+
+        /* lock us into memory */
+        if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0)
+                log_warning("Cannot lock process memory: %m");
+
+        log_info("Sending SIGTERM to remaining processes...");
+        send_signal(SIGTERM);
+
+        log_info("Sending SIGKILL to remaining processes...");
+        send_signal(SIGKILL);
+
+        if (in_container)
+                need_swapoff = false;
+
+        /* Unmount all mountpoints, swaps, and loopback devices */
+        for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
+                bool changed = false;
+
+                if (use_watchdog)
+                        watchdog_ping();
+
+                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);
+                        if (r == 0)
+                                need_swapoff = false;
+                        else if (r > 0)
+                                log_info("Not all swaps are turned off, %d left.", r);
+                        else
+                                log_error("Failed to turn off swaps: %s", strerror(-r));
+                }
+
+                if (need_loop_detach) {
+                        log_info("Detaching loop devices.");
+                        r = loopback_detach_all(&changed);
+                        if (r == 0)
+                                need_loop_detach = false;
+                        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));
+                }
+
+                if (need_dm_detach) {
+                        log_info("Detaching DM devices.");
+                        r = dm_detach_all(&changed);
+                        if (r == 0)
+                                need_dm_detach = false;
+                        else if (r > 0)
+                                log_warning("Not all DM devices detached, %d left.", r);
+                        else
+                                log_error("Failed to detach DM devices: %s", strerror(-r));
+                }
+
+                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 */
+                        break;
+                }
+
+                /* If in this iteration we didn't manage to
+                 * unmount/deactivate anything, we either kill more
+                 * processes, or simply give up */
+                if (!changed) {
+
+                        if (killed_everbody) {
+                                /* Hmm, we already killed everybody,
+                                 * let's just give up */
+                                log_error("Cannot finalize remaining file systems and devices, giving up.");
+                                break;
+                        }
+
+                        log_warning("Cannot finalize remaining file systems and devices, trying to kill remaining processes.");
+                        ultimate_send_signal(SIGTERM);
+                        ultimate_send_signal(SIGKILL);
+                        killed_everbody = true;
+                }
+
+                log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
+        }
+
+        if (retries >= FINALIZE_ATTEMPTS)
+                log_error("Too many iterations, giving up.");
+
+        execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, NULL);
+
+        /* 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 (prepare_new_root() >= 0 &&
+                    pivot_to_new_root() >= 0) {
+                        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;
+                }
+
+                cmd = RB_AUTOBOOT;
+        }
+
+        reboot(cmd);
+        log_error("Failed to invoke reboot(): %m");
+        r = -errno;
+
+  error:
+        log_error("Critical error while doing system shutdown: %s", strerror(-r));
+
+        freeze();
+        return EXIT_FAILURE;
+}
diff --git a/src/core/umount.c b/src/core/umount.c
new file mode 100644
index 0000000..488e1e4
--- /dev/null
+++ b/src/core/umount.c
@@ -0,0 +1,649 @@
+/*-*- 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 <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/swap.h>
+#include <unistd.h>
+#include <linux/loop.h>
+#include <linux/dm-ioctl.h>
+#include <libudev.h>
+
+#include "list.h"
+#include "mount-setup.h"
+#include "umount.h"
+#include "util.h"
+
+typedef struct MountPoint {
+        char *path;
+        dev_t devnum;
+        bool skip_ro;
+        LIST_FIELDS (struct MountPoint, mount_point);
+} MountPoint;
+
+static void mount_point_free(MountPoint **head, MountPoint *m) {
+        assert(head);
+        assert(m);
+
+        LIST_REMOVE(MountPoint, mount_point, *head, m);
+
+        free(m->path);
+        free(m);
+}
+
+static void mount_points_list_free(MountPoint **head) {
+        assert(head);
+
+        while (*head)
+                mount_point_free(head, *head);
+}
+
+static int mount_points_list_get(MountPoint **head) {
+        FILE *proc_self_mountinfo;
+        char *path, *p;
+        unsigned int i;
+        int r;
+
+        assert(head);
+
+        if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
+                return -errno;
+
+        for (i = 1;; i++) {
+                int k;
+                MountPoint *m;
+                char *root;
+                bool skip_ro;
+
+                path = p = NULL;
+
+                if ((k = fscanf(proc_self_mountinfo,
+                                "%*s "       /* (1) mount id */
+                                "%*s "       /* (2) parent id */
+                                "%*s "       /* (3) major:minor */
+                                "%ms "       /* (4) root */
+                                "%ms "       /* (5) mount point */
+                                "%*s"        /* (6) mount options */
+                                "%*[^-]"     /* (7) optional fields */
+                                "- "         /* (8) separator */
+                                "%*s "       /* (9) file system type */
+                                "%*s"        /* (10) mount source */
+                                "%*s"        /* (11) mount options 2 */
+                                "%*[^\n]",   /* some rubbish at the end */
+                                &root,
+                                &path)) != 2) {
+                        if (k == EOF)
+                                break;
+
+                        log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
+
+                        free(path);
+                        continue;
+                }
+
+                /* If we encounter a bind mount, don't try to remount
+                 * the source dir too early */
+                skip_ro = !streq(root, "/");
+                free(root);
+
+                p = cunescape(path);
+                free(path);
+
+                if (!p) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                /* Ignore mount points we can't unmount because they
+                 * are API or because we are keeping them open (like
+                 * /dev/console) */
+                if (mount_point_is_api(p) ||
+                    mount_point_ignore(p) ||
+                    path_equal(p, "/dev/console")) {
+                        free(p);
+                        continue;
+                }
+
+                if (!(m = new0(MountPoint, 1))) {
+                        free(p);
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                m->path = p;
+                m->skip_ro = skip_ro;
+                LIST_PREPEND(MountPoint, mount_point, *head, m);
+        }
+
+        r = 0;
+
+finish:
+        fclose(proc_self_mountinfo);
+
+        return r;
+}
+
+static int swap_list_get(MountPoint **head) {
+        FILE *proc_swaps;
+        unsigned int i;
+        int r;
+
+        assert(head);
+
+        if (!(proc_swaps = fopen("/proc/swaps", "re")))
+                return (errno == ENOENT) ? 0 : -errno;
+
+        (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
+
+        for (i = 2;; i++) {
+                MountPoint *swap;
+                char *dev = NULL, *d;
+                int k;
+
+                if ((k = fscanf(proc_swaps,
+                                "%ms " /* device/file */
+                                "%*s " /* type of swap */
+                                "%*s " /* swap size */
+                                "%*s " /* used */
+                                "%*s\n", /* priority */
+                                &dev)) != 1) {
+
+                        if (k == EOF)
+                                break;
+
+                        log_warning("Failed to parse /proc/swaps:%u.", i);
+
+                        free(dev);
+                        continue;
+                }
+
+                if (endswith(dev, "(deleted)")) {
+                        free(dev);
+                        continue;
+                }
+
+                d = cunescape(dev);
+                free(dev);
+
+                if (!d) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(swap = new0(MountPoint, 1))) {
+                        free(d);
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                swap->path = d;
+                LIST_PREPEND(MountPoint, mount_point, *head, swap);
+        }
+
+        r = 0;
+
+finish:
+        fclose(proc_swaps);
+
+        return r;
+}
+
+static int loopback_list_get(MountPoint **head) {
+        int r;
+        struct udev *udev;
+        struct udev_enumerate *e = NULL;
+        struct udev_list_entry *item = NULL, *first = NULL;
+
+        assert(head);
+
+        if (!(udev = udev_new())) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(e = udev_enumerate_new(udev))) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
+            udev_enumerate_add_match_sysname(e, "loop*") < 0) {
+                r = -EIO;
+                goto finish;
+        }
+
+        if (udev_enumerate_scan_devices(e) < 0) {
+                r = -EIO;
+                goto finish;
+        }
+
+        first = udev_enumerate_get_list_entry(e);
+        udev_list_entry_foreach(item, first) {
+                MountPoint *lb;
+                struct udev_device *d;
+                char *loop;
+                const char *dn;
+
+                if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(dn = udev_device_get_devnode(d))) {
+                        udev_device_unref(d);
+                        continue;
+                }
+
+                loop = strdup(dn);
+                udev_device_unref(d);
+
+                if (!loop) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(lb = new0(MountPoint, 1))) {
+                        free(loop);
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                lb->path = loop;
+                LIST_PREPEND(MountPoint, mount_point, *head, lb);
+        }
+
+        r = 0;
+
+finish:
+        if (e)
+                udev_enumerate_unref(e);
+
+        if (udev)
+                udev_unref(udev);
+
+        return r;
+}
+
+static int dm_list_get(MountPoint **head) {
+        int r;
+        struct udev *udev;
+        struct udev_enumerate *e = NULL;
+        struct udev_list_entry *item = NULL, *first = NULL;
+
+        assert(head);
+
+        if (!(udev = udev_new())) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(e = udev_enumerate_new(udev))) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
+            udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
+                r = -EIO;
+                goto finish;
+        }
+
+        if (udev_enumerate_scan_devices(e) < 0) {
+                r = -EIO;
+                goto finish;
+        }
+
+        first = udev_enumerate_get_list_entry(e);
+
+        udev_list_entry_foreach(item, first) {
+                MountPoint *m;
+                struct udev_device *d;
+                dev_t devnum;
+                char *node;
+                const char *dn;
+
+                if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                devnum = udev_device_get_devnum(d);
+                dn = udev_device_get_devnode(d);
+
+                if (major(devnum) == 0 || !dn) {
+                        udev_device_unref(d);
+                        continue;
+                }
+
+                node = strdup(dn);
+                udev_device_unref(d);
+
+                if (!node) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(m = new(MountPoint, 1))) {
+                        free(node);
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                m->path = node;
+                m->devnum = devnum;
+                LIST_PREPEND(MountPoint, mount_point, *head, m);
+        }
+
+        r = 0;
+
+finish:
+        if (e)
+                udev_enumerate_unref(e);
+
+        if (udev)
+                udev_unref(udev);
+
+        return r;
+}
+
+static int delete_loopback(const char *device) {
+        int fd, r;
+
+        if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
+                return errno == ENOENT ? 0 : -errno;
+
+        r = ioctl(fd, LOOP_CLR_FD, 0);
+        close_nointr_nofail(fd);
+
+        if (r >= 0)
+                return 1;
+
+        /* ENXIO: not bound, so no error */
+        if (errno == ENXIO)
+                return 0;
+
+        return -errno;
+}
+
+static int delete_dm(dev_t devnum) {
+        int fd, r;
+        struct dm_ioctl dm;
+
+        assert(major(devnum) != 0);
+
+        if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
+                return -errno;
+
+        zero(dm);
+        dm.version[0] = DM_VERSION_MAJOR;
+        dm.version[1] = DM_VERSION_MINOR;
+        dm.version[2] = DM_VERSION_PATCHLEVEL;
+
+        dm.data_size = sizeof(dm);
+        dm.dev = devnum;
+
+        r = ioctl(fd, DM_DEV_REMOVE, &dm);
+        close_nointr_nofail(fd);
+
+        return r >= 0 ? 0 : -errno;
+}
+
+static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
+        MountPoint *m, *n;
+        int n_failed = 0;
+
+        assert(head);
+
+        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+                if (path_equal(m->path, "/")
+#ifndef HAVE_SPLIT_USR
+                    || path_equal(m->path, "/usr")
+#endif
+                ) {
+                        n_failed++;
+                        continue;
+                }
+
+                /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
+                if (umount2(m->path, MNT_FORCE) == 0) {
+                        log_info("Unmounted %s.", m->path);
+                        if (changed)
+                                *changed = true;
+
+                        mount_point_free(head, m);
+                } else if (log_error) {
+                        log_warning("Could not unmount %s: %m", m->path);
+                        n_failed++;
+                }
+        }
+
+        return n_failed;
+}
+
+static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) {
+        MountPoint *m, *n;
+        int n_failed = 0;
+
+        assert(head);
+
+        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+
+                if (m->skip_ro) {
+                        n_failed++;
+                        continue;
+                }
+
+                /* Trying to remount read-only */
+                if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
+                        if (changed)
+                                *changed = true;
+
+                        mount_point_free(head, m);
+                } else {
+                        log_warning("Could not remount as read-only %s: %m", m->path);
+                        n_failed++;
+                }
+        }
+
+        return n_failed;
+}
+
+static int swap_points_list_off(MountPoint **head, bool *changed) {
+        MountPoint *m, *n;
+        int n_failed = 0;
+
+        assert(head);
+
+        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+                if (swapoff(m->path) == 0) {
+                        if (changed)
+                                *changed = true;
+
+                        mount_point_free(head, m);
+                } else {
+                        log_warning("Could not deactivate swap %s: %m", m->path);
+                        n_failed++;
+                }
+        }
+
+        return n_failed;
+}
+
+static int loopback_points_list_detach(MountPoint **head, bool *changed) {
+        MountPoint *m, *n;
+        int n_failed = 0, k;
+        struct stat root_st;
+
+        assert(head);
+
+        k = lstat("/", &root_st);
+
+        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+                int r;
+                struct stat loopback_st;
+
+                if (k >= 0 &&
+                    major(root_st.st_dev) != 0 &&
+                    lstat(m->path, &loopback_st) >= 0 &&
+                    root_st.st_dev == loopback_st.st_rdev) {
+                        n_failed ++;
+                        continue;
+                }
+
+                if ((r = delete_loopback(m->path)) >= 0) {
+
+                        if (r > 0 && changed)
+                                *changed = true;
+
+                        mount_point_free(head, m);
+                } else {
+                        log_warning("Could not delete loopback %s: %m", m->path);
+                        n_failed++;
+                }
+        }
+
+        return n_failed;
+}
+
+static int dm_points_list_detach(MountPoint **head, bool *changed) {
+        MountPoint *m, *n;
+        int n_failed = 0, k;
+        struct stat root_st;
+
+        assert(head);
+
+        k = lstat("/", &root_st);
+
+        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
+                int r;
+
+                if (k >= 0 &&
+                    major(root_st.st_dev) != 0 &&
+                    root_st.st_dev == m->devnum) {
+                        n_failed ++;
+                        continue;
+                }
+
+                if ((r = delete_dm(m->devnum)) >= 0) {
+
+                        if (r > 0 && changed)
+                                *changed = true;
+
+                        mount_point_free(head, m);
+                } else {
+                        log_warning("Could not delete dm %s: %m", m->path);
+                        n_failed++;
+                }
+        }
+
+        return n_failed;
+}
+
+int umount_all(bool *changed) {
+        int r;
+        bool umount_changed;
+
+        LIST_HEAD(MountPoint, mp_list_head);
+
+        LIST_HEAD_INIT(MountPoint, mp_list_head);
+
+        r = mount_points_list_get(&mp_list_head);
+        if (r < 0)
+                goto end;
+
+        /* retry umount, until nothing can be umounted anymore */
+        do {
+                umount_changed = false;
+
+                mount_points_list_umount(&mp_list_head, &umount_changed, false);
+                if (umount_changed)
+                        *changed = true;
+
+        } while (umount_changed);
+
+        /* umount one more time with logging enabled */
+        r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
+        if (r <= 0)
+                goto end;
+
+        r = mount_points_list_remount_read_only(&mp_list_head, changed);
+
+  end:
+        mount_points_list_free(&mp_list_head);
+
+        return r;
+}
+
+int swapoff_all(bool *changed) {
+        int r;
+        LIST_HEAD(MountPoint, swap_list_head);
+
+        LIST_HEAD_INIT(MountPoint, swap_list_head);
+
+        r = swap_list_get(&swap_list_head);
+        if (r < 0)
+                goto end;
+
+        r = swap_points_list_off(&swap_list_head, changed);
+
+  end:
+        mount_points_list_free(&swap_list_head);
+
+        return r;
+}
+
+int loopback_detach_all(bool *changed) {
+        int r;
+        LIST_HEAD(MountPoint, loopback_list_head);
+
+        LIST_HEAD_INIT(MountPoint, loopback_list_head);
+
+        r = loopback_list_get(&loopback_list_head);
+        if (r < 0)
+                goto end;
+
+        r = loopback_points_list_detach(&loopback_list_head, changed);
+
+  end:
+        mount_points_list_free(&loopback_list_head);
+
+        return r;
+}
+
+int dm_detach_all(bool *changed) {
+        int r;
+        LIST_HEAD(MountPoint, dm_list_head);
+
+        LIST_HEAD_INIT(MountPoint, dm_list_head);
+
+        r = dm_list_get(&dm_list_head);
+        if (r < 0)
+                goto end;
+
+        r = dm_points_list_detach(&dm_list_head, changed);
+
+  end:
+        mount_points_list_free(&dm_list_head);
+
+        return r;
+}
diff --git a/src/core/umount.h b/src/core/umount.h
new file mode 100644
index 0000000..2e2f9c1
--- /dev/null
+++ b/src/core/umount.h
@@ -0,0 +1,33 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+#ifndef fooumounthfoo
+#define fooumounthfoo
+
+/***
+  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/>.
+***/
+
+int umount_all(bool *changed);
+
+int swapoff_all(bool *changed);
+
+int loopback_detach_all(bool *changed);
+
+int dm_detach_all(bool *changed);
+
+#endif
diff --git a/src/shutdown.c b/src/shutdown.c
deleted file mode 100644
index cd478b0..0000000
--- a/src/shutdown.c
+++ /dev/null
@@ -1,492 +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/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>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "missing.h"
-#include "log.h"
-#include "umount.h"
-#include "util.h"
-#include "mkdir.h"
-#include "virt.h"
-#include "watchdog.h"
-
-#define TIMEOUT_USEC (5 * USEC_PER_SEC)
-#define FINALIZE_ATTEMPTS 50
-
-static bool ignore_proc(pid_t pid) {
-        char buf[PATH_MAX];
-        FILE *f;
-        char c;
-        size_t count;
-        uid_t uid;
-        int r;
-
-        /* We are PID 1, let's not commit suicide */
-        if (pid == 1)
-                return true;
-
-        r = get_process_uid(pid, &uid);
-        if (r < 0)
-                return true; /* not really, but better safe than sorry */
-
-        /* Non-root processes otherwise are always subject to be killed */
-        if (uid != 0)
-                return false;
-
-        snprintf(buf, sizeof(buf), "/proc/%lu/cmdline", (unsigned long) pid);
-        char_array_0(buf);
-
-        f = fopen(buf, "re");
-        if (!f)
-                return true; /* not really, but has the desired effect */
-
-        count = fread(&c, 1, 1, f);
-        fclose(f);
-
-        /* Kernel threads have an empty cmdline */
-        if (count <= 0)
-                return true;
-
-        /* Processes with argv[0][0] = '@' we ignore from the killing
-         * spree.
-         *
-         * http://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons */
-        if (count == 1 && c == '@')
-                return true;
-
-        return false;
-}
-
-static int killall(int sign) {
-        DIR *dir;
-        struct dirent *d;
-        unsigned int n_processes = 0;
-
-        dir = opendir("/proc");
-        if (!dir)
-                return -errno;
-
-        while ((d = readdir(dir))) {
-                pid_t pid;
-
-                if (parse_pid(d->d_name, &pid) < 0)
-                        continue;
-
-                if (ignore_proc(pid))
-                        continue;
-
-                if (kill(pid, sign) == 0)
-                        n_processes++;
-                else
-                        log_warning("Could not kill %d: %m", pid);
-        }
-
-        closedir(dir);
-
-        return n_processes;
-}
-
-static void wait_for_children(int n_processes, sigset_t *mask) {
-        usec_t until;
-
-        assert(mask);
-
-        until = now(CLOCK_MONOTONIC) + TIMEOUT_USEC;
-        for (;;) {
-                struct timespec ts;
-                int k;
-                usec_t n;
-
-                for (;;) {
-                        pid_t pid = waitpid(-1, NULL, WNOHANG);
-
-                        if (pid == 0)
-                                break;
-
-                        if (pid < 0 && errno == ECHILD)
-                                return;
-
-                        if (n_processes > 0)
-                                if (--n_processes == 0)
-                                        return;
-                }
-
-                n = now(CLOCK_MONOTONIC);
-                if (n >= until)
-                        return;
-
-                timespec_store(&ts, until - n);
-
-                if ((k = sigtimedwait(mask, NULL, &ts)) != SIGCHLD) {
-
-                        if (k < 0 && errno != EAGAIN) {
-                                log_error("sigtimedwait() failed: %m");
-                                return;
-                        }
-
-                        if (k >= 0)
-                                log_warning("sigtimedwait() returned unexpected signal.");
-                }
-        }
-}
-
-static void send_signal(int sign) {
-        sigset_t mask, oldmask;
-        int n_processes;
-
-        assert_se(sigemptyset(&mask) == 0);
-        assert_se(sigaddset(&mask, SIGCHLD) == 0);
-        assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
-
-        if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
-                log_warning("kill(-1, SIGSTOP) failed: %m");
-
-        n_processes = killall(sign);
-
-        if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
-                log_warning("kill(-1, SIGCONT) failed: %m");
-
-        if (n_processes <= 0)
-                goto finish;
-
-        wait_for_children(n_processes, &mask);
-
-finish:
-        sigprocmask(SIG_SETMASK, &oldmask, NULL);
-}
-
-static void ultimate_send_signal(int sign) {
-        sigset_t mask, oldmask;
-        int r;
-
-        assert_se(sigemptyset(&mask) == 0);
-        assert_se(sigaddset(&mask, SIGCHLD) == 0);
-        assert_se(sigprocmask(SIG_BLOCK, &mask, &oldmask) == 0);
-
-        if (kill(-1, SIGSTOP) < 0 && errno != ESRCH)
-                log_warning("kill(-1, SIGSTOP) failed: %m");
-
-        r = kill(-1, sign);
-        if (r < 0 && errno != ESRCH)
-                log_warning("kill(-1, %s) failed: %m", signal_to_string(sign));
-
-        if (kill(-1, SIGCONT) < 0 && errno != ESRCH)
-                log_warning("kill(-1, SIGCONT) failed: %m");
-
-        if (r < 0)
-                goto finish;
-
-        wait_for_children(0, &mask);
-
-finish:
-        sigprocmask(SIG_SETMASK, &oldmask, NULL);
-}
-
-static int prepare_new_root(void) {
-        static const char dirs[] =
-                "/run/initramfs/oldroot\0"
-                "/run/initramfs/proc\0"
-                "/run/initramfs/sys\0"
-                "/run/initramfs/dev\0"
-                "/run/initramfs/run\0";
-
-        const char *dir;
-
-        if (mount("/run/initramfs", "/run/initramfs", NULL, MS_BIND, NULL) < 0) {
-                log_error("Failed to mount bind /run/initramfs on /run/initramfs: %m");
-                return -errno;
-        }
-
-        if (mount(NULL, "/run/initramfs", NULL, MS_PRIVATE, NULL) < 0) {
-                log_error("Failed to make /run/initramfs private mount: %m");
-                return -errno;
-        }
-
-        NULSTR_FOREACH(dir, dirs)
-                if (mkdir_p(dir, 0755) < 0 && errno != EEXIST) {
-                        log_error("Failed to mkdir %s: %m", dir);
-                        return -errno;
-                }
-
-        if (mount("/sys", "/run/initramfs/sys", NULL, MS_BIND, NULL) < 0) {
-                log_error("Failed to mount bind /sys on /run/initramfs/sys: %m");
-                return -errno;
-        }
-
-        if (mount("/proc", "/run/initramfs/proc", NULL, MS_BIND, NULL) < 0) {
-                log_error("Failed to mount bind /proc on /run/initramfs/proc: %m");
-                return -errno;
-        }
-
-        if (mount("/dev", "/run/initramfs/dev", NULL, MS_BIND, NULL) < 0) {
-                log_error("Failed to mount bind /dev on /run/initramfs/dev: %m");
-                return -errno;
-        }
-
-        if (mount("/run", "/run/initramfs/run", NULL, MS_BIND, NULL) < 0) {
-                log_error("Failed to mount bind /run on /run/initramfs/run: %m");
-                return -errno;
-        }
-
-        return 0;
-}
-
-static int pivot_to_new_root(void) {
-        int fd;
-
-        chdir("/run/initramfs");
-
-        /*
-          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;
-        }
-
-        if (pivot_root(".", "oldroot") < 0) {
-                log_error("pivot failed: %m");
-                /* only chroot if pivot root succeded */
-                return -errno;
-        }
-
-        chroot(".");
-        log_info("Successfully changed into root pivot.");
-
-        fd = open("/dev/console", O_RDWR);
-        if (fd < 0)
-                log_error("Failed to open /dev/console: %m");
-        else {
-                make_stdio(fd);
-
-                /* Initialize the controlling terminal */
-                setsid();
-                ioctl(STDIN_FILENO, TIOCSCTTY, NULL);
-        }
-
-        return 0;
-}
-
-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, use_watchdog = false;
-
-        log_parse_environment();
-        log_set_target(LOG_TARGET_CONSOLE); /* syslog will die if not gone yet */
-        log_open();
-
-        umask(0022);
-
-        if (getpid() != 1) {
-                log_error("Not executed by init (pid 1).");
-                r = -EPERM;
-                goto error;
-        }
-
-        if (argc != 2) {
-                log_error("Invalid number of arguments.");
-                r = -EINVAL;
-                goto error;
-        }
-
-        in_container = detect_container(NULL) > 0;
-
-        if (streq(argv[1], "reboot"))
-                cmd = RB_AUTOBOOT;
-        else if (streq(argv[1], "poweroff"))
-                cmd = RB_POWER_OFF;
-        else if (streq(argv[1], "halt"))
-                cmd = RB_HALT_SYSTEM;
-        else if (streq(argv[1], "kexec"))
-                cmd = LINUX_REBOOT_CMD_KEXEC;
-        else {
-                log_error("Unknown action '%s'.", argv[1]);
-                r = -EINVAL;
-                goto error;
-        }
-
-        use_watchdog = !!getenv("WATCHDOG_USEC");
-
-        /* lock us into memory */
-        if (mlockall(MCL_CURRENT|MCL_FUTURE) != 0)
-                log_warning("Cannot lock process memory: %m");
-
-        log_info("Sending SIGTERM to remaining processes...");
-        send_signal(SIGTERM);
-
-        log_info("Sending SIGKILL to remaining processes...");
-        send_signal(SIGKILL);
-
-        if (in_container)
-                need_swapoff = false;
-
-        /* Unmount all mountpoints, swaps, and loopback devices */
-        for (retries = 0; retries < FINALIZE_ATTEMPTS; retries++) {
-                bool changed = false;
-
-                if (use_watchdog)
-                        watchdog_ping();
-
-                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);
-                        if (r == 0)
-                                need_swapoff = false;
-                        else if (r > 0)
-                                log_info("Not all swaps are turned off, %d left.", r);
-                        else
-                                log_error("Failed to turn off swaps: %s", strerror(-r));
-                }
-
-                if (need_loop_detach) {
-                        log_info("Detaching loop devices.");
-                        r = loopback_detach_all(&changed);
-                        if (r == 0)
-                                need_loop_detach = false;
-                        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));
-                }
-
-                if (need_dm_detach) {
-                        log_info("Detaching DM devices.");
-                        r = dm_detach_all(&changed);
-                        if (r == 0)
-                                need_dm_detach = false;
-                        else if (r > 0)
-                                log_warning("Not all DM devices detached, %d left.", r);
-                        else
-                                log_error("Failed to detach DM devices: %s", strerror(-r));
-                }
-
-                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 */
-                        break;
-                }
-
-                /* If in this iteration we didn't manage to
-                 * unmount/deactivate anything, we either kill more
-                 * processes, or simply give up */
-                if (!changed) {
-
-                        if (killed_everbody) {
-                                /* Hmm, we already killed everybody,
-                                 * let's just give up */
-                                log_error("Cannot finalize remaining file systems and devices, giving up.");
-                                break;
-                        }
-
-                        log_warning("Cannot finalize remaining file systems and devices, trying to kill remaining processes.");
-                        ultimate_send_signal(SIGTERM);
-                        ultimate_send_signal(SIGKILL);
-                        killed_everbody = true;
-                }
-
-                log_debug("Couldn't finalize remaining file systems and devices after %u retries, trying again.", retries+1);
-        }
-
-        if (retries >= FINALIZE_ATTEMPTS)
-                log_error("Too many iterations, giving up.");
-
-        execute_directory(SYSTEM_SHUTDOWN_PATH, NULL, NULL);
-
-        /* 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 (prepare_new_root() >= 0 &&
-                    pivot_to_new_root() >= 0) {
-                        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;
-                }
-
-                cmd = RB_AUTOBOOT;
-        }
-
-        reboot(cmd);
-        log_error("Failed to invoke reboot(): %m");
-        r = -errno;
-
-  error:
-        log_error("Critical error while doing system shutdown: %s", strerror(-r));
-
-        freeze();
-        return EXIT_FAILURE;
-}
diff --git a/src/umount.c b/src/umount.c
deleted file mode 100644
index 488e1e4..0000000
--- a/src/umount.c
+++ /dev/null
@@ -1,649 +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 <errno.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/swap.h>
-#include <unistd.h>
-#include <linux/loop.h>
-#include <linux/dm-ioctl.h>
-#include <libudev.h>
-
-#include "list.h"
-#include "mount-setup.h"
-#include "umount.h"
-#include "util.h"
-
-typedef struct MountPoint {
-        char *path;
-        dev_t devnum;
-        bool skip_ro;
-        LIST_FIELDS (struct MountPoint, mount_point);
-} MountPoint;
-
-static void mount_point_free(MountPoint **head, MountPoint *m) {
-        assert(head);
-        assert(m);
-
-        LIST_REMOVE(MountPoint, mount_point, *head, m);
-
-        free(m->path);
-        free(m);
-}
-
-static void mount_points_list_free(MountPoint **head) {
-        assert(head);
-
-        while (*head)
-                mount_point_free(head, *head);
-}
-
-static int mount_points_list_get(MountPoint **head) {
-        FILE *proc_self_mountinfo;
-        char *path, *p;
-        unsigned int i;
-        int r;
-
-        assert(head);
-
-        if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
-                return -errno;
-
-        for (i = 1;; i++) {
-                int k;
-                MountPoint *m;
-                char *root;
-                bool skip_ro;
-
-                path = p = NULL;
-
-                if ((k = fscanf(proc_self_mountinfo,
-                                "%*s "       /* (1) mount id */
-                                "%*s "       /* (2) parent id */
-                                "%*s "       /* (3) major:minor */
-                                "%ms "       /* (4) root */
-                                "%ms "       /* (5) mount point */
-                                "%*s"        /* (6) mount options */
-                                "%*[^-]"     /* (7) optional fields */
-                                "- "         /* (8) separator */
-                                "%*s "       /* (9) file system type */
-                                "%*s"        /* (10) mount source */
-                                "%*s"        /* (11) mount options 2 */
-                                "%*[^\n]",   /* some rubbish at the end */
-                                &root,
-                                &path)) != 2) {
-                        if (k == EOF)
-                                break;
-
-                        log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
-
-                        free(path);
-                        continue;
-                }
-
-                /* If we encounter a bind mount, don't try to remount
-                 * the source dir too early */
-                skip_ro = !streq(root, "/");
-                free(root);
-
-                p = cunescape(path);
-                free(path);
-
-                if (!p) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                /* Ignore mount points we can't unmount because they
-                 * are API or because we are keeping them open (like
-                 * /dev/console) */
-                if (mount_point_is_api(p) ||
-                    mount_point_ignore(p) ||
-                    path_equal(p, "/dev/console")) {
-                        free(p);
-                        continue;
-                }
-
-                if (!(m = new0(MountPoint, 1))) {
-                        free(p);
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                m->path = p;
-                m->skip_ro = skip_ro;
-                LIST_PREPEND(MountPoint, mount_point, *head, m);
-        }
-
-        r = 0;
-
-finish:
-        fclose(proc_self_mountinfo);
-
-        return r;
-}
-
-static int swap_list_get(MountPoint **head) {
-        FILE *proc_swaps;
-        unsigned int i;
-        int r;
-
-        assert(head);
-
-        if (!(proc_swaps = fopen("/proc/swaps", "re")))
-                return (errno == ENOENT) ? 0 : -errno;
-
-        (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
-
-        for (i = 2;; i++) {
-                MountPoint *swap;
-                char *dev = NULL, *d;
-                int k;
-
-                if ((k = fscanf(proc_swaps,
-                                "%ms " /* device/file */
-                                "%*s " /* type of swap */
-                                "%*s " /* swap size */
-                                "%*s " /* used */
-                                "%*s\n", /* priority */
-                                &dev)) != 1) {
-
-                        if (k == EOF)
-                                break;
-
-                        log_warning("Failed to parse /proc/swaps:%u.", i);
-
-                        free(dev);
-                        continue;
-                }
-
-                if (endswith(dev, "(deleted)")) {
-                        free(dev);
-                        continue;
-                }
-
-                d = cunescape(dev);
-                free(dev);
-
-                if (!d) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(swap = new0(MountPoint, 1))) {
-                        free(d);
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                swap->path = d;
-                LIST_PREPEND(MountPoint, mount_point, *head, swap);
-        }
-
-        r = 0;
-
-finish:
-        fclose(proc_swaps);
-
-        return r;
-}
-
-static int loopback_list_get(MountPoint **head) {
-        int r;
-        struct udev *udev;
-        struct udev_enumerate *e = NULL;
-        struct udev_list_entry *item = NULL, *first = NULL;
-
-        assert(head);
-
-        if (!(udev = udev_new())) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(e = udev_enumerate_new(udev))) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
-            udev_enumerate_add_match_sysname(e, "loop*") < 0) {
-                r = -EIO;
-                goto finish;
-        }
-
-        if (udev_enumerate_scan_devices(e) < 0) {
-                r = -EIO;
-                goto finish;
-        }
-
-        first = udev_enumerate_get_list_entry(e);
-        udev_list_entry_foreach(item, first) {
-                MountPoint *lb;
-                struct udev_device *d;
-                char *loop;
-                const char *dn;
-
-                if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(dn = udev_device_get_devnode(d))) {
-                        udev_device_unref(d);
-                        continue;
-                }
-
-                loop = strdup(dn);
-                udev_device_unref(d);
-
-                if (!loop) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(lb = new0(MountPoint, 1))) {
-                        free(loop);
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                lb->path = loop;
-                LIST_PREPEND(MountPoint, mount_point, *head, lb);
-        }
-
-        r = 0;
-
-finish:
-        if (e)
-                udev_enumerate_unref(e);
-
-        if (udev)
-                udev_unref(udev);
-
-        return r;
-}
-
-static int dm_list_get(MountPoint **head) {
-        int r;
-        struct udev *udev;
-        struct udev_enumerate *e = NULL;
-        struct udev_list_entry *item = NULL, *first = NULL;
-
-        assert(head);
-
-        if (!(udev = udev_new())) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(e = udev_enumerate_new(udev))) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
-            udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
-                r = -EIO;
-                goto finish;
-        }
-
-        if (udev_enumerate_scan_devices(e) < 0) {
-                r = -EIO;
-                goto finish;
-        }
-
-        first = udev_enumerate_get_list_entry(e);
-
-        udev_list_entry_foreach(item, first) {
-                MountPoint *m;
-                struct udev_device *d;
-                dev_t devnum;
-                char *node;
-                const char *dn;
-
-                if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                devnum = udev_device_get_devnum(d);
-                dn = udev_device_get_devnode(d);
-
-                if (major(devnum) == 0 || !dn) {
-                        udev_device_unref(d);
-                        continue;
-                }
-
-                node = strdup(dn);
-                udev_device_unref(d);
-
-                if (!node) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(m = new(MountPoint, 1))) {
-                        free(node);
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                m->path = node;
-                m->devnum = devnum;
-                LIST_PREPEND(MountPoint, mount_point, *head, m);
-        }
-
-        r = 0;
-
-finish:
-        if (e)
-                udev_enumerate_unref(e);
-
-        if (udev)
-                udev_unref(udev);
-
-        return r;
-}
-
-static int delete_loopback(const char *device) {
-        int fd, r;
-
-        if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
-                return errno == ENOENT ? 0 : -errno;
-
-        r = ioctl(fd, LOOP_CLR_FD, 0);
-        close_nointr_nofail(fd);
-
-        if (r >= 0)
-                return 1;
-
-        /* ENXIO: not bound, so no error */
-        if (errno == ENXIO)
-                return 0;
-
-        return -errno;
-}
-
-static int delete_dm(dev_t devnum) {
-        int fd, r;
-        struct dm_ioctl dm;
-
-        assert(major(devnum) != 0);
-
-        if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
-                return -errno;
-
-        zero(dm);
-        dm.version[0] = DM_VERSION_MAJOR;
-        dm.version[1] = DM_VERSION_MINOR;
-        dm.version[2] = DM_VERSION_PATCHLEVEL;
-
-        dm.data_size = sizeof(dm);
-        dm.dev = devnum;
-
-        r = ioctl(fd, DM_DEV_REMOVE, &dm);
-        close_nointr_nofail(fd);
-
-        return r >= 0 ? 0 : -errno;
-}
-
-static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
-        MountPoint *m, *n;
-        int n_failed = 0;
-
-        assert(head);
-
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
-                if (path_equal(m->path, "/")
-#ifndef HAVE_SPLIT_USR
-                    || path_equal(m->path, "/usr")
-#endif
-                ) {
-                        n_failed++;
-                        continue;
-                }
-
-                /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
-                if (umount2(m->path, MNT_FORCE) == 0) {
-                        log_info("Unmounted %s.", m->path);
-                        if (changed)
-                                *changed = true;
-
-                        mount_point_free(head, m);
-                } else if (log_error) {
-                        log_warning("Could not unmount %s: %m", m->path);
-                        n_failed++;
-                }
-        }
-
-        return n_failed;
-}
-
-static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) {
-        MountPoint *m, *n;
-        int n_failed = 0;
-
-        assert(head);
-
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
-
-                if (m->skip_ro) {
-                        n_failed++;
-                        continue;
-                }
-
-                /* Trying to remount read-only */
-                if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
-                        if (changed)
-                                *changed = true;
-
-                        mount_point_free(head, m);
-                } else {
-                        log_warning("Could not remount as read-only %s: %m", m->path);
-                        n_failed++;
-                }
-        }
-
-        return n_failed;
-}
-
-static int swap_points_list_off(MountPoint **head, bool *changed) {
-        MountPoint *m, *n;
-        int n_failed = 0;
-
-        assert(head);
-
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
-                if (swapoff(m->path) == 0) {
-                        if (changed)
-                                *changed = true;
-
-                        mount_point_free(head, m);
-                } else {
-                        log_warning("Could not deactivate swap %s: %m", m->path);
-                        n_failed++;
-                }
-        }
-
-        return n_failed;
-}
-
-static int loopback_points_list_detach(MountPoint **head, bool *changed) {
-        MountPoint *m, *n;
-        int n_failed = 0, k;
-        struct stat root_st;
-
-        assert(head);
-
-        k = lstat("/", &root_st);
-
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
-                int r;
-                struct stat loopback_st;
-
-                if (k >= 0 &&
-                    major(root_st.st_dev) != 0 &&
-                    lstat(m->path, &loopback_st) >= 0 &&
-                    root_st.st_dev == loopback_st.st_rdev) {
-                        n_failed ++;
-                        continue;
-                }
-
-                if ((r = delete_loopback(m->path)) >= 0) {
-
-                        if (r > 0 && changed)
-                                *changed = true;
-
-                        mount_point_free(head, m);
-                } else {
-                        log_warning("Could not delete loopback %s: %m", m->path);
-                        n_failed++;
-                }
-        }
-
-        return n_failed;
-}
-
-static int dm_points_list_detach(MountPoint **head, bool *changed) {
-        MountPoint *m, *n;
-        int n_failed = 0, k;
-        struct stat root_st;
-
-        assert(head);
-
-        k = lstat("/", &root_st);
-
-        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
-                int r;
-
-                if (k >= 0 &&
-                    major(root_st.st_dev) != 0 &&
-                    root_st.st_dev == m->devnum) {
-                        n_failed ++;
-                        continue;
-                }
-
-                if ((r = delete_dm(m->devnum)) >= 0) {
-
-                        if (r > 0 && changed)
-                                *changed = true;
-
-                        mount_point_free(head, m);
-                } else {
-                        log_warning("Could not delete dm %s: %m", m->path);
-                        n_failed++;
-                }
-        }
-
-        return n_failed;
-}
-
-int umount_all(bool *changed) {
-        int r;
-        bool umount_changed;
-
-        LIST_HEAD(MountPoint, mp_list_head);
-
-        LIST_HEAD_INIT(MountPoint, mp_list_head);
-
-        r = mount_points_list_get(&mp_list_head);
-        if (r < 0)
-                goto end;
-
-        /* retry umount, until nothing can be umounted anymore */
-        do {
-                umount_changed = false;
-
-                mount_points_list_umount(&mp_list_head, &umount_changed, false);
-                if (umount_changed)
-                        *changed = true;
-
-        } while (umount_changed);
-
-        /* umount one more time with logging enabled */
-        r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
-        if (r <= 0)
-                goto end;
-
-        r = mount_points_list_remount_read_only(&mp_list_head, changed);
-
-  end:
-        mount_points_list_free(&mp_list_head);
-
-        return r;
-}
-
-int swapoff_all(bool *changed) {
-        int r;
-        LIST_HEAD(MountPoint, swap_list_head);
-
-        LIST_HEAD_INIT(MountPoint, swap_list_head);
-
-        r = swap_list_get(&swap_list_head);
-        if (r < 0)
-                goto end;
-
-        r = swap_points_list_off(&swap_list_head, changed);
-
-  end:
-        mount_points_list_free(&swap_list_head);
-
-        return r;
-}
-
-int loopback_detach_all(bool *changed) {
-        int r;
-        LIST_HEAD(MountPoint, loopback_list_head);
-
-        LIST_HEAD_INIT(MountPoint, loopback_list_head);
-
-        r = loopback_list_get(&loopback_list_head);
-        if (r < 0)
-                goto end;
-
-        r = loopback_points_list_detach(&loopback_list_head, changed);
-
-  end:
-        mount_points_list_free(&loopback_list_head);
-
-        return r;
-}
-
-int dm_detach_all(bool *changed) {
-        int r;
-        LIST_HEAD(MountPoint, dm_list_head);
-
-        LIST_HEAD_INIT(MountPoint, dm_list_head);
-
-        r = dm_list_get(&dm_list_head);
-        if (r < 0)
-                goto end;
-
-        r = dm_points_list_detach(&dm_list_head, changed);
-
-  end:
-        mount_points_list_free(&dm_list_head);
-
-        return r;
-}
diff --git a/src/umount.h b/src/umount.h
deleted file mode 100644
index 2e2f9c1..0000000
--- a/src/umount.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-#ifndef fooumounthfoo
-#define fooumounthfoo
-
-/***
-  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/>.
-***/
-
-int umount_all(bool *changed);
-
-int swapoff_all(bool *changed);
-
-int loopback_detach_all(bool *changed);
-
-int dm_detach_all(bool *changed);
-
-#endif



More information about the systemd-commits mailing list