[systemd-commits] Makefile.am src/ac-power src/ac-power.c src/ask-password src/ask-password.c src/cgls src/cgls.c src/cgroups-agent src/cgroups-agent.c src/cgtop src/cgtop.c src/detect-virt src/detect-virt.c src/fsck src/fsck.c src/getty-generator src/getty-generator.c src/initctl src/initctl.c src/modules-load src/modules-load.c src/notify src/notify.c src/nspawn src/nspawn.c src/quotacheck src/quotacheck.c src/random-seed src/random-seed.c src/rc-local-generator src/rc-local-generator.c src/remount-api-vfs src/remount-api-vfs.c src/reply-password src/reply-password.c src/shutdownd src/shutdownd.c src/sysctl src/sysctl.c src/systemctl src/systemctl.c src/timestamp src/timestamp.c src/tmpfiles src/tmpfiles.c src/tty-ask-password-agent src/tty-ask-password-agent.c src/update-utmp src/update-utmp.c

Kay Sievers kay at kemper.freedesktop.org
Thu Apr 12 08:54:54 PDT 2012


 Makefile.am                                         |   48 
 src/ac-power.c                                      |  111 
 src/ac-power/ac-power.c                             |  111 
 src/ask-password.c                                  |  184 
 src/ask-password/ask-password.c                     |  184 
 src/cgls.c                                          |  164 
 src/cgls/cgls.c                                     |  164 
 src/cgroups-agent.c                                 |  101 
 src/cgroups-agent/cgroups-agent.c                   |  101 
 src/cgtop.c                                         |  729 --
 src/cgtop/cgtop.c                                   |  729 ++
 src/detect-virt.c                                   |  171 
 src/detect-virt/detect-virt.c                       |  171 
 src/fsck.c                                          |  406 -
 src/fsck/fsck.c                                     |  406 +
 src/getty-generator.c                               |  183 
 src/getty-generator/getty-generator.c               |  183 
 src/initctl.c                                       |  451 -
 src/initctl/initctl.c                               |  451 +
 src/modules-load.c                                  |  155 
 src/modules-load/modules-load.c                     |  155 
 src/notify.c                                        |  228 
 src/notify/notify.c                                 |  228 
 src/nspawn.c                                        |  925 ---
 src/nspawn/nspawn.c                                 |  925 +++
 src/quotacheck.c                                    |  120 
 src/quotacheck/quotacheck.c                         |  120 
 src/random-seed.c                                   |  148 
 src/random-seed/random-seed.c                       |  148 
 src/rc-local-generator.c                            |  108 
 src/rc-local-generator/rc-local-generator.c         |  108 
 src/remount-api-vfs.c                               |  161 
 src/remount-api-vfs/remount-api-vfs.c               |  161 
 src/reply-password.c                                |  109 
 src/reply-password/reply-password.c                 |  109 
 src/shutdownd.c                                     |  476 -
 src/shutdownd/shutdownd.c                           |  476 +
 src/sysctl.c                                        |  272 
 src/sysctl/sysctl.c                                 |  272 
 src/systemctl.c                                     | 5523 --------------------
 src/systemctl/systemctl.c                           | 5523 ++++++++++++++++++++
 src/timestamp.c                                     |   39 
 src/timestamp/timestamp.c                           |   39 
 src/tmpfiles.c                                      | 1315 ----
 src/tmpfiles/tmpfiles.c                             | 1315 ++++
 src/tty-ask-password-agent.c                        |  754 --
 src/tty-ask-password-agent/tty-ask-password-agent.c |  754 ++
 src/update-utmp.c                                   |  423 -
 src/update-utmp/update-utmp.c                       |  423 +
 49 files changed, 13280 insertions(+), 13280 deletions(-)

New commits:
commit dce818b390a857a11f7dd634684500675cf79833
Author: Kay Sievers <kay at vrfy.org>
Date:   Thu Apr 12 17:15:18 2012 +0200

    move all tools to subdirs

diff --git a/Makefile.am b/Makefile.am
index edf92e3..ec7507f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -954,7 +954,7 @@ test_watchdog_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_initctl_SOURCES = \
-	src/initctl.c
+	src/initctl/initctl.c
 
 systemd_initctl_CFLAGS = \
 	$(AM_CFLAGS) \
@@ -968,7 +968,7 @@ systemd_initctl_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_update_utmp_SOURCES = \
-	src/update-utmp.c
+	src/update-utmp/update-utmp.c
 
 systemd_update_utmp_CFLAGS = \
 	$(AM_CFLAGS) \
@@ -983,7 +983,7 @@ systemd_update_utmp_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_shutdownd_SOURCES = \
-	src/shutdownd.c
+	src/shutdownd/shutdownd.c
 
 systemd_shutdownd_LDADD = \
 	libsystemd-label.la \
@@ -1006,7 +1006,7 @@ systemd_shutdown_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_modules_load_SOURCES = \
-	src/modules-load.c
+	src/modules-load/modules-load.c
 
 systemd_modules_load_CFLAGS = \
 	$(AM_CFLAGS) \
@@ -1018,7 +1018,7 @@ systemd_modules_load_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_tmpfiles_SOURCES = \
-	src/tmpfiles.c
+	src/tmpfiles/tmpfiles.c
 
 systemd_tmpfiles_LDADD = \
 	libsystemd-label.la \
@@ -1037,14 +1037,14 @@ systemd_machine_id_setup_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_sysctl_SOURCES = \
-	src/sysctl.c
+	src/sysctl/sysctl.c
 
 systemd_sysctl_LDADD = \
 	libsystemd-shared.la
 
 # ------------------------------------------------------------------------------
 systemd_fsck_SOURCES = \
-	src/fsck.c
+	src/fsck/fsck.c
 
 systemd_fsck_CFLAGS = \
 	$(AM_CFLAGS) \
@@ -1058,14 +1058,14 @@ systemd_fsck_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_timestamp_SOURCES = \
-	src/timestamp.c
+	src/timestamp/timestamp.c
 
 systemd_timestamp_LDADD = \
 	libsystemd-shared.la
 
 # ------------------------------------------------------------------------------
 systemd_ac_power_SOURCES = \
-	src/ac-power.c
+	src/ac-power/ac-power.c
 
 systemd_ac_power_LDADD = \
 	libsystemd-shared.la \
@@ -1073,14 +1073,14 @@ systemd_ac_power_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_detect_virt_SOURCES = \
-	src/detect-virt.c
+	src/detect-virt/detect-virt.c
 
 systemd_detect_virt_LDADD = \
 	libsystemd-shared.la
 
 # ------------------------------------------------------------------------------
 systemd_getty_generator_SOURCES = \
-	src/getty-generator.c
+	src/getty-generator/getty-generator.c
 
 systemd_getty_generator_LDADD = \
 	libsystemd-label.la \
@@ -1088,7 +1088,7 @@ systemd_getty_generator_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_rc_local_generator_SOURCES = \
-	src/rc-local-generator.c
+	src/rc-local-generator/rc-local-generator.c
 
 systemd_rc_local_generator_LDADD = \
 	libsystemd-label.la \
@@ -1096,14 +1096,14 @@ systemd_rc_local_generator_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_remount_api_vfs_SOURCES = \
-	src/remount-api-vfs.c
+	src/remount-api-vfs/remount-api-vfs.c
 
 systemd_remount_api_vfs_LDADD = \
 	libsystemd-shared.la
 
 # ------------------------------------------------------------------------------
 systemd_cgroups_agent_SOURCES = \
-	src/cgroups-agent.c
+	src/cgroups-agent/cgroups-agent.c
 
 systemd_cgroups_agent_CFLAGS = \
 	$(AM_CFLAGS) \
@@ -1116,7 +1116,7 @@ systemd_cgroups_agent_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemctl_SOURCES = \
-	src/systemctl.c
+	src/systemctl/systemctl.c
 
 systemctl_CFLAGS = \
 	$(AM_CFLAGS) \
@@ -1134,7 +1134,7 @@ systemctl_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_notify_SOURCES = \
-	src/notify.c \
+	src/notify/notify.c \
 	src/readahead/sd-readahead.c
 
 systemd_notify_LDADD = \
@@ -1143,7 +1143,7 @@ systemd_notify_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_ask_password_SOURCES = \
-	src/ask-password.c
+	src/ask-password/ask-password.c
 
 systemd_ask_password_LDADD = \
 	libsystemd-label.la \
@@ -1151,28 +1151,28 @@ systemd_ask_password_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_reply_password_SOURCES = \
-	src/reply-password.c
+	src/reply-password/reply-password.c
 
 systemd_reply_password_LDADD = \
 	libsystemd-shared.la
 
 # ------------------------------------------------------------------------------
 systemd_cgls_SOURCES = \
-	src/cgls.c
+	src/cgls/cgls.c
 
 systemd_cgls_LDADD = \
 	libsystemd-shared.la
 
 # ------------------------------------------------------------------------------
 systemd_cgtop_SOURCES = \
-	src/cgtop.c
+	src/cgtop/cgtop.c
 
 systemd_cgtop_LDADD = \
 	libsystemd-shared.la
 
 # ------------------------------------------------------------------------------
 systemd_nspawn_SOURCES = \
-	src/nspawn.c
+	src/nspawn/nspawn.c
 
 systemd_nspawn_LDADD = \
 	libsystemd-label.la \
@@ -1189,7 +1189,7 @@ systemd_stdio_bridge_LDADD = \
 
 # ------------------------------------------------------------------------------
 systemd_tty_ask_password_agent_SOURCES = \
-	src/tty-ask-password-agent.c
+	src/tty-ask-password-agent/tty-ask-password-agent.c
 
 systemd_tty_ask_password_agent_LDADD = \
 	libsystemd-label.la \
@@ -2306,7 +2306,7 @@ EXTRA_DIST += \
 	units/quotacheck.service.in
 
 systemd_quotacheck_SOURCES = \
-	src/quotacheck.c
+	src/quotacheck/quotacheck.c
 
 systemd_quotacheck_LDADD = \
 	libsystemd-shared.la
@@ -2326,7 +2326,7 @@ EXTRA_DIST += \
 	units/systemd-random-seed-load.service.in
 
 systemd_random_seed_SOURCES = \
-	src/random-seed.c
+	src/random-seed/random-seed.c
 
 systemd_random_seed_LDADD = \
 	libsystemd-label.la \
diff --git a/src/ac-power.c b/src/ac-power.c
deleted file mode 100644
index 37313cf..0000000
--- a/src/ac-power.c
+++ /dev/null
@@ -1,111 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <libudev.h>
-
-#include "util.h"
-
-static int on_ac_power(void) {
-        int r;
-
-        struct udev *udev;
-        struct udev_enumerate *e = NULL;
-        struct udev_list_entry *item = NULL, *first = NULL;
-        bool found_offline = false, found_online = false;
-
-        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, "power_supply") < 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) {
-                struct udev_device *d;
-                const char *type, *online;
-
-                if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(type = udev_device_get_sysattr_value(d, "type")))
-                        goto next;
-
-                if (!streq(type, "Mains"))
-                        goto next;
-
-                if (!(online = udev_device_get_sysattr_value(d, "online")))
-                        goto next;
-
-                if (streq(online, "1")) {
-                        found_online = true;
-                        break;
-                } else if (streq(online, "0"))
-                        found_offline = true;
-
-        next:
-                udev_device_unref(d);
-        }
-
-        r = found_online || !found_offline;
-
-finish:
-        if (e)
-                udev_enumerate_unref(e);
-
-        if (udev)
-                udev_unref(udev);
-
-        return r;
-}
-
-int main(int argc, char *argv[]) {
-        int r;
-
-        /* This is mostly intended to be used for scripts which want
-         * to detect whether AC power is plugged in or not. */
-
-        if ((r = on_ac_power()) < 0) {
-                log_error("Failed to read AC status: %s", strerror(-r));
-                return EXIT_FAILURE;
-        }
-
-        return r == 0;
-}
diff --git a/src/ac-power/ac-power.c b/src/ac-power/ac-power.c
new file mode 100644
index 0000000..37313cf
--- /dev/null
+++ b/src/ac-power/ac-power.c
@@ -0,0 +1,111 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <libudev.h>
+
+#include "util.h"
+
+static int on_ac_power(void) {
+        int r;
+
+        struct udev *udev;
+        struct udev_enumerate *e = NULL;
+        struct udev_list_entry *item = NULL, *first = NULL;
+        bool found_offline = false, found_online = false;
+
+        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, "power_supply") < 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) {
+                struct udev_device *d;
+                const char *type, *online;
+
+                if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(type = udev_device_get_sysattr_value(d, "type")))
+                        goto next;
+
+                if (!streq(type, "Mains"))
+                        goto next;
+
+                if (!(online = udev_device_get_sysattr_value(d, "online")))
+                        goto next;
+
+                if (streq(online, "1")) {
+                        found_online = true;
+                        break;
+                } else if (streq(online, "0"))
+                        found_offline = true;
+
+        next:
+                udev_device_unref(d);
+        }
+
+        r = found_online || !found_offline;
+
+finish:
+        if (e)
+                udev_enumerate_unref(e);
+
+        if (udev)
+                udev_unref(udev);
+
+        return r;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+
+        /* This is mostly intended to be used for scripts which want
+         * to detect whether AC power is plugged in or not. */
+
+        if ((r = on_ac_power()) < 0) {
+                log_error("Failed to read AC status: %s", strerror(-r));
+                return EXIT_FAILURE;
+        }
+
+        return r == 0;
+}
diff --git a/src/ask-password.c b/src/ask-password.c
deleted file mode 100644
index 5f67570..0000000
--- a/src/ask-password.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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/socket.h>
-#include <sys/poll.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/un.h>
-#include <sys/stat.h>
-#include <sys/signalfd.h>
-#include <getopt.h>
-#include <termios.h>
-#include <limits.h>
-#include <stddef.h>
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-#include "strv.h"
-#include "ask-password-api.h"
-#include "def.h"
-
-static const char *arg_icon = NULL;
-static const char *arg_message = NULL;
-static bool arg_use_tty = true;
-static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
-static bool arg_accept_cached = false;
-static bool arg_multiple = false;
-
-static int help(void) {
-
-        printf("%s [OPTIONS...] MESSAGE\n\n"
-               "Query the user for a system passphrase, via the TTY or an UI agent.\n\n"
-               "  -h --help          Show this help\n"
-               "     --icon=NAME     Icon name\n"
-               "     --timeout=SEC   Timeout in sec\n"
-               "     --no-tty        Ask question via agent even on TTY\n"
-               "     --accept-cached Accept cached passwords\n"
-               "     --multiple      List multiple passwords if available\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_ICON = 0x100,
-                ARG_TIMEOUT,
-                ARG_NO_TTY,
-                ARG_ACCEPT_CACHED,
-                ARG_MULTIPLE
-        };
-
-        static const struct option options[] = {
-                { "help",          no_argument,       NULL, 'h'               },
-                { "icon",          required_argument, NULL, ARG_ICON          },
-                { "timeout",       required_argument, NULL, ARG_TIMEOUT       },
-                { "no-tty",        no_argument,       NULL, ARG_NO_TTY        },
-                { "accept-cached", no_argument,       NULL, ARG_ACCEPT_CACHED },
-                { "multiple",      no_argument,       NULL, ARG_MULTIPLE      },
-                { NULL,            0,                 NULL, 0                 }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_ICON:
-                        arg_icon = optarg;
-                        break;
-
-                case ARG_TIMEOUT:
-                        if (parse_usec(optarg, &arg_timeout) < 0) {
-                                log_error("Failed to parse --timeout parameter %s", optarg);
-                                return -EINVAL;
-                        }
-                        break;
-
-                case ARG_NO_TTY:
-                        arg_use_tty = false;
-                        break;
-
-                case ARG_ACCEPT_CACHED:
-                        arg_accept_cached = true;
-                        break;
-
-                case ARG_MULTIPLE:
-                        arg_multiple = true;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind != argc-1) {
-                help();
-                return -EINVAL;
-        }
-
-        arg_message = argv[optind];
-        return 1;
-}
-
-int main(int argc, char *argv[]) {
-        int r;
-        usec_t timeout;
-
-        log_parse_environment();
-        log_open();
-
-        if ((r = parse_argv(argc, argv)) <= 0)
-                goto finish;
-
-        if (arg_timeout > 0)
-                timeout = now(CLOCK_MONOTONIC) + arg_timeout;
-        else
-                timeout = 0;
-
-        if (arg_use_tty && isatty(STDIN_FILENO)) {
-                char *password = NULL;
-
-                if ((r = ask_password_tty(arg_message, timeout, NULL, &password)) >= 0) {
-                        puts(password);
-                        free(password);
-                }
-
-        } else {
-                char **l;
-
-                if ((r = ask_password_agent(arg_message, arg_icon, timeout, arg_accept_cached, &l)) >= 0) {
-                        char **p;
-
-                        STRV_FOREACH(p, l) {
-                                puts(*p);
-
-                                if (!arg_multiple)
-                                        break;
-                        }
-
-                        strv_free(l);
-                }
-        }
-
-finish:
-
-        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c
new file mode 100644
index 0000000..5f67570
--- /dev/null
+++ b/src/ask-password/ask-password.c
@@ -0,0 +1,184 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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/socket.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/signalfd.h>
+#include <getopt.h>
+#include <termios.h>
+#include <limits.h>
+#include <stddef.h>
+
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+#include "strv.h"
+#include "ask-password-api.h"
+#include "def.h"
+
+static const char *arg_icon = NULL;
+static const char *arg_message = NULL;
+static bool arg_use_tty = true;
+static usec_t arg_timeout = DEFAULT_TIMEOUT_USEC;
+static bool arg_accept_cached = false;
+static bool arg_multiple = false;
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] MESSAGE\n\n"
+               "Query the user for a system passphrase, via the TTY or an UI agent.\n\n"
+               "  -h --help          Show this help\n"
+               "     --icon=NAME     Icon name\n"
+               "     --timeout=SEC   Timeout in sec\n"
+               "     --no-tty        Ask question via agent even on TTY\n"
+               "     --accept-cached Accept cached passwords\n"
+               "     --multiple      List multiple passwords if available\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_ICON = 0x100,
+                ARG_TIMEOUT,
+                ARG_NO_TTY,
+                ARG_ACCEPT_CACHED,
+                ARG_MULTIPLE
+        };
+
+        static const struct option options[] = {
+                { "help",          no_argument,       NULL, 'h'               },
+                { "icon",          required_argument, NULL, ARG_ICON          },
+                { "timeout",       required_argument, NULL, ARG_TIMEOUT       },
+                { "no-tty",        no_argument,       NULL, ARG_NO_TTY        },
+                { "accept-cached", no_argument,       NULL, ARG_ACCEPT_CACHED },
+                { "multiple",      no_argument,       NULL, ARG_MULTIPLE      },
+                { NULL,            0,                 NULL, 0                 }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_ICON:
+                        arg_icon = optarg;
+                        break;
+
+                case ARG_TIMEOUT:
+                        if (parse_usec(optarg, &arg_timeout) < 0) {
+                                log_error("Failed to parse --timeout parameter %s", optarg);
+                                return -EINVAL;
+                        }
+                        break;
+
+                case ARG_NO_TTY:
+                        arg_use_tty = false;
+                        break;
+
+                case ARG_ACCEPT_CACHED:
+                        arg_accept_cached = true;
+                        break;
+
+                case ARG_MULTIPLE:
+                        arg_multiple = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind != argc-1) {
+                help();
+                return -EINVAL;
+        }
+
+        arg_message = argv[optind];
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+        usec_t timeout;
+
+        log_parse_environment();
+        log_open();
+
+        if ((r = parse_argv(argc, argv)) <= 0)
+                goto finish;
+
+        if (arg_timeout > 0)
+                timeout = now(CLOCK_MONOTONIC) + arg_timeout;
+        else
+                timeout = 0;
+
+        if (arg_use_tty && isatty(STDIN_FILENO)) {
+                char *password = NULL;
+
+                if ((r = ask_password_tty(arg_message, timeout, NULL, &password)) >= 0) {
+                        puts(password);
+                        free(password);
+                }
+
+        } else {
+                char **l;
+
+                if ((r = ask_password_agent(arg_message, arg_icon, timeout, arg_accept_cached, &l)) >= 0) {
+                        char **p;
+
+                        STRV_FOREACH(p, l) {
+                                puts(*p);
+
+                                if (!arg_multiple)
+                                        break;
+                        }
+
+                        strv_free(l);
+                }
+        }
+
+finish:
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/cgls.c b/src/cgls.c
deleted file mode 100644
index fd02d52..0000000
--- a/src/cgls.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <limits.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <getopt.h>
-#include <string.h>
-
-#include "cgroup-show.h"
-#include "cgroup-util.h"
-#include "log.h"
-#include "util.h"
-#include "pager.h"
-
-static bool arg_no_pager = false;
-static bool arg_kernel_threads = false;
-
-static void help(void) {
-
-        printf("%s [OPTIONS...] [CGROUP...]\n\n"
-               "Recursively show control group contents.\n\n"
-               "  -h --help           Show this help\n"
-               "     --no-pager       Do not pipe output into a pager\n"
-               "  -k                  Include kernel threads in output\n",
-               program_invocation_short_name);
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_NO_PAGER = 0x100
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'          },
-                { "no-pager",  no_argument,       NULL, ARG_NO_PAGER },
-                { NULL,        0,                 NULL, 0            }
-        };
-
-        int c;
-
-        assert(argc >= 1);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "hk", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_NO_PAGER:
-                        arg_no_pager = true;
-                        break;
-
-                case 'k':
-                        arg_kernel_threads = true;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        return 1;
-}
-
-int main(int argc, char *argv[]) {
-        int r = 0, retval = EXIT_FAILURE;
-
-        log_parse_environment();
-        log_open();
-
-        r = parse_argv(argc, argv);
-        if (r < 0)
-                goto finish;
-        else if (r == 0) {
-                retval = EXIT_SUCCESS;
-                goto finish;
-        }
-
-        if (!arg_no_pager)
-                pager_open();
-
-        if (optind < argc) {
-                unsigned i;
-
-                for (i = (unsigned) optind; i < (unsigned) argc; i++) {
-                        int q;
-                        printf("%s:\n", argv[i]);
-
-                        q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads);
-                        if (q < 0)
-                                r = q;
-                }
-
-        } else {
-                char *p;
-
-                p = get_current_dir_name();
-                if (!p) {
-                        log_error("Cannot determine current working directory: %m");
-                        goto finish;
-                }
-
-                if (path_startswith(p, "/sys/fs/cgroup")) {
-                        printf("Working Directory %s:\n", p);
-                        r = show_cgroup_by_path(p, NULL, 0, arg_kernel_threads);
-                } else {
-                        char *root = NULL;
-                        const char *t = NULL;
-
-                        r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root);
-                        if (r < 0)
-                                t = "/";
-                        else {
-                                if (endswith(root, "/system"))
-                                        root[strlen(root)-7] = 0;
-
-                                t = root[0] ? root : "/";
-                        }
-
-                        r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, t, NULL, 0, arg_kernel_threads);
-                        free(root);
-                }
-
-                free(p);
-        }
-
-        if (r < 0)
-                log_error("Failed to list cgroup tree: %s", strerror(-r));
-
-        retval = EXIT_SUCCESS;
-
-finish:
-        pager_close();
-
-        return retval;
-}
diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c
new file mode 100644
index 0000000..fd02d52
--- /dev/null
+++ b/src/cgls/cgls.c
@@ -0,0 +1,164 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+
+#include "cgroup-show.h"
+#include "cgroup-util.h"
+#include "log.h"
+#include "util.h"
+#include "pager.h"
+
+static bool arg_no_pager = false;
+static bool arg_kernel_threads = false;
+
+static void help(void) {
+
+        printf("%s [OPTIONS...] [CGROUP...]\n\n"
+               "Recursively show control group contents.\n\n"
+               "  -h --help           Show this help\n"
+               "     --no-pager       Do not pipe output into a pager\n"
+               "  -k                  Include kernel threads in output\n",
+               program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_NO_PAGER = 0x100
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'          },
+                { "no-pager",  no_argument,       NULL, ARG_NO_PAGER },
+                { NULL,        0,                 NULL, 0            }
+        };
+
+        int c;
+
+        assert(argc >= 1);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "hk", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_NO_PAGER:
+                        arg_no_pager = true;
+                        break;
+
+                case 'k':
+                        arg_kernel_threads = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        int r = 0, retval = EXIT_FAILURE;
+
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r < 0)
+                goto finish;
+        else if (r == 0) {
+                retval = EXIT_SUCCESS;
+                goto finish;
+        }
+
+        if (!arg_no_pager)
+                pager_open();
+
+        if (optind < argc) {
+                unsigned i;
+
+                for (i = (unsigned) optind; i < (unsigned) argc; i++) {
+                        int q;
+                        printf("%s:\n", argv[i]);
+
+                        q = show_cgroup_by_path(argv[i], NULL, 0, arg_kernel_threads);
+                        if (q < 0)
+                                r = q;
+                }
+
+        } else {
+                char *p;
+
+                p = get_current_dir_name();
+                if (!p) {
+                        log_error("Cannot determine current working directory: %m");
+                        goto finish;
+                }
+
+                if (path_startswith(p, "/sys/fs/cgroup")) {
+                        printf("Working Directory %s:\n", p);
+                        r = show_cgroup_by_path(p, NULL, 0, arg_kernel_threads);
+                } else {
+                        char *root = NULL;
+                        const char *t = NULL;
+
+                        r = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 1, &root);
+                        if (r < 0)
+                                t = "/";
+                        else {
+                                if (endswith(root, "/system"))
+                                        root[strlen(root)-7] = 0;
+
+                                t = root[0] ? root : "/";
+                        }
+
+                        r = show_cgroup(SYSTEMD_CGROUP_CONTROLLER, t, NULL, 0, arg_kernel_threads);
+                        free(root);
+                }
+
+                free(p);
+        }
+
+        if (r < 0)
+                log_error("Failed to list cgroup tree: %s", strerror(-r));
+
+        retval = EXIT_SUCCESS;
+
+finish:
+        pager_close();
+
+        return retval;
+}
diff --git a/src/cgroups-agent.c b/src/cgroups-agent.c
deleted file mode 100644
index 7a6173e..0000000
--- a/src/cgroups-agent.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <dbus/dbus.h>
-
-#include <stdlib.h>
-
-#include "log.h"
-#include "dbus-common.h"
-
-int main(int argc, char *argv[]) {
-        DBusError error;
-        DBusConnection *bus = NULL;
-        DBusMessage *m = NULL;
-        int r = EXIT_FAILURE;
-
-        dbus_error_init(&error);
-
-        if (argc != 2) {
-                log_error("Incorrect number of arguments.");
-                goto finish;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        /* We send this event to the private D-Bus socket and then the
-         * system instance will forward this to the system bus. We do
-         * this to avoid an activation loop when we start dbus when we
-         * are called when the dbus service is shut down. */
-
-        if (!(bus = dbus_connection_open_private("unix:path=/run/systemd/private", &error))) {
-#ifndef LEGACY
-                dbus_error_free(&error);
-
-                /* Retry with the pre v21 socket name, to ease upgrades */
-                if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", &error))) {
-#endif
-                        log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
-                        goto finish;
-                }
-#ifndef LEGACY
-        }
-#endif
-
-        if (bus_check_peercred(bus) < 0) {
-                log_error("Bus owner not root.");
-                goto finish;
-        }
-
-        if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1/agent", "org.freedesktop.systemd1.Agent", "Released"))) {
-                log_error("Could not allocate signal message.");
-                goto finish;
-        }
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &argv[1],
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not attach group information to signal message.");
-                goto finish;
-        }
-
-        if (!dbus_connection_send(bus, m, NULL)) {
-                log_error("Failed to send signal message on private connection.");
-                goto finish;
-        }
-
-        r = EXIT_SUCCESS;
-
-finish:
-        if (bus) {
-                dbus_connection_flush(bus);
-                dbus_connection_close(bus);
-                dbus_connection_unref(bus);
-        }
-
-        if (m)
-                dbus_message_unref(m);
-
-        dbus_error_free(&error);
-        return r;
-}
diff --git a/src/cgroups-agent/cgroups-agent.c b/src/cgroups-agent/cgroups-agent.c
new file mode 100644
index 0000000..7a6173e
--- /dev/null
+++ b/src/cgroups-agent/cgroups-agent.c
@@ -0,0 +1,101 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <dbus/dbus.h>
+
+#include <stdlib.h>
+
+#include "log.h"
+#include "dbus-common.h"
+
+int main(int argc, char *argv[]) {
+        DBusError error;
+        DBusConnection *bus = NULL;
+        DBusMessage *m = NULL;
+        int r = EXIT_FAILURE;
+
+        dbus_error_init(&error);
+
+        if (argc != 2) {
+                log_error("Incorrect number of arguments.");
+                goto finish;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        /* We send this event to the private D-Bus socket and then the
+         * system instance will forward this to the system bus. We do
+         * this to avoid an activation loop when we start dbus when we
+         * are called when the dbus service is shut down. */
+
+        if (!(bus = dbus_connection_open_private("unix:path=/run/systemd/private", &error))) {
+#ifndef LEGACY
+                dbus_error_free(&error);
+
+                /* Retry with the pre v21 socket name, to ease upgrades */
+                if (!(bus = dbus_connection_open_private("unix:abstract=/org/freedesktop/systemd1/private", &error))) {
+#endif
+                        log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
+                        goto finish;
+                }
+#ifndef LEGACY
+        }
+#endif
+
+        if (bus_check_peercred(bus) < 0) {
+                log_error("Bus owner not root.");
+                goto finish;
+        }
+
+        if (!(m = dbus_message_new_signal("/org/freedesktop/systemd1/agent", "org.freedesktop.systemd1.Agent", "Released"))) {
+                log_error("Could not allocate signal message.");
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &argv[1],
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not attach group information to signal message.");
+                goto finish;
+        }
+
+        if (!dbus_connection_send(bus, m, NULL)) {
+                log_error("Failed to send signal message on private connection.");
+                goto finish;
+        }
+
+        r = EXIT_SUCCESS;
+
+finish:
+        if (bus) {
+                dbus_connection_flush(bus);
+                dbus_connection_close(bus);
+                dbus_connection_unref(bus);
+        }
+
+        if (m)
+                dbus_message_unref(m);
+
+        dbus_error_free(&error);
+        return r;
+}
diff --git a/src/cgtop.c b/src/cgtop.c
deleted file mode 100644
index 1fe247c..0000000
--- a/src/cgtop.c
+++ /dev/null
@@ -1,729 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2012 Lennart Poettering
-
-  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 <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <alloca.h>
-#include <getopt.h>
-
-#include "util.h"
-#include "hashmap.h"
-#include "cgroup-util.h"
-
-typedef struct Group {
-        char *path;
-
-        bool n_tasks_valid:1;
-        bool cpu_valid:1;
-        bool memory_valid:1;
-        bool io_valid:1;
-
-        unsigned n_tasks;
-
-        unsigned cpu_iteration;
-        uint64_t cpu_usage;
-        struct timespec cpu_timestamp;
-        double cpu_fraction;
-
-        uint64_t memory;
-
-        unsigned io_iteration;
-        uint64_t io_input, io_output;
-        struct timespec io_timestamp;
-        uint64_t io_input_bps, io_output_bps;
-} Group;
-
-static unsigned arg_depth = 2;
-static usec_t arg_delay = 1*USEC_PER_SEC;
-
-static enum {
-        ORDER_PATH,
-        ORDER_TASKS,
-        ORDER_CPU,
-        ORDER_MEMORY,
-        ORDER_IO
-} arg_order = ORDER_CPU;
-
-static void group_free(Group *g) {
-        assert(g);
-
-        free(g->path);
-        free(g);
-}
-
-static void group_hashmap_clear(Hashmap *h) {
-        Group *g;
-
-        while ((g = hashmap_steal_first(h)))
-                group_free(g);
-}
-
-static void group_hashmap_free(Hashmap *h) {
-        group_hashmap_clear(h);
-        hashmap_free(h);
-}
-
-static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
-        Group *g;
-        int r;
-        FILE *f;
-        pid_t pid;
-        unsigned n;
-
-        assert(controller);
-        assert(path);
-        assert(a);
-
-        g = hashmap_get(a, path);
-        if (!g) {
-                g = hashmap_get(b, path);
-                if (!g) {
-                        g = new0(Group, 1);
-                        if (!g)
-                                return -ENOMEM;
-
-                        g->path = strdup(path);
-                        if (!g->path) {
-                                group_free(g);
-                                return -ENOMEM;
-                        }
-
-                        r = hashmap_put(a, g->path, g);
-                        if (r < 0) {
-                                group_free(g);
-                                return r;
-                        }
-                } else {
-                        assert_se(hashmap_move_one(a, b, path) == 0);
-                        g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
-                }
-        }
-
-        /* Regardless which controller, let's find the maximum number
-         * of processes in any of it */
-
-        r = cg_enumerate_tasks(controller, path, &f);
-        if (r < 0)
-                return r;
-
-        n = 0;
-        while (cg_read_pid(f, &pid) > 0)
-                n++;
-        fclose(f);
-
-        if (n > 0) {
-                if (g->n_tasks_valid)
-                        g->n_tasks = MAX(g->n_tasks, n);
-                else
-                        g->n_tasks = n;
-
-                g->n_tasks_valid = true;
-        }
-
-        if (streq(controller, "cpuacct")) {
-                uint64_t new_usage;
-                char *p, *v;
-                struct timespec ts;
-
-                r = cg_get_path(controller, path, "cpuacct.usage", &p);
-                if (r < 0)
-                        return r;
-
-                r = read_one_line_file(p, &v);
-                free(p);
-                if (r < 0)
-                        return r;
-
-                r = safe_atou64(v, &new_usage);
-                free(v);
-                if (r < 0)
-                        return r;
-
-                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
-
-                if (g->cpu_iteration == iteration - 1) {
-                        uint64_t x, y;
-
-                        x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
-                                ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec);
-
-                        y = new_usage - g->cpu_usage;
-
-                        if (y > 0) {
-                                g->cpu_fraction = (double) y / (double) x;
-                                g->cpu_valid = true;
-                        }
-                }
-
-                g->cpu_usage = new_usage;
-                g->cpu_timestamp = ts;
-                g->cpu_iteration = iteration;
-
-        } else if (streq(controller, "memory")) {
-                char *p, *v;
-
-                r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
-                if (r < 0)
-                        return r;
-
-                r = read_one_line_file(p, &v);
-                free(p);
-                if (r < 0)
-                        return r;
-
-                r = safe_atou64(v, &g->memory);
-                free(v);
-                if (r < 0)
-                        return r;
-
-                if (g->memory > 0)
-                        g->memory_valid = true;
-
-        } else if (streq(controller, "blkio")) {
-                char *p;
-                uint64_t wr = 0, rd = 0;
-                struct timespec ts;
-
-                r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
-                if (r < 0)
-                        return r;
-
-                f = fopen(p, "re");
-                free(p);
-
-                if (!f)
-                        return -errno;
-
-                for (;;) {
-                        char line[LINE_MAX], *l;
-                        uint64_t k, *q;
-
-                        if (!fgets(line, sizeof(line), f))
-                                break;
-
-                        l = strstrip(line);
-                        l += strcspn(l, WHITESPACE);
-                        l += strspn(l, WHITESPACE);
-
-                        if (first_word(l, "Read")) {
-                                l += 4;
-                                q = &rd;
-                        } else if (first_word(l, "Write")) {
-                                l += 5;
-                                q = &wr;
-                        } else
-                                continue;
-
-                        l += strspn(l, WHITESPACE);
-                        r = safe_atou64(l, &k);
-                        if (r < 0)
-                                continue;
-
-                        *q += k;
-                }
-
-                fclose(f);
-
-                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
-
-                if (g->io_iteration == iteration - 1) {
-                        uint64_t x, yr, yw;
-
-                        x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
-                                ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec);
-
-                        yr = rd - g->io_input;
-                        yw = wr - g->io_output;
-
-                        if (yr > 0 || yw > 0) {
-                                g->io_input_bps = (yr * 1000000000ULL) / x;
-                                g->io_output_bps = (yw * 1000000000ULL) / x;
-                                g->io_valid = true;
-
-                        }
-                }
-
-                g->io_input = rd;
-                g->io_output = wr;
-                g->io_timestamp = ts;
-                g->io_iteration = iteration;
-        }
-
-        return 0;
-}
-
-static int refresh_one(
-                const char *controller,
-                const char *path,
-                Hashmap *a,
-                Hashmap *b,
-                unsigned iteration,
-                unsigned depth) {
-
-        DIR *d = NULL;
-        int r;
-
-        assert(controller);
-        assert(path);
-        assert(a);
-
-        if (depth > arg_depth)
-                return 0;
-
-        r = process(controller, path, a, b, iteration);
-        if (r < 0)
-                return r;
-
-        r = cg_enumerate_subgroups(controller, path, &d);
-        if (r < 0) {
-                if (r == ENOENT)
-                        return 0;
-
-                return r;
-        }
-
-        for (;;) {
-                char *fn, *p;
-
-                r = cg_read_subgroup(d, &fn);
-                if (r <= 0)
-                        goto finish;
-
-                p = join(path, "/", fn, NULL);
-                free(fn);
-
-                if (!p) {
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                path_kill_slashes(p);
-
-                r = refresh_one(controller, p, a, b, iteration, depth + 1);
-                free(p);
-
-                if (r < 0)
-                        goto finish;
-        }
-
-finish:
-        if (d)
-                closedir(d);
-
-        return r;
-}
-
-static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) {
-        int r;
-
-        assert(a);
-
-        r = refresh_one("name=systemd", "/", a, b, iteration, 0);
-        if (r < 0)
-                return r;
-
-        r = refresh_one("cpuacct", "/", a, b, iteration, 0);
-        if (r < 0)
-                return r;
-
-        r = refresh_one("memory", "/", a, b, iteration, 0);
-        if (r < 0)
-                return r;
-
-        return refresh_one("blkio", "/", a, b, iteration, 0);
-}
-
-static int group_compare(const void*a, const void *b) {
-        const Group *x = *(Group**)a, *y = *(Group**)b;
-
-        if (path_startswith(y->path, x->path))
-                return -1;
-        if (path_startswith(x->path, y->path))
-                return 1;
-
-        if (arg_order == ORDER_CPU) {
-                if (x->cpu_valid && y->cpu_valid) {
-
-                        if (x->cpu_fraction > y->cpu_fraction)
-                                return -1;
-                        else if (x->cpu_fraction < y->cpu_fraction)
-                                return 1;
-                } else if (x->cpu_valid)
-                        return -1;
-                else if (y->cpu_valid)
-                        return 1;
-        }
-
-        if (arg_order == ORDER_TASKS) {
-
-                if (x->n_tasks_valid && y->n_tasks_valid) {
-                        if (x->n_tasks > y->n_tasks)
-                                return -1;
-                        else if (x->n_tasks < y->n_tasks)
-                                return 1;
-                } else if (x->n_tasks_valid)
-                        return -1;
-                else if (y->n_tasks_valid)
-                        return 1;
-        }
-
-        if (arg_order == ORDER_MEMORY) {
-                if (x->memory_valid && y->memory_valid) {
-                        if (x->memory > y->memory)
-                                return -1;
-                        else if (x->memory < y->memory)
-                                return 1;
-                } else if (x->memory_valid)
-                        return -1;
-                else if (y->memory_valid)
-                        return 1;
-        }
-
-        if (arg_order == ORDER_IO) {
-                if (x->io_valid && y->io_valid) {
-                        if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
-                                return -1;
-                        else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
-                                return 1;
-                } else if (x->io_valid)
-                        return -1;
-                else if (y->io_valid)
-                        return 1;
-        }
-
-        return strcmp(x->path, y->path);
-}
-
-static int display(Hashmap *a) {
-        Iterator i;
-        Group *g;
-        Group **array;
-        unsigned rows, n = 0, j;
-
-        assert(a);
-
-        /* Set cursor to top left corner and clear screen */
-        fputs("\033[H"
-              "\033[2J", stdout);
-
-        array = alloca(sizeof(Group*) * hashmap_size(a));
-
-        HASHMAP_FOREACH(g, a, i)
-                if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
-                        array[n++] = g;
-
-        qsort(array, n, sizeof(Group*), group_compare);
-
-        rows = fd_lines(STDOUT_FILENO);
-        if (rows <= 0)
-                rows = 25;
-
-        printf("%s%-37s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
-               arg_order == ORDER_PATH   ? ANSI_HIGHLIGHT_ON : "", "Path",     arg_order == ORDER_PATH   ? ANSI_HIGHLIGHT_OFF : "",
-               arg_order == ORDER_TASKS  ? ANSI_HIGHLIGHT_ON : "", "Tasks",    arg_order == ORDER_TASKS  ? ANSI_HIGHLIGHT_OFF : "",
-               arg_order == ORDER_CPU    ? ANSI_HIGHLIGHT_ON : "", "%CPU",     arg_order == ORDER_CPU    ? ANSI_HIGHLIGHT_OFF : "",
-               arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "", "Memory",   arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF : "",
-               arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_ON : "", "Input/s",  arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_OFF : "",
-               arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_ON : "", "Output/s", arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_OFF : "");
-
-        for (j = 0; j < n; j++) {
-                char *p;
-                char m[FORMAT_BYTES_MAX];
-
-                if (j + 5 > rows)
-                        break;
-
-                g = array[j];
-
-                p = ellipsize(g->path, 37, 33);
-                printf("%-37s", p ? p : g->path);
-                free(p);
-
-                if (g->n_tasks_valid)
-                        printf(" %7u", g->n_tasks);
-                else
-                        fputs("       -", stdout);
-
-                if (g->cpu_valid)
-                        printf(" %6.1f", g->cpu_fraction*100);
-                else
-                        fputs("      -", stdout);
-
-                if (g->memory_valid)
-                        printf(" %8s", format_bytes(m, sizeof(m), g->memory));
-                else
-                        fputs("        -", stdout);
-
-                if (g->io_valid) {
-                        printf(" %8s",
-                               format_bytes(m, sizeof(m), g->io_input_bps));
-                        printf(" %8s",
-                               format_bytes(m, sizeof(m), g->io_output_bps));
-                } else
-                        fputs("        -        -", stdout);
-
-                putchar('\n');
-        }
-
-        return 0;
-}
-
-static void help(void) {
-
-        printf("%s [OPTIONS...]\n\n"
-               "Show top control groups by their resource usage.\n\n"
-               "  -h --help           Show this help\n"
-               "  -p                  Order by path\n"
-               "  -t                  Order by number of tasks\n"
-               "  -c                  Order by CPU load\n"
-               "  -m                  Order by memory load\n"
-               "  -i                  Order by IO load\n"
-               "  -d --delay=DELAY    Specify delay\n"
-               "     --depth=DEPTH    Maximum traversal depth (default: 2)\n",
-               program_invocation_short_name);
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_DEPTH = 0x100
-        };
-
-        static const struct option options[] = {
-                { "help",  no_argument,       NULL, 'h'       },
-                { "delay", required_argument, NULL, 'd'       },
-                { "depth", required_argument, NULL, ARG_DEPTH },
-                { NULL,    0,                 NULL, 0         }
-        };
-
-        int c;
-        int r;
-
-        assert(argc >= 1);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "hptcmid:", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_DEPTH:
-                        r = safe_atou(optarg, &arg_depth);
-                        if (r < 0) {
-                                log_error("Failed to parse depth parameter.");
-                                return -EINVAL;
-                        }
-
-                        break;
-
-                case 'd':
-                        r = parse_usec(optarg, &arg_delay);
-                        if (r < 0 || arg_delay <= 0) {
-                                log_error("Failed to parse delay parameter.");
-                                return -EINVAL;
-                        }
-
-                        break;
-
-                case 'p':
-                        arg_order = ORDER_PATH;
-                        break;
-
-                case 't':
-                        arg_order = ORDER_TASKS;
-                        break;
-
-                case 'c':
-                        arg_order = ORDER_CPU;
-                        break;
-
-                case 'm':
-                        arg_order = ORDER_MEMORY;
-                        break;
-
-                case 'i':
-                        arg_order = ORDER_IO;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind < argc) {
-                log_error("Too many arguments.");
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-int main(int argc, char *argv[]) {
-        int r;
-        Hashmap *a = NULL, *b = NULL;
-        unsigned iteration = 0;
-        usec_t last_refresh = 0;
-        bool quit = false, immediate_refresh = false;
-
-        log_parse_environment();
-        log_open();
-
-        r = parse_argv(argc, argv);
-        if (r <= 0)
-                goto finish;
-
-        a = hashmap_new(string_hash_func, string_compare_func);
-        b = hashmap_new(string_hash_func, string_compare_func);
-        if (!a || !b) {
-                log_error("Out of memory");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        while (!quit) {
-                Hashmap *c;
-                usec_t t;
-                char key;
-                char h[FORMAT_TIMESPAN_MAX];
-
-                t = now(CLOCK_MONOTONIC);
-
-                if (t >= last_refresh + arg_delay || immediate_refresh) {
-
-                        r = refresh(a, b, iteration++);
-                        if (r < 0)
-                                goto finish;
-
-                        group_hashmap_clear(b);
-
-                        c = a;
-                        a = b;
-                        b = c;
-
-                        last_refresh = t;
-                        immediate_refresh = false;
-                }
-
-                r = display(b);
-                if (r < 0)
-                        goto finish;
-
-                r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
-                if (r == -ETIMEDOUT)
-                        continue;
-                if (r < 0) {
-                        log_error("Couldn't read key: %s", strerror(-r));
-                        goto finish;
-                }
-
-                fputs("\r \r", stdout);
-                fflush(stdout);
-
-                switch (key) {
-
-                case ' ':
-                        immediate_refresh = true;
-                        break;
-
-                case 'q':
-                        quit = true;
-                        break;
-
-                case 'p':
-                        arg_order = ORDER_PATH;
-                        break;
-
-                case 't':
-                        arg_order = ORDER_TASKS;
-                        break;
-
-                case 'c':
-                        arg_order = ORDER_CPU;
-                        break;
-
-                case 'm':
-                        arg_order = ORDER_MEMORY;
-                        break;
-
-                case 'i':
-                        arg_order = ORDER_IO;
-                        break;
-
-                case '+':
-                        if (arg_delay < USEC_PER_SEC)
-                                arg_delay += USEC_PER_MSEC*250;
-                        else
-                                arg_delay += USEC_PER_SEC;
-
-                        fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
-                        fflush(stdout);
-                        sleep(1);
-                        break;
-
-                case '-':
-                        if (arg_delay <= USEC_PER_MSEC*500)
-                                arg_delay = USEC_PER_MSEC*250;
-                        else if (arg_delay < USEC_PER_MSEC*1250)
-                                arg_delay -= USEC_PER_MSEC*250;
-                        else
-                                arg_delay -= USEC_PER_SEC;
-
-                        fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
-                        fflush(stdout);
-                        sleep(1);
-                        break;
-
-                case '?':
-                case 'h':
-                        fprintf(stdout,
-                                "\t<" ANSI_HIGHLIGHT_ON "P" ANSI_HIGHLIGHT_OFF "> By path; <" ANSI_HIGHLIGHT_ON "T" ANSI_HIGHLIGHT_OFF "> By tasks; <" ANSI_HIGHLIGHT_ON "C" ANSI_HIGHLIGHT_OFF "> By CPU; <" ANSI_HIGHLIGHT_ON "M" ANSI_HIGHLIGHT_OFF "> By memory; <" ANSI_HIGHLIGHT_ON "I" ANSI_HIGHLIGHT_OFF "> By I/O\n"
-                                "\t<" ANSI_HIGHLIGHT_ON "Q" ANSI_HIGHLIGHT_OFF "> Quit; <" ANSI_HIGHLIGHT_ON "+" ANSI_HIGHLIGHT_OFF "> Increase delay; <" ANSI_HIGHLIGHT_ON "-" ANSI_HIGHLIGHT_OFF "> Decrease delay; <" ANSI_HIGHLIGHT_ON "SPACE" ANSI_HIGHLIGHT_OFF "> Refresh");
-                        fflush(stdout);
-                        sleep(3);
-                        break;
-
-                default:
-                        fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
-                        fflush(stdout);
-                        sleep(1);
-                        break;
-                }
-        }
-
-        log_info("Exiting.");
-
-        r = 0;
-
-finish:
-        group_hashmap_free(a);
-        group_hashmap_free(b);
-
-        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c
new file mode 100644
index 0000000..1fe247c
--- /dev/null
+++ b/src/cgtop/cgtop.c
@@ -0,0 +1,729 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2012 Lennart Poettering
+
+  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 <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <alloca.h>
+#include <getopt.h>
+
+#include "util.h"
+#include "hashmap.h"
+#include "cgroup-util.h"
+
+typedef struct Group {
+        char *path;
+
+        bool n_tasks_valid:1;
+        bool cpu_valid:1;
+        bool memory_valid:1;
+        bool io_valid:1;
+
+        unsigned n_tasks;
+
+        unsigned cpu_iteration;
+        uint64_t cpu_usage;
+        struct timespec cpu_timestamp;
+        double cpu_fraction;
+
+        uint64_t memory;
+
+        unsigned io_iteration;
+        uint64_t io_input, io_output;
+        struct timespec io_timestamp;
+        uint64_t io_input_bps, io_output_bps;
+} Group;
+
+static unsigned arg_depth = 2;
+static usec_t arg_delay = 1*USEC_PER_SEC;
+
+static enum {
+        ORDER_PATH,
+        ORDER_TASKS,
+        ORDER_CPU,
+        ORDER_MEMORY,
+        ORDER_IO
+} arg_order = ORDER_CPU;
+
+static void group_free(Group *g) {
+        assert(g);
+
+        free(g->path);
+        free(g);
+}
+
+static void group_hashmap_clear(Hashmap *h) {
+        Group *g;
+
+        while ((g = hashmap_steal_first(h)))
+                group_free(g);
+}
+
+static void group_hashmap_free(Hashmap *h) {
+        group_hashmap_clear(h);
+        hashmap_free(h);
+}
+
+static int process(const char *controller, const char *path, Hashmap *a, Hashmap *b, unsigned iteration) {
+        Group *g;
+        int r;
+        FILE *f;
+        pid_t pid;
+        unsigned n;
+
+        assert(controller);
+        assert(path);
+        assert(a);
+
+        g = hashmap_get(a, path);
+        if (!g) {
+                g = hashmap_get(b, path);
+                if (!g) {
+                        g = new0(Group, 1);
+                        if (!g)
+                                return -ENOMEM;
+
+                        g->path = strdup(path);
+                        if (!g->path) {
+                                group_free(g);
+                                return -ENOMEM;
+                        }
+
+                        r = hashmap_put(a, g->path, g);
+                        if (r < 0) {
+                                group_free(g);
+                                return r;
+                        }
+                } else {
+                        assert_se(hashmap_move_one(a, b, path) == 0);
+                        g->cpu_valid = g->memory_valid = g->io_valid = g->n_tasks_valid = false;
+                }
+        }
+
+        /* Regardless which controller, let's find the maximum number
+         * of processes in any of it */
+
+        r = cg_enumerate_tasks(controller, path, &f);
+        if (r < 0)
+                return r;
+
+        n = 0;
+        while (cg_read_pid(f, &pid) > 0)
+                n++;
+        fclose(f);
+
+        if (n > 0) {
+                if (g->n_tasks_valid)
+                        g->n_tasks = MAX(g->n_tasks, n);
+                else
+                        g->n_tasks = n;
+
+                g->n_tasks_valid = true;
+        }
+
+        if (streq(controller, "cpuacct")) {
+                uint64_t new_usage;
+                char *p, *v;
+                struct timespec ts;
+
+                r = cg_get_path(controller, path, "cpuacct.usage", &p);
+                if (r < 0)
+                        return r;
+
+                r = read_one_line_file(p, &v);
+                free(p);
+                if (r < 0)
+                        return r;
+
+                r = safe_atou64(v, &new_usage);
+                free(v);
+                if (r < 0)
+                        return r;
+
+                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
+
+                if (g->cpu_iteration == iteration - 1) {
+                        uint64_t x, y;
+
+                        x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
+                                ((uint64_t) g->cpu_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->cpu_timestamp.tv_nsec);
+
+                        y = new_usage - g->cpu_usage;
+
+                        if (y > 0) {
+                                g->cpu_fraction = (double) y / (double) x;
+                                g->cpu_valid = true;
+                        }
+                }
+
+                g->cpu_usage = new_usage;
+                g->cpu_timestamp = ts;
+                g->cpu_iteration = iteration;
+
+        } else if (streq(controller, "memory")) {
+                char *p, *v;
+
+                r = cg_get_path(controller, path, "memory.usage_in_bytes", &p);
+                if (r < 0)
+                        return r;
+
+                r = read_one_line_file(p, &v);
+                free(p);
+                if (r < 0)
+                        return r;
+
+                r = safe_atou64(v, &g->memory);
+                free(v);
+                if (r < 0)
+                        return r;
+
+                if (g->memory > 0)
+                        g->memory_valid = true;
+
+        } else if (streq(controller, "blkio")) {
+                char *p;
+                uint64_t wr = 0, rd = 0;
+                struct timespec ts;
+
+                r = cg_get_path(controller, path, "blkio.io_service_bytes", &p);
+                if (r < 0)
+                        return r;
+
+                f = fopen(p, "re");
+                free(p);
+
+                if (!f)
+                        return -errno;
+
+                for (;;) {
+                        char line[LINE_MAX], *l;
+                        uint64_t k, *q;
+
+                        if (!fgets(line, sizeof(line), f))
+                                break;
+
+                        l = strstrip(line);
+                        l += strcspn(l, WHITESPACE);
+                        l += strspn(l, WHITESPACE);
+
+                        if (first_word(l, "Read")) {
+                                l += 4;
+                                q = &rd;
+                        } else if (first_word(l, "Write")) {
+                                l += 5;
+                                q = &wr;
+                        } else
+                                continue;
+
+                        l += strspn(l, WHITESPACE);
+                        r = safe_atou64(l, &k);
+                        if (r < 0)
+                                continue;
+
+                        *q += k;
+                }
+
+                fclose(f);
+
+                assert_se(clock_gettime(CLOCK_MONOTONIC, &ts) == 0);
+
+                if (g->io_iteration == iteration - 1) {
+                        uint64_t x, yr, yw;
+
+                        x = ((uint64_t) ts.tv_sec * 1000000000ULL + (uint64_t) ts.tv_nsec) -
+                                ((uint64_t) g->io_timestamp.tv_sec * 1000000000ULL + (uint64_t) g->io_timestamp.tv_nsec);
+
+                        yr = rd - g->io_input;
+                        yw = wr - g->io_output;
+
+                        if (yr > 0 || yw > 0) {
+                                g->io_input_bps = (yr * 1000000000ULL) / x;
+                                g->io_output_bps = (yw * 1000000000ULL) / x;
+                                g->io_valid = true;
+
+                        }
+                }
+
+                g->io_input = rd;
+                g->io_output = wr;
+                g->io_timestamp = ts;
+                g->io_iteration = iteration;
+        }
+
+        return 0;
+}
+
+static int refresh_one(
+                const char *controller,
+                const char *path,
+                Hashmap *a,
+                Hashmap *b,
+                unsigned iteration,
+                unsigned depth) {
+
+        DIR *d = NULL;
+        int r;
+
+        assert(controller);
+        assert(path);
+        assert(a);
+
+        if (depth > arg_depth)
+                return 0;
+
+        r = process(controller, path, a, b, iteration);
+        if (r < 0)
+                return r;
+
+        r = cg_enumerate_subgroups(controller, path, &d);
+        if (r < 0) {
+                if (r == ENOENT)
+                        return 0;
+
+                return r;
+        }
+
+        for (;;) {
+                char *fn, *p;
+
+                r = cg_read_subgroup(d, &fn);
+                if (r <= 0)
+                        goto finish;
+
+                p = join(path, "/", fn, NULL);
+                free(fn);
+
+                if (!p) {
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                path_kill_slashes(p);
+
+                r = refresh_one(controller, p, a, b, iteration, depth + 1);
+                free(p);
+
+                if (r < 0)
+                        goto finish;
+        }
+
+finish:
+        if (d)
+                closedir(d);
+
+        return r;
+}
+
+static int refresh(Hashmap *a, Hashmap *b, unsigned iteration) {
+        int r;
+
+        assert(a);
+
+        r = refresh_one("name=systemd", "/", a, b, iteration, 0);
+        if (r < 0)
+                return r;
+
+        r = refresh_one("cpuacct", "/", a, b, iteration, 0);
+        if (r < 0)
+                return r;
+
+        r = refresh_one("memory", "/", a, b, iteration, 0);
+        if (r < 0)
+                return r;
+
+        return refresh_one("blkio", "/", a, b, iteration, 0);
+}
+
+static int group_compare(const void*a, const void *b) {
+        const Group *x = *(Group**)a, *y = *(Group**)b;
+
+        if (path_startswith(y->path, x->path))
+                return -1;
+        if (path_startswith(x->path, y->path))
+                return 1;
+
+        if (arg_order == ORDER_CPU) {
+                if (x->cpu_valid && y->cpu_valid) {
+
+                        if (x->cpu_fraction > y->cpu_fraction)
+                                return -1;
+                        else if (x->cpu_fraction < y->cpu_fraction)
+                                return 1;
+                } else if (x->cpu_valid)
+                        return -1;
+                else if (y->cpu_valid)
+                        return 1;
+        }
+
+        if (arg_order == ORDER_TASKS) {
+
+                if (x->n_tasks_valid && y->n_tasks_valid) {
+                        if (x->n_tasks > y->n_tasks)
+                                return -1;
+                        else if (x->n_tasks < y->n_tasks)
+                                return 1;
+                } else if (x->n_tasks_valid)
+                        return -1;
+                else if (y->n_tasks_valid)
+                        return 1;
+        }
+
+        if (arg_order == ORDER_MEMORY) {
+                if (x->memory_valid && y->memory_valid) {
+                        if (x->memory > y->memory)
+                                return -1;
+                        else if (x->memory < y->memory)
+                                return 1;
+                } else if (x->memory_valid)
+                        return -1;
+                else if (y->memory_valid)
+                        return 1;
+        }
+
+        if (arg_order == ORDER_IO) {
+                if (x->io_valid && y->io_valid) {
+                        if (x->io_input_bps + x->io_output_bps > y->io_input_bps + y->io_output_bps)
+                                return -1;
+                        else if (x->io_input_bps + x->io_output_bps < y->io_input_bps + y->io_output_bps)
+                                return 1;
+                } else if (x->io_valid)
+                        return -1;
+                else if (y->io_valid)
+                        return 1;
+        }
+
+        return strcmp(x->path, y->path);
+}
+
+static int display(Hashmap *a) {
+        Iterator i;
+        Group *g;
+        Group **array;
+        unsigned rows, n = 0, j;
+
+        assert(a);
+
+        /* Set cursor to top left corner and clear screen */
+        fputs("\033[H"
+              "\033[2J", stdout);
+
+        array = alloca(sizeof(Group*) * hashmap_size(a));
+
+        HASHMAP_FOREACH(g, a, i)
+                if (g->n_tasks_valid || g->cpu_valid || g->memory_valid || g->io_valid)
+                        array[n++] = g;
+
+        qsort(array, n, sizeof(Group*), group_compare);
+
+        rows = fd_lines(STDOUT_FILENO);
+        if (rows <= 0)
+                rows = 25;
+
+        printf("%s%-37s%s %s%7s%s %s%6s%s %s%8s%s %s%8s%s %s%8s%s\n\n",
+               arg_order == ORDER_PATH   ? ANSI_HIGHLIGHT_ON : "", "Path",     arg_order == ORDER_PATH   ? ANSI_HIGHLIGHT_OFF : "",
+               arg_order == ORDER_TASKS  ? ANSI_HIGHLIGHT_ON : "", "Tasks",    arg_order == ORDER_TASKS  ? ANSI_HIGHLIGHT_OFF : "",
+               arg_order == ORDER_CPU    ? ANSI_HIGHLIGHT_ON : "", "%CPU",     arg_order == ORDER_CPU    ? ANSI_HIGHLIGHT_OFF : "",
+               arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_ON : "", "Memory",   arg_order == ORDER_MEMORY ? ANSI_HIGHLIGHT_OFF : "",
+               arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_ON : "", "Input/s",  arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_OFF : "",
+               arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_ON : "", "Output/s", arg_order == ORDER_IO     ? ANSI_HIGHLIGHT_OFF : "");
+
+        for (j = 0; j < n; j++) {
+                char *p;
+                char m[FORMAT_BYTES_MAX];
+
+                if (j + 5 > rows)
+                        break;
+
+                g = array[j];
+
+                p = ellipsize(g->path, 37, 33);
+                printf("%-37s", p ? p : g->path);
+                free(p);
+
+                if (g->n_tasks_valid)
+                        printf(" %7u", g->n_tasks);
+                else
+                        fputs("       -", stdout);
+
+                if (g->cpu_valid)
+                        printf(" %6.1f", g->cpu_fraction*100);
+                else
+                        fputs("      -", stdout);
+
+                if (g->memory_valid)
+                        printf(" %8s", format_bytes(m, sizeof(m), g->memory));
+                else
+                        fputs("        -", stdout);
+
+                if (g->io_valid) {
+                        printf(" %8s",
+                               format_bytes(m, sizeof(m), g->io_input_bps));
+                        printf(" %8s",
+                               format_bytes(m, sizeof(m), g->io_output_bps));
+                } else
+                        fputs("        -        -", stdout);
+
+                putchar('\n');
+        }
+
+        return 0;
+}
+
+static void help(void) {
+
+        printf("%s [OPTIONS...]\n\n"
+               "Show top control groups by their resource usage.\n\n"
+               "  -h --help           Show this help\n"
+               "  -p                  Order by path\n"
+               "  -t                  Order by number of tasks\n"
+               "  -c                  Order by CPU load\n"
+               "  -m                  Order by memory load\n"
+               "  -i                  Order by IO load\n"
+               "  -d --delay=DELAY    Specify delay\n"
+               "     --depth=DEPTH    Maximum traversal depth (default: 2)\n",
+               program_invocation_short_name);
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_DEPTH = 0x100
+        };
+
+        static const struct option options[] = {
+                { "help",  no_argument,       NULL, 'h'       },
+                { "delay", required_argument, NULL, 'd'       },
+                { "depth", required_argument, NULL, ARG_DEPTH },
+                { NULL,    0,                 NULL, 0         }
+        };
+
+        int c;
+        int r;
+
+        assert(argc >= 1);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "hptcmid:", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_DEPTH:
+                        r = safe_atou(optarg, &arg_depth);
+                        if (r < 0) {
+                                log_error("Failed to parse depth parameter.");
+                                return -EINVAL;
+                        }
+
+                        break;
+
+                case 'd':
+                        r = parse_usec(optarg, &arg_delay);
+                        if (r < 0 || arg_delay <= 0) {
+                                log_error("Failed to parse delay parameter.");
+                                return -EINVAL;
+                        }
+
+                        break;
+
+                case 'p':
+                        arg_order = ORDER_PATH;
+                        break;
+
+                case 't':
+                        arg_order = ORDER_TASKS;
+                        break;
+
+                case 'c':
+                        arg_order = ORDER_CPU;
+                        break;
+
+                case 'm':
+                        arg_order = ORDER_MEMORY;
+                        break;
+
+                case 'i':
+                        arg_order = ORDER_IO;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind < argc) {
+                log_error("Too many arguments.");
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+        Hashmap *a = NULL, *b = NULL;
+        unsigned iteration = 0;
+        usec_t last_refresh = 0;
+        bool quit = false, immediate_refresh = false;
+
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                goto finish;
+
+        a = hashmap_new(string_hash_func, string_compare_func);
+        b = hashmap_new(string_hash_func, string_compare_func);
+        if (!a || !b) {
+                log_error("Out of memory");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        while (!quit) {
+                Hashmap *c;
+                usec_t t;
+                char key;
+                char h[FORMAT_TIMESPAN_MAX];
+
+                t = now(CLOCK_MONOTONIC);
+
+                if (t >= last_refresh + arg_delay || immediate_refresh) {
+
+                        r = refresh(a, b, iteration++);
+                        if (r < 0)
+                                goto finish;
+
+                        group_hashmap_clear(b);
+
+                        c = a;
+                        a = b;
+                        b = c;
+
+                        last_refresh = t;
+                        immediate_refresh = false;
+                }
+
+                r = display(b);
+                if (r < 0)
+                        goto finish;
+
+                r = read_one_char(stdin, &key, last_refresh + arg_delay - t, NULL);
+                if (r == -ETIMEDOUT)
+                        continue;
+                if (r < 0) {
+                        log_error("Couldn't read key: %s", strerror(-r));
+                        goto finish;
+                }
+
+                fputs("\r \r", stdout);
+                fflush(stdout);
+
+                switch (key) {
+
+                case ' ':
+                        immediate_refresh = true;
+                        break;
+
+                case 'q':
+                        quit = true;
+                        break;
+
+                case 'p':
+                        arg_order = ORDER_PATH;
+                        break;
+
+                case 't':
+                        arg_order = ORDER_TASKS;
+                        break;
+
+                case 'c':
+                        arg_order = ORDER_CPU;
+                        break;
+
+                case 'm':
+                        arg_order = ORDER_MEMORY;
+                        break;
+
+                case 'i':
+                        arg_order = ORDER_IO;
+                        break;
+
+                case '+':
+                        if (arg_delay < USEC_PER_SEC)
+                                arg_delay += USEC_PER_MSEC*250;
+                        else
+                                arg_delay += USEC_PER_SEC;
+
+                        fprintf(stdout, "\nIncreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
+                        fflush(stdout);
+                        sleep(1);
+                        break;
+
+                case '-':
+                        if (arg_delay <= USEC_PER_MSEC*500)
+                                arg_delay = USEC_PER_MSEC*250;
+                        else if (arg_delay < USEC_PER_MSEC*1250)
+                                arg_delay -= USEC_PER_MSEC*250;
+                        else
+                                arg_delay -= USEC_PER_SEC;
+
+                        fprintf(stdout, "\nDecreased delay to %s.", format_timespan(h, sizeof(h), arg_delay));
+                        fflush(stdout);
+                        sleep(1);
+                        break;
+
+                case '?':
+                case 'h':
+                        fprintf(stdout,
+                                "\t<" ANSI_HIGHLIGHT_ON "P" ANSI_HIGHLIGHT_OFF "> By path; <" ANSI_HIGHLIGHT_ON "T" ANSI_HIGHLIGHT_OFF "> By tasks; <" ANSI_HIGHLIGHT_ON "C" ANSI_HIGHLIGHT_OFF "> By CPU; <" ANSI_HIGHLIGHT_ON "M" ANSI_HIGHLIGHT_OFF "> By memory; <" ANSI_HIGHLIGHT_ON "I" ANSI_HIGHLIGHT_OFF "> By I/O\n"
+                                "\t<" ANSI_HIGHLIGHT_ON "Q" ANSI_HIGHLIGHT_OFF "> Quit; <" ANSI_HIGHLIGHT_ON "+" ANSI_HIGHLIGHT_OFF "> Increase delay; <" ANSI_HIGHLIGHT_ON "-" ANSI_HIGHLIGHT_OFF "> Decrease delay; <" ANSI_HIGHLIGHT_ON "SPACE" ANSI_HIGHLIGHT_OFF "> Refresh");
+                        fflush(stdout);
+                        sleep(3);
+                        break;
+
+                default:
+                        fprintf(stdout, "\nUnknown key '%c'. Ignoring.", key);
+                        fflush(stdout);
+                        sleep(1);
+                        break;
+                }
+        }
+
+        log_info("Exiting.");
+
+        r = 0;
+
+finish:
+        group_hashmap_free(a);
+        group_hashmap_free(b);
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/detect-virt.c b/src/detect-virt.c
deleted file mode 100644
index a83fb3c..0000000
--- a/src/detect-virt.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <getopt.h>
-
-#include "util.h"
-#include "virt.h"
-#include "build.h"
-
-static bool arg_quiet = false;
-static enum {
-        ANY_VIRTUALIZATION,
-        ONLY_VM,
-        ONLY_CONTAINER
-} arg_mode = ANY_VIRTUALIZATION;
-
-static int help(void) {
-
-        printf("%s [OPTIONS...]\n\n"
-               "Detect execution in a virtualized environment.\n\n"
-               "  -h --help             Show this help\n"
-               "     --version          Show package version\n"
-               "  -c --container        Only detect whether we are run in a container\n"
-               "  -v --vm               Only detect whether we are run in a VM\n"
-               "  -q --quiet            Don't output anything, just set return value\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_VERSION = 0x100
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'           },
-                { "version",   no_argument,       NULL, ARG_VERSION   },
-                { "container", no_argument,       NULL, 'c'           },
-                { "vm",        optional_argument, NULL, 'v'           },
-                { "quiet",     required_argument, NULL, 'q'           },
-                { NULL,        0,                 NULL, 0             }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "hqcv", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_VERSION:
-                        puts(PACKAGE_STRING);
-                        puts(DISTRIBUTION);
-                        puts(SYSTEMD_FEATURES);
-                        return 0;
-
-                case 'q':
-                        arg_quiet = true;
-                        break;
-
-                case 'c':
-                        arg_mode = ONLY_CONTAINER;
-                        break;
-
-                case 'v':
-                        arg_mode = ONLY_VM;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind < argc) {
-                help();
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-int main(int argc, char *argv[]) {
-        const char *id = NULL;
-        int retval, r;
-
-        /* This is mostly intended to be used for scripts which want
-         * to detect whether we are being run in a virtualized
-         * environment or not */
-
-        log_parse_environment();
-        log_open();
-
-        r = parse_argv(argc, argv);
-        if (r <= 0)
-                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
-        switch (arg_mode) {
-
-        case ANY_VIRTUALIZATION: {
-                Virtualization v;
-
-                v = detect_virtualization(&id);
-                if (v < 0) {
-                        log_error("Failed to check for virtualization: %s", strerror(-v));
-                        return EXIT_FAILURE;
-                }
-
-                retval = v != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE;
-                break;
-        }
-
-        case ONLY_CONTAINER:
-                r = detect_container(&id);
-                if (r < 0) {
-                        log_error("Failed to check for container: %s", strerror(-r));
-                        return EXIT_FAILURE;
-                }
-
-                retval = r > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-                break;
-
-        case ONLY_VM:
-                r = detect_vm(&id);
-                if (r < 0) {
-                        log_error("Failed to check for vm: %s", strerror(-r));
-                        return EXIT_FAILURE;
-                }
-
-                retval = r > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-                break;
-        }
-
-        if (!arg_quiet)
-                puts(id ? id : "none");
-
-        return retval;
-}
diff --git a/src/detect-virt/detect-virt.c b/src/detect-virt/detect-virt.c
new file mode 100644
index 0000000..a83fb3c
--- /dev/null
+++ b/src/detect-virt/detect-virt.c
@@ -0,0 +1,171 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+
+#include "util.h"
+#include "virt.h"
+#include "build.h"
+
+static bool arg_quiet = false;
+static enum {
+        ANY_VIRTUALIZATION,
+        ONLY_VM,
+        ONLY_CONTAINER
+} arg_mode = ANY_VIRTUALIZATION;
+
+static int help(void) {
+
+        printf("%s [OPTIONS...]\n\n"
+               "Detect execution in a virtualized environment.\n\n"
+               "  -h --help             Show this help\n"
+               "     --version          Show package version\n"
+               "  -c --container        Only detect whether we are run in a container\n"
+               "  -v --vm               Only detect whether we are run in a VM\n"
+               "  -q --quiet            Don't output anything, just set return value\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_VERSION = 0x100
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { "version",   no_argument,       NULL, ARG_VERSION   },
+                { "container", no_argument,       NULL, 'c'           },
+                { "vm",        optional_argument, NULL, 'v'           },
+                { "quiet",     required_argument, NULL, 'q'           },
+                { NULL,        0,                 NULL, 0             }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "hqcv", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_VERSION:
+                        puts(PACKAGE_STRING);
+                        puts(DISTRIBUTION);
+                        puts(SYSTEMD_FEATURES);
+                        return 0;
+
+                case 'q':
+                        arg_quiet = true;
+                        break;
+
+                case 'c':
+                        arg_mode = ONLY_CONTAINER;
+                        break;
+
+                case 'v':
+                        arg_mode = ONLY_VM;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind < argc) {
+                help();
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        const char *id = NULL;
+        int retval, r;
+
+        /* This is mostly intended to be used for scripts which want
+         * to detect whether we are being run in a virtualized
+         * environment or not */
+
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+        switch (arg_mode) {
+
+        case ANY_VIRTUALIZATION: {
+                Virtualization v;
+
+                v = detect_virtualization(&id);
+                if (v < 0) {
+                        log_error("Failed to check for virtualization: %s", strerror(-v));
+                        return EXIT_FAILURE;
+                }
+
+                retval = v != VIRTUALIZATION_NONE ? EXIT_SUCCESS : EXIT_FAILURE;
+                break;
+        }
+
+        case ONLY_CONTAINER:
+                r = detect_container(&id);
+                if (r < 0) {
+                        log_error("Failed to check for container: %s", strerror(-r));
+                        return EXIT_FAILURE;
+                }
+
+                retval = r > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+                break;
+
+        case ONLY_VM:
+                r = detect_vm(&id);
+                if (r < 0) {
+                        log_error("Failed to check for vm: %s", strerror(-r));
+                        return EXIT_FAILURE;
+                }
+
+                retval = r > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+                break;
+        }
+
+        if (!arg_quiet)
+                puts(id ? id : "none");
+
+        return retval;
+}
diff --git a/src/fsck.c b/src/fsck.c
deleted file mode 100644
index f25ec49..0000000
--- a/src/fsck.c
+++ /dev/null
@@ -1,406 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/file.h>
-
-#include <libudev.h>
-#include <dbus/dbus.h>
-
-#include "util.h"
-#include "dbus-common.h"
-#include "special.h"
-#include "bus-errors.h"
-#include "virt.h"
-
-static bool arg_skip = false;
-static bool arg_force = false;
-static bool arg_show_progress = false;
-
-static void start_target(const char *target, bool isolate) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        const char *mode, *basic_target = "basic.target";
-        DBusConnection *bus = NULL;
-
-        assert(target);
-
-        dbus_error_init(&error);
-
-        if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) {
-                log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
-                goto finish;
-        }
-
-        if (isolate)
-                mode = "isolate";
-        else
-                mode = "replace";
-
-        log_info("Running request %s/start/%s", target, mode);
-
-        if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
-                log_error("Could not allocate message.");
-                goto finish;
-        }
-
-        /* Start these units only if we can replace base.target with it */
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &basic_target,
-                                      DBUS_TYPE_STRING, &target,
-                                      DBUS_TYPE_STRING, &mode,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not attach target and flag information to message.");
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
-                /* Don't print a warning if we aren't called during
-                 * startup */
-                if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
-                        log_error("Failed to start unit: %s", bus_error_message(&error));
-
-                goto finish;
-        }
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        if (bus) {
-                dbus_connection_flush(bus);
-                dbus_connection_close(bus);
-                dbus_connection_unref(bus);
-        }
-
-        dbus_error_free(&error);
-}
-
-static int parse_proc_cmdline(void) {
-        char *line, *w, *state;
-        int r;
-        size_t l;
-
-        if (detect_container(NULL) > 0)
-                return 0;
-
-        if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
-                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
-                return 0;
-        }
-
-        FOREACH_WORD_QUOTED(w, l, line, state) {
-
-                if (strneq(w, "fsck.mode=auto", l))
-                        arg_force = arg_skip = false;
-                else if (strneq(w, "fsck.mode=force", l))
-                        arg_force = true;
-                else if (strneq(w, "fsck.mode=skip", l))
-                        arg_skip = true;
-                else if (startswith(w, "fsck.mode"))
-                        log_warning("Invalid fsck.mode= parameter. Ignoring.");
-#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-                else if (strneq(w, "fastboot", l))
-                        arg_skip = true;
-                else if (strneq(w, "forcefsck", l))
-                        arg_force = true;
-#endif
-        }
-
-        free(line);
-        return 0;
-}
-
-static void test_files(void) {
-        if (access("/fastboot", F_OK) >= 0)
-                arg_skip = true;
-
-        if (access("/forcefsck", F_OK) >= 0)
-                arg_force = true;
-
-        if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
-                arg_show_progress = true;
-}
-
-static double percent(int pass, unsigned long cur, unsigned long max) {
-        /* Values stolen from e2fsck */
-
-        static const int pass_table[] = {
-                0, 70, 90, 92, 95, 100
-        };
-
-        if (pass <= 0)
-                return 0.0;
-
-        if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
-                return 100.0;
-
-        return (double) pass_table[pass-1] +
-                ((double) pass_table[pass] - (double) pass_table[pass-1]) *
-                (double) cur / (double) max;
-}
-
-static int process_progress(int fd) {
-        FILE *f, *console;
-        usec_t last = 0;
-        bool locked = false;
-        int clear = 0;
-
-        f = fdopen(fd, "r");
-        if (!f) {
-                close_nointr_nofail(fd);
-                return -errno;
-        }
-
-        console = fopen("/dev/console", "w");
-        if (!console) {
-                fclose(f);
-                return -ENOMEM;
-        }
-
-        while (!feof(f)) {
-                int pass, m;
-                unsigned long cur, max;
-                char *device;
-                double p;
-                usec_t t;
-
-                if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
-                        break;
-
-                /* Only show one progress counter at max */
-                if (!locked) {
-                        if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
-                                free(device);
-                                continue;
-                        }
-
-                        locked = true;
-                }
-
-                /* Only update once every 50ms */
-                t = now(CLOCK_MONOTONIC);
-                if (last + 50 * USEC_PER_MSEC > t)  {
-                        free(device);
-                        continue;
-                }
-
-                last = t;
-
-                p = percent(pass, cur, max);
-                fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
-                fflush(console);
-
-                free(device);
-
-                if (m > clear)
-                        clear = m;
-        }
-
-        if (clear > 0) {
-                unsigned j;
-
-                fputc('\r', console);
-                for (j = 0; j < (unsigned) clear; j++)
-                        fputc(' ', console);
-                fputc('\r', console);
-                fflush(console);
-        }
-
-        fclose(f);
-        fclose(console);
-        return 0;
-}
-
-int main(int argc, char *argv[]) {
-        const char *cmdline[9];
-        int i = 0, r = EXIT_FAILURE, q;
-        pid_t pid;
-        siginfo_t status;
-        struct udev *udev = NULL;
-        struct udev_device *udev_device = NULL;
-        const char *device;
-        bool root_directory;
-        int progress_pipe[2] = { -1, -1 };
-        char dash_c[2+10+1];
-
-        if (argc > 2) {
-                log_error("This program expects one or no arguments.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        parse_proc_cmdline();
-        test_files();
-
-        if (!arg_force && arg_skip)
-                return 0;
-
-        if (argc > 1) {
-                device = argv[1];
-                root_directory = false;
-        } else {
-                struct stat st;
-                struct timespec times[2];
-
-                /* Find root device */
-
-                if (stat("/", &st) < 0) {
-                        log_error("Failed to stat() the root directory: %m");
-                        goto finish;
-                }
-
-                /* Virtual root devices don't need an fsck */
-                if (major(st.st_dev) == 0)
-                        return 0;
-
-                /* check if we are already writable */
-                times[0] = st.st_atim;
-                times[1] = st.st_mtim;
-                if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
-                        log_info("Root directory is writable, skipping check.");
-                        return 0;
-                }
-
-                if (!(udev = udev_new())) {
-                        log_error("Out of memory");
-                        goto finish;
-                }
-
-                if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
-                        log_error("Failed to detect root device.");
-                        goto finish;
-                }
-
-                if (!(device = udev_device_get_devnode(udev_device))) {
-                        log_error("Failed to detect device node of root directory.");
-                        goto finish;
-                }
-
-                root_directory = true;
-        }
-
-        if (arg_show_progress)
-                if (pipe(progress_pipe) < 0) {
-                        log_error("pipe(): %m");
-                        goto finish;
-                }
-
-        cmdline[i++] = "/sbin/fsck";
-        cmdline[i++] = "-a";
-        cmdline[i++] = "-T";
-        cmdline[i++] = "-l";
-
-        if (!root_directory)
-                cmdline[i++] = "-M";
-
-        if (arg_force)
-                cmdline[i++] = "-f";
-
-        if (progress_pipe[1] >= 0) {
-                snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
-                char_array_0(dash_c);
-                cmdline[i++] = dash_c;
-        }
-
-        cmdline[i++] = device;
-        cmdline[i++] = NULL;
-
-        pid = fork();
-        if (pid < 0) {
-                log_error("fork(): %m");
-                goto finish;
-        } else if (pid == 0) {
-                /* Child */
-                if (progress_pipe[0] >= 0)
-                        close_nointr_nofail(progress_pipe[0]);
-                execv(cmdline[0], (char**) cmdline);
-                _exit(8); /* Operational error */
-        }
-
-        if (progress_pipe[1] >= 0) {
-                close_nointr_nofail(progress_pipe[1]);
-                progress_pipe[1] = -1;
-        }
-
-        if (progress_pipe[0] >= 0) {
-                process_progress(progress_pipe[0]);
-                progress_pipe[0] = -1;
-        }
-
-        q = wait_for_terminate(pid, &status);
-        if (q < 0) {
-                log_error("waitid(): %s", strerror(-q));
-                goto finish;
-        }
-
-        if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
-
-                if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
-                        log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
-                else if (status.si_code == CLD_EXITED)
-                        log_error("fsck failed with error code %i.", status.si_status);
-                else
-                        log_error("fsck failed due to unknown reason.");
-
-                if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
-                        /* System should be rebooted. */
-                        start_target(SPECIAL_REBOOT_TARGET, false);
-                else if (status.si_code == CLD_EXITED && (status.si_status & 6))
-                        /* Some other problem */
-                        start_target(SPECIAL_EMERGENCY_TARGET, true);
-                else {
-                        r = EXIT_SUCCESS;
-                        log_warning("Ignoring error.");
-                }
-
-        } else
-                r = EXIT_SUCCESS;
-
-        if (status.si_code == CLD_EXITED && (status.si_status & 1))
-                touch("/run/systemd/quotacheck");
-
-finish:
-        if (udev_device)
-                udev_device_unref(udev_device);
-
-        if (udev)
-                udev_unref(udev);
-
-        close_pipe(progress_pipe);
-
-        return r;
-}
diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c
new file mode 100644
index 0000000..f25ec49
--- /dev/null
+++ b/src/fsck/fsck.c
@@ -0,0 +1,406 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/file.h>
+
+#include <libudev.h>
+#include <dbus/dbus.h>
+
+#include "util.h"
+#include "dbus-common.h"
+#include "special.h"
+#include "bus-errors.h"
+#include "virt.h"
+
+static bool arg_skip = false;
+static bool arg_force = false;
+static bool arg_show_progress = false;
+
+static void start_target(const char *target, bool isolate) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        const char *mode, *basic_target = "basic.target";
+        DBusConnection *bus = NULL;
+
+        assert(target);
+
+        dbus_error_init(&error);
+
+        if (bus_connect(DBUS_BUS_SYSTEM, &bus, NULL, &error) < 0) {
+                log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
+                goto finish;
+        }
+
+        if (isolate)
+                mode = "isolate";
+        else
+                mode = "replace";
+
+        log_info("Running request %s/start/%s", target, mode);
+
+        if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnitReplace"))) {
+                log_error("Could not allocate message.");
+                goto finish;
+        }
+
+        /* Start these units only if we can replace base.target with it */
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &basic_target,
+                                      DBUS_TYPE_STRING, &target,
+                                      DBUS_TYPE_STRING, &mode,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not attach target and flag information to message.");
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+                /* Don't print a warning if we aren't called during
+                 * startup */
+                if (!dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
+                        log_error("Failed to start unit: %s", bus_error_message(&error));
+
+                goto finish;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        if (bus) {
+                dbus_connection_flush(bus);
+                dbus_connection_close(bus);
+                dbus_connection_unref(bus);
+        }
+
+        dbus_error_free(&error);
+}
+
+static int parse_proc_cmdline(void) {
+        char *line, *w, *state;
+        int r;
+        size_t l;
+
+        if (detect_container(NULL) > 0)
+                return 0;
+
+        if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
+                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
+                return 0;
+        }
+
+        FOREACH_WORD_QUOTED(w, l, line, state) {
+
+                if (strneq(w, "fsck.mode=auto", l))
+                        arg_force = arg_skip = false;
+                else if (strneq(w, "fsck.mode=force", l))
+                        arg_force = true;
+                else if (strneq(w, "fsck.mode=skip", l))
+                        arg_skip = true;
+                else if (startswith(w, "fsck.mode"))
+                        log_warning("Invalid fsck.mode= parameter. Ignoring.");
+#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+                else if (strneq(w, "fastboot", l))
+                        arg_skip = true;
+                else if (strneq(w, "forcefsck", l))
+                        arg_force = true;
+#endif
+        }
+
+        free(line);
+        return 0;
+}
+
+static void test_files(void) {
+        if (access("/fastboot", F_OK) >= 0)
+                arg_skip = true;
+
+        if (access("/forcefsck", F_OK) >= 0)
+                arg_force = true;
+
+        if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
+                arg_show_progress = true;
+}
+
+static double percent(int pass, unsigned long cur, unsigned long max) {
+        /* Values stolen from e2fsck */
+
+        static const int pass_table[] = {
+                0, 70, 90, 92, 95, 100
+        };
+
+        if (pass <= 0)
+                return 0.0;
+
+        if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
+                return 100.0;
+
+        return (double) pass_table[pass-1] +
+                ((double) pass_table[pass] - (double) pass_table[pass-1]) *
+                (double) cur / (double) max;
+}
+
+static int process_progress(int fd) {
+        FILE *f, *console;
+        usec_t last = 0;
+        bool locked = false;
+        int clear = 0;
+
+        f = fdopen(fd, "r");
+        if (!f) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        console = fopen("/dev/console", "w");
+        if (!console) {
+                fclose(f);
+                return -ENOMEM;
+        }
+
+        while (!feof(f)) {
+                int pass, m;
+                unsigned long cur, max;
+                char *device;
+                double p;
+                usec_t t;
+
+                if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
+                        break;
+
+                /* Only show one progress counter at max */
+                if (!locked) {
+                        if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0) {
+                                free(device);
+                                continue;
+                        }
+
+                        locked = true;
+                }
+
+                /* Only update once every 50ms */
+                t = now(CLOCK_MONOTONIC);
+                if (last + 50 * USEC_PER_MSEC > t)  {
+                        free(device);
+                        continue;
+                }
+
+                last = t;
+
+                p = percent(pass, cur, max);
+                fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
+                fflush(console);
+
+                free(device);
+
+                if (m > clear)
+                        clear = m;
+        }
+
+        if (clear > 0) {
+                unsigned j;
+
+                fputc('\r', console);
+                for (j = 0; j < (unsigned) clear; j++)
+                        fputc(' ', console);
+                fputc('\r', console);
+                fflush(console);
+        }
+
+        fclose(f);
+        fclose(console);
+        return 0;
+}
+
+int main(int argc, char *argv[]) {
+        const char *cmdline[9];
+        int i = 0, r = EXIT_FAILURE, q;
+        pid_t pid;
+        siginfo_t status;
+        struct udev *udev = NULL;
+        struct udev_device *udev_device = NULL;
+        const char *device;
+        bool root_directory;
+        int progress_pipe[2] = { -1, -1 };
+        char dash_c[2+10+1];
+
+        if (argc > 2) {
+                log_error("This program expects one or no arguments.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        parse_proc_cmdline();
+        test_files();
+
+        if (!arg_force && arg_skip)
+                return 0;
+
+        if (argc > 1) {
+                device = argv[1];
+                root_directory = false;
+        } else {
+                struct stat st;
+                struct timespec times[2];
+
+                /* Find root device */
+
+                if (stat("/", &st) < 0) {
+                        log_error("Failed to stat() the root directory: %m");
+                        goto finish;
+                }
+
+                /* Virtual root devices don't need an fsck */
+                if (major(st.st_dev) == 0)
+                        return 0;
+
+                /* check if we are already writable */
+                times[0] = st.st_atim;
+                times[1] = st.st_mtim;
+                if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
+                        log_info("Root directory is writable, skipping check.");
+                        return 0;
+                }
+
+                if (!(udev = udev_new())) {
+                        log_error("Out of memory");
+                        goto finish;
+                }
+
+                if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev))) {
+                        log_error("Failed to detect root device.");
+                        goto finish;
+                }
+
+                if (!(device = udev_device_get_devnode(udev_device))) {
+                        log_error("Failed to detect device node of root directory.");
+                        goto finish;
+                }
+
+                root_directory = true;
+        }
+
+        if (arg_show_progress)
+                if (pipe(progress_pipe) < 0) {
+                        log_error("pipe(): %m");
+                        goto finish;
+                }
+
+        cmdline[i++] = "/sbin/fsck";
+        cmdline[i++] = "-a";
+        cmdline[i++] = "-T";
+        cmdline[i++] = "-l";
+
+        if (!root_directory)
+                cmdline[i++] = "-M";
+
+        if (arg_force)
+                cmdline[i++] = "-f";
+
+        if (progress_pipe[1] >= 0) {
+                snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
+                char_array_0(dash_c);
+                cmdline[i++] = dash_c;
+        }
+
+        cmdline[i++] = device;
+        cmdline[i++] = NULL;
+
+        pid = fork();
+        if (pid < 0) {
+                log_error("fork(): %m");
+                goto finish;
+        } else if (pid == 0) {
+                /* Child */
+                if (progress_pipe[0] >= 0)
+                        close_nointr_nofail(progress_pipe[0]);
+                execv(cmdline[0], (char**) cmdline);
+                _exit(8); /* Operational error */
+        }
+
+        if (progress_pipe[1] >= 0) {
+                close_nointr_nofail(progress_pipe[1]);
+                progress_pipe[1] = -1;
+        }
+
+        if (progress_pipe[0] >= 0) {
+                process_progress(progress_pipe[0]);
+                progress_pipe[0] = -1;
+        }
+
+        q = wait_for_terminate(pid, &status);
+        if (q < 0) {
+                log_error("waitid(): %s", strerror(-q));
+                goto finish;
+        }
+
+        if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
+
+                if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
+                        log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
+                else if (status.si_code == CLD_EXITED)
+                        log_error("fsck failed with error code %i.", status.si_status);
+                else
+                        log_error("fsck failed due to unknown reason.");
+
+                if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
+                        /* System should be rebooted. */
+                        start_target(SPECIAL_REBOOT_TARGET, false);
+                else if (status.si_code == CLD_EXITED && (status.si_status & 6))
+                        /* Some other problem */
+                        start_target(SPECIAL_EMERGENCY_TARGET, true);
+                else {
+                        r = EXIT_SUCCESS;
+                        log_warning("Ignoring error.");
+                }
+
+        } else
+                r = EXIT_SUCCESS;
+
+        if (status.si_code == CLD_EXITED && (status.si_status & 1))
+                touch("/run/systemd/quotacheck");
+
+finish:
+        if (udev_device)
+                udev_device_unref(udev_device);
+
+        if (udev)
+                udev_unref(udev);
+
+        close_pipe(progress_pipe);
+
+        return r;
+}
diff --git a/src/getty-generator.c b/src/getty-generator.c
deleted file mode 100644
index 58ad8a6..0000000
--- a/src/getty-generator.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <string.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "unit-name.h"
-#include "virt.h"
-
-static const char *arg_dest = "/tmp";
-
-static int add_symlink(const char *fservice, const char *tservice) {
-        char *from = NULL, *to = NULL;
-        int r;
-
-        assert(fservice);
-        assert(tservice);
-
-        asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", fservice);
-        asprintf(&to, "%s/getty.target.wants/%s", arg_dest, tservice);
-
-        if (!from || !to) {
-                log_error("Out of memory");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        mkdir_parents(to, 0755);
-
-        r = symlink(from, to);
-        if (r < 0) {
-                if (errno == EEXIST)
-                        /* In case console=hvc0 is passed this will very likely result in EEXIST */
-                        r = 0;
-                else {
-                        log_error("Failed to create symlink from %s to %s: %m", from, to);
-                        r = -errno;
-                }
-        }
-
-finish:
-
-        free(from);
-        free(to);
-
-        return r;
-}
-
-static int add_serial_getty(const char *tty) {
-        char *n;
-        int r;
-
-        assert(tty);
-
-        log_debug("Automatically adding serial getty for /dev/%s.", tty);
-
-        n = unit_name_replace_instance("serial-getty at .service", tty);
-        if (!n) {
-                log_error("Out of memory");
-                return -ENOMEM;
-        }
-
-        r = add_symlink("serial-getty at .service", n);
-        free(n);
-
-        return r;
-}
-
-int main(int argc, char *argv[]) {
-
-        static const char virtualization_consoles[] =
-                "hvc0\0"
-                "xvc0\0"
-                "hvsi0\0";
-
-        int r = EXIT_SUCCESS;
-        char *active;
-        const char *j;
-
-        if (argc > 2) {
-                log_error("This program takes one or no arguments.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        if (argc > 1)
-            arg_dest = argv[1];
-
-        if (detect_container(NULL) > 0) {
-                log_debug("Automatically adding console shell.");
-
-                if (add_symlink("console-shell.service", "console-shell.service") < 0)
-                        r = EXIT_FAILURE;
-
-                /* Don't add any further magic if we are in a container */
-                goto finish;
-        }
-
-        if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
-                const char *tty;
-
-                tty = strrchr(active, ' ');
-                if (tty)
-                        tty ++;
-                else
-                        tty = active;
-
-                /* Automatically add in a serial getty on the kernel
-                 * console */
-                if (tty_is_vc(tty))
-                        free(active);
-                else {
-                        int k;
-
-                        /* We assume that gettys on virtual terminals are
-                         * started via manual configuration and do this magic
-                         * only for non-VC terminals. */
-
-                        k = add_serial_getty(tty);
-                        free(active);
-
-                        if (k < 0) {
-                                r = EXIT_FAILURE;
-                                goto finish;
-                        }
-                }
-        }
-
-        /* Automatically add in a serial getty on the first
-         * virtualizer console */
-        NULSTR_FOREACH(j, virtualization_consoles) {
-                char *p;
-                int k;
-
-                if (asprintf(&p, "/sys/class/tty/%s", j) < 0) {
-                        log_error("Out of memory");
-                        r = EXIT_FAILURE;
-                        goto finish;
-                }
-
-                k = access(p, F_OK);
-                free(p);
-
-                if (k < 0)
-                        continue;
-
-                k = add_serial_getty(j);
-                if (k < 0) {
-                        r = EXIT_FAILURE;
-                        goto finish;
-                }
-        }
-
-finish:
-        return r;
-}
diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c
new file mode 100644
index 0000000..58ad8a6
--- /dev/null
+++ b/src/getty-generator/getty-generator.c
@@ -0,0 +1,183 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+#include "unit-name.h"
+#include "virt.h"
+
+static const char *arg_dest = "/tmp";
+
+static int add_symlink(const char *fservice, const char *tservice) {
+        char *from = NULL, *to = NULL;
+        int r;
+
+        assert(fservice);
+        assert(tservice);
+
+        asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", fservice);
+        asprintf(&to, "%s/getty.target.wants/%s", arg_dest, tservice);
+
+        if (!from || !to) {
+                log_error("Out of memory");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        mkdir_parents(to, 0755);
+
+        r = symlink(from, to);
+        if (r < 0) {
+                if (errno == EEXIST)
+                        /* In case console=hvc0 is passed this will very likely result in EEXIST */
+                        r = 0;
+                else {
+                        log_error("Failed to create symlink from %s to %s: %m", from, to);
+                        r = -errno;
+                }
+        }
+
+finish:
+
+        free(from);
+        free(to);
+
+        return r;
+}
+
+static int add_serial_getty(const char *tty) {
+        char *n;
+        int r;
+
+        assert(tty);
+
+        log_debug("Automatically adding serial getty for /dev/%s.", tty);
+
+        n = unit_name_replace_instance("serial-getty at .service", tty);
+        if (!n) {
+                log_error("Out of memory");
+                return -ENOMEM;
+        }
+
+        r = add_symlink("serial-getty at .service", n);
+        free(n);
+
+        return r;
+}
+
+int main(int argc, char *argv[]) {
+
+        static const char virtualization_consoles[] =
+                "hvc0\0"
+                "xvc0\0"
+                "hvsi0\0";
+
+        int r = EXIT_SUCCESS;
+        char *active;
+        const char *j;
+
+        if (argc > 2) {
+                log_error("This program takes one or no arguments.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if (argc > 1)
+            arg_dest = argv[1];
+
+        if (detect_container(NULL) > 0) {
+                log_debug("Automatically adding console shell.");
+
+                if (add_symlink("console-shell.service", "console-shell.service") < 0)
+                        r = EXIT_FAILURE;
+
+                /* Don't add any further magic if we are in a container */
+                goto finish;
+        }
+
+        if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
+                const char *tty;
+
+                tty = strrchr(active, ' ');
+                if (tty)
+                        tty ++;
+                else
+                        tty = active;
+
+                /* Automatically add in a serial getty on the kernel
+                 * console */
+                if (tty_is_vc(tty))
+                        free(active);
+                else {
+                        int k;
+
+                        /* We assume that gettys on virtual terminals are
+                         * started via manual configuration and do this magic
+                         * only for non-VC terminals. */
+
+                        k = add_serial_getty(tty);
+                        free(active);
+
+                        if (k < 0) {
+                                r = EXIT_FAILURE;
+                                goto finish;
+                        }
+                }
+        }
+
+        /* Automatically add in a serial getty on the first
+         * virtualizer console */
+        NULSTR_FOREACH(j, virtualization_consoles) {
+                char *p;
+                int k;
+
+                if (asprintf(&p, "/sys/class/tty/%s", j) < 0) {
+                        log_error("Out of memory");
+                        r = EXIT_FAILURE;
+                        goto finish;
+                }
+
+                k = access(p, F_OK);
+                free(p);
+
+                if (k < 0)
+                        continue;
+
+                k = add_serial_getty(j);
+                if (k < 0) {
+                        r = EXIT_FAILURE;
+                        goto finish;
+                }
+        }
+
+finish:
+        return r;
+}
diff --git a/src/initctl.c b/src/initctl.c
deleted file mode 100644
index 0eb008d..0000000
--- a/src/initctl.c
+++ /dev/null
@@ -1,451 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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/socket.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <time.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <sys/poll.h>
-#include <sys/epoll.h>
-#include <sys/un.h>
-#include <fcntl.h>
-#include <ctype.h>
-
-#include <dbus/dbus.h>
-#include <systemd/sd-daemon.h>
-
-#include "util.h"
-#include "log.h"
-#include "list.h"
-#include "initreq.h"
-#include "special.h"
-#include "dbus-common.h"
-#include "def.h"
-
-#define SERVER_FD_MAX 16
-#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
-
-typedef struct Fifo Fifo;
-
-typedef struct Server {
-        int epoll_fd;
-
-        LIST_HEAD(Fifo, fifos);
-        unsigned n_fifos;
-
-        DBusConnection *bus;
-
-        bool quit;
-} Server;
-
-struct Fifo {
-        Server *server;
-
-        int fd;
-
-        struct init_request buffer;
-        size_t bytes_read;
-
-        LIST_FIELDS(Fifo, fifo);
-};
-
-static const char *translate_runlevel(int runlevel, bool *isolate) {
-        static const struct {
-                const int runlevel;
-                const char *special;
-                bool isolate;
-        } table[] = {
-                { '0', SPECIAL_POWEROFF_TARGET,  false },
-                { '1', SPECIAL_RESCUE_TARGET,    true  },
-                { 's', SPECIAL_RESCUE_TARGET,    true  },
-                { 'S', SPECIAL_RESCUE_TARGET,    true  },
-                { '2', SPECIAL_RUNLEVEL2_TARGET, true  },
-                { '3', SPECIAL_RUNLEVEL3_TARGET, true  },
-                { '4', SPECIAL_RUNLEVEL4_TARGET, true  },
-                { '5', SPECIAL_RUNLEVEL5_TARGET, true  },
-                { '6', SPECIAL_REBOOT_TARGET,    false },
-        };
-
-        unsigned i;
-
-        assert(isolate);
-
-        for (i = 0; i < ELEMENTSOF(table); i++)
-                if (table[i].runlevel == runlevel) {
-                        *isolate = table[i].isolate;
-                        if (runlevel == '6' && kexec_loaded())
-                                return SPECIAL_KEXEC_TARGET;
-                        return table[i].special;
-                }
-
-        return NULL;
-}
-
-static void change_runlevel(Server *s, int runlevel) {
-        const char *target;
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        const char *mode;
-        bool isolate = false;
-
-        assert(s);
-
-        dbus_error_init(&error);
-
-        if (!(target = translate_runlevel(runlevel, &isolate))) {
-                log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
-                goto finish;
-        }
-
-        if (isolate)
-                mode = "isolate";
-        else
-                mode = "replace";
-
-        log_debug("Running request %s/start/%s", target, mode);
-
-        if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) {
-                log_error("Could not allocate message.");
-                goto finish;
-        }
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &target,
-                                      DBUS_TYPE_STRING, &mode,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not attach target and flag information to message.");
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
-                log_error("Failed to start unit: %s", bus_error_message(&error));
-                goto finish;
-        }
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-}
-
-static void request_process(Server *s, const struct init_request *req) {
-        assert(s);
-        assert(req);
-
-        if (req->magic != INIT_MAGIC) {
-                log_error("Got initctl request with invalid magic. Ignoring.");
-                return;
-        }
-
-        switch (req->cmd) {
-
-        case INIT_CMD_RUNLVL:
-                if (!isprint(req->runlevel))
-                        log_error("Got invalid runlevel. Ignoring.");
-                else
-                        switch (req->runlevel) {
-
-                        /* we are async anyway, so just use kill for reexec/reload */
-                        case 'u':
-                        case 'U':
-                                if (kill(1, SIGTERM) < 0)
-                                        log_error("kill() failed: %m");
-
-                                /* The bus connection will be
-                                 * terminated if PID 1 is reexecuted,
-                                 * hence let's just exit here, and
-                                 * rely on that we'll be restarted on
-                                 * the next request */
-                                s->quit = true;
-                                break;
-
-                        case 'q':
-                        case 'Q':
-                                if (kill(1, SIGHUP) < 0)
-                                        log_error("kill() failed: %m");
-                                break;
-
-                        default:
-                                change_runlevel(s, req->runlevel);
-                        }
-                return;
-
-        case INIT_CMD_POWERFAIL:
-        case INIT_CMD_POWERFAILNOW:
-        case INIT_CMD_POWEROK:
-                log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
-                return;
-
-        case INIT_CMD_CHANGECONS:
-                log_warning("Received console change initctl request. This is not implemented in systemd.");
-                return;
-
-        case INIT_CMD_SETENV:
-        case INIT_CMD_UNSETENV:
-                log_warning("Received environment initctl request. This is not implemented in systemd.");
-                return;
-
-        default:
-                log_warning("Received unknown initctl request. Ignoring.");
-                return;
-        }
-}
-
-static int fifo_process(Fifo *f) {
-        ssize_t l;
-
-        assert(f);
-
-        errno = EIO;
-        if ((l = read(f->fd, ((uint8_t*) &f->buffer) + f->bytes_read, sizeof(f->buffer) - f->bytes_read)) <= 0) {
-
-                if (errno == EAGAIN)
-                        return 0;
-
-                log_warning("Failed to read from fifo: %s", strerror(errno));
-                return -1;
-        }
-
-        f->bytes_read += l;
-        assert(f->bytes_read <= sizeof(f->buffer));
-
-        if (f->bytes_read == sizeof(f->buffer)) {
-                request_process(f->server, &f->buffer);
-                f->bytes_read = 0;
-        }
-
-        return 0;
-}
-
-static void fifo_free(Fifo *f) {
-        assert(f);
-
-        if (f->server) {
-                assert(f->server->n_fifos > 0);
-                f->server->n_fifos--;
-                LIST_REMOVE(Fifo, fifo, f->server->fifos, f);
-        }
-
-        if (f->fd >= 0) {
-                if (f->server)
-                        epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
-
-                close_nointr_nofail(f->fd);
-        }
-
-        free(f);
-}
-
-static void server_done(Server *s) {
-        assert(s);
-
-        while (s->fifos)
-                fifo_free(s->fifos);
-
-        if (s->epoll_fd >= 0)
-                close_nointr_nofail(s->epoll_fd);
-
-        if (s->bus) {
-                dbus_connection_flush(s->bus);
-                dbus_connection_close(s->bus);
-                dbus_connection_unref(s->bus);
-        }
-}
-
-static int server_init(Server *s, unsigned n_sockets) {
-        int r;
-        unsigned i;
-        DBusError error;
-
-        assert(s);
-        assert(n_sockets > 0);
-
-        dbus_error_init(&error);
-
-        zero(*s);
-
-        if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
-                r = -errno;
-                log_error("Failed to create epoll object: %s", strerror(errno));
-                goto fail;
-        }
-
-        for (i = 0; i < n_sockets; i++) {
-                struct epoll_event ev;
-                Fifo *f;
-                int fd;
-
-                fd = SD_LISTEN_FDS_START+i;
-
-                if ((r = sd_is_fifo(fd, NULL)) < 0) {
-                        log_error("Failed to determine file descriptor type: %s", strerror(-r));
-                        goto fail;
-                }
-
-                if (!r) {
-                        log_error("Wrong file descriptor type.");
-                        r = -EINVAL;
-                        goto fail;
-                }
-
-                if (!(f = new0(Fifo, 1))) {
-                        r = -ENOMEM;
-                        log_error("Failed to create fifo object: %s", strerror(errno));
-                        goto fail;
-                }
-
-                f->fd = -1;
-
-                zero(ev);
-                ev.events = EPOLLIN;
-                ev.data.ptr = f;
-                if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
-                        r = -errno;
-                        fifo_free(f);
-                        log_error("Failed to add fifo fd to epoll object: %s", strerror(errno));
-                        goto fail;
-                }
-
-                f->fd = fd;
-                LIST_PREPEND(Fifo, fifo, s->fifos, f);
-                f->server = s;
-                s->n_fifos ++;
-        }
-
-        if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
-                log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
-                goto fail;
-        }
-
-        return 0;
-
-fail:
-        server_done(s);
-
-        dbus_error_free(&error);
-        return r;
-}
-
-static int process_event(Server *s, struct epoll_event *ev) {
-        int r;
-        Fifo *f;
-
-        assert(s);
-
-        if (!(ev->events & EPOLLIN)) {
-                log_info("Got invalid event from epoll. (3)");
-                return -EIO;
-        }
-
-        f = (Fifo*) ev->data.ptr;
-
-        if ((r = fifo_process(f)) < 0) {
-                log_info("Got error on fifo: %s", strerror(-r));
-                fifo_free(f);
-                return r;
-        }
-
-        return 0;
-}
-
-int main(int argc, char *argv[]) {
-        Server server;
-        int r = EXIT_FAILURE, n;
-
-        if (getppid() != 1) {
-                log_error("This program should be invoked by init only.");
-                return EXIT_FAILURE;
-        }
-
-        if (argc > 1) {
-                log_error("This program does not take arguments.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        if ((n = sd_listen_fds(true)) < 0) {
-                log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
-                return EXIT_FAILURE;
-        }
-
-        if (n <= 0 || n > SERVER_FD_MAX) {
-                log_error("No or too many file descriptors passed.");
-                return EXIT_FAILURE;
-        }
-
-        if (server_init(&server, (unsigned) n) < 0)
-                return EXIT_FAILURE;
-
-        log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
-
-        sd_notify(false,
-                  "READY=1\n"
-                  "STATUS=Processing requests...");
-
-        while (!server.quit) {
-                struct epoll_event event;
-                int k;
-
-                if ((k = epoll_wait(server.epoll_fd,
-                                    &event, 1,
-                                    TIMEOUT_MSEC)) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        log_error("epoll_wait() failed: %s", strerror(errno));
-                        goto fail;
-                }
-
-                if (k <= 0)
-                        break;
-
-                if (process_event(&server, &event) < 0)
-                        goto fail;
-        }
-
-        r = EXIT_SUCCESS;
-
-        log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
-
-fail:
-        sd_notify(false,
-                  "STATUS=Shutting down...");
-
-        server_done(&server);
-
-        dbus_shutdown();
-
-        return r;
-}
diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c
new file mode 100644
index 0000000..0eb008d
--- /dev/null
+++ b/src/initctl/initctl.c
@@ -0,0 +1,451 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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/socket.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <time.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/epoll.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include <dbus/dbus.h>
+#include <systemd/sd-daemon.h>
+
+#include "util.h"
+#include "log.h"
+#include "list.h"
+#include "initreq.h"
+#include "special.h"
+#include "dbus-common.h"
+#include "def.h"
+
+#define SERVER_FD_MAX 16
+#define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC))
+
+typedef struct Fifo Fifo;
+
+typedef struct Server {
+        int epoll_fd;
+
+        LIST_HEAD(Fifo, fifos);
+        unsigned n_fifos;
+
+        DBusConnection *bus;
+
+        bool quit;
+} Server;
+
+struct Fifo {
+        Server *server;
+
+        int fd;
+
+        struct init_request buffer;
+        size_t bytes_read;
+
+        LIST_FIELDS(Fifo, fifo);
+};
+
+static const char *translate_runlevel(int runlevel, bool *isolate) {
+        static const struct {
+                const int runlevel;
+                const char *special;
+                bool isolate;
+        } table[] = {
+                { '0', SPECIAL_POWEROFF_TARGET,  false },
+                { '1', SPECIAL_RESCUE_TARGET,    true  },
+                { 's', SPECIAL_RESCUE_TARGET,    true  },
+                { 'S', SPECIAL_RESCUE_TARGET,    true  },
+                { '2', SPECIAL_RUNLEVEL2_TARGET, true  },
+                { '3', SPECIAL_RUNLEVEL3_TARGET, true  },
+                { '4', SPECIAL_RUNLEVEL4_TARGET, true  },
+                { '5', SPECIAL_RUNLEVEL5_TARGET, true  },
+                { '6', SPECIAL_REBOOT_TARGET,    false },
+        };
+
+        unsigned i;
+
+        assert(isolate);
+
+        for (i = 0; i < ELEMENTSOF(table); i++)
+                if (table[i].runlevel == runlevel) {
+                        *isolate = table[i].isolate;
+                        if (runlevel == '6' && kexec_loaded())
+                                return SPECIAL_KEXEC_TARGET;
+                        return table[i].special;
+                }
+
+        return NULL;
+}
+
+static void change_runlevel(Server *s, int runlevel) {
+        const char *target;
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        const char *mode;
+        bool isolate = false;
+
+        assert(s);
+
+        dbus_error_init(&error);
+
+        if (!(target = translate_runlevel(runlevel, &isolate))) {
+                log_warning("Got request for unknown runlevel %c, ignoring.", runlevel);
+                goto finish;
+        }
+
+        if (isolate)
+                mode = "isolate";
+        else
+                mode = "replace";
+
+        log_debug("Running request %s/start/%s", target, mode);
+
+        if (!(m = dbus_message_new_method_call("org.freedesktop.systemd1", "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartUnit"))) {
+                log_error("Could not allocate message.");
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &target,
+                                      DBUS_TYPE_STRING, &mode,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not attach target and flag information to message.");
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(s->bus, m, -1, &error))) {
+                log_error("Failed to start unit: %s", bus_error_message(&error));
+                goto finish;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+}
+
+static void request_process(Server *s, const struct init_request *req) {
+        assert(s);
+        assert(req);
+
+        if (req->magic != INIT_MAGIC) {
+                log_error("Got initctl request with invalid magic. Ignoring.");
+                return;
+        }
+
+        switch (req->cmd) {
+
+        case INIT_CMD_RUNLVL:
+                if (!isprint(req->runlevel))
+                        log_error("Got invalid runlevel. Ignoring.");
+                else
+                        switch (req->runlevel) {
+
+                        /* we are async anyway, so just use kill for reexec/reload */
+                        case 'u':
+                        case 'U':
+                                if (kill(1, SIGTERM) < 0)
+                                        log_error("kill() failed: %m");
+
+                                /* The bus connection will be
+                                 * terminated if PID 1 is reexecuted,
+                                 * hence let's just exit here, and
+                                 * rely on that we'll be restarted on
+                                 * the next request */
+                                s->quit = true;
+                                break;
+
+                        case 'q':
+                        case 'Q':
+                                if (kill(1, SIGHUP) < 0)
+                                        log_error("kill() failed: %m");
+                                break;
+
+                        default:
+                                change_runlevel(s, req->runlevel);
+                        }
+                return;
+
+        case INIT_CMD_POWERFAIL:
+        case INIT_CMD_POWERFAILNOW:
+        case INIT_CMD_POWEROK:
+                log_warning("Received UPS/power initctl request. This is not implemented in systemd. Upgrade your UPS daemon!");
+                return;
+
+        case INIT_CMD_CHANGECONS:
+                log_warning("Received console change initctl request. This is not implemented in systemd.");
+                return;
+
+        case INIT_CMD_SETENV:
+        case INIT_CMD_UNSETENV:
+                log_warning("Received environment initctl request. This is not implemented in systemd.");
+                return;
+
+        default:
+                log_warning("Received unknown initctl request. Ignoring.");
+                return;
+        }
+}
+
+static int fifo_process(Fifo *f) {
+        ssize_t l;
+
+        assert(f);
+
+        errno = EIO;
+        if ((l = read(f->fd, ((uint8_t*) &f->buffer) + f->bytes_read, sizeof(f->buffer) - f->bytes_read)) <= 0) {
+
+                if (errno == EAGAIN)
+                        return 0;
+
+                log_warning("Failed to read from fifo: %s", strerror(errno));
+                return -1;
+        }
+
+        f->bytes_read += l;
+        assert(f->bytes_read <= sizeof(f->buffer));
+
+        if (f->bytes_read == sizeof(f->buffer)) {
+                request_process(f->server, &f->buffer);
+                f->bytes_read = 0;
+        }
+
+        return 0;
+}
+
+static void fifo_free(Fifo *f) {
+        assert(f);
+
+        if (f->server) {
+                assert(f->server->n_fifos > 0);
+                f->server->n_fifos--;
+                LIST_REMOVE(Fifo, fifo, f->server->fifos, f);
+        }
+
+        if (f->fd >= 0) {
+                if (f->server)
+                        epoll_ctl(f->server->epoll_fd, EPOLL_CTL_DEL, f->fd, NULL);
+
+                close_nointr_nofail(f->fd);
+        }
+
+        free(f);
+}
+
+static void server_done(Server *s) {
+        assert(s);
+
+        while (s->fifos)
+                fifo_free(s->fifos);
+
+        if (s->epoll_fd >= 0)
+                close_nointr_nofail(s->epoll_fd);
+
+        if (s->bus) {
+                dbus_connection_flush(s->bus);
+                dbus_connection_close(s->bus);
+                dbus_connection_unref(s->bus);
+        }
+}
+
+static int server_init(Server *s, unsigned n_sockets) {
+        int r;
+        unsigned i;
+        DBusError error;
+
+        assert(s);
+        assert(n_sockets > 0);
+
+        dbus_error_init(&error);
+
+        zero(*s);
+
+        if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
+                r = -errno;
+                log_error("Failed to create epoll object: %s", strerror(errno));
+                goto fail;
+        }
+
+        for (i = 0; i < n_sockets; i++) {
+                struct epoll_event ev;
+                Fifo *f;
+                int fd;
+
+                fd = SD_LISTEN_FDS_START+i;
+
+                if ((r = sd_is_fifo(fd, NULL)) < 0) {
+                        log_error("Failed to determine file descriptor type: %s", strerror(-r));
+                        goto fail;
+                }
+
+                if (!r) {
+                        log_error("Wrong file descriptor type.");
+                        r = -EINVAL;
+                        goto fail;
+                }
+
+                if (!(f = new0(Fifo, 1))) {
+                        r = -ENOMEM;
+                        log_error("Failed to create fifo object: %s", strerror(errno));
+                        goto fail;
+                }
+
+                f->fd = -1;
+
+                zero(ev);
+                ev.events = EPOLLIN;
+                ev.data.ptr = f;
+                if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+                        r = -errno;
+                        fifo_free(f);
+                        log_error("Failed to add fifo fd to epoll object: %s", strerror(errno));
+                        goto fail;
+                }
+
+                f->fd = fd;
+                LIST_PREPEND(Fifo, fifo, s->fifos, f);
+                f->server = s;
+                s->n_fifos ++;
+        }
+
+        if (bus_connect(DBUS_BUS_SYSTEM, &s->bus, NULL, &error) < 0) {
+                log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
+                goto fail;
+        }
+
+        return 0;
+
+fail:
+        server_done(s);
+
+        dbus_error_free(&error);
+        return r;
+}
+
+static int process_event(Server *s, struct epoll_event *ev) {
+        int r;
+        Fifo *f;
+
+        assert(s);
+
+        if (!(ev->events & EPOLLIN)) {
+                log_info("Got invalid event from epoll. (3)");
+                return -EIO;
+        }
+
+        f = (Fifo*) ev->data.ptr;
+
+        if ((r = fifo_process(f)) < 0) {
+                log_info("Got error on fifo: %s", strerror(-r));
+                fifo_free(f);
+                return r;
+        }
+
+        return 0;
+}
+
+int main(int argc, char *argv[]) {
+        Server server;
+        int r = EXIT_FAILURE, n;
+
+        if (getppid() != 1) {
+                log_error("This program should be invoked by init only.");
+                return EXIT_FAILURE;
+        }
+
+        if (argc > 1) {
+                log_error("This program does not take arguments.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if ((n = sd_listen_fds(true)) < 0) {
+                log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
+                return EXIT_FAILURE;
+        }
+
+        if (n <= 0 || n > SERVER_FD_MAX) {
+                log_error("No or too many file descriptors passed.");
+                return EXIT_FAILURE;
+        }
+
+        if (server_init(&server, (unsigned) n) < 0)
+                return EXIT_FAILURE;
+
+        log_debug("systemd-initctl running as pid %lu", (unsigned long) getpid());
+
+        sd_notify(false,
+                  "READY=1\n"
+                  "STATUS=Processing requests...");
+
+        while (!server.quit) {
+                struct epoll_event event;
+                int k;
+
+                if ((k = epoll_wait(server.epoll_fd,
+                                    &event, 1,
+                                    TIMEOUT_MSEC)) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        log_error("epoll_wait() failed: %s", strerror(errno));
+                        goto fail;
+                }
+
+                if (k <= 0)
+                        break;
+
+                if (process_event(&server, &event) < 0)
+                        goto fail;
+        }
+
+        r = EXIT_SUCCESS;
+
+        log_debug("systemd-initctl stopped as pid %lu", (unsigned long) getpid());
+
+fail:
+        sd_notify(false,
+                  "STATUS=Shutting down...");
+
+        server_done(&server);
+
+        dbus_shutdown();
+
+        return r;
+}
diff --git a/src/modules-load.c b/src/modules-load.c
deleted file mode 100644
index 0f2144c..0000000
--- a/src/modules-load.c
+++ /dev/null
@@ -1,155 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <dirent.h>
-#include <libkmod.h>
-
-#include "log.h"
-#include "util.h"
-#include "strv.h"
-
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wformat-nonliteral"
-static void systemd_kmod_log(void *data, int priority, const char *file, int line,
-                             const char *fn, const char *format, va_list args)
-{
-        log_meta(priority, file, line, fn, format, args);
-}
-#pragma GCC diagnostic pop
-
-int main(int argc, char *argv[]) {
-        int r = EXIT_FAILURE;
-        char **files, **fn;
-        struct kmod_ctx *ctx;
-        const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST|KMOD_PROBE_IGNORE_LOADED;
-
-        if (argc > 1) {
-                log_error("This program takes no argument.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        ctx = kmod_new(NULL, NULL);
-        if (!ctx) {
-                log_error("Failed to allocate memory for kmod.");
-                goto finish;
-        }
-
-        kmod_load_resources(ctx);
-
-        kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
-
-        if (conf_files_list(&files, ".conf",
-                            "/etc/modules-load.d",
-                            "/run/modules-load.d",
-                            "/usr/local/lib/modules-load.d",
-                            "/usr/lib/modules-load.d",
-#ifdef HAVE_SPLIT_USR
-                            "/lib/modules-load.d",
-#endif
-                            NULL) < 0) {
-                log_error("Failed to enumerate modules-load.d files: %s", strerror(-r));
-                goto finish;
-        }
-
-        r = EXIT_SUCCESS;
-
-        STRV_FOREACH(fn, files) {
-                FILE *f;
-
-                f = fopen(*fn, "re");
-                if (!f) {
-                        if (errno == ENOENT)
-                                continue;
-
-                        log_error("Failed to open %s: %m", *fn);
-                        r = EXIT_FAILURE;
-                        continue;
-                }
-
-                log_debug("apply: %s\n", *fn);
-                for (;;) {
-                        char line[LINE_MAX], *l;
-                        struct kmod_list *itr, *modlist = NULL;
-                        int err;
-
-                        if (!fgets(line, sizeof(line), f))
-                                break;
-
-                        l = strstrip(line);
-                        if (*l == '#' || *l == 0)
-                                continue;
-
-                        err = kmod_module_new_from_lookup(ctx, l, &modlist);
-                        if (err < 0) {
-                                log_error("Failed to lookup alias '%s'", l);
-                                r = EXIT_FAILURE;
-                                continue;
-                        }
-
-                        kmod_list_foreach(itr, modlist) {
-                                struct kmod_module *mod;
-
-                                mod = kmod_module_get_module(itr);
-                                err = kmod_module_probe_insert_module(mod, probe_flags,
-                                                                      NULL, NULL, NULL, NULL);
-
-                                if (err == 0)
-                                        log_info("Inserted module '%s'", kmod_module_get_name(mod));
-                                else if (err == KMOD_PROBE_APPLY_BLACKLIST)
-                                        log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
-                                else {
-                                        log_error("Failed to insert '%s': %s", kmod_module_get_name(mod),
-                                                        strerror(-err));
-                                        r = EXIT_FAILURE;
-                                }
-
-                                kmod_module_unref(mod);
-                        }
-
-                        kmod_module_unref_list(modlist);
-                }
-
-                if (ferror(f)) {
-                        log_error("Failed to read from file: %m");
-                        r = EXIT_FAILURE;
-                }
-
-                fclose(f);
-        }
-
-finish:
-        strv_free(files);
-        kmod_unref(ctx);
-
-        return r;
-}
diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c
new file mode 100644
index 0000000..0f2144c
--- /dev/null
+++ b/src/modules-load/modules-load.c
@@ -0,0 +1,155 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <dirent.h>
+#include <libkmod.h>
+
+#include "log.h"
+#include "util.h"
+#include "strv.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+static void systemd_kmod_log(void *data, int priority, const char *file, int line,
+                             const char *fn, const char *format, va_list args)
+{
+        log_meta(priority, file, line, fn, format, args);
+}
+#pragma GCC diagnostic pop
+
+int main(int argc, char *argv[]) {
+        int r = EXIT_FAILURE;
+        char **files, **fn;
+        struct kmod_ctx *ctx;
+        const int probe_flags = KMOD_PROBE_APPLY_BLACKLIST|KMOD_PROBE_IGNORE_LOADED;
+
+        if (argc > 1) {
+                log_error("This program takes no argument.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        ctx = kmod_new(NULL, NULL);
+        if (!ctx) {
+                log_error("Failed to allocate memory for kmod.");
+                goto finish;
+        }
+
+        kmod_load_resources(ctx);
+
+        kmod_set_log_fn(ctx, systemd_kmod_log, NULL);
+
+        if (conf_files_list(&files, ".conf",
+                            "/etc/modules-load.d",
+                            "/run/modules-load.d",
+                            "/usr/local/lib/modules-load.d",
+                            "/usr/lib/modules-load.d",
+#ifdef HAVE_SPLIT_USR
+                            "/lib/modules-load.d",
+#endif
+                            NULL) < 0) {
+                log_error("Failed to enumerate modules-load.d files: %s", strerror(-r));
+                goto finish;
+        }
+
+        r = EXIT_SUCCESS;
+
+        STRV_FOREACH(fn, files) {
+                FILE *f;
+
+                f = fopen(*fn, "re");
+                if (!f) {
+                        if (errno == ENOENT)
+                                continue;
+
+                        log_error("Failed to open %s: %m", *fn);
+                        r = EXIT_FAILURE;
+                        continue;
+                }
+
+                log_debug("apply: %s\n", *fn);
+                for (;;) {
+                        char line[LINE_MAX], *l;
+                        struct kmod_list *itr, *modlist = NULL;
+                        int err;
+
+                        if (!fgets(line, sizeof(line), f))
+                                break;
+
+                        l = strstrip(line);
+                        if (*l == '#' || *l == 0)
+                                continue;
+
+                        err = kmod_module_new_from_lookup(ctx, l, &modlist);
+                        if (err < 0) {
+                                log_error("Failed to lookup alias '%s'", l);
+                                r = EXIT_FAILURE;
+                                continue;
+                        }
+
+                        kmod_list_foreach(itr, modlist) {
+                                struct kmod_module *mod;
+
+                                mod = kmod_module_get_module(itr);
+                                err = kmod_module_probe_insert_module(mod, probe_flags,
+                                                                      NULL, NULL, NULL, NULL);
+
+                                if (err == 0)
+                                        log_info("Inserted module '%s'", kmod_module_get_name(mod));
+                                else if (err == KMOD_PROBE_APPLY_BLACKLIST)
+                                        log_info("Module '%s' is blacklisted", kmod_module_get_name(mod));
+                                else {
+                                        log_error("Failed to insert '%s': %s", kmod_module_get_name(mod),
+                                                        strerror(-err));
+                                        r = EXIT_FAILURE;
+                                }
+
+                                kmod_module_unref(mod);
+                        }
+
+                        kmod_module_unref_list(modlist);
+                }
+
+                if (ferror(f)) {
+                        log_error("Failed to read from file: %m");
+                        r = EXIT_FAILURE;
+                }
+
+                fclose(f);
+        }
+
+finish:
+        strv_free(files);
+        kmod_unref(ctx);
+
+        return r;
+}
diff --git a/src/notify.c b/src/notify.c
deleted file mode 100644
index ffc8dfe..0000000
--- a/src/notify.c
+++ /dev/null
@@ -1,228 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <stdio.h>
-#include <getopt.h>
-#include <error.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <systemd/sd-daemon.h>
-
-#include "strv.h"
-#include "util.h"
-#include "log.h"
-#include "sd-readahead.h"
-#include "build.h"
-
-static bool arg_ready = false;
-static pid_t arg_pid = 0;
-static const char *arg_status = NULL;
-static bool arg_booted = false;
-static const char *arg_readahead = NULL;
-
-static int help(void) {
-
-        printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n"
-               "Notify the init system about service status updates.\n\n"
-               "  -h --help             Show this help\n"
-               "     --version          Show package version\n"
-               "     --ready            Inform the init system about service start-up completion\n"
-               "     --pid[=PID]        Set main pid of daemon\n"
-               "     --status=TEXT      Set status text\n"
-               "     --booted           Returns 0 if the system was booted up with systemd, non-zero otherwise\n"
-               "     --readahead=ACTION Controls read-ahead operations\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_READY = 0x100,
-                ARG_VERSION,
-                ARG_PID,
-                ARG_STATUS,
-                ARG_BOOTED,
-                ARG_READAHEAD
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'           },
-                { "version",   no_argument,       NULL, ARG_VERSION   },
-                { "ready",     no_argument,       NULL, ARG_READY     },
-                { "pid",       optional_argument, NULL, ARG_PID       },
-                { "status",    required_argument, NULL, ARG_STATUS    },
-                { "booted",    no_argument,       NULL, ARG_BOOTED    },
-                { "readahead", required_argument, NULL, ARG_READAHEAD },
-                { NULL,        0,                 NULL, 0             }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_VERSION:
-                        puts(PACKAGE_STRING);
-                        puts(DISTRIBUTION);
-                        puts(SYSTEMD_FEATURES);
-                        return 0;
-
-                case ARG_READY:
-                        arg_ready = true;
-                        break;
-
-                case ARG_PID:
-
-                        if (optarg) {
-                                if (parse_pid(optarg, &arg_pid) < 0) {
-                                        log_error("Failed to parse PID %s.", optarg);
-                                        return -EINVAL;
-                                }
-                        } else
-                                arg_pid = getppid();
-
-                        break;
-
-                case ARG_STATUS:
-                        arg_status = optarg;
-                        break;
-
-                case ARG_BOOTED:
-                        arg_booted = true;
-                        break;
-
-                case ARG_READAHEAD:
-                        arg_readahead = optarg;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind >= argc &&
-            !arg_ready &&
-            !arg_status &&
-            !arg_pid &&
-            !arg_booted &&
-            !arg_readahead) {
-                help();
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-int main(int argc, char* argv[]) {
-        char* our_env[4], **final_env = NULL;
-        unsigned i = 0;
-        char *status = NULL, *cpid = NULL, *n = NULL;
-        int r, retval = EXIT_FAILURE;
-
-        log_parse_environment();
-        log_open();
-
-        if ((r = parse_argv(argc, argv)) <= 0) {
-                retval = r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-                goto finish;
-        }
-
-        if (arg_booted)
-                return sd_booted() <= 0;
-
-        if (arg_readahead) {
-                if ((r = sd_readahead(arg_readahead)) < 0) {
-                        log_error("Failed to issue read-ahead control command: %s", strerror(-r));
-                        goto finish;
-                }
-        }
-
-        if (arg_ready)
-                our_env[i++] = (char*) "READY=1";
-
-        if (arg_status) {
-                if (!(status = strappend("STATUS=", arg_status))) {
-                        log_error("Failed to allocate STATUS string.");
-                        goto finish;
-                }
-
-                our_env[i++] = status;
-        }
-
-        if (arg_pid > 0) {
-                if (asprintf(&cpid, "MAINPID=%lu", (unsigned long) arg_pid) < 0) {
-                        log_error("Failed to allocate MAINPID string.");
-                        goto finish;
-                }
-
-                our_env[i++] = cpid;
-        }
-
-        our_env[i++] = NULL;
-
-        if (!(final_env = strv_env_merge(2, our_env, argv + optind))) {
-                log_error("Failed to merge string sets.");
-                goto finish;
-        }
-
-        if (strv_length(final_env) <= 0) {
-                retval = EXIT_SUCCESS;
-                goto finish;
-        }
-
-        if (!(n = strv_join(final_env, "\n"))) {
-                log_error("Failed to concatenate strings.");
-                goto finish;
-        }
-
-        if ((r = sd_notify(false, n)) < 0) {
-                log_error("Failed to notify init system: %s", strerror(-r));
-                goto finish;
-        }
-
-        retval = r <= 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
-finish:
-        free(status);
-        free(cpid);
-        free(n);
-
-        strv_free(final_env);
-
-        return retval;
-}
diff --git a/src/notify/notify.c b/src/notify/notify.c
new file mode 100644
index 0000000..ffc8dfe
--- /dev/null
+++ b/src/notify/notify.c
@@ -0,0 +1,228 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <stdio.h>
+#include <getopt.h>
+#include <error.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <systemd/sd-daemon.h>
+
+#include "strv.h"
+#include "util.h"
+#include "log.h"
+#include "sd-readahead.h"
+#include "build.h"
+
+static bool arg_ready = false;
+static pid_t arg_pid = 0;
+static const char *arg_status = NULL;
+static bool arg_booted = false;
+static const char *arg_readahead = NULL;
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n\n"
+               "Notify the init system about service status updates.\n\n"
+               "  -h --help             Show this help\n"
+               "     --version          Show package version\n"
+               "     --ready            Inform the init system about service start-up completion\n"
+               "     --pid[=PID]        Set main pid of daemon\n"
+               "     --status=TEXT      Set status text\n"
+               "     --booted           Returns 0 if the system was booted up with systemd, non-zero otherwise\n"
+               "     --readahead=ACTION Controls read-ahead operations\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_READY = 0x100,
+                ARG_VERSION,
+                ARG_PID,
+                ARG_STATUS,
+                ARG_BOOTED,
+                ARG_READAHEAD
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { "version",   no_argument,       NULL, ARG_VERSION   },
+                { "ready",     no_argument,       NULL, ARG_READY     },
+                { "pid",       optional_argument, NULL, ARG_PID       },
+                { "status",    required_argument, NULL, ARG_STATUS    },
+                { "booted",    no_argument,       NULL, ARG_BOOTED    },
+                { "readahead", required_argument, NULL, ARG_READAHEAD },
+                { NULL,        0,                 NULL, 0             }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_VERSION:
+                        puts(PACKAGE_STRING);
+                        puts(DISTRIBUTION);
+                        puts(SYSTEMD_FEATURES);
+                        return 0;
+
+                case ARG_READY:
+                        arg_ready = true;
+                        break;
+
+                case ARG_PID:
+
+                        if (optarg) {
+                                if (parse_pid(optarg, &arg_pid) < 0) {
+                                        log_error("Failed to parse PID %s.", optarg);
+                                        return -EINVAL;
+                                }
+                        } else
+                                arg_pid = getppid();
+
+                        break;
+
+                case ARG_STATUS:
+                        arg_status = optarg;
+                        break;
+
+                case ARG_BOOTED:
+                        arg_booted = true;
+                        break;
+
+                case ARG_READAHEAD:
+                        arg_readahead = optarg;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind >= argc &&
+            !arg_ready &&
+            !arg_status &&
+            !arg_pid &&
+            !arg_booted &&
+            !arg_readahead) {
+                help();
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+int main(int argc, char* argv[]) {
+        char* our_env[4], **final_env = NULL;
+        unsigned i = 0;
+        char *status = NULL, *cpid = NULL, *n = NULL;
+        int r, retval = EXIT_FAILURE;
+
+        log_parse_environment();
+        log_open();
+
+        if ((r = parse_argv(argc, argv)) <= 0) {
+                retval = r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+                goto finish;
+        }
+
+        if (arg_booted)
+                return sd_booted() <= 0;
+
+        if (arg_readahead) {
+                if ((r = sd_readahead(arg_readahead)) < 0) {
+                        log_error("Failed to issue read-ahead control command: %s", strerror(-r));
+                        goto finish;
+                }
+        }
+
+        if (arg_ready)
+                our_env[i++] = (char*) "READY=1";
+
+        if (arg_status) {
+                if (!(status = strappend("STATUS=", arg_status))) {
+                        log_error("Failed to allocate STATUS string.");
+                        goto finish;
+                }
+
+                our_env[i++] = status;
+        }
+
+        if (arg_pid > 0) {
+                if (asprintf(&cpid, "MAINPID=%lu", (unsigned long) arg_pid) < 0) {
+                        log_error("Failed to allocate MAINPID string.");
+                        goto finish;
+                }
+
+                our_env[i++] = cpid;
+        }
+
+        our_env[i++] = NULL;
+
+        if (!(final_env = strv_env_merge(2, our_env, argv + optind))) {
+                log_error("Failed to merge string sets.");
+                goto finish;
+        }
+
+        if (strv_length(final_env) <= 0) {
+                retval = EXIT_SUCCESS;
+                goto finish;
+        }
+
+        if (!(n = strv_join(final_env, "\n"))) {
+                log_error("Failed to concatenate strings.");
+                goto finish;
+        }
+
+        if ((r = sd_notify(false, n)) < 0) {
+                log_error("Failed to notify init system: %s", strerror(-r));
+                goto finish;
+        }
+
+        retval = r <= 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+finish:
+        free(status);
+        free(cpid);
+        free(n);
+
+        strv_free(final_env);
+
+        return retval;
+}
diff --git a/src/nspawn.c b/src/nspawn.c
deleted file mode 100644
index 685b4d4..0000000
--- a/src/nspawn.c
+++ /dev/null
@@ -1,925 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <signal.h>
-#include <sched.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/syscall.h>
-#include <sys/mount.h>
-#include <sys/wait.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <sys/prctl.h>
-#include <sys/capability.h>
-#include <getopt.h>
-#include <sys/epoll.h>
-#include <termios.h>
-#include <sys/signalfd.h>
-#include <grp.h>
-#include <linux/fs.h>
-
-#include <systemd/sd-daemon.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "audit.h"
-#include "missing.h"
-#include "cgroup-util.h"
-#include "strv.h"
-#include "loopback-setup.h"
-
-static char *arg_directory = NULL;
-static char *arg_user = NULL;
-static char **arg_controllers = NULL;
-static bool arg_private_network = false;
-
-static int help(void) {
-
-        printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
-               "Spawn a minimal namespace container for debugging, testing and building.\n\n"
-               "  -h --help             Show this help\n"
-               "  -D --directory=NAME   Root directory for the container\n"
-               "  -u --user=USER        Run the command under specified user or uid\n"
-               "  -C --controllers=LIST Put the container in specified comma-separated cgroup hierarchies\n"
-               "     --private-network  Disable network in container\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_PRIVATE_NETWORK = 0x100
-        };
-
-        static const struct option options[] = {
-                { "help",            no_argument,       NULL, 'h'                 },
-                { "directory",       required_argument, NULL, 'D'                 },
-                { "user",            required_argument, NULL, 'u'                 },
-                { "controllers",     required_argument, NULL, 'C'                 },
-                { "private-network", no_argument,       NULL, ARG_PRIVATE_NETWORK },
-                { NULL,              0,                 NULL, 0                   }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "+hD:u:C:", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case 'D':
-                        free(arg_directory);
-                        if (!(arg_directory = strdup(optarg))) {
-                                log_error("Failed to duplicate root directory.");
-                                return -ENOMEM;
-                        }
-
-                        break;
-
-                case 'u':
-                        free(arg_user);
-                        if (!(arg_user = strdup(optarg))) {
-                                log_error("Failed to duplicate user name.");
-                                return -ENOMEM;
-                        }
-
-                        break;
-
-                case 'C':
-                        strv_free(arg_controllers);
-                        arg_controllers = strv_split(optarg, ",");
-                        if (!arg_controllers) {
-                                log_error("Failed to split controllers list.");
-                                return -ENOMEM;
-                        }
-                        strv_uniq(arg_controllers);
-
-                        break;
-
-                case ARG_PRIVATE_NETWORK:
-                        arg_private_network = true;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        return 1;
-}
-
-static int mount_all(const char *dest) {
-
-        typedef struct MountPoint {
-                const char *what;
-                const char *where;
-                const char *type;
-                const char *options;
-                unsigned long flags;
-                bool fatal;
-        } MountPoint;
-
-        static const MountPoint mount_table[] = {
-                { "proc",      "/proc",     "proc",  NULL,       MS_NOSUID|MS_NOEXEC|MS_NODEV, true  },
-                { "/proc/sys", "/proc/sys", "bind",  NULL,       MS_BIND, true                       },   /* Bind mount first */
-                { "/proc/sys", "/proc/sys", "bind",  NULL,       MS_BIND|MS_RDONLY|MS_REMOUNT, true  },   /* Then, make it r/o */
-                { "/sys",      "/sys",      "bind",  NULL,       MS_BIND,                      true  },   /* Bind mount first */
-                { "/sys",      "/sys",      "bind",  NULL,       MS_BIND|MS_RDONLY|MS_REMOUNT, true  },   /* Then, make it r/o */
-                { "tmpfs",     "/dev",      "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME,     true  },
-                { "/dev/pts",  "/dev/pts",  "bind",  NULL,       MS_BIND,                      true  },
-                { "tmpfs",     "/run",      "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true  },
-#ifdef HAVE_SELINUX
-                { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND,                      false },  /* Bind mount first */
-                { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false },  /* Then, make it r/o */
-#endif
-        };
-
-        unsigned k;
-        int r = 0;
-        char *where;
-
-        for (k = 0; k < ELEMENTSOF(mount_table); k++) {
-                int t;
-
-                if (asprintf(&where, "%s/%s", dest, mount_table[k].where) < 0) {
-                        log_error("Out of memory");
-
-                        if (r == 0)
-                                r = -ENOMEM;
-
-                        break;
-                }
-
-                t = path_is_mount_point(where, false);
-                if (t < 0) {
-                        log_error("Failed to detect whether %s is a mount point: %s", where, strerror(-t));
-                        free(where);
-
-                        if (r == 0)
-                                r = t;
-
-                        continue;
-                }
-
-                mkdir_p(where, 0755);
-
-                if (mount(mount_table[k].what,
-                          where,
-                          mount_table[k].type,
-                          mount_table[k].flags,
-                          mount_table[k].options) < 0 &&
-                    mount_table[k].fatal) {
-
-                        log_error("mount(%s) failed: %m", where);
-
-                        if (r == 0)
-                                r = -errno;
-                }
-
-                free(where);
-        }
-
-        /* Fix the timezone, if possible */
-        if (asprintf(&where, "%s/etc/localtime", dest) >= 0) {
-
-                if (mount("/etc/localtime", where, "bind", MS_BIND, NULL) >= 0)
-                        mount("/etc/localtime", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
-
-                free(where);
-        }
-
-        if (asprintf(&where, "%s/etc/timezone", dest) >= 0) {
-
-                if (mount("/etc/timezone", where, "bind", MS_BIND, NULL) >= 0)
-                        mount("/etc/timezone", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
-
-                free(where);
-        }
-
-        if (asprintf(&where, "%s/proc/kmsg", dest) >= 0) {
-                mount("/dev/null", where, "bind", MS_BIND, NULL);
-                free(where);
-        }
-
-        return r;
-}
-
-static int copy_devnodes(const char *dest, const char *console) {
-
-        static const char devnodes[] =
-                "null\0"
-                "zero\0"
-                "full\0"
-                "random\0"
-                "urandom\0"
-                "tty\0"
-                "ptmx\0"
-                "kmsg\0"
-                "rtc0\0";
-
-        const char *d;
-        int r = 0, k;
-        mode_t u;
-        struct stat st;
-        char *from = NULL, *to = NULL;
-
-        assert(dest);
-        assert(console);
-
-        u = umask(0000);
-
-        NULSTR_FOREACH(d, devnodes) {
-                from = to = NULL;
-
-                asprintf(&from, "/dev/%s", d);
-                asprintf(&to, "%s/dev/%s", dest, d);
-
-                if (!from || !to) {
-                        log_error("Failed to allocate devnode path");
-
-                        free(from);
-                        free(to);
-
-                        from = to = NULL;
-
-                        if (r == 0)
-                                r = -ENOMEM;
-
-                        break;
-                }
-
-                if (stat(from, &st) < 0) {
-
-                        if (errno != ENOENT) {
-                                log_error("Failed to stat %s: %m", from);
-                                if (r == 0)
-                                        r = -errno;
-                        }
-
-                } else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
-
-                        log_error("%s is not a char or block device, cannot copy.", from);
-                        if (r == 0)
-                                r = -EIO;
-
-                } else if (mknod(to, st.st_mode, st.st_rdev) < 0) {
-
-                        log_error("mknod(%s) failed: %m", dest);
-                        if (r == 0)
-                                r = -errno;
-                }
-
-                free(from);
-                free(to);
-        }
-
-        if (stat(console, &st) < 0) {
-
-                log_error("Failed to stat %s: %m", console);
-                if (r == 0)
-                        r = -errno;
-
-                goto finish;
-
-        } else if (!S_ISCHR(st.st_mode)) {
-
-                log_error("/dev/console is not a char device.");
-                if (r == 0)
-                        r = -EIO;
-
-                goto finish;
-        }
-
-        if (asprintf(&to, "%s/dev/console", dest) < 0) {
-
-                log_error("Out of memory");
-                if (r == 0)
-                        r = -ENOMEM;
-
-                 goto finish;
-        }
-
-        /* We need to bind mount the right tty to /dev/console since
-         * ptys can only exist on pts file systems. To have something
-         * to bind mount things on we create a device node first, that
-         * has the right major/minor (note that the major minor
-         * doesn't actually matter here, since we mount it over
-         * anyway). */
-
-        if (mknod(to, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0)
-                log_error("mknod for /dev/console failed: %m");
-
-        if (mount(console, to, "bind", MS_BIND, NULL) < 0) {
-                log_error("bind mount for /dev/console failed: %m");
-
-                if (r == 0)
-                        r = -errno;
-        }
-
-        free(to);
-
-        if ((k = chmod_and_chown(console, 0600, 0, 0)) < 0) {
-                log_error("Failed to correct access mode for TTY: %s", strerror(-k));
-
-                if (r == 0)
-                        r = k;
-        }
-
-finish:
-        umask(u);
-
-        return r;
-}
-
-static int drop_capabilities(void) {
-        static const unsigned long retain[] = {
-                CAP_CHOWN,
-                CAP_DAC_OVERRIDE,
-                CAP_DAC_READ_SEARCH,
-                CAP_FOWNER,
-                CAP_FSETID,
-                CAP_IPC_OWNER,
-                CAP_KILL,
-                CAP_LEASE,
-                CAP_LINUX_IMMUTABLE,
-                CAP_NET_BIND_SERVICE,
-                CAP_NET_BROADCAST,
-                CAP_NET_RAW,
-                CAP_SETGID,
-                CAP_SETFCAP,
-                CAP_SETPCAP,
-                CAP_SETUID,
-                CAP_SYS_ADMIN,
-                CAP_SYS_CHROOT,
-                CAP_SYS_NICE,
-                CAP_SYS_PTRACE,
-                CAP_SYS_TTY_CONFIG
-        };
-
-        unsigned long l;
-
-        for (l = 0; l <= cap_last_cap(); l++) {
-                unsigned i;
-
-                for (i = 0; i < ELEMENTSOF(retain); i++)
-                        if (retain[i] == l)
-                                break;
-
-                if (i < ELEMENTSOF(retain))
-                        continue;
-
-                if (prctl(PR_CAPBSET_DROP, l) < 0) {
-                        log_error("PR_CAPBSET_DROP failed: %m");
-                        return -errno;
-                }
-        }
-
-        return 0;
-}
-
-static int is_os_tree(const char *path) {
-        int r;
-        char *p;
-        /* We use /bin/sh as flag file if something is an OS */
-
-        if (asprintf(&p, "%s/bin/sh", path) < 0)
-                return -ENOMEM;
-
-        r = access(p, F_OK);
-        free(p);
-
-        return r < 0 ? 0 : 1;
-}
-
-static int process_pty(int master, sigset_t *mask) {
-
-        char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
-        size_t in_buffer_full = 0, out_buffer_full = 0;
-        struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev;
-        bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false;
-        int ep = -1, signal_fd = -1, r;
-
-        fd_nonblock(STDIN_FILENO, 1);
-        fd_nonblock(STDOUT_FILENO, 1);
-        fd_nonblock(master, 1);
-
-        if ((signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
-                log_error("signalfd(): %m");
-                r = -errno;
-                goto finish;
-        }
-
-        if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) {
-                log_error("Failed to create epoll: %m");
-                r = -errno;
-                goto finish;
-        }
-
-        zero(stdin_ev);
-        stdin_ev.events = EPOLLIN|EPOLLET;
-        stdin_ev.data.fd = STDIN_FILENO;
-
-        zero(stdout_ev);
-        stdout_ev.events = EPOLLOUT|EPOLLET;
-        stdout_ev.data.fd = STDOUT_FILENO;
-
-        zero(master_ev);
-        master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
-        master_ev.data.fd = master;
-
-        zero(signal_ev);
-        signal_ev.events = EPOLLIN;
-        signal_ev.data.fd = signal_fd;
-
-        if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 ||
-            epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 ||
-            epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
-            epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) {
-                log_error("Failed to regiser fds in epoll: %m");
-                r = -errno;
-                goto finish;
-        }
-
-        for (;;) {
-                struct epoll_event ev[16];
-                ssize_t k;
-                int i, nfds;
-
-                if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) {
-
-                        if (errno == EINTR || errno == EAGAIN)
-                                continue;
-
-                        log_error("epoll_wait(): %m");
-                        r = -errno;
-                        goto finish;
-                }
-
-                assert(nfds >= 1);
-
-                for (i = 0; i < nfds; i++) {
-                        if (ev[i].data.fd == STDIN_FILENO) {
-
-                                if (ev[i].events & (EPOLLIN|EPOLLHUP))
-                                        stdin_readable = true;
-
-                        } else if (ev[i].data.fd == STDOUT_FILENO) {
-
-                                if (ev[i].events & (EPOLLOUT|EPOLLHUP))
-                                        stdout_writable = true;
-
-                        } else if (ev[i].data.fd == master) {
-
-                                if (ev[i].events & (EPOLLIN|EPOLLHUP))
-                                        master_readable = true;
-
-                                if (ev[i].events & (EPOLLOUT|EPOLLHUP))
-                                        master_writable = true;
-
-                        } else if (ev[i].data.fd == signal_fd) {
-                                struct signalfd_siginfo sfsi;
-                                ssize_t n;
-
-                                if ((n = read(signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
-
-                                        if (n >= 0) {
-                                                log_error("Failed to read from signalfd: invalid block size");
-                                                r = -EIO;
-                                                goto finish;
-                                        }
-
-                                        if (errno != EINTR && errno != EAGAIN) {
-                                                log_error("Failed to read from signalfd: %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-                                } else {
-
-                                        if (sfsi.ssi_signo == SIGWINCH) {
-                                                struct winsize ws;
-
-                                                /* The window size changed, let's forward that. */
-                                                if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
-                                                        ioctl(master, TIOCSWINSZ, &ws);
-                                        } else {
-                                                r = 0;
-                                                goto finish;
-                                        }
-                                }
-                        }
-                }
-
-                while ((stdin_readable && in_buffer_full <= 0) ||
-                       (master_writable && in_buffer_full > 0) ||
-                       (master_readable && out_buffer_full <= 0) ||
-                       (stdout_writable && out_buffer_full > 0)) {
-
-                        if (stdin_readable && in_buffer_full < LINE_MAX) {
-
-                                if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full)) < 0) {
-
-                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
-                                                stdin_readable = false;
-                                        else {
-                                                log_error("read(): %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-                                } else
-                                        in_buffer_full += (size_t) k;
-                        }
-
-                        if (master_writable && in_buffer_full > 0) {
-
-                                if ((k = write(master, in_buffer, in_buffer_full)) < 0) {
-
-                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
-                                                master_writable = false;
-                                        else {
-                                                log_error("write(): %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-
-                                } else {
-                                        assert(in_buffer_full >= (size_t) k);
-                                        memmove(in_buffer, in_buffer + k, in_buffer_full - k);
-                                        in_buffer_full -= k;
-                                }
-                        }
-
-                        if (master_readable && out_buffer_full < LINE_MAX) {
-
-                                if ((k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full)) < 0) {
-
-                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
-                                                master_readable = false;
-                                        else {
-                                                log_error("read(): %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-                                }  else
-                                        out_buffer_full += (size_t) k;
-                        }
-
-                        if (stdout_writable && out_buffer_full > 0) {
-
-                                if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) {
-
-                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
-                                                stdout_writable = false;
-                                        else {
-                                                log_error("write(): %m");
-                                                r = -errno;
-                                                goto finish;
-                                        }
-
-                                } else {
-                                        assert(out_buffer_full >= (size_t) k);
-                                        memmove(out_buffer, out_buffer + k, out_buffer_full - k);
-                                        out_buffer_full -= k;
-                                }
-                        }
-                }
-        }
-
-finish:
-        if (ep >= 0)
-                close_nointr_nofail(ep);
-
-        if (signal_fd >= 0)
-                close_nointr_nofail(signal_fd);
-
-        return r;
-}
-
-int main(int argc, char *argv[]) {
-        pid_t pid = 0;
-        int r = EXIT_FAILURE, k;
-        char *oldcg = NULL, *newcg = NULL;
-        char **controller = NULL;
-        int master = -1;
-        const char *console = NULL;
-        struct termios saved_attr, raw_attr;
-        sigset_t mask;
-        bool saved_attr_valid = false;
-        struct winsize ws;
-
-        log_parse_environment();
-        log_open();
-
-        if ((r = parse_argv(argc, argv)) <= 0)
-                goto finish;
-
-        if (arg_directory) {
-                char *p;
-
-                p = path_make_absolute_cwd(arg_directory);
-                free(arg_directory);
-                arg_directory = p;
-        } else
-                arg_directory = get_current_dir_name();
-
-        if (!arg_directory) {
-                log_error("Failed to determine path");
-                goto finish;
-        }
-
-        path_kill_slashes(arg_directory);
-
-        if (geteuid() != 0) {
-                log_error("Need to be root.");
-                goto finish;
-        }
-
-        if (sd_booted() <= 0) {
-                log_error("Not running on a systemd system.");
-                goto finish;
-        }
-
-        if (path_equal(arg_directory, "/")) {
-                log_error("Spawning container on root directory not supported.");
-                goto finish;
-        }
-
-        if (is_os_tree(arg_directory) <= 0) {
-                log_error("Directory %s doesn't look like an OS root directory. Refusing.", arg_directory);
-                goto finish;
-        }
-
-        if ((k = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &oldcg)) < 0) {
-                log_error("Failed to determine current cgroup: %s", strerror(-k));
-                goto finish;
-        }
-
-        if (asprintf(&newcg, "%s/nspawn-%lu", oldcg, (unsigned long) getpid()) < 0) {
-                log_error("Failed to allocate cgroup path.");
-                goto finish;
-        }
-
-        k = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, newcg, 0);
-        if (k < 0)  {
-                log_error("Failed to create cgroup: %s", strerror(-k));
-                goto finish;
-        }
-
-        STRV_FOREACH(controller,arg_controllers) {
-                k = cg_create_and_attach(*controller, newcg, 0);
-                if (k < 0)
-                        log_warning("Failed to create cgroup in controller %s: %s", *controller, strerror(-k));
-        }
-
-        if ((master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY)) < 0) {
-                log_error("Failed to acquire pseudo tty: %m");
-                goto finish;
-        }
-
-        if (!(console = ptsname(master))) {
-                log_error("Failed to determine tty name: %m");
-                goto finish;
-        }
-
-        log_info("Spawning namespace container on %s (console is %s).", arg_directory, console);
-
-        if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
-                ioctl(master, TIOCSWINSZ, &ws);
-
-        if (unlockpt(master) < 0) {
-                log_error("Failed to unlock tty: %m");
-                goto finish;
-        }
-
-        if (tcgetattr(STDIN_FILENO, &saved_attr) < 0) {
-                log_error("Failed to get terminal attributes: %m");
-                goto finish;
-        }
-
-        saved_attr_valid = true;
-
-        raw_attr = saved_attr;
-        cfmakeraw(&raw_attr);
-        raw_attr.c_lflag &= ~ECHO;
-
-        if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) {
-                log_error("Failed to set terminal attributes: %m");
-                goto finish;
-        }
-
-        assert_se(sigemptyset(&mask) == 0);
-        sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1);
-        assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
-
-        pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
-        if (pid < 0) {
-                if (errno == EINVAL)
-                        log_error("clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m");
-                else
-                        log_error("clone() failed: %m");
-
-                goto finish;
-        }
-
-        if (pid == 0) {
-                /* child */
-
-                const char *hn;
-                const char *home = NULL;
-                uid_t uid = (uid_t) -1;
-                gid_t gid = (gid_t) -1;
-                const char *envp[] = {
-                        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
-                        "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
-                        NULL, /* TERM */
-                        NULL, /* HOME */
-                        NULL, /* USER */
-                        NULL, /* LOGNAME */
-                        NULL
-                };
-
-                envp[2] = strv_find_prefix(environ, "TERM=");
-
-                close_nointr_nofail(master);
-
-                close_nointr(STDIN_FILENO);
-                close_nointr(STDOUT_FILENO);
-                close_nointr(STDERR_FILENO);
-
-                close_all_fds(NULL, 0);
-
-                reset_all_signal_handlers();
-
-                assert_se(sigemptyset(&mask) == 0);
-                assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
-
-                if (setsid() < 0)
-                        goto child_fail;
-
-                if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
-                        goto child_fail;
-
-                /* Mark / as private, in case somebody marked it shared */
-                if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0)
-                        goto child_fail;
-
-                if (mount_all(arg_directory) < 0)
-                        goto child_fail;
-
-                if (copy_devnodes(arg_directory, console) < 0)
-                        goto child_fail;
-
-                if (chdir(arg_directory) < 0) {
-                        log_error("chdir(%s) failed: %m", arg_directory);
-                        goto child_fail;
-                }
-
-                if (open_terminal("dev/console", O_RDWR) != STDIN_FILENO ||
-                    dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO ||
-                    dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO)
-                        goto child_fail;
-
-                if (mount(arg_directory, "/", "bind", MS_BIND|MS_MOVE, NULL) < 0) {
-                        log_error("mount(MS_MOVE) failed: %m");
-                        goto child_fail;
-                }
-
-                if (chroot(".") < 0) {
-                        log_error("chroot() failed: %m");
-                        goto child_fail;
-                }
-
-                if (chdir("/") < 0) {
-                        log_error("chdir() failed: %m");
-                        goto child_fail;
-                }
-
-                umask(0022);
-
-                loopback_setup();
-
-                if (drop_capabilities() < 0)
-                        goto child_fail;
-
-                if (arg_user) {
-
-                        if (get_user_creds((const char**)&arg_user, &uid, &gid, &home) < 0) {
-                                log_error("get_user_creds() failed: %m");
-                                goto child_fail;
-                        }
-
-                        if (mkdir_parents(home, 0775) < 0) {
-                                log_error("mkdir_parents() failed: %m");
-                                goto child_fail;
-                        }
-
-                        if (safe_mkdir(home, 0775, uid, gid) < 0) {
-                                log_error("safe_mkdir() failed: %m");
-                                goto child_fail;
-                        }
-
-                        if (initgroups((const char*)arg_user, gid) < 0) {
-                                log_error("initgroups() failed: %m");
-                                goto child_fail;
-                        }
-
-                        if (setresgid(gid, gid, gid) < 0) {
-                                log_error("setregid() failed: %m");
-                                goto child_fail;
-                        }
-
-                        if (setresuid(uid, uid, uid) < 0) {
-                                log_error("setreuid() failed: %m");
-                                goto child_fail;
-                        }
-                }
-
-                if ((asprintf((char**)(envp + 3), "HOME=%s", home? home: "/root") < 0) ||
-                    (asprintf((char**)(envp + 4), "USER=%s", arg_user? arg_user : "root") < 0) ||
-                    (asprintf((char**)(envp + 5), "LOGNAME=%s", arg_user? arg_user : "root") < 0)) {
-                    log_error("Out of memory");
-                    goto child_fail;
-                }
-
-                if ((hn = file_name_from_path(arg_directory)))
-                        sethostname(hn, strlen(hn));
-
-                if (argc > optind)
-                        execvpe(argv[optind], argv + optind, (char**) envp);
-                else {
-                        chdir(home ? home : "/root");
-                        execle("/bin/bash", "-bash", NULL, (char**) envp);
-                }
-
-                log_error("execv() failed: %m");
-
-        child_fail:
-                _exit(EXIT_FAILURE);
-        }
-
-        if (process_pty(master, &mask) < 0)
-                goto finish;
-
-        if (saved_attr_valid) {
-                tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
-                saved_attr_valid = false;
-        }
-
-        r = wait_for_terminate_and_warn(argc > optind ? argv[optind] : "bash", pid);
-
-        if (r < 0)
-                r = EXIT_FAILURE;
-
-finish:
-        if (saved_attr_valid)
-                tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
-
-        if (master >= 0)
-                close_nointr_nofail(master);
-
-        if (oldcg)
-                cg_attach(SYSTEMD_CGROUP_CONTROLLER, oldcg, 0);
-
-        if (newcg)
-                cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, newcg, true);
-
-        free(arg_directory);
-        strv_free(arg_controllers);
-        free(oldcg);
-        free(newcg);
-
-        return r;
-}
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
new file mode 100644
index 0000000..685b4d4
--- /dev/null
+++ b/src/nspawn/nspawn.c
@@ -0,0 +1,925 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <signal.h>
+#include <sched.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+#include <getopt.h>
+#include <sys/epoll.h>
+#include <termios.h>
+#include <sys/signalfd.h>
+#include <grp.h>
+#include <linux/fs.h>
+
+#include <systemd/sd-daemon.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+#include "audit.h"
+#include "missing.h"
+#include "cgroup-util.h"
+#include "strv.h"
+#include "loopback-setup.h"
+
+static char *arg_directory = NULL;
+static char *arg_user = NULL;
+static char **arg_controllers = NULL;
+static bool arg_private_network = false;
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
+               "Spawn a minimal namespace container for debugging, testing and building.\n\n"
+               "  -h --help             Show this help\n"
+               "  -D --directory=NAME   Root directory for the container\n"
+               "  -u --user=USER        Run the command under specified user or uid\n"
+               "  -C --controllers=LIST Put the container in specified comma-separated cgroup hierarchies\n"
+               "     --private-network  Disable network in container\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_PRIVATE_NETWORK = 0x100
+        };
+
+        static const struct option options[] = {
+                { "help",            no_argument,       NULL, 'h'                 },
+                { "directory",       required_argument, NULL, 'D'                 },
+                { "user",            required_argument, NULL, 'u'                 },
+                { "controllers",     required_argument, NULL, 'C'                 },
+                { "private-network", no_argument,       NULL, ARG_PRIVATE_NETWORK },
+                { NULL,              0,                 NULL, 0                   }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "+hD:u:C:", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case 'D':
+                        free(arg_directory);
+                        if (!(arg_directory = strdup(optarg))) {
+                                log_error("Failed to duplicate root directory.");
+                                return -ENOMEM;
+                        }
+
+                        break;
+
+                case 'u':
+                        free(arg_user);
+                        if (!(arg_user = strdup(optarg))) {
+                                log_error("Failed to duplicate user name.");
+                                return -ENOMEM;
+                        }
+
+                        break;
+
+                case 'C':
+                        strv_free(arg_controllers);
+                        arg_controllers = strv_split(optarg, ",");
+                        if (!arg_controllers) {
+                                log_error("Failed to split controllers list.");
+                                return -ENOMEM;
+                        }
+                        strv_uniq(arg_controllers);
+
+                        break;
+
+                case ARG_PRIVATE_NETWORK:
+                        arg_private_network = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        return 1;
+}
+
+static int mount_all(const char *dest) {
+
+        typedef struct MountPoint {
+                const char *what;
+                const char *where;
+                const char *type;
+                const char *options;
+                unsigned long flags;
+                bool fatal;
+        } MountPoint;
+
+        static const MountPoint mount_table[] = {
+                { "proc",      "/proc",     "proc",  NULL,       MS_NOSUID|MS_NOEXEC|MS_NODEV, true  },
+                { "/proc/sys", "/proc/sys", "bind",  NULL,       MS_BIND, true                       },   /* Bind mount first */
+                { "/proc/sys", "/proc/sys", "bind",  NULL,       MS_BIND|MS_RDONLY|MS_REMOUNT, true  },   /* Then, make it r/o */
+                { "/sys",      "/sys",      "bind",  NULL,       MS_BIND,                      true  },   /* Bind mount first */
+                { "/sys",      "/sys",      "bind",  NULL,       MS_BIND|MS_RDONLY|MS_REMOUNT, true  },   /* Then, make it r/o */
+                { "tmpfs",     "/dev",      "tmpfs", "mode=755", MS_NOSUID|MS_STRICTATIME,     true  },
+                { "/dev/pts",  "/dev/pts",  "bind",  NULL,       MS_BIND,                      true  },
+                { "tmpfs",     "/run",      "tmpfs", "mode=755", MS_NOSUID|MS_NODEV|MS_STRICTATIME, true  },
+#ifdef HAVE_SELINUX
+                { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND,                      false },  /* Bind mount first */
+                { "/sys/fs/selinux", "/sys/fs/selinux", "bind", NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, false },  /* Then, make it r/o */
+#endif
+        };
+
+        unsigned k;
+        int r = 0;
+        char *where;
+
+        for (k = 0; k < ELEMENTSOF(mount_table); k++) {
+                int t;
+
+                if (asprintf(&where, "%s/%s", dest, mount_table[k].where) < 0) {
+                        log_error("Out of memory");
+
+                        if (r == 0)
+                                r = -ENOMEM;
+
+                        break;
+                }
+
+                t = path_is_mount_point(where, false);
+                if (t < 0) {
+                        log_error("Failed to detect whether %s is a mount point: %s", where, strerror(-t));
+                        free(where);
+
+                        if (r == 0)
+                                r = t;
+
+                        continue;
+                }
+
+                mkdir_p(where, 0755);
+
+                if (mount(mount_table[k].what,
+                          where,
+                          mount_table[k].type,
+                          mount_table[k].flags,
+                          mount_table[k].options) < 0 &&
+                    mount_table[k].fatal) {
+
+                        log_error("mount(%s) failed: %m", where);
+
+                        if (r == 0)
+                                r = -errno;
+                }
+
+                free(where);
+        }
+
+        /* Fix the timezone, if possible */
+        if (asprintf(&where, "%s/etc/localtime", dest) >= 0) {
+
+                if (mount("/etc/localtime", where, "bind", MS_BIND, NULL) >= 0)
+                        mount("/etc/localtime", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
+
+                free(where);
+        }
+
+        if (asprintf(&where, "%s/etc/timezone", dest) >= 0) {
+
+                if (mount("/etc/timezone", where, "bind", MS_BIND, NULL) >= 0)
+                        mount("/etc/timezone", where, "bind", MS_BIND|MS_REMOUNT|MS_RDONLY, NULL);
+
+                free(where);
+        }
+
+        if (asprintf(&where, "%s/proc/kmsg", dest) >= 0) {
+                mount("/dev/null", where, "bind", MS_BIND, NULL);
+                free(where);
+        }
+
+        return r;
+}
+
+static int copy_devnodes(const char *dest, const char *console) {
+
+        static const char devnodes[] =
+                "null\0"
+                "zero\0"
+                "full\0"
+                "random\0"
+                "urandom\0"
+                "tty\0"
+                "ptmx\0"
+                "kmsg\0"
+                "rtc0\0";
+
+        const char *d;
+        int r = 0, k;
+        mode_t u;
+        struct stat st;
+        char *from = NULL, *to = NULL;
+
+        assert(dest);
+        assert(console);
+
+        u = umask(0000);
+
+        NULSTR_FOREACH(d, devnodes) {
+                from = to = NULL;
+
+                asprintf(&from, "/dev/%s", d);
+                asprintf(&to, "%s/dev/%s", dest, d);
+
+                if (!from || !to) {
+                        log_error("Failed to allocate devnode path");
+
+                        free(from);
+                        free(to);
+
+                        from = to = NULL;
+
+                        if (r == 0)
+                                r = -ENOMEM;
+
+                        break;
+                }
+
+                if (stat(from, &st) < 0) {
+
+                        if (errno != ENOENT) {
+                                log_error("Failed to stat %s: %m", from);
+                                if (r == 0)
+                                        r = -errno;
+                        }
+
+                } else if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) {
+
+                        log_error("%s is not a char or block device, cannot copy.", from);
+                        if (r == 0)
+                                r = -EIO;
+
+                } else if (mknod(to, st.st_mode, st.st_rdev) < 0) {
+
+                        log_error("mknod(%s) failed: %m", dest);
+                        if (r == 0)
+                                r = -errno;
+                }
+
+                free(from);
+                free(to);
+        }
+
+        if (stat(console, &st) < 0) {
+
+                log_error("Failed to stat %s: %m", console);
+                if (r == 0)
+                        r = -errno;
+
+                goto finish;
+
+        } else if (!S_ISCHR(st.st_mode)) {
+
+                log_error("/dev/console is not a char device.");
+                if (r == 0)
+                        r = -EIO;
+
+                goto finish;
+        }
+
+        if (asprintf(&to, "%s/dev/console", dest) < 0) {
+
+                log_error("Out of memory");
+                if (r == 0)
+                        r = -ENOMEM;
+
+                 goto finish;
+        }
+
+        /* We need to bind mount the right tty to /dev/console since
+         * ptys can only exist on pts file systems. To have something
+         * to bind mount things on we create a device node first, that
+         * has the right major/minor (note that the major minor
+         * doesn't actually matter here, since we mount it over
+         * anyway). */
+
+        if (mknod(to, (st.st_mode & ~07777) | 0600, st.st_rdev) < 0)
+                log_error("mknod for /dev/console failed: %m");
+
+        if (mount(console, to, "bind", MS_BIND, NULL) < 0) {
+                log_error("bind mount for /dev/console failed: %m");
+
+                if (r == 0)
+                        r = -errno;
+        }
+
+        free(to);
+
+        if ((k = chmod_and_chown(console, 0600, 0, 0)) < 0) {
+                log_error("Failed to correct access mode for TTY: %s", strerror(-k));
+
+                if (r == 0)
+                        r = k;
+        }
+
+finish:
+        umask(u);
+
+        return r;
+}
+
+static int drop_capabilities(void) {
+        static const unsigned long retain[] = {
+                CAP_CHOWN,
+                CAP_DAC_OVERRIDE,
+                CAP_DAC_READ_SEARCH,
+                CAP_FOWNER,
+                CAP_FSETID,
+                CAP_IPC_OWNER,
+                CAP_KILL,
+                CAP_LEASE,
+                CAP_LINUX_IMMUTABLE,
+                CAP_NET_BIND_SERVICE,
+                CAP_NET_BROADCAST,
+                CAP_NET_RAW,
+                CAP_SETGID,
+                CAP_SETFCAP,
+                CAP_SETPCAP,
+                CAP_SETUID,
+                CAP_SYS_ADMIN,
+                CAP_SYS_CHROOT,
+                CAP_SYS_NICE,
+                CAP_SYS_PTRACE,
+                CAP_SYS_TTY_CONFIG
+        };
+
+        unsigned long l;
+
+        for (l = 0; l <= cap_last_cap(); l++) {
+                unsigned i;
+
+                for (i = 0; i < ELEMENTSOF(retain); i++)
+                        if (retain[i] == l)
+                                break;
+
+                if (i < ELEMENTSOF(retain))
+                        continue;
+
+                if (prctl(PR_CAPBSET_DROP, l) < 0) {
+                        log_error("PR_CAPBSET_DROP failed: %m");
+                        return -errno;
+                }
+        }
+
+        return 0;
+}
+
+static int is_os_tree(const char *path) {
+        int r;
+        char *p;
+        /* We use /bin/sh as flag file if something is an OS */
+
+        if (asprintf(&p, "%s/bin/sh", path) < 0)
+                return -ENOMEM;
+
+        r = access(p, F_OK);
+        free(p);
+
+        return r < 0 ? 0 : 1;
+}
+
+static int process_pty(int master, sigset_t *mask) {
+
+        char in_buffer[LINE_MAX], out_buffer[LINE_MAX];
+        size_t in_buffer_full = 0, out_buffer_full = 0;
+        struct epoll_event stdin_ev, stdout_ev, master_ev, signal_ev;
+        bool stdin_readable = false, stdout_writable = false, master_readable = false, master_writable = false;
+        int ep = -1, signal_fd = -1, r;
+
+        fd_nonblock(STDIN_FILENO, 1);
+        fd_nonblock(STDOUT_FILENO, 1);
+        fd_nonblock(master, 1);
+
+        if ((signal_fd = signalfd(-1, mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
+                log_error("signalfd(): %m");
+                r = -errno;
+                goto finish;
+        }
+
+        if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) {
+                log_error("Failed to create epoll: %m");
+                r = -errno;
+                goto finish;
+        }
+
+        zero(stdin_ev);
+        stdin_ev.events = EPOLLIN|EPOLLET;
+        stdin_ev.data.fd = STDIN_FILENO;
+
+        zero(stdout_ev);
+        stdout_ev.events = EPOLLOUT|EPOLLET;
+        stdout_ev.data.fd = STDOUT_FILENO;
+
+        zero(master_ev);
+        master_ev.events = EPOLLIN|EPOLLOUT|EPOLLET;
+        master_ev.data.fd = master;
+
+        zero(signal_ev);
+        signal_ev.events = EPOLLIN;
+        signal_ev.data.fd = signal_fd;
+
+        if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 ||
+            epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 ||
+            epoll_ctl(ep, EPOLL_CTL_ADD, master, &master_ev) < 0 ||
+            epoll_ctl(ep, EPOLL_CTL_ADD, signal_fd, &signal_ev) < 0) {
+                log_error("Failed to regiser fds in epoll: %m");
+                r = -errno;
+                goto finish;
+        }
+
+        for (;;) {
+                struct epoll_event ev[16];
+                ssize_t k;
+                int i, nfds;
+
+                if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) {
+
+                        if (errno == EINTR || errno == EAGAIN)
+                                continue;
+
+                        log_error("epoll_wait(): %m");
+                        r = -errno;
+                        goto finish;
+                }
+
+                assert(nfds >= 1);
+
+                for (i = 0; i < nfds; i++) {
+                        if (ev[i].data.fd == STDIN_FILENO) {
+
+                                if (ev[i].events & (EPOLLIN|EPOLLHUP))
+                                        stdin_readable = true;
+
+                        } else if (ev[i].data.fd == STDOUT_FILENO) {
+
+                                if (ev[i].events & (EPOLLOUT|EPOLLHUP))
+                                        stdout_writable = true;
+
+                        } else if (ev[i].data.fd == master) {
+
+                                if (ev[i].events & (EPOLLIN|EPOLLHUP))
+                                        master_readable = true;
+
+                                if (ev[i].events & (EPOLLOUT|EPOLLHUP))
+                                        master_writable = true;
+
+                        } else if (ev[i].data.fd == signal_fd) {
+                                struct signalfd_siginfo sfsi;
+                                ssize_t n;
+
+                                if ((n = read(signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
+
+                                        if (n >= 0) {
+                                                log_error("Failed to read from signalfd: invalid block size");
+                                                r = -EIO;
+                                                goto finish;
+                                        }
+
+                                        if (errno != EINTR && errno != EAGAIN) {
+                                                log_error("Failed to read from signalfd: %m");
+                                                r = -errno;
+                                                goto finish;
+                                        }
+                                } else {
+
+                                        if (sfsi.ssi_signo == SIGWINCH) {
+                                                struct winsize ws;
+
+                                                /* The window size changed, let's forward that. */
+                                                if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
+                                                        ioctl(master, TIOCSWINSZ, &ws);
+                                        } else {
+                                                r = 0;
+                                                goto finish;
+                                        }
+                                }
+                        }
+                }
+
+                while ((stdin_readable && in_buffer_full <= 0) ||
+                       (master_writable && in_buffer_full > 0) ||
+                       (master_readable && out_buffer_full <= 0) ||
+                       (stdout_writable && out_buffer_full > 0)) {
+
+                        if (stdin_readable && in_buffer_full < LINE_MAX) {
+
+                                if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, LINE_MAX - in_buffer_full)) < 0) {
+
+                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+                                                stdin_readable = false;
+                                        else {
+                                                log_error("read(): %m");
+                                                r = -errno;
+                                                goto finish;
+                                        }
+                                } else
+                                        in_buffer_full += (size_t) k;
+                        }
+
+                        if (master_writable && in_buffer_full > 0) {
+
+                                if ((k = write(master, in_buffer, in_buffer_full)) < 0) {
+
+                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+                                                master_writable = false;
+                                        else {
+                                                log_error("write(): %m");
+                                                r = -errno;
+                                                goto finish;
+                                        }
+
+                                } else {
+                                        assert(in_buffer_full >= (size_t) k);
+                                        memmove(in_buffer, in_buffer + k, in_buffer_full - k);
+                                        in_buffer_full -= k;
+                                }
+                        }
+
+                        if (master_readable && out_buffer_full < LINE_MAX) {
+
+                                if ((k = read(master, out_buffer + out_buffer_full, LINE_MAX - out_buffer_full)) < 0) {
+
+                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+                                                master_readable = false;
+                                        else {
+                                                log_error("read(): %m");
+                                                r = -errno;
+                                                goto finish;
+                                        }
+                                }  else
+                                        out_buffer_full += (size_t) k;
+                        }
+
+                        if (stdout_writable && out_buffer_full > 0) {
+
+                                if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) {
+
+                                        if (errno == EAGAIN || errno == EPIPE || errno == ECONNRESET || errno == EIO)
+                                                stdout_writable = false;
+                                        else {
+                                                log_error("write(): %m");
+                                                r = -errno;
+                                                goto finish;
+                                        }
+
+                                } else {
+                                        assert(out_buffer_full >= (size_t) k);
+                                        memmove(out_buffer, out_buffer + k, out_buffer_full - k);
+                                        out_buffer_full -= k;
+                                }
+                        }
+                }
+        }
+
+finish:
+        if (ep >= 0)
+                close_nointr_nofail(ep);
+
+        if (signal_fd >= 0)
+                close_nointr_nofail(signal_fd);
+
+        return r;
+}
+
+int main(int argc, char *argv[]) {
+        pid_t pid = 0;
+        int r = EXIT_FAILURE, k;
+        char *oldcg = NULL, *newcg = NULL;
+        char **controller = NULL;
+        int master = -1;
+        const char *console = NULL;
+        struct termios saved_attr, raw_attr;
+        sigset_t mask;
+        bool saved_attr_valid = false;
+        struct winsize ws;
+
+        log_parse_environment();
+        log_open();
+
+        if ((r = parse_argv(argc, argv)) <= 0)
+                goto finish;
+
+        if (arg_directory) {
+                char *p;
+
+                p = path_make_absolute_cwd(arg_directory);
+                free(arg_directory);
+                arg_directory = p;
+        } else
+                arg_directory = get_current_dir_name();
+
+        if (!arg_directory) {
+                log_error("Failed to determine path");
+                goto finish;
+        }
+
+        path_kill_slashes(arg_directory);
+
+        if (geteuid() != 0) {
+                log_error("Need to be root.");
+                goto finish;
+        }
+
+        if (sd_booted() <= 0) {
+                log_error("Not running on a systemd system.");
+                goto finish;
+        }
+
+        if (path_equal(arg_directory, "/")) {
+                log_error("Spawning container on root directory not supported.");
+                goto finish;
+        }
+
+        if (is_os_tree(arg_directory) <= 0) {
+                log_error("Directory %s doesn't look like an OS root directory. Refusing.", arg_directory);
+                goto finish;
+        }
+
+        if ((k = cg_get_by_pid(SYSTEMD_CGROUP_CONTROLLER, 0, &oldcg)) < 0) {
+                log_error("Failed to determine current cgroup: %s", strerror(-k));
+                goto finish;
+        }
+
+        if (asprintf(&newcg, "%s/nspawn-%lu", oldcg, (unsigned long) getpid()) < 0) {
+                log_error("Failed to allocate cgroup path.");
+                goto finish;
+        }
+
+        k = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, newcg, 0);
+        if (k < 0)  {
+                log_error("Failed to create cgroup: %s", strerror(-k));
+                goto finish;
+        }
+
+        STRV_FOREACH(controller,arg_controllers) {
+                k = cg_create_and_attach(*controller, newcg, 0);
+                if (k < 0)
+                        log_warning("Failed to create cgroup in controller %s: %s", *controller, strerror(-k));
+        }
+
+        if ((master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NDELAY)) < 0) {
+                log_error("Failed to acquire pseudo tty: %m");
+                goto finish;
+        }
+
+        if (!(console = ptsname(master))) {
+                log_error("Failed to determine tty name: %m");
+                goto finish;
+        }
+
+        log_info("Spawning namespace container on %s (console is %s).", arg_directory, console);
+
+        if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
+                ioctl(master, TIOCSWINSZ, &ws);
+
+        if (unlockpt(master) < 0) {
+                log_error("Failed to unlock tty: %m");
+                goto finish;
+        }
+
+        if (tcgetattr(STDIN_FILENO, &saved_attr) < 0) {
+                log_error("Failed to get terminal attributes: %m");
+                goto finish;
+        }
+
+        saved_attr_valid = true;
+
+        raw_attr = saved_attr;
+        cfmakeraw(&raw_attr);
+        raw_attr.c_lflag &= ~ECHO;
+
+        if (tcsetattr(STDIN_FILENO, TCSANOW, &raw_attr) < 0) {
+                log_error("Failed to set terminal attributes: %m");
+                goto finish;
+        }
+
+        assert_se(sigemptyset(&mask) == 0);
+        sigset_add_many(&mask, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, -1);
+        assert_se(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
+
+        pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWIPC|CLONE_NEWNS|CLONE_NEWPID|CLONE_NEWUTS|(arg_private_network ? CLONE_NEWNET : 0), NULL);
+        if (pid < 0) {
+                if (errno == EINVAL)
+                        log_error("clone() failed, do you have namespace support enabled in your kernel? (You need UTS, IPC, PID and NET namespacing built in): %m");
+                else
+                        log_error("clone() failed: %m");
+
+                goto finish;
+        }
+
+        if (pid == 0) {
+                /* child */
+
+                const char *hn;
+                const char *home = NULL;
+                uid_t uid = (uid_t) -1;
+                gid_t gid = (gid_t) -1;
+                const char *envp[] = {
+                        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
+                        "container=systemd-nspawn", /* LXC sets container=lxc, so follow the scheme here */
+                        NULL, /* TERM */
+                        NULL, /* HOME */
+                        NULL, /* USER */
+                        NULL, /* LOGNAME */
+                        NULL
+                };
+
+                envp[2] = strv_find_prefix(environ, "TERM=");
+
+                close_nointr_nofail(master);
+
+                close_nointr(STDIN_FILENO);
+                close_nointr(STDOUT_FILENO);
+                close_nointr(STDERR_FILENO);
+
+                close_all_fds(NULL, 0);
+
+                reset_all_signal_handlers();
+
+                assert_se(sigemptyset(&mask) == 0);
+                assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+
+                if (setsid() < 0)
+                        goto child_fail;
+
+                if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
+                        goto child_fail;
+
+                /* Mark / as private, in case somebody marked it shared */
+                if (mount(NULL, "/", NULL, MS_PRIVATE|MS_REC, NULL) < 0)
+                        goto child_fail;
+
+                if (mount_all(arg_directory) < 0)
+                        goto child_fail;
+
+                if (copy_devnodes(arg_directory, console) < 0)
+                        goto child_fail;
+
+                if (chdir(arg_directory) < 0) {
+                        log_error("chdir(%s) failed: %m", arg_directory);
+                        goto child_fail;
+                }
+
+                if (open_terminal("dev/console", O_RDWR) != STDIN_FILENO ||
+                    dup2(STDIN_FILENO, STDOUT_FILENO) != STDOUT_FILENO ||
+                    dup2(STDIN_FILENO, STDERR_FILENO) != STDERR_FILENO)
+                        goto child_fail;
+
+                if (mount(arg_directory, "/", "bind", MS_BIND|MS_MOVE, NULL) < 0) {
+                        log_error("mount(MS_MOVE) failed: %m");
+                        goto child_fail;
+                }
+
+                if (chroot(".") < 0) {
+                        log_error("chroot() failed: %m");
+                        goto child_fail;
+                }
+
+                if (chdir("/") < 0) {
+                        log_error("chdir() failed: %m");
+                        goto child_fail;
+                }
+
+                umask(0022);
+
+                loopback_setup();
+
+                if (drop_capabilities() < 0)
+                        goto child_fail;
+
+                if (arg_user) {
+
+                        if (get_user_creds((const char**)&arg_user, &uid, &gid, &home) < 0) {
+                                log_error("get_user_creds() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (mkdir_parents(home, 0775) < 0) {
+                                log_error("mkdir_parents() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (safe_mkdir(home, 0775, uid, gid) < 0) {
+                                log_error("safe_mkdir() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (initgroups((const char*)arg_user, gid) < 0) {
+                                log_error("initgroups() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (setresgid(gid, gid, gid) < 0) {
+                                log_error("setregid() failed: %m");
+                                goto child_fail;
+                        }
+
+                        if (setresuid(uid, uid, uid) < 0) {
+                                log_error("setreuid() failed: %m");
+                                goto child_fail;
+                        }
+                }
+
+                if ((asprintf((char**)(envp + 3), "HOME=%s", home? home: "/root") < 0) ||
+                    (asprintf((char**)(envp + 4), "USER=%s", arg_user? arg_user : "root") < 0) ||
+                    (asprintf((char**)(envp + 5), "LOGNAME=%s", arg_user? arg_user : "root") < 0)) {
+                    log_error("Out of memory");
+                    goto child_fail;
+                }
+
+                if ((hn = file_name_from_path(arg_directory)))
+                        sethostname(hn, strlen(hn));
+
+                if (argc > optind)
+                        execvpe(argv[optind], argv + optind, (char**) envp);
+                else {
+                        chdir(home ? home : "/root");
+                        execle("/bin/bash", "-bash", NULL, (char**) envp);
+                }
+
+                log_error("execv() failed: %m");
+
+        child_fail:
+                _exit(EXIT_FAILURE);
+        }
+
+        if (process_pty(master, &mask) < 0)
+                goto finish;
+
+        if (saved_attr_valid) {
+                tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
+                saved_attr_valid = false;
+        }
+
+        r = wait_for_terminate_and_warn(argc > optind ? argv[optind] : "bash", pid);
+
+        if (r < 0)
+                r = EXIT_FAILURE;
+
+finish:
+        if (saved_attr_valid)
+                tcsetattr(STDIN_FILENO, TCSANOW, &saved_attr);
+
+        if (master >= 0)
+                close_nointr_nofail(master);
+
+        if (oldcg)
+                cg_attach(SYSTEMD_CGROUP_CONTROLLER, oldcg, 0);
+
+        if (newcg)
+                cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, newcg, true);
+
+        free(arg_directory);
+        strv_free(arg_controllers);
+        free(oldcg);
+        free(newcg);
+
+        return r;
+}
diff --git a/src/quotacheck.c b/src/quotacheck.c
deleted file mode 100644
index e4420ee..0000000
--- a/src/quotacheck.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <stdio.h>
-#include <stdbool.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include "util.h"
-#include "virt.h"
-
-static bool arg_skip = false;
-static bool arg_force = false;
-
-static int parse_proc_cmdline(void) {
-        char *line, *w, *state;
-        int r;
-        size_t l;
-
-        if (detect_container(NULL) > 0)
-                return 0;
-
-        if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
-                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
-                return 0;
-        }
-
-        FOREACH_WORD_QUOTED(w, l, line, state) {
-
-                if (strneq(w, "quotacheck.mode=auto", l))
-                        arg_force = arg_skip = false;
-                else if (strneq(w, "quotacheck.mode=force", l))
-                        arg_force = true;
-                else if (strneq(w, "quotacheck.mode=skip", l))
-                        arg_skip = true;
-                else if (startswith(w, "quotacheck.mode"))
-                        log_warning("Invalid quotacheck.mode= parameter. Ignoring.");
-#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-                else if (strneq(w, "forcequotacheck", l))
-                        arg_force = true;
-#endif
-        }
-
-        free(line);
-        return 0;
-}
-
-static void test_files(void) {
-#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-        /* This exists only on Fedora, Mandriva or Mageia */
-        if (access("/forcequotacheck", F_OK) >= 0)
-                arg_force = true;
-#endif
-}
-
-int main(int argc, char *argv[]) {
-        static const char * const cmdline[] = {
-                "/sbin/quotacheck",
-                "-anug",
-                NULL
-        };
-
-        int r = EXIT_FAILURE;
-        pid_t pid;
-
-        if (argc > 1) {
-                log_error("This program takes no arguments.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        parse_proc_cmdline();
-        test_files();
-
-        if (!arg_force) {
-                if (arg_skip)
-                        return 0;
-
-                if (access("/run/systemd/quotacheck", F_OK) < 0)
-                        return 0;
-        }
-
-        if ((pid = fork()) < 0) {
-                log_error("fork(): %m");
-                goto finish;
-        } else if (pid == 0) {
-                /* Child */
-                execv(cmdline[0], (char**) cmdline);
-                _exit(1); /* Operational error */
-        }
-
-        r = wait_for_terminate_and_warn("quotacheck", pid) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
-
-finish:
-        return r;
-}
diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c
new file mode 100644
index 0000000..e4420ee
--- /dev/null
+++ b/src/quotacheck/quotacheck.c
@@ -0,0 +1,120 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "virt.h"
+
+static bool arg_skip = false;
+static bool arg_force = false;
+
+static int parse_proc_cmdline(void) {
+        char *line, *w, *state;
+        int r;
+        size_t l;
+
+        if (detect_container(NULL) > 0)
+                return 0;
+
+        if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
+                log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
+                return 0;
+        }
+
+        FOREACH_WORD_QUOTED(w, l, line, state) {
+
+                if (strneq(w, "quotacheck.mode=auto", l))
+                        arg_force = arg_skip = false;
+                else if (strneq(w, "quotacheck.mode=force", l))
+                        arg_force = true;
+                else if (strneq(w, "quotacheck.mode=skip", l))
+                        arg_skip = true;
+                else if (startswith(w, "quotacheck.mode"))
+                        log_warning("Invalid quotacheck.mode= parameter. Ignoring.");
+#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+                else if (strneq(w, "forcequotacheck", l))
+                        arg_force = true;
+#endif
+        }
+
+        free(line);
+        return 0;
+}
+
+static void test_files(void) {
+#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+        /* This exists only on Fedora, Mandriva or Mageia */
+        if (access("/forcequotacheck", F_OK) >= 0)
+                arg_force = true;
+#endif
+}
+
+int main(int argc, char *argv[]) {
+        static const char * const cmdline[] = {
+                "/sbin/quotacheck",
+                "-anug",
+                NULL
+        };
+
+        int r = EXIT_FAILURE;
+        pid_t pid;
+
+        if (argc > 1) {
+                log_error("This program takes no arguments.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        parse_proc_cmdline();
+        test_files();
+
+        if (!arg_force) {
+                if (arg_skip)
+                        return 0;
+
+                if (access("/run/systemd/quotacheck", F_OK) < 0)
+                        return 0;
+        }
+
+        if ((pid = fork()) < 0) {
+                log_error("fork(): %m");
+                goto finish;
+        } else if (pid == 0) {
+                /* Child */
+                execv(cmdline[0], (char**) cmdline);
+                _exit(1); /* Operational error */
+        }
+
+        r = wait_for_terminate_and_warn("quotacheck", pid) == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+
+finish:
+        return r;
+}
diff --git a/src/random-seed.c b/src/random-seed.c
deleted file mode 100644
index d1cab8b..0000000
--- a/src/random-seed.c
+++ /dev/null
@@ -1,148 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-
-#define POOL_SIZE_MIN 512
-
-int main(int argc, char *argv[]) {
-        int seed_fd = -1, random_fd = -1;
-        int ret = EXIT_FAILURE;
-        void* buf;
-        size_t buf_size = 0;
-        ssize_t r;
-        FILE *f;
-
-        if (argc != 2) {
-                log_error("This program requires one argument.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        /* Read pool size, if possible */
-        if ((f = fopen("/proc/sys/kernel/random/poolsize", "re"))) {
-                if (fscanf(f, "%zu", &buf_size) > 0) {
-                        /* poolsize is in bits on 2.6, but we want bytes */
-                        buf_size /= 8;
-                }
-
-                fclose(f);
-        }
-
-        if (buf_size <= POOL_SIZE_MIN)
-                buf_size = POOL_SIZE_MIN;
-
-        if (!(buf = malloc(buf_size))) {
-                log_error("Failed to allocate buffer.");
-                goto finish;
-        }
-
-        if (mkdir_parents(RANDOM_SEED, 0755) < 0) {
-                log_error("Failed to create directories parents of %s: %m", RANDOM_SEED);
-                goto finish;
-        }
-
-        /* When we load the seed we read it and write it to the device
-         * and then immediately update the saved seed with new data,
-         * to make sure the next boot gets seeded differently. */
-
-        if (streq(argv[1], "load")) {
-
-                if ((seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) {
-                        if ((seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) {
-                                log_error("Failed to open random seed: %m");
-                                goto finish;
-                        }
-                }
-
-                if ((random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600)) < 0) {
-                        if ((random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600)) < 0) {
-                                log_error("Failed to open /dev/urandom: %m");
-                                goto finish;
-                        }
-                }
-
-                if ((r = loop_read(seed_fd, buf, buf_size, false)) <= 0) {
-
-                        if (r != 0)
-                                log_error("Failed to read seed file: %m");
-                } else {
-                        lseek(seed_fd, 0, SEEK_SET);
-
-                        if ((r = loop_write(random_fd, buf, (size_t) r, false)) <= 0)
-                                log_error("Failed to write seed to /dev/random: %s", r < 0 ? strerror(errno) : "short write");
-                }
-
-        } else if (streq(argv[1], "save")) {
-
-                if ((seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) {
-                        log_error("Failed to open random seed: %m");
-                        goto finish;
-                }
-
-                if ((random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) {
-                        log_error("Failed to open /dev/urandom: %m");
-                        goto finish;
-                }
-        } else {
-                log_error("Unknown verb %s.", argv[1]);
-                goto finish;
-        }
-
-        /* This is just a safety measure. Given that we are root and
-         * most likely created the file ourselves the mode and owner
-         * should be correct anyway. */
-        fchmod(seed_fd, 0600);
-        fchown(seed_fd, 0, 0);
-
-        if ((r = loop_read(random_fd, buf, buf_size, false)) <= 0)
-                log_error("Failed to read new seed from /dev/urandom: %s", r < 0 ? strerror(errno) : "EOF");
-        else {
-                if ((r = loop_write(seed_fd, buf, (size_t) r, false)) <= 0)
-                        log_error("Failed to write new random seed file: %s", r < 0 ? strerror(errno) : "short write");
-        }
-
-        ret = EXIT_SUCCESS;
-
-finish:
-        if (random_fd >= 0)
-                close_nointr_nofail(random_fd);
-
-        if (seed_fd >= 0)
-                close_nointr_nofail(seed_fd);
-
-        free(buf);
-
-        return ret;
-}
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
new file mode 100644
index 0000000..d1cab8b
--- /dev/null
+++ b/src/random-seed/random-seed.c
@@ -0,0 +1,148 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+
+#define POOL_SIZE_MIN 512
+
+int main(int argc, char *argv[]) {
+        int seed_fd = -1, random_fd = -1;
+        int ret = EXIT_FAILURE;
+        void* buf;
+        size_t buf_size = 0;
+        ssize_t r;
+        FILE *f;
+
+        if (argc != 2) {
+                log_error("This program requires one argument.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        /* Read pool size, if possible */
+        if ((f = fopen("/proc/sys/kernel/random/poolsize", "re"))) {
+                if (fscanf(f, "%zu", &buf_size) > 0) {
+                        /* poolsize is in bits on 2.6, but we want bytes */
+                        buf_size /= 8;
+                }
+
+                fclose(f);
+        }
+
+        if (buf_size <= POOL_SIZE_MIN)
+                buf_size = POOL_SIZE_MIN;
+
+        if (!(buf = malloc(buf_size))) {
+                log_error("Failed to allocate buffer.");
+                goto finish;
+        }
+
+        if (mkdir_parents(RANDOM_SEED, 0755) < 0) {
+                log_error("Failed to create directories parents of %s: %m", RANDOM_SEED);
+                goto finish;
+        }
+
+        /* When we load the seed we read it and write it to the device
+         * and then immediately update the saved seed with new data,
+         * to make sure the next boot gets seeded differently. */
+
+        if (streq(argv[1], "load")) {
+
+                if ((seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) {
+                        if ((seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) {
+                                log_error("Failed to open random seed: %m");
+                                goto finish;
+                        }
+                }
+
+                if ((random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600)) < 0) {
+                        if ((random_fd = open("/dev/urandom", O_WRONLY|O_CLOEXEC|O_NOCTTY, 0600)) < 0) {
+                                log_error("Failed to open /dev/urandom: %m");
+                                goto finish;
+                        }
+                }
+
+                if ((r = loop_read(seed_fd, buf, buf_size, false)) <= 0) {
+
+                        if (r != 0)
+                                log_error("Failed to read seed file: %m");
+                } else {
+                        lseek(seed_fd, 0, SEEK_SET);
+
+                        if ((r = loop_write(random_fd, buf, (size_t) r, false)) <= 0)
+                                log_error("Failed to write seed to /dev/random: %s", r < 0 ? strerror(errno) : "short write");
+                }
+
+        } else if (streq(argv[1], "save")) {
+
+                if ((seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600)) < 0) {
+                        log_error("Failed to open random seed: %m");
+                        goto finish;
+                }
+
+                if ((random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0) {
+                        log_error("Failed to open /dev/urandom: %m");
+                        goto finish;
+                }
+        } else {
+                log_error("Unknown verb %s.", argv[1]);
+                goto finish;
+        }
+
+        /* This is just a safety measure. Given that we are root and
+         * most likely created the file ourselves the mode and owner
+         * should be correct anyway. */
+        fchmod(seed_fd, 0600);
+        fchown(seed_fd, 0, 0);
+
+        if ((r = loop_read(random_fd, buf, buf_size, false)) <= 0)
+                log_error("Failed to read new seed from /dev/urandom: %s", r < 0 ? strerror(errno) : "EOF");
+        else {
+                if ((r = loop_write(seed_fd, buf, (size_t) r, false)) <= 0)
+                        log_error("Failed to write new random seed file: %s", r < 0 ? strerror(errno) : "short write");
+        }
+
+        ret = EXIT_SUCCESS;
+
+finish:
+        if (random_fd >= 0)
+                close_nointr_nofail(random_fd);
+
+        if (seed_fd >= 0)
+                close_nointr_nofail(seed_fd);
+
+        free(buf);
+
+        return ret;
+}
diff --git a/src/rc-local-generator.c b/src/rc-local-generator.c
deleted file mode 100644
index 42d7ae4..0000000
--- a/src/rc-local-generator.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-  Copyright 2011 Michal Schmidt
-
-  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 <stdio.h>
-#include <unistd.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-
-#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
-#define SCRIPT_PATH "/etc/rc.d/rc.local"
-#elif defined(TARGET_SUSE)
-#define SCRIPT_PATH "/etc/init.d/boot.local"
-#endif
-
-const char *arg_dest = "/tmp";
-
-static int add_symlink(const char *service) {
-        char *from = NULL, *to = NULL;
-        int r;
-
-        assert(service);
-
-        asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", service);
-        asprintf(&to, "%s/multi-user.target.wants/%s", arg_dest, service);
-
-        if (!from || !to) {
-                log_error("Out of memory");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        mkdir_parents(to, 0755);
-
-        r = symlink(from, to);
-        if (r < 0) {
-                if (errno == EEXIST)
-                        r = 0;
-                else {
-                        log_error("Failed to create symlink from %s to %s: %m", from, to);
-                        r = -errno;
-                }
-        }
-
-finish:
-
-        free(from);
-        free(to);
-
-        return r;
-}
-
-static bool file_is_executable(const char *f) {
-        struct stat st;
-
-        if (stat(f, &st) < 0)
-                return false;
-
-        return S_ISREG(st.st_mode) && (st.st_mode & 0111);
-}
-
-int main(int argc, char *argv[]) {
-
-        int r = EXIT_SUCCESS;
-
-        if (argc > 2) {
-                log_error("This program takes one or no arguments.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        if (argc > 1)
-                arg_dest = argv[1];
-
-        if (file_is_executable(SCRIPT_PATH)) {
-                log_debug("Automatically adding rc-local.service.");
-
-                if (add_symlink("rc-local.service") < 0)
-                        r = EXIT_FAILURE;
-
-        }
-
-        return r;
-}
diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c
new file mode 100644
index 0000000..42d7ae4
--- /dev/null
+++ b/src/rc-local-generator/rc-local-generator.c
@@ -0,0 +1,108 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+  Copyright 2011 Michal Schmidt
+
+  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 <stdio.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+
+#if defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA)
+#define SCRIPT_PATH "/etc/rc.d/rc.local"
+#elif defined(TARGET_SUSE)
+#define SCRIPT_PATH "/etc/init.d/boot.local"
+#endif
+
+const char *arg_dest = "/tmp";
+
+static int add_symlink(const char *service) {
+        char *from = NULL, *to = NULL;
+        int r;
+
+        assert(service);
+
+        asprintf(&from, SYSTEM_DATA_UNIT_PATH "/%s", service);
+        asprintf(&to, "%s/multi-user.target.wants/%s", arg_dest, service);
+
+        if (!from || !to) {
+                log_error("Out of memory");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        mkdir_parents(to, 0755);
+
+        r = symlink(from, to);
+        if (r < 0) {
+                if (errno == EEXIST)
+                        r = 0;
+                else {
+                        log_error("Failed to create symlink from %s to %s: %m", from, to);
+                        r = -errno;
+                }
+        }
+
+finish:
+
+        free(from);
+        free(to);
+
+        return r;
+}
+
+static bool file_is_executable(const char *f) {
+        struct stat st;
+
+        if (stat(f, &st) < 0)
+                return false;
+
+        return S_ISREG(st.st_mode) && (st.st_mode & 0111);
+}
+
+int main(int argc, char *argv[]) {
+
+        int r = EXIT_SUCCESS;
+
+        if (argc > 2) {
+                log_error("This program takes one or no arguments.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        if (argc > 1)
+                arg_dest = argv[1];
+
+        if (file_is_executable(SCRIPT_PATH)) {
+                log_debug("Automatically adding rc-local.service.");
+
+                if (add_symlink("rc-local.service") < 0)
+                        r = EXIT_FAILURE;
+
+        }
+
+        return r;
+}
diff --git a/src/remount-api-vfs.c b/src/remount-api-vfs.c
deleted file mode 100644
index 6cb77c1..0000000
--- a/src/remount-api-vfs.c
+++ /dev/null
@@ -1,161 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <mntent.h>
-
-#include "log.h"
-#include "util.h"
-#include "set.h"
-#include "mount-setup.h"
-#include "exit-status.h"
-
-/* Goes through /etc/fstab and remounts all API file systems, applying
- * options that are in /etc/fstab that systemd might not have
- * respected */
-
-int main(int argc, char *argv[]) {
-        int ret = EXIT_FAILURE;
-        FILE *f = NULL;
-        struct mntent* me;
-        Hashmap *pids = NULL;
-
-        if (argc > 1) {
-                log_error("This program takes no argument.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        f = setmntent("/etc/fstab", "r");
-        if (!f) {
-                log_error("Failed to open /etc/fstab: %m");
-                goto finish;
-        }
-
-        pids = hashmap_new(trivial_hash_func, trivial_compare_func);
-        if (!pids) {
-                log_error("Failed to allocate set");
-                goto finish;
-        }
-
-        ret = EXIT_SUCCESS;
-
-        while ((me = getmntent(f))) {
-                pid_t pid;
-                int k;
-                char *s;
-
-                if (!mount_point_is_api(me->mnt_dir))
-                        continue;
-
-                log_debug("Remounting %s", me->mnt_dir);
-
-                pid = fork();
-                if (pid < 0) {
-                        log_error("Failed to fork: %m");
-                        ret = EXIT_FAILURE;
-                        continue;
-                }
-
-                if (pid == 0) {
-                        const char *arguments[5];
-                        /* Child */
-
-                        arguments[0] = "/bin/mount";
-                        arguments[1] = me->mnt_dir;
-                        arguments[2] = "-o";
-                        arguments[3] = "remount";
-                        arguments[4] = NULL;
-
-                        execv("/bin/mount", (char **) arguments);
-
-                        log_error("Failed to execute /bin/mount: %m");
-                        _exit(EXIT_FAILURE);
-                }
-
-                /* Parent */
-
-                s = strdup(me->mnt_dir);
-                if (!s) {
-                        log_error("Out of memory.");
-                        ret = EXIT_FAILURE;
-                        continue;
-                }
-
-
-                k = hashmap_put(pids, UINT_TO_PTR(pid), s);
-                if (k < 0) {
-                        log_error("Failed to add PID to set: %s", strerror(-k));
-                        ret = EXIT_FAILURE;
-                        continue;
-                }
-        }
-
-        while (!hashmap_isempty(pids)) {
-                siginfo_t si;
-                char *s;
-
-                zero(si);
-                if (waitid(P_ALL, 0, &si, WEXITED) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        log_error("waitid() failed: %m");
-                        ret = EXIT_FAILURE;
-                        break;
-                }
-
-                s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid));
-                if (s) {
-                        if (!is_clean_exit(si.si_code, si.si_status)) {
-                                if (si.si_code == CLD_EXITED)
-                                        log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status);
-                                else
-                                        log_error("/bin/mount for %s terminated by signal %s.", s, signal_to_string(si.si_status));
-
-                                ret = EXIT_FAILURE;
-                        }
-
-                        free(s);
-                }
-        }
-
-finish:
-
-        if (pids)
-                hashmap_free_free(pids);
-
-        if (f)
-                endmntent(f);
-
-        return ret;
-}
diff --git a/src/remount-api-vfs/remount-api-vfs.c b/src/remount-api-vfs/remount-api-vfs.c
new file mode 100644
index 0000000..6cb77c1
--- /dev/null
+++ b/src/remount-api-vfs/remount-api-vfs.c
@@ -0,0 +1,161 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <mntent.h>
+
+#include "log.h"
+#include "util.h"
+#include "set.h"
+#include "mount-setup.h"
+#include "exit-status.h"
+
+/* Goes through /etc/fstab and remounts all API file systems, applying
+ * options that are in /etc/fstab that systemd might not have
+ * respected */
+
+int main(int argc, char *argv[]) {
+        int ret = EXIT_FAILURE;
+        FILE *f = NULL;
+        struct mntent* me;
+        Hashmap *pids = NULL;
+
+        if (argc > 1) {
+                log_error("This program takes no argument.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        f = setmntent("/etc/fstab", "r");
+        if (!f) {
+                log_error("Failed to open /etc/fstab: %m");
+                goto finish;
+        }
+
+        pids = hashmap_new(trivial_hash_func, trivial_compare_func);
+        if (!pids) {
+                log_error("Failed to allocate set");
+                goto finish;
+        }
+
+        ret = EXIT_SUCCESS;
+
+        while ((me = getmntent(f))) {
+                pid_t pid;
+                int k;
+                char *s;
+
+                if (!mount_point_is_api(me->mnt_dir))
+                        continue;
+
+                log_debug("Remounting %s", me->mnt_dir);
+
+                pid = fork();
+                if (pid < 0) {
+                        log_error("Failed to fork: %m");
+                        ret = EXIT_FAILURE;
+                        continue;
+                }
+
+                if (pid == 0) {
+                        const char *arguments[5];
+                        /* Child */
+
+                        arguments[0] = "/bin/mount";
+                        arguments[1] = me->mnt_dir;
+                        arguments[2] = "-o";
+                        arguments[3] = "remount";
+                        arguments[4] = NULL;
+
+                        execv("/bin/mount", (char **) arguments);
+
+                        log_error("Failed to execute /bin/mount: %m");
+                        _exit(EXIT_FAILURE);
+                }
+
+                /* Parent */
+
+                s = strdup(me->mnt_dir);
+                if (!s) {
+                        log_error("Out of memory.");
+                        ret = EXIT_FAILURE;
+                        continue;
+                }
+
+
+                k = hashmap_put(pids, UINT_TO_PTR(pid), s);
+                if (k < 0) {
+                        log_error("Failed to add PID to set: %s", strerror(-k));
+                        ret = EXIT_FAILURE;
+                        continue;
+                }
+        }
+
+        while (!hashmap_isempty(pids)) {
+                siginfo_t si;
+                char *s;
+
+                zero(si);
+                if (waitid(P_ALL, 0, &si, WEXITED) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        log_error("waitid() failed: %m");
+                        ret = EXIT_FAILURE;
+                        break;
+                }
+
+                s = hashmap_remove(pids, UINT_TO_PTR(si.si_pid));
+                if (s) {
+                        if (!is_clean_exit(si.si_code, si.si_status)) {
+                                if (si.si_code == CLD_EXITED)
+                                        log_error("/bin/mount for %s exited with exit status %i.", s, si.si_status);
+                                else
+                                        log_error("/bin/mount for %s terminated by signal %s.", s, signal_to_string(si.si_status));
+
+                                ret = EXIT_FAILURE;
+                        }
+
+                        free(s);
+                }
+        }
+
+finish:
+
+        if (pids)
+                hashmap_free_free(pids);
+
+        if (f)
+                endmntent(f);
+
+        return ret;
+}
diff --git a/src/reply-password.c b/src/reply-password.c
deleted file mode 100644
index a935d0f..0000000
--- a/src/reply-password.c
+++ /dev/null
@@ -1,109 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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/socket.h>
-#include <sys/poll.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/un.h>
-#include <sys/stat.h>
-#include <sys/signalfd.h>
-#include <getopt.h>
-#include <stddef.h>
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-
-static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) {
-        union {
-                struct sockaddr sa;
-                struct sockaddr_un un;
-        } sa;
-
-        assert(fd >= 0);
-        assert(socket_name);
-        assert(packet);
-
-        zero(sa);
-        sa.un.sun_family = AF_UNIX;
-        strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
-
-        if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
-                log_error("Failed to send: %m");
-                return -1;
-        }
-
-        return 0;
-}
-
-int main(int argc, char *argv[]) {
-        int fd = -1, r = EXIT_FAILURE;
-        char packet[LINE_MAX];
-        size_t length;
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        if (argc != 3) {
-                log_error("Wrong number of arguments.");
-                goto finish;
-        }
-
-        if (streq(argv[1], "1")) {
-
-                packet[0] = '+';
-                if (!fgets(packet+1, sizeof(packet)-1, stdin)) {
-                        log_error("Failed to read password: %m");
-                        goto finish;
-                }
-
-                truncate_nl(packet+1);
-                length = 1 + strlen(packet+1) + 1;
-        } else if (streq(argv[1], "0")) {
-                packet[0] = '-';
-                length = 1;
-        } else {
-                log_error("Invalid first argument %s", argv[1]);
-                goto finish;
-        }
-
-        if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
-                log_error("socket() failed: %m");
-                goto finish;
-        }
-
-        if (send_on_socket(fd, argv[2], packet, length) < 0)
-                goto finish;
-
-        r = EXIT_SUCCESS;
-
-finish:
-        if (fd >= 0)
-                close_nointr_nofail(fd);
-
-        return r;
-}
diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c
new file mode 100644
index 0000000..a935d0f
--- /dev/null
+++ b/src/reply-password/reply-password.c
@@ -0,0 +1,109 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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/socket.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/signalfd.h>
+#include <getopt.h>
+#include <stddef.h>
+
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+
+static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) {
+        union {
+                struct sockaddr sa;
+                struct sockaddr_un un;
+        } sa;
+
+        assert(fd >= 0);
+        assert(socket_name);
+        assert(packet);
+
+        zero(sa);
+        sa.un.sun_family = AF_UNIX;
+        strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
+
+        if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
+                log_error("Failed to send: %m");
+                return -1;
+        }
+
+        return 0;
+}
+
+int main(int argc, char *argv[]) {
+        int fd = -1, r = EXIT_FAILURE;
+        char packet[LINE_MAX];
+        size_t length;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        if (argc != 3) {
+                log_error("Wrong number of arguments.");
+                goto finish;
+        }
+
+        if (streq(argv[1], "1")) {
+
+                packet[0] = '+';
+                if (!fgets(packet+1, sizeof(packet)-1, stdin)) {
+                        log_error("Failed to read password: %m");
+                        goto finish;
+                }
+
+                truncate_nl(packet+1);
+                length = 1 + strlen(packet+1) + 1;
+        } else if (streq(argv[1], "0")) {
+                packet[0] = '-';
+                length = 1;
+        } else {
+                log_error("Invalid first argument %s", argv[1]);
+                goto finish;
+        }
+
+        if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+                log_error("socket() failed: %m");
+                goto finish;
+        }
+
+        if (send_on_socket(fd, argv[2], packet, length) < 0)
+                goto finish;
+
+        r = EXIT_SUCCESS;
+
+finish:
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        return r;
+}
diff --git a/src/shutdownd.c b/src/shutdownd.c
deleted file mode 100644
index 0497cd4..0000000
--- a/src/shutdownd.c
+++ /dev/null
@@ -1,476 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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/socket.h>
-#include <sys/poll.h>
-#include <sys/types.h>
-#include <sys/timerfd.h>
-#include <assert.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stddef.h>
-
-#include <systemd/sd-daemon.h>
-#include <systemd/sd-shutdown.h>
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-#include "utmp-wtmp.h"
-#include "mkdir.h"
-
-union shutdown_buffer {
-        struct sd_shutdown_command command;
-        char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
-};
-
-static int read_packet(int fd, union shutdown_buffer *_b) {
-        struct msghdr msghdr;
-        struct iovec iovec;
-        struct ucred *ucred;
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
-        } control;
-        ssize_t n;
-        union shutdown_buffer b; /* We maintain our own copy here, in order not to corrupt the last message */
-
-        assert(fd >= 0);
-        assert(_b);
-
-        zero(iovec);
-        iovec.iov_base = &b;
-        iovec.iov_len = sizeof(b) - 1;
-
-        zero(control);
-        zero(msghdr);
-        msghdr.msg_iov = &iovec;
-        msghdr.msg_iovlen = 1;
-        msghdr.msg_control = &control;
-        msghdr.msg_controllen = sizeof(control);
-
-        n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
-        if (n <= 0) {
-                if (n == 0) {
-                        log_error("Short read");
-                        return -EIO;
-                }
-
-                if (errno == EAGAIN || errno == EINTR)
-                        return 0;
-
-                log_error("recvmsg(): %m");
-                return -errno;
-        }
-
-        if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
-            control.cmsghdr.cmsg_level != SOL_SOCKET ||
-            control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
-            control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
-                log_warning("Received message without credentials. Ignoring.");
-                return 0;
-        }
-
-        ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
-        if (ucred->uid != 0) {
-                log_warning("Got request from unprivileged user. Ignoring.");
-                return 0;
-        }
-
-        if ((size_t) n < offsetof(struct sd_shutdown_command, wall_message)) {
-                log_warning("Message has invalid size. Ignoring.");
-                return 0;
-        }
-
-        if (b.command.mode != SD_SHUTDOWN_NONE &&
-            b.command.mode != SD_SHUTDOWN_REBOOT &&
-            b.command.mode != SD_SHUTDOWN_POWEROFF &&
-            b.command.mode != SD_SHUTDOWN_HALT &&
-            b.command.mode != SD_SHUTDOWN_KEXEC) {
-                log_warning("Message has invalid mode. Ignoring.");
-                return 0;
-        }
-
-        b.space[n] = 0;
-
-        *_b = b;
-        return 1;
-}
-
-static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
-        char date[FORMAT_TIMESTAMP_MAX];
-        const char *prefix;
-        char *l = NULL;
-
-        assert(c);
-        assert(c->warn_wall);
-
-        if (n >= c->usec)
-                return;
-
-        if (c->mode == SD_SHUTDOWN_HALT)
-                prefix = "The system is going down for system halt at ";
-        else if (c->mode == SD_SHUTDOWN_POWEROFF)
-                prefix = "The system is going down for power-off at ";
-        else if (c->mode == SD_SHUTDOWN_REBOOT)
-                prefix = "The system is going down for reboot at ";
-        else if (c->mode == SD_SHUTDOWN_KEXEC)
-                prefix = "The system is going down for kexec reboot at ";
-        else
-                assert_not_reached("Unknown mode!");
-
-        if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
-                     prefix, format_timestamp(date, sizeof(date), c->usec)) < 0)
-                log_error("Failed to allocate wall message");
-        else {
-                utmp_wall(l, NULL);
-                free(l);
-        }
-}
-
-static usec_t when_wall(usec_t n, usec_t elapse) {
-
-        static const struct {
-                usec_t delay;
-                usec_t interval;
-        } table[] = {
-                { 10 * USEC_PER_MINUTE, USEC_PER_MINUTE      },
-                { USEC_PER_HOUR,        15 * USEC_PER_MINUTE },
-                { 3 * USEC_PER_HOUR,    30 * USEC_PER_MINUTE }
-        };
-
-        usec_t left, sub;
-        unsigned i;
-
-        /* If the time is already passed, then don't announce */
-        if (n >= elapse)
-                return 0;
-
-        left = elapse - n;
-        for (i = 0; i < ELEMENTSOF(table); i++)
-                if (n + table[i].delay >= elapse) {
-                        sub = ((left / table[i].interval) * table[i].interval);
-                        break;
-                }
-
-        if (i >= ELEMENTSOF(table))
-                sub = ((left / USEC_PER_HOUR) * USEC_PER_HOUR);
-
-        return elapse > sub ? elapse - sub : 1;
-}
-
-static usec_t when_nologin(usec_t elapse) {
-        return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
-}
-
-static const char *mode_to_string(enum sd_shutdown_mode m) {
-        switch (m) {
-        case SD_SHUTDOWN_REBOOT:
-                return "reboot";
-        case SD_SHUTDOWN_POWEROFF:
-                return "poweroff";
-        case SD_SHUTDOWN_HALT:
-                return "halt";
-        case SD_SHUTDOWN_KEXEC:
-                return "kexec";
-        default:
-                return NULL;
-        }
-}
-
-static int update_schedule_file(struct sd_shutdown_command *c) {
-        int r;
-        FILE *f;
-        char *temp_path, *t;
-
-        assert(c);
-
-        r = safe_mkdir("/run/systemd/shutdown", 0755, 0, 0);
-        if (r < 0) {
-                log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
-                return r;
-        }
-
-        t = cescape(c->wall_message);
-        if (!t) {
-                log_error("Out of memory");
-                return -ENOMEM;
-        }
-
-        r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
-        if (r < 0) {
-                log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
-                free(t);
-                return r;
-        }
-
-        fchmod(fileno(f), 0644);
-
-        fprintf(f,
-                "USEC=%llu\n"
-                "WARN_WALL=%i\n"
-                "MODE=%s\n",
-                (unsigned long long) c->usec,
-                c->warn_wall,
-                mode_to_string(c->mode));
-
-        if (c->dry_run)
-                fputs("DRY_RUN=1\n", f);
-
-        if (!isempty(t))
-                fprintf(f, "WALL_MESSAGE=%s\n", t);
-
-        free(t);
-
-        fflush(f);
-
-        if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
-                log_error("Failed to write information about scheduled shutdowns: %m");
-                r = -errno;
-
-                unlink(temp_path);
-                unlink("/run/systemd/shutdown/scheduled");
-        }
-
-        fclose(f);
-        free(temp_path);
-
-        return r;
-}
-
-static bool scheduled(struct sd_shutdown_command *c) {
-        return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
-}
-
-int main(int argc, char *argv[]) {
-        enum {
-                FD_SOCKET,
-                FD_WALL_TIMER,
-                FD_NOLOGIN_TIMER,
-                FD_SHUTDOWN_TIMER,
-                _FD_MAX
-        };
-
-        int r = EXIT_FAILURE, n_fds;
-        union shutdown_buffer b;
-        struct pollfd pollfd[_FD_MAX];
-        bool exec_shutdown = false, unlink_nologin = false;
-        unsigned i;
-
-        if (getppid() != 1) {
-                log_error("This program should be invoked by init only.");
-                return EXIT_FAILURE;
-        }
-
-        if (argc > 1) {
-                log_error("This program does not take arguments.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        n_fds = sd_listen_fds(true);
-        if (n_fds < 0) {
-                log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
-                return EXIT_FAILURE;
-        }
-
-        if (n_fds != 1) {
-                log_error("Need exactly one file descriptor.");
-                return EXIT_FAILURE;
-        }
-
-        zero(b);
-        zero(pollfd);
-
-        pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
-        pollfd[FD_SOCKET].events = POLLIN;
-
-        for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
-                pollfd[i].events = POLLIN;
-                pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
-                if (pollfd[i].fd < 0) {
-                        log_error("timerfd_create(): %m");
-                        goto finish;
-                }
-        }
-
-        log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
-
-        sd_notify(false,
-                  "READY=1\n"
-                  "STATUS=Processing requests...");
-
-        for (;;) {
-                int k;
-                usec_t n;
-
-                k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
-                if (k < 0) {
-
-                        if (errno == EAGAIN || errno == EINTR)
-                                continue;
-
-                        log_error("poll(): %m");
-                        goto finish;
-                }
-
-                /* Exit on idle */
-                if (k == 0)
-                        break;
-
-                n = now(CLOCK_REALTIME);
-
-                if (pollfd[FD_SOCKET].revents) {
-
-                        k = read_packet(pollfd[FD_SOCKET].fd, &b);
-                        if (k < 0)
-                                goto finish;
-                        else if (k > 0) {
-                                struct itimerspec its;
-                                char date[FORMAT_TIMESTAMP_MAX];
-
-                                if (!scheduled(&b.command)) {
-                                        log_info("Shutdown canceled.");
-                                        break;
-                                }
-
-                                zero(its);
-                                if (b.command.warn_wall) {
-                                        /* Send wall messages every so often */
-                                        timespec_store(&its.it_value, when_wall(n, b.command.usec));
-
-                                        /* Warn immediately if less than 15 minutes are left */
-                                        if (n < b.command.usec &&
-                                            n + 15*USEC_PER_MINUTE >= b.command.usec)
-                                                warn_wall(n, &b.command);
-                                }
-                                if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
-                                        log_error("timerfd_settime(): %m");
-                                        goto finish;
-                                }
-
-                                /* Disallow logins 5 minutes prior to shutdown */
-                                zero(its);
-                                timespec_store(&its.it_value, when_nologin(b.command.usec));
-                                if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
-                                        log_error("timerfd_settime(): %m");
-                                        goto finish;
-                                }
-
-                                /* Shutdown after the specified time is reached */
-                                zero(its);
-                                timespec_store(&its.it_value, b.command.usec);
-                                if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
-                                        log_error("timerfd_settime(): %m");
-                                        goto finish;
-                                }
-
-                                update_schedule_file(&b.command);
-
-                                sd_notifyf(false,
-                                           "STATUS=Shutting down at %s (%s)...",
-                                           format_timestamp(date, sizeof(date), b.command.usec),
-                                           mode_to_string(b.command.mode));
-
-                                log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
-                        }
-                }
-
-                if (pollfd[FD_WALL_TIMER].revents) {
-                        struct itimerspec its;
-
-                        warn_wall(n, &b.command);
-                        flush_fd(pollfd[FD_WALL_TIMER].fd);
-
-                        /* Restart timer */
-                        zero(its);
-                        timespec_store(&its.it_value, when_wall(n, b.command.usec));
-                        if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
-                                log_error("timerfd_settime(): %m");
-                                goto finish;
-                        }
-                }
-
-                if (pollfd[FD_NOLOGIN_TIMER].revents) {
-                        int e;
-
-                        log_info("Creating /run/nologin, blocking further logins...");
-
-                        e = write_one_line_file_atomic("/run/nologin", "System is going down.");
-                        if (e < 0)
-                                log_error("Failed to create /run/nologin: %s", strerror(-e));
-                        else
-                                unlink_nologin = true;
-
-                        flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
-                }
-
-                if (pollfd[FD_SHUTDOWN_TIMER].revents) {
-                        exec_shutdown = true;
-                        goto finish;
-                }
-        }
-
-        r = EXIT_SUCCESS;
-
-        log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
-
-finish:
-
-        for (i = 0; i < _FD_MAX; i++)
-                if (pollfd[i].fd >= 0)
-                        close_nointr_nofail(pollfd[i].fd);
-
-        if (unlink_nologin)
-                unlink("/run/nologin");
-
-        unlink("/run/systemd/shutdown/scheduled");
-
-        if (exec_shutdown && !b.command.dry_run) {
-                char sw[3];
-
-                sw[0] = '-';
-                sw[1] = b.command.mode;
-                sw[2] = 0;
-
-                execl(SYSTEMCTL_BINARY_PATH,
-                      "shutdown",
-                      sw,
-                      "now",
-                      (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
-                      (b.command.warn_wall ? NULL : "--no-wall"),
-                      NULL);
-
-                log_error("Failed to execute /sbin/shutdown: %m");
-        }
-
-        sd_notify(false,
-                  "STATUS=Exiting...");
-
-        return r;
-}
diff --git a/src/shutdownd/shutdownd.c b/src/shutdownd/shutdownd.c
new file mode 100644
index 0000000..0497cd4
--- /dev/null
+++ b/src/shutdownd/shutdownd.c
@@ -0,0 +1,476 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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/socket.h>
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <sys/timerfd.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stddef.h>
+
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-shutdown.h>
+
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+#include "utmp-wtmp.h"
+#include "mkdir.h"
+
+union shutdown_buffer {
+        struct sd_shutdown_command command;
+        char space[offsetof(struct sd_shutdown_command, wall_message) + LINE_MAX];
+};
+
+static int read_packet(int fd, union shutdown_buffer *_b) {
+        struct msghdr msghdr;
+        struct iovec iovec;
+        struct ucred *ucred;
+        union {
+                struct cmsghdr cmsghdr;
+                uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
+        } control;
+        ssize_t n;
+        union shutdown_buffer b; /* We maintain our own copy here, in order not to corrupt the last message */
+
+        assert(fd >= 0);
+        assert(_b);
+
+        zero(iovec);
+        iovec.iov_base = &b;
+        iovec.iov_len = sizeof(b) - 1;
+
+        zero(control);
+        zero(msghdr);
+        msghdr.msg_iov = &iovec;
+        msghdr.msg_iovlen = 1;
+        msghdr.msg_control = &control;
+        msghdr.msg_controllen = sizeof(control);
+
+        n = recvmsg(fd, &msghdr, MSG_DONTWAIT);
+        if (n <= 0) {
+                if (n == 0) {
+                        log_error("Short read");
+                        return -EIO;
+                }
+
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                log_error("recvmsg(): %m");
+                return -errno;
+        }
+
+        if (msghdr.msg_controllen < CMSG_LEN(sizeof(struct ucred)) ||
+            control.cmsghdr.cmsg_level != SOL_SOCKET ||
+            control.cmsghdr.cmsg_type != SCM_CREDENTIALS ||
+            control.cmsghdr.cmsg_len != CMSG_LEN(sizeof(struct ucred))) {
+                log_warning("Received message without credentials. Ignoring.");
+                return 0;
+        }
+
+        ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
+        if (ucred->uid != 0) {
+                log_warning("Got request from unprivileged user. Ignoring.");
+                return 0;
+        }
+
+        if ((size_t) n < offsetof(struct sd_shutdown_command, wall_message)) {
+                log_warning("Message has invalid size. Ignoring.");
+                return 0;
+        }
+
+        if (b.command.mode != SD_SHUTDOWN_NONE &&
+            b.command.mode != SD_SHUTDOWN_REBOOT &&
+            b.command.mode != SD_SHUTDOWN_POWEROFF &&
+            b.command.mode != SD_SHUTDOWN_HALT &&
+            b.command.mode != SD_SHUTDOWN_KEXEC) {
+                log_warning("Message has invalid mode. Ignoring.");
+                return 0;
+        }
+
+        b.space[n] = 0;
+
+        *_b = b;
+        return 1;
+}
+
+static void warn_wall(usec_t n, struct sd_shutdown_command *c) {
+        char date[FORMAT_TIMESTAMP_MAX];
+        const char *prefix;
+        char *l = NULL;
+
+        assert(c);
+        assert(c->warn_wall);
+
+        if (n >= c->usec)
+                return;
+
+        if (c->mode == SD_SHUTDOWN_HALT)
+                prefix = "The system is going down for system halt at ";
+        else if (c->mode == SD_SHUTDOWN_POWEROFF)
+                prefix = "The system is going down for power-off at ";
+        else if (c->mode == SD_SHUTDOWN_REBOOT)
+                prefix = "The system is going down for reboot at ";
+        else if (c->mode == SD_SHUTDOWN_KEXEC)
+                prefix = "The system is going down for kexec reboot at ";
+        else
+                assert_not_reached("Unknown mode!");
+
+        if (asprintf(&l, "%s%s%s%s!", c->wall_message, c->wall_message[0] ? "\n" : "",
+                     prefix, format_timestamp(date, sizeof(date), c->usec)) < 0)
+                log_error("Failed to allocate wall message");
+        else {
+                utmp_wall(l, NULL);
+                free(l);
+        }
+}
+
+static usec_t when_wall(usec_t n, usec_t elapse) {
+
+        static const struct {
+                usec_t delay;
+                usec_t interval;
+        } table[] = {
+                { 10 * USEC_PER_MINUTE, USEC_PER_MINUTE      },
+                { USEC_PER_HOUR,        15 * USEC_PER_MINUTE },
+                { 3 * USEC_PER_HOUR,    30 * USEC_PER_MINUTE }
+        };
+
+        usec_t left, sub;
+        unsigned i;
+
+        /* If the time is already passed, then don't announce */
+        if (n >= elapse)
+                return 0;
+
+        left = elapse - n;
+        for (i = 0; i < ELEMENTSOF(table); i++)
+                if (n + table[i].delay >= elapse) {
+                        sub = ((left / table[i].interval) * table[i].interval);
+                        break;
+                }
+
+        if (i >= ELEMENTSOF(table))
+                sub = ((left / USEC_PER_HOUR) * USEC_PER_HOUR);
+
+        return elapse > sub ? elapse - sub : 1;
+}
+
+static usec_t when_nologin(usec_t elapse) {
+        return elapse > 5*USEC_PER_MINUTE ? elapse - 5*USEC_PER_MINUTE : 1;
+}
+
+static const char *mode_to_string(enum sd_shutdown_mode m) {
+        switch (m) {
+        case SD_SHUTDOWN_REBOOT:
+                return "reboot";
+        case SD_SHUTDOWN_POWEROFF:
+                return "poweroff";
+        case SD_SHUTDOWN_HALT:
+                return "halt";
+        case SD_SHUTDOWN_KEXEC:
+                return "kexec";
+        default:
+                return NULL;
+        }
+}
+
+static int update_schedule_file(struct sd_shutdown_command *c) {
+        int r;
+        FILE *f;
+        char *temp_path, *t;
+
+        assert(c);
+
+        r = safe_mkdir("/run/systemd/shutdown", 0755, 0, 0);
+        if (r < 0) {
+                log_error("Failed to create shutdown subdirectory: %s", strerror(-r));
+                return r;
+        }
+
+        t = cescape(c->wall_message);
+        if (!t) {
+                log_error("Out of memory");
+                return -ENOMEM;
+        }
+
+        r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path);
+        if (r < 0) {
+                log_error("Failed to save information about scheduled shutdowns: %s", strerror(-r));
+                free(t);
+                return r;
+        }
+
+        fchmod(fileno(f), 0644);
+
+        fprintf(f,
+                "USEC=%llu\n"
+                "WARN_WALL=%i\n"
+                "MODE=%s\n",
+                (unsigned long long) c->usec,
+                c->warn_wall,
+                mode_to_string(c->mode));
+
+        if (c->dry_run)
+                fputs("DRY_RUN=1\n", f);
+
+        if (!isempty(t))
+                fprintf(f, "WALL_MESSAGE=%s\n", t);
+
+        free(t);
+
+        fflush(f);
+
+        if (ferror(f) || rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) {
+                log_error("Failed to write information about scheduled shutdowns: %m");
+                r = -errno;
+
+                unlink(temp_path);
+                unlink("/run/systemd/shutdown/scheduled");
+        }
+
+        fclose(f);
+        free(temp_path);
+
+        return r;
+}
+
+static bool scheduled(struct sd_shutdown_command *c) {
+        return c->usec > 0 && c->mode != SD_SHUTDOWN_NONE;
+}
+
+int main(int argc, char *argv[]) {
+        enum {
+                FD_SOCKET,
+                FD_WALL_TIMER,
+                FD_NOLOGIN_TIMER,
+                FD_SHUTDOWN_TIMER,
+                _FD_MAX
+        };
+
+        int r = EXIT_FAILURE, n_fds;
+        union shutdown_buffer b;
+        struct pollfd pollfd[_FD_MAX];
+        bool exec_shutdown = false, unlink_nologin = false;
+        unsigned i;
+
+        if (getppid() != 1) {
+                log_error("This program should be invoked by init only.");
+                return EXIT_FAILURE;
+        }
+
+        if (argc > 1) {
+                log_error("This program does not take arguments.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        n_fds = sd_listen_fds(true);
+        if (n_fds < 0) {
+                log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
+                return EXIT_FAILURE;
+        }
+
+        if (n_fds != 1) {
+                log_error("Need exactly one file descriptor.");
+                return EXIT_FAILURE;
+        }
+
+        zero(b);
+        zero(pollfd);
+
+        pollfd[FD_SOCKET].fd = SD_LISTEN_FDS_START;
+        pollfd[FD_SOCKET].events = POLLIN;
+
+        for (i = FD_WALL_TIMER; i < _FD_MAX; i++) {
+                pollfd[i].events = POLLIN;
+                pollfd[i].fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
+                if (pollfd[i].fd < 0) {
+                        log_error("timerfd_create(): %m");
+                        goto finish;
+                }
+        }
+
+        log_debug("systemd-shutdownd running as pid %lu", (unsigned long) getpid());
+
+        sd_notify(false,
+                  "READY=1\n"
+                  "STATUS=Processing requests...");
+
+        for (;;) {
+                int k;
+                usec_t n;
+
+                k = poll(pollfd, _FD_MAX, scheduled(&b.command) ? -1 : 0);
+                if (k < 0) {
+
+                        if (errno == EAGAIN || errno == EINTR)
+                                continue;
+
+                        log_error("poll(): %m");
+                        goto finish;
+                }
+
+                /* Exit on idle */
+                if (k == 0)
+                        break;
+
+                n = now(CLOCK_REALTIME);
+
+                if (pollfd[FD_SOCKET].revents) {
+
+                        k = read_packet(pollfd[FD_SOCKET].fd, &b);
+                        if (k < 0)
+                                goto finish;
+                        else if (k > 0) {
+                                struct itimerspec its;
+                                char date[FORMAT_TIMESTAMP_MAX];
+
+                                if (!scheduled(&b.command)) {
+                                        log_info("Shutdown canceled.");
+                                        break;
+                                }
+
+                                zero(its);
+                                if (b.command.warn_wall) {
+                                        /* Send wall messages every so often */
+                                        timespec_store(&its.it_value, when_wall(n, b.command.usec));
+
+                                        /* Warn immediately if less than 15 minutes are left */
+                                        if (n < b.command.usec &&
+                                            n + 15*USEC_PER_MINUTE >= b.command.usec)
+                                                warn_wall(n, &b.command);
+                                }
+                                if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
+                                        log_error("timerfd_settime(): %m");
+                                        goto finish;
+                                }
+
+                                /* Disallow logins 5 minutes prior to shutdown */
+                                zero(its);
+                                timespec_store(&its.it_value, when_nologin(b.command.usec));
+                                if (timerfd_settime(pollfd[FD_NOLOGIN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
+                                        log_error("timerfd_settime(): %m");
+                                        goto finish;
+                                }
+
+                                /* Shutdown after the specified time is reached */
+                                zero(its);
+                                timespec_store(&its.it_value, b.command.usec);
+                                if (timerfd_settime(pollfd[FD_SHUTDOWN_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
+                                        log_error("timerfd_settime(): %m");
+                                        goto finish;
+                                }
+
+                                update_schedule_file(&b.command);
+
+                                sd_notifyf(false,
+                                           "STATUS=Shutting down at %s (%s)...",
+                                           format_timestamp(date, sizeof(date), b.command.usec),
+                                           mode_to_string(b.command.mode));
+
+                                log_info("Shutting down at %s (%s)...", date, mode_to_string(b.command.mode));
+                        }
+                }
+
+                if (pollfd[FD_WALL_TIMER].revents) {
+                        struct itimerspec its;
+
+                        warn_wall(n, &b.command);
+                        flush_fd(pollfd[FD_WALL_TIMER].fd);
+
+                        /* Restart timer */
+                        zero(its);
+                        timespec_store(&its.it_value, when_wall(n, b.command.usec));
+                        if (timerfd_settime(pollfd[FD_WALL_TIMER].fd, TFD_TIMER_ABSTIME, &its, NULL) < 0) {
+                                log_error("timerfd_settime(): %m");
+                                goto finish;
+                        }
+                }
+
+                if (pollfd[FD_NOLOGIN_TIMER].revents) {
+                        int e;
+
+                        log_info("Creating /run/nologin, blocking further logins...");
+
+                        e = write_one_line_file_atomic("/run/nologin", "System is going down.");
+                        if (e < 0)
+                                log_error("Failed to create /run/nologin: %s", strerror(-e));
+                        else
+                                unlink_nologin = true;
+
+                        flush_fd(pollfd[FD_NOLOGIN_TIMER].fd);
+                }
+
+                if (pollfd[FD_SHUTDOWN_TIMER].revents) {
+                        exec_shutdown = true;
+                        goto finish;
+                }
+        }
+
+        r = EXIT_SUCCESS;
+
+        log_debug("systemd-shutdownd stopped as pid %lu", (unsigned long) getpid());
+
+finish:
+
+        for (i = 0; i < _FD_MAX; i++)
+                if (pollfd[i].fd >= 0)
+                        close_nointr_nofail(pollfd[i].fd);
+
+        if (unlink_nologin)
+                unlink("/run/nologin");
+
+        unlink("/run/systemd/shutdown/scheduled");
+
+        if (exec_shutdown && !b.command.dry_run) {
+                char sw[3];
+
+                sw[0] = '-';
+                sw[1] = b.command.mode;
+                sw[2] = 0;
+
+                execl(SYSTEMCTL_BINARY_PATH,
+                      "shutdown",
+                      sw,
+                      "now",
+                      (b.command.warn_wall && b.command.wall_message[0]) ? b.command.wall_message :
+                      (b.command.warn_wall ? NULL : "--no-wall"),
+                      NULL);
+
+                log_error("Failed to execute /sbin/shutdown: %m");
+        }
+
+        sd_notify(false,
+                  "STATUS=Exiting...");
+
+        return r;
+}
diff --git a/src/sysctl.c b/src/sysctl.c
deleted file mode 100644
index 57fba25..0000000
--- a/src/sysctl.c
+++ /dev/null
@@ -1,272 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <stdlib.h>
-#include <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <stdio.h>
-#include <limits.h>
-#include <getopt.h>
-
-#include "log.h"
-#include "strv.h"
-#include "util.h"
-#include "strv.h"
-
-#define PROC_SYS_PREFIX "/proc/sys/"
-
-static char **arg_prefixes = NULL;
-
-static int apply_sysctl(const char *property, const char *value) {
-        char *p, *n;
-        int r = 0, k;
-
-        log_debug("Setting '%s' to '%s'", property, value);
-
-        p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property));
-        if (!p) {
-                log_error("Out of memory");
-                return -ENOMEM;
-        }
-
-        n = stpcpy(p, PROC_SYS_PREFIX);
-        strcpy(n, property);
-
-        for (; *n; n++)
-                if (*n == '.')
-                        *n = '/';
-
-        if (!strv_isempty(arg_prefixes)) {
-                char **i;
-                bool good = false;
-
-                STRV_FOREACH(i, arg_prefixes)
-                        if (path_startswith(p, *i)) {
-                                good = true;
-                                break;
-                        }
-
-                if (!good) {
-                        log_debug("Skipping %s", p);
-                        free(p);
-                        return 0;
-                }
-        }
-
-        k = write_one_line_file(p, value);
-        if (k < 0) {
-
-                log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
-                         "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
-
-                if (k != -ENOENT && r == 0)
-                        r = k;
-        }
-
-        free(p);
-
-        return r;
-}
-
-static int apply_file(const char *path, bool ignore_enoent) {
-        FILE *f;
-        int r = 0;
-
-        assert(path);
-
-        if (!(f = fopen(path, "re"))) {
-                if (ignore_enoent && errno == ENOENT)
-                        return 0;
-
-                log_error("Failed to open file '%s', ignoring: %m", path);
-                return -errno;
-        }
-
-        log_debug("apply: %s\n", path);
-        while (!feof(f)) {
-                char l[LINE_MAX], *p, *value;
-                int k;
-
-                if (!fgets(l, sizeof(l), f)) {
-                        if (feof(f))
-                                break;
-
-                        log_error("Failed to read file '%s', ignoring: %m", path);
-                        r = -errno;
-                        goto finish;
-                }
-
-                p = strstrip(l);
-
-                if (!*p)
-                        continue;
-
-                if (strchr(COMMENTS, *p))
-                        continue;
-
-                if (!(value = strchr(p, '='))) {
-                        log_error("Line is not an assignment in file '%s': %s", path, value);
-
-                        if (r == 0)
-                                r = -EINVAL;
-                        continue;
-                }
-
-                *value = 0;
-                value++;
-
-                if ((k = apply_sysctl(strstrip(p), strstrip(value))) < 0 && r == 0)
-                        r = k;
-        }
-
-finish:
-        fclose(f);
-
-        return r;
-}
-
-static int help(void) {
-
-        printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
-               "Applies kernel sysctl settings.\n\n"
-               "  -h --help             Show this help\n"
-               "     --prefix=PATH      Only apply rules that apply to paths with the specified prefix\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_PREFIX
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'           },
-                { "prefix",    required_argument, NULL, ARG_PREFIX    },
-                { NULL,        0,                 NULL, 0             }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_PREFIX: {
-                        char *p;
-                        char **l;
-
-                        for (p = optarg; *p; p++)
-                                if (*p == '.')
-                                        *p = '/';
-
-                        l = strv_append(arg_prefixes, optarg);
-                        if (!l) {
-                                log_error("Out of memory");
-                                return -ENOMEM;
-                        }
-
-                        strv_free(arg_prefixes);
-                        arg_prefixes = l;
-
-                        break;
-                }
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        return 1;
-}
-
-int main(int argc, char *argv[]) {
-        int r = 0;
-
-        r = parse_argv(argc, argv);
-        if (r <= 0)
-                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        if (argc > optind) {
-                int i;
-
-                for (i = optind; i < argc; i++) {
-                        int k;
-
-                        k = apply_file(argv[i], false);
-                        if (k < 0 && r == 0)
-                                r = k;
-                }
-        } else {
-                char **files, **f;
-                int k;
-
-                r = conf_files_list(&files, ".conf",
-                                    "/etc/sysctl.d",
-                                    "/run/sysctl.d",
-                                    "/usr/local/lib/sysctl.d",
-                                    "/usr/lib/sysctl.d",
-#ifdef HAVE_SPLIT_USR
-                                    "/lib/sysctl.d",
-#endif
-                                    NULL);
-                if (r < 0) {
-                        log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
-                        goto finish;
-                }
-
-                STRV_FOREACH(f, files) {
-                        k = apply_file(*f, true);
-                        if (k < 0 && r == 0)
-                                r = k;
-                }
-
-                k = apply_file("/etc/sysctl.conf", true);
-                if (k < 0 && r == 0)
-                        r = k;
-
-                strv_free(files);
-        }
-finish:
-        strv_free(arg_prefixes);
-
-        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c
new file mode 100644
index 0000000..57fba25
--- /dev/null
+++ b/src/sysctl/sysctl.c
@@ -0,0 +1,272 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <getopt.h>
+
+#include "log.h"
+#include "strv.h"
+#include "util.h"
+#include "strv.h"
+
+#define PROC_SYS_PREFIX "/proc/sys/"
+
+static char **arg_prefixes = NULL;
+
+static int apply_sysctl(const char *property, const char *value) {
+        char *p, *n;
+        int r = 0, k;
+
+        log_debug("Setting '%s' to '%s'", property, value);
+
+        p = new(char, sizeof(PROC_SYS_PREFIX) + strlen(property));
+        if (!p) {
+                log_error("Out of memory");
+                return -ENOMEM;
+        }
+
+        n = stpcpy(p, PROC_SYS_PREFIX);
+        strcpy(n, property);
+
+        for (; *n; n++)
+                if (*n == '.')
+                        *n = '/';
+
+        if (!strv_isempty(arg_prefixes)) {
+                char **i;
+                bool good = false;
+
+                STRV_FOREACH(i, arg_prefixes)
+                        if (path_startswith(p, *i)) {
+                                good = true;
+                                break;
+                        }
+
+                if (!good) {
+                        log_debug("Skipping %s", p);
+                        free(p);
+                        return 0;
+                }
+        }
+
+        k = write_one_line_file(p, value);
+        if (k < 0) {
+
+                log_full(k == -ENOENT ? LOG_DEBUG : LOG_WARNING,
+                         "Failed to write '%s' to '%s': %s", value, p, strerror(-k));
+
+                if (k != -ENOENT && r == 0)
+                        r = k;
+        }
+
+        free(p);
+
+        return r;
+}
+
+static int apply_file(const char *path, bool ignore_enoent) {
+        FILE *f;
+        int r = 0;
+
+        assert(path);
+
+        if (!(f = fopen(path, "re"))) {
+                if (ignore_enoent && errno == ENOENT)
+                        return 0;
+
+                log_error("Failed to open file '%s', ignoring: %m", path);
+                return -errno;
+        }
+
+        log_debug("apply: %s\n", path);
+        while (!feof(f)) {
+                char l[LINE_MAX], *p, *value;
+                int k;
+
+                if (!fgets(l, sizeof(l), f)) {
+                        if (feof(f))
+                                break;
+
+                        log_error("Failed to read file '%s', ignoring: %m", path);
+                        r = -errno;
+                        goto finish;
+                }
+
+                p = strstrip(l);
+
+                if (!*p)
+                        continue;
+
+                if (strchr(COMMENTS, *p))
+                        continue;
+
+                if (!(value = strchr(p, '='))) {
+                        log_error("Line is not an assignment in file '%s': %s", path, value);
+
+                        if (r == 0)
+                                r = -EINVAL;
+                        continue;
+                }
+
+                *value = 0;
+                value++;
+
+                if ((k = apply_sysctl(strstrip(p), strstrip(value))) < 0 && r == 0)
+                        r = k;
+        }
+
+finish:
+        fclose(f);
+
+        return r;
+}
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
+               "Applies kernel sysctl settings.\n\n"
+               "  -h --help             Show this help\n"
+               "     --prefix=PATH      Only apply rules that apply to paths with the specified prefix\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_PREFIX
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { "prefix",    required_argument, NULL, ARG_PREFIX    },
+                { NULL,        0,                 NULL, 0             }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_PREFIX: {
+                        char *p;
+                        char **l;
+
+                        for (p = optarg; *p; p++)
+                                if (*p == '.')
+                                        *p = '/';
+
+                        l = strv_append(arg_prefixes, optarg);
+                        if (!l) {
+                                log_error("Out of memory");
+                                return -ENOMEM;
+                        }
+
+                        strv_free(arg_prefixes);
+                        arg_prefixes = l;
+
+                        break;
+                }
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        int r = 0;
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if (argc > optind) {
+                int i;
+
+                for (i = optind; i < argc; i++) {
+                        int k;
+
+                        k = apply_file(argv[i], false);
+                        if (k < 0 && r == 0)
+                                r = k;
+                }
+        } else {
+                char **files, **f;
+                int k;
+
+                r = conf_files_list(&files, ".conf",
+                                    "/etc/sysctl.d",
+                                    "/run/sysctl.d",
+                                    "/usr/local/lib/sysctl.d",
+                                    "/usr/lib/sysctl.d",
+#ifdef HAVE_SPLIT_USR
+                                    "/lib/sysctl.d",
+#endif
+                                    NULL);
+                if (r < 0) {
+                        log_error("Failed to enumerate sysctl.d files: %s", strerror(-r));
+                        goto finish;
+                }
+
+                STRV_FOREACH(f, files) {
+                        k = apply_file(*f, true);
+                        if (k < 0 && r == 0)
+                                r = k;
+                }
+
+                k = apply_file("/etc/sysctl.conf", true);
+                if (k < 0 && r == 0)
+                        r = k;
+
+                strv_free(files);
+        }
+finish:
+        strv_free(arg_prefixes);
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/systemctl.c b/src/systemctl.c
deleted file mode 100644
index 28bdfa9..0000000
--- a/src/systemctl.c
+++ /dev/null
@@ -1,5523 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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/reboot.h>
-#include <stdio.h>
-#include <getopt.h>
-#include <stdbool.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <stddef.h>
-#include <sys/prctl.h>
-#include <dbus/dbus.h>
-
-#include <systemd/sd-daemon.h>
-#include <systemd/sd-shutdown.h>
-
-#include "log.h"
-#include "util.h"
-#include "macro.h"
-#include "set.h"
-#include "utmp-wtmp.h"
-#include "special.h"
-#include "initreq.h"
-#include "strv.h"
-#include "dbus-common.h"
-#include "cgroup-show.h"
-#include "cgroup-util.h"
-#include "list.h"
-#include "path-lookup.h"
-#include "conf-parser.h"
-#include "exit-status.h"
-#include "bus-errors.h"
-#include "build.h"
-#include "unit-name.h"
-#include "pager.h"
-#include "spawn-ask-password-agent.h"
-#include "spawn-polkit-agent.h"
-#include "install.h"
-#include "logs-show.h"
-
-static const char *arg_type = NULL;
-static char **arg_property = NULL;
-static bool arg_all = false;
-static const char *arg_job_mode = "replace";
-static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
-static bool arg_immediate = false;
-static bool arg_no_block = false;
-static bool arg_no_legend = false;
-static bool arg_no_pager = false;
-static bool arg_no_wtmp = false;
-static bool arg_no_sync = false;
-static bool arg_no_wall = false;
-static bool arg_no_reload = false;
-static bool arg_dry = false;
-static bool arg_quiet = false;
-static bool arg_full = false;
-static int arg_force = 0;
-static bool arg_ask_password = true;
-static bool arg_failed = false;
-static bool arg_runtime = false;
-static char **arg_wall = NULL;
-static const char *arg_kill_who = NULL;
-static const char *arg_kill_mode = NULL;
-static int arg_signal = SIGTERM;
-static const char *arg_root = NULL;
-static usec_t arg_when = 0;
-static enum action {
-        ACTION_INVALID,
-        ACTION_SYSTEMCTL,
-        ACTION_HALT,
-        ACTION_POWEROFF,
-        ACTION_REBOOT,
-        ACTION_KEXEC,
-        ACTION_EXIT,
-        ACTION_RUNLEVEL2,
-        ACTION_RUNLEVEL3,
-        ACTION_RUNLEVEL4,
-        ACTION_RUNLEVEL5,
-        ACTION_RESCUE,
-        ACTION_EMERGENCY,
-        ACTION_DEFAULT,
-        ACTION_RELOAD,
-        ACTION_REEXEC,
-        ACTION_RUNLEVEL,
-        ACTION_CANCEL_SHUTDOWN,
-        _ACTION_MAX
-} arg_action = ACTION_SYSTEMCTL;
-static enum dot {
-        DOT_ALL,
-        DOT_ORDER,
-        DOT_REQUIRE
-} arg_dot = DOT_ALL;
-static enum transport {
-        TRANSPORT_NORMAL,
-        TRANSPORT_SSH,
-        TRANSPORT_POLKIT
-} arg_transport = TRANSPORT_NORMAL;
-static const char *arg_host = NULL;
-static bool arg_follow = false;
-static unsigned arg_lines = 10;
-static OutputMode arg_output = OUTPUT_SHORT;
-
-static bool private_bus = false;
-
-static int daemon_reload(DBusConnection *bus, char **args);
-static void halt_now(enum action a);
-
-static bool on_tty(void) {
-        static int t = -1;
-
-        /* Note that this is invoked relatively early, before we start
-         * the pager. That means the value we return reflects whether
-         * we originally were started on a tty, not if we currently
-         * are. But this is intended, since we want colour and so on
-         * when run in our own pager. */
-
-        if (_unlikely_(t < 0))
-                t = isatty(STDOUT_FILENO) > 0;
-
-        return t;
-}
-
-static void pager_open_if_enabled(void) {
-
-        /* Cache result before we open the pager */
-        on_tty();
-
-        if (arg_no_pager)
-                return;
-
-        pager_open();
-}
-
-static void ask_password_agent_open_if_enabled(void) {
-
-        /* Open the password agent as a child process if necessary */
-
-        if (!arg_ask_password)
-                return;
-
-        if (arg_scope != UNIT_FILE_SYSTEM)
-                return;
-
-        ask_password_agent_open();
-}
-
-static void polkit_agent_open_if_enabled(void) {
-
-        /* Open the polkit agent as a child process if necessary */
-
-        if (!arg_ask_password)
-                return;
-
-        if (arg_scope != UNIT_FILE_SYSTEM)
-                return;
-
-        polkit_agent_open();
-}
-
-static const char *ansi_highlight_red(bool b) {
-
-        if (!on_tty())
-                return "";
-
-        return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF;
-}
-
-static const char *ansi_highlight_green(bool b) {
-
-        if (!on_tty())
-                return "";
-
-        return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF;
-}
-
-static bool error_is_no_service(const DBusError *error) {
-        assert(error);
-
-        if (!dbus_error_is_set(error))
-                return false;
-
-        if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
-                return true;
-
-        if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
-                return true;
-
-        return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
-}
-
-static int translate_bus_error_to_exit_status(int r, const DBusError *error) {
-        assert(error);
-
-        if (!dbus_error_is_set(error))
-                return r;
-
-        if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED) ||
-            dbus_error_has_name(error, BUS_ERROR_ONLY_BY_DEPENDENCY) ||
-            dbus_error_has_name(error, BUS_ERROR_NO_ISOLATION) ||
-            dbus_error_has_name(error, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE))
-                return EXIT_NOPERMISSION;
-
-        if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT))
-                return EXIT_NOTINSTALLED;
-
-        if (dbus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) ||
-            dbus_error_has_name(error, BUS_ERROR_NOT_SUPPORTED))
-                return EXIT_NOTIMPLEMENTED;
-
-        if (dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED))
-                return EXIT_NOTCONFIGURED;
-
-        if (r != 0)
-                return r;
-
-        return EXIT_FAILURE;
-}
-
-static void warn_wall(enum action a) {
-        static const char *table[_ACTION_MAX] = {
-                [ACTION_HALT]      = "The system is going down for system halt NOW!",
-                [ACTION_REBOOT]    = "The system is going down for reboot NOW!",
-                [ACTION_POWEROFF]  = "The system is going down for power-off NOW!",
-                [ACTION_KEXEC]     = "The system is going down for kexec reboot NOW!",
-                [ACTION_RESCUE]    = "The system is going down to rescue mode NOW!",
-                [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!"
-        };
-
-        if (arg_no_wall)
-                return;
-
-        if (arg_wall) {
-                char *p;
-
-                p = strv_join(arg_wall, " ");
-                if (!p) {
-                        log_error("Failed to join strings.");
-                        return;
-                }
-
-                if (*p) {
-                        utmp_wall(p, NULL);
-                        free(p);
-                        return;
-                }
-
-                free(p);
-        }
-
-        if (!table[a])
-                return;
-
-        utmp_wall(table[a], NULL);
-}
-
-static bool avoid_bus(void) {
-
-        if (running_in_chroot() > 0)
-                return true;
-
-        if (sd_booted() <= 0)
-                return true;
-
-        if (!isempty(arg_root))
-                return true;
-
-        if (arg_scope == UNIT_FILE_GLOBAL)
-                return true;
-
-        return false;
-}
-
-struct unit_info {
-        const char *id;
-        const char *description;
-        const char *load_state;
-        const char *active_state;
-        const char *sub_state;
-        const char *following;
-        const char *unit_path;
-        uint32_t job_id;
-        const char *job_type;
-        const char *job_path;
-};
-
-static int compare_unit_info(const void *a, const void *b) {
-        const char *d1, *d2;
-        const struct unit_info *u = a, *v = b;
-
-        d1 = strrchr(u->id, '.');
-        d2 = strrchr(v->id, '.');
-
-        if (d1 && d2) {
-                int r;
-
-                if ((r = strcasecmp(d1, d2)) != 0)
-                        return r;
-        }
-
-        return strcasecmp(u->id, v->id);
-}
-
-static bool output_show_unit(const struct unit_info *u) {
-        const char *dot;
-
-        if (arg_failed)
-                return streq(u->active_state, "failed");
-
-        return (!arg_type || ((dot = strrchr(u->id, '.')) &&
-                              streq(dot+1, arg_type))) &&
-                (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0);
-}
-
-static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
-        unsigned id_len, max_id_len, active_len, sub_len, job_len, desc_len, n_shown = 0;
-        const struct unit_info *u;
-
-        max_id_len = sizeof("UNIT")-1;
-        active_len = sizeof("ACTIVE")-1;
-        sub_len = sizeof("SUB")-1;
-        job_len = sizeof("JOB")-1;
-        desc_len = 0;
-
-        for (u = unit_infos; u < unit_infos + c; u++) {
-                if (!output_show_unit(u))
-                        continue;
-
-                max_id_len = MAX(max_id_len, strlen(u->id));
-                active_len = MAX(active_len, strlen(u->active_state));
-                sub_len = MAX(sub_len, strlen(u->sub_state));
-                if (u->job_id != 0)
-                        job_len = MAX(job_len, strlen(u->job_type));
-        }
-
-        if (!arg_full) {
-                unsigned basic_len;
-                id_len = MIN(max_id_len, 25);
-                basic_len = 5 + id_len + 6 + active_len + sub_len + job_len;
-                if (basic_len < (unsigned) columns()) {
-                        unsigned extra_len, incr;
-                        extra_len = columns() - basic_len;
-                        /* Either UNIT already got 25, or is fully satisfied.
-                         * Grant up to 25 to DESC now. */
-                        incr = MIN(extra_len, 25);
-                        desc_len += incr;
-                        extra_len -= incr;
-                        /* split the remaining space between UNIT and DESC,
-                         * but do not give UNIT more than it needs. */
-                        if (extra_len > 0) {
-                                incr = MIN(extra_len / 2, max_id_len - id_len);
-                                id_len += incr;
-                                desc_len += extra_len - incr;
-                        }
-                }
-        } else
-                id_len = max_id_len;
-
-        if (!arg_no_legend) {
-                printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD",
-                       active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
-                if (!arg_full && arg_no_pager)
-                        printf("%.*s\n", desc_len, "DESCRIPTION");
-                else
-                        printf("%s\n", "DESCRIPTION");
-        }
-
-        for (u = unit_infos; u < unit_infos + c; u++) {
-                char *e;
-                const char *on_loaded, *off_loaded;
-                const char *on_active, *off_active;
-
-                if (!output_show_unit(u))
-                        continue;
-
-                n_shown++;
-
-                if (streq(u->load_state, "error")) {
-                        on_loaded = ansi_highlight_red(true);
-                        off_loaded = ansi_highlight_red(false);
-                } else
-                        on_loaded = off_loaded = "";
-
-                if (streq(u->active_state, "failed")) {
-                        on_active = ansi_highlight_red(true);
-                        off_active = ansi_highlight_red(false);
-                } else
-                        on_active = off_active = "";
-
-                e = arg_full ? NULL : ellipsize(u->id, id_len, 33);
-
-                printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ",
-                       id_len, e ? e : u->id,
-                       on_loaded, u->load_state, off_loaded,
-                       on_active, active_len, u->active_state,
-                       sub_len, u->sub_state, off_active,
-                       job_len, u->job_id ? u->job_type : "");
-                if (!arg_full && arg_no_pager)
-                        printf("%.*s\n", desc_len, u->description);
-                else
-                        printf("%s\n", u->description);
-
-                free(e);
-        }
-
-        if (!arg_no_legend) {
-                printf("\nLOAD   = Reflects whether the unit definition was properly loaded.\n"
-                       "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
-                       "SUB    = The low-level unit activation state, values depend on unit type.\n"
-                       "JOB    = Pending job for the unit.\n");
-
-                if (arg_all)
-                        printf("\n%u units listed.\n", n_shown);
-                else
-                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown);
-        }
-}
-
-static int list_units(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-        DBusMessageIter iter, sub, sub2;
-        unsigned c = 0, n_units = 0;
-        struct unit_info *unit_infos = NULL;
-
-        dbus_error_init(&error);
-
-        assert(bus);
-
-        pager_open_if_enabled();
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              "ListUnits"))) {
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_iter_init(reply, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
-            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
-                log_error("Failed to parse reply.");
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_recurse(&iter, &sub);
-
-        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                struct unit_info *u;
-
-                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (c >= n_units) {
-                        struct unit_info *w;
-
-                        n_units = MAX(2*c, 16);
-                        w = realloc(unit_infos, sizeof(struct unit_info) * n_units);
-
-                        if (!w) {
-                                log_error("Failed to allocate unit array.");
-                                r = -ENOMEM;
-                                goto finish;
-                        }
-
-                        unit_infos = w;
-                }
-
-                u = unit_infos+c;
-
-                dbus_message_iter_recurse(&sub, &sub2);
-
-                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->id, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->description, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->following, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_next(&sub);
-                c++;
-        }
-
-        if (c > 0) {
-                qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
-                output_units_list(unit_infos, c);
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        free(unit_infos);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int compare_unit_file_list(const void *a, const void *b) {
-        const char *d1, *d2;
-        const UnitFileList *u = a, *v = b;
-
-        d1 = strrchr(u->path, '.');
-        d2 = strrchr(v->path, '.');
-
-        if (d1 && d2) {
-                int r;
-
-                r = strcasecmp(d1, d2);
-                if (r != 0)
-                        return r;
-        }
-
-        return strcasecmp(file_name_from_path(u->path), file_name_from_path(v->path));
-}
-
-static bool output_show_unit_file(const UnitFileList *u) {
-        const char *dot;
-
-        return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type));
-}
-
-static void output_unit_file_list(const UnitFileList *units, unsigned c) {
-        unsigned max_id_len, id_cols, state_cols, n_shown = 0;
-        const UnitFileList *u;
-
-        max_id_len = sizeof("UNIT FILE")-1;
-        state_cols = sizeof("STATE")-1;
-        for (u = units; u < units + c; u++) {
-                if (!output_show_unit_file(u))
-                        continue;
-
-                max_id_len = MAX(max_id_len, strlen(file_name_from_path(u->path)));
-                state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
-        }
-
-        if (!arg_full) {
-                unsigned basic_cols;
-                id_cols = MIN(max_id_len, 25);
-                basic_cols = 1 + id_cols + state_cols;
-                if (basic_cols < (unsigned) columns())
-                        id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
-        } else
-                id_cols = max_id_len;
-
-        if (!arg_no_legend)
-                printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE");
-
-        for (u = units; u < units + c; u++) {
-                char *e;
-                const char *on, *off;
-                const char *id;
-
-                if (!output_show_unit_file(u))
-                        continue;
-
-                n_shown++;
-
-                if (u->state == UNIT_FILE_MASKED ||
-                    u->state == UNIT_FILE_MASKED_RUNTIME ||
-                    u->state == UNIT_FILE_DISABLED) {
-                        on  = ansi_highlight_red(true);
-                        off = ansi_highlight_red(false);
-                } else if (u->state == UNIT_FILE_ENABLED) {
-                        on  = ansi_highlight_green(true);
-                        off = ansi_highlight_green(false);
-                } else
-                        on = off = "";
-
-                id = file_name_from_path(u->path);
-
-                e = arg_full ? NULL : ellipsize(id, id_cols, 33);
-
-                printf("%-*s %s%-*s%s\n",
-                       id_cols, e ? e : id,
-                       on, state_cols, unit_file_state_to_string(u->state), off);
-
-                free(e);
-        }
-
-        if (!arg_no_legend)
-                printf("\n%u unit files listed.\n", n_shown);
-}
-
-static int list_unit_files(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-        DBusMessageIter iter, sub, sub2;
-        unsigned c = 0, n_units = 0;
-        UnitFileList *units = NULL;
-
-        dbus_error_init(&error);
-
-        pager_open_if_enabled();
-
-        if (avoid_bus()) {
-                Hashmap *h;
-                UnitFileList *u;
-                Iterator i;
-
-                h = hashmap_new(string_hash_func, string_compare_func);
-                if (!h) {
-                        log_error("Out of memory");
-                        return -ENOMEM;
-                }
-
-                r = unit_file_get_list(arg_scope, arg_root, h);
-                if (r < 0) {
-                        unit_file_list_free(h);
-                        log_error("Failed to get unit file list: %s", strerror(-r));
-                        return r;
-                }
-
-                n_units = hashmap_size(h);
-                units = new(UnitFileList, n_units);
-                if (!units) {
-                        unit_file_list_free(h);
-                        log_error("Out of memory");
-                        return -ENOMEM;
-                }
-
-                HASHMAP_FOREACH(u, h, i) {
-                        memcpy(units + c++, u, sizeof(UnitFileList));
-                        free(u);
-                }
-
-                hashmap_free(h);
-        } else {
-                assert(bus);
-
-                m = dbus_message_new_method_call(
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "ListUnitFiles");
-                if (!m) {
-                        log_error("Could not allocate message.");
-                        return -ENOMEM;
-                }
-
-                reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
-                if (!reply) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (!dbus_message_iter_init(reply, &iter) ||
-                    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
-                    dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&iter, &sub);
-
-                while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                        UnitFileList *u;
-                        const char *state;
-
-                        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
-                                log_error("Failed to parse reply.");
-                                r = -EIO;
-                                goto finish;
-                        }
-
-                        if (c >= n_units) {
-                                UnitFileList *w;
-
-                                n_units = MAX(2*c, 16);
-                                w = realloc(units, sizeof(struct UnitFileList) * n_units);
-
-                                if (!w) {
-                                        log_error("Failed to allocate unit array.");
-                                        r = -ENOMEM;
-                                        goto finish;
-                                }
-
-                                units = w;
-                        }
-
-                        u = units+c;
-
-                        dbus_message_iter_recurse(&sub, &sub2);
-
-                        if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->path, true) < 0 ||
-                            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, false) < 0) {
-                                log_error("Failed to parse reply.");
-                                r = -EIO;
-                                goto finish;
-                        }
-
-                        u->state = unit_file_state_from_string(state);
-
-                        dbus_message_iter_next(&sub);
-                        c++;
-                }
-        }
-
-        if (c > 0) {
-                qsort(units, c, sizeof(UnitFileList), compare_unit_file_list);
-                output_unit_file_list(units, c);
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        free(units);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
-        static const char * const colors[] = {
-                "Requires",              "[color=\"black\"]",
-                "RequiresOverridable",   "[color=\"black\"]",
-                "Requisite",             "[color=\"darkblue\"]",
-                "RequisiteOverridable",  "[color=\"darkblue\"]",
-                "Wants",                 "[color=\"darkgrey\"]",
-                "Conflicts",             "[color=\"red\"]",
-                "ConflictedBy",          "[color=\"red\"]",
-                "After",                 "[color=\"green\"]"
-        };
-
-        const char *c = NULL;
-        unsigned i;
-
-        assert(name);
-        assert(prop);
-        assert(iter);
-
-        for (i = 0; i < ELEMENTSOF(colors); i += 2)
-                if (streq(colors[i], prop)) {
-                        c = colors[i+1];
-                        break;
-                }
-
-        if (!c)
-                return 0;
-
-        if (arg_dot != DOT_ALL)
-                if ((arg_dot == DOT_ORDER) != streq(prop, "After"))
-                        return 0;
-
-        switch (dbus_message_iter_get_arg_type(iter)) {
-
-        case DBUS_TYPE_ARRAY:
-
-                if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
-                        DBusMessageIter sub;
-
-                        dbus_message_iter_recurse(iter, &sub);
-
-                        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                                const char *s;
-
-                                assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
-                                dbus_message_iter_get_basic(&sub, &s);
-                                printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
-
-                                dbus_message_iter_next(&sub);
-                        }
-
-                        return 0;
-                }
-        }
-
-        return 0;
-}
-
-static int dot_one(DBusConnection *bus, const char *name, const char *path) {
-        DBusMessage *m = NULL, *reply = NULL;
-        const char *interface = "org.freedesktop.systemd1.Unit";
-        int r;
-        DBusError error;
-        DBusMessageIter iter, sub, sub2, sub3;
-
-        assert(bus);
-        assert(path);
-
-        dbus_error_init(&error);
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              path,
-                              "org.freedesktop.DBus.Properties",
-                              "GetAll"))) {
-                log_error("Could not allocate message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &interface,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_iter_init(reply, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
-            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
-                log_error("Failed to parse reply.");
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_recurse(&iter, &sub);
-
-        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                const char *prop;
-
-                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&sub, &sub2);
-
-                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&sub2, &sub3);
-
-                if (dot_one_property(name, prop, &sub3)) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_next(&sub);
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int dot(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-        DBusMessageIter iter, sub, sub2;
-
-        dbus_error_init(&error);
-
-        assert(bus);
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              "ListUnits"))) {
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_iter_init(reply, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
-            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
-                log_error("Failed to parse reply.");
-                r = -EIO;
-                goto finish;
-        }
-
-        printf("digraph systemd {\n");
-
-        dbus_message_iter_recurse(&iter, &sub);
-        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path;
-
-                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&sub, &sub2);
-
-                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if ((r = dot_one(bus, id, unit_path)) < 0)
-                        goto finish;
-
-                /* printf("\t\"%s\";\n", id); */
-                dbus_message_iter_next(&sub);
-        }
-
-        printf("}\n");
-
-        log_info("   Color legend: black     = Requires\n"
-                 "                 dark blue = Requisite\n"
-                 "                 dark grey = Wants\n"
-                 "                 red       = Conflicts\n"
-                 "                 green     = After\n");
-
-        if (on_tty())
-                log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
-                           "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n");
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int list_jobs(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-        DBusMessageIter iter, sub, sub2;
-        unsigned k = 0;
-
-        dbus_error_init(&error);
-
-        assert(bus);
-
-        pager_open_if_enabled();
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              "ListJobs"))) {
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_iter_init(reply, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
-            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
-                log_error("Failed to parse reply.");
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_recurse(&iter, &sub);
-
-        if (on_tty())
-                printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
-
-        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                const char *name, *type, *state, *job_path, *unit_path;
-                uint32_t id;
-                char *e;
-
-                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&sub, &sub2);
-
-                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                e = arg_full ? NULL : ellipsize(name, 25, 33);
-                printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state);
-                free(e);
-
-                k++;
-
-                dbus_message_iter_next(&sub);
-        }
-
-        if (on_tty())
-                printf("\n%u jobs listed.\n", k);
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int load_unit(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL;
-        DBusError error;
-        int r;
-        char **name;
-
-        dbus_error_init(&error);
-
-        assert(bus);
-        assert(args);
-
-        STRV_FOREACH(name, args+1) {
-                DBusMessage *reply;
-
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      "/org/freedesktop/systemd1",
-                                      "org.freedesktop.systemd1.Manager",
-                                      "LoadUnit"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, name,
-                                              DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_unref(m);
-                dbus_message_unref(reply);
-
-                m = reply = NULL;
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int cancel_job(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-        char **name;
-
-        dbus_error_init(&error);
-
-        assert(bus);
-        assert(args);
-
-        if (strv_length(args) <= 1)
-                return daemon_reload(bus, args);
-
-        STRV_FOREACH(name, args+1) {
-                unsigned id;
-                const char *path;
-
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      "/org/freedesktop/systemd1",
-                                      "org.freedesktop.systemd1.Manager",
-                                      "GetJob"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if ((r = safe_atou(*name, &id)) < 0) {
-                        log_error("Failed to parse job id: %s", strerror(-r));
-                        goto finish;
-                }
-
-                assert_cc(sizeof(uint32_t) == sizeof(id));
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_UINT32, &id,
-                                              DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (!dbus_message_get_args(reply, &error,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_unref(m);
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      path,
-                                      "org.freedesktop.systemd1.Job",
-                                      "Cancel"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                dbus_message_unref(reply);
-                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_unref(m);
-                dbus_message_unref(reply);
-                m = reply = NULL;
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
-        DBusMessage *m = NULL, *reply = NULL;
-        dbus_bool_t b = FALSE;
-        DBusMessageIter iter, sub;
-        const char
-                *interface = "org.freedesktop.systemd1.Unit",
-                *property = "NeedDaemonReload",
-                *path;
-
-        /* We ignore all errors here, since this is used to show a warning only */
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              "GetUnit")))
-                goto finish;
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &unit,
-                                      DBUS_TYPE_INVALID))
-                goto finish;
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
-                goto finish;
-
-        if (!dbus_message_get_args(reply, NULL,
-                                   DBUS_TYPE_OBJECT_PATH, &path,
-                                   DBUS_TYPE_INVALID))
-                goto finish;
-
-        dbus_message_unref(m);
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              path,
-                              "org.freedesktop.DBus.Properties",
-                              "Get")))
-                goto finish;
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &interface,
-                                      DBUS_TYPE_STRING, &property,
-                                      DBUS_TYPE_INVALID)) {
-                goto finish;
-        }
-
-        dbus_message_unref(reply);
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
-                goto finish;
-
-        if (!dbus_message_iter_init(reply, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
-                goto finish;
-
-        dbus_message_iter_recurse(&iter, &sub);
-
-        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
-                goto finish;
-
-        dbus_message_iter_get_basic(&sub, &b);
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        return b;
-}
-
-typedef struct WaitData {
-        Set *set;
-        char *result;
-} WaitData;
-
-static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
-        DBusError error;
-        WaitData *d = data;
-
-        assert(connection);
-        assert(message);
-        assert(d);
-
-        dbus_error_init(&error);
-
-        log_debug("Got D-Bus request: %s.%s() on %s",
-                  dbus_message_get_interface(message),
-                  dbus_message_get_member(message),
-                  dbus_message_get_path(message));
-
-        if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
-                log_error("Warning! D-Bus connection terminated.");
-                dbus_connection_close(connection);
-
-        } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
-                uint32_t id;
-                const char *path, *result;
-                dbus_bool_t success = true;
-
-                if (dbus_message_get_args(message, &error,
-                                          DBUS_TYPE_UINT32, &id,
-                                          DBUS_TYPE_OBJECT_PATH, &path,
-                                          DBUS_TYPE_STRING, &result,
-                                          DBUS_TYPE_INVALID)) {
-                        char *p;
-
-                        if ((p = set_remove(d->set, (char*) path)))
-                                free(p);
-
-                        if (*result)
-                                d->result = strdup(result);
-
-                        goto finish;
-                }
-#ifndef LEGACY
-                dbus_error_free(&error);
-
-                if (dbus_message_get_args(message, &error,
-                                          DBUS_TYPE_UINT32, &id,
-                                          DBUS_TYPE_OBJECT_PATH, &path,
-                                          DBUS_TYPE_BOOLEAN, &success,
-                                          DBUS_TYPE_INVALID)) {
-                        char *p;
-
-                        /* Compatibility with older systemd versions <
-                         * 19 during upgrades. This should be dropped
-                         * one day */
-
-                        if ((p = set_remove(d->set, (char*) path)))
-                                free(p);
-
-                        if (!success)
-                                d->result = strdup("failed");
-
-                        goto finish;
-                }
-#endif
-
-                log_error("Failed to parse message: %s", bus_error_message(&error));
-        }
-
-finish:
-        dbus_error_free(&error);
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static int enable_wait_for_jobs(DBusConnection *bus) {
-        DBusError error;
-
-        assert(bus);
-
-        if (private_bus)
-                return 0;
-
-        dbus_error_init(&error);
-        dbus_bus_add_match(bus,
-                           "type='signal',"
-                           "sender='org.freedesktop.systemd1',"
-                           "interface='org.freedesktop.systemd1.Manager',"
-                           "member='JobRemoved',"
-                           "path='/org/freedesktop/systemd1'",
-                           &error);
-
-        if (dbus_error_is_set(&error)) {
-                log_error("Failed to add match: %s", bus_error_message(&error));
-                dbus_error_free(&error);
-                return -EIO;
-        }
-
-        /* This is slightly dirty, since we don't undo the match registrations. */
-        return 0;
-}
-
-static int wait_for_jobs(DBusConnection *bus, Set *s) {
-        int r;
-        WaitData d;
-
-        assert(bus);
-        assert(s);
-
-        zero(d);
-        d.set = s;
-
-        if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) {
-                log_error("Failed to add filter.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        while (!set_isempty(s) &&
-               dbus_connection_read_write_dispatch(bus, -1))
-                ;
-
-        if (!arg_quiet && d.result) {
-                if (streq(d.result, "timeout"))
-                        log_error("Job timed out.");
-                else if (streq(d.result, "canceled"))
-                        log_error("Job canceled.");
-                else if (streq(d.result, "dependency"))
-                        log_error("A dependency job failed. See system journal for details.");
-                else if (!streq(d.result, "done") && !streq(d.result, "skipped"))
-                        log_error("Job failed. See system journal and 'systemctl status' for details.");
-        }
-
-        if (streq_ptr(d.result, "timeout"))
-                r = -ETIME;
-        else if (streq_ptr(d.result, "canceled"))
-                r = -ECANCELED;
-        else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped"))
-                r = -EIO;
-        else
-                r = 0;
-
-        free(d.result);
-
-finish:
-        /* This is slightly dirty, since we don't undo the filter registration. */
-
-        return r;
-}
-
-static int start_unit_one(
-                DBusConnection *bus,
-                const char *method,
-                const char *name,
-                const char *mode,
-                DBusError *error,
-                Set *s) {
-
-        DBusMessage *m = NULL, *reply = NULL;
-        const char *path;
-        int r;
-
-        assert(bus);
-        assert(method);
-        assert(name);
-        assert(mode);
-        assert(error);
-        assert(arg_no_block || s);
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              method))) {
-                log_error("Could not allocate message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &name,
-                                      DBUS_TYPE_STRING, &mode,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error))) {
-
-                if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(error)) {
-                        /* There's always a fallback possible for
-                         * legacy actions. */
-                        r = -EADDRNOTAVAIL;
-                        goto finish;
-                }
-
-                log_error("Failed to issue method call: %s", bus_error_message(error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_get_args(reply, error,
-                                   DBUS_TYPE_OBJECT_PATH, &path,
-                                   DBUS_TYPE_INVALID)) {
-                log_error("Failed to parse reply: %s", bus_error_message(error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (need_daemon_reload(bus, name))
-                log_warning("Warning: Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
-                            arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
-
-        if (!arg_no_block) {
-                char *p;
-
-                if (!(p = strdup(path))) {
-                        log_error("Failed to duplicate path.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if ((r = set_put(s, p)) < 0) {
-                        free(p);
-                        log_error("Failed to add path to set.");
-                        goto finish;
-                }
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        return r;
-}
-
-static enum action verb_to_action(const char *verb) {
-        if (streq(verb, "halt"))
-                return ACTION_HALT;
-        else if (streq(verb, "poweroff"))
-                return ACTION_POWEROFF;
-        else if (streq(verb, "reboot"))
-                return ACTION_REBOOT;
-        else if (streq(verb, "kexec"))
-                return ACTION_KEXEC;
-        else if (streq(verb, "rescue"))
-                return ACTION_RESCUE;
-        else if (streq(verb, "emergency"))
-                return ACTION_EMERGENCY;
-        else if (streq(verb, "default"))
-                return ACTION_DEFAULT;
-        else if (streq(verb, "exit"))
-                return ACTION_EXIT;
-        else
-                return ACTION_INVALID;
-}
-
-static int start_unit(DBusConnection *bus, char **args) {
-
-        static const char * const table[_ACTION_MAX] = {
-                [ACTION_HALT] = SPECIAL_HALT_TARGET,
-                [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
-                [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
-                [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET,
-                [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
-                [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
-                [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
-                [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
-                [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
-                [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
-                [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
-                [ACTION_EXIT] = SPECIAL_EXIT_TARGET
-        };
-
-        int r, ret = 0;
-        const char *method, *mode, *one_name;
-        Set *s = NULL;
-        DBusError error;
-        char **name;
-
-        dbus_error_init(&error);
-
-        assert(bus);
-
-        ask_password_agent_open_if_enabled();
-
-        if (arg_action == ACTION_SYSTEMCTL) {
-                method =
-                        streq(args[0], "stop") ||
-                        streq(args[0], "condstop")              ? "StopUnit" :
-                        streq(args[0], "reload")                ? "ReloadUnit" :
-                        streq(args[0], "restart")               ? "RestartUnit" :
-
-                        streq(args[0], "try-restart")           ||
-                        streq(args[0], "condrestart")           ? "TryRestartUnit" :
-
-                        streq(args[0], "reload-or-restart")     ? "ReloadOrRestartUnit" :
-
-                        streq(args[0], "reload-or-try-restart") ||
-                        streq(args[0], "condreload") ||
-
-                        streq(args[0], "force-reload")          ? "ReloadOrTryRestartUnit" :
-                                                                  "StartUnit";
-
-                mode =
-                        (streq(args[0], "isolate") ||
-                         streq(args[0], "rescue")  ||
-                         streq(args[0], "emergency")) ? "isolate" : arg_job_mode;
-
-                one_name = table[verb_to_action(args[0])];
-
-        } else {
-                assert(arg_action < ELEMENTSOF(table));
-                assert(table[arg_action]);
-
-                method = "StartUnit";
-
-                mode = (arg_action == ACTION_EMERGENCY ||
-                        arg_action == ACTION_RESCUE ||
-                        arg_action == ACTION_RUNLEVEL2 ||
-                        arg_action == ACTION_RUNLEVEL3 ||
-                        arg_action == ACTION_RUNLEVEL4 ||
-                        arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace";
-
-                one_name = table[arg_action];
-        }
-
-        if (!arg_no_block) {
-                if ((ret = enable_wait_for_jobs(bus)) < 0) {
-                        log_error("Could not watch jobs: %s", strerror(-ret));
-                        goto finish;
-                }
-
-                if (!(s = set_new(string_hash_func, string_compare_func))) {
-                        log_error("Failed to allocate set.");
-                        ret = -ENOMEM;
-                        goto finish;
-                }
-        }
-
-        if (one_name) {
-                if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0)
-                        goto finish;
-        } else {
-                STRV_FOREACH(name, args+1)
-                        if ((r = start_unit_one(bus, method, *name, mode, &error, s)) != 0) {
-                                ret = translate_bus_error_to_exit_status(r, &error);
-                                dbus_error_free(&error);
-                        }
-        }
-
-        if (!arg_no_block)
-                if ((r = wait_for_jobs(bus, s)) < 0) {
-                        ret = r;
-                        goto finish;
-                }
-
-finish:
-        if (s)
-                set_free_free(s);
-
-        dbus_error_free(&error);
-
-        return ret;
-}
-
-/* Ask systemd-logind, which might grant access to unprivileged users
- * through PolicyKit */
-static int reboot_with_logind(DBusConnection *bus, enum action a) {
-#ifdef HAVE_LOGIND
-        const char *method;
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        dbus_bool_t interactive = true;
-        int r;
-
-        dbus_error_init(&error);
-
-        polkit_agent_open_if_enabled();
-
-        switch (a) {
-
-        case ACTION_REBOOT:
-                method = "Reboot";
-                break;
-
-        case ACTION_POWEROFF:
-                method = "PowerOff";
-                break;
-
-        default:
-                return -EINVAL;
-        }
-
-        m = dbus_message_new_method_call(
-                                "org.freedesktop.login1",
-                                "/org/freedesktop/login1",
-                                "org.freedesktop.login1.Manager",
-                                method);
-        if (!m) {
-                log_error("Could not allocate message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_BOOLEAN, &interactive,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
-        if (!reply) {
-                if (error_is_no_service(&error)) {
-                        log_debug("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -ENOENT;
-                        goto finish;
-                }
-
-                if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
-                        log_debug("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EACCES;
-                        goto finish;
-                }
-
-                log_info("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-#else
-        return -ENOSYS;
-#endif
-}
-
-static int start_special(DBusConnection *bus, char **args) {
-        enum action a;
-        int r;
-
-        assert(bus);
-        assert(args);
-
-        a = verb_to_action(args[0]);
-
-        if (arg_force >= 2 &&
-            (a == ACTION_HALT ||
-             a == ACTION_POWEROFF ||
-             a == ACTION_REBOOT))
-                halt_now(a);
-
-        if (arg_force >= 1 &&
-            (a == ACTION_HALT ||
-             a == ACTION_POWEROFF ||
-             a == ACTION_REBOOT ||
-             a == ACTION_KEXEC ||
-             a == ACTION_EXIT))
-                return daemon_reload(bus, args);
-
-        /* first try logind, to allow authentication with polkit */
-        if (geteuid() != 0 &&
-            (a == ACTION_POWEROFF ||
-             a == ACTION_REBOOT)) {
-                r = reboot_with_logind(bus, a);
-                if (r >= 0)
-                        return r;
-        }
-
-        r = start_unit(bus, args);
-        if (r >= 0)
-                warn_wall(a);
-
-        return r;
-}
-
-static int check_unit(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        const char
-                *interface = "org.freedesktop.systemd1.Unit",
-                *property = "ActiveState";
-        int r = 3; /* According to LSB: "program is not running" */
-        DBusError error;
-        char **name;
-
-        assert(bus);
-        assert(args);
-
-        dbus_error_init(&error);
-
-        STRV_FOREACH(name, args+1) {
-                const char *path = NULL;
-                const char *state;
-                DBusMessageIter iter, sub;
-
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      "/org/freedesktop/systemd1",
-                                      "org.freedesktop.systemd1.Manager",
-                                      "GetUnit"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, name,
-                                              DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
-                        /* Hmm, cannot figure out anything about this unit... */
-                        if (!arg_quiet)
-                                puts("unknown");
-
-                        dbus_error_free(&error);
-                        dbus_message_unref(m);
-                        m = NULL;
-                        continue;
-                }
-
-                if (!dbus_message_get_args(reply, &error,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_unref(m);
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      path,
-                                      "org.freedesktop.DBus.Properties",
-                                      "Get"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &interface,
-                                              DBUS_TYPE_STRING, &property,
-                                              DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                dbus_message_unref(reply);
-                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (!dbus_message_iter_init(reply, &iter) ||
-                    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&iter, &sub);
-
-                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_get_basic(&sub, &state);
-
-                if (!arg_quiet)
-                        puts(state);
-
-                if (streq(state, "active") || streq(state, "reloading"))
-                        r = 0;
-
-                dbus_message_unref(m);
-                dbus_message_unref(reply);
-                m = reply = NULL;
-        }
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int kill_unit(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL;
-        int r = 0;
-        DBusError error;
-        char **name;
-
-        assert(bus);
-        assert(args);
-
-        dbus_error_init(&error);
-
-        if (!arg_kill_who)
-                arg_kill_who = "all";
-
-        if (!arg_kill_mode)
-                arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
-
-        STRV_FOREACH(name, args+1) {
-                DBusMessage *reply;
-
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      "/org/freedesktop/systemd1",
-                                      "org.freedesktop.systemd1.Manager",
-                                      "KillUnit"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, name,
-                                              DBUS_TYPE_STRING, &arg_kill_who,
-                                              DBUS_TYPE_STRING, &arg_kill_mode,
-                                              DBUS_TYPE_INT32, &arg_signal,
-                                              DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        dbus_error_free(&error);
-                        r = -EIO;
-                }
-
-                dbus_message_unref(m);
-
-                if (reply)
-                        dbus_message_unref(reply);
-                m = reply = NULL;
-        }
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-typedef struct ExecStatusInfo {
-        char *name;
-
-        char *path;
-        char **argv;
-
-        bool ignore;
-
-        usec_t start_timestamp;
-        usec_t exit_timestamp;
-        pid_t pid;
-        int code;
-        int status;
-
-        LIST_FIELDS(struct ExecStatusInfo, exec);
-} ExecStatusInfo;
-
-static void exec_status_info_free(ExecStatusInfo *i) {
-        assert(i);
-
-        free(i->name);
-        free(i->path);
-        strv_free(i->argv);
-        free(i);
-}
-
-static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) {
-        uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
-        DBusMessageIter sub2, sub3;
-        const char*path;
-        unsigned n;
-        uint32_t pid;
-        int32_t code, status;
-        dbus_bool_t ignore;
-
-        assert(i);
-        assert(i);
-
-        if (dbus_message_iter_get_arg_type(sub) != DBUS_TYPE_STRUCT)
-                return -EIO;
-
-        dbus_message_iter_recurse(sub, &sub2);
-
-        if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
-                return -EIO;
-
-        if (!(i->path = strdup(path)))
-                return -ENOMEM;
-
-        if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY ||
-            dbus_message_iter_get_element_type(&sub2) != DBUS_TYPE_STRING)
-                return -EIO;
-
-        n = 0;
-        dbus_message_iter_recurse(&sub2, &sub3);
-        while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
-                assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
-                dbus_message_iter_next(&sub3);
-                n++;
-        }
-
-
-        if (!(i->argv = new0(char*, n+1)))
-                return -ENOMEM;
-
-        n = 0;
-        dbus_message_iter_recurse(&sub2, &sub3);
-        while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
-                const char *s;
-
-                assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
-                dbus_message_iter_get_basic(&sub3, &s);
-                dbus_message_iter_next(&sub3);
-
-                if (!(i->argv[n++] = strdup(s)))
-                        return -ENOMEM;
-        }
-
-        if (!dbus_message_iter_next(&sub2) ||
-            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, true) < 0 ||
-            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp, true) < 0 ||
-            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp_monotonic, true) < 0 ||
-            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp, true) < 0 ||
-            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp_monotonic, true) < 0 ||
-            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, true) < 0 ||
-            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &code, true) < 0 ||
-            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &status, false) < 0)
-                return -EIO;
-
-        i->ignore = ignore;
-        i->start_timestamp = (usec_t) start_timestamp;
-        i->exit_timestamp = (usec_t) exit_timestamp;
-        i->pid = (pid_t) pid;
-        i->code = code;
-        i->status = status;
-
-        return 0;
-}
-
-typedef struct UnitStatusInfo {
-        const char *id;
-        const char *load_state;
-        const char *active_state;
-        const char *sub_state;
-        const char *unit_file_state;
-
-        const char *description;
-        const char *following;
-
-        const char *path;
-        const char *default_control_group;
-
-        const char *load_error;
-        const char *result;
-
-        usec_t inactive_exit_timestamp;
-        usec_t inactive_exit_timestamp_monotonic;
-        usec_t active_enter_timestamp;
-        usec_t active_exit_timestamp;
-        usec_t inactive_enter_timestamp;
-
-        bool need_daemon_reload;
-
-        /* Service */
-        pid_t main_pid;
-        pid_t control_pid;
-        const char *status_text;
-        bool running:1;
-#ifdef HAVE_SYSV_COMPAT
-        bool is_sysv:1;
-#endif
-
-        usec_t start_timestamp;
-        usec_t exit_timestamp;
-
-        int exit_code, exit_status;
-
-        usec_t condition_timestamp;
-        bool condition_result;
-
-        /* Socket */
-        unsigned n_accepted;
-        unsigned n_connections;
-        bool accept;
-
-        /* Device */
-        const char *sysfs_path;
-
-        /* Mount, Automount */
-        const char *where;
-
-        /* Swap */
-        const char *what;
-
-        LIST_HEAD(ExecStatusInfo, exec);
-} UnitStatusInfo;
-
-static void print_status_info(UnitStatusInfo *i) {
-        ExecStatusInfo *p;
-        const char *on, *off, *ss;
-        usec_t timestamp;
-        char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
-        char since2[FORMAT_TIMESTAMP_MAX], *s2;
-
-        assert(i);
-
-        /* This shows pretty information about a unit. See
-         * print_property() for a low-level property printer */
-
-        printf("%s", strna(i->id));
-
-        if (i->description && !streq_ptr(i->id, i->description))
-                printf(" - %s", i->description);
-
-        printf("\n");
-
-        if (i->following)
-                printf("\t  Follow: unit currently follows state of %s\n", i->following);
-
-        if (streq_ptr(i->load_state, "error")) {
-                on = ansi_highlight_red(true);
-                off = ansi_highlight_red(false);
-        } else
-                on = off = "";
-
-        if (i->load_error)
-                printf("\t  Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
-        else if (i->path && i->unit_file_state)
-                printf("\t  Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, i->path, i->unit_file_state);
-        else if (i->path)
-                printf("\t  Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
-        else
-                printf("\t  Loaded: %s%s%s\n", on, strna(i->load_state), off);
-
-        ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
-
-        if (streq_ptr(i->active_state, "failed")) {
-                on = ansi_highlight_red(true);
-                off = ansi_highlight_red(false);
-        } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
-                on = ansi_highlight_green(true);
-                off = ansi_highlight_green(false);
-        } else
-                on = off = "";
-
-        if (ss)
-                printf("\t  Active: %s%s (%s)%s",
-                       on,
-                       strna(i->active_state),
-                       ss,
-                       off);
-        else
-                printf("\t  Active: %s%s%s",
-                       on,
-                       strna(i->active_state),
-                       off);
-
-        if (!isempty(i->result) && !streq(i->result, "success"))
-                printf(" (Result: %s)", i->result);
-
-        timestamp = (streq_ptr(i->active_state, "active")      ||
-                     streq_ptr(i->active_state, "reloading"))   ? i->active_enter_timestamp :
-                    (streq_ptr(i->active_state, "inactive")    ||
-                     streq_ptr(i->active_state, "failed"))      ? i->inactive_enter_timestamp :
-                    streq_ptr(i->active_state, "activating")    ? i->inactive_exit_timestamp :
-                                                                  i->active_exit_timestamp;
-
-        s1 = format_timestamp_pretty(since1, sizeof(since1), timestamp);
-        s2 = format_timestamp(since2, sizeof(since2), timestamp);
-
-        if (s1)
-                printf(" since %s; %s\n", s2, s1);
-        else if (s2)
-                printf(" since %s\n", s2);
-        else
-                printf("\n");
-
-        if (!i->condition_result && i->condition_timestamp > 0) {
-                s1 = format_timestamp_pretty(since1, sizeof(since1), i->condition_timestamp);
-                s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
-
-                if (s1)
-                        printf("\t          start condition failed at %s; %s\n", s2, s1);
-                else if (s2)
-                        printf("\t          start condition failed at %s\n", s2);
-        }
-
-        if (i->sysfs_path)
-                printf("\t  Device: %s\n", i->sysfs_path);
-        if (i->where)
-                printf("\t   Where: %s\n", i->where);
-        if (i->what)
-                printf("\t    What: %s\n", i->what);
-
-        if (i->accept)
-                printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
-
-        LIST_FOREACH(exec, p, i->exec) {
-                char *t;
-                bool good;
-
-                /* Only show exited processes here */
-                if (p->code == 0)
-                        continue;
-
-                t = strv_join(p->argv, " ");
-                printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
-                free(t);
-
-#ifdef HAVE_SYSV_COMPAT
-                if (i->is_sysv)
-                        good = is_clean_exit_lsb(p->code, p->status);
-                else
-#endif
-                        good = is_clean_exit(p->code, p->status);
-
-                if (!good) {
-                        on = ansi_highlight_red(true);
-                        off = ansi_highlight_red(false);
-                } else
-                        on = off = "";
-
-                printf("%s(code=%s, ", on, sigchld_code_to_string(p->code));
-
-                if (p->code == CLD_EXITED) {
-                        const char *c;
-
-                        printf("status=%i", p->status);
-
-#ifdef HAVE_SYSV_COMPAT
-                        if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
-#else
-                        if ((c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD)))
-#endif
-                                printf("/%s", c);
-
-                } else
-                        printf("signal=%s", signal_to_string(p->status));
-
-                printf(")%s\n", off);
-
-                if (i->main_pid == p->pid &&
-                    i->start_timestamp == p->start_timestamp &&
-                    i->exit_timestamp == p->start_timestamp)
-                        /* Let's not show this twice */
-                        i->main_pid = 0;
-
-                if (p->pid == i->control_pid)
-                        i->control_pid = 0;
-        }
-
-        if (i->main_pid > 0 || i->control_pid > 0) {
-                printf("\t");
-
-                if (i->main_pid > 0) {
-                        printf("Main PID: %u", (unsigned) i->main_pid);
-
-                        if (i->running) {
-                                char *t = NULL;
-                                get_process_comm(i->main_pid, &t);
-                                if (t) {
-                                        printf(" (%s)", t);
-                                        free(t);
-                                }
-                        } else if (i->exit_code > 0) {
-                                printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
-
-                                if (i->exit_code == CLD_EXITED) {
-                                        const char *c;
-
-                                        printf("status=%i", i->exit_status);
-
-#ifdef HAVE_SYSV_COMPAT
-                                        if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
-#else
-                                        if ((c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD)))
-#endif
-                                                printf("/%s", c);
-
-                                } else
-                                        printf("signal=%s", signal_to_string(i->exit_status));
-                                printf(")");
-                        }
-                }
-
-                if (i->main_pid > 0 && i->control_pid > 0)
-                        printf(";");
-
-                if (i->control_pid > 0) {
-                        char *t = NULL;
-
-                        printf(" Control: %u", (unsigned) i->control_pid);
-
-                        get_process_comm(i->control_pid, &t);
-                        if (t) {
-                                printf(" (%s)", t);
-                                free(t);
-                        }
-                }
-
-                printf("\n");
-        }
-
-        if (i->status_text)
-                printf("\t  Status: \"%s\"\n", i->status_text);
-
-        if (i->default_control_group) {
-                unsigned c;
-
-                printf("\t  CGroup: %s\n", i->default_control_group);
-
-                if (arg_transport != TRANSPORT_SSH) {
-                        if ((c = columns()) > 18)
-                                c -= 18;
-                        else
-                                c = 0;
-
-                        show_cgroup_by_path(i->default_control_group, "\t\t  ", c, false);
-                }
-        }
-
-        if (i->id && arg_transport != TRANSPORT_SSH) {
-                printf("\n");
-                show_journal_by_unit(i->id, arg_output, 0, i->inactive_exit_timestamp_monotonic, arg_lines, arg_all, arg_follow);
-        }
-
-        if (i->need_daemon_reload)
-                printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
-                       ansi_highlight_red(true),
-                       ansi_highlight_red(false),
-                       arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
-}
-
-static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
-
-        assert(name);
-        assert(iter);
-        assert(i);
-
-        switch (dbus_message_iter_get_arg_type(iter)) {
-
-        case DBUS_TYPE_STRING: {
-                const char *s;
-
-                dbus_message_iter_get_basic(iter, &s);
-
-                if (!isempty(s)) {
-                        if (streq(name, "Id"))
-                                i->id = s;
-                        else if (streq(name, "LoadState"))
-                                i->load_state = s;
-                        else if (streq(name, "ActiveState"))
-                                i->active_state = s;
-                        else if (streq(name, "SubState"))
-                                i->sub_state = s;
-                        else if (streq(name, "Description"))
-                                i->description = s;
-                        else if (streq(name, "FragmentPath"))
-                                i->path = s;
-#ifdef HAVE_SYSV_COMPAT
-                        else if (streq(name, "SysVPath")) {
-                                i->is_sysv = true;
-                                i->path = s;
-                        }
-#endif
-                        else if (streq(name, "DefaultControlGroup"))
-                                i->default_control_group = s;
-                        else if (streq(name, "StatusText"))
-                                i->status_text = s;
-                        else if (streq(name, "SysFSPath"))
-                                i->sysfs_path = s;
-                        else if (streq(name, "Where"))
-                                i->where = s;
-                        else if (streq(name, "What"))
-                                i->what = s;
-                        else if (streq(name, "Following"))
-                                i->following = s;
-                        else if (streq(name, "UnitFileState"))
-                                i->unit_file_state = s;
-                        else if (streq(name, "Result"))
-                                i->result = s;
-                }
-
-                break;
-        }
-
-        case DBUS_TYPE_BOOLEAN: {
-                dbus_bool_t b;
-
-                dbus_message_iter_get_basic(iter, &b);
-
-                if (streq(name, "Accept"))
-                        i->accept = b;
-                else if (streq(name, "NeedDaemonReload"))
-                        i->need_daemon_reload = b;
-                else if (streq(name, "ConditionResult"))
-                        i->condition_result = b;
-
-                break;
-        }
-
-        case DBUS_TYPE_UINT32: {
-                uint32_t u;
-
-                dbus_message_iter_get_basic(iter, &u);
-
-                if (streq(name, "MainPID")) {
-                        if (u > 0) {
-                                i->main_pid = (pid_t) u;
-                                i->running = true;
-                        }
-                } else if (streq(name, "ControlPID"))
-                        i->control_pid = (pid_t) u;
-                else if (streq(name, "ExecMainPID")) {
-                        if (u > 0)
-                                i->main_pid = (pid_t) u;
-                } else if (streq(name, "NAccepted"))
-                        i->n_accepted = u;
-                else if (streq(name, "NConnections"))
-                        i->n_connections = u;
-
-                break;
-        }
-
-        case DBUS_TYPE_INT32: {
-                int32_t j;
-
-                dbus_message_iter_get_basic(iter, &j);
-
-                if (streq(name, "ExecMainCode"))
-                        i->exit_code = (int) j;
-                else if (streq(name, "ExecMainStatus"))
-                        i->exit_status = (int) j;
-
-                break;
-        }
-
-        case DBUS_TYPE_UINT64: {
-                uint64_t u;
-
-                dbus_message_iter_get_basic(iter, &u);
-
-                if (streq(name, "ExecMainStartTimestamp"))
-                        i->start_timestamp = (usec_t) u;
-                else if (streq(name, "ExecMainExitTimestamp"))
-                        i->exit_timestamp = (usec_t) u;
-                else if (streq(name, "ActiveEnterTimestamp"))
-                        i->active_enter_timestamp = (usec_t) u;
-                else if (streq(name, "InactiveEnterTimestamp"))
-                        i->inactive_enter_timestamp = (usec_t) u;
-                else if (streq(name, "InactiveExitTimestamp"))
-                        i->inactive_exit_timestamp = (usec_t) u;
-                else if (streq(name, "InactiveExitTimestampMonotonic"))
-                        i->inactive_exit_timestamp_monotonic = (usec_t) u;
-                else if (streq(name, "ActiveExitTimestamp"))
-                        i->active_exit_timestamp = (usec_t) u;
-                else if (streq(name, "ConditionTimestamp"))
-                        i->condition_timestamp = (usec_t) u;
-
-                break;
-        }
-
-        case DBUS_TYPE_ARRAY: {
-
-                if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT &&
-                    startswith(name, "Exec")) {
-                        DBusMessageIter sub;
-
-                        dbus_message_iter_recurse(iter, &sub);
-                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
-                                ExecStatusInfo *info;
-                                int r;
-
-                                if (!(info = new0(ExecStatusInfo, 1)))
-                                        return -ENOMEM;
-
-                                if (!(info->name = strdup(name))) {
-                                        free(info);
-                                        return -ENOMEM;
-                                }
-
-                                if ((r = exec_status_info_deserialize(&sub, info)) < 0) {
-                                        free(info);
-                                        return r;
-                                }
-
-                                LIST_PREPEND(ExecStatusInfo, exec, i->exec, info);
-
-                                dbus_message_iter_next(&sub);
-                        }
-                }
-
-                break;
-        }
-
-        case DBUS_TYPE_STRUCT: {
-
-                if (streq(name, "LoadError")) {
-                        DBusMessageIter sub;
-                        const char *n, *message;
-                        int r;
-
-                        dbus_message_iter_recurse(iter, &sub);
-
-                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &n, true);
-                        if (r < 0)
-                                return r;
-
-                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &message, false);
-                        if (r < 0)
-                                return r;
-
-                        if (!isempty(message))
-                                i->load_error = message;
-                }
-
-                break;
-        }
-        }
-
-        return 0;
-}
-
-static int print_property(const char *name, DBusMessageIter *iter) {
-        assert(name);
-        assert(iter);
-
-        /* This is a low-level property printer, see
-         * print_status_info() for the nicer output */
-
-        if (arg_property && !strv_find(arg_property, name))
-                return 0;
-
-        switch (dbus_message_iter_get_arg_type(iter)) {
-
-        case DBUS_TYPE_STRUCT: {
-                DBusMessageIter sub;
-                dbus_message_iter_recurse(iter, &sub);
-
-                if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "Job")) {
-                        uint32_t u;
-
-                        dbus_message_iter_get_basic(&sub, &u);
-
-                        if (u)
-                                printf("%s=%u\n", name, (unsigned) u);
-                        else if (arg_all)
-                                printf("%s=\n", name);
-
-                        return 0;
-                } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Unit")) {
-                        const char *s;
-
-                        dbus_message_iter_get_basic(&sub, &s);
-
-                        if (arg_all || s[0])
-                                printf("%s=%s\n", name, s);
-
-                        return 0;
-                } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "LoadError")) {
-                        const char *a = NULL, *b = NULL;
-
-                        if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) >= 0)
-                                bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &b, false);
-
-                        if (arg_all || !isempty(a) || !isempty(b))
-                                printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
-
-                        return 0;
-                }
-
-                break;
-        }
-
-        case DBUS_TYPE_ARRAY:
-
-                if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "EnvironmentFiles")) {
-                        DBusMessageIter sub, sub2;
-
-                        dbus_message_iter_recurse(iter, &sub);
-                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
-                                const char *path;
-                                dbus_bool_t ignore;
-
-                                dbus_message_iter_recurse(&sub, &sub2);
-
-                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 &&
-                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, false) >= 0)
-                                        printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore));
-
-                                dbus_message_iter_next(&sub);
-                        }
-
-                        return 0;
-
-                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Paths")) {
-                        DBusMessageIter sub, sub2;
-
-                        dbus_message_iter_recurse(iter, &sub);
-                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
-                                const char *type, *path;
-
-                                dbus_message_iter_recurse(&sub, &sub2);
-
-                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
-                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0)
-                                        printf("%s=%s\n", type, path);
-
-                                dbus_message_iter_next(&sub);
-                        }
-
-                        return 0;
-
-                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) {
-                        DBusMessageIter sub, sub2;
-
-                        dbus_message_iter_recurse(iter, &sub);
-                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
-                                const char *base;
-                                uint64_t value, next_elapse;
-
-                                dbus_message_iter_recurse(&sub, &sub2);
-
-                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 &&
-                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 &&
-                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) {
-                                        char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
-
-                                        printf("%s={ value=%s ; next_elapse=%s }\n",
-                                               base,
-                                               format_timespan(timespan1, sizeof(timespan1), value),
-                                               format_timespan(timespan2, sizeof(timespan2), next_elapse));
-                                }
-
-                                dbus_message_iter_next(&sub);
-                        }
-
-                        return 0;
-
-                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "ControlGroupAttributes")) {
-                        DBusMessageIter sub, sub2;
-
-                        dbus_message_iter_recurse(iter, &sub);
-                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
-                                const char *controller, *attr, *value;
-
-                                dbus_message_iter_recurse(&sub, &sub2);
-
-                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &controller, true) >= 0 &&
-                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 &&
-                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) {
-
-                                        printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n",
-                                               controller,
-                                               attr,
-                                               value);
-                                }
-
-                                dbus_message_iter_next(&sub);
-                        }
-
-                        return 0;
-
-                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) {
-                        DBusMessageIter sub;
-
-                        dbus_message_iter_recurse(iter, &sub);
-                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
-                                ExecStatusInfo info;
-
-                                zero(info);
-                                if (exec_status_info_deserialize(&sub, &info) >= 0) {
-                                        char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
-                                        char *t;
-
-                                        t = strv_join(info.argv, " ");
-
-                                        printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
-                                               name,
-                                               strna(info.path),
-                                               strna(t),
-                                               yes_no(info.ignore),
-                                               strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
-                                               strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
-                                               (unsigned) info. pid,
-                                               sigchld_code_to_string(info.code),
-                                               info.status,
-                                               info.code == CLD_EXITED ? "" : "/",
-                                               strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
-
-                                        free(t);
-                                }
-
-                                free(info.path);
-                                strv_free(info.argv);
-
-                                dbus_message_iter_next(&sub);
-                        }
-
-                        return 0;
-                }
-
-                break;
-        }
-
-        if (generic_print_property(name, iter, arg_all) > 0)
-                return 0;
-
-        if (arg_all)
-                printf("%s=[unprintable]\n", name);
-
-        return 0;
-}
-
-static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
-        DBusMessage *m = NULL, *reply = NULL;
-        const char *interface = "";
-        int r;
-        DBusError error;
-        DBusMessageIter iter, sub, sub2, sub3;
-        UnitStatusInfo info;
-        ExecStatusInfo *p;
-
-        assert(bus);
-        assert(path);
-        assert(new_line);
-
-        zero(info);
-        dbus_error_init(&error);
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              path,
-                              "org.freedesktop.DBus.Properties",
-                              "GetAll"))) {
-                log_error("Could not allocate message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &interface,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_iter_init(reply, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
-            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
-                log_error("Failed to parse reply.");
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_recurse(&iter, &sub);
-
-        if (*new_line)
-                printf("\n");
-
-        *new_line = true;
-
-        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                const char *name;
-
-                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&sub, &sub2);
-
-                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&sub2, &sub3);
-
-                if (show_properties)
-                        r = print_property(name, &sub3);
-                else
-                        r = status_property(name, &sub3, &info);
-
-                if (r < 0) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_next(&sub);
-        }
-
-        r = 0;
-
-        if (!show_properties)
-                print_status_info(&info);
-
-        if (!streq_ptr(info.active_state, "active") &&
-            !streq_ptr(info.active_state, "reloading") &&
-            streq(verb, "status"))
-                /* According to LSB: "program not running" */
-                r = 3;
-
-        while ((p = info.exec)) {
-                LIST_REMOVE(ExecStatusInfo, exec, info.exec, p);
-                exec_status_info_free(p);
-        }
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int show(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        int r, ret = 0;
-        DBusError error;
-        bool show_properties, new_line = false;
-        char **name;
-
-        assert(bus);
-        assert(args);
-
-        dbus_error_init(&error);
-
-        show_properties = !streq(args[0], "status");
-
-        if (show_properties)
-                pager_open_if_enabled();
-
-        if (show_properties && strv_length(args) <= 1) {
-                /* If not argument is specified inspect the manager
-                 * itself */
-
-                ret = show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line);
-                goto finish;
-        }
-
-        STRV_FOREACH(name, args+1) {
-                const char *path = NULL;
-                uint32_t id;
-
-                if (safe_atou32(*name, &id) < 0) {
-
-                        /* Interpret as unit name */
-
-                        if (!(m = dbus_message_new_method_call(
-                                              "org.freedesktop.systemd1",
-                                              "/org/freedesktop/systemd1",
-                                              "org.freedesktop.systemd1.Manager",
-                                              "LoadUnit"))) {
-                                log_error("Could not allocate message.");
-                                ret = -ENOMEM;
-                                goto finish;
-                        }
-
-                        if (!dbus_message_append_args(m,
-                                                      DBUS_TYPE_STRING, name,
-                                                      DBUS_TYPE_INVALID)) {
-                                log_error("Could not append arguments to message.");
-                                ret = -ENOMEM;
-                                goto finish;
-                        }
-
-                        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
-                                if (!dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
-                                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                                        ret = -EIO;
-                                        goto finish;
-                                }
-
-                                dbus_error_free(&error);
-
-                                dbus_message_unref(m);
-                                if (!(m = dbus_message_new_method_call(
-                                                      "org.freedesktop.systemd1",
-                                                      "/org/freedesktop/systemd1",
-                                                      "org.freedesktop.systemd1.Manager",
-                                                      "GetUnit"))) {
-                                        log_error("Could not allocate message.");
-                                        ret = -ENOMEM;
-                                        goto finish;
-                                }
-
-                                if (!dbus_message_append_args(m,
-                                                              DBUS_TYPE_STRING, name,
-                                                              DBUS_TYPE_INVALID)) {
-                                        log_error("Could not append arguments to message.");
-                                        ret = -ENOMEM;
-                                        goto finish;
-                                }
-
-                                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-
-                                        if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT))
-                                                ret = 4; /* According to LSB: "program or service status is unknown" */
-                                        else
-                                                ret = -EIO;
-                                        goto finish;
-                                }
-                        }
-
-                } else if (show_properties) {
-
-                        /* Interpret as job id */
-
-                        if (!(m = dbus_message_new_method_call(
-                                              "org.freedesktop.systemd1",
-                                              "/org/freedesktop/systemd1",
-                                              "org.freedesktop.systemd1.Manager",
-                                              "GetJob"))) {
-                                log_error("Could not allocate message.");
-                                ret = -ENOMEM;
-                                goto finish;
-                        }
-
-                        if (!dbus_message_append_args(m,
-                                                      DBUS_TYPE_UINT32, &id,
-                                                      DBUS_TYPE_INVALID)) {
-                                log_error("Could not append arguments to message.");
-                                ret = -ENOMEM;
-                                goto finish;
-                        }
-
-                        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                                ret = -EIO;
-                                goto finish;
-                        }
-                } else {
-
-                        /* Interpret as PID */
-
-                        if (!(m = dbus_message_new_method_call(
-                                              "org.freedesktop.systemd1",
-                                              "/org/freedesktop/systemd1",
-                                              "org.freedesktop.systemd1.Manager",
-                                              "GetUnitByPID"))) {
-                                log_error("Could not allocate message.");
-                                ret = -ENOMEM;
-                                goto finish;
-                        }
-
-                        if (!dbus_message_append_args(m,
-                                                      DBUS_TYPE_UINT32, &id,
-                                                      DBUS_TYPE_INVALID)) {
-                                log_error("Could not append arguments to message.");
-                                ret = -ENOMEM;
-                                goto finish;
-                        }
-
-                        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                                ret = -EIO;
-                                goto finish;
-                        }
-                }
-
-                if (!dbus_message_get_args(reply, &error,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", bus_error_message(&error));
-                        ret = -EIO;
-                        goto finish;
-                }
-
-                if ((r = show_one(args[0], bus, path, show_properties, &new_line)) != 0)
-                        ret = r;
-
-                dbus_message_unref(m);
-                dbus_message_unref(reply);
-                m = reply = NULL;
-        }
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return ret;
-}
-
-static int dump(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-        const char *text;
-
-        dbus_error_init(&error);
-
-        pager_open_if_enabled();
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              "Dump"))) {
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_get_args(reply, &error,
-                                   DBUS_TYPE_STRING, &text,
-                                   DBUS_TYPE_INVALID)) {
-                log_error("Failed to parse reply: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        fputs(text, stdout);
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int snapshot(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-        const char *name = "", *path, *id;
-        dbus_bool_t cleanup = FALSE;
-        DBusMessageIter iter, sub;
-        const char
-                *interface = "org.freedesktop.systemd1.Unit",
-                *property = "Id";
-
-        dbus_error_init(&error);
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              "CreateSnapshot"))) {
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
-
-        if (strv_length(args) > 1)
-                name = args[1];
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &name,
-                                      DBUS_TYPE_BOOLEAN, &cleanup,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_get_args(reply, &error,
-                                   DBUS_TYPE_OBJECT_PATH, &path,
-                                   DBUS_TYPE_INVALID)) {
-                log_error("Failed to parse reply: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_unref(m);
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              path,
-                              "org.freedesktop.DBus.Properties",
-                              "Get"))) {
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &interface,
-                                      DBUS_TYPE_STRING, &property,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        dbus_message_unref(reply);
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_iter_init(reply, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
-                log_error("Failed to parse reply.");
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_recurse(&iter, &sub);
-
-        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
-                log_error("Failed to parse reply.");
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_get_basic(&sub, &id);
-
-        if (!arg_quiet)
-                puts(id);
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int delete_snapshot(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        int r;
-        DBusError error;
-        char **name;
-
-        assert(bus);
-        assert(args);
-
-        dbus_error_init(&error);
-
-        STRV_FOREACH(name, args+1) {
-                const char *path = NULL;
-
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      "/org/freedesktop/systemd1",
-                                      "org.freedesktop.systemd1.Manager",
-                                      "GetUnit"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, name,
-                                              DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (!dbus_message_get_args(reply, &error,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_unref(m);
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      path,
-                                      "org.freedesktop.systemd1.Snapshot",
-                                      "Remove"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                dbus_message_unref(reply);
-                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_unref(m);
-                dbus_message_unref(reply);
-                m = reply = NULL;
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int daemon_reload(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-        const char *method;
-
-        dbus_error_init(&error);
-
-        if (arg_action == ACTION_RELOAD)
-                method = "Reload";
-        else if (arg_action == ACTION_REEXEC)
-                method = "Reexecute";
-        else {
-                assert(arg_action == ACTION_SYSTEMCTL);
-
-                method =
-                        streq(args[0], "clear-jobs")    ||
-                        streq(args[0], "cancel")        ? "ClearJobs" :
-                        streq(args[0], "daemon-reexec") ? "Reexecute" :
-                        streq(args[0], "reset-failed")  ? "ResetFailed" :
-                        streq(args[0], "halt")          ? "Halt" :
-                        streq(args[0], "poweroff")      ? "PowerOff" :
-                        streq(args[0], "reboot")        ? "Reboot" :
-                        streq(args[0], "kexec")         ? "KExec" :
-                        streq(args[0], "exit")          ? "Exit" :
-                                    /* "daemon-reload" */ "Reload";
-        }
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              method))) {
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
-                if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
-                        /* There's always a fallback possible for
-                         * legacy actions. */
-                        r = -EADDRNOTAVAIL;
-                        goto finish;
-                }
-
-                if (streq(method, "Reexecute") && dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY)) {
-                        /* On reexecution, we expect a disconnect, not
-                         * a reply */
-                        r = 0;
-                        goto finish;
-                }
-
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int reset_failed(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL;
-        int r;
-        DBusError error;
-        char **name;
-
-        assert(bus);
-        dbus_error_init(&error);
-
-        if (strv_length(args) <= 1)
-                return daemon_reload(bus, args);
-
-        STRV_FOREACH(name, args+1) {
-                DBusMessage *reply;
-
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      "/org/freedesktop/systemd1",
-                                      "org.freedesktop.systemd1.Manager",
-                                      "ResetFailedUnit"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, name,
-                                              DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_unref(m);
-                dbus_message_unref(reply);
-                m = reply = NULL;
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int show_enviroment(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        DBusMessageIter iter, sub, sub2;
-        int r;
-        const char
-                *interface = "org.freedesktop.systemd1.Manager",
-                *property = "Environment";
-
-        dbus_error_init(&error);
-
-        pager_open_if_enabled();
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.DBus.Properties",
-                              "Get"))) {
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &interface,
-                                      DBUS_TYPE_STRING, &property,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if (!dbus_message_iter_init(reply, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
-                log_error("Failed to parse reply.");
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_recurse(&iter, &sub);
-
-        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
-            dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING)  {
-                log_error("Failed to parse reply.");
-                r = -EIO;
-                goto finish;
-        }
-
-        dbus_message_iter_recurse(&sub, &sub2);
-
-        while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
-                const char *text;
-
-                if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_get_basic(&sub2, &text);
-                printf("%s\n", text);
-
-                dbus_message_iter_next(&sub2);
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int set_environment(DBusConnection *bus, char **args) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-        const char *method;
-        DBusMessageIter iter, sub;
-        char **name;
-
-        dbus_error_init(&error);
-
-        method = streq(args[0], "set-environment")
-                ? "SetEnvironment"
-                : "UnsetEnvironment";
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              method))) {
-
-                log_error("Could not allocate message.");
-                return -ENOMEM;
-        }
-
-        dbus_message_iter_init_append(m, &iter);
-
-        if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        STRV_FOREACH(name, args+1)
-                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-        if (!dbus_message_iter_close_container(&iter, &sub)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        r = 0;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int enable_sysv_units(char **args) {
-        int r = 0;
-
-#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA))
-        const char *verb = args[0];
-        unsigned f = 1, t = 1;
-        LookupPaths paths;
-
-        if (arg_scope != UNIT_FILE_SYSTEM)
-                return 0;
-
-        if (!streq(verb, "enable") &&
-            !streq(verb, "disable") &&
-            !streq(verb, "is-enabled"))
-                return 0;
-
-        /* Processes all SysV units, and reshuffles the array so that
-         * afterwards only the native units remain */
-
-        zero(paths);
-        r = lookup_paths_init(&paths, MANAGER_SYSTEM, false);
-        if (r < 0)
-                return r;
-
-        r = 0;
-
-        for (f = 1; args[f]; f++) {
-                const char *name;
-                char *p;
-                bool found_native = false, found_sysv;
-                unsigned c = 1;
-                const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
-                char **k, *l, *q = NULL;
-                int j;
-                pid_t pid;
-                siginfo_t status;
-
-                name = args[f];
-
-                if (!endswith(name, ".service"))
-                        continue;
-
-                if (path_is_absolute(name))
-                        continue;
-
-                STRV_FOREACH(k, paths.unit_path) {
-                        p = NULL;
-
-                        if (!isempty(arg_root))
-                                asprintf(&p, "%s/%s/%s", arg_root, *k, name);
-                        else
-                                asprintf(&p, "%s/%s", *k, name);
-
-                        if (!p) {
-                                log_error("No memory");
-                                r = -ENOMEM;
-                                goto finish;
-                        }
-
-                        found_native = access(p, F_OK) >= 0;
-                        free(p);
-
-                        if (found_native)
-                                break;
-                }
-
-                if (found_native)
-                        continue;
-
-                p = NULL;
-                if (!isempty(arg_root))
-                        asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
-                else
-                        asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name);
-                if (!p) {
-                        log_error("No memory");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                p[strlen(p) - sizeof(".service") + 1] = 0;
-                found_sysv = access(p, F_OK) >= 0;
-
-                if (!found_sysv) {
-                        free(p);
-                        continue;
-                }
-
-                /* Mark this entry, so that we don't try enabling it as native unit */
-                args[f] = (char*) "";
-
-                log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
-
-                if (!isempty(arg_root))
-                        argv[c++] = q = strappend("--root=", arg_root);
-
-                argv[c++] = file_name_from_path(p);
-                argv[c++] =
-                        streq(verb, "enable") ? "on" :
-                        streq(verb, "disable") ? "off" : "--level=5";
-                argv[c] = NULL;
-
-                l = strv_join((char**)argv, " ");
-                if (!l) {
-                        log_error("No memory.");
-                        free(q);
-                        free(p);
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                log_info("Executing %s", l);
-                free(l);
-
-                pid = fork();
-                if (pid < 0) {
-                        log_error("Failed to fork: %m");
-                        free(p);
-                        free(q);
-                        r = -errno;
-                        goto finish;
-                } else if (pid == 0) {
-                        /* Child */
-
-                        execv(argv[0], (char**) argv);
-                        _exit(EXIT_FAILURE);
-                }
-
-                free(p);
-                free(q);
-
-                j = wait_for_terminate(pid, &status);
-                if (j < 0) {
-                        log_error("Failed to wait for child: %s", strerror(-r));
-                        r = j;
-                        goto finish;
-                }
-
-                if (status.si_code == CLD_EXITED) {
-                        if (streq(verb, "is-enabled")) {
-                                if (status.si_status == 0) {
-                                        if (!arg_quiet)
-                                                puts("enabled");
-                                        r = 1;
-                                } else {
-                                        if (!arg_quiet)
-                                                puts("disabled");
-                                }
-
-                        } else if (status.si_status != 0) {
-                                r = -EINVAL;
-                                goto finish;
-                        }
-                } else {
-                        r = -EPROTO;
-                        goto finish;
-                }
-        }
-
-finish:
-        lookup_paths_free(&paths);
-
-        /* Drop all SysV units */
-        for (f = 1, t = 1; args[f]; f++) {
-
-                if (isempty(args[f]))
-                        continue;
-
-                args[t++] = args[f];
-        }
-
-        args[t] = NULL;
-
-#endif
-        return r;
-}
-
-static int enable_unit(DBusConnection *bus, char **args) {
-        const char *verb = args[0];
-        UnitFileChange *changes = NULL;
-        unsigned n_changes = 0, i;
-        int carries_install_info = -1;
-        DBusMessage *m = NULL, *reply = NULL;
-        int r;
-        DBusError error;
-
-        r = enable_sysv_units(args);
-        if (r < 0)
-                return r;
-
-        if (!args[1])
-                return 0;
-
-        dbus_error_init(&error);
-
-        if (!bus || avoid_bus()) {
-                if (streq(verb, "enable")) {
-                        r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
-                        carries_install_info = r;
-                } else if (streq(verb, "disable"))
-                        r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
-                else if (streq(verb, "reenable")) {
-                        r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
-                        carries_install_info = r;
-                } else if (streq(verb, "link"))
-                        r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
-                else if (streq(verb, "preset")) {
-                        r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
-                        carries_install_info = r;
-                } else if (streq(verb, "mask"))
-                        r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
-                else if (streq(verb, "unmask"))
-                        r = unit_file_unmask(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
-                else
-                        assert_not_reached("Unknown verb");
-
-                if (r < 0) {
-                        log_error("Operation failed: %s", strerror(-r));
-                        goto finish;
-                }
-
-                if (!arg_quiet) {
-                        for (i = 0; i < n_changes; i++) {
-                                if (changes[i].type == UNIT_FILE_SYMLINK)
-                                        log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
-                                else
-                                        log_info("rm '%s'", changes[i].path);
-                        }
-                }
-
-        } else {
-                const char *method;
-                bool send_force = true, expect_carries_install_info = false;
-                dbus_bool_t a, b;
-                DBusMessageIter iter, sub, sub2;
-
-                if (streq(verb, "enable")) {
-                        method = "EnableUnitFiles";
-                        expect_carries_install_info = true;
-                } else if (streq(verb, "disable")) {
-                        method = "DisableUnitFiles";
-                        send_force = false;
-                } else if (streq(verb, "reenable")) {
-                        method = "ReenableUnitFiles";
-                        expect_carries_install_info = true;
-                } else if (streq(verb, "link"))
-                        method = "LinkUnitFiles";
-                else if (streq(verb, "preset")) {
-                        method = "PresetUnitFiles";
-                        expect_carries_install_info = true;
-                } else if (streq(verb, "mask"))
-                        method = "MaskUnitFiles";
-                else if (streq(verb, "unmask")) {
-                        method = "UnmaskUnitFiles";
-                        send_force = false;
-                } else
-                        assert_not_reached("Unknown verb");
-
-                m = dbus_message_new_method_call(
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                method);
-                if (!m) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                dbus_message_iter_init_append(m, &iter);
-
-                r = bus_append_strv_iter(&iter, args+1);
-                if (r < 0) {
-                        log_error("Failed to append unit files.");
-                        goto finish;
-                }
-
-                a = arg_runtime;
-                if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &a)) {
-                        log_error("Failed to append runtime boolean.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (send_force) {
-                        b = arg_force;
-
-                        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) {
-                                log_error("Failed to append force boolean.");
-                                r = -ENOMEM;
-                                goto finish;
-                        }
-                }
-
-                reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
-                if (!reply) {
-                        log_error("Failed to issue method call: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (!dbus_message_iter_init(reply, &iter)) {
-                        log_error("Failed to initialize iterator.");
-                        goto finish;
-                }
-
-                if (expect_carries_install_info) {
-                        r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &b, true);
-                        if (r < 0) {
-                                log_error("Failed to parse reply.");
-                                goto finish;
-                        }
-
-                        carries_install_info = b;
-                }
-
-                if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
-                    dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&iter, &sub);
-                while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                        const char *type, *path, *source;
-
-                        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
-                                log_error("Failed to parse reply.");
-                                r = -EIO;
-                                goto finish;
-                        }
-
-                        dbus_message_iter_recurse(&sub, &sub2);
-
-                        if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
-                            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
-                            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &source, false) < 0) {
-                                log_error("Failed to parse reply.");
-                                r = -EIO;
-                                goto finish;
-                        }
-
-                        if (!arg_quiet) {
-                                if (streq(type, "symlink"))
-                                        log_info("ln -s '%s' '%s'", source, path);
-                                else
-                                        log_info("rm '%s'", path);
-                        }
-
-                        dbus_message_iter_next(&sub);
-                }
-
-                /* Try to reload if enabeld */
-                if (!arg_no_reload)
-                        r = daemon_reload(bus, args);
-        }
-
-        if (carries_install_info == 0)
-                log_warning("Warning: unit files do not carry install information. No operation executed.");
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        unit_file_changes_free(changes, n_changes);
-
-        dbus_error_free(&error);
-        return r;
-}
-
-static int unit_is_enabled(DBusConnection *bus, char **args) {
-        DBusError error;
-        int r;
-        DBusMessage *m = NULL, *reply = NULL;
-        bool enabled;
-        char **name;
-
-        dbus_error_init(&error);
-
-        r = enable_sysv_units(args);
-        if (r < 0)
-                return r;
-
-        enabled = r > 0;
-
-        if (!bus || avoid_bus()) {
-
-                STRV_FOREACH(name, args+1) {
-                        UnitFileState state;
-
-                        state = unit_file_get_state(arg_scope, arg_root, *name);
-                        if (state < 0) {
-                                r = state;
-                                goto finish;
-                        }
-
-                        if (state == UNIT_FILE_ENABLED ||
-                            state == UNIT_FILE_ENABLED_RUNTIME ||
-                            state == UNIT_FILE_STATIC)
-                                enabled = true;
-
-                        if (!arg_quiet)
-                                puts(unit_file_state_to_string(state));
-                }
-
-        } else {
-                STRV_FOREACH(name, args+1) {
-                        const char *s;
-
-                        m = dbus_message_new_method_call(
-                                        "org.freedesktop.systemd1",
-                                        "/org/freedesktop/systemd1",
-                                        "org.freedesktop.systemd1.Manager",
-                                        "GetUnitFileState");
-                        if (!m) {
-                                log_error("Out of memory");
-                                r = -ENOMEM;
-                                goto finish;
-                        }
-
-                        if (!dbus_message_append_args(m,
-                                                      DBUS_TYPE_STRING, name,
-                                                      DBUS_TYPE_INVALID)) {
-                                log_error("Could not append arguments to message.");
-                                r = -ENOMEM;
-                                goto finish;
-                        }
-
-                        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
-                        if (!reply) {
-                                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                                r = -EIO;
-                                goto finish;
-                        }
-
-                        if (!dbus_message_get_args(reply, &error,
-                                                   DBUS_TYPE_STRING, &s,
-                                                   DBUS_TYPE_INVALID)) {
-                                log_error("Failed to parse reply: %s", bus_error_message(&error));
-                                r = -EIO;
-                                goto finish;
-                        }
-
-                        dbus_message_unref(m);
-                        dbus_message_unref(reply);
-                        m = reply = NULL;
-
-                        if (streq(s, "enabled") ||
-                            streq(s, "enabled-runtime") ||
-                            streq(s, "static"))
-                                enabled = true;
-
-                        if (!arg_quiet)
-                                puts(s);
-                }
-        }
-
-        r = enabled ? 0 : 1;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-        return r;
-}
-
-static int systemctl_help(void) {
-
-        pager_open_if_enabled();
-
-        printf("%s [OPTIONS...] {COMMAND} ...\n\n"
-               "Query or send control commands to the systemd manager.\n\n"
-               "  -h --help           Show this help\n"
-               "     --version        Show package version\n"
-               "  -t --type=TYPE      List only units of a particular type\n"
-               "  -p --property=NAME  Show only properties by this name\n"
-               "  -a --all            Show all units/properties, including dead/empty ones\n"
-               "     --failed         Show only failed units\n"
-               "     --full           Don't ellipsize unit names on output\n"
-               "     --fail           When queueing a new job, fail if conflicting jobs are\n"
-               "                      pending\n"
-               "     --ignore-dependencies\n"
-               "                      When queueing a new job, ignore all its dependencies\n"
-               "     --kill-who=WHO   Who to send signal to\n"
-               "  -s --signal=SIGNAL  Which signal to send\n"
-               "  -H --host=[USER@]HOST\n"
-               "                      Show information for remote host\n"
-               "  -P --privileged     Acquire privileges before execution\n"
-               "  -q --quiet          Suppress output\n"
-               "     --no-block       Do not wait until operation finished\n"
-               "     --no-wall        Don't send wall message before halt/power-off/reboot\n"
-               "     --no-reload      When enabling/disabling unit files, don't reload daemon\n"
-               "                      configuration\n"
-               "     --no-legend      Do not print a legend (column headers and hints)\n"
-               "     --no-pager       Do not pipe output into a pager\n"
-               "     --no-ask-password\n"
-               "                      Do not ask for system passwords\n"
-               "     --order          When generating graph for dot, show only order\n"
-               "     --require        When generating graph for dot, show only requirement\n"
-               "     --system         Connect to system manager\n"
-               "     --user           Connect to user service manager\n"
-               "     --global         Enable/disable unit files globally\n"
-               "  -f --force          When enabling unit files, override existing symlinks\n"
-               "                      When shutting down, execute action immediately\n"
-               "     --root=PATH      Enable unit files in the specified root directory\n"
-               "     --runtime        Enable unit files only temporarily until next reboot\n"
-               "  -n --lines=INTEGER  Journal entries to show\n"
-               "     --follow         Follow journal\n"
-               "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
-               "                      verbose, export, json, cat)\n\n"
-               "Unit Commands:\n"
-               "  list-units                      List loaded units\n"
-               "  start [NAME...]                 Start (activate) one or more units\n"
-               "  stop [NAME...]                  Stop (deactivate) one or more units\n"
-               "  reload [NAME...]                Reload one or more units\n"
-               "  restart [NAME...]               Start or restart one or more units\n"
-               "  try-restart [NAME...]           Restart one or more units if active\n"
-               "  reload-or-restart [NAME...]     Reload one or more units is possible,\n"
-               "                                  otherwise start or restart\n"
-               "  reload-or-try-restart [NAME...] Reload one or more units is possible,\n"
-               "                                  otherwise restart if active\n"
-               "  isolate [NAME]                  Start one unit and stop all others\n"
-               "  kill [NAME...]                  Send signal to processes of a unit\n"
-               "  is-active [NAME...]             Check whether units are active\n"
-               "  status [NAME...|PID...]         Show runtime status of one or more units\n"
-               "  show [NAME...|JOB...]           Show properties of one or more\n"
-               "                                  units/jobs or the manager\n"
-               "  reset-failed [NAME...]          Reset failed state for all, one, or more\n"
-               "                                  units\n"
-               "  load [NAME...]                  Load one or more units\n\n"
-               "Unit File Commands:\n"
-               "  list-unit-files                 List installed unit files\n"
-               "  enable [NAME...]                Enable one or more unit files\n"
-               "  disable [NAME...]               Disable one or more unit files\n"
-               "  reenable [NAME...]              Reenable one or more unit files\n"
-               "  preset [NAME...]                Enable/disable one or more unit files\n"
-               "                                  based on preset configuration\n"
-               "  mask [NAME...]                  Mask one or more units\n"
-               "  unmask [NAME...]                Unmask one or more units\n"
-               "  link [PATH...]                  Link one or more units files into\n"
-               "                                  the search path\n"
-               "  is-enabled [NAME...]            Check whether unit files are enabled\n\n"
-               "Job Commands:\n"
-               "  list-jobs                       List jobs\n"
-               "  cancel [JOB...]                 Cancel all, one, or more jobs\n\n"
-               "Status Commands:\n"
-               "  dump                            Dump server status\n"
-               "  dot                             Dump dependency graph for dot(1)\n\n"
-               "Snapshot Commands:\n"
-               "  snapshot [NAME]                 Create a snapshot\n"
-               "  delete [NAME...]                Remove one or more snapshots\n\n"
-               "Environment Commands:\n"
-               "  show-environment                Dump environment\n"
-               "  set-environment [NAME=VALUE...] Set one or more environment variables\n"
-               "  unset-environment [NAME...]     Unset one or more environment variables\n\n"
-               "Manager Lifecycle Commands:\n"
-               "  daemon-reload                   Reload systemd manager configuration\n"
-               "  daemon-reexec                   Reexecute systemd manager\n\n"
-               "System Commands:\n"
-               "  default                         Enter system default mode\n"
-               "  rescue                          Enter system rescue mode\n"
-               "  emergency                       Enter system emergency mode\n"
-               "  halt                            Shut down and halt the system\n"
-               "  poweroff                        Shut down and power-off the system\n"
-               "  reboot                          Shut down and reboot the system\n"
-               "  kexec                           Shut down and reboot the system with kexec\n"
-               "  exit                            Ask for user instance termination\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int halt_help(void) {
-
-        printf("%s [OPTIONS...]\n\n"
-               "%s the system.\n\n"
-               "     --help      Show this help\n"
-               "     --halt      Halt the machine\n"
-               "  -p --poweroff  Switch off the machine\n"
-               "     --reboot    Reboot the machine\n"
-               "  -f --force     Force immediate halt/power-off/reboot\n"
-               "  -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
-               "  -d --no-wtmp   Don't write wtmp record\n"
-               "  -n --no-sync   Don't sync before halt/power-off/reboot\n"
-               "     --no-wall   Don't send wall message before halt/power-off/reboot\n",
-               program_invocation_short_name,
-               arg_action == ACTION_REBOOT   ? "Reboot" :
-               arg_action == ACTION_POWEROFF ? "Power off" :
-                                               "Halt");
-
-        return 0;
-}
-
-static int shutdown_help(void) {
-
-        printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
-               "Shut down the system.\n\n"
-               "     --help      Show this help\n"
-               "  -H --halt      Halt the machine\n"
-               "  -P --poweroff  Power-off the machine\n"
-               "  -r --reboot    Reboot the machine\n"
-               "  -h             Equivalent to --poweroff, overriden by --halt\n"
-               "  -k             Don't halt/power-off/reboot, just send warnings\n"
-               "     --no-wall   Don't send wall message before halt/power-off/reboot\n"
-               "  -c             Cancel a pending shutdown\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int telinit_help(void) {
-
-        printf("%s [OPTIONS...] {COMMAND}\n\n"
-               "Send control commands to the init daemon.\n\n"
-               "     --help      Show this help\n"
-               "     --no-wall   Don't send wall message before halt/power-off/reboot\n\n"
-               "Commands:\n"
-               "  0              Power-off the machine\n"
-               "  6              Reboot the machine\n"
-               "  2, 3, 4, 5     Start runlevelX.target unit\n"
-               "  1, s, S        Enter rescue mode\n"
-               "  q, Q           Reload init daemon configuration\n"
-               "  u, U           Reexecute init daemon\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int runlevel_help(void) {
-
-        printf("%s [OPTIONS...]\n\n"
-               "Prints the previous and current runlevel of the init system.\n\n"
-               "     --help      Show this help\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int systemctl_parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_FAIL = 0x100,
-                ARG_IGNORE_DEPENDENCIES,
-                ARG_VERSION,
-                ARG_USER,
-                ARG_SYSTEM,
-                ARG_GLOBAL,
-                ARG_NO_BLOCK,
-                ARG_NO_LEGEND,
-                ARG_NO_PAGER,
-                ARG_NO_WALL,
-                ARG_ORDER,
-                ARG_REQUIRE,
-                ARG_ROOT,
-                ARG_FULL,
-                ARG_NO_RELOAD,
-                ARG_KILL_MODE,
-                ARG_KILL_WHO,
-                ARG_NO_ASK_PASSWORD,
-                ARG_FAILED,
-                ARG_RUNTIME,
-                ARG_FOLLOW,
-                ARG_FORCE
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'           },
-                { "version",   no_argument,       NULL, ARG_VERSION   },
-                { "type",      required_argument, NULL, 't'           },
-                { "property",  required_argument, NULL, 'p'           },
-                { "all",       no_argument,       NULL, 'a'           },
-                { "failed",    no_argument,       NULL, ARG_FAILED    },
-                { "full",      no_argument,       NULL, ARG_FULL      },
-                { "fail",      no_argument,       NULL, ARG_FAIL      },
-                { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
-                { "user",      no_argument,       NULL, ARG_USER      },
-                { "system",    no_argument,       NULL, ARG_SYSTEM    },
-                { "global",    no_argument,       NULL, ARG_GLOBAL    },
-                { "no-block",  no_argument,       NULL, ARG_NO_BLOCK  },
-                { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
-                { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
-                { "no-wall",   no_argument,       NULL, ARG_NO_WALL   },
-                { "quiet",     no_argument,       NULL, 'q'           },
-                { "order",     no_argument,       NULL, ARG_ORDER     },
-                { "require",   no_argument,       NULL, ARG_REQUIRE   },
-                { "root",      required_argument, NULL, ARG_ROOT      },
-                { "force",     no_argument,       NULL, ARG_FORCE     },
-                { "no-reload", no_argument,       NULL, ARG_NO_RELOAD },
-                { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
-                { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
-                { "signal",    required_argument, NULL, 's'           },
-                { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
-                { "host",      required_argument, NULL, 'H'           },
-                { "privileged",no_argument,       NULL, 'P'           },
-                { "runtime",   no_argument,       NULL, ARG_RUNTIME   },
-                { "lines",     required_argument, NULL, 'n'           },
-                { "follow",    no_argument,       NULL, ARG_FOLLOW    },
-                { "output",    required_argument, NULL, 'o'           },
-                { NULL,        0,                 NULL, 0             }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        systemctl_help();
-                        return 0;
-
-                case ARG_VERSION:
-                        puts(PACKAGE_STRING);
-                        puts(DISTRIBUTION);
-                        puts(SYSTEMD_FEATURES);
-                        return 0;
-
-                case 't':
-                        arg_type = optarg;
-                        break;
-
-                case 'p': {
-                        char **l;
-
-                        if (!(l = strv_append(arg_property, optarg)))
-                                return -ENOMEM;
-
-                        strv_free(arg_property);
-                        arg_property = l;
-
-                        /* If the user asked for a particular
-                         * property, show it to him, even if it is
-                         * empty. */
-                        arg_all = true;
-                        break;
-                }
-
-                case 'a':
-                        arg_all = true;
-                        break;
-
-                case ARG_FAIL:
-                        arg_job_mode = "fail";
-                        break;
-
-                case ARG_IGNORE_DEPENDENCIES:
-                        arg_job_mode = "ignore-dependencies";
-                        break;
-
-                case ARG_USER:
-                        arg_scope = UNIT_FILE_USER;
-                        break;
-
-                case ARG_SYSTEM:
-                        arg_scope = UNIT_FILE_SYSTEM;
-                        break;
-
-                case ARG_GLOBAL:
-                        arg_scope = UNIT_FILE_GLOBAL;
-                        break;
-
-                case ARG_NO_BLOCK:
-                        arg_no_block = true;
-                        break;
-
-                case ARG_NO_LEGEND:
-                        arg_no_legend = true;
-                        break;
-
-                case ARG_NO_PAGER:
-                        arg_no_pager = true;
-                        break;
-
-                case ARG_NO_WALL:
-                        arg_no_wall = true;
-                        break;
-
-                case ARG_ORDER:
-                        arg_dot = DOT_ORDER;
-                        break;
-
-                case ARG_REQUIRE:
-                        arg_dot = DOT_REQUIRE;
-                        break;
-
-                case ARG_ROOT:
-                        arg_root = optarg;
-                        break;
-
-                case ARG_FULL:
-                        arg_full = true;
-                        break;
-
-                case ARG_FAILED:
-                        arg_failed = true;
-                        break;
-
-                case 'q':
-                        arg_quiet = true;
-                        break;
-
-                case ARG_FORCE:
-                        arg_force ++;
-                        break;
-
-                case ARG_FOLLOW:
-                        arg_follow = true;
-                        break;
-
-                case 'f':
-                        /* -f is short for both --follow and --force! */
-                        arg_force ++;
-                        arg_follow = true;
-                        break;
-
-                case ARG_NO_RELOAD:
-                        arg_no_reload = true;
-                        break;
-
-                case ARG_KILL_WHO:
-                        arg_kill_who = optarg;
-                        break;
-
-                case ARG_KILL_MODE:
-                        arg_kill_mode = optarg;
-                        break;
-
-                case 's':
-                        if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
-                                log_error("Failed to parse signal string %s.", optarg);
-                                return -EINVAL;
-                        }
-                        break;
-
-                case ARG_NO_ASK_PASSWORD:
-                        arg_ask_password = false;
-                        break;
-
-                case 'P':
-                        arg_transport = TRANSPORT_POLKIT;
-                        break;
-
-                case 'H':
-                        arg_transport = TRANSPORT_SSH;
-                        arg_host = optarg;
-                        break;
-
-                case ARG_RUNTIME:
-                        arg_runtime = true;
-                        break;
-
-                case 'n':
-                        if (safe_atou(optarg, &arg_lines) < 0) {
-                                log_error("Failed to parse lines '%s'", optarg);
-                                return -EINVAL;
-                        }
-                        break;
-
-                case 'o':
-                        arg_output = output_mode_from_string(optarg);
-                        if (arg_output < 0) {
-                                log_error("Unknown output '%s'.", optarg);
-                                return -EINVAL;
-                        }
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (arg_transport != TRANSPORT_NORMAL && arg_scope != UNIT_FILE_SYSTEM) {
-                log_error("Cannot access user instance remotely.");
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-static int halt_parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_HELP = 0x100,
-                ARG_HALT,
-                ARG_REBOOT,
-                ARG_NO_WALL
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, ARG_HELP    },
-                { "halt",      no_argument,       NULL, ARG_HALT    },
-                { "poweroff",  no_argument,       NULL, 'p'         },
-                { "reboot",    no_argument,       NULL, ARG_REBOOT  },
-                { "force",     no_argument,       NULL, 'f'         },
-                { "wtmp-only", no_argument,       NULL, 'w'         },
-                { "no-wtmp",   no_argument,       NULL, 'd'         },
-                { "no-sync",   no_argument,       NULL, 'n'         },
-                { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
-                { NULL,        0,                 NULL, 0           }
-        };
-
-        int c, runlevel;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        if (utmp_get_runlevel(&runlevel, NULL) >= 0)
-                if (runlevel == '0' || runlevel == '6')
-                        arg_immediate = true;
-
-        while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
-                switch (c) {
-
-                case ARG_HELP:
-                        halt_help();
-                        return 0;
-
-                case ARG_HALT:
-                        arg_action = ACTION_HALT;
-                        break;
-
-                case 'p':
-                        if (arg_action != ACTION_REBOOT)
-                                arg_action = ACTION_POWEROFF;
-                        break;
-
-                case ARG_REBOOT:
-                        arg_action = ACTION_REBOOT;
-                        break;
-
-                case 'f':
-                        arg_immediate = true;
-                        break;
-
-                case 'w':
-                        arg_dry = true;
-                        break;
-
-                case 'd':
-                        arg_no_wtmp = true;
-                        break;
-
-                case 'n':
-                        arg_no_sync = true;
-                        break;
-
-                case ARG_NO_WALL:
-                        arg_no_wall = true;
-                        break;
-
-                case 'i':
-                case 'h':
-                        /* Compatibility nops */
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind < argc) {
-                log_error("Too many arguments.");
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-static int parse_time_spec(const char *t, usec_t *_u) {
-        assert(t);
-        assert(_u);
-
-        if (streq(t, "now"))
-                *_u = 0;
-        else if (!strchr(t, ':')) {
-                uint64_t u;
-
-                if (safe_atou64(t, &u) < 0)
-                        return -EINVAL;
-
-                *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
-        } else {
-                char *e = NULL;
-                long hour, minute;
-                struct tm tm;
-                time_t s;
-                usec_t n;
-
-                errno = 0;
-                hour = strtol(t, &e, 10);
-                if (errno != 0 || *e != ':' || hour < 0 || hour > 23)
-                        return -EINVAL;
-
-                minute = strtol(e+1, &e, 10);
-                if (errno != 0 || *e != 0 || minute < 0 || minute > 59)
-                        return -EINVAL;
-
-                n = now(CLOCK_REALTIME);
-                s = (time_t) (n / USEC_PER_SEC);
-
-                zero(tm);
-                assert_se(localtime_r(&s, &tm));
-
-                tm.tm_hour = (int) hour;
-                tm.tm_min = (int) minute;
-                tm.tm_sec = 0;
-
-                assert_se(s = mktime(&tm));
-
-                *_u = (usec_t) s * USEC_PER_SEC;
-
-                while (*_u <= n)
-                        *_u += USEC_PER_DAY;
-        }
-
-        return 0;
-}
-
-static int shutdown_parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_HELP = 0x100,
-                ARG_NO_WALL
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, ARG_HELP    },
-                { "halt",      no_argument,       NULL, 'H'         },
-                { "poweroff",  no_argument,       NULL, 'P'         },
-                { "reboot",    no_argument,       NULL, 'r'         },
-                { "kexec",     no_argument,       NULL, 'K'         }, /* not documented extension */
-                { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
-                { NULL,        0,                 NULL, 0           }
-        };
-
-        int c, r;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) {
-                switch (c) {
-
-                case ARG_HELP:
-                        shutdown_help();
-                        return 0;
-
-                case 'H':
-                        arg_action = ACTION_HALT;
-                        break;
-
-                case 'P':
-                        arg_action = ACTION_POWEROFF;
-                        break;
-
-                case 'r':
-                        if (kexec_loaded())
-                                arg_action = ACTION_KEXEC;
-                        else
-                                arg_action = ACTION_REBOOT;
-                        break;
-
-                case 'K':
-                        arg_action = ACTION_KEXEC;
-                        break;
-
-                case 'h':
-                        if (arg_action != ACTION_HALT)
-                                arg_action = ACTION_POWEROFF;
-                        break;
-
-                case 'k':
-                        arg_dry = true;
-                        break;
-
-                case ARG_NO_WALL:
-                        arg_no_wall = true;
-                        break;
-
-                case 't':
-                case 'a':
-                        /* Compatibility nops */
-                        break;
-
-                case 'c':
-                        arg_action = ACTION_CANCEL_SHUTDOWN;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (argc > optind) {
-                r = parse_time_spec(argv[optind], &arg_when);
-                if (r < 0) {
-                        log_error("Failed to parse time specification: %s", argv[optind]);
-                        return r;
-                }
-        } else
-                arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE;
-
-        /* We skip the time argument */
-        if (argc > optind + 1)
-                arg_wall = argv + optind + 1;
-
-        optind = argc;
-
-        return 1;
-}
-
-static int telinit_parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_HELP = 0x100,
-                ARG_NO_WALL
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, ARG_HELP    },
-                { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
-                { NULL,        0,                 NULL, 0           }
-        };
-
-        static const struct {
-                char from;
-                enum action to;
-        } table[] = {
-                { '0', ACTION_POWEROFF },
-                { '6', ACTION_REBOOT },
-                { '1', ACTION_RESCUE },
-                { '2', ACTION_RUNLEVEL2 },
-                { '3', ACTION_RUNLEVEL3 },
-                { '4', ACTION_RUNLEVEL4 },
-                { '5', ACTION_RUNLEVEL5 },
-                { 's', ACTION_RESCUE },
-                { 'S', ACTION_RESCUE },
-                { 'q', ACTION_RELOAD },
-                { 'Q', ACTION_RELOAD },
-                { 'u', ACTION_REEXEC },
-                { 'U', ACTION_REEXEC }
-        };
-
-        unsigned i;
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
-                switch (c) {
-
-                case ARG_HELP:
-                        telinit_help();
-                        return 0;
-
-                case ARG_NO_WALL:
-                        arg_no_wall = true;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind >= argc) {
-                telinit_help();
-                return -EINVAL;
-        }
-
-        if (optind + 1 < argc) {
-                log_error("Too many arguments.");
-                return -EINVAL;
-        }
-
-        if (strlen(argv[optind]) != 1) {
-                log_error("Expected single character argument.");
-                return -EINVAL;
-        }
-
-        for (i = 0; i < ELEMENTSOF(table); i++)
-                if (table[i].from == argv[optind][0])
-                        break;
-
-        if (i >= ELEMENTSOF(table)) {
-                log_error("Unknown command %s.", argv[optind]);
-                return -EINVAL;
-        }
-
-        arg_action = table[i].to;
-
-        optind ++;
-
-        return 1;
-}
-
-static int runlevel_parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_HELP = 0x100,
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, ARG_HELP    },
-                { NULL,        0,                 NULL, 0           }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
-                switch (c) {
-
-                case ARG_HELP:
-                        runlevel_help();
-                        return 0;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind < argc) {
-                log_error("Too many arguments.");
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-        assert(argc >= 0);
-        assert(argv);
-
-        if (program_invocation_short_name) {
-
-                if (strstr(program_invocation_short_name, "halt")) {
-                        arg_action = ACTION_HALT;
-                        return halt_parse_argv(argc, argv);
-                } else if (strstr(program_invocation_short_name, "poweroff")) {
-                        arg_action = ACTION_POWEROFF;
-                        return halt_parse_argv(argc, argv);
-                } else if (strstr(program_invocation_short_name, "reboot")) {
-                        if (kexec_loaded())
-                                arg_action = ACTION_KEXEC;
-                        else
-                                arg_action = ACTION_REBOOT;
-                        return halt_parse_argv(argc, argv);
-                } else if (strstr(program_invocation_short_name, "shutdown")) {
-                        arg_action = ACTION_POWEROFF;
-                        return shutdown_parse_argv(argc, argv);
-                } else if (strstr(program_invocation_short_name, "init")) {
-
-                        if (sd_booted() > 0) {
-                                arg_action = ACTION_INVALID;
-                                return telinit_parse_argv(argc, argv);
-                        } else {
-                                /* Hmm, so some other init system is
-                                 * running, we need to forward this
-                                 * request to it. For now we simply
-                                 * guess that it is Upstart. */
-
-                                execv("/lib/upstart/telinit", argv);
-
-                                log_error("Couldn't find an alternative telinit implementation to spawn.");
-                                return -EIO;
-                        }
-
-                } else if (strstr(program_invocation_short_name, "runlevel")) {
-                        arg_action = ACTION_RUNLEVEL;
-                        return runlevel_parse_argv(argc, argv);
-                }
-        }
-
-        arg_action = ACTION_SYSTEMCTL;
-        return systemctl_parse_argv(argc, argv);
-}
-
-static int action_to_runlevel(void) {
-
-        static const char table[_ACTION_MAX] = {
-                [ACTION_HALT] =      '0',
-                [ACTION_POWEROFF] =  '0',
-                [ACTION_REBOOT] =    '6',
-                [ACTION_RUNLEVEL2] = '2',
-                [ACTION_RUNLEVEL3] = '3',
-                [ACTION_RUNLEVEL4] = '4',
-                [ACTION_RUNLEVEL5] = '5',
-                [ACTION_RESCUE] =    '1'
-        };
-
-        assert(arg_action < _ACTION_MAX);
-
-        return table[arg_action];
-}
-
-static int talk_upstart(void) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int previous, rl, r;
-        char
-                env1_buf[] = "RUNLEVEL=X",
-                env2_buf[] = "PREVLEVEL=X";
-        char *env1 = env1_buf, *env2 = env2_buf;
-        const char *emit = "runlevel";
-        dbus_bool_t b_false = FALSE;
-        DBusMessageIter iter, sub;
-        DBusConnection *bus;
-
-        dbus_error_init(&error);
-
-        if (!(rl = action_to_runlevel()))
-                return 0;
-
-        if (utmp_get_runlevel(&previous, NULL) < 0)
-                previous = 'N';
-
-        if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) {
-                if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
-                        r = 0;
-                        goto finish;
-                }
-
-                log_error("Failed to connect to Upstart bus: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        if ((r = bus_check_peercred(bus)) < 0) {
-                log_error("Failed to verify owner of bus.");
-                goto finish;
-        }
-
-        if (!(m = dbus_message_new_method_call(
-                              "com.ubuntu.Upstart",
-                              "/com/ubuntu/Upstart",
-                              "com.ubuntu.Upstart0_6",
-                              "EmitEvent"))) {
-
-                log_error("Could not allocate message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        dbus_message_iter_init_append(m, &iter);
-
-        env1_buf[sizeof(env1_buf)-2] = rl;
-        env2_buf[sizeof(env2_buf)-2] = previous;
-
-        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
-            !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
-            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
-            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
-            !dbus_message_iter_close_container(&iter, &sub) ||
-            !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
-                log_error("Could not append arguments to message.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-
-                if (error_is_no_service(&error)) {
-                        r = -EADDRNOTAVAIL;
-                        goto finish;
-                }
-
-                log_error("Failed to issue method call: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        r = 1;
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        if (bus) {
-                dbus_connection_flush(bus);
-                dbus_connection_close(bus);
-                dbus_connection_unref(bus);
-        }
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int talk_initctl(void) {
-        struct init_request request;
-        int r, fd;
-        char rl;
-
-        if (!(rl = action_to_runlevel()))
-                return 0;
-
-        zero(request);
-        request.magic = INIT_MAGIC;
-        request.sleeptime = 0;
-        request.cmd = INIT_CMD_RUNLVL;
-        request.runlevel = rl;
-
-        if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
-
-                if (errno == ENOENT)
-                        return 0;
-
-                log_error("Failed to open "INIT_FIFO": %m");
-                return -errno;
-        }
-
-        errno = 0;
-        r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
-        close_nointr_nofail(fd);
-
-        if (r < 0) {
-                log_error("Failed to write to "INIT_FIFO": %m");
-                return errno ? -errno : -EIO;
-        }
-
-        return 1;
-}
-
-static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
-
-        static const struct {
-                const char* verb;
-                const enum {
-                        MORE,
-                        LESS,
-                        EQUAL
-                } argc_cmp;
-                const int argc;
-                int (* const dispatch)(DBusConnection *bus, char **args);
-        } verbs[] = {
-                { "list-units",            LESS,  1, list_units        },
-                { "list-unit-files",       EQUAL, 1, list_unit_files   },
-                { "list-jobs",             EQUAL, 1, list_jobs         },
-                { "clear-jobs",            EQUAL, 1, daemon_reload     },
-                { "load",                  MORE,  2, load_unit         },
-                { "cancel",                MORE,  2, cancel_job        },
-                { "start",                 MORE,  2, start_unit        },
-                { "stop",                  MORE,  2, start_unit        },
-                { "condstop",              MORE,  2, start_unit        }, /* For compatibility with ALTLinux */
-                { "reload",                MORE,  2, start_unit        },
-                { "restart",               MORE,  2, start_unit        },
-                { "try-restart",           MORE,  2, start_unit        },
-                { "reload-or-restart",     MORE,  2, start_unit        },
-                { "reload-or-try-restart", MORE,  2, start_unit        },
-                { "force-reload",          MORE,  2, start_unit        }, /* For compatibility with SysV */
-                { "condreload",            MORE,  2, start_unit        }, /* For compatibility with ALTLinux */
-                { "condrestart",           MORE,  2, start_unit        }, /* For compatibility with RH */
-                { "isolate",               EQUAL, 2, start_unit        },
-                { "kill",                  MORE,  2, kill_unit         },
-                { "is-active",             MORE,  2, check_unit        },
-                { "check",                 MORE,  2, check_unit        },
-                { "show",                  MORE,  1, show              },
-                { "status",                MORE,  2, show              },
-                { "dump",                  EQUAL, 1, dump              },
-                { "dot",                   EQUAL, 1, dot               },
-                { "snapshot",              LESS,  2, snapshot          },
-                { "delete",                MORE,  2, delete_snapshot   },
-                { "daemon-reload",         EQUAL, 1, daemon_reload     },
-                { "daemon-reexec",         EQUAL, 1, daemon_reload     },
-                { "show-environment",      EQUAL, 1, show_enviroment   },
-                { "set-environment",       MORE,  2, set_environment   },
-                { "unset-environment",     MORE,  2, set_environment   },
-                { "halt",                  EQUAL, 1, start_special     },
-                { "poweroff",              EQUAL, 1, start_special     },
-                { "reboot",                EQUAL, 1, start_special     },
-                { "kexec",                 EQUAL, 1, start_special     },
-                { "default",               EQUAL, 1, start_special     },
-                { "rescue",                EQUAL, 1, start_special     },
-                { "emergency",             EQUAL, 1, start_special     },
-                { "exit",                  EQUAL, 1, start_special     },
-                { "reset-failed",          MORE,  1, reset_failed      },
-                { "enable",                MORE,  2, enable_unit       },
-                { "disable",               MORE,  2, enable_unit       },
-                { "is-enabled",            MORE,  2, unit_is_enabled   },
-                { "reenable",              MORE,  2, enable_unit       },
-                { "preset",                MORE,  2, enable_unit       },
-                { "mask",                  MORE,  2, enable_unit       },
-                { "unmask",                MORE,  2, enable_unit       },
-                { "link",                  MORE,  2, enable_unit       }
-        };
-
-        int left;
-        unsigned i;
-
-        assert(argc >= 0);
-        assert(argv);
-        assert(error);
-
-        left = argc - optind;
-
-        if (left <= 0)
-                /* Special rule: no arguments means "list-units" */
-                i = 0;
-        else {
-                if (streq(argv[optind], "help")) {
-                        systemctl_help();
-                        return 0;
-                }
-
-                for (i = 0; i < ELEMENTSOF(verbs); i++)
-                        if (streq(argv[optind], verbs[i].verb))
-                                break;
-
-                if (i >= ELEMENTSOF(verbs)) {
-                        log_error("Unknown operation %s", argv[optind]);
-                        return -EINVAL;
-                }
-        }
-
-        switch (verbs[i].argc_cmp) {
-
-        case EQUAL:
-                if (left != verbs[i].argc) {
-                        log_error("Invalid number of arguments.");
-                        return -EINVAL;
-                }
-
-                break;
-
-        case MORE:
-                if (left < verbs[i].argc) {
-                        log_error("Too few arguments.");
-                        return -EINVAL;
-                }
-
-                break;
-
-        case LESS:
-                if (left > verbs[i].argc) {
-                        log_error("Too many arguments.");
-                        return -EINVAL;
-                }
-
-                break;
-
-        default:
-                assert_not_reached("Unknown comparison operator.");
-        }
-
-        /* Require a bus connection for all operations but
-         * enable/disable */
-        if (!streq(verbs[i].verb, "enable") &&
-            !streq(verbs[i].verb, "disable") &&
-            !streq(verbs[i].verb, "is-enabled") &&
-            !streq(verbs[i].verb, "list-unit-files") &&
-            !streq(verbs[i].verb, "reenable") &&
-            !streq(verbs[i].verb, "preset") &&
-            !streq(verbs[i].verb, "mask") &&
-            !streq(verbs[i].verb, "unmask") &&
-            !streq(verbs[i].verb, "link")) {
-
-                if (running_in_chroot() > 0) {
-                        log_info("Running in chroot, ignoring request.");
-                        return 0;
-                }
-
-                if (!bus) {
-                        log_error("Failed to get D-Bus connection: %s",
-                                  dbus_error_is_set(error) ? error->message : "No connection to service manager.");
-                        return -EIO;
-                }
-
-        } else {
-
-                if (!bus && !avoid_bus()) {
-                        log_error("Failed to get D-Bus connection: %s",
-                                  dbus_error_is_set(error) ? error->message : "No connection to service manager.");
-                        return -EIO;
-                }
-        }
-
-        return verbs[i].dispatch(bus, argv + optind);
-}
-
-static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
-        int fd;
-        struct msghdr msghdr;
-        struct iovec iovec[2];
-        union sockaddr_union sockaddr;
-        struct sd_shutdown_command c;
-
-        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
-        if (fd < 0)
-                return -errno;
-
-        zero(c);
-        c.usec = t;
-        c.mode = mode;
-        c.dry_run = dry_run;
-        c.warn_wall = warn;
-
-        zero(sockaddr);
-        sockaddr.sa.sa_family = AF_UNIX;
-        strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
-
-        zero(msghdr);
-        msghdr.msg_name = &sockaddr;
-        msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
-
-        zero(iovec);
-        iovec[0].iov_base = (char*) &c;
-        iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
-
-        if (isempty(message))
-                msghdr.msg_iovlen = 1;
-        else {
-                iovec[1].iov_base = (char*) message;
-                iovec[1].iov_len = strlen(message);
-                msghdr.msg_iovlen = 2;
-        }
-        msghdr.msg_iov = iovec;
-
-        if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
-                close_nointr_nofail(fd);
-                return -errno;
-        }
-
-        close_nointr_nofail(fd);
-        return 0;
-}
-
-static int reload_with_fallback(DBusConnection *bus) {
-
-        if (bus) {
-                /* First, try systemd via D-Bus. */
-                if (daemon_reload(bus, NULL) >= 0)
-                        return 0;
-        }
-
-        /* Nothing else worked, so let's try signals */
-        assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
-
-        if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
-                log_error("kill() failed: %m");
-                return -errno;
-        }
-
-        return 0;
-}
-
-static int start_with_fallback(DBusConnection *bus) {
-
-        if (bus) {
-                /* First, try systemd via D-Bus. */
-                if (start_unit(bus, NULL) >= 0)
-                        goto done;
-        }
-
-        /* Hmm, talking to systemd via D-Bus didn't work. Then
-         * let's try to talk to Upstart via D-Bus. */
-        if (talk_upstart() > 0)
-                goto done;
-
-        /* Nothing else worked, so let's try
-         * /dev/initctl */
-        if (talk_initctl() > 0)
-                goto done;
-
-        log_error("Failed to talk to init daemon.");
-        return -EIO;
-
-done:
-        warn_wall(arg_action);
-        return 0;
-}
-
-static void halt_now(enum action a) {
-
-       /* Make sure C-A-D is handled by the kernel from this
-         * point on... */
-        reboot(RB_ENABLE_CAD);
-
-        switch (a) {
-
-        case ACTION_HALT:
-                log_info("Halting.");
-                reboot(RB_HALT_SYSTEM);
-                break;
-
-        case ACTION_POWEROFF:
-                log_info("Powering off.");
-                reboot(RB_POWER_OFF);
-                break;
-
-        case ACTION_REBOOT:
-                log_info("Rebooting.");
-                reboot(RB_AUTOBOOT);
-                break;
-
-        default:
-                assert_not_reached("Unknown halt action.");
-        }
-
-        assert_not_reached("Uh? This shouldn't happen.");
-}
-
-static int halt_main(DBusConnection *bus) {
-        int r;
-
-        if (geteuid() != 0) {
-                /* Try logind if we are a normal user and no special
-                 * mode applies. Maybe PolicyKit allows us to shutdown
-                 * the machine. */
-
-                if (arg_when <= 0 &&
-                    !arg_dry &&
-                    !arg_immediate &&
-                    (arg_action == ACTION_POWEROFF ||
-                     arg_action == ACTION_REBOOT)) {
-                        r = reboot_with_logind(bus, arg_action);
-                        if (r >= 0)
-                                return r;
-                }
-
-                log_error("Must be root.");
-                return -EPERM;
-        }
-
-        if (arg_when > 0) {
-                char *m;
-
-                m = strv_join(arg_wall, " ");
-                r = send_shutdownd(arg_when,
-                                   arg_action == ACTION_HALT     ? 'H' :
-                                   arg_action == ACTION_POWEROFF ? 'P' :
-                                   arg_action == ACTION_KEXEC    ? 'K' :
-                                                                   'r',
-                                   arg_dry,
-                                   !arg_no_wall,
-                                   m);
-                free(m);
-
-                if (r < 0)
-                        log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
-                else {
-                        char date[FORMAT_TIMESTAMP_MAX];
-
-                        log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
-                                 format_timestamp(date, sizeof(date), arg_when));
-                        return 0;
-                }
-        }
-
-        if (!arg_dry && !arg_immediate)
-                return start_with_fallback(bus);
-
-        if (!arg_no_wtmp) {
-                if (sd_booted() > 0)
-                        log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
-                else {
-                        r = utmp_put_shutdown();
-                        if (r < 0)
-                                log_warning("Failed to write utmp record: %s", strerror(-r));
-                }
-        }
-
-        if (!arg_no_sync)
-                sync();
-
-        if (arg_dry)
-                return 0;
-
-        halt_now(arg_action);
-        /* We should never reach this. */
-        return -ENOSYS;
-}
-
-static int runlevel_main(void) {
-        int r, runlevel, previous;
-
-        r = utmp_get_runlevel(&runlevel, &previous);
-        if (r < 0) {
-                puts("unknown");
-                return r;
-        }
-
-        printf("%c %c\n",
-               previous <= 0 ? 'N' : previous,
-               runlevel <= 0 ? 'N' : runlevel);
-
-        return 0;
-}
-
-int main(int argc, char*argv[]) {
-        int r, retval = EXIT_FAILURE;
-        DBusConnection *bus = NULL;
-        DBusError error;
-
-        dbus_error_init(&error);
-
-        log_parse_environment();
-        log_open();
-
-        r = parse_argv(argc, argv);
-        if (r < 0)
-                goto finish;
-        else if (r == 0) {
-                retval = EXIT_SUCCESS;
-                goto finish;
-        }
-
-        /* /sbin/runlevel doesn't need to communicate via D-Bus, so
-         * let's shortcut this */
-        if (arg_action == ACTION_RUNLEVEL) {
-                r = runlevel_main();
-                retval = r < 0 ? EXIT_FAILURE : r;
-                goto finish;
-        }
-
-        if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
-                log_info("Running in chroot, ignoring request.");
-                retval = 0;
-                goto finish;
-        }
-
-        if (!avoid_bus()) {
-                if (arg_transport == TRANSPORT_NORMAL)
-                        bus_connect(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &bus, &private_bus, &error);
-                else if (arg_transport == TRANSPORT_POLKIT) {
-                        bus_connect_system_polkit(&bus, &error);
-                        private_bus = false;
-                } else if (arg_transport == TRANSPORT_SSH) {
-                        bus_connect_system_ssh(NULL, arg_host, &bus, &error);
-                        private_bus = false;
-                } else
-                        assert_not_reached("Uh, invalid transport...");
-        }
-
-        switch (arg_action) {
-
-        case ACTION_SYSTEMCTL:
-                r = systemctl_main(bus, argc, argv, &error);
-                break;
-
-        case ACTION_HALT:
-        case ACTION_POWEROFF:
-        case ACTION_REBOOT:
-        case ACTION_KEXEC:
-                r = halt_main(bus);
-                break;
-
-        case ACTION_RUNLEVEL2:
-        case ACTION_RUNLEVEL3:
-        case ACTION_RUNLEVEL4:
-        case ACTION_RUNLEVEL5:
-        case ACTION_RESCUE:
-        case ACTION_EMERGENCY:
-        case ACTION_DEFAULT:
-                r = start_with_fallback(bus);
-                break;
-
-        case ACTION_RELOAD:
-        case ACTION_REEXEC:
-                r = reload_with_fallback(bus);
-                break;
-
-        case ACTION_CANCEL_SHUTDOWN:
-                r = send_shutdownd(0, 0, false, false, NULL);
-                break;
-
-        case ACTION_INVALID:
-        case ACTION_RUNLEVEL:
-        default:
-                assert_not_reached("Unknown action");
-        }
-
-        retval = r < 0 ? EXIT_FAILURE : r;
-
-finish:
-        if (bus) {
-                dbus_connection_flush(bus);
-                dbus_connection_close(bus);
-                dbus_connection_unref(bus);
-        }
-
-        dbus_error_free(&error);
-
-        dbus_shutdown();
-
-        strv_free(arg_property);
-
-        pager_close();
-        ask_password_agent_close();
-        polkit_agent_close();
-
-        return retval;
-}
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
new file mode 100644
index 0000000..28bdfa9
--- /dev/null
+++ b/src/systemctl/systemctl.c
@@ -0,0 +1,5523 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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/reboot.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <stddef.h>
+#include <sys/prctl.h>
+#include <dbus/dbus.h>
+
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-shutdown.h>
+
+#include "log.h"
+#include "util.h"
+#include "macro.h"
+#include "set.h"
+#include "utmp-wtmp.h"
+#include "special.h"
+#include "initreq.h"
+#include "strv.h"
+#include "dbus-common.h"
+#include "cgroup-show.h"
+#include "cgroup-util.h"
+#include "list.h"
+#include "path-lookup.h"
+#include "conf-parser.h"
+#include "exit-status.h"
+#include "bus-errors.h"
+#include "build.h"
+#include "unit-name.h"
+#include "pager.h"
+#include "spawn-ask-password-agent.h"
+#include "spawn-polkit-agent.h"
+#include "install.h"
+#include "logs-show.h"
+
+static const char *arg_type = NULL;
+static char **arg_property = NULL;
+static bool arg_all = false;
+static const char *arg_job_mode = "replace";
+static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
+static bool arg_immediate = false;
+static bool arg_no_block = false;
+static bool arg_no_legend = false;
+static bool arg_no_pager = false;
+static bool arg_no_wtmp = false;
+static bool arg_no_sync = false;
+static bool arg_no_wall = false;
+static bool arg_no_reload = false;
+static bool arg_dry = false;
+static bool arg_quiet = false;
+static bool arg_full = false;
+static int arg_force = 0;
+static bool arg_ask_password = true;
+static bool arg_failed = false;
+static bool arg_runtime = false;
+static char **arg_wall = NULL;
+static const char *arg_kill_who = NULL;
+static const char *arg_kill_mode = NULL;
+static int arg_signal = SIGTERM;
+static const char *arg_root = NULL;
+static usec_t arg_when = 0;
+static enum action {
+        ACTION_INVALID,
+        ACTION_SYSTEMCTL,
+        ACTION_HALT,
+        ACTION_POWEROFF,
+        ACTION_REBOOT,
+        ACTION_KEXEC,
+        ACTION_EXIT,
+        ACTION_RUNLEVEL2,
+        ACTION_RUNLEVEL3,
+        ACTION_RUNLEVEL4,
+        ACTION_RUNLEVEL5,
+        ACTION_RESCUE,
+        ACTION_EMERGENCY,
+        ACTION_DEFAULT,
+        ACTION_RELOAD,
+        ACTION_REEXEC,
+        ACTION_RUNLEVEL,
+        ACTION_CANCEL_SHUTDOWN,
+        _ACTION_MAX
+} arg_action = ACTION_SYSTEMCTL;
+static enum dot {
+        DOT_ALL,
+        DOT_ORDER,
+        DOT_REQUIRE
+} arg_dot = DOT_ALL;
+static enum transport {
+        TRANSPORT_NORMAL,
+        TRANSPORT_SSH,
+        TRANSPORT_POLKIT
+} arg_transport = TRANSPORT_NORMAL;
+static const char *arg_host = NULL;
+static bool arg_follow = false;
+static unsigned arg_lines = 10;
+static OutputMode arg_output = OUTPUT_SHORT;
+
+static bool private_bus = false;
+
+static int daemon_reload(DBusConnection *bus, char **args);
+static void halt_now(enum action a);
+
+static bool on_tty(void) {
+        static int t = -1;
+
+        /* Note that this is invoked relatively early, before we start
+         * the pager. That means the value we return reflects whether
+         * we originally were started on a tty, not if we currently
+         * are. But this is intended, since we want colour and so on
+         * when run in our own pager. */
+
+        if (_unlikely_(t < 0))
+                t = isatty(STDOUT_FILENO) > 0;
+
+        return t;
+}
+
+static void pager_open_if_enabled(void) {
+
+        /* Cache result before we open the pager */
+        on_tty();
+
+        if (arg_no_pager)
+                return;
+
+        pager_open();
+}
+
+static void ask_password_agent_open_if_enabled(void) {
+
+        /* Open the password agent as a child process if necessary */
+
+        if (!arg_ask_password)
+                return;
+
+        if (arg_scope != UNIT_FILE_SYSTEM)
+                return;
+
+        ask_password_agent_open();
+}
+
+static void polkit_agent_open_if_enabled(void) {
+
+        /* Open the polkit agent as a child process if necessary */
+
+        if (!arg_ask_password)
+                return;
+
+        if (arg_scope != UNIT_FILE_SYSTEM)
+                return;
+
+        polkit_agent_open();
+}
+
+static const char *ansi_highlight_red(bool b) {
+
+        if (!on_tty())
+                return "";
+
+        return b ? ANSI_HIGHLIGHT_RED_ON : ANSI_HIGHLIGHT_OFF;
+}
+
+static const char *ansi_highlight_green(bool b) {
+
+        if (!on_tty())
+                return "";
+
+        return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF;
+}
+
+static bool error_is_no_service(const DBusError *error) {
+        assert(error);
+
+        if (!dbus_error_is_set(error))
+                return false;
+
+        if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
+                return true;
+
+        if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
+                return true;
+
+        return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
+}
+
+static int translate_bus_error_to_exit_status(int r, const DBusError *error) {
+        assert(error);
+
+        if (!dbus_error_is_set(error))
+                return r;
+
+        if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED) ||
+            dbus_error_has_name(error, BUS_ERROR_ONLY_BY_DEPENDENCY) ||
+            dbus_error_has_name(error, BUS_ERROR_NO_ISOLATION) ||
+            dbus_error_has_name(error, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE))
+                return EXIT_NOPERMISSION;
+
+        if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT))
+                return EXIT_NOTINSTALLED;
+
+        if (dbus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) ||
+            dbus_error_has_name(error, BUS_ERROR_NOT_SUPPORTED))
+                return EXIT_NOTIMPLEMENTED;
+
+        if (dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED))
+                return EXIT_NOTCONFIGURED;
+
+        if (r != 0)
+                return r;
+
+        return EXIT_FAILURE;
+}
+
+static void warn_wall(enum action a) {
+        static const char *table[_ACTION_MAX] = {
+                [ACTION_HALT]      = "The system is going down for system halt NOW!",
+                [ACTION_REBOOT]    = "The system is going down for reboot NOW!",
+                [ACTION_POWEROFF]  = "The system is going down for power-off NOW!",
+                [ACTION_KEXEC]     = "The system is going down for kexec reboot NOW!",
+                [ACTION_RESCUE]    = "The system is going down to rescue mode NOW!",
+                [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!"
+        };
+
+        if (arg_no_wall)
+                return;
+
+        if (arg_wall) {
+                char *p;
+
+                p = strv_join(arg_wall, " ");
+                if (!p) {
+                        log_error("Failed to join strings.");
+                        return;
+                }
+
+                if (*p) {
+                        utmp_wall(p, NULL);
+                        free(p);
+                        return;
+                }
+
+                free(p);
+        }
+
+        if (!table[a])
+                return;
+
+        utmp_wall(table[a], NULL);
+}
+
+static bool avoid_bus(void) {
+
+        if (running_in_chroot() > 0)
+                return true;
+
+        if (sd_booted() <= 0)
+                return true;
+
+        if (!isempty(arg_root))
+                return true;
+
+        if (arg_scope == UNIT_FILE_GLOBAL)
+                return true;
+
+        return false;
+}
+
+struct unit_info {
+        const char *id;
+        const char *description;
+        const char *load_state;
+        const char *active_state;
+        const char *sub_state;
+        const char *following;
+        const char *unit_path;
+        uint32_t job_id;
+        const char *job_type;
+        const char *job_path;
+};
+
+static int compare_unit_info(const void *a, const void *b) {
+        const char *d1, *d2;
+        const struct unit_info *u = a, *v = b;
+
+        d1 = strrchr(u->id, '.');
+        d2 = strrchr(v->id, '.');
+
+        if (d1 && d2) {
+                int r;
+
+                if ((r = strcasecmp(d1, d2)) != 0)
+                        return r;
+        }
+
+        return strcasecmp(u->id, v->id);
+}
+
+static bool output_show_unit(const struct unit_info *u) {
+        const char *dot;
+
+        if (arg_failed)
+                return streq(u->active_state, "failed");
+
+        return (!arg_type || ((dot = strrchr(u->id, '.')) &&
+                              streq(dot+1, arg_type))) &&
+                (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0);
+}
+
+static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
+        unsigned id_len, max_id_len, active_len, sub_len, job_len, desc_len, n_shown = 0;
+        const struct unit_info *u;
+
+        max_id_len = sizeof("UNIT")-1;
+        active_len = sizeof("ACTIVE")-1;
+        sub_len = sizeof("SUB")-1;
+        job_len = sizeof("JOB")-1;
+        desc_len = 0;
+
+        for (u = unit_infos; u < unit_infos + c; u++) {
+                if (!output_show_unit(u))
+                        continue;
+
+                max_id_len = MAX(max_id_len, strlen(u->id));
+                active_len = MAX(active_len, strlen(u->active_state));
+                sub_len = MAX(sub_len, strlen(u->sub_state));
+                if (u->job_id != 0)
+                        job_len = MAX(job_len, strlen(u->job_type));
+        }
+
+        if (!arg_full) {
+                unsigned basic_len;
+                id_len = MIN(max_id_len, 25);
+                basic_len = 5 + id_len + 6 + active_len + sub_len + job_len;
+                if (basic_len < (unsigned) columns()) {
+                        unsigned extra_len, incr;
+                        extra_len = columns() - basic_len;
+                        /* Either UNIT already got 25, or is fully satisfied.
+                         * Grant up to 25 to DESC now. */
+                        incr = MIN(extra_len, 25);
+                        desc_len += incr;
+                        extra_len -= incr;
+                        /* split the remaining space between UNIT and DESC,
+                         * but do not give UNIT more than it needs. */
+                        if (extra_len > 0) {
+                                incr = MIN(extra_len / 2, max_id_len - id_len);
+                                id_len += incr;
+                                desc_len += extra_len - incr;
+                        }
+                }
+        } else
+                id_len = max_id_len;
+
+        if (!arg_no_legend) {
+                printf("%-*s %-6s %-*s %-*s %-*s ", id_len, "UNIT", "LOAD",
+                       active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
+                if (!arg_full && arg_no_pager)
+                        printf("%.*s\n", desc_len, "DESCRIPTION");
+                else
+                        printf("%s\n", "DESCRIPTION");
+        }
+
+        for (u = unit_infos; u < unit_infos + c; u++) {
+                char *e;
+                const char *on_loaded, *off_loaded;
+                const char *on_active, *off_active;
+
+                if (!output_show_unit(u))
+                        continue;
+
+                n_shown++;
+
+                if (streq(u->load_state, "error")) {
+                        on_loaded = ansi_highlight_red(true);
+                        off_loaded = ansi_highlight_red(false);
+                } else
+                        on_loaded = off_loaded = "";
+
+                if (streq(u->active_state, "failed")) {
+                        on_active = ansi_highlight_red(true);
+                        off_active = ansi_highlight_red(false);
+                } else
+                        on_active = off_active = "";
+
+                e = arg_full ? NULL : ellipsize(u->id, id_len, 33);
+
+                printf("%-*s %s%-6s%s %s%-*s %-*s%s %-*s ",
+                       id_len, e ? e : u->id,
+                       on_loaded, u->load_state, off_loaded,
+                       on_active, active_len, u->active_state,
+                       sub_len, u->sub_state, off_active,
+                       job_len, u->job_id ? u->job_type : "");
+                if (!arg_full && arg_no_pager)
+                        printf("%.*s\n", desc_len, u->description);
+                else
+                        printf("%s\n", u->description);
+
+                free(e);
+        }
+
+        if (!arg_no_legend) {
+                printf("\nLOAD   = Reflects whether the unit definition was properly loaded.\n"
+                       "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
+                       "SUB    = The low-level unit activation state, values depend on unit type.\n"
+                       "JOB    = Pending job for the unit.\n");
+
+                if (arg_all)
+                        printf("\n%u units listed.\n", n_shown);
+                else
+                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown);
+        }
+}
+
+static int list_units(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        DBusMessageIter iter, sub, sub2;
+        unsigned c = 0, n_units = 0;
+        struct unit_info *unit_infos = NULL;
+
+        dbus_error_init(&error);
+
+        assert(bus);
+
+        pager_open_if_enabled();
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              "ListUnits"))) {
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                struct unit_info *u;
+
+                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (c >= n_units) {
+                        struct unit_info *w;
+
+                        n_units = MAX(2*c, 16);
+                        w = realloc(unit_infos, sizeof(struct unit_info) * n_units);
+
+                        if (!w) {
+                                log_error("Failed to allocate unit array.");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        unit_infos = w;
+                }
+
+                u = unit_infos+c;
+
+                dbus_message_iter_recurse(&sub, &sub2);
+
+                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->id, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->description, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->following, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_next(&sub);
+                c++;
+        }
+
+        if (c > 0) {
+                qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
+                output_units_list(unit_infos, c);
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        free(unit_infos);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int compare_unit_file_list(const void *a, const void *b) {
+        const char *d1, *d2;
+        const UnitFileList *u = a, *v = b;
+
+        d1 = strrchr(u->path, '.');
+        d2 = strrchr(v->path, '.');
+
+        if (d1 && d2) {
+                int r;
+
+                r = strcasecmp(d1, d2);
+                if (r != 0)
+                        return r;
+        }
+
+        return strcasecmp(file_name_from_path(u->path), file_name_from_path(v->path));
+}
+
+static bool output_show_unit_file(const UnitFileList *u) {
+        const char *dot;
+
+        return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type));
+}
+
+static void output_unit_file_list(const UnitFileList *units, unsigned c) {
+        unsigned max_id_len, id_cols, state_cols, n_shown = 0;
+        const UnitFileList *u;
+
+        max_id_len = sizeof("UNIT FILE")-1;
+        state_cols = sizeof("STATE")-1;
+        for (u = units; u < units + c; u++) {
+                if (!output_show_unit_file(u))
+                        continue;
+
+                max_id_len = MAX(max_id_len, strlen(file_name_from_path(u->path)));
+                state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
+        }
+
+        if (!arg_full) {
+                unsigned basic_cols;
+                id_cols = MIN(max_id_len, 25);
+                basic_cols = 1 + id_cols + state_cols;
+                if (basic_cols < (unsigned) columns())
+                        id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
+        } else
+                id_cols = max_id_len;
+
+        if (!arg_no_legend)
+                printf("%-*s %-*s\n", id_cols, "UNIT FILE", state_cols, "STATE");
+
+        for (u = units; u < units + c; u++) {
+                char *e;
+                const char *on, *off;
+                const char *id;
+
+                if (!output_show_unit_file(u))
+                        continue;
+
+                n_shown++;
+
+                if (u->state == UNIT_FILE_MASKED ||
+                    u->state == UNIT_FILE_MASKED_RUNTIME ||
+                    u->state == UNIT_FILE_DISABLED) {
+                        on  = ansi_highlight_red(true);
+                        off = ansi_highlight_red(false);
+                } else if (u->state == UNIT_FILE_ENABLED) {
+                        on  = ansi_highlight_green(true);
+                        off = ansi_highlight_green(false);
+                } else
+                        on = off = "";
+
+                id = file_name_from_path(u->path);
+
+                e = arg_full ? NULL : ellipsize(id, id_cols, 33);
+
+                printf("%-*s %s%-*s%s\n",
+                       id_cols, e ? e : id,
+                       on, state_cols, unit_file_state_to_string(u->state), off);
+
+                free(e);
+        }
+
+        if (!arg_no_legend)
+                printf("\n%u unit files listed.\n", n_shown);
+}
+
+static int list_unit_files(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        DBusMessageIter iter, sub, sub2;
+        unsigned c = 0, n_units = 0;
+        UnitFileList *units = NULL;
+
+        dbus_error_init(&error);
+
+        pager_open_if_enabled();
+
+        if (avoid_bus()) {
+                Hashmap *h;
+                UnitFileList *u;
+                Iterator i;
+
+                h = hashmap_new(string_hash_func, string_compare_func);
+                if (!h) {
+                        log_error("Out of memory");
+                        return -ENOMEM;
+                }
+
+                r = unit_file_get_list(arg_scope, arg_root, h);
+                if (r < 0) {
+                        unit_file_list_free(h);
+                        log_error("Failed to get unit file list: %s", strerror(-r));
+                        return r;
+                }
+
+                n_units = hashmap_size(h);
+                units = new(UnitFileList, n_units);
+                if (!units) {
+                        unit_file_list_free(h);
+                        log_error("Out of memory");
+                        return -ENOMEM;
+                }
+
+                HASHMAP_FOREACH(u, h, i) {
+                        memcpy(units + c++, u, sizeof(UnitFileList));
+                        free(u);
+                }
+
+                hashmap_free(h);
+        } else {
+                assert(bus);
+
+                m = dbus_message_new_method_call(
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                "ListUnitFiles");
+                if (!m) {
+                        log_error("Could not allocate message.");
+                        return -ENOMEM;
+                }
+
+                reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+                if (!reply) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (!dbus_message_iter_init(reply, &iter) ||
+                    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+                    dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&iter, &sub);
+
+                while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                        UnitFileList *u;
+                        const char *state;
+
+                        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+                                log_error("Failed to parse reply.");
+                                r = -EIO;
+                                goto finish;
+                        }
+
+                        if (c >= n_units) {
+                                UnitFileList *w;
+
+                                n_units = MAX(2*c, 16);
+                                w = realloc(units, sizeof(struct UnitFileList) * n_units);
+
+                                if (!w) {
+                                        log_error("Failed to allocate unit array.");
+                                        r = -ENOMEM;
+                                        goto finish;
+                                }
+
+                                units = w;
+                        }
+
+                        u = units+c;
+
+                        dbus_message_iter_recurse(&sub, &sub2);
+
+                        if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->path, true) < 0 ||
+                            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, false) < 0) {
+                                log_error("Failed to parse reply.");
+                                r = -EIO;
+                                goto finish;
+                        }
+
+                        u->state = unit_file_state_from_string(state);
+
+                        dbus_message_iter_next(&sub);
+                        c++;
+                }
+        }
+
+        if (c > 0) {
+                qsort(units, c, sizeof(UnitFileList), compare_unit_file_list);
+                output_unit_file_list(units, c);
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        free(units);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
+        static const char * const colors[] = {
+                "Requires",              "[color=\"black\"]",
+                "RequiresOverridable",   "[color=\"black\"]",
+                "Requisite",             "[color=\"darkblue\"]",
+                "RequisiteOverridable",  "[color=\"darkblue\"]",
+                "Wants",                 "[color=\"darkgrey\"]",
+                "Conflicts",             "[color=\"red\"]",
+                "ConflictedBy",          "[color=\"red\"]",
+                "After",                 "[color=\"green\"]"
+        };
+
+        const char *c = NULL;
+        unsigned i;
+
+        assert(name);
+        assert(prop);
+        assert(iter);
+
+        for (i = 0; i < ELEMENTSOF(colors); i += 2)
+                if (streq(colors[i], prop)) {
+                        c = colors[i+1];
+                        break;
+                }
+
+        if (!c)
+                return 0;
+
+        if (arg_dot != DOT_ALL)
+                if ((arg_dot == DOT_ORDER) != streq(prop, "After"))
+                        return 0;
+
+        switch (dbus_message_iter_get_arg_type(iter)) {
+
+        case DBUS_TYPE_ARRAY:
+
+                if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
+                        DBusMessageIter sub;
+
+                        dbus_message_iter_recurse(iter, &sub);
+
+                        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                                const char *s;
+
+                                assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
+                                dbus_message_iter_get_basic(&sub, &s);
+                                printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
+
+                                dbus_message_iter_next(&sub);
+                        }
+
+                        return 0;
+                }
+        }
+
+        return 0;
+}
+
+static int dot_one(DBusConnection *bus, const char *name, const char *path) {
+        DBusMessage *m = NULL, *reply = NULL;
+        const char *interface = "org.freedesktop.systemd1.Unit";
+        int r;
+        DBusError error;
+        DBusMessageIter iter, sub, sub2, sub3;
+
+        assert(bus);
+        assert(path);
+
+        dbus_error_init(&error);
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              path,
+                              "org.freedesktop.DBus.Properties",
+                              "GetAll"))) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &interface,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                const char *prop;
+
+                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&sub, &sub2);
+
+                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&sub2, &sub3);
+
+                if (dot_one_property(name, prop, &sub3)) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_next(&sub);
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int dot(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        DBusMessageIter iter, sub, sub2;
+
+        dbus_error_init(&error);
+
+        assert(bus);
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              "ListUnits"))) {
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        printf("digraph systemd {\n");
+
+        dbus_message_iter_recurse(&iter, &sub);
+        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path;
+
+                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&sub, &sub2);
+
+                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if ((r = dot_one(bus, id, unit_path)) < 0)
+                        goto finish;
+
+                /* printf("\t\"%s\";\n", id); */
+                dbus_message_iter_next(&sub);
+        }
+
+        printf("}\n");
+
+        log_info("   Color legend: black     = Requires\n"
+                 "                 dark blue = Requisite\n"
+                 "                 dark grey = Wants\n"
+                 "                 red       = Conflicts\n"
+                 "                 green     = After\n");
+
+        if (on_tty())
+                log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
+                           "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n");
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int list_jobs(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        DBusMessageIter iter, sub, sub2;
+        unsigned k = 0;
+
+        dbus_error_init(&error);
+
+        assert(bus);
+
+        pager_open_if_enabled();
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              "ListJobs"))) {
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        if (on_tty())
+                printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
+
+        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                const char *name, *type, *state, *job_path, *unit_path;
+                uint32_t id;
+                char *e;
+
+                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&sub, &sub2);
+
+                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                e = arg_full ? NULL : ellipsize(name, 25, 33);
+                printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state);
+                free(e);
+
+                k++;
+
+                dbus_message_iter_next(&sub);
+        }
+
+        if (on_tty())
+                printf("\n%u jobs listed.\n", k);
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int load_unit(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL;
+        DBusError error;
+        int r;
+        char **name;
+
+        dbus_error_init(&error);
+
+        assert(bus);
+        assert(args);
+
+        STRV_FOREACH(name, args+1) {
+                DBusMessage *reply;
+
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      "/org/freedesktop/systemd1",
+                                      "org.freedesktop.systemd1.Manager",
+                                      "LoadUnit"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, name,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_unref(m);
+                dbus_message_unref(reply);
+
+                m = reply = NULL;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int cancel_job(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        char **name;
+
+        dbus_error_init(&error);
+
+        assert(bus);
+        assert(args);
+
+        if (strv_length(args) <= 1)
+                return daemon_reload(bus, args);
+
+        STRV_FOREACH(name, args+1) {
+                unsigned id;
+                const char *path;
+
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      "/org/freedesktop/systemd1",
+                                      "org.freedesktop.systemd1.Manager",
+                                      "GetJob"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if ((r = safe_atou(*name, &id)) < 0) {
+                        log_error("Failed to parse job id: %s", strerror(-r));
+                        goto finish;
+                }
+
+                assert_cc(sizeof(uint32_t) == sizeof(id));
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_UINT32, &id,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (!dbus_message_get_args(reply, &error,
+                                           DBUS_TYPE_OBJECT_PATH, &path,
+                                           DBUS_TYPE_INVALID)) {
+                        log_error("Failed to parse reply: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_unref(m);
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      path,
+                                      "org.freedesktop.systemd1.Job",
+                                      "Cancel"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                dbus_message_unref(reply);
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_unref(m);
+                dbus_message_unref(reply);
+                m = reply = NULL;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
+        DBusMessage *m = NULL, *reply = NULL;
+        dbus_bool_t b = FALSE;
+        DBusMessageIter iter, sub;
+        const char
+                *interface = "org.freedesktop.systemd1.Unit",
+                *property = "NeedDaemonReload",
+                *path;
+
+        /* We ignore all errors here, since this is used to show a warning only */
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              "GetUnit")))
+                goto finish;
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &unit,
+                                      DBUS_TYPE_INVALID))
+                goto finish;
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
+                goto finish;
+
+        if (!dbus_message_get_args(reply, NULL,
+                                   DBUS_TYPE_OBJECT_PATH, &path,
+                                   DBUS_TYPE_INVALID))
+                goto finish;
+
+        dbus_message_unref(m);
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              path,
+                              "org.freedesktop.DBus.Properties",
+                              "Get")))
+                goto finish;
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &interface,
+                                      DBUS_TYPE_STRING, &property,
+                                      DBUS_TYPE_INVALID)) {
+                goto finish;
+        }
+
+        dbus_message_unref(reply);
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
+                goto finish;
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+                goto finish;
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+                goto finish;
+
+        dbus_message_iter_get_basic(&sub, &b);
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        return b;
+}
+
+typedef struct WaitData {
+        Set *set;
+        char *result;
+} WaitData;
+
+static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
+        DBusError error;
+        WaitData *d = data;
+
+        assert(connection);
+        assert(message);
+        assert(d);
+
+        dbus_error_init(&error);
+
+        log_debug("Got D-Bus request: %s.%s() on %s",
+                  dbus_message_get_interface(message),
+                  dbus_message_get_member(message),
+                  dbus_message_get_path(message));
+
+        if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
+                log_error("Warning! D-Bus connection terminated.");
+                dbus_connection_close(connection);
+
+        } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
+                uint32_t id;
+                const char *path, *result;
+                dbus_bool_t success = true;
+
+                if (dbus_message_get_args(message, &error,
+                                          DBUS_TYPE_UINT32, &id,
+                                          DBUS_TYPE_OBJECT_PATH, &path,
+                                          DBUS_TYPE_STRING, &result,
+                                          DBUS_TYPE_INVALID)) {
+                        char *p;
+
+                        if ((p = set_remove(d->set, (char*) path)))
+                                free(p);
+
+                        if (*result)
+                                d->result = strdup(result);
+
+                        goto finish;
+                }
+#ifndef LEGACY
+                dbus_error_free(&error);
+
+                if (dbus_message_get_args(message, &error,
+                                          DBUS_TYPE_UINT32, &id,
+                                          DBUS_TYPE_OBJECT_PATH, &path,
+                                          DBUS_TYPE_BOOLEAN, &success,
+                                          DBUS_TYPE_INVALID)) {
+                        char *p;
+
+                        /* Compatibility with older systemd versions <
+                         * 19 during upgrades. This should be dropped
+                         * one day */
+
+                        if ((p = set_remove(d->set, (char*) path)))
+                                free(p);
+
+                        if (!success)
+                                d->result = strdup("failed");
+
+                        goto finish;
+                }
+#endif
+
+                log_error("Failed to parse message: %s", bus_error_message(&error));
+        }
+
+finish:
+        dbus_error_free(&error);
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int enable_wait_for_jobs(DBusConnection *bus) {
+        DBusError error;
+
+        assert(bus);
+
+        if (private_bus)
+                return 0;
+
+        dbus_error_init(&error);
+        dbus_bus_add_match(bus,
+                           "type='signal',"
+                           "sender='org.freedesktop.systemd1',"
+                           "interface='org.freedesktop.systemd1.Manager',"
+                           "member='JobRemoved',"
+                           "path='/org/freedesktop/systemd1'",
+                           &error);
+
+        if (dbus_error_is_set(&error)) {
+                log_error("Failed to add match: %s", bus_error_message(&error));
+                dbus_error_free(&error);
+                return -EIO;
+        }
+
+        /* This is slightly dirty, since we don't undo the match registrations. */
+        return 0;
+}
+
+static int wait_for_jobs(DBusConnection *bus, Set *s) {
+        int r;
+        WaitData d;
+
+        assert(bus);
+        assert(s);
+
+        zero(d);
+        d.set = s;
+
+        if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) {
+                log_error("Failed to add filter.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        while (!set_isempty(s) &&
+               dbus_connection_read_write_dispatch(bus, -1))
+                ;
+
+        if (!arg_quiet && d.result) {
+                if (streq(d.result, "timeout"))
+                        log_error("Job timed out.");
+                else if (streq(d.result, "canceled"))
+                        log_error("Job canceled.");
+                else if (streq(d.result, "dependency"))
+                        log_error("A dependency job failed. See system journal for details.");
+                else if (!streq(d.result, "done") && !streq(d.result, "skipped"))
+                        log_error("Job failed. See system journal and 'systemctl status' for details.");
+        }
+
+        if (streq_ptr(d.result, "timeout"))
+                r = -ETIME;
+        else if (streq_ptr(d.result, "canceled"))
+                r = -ECANCELED;
+        else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped"))
+                r = -EIO;
+        else
+                r = 0;
+
+        free(d.result);
+
+finish:
+        /* This is slightly dirty, since we don't undo the filter registration. */
+
+        return r;
+}
+
+static int start_unit_one(
+                DBusConnection *bus,
+                const char *method,
+                const char *name,
+                const char *mode,
+                DBusError *error,
+                Set *s) {
+
+        DBusMessage *m = NULL, *reply = NULL;
+        const char *path;
+        int r;
+
+        assert(bus);
+        assert(method);
+        assert(name);
+        assert(mode);
+        assert(error);
+        assert(arg_no_block || s);
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              method))) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &name,
+                                      DBUS_TYPE_STRING, &mode,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error))) {
+
+                if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(error)) {
+                        /* There's always a fallback possible for
+                         * legacy actions. */
+                        r = -EADDRNOTAVAIL;
+                        goto finish;
+                }
+
+                log_error("Failed to issue method call: %s", bus_error_message(error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_get_args(reply, error,
+                                   DBUS_TYPE_OBJECT_PATH, &path,
+                                   DBUS_TYPE_INVALID)) {
+                log_error("Failed to parse reply: %s", bus_error_message(error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (need_daemon_reload(bus, name))
+                log_warning("Warning: Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
+                            arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
+
+        if (!arg_no_block) {
+                char *p;
+
+                if (!(p = strdup(path))) {
+                        log_error("Failed to duplicate path.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if ((r = set_put(s, p)) < 0) {
+                        free(p);
+                        log_error("Failed to add path to set.");
+                        goto finish;
+                }
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        return r;
+}
+
+static enum action verb_to_action(const char *verb) {
+        if (streq(verb, "halt"))
+                return ACTION_HALT;
+        else if (streq(verb, "poweroff"))
+                return ACTION_POWEROFF;
+        else if (streq(verb, "reboot"))
+                return ACTION_REBOOT;
+        else if (streq(verb, "kexec"))
+                return ACTION_KEXEC;
+        else if (streq(verb, "rescue"))
+                return ACTION_RESCUE;
+        else if (streq(verb, "emergency"))
+                return ACTION_EMERGENCY;
+        else if (streq(verb, "default"))
+                return ACTION_DEFAULT;
+        else if (streq(verb, "exit"))
+                return ACTION_EXIT;
+        else
+                return ACTION_INVALID;
+}
+
+static int start_unit(DBusConnection *bus, char **args) {
+
+        static const char * const table[_ACTION_MAX] = {
+                [ACTION_HALT] = SPECIAL_HALT_TARGET,
+                [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
+                [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
+                [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET,
+                [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
+                [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
+                [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
+                [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
+                [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
+                [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
+                [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
+                [ACTION_EXIT] = SPECIAL_EXIT_TARGET
+        };
+
+        int r, ret = 0;
+        const char *method, *mode, *one_name;
+        Set *s = NULL;
+        DBusError error;
+        char **name;
+
+        dbus_error_init(&error);
+
+        assert(bus);
+
+        ask_password_agent_open_if_enabled();
+
+        if (arg_action == ACTION_SYSTEMCTL) {
+                method =
+                        streq(args[0], "stop") ||
+                        streq(args[0], "condstop")              ? "StopUnit" :
+                        streq(args[0], "reload")                ? "ReloadUnit" :
+                        streq(args[0], "restart")               ? "RestartUnit" :
+
+                        streq(args[0], "try-restart")           ||
+                        streq(args[0], "condrestart")           ? "TryRestartUnit" :
+
+                        streq(args[0], "reload-or-restart")     ? "ReloadOrRestartUnit" :
+
+                        streq(args[0], "reload-or-try-restart") ||
+                        streq(args[0], "condreload") ||
+
+                        streq(args[0], "force-reload")          ? "ReloadOrTryRestartUnit" :
+                                                                  "StartUnit";
+
+                mode =
+                        (streq(args[0], "isolate") ||
+                         streq(args[0], "rescue")  ||
+                         streq(args[0], "emergency")) ? "isolate" : arg_job_mode;
+
+                one_name = table[verb_to_action(args[0])];
+
+        } else {
+                assert(arg_action < ELEMENTSOF(table));
+                assert(table[arg_action]);
+
+                method = "StartUnit";
+
+                mode = (arg_action == ACTION_EMERGENCY ||
+                        arg_action == ACTION_RESCUE ||
+                        arg_action == ACTION_RUNLEVEL2 ||
+                        arg_action == ACTION_RUNLEVEL3 ||
+                        arg_action == ACTION_RUNLEVEL4 ||
+                        arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace";
+
+                one_name = table[arg_action];
+        }
+
+        if (!arg_no_block) {
+                if ((ret = enable_wait_for_jobs(bus)) < 0) {
+                        log_error("Could not watch jobs: %s", strerror(-ret));
+                        goto finish;
+                }
+
+                if (!(s = set_new(string_hash_func, string_compare_func))) {
+                        log_error("Failed to allocate set.");
+                        ret = -ENOMEM;
+                        goto finish;
+                }
+        }
+
+        if (one_name) {
+                if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0)
+                        goto finish;
+        } else {
+                STRV_FOREACH(name, args+1)
+                        if ((r = start_unit_one(bus, method, *name, mode, &error, s)) != 0) {
+                                ret = translate_bus_error_to_exit_status(r, &error);
+                                dbus_error_free(&error);
+                        }
+        }
+
+        if (!arg_no_block)
+                if ((r = wait_for_jobs(bus, s)) < 0) {
+                        ret = r;
+                        goto finish;
+                }
+
+finish:
+        if (s)
+                set_free_free(s);
+
+        dbus_error_free(&error);
+
+        return ret;
+}
+
+/* Ask systemd-logind, which might grant access to unprivileged users
+ * through PolicyKit */
+static int reboot_with_logind(DBusConnection *bus, enum action a) {
+#ifdef HAVE_LOGIND
+        const char *method;
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        dbus_bool_t interactive = true;
+        int r;
+
+        dbus_error_init(&error);
+
+        polkit_agent_open_if_enabled();
+
+        switch (a) {
+
+        case ACTION_REBOOT:
+                method = "Reboot";
+                break;
+
+        case ACTION_POWEROFF:
+                method = "PowerOff";
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        m = dbus_message_new_method_call(
+                                "org.freedesktop.login1",
+                                "/org/freedesktop/login1",
+                                "org.freedesktop.login1.Manager",
+                                method);
+        if (!m) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_BOOLEAN, &interactive,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+        if (!reply) {
+                if (error_is_no_service(&error)) {
+                        log_debug("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -ENOENT;
+                        goto finish;
+                }
+
+                if (dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
+                        log_debug("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EACCES;
+                        goto finish;
+                }
+
+                log_info("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+#else
+        return -ENOSYS;
+#endif
+}
+
+static int start_special(DBusConnection *bus, char **args) {
+        enum action a;
+        int r;
+
+        assert(bus);
+        assert(args);
+
+        a = verb_to_action(args[0]);
+
+        if (arg_force >= 2 &&
+            (a == ACTION_HALT ||
+             a == ACTION_POWEROFF ||
+             a == ACTION_REBOOT))
+                halt_now(a);
+
+        if (arg_force >= 1 &&
+            (a == ACTION_HALT ||
+             a == ACTION_POWEROFF ||
+             a == ACTION_REBOOT ||
+             a == ACTION_KEXEC ||
+             a == ACTION_EXIT))
+                return daemon_reload(bus, args);
+
+        /* first try logind, to allow authentication with polkit */
+        if (geteuid() != 0 &&
+            (a == ACTION_POWEROFF ||
+             a == ACTION_REBOOT)) {
+                r = reboot_with_logind(bus, a);
+                if (r >= 0)
+                        return r;
+        }
+
+        r = start_unit(bus, args);
+        if (r >= 0)
+                warn_wall(a);
+
+        return r;
+}
+
+static int check_unit(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        const char
+                *interface = "org.freedesktop.systemd1.Unit",
+                *property = "ActiveState";
+        int r = 3; /* According to LSB: "program is not running" */
+        DBusError error;
+        char **name;
+
+        assert(bus);
+        assert(args);
+
+        dbus_error_init(&error);
+
+        STRV_FOREACH(name, args+1) {
+                const char *path = NULL;
+                const char *state;
+                DBusMessageIter iter, sub;
+
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      "/org/freedesktop/systemd1",
+                                      "org.freedesktop.systemd1.Manager",
+                                      "GetUnit"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, name,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+                        /* Hmm, cannot figure out anything about this unit... */
+                        if (!arg_quiet)
+                                puts("unknown");
+
+                        dbus_error_free(&error);
+                        dbus_message_unref(m);
+                        m = NULL;
+                        continue;
+                }
+
+                if (!dbus_message_get_args(reply, &error,
+                                           DBUS_TYPE_OBJECT_PATH, &path,
+                                           DBUS_TYPE_INVALID)) {
+                        log_error("Failed to parse reply: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_unref(m);
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      path,
+                                      "org.freedesktop.DBus.Properties",
+                                      "Get"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, &interface,
+                                              DBUS_TYPE_STRING, &property,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                dbus_message_unref(reply);
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (!dbus_message_iter_init(reply, &iter) ||
+                    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&iter, &sub);
+
+                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_get_basic(&sub, &state);
+
+                if (!arg_quiet)
+                        puts(state);
+
+                if (streq(state, "active") || streq(state, "reloading"))
+                        r = 0;
+
+                dbus_message_unref(m);
+                dbus_message_unref(reply);
+                m = reply = NULL;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int kill_unit(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL;
+        int r = 0;
+        DBusError error;
+        char **name;
+
+        assert(bus);
+        assert(args);
+
+        dbus_error_init(&error);
+
+        if (!arg_kill_who)
+                arg_kill_who = "all";
+
+        if (!arg_kill_mode)
+                arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
+
+        STRV_FOREACH(name, args+1) {
+                DBusMessage *reply;
+
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      "/org/freedesktop/systemd1",
+                                      "org.freedesktop.systemd1.Manager",
+                                      "KillUnit"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, name,
+                                              DBUS_TYPE_STRING, &arg_kill_who,
+                                              DBUS_TYPE_STRING, &arg_kill_mode,
+                                              DBUS_TYPE_INT32, &arg_signal,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        dbus_error_free(&error);
+                        r = -EIO;
+                }
+
+                dbus_message_unref(m);
+
+                if (reply)
+                        dbus_message_unref(reply);
+                m = reply = NULL;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+typedef struct ExecStatusInfo {
+        char *name;
+
+        char *path;
+        char **argv;
+
+        bool ignore;
+
+        usec_t start_timestamp;
+        usec_t exit_timestamp;
+        pid_t pid;
+        int code;
+        int status;
+
+        LIST_FIELDS(struct ExecStatusInfo, exec);
+} ExecStatusInfo;
+
+static void exec_status_info_free(ExecStatusInfo *i) {
+        assert(i);
+
+        free(i->name);
+        free(i->path);
+        strv_free(i->argv);
+        free(i);
+}
+
+static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) {
+        uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
+        DBusMessageIter sub2, sub3;
+        const char*path;
+        unsigned n;
+        uint32_t pid;
+        int32_t code, status;
+        dbus_bool_t ignore;
+
+        assert(i);
+        assert(i);
+
+        if (dbus_message_iter_get_arg_type(sub) != DBUS_TYPE_STRUCT)
+                return -EIO;
+
+        dbus_message_iter_recurse(sub, &sub2);
+
+        if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
+                return -EIO;
+
+        if (!(i->path = strdup(path)))
+                return -ENOMEM;
+
+        if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&sub2) != DBUS_TYPE_STRING)
+                return -EIO;
+
+        n = 0;
+        dbus_message_iter_recurse(&sub2, &sub3);
+        while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
+                assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
+                dbus_message_iter_next(&sub3);
+                n++;
+        }
+
+
+        if (!(i->argv = new0(char*, n+1)))
+                return -ENOMEM;
+
+        n = 0;
+        dbus_message_iter_recurse(&sub2, &sub3);
+        while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
+                const char *s;
+
+                assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
+                dbus_message_iter_get_basic(&sub3, &s);
+                dbus_message_iter_next(&sub3);
+
+                if (!(i->argv[n++] = strdup(s)))
+                        return -ENOMEM;
+        }
+
+        if (!dbus_message_iter_next(&sub2) ||
+            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, true) < 0 ||
+            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp, true) < 0 ||
+            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp_monotonic, true) < 0 ||
+            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp, true) < 0 ||
+            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp_monotonic, true) < 0 ||
+            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, true) < 0 ||
+            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &code, true) < 0 ||
+            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &status, false) < 0)
+                return -EIO;
+
+        i->ignore = ignore;
+        i->start_timestamp = (usec_t) start_timestamp;
+        i->exit_timestamp = (usec_t) exit_timestamp;
+        i->pid = (pid_t) pid;
+        i->code = code;
+        i->status = status;
+
+        return 0;
+}
+
+typedef struct UnitStatusInfo {
+        const char *id;
+        const char *load_state;
+        const char *active_state;
+        const char *sub_state;
+        const char *unit_file_state;
+
+        const char *description;
+        const char *following;
+
+        const char *path;
+        const char *default_control_group;
+
+        const char *load_error;
+        const char *result;
+
+        usec_t inactive_exit_timestamp;
+        usec_t inactive_exit_timestamp_monotonic;
+        usec_t active_enter_timestamp;
+        usec_t active_exit_timestamp;
+        usec_t inactive_enter_timestamp;
+
+        bool need_daemon_reload;
+
+        /* Service */
+        pid_t main_pid;
+        pid_t control_pid;
+        const char *status_text;
+        bool running:1;
+#ifdef HAVE_SYSV_COMPAT
+        bool is_sysv:1;
+#endif
+
+        usec_t start_timestamp;
+        usec_t exit_timestamp;
+
+        int exit_code, exit_status;
+
+        usec_t condition_timestamp;
+        bool condition_result;
+
+        /* Socket */
+        unsigned n_accepted;
+        unsigned n_connections;
+        bool accept;
+
+        /* Device */
+        const char *sysfs_path;
+
+        /* Mount, Automount */
+        const char *where;
+
+        /* Swap */
+        const char *what;
+
+        LIST_HEAD(ExecStatusInfo, exec);
+} UnitStatusInfo;
+
+static void print_status_info(UnitStatusInfo *i) {
+        ExecStatusInfo *p;
+        const char *on, *off, *ss;
+        usec_t timestamp;
+        char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
+        char since2[FORMAT_TIMESTAMP_MAX], *s2;
+
+        assert(i);
+
+        /* This shows pretty information about a unit. See
+         * print_property() for a low-level property printer */
+
+        printf("%s", strna(i->id));
+
+        if (i->description && !streq_ptr(i->id, i->description))
+                printf(" - %s", i->description);
+
+        printf("\n");
+
+        if (i->following)
+                printf("\t  Follow: unit currently follows state of %s\n", i->following);
+
+        if (streq_ptr(i->load_state, "error")) {
+                on = ansi_highlight_red(true);
+                off = ansi_highlight_red(false);
+        } else
+                on = off = "";
+
+        if (i->load_error)
+                printf("\t  Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
+        else if (i->path && i->unit_file_state)
+                printf("\t  Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, i->path, i->unit_file_state);
+        else if (i->path)
+                printf("\t  Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
+        else
+                printf("\t  Loaded: %s%s%s\n", on, strna(i->load_state), off);
+
+        ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
+
+        if (streq_ptr(i->active_state, "failed")) {
+                on = ansi_highlight_red(true);
+                off = ansi_highlight_red(false);
+        } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
+                on = ansi_highlight_green(true);
+                off = ansi_highlight_green(false);
+        } else
+                on = off = "";
+
+        if (ss)
+                printf("\t  Active: %s%s (%s)%s",
+                       on,
+                       strna(i->active_state),
+                       ss,
+                       off);
+        else
+                printf("\t  Active: %s%s%s",
+                       on,
+                       strna(i->active_state),
+                       off);
+
+        if (!isempty(i->result) && !streq(i->result, "success"))
+                printf(" (Result: %s)", i->result);
+
+        timestamp = (streq_ptr(i->active_state, "active")      ||
+                     streq_ptr(i->active_state, "reloading"))   ? i->active_enter_timestamp :
+                    (streq_ptr(i->active_state, "inactive")    ||
+                     streq_ptr(i->active_state, "failed"))      ? i->inactive_enter_timestamp :
+                    streq_ptr(i->active_state, "activating")    ? i->inactive_exit_timestamp :
+                                                                  i->active_exit_timestamp;
+
+        s1 = format_timestamp_pretty(since1, sizeof(since1), timestamp);
+        s2 = format_timestamp(since2, sizeof(since2), timestamp);
+
+        if (s1)
+                printf(" since %s; %s\n", s2, s1);
+        else if (s2)
+                printf(" since %s\n", s2);
+        else
+                printf("\n");
+
+        if (!i->condition_result && i->condition_timestamp > 0) {
+                s1 = format_timestamp_pretty(since1, sizeof(since1), i->condition_timestamp);
+                s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
+
+                if (s1)
+                        printf("\t          start condition failed at %s; %s\n", s2, s1);
+                else if (s2)
+                        printf("\t          start condition failed at %s\n", s2);
+        }
+
+        if (i->sysfs_path)
+                printf("\t  Device: %s\n", i->sysfs_path);
+        if (i->where)
+                printf("\t   Where: %s\n", i->where);
+        if (i->what)
+                printf("\t    What: %s\n", i->what);
+
+        if (i->accept)
+                printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
+
+        LIST_FOREACH(exec, p, i->exec) {
+                char *t;
+                bool good;
+
+                /* Only show exited processes here */
+                if (p->code == 0)
+                        continue;
+
+                t = strv_join(p->argv, " ");
+                printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
+                free(t);
+
+#ifdef HAVE_SYSV_COMPAT
+                if (i->is_sysv)
+                        good = is_clean_exit_lsb(p->code, p->status);
+                else
+#endif
+                        good = is_clean_exit(p->code, p->status);
+
+                if (!good) {
+                        on = ansi_highlight_red(true);
+                        off = ansi_highlight_red(false);
+                } else
+                        on = off = "";
+
+                printf("%s(code=%s, ", on, sigchld_code_to_string(p->code));
+
+                if (p->code == CLD_EXITED) {
+                        const char *c;
+
+                        printf("status=%i", p->status);
+
+#ifdef HAVE_SYSV_COMPAT
+                        if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+#else
+                        if ((c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD)))
+#endif
+                                printf("/%s", c);
+
+                } else
+                        printf("signal=%s", signal_to_string(p->status));
+
+                printf(")%s\n", off);
+
+                if (i->main_pid == p->pid &&
+                    i->start_timestamp == p->start_timestamp &&
+                    i->exit_timestamp == p->start_timestamp)
+                        /* Let's not show this twice */
+                        i->main_pid = 0;
+
+                if (p->pid == i->control_pid)
+                        i->control_pid = 0;
+        }
+
+        if (i->main_pid > 0 || i->control_pid > 0) {
+                printf("\t");
+
+                if (i->main_pid > 0) {
+                        printf("Main PID: %u", (unsigned) i->main_pid);
+
+                        if (i->running) {
+                                char *t = NULL;
+                                get_process_comm(i->main_pid, &t);
+                                if (t) {
+                                        printf(" (%s)", t);
+                                        free(t);
+                                }
+                        } else if (i->exit_code > 0) {
+                                printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
+
+                                if (i->exit_code == CLD_EXITED) {
+                                        const char *c;
+
+                                        printf("status=%i", i->exit_status);
+
+#ifdef HAVE_SYSV_COMPAT
+                                        if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+#else
+                                        if ((c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD)))
+#endif
+                                                printf("/%s", c);
+
+                                } else
+                                        printf("signal=%s", signal_to_string(i->exit_status));
+                                printf(")");
+                        }
+                }
+
+                if (i->main_pid > 0 && i->control_pid > 0)
+                        printf(";");
+
+                if (i->control_pid > 0) {
+                        char *t = NULL;
+
+                        printf(" Control: %u", (unsigned) i->control_pid);
+
+                        get_process_comm(i->control_pid, &t);
+                        if (t) {
+                                printf(" (%s)", t);
+                                free(t);
+                        }
+                }
+
+                printf("\n");
+        }
+
+        if (i->status_text)
+                printf("\t  Status: \"%s\"\n", i->status_text);
+
+        if (i->default_control_group) {
+                unsigned c;
+
+                printf("\t  CGroup: %s\n", i->default_control_group);
+
+                if (arg_transport != TRANSPORT_SSH) {
+                        if ((c = columns()) > 18)
+                                c -= 18;
+                        else
+                                c = 0;
+
+                        show_cgroup_by_path(i->default_control_group, "\t\t  ", c, false);
+                }
+        }
+
+        if (i->id && arg_transport != TRANSPORT_SSH) {
+                printf("\n");
+                show_journal_by_unit(i->id, arg_output, 0, i->inactive_exit_timestamp_monotonic, arg_lines, arg_all, arg_follow);
+        }
+
+        if (i->need_daemon_reload)
+                printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
+                       ansi_highlight_red(true),
+                       ansi_highlight_red(false),
+                       arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
+}
+
+static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
+
+        assert(name);
+        assert(iter);
+        assert(i);
+
+        switch (dbus_message_iter_get_arg_type(iter)) {
+
+        case DBUS_TYPE_STRING: {
+                const char *s;
+
+                dbus_message_iter_get_basic(iter, &s);
+
+                if (!isempty(s)) {
+                        if (streq(name, "Id"))
+                                i->id = s;
+                        else if (streq(name, "LoadState"))
+                                i->load_state = s;
+                        else if (streq(name, "ActiveState"))
+                                i->active_state = s;
+                        else if (streq(name, "SubState"))
+                                i->sub_state = s;
+                        else if (streq(name, "Description"))
+                                i->description = s;
+                        else if (streq(name, "FragmentPath"))
+                                i->path = s;
+#ifdef HAVE_SYSV_COMPAT
+                        else if (streq(name, "SysVPath")) {
+                                i->is_sysv = true;
+                                i->path = s;
+                        }
+#endif
+                        else if (streq(name, "DefaultControlGroup"))
+                                i->default_control_group = s;
+                        else if (streq(name, "StatusText"))
+                                i->status_text = s;
+                        else if (streq(name, "SysFSPath"))
+                                i->sysfs_path = s;
+                        else if (streq(name, "Where"))
+                                i->where = s;
+                        else if (streq(name, "What"))
+                                i->what = s;
+                        else if (streq(name, "Following"))
+                                i->following = s;
+                        else if (streq(name, "UnitFileState"))
+                                i->unit_file_state = s;
+                        else if (streq(name, "Result"))
+                                i->result = s;
+                }
+
+                break;
+        }
+
+        case DBUS_TYPE_BOOLEAN: {
+                dbus_bool_t b;
+
+                dbus_message_iter_get_basic(iter, &b);
+
+                if (streq(name, "Accept"))
+                        i->accept = b;
+                else if (streq(name, "NeedDaemonReload"))
+                        i->need_daemon_reload = b;
+                else if (streq(name, "ConditionResult"))
+                        i->condition_result = b;
+
+                break;
+        }
+
+        case DBUS_TYPE_UINT32: {
+                uint32_t u;
+
+                dbus_message_iter_get_basic(iter, &u);
+
+                if (streq(name, "MainPID")) {
+                        if (u > 0) {
+                                i->main_pid = (pid_t) u;
+                                i->running = true;
+                        }
+                } else if (streq(name, "ControlPID"))
+                        i->control_pid = (pid_t) u;
+                else if (streq(name, "ExecMainPID")) {
+                        if (u > 0)
+                                i->main_pid = (pid_t) u;
+                } else if (streq(name, "NAccepted"))
+                        i->n_accepted = u;
+                else if (streq(name, "NConnections"))
+                        i->n_connections = u;
+
+                break;
+        }
+
+        case DBUS_TYPE_INT32: {
+                int32_t j;
+
+                dbus_message_iter_get_basic(iter, &j);
+
+                if (streq(name, "ExecMainCode"))
+                        i->exit_code = (int) j;
+                else if (streq(name, "ExecMainStatus"))
+                        i->exit_status = (int) j;
+
+                break;
+        }
+
+        case DBUS_TYPE_UINT64: {
+                uint64_t u;
+
+                dbus_message_iter_get_basic(iter, &u);
+
+                if (streq(name, "ExecMainStartTimestamp"))
+                        i->start_timestamp = (usec_t) u;
+                else if (streq(name, "ExecMainExitTimestamp"))
+                        i->exit_timestamp = (usec_t) u;
+                else if (streq(name, "ActiveEnterTimestamp"))
+                        i->active_enter_timestamp = (usec_t) u;
+                else if (streq(name, "InactiveEnterTimestamp"))
+                        i->inactive_enter_timestamp = (usec_t) u;
+                else if (streq(name, "InactiveExitTimestamp"))
+                        i->inactive_exit_timestamp = (usec_t) u;
+                else if (streq(name, "InactiveExitTimestampMonotonic"))
+                        i->inactive_exit_timestamp_monotonic = (usec_t) u;
+                else if (streq(name, "ActiveExitTimestamp"))
+                        i->active_exit_timestamp = (usec_t) u;
+                else if (streq(name, "ConditionTimestamp"))
+                        i->condition_timestamp = (usec_t) u;
+
+                break;
+        }
+
+        case DBUS_TYPE_ARRAY: {
+
+                if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT &&
+                    startswith(name, "Exec")) {
+                        DBusMessageIter sub;
+
+                        dbus_message_iter_recurse(iter, &sub);
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                ExecStatusInfo *info;
+                                int r;
+
+                                if (!(info = new0(ExecStatusInfo, 1)))
+                                        return -ENOMEM;
+
+                                if (!(info->name = strdup(name))) {
+                                        free(info);
+                                        return -ENOMEM;
+                                }
+
+                                if ((r = exec_status_info_deserialize(&sub, info)) < 0) {
+                                        free(info);
+                                        return r;
+                                }
+
+                                LIST_PREPEND(ExecStatusInfo, exec, i->exec, info);
+
+                                dbus_message_iter_next(&sub);
+                        }
+                }
+
+                break;
+        }
+
+        case DBUS_TYPE_STRUCT: {
+
+                if (streq(name, "LoadError")) {
+                        DBusMessageIter sub;
+                        const char *n, *message;
+                        int r;
+
+                        dbus_message_iter_recurse(iter, &sub);
+
+                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &n, true);
+                        if (r < 0)
+                                return r;
+
+                        r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &message, false);
+                        if (r < 0)
+                                return r;
+
+                        if (!isempty(message))
+                                i->load_error = message;
+                }
+
+                break;
+        }
+        }
+
+        return 0;
+}
+
+static int print_property(const char *name, DBusMessageIter *iter) {
+        assert(name);
+        assert(iter);
+
+        /* This is a low-level property printer, see
+         * print_status_info() for the nicer output */
+
+        if (arg_property && !strv_find(arg_property, name))
+                return 0;
+
+        switch (dbus_message_iter_get_arg_type(iter)) {
+
+        case DBUS_TYPE_STRUCT: {
+                DBusMessageIter sub;
+                dbus_message_iter_recurse(iter, &sub);
+
+                if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "Job")) {
+                        uint32_t u;
+
+                        dbus_message_iter_get_basic(&sub, &u);
+
+                        if (u)
+                                printf("%s=%u\n", name, (unsigned) u);
+                        else if (arg_all)
+                                printf("%s=\n", name);
+
+                        return 0;
+                } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Unit")) {
+                        const char *s;
+
+                        dbus_message_iter_get_basic(&sub, &s);
+
+                        if (arg_all || s[0])
+                                printf("%s=%s\n", name, s);
+
+                        return 0;
+                } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "LoadError")) {
+                        const char *a = NULL, *b = NULL;
+
+                        if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) >= 0)
+                                bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &b, false);
+
+                        if (arg_all || !isempty(a) || !isempty(b))
+                                printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
+
+                        return 0;
+                }
+
+                break;
+        }
+
+        case DBUS_TYPE_ARRAY:
+
+                if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "EnvironmentFiles")) {
+                        DBusMessageIter sub, sub2;
+
+                        dbus_message_iter_recurse(iter, &sub);
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                const char *path;
+                                dbus_bool_t ignore;
+
+                                dbus_message_iter_recurse(&sub, &sub2);
+
+                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 &&
+                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, false) >= 0)
+                                        printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore));
+
+                                dbus_message_iter_next(&sub);
+                        }
+
+                        return 0;
+
+                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Paths")) {
+                        DBusMessageIter sub, sub2;
+
+                        dbus_message_iter_recurse(iter, &sub);
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                const char *type, *path;
+
+                                dbus_message_iter_recurse(&sub, &sub2);
+
+                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
+                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0)
+                                        printf("%s=%s\n", type, path);
+
+                                dbus_message_iter_next(&sub);
+                        }
+
+                        return 0;
+
+                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) {
+                        DBusMessageIter sub, sub2;
+
+                        dbus_message_iter_recurse(iter, &sub);
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                const char *base;
+                                uint64_t value, next_elapse;
+
+                                dbus_message_iter_recurse(&sub, &sub2);
+
+                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 &&
+                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 &&
+                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) {
+                                        char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
+
+                                        printf("%s={ value=%s ; next_elapse=%s }\n",
+                                               base,
+                                               format_timespan(timespan1, sizeof(timespan1), value),
+                                               format_timespan(timespan2, sizeof(timespan2), next_elapse));
+                                }
+
+                                dbus_message_iter_next(&sub);
+                        }
+
+                        return 0;
+
+                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "ControlGroupAttributes")) {
+                        DBusMessageIter sub, sub2;
+
+                        dbus_message_iter_recurse(iter, &sub);
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                const char *controller, *attr, *value;
+
+                                dbus_message_iter_recurse(&sub, &sub2);
+
+                                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &controller, true) >= 0 &&
+                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 &&
+                                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) {
+
+                                        printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n",
+                                               controller,
+                                               attr,
+                                               value);
+                                }
+
+                                dbus_message_iter_next(&sub);
+                        }
+
+                        return 0;
+
+                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) {
+                        DBusMessageIter sub;
+
+                        dbus_message_iter_recurse(iter, &sub);
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                ExecStatusInfo info;
+
+                                zero(info);
+                                if (exec_status_info_deserialize(&sub, &info) >= 0) {
+                                        char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
+                                        char *t;
+
+                                        t = strv_join(info.argv, " ");
+
+                                        printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
+                                               name,
+                                               strna(info.path),
+                                               strna(t),
+                                               yes_no(info.ignore),
+                                               strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+                                               strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+                                               (unsigned) info. pid,
+                                               sigchld_code_to_string(info.code),
+                                               info.status,
+                                               info.code == CLD_EXITED ? "" : "/",
+                                               strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+
+                                        free(t);
+                                }
+
+                                free(info.path);
+                                strv_free(info.argv);
+
+                                dbus_message_iter_next(&sub);
+                        }
+
+                        return 0;
+                }
+
+                break;
+        }
+
+        if (generic_print_property(name, iter, arg_all) > 0)
+                return 0;
+
+        if (arg_all)
+                printf("%s=[unprintable]\n", name);
+
+        return 0;
+}
+
+static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
+        DBusMessage *m = NULL, *reply = NULL;
+        const char *interface = "";
+        int r;
+        DBusError error;
+        DBusMessageIter iter, sub, sub2, sub3;
+        UnitStatusInfo info;
+        ExecStatusInfo *p;
+
+        assert(bus);
+        assert(path);
+        assert(new_line);
+
+        zero(info);
+        dbus_error_init(&error);
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              path,
+                              "org.freedesktop.DBus.Properties",
+                              "GetAll"))) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &interface,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        if (*new_line)
+                printf("\n");
+
+        *new_line = true;
+
+        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                const char *name;
+
+                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&sub, &sub2);
+
+                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&sub2, &sub3);
+
+                if (show_properties)
+                        r = print_property(name, &sub3);
+                else
+                        r = status_property(name, &sub3, &info);
+
+                if (r < 0) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_next(&sub);
+        }
+
+        r = 0;
+
+        if (!show_properties)
+                print_status_info(&info);
+
+        if (!streq_ptr(info.active_state, "active") &&
+            !streq_ptr(info.active_state, "reloading") &&
+            streq(verb, "status"))
+                /* According to LSB: "program not running" */
+                r = 3;
+
+        while ((p = info.exec)) {
+                LIST_REMOVE(ExecStatusInfo, exec, info.exec, p);
+                exec_status_info_free(p);
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int show(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        int r, ret = 0;
+        DBusError error;
+        bool show_properties, new_line = false;
+        char **name;
+
+        assert(bus);
+        assert(args);
+
+        dbus_error_init(&error);
+
+        show_properties = !streq(args[0], "status");
+
+        if (show_properties)
+                pager_open_if_enabled();
+
+        if (show_properties && strv_length(args) <= 1) {
+                /* If not argument is specified inspect the manager
+                 * itself */
+
+                ret = show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line);
+                goto finish;
+        }
+
+        STRV_FOREACH(name, args+1) {
+                const char *path = NULL;
+                uint32_t id;
+
+                if (safe_atou32(*name, &id) < 0) {
+
+                        /* Interpret as unit name */
+
+                        if (!(m = dbus_message_new_method_call(
+                                              "org.freedesktop.systemd1",
+                                              "/org/freedesktop/systemd1",
+                                              "org.freedesktop.systemd1.Manager",
+                                              "LoadUnit"))) {
+                                log_error("Could not allocate message.");
+                                ret = -ENOMEM;
+                                goto finish;
+                        }
+
+                        if (!dbus_message_append_args(m,
+                                                      DBUS_TYPE_STRING, name,
+                                                      DBUS_TYPE_INVALID)) {
+                                log_error("Could not append arguments to message.");
+                                ret = -ENOMEM;
+                                goto finish;
+                        }
+
+                        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+                                if (!dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
+                                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                                        ret = -EIO;
+                                        goto finish;
+                                }
+
+                                dbus_error_free(&error);
+
+                                dbus_message_unref(m);
+                                if (!(m = dbus_message_new_method_call(
+                                                      "org.freedesktop.systemd1",
+                                                      "/org/freedesktop/systemd1",
+                                                      "org.freedesktop.systemd1.Manager",
+                                                      "GetUnit"))) {
+                                        log_error("Could not allocate message.");
+                                        ret = -ENOMEM;
+                                        goto finish;
+                                }
+
+                                if (!dbus_message_append_args(m,
+                                                              DBUS_TYPE_STRING, name,
+                                                              DBUS_TYPE_INVALID)) {
+                                        log_error("Could not append arguments to message.");
+                                        ret = -ENOMEM;
+                                        goto finish;
+                                }
+
+                                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+
+                                        if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT))
+                                                ret = 4; /* According to LSB: "program or service status is unknown" */
+                                        else
+                                                ret = -EIO;
+                                        goto finish;
+                                }
+                        }
+
+                } else if (show_properties) {
+
+                        /* Interpret as job id */
+
+                        if (!(m = dbus_message_new_method_call(
+                                              "org.freedesktop.systemd1",
+                                              "/org/freedesktop/systemd1",
+                                              "org.freedesktop.systemd1.Manager",
+                                              "GetJob"))) {
+                                log_error("Could not allocate message.");
+                                ret = -ENOMEM;
+                                goto finish;
+                        }
+
+                        if (!dbus_message_append_args(m,
+                                                      DBUS_TYPE_UINT32, &id,
+                                                      DBUS_TYPE_INVALID)) {
+                                log_error("Could not append arguments to message.");
+                                ret = -ENOMEM;
+                                goto finish;
+                        }
+
+                        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                                ret = -EIO;
+                                goto finish;
+                        }
+                } else {
+
+                        /* Interpret as PID */
+
+                        if (!(m = dbus_message_new_method_call(
+                                              "org.freedesktop.systemd1",
+                                              "/org/freedesktop/systemd1",
+                                              "org.freedesktop.systemd1.Manager",
+                                              "GetUnitByPID"))) {
+                                log_error("Could not allocate message.");
+                                ret = -ENOMEM;
+                                goto finish;
+                        }
+
+                        if (!dbus_message_append_args(m,
+                                                      DBUS_TYPE_UINT32, &id,
+                                                      DBUS_TYPE_INVALID)) {
+                                log_error("Could not append arguments to message.");
+                                ret = -ENOMEM;
+                                goto finish;
+                        }
+
+                        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                                ret = -EIO;
+                                goto finish;
+                        }
+                }
+
+                if (!dbus_message_get_args(reply, &error,
+                                           DBUS_TYPE_OBJECT_PATH, &path,
+                                           DBUS_TYPE_INVALID)) {
+                        log_error("Failed to parse reply: %s", bus_error_message(&error));
+                        ret = -EIO;
+                        goto finish;
+                }
+
+                if ((r = show_one(args[0], bus, path, show_properties, &new_line)) != 0)
+                        ret = r;
+
+                dbus_message_unref(m);
+                dbus_message_unref(reply);
+                m = reply = NULL;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return ret;
+}
+
+static int dump(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        const char *text;
+
+        dbus_error_init(&error);
+
+        pager_open_if_enabled();
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              "Dump"))) {
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_get_args(reply, &error,
+                                   DBUS_TYPE_STRING, &text,
+                                   DBUS_TYPE_INVALID)) {
+                log_error("Failed to parse reply: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        fputs(text, stdout);
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int snapshot(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        const char *name = "", *path, *id;
+        dbus_bool_t cleanup = FALSE;
+        DBusMessageIter iter, sub;
+        const char
+                *interface = "org.freedesktop.systemd1.Unit",
+                *property = "Id";
+
+        dbus_error_init(&error);
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              "CreateSnapshot"))) {
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        if (strv_length(args) > 1)
+                name = args[1];
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &name,
+                                      DBUS_TYPE_BOOLEAN, &cleanup,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_get_args(reply, &error,
+                                   DBUS_TYPE_OBJECT_PATH, &path,
+                                   DBUS_TYPE_INVALID)) {
+                log_error("Failed to parse reply: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_unref(m);
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              path,
+                              "org.freedesktop.DBus.Properties",
+                              "Get"))) {
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &interface,
+                                      DBUS_TYPE_STRING, &property,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        dbus_message_unref(reply);
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_get_basic(&sub, &id);
+
+        if (!arg_quiet)
+                puts(id);
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int delete_snapshot(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        int r;
+        DBusError error;
+        char **name;
+
+        assert(bus);
+        assert(args);
+
+        dbus_error_init(&error);
+
+        STRV_FOREACH(name, args+1) {
+                const char *path = NULL;
+
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      "/org/freedesktop/systemd1",
+                                      "org.freedesktop.systemd1.Manager",
+                                      "GetUnit"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, name,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (!dbus_message_get_args(reply, &error,
+                                           DBUS_TYPE_OBJECT_PATH, &path,
+                                           DBUS_TYPE_INVALID)) {
+                        log_error("Failed to parse reply: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_unref(m);
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      path,
+                                      "org.freedesktop.systemd1.Snapshot",
+                                      "Remove"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                dbus_message_unref(reply);
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_unref(m);
+                dbus_message_unref(reply);
+                m = reply = NULL;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int daemon_reload(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        const char *method;
+
+        dbus_error_init(&error);
+
+        if (arg_action == ACTION_RELOAD)
+                method = "Reload";
+        else if (arg_action == ACTION_REEXEC)
+                method = "Reexecute";
+        else {
+                assert(arg_action == ACTION_SYSTEMCTL);
+
+                method =
+                        streq(args[0], "clear-jobs")    ||
+                        streq(args[0], "cancel")        ? "ClearJobs" :
+                        streq(args[0], "daemon-reexec") ? "Reexecute" :
+                        streq(args[0], "reset-failed")  ? "ResetFailed" :
+                        streq(args[0], "halt")          ? "Halt" :
+                        streq(args[0], "poweroff")      ? "PowerOff" :
+                        streq(args[0], "reboot")        ? "Reboot" :
+                        streq(args[0], "kexec")         ? "KExec" :
+                        streq(args[0], "exit")          ? "Exit" :
+                                    /* "daemon-reload" */ "Reload";
+        }
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              method))) {
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+                if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
+                        /* There's always a fallback possible for
+                         * legacy actions. */
+                        r = -EADDRNOTAVAIL;
+                        goto finish;
+                }
+
+                if (streq(method, "Reexecute") && dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY)) {
+                        /* On reexecution, we expect a disconnect, not
+                         * a reply */
+                        r = 0;
+                        goto finish;
+                }
+
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int reset_failed(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL;
+        int r;
+        DBusError error;
+        char **name;
+
+        assert(bus);
+        dbus_error_init(&error);
+
+        if (strv_length(args) <= 1)
+                return daemon_reload(bus, args);
+
+        STRV_FOREACH(name, args+1) {
+                DBusMessage *reply;
+
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      "/org/freedesktop/systemd1",
+                                      "org.freedesktop.systemd1.Manager",
+                                      "ResetFailedUnit"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, name,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_unref(m);
+                dbus_message_unref(reply);
+                m = reply = NULL;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int show_enviroment(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        DBusMessageIter iter, sub, sub2;
+        int r;
+        const char
+                *interface = "org.freedesktop.systemd1.Manager",
+                *property = "Environment";
+
+        dbus_error_init(&error);
+
+        pager_open_if_enabled();
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.DBus.Properties",
+                              "Get"))) {
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &interface,
+                                      DBUS_TYPE_STRING, &property,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&sub, &sub2);
+
+        while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
+                const char *text;
+
+                if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_get_basic(&sub2, &text);
+                printf("%s\n", text);
+
+                dbus_message_iter_next(&sub2);
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int set_environment(DBusConnection *bus, char **args) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        const char *method;
+        DBusMessageIter iter, sub;
+        char **name;
+
+        dbus_error_init(&error);
+
+        method = streq(args[0], "set-environment")
+                ? "SetEnvironment"
+                : "UnsetEnvironment";
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              method))) {
+
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        dbus_message_iter_init_append(m, &iter);
+
+        if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        STRV_FOREACH(name, args+1)
+                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+        if (!dbus_message_iter_close_container(&iter, &sub)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int enable_sysv_units(char **args) {
+        int r = 0;
+
+#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX) || defined(TARGET_MAGEIA))
+        const char *verb = args[0];
+        unsigned f = 1, t = 1;
+        LookupPaths paths;
+
+        if (arg_scope != UNIT_FILE_SYSTEM)
+                return 0;
+
+        if (!streq(verb, "enable") &&
+            !streq(verb, "disable") &&
+            !streq(verb, "is-enabled"))
+                return 0;
+
+        /* Processes all SysV units, and reshuffles the array so that
+         * afterwards only the native units remain */
+
+        zero(paths);
+        r = lookup_paths_init(&paths, MANAGER_SYSTEM, false);
+        if (r < 0)
+                return r;
+
+        r = 0;
+
+        for (f = 1; args[f]; f++) {
+                const char *name;
+                char *p;
+                bool found_native = false, found_sysv;
+                unsigned c = 1;
+                const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
+                char **k, *l, *q = NULL;
+                int j;
+                pid_t pid;
+                siginfo_t status;
+
+                name = args[f];
+
+                if (!endswith(name, ".service"))
+                        continue;
+
+                if (path_is_absolute(name))
+                        continue;
+
+                STRV_FOREACH(k, paths.unit_path) {
+                        p = NULL;
+
+                        if (!isempty(arg_root))
+                                asprintf(&p, "%s/%s/%s", arg_root, *k, name);
+                        else
+                                asprintf(&p, "%s/%s", *k, name);
+
+                        if (!p) {
+                                log_error("No memory");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        found_native = access(p, F_OK) >= 0;
+                        free(p);
+
+                        if (found_native)
+                                break;
+                }
+
+                if (found_native)
+                        continue;
+
+                p = NULL;
+                if (!isempty(arg_root))
+                        asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
+                else
+                        asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name);
+                if (!p) {
+                        log_error("No memory");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                p[strlen(p) - sizeof(".service") + 1] = 0;
+                found_sysv = access(p, F_OK) >= 0;
+
+                if (!found_sysv) {
+                        free(p);
+                        continue;
+                }
+
+                /* Mark this entry, so that we don't try enabling it as native unit */
+                args[f] = (char*) "";
+
+                log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
+
+                if (!isempty(arg_root))
+                        argv[c++] = q = strappend("--root=", arg_root);
+
+                argv[c++] = file_name_from_path(p);
+                argv[c++] =
+                        streq(verb, "enable") ? "on" :
+                        streq(verb, "disable") ? "off" : "--level=5";
+                argv[c] = NULL;
+
+                l = strv_join((char**)argv, " ");
+                if (!l) {
+                        log_error("No memory.");
+                        free(q);
+                        free(p);
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                log_info("Executing %s", l);
+                free(l);
+
+                pid = fork();
+                if (pid < 0) {
+                        log_error("Failed to fork: %m");
+                        free(p);
+                        free(q);
+                        r = -errno;
+                        goto finish;
+                } else if (pid == 0) {
+                        /* Child */
+
+                        execv(argv[0], (char**) argv);
+                        _exit(EXIT_FAILURE);
+                }
+
+                free(p);
+                free(q);
+
+                j = wait_for_terminate(pid, &status);
+                if (j < 0) {
+                        log_error("Failed to wait for child: %s", strerror(-r));
+                        r = j;
+                        goto finish;
+                }
+
+                if (status.si_code == CLD_EXITED) {
+                        if (streq(verb, "is-enabled")) {
+                                if (status.si_status == 0) {
+                                        if (!arg_quiet)
+                                                puts("enabled");
+                                        r = 1;
+                                } else {
+                                        if (!arg_quiet)
+                                                puts("disabled");
+                                }
+
+                        } else if (status.si_status != 0) {
+                                r = -EINVAL;
+                                goto finish;
+                        }
+                } else {
+                        r = -EPROTO;
+                        goto finish;
+                }
+        }
+
+finish:
+        lookup_paths_free(&paths);
+
+        /* Drop all SysV units */
+        for (f = 1, t = 1; args[f]; f++) {
+
+                if (isempty(args[f]))
+                        continue;
+
+                args[t++] = args[f];
+        }
+
+        args[t] = NULL;
+
+#endif
+        return r;
+}
+
+static int enable_unit(DBusConnection *bus, char **args) {
+        const char *verb = args[0];
+        UnitFileChange *changes = NULL;
+        unsigned n_changes = 0, i;
+        int carries_install_info = -1;
+        DBusMessage *m = NULL, *reply = NULL;
+        int r;
+        DBusError error;
+
+        r = enable_sysv_units(args);
+        if (r < 0)
+                return r;
+
+        if (!args[1])
+                return 0;
+
+        dbus_error_init(&error);
+
+        if (!bus || avoid_bus()) {
+                if (streq(verb, "enable")) {
+                        r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                        carries_install_info = r;
+                } else if (streq(verb, "disable"))
+                        r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
+                else if (streq(verb, "reenable")) {
+                        r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                        carries_install_info = r;
+                } else if (streq(verb, "link"))
+                        r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                else if (streq(verb, "preset")) {
+                        r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                        carries_install_info = r;
+                } else if (streq(verb, "mask"))
+                        r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
+                else if (streq(verb, "unmask"))
+                        r = unit_file_unmask(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
+                else
+                        assert_not_reached("Unknown verb");
+
+                if (r < 0) {
+                        log_error("Operation failed: %s", strerror(-r));
+                        goto finish;
+                }
+
+                if (!arg_quiet) {
+                        for (i = 0; i < n_changes; i++) {
+                                if (changes[i].type == UNIT_FILE_SYMLINK)
+                                        log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
+                                else
+                                        log_info("rm '%s'", changes[i].path);
+                        }
+                }
+
+        } else {
+                const char *method;
+                bool send_force = true, expect_carries_install_info = false;
+                dbus_bool_t a, b;
+                DBusMessageIter iter, sub, sub2;
+
+                if (streq(verb, "enable")) {
+                        method = "EnableUnitFiles";
+                        expect_carries_install_info = true;
+                } else if (streq(verb, "disable")) {
+                        method = "DisableUnitFiles";
+                        send_force = false;
+                } else if (streq(verb, "reenable")) {
+                        method = "ReenableUnitFiles";
+                        expect_carries_install_info = true;
+                } else if (streq(verb, "link"))
+                        method = "LinkUnitFiles";
+                else if (streq(verb, "preset")) {
+                        method = "PresetUnitFiles";
+                        expect_carries_install_info = true;
+                } else if (streq(verb, "mask"))
+                        method = "MaskUnitFiles";
+                else if (streq(verb, "unmask")) {
+                        method = "UnmaskUnitFiles";
+                        send_force = false;
+                } else
+                        assert_not_reached("Unknown verb");
+
+                m = dbus_message_new_method_call(
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                method);
+                if (!m) {
+                        log_error("Out of memory");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                dbus_message_iter_init_append(m, &iter);
+
+                r = bus_append_strv_iter(&iter, args+1);
+                if (r < 0) {
+                        log_error("Failed to append unit files.");
+                        goto finish;
+                }
+
+                a = arg_runtime;
+                if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &a)) {
+                        log_error("Failed to append runtime boolean.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (send_force) {
+                        b = arg_force;
+
+                        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) {
+                                log_error("Failed to append force boolean.");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+                }
+
+                reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+                if (!reply) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (!dbus_message_iter_init(reply, &iter)) {
+                        log_error("Failed to initialize iterator.");
+                        goto finish;
+                }
+
+                if (expect_carries_install_info) {
+                        r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &b, true);
+                        if (r < 0) {
+                                log_error("Failed to parse reply.");
+                                goto finish;
+                        }
+
+                        carries_install_info = b;
+                }
+
+                if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+                    dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&iter, &sub);
+                while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                        const char *type, *path, *source;
+
+                        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+                                log_error("Failed to parse reply.");
+                                r = -EIO;
+                                goto finish;
+                        }
+
+                        dbus_message_iter_recurse(&sub, &sub2);
+
+                        if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
+                            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
+                            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &source, false) < 0) {
+                                log_error("Failed to parse reply.");
+                                r = -EIO;
+                                goto finish;
+                        }
+
+                        if (!arg_quiet) {
+                                if (streq(type, "symlink"))
+                                        log_info("ln -s '%s' '%s'", source, path);
+                                else
+                                        log_info("rm '%s'", path);
+                        }
+
+                        dbus_message_iter_next(&sub);
+                }
+
+                /* Try to reload if enabeld */
+                if (!arg_no_reload)
+                        r = daemon_reload(bus, args);
+        }
+
+        if (carries_install_info == 0)
+                log_warning("Warning: unit files do not carry install information. No operation executed.");
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        unit_file_changes_free(changes, n_changes);
+
+        dbus_error_free(&error);
+        return r;
+}
+
+static int unit_is_enabled(DBusConnection *bus, char **args) {
+        DBusError error;
+        int r;
+        DBusMessage *m = NULL, *reply = NULL;
+        bool enabled;
+        char **name;
+
+        dbus_error_init(&error);
+
+        r = enable_sysv_units(args);
+        if (r < 0)
+                return r;
+
+        enabled = r > 0;
+
+        if (!bus || avoid_bus()) {
+
+                STRV_FOREACH(name, args+1) {
+                        UnitFileState state;
+
+                        state = unit_file_get_state(arg_scope, arg_root, *name);
+                        if (state < 0) {
+                                r = state;
+                                goto finish;
+                        }
+
+                        if (state == UNIT_FILE_ENABLED ||
+                            state == UNIT_FILE_ENABLED_RUNTIME ||
+                            state == UNIT_FILE_STATIC)
+                                enabled = true;
+
+                        if (!arg_quiet)
+                                puts(unit_file_state_to_string(state));
+                }
+
+        } else {
+                STRV_FOREACH(name, args+1) {
+                        const char *s;
+
+                        m = dbus_message_new_method_call(
+                                        "org.freedesktop.systemd1",
+                                        "/org/freedesktop/systemd1",
+                                        "org.freedesktop.systemd1.Manager",
+                                        "GetUnitFileState");
+                        if (!m) {
+                                log_error("Out of memory");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        if (!dbus_message_append_args(m,
+                                                      DBUS_TYPE_STRING, name,
+                                                      DBUS_TYPE_INVALID)) {
+                                log_error("Could not append arguments to message.");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
+                        if (!reply) {
+                                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                                r = -EIO;
+                                goto finish;
+                        }
+
+                        if (!dbus_message_get_args(reply, &error,
+                                                   DBUS_TYPE_STRING, &s,
+                                                   DBUS_TYPE_INVALID)) {
+                                log_error("Failed to parse reply: %s", bus_error_message(&error));
+                                r = -EIO;
+                                goto finish;
+                        }
+
+                        dbus_message_unref(m);
+                        dbus_message_unref(reply);
+                        m = reply = NULL;
+
+                        if (streq(s, "enabled") ||
+                            streq(s, "enabled-runtime") ||
+                            streq(s, "static"))
+                                enabled = true;
+
+                        if (!arg_quiet)
+                                puts(s);
+                }
+        }
+
+        r = enabled ? 0 : 1;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+        return r;
+}
+
+static int systemctl_help(void) {
+
+        pager_open_if_enabled();
+
+        printf("%s [OPTIONS...] {COMMAND} ...\n\n"
+               "Query or send control commands to the systemd manager.\n\n"
+               "  -h --help           Show this help\n"
+               "     --version        Show package version\n"
+               "  -t --type=TYPE      List only units of a particular type\n"
+               "  -p --property=NAME  Show only properties by this name\n"
+               "  -a --all            Show all units/properties, including dead/empty ones\n"
+               "     --failed         Show only failed units\n"
+               "     --full           Don't ellipsize unit names on output\n"
+               "     --fail           When queueing a new job, fail if conflicting jobs are\n"
+               "                      pending\n"
+               "     --ignore-dependencies\n"
+               "                      When queueing a new job, ignore all its dependencies\n"
+               "     --kill-who=WHO   Who to send signal to\n"
+               "  -s --signal=SIGNAL  Which signal to send\n"
+               "  -H --host=[USER@]HOST\n"
+               "                      Show information for remote host\n"
+               "  -P --privileged     Acquire privileges before execution\n"
+               "  -q --quiet          Suppress output\n"
+               "     --no-block       Do not wait until operation finished\n"
+               "     --no-wall        Don't send wall message before halt/power-off/reboot\n"
+               "     --no-reload      When enabling/disabling unit files, don't reload daemon\n"
+               "                      configuration\n"
+               "     --no-legend      Do not print a legend (column headers and hints)\n"
+               "     --no-pager       Do not pipe output into a pager\n"
+               "     --no-ask-password\n"
+               "                      Do not ask for system passwords\n"
+               "     --order          When generating graph for dot, show only order\n"
+               "     --require        When generating graph for dot, show only requirement\n"
+               "     --system         Connect to system manager\n"
+               "     --user           Connect to user service manager\n"
+               "     --global         Enable/disable unit files globally\n"
+               "  -f --force          When enabling unit files, override existing symlinks\n"
+               "                      When shutting down, execute action immediately\n"
+               "     --root=PATH      Enable unit files in the specified root directory\n"
+               "     --runtime        Enable unit files only temporarily until next reboot\n"
+               "  -n --lines=INTEGER  Journal entries to show\n"
+               "     --follow         Follow journal\n"
+               "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
+               "                      verbose, export, json, cat)\n\n"
+               "Unit Commands:\n"
+               "  list-units                      List loaded units\n"
+               "  start [NAME...]                 Start (activate) one or more units\n"
+               "  stop [NAME...]                  Stop (deactivate) one or more units\n"
+               "  reload [NAME...]                Reload one or more units\n"
+               "  restart [NAME...]               Start or restart one or more units\n"
+               "  try-restart [NAME...]           Restart one or more units if active\n"
+               "  reload-or-restart [NAME...]     Reload one or more units is possible,\n"
+               "                                  otherwise start or restart\n"
+               "  reload-or-try-restart [NAME...] Reload one or more units is possible,\n"
+               "                                  otherwise restart if active\n"
+               "  isolate [NAME]                  Start one unit and stop all others\n"
+               "  kill [NAME...]                  Send signal to processes of a unit\n"
+               "  is-active [NAME...]             Check whether units are active\n"
+               "  status [NAME...|PID...]         Show runtime status of one or more units\n"
+               "  show [NAME...|JOB...]           Show properties of one or more\n"
+               "                                  units/jobs or the manager\n"
+               "  reset-failed [NAME...]          Reset failed state for all, one, or more\n"
+               "                                  units\n"
+               "  load [NAME...]                  Load one or more units\n\n"
+               "Unit File Commands:\n"
+               "  list-unit-files                 List installed unit files\n"
+               "  enable [NAME...]                Enable one or more unit files\n"
+               "  disable [NAME...]               Disable one or more unit files\n"
+               "  reenable [NAME...]              Reenable one or more unit files\n"
+               "  preset [NAME...]                Enable/disable one or more unit files\n"
+               "                                  based on preset configuration\n"
+               "  mask [NAME...]                  Mask one or more units\n"
+               "  unmask [NAME...]                Unmask one or more units\n"
+               "  link [PATH...]                  Link one or more units files into\n"
+               "                                  the search path\n"
+               "  is-enabled [NAME...]            Check whether unit files are enabled\n\n"
+               "Job Commands:\n"
+               "  list-jobs                       List jobs\n"
+               "  cancel [JOB...]                 Cancel all, one, or more jobs\n\n"
+               "Status Commands:\n"
+               "  dump                            Dump server status\n"
+               "  dot                             Dump dependency graph for dot(1)\n\n"
+               "Snapshot Commands:\n"
+               "  snapshot [NAME]                 Create a snapshot\n"
+               "  delete [NAME...]                Remove one or more snapshots\n\n"
+               "Environment Commands:\n"
+               "  show-environment                Dump environment\n"
+               "  set-environment [NAME=VALUE...] Set one or more environment variables\n"
+               "  unset-environment [NAME...]     Unset one or more environment variables\n\n"
+               "Manager Lifecycle Commands:\n"
+               "  daemon-reload                   Reload systemd manager configuration\n"
+               "  daemon-reexec                   Reexecute systemd manager\n\n"
+               "System Commands:\n"
+               "  default                         Enter system default mode\n"
+               "  rescue                          Enter system rescue mode\n"
+               "  emergency                       Enter system emergency mode\n"
+               "  halt                            Shut down and halt the system\n"
+               "  poweroff                        Shut down and power-off the system\n"
+               "  reboot                          Shut down and reboot the system\n"
+               "  kexec                           Shut down and reboot the system with kexec\n"
+               "  exit                            Ask for user instance termination\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int halt_help(void) {
+
+        printf("%s [OPTIONS...]\n\n"
+               "%s the system.\n\n"
+               "     --help      Show this help\n"
+               "     --halt      Halt the machine\n"
+               "  -p --poweroff  Switch off the machine\n"
+               "     --reboot    Reboot the machine\n"
+               "  -f --force     Force immediate halt/power-off/reboot\n"
+               "  -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
+               "  -d --no-wtmp   Don't write wtmp record\n"
+               "  -n --no-sync   Don't sync before halt/power-off/reboot\n"
+               "     --no-wall   Don't send wall message before halt/power-off/reboot\n",
+               program_invocation_short_name,
+               arg_action == ACTION_REBOOT   ? "Reboot" :
+               arg_action == ACTION_POWEROFF ? "Power off" :
+                                               "Halt");
+
+        return 0;
+}
+
+static int shutdown_help(void) {
+
+        printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
+               "Shut down the system.\n\n"
+               "     --help      Show this help\n"
+               "  -H --halt      Halt the machine\n"
+               "  -P --poweroff  Power-off the machine\n"
+               "  -r --reboot    Reboot the machine\n"
+               "  -h             Equivalent to --poweroff, overriden by --halt\n"
+               "  -k             Don't halt/power-off/reboot, just send warnings\n"
+               "     --no-wall   Don't send wall message before halt/power-off/reboot\n"
+               "  -c             Cancel a pending shutdown\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int telinit_help(void) {
+
+        printf("%s [OPTIONS...] {COMMAND}\n\n"
+               "Send control commands to the init daemon.\n\n"
+               "     --help      Show this help\n"
+               "     --no-wall   Don't send wall message before halt/power-off/reboot\n\n"
+               "Commands:\n"
+               "  0              Power-off the machine\n"
+               "  6              Reboot the machine\n"
+               "  2, 3, 4, 5     Start runlevelX.target unit\n"
+               "  1, s, S        Enter rescue mode\n"
+               "  q, Q           Reload init daemon configuration\n"
+               "  u, U           Reexecute init daemon\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int runlevel_help(void) {
+
+        printf("%s [OPTIONS...]\n\n"
+               "Prints the previous and current runlevel of the init system.\n\n"
+               "     --help      Show this help\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int systemctl_parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_FAIL = 0x100,
+                ARG_IGNORE_DEPENDENCIES,
+                ARG_VERSION,
+                ARG_USER,
+                ARG_SYSTEM,
+                ARG_GLOBAL,
+                ARG_NO_BLOCK,
+                ARG_NO_LEGEND,
+                ARG_NO_PAGER,
+                ARG_NO_WALL,
+                ARG_ORDER,
+                ARG_REQUIRE,
+                ARG_ROOT,
+                ARG_FULL,
+                ARG_NO_RELOAD,
+                ARG_KILL_MODE,
+                ARG_KILL_WHO,
+                ARG_NO_ASK_PASSWORD,
+                ARG_FAILED,
+                ARG_RUNTIME,
+                ARG_FOLLOW,
+                ARG_FORCE
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { "version",   no_argument,       NULL, ARG_VERSION   },
+                { "type",      required_argument, NULL, 't'           },
+                { "property",  required_argument, NULL, 'p'           },
+                { "all",       no_argument,       NULL, 'a'           },
+                { "failed",    no_argument,       NULL, ARG_FAILED    },
+                { "full",      no_argument,       NULL, ARG_FULL      },
+                { "fail",      no_argument,       NULL, ARG_FAIL      },
+                { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
+                { "user",      no_argument,       NULL, ARG_USER      },
+                { "system",    no_argument,       NULL, ARG_SYSTEM    },
+                { "global",    no_argument,       NULL, ARG_GLOBAL    },
+                { "no-block",  no_argument,       NULL, ARG_NO_BLOCK  },
+                { "no-legend", no_argument,       NULL, ARG_NO_LEGEND },
+                { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
+                { "no-wall",   no_argument,       NULL, ARG_NO_WALL   },
+                { "quiet",     no_argument,       NULL, 'q'           },
+                { "order",     no_argument,       NULL, ARG_ORDER     },
+                { "require",   no_argument,       NULL, ARG_REQUIRE   },
+                { "root",      required_argument, NULL, ARG_ROOT      },
+                { "force",     no_argument,       NULL, ARG_FORCE     },
+                { "no-reload", no_argument,       NULL, ARG_NO_RELOAD },
+                { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
+                { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
+                { "signal",    required_argument, NULL, 's'           },
+                { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
+                { "host",      required_argument, NULL, 'H'           },
+                { "privileged",no_argument,       NULL, 'P'           },
+                { "runtime",   no_argument,       NULL, ARG_RUNTIME   },
+                { "lines",     required_argument, NULL, 'n'           },
+                { "follow",    no_argument,       NULL, ARG_FOLLOW    },
+                { "output",    required_argument, NULL, 'o'           },
+                { NULL,        0,                 NULL, 0             }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        systemctl_help();
+                        return 0;
+
+                case ARG_VERSION:
+                        puts(PACKAGE_STRING);
+                        puts(DISTRIBUTION);
+                        puts(SYSTEMD_FEATURES);
+                        return 0;
+
+                case 't':
+                        arg_type = optarg;
+                        break;
+
+                case 'p': {
+                        char **l;
+
+                        if (!(l = strv_append(arg_property, optarg)))
+                                return -ENOMEM;
+
+                        strv_free(arg_property);
+                        arg_property = l;
+
+                        /* If the user asked for a particular
+                         * property, show it to him, even if it is
+                         * empty. */
+                        arg_all = true;
+                        break;
+                }
+
+                case 'a':
+                        arg_all = true;
+                        break;
+
+                case ARG_FAIL:
+                        arg_job_mode = "fail";
+                        break;
+
+                case ARG_IGNORE_DEPENDENCIES:
+                        arg_job_mode = "ignore-dependencies";
+                        break;
+
+                case ARG_USER:
+                        arg_scope = UNIT_FILE_USER;
+                        break;
+
+                case ARG_SYSTEM:
+                        arg_scope = UNIT_FILE_SYSTEM;
+                        break;
+
+                case ARG_GLOBAL:
+                        arg_scope = UNIT_FILE_GLOBAL;
+                        break;
+
+                case ARG_NO_BLOCK:
+                        arg_no_block = true;
+                        break;
+
+                case ARG_NO_LEGEND:
+                        arg_no_legend = true;
+                        break;
+
+                case ARG_NO_PAGER:
+                        arg_no_pager = true;
+                        break;
+
+                case ARG_NO_WALL:
+                        arg_no_wall = true;
+                        break;
+
+                case ARG_ORDER:
+                        arg_dot = DOT_ORDER;
+                        break;
+
+                case ARG_REQUIRE:
+                        arg_dot = DOT_REQUIRE;
+                        break;
+
+                case ARG_ROOT:
+                        arg_root = optarg;
+                        break;
+
+                case ARG_FULL:
+                        arg_full = true;
+                        break;
+
+                case ARG_FAILED:
+                        arg_failed = true;
+                        break;
+
+                case 'q':
+                        arg_quiet = true;
+                        break;
+
+                case ARG_FORCE:
+                        arg_force ++;
+                        break;
+
+                case ARG_FOLLOW:
+                        arg_follow = true;
+                        break;
+
+                case 'f':
+                        /* -f is short for both --follow and --force! */
+                        arg_force ++;
+                        arg_follow = true;
+                        break;
+
+                case ARG_NO_RELOAD:
+                        arg_no_reload = true;
+                        break;
+
+                case ARG_KILL_WHO:
+                        arg_kill_who = optarg;
+                        break;
+
+                case ARG_KILL_MODE:
+                        arg_kill_mode = optarg;
+                        break;
+
+                case 's':
+                        if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
+                                log_error("Failed to parse signal string %s.", optarg);
+                                return -EINVAL;
+                        }
+                        break;
+
+                case ARG_NO_ASK_PASSWORD:
+                        arg_ask_password = false;
+                        break;
+
+                case 'P':
+                        arg_transport = TRANSPORT_POLKIT;
+                        break;
+
+                case 'H':
+                        arg_transport = TRANSPORT_SSH;
+                        arg_host = optarg;
+                        break;
+
+                case ARG_RUNTIME:
+                        arg_runtime = true;
+                        break;
+
+                case 'n':
+                        if (safe_atou(optarg, &arg_lines) < 0) {
+                                log_error("Failed to parse lines '%s'", optarg);
+                                return -EINVAL;
+                        }
+                        break;
+
+                case 'o':
+                        arg_output = output_mode_from_string(optarg);
+                        if (arg_output < 0) {
+                                log_error("Unknown output '%s'.", optarg);
+                                return -EINVAL;
+                        }
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (arg_transport != TRANSPORT_NORMAL && arg_scope != UNIT_FILE_SYSTEM) {
+                log_error("Cannot access user instance remotely.");
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+static int halt_parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_HELP = 0x100,
+                ARG_HALT,
+                ARG_REBOOT,
+                ARG_NO_WALL
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, ARG_HELP    },
+                { "halt",      no_argument,       NULL, ARG_HALT    },
+                { "poweroff",  no_argument,       NULL, 'p'         },
+                { "reboot",    no_argument,       NULL, ARG_REBOOT  },
+                { "force",     no_argument,       NULL, 'f'         },
+                { "wtmp-only", no_argument,       NULL, 'w'         },
+                { "no-wtmp",   no_argument,       NULL, 'd'         },
+                { "no-sync",   no_argument,       NULL, 'n'         },
+                { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
+                { NULL,        0,                 NULL, 0           }
+        };
+
+        int c, runlevel;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        if (utmp_get_runlevel(&runlevel, NULL) >= 0)
+                if (runlevel == '0' || runlevel == '6')
+                        arg_immediate = true;
+
+        while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
+                switch (c) {
+
+                case ARG_HELP:
+                        halt_help();
+                        return 0;
+
+                case ARG_HALT:
+                        arg_action = ACTION_HALT;
+                        break;
+
+                case 'p':
+                        if (arg_action != ACTION_REBOOT)
+                                arg_action = ACTION_POWEROFF;
+                        break;
+
+                case ARG_REBOOT:
+                        arg_action = ACTION_REBOOT;
+                        break;
+
+                case 'f':
+                        arg_immediate = true;
+                        break;
+
+                case 'w':
+                        arg_dry = true;
+                        break;
+
+                case 'd':
+                        arg_no_wtmp = true;
+                        break;
+
+                case 'n':
+                        arg_no_sync = true;
+                        break;
+
+                case ARG_NO_WALL:
+                        arg_no_wall = true;
+                        break;
+
+                case 'i':
+                case 'h':
+                        /* Compatibility nops */
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind < argc) {
+                log_error("Too many arguments.");
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+static int parse_time_spec(const char *t, usec_t *_u) {
+        assert(t);
+        assert(_u);
+
+        if (streq(t, "now"))
+                *_u = 0;
+        else if (!strchr(t, ':')) {
+                uint64_t u;
+
+                if (safe_atou64(t, &u) < 0)
+                        return -EINVAL;
+
+                *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
+        } else {
+                char *e = NULL;
+                long hour, minute;
+                struct tm tm;
+                time_t s;
+                usec_t n;
+
+                errno = 0;
+                hour = strtol(t, &e, 10);
+                if (errno != 0 || *e != ':' || hour < 0 || hour > 23)
+                        return -EINVAL;
+
+                minute = strtol(e+1, &e, 10);
+                if (errno != 0 || *e != 0 || minute < 0 || minute > 59)
+                        return -EINVAL;
+
+                n = now(CLOCK_REALTIME);
+                s = (time_t) (n / USEC_PER_SEC);
+
+                zero(tm);
+                assert_se(localtime_r(&s, &tm));
+
+                tm.tm_hour = (int) hour;
+                tm.tm_min = (int) minute;
+                tm.tm_sec = 0;
+
+                assert_se(s = mktime(&tm));
+
+                *_u = (usec_t) s * USEC_PER_SEC;
+
+                while (*_u <= n)
+                        *_u += USEC_PER_DAY;
+        }
+
+        return 0;
+}
+
+static int shutdown_parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_HELP = 0x100,
+                ARG_NO_WALL
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, ARG_HELP    },
+                { "halt",      no_argument,       NULL, 'H'         },
+                { "poweroff",  no_argument,       NULL, 'P'         },
+                { "reboot",    no_argument,       NULL, 'r'         },
+                { "kexec",     no_argument,       NULL, 'K'         }, /* not documented extension */
+                { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
+                { NULL,        0,                 NULL, 0           }
+        };
+
+        int c, r;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) {
+                switch (c) {
+
+                case ARG_HELP:
+                        shutdown_help();
+                        return 0;
+
+                case 'H':
+                        arg_action = ACTION_HALT;
+                        break;
+
+                case 'P':
+                        arg_action = ACTION_POWEROFF;
+                        break;
+
+                case 'r':
+                        if (kexec_loaded())
+                                arg_action = ACTION_KEXEC;
+                        else
+                                arg_action = ACTION_REBOOT;
+                        break;
+
+                case 'K':
+                        arg_action = ACTION_KEXEC;
+                        break;
+
+                case 'h':
+                        if (arg_action != ACTION_HALT)
+                                arg_action = ACTION_POWEROFF;
+                        break;
+
+                case 'k':
+                        arg_dry = true;
+                        break;
+
+                case ARG_NO_WALL:
+                        arg_no_wall = true;
+                        break;
+
+                case 't':
+                case 'a':
+                        /* Compatibility nops */
+                        break;
+
+                case 'c':
+                        arg_action = ACTION_CANCEL_SHUTDOWN;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (argc > optind) {
+                r = parse_time_spec(argv[optind], &arg_when);
+                if (r < 0) {
+                        log_error("Failed to parse time specification: %s", argv[optind]);
+                        return r;
+                }
+        } else
+                arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE;
+
+        /* We skip the time argument */
+        if (argc > optind + 1)
+                arg_wall = argv + optind + 1;
+
+        optind = argc;
+
+        return 1;
+}
+
+static int telinit_parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_HELP = 0x100,
+                ARG_NO_WALL
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, ARG_HELP    },
+                { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
+                { NULL,        0,                 NULL, 0           }
+        };
+
+        static const struct {
+                char from;
+                enum action to;
+        } table[] = {
+                { '0', ACTION_POWEROFF },
+                { '6', ACTION_REBOOT },
+                { '1', ACTION_RESCUE },
+                { '2', ACTION_RUNLEVEL2 },
+                { '3', ACTION_RUNLEVEL3 },
+                { '4', ACTION_RUNLEVEL4 },
+                { '5', ACTION_RUNLEVEL5 },
+                { 's', ACTION_RESCUE },
+                { 'S', ACTION_RESCUE },
+                { 'q', ACTION_RELOAD },
+                { 'Q', ACTION_RELOAD },
+                { 'u', ACTION_REEXEC },
+                { 'U', ACTION_REEXEC }
+        };
+
+        unsigned i;
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
+                switch (c) {
+
+                case ARG_HELP:
+                        telinit_help();
+                        return 0;
+
+                case ARG_NO_WALL:
+                        arg_no_wall = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind >= argc) {
+                telinit_help();
+                return -EINVAL;
+        }
+
+        if (optind + 1 < argc) {
+                log_error("Too many arguments.");
+                return -EINVAL;
+        }
+
+        if (strlen(argv[optind]) != 1) {
+                log_error("Expected single character argument.");
+                return -EINVAL;
+        }
+
+        for (i = 0; i < ELEMENTSOF(table); i++)
+                if (table[i].from == argv[optind][0])
+                        break;
+
+        if (i >= ELEMENTSOF(table)) {
+                log_error("Unknown command %s.", argv[optind]);
+                return -EINVAL;
+        }
+
+        arg_action = table[i].to;
+
+        optind ++;
+
+        return 1;
+}
+
+static int runlevel_parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_HELP = 0x100,
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, ARG_HELP    },
+                { NULL,        0,                 NULL, 0           }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
+                switch (c) {
+
+                case ARG_HELP:
+                        runlevel_help();
+                        return 0;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind < argc) {
+                log_error("Too many arguments.");
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+        assert(argc >= 0);
+        assert(argv);
+
+        if (program_invocation_short_name) {
+
+                if (strstr(program_invocation_short_name, "halt")) {
+                        arg_action = ACTION_HALT;
+                        return halt_parse_argv(argc, argv);
+                } else if (strstr(program_invocation_short_name, "poweroff")) {
+                        arg_action = ACTION_POWEROFF;
+                        return halt_parse_argv(argc, argv);
+                } else if (strstr(program_invocation_short_name, "reboot")) {
+                        if (kexec_loaded())
+                                arg_action = ACTION_KEXEC;
+                        else
+                                arg_action = ACTION_REBOOT;
+                        return halt_parse_argv(argc, argv);
+                } else if (strstr(program_invocation_short_name, "shutdown")) {
+                        arg_action = ACTION_POWEROFF;
+                        return shutdown_parse_argv(argc, argv);
+                } else if (strstr(program_invocation_short_name, "init")) {
+
+                        if (sd_booted() > 0) {
+                                arg_action = ACTION_INVALID;
+                                return telinit_parse_argv(argc, argv);
+                        } else {
+                                /* Hmm, so some other init system is
+                                 * running, we need to forward this
+                                 * request to it. For now we simply
+                                 * guess that it is Upstart. */
+
+                                execv("/lib/upstart/telinit", argv);
+
+                                log_error("Couldn't find an alternative telinit implementation to spawn.");
+                                return -EIO;
+                        }
+
+                } else if (strstr(program_invocation_short_name, "runlevel")) {
+                        arg_action = ACTION_RUNLEVEL;
+                        return runlevel_parse_argv(argc, argv);
+                }
+        }
+
+        arg_action = ACTION_SYSTEMCTL;
+        return systemctl_parse_argv(argc, argv);
+}
+
+static int action_to_runlevel(void) {
+
+        static const char table[_ACTION_MAX] = {
+                [ACTION_HALT] =      '0',
+                [ACTION_POWEROFF] =  '0',
+                [ACTION_REBOOT] =    '6',
+                [ACTION_RUNLEVEL2] = '2',
+                [ACTION_RUNLEVEL3] = '3',
+                [ACTION_RUNLEVEL4] = '4',
+                [ACTION_RUNLEVEL5] = '5',
+                [ACTION_RESCUE] =    '1'
+        };
+
+        assert(arg_action < _ACTION_MAX);
+
+        return table[arg_action];
+}
+
+static int talk_upstart(void) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int previous, rl, r;
+        char
+                env1_buf[] = "RUNLEVEL=X",
+                env2_buf[] = "PREVLEVEL=X";
+        char *env1 = env1_buf, *env2 = env2_buf;
+        const char *emit = "runlevel";
+        dbus_bool_t b_false = FALSE;
+        DBusMessageIter iter, sub;
+        DBusConnection *bus;
+
+        dbus_error_init(&error);
+
+        if (!(rl = action_to_runlevel()))
+                return 0;
+
+        if (utmp_get_runlevel(&previous, NULL) < 0)
+                previous = 'N';
+
+        if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) {
+                if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
+                        r = 0;
+                        goto finish;
+                }
+
+                log_error("Failed to connect to Upstart bus: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        if ((r = bus_check_peercred(bus)) < 0) {
+                log_error("Failed to verify owner of bus.");
+                goto finish;
+        }
+
+        if (!(m = dbus_message_new_method_call(
+                              "com.ubuntu.Upstart",
+                              "/com/ubuntu/Upstart",
+                              "com.ubuntu.Upstart0_6",
+                              "EmitEvent"))) {
+
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        dbus_message_iter_init_append(m, &iter);
+
+        env1_buf[sizeof(env1_buf)-2] = rl;
+        env2_buf[sizeof(env2_buf)-2] = previous;
+
+        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
+            !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
+            !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
+            !dbus_message_iter_close_container(&iter, &sub) ||
+            !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+
+                if (error_is_no_service(&error)) {
+                        r = -EADDRNOTAVAIL;
+                        goto finish;
+                }
+
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        r = 1;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        if (bus) {
+                dbus_connection_flush(bus);
+                dbus_connection_close(bus);
+                dbus_connection_unref(bus);
+        }
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int talk_initctl(void) {
+        struct init_request request;
+        int r, fd;
+        char rl;
+
+        if (!(rl = action_to_runlevel()))
+                return 0;
+
+        zero(request);
+        request.magic = INIT_MAGIC;
+        request.sleeptime = 0;
+        request.cmd = INIT_CMD_RUNLVL;
+        request.runlevel = rl;
+
+        if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
+
+                if (errno == ENOENT)
+                        return 0;
+
+                log_error("Failed to open "INIT_FIFO": %m");
+                return -errno;
+        }
+
+        errno = 0;
+        r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
+        close_nointr_nofail(fd);
+
+        if (r < 0) {
+                log_error("Failed to write to "INIT_FIFO": %m");
+                return errno ? -errno : -EIO;
+        }
+
+        return 1;
+}
+
+static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
+
+        static const struct {
+                const char* verb;
+                const enum {
+                        MORE,
+                        LESS,
+                        EQUAL
+                } argc_cmp;
+                const int argc;
+                int (* const dispatch)(DBusConnection *bus, char **args);
+        } verbs[] = {
+                { "list-units",            LESS,  1, list_units        },
+                { "list-unit-files",       EQUAL, 1, list_unit_files   },
+                { "list-jobs",             EQUAL, 1, list_jobs         },
+                { "clear-jobs",            EQUAL, 1, daemon_reload     },
+                { "load",                  MORE,  2, load_unit         },
+                { "cancel",                MORE,  2, cancel_job        },
+                { "start",                 MORE,  2, start_unit        },
+                { "stop",                  MORE,  2, start_unit        },
+                { "condstop",              MORE,  2, start_unit        }, /* For compatibility with ALTLinux */
+                { "reload",                MORE,  2, start_unit        },
+                { "restart",               MORE,  2, start_unit        },
+                { "try-restart",           MORE,  2, start_unit        },
+                { "reload-or-restart",     MORE,  2, start_unit        },
+                { "reload-or-try-restart", MORE,  2, start_unit        },
+                { "force-reload",          MORE,  2, start_unit        }, /* For compatibility with SysV */
+                { "condreload",            MORE,  2, start_unit        }, /* For compatibility with ALTLinux */
+                { "condrestart",           MORE,  2, start_unit        }, /* For compatibility with RH */
+                { "isolate",               EQUAL, 2, start_unit        },
+                { "kill",                  MORE,  2, kill_unit         },
+                { "is-active",             MORE,  2, check_unit        },
+                { "check",                 MORE,  2, check_unit        },
+                { "show",                  MORE,  1, show              },
+                { "status",                MORE,  2, show              },
+                { "dump",                  EQUAL, 1, dump              },
+                { "dot",                   EQUAL, 1, dot               },
+                { "snapshot",              LESS,  2, snapshot          },
+                { "delete",                MORE,  2, delete_snapshot   },
+                { "daemon-reload",         EQUAL, 1, daemon_reload     },
+                { "daemon-reexec",         EQUAL, 1, daemon_reload     },
+                { "show-environment",      EQUAL, 1, show_enviroment   },
+                { "set-environment",       MORE,  2, set_environment   },
+                { "unset-environment",     MORE,  2, set_environment   },
+                { "halt",                  EQUAL, 1, start_special     },
+                { "poweroff",              EQUAL, 1, start_special     },
+                { "reboot",                EQUAL, 1, start_special     },
+                { "kexec",                 EQUAL, 1, start_special     },
+                { "default",               EQUAL, 1, start_special     },
+                { "rescue",                EQUAL, 1, start_special     },
+                { "emergency",             EQUAL, 1, start_special     },
+                { "exit",                  EQUAL, 1, start_special     },
+                { "reset-failed",          MORE,  1, reset_failed      },
+                { "enable",                MORE,  2, enable_unit       },
+                { "disable",               MORE,  2, enable_unit       },
+                { "is-enabled",            MORE,  2, unit_is_enabled   },
+                { "reenable",              MORE,  2, enable_unit       },
+                { "preset",                MORE,  2, enable_unit       },
+                { "mask",                  MORE,  2, enable_unit       },
+                { "unmask",                MORE,  2, enable_unit       },
+                { "link",                  MORE,  2, enable_unit       }
+        };
+
+        int left;
+        unsigned i;
+
+        assert(argc >= 0);
+        assert(argv);
+        assert(error);
+
+        left = argc - optind;
+
+        if (left <= 0)
+                /* Special rule: no arguments means "list-units" */
+                i = 0;
+        else {
+                if (streq(argv[optind], "help")) {
+                        systemctl_help();
+                        return 0;
+                }
+
+                for (i = 0; i < ELEMENTSOF(verbs); i++)
+                        if (streq(argv[optind], verbs[i].verb))
+                                break;
+
+                if (i >= ELEMENTSOF(verbs)) {
+                        log_error("Unknown operation %s", argv[optind]);
+                        return -EINVAL;
+                }
+        }
+
+        switch (verbs[i].argc_cmp) {
+
+        case EQUAL:
+                if (left != verbs[i].argc) {
+                        log_error("Invalid number of arguments.");
+                        return -EINVAL;
+                }
+
+                break;
+
+        case MORE:
+                if (left < verbs[i].argc) {
+                        log_error("Too few arguments.");
+                        return -EINVAL;
+                }
+
+                break;
+
+        case LESS:
+                if (left > verbs[i].argc) {
+                        log_error("Too many arguments.");
+                        return -EINVAL;
+                }
+
+                break;
+
+        default:
+                assert_not_reached("Unknown comparison operator.");
+        }
+
+        /* Require a bus connection for all operations but
+         * enable/disable */
+        if (!streq(verbs[i].verb, "enable") &&
+            !streq(verbs[i].verb, "disable") &&
+            !streq(verbs[i].verb, "is-enabled") &&
+            !streq(verbs[i].verb, "list-unit-files") &&
+            !streq(verbs[i].verb, "reenable") &&
+            !streq(verbs[i].verb, "preset") &&
+            !streq(verbs[i].verb, "mask") &&
+            !streq(verbs[i].verb, "unmask") &&
+            !streq(verbs[i].verb, "link")) {
+
+                if (running_in_chroot() > 0) {
+                        log_info("Running in chroot, ignoring request.");
+                        return 0;
+                }
+
+                if (!bus) {
+                        log_error("Failed to get D-Bus connection: %s",
+                                  dbus_error_is_set(error) ? error->message : "No connection to service manager.");
+                        return -EIO;
+                }
+
+        } else {
+
+                if (!bus && !avoid_bus()) {
+                        log_error("Failed to get D-Bus connection: %s",
+                                  dbus_error_is_set(error) ? error->message : "No connection to service manager.");
+                        return -EIO;
+                }
+        }
+
+        return verbs[i].dispatch(bus, argv + optind);
+}
+
+static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
+        int fd;
+        struct msghdr msghdr;
+        struct iovec iovec[2];
+        union sockaddr_union sockaddr;
+        struct sd_shutdown_command c;
+
+        fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+        if (fd < 0)
+                return -errno;
+
+        zero(c);
+        c.usec = t;
+        c.mode = mode;
+        c.dry_run = dry_run;
+        c.warn_wall = warn;
+
+        zero(sockaddr);
+        sockaddr.sa.sa_family = AF_UNIX;
+        strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
+
+        zero(msghdr);
+        msghdr.msg_name = &sockaddr;
+        msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
+
+        zero(iovec);
+        iovec[0].iov_base = (char*) &c;
+        iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
+
+        if (isempty(message))
+                msghdr.msg_iovlen = 1;
+        else {
+                iovec[1].iov_base = (char*) message;
+                iovec[1].iov_len = strlen(message);
+                msghdr.msg_iovlen = 2;
+        }
+        msghdr.msg_iov = iovec;
+
+        if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
+                close_nointr_nofail(fd);
+                return -errno;
+        }
+
+        close_nointr_nofail(fd);
+        return 0;
+}
+
+static int reload_with_fallback(DBusConnection *bus) {
+
+        if (bus) {
+                /* First, try systemd via D-Bus. */
+                if (daemon_reload(bus, NULL) >= 0)
+                        return 0;
+        }
+
+        /* Nothing else worked, so let's try signals */
+        assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
+
+        if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
+                log_error("kill() failed: %m");
+                return -errno;
+        }
+
+        return 0;
+}
+
+static int start_with_fallback(DBusConnection *bus) {
+
+        if (bus) {
+                /* First, try systemd via D-Bus. */
+                if (start_unit(bus, NULL) >= 0)
+                        goto done;
+        }
+
+        /* Hmm, talking to systemd via D-Bus didn't work. Then
+         * let's try to talk to Upstart via D-Bus. */
+        if (talk_upstart() > 0)
+                goto done;
+
+        /* Nothing else worked, so let's try
+         * /dev/initctl */
+        if (talk_initctl() > 0)
+                goto done;
+
+        log_error("Failed to talk to init daemon.");
+        return -EIO;
+
+done:
+        warn_wall(arg_action);
+        return 0;
+}
+
+static void halt_now(enum action a) {
+
+       /* Make sure C-A-D is handled by the kernel from this
+         * point on... */
+        reboot(RB_ENABLE_CAD);
+
+        switch (a) {
+
+        case ACTION_HALT:
+                log_info("Halting.");
+                reboot(RB_HALT_SYSTEM);
+                break;
+
+        case ACTION_POWEROFF:
+                log_info("Powering off.");
+                reboot(RB_POWER_OFF);
+                break;
+
+        case ACTION_REBOOT:
+                log_info("Rebooting.");
+                reboot(RB_AUTOBOOT);
+                break;
+
+        default:
+                assert_not_reached("Unknown halt action.");
+        }
+
+        assert_not_reached("Uh? This shouldn't happen.");
+}
+
+static int halt_main(DBusConnection *bus) {
+        int r;
+
+        if (geteuid() != 0) {
+                /* Try logind if we are a normal user and no special
+                 * mode applies. Maybe PolicyKit allows us to shutdown
+                 * the machine. */
+
+                if (arg_when <= 0 &&
+                    !arg_dry &&
+                    !arg_immediate &&
+                    (arg_action == ACTION_POWEROFF ||
+                     arg_action == ACTION_REBOOT)) {
+                        r = reboot_with_logind(bus, arg_action);
+                        if (r >= 0)
+                                return r;
+                }
+
+                log_error("Must be root.");
+                return -EPERM;
+        }
+
+        if (arg_when > 0) {
+                char *m;
+
+                m = strv_join(arg_wall, " ");
+                r = send_shutdownd(arg_when,
+                                   arg_action == ACTION_HALT     ? 'H' :
+                                   arg_action == ACTION_POWEROFF ? 'P' :
+                                   arg_action == ACTION_KEXEC    ? 'K' :
+                                                                   'r',
+                                   arg_dry,
+                                   !arg_no_wall,
+                                   m);
+                free(m);
+
+                if (r < 0)
+                        log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
+                else {
+                        char date[FORMAT_TIMESTAMP_MAX];
+
+                        log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
+                                 format_timestamp(date, sizeof(date), arg_when));
+                        return 0;
+                }
+        }
+
+        if (!arg_dry && !arg_immediate)
+                return start_with_fallback(bus);
+
+        if (!arg_no_wtmp) {
+                if (sd_booted() > 0)
+                        log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
+                else {
+                        r = utmp_put_shutdown();
+                        if (r < 0)
+                                log_warning("Failed to write utmp record: %s", strerror(-r));
+                }
+        }
+
+        if (!arg_no_sync)
+                sync();
+
+        if (arg_dry)
+                return 0;
+
+        halt_now(arg_action);
+        /* We should never reach this. */
+        return -ENOSYS;
+}
+
+static int runlevel_main(void) {
+        int r, runlevel, previous;
+
+        r = utmp_get_runlevel(&runlevel, &previous);
+        if (r < 0) {
+                puts("unknown");
+                return r;
+        }
+
+        printf("%c %c\n",
+               previous <= 0 ? 'N' : previous,
+               runlevel <= 0 ? 'N' : runlevel);
+
+        return 0;
+}
+
+int main(int argc, char*argv[]) {
+        int r, retval = EXIT_FAILURE;
+        DBusConnection *bus = NULL;
+        DBusError error;
+
+        dbus_error_init(&error);
+
+        log_parse_environment();
+        log_open();
+
+        r = parse_argv(argc, argv);
+        if (r < 0)
+                goto finish;
+        else if (r == 0) {
+                retval = EXIT_SUCCESS;
+                goto finish;
+        }
+
+        /* /sbin/runlevel doesn't need to communicate via D-Bus, so
+         * let's shortcut this */
+        if (arg_action == ACTION_RUNLEVEL) {
+                r = runlevel_main();
+                retval = r < 0 ? EXIT_FAILURE : r;
+                goto finish;
+        }
+
+        if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
+                log_info("Running in chroot, ignoring request.");
+                retval = 0;
+                goto finish;
+        }
+
+        if (!avoid_bus()) {
+                if (arg_transport == TRANSPORT_NORMAL)
+                        bus_connect(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &bus, &private_bus, &error);
+                else if (arg_transport == TRANSPORT_POLKIT) {
+                        bus_connect_system_polkit(&bus, &error);
+                        private_bus = false;
+                } else if (arg_transport == TRANSPORT_SSH) {
+                        bus_connect_system_ssh(NULL, arg_host, &bus, &error);
+                        private_bus = false;
+                } else
+                        assert_not_reached("Uh, invalid transport...");
+        }
+
+        switch (arg_action) {
+
+        case ACTION_SYSTEMCTL:
+                r = systemctl_main(bus, argc, argv, &error);
+                break;
+
+        case ACTION_HALT:
+        case ACTION_POWEROFF:
+        case ACTION_REBOOT:
+        case ACTION_KEXEC:
+                r = halt_main(bus);
+                break;
+
+        case ACTION_RUNLEVEL2:
+        case ACTION_RUNLEVEL3:
+        case ACTION_RUNLEVEL4:
+        case ACTION_RUNLEVEL5:
+        case ACTION_RESCUE:
+        case ACTION_EMERGENCY:
+        case ACTION_DEFAULT:
+                r = start_with_fallback(bus);
+                break;
+
+        case ACTION_RELOAD:
+        case ACTION_REEXEC:
+                r = reload_with_fallback(bus);
+                break;
+
+        case ACTION_CANCEL_SHUTDOWN:
+                r = send_shutdownd(0, 0, false, false, NULL);
+                break;
+
+        case ACTION_INVALID:
+        case ACTION_RUNLEVEL:
+        default:
+                assert_not_reached("Unknown action");
+        }
+
+        retval = r < 0 ? EXIT_FAILURE : r;
+
+finish:
+        if (bus) {
+                dbus_connection_flush(bus);
+                dbus_connection_close(bus);
+                dbus_connection_unref(bus);
+        }
+
+        dbus_error_free(&error);
+
+        dbus_shutdown();
+
+        strv_free(arg_property);
+
+        pager_close();
+        ask_password_agent_close();
+        polkit_agent_close();
+
+        return retval;
+}
diff --git a/src/timestamp.c b/src/timestamp.c
deleted file mode 100644
index 1152f1b..0000000
--- a/src/timestamp.c
+++ /dev/null
@@ -1,39 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <stdio.h>
-
-#include "util.h"
-
-int main(int argc, char *argv[]) {
-        struct dual_timestamp t;
-
-        /* This is mostly useful for stuff like init ram disk scripts
-         * which want to take a proper timestamp to do minimal bootup
-         * profiling. */
-
-        dual_timestamp_get(&t);
-        printf("%llu %llu\n",
-               (unsigned long long) t.realtime,
-               (unsigned long long) t.monotonic);
-
-        return 0;
-}
diff --git a/src/timestamp/timestamp.c b/src/timestamp/timestamp.c
new file mode 100644
index 0000000..1152f1b
--- /dev/null
+++ b/src/timestamp/timestamp.c
@@ -0,0 +1,39 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <stdio.h>
+
+#include "util.h"
+
+int main(int argc, char *argv[]) {
+        struct dual_timestamp t;
+
+        /* This is mostly useful for stuff like init ram disk scripts
+         * which want to take a proper timestamp to do minimal bootup
+         * profiling. */
+
+        dual_timestamp_get(&t);
+        printf("%llu %llu\n",
+               (unsigned long long) t.realtime,
+               (unsigned long long) t.monotonic);
+
+        return 0;
+}
diff --git a/src/tmpfiles.c b/src/tmpfiles.c
deleted file mode 100644
index 1591308..0000000
--- a/src/tmpfiles.c
+++ /dev/null
@@ -1,1315 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering, Kay Sievers
-
-  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 <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <dirent.h>
-#include <grp.h>
-#include <pwd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <getopt.h>
-#include <stdbool.h>
-#include <time.h>
-#include <sys/types.h>
-#include <sys/param.h>
-#include <glob.h>
-#include <fnmatch.h>
-
-#include "log.h"
-#include "util.h"
-#include "mkdir.h"
-#include "strv.h"
-#include "label.h"
-#include "set.h"
-
-/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
- * them in the file system. This is intended to be used to create
- * properly owned directories beneath /tmp, /var/tmp, /run, which are
- * volatile and hence need to be recreated on bootup. */
-
-typedef enum ItemType {
-        /* These ones take file names */
-        CREATE_FILE = 'f',
-        TRUNCATE_FILE = 'F',
-        WRITE_FILE = 'w',
-        CREATE_DIRECTORY = 'd',
-        TRUNCATE_DIRECTORY = 'D',
-        CREATE_FIFO = 'p',
-        CREATE_SYMLINK = 'L',
-        CREATE_CHAR_DEVICE = 'c',
-        CREATE_BLOCK_DEVICE = 'b',
-
-        /* These ones take globs */
-        IGNORE_PATH = 'x',
-        REMOVE_PATH = 'r',
-        RECURSIVE_REMOVE_PATH = 'R',
-        RELABEL_PATH = 'z',
-        RECURSIVE_RELABEL_PATH = 'Z'
-} ItemType;
-
-typedef struct Item {
-        ItemType type;
-
-        char *path;
-        char *argument;
-        uid_t uid;
-        gid_t gid;
-        mode_t mode;
-        usec_t age;
-
-        dev_t major_minor;
-
-        bool uid_set:1;
-        bool gid_set:1;
-        bool mode_set:1;
-        bool age_set:1;
-} Item;
-
-static Hashmap *items = NULL, *globs = NULL;
-static Set *unix_sockets = NULL;
-
-static bool arg_create = false;
-static bool arg_clean = false;
-static bool arg_remove = false;
-
-static const char *arg_prefix = NULL;
-
-#define MAX_DEPTH 256
-
-static bool needs_glob(ItemType t) {
-        return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
-}
-
-static struct Item* find_glob(Hashmap *h, const char *match) {
-        Item *j;
-        Iterator i;
-
-        HASHMAP_FOREACH(j, h, i)
-                if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
-                        return j;
-
-        return NULL;
-}
-
-static void load_unix_sockets(void) {
-        FILE *f = NULL;
-        char line[LINE_MAX];
-
-        if (unix_sockets)
-                return;
-
-        /* We maintain a cache of the sockets we found in
-         * /proc/net/unix to speed things up a little. */
-
-        unix_sockets = set_new(string_hash_func, string_compare_func);
-        if (!unix_sockets)
-                return;
-
-        f = fopen("/proc/net/unix", "re");
-        if (!f)
-                return;
-
-        /* Skip header */
-        if (!fgets(line, sizeof(line), f))
-                goto fail;
-
-        for (;;) {
-                char *p, *s;
-                int k;
-
-                if (!fgets(line, sizeof(line), f))
-                        break;
-
-                truncate_nl(line);
-
-                p = strchr(line, ':');
-                if (!p)
-                        continue;
-
-                if (strlen(p) < 37)
-                        continue;
-
-                p += 37;
-                p += strspn(p, WHITESPACE);
-                p += strcspn(p, WHITESPACE); /* skip one more word */
-                p += strspn(p, WHITESPACE);
-
-                if (*p != '/')
-                        continue;
-
-                s = strdup(p);
-                if (!s)
-                        goto fail;
-
-                path_kill_slashes(s);
-
-                k = set_put(unix_sockets, s);
-                if (k < 0) {
-                        free(s);
-
-                        if (k != -EEXIST)
-                                goto fail;
-                }
-        }
-
-        fclose(f);
-        return;
-
-fail:
-        set_free_free(unix_sockets);
-        unix_sockets = NULL;
-
-        if (f)
-                fclose(f);
-}
-
-static bool unix_socket_alive(const char *fn) {
-        assert(fn);
-
-        load_unix_sockets();
-
-        if (unix_sockets)
-                return !!set_get(unix_sockets, (char*) fn);
-
-        /* We don't know, so assume yes */
-        return true;
-}
-
-static int dir_cleanup(
-                const char *p,
-                DIR *d,
-                const struct stat *ds,
-                usec_t cutoff,
-                dev_t rootdev,
-                bool mountpoint,
-                int maxdepth)
-{
-        struct dirent *dent;
-        struct timespec times[2];
-        bool deleted = false;
-        char *sub_path = NULL;
-        int r = 0;
-
-        while ((dent = readdir(d))) {
-                struct stat s;
-                usec_t age;
-
-                if (streq(dent->d_name, ".") ||
-                    streq(dent->d_name, ".."))
-                        continue;
-
-                if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
-
-                        if (errno != ENOENT) {
-                                log_error("stat(%s/%s) failed: %m", p, dent->d_name);
-                                r = -errno;
-                        }
-
-                        continue;
-                }
-
-                /* Stay on the same filesystem */
-                if (s.st_dev != rootdev)
-                        continue;
-
-                /* Do not delete read-only files owned by root */
-                if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
-                        continue;
-
-                free(sub_path);
-                sub_path = NULL;
-
-                if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                /* Is there an item configured for this path? */
-                if (hashmap_get(items, sub_path))
-                        continue;
-
-                if (find_glob(globs, sub_path))
-                        continue;
-
-                if (S_ISDIR(s.st_mode)) {
-
-                        if (mountpoint &&
-                            streq(dent->d_name, "lost+found") &&
-                            s.st_uid == 0)
-                                continue;
-
-                        if (maxdepth <= 0)
-                                log_warning("Reached max depth on %s.", sub_path);
-                        else {
-                                DIR *sub_dir;
-                                int q;
-
-                                sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
-                                if (sub_dir == NULL) {
-                                        if (errno != ENOENT) {
-                                                log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
-                                                r = -errno;
-                                        }
-
-                                        continue;
-                                }
-
-                                q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
-                                closedir(sub_dir);
-
-                                if (q < 0)
-                                        r = q;
-                        }
-
-                        /* Ignore ctime, we change it when deleting */
-                        age = MAX(timespec_load(&s.st_mtim),
-                                  timespec_load(&s.st_atim));
-                        if (age >= cutoff)
-                                continue;
-
-                        log_debug("rmdir '%s'\n", sub_path);
-
-                        if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
-                                if (errno != ENOENT && errno != ENOTEMPTY) {
-                                        log_error("rmdir(%s): %m", sub_path);
-                                        r = -errno;
-                                }
-                        }
-
-                } else {
-                        /* Skip files for which the sticky bit is
-                         * set. These are semantics we define, and are
-                         * unknown elsewhere. See XDG_RUNTIME_DIR
-                         * specification for details. */
-                        if (s.st_mode & S_ISVTX)
-                                continue;
-
-                        if (mountpoint && S_ISREG(s.st_mode)) {
-                                if (streq(dent->d_name, ".journal") &&
-                                    s.st_uid == 0)
-                                        continue;
-
-                                if (streq(dent->d_name, "aquota.user") ||
-                                    streq(dent->d_name, "aquota.group"))
-                                        continue;
-                        }
-
-                        /* Ignore sockets that are listed in /proc/net/unix */
-                        if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
-                                continue;
-
-                        /* Ignore device nodes */
-                        if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
-                                continue;
-
-                        age = MAX3(timespec_load(&s.st_mtim),
-                                   timespec_load(&s.st_atim),
-                                   timespec_load(&s.st_ctim));
-
-                        if (age >= cutoff)
-                                continue;
-
-                        log_debug("unlink '%s'\n", sub_path);
-
-                        if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
-                                if (errno != ENOENT) {
-                                        log_error("unlink(%s): %m", sub_path);
-                                        r = -errno;
-                                }
-                        }
-
-                        deleted = true;
-                }
-        }
-
-finish:
-        if (deleted) {
-                /* Restore original directory timestamps */
-                times[0] = ds->st_atim;
-                times[1] = ds->st_mtim;
-
-                if (futimens(dirfd(d), times) < 0)
-                        log_error("utimensat(%s): %m", p);
-        }
-
-        free(sub_path);
-
-        return r;
-}
-
-static int clean_item(Item *i) {
-        DIR *d;
-        struct stat s, ps;
-        bool mountpoint;
-        int r;
-        usec_t cutoff, n;
-
-        assert(i);
-
-        if (i->type != CREATE_DIRECTORY &&
-            i->type != TRUNCATE_DIRECTORY &&
-            i->type != IGNORE_PATH)
-                return 0;
-
-        if (!i->age_set || i->age <= 0)
-                return 0;
-
-        n = now(CLOCK_REALTIME);
-        if (n < i->age)
-                return 0;
-
-        cutoff = n - i->age;
-
-        d = opendir(i->path);
-        if (!d) {
-                if (errno == ENOENT)
-                        return 0;
-
-                log_error("Failed to open directory %s: %m", i->path);
-                return -errno;
-        }
-
-        if (fstat(dirfd(d), &s) < 0) {
-                log_error("stat(%s) failed: %m", i->path);
-                r = -errno;
-                goto finish;
-        }
-
-        if (!S_ISDIR(s.st_mode)) {
-                log_error("%s is not a directory.", i->path);
-                r = -ENOTDIR;
-                goto finish;
-        }
-
-        if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
-                log_error("stat(%s/..) failed: %m", i->path);
-                r = -errno;
-                goto finish;
-        }
-
-        mountpoint = s.st_dev != ps.st_dev ||
-                     (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
-
-        r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
-
-finish:
-        if (d)
-                closedir(d);
-
-        return r;
-}
-
-static int item_set_perms(Item *i, const char *path) {
-        /* not using i->path directly because it may be a glob */
-        if (i->mode_set)
-                if (chmod(path, i->mode) < 0) {
-                        log_error("chmod(%s) failed: %m", path);
-                        return -errno;
-                }
-
-        if (i->uid_set || i->gid_set)
-                if (chown(path,
-                          i->uid_set ? i->uid : (uid_t) -1,
-                          i->gid_set ? i->gid : (gid_t) -1) < 0) {
-
-                        log_error("chown(%s) failed: %m", path);
-                        return -errno;
-                }
-
-        return label_fix(path, false);
-}
-
-static int recursive_relabel_children(Item *i, const char *path) {
-        DIR *d;
-        int ret = 0;
-
-        /* This returns the first error we run into, but nevertheless
-         * tries to go on */
-
-        d = opendir(path);
-        if (!d)
-                return errno == ENOENT ? 0 : -errno;
-
-        for (;;) {
-                struct dirent buf, *de;
-                bool is_dir;
-                int r;
-                char *entry_path;
-
-                r = readdir_r(d, &buf, &de);
-                if (r != 0) {
-                        if (ret == 0)
-                                ret = -r;
-                        break;
-                }
-
-                if (!de)
-                        break;
-
-                if (streq(de->d_name, ".") || streq(de->d_name, ".."))
-                        continue;
-
-                if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
-                        if (ret == 0)
-                                ret = -ENOMEM;
-                        continue;
-                }
-
-                if (de->d_type == DT_UNKNOWN) {
-                        struct stat st;
-
-                        if (lstat(entry_path, &st) < 0) {
-                                if (ret == 0 && errno != ENOENT)
-                                        ret = -errno;
-                                free(entry_path);
-                                continue;
-                        }
-
-                        is_dir = S_ISDIR(st.st_mode);
-
-                } else
-                        is_dir = de->d_type == DT_DIR;
-
-                r = item_set_perms(i, entry_path);
-                if (r < 0) {
-                        if (ret == 0 && r != -ENOENT)
-                                ret = r;
-                        free(entry_path);
-                        continue;
-                }
-
-                if (is_dir) {
-                        r = recursive_relabel_children(i, entry_path);
-                        if (r < 0 && ret == 0)
-                                ret = r;
-                }
-
-                free(entry_path);
-        }
-
-        closedir(d);
-
-        return ret;
-}
-
-static int recursive_relabel(Item *i, const char *path) {
-        int r;
-        struct stat st;
-
-        r = item_set_perms(i, path);
-        if (r < 0)
-                return r;
-
-        if (lstat(path, &st) < 0)
-                return -errno;
-
-        if (S_ISDIR(st.st_mode))
-                r = recursive_relabel_children(i, path);
-
-        return r;
-}
-
-static int glob_item(Item *i, int (*action)(Item *, const char *)) {
-        int r = 0, k;
-        glob_t g;
-        char **fn;
-
-        zero(g);
-
-        errno = 0;
-        if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
-
-                if (k != GLOB_NOMATCH) {
-                        if (errno != 0)
-                                errno = EIO;
-
-                        log_error("glob(%s) failed: %m", i->path);
-                        return -errno;
-                }
-        }
-
-        STRV_FOREACH(fn, g.gl_pathv)
-                if ((k = action(i, *fn)) < 0)
-                        r = k;
-
-        globfree(&g);
-        return r;
-}
-
-static int create_item(Item *i) {
-        int r;
-        mode_t u;
-        struct stat st;
-
-        assert(i);
-
-        switch (i->type) {
-
-        case IGNORE_PATH:
-        case REMOVE_PATH:
-        case RECURSIVE_REMOVE_PATH:
-                return 0;
-
-        case CREATE_FILE:
-        case TRUNCATE_FILE:
-        case WRITE_FILE: {
-                int fd, flags;
-
-                flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
-                        i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
-
-                u = umask(0);
-                fd = open(i->path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
-                umask(u);
-
-                if (fd < 0) {
-                        if (i->type == WRITE_FILE && errno == ENOENT)
-                                break;
-
-                        log_error("Failed to create file %s: %m", i->path);
-                        return -errno;
-                }
-
-                if (i->argument) {
-                        ssize_t n;
-                        size_t l;
-                        struct iovec iovec[2];
-                        static const char new_line = '\n';
-
-                        l = strlen(i->argument);
-
-                        zero(iovec);
-                        iovec[0].iov_base = i->argument;
-                        iovec[0].iov_len = l;
-
-                        iovec[1].iov_base = (void*) &new_line;
-                        iovec[1].iov_len = 1;
-
-                        n = writev(fd, iovec, 2);
-                        if (n < 0 || (size_t) n != l+1) {
-                                log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short");
-                                close_nointr_nofail(fd);
-                                return n < 0 ? n : -EIO;
-                        }
-                }
-
-                close_nointr_nofail(fd);
-
-                if (stat(i->path, &st) < 0) {
-                        log_error("stat(%s) failed: %m", i->path);
-                        return -errno;
-                }
-
-                if (!S_ISREG(st.st_mode)) {
-                        log_error("%s is not a file.", i->path);
-                        return -EEXIST;
-                }
-
-                r = item_set_perms(i, i->path);
-                if (r < 0)
-                        return r;
-
-                break;
-        }
-
-        case TRUNCATE_DIRECTORY:
-        case CREATE_DIRECTORY:
-
-                u = umask(0);
-                mkdir_parents(i->path, 0755);
-                r = mkdir(i->path, i->mode);
-                umask(u);
-
-                if (r < 0 && errno != EEXIST) {
-                        log_error("Failed to create directory %s: %m", i->path);
-                        return -errno;
-                }
-
-                if (stat(i->path, &st) < 0) {
-                        log_error("stat(%s) failed: %m", i->path);
-                        return -errno;
-                }
-
-                if (!S_ISDIR(st.st_mode)) {
-                        log_error("%s is not a directory.", i->path);
-                        return -EEXIST;
-                }
-
-                r = item_set_perms(i, i->path);
-                if (r < 0)
-                        return r;
-
-                break;
-
-        case CREATE_FIFO:
-
-                u = umask(0);
-                r = mkfifo(i->path, i->mode);
-                umask(u);
-
-                if (r < 0 && errno != EEXIST) {
-                        log_error("Failed to create fifo %s: %m", i->path);
-                        return -errno;
-                }
-
-                if (stat(i->path, &st) < 0) {
-                        log_error("stat(%s) failed: %m", i->path);
-                        return -errno;
-                }
-
-                if (!S_ISFIFO(st.st_mode)) {
-                        log_error("%s is not a fifo.", i->path);
-                        return -EEXIST;
-                }
-
-                r = item_set_perms(i, i->path);
-                if (r < 0)
-                        return r;
-
-                break;
-
-        case CREATE_SYMLINK: {
-                char *x;
-
-                r = symlink(i->argument, i->path);
-                if (r < 0 && errno != EEXIST) {
-                        log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
-                        return -errno;
-                }
-
-                r = readlink_malloc(i->path, &x);
-                if (r < 0) {
-                        log_error("readlink(%s) failed: %s", i->path, strerror(-r));
-                        return -errno;
-                }
-
-                if (!streq(i->argument, x)) {
-                        free(x);
-                        log_error("%s is not the right symlinks.", i->path);
-                        return -EEXIST;
-                }
-
-                free(x);
-                break;
-        }
-
-        case CREATE_BLOCK_DEVICE:
-        case CREATE_CHAR_DEVICE: {
-
-                u = umask(0);
-                r = mknod(i->path, i->mode | (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR), i->major_minor);
-                umask(u);
-
-                if (r < 0 && errno != EEXIST) {
-                        log_error("Failed to create device node %s: %m", i->path);
-                        return -errno;
-                }
-
-                if (stat(i->path, &st) < 0) {
-                        log_error("stat(%s) failed: %m", i->path);
-                        return -errno;
-                }
-
-                if (i->type == CREATE_BLOCK_DEVICE ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) {
-                        log_error("%s is not a device node.", i->path);
-                        return -EEXIST;
-                }
-
-                r = item_set_perms(i, i->path);
-                if (r < 0)
-                        return r;
-
-                break;
-        }
-
-        case RELABEL_PATH:
-
-                r = glob_item(i, item_set_perms);
-                if (r < 0)
-                        return 0;
-                break;
-
-        case RECURSIVE_RELABEL_PATH:
-
-                r = glob_item(i, recursive_relabel);
-                if (r < 0)
-                        return r;
-        }
-
-        log_debug("%s created successfully.", i->path);
-
-        return 0;
-}
-
-static int remove_item_instance(Item *i, const char *instance) {
-        int r;
-
-        assert(i);
-
-        switch (i->type) {
-
-        case CREATE_FILE:
-        case TRUNCATE_FILE:
-        case CREATE_DIRECTORY:
-        case CREATE_FIFO:
-        case CREATE_SYMLINK:
-        case CREATE_BLOCK_DEVICE:
-        case CREATE_CHAR_DEVICE:
-        case IGNORE_PATH:
-        case RELABEL_PATH:
-        case RECURSIVE_RELABEL_PATH:
-        case WRITE_FILE:
-                break;
-
-        case REMOVE_PATH:
-                if (remove(instance) < 0 && errno != ENOENT) {
-                        log_error("remove(%s): %m", instance);
-                        return -errno;
-                }
-
-                break;
-
-        case TRUNCATE_DIRECTORY:
-        case RECURSIVE_REMOVE_PATH:
-                r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
-                if (r < 0 && r != -ENOENT) {
-                        log_error("rm_rf(%s): %s", instance, strerror(-r));
-                        return r;
-                }
-
-                break;
-        }
-
-        return 0;
-}
-
-static int remove_item(Item *i) {
-        int r = 0;
-
-        assert(i);
-
-        switch (i->type) {
-
-        case CREATE_FILE:
-        case TRUNCATE_FILE:
-        case CREATE_DIRECTORY:
-        case CREATE_FIFO:
-        case CREATE_SYMLINK:
-        case CREATE_CHAR_DEVICE:
-        case CREATE_BLOCK_DEVICE:
-        case IGNORE_PATH:
-        case RELABEL_PATH:
-        case RECURSIVE_RELABEL_PATH:
-        case WRITE_FILE:
-                break;
-
-        case REMOVE_PATH:
-        case TRUNCATE_DIRECTORY:
-        case RECURSIVE_REMOVE_PATH:
-                r = glob_item(i, remove_item_instance);
-                break;
-        }
-
-        return r;
-}
-
-static int process_item(Item *i) {
-        int r, q, p;
-
-        assert(i);
-
-        r = arg_create ? create_item(i) : 0;
-        q = arg_remove ? remove_item(i) : 0;
-        p = arg_clean ? clean_item(i) : 0;
-
-        if (r < 0)
-                return r;
-
-        if (q < 0)
-                return q;
-
-        return p;
-}
-
-static void item_free(Item *i) {
-        assert(i);
-
-        free(i->path);
-        free(i->argument);
-        free(i);
-}
-
-static bool item_equal(Item *a, Item *b) {
-        assert(a);
-        assert(b);
-
-        if (!streq_ptr(a->path, b->path))
-                return false;
-
-        if (a->type != b->type)
-                return false;
-
-        if (a->uid_set != b->uid_set ||
-            (a->uid_set && a->uid != b->uid))
-            return false;
-
-        if (a->gid_set != b->gid_set ||
-            (a->gid_set && a->gid != b->gid))
-            return false;
-
-        if (a->mode_set != b->mode_set ||
-            (a->mode_set && a->mode != b->mode))
-            return false;
-
-        if (a->age_set != b->age_set ||
-            (a->age_set && a->age != b->age))
-            return false;
-
-        if ((a->type == CREATE_FILE ||
-             a->type == TRUNCATE_FILE ||
-             a->type == WRITE_FILE ||
-             a->type == CREATE_SYMLINK) &&
-            !streq_ptr(a->argument, b->argument))
-                return false;
-
-        if ((a->type == CREATE_CHAR_DEVICE ||
-             a->type == CREATE_BLOCK_DEVICE) &&
-            a->major_minor != b->major_minor)
-                return false;
-
-        return true;
-}
-
-static int parse_line(const char *fname, unsigned line, const char *buffer) {
-        Item *i, *existing;
-        char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
-        char type;
-        Hashmap *h;
-        int r, n = -1;
-
-        assert(fname);
-        assert(line >= 1);
-        assert(buffer);
-
-        i = new0(Item, 1);
-        if (!i) {
-                log_error("Out of memory");
-                return -ENOMEM;
-        }
-
-        if (sscanf(buffer,
-                   "%c "
-                   "%ms "
-                   "%ms "
-                   "%ms "
-                   "%ms "
-                   "%ms "
-                   "%n",
-                   &type,
-                   &i->path,
-                   &mode,
-                   &user,
-                   &group,
-                   &age,
-                   &n) < 2) {
-                log_error("[%s:%u] Syntax error.", fname, line);
-                r = -EIO;
-                goto finish;
-        }
-
-        if (n >= 0)  {
-                n += strspn(buffer+n, WHITESPACE);
-                if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
-                        i->argument = unquote(buffer+n, "\"");
-                        if (!i->argument) {
-                                log_error("Out of memory");
-                                return -ENOMEM;
-                        }
-                }
-        }
-
-        switch(type) {
-
-        case CREATE_FILE:
-        case TRUNCATE_FILE:
-        case CREATE_DIRECTORY:
-        case TRUNCATE_DIRECTORY:
-        case CREATE_FIFO:
-        case IGNORE_PATH:
-        case REMOVE_PATH:
-        case RECURSIVE_REMOVE_PATH:
-        case RELABEL_PATH:
-        case RECURSIVE_RELABEL_PATH:
-                break;
-
-        case CREATE_SYMLINK:
-                if (!i->argument) {
-                        log_error("[%s:%u] Symlink file requires argument.", fname, line);
-                        r = -EBADMSG;
-                        goto finish;
-                }
-                break;
-
-        case WRITE_FILE:
-                if (!i->argument) {
-                        log_error("[%s:%u] Write file requires argument.", fname, line);
-                        r = -EBADMSG;
-                        goto finish;
-                }
-                break;
-
-        case CREATE_CHAR_DEVICE:
-        case CREATE_BLOCK_DEVICE: {
-                unsigned major, minor;
-
-                if (!i->argument) {
-                        log_error("[%s:%u] Device file requires argument.", fname, line);
-                        r = -EBADMSG;
-                        goto finish;
-                }
-
-                if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
-                        log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
-                        r = -EBADMSG;
-                        goto finish;
-                }
-
-                i->major_minor = makedev(major, minor);
-                break;
-        }
-
-        default:
-                log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
-                r = -EBADMSG;
-                goto finish;
-        }
-
-        i->type = type;
-
-        if (!path_is_absolute(i->path)) {
-                log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
-                r = -EBADMSG;
-                goto finish;
-        }
-
-        path_kill_slashes(i->path);
-
-        if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
-                r = 0;
-                goto finish;
-        }
-
-        if (user && !streq(user, "-")) {
-                const char *u = user;
-
-                r = get_user_creds(&u, &i->uid, NULL, NULL);
-                if (r < 0) {
-                        log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
-                        goto finish;
-                }
-
-                i->uid_set = true;
-        }
-
-        if (group && !streq(group, "-")) {
-                const char *g = group;
-
-                r = get_group_creds(&g, &i->gid);
-                if (r < 0) {
-                        log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
-                        goto finish;
-                }
-
-                i->gid_set = true;
-        }
-
-        if (mode && !streq(mode, "-")) {
-                unsigned m;
-
-                if (sscanf(mode, "%o", &m) != 1) {
-                        log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
-                        r = -ENOENT;
-                        goto finish;
-                }
-
-                i->mode = m;
-                i->mode_set = true;
-        } else
-                i->mode =
-                        i->type == CREATE_DIRECTORY ||
-                        i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
-
-        if (age && !streq(age, "-")) {
-                if (parse_usec(age, &i->age) < 0) {
-                        log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
-                        r = -EBADMSG;
-                        goto finish;
-                }
-
-                i->age_set = true;
-        }
-
-        h = needs_glob(i->type) ? globs : items;
-
-        existing = hashmap_get(h, i->path);
-        if (existing) {
-
-                /* Two identical items are fine */
-                if (!item_equal(existing, i))
-                        log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
-
-                r = 0;
-                goto finish;
-        }
-
-        r = hashmap_put(h, i->path, i);
-        if (r < 0) {
-                log_error("Failed to insert item %s: %s", i->path, strerror(-r));
-                goto finish;
-        }
-
-        i = NULL;
-        r = 0;
-
-finish:
-        free(user);
-        free(group);
-        free(mode);
-        free(age);
-
-        if (i)
-                item_free(i);
-
-        return r;
-}
-
-static int help(void) {
-
-        printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
-               "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
-               "  -h --help             Show this help\n"
-               "     --create           Create marked files/directories\n"
-               "     --clean            Clean up marked directories\n"
-               "     --remove           Remove marked files/directories\n"
-               "     --prefix=PATH      Only apply rules that apply to paths with the specified prefix\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_CREATE,
-                ARG_CLEAN,
-                ARG_REMOVE,
-                ARG_PREFIX
-        };
-
-        static const struct option options[] = {
-                { "help",      no_argument,       NULL, 'h'           },
-                { "create",    no_argument,       NULL, ARG_CREATE    },
-                { "clean",     no_argument,       NULL, ARG_CLEAN     },
-                { "remove",    no_argument,       NULL, ARG_REMOVE    },
-                { "prefix",    required_argument, NULL, ARG_PREFIX    },
-                { NULL,        0,                 NULL, 0             }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_CREATE:
-                        arg_create = true;
-                        break;
-
-                case ARG_CLEAN:
-                        arg_clean = true;
-                        break;
-
-                case ARG_REMOVE:
-                        arg_remove = true;
-                        break;
-
-                case ARG_PREFIX:
-                        arg_prefix = optarg;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (!arg_clean && !arg_create && !arg_remove) {
-                log_error("You need to specify at least one of --clean, --create or --remove.");
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-static int read_config_file(const char *fn, bool ignore_enoent) {
-        FILE *f;
-        unsigned v = 0;
-        int r = 0;
-
-        assert(fn);
-
-        f = fopen(fn, "re");
-        if (!f) {
-
-                if (ignore_enoent && errno == ENOENT)
-                        return 0;
-
-                log_error("Failed to open %s: %m", fn);
-                return -errno;
-        }
-
-        log_debug("apply: %s\n", fn);
-        for (;;) {
-                char line[LINE_MAX], *l;
-                int k;
-
-                if (!(fgets(line, sizeof(line), f)))
-                        break;
-
-                v++;
-
-                l = strstrip(line);
-                if (*l == '#' || *l == 0)
-                        continue;
-
-                if ((k = parse_line(fn, v, l)) < 0)
-                        if (r == 0)
-                                r = k;
-        }
-
-        if (ferror(f)) {
-                log_error("Failed to read from file %s: %m", fn);
-                if (r == 0)
-                        r = -EIO;
-        }
-
-        fclose(f);
-
-        return r;
-}
-
-int main(int argc, char *argv[]) {
-        int r;
-        Item *i;
-        Iterator iterator;
-
-        r = parse_argv(argc, argv);
-        if (r <= 0)
-                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        label_init();
-
-        items = hashmap_new(string_hash_func, string_compare_func);
-        globs = hashmap_new(string_hash_func, string_compare_func);
-
-        if (!items || !globs) {
-                log_error("Out of memory");
-                r = EXIT_FAILURE;
-                goto finish;
-        }
-
-        r = EXIT_SUCCESS;
-
-        if (optind < argc) {
-                int j;
-
-                for (j = optind; j < argc; j++)
-                        if (read_config_file(argv[j], false) < 0)
-                                r = EXIT_FAILURE;
-
-        } else {
-                char **files, **f;
-
-                r = conf_files_list(&files, ".conf",
-                                    "/etc/tmpfiles.d",
-                                    "/run/tmpfiles.d",
-                                    "/usr/local/lib/tmpfiles.d",
-                                    "/usr/lib/tmpfiles.d",
-                                    NULL);
-                if (r < 0) {
-                        r = EXIT_FAILURE;
-                        log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
-                        goto finish;
-                }
-
-                STRV_FOREACH(f, files) {
-                        if (read_config_file(*f, true) < 0)
-                                r = EXIT_FAILURE;
-                }
-
-                strv_free(files);
-        }
-
-        HASHMAP_FOREACH(i, globs, iterator)
-                process_item(i);
-
-        HASHMAP_FOREACH(i, items, iterator)
-                process_item(i);
-
-finish:
-        while ((i = hashmap_steal_first(items)))
-                item_free(i);
-
-        while ((i = hashmap_steal_first(globs)))
-                item_free(i);
-
-        hashmap_free(items);
-        hashmap_free(globs);
-
-        set_free_free(unix_sockets);
-
-        label_finish();
-
-        return r;
-}
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
new file mode 100644
index 0000000..1591308
--- /dev/null
+++ b/src/tmpfiles/tmpfiles.c
@@ -0,0 +1,1315 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering, Kay Sievers
+
+  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 <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <dirent.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <glob.h>
+#include <fnmatch.h>
+
+#include "log.h"
+#include "util.h"
+#include "mkdir.h"
+#include "strv.h"
+#include "label.h"
+#include "set.h"
+
+/* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates
+ * them in the file system. This is intended to be used to create
+ * properly owned directories beneath /tmp, /var/tmp, /run, which are
+ * volatile and hence need to be recreated on bootup. */
+
+typedef enum ItemType {
+        /* These ones take file names */
+        CREATE_FILE = 'f',
+        TRUNCATE_FILE = 'F',
+        WRITE_FILE = 'w',
+        CREATE_DIRECTORY = 'd',
+        TRUNCATE_DIRECTORY = 'D',
+        CREATE_FIFO = 'p',
+        CREATE_SYMLINK = 'L',
+        CREATE_CHAR_DEVICE = 'c',
+        CREATE_BLOCK_DEVICE = 'b',
+
+        /* These ones take globs */
+        IGNORE_PATH = 'x',
+        REMOVE_PATH = 'r',
+        RECURSIVE_REMOVE_PATH = 'R',
+        RELABEL_PATH = 'z',
+        RECURSIVE_RELABEL_PATH = 'Z'
+} ItemType;
+
+typedef struct Item {
+        ItemType type;
+
+        char *path;
+        char *argument;
+        uid_t uid;
+        gid_t gid;
+        mode_t mode;
+        usec_t age;
+
+        dev_t major_minor;
+
+        bool uid_set:1;
+        bool gid_set:1;
+        bool mode_set:1;
+        bool age_set:1;
+} Item;
+
+static Hashmap *items = NULL, *globs = NULL;
+static Set *unix_sockets = NULL;
+
+static bool arg_create = false;
+static bool arg_clean = false;
+static bool arg_remove = false;
+
+static const char *arg_prefix = NULL;
+
+#define MAX_DEPTH 256
+
+static bool needs_glob(ItemType t) {
+        return t == IGNORE_PATH || t == REMOVE_PATH || t == RECURSIVE_REMOVE_PATH || t == RELABEL_PATH || t == RECURSIVE_RELABEL_PATH;
+}
+
+static struct Item* find_glob(Hashmap *h, const char *match) {
+        Item *j;
+        Iterator i;
+
+        HASHMAP_FOREACH(j, h, i)
+                if (fnmatch(j->path, match, FNM_PATHNAME|FNM_PERIOD) == 0)
+                        return j;
+
+        return NULL;
+}
+
+static void load_unix_sockets(void) {
+        FILE *f = NULL;
+        char line[LINE_MAX];
+
+        if (unix_sockets)
+                return;
+
+        /* We maintain a cache of the sockets we found in
+         * /proc/net/unix to speed things up a little. */
+
+        unix_sockets = set_new(string_hash_func, string_compare_func);
+        if (!unix_sockets)
+                return;
+
+        f = fopen("/proc/net/unix", "re");
+        if (!f)
+                return;
+
+        /* Skip header */
+        if (!fgets(line, sizeof(line), f))
+                goto fail;
+
+        for (;;) {
+                char *p, *s;
+                int k;
+
+                if (!fgets(line, sizeof(line), f))
+                        break;
+
+                truncate_nl(line);
+
+                p = strchr(line, ':');
+                if (!p)
+                        continue;
+
+                if (strlen(p) < 37)
+                        continue;
+
+                p += 37;
+                p += strspn(p, WHITESPACE);
+                p += strcspn(p, WHITESPACE); /* skip one more word */
+                p += strspn(p, WHITESPACE);
+
+                if (*p != '/')
+                        continue;
+
+                s = strdup(p);
+                if (!s)
+                        goto fail;
+
+                path_kill_slashes(s);
+
+                k = set_put(unix_sockets, s);
+                if (k < 0) {
+                        free(s);
+
+                        if (k != -EEXIST)
+                                goto fail;
+                }
+        }
+
+        fclose(f);
+        return;
+
+fail:
+        set_free_free(unix_sockets);
+        unix_sockets = NULL;
+
+        if (f)
+                fclose(f);
+}
+
+static bool unix_socket_alive(const char *fn) {
+        assert(fn);
+
+        load_unix_sockets();
+
+        if (unix_sockets)
+                return !!set_get(unix_sockets, (char*) fn);
+
+        /* We don't know, so assume yes */
+        return true;
+}
+
+static int dir_cleanup(
+                const char *p,
+                DIR *d,
+                const struct stat *ds,
+                usec_t cutoff,
+                dev_t rootdev,
+                bool mountpoint,
+                int maxdepth)
+{
+        struct dirent *dent;
+        struct timespec times[2];
+        bool deleted = false;
+        char *sub_path = NULL;
+        int r = 0;
+
+        while ((dent = readdir(d))) {
+                struct stat s;
+                usec_t age;
+
+                if (streq(dent->d_name, ".") ||
+                    streq(dent->d_name, ".."))
+                        continue;
+
+                if (fstatat(dirfd(d), dent->d_name, &s, AT_SYMLINK_NOFOLLOW) < 0) {
+
+                        if (errno != ENOENT) {
+                                log_error("stat(%s/%s) failed: %m", p, dent->d_name);
+                                r = -errno;
+                        }
+
+                        continue;
+                }
+
+                /* Stay on the same filesystem */
+                if (s.st_dev != rootdev)
+                        continue;
+
+                /* Do not delete read-only files owned by root */
+                if (s.st_uid == 0 && !(s.st_mode & S_IWUSR))
+                        continue;
+
+                free(sub_path);
+                sub_path = NULL;
+
+                if (asprintf(&sub_path, "%s/%s", p, dent->d_name) < 0) {
+                        log_error("Out of memory");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                /* Is there an item configured for this path? */
+                if (hashmap_get(items, sub_path))
+                        continue;
+
+                if (find_glob(globs, sub_path))
+                        continue;
+
+                if (S_ISDIR(s.st_mode)) {
+
+                        if (mountpoint &&
+                            streq(dent->d_name, "lost+found") &&
+                            s.st_uid == 0)
+                                continue;
+
+                        if (maxdepth <= 0)
+                                log_warning("Reached max depth on %s.", sub_path);
+                        else {
+                                DIR *sub_dir;
+                                int q;
+
+                                sub_dir = xopendirat(dirfd(d), dent->d_name, O_NOFOLLOW|O_NOATIME);
+                                if (sub_dir == NULL) {
+                                        if (errno != ENOENT) {
+                                                log_error("opendir(%s/%s) failed: %m", p, dent->d_name);
+                                                r = -errno;
+                                        }
+
+                                        continue;
+                                }
+
+                                q = dir_cleanup(sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1);
+                                closedir(sub_dir);
+
+                                if (q < 0)
+                                        r = q;
+                        }
+
+                        /* Ignore ctime, we change it when deleting */
+                        age = MAX(timespec_load(&s.st_mtim),
+                                  timespec_load(&s.st_atim));
+                        if (age >= cutoff)
+                                continue;
+
+                        log_debug("rmdir '%s'\n", sub_path);
+
+                        if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) {
+                                if (errno != ENOENT && errno != ENOTEMPTY) {
+                                        log_error("rmdir(%s): %m", sub_path);
+                                        r = -errno;
+                                }
+                        }
+
+                } else {
+                        /* Skip files for which the sticky bit is
+                         * set. These are semantics we define, and are
+                         * unknown elsewhere. See XDG_RUNTIME_DIR
+                         * specification for details. */
+                        if (s.st_mode & S_ISVTX)
+                                continue;
+
+                        if (mountpoint && S_ISREG(s.st_mode)) {
+                                if (streq(dent->d_name, ".journal") &&
+                                    s.st_uid == 0)
+                                        continue;
+
+                                if (streq(dent->d_name, "aquota.user") ||
+                                    streq(dent->d_name, "aquota.group"))
+                                        continue;
+                        }
+
+                        /* Ignore sockets that are listed in /proc/net/unix */
+                        if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path))
+                                continue;
+
+                        /* Ignore device nodes */
+                        if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
+                                continue;
+
+                        age = MAX3(timespec_load(&s.st_mtim),
+                                   timespec_load(&s.st_atim),
+                                   timespec_load(&s.st_ctim));
+
+                        if (age >= cutoff)
+                                continue;
+
+                        log_debug("unlink '%s'\n", sub_path);
+
+                        if (unlinkat(dirfd(d), dent->d_name, 0) < 0) {
+                                if (errno != ENOENT) {
+                                        log_error("unlink(%s): %m", sub_path);
+                                        r = -errno;
+                                }
+                        }
+
+                        deleted = true;
+                }
+        }
+
+finish:
+        if (deleted) {
+                /* Restore original directory timestamps */
+                times[0] = ds->st_atim;
+                times[1] = ds->st_mtim;
+
+                if (futimens(dirfd(d), times) < 0)
+                        log_error("utimensat(%s): %m", p);
+        }
+
+        free(sub_path);
+
+        return r;
+}
+
+static int clean_item(Item *i) {
+        DIR *d;
+        struct stat s, ps;
+        bool mountpoint;
+        int r;
+        usec_t cutoff, n;
+
+        assert(i);
+
+        if (i->type != CREATE_DIRECTORY &&
+            i->type != TRUNCATE_DIRECTORY &&
+            i->type != IGNORE_PATH)
+                return 0;
+
+        if (!i->age_set || i->age <= 0)
+                return 0;
+
+        n = now(CLOCK_REALTIME);
+        if (n < i->age)
+                return 0;
+
+        cutoff = n - i->age;
+
+        d = opendir(i->path);
+        if (!d) {
+                if (errno == ENOENT)
+                        return 0;
+
+                log_error("Failed to open directory %s: %m", i->path);
+                return -errno;
+        }
+
+        if (fstat(dirfd(d), &s) < 0) {
+                log_error("stat(%s) failed: %m", i->path);
+                r = -errno;
+                goto finish;
+        }
+
+        if (!S_ISDIR(s.st_mode)) {
+                log_error("%s is not a directory.", i->path);
+                r = -ENOTDIR;
+                goto finish;
+        }
+
+        if (fstatat(dirfd(d), "..", &ps, AT_SYMLINK_NOFOLLOW) != 0) {
+                log_error("stat(%s/..) failed: %m", i->path);
+                r = -errno;
+                goto finish;
+        }
+
+        mountpoint = s.st_dev != ps.st_dev ||
+                     (s.st_dev == ps.st_dev && s.st_ino == ps.st_ino);
+
+        r = dir_cleanup(i->path, d, &s, cutoff, s.st_dev, mountpoint, MAX_DEPTH);
+
+finish:
+        if (d)
+                closedir(d);
+
+        return r;
+}
+
+static int item_set_perms(Item *i, const char *path) {
+        /* not using i->path directly because it may be a glob */
+        if (i->mode_set)
+                if (chmod(path, i->mode) < 0) {
+                        log_error("chmod(%s) failed: %m", path);
+                        return -errno;
+                }
+
+        if (i->uid_set || i->gid_set)
+                if (chown(path,
+                          i->uid_set ? i->uid : (uid_t) -1,
+                          i->gid_set ? i->gid : (gid_t) -1) < 0) {
+
+                        log_error("chown(%s) failed: %m", path);
+                        return -errno;
+                }
+
+        return label_fix(path, false);
+}
+
+static int recursive_relabel_children(Item *i, const char *path) {
+        DIR *d;
+        int ret = 0;
+
+        /* This returns the first error we run into, but nevertheless
+         * tries to go on */
+
+        d = opendir(path);
+        if (!d)
+                return errno == ENOENT ? 0 : -errno;
+
+        for (;;) {
+                struct dirent buf, *de;
+                bool is_dir;
+                int r;
+                char *entry_path;
+
+                r = readdir_r(d, &buf, &de);
+                if (r != 0) {
+                        if (ret == 0)
+                                ret = -r;
+                        break;
+                }
+
+                if (!de)
+                        break;
+
+                if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+                        continue;
+
+                if (asprintf(&entry_path, "%s/%s", path, de->d_name) < 0) {
+                        if (ret == 0)
+                                ret = -ENOMEM;
+                        continue;
+                }
+
+                if (de->d_type == DT_UNKNOWN) {
+                        struct stat st;
+
+                        if (lstat(entry_path, &st) < 0) {
+                                if (ret == 0 && errno != ENOENT)
+                                        ret = -errno;
+                                free(entry_path);
+                                continue;
+                        }
+
+                        is_dir = S_ISDIR(st.st_mode);
+
+                } else
+                        is_dir = de->d_type == DT_DIR;
+
+                r = item_set_perms(i, entry_path);
+                if (r < 0) {
+                        if (ret == 0 && r != -ENOENT)
+                                ret = r;
+                        free(entry_path);
+                        continue;
+                }
+
+                if (is_dir) {
+                        r = recursive_relabel_children(i, entry_path);
+                        if (r < 0 && ret == 0)
+                                ret = r;
+                }
+
+                free(entry_path);
+        }
+
+        closedir(d);
+
+        return ret;
+}
+
+static int recursive_relabel(Item *i, const char *path) {
+        int r;
+        struct stat st;
+
+        r = item_set_perms(i, path);
+        if (r < 0)
+                return r;
+
+        if (lstat(path, &st) < 0)
+                return -errno;
+
+        if (S_ISDIR(st.st_mode))
+                r = recursive_relabel_children(i, path);
+
+        return r;
+}
+
+static int glob_item(Item *i, int (*action)(Item *, const char *)) {
+        int r = 0, k;
+        glob_t g;
+        char **fn;
+
+        zero(g);
+
+        errno = 0;
+        if ((k = glob(i->path, GLOB_NOSORT|GLOB_BRACE, NULL, &g)) != 0) {
+
+                if (k != GLOB_NOMATCH) {
+                        if (errno != 0)
+                                errno = EIO;
+
+                        log_error("glob(%s) failed: %m", i->path);
+                        return -errno;
+                }
+        }
+
+        STRV_FOREACH(fn, g.gl_pathv)
+                if ((k = action(i, *fn)) < 0)
+                        r = k;
+
+        globfree(&g);
+        return r;
+}
+
+static int create_item(Item *i) {
+        int r;
+        mode_t u;
+        struct stat st;
+
+        assert(i);
+
+        switch (i->type) {
+
+        case IGNORE_PATH:
+        case REMOVE_PATH:
+        case RECURSIVE_REMOVE_PATH:
+                return 0;
+
+        case CREATE_FILE:
+        case TRUNCATE_FILE:
+        case WRITE_FILE: {
+                int fd, flags;
+
+                flags = i->type == CREATE_FILE ? O_CREAT|O_APPEND :
+                        i->type == TRUNCATE_FILE ? O_CREAT|O_TRUNC : 0;
+
+                u = umask(0);
+                fd = open(i->path, flags|O_NDELAY|O_CLOEXEC|O_WRONLY|O_NOCTTY|O_NOFOLLOW, i->mode);
+                umask(u);
+
+                if (fd < 0) {
+                        if (i->type == WRITE_FILE && errno == ENOENT)
+                                break;
+
+                        log_error("Failed to create file %s: %m", i->path);
+                        return -errno;
+                }
+
+                if (i->argument) {
+                        ssize_t n;
+                        size_t l;
+                        struct iovec iovec[2];
+                        static const char new_line = '\n';
+
+                        l = strlen(i->argument);
+
+                        zero(iovec);
+                        iovec[0].iov_base = i->argument;
+                        iovec[0].iov_len = l;
+
+                        iovec[1].iov_base = (void*) &new_line;
+                        iovec[1].iov_len = 1;
+
+                        n = writev(fd, iovec, 2);
+                        if (n < 0 || (size_t) n != l+1) {
+                                log_error("Failed to write file %s: %s", i->path, n < 0 ? strerror(-n) : "Short");
+                                close_nointr_nofail(fd);
+                                return n < 0 ? n : -EIO;
+                        }
+                }
+
+                close_nointr_nofail(fd);
+
+                if (stat(i->path, &st) < 0) {
+                        log_error("stat(%s) failed: %m", i->path);
+                        return -errno;
+                }
+
+                if (!S_ISREG(st.st_mode)) {
+                        log_error("%s is not a file.", i->path);
+                        return -EEXIST;
+                }
+
+                r = item_set_perms(i, i->path);
+                if (r < 0)
+                        return r;
+
+                break;
+        }
+
+        case TRUNCATE_DIRECTORY:
+        case CREATE_DIRECTORY:
+
+                u = umask(0);
+                mkdir_parents(i->path, 0755);
+                r = mkdir(i->path, i->mode);
+                umask(u);
+
+                if (r < 0 && errno != EEXIST) {
+                        log_error("Failed to create directory %s: %m", i->path);
+                        return -errno;
+                }
+
+                if (stat(i->path, &st) < 0) {
+                        log_error("stat(%s) failed: %m", i->path);
+                        return -errno;
+                }
+
+                if (!S_ISDIR(st.st_mode)) {
+                        log_error("%s is not a directory.", i->path);
+                        return -EEXIST;
+                }
+
+                r = item_set_perms(i, i->path);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case CREATE_FIFO:
+
+                u = umask(0);
+                r = mkfifo(i->path, i->mode);
+                umask(u);
+
+                if (r < 0 && errno != EEXIST) {
+                        log_error("Failed to create fifo %s: %m", i->path);
+                        return -errno;
+                }
+
+                if (stat(i->path, &st) < 0) {
+                        log_error("stat(%s) failed: %m", i->path);
+                        return -errno;
+                }
+
+                if (!S_ISFIFO(st.st_mode)) {
+                        log_error("%s is not a fifo.", i->path);
+                        return -EEXIST;
+                }
+
+                r = item_set_perms(i, i->path);
+                if (r < 0)
+                        return r;
+
+                break;
+
+        case CREATE_SYMLINK: {
+                char *x;
+
+                r = symlink(i->argument, i->path);
+                if (r < 0 && errno != EEXIST) {
+                        log_error("symlink(%s, %s) failed: %m", i->argument, i->path);
+                        return -errno;
+                }
+
+                r = readlink_malloc(i->path, &x);
+                if (r < 0) {
+                        log_error("readlink(%s) failed: %s", i->path, strerror(-r));
+                        return -errno;
+                }
+
+                if (!streq(i->argument, x)) {
+                        free(x);
+                        log_error("%s is not the right symlinks.", i->path);
+                        return -EEXIST;
+                }
+
+                free(x);
+                break;
+        }
+
+        case CREATE_BLOCK_DEVICE:
+        case CREATE_CHAR_DEVICE: {
+
+                u = umask(0);
+                r = mknod(i->path, i->mode | (i->type == CREATE_BLOCK_DEVICE ? S_IFBLK : S_IFCHR), i->major_minor);
+                umask(u);
+
+                if (r < 0 && errno != EEXIST) {
+                        log_error("Failed to create device node %s: %m", i->path);
+                        return -errno;
+                }
+
+                if (stat(i->path, &st) < 0) {
+                        log_error("stat(%s) failed: %m", i->path);
+                        return -errno;
+                }
+
+                if (i->type == CREATE_BLOCK_DEVICE ? !S_ISBLK(st.st_mode) : !S_ISCHR(st.st_mode)) {
+                        log_error("%s is not a device node.", i->path);
+                        return -EEXIST;
+                }
+
+                r = item_set_perms(i, i->path);
+                if (r < 0)
+                        return r;
+
+                break;
+        }
+
+        case RELABEL_PATH:
+
+                r = glob_item(i, item_set_perms);
+                if (r < 0)
+                        return 0;
+                break;
+
+        case RECURSIVE_RELABEL_PATH:
+
+                r = glob_item(i, recursive_relabel);
+                if (r < 0)
+                        return r;
+        }
+
+        log_debug("%s created successfully.", i->path);
+
+        return 0;
+}
+
+static int remove_item_instance(Item *i, const char *instance) {
+        int r;
+
+        assert(i);
+
+        switch (i->type) {
+
+        case CREATE_FILE:
+        case TRUNCATE_FILE:
+        case CREATE_DIRECTORY:
+        case CREATE_FIFO:
+        case CREATE_SYMLINK:
+        case CREATE_BLOCK_DEVICE:
+        case CREATE_CHAR_DEVICE:
+        case IGNORE_PATH:
+        case RELABEL_PATH:
+        case RECURSIVE_RELABEL_PATH:
+        case WRITE_FILE:
+                break;
+
+        case REMOVE_PATH:
+                if (remove(instance) < 0 && errno != ENOENT) {
+                        log_error("remove(%s): %m", instance);
+                        return -errno;
+                }
+
+                break;
+
+        case TRUNCATE_DIRECTORY:
+        case RECURSIVE_REMOVE_PATH:
+                r = rm_rf(instance, false, i->type == RECURSIVE_REMOVE_PATH, false);
+                if (r < 0 && r != -ENOENT) {
+                        log_error("rm_rf(%s): %s", instance, strerror(-r));
+                        return r;
+                }
+
+                break;
+        }
+
+        return 0;
+}
+
+static int remove_item(Item *i) {
+        int r = 0;
+
+        assert(i);
+
+        switch (i->type) {
+
+        case CREATE_FILE:
+        case TRUNCATE_FILE:
+        case CREATE_DIRECTORY:
+        case CREATE_FIFO:
+        case CREATE_SYMLINK:
+        case CREATE_CHAR_DEVICE:
+        case CREATE_BLOCK_DEVICE:
+        case IGNORE_PATH:
+        case RELABEL_PATH:
+        case RECURSIVE_RELABEL_PATH:
+        case WRITE_FILE:
+                break;
+
+        case REMOVE_PATH:
+        case TRUNCATE_DIRECTORY:
+        case RECURSIVE_REMOVE_PATH:
+                r = glob_item(i, remove_item_instance);
+                break;
+        }
+
+        return r;
+}
+
+static int process_item(Item *i) {
+        int r, q, p;
+
+        assert(i);
+
+        r = arg_create ? create_item(i) : 0;
+        q = arg_remove ? remove_item(i) : 0;
+        p = arg_clean ? clean_item(i) : 0;
+
+        if (r < 0)
+                return r;
+
+        if (q < 0)
+                return q;
+
+        return p;
+}
+
+static void item_free(Item *i) {
+        assert(i);
+
+        free(i->path);
+        free(i->argument);
+        free(i);
+}
+
+static bool item_equal(Item *a, Item *b) {
+        assert(a);
+        assert(b);
+
+        if (!streq_ptr(a->path, b->path))
+                return false;
+
+        if (a->type != b->type)
+                return false;
+
+        if (a->uid_set != b->uid_set ||
+            (a->uid_set && a->uid != b->uid))
+            return false;
+
+        if (a->gid_set != b->gid_set ||
+            (a->gid_set && a->gid != b->gid))
+            return false;
+
+        if (a->mode_set != b->mode_set ||
+            (a->mode_set && a->mode != b->mode))
+            return false;
+
+        if (a->age_set != b->age_set ||
+            (a->age_set && a->age != b->age))
+            return false;
+
+        if ((a->type == CREATE_FILE ||
+             a->type == TRUNCATE_FILE ||
+             a->type == WRITE_FILE ||
+             a->type == CREATE_SYMLINK) &&
+            !streq_ptr(a->argument, b->argument))
+                return false;
+
+        if ((a->type == CREATE_CHAR_DEVICE ||
+             a->type == CREATE_BLOCK_DEVICE) &&
+            a->major_minor != b->major_minor)
+                return false;
+
+        return true;
+}
+
+static int parse_line(const char *fname, unsigned line, const char *buffer) {
+        Item *i, *existing;
+        char *mode = NULL, *user = NULL, *group = NULL, *age = NULL;
+        char type;
+        Hashmap *h;
+        int r, n = -1;
+
+        assert(fname);
+        assert(line >= 1);
+        assert(buffer);
+
+        i = new0(Item, 1);
+        if (!i) {
+                log_error("Out of memory");
+                return -ENOMEM;
+        }
+
+        if (sscanf(buffer,
+                   "%c "
+                   "%ms "
+                   "%ms "
+                   "%ms "
+                   "%ms "
+                   "%ms "
+                   "%n",
+                   &type,
+                   &i->path,
+                   &mode,
+                   &user,
+                   &group,
+                   &age,
+                   &n) < 2) {
+                log_error("[%s:%u] Syntax error.", fname, line);
+                r = -EIO;
+                goto finish;
+        }
+
+        if (n >= 0)  {
+                n += strspn(buffer+n, WHITESPACE);
+                if (buffer[n] != 0 && (buffer[n] != '-' || buffer[n+1] != 0)) {
+                        i->argument = unquote(buffer+n, "\"");
+                        if (!i->argument) {
+                                log_error("Out of memory");
+                                return -ENOMEM;
+                        }
+                }
+        }
+
+        switch(type) {
+
+        case CREATE_FILE:
+        case TRUNCATE_FILE:
+        case CREATE_DIRECTORY:
+        case TRUNCATE_DIRECTORY:
+        case CREATE_FIFO:
+        case IGNORE_PATH:
+        case REMOVE_PATH:
+        case RECURSIVE_REMOVE_PATH:
+        case RELABEL_PATH:
+        case RECURSIVE_RELABEL_PATH:
+                break;
+
+        case CREATE_SYMLINK:
+                if (!i->argument) {
+                        log_error("[%s:%u] Symlink file requires argument.", fname, line);
+                        r = -EBADMSG;
+                        goto finish;
+                }
+                break;
+
+        case WRITE_FILE:
+                if (!i->argument) {
+                        log_error("[%s:%u] Write file requires argument.", fname, line);
+                        r = -EBADMSG;
+                        goto finish;
+                }
+                break;
+
+        case CREATE_CHAR_DEVICE:
+        case CREATE_BLOCK_DEVICE: {
+                unsigned major, minor;
+
+                if (!i->argument) {
+                        log_error("[%s:%u] Device file requires argument.", fname, line);
+                        r = -EBADMSG;
+                        goto finish;
+                }
+
+                if (sscanf(i->argument, "%u:%u", &major, &minor) != 2) {
+                        log_error("[%s:%u] Can't parse device file major/minor '%s'.", fname, line, i->argument);
+                        r = -EBADMSG;
+                        goto finish;
+                }
+
+                i->major_minor = makedev(major, minor);
+                break;
+        }
+
+        default:
+                log_error("[%s:%u] Unknown file type '%c'.", fname, line, type);
+                r = -EBADMSG;
+                goto finish;
+        }
+
+        i->type = type;
+
+        if (!path_is_absolute(i->path)) {
+                log_error("[%s:%u] Path '%s' not absolute.", fname, line, i->path);
+                r = -EBADMSG;
+                goto finish;
+        }
+
+        path_kill_slashes(i->path);
+
+        if (arg_prefix && !path_startswith(i->path, arg_prefix)) {
+                r = 0;
+                goto finish;
+        }
+
+        if (user && !streq(user, "-")) {
+                const char *u = user;
+
+                r = get_user_creds(&u, &i->uid, NULL, NULL);
+                if (r < 0) {
+                        log_error("[%s:%u] Unknown user '%s'.", fname, line, user);
+                        goto finish;
+                }
+
+                i->uid_set = true;
+        }
+
+        if (group && !streq(group, "-")) {
+                const char *g = group;
+
+                r = get_group_creds(&g, &i->gid);
+                if (r < 0) {
+                        log_error("[%s:%u] Unknown group '%s'.", fname, line, group);
+                        goto finish;
+                }
+
+                i->gid_set = true;
+        }
+
+        if (mode && !streq(mode, "-")) {
+                unsigned m;
+
+                if (sscanf(mode, "%o", &m) != 1) {
+                        log_error("[%s:%u] Invalid mode '%s'.", fname, line, mode);
+                        r = -ENOENT;
+                        goto finish;
+                }
+
+                i->mode = m;
+                i->mode_set = true;
+        } else
+                i->mode =
+                        i->type == CREATE_DIRECTORY ||
+                        i->type == TRUNCATE_DIRECTORY ? 0755 : 0644;
+
+        if (age && !streq(age, "-")) {
+                if (parse_usec(age, &i->age) < 0) {
+                        log_error("[%s:%u] Invalid age '%s'.", fname, line, age);
+                        r = -EBADMSG;
+                        goto finish;
+                }
+
+                i->age_set = true;
+        }
+
+        h = needs_glob(i->type) ? globs : items;
+
+        existing = hashmap_get(h, i->path);
+        if (existing) {
+
+                /* Two identical items are fine */
+                if (!item_equal(existing, i))
+                        log_warning("Two or more conflicting lines for %s configured, ignoring.", i->path);
+
+                r = 0;
+                goto finish;
+        }
+
+        r = hashmap_put(h, i->path, i);
+        if (r < 0) {
+                log_error("Failed to insert item %s: %s", i->path, strerror(-r));
+                goto finish;
+        }
+
+        i = NULL;
+        r = 0;
+
+finish:
+        free(user);
+        free(group);
+        free(mode);
+        free(age);
+
+        if (i)
+                item_free(i);
+
+        return r;
+}
+
+static int help(void) {
+
+        printf("%s [OPTIONS...] [CONFIGURATION FILE...]\n\n"
+               "Creates, deletes and cleans up volatile and temporary files and directories.\n\n"
+               "  -h --help             Show this help\n"
+               "     --create           Create marked files/directories\n"
+               "     --clean            Clean up marked directories\n"
+               "     --remove           Remove marked files/directories\n"
+               "     --prefix=PATH      Only apply rules that apply to paths with the specified prefix\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_CREATE,
+                ARG_CLEAN,
+                ARG_REMOVE,
+                ARG_PREFIX
+        };
+
+        static const struct option options[] = {
+                { "help",      no_argument,       NULL, 'h'           },
+                { "create",    no_argument,       NULL, ARG_CREATE    },
+                { "clean",     no_argument,       NULL, ARG_CLEAN     },
+                { "remove",    no_argument,       NULL, ARG_REMOVE    },
+                { "prefix",    required_argument, NULL, ARG_PREFIX    },
+                { NULL,        0,                 NULL, 0             }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_CREATE:
+                        arg_create = true;
+                        break;
+
+                case ARG_CLEAN:
+                        arg_clean = true;
+                        break;
+
+                case ARG_REMOVE:
+                        arg_remove = true;
+                        break;
+
+                case ARG_PREFIX:
+                        arg_prefix = optarg;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (!arg_clean && !arg_create && !arg_remove) {
+                log_error("You need to specify at least one of --clean, --create or --remove.");
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+static int read_config_file(const char *fn, bool ignore_enoent) {
+        FILE *f;
+        unsigned v = 0;
+        int r = 0;
+
+        assert(fn);
+
+        f = fopen(fn, "re");
+        if (!f) {
+
+                if (ignore_enoent && errno == ENOENT)
+                        return 0;
+
+                log_error("Failed to open %s: %m", fn);
+                return -errno;
+        }
+
+        log_debug("apply: %s\n", fn);
+        for (;;) {
+                char line[LINE_MAX], *l;
+                int k;
+
+                if (!(fgets(line, sizeof(line), f)))
+                        break;
+
+                v++;
+
+                l = strstrip(line);
+                if (*l == '#' || *l == 0)
+                        continue;
+
+                if ((k = parse_line(fn, v, l)) < 0)
+                        if (r == 0)
+                                r = k;
+        }
+
+        if (ferror(f)) {
+                log_error("Failed to read from file %s: %m", fn);
+                if (r == 0)
+                        r = -EIO;
+        }
+
+        fclose(f);
+
+        return r;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+        Item *i;
+        Iterator iterator;
+
+        r = parse_argv(argc, argv);
+        if (r <= 0)
+                return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        label_init();
+
+        items = hashmap_new(string_hash_func, string_compare_func);
+        globs = hashmap_new(string_hash_func, string_compare_func);
+
+        if (!items || !globs) {
+                log_error("Out of memory");
+                r = EXIT_FAILURE;
+                goto finish;
+        }
+
+        r = EXIT_SUCCESS;
+
+        if (optind < argc) {
+                int j;
+
+                for (j = optind; j < argc; j++)
+                        if (read_config_file(argv[j], false) < 0)
+                                r = EXIT_FAILURE;
+
+        } else {
+                char **files, **f;
+
+                r = conf_files_list(&files, ".conf",
+                                    "/etc/tmpfiles.d",
+                                    "/run/tmpfiles.d",
+                                    "/usr/local/lib/tmpfiles.d",
+                                    "/usr/lib/tmpfiles.d",
+                                    NULL);
+                if (r < 0) {
+                        r = EXIT_FAILURE;
+                        log_error("Failed to enumerate tmpfiles.d files: %s", strerror(-r));
+                        goto finish;
+                }
+
+                STRV_FOREACH(f, files) {
+                        if (read_config_file(*f, true) < 0)
+                                r = EXIT_FAILURE;
+                }
+
+                strv_free(files);
+        }
+
+        HASHMAP_FOREACH(i, globs, iterator)
+                process_item(i);
+
+        HASHMAP_FOREACH(i, items, iterator)
+                process_item(i);
+
+finish:
+        while ((i = hashmap_steal_first(items)))
+                item_free(i);
+
+        while ((i = hashmap_steal_first(globs)))
+                item_free(i);
+
+        hashmap_free(items);
+        hashmap_free(globs);
+
+        set_free_free(unix_sockets);
+
+        label_finish();
+
+        return r;
+}
diff --git a/src/tty-ask-password-agent.c b/src/tty-ask-password-agent.c
deleted file mode 100644
index 9fbd7f5..0000000
--- a/src/tty-ask-password-agent.c
+++ /dev/null
@@ -1,754 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <stdbool.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <stddef.h>
-#include <sys/poll.h>
-#include <sys/inotify.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <sys/signalfd.h>
-#include <fcntl.h>
-
-#include "util.h"
-#include "mkdir.h"
-#include "conf-parser.h"
-#include "utmp-wtmp.h"
-#include "socket-util.h"
-#include "ask-password-api.h"
-#include "strv.h"
-
-static enum {
-        ACTION_LIST,
-        ACTION_QUERY,
-        ACTION_WATCH,
-        ACTION_WALL
-} arg_action = ACTION_QUERY;
-
-static bool arg_plymouth = false;
-static bool arg_console = false;
-
-static int ask_password_plymouth(
-                const char *message,
-                usec_t until,
-                const char *flag_file,
-                bool accept_cached,
-                char ***_passphrases) {
-
-        int fd = -1, notify = -1;
-        union sockaddr_union sa;
-        char *packet = NULL;
-        ssize_t k;
-        int r, n;
-        struct pollfd pollfd[2];
-        char buffer[LINE_MAX];
-        size_t p = 0;
-        enum {
-                POLL_SOCKET,
-                POLL_INOTIFY
-        };
-
-        assert(_passphrases);
-
-        if (flag_file) {
-                if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
-                        r = -errno;
-                        goto finish;
-                }
-
-                if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
-                        r = -errno;
-                        goto finish;
-                }
-        }
-
-        if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        zero(sa);
-        sa.sa.sa_family = AF_UNIX;
-        strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
-        if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
-                log_error("Failed to connect to Plymouth: %m");
-                r = -errno;
-                goto finish;
-        }
-
-        if (accept_cached) {
-                packet = strdup("c");
-                n = 1;
-        } else
-                asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
-
-        if (!packet) {
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
-                r = k < 0 ? (int) k : -EIO;
-                goto finish;
-        }
-
-        zero(pollfd);
-        pollfd[POLL_SOCKET].fd = fd;
-        pollfd[POLL_SOCKET].events = POLLIN;
-        pollfd[POLL_INOTIFY].fd = notify;
-        pollfd[POLL_INOTIFY].events = POLLIN;
-
-        for (;;) {
-                int sleep_for = -1, j;
-
-                if (until > 0) {
-                        usec_t y;
-
-                        y = now(CLOCK_MONOTONIC);
-
-                        if (y > until) {
-                                r = -ETIME;
-                                goto finish;
-                        }
-
-                        sleep_for = (int) ((until - y) / USEC_PER_MSEC);
-                }
-
-                if (flag_file)
-                        if (access(flag_file, F_OK) < 0) {
-                                r = -errno;
-                                goto finish;
-                        }
-
-                if ((j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        r = -errno;
-                        goto finish;
-                } else if (j == 0) {
-                        r = -ETIME;
-                        goto finish;
-                }
-
-                if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
-                        flush_fd(notify);
-
-                if (pollfd[POLL_SOCKET].revents == 0)
-                        continue;
-
-                if ((k = read(fd, buffer + p, sizeof(buffer) - p)) <= 0) {
-                        r = k < 0 ? -errno : -EIO;
-                        goto finish;
-                }
-
-                p += k;
-
-                if (p < 1)
-                        continue;
-
-                if (buffer[0] == 5) {
-
-                        if (accept_cached) {
-                                /* Hmm, first try with cached
-                                 * passwords failed, so let's retry
-                                 * with a normal password request */
-                                free(packet);
-                                packet = NULL;
-
-                                if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
-                                        r = -ENOMEM;
-                                        goto finish;
-                                }
-
-                                if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
-                                        r = k < 0 ? (int) k : -EIO;
-                                        goto finish;
-                                }
-
-                                accept_cached = false;
-                                p = 0;
-                                continue;
-                        }
-
-                        /* No password, because UI not shown */
-                        r = -ENOENT;
-                        goto finish;
-
-                } else if (buffer[0] == 2 || buffer[0] == 9) {
-                        uint32_t size;
-                        char **l;
-
-                        /* One ore more answers */
-                        if (p < 5)
-                                continue;
-
-                        memcpy(&size, buffer+1, sizeof(size));
-                        size = le32toh(size);
-                        if (size+5 > sizeof(buffer)) {
-                                r = -EIO;
-                                goto finish;
-                        }
-
-                        if (p-5 < size)
-                                continue;
-
-                        if (!(l = strv_parse_nulstr(buffer + 5, size))) {
-                                r = -ENOMEM;
-                                goto finish;
-                        }
-
-                        *_passphrases = l;
-                        break;
-
-                } else {
-                        /* Unknown packet */
-                        r = -EIO;
-                        goto finish;
-                }
-        }
-
-        r = 0;
-
-finish:
-        if (notify >= 0)
-                close_nointr_nofail(notify);
-
-        if (fd >= 0)
-                close_nointr_nofail(fd);
-
-        free(packet);
-
-        return r;
-}
-
-static int parse_password(const char *filename, char **wall) {
-        char *socket_name = NULL, *message = NULL, *packet = NULL;
-        uint64_t not_after = 0;
-        unsigned pid = 0;
-        int socket_fd = -1;
-        bool accept_cached = false;
-
-        const ConfigTableItem items[] = {
-                { "Ask", "Socket",       config_parse_string,   0, &socket_name   },
-                { "Ask", "NotAfter",     config_parse_uint64,   0, &not_after     },
-                { "Ask", "Message",      config_parse_string,   0, &message       },
-                { "Ask", "PID",          config_parse_unsigned, 0, &pid           },
-                { "Ask", "AcceptCached", config_parse_bool,     0, &accept_cached },
-                { NULL, NULL, NULL, 0, NULL }
-        };
-
-        FILE *f;
-        int r;
-
-        assert(filename);
-
-        f = fopen(filename, "re");
-        if (!f) {
-                if (errno == ENOENT)
-                        return 0;
-
-                log_error("open(%s): %m", filename);
-                return -errno;
-        }
-
-        r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
-        if (r < 0) {
-                log_error("Failed to parse password file %s: %s", filename, strerror(-r));
-                goto finish;
-        }
-
-        if (!socket_name) {
-                log_error("Invalid password file %s", filename);
-                r = -EBADMSG;
-                goto finish;
-        }
-
-        if (not_after > 0) {
-                if (now(CLOCK_MONOTONIC) > not_after) {
-                        r = 0;
-                        goto finish;
-                }
-        }
-
-        if (pid > 0 &&
-            kill(pid, 0) < 0 &&
-            errno == ESRCH) {
-                r = 0;
-                goto finish;
-        }
-
-        if (arg_action == ACTION_LIST)
-                printf("'%s' (PID %u)\n", message, pid);
-        else if (arg_action == ACTION_WALL) {
-                char *_wall;
-
-                if (asprintf(&_wall,
-                             "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
-                             "Please enter password with the systemd-tty-ask-password-agent tool!",
-                             *wall ? *wall : "",
-                             *wall ? "\r\n\r\n" : "",
-                             message,
-                             pid) < 0) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                free(*wall);
-                *wall = _wall;
-        } else {
-                union {
-                        struct sockaddr sa;
-                        struct sockaddr_un un;
-                } sa;
-                size_t packet_length = 0;
-
-                assert(arg_action == ACTION_QUERY ||
-                       arg_action == ACTION_WATCH);
-
-                if (access(socket_name, W_OK) < 0) {
-
-                        if (arg_action == ACTION_QUERY)
-                                log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
-
-                        r = 0;
-                        goto finish;
-                }
-
-                if (arg_plymouth) {
-                        char **passwords = NULL;
-
-                        if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
-                                char **p;
-
-                                packet_length = 1;
-                                STRV_FOREACH(p, passwords)
-                                        packet_length += strlen(*p) + 1;
-
-                                if (!(packet = new(char, packet_length)))
-                                        r = -ENOMEM;
-                                else {
-                                        char *d;
-
-                                        packet[0] = '+';
-                                        d = packet+1;
-
-                                        STRV_FOREACH(p, passwords)
-                                                d = stpcpy(d, *p) + 1;
-                                }
-                        }
-
-                } else {
-                        int tty_fd = -1;
-                        char *password;
-
-                        if (arg_console)
-                                if ((tty_fd = acquire_terminal("/dev/console", false, false, false)) < 0) {
-                                        r = tty_fd;
-                                        goto finish;
-                                }
-
-                        r = ask_password_tty(message, not_after, filename, &password);
-
-                        if (arg_console) {
-                                close_nointr_nofail(tty_fd);
-                                release_terminal();
-                        }
-
-                        if (r >= 0) {
-                                packet_length = 1+strlen(password)+1;
-                                if (!(packet = new(char, packet_length)))
-                                        r = -ENOMEM;
-                                else {
-                                        packet[0] = '+';
-                                        strcpy(packet+1, password);
-                                }
-
-                                free(password);
-                        }
-                }
-
-                if (r == -ETIME || r == -ENOENT) {
-                        /* If the query went away, that's OK */
-                        r = 0;
-                        goto finish;
-                }
-
-                if (r < 0) {
-                        log_error("Failed to query password: %s", strerror(-r));
-                        goto finish;
-                }
-
-                if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
-                        log_error("socket(): %m");
-                        r = -errno;
-                        goto finish;
-                }
-
-                zero(sa);
-                sa.un.sun_family = AF_UNIX;
-                strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
-
-                if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
-                        log_error("Failed to send: %m");
-                        r = -errno;
-                        goto finish;
-                }
-        }
-
-finish:
-        fclose(f);
-
-        if (socket_fd >= 0)
-                close_nointr_nofail(socket_fd);
-
-        free(packet);
-        free(socket_name);
-        free(message);
-
-        return r;
-}
-
-static int wall_tty_block(void) {
-        char *p;
-        int fd, r;
-        dev_t devnr;
-
-        r = get_ctty_devnr(0, &devnr);
-        if (r < 0)
-                return -r;
-
-        if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
-                return -ENOMEM;
-
-        mkdir_parents(p, 0700);
-        mkfifo(p, 0600);
-
-        fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
-        free(p);
-
-        if (fd < 0)
-                return -errno;
-
-        return fd;
-}
-
-static bool wall_tty_match(const char *path) {
-        int fd, k;
-        char *p;
-        struct stat st;
-
-        if (path_is_absolute(path))
-                k = lstat(path, &st);
-        else {
-                if (asprintf(&p, "/dev/%s", path) < 0)
-                        return true;
-
-                k = lstat(p, &st);
-                free(p);
-        }
-
-        if (k < 0)
-                return true;
-
-        if (!S_ISCHR(st.st_mode))
-                return true;
-
-        /* We use named pipes to ensure that wall messages suggesting
-         * password entry are not printed over password prompts
-         * already shown. We use the fact here that opening a pipe in
-         * non-blocking mode for write-only will succeed only if
-         * there's some writer behind it. Using pipes has the
-         * advantage that the block will automatically go away if the
-         * process dies. */
-
-        if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
-                return true;
-
-        fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
-        free(p);
-
-        if (fd < 0)
-                return true;
-
-        /* What, we managed to open the pipe? Then this tty is filtered. */
-        close_nointr_nofail(fd);
-        return false;
-}
-
-static int show_passwords(void) {
-        DIR *d;
-        struct dirent *de;
-        int r = 0;
-
-        if (!(d = opendir("/run/systemd/ask-password"))) {
-                if (errno == ENOENT)
-                        return 0;
-
-                log_error("opendir(): %m");
-                return -errno;
-        }
-
-        while ((de = readdir(d))) {
-                char *p;
-                int q;
-                char *wall;
-
-                /* We only support /dev on tmpfs, hence we can rely on
-                 * d_type to be reliable */
-
-                if (de->d_type != DT_REG)
-                        continue;
-
-                if (ignore_file(de->d_name))
-                        continue;
-
-                if (!startswith(de->d_name, "ask."))
-                        continue;
-
-                if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                wall = NULL;
-                if ((q = parse_password(p, &wall)) < 0)
-                        r = q;
-
-                free(p);
-
-                if (wall) {
-                        utmp_wall(wall, wall_tty_match);
-                        free(wall);
-                }
-        }
-
-finish:
-        if (d)
-                closedir(d);
-
-        return r;
-}
-
-static int watch_passwords(void) {
-        enum {
-                FD_INOTIFY,
-                FD_SIGNAL,
-                _FD_MAX
-        };
-
-        int notify = -1, signal_fd = -1, tty_block_fd = -1;
-        struct pollfd pollfd[_FD_MAX];
-        sigset_t mask;
-        int r;
-
-        tty_block_fd = wall_tty_block();
-
-        mkdir_p("/run/systemd/ask-password", 0755);
-
-        if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
-                r = -errno;
-                goto finish;
-        }
-
-        assert_se(sigemptyset(&mask) == 0);
-        sigset_add_many(&mask, SIGINT, SIGTERM, -1);
-        assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
-
-        if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
-                log_error("signalfd(): %m");
-                r = -errno;
-                goto finish;
-        }
-
-        zero(pollfd);
-        pollfd[FD_INOTIFY].fd = notify;
-        pollfd[FD_INOTIFY].events = POLLIN;
-        pollfd[FD_SIGNAL].fd = signal_fd;
-        pollfd[FD_SIGNAL].events = POLLIN;
-
-        for (;;) {
-                if ((r = show_passwords()) < 0)
-                        log_error("Failed to show password: %s", strerror(-r));
-
-                if (poll(pollfd, _FD_MAX, -1) < 0) {
-
-                        if (errno == EINTR)
-                                continue;
-
-                        r = -errno;
-                        goto finish;
-                }
-
-                if (pollfd[FD_INOTIFY].revents != 0)
-                        flush_fd(notify);
-
-                if (pollfd[FD_SIGNAL].revents != 0)
-                        break;
-        }
-
-        r = 0;
-
-finish:
-        if (notify >= 0)
-                close_nointr_nofail(notify);
-
-        if (signal_fd >= 0)
-                close_nointr_nofail(signal_fd);
-
-        if (tty_block_fd >= 0)
-                close_nointr_nofail(tty_block_fd);
-
-        return r;
-}
-
-static int help(void) {
-
-        printf("%s [OPTIONS...]\n\n"
-               "Process system password requests.\n\n"
-               "  -h --help     Show this help\n"
-               "     --list     Show pending password requests\n"
-               "     --query    Process pending password requests\n"
-               "     --watch    Continuously process password requests\n"
-               "     --wall     Continuously forward password requests to wall\n"
-               "     --plymouth Ask question with Plymouth instead of on TTY\n"
-               "     --console  Ask question on /dev/console instead of current TTY\n",
-               program_invocation_short_name);
-
-        return 0;
-}
-
-static int parse_argv(int argc, char *argv[]) {
-
-        enum {
-                ARG_LIST = 0x100,
-                ARG_QUERY,
-                ARG_WATCH,
-                ARG_WALL,
-                ARG_PLYMOUTH,
-                ARG_CONSOLE
-        };
-
-        static const struct option options[] = {
-                { "help",     no_argument, NULL, 'h'          },
-                { "list",     no_argument, NULL, ARG_LIST     },
-                { "query",    no_argument, NULL, ARG_QUERY    },
-                { "watch",    no_argument, NULL, ARG_WATCH    },
-                { "wall",     no_argument, NULL, ARG_WALL     },
-                { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
-                { "console",  no_argument, NULL, ARG_CONSOLE  },
-                { NULL,    0,           NULL, 0               }
-        };
-
-        int c;
-
-        assert(argc >= 0);
-        assert(argv);
-
-        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
-
-                switch (c) {
-
-                case 'h':
-                        help();
-                        return 0;
-
-                case ARG_LIST:
-                        arg_action = ACTION_LIST;
-                        break;
-
-                case ARG_QUERY:
-                        arg_action = ACTION_QUERY;
-                        break;
-
-                case ARG_WATCH:
-                        arg_action = ACTION_WATCH;
-                        break;
-
-                case ARG_WALL:
-                        arg_action = ACTION_WALL;
-                        break;
-
-                case ARG_PLYMOUTH:
-                        arg_plymouth = true;
-                        break;
-
-                case ARG_CONSOLE:
-                        arg_console = true;
-                        break;
-
-                case '?':
-                        return -EINVAL;
-
-                default:
-                        log_error("Unknown option code %c", c);
-                        return -EINVAL;
-                }
-        }
-
-        if (optind != argc) {
-                help();
-                return -EINVAL;
-        }
-
-        return 1;
-}
-
-int main(int argc, char *argv[]) {
-        int r;
-
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-        if ((r = parse_argv(argc, argv)) <= 0)
-                goto finish;
-
-        if (arg_console) {
-                setsid();
-                release_terminal();
-        }
-
-        if (arg_action == ACTION_WATCH ||
-            arg_action == ACTION_WALL)
-                r = watch_passwords();
-        else
-                r = show_passwords();
-
-        if (r < 0)
-                log_error("Error: %s", strerror(-r));
-
-finish:
-        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c
new file mode 100644
index 0000000..9fbd7f5
--- /dev/null
+++ b/src/tty-ask-password-agent/tty-ask-password-agent.c
@@ -0,0 +1,754 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <stdbool.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stddef.h>
+#include <sys/poll.h>
+#include <sys/inotify.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sys/signalfd.h>
+#include <fcntl.h>
+
+#include "util.h"
+#include "mkdir.h"
+#include "conf-parser.h"
+#include "utmp-wtmp.h"
+#include "socket-util.h"
+#include "ask-password-api.h"
+#include "strv.h"
+
+static enum {
+        ACTION_LIST,
+        ACTION_QUERY,
+        ACTION_WATCH,
+        ACTION_WALL
+} arg_action = ACTION_QUERY;
+
+static bool arg_plymouth = false;
+static bool arg_console = false;
+
+static int ask_password_plymouth(
+                const char *message,
+                usec_t until,
+                const char *flag_file,
+                bool accept_cached,
+                char ***_passphrases) {
+
+        int fd = -1, notify = -1;
+        union sockaddr_union sa;
+        char *packet = NULL;
+        ssize_t k;
+        int r, n;
+        struct pollfd pollfd[2];
+        char buffer[LINE_MAX];
+        size_t p = 0;
+        enum {
+                POLL_SOCKET,
+                POLL_INOTIFY
+        };
+
+        assert(_passphrases);
+
+        if (flag_file) {
+                if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
+                        r = -errno;
+                        goto finish;
+                }
+
+                if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
+                        r = -errno;
+                        goto finish;
+                }
+        }
+
+        if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        zero(sa);
+        sa.sa.sa_family = AF_UNIX;
+        strncpy(sa.un.sun_path+1, "/org/freedesktop/plymouthd", sizeof(sa.un.sun_path)-1);
+        if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + strlen(sa.un.sun_path+1)) < 0) {
+                log_error("Failed to connect to Plymouth: %m");
+                r = -errno;
+                goto finish;
+        }
+
+        if (accept_cached) {
+                packet = strdup("c");
+                n = 1;
+        } else
+                asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n);
+
+        if (!packet) {
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
+                r = k < 0 ? (int) k : -EIO;
+                goto finish;
+        }
+
+        zero(pollfd);
+        pollfd[POLL_SOCKET].fd = fd;
+        pollfd[POLL_SOCKET].events = POLLIN;
+        pollfd[POLL_INOTIFY].fd = notify;
+        pollfd[POLL_INOTIFY].events = POLLIN;
+
+        for (;;) {
+                int sleep_for = -1, j;
+
+                if (until > 0) {
+                        usec_t y;
+
+                        y = now(CLOCK_MONOTONIC);
+
+                        if (y > until) {
+                                r = -ETIME;
+                                goto finish;
+                        }
+
+                        sleep_for = (int) ((until - y) / USEC_PER_MSEC);
+                }
+
+                if (flag_file)
+                        if (access(flag_file, F_OK) < 0) {
+                                r = -errno;
+                                goto finish;
+                        }
+
+                if ((j = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        r = -errno;
+                        goto finish;
+                } else if (j == 0) {
+                        r = -ETIME;
+                        goto finish;
+                }
+
+                if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
+                        flush_fd(notify);
+
+                if (pollfd[POLL_SOCKET].revents == 0)
+                        continue;
+
+                if ((k = read(fd, buffer + p, sizeof(buffer) - p)) <= 0) {
+                        r = k < 0 ? -errno : -EIO;
+                        goto finish;
+                }
+
+                p += k;
+
+                if (p < 1)
+                        continue;
+
+                if (buffer[0] == 5) {
+
+                        if (accept_cached) {
+                                /* Hmm, first try with cached
+                                 * passwords failed, so let's retry
+                                 * with a normal password request */
+                                free(packet);
+                                packet = NULL;
+
+                                if (asprintf(&packet, "*\002%c%s%n", (int) (strlen(message) + 1), message, &n) < 0) {
+                                        r = -ENOMEM;
+                                        goto finish;
+                                }
+
+                                if ((k = loop_write(fd, packet, n+1, true)) != n+1) {
+                                        r = k < 0 ? (int) k : -EIO;
+                                        goto finish;
+                                }
+
+                                accept_cached = false;
+                                p = 0;
+                                continue;
+                        }
+
+                        /* No password, because UI not shown */
+                        r = -ENOENT;
+                        goto finish;
+
+                } else if (buffer[0] == 2 || buffer[0] == 9) {
+                        uint32_t size;
+                        char **l;
+
+                        /* One ore more answers */
+                        if (p < 5)
+                                continue;
+
+                        memcpy(&size, buffer+1, sizeof(size));
+                        size = le32toh(size);
+                        if (size+5 > sizeof(buffer)) {
+                                r = -EIO;
+                                goto finish;
+                        }
+
+                        if (p-5 < size)
+                                continue;
+
+                        if (!(l = strv_parse_nulstr(buffer + 5, size))) {
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        *_passphrases = l;
+                        break;
+
+                } else {
+                        /* Unknown packet */
+                        r = -EIO;
+                        goto finish;
+                }
+        }
+
+        r = 0;
+
+finish:
+        if (notify >= 0)
+                close_nointr_nofail(notify);
+
+        if (fd >= 0)
+                close_nointr_nofail(fd);
+
+        free(packet);
+
+        return r;
+}
+
+static int parse_password(const char *filename, char **wall) {
+        char *socket_name = NULL, *message = NULL, *packet = NULL;
+        uint64_t not_after = 0;
+        unsigned pid = 0;
+        int socket_fd = -1;
+        bool accept_cached = false;
+
+        const ConfigTableItem items[] = {
+                { "Ask", "Socket",       config_parse_string,   0, &socket_name   },
+                { "Ask", "NotAfter",     config_parse_uint64,   0, &not_after     },
+                { "Ask", "Message",      config_parse_string,   0, &message       },
+                { "Ask", "PID",          config_parse_unsigned, 0, &pid           },
+                { "Ask", "AcceptCached", config_parse_bool,     0, &accept_cached },
+                { NULL, NULL, NULL, 0, NULL }
+        };
+
+        FILE *f;
+        int r;
+
+        assert(filename);
+
+        f = fopen(filename, "re");
+        if (!f) {
+                if (errno == ENOENT)
+                        return 0;
+
+                log_error("open(%s): %m", filename);
+                return -errno;
+        }
+
+        r = config_parse(filename, f, NULL, config_item_table_lookup, (void*) items, true, NULL);
+        if (r < 0) {
+                log_error("Failed to parse password file %s: %s", filename, strerror(-r));
+                goto finish;
+        }
+
+        if (!socket_name) {
+                log_error("Invalid password file %s", filename);
+                r = -EBADMSG;
+                goto finish;
+        }
+
+        if (not_after > 0) {
+                if (now(CLOCK_MONOTONIC) > not_after) {
+                        r = 0;
+                        goto finish;
+                }
+        }
+
+        if (pid > 0 &&
+            kill(pid, 0) < 0 &&
+            errno == ESRCH) {
+                r = 0;
+                goto finish;
+        }
+
+        if (arg_action == ACTION_LIST)
+                printf("'%s' (PID %u)\n", message, pid);
+        else if (arg_action == ACTION_WALL) {
+                char *_wall;
+
+                if (asprintf(&_wall,
+                             "%s%sPassword entry required for \'%s\' (PID %u).\r\n"
+                             "Please enter password with the systemd-tty-ask-password-agent tool!",
+                             *wall ? *wall : "",
+                             *wall ? "\r\n\r\n" : "",
+                             message,
+                             pid) < 0) {
+                        log_error("Out of memory");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                free(*wall);
+                *wall = _wall;
+        } else {
+                union {
+                        struct sockaddr sa;
+                        struct sockaddr_un un;
+                } sa;
+                size_t packet_length = 0;
+
+                assert(arg_action == ACTION_QUERY ||
+                       arg_action == ACTION_WATCH);
+
+                if (access(socket_name, W_OK) < 0) {
+
+                        if (arg_action == ACTION_QUERY)
+                                log_info("Not querying '%s' (PID %u), lacking privileges.", message, pid);
+
+                        r = 0;
+                        goto finish;
+                }
+
+                if (arg_plymouth) {
+                        char **passwords = NULL;
+
+                        if ((r = ask_password_plymouth(message, not_after, filename, accept_cached, &passwords)) >= 0) {
+                                char **p;
+
+                                packet_length = 1;
+                                STRV_FOREACH(p, passwords)
+                                        packet_length += strlen(*p) + 1;
+
+                                if (!(packet = new(char, packet_length)))
+                                        r = -ENOMEM;
+                                else {
+                                        char *d;
+
+                                        packet[0] = '+';
+                                        d = packet+1;
+
+                                        STRV_FOREACH(p, passwords)
+                                                d = stpcpy(d, *p) + 1;
+                                }
+                        }
+
+                } else {
+                        int tty_fd = -1;
+                        char *password;
+
+                        if (arg_console)
+                                if ((tty_fd = acquire_terminal("/dev/console", false, false, false)) < 0) {
+                                        r = tty_fd;
+                                        goto finish;
+                                }
+
+                        r = ask_password_tty(message, not_after, filename, &password);
+
+                        if (arg_console) {
+                                close_nointr_nofail(tty_fd);
+                                release_terminal();
+                        }
+
+                        if (r >= 0) {
+                                packet_length = 1+strlen(password)+1;
+                                if (!(packet = new(char, packet_length)))
+                                        r = -ENOMEM;
+                                else {
+                                        packet[0] = '+';
+                                        strcpy(packet+1, password);
+                                }
+
+                                free(password);
+                        }
+                }
+
+                if (r == -ETIME || r == -ENOENT) {
+                        /* If the query went away, that's OK */
+                        r = 0;
+                        goto finish;
+                }
+
+                if (r < 0) {
+                        log_error("Failed to query password: %s", strerror(-r));
+                        goto finish;
+                }
+
+                if ((socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
+                        log_error("socket(): %m");
+                        r = -errno;
+                        goto finish;
+                }
+
+                zero(sa);
+                sa.un.sun_family = AF_UNIX;
+                strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
+
+                if (sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
+                        log_error("Failed to send: %m");
+                        r = -errno;
+                        goto finish;
+                }
+        }
+
+finish:
+        fclose(f);
+
+        if (socket_fd >= 0)
+                close_nointr_nofail(socket_fd);
+
+        free(packet);
+        free(socket_name);
+        free(message);
+
+        return r;
+}
+
+static int wall_tty_block(void) {
+        char *p;
+        int fd, r;
+        dev_t devnr;
+
+        r = get_ctty_devnr(0, &devnr);
+        if (r < 0)
+                return -r;
+
+        if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(devnr), minor(devnr)) < 0)
+                return -ENOMEM;
+
+        mkdir_parents(p, 0700);
+        mkfifo(p, 0600);
+
+        fd = open(p, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+        free(p);
+
+        if (fd < 0)
+                return -errno;
+
+        return fd;
+}
+
+static bool wall_tty_match(const char *path) {
+        int fd, k;
+        char *p;
+        struct stat st;
+
+        if (path_is_absolute(path))
+                k = lstat(path, &st);
+        else {
+                if (asprintf(&p, "/dev/%s", path) < 0)
+                        return true;
+
+                k = lstat(p, &st);
+                free(p);
+        }
+
+        if (k < 0)
+                return true;
+
+        if (!S_ISCHR(st.st_mode))
+                return true;
+
+        /* We use named pipes to ensure that wall messages suggesting
+         * password entry are not printed over password prompts
+         * already shown. We use the fact here that opening a pipe in
+         * non-blocking mode for write-only will succeed only if
+         * there's some writer behind it. Using pipes has the
+         * advantage that the block will automatically go away if the
+         * process dies. */
+
+        if (asprintf(&p, "/run/systemd/ask-password-block/%u:%u", major(st.st_rdev), minor(st.st_rdev)) < 0)
+                return true;
+
+        fd = open(p, O_WRONLY|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
+        free(p);
+
+        if (fd < 0)
+                return true;
+
+        /* What, we managed to open the pipe? Then this tty is filtered. */
+        close_nointr_nofail(fd);
+        return false;
+}
+
+static int show_passwords(void) {
+        DIR *d;
+        struct dirent *de;
+        int r = 0;
+
+        if (!(d = opendir("/run/systemd/ask-password"))) {
+                if (errno == ENOENT)
+                        return 0;
+
+                log_error("opendir(): %m");
+                return -errno;
+        }
+
+        while ((de = readdir(d))) {
+                char *p;
+                int q;
+                char *wall;
+
+                /* We only support /dev on tmpfs, hence we can rely on
+                 * d_type to be reliable */
+
+                if (de->d_type != DT_REG)
+                        continue;
+
+                if (ignore_file(de->d_name))
+                        continue;
+
+                if (!startswith(de->d_name, "ask."))
+                        continue;
+
+                if (!(p = strappend("/run/systemd/ask-password/", de->d_name))) {
+                        log_error("Out of memory");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                wall = NULL;
+                if ((q = parse_password(p, &wall)) < 0)
+                        r = q;
+
+                free(p);
+
+                if (wall) {
+                        utmp_wall(wall, wall_tty_match);
+                        free(wall);
+                }
+        }
+
+finish:
+        if (d)
+                closedir(d);
+
+        return r;
+}
+
+static int watch_passwords(void) {
+        enum {
+                FD_INOTIFY,
+                FD_SIGNAL,
+                _FD_MAX
+        };
+
+        int notify = -1, signal_fd = -1, tty_block_fd = -1;
+        struct pollfd pollfd[_FD_MAX];
+        sigset_t mask;
+        int r;
+
+        tty_block_fd = wall_tty_block();
+
+        mkdir_p("/run/systemd/ask-password", 0755);
+
+        if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        if (inotify_add_watch(notify, "/run/systemd/ask-password", IN_CLOSE_WRITE|IN_MOVED_TO) < 0) {
+                r = -errno;
+                goto finish;
+        }
+
+        assert_se(sigemptyset(&mask) == 0);
+        sigset_add_many(&mask, SIGINT, SIGTERM, -1);
+        assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+
+        if ((signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
+                log_error("signalfd(): %m");
+                r = -errno;
+                goto finish;
+        }
+
+        zero(pollfd);
+        pollfd[FD_INOTIFY].fd = notify;
+        pollfd[FD_INOTIFY].events = POLLIN;
+        pollfd[FD_SIGNAL].fd = signal_fd;
+        pollfd[FD_SIGNAL].events = POLLIN;
+
+        for (;;) {
+                if ((r = show_passwords()) < 0)
+                        log_error("Failed to show password: %s", strerror(-r));
+
+                if (poll(pollfd, _FD_MAX, -1) < 0) {
+
+                        if (errno == EINTR)
+                                continue;
+
+                        r = -errno;
+                        goto finish;
+                }
+
+                if (pollfd[FD_INOTIFY].revents != 0)
+                        flush_fd(notify);
+
+                if (pollfd[FD_SIGNAL].revents != 0)
+                        break;
+        }
+
+        r = 0;
+
+finish:
+        if (notify >= 0)
+                close_nointr_nofail(notify);
+
+        if (signal_fd >= 0)
+                close_nointr_nofail(signal_fd);
+
+        if (tty_block_fd >= 0)
+                close_nointr_nofail(tty_block_fd);
+
+        return r;
+}
+
+static int help(void) {
+
+        printf("%s [OPTIONS...]\n\n"
+               "Process system password requests.\n\n"
+               "  -h --help     Show this help\n"
+               "     --list     Show pending password requests\n"
+               "     --query    Process pending password requests\n"
+               "     --watch    Continuously process password requests\n"
+               "     --wall     Continuously forward password requests to wall\n"
+               "     --plymouth Ask question with Plymouth instead of on TTY\n"
+               "     --console  Ask question on /dev/console instead of current TTY\n",
+               program_invocation_short_name);
+
+        return 0;
+}
+
+static int parse_argv(int argc, char *argv[]) {
+
+        enum {
+                ARG_LIST = 0x100,
+                ARG_QUERY,
+                ARG_WATCH,
+                ARG_WALL,
+                ARG_PLYMOUTH,
+                ARG_CONSOLE
+        };
+
+        static const struct option options[] = {
+                { "help",     no_argument, NULL, 'h'          },
+                { "list",     no_argument, NULL, ARG_LIST     },
+                { "query",    no_argument, NULL, ARG_QUERY    },
+                { "watch",    no_argument, NULL, ARG_WATCH    },
+                { "wall",     no_argument, NULL, ARG_WALL     },
+                { "plymouth", no_argument, NULL, ARG_PLYMOUTH },
+                { "console",  no_argument, NULL, ARG_CONSOLE  },
+                { NULL,    0,           NULL, 0               }
+        };
+
+        int c;
+
+        assert(argc >= 0);
+        assert(argv);
+
+        while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
+
+                switch (c) {
+
+                case 'h':
+                        help();
+                        return 0;
+
+                case ARG_LIST:
+                        arg_action = ACTION_LIST;
+                        break;
+
+                case ARG_QUERY:
+                        arg_action = ACTION_QUERY;
+                        break;
+
+                case ARG_WATCH:
+                        arg_action = ACTION_WATCH;
+                        break;
+
+                case ARG_WALL:
+                        arg_action = ACTION_WALL;
+                        break;
+
+                case ARG_PLYMOUTH:
+                        arg_plymouth = true;
+                        break;
+
+                case ARG_CONSOLE:
+                        arg_console = true;
+                        break;
+
+                case '?':
+                        return -EINVAL;
+
+                default:
+                        log_error("Unknown option code %c", c);
+                        return -EINVAL;
+                }
+        }
+
+        if (optind != argc) {
+                help();
+                return -EINVAL;
+        }
+
+        return 1;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+        if ((r = parse_argv(argc, argv)) <= 0)
+                goto finish;
+
+        if (arg_console) {
+                setsid();
+                release_terminal();
+        }
+
+        if (arg_action == ACTION_WATCH ||
+            arg_action == ACTION_WALL)
+                r = watch_passwords();
+        else
+                r = show_passwords();
+
+        if (r < 0)
+                log_error("Error: %s", strerror(-r));
+
+finish:
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/update-utmp.c b/src/update-utmp.c
deleted file mode 100644
index ec07b92..0000000
--- a/src/update-utmp.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
-/***
-  This file is part of systemd.
-
-  Copyright 2010 Lennart Poettering
-
-  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 <assert.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <dbus/dbus.h>
-
-#ifdef HAVE_AUDIT
-#include <libaudit.h>
-#endif
-
-#include "log.h"
-#include "macro.h"
-#include "util.h"
-#include "special.h"
-#include "utmp-wtmp.h"
-#include "dbus-common.h"
-
-typedef struct Context {
-        DBusConnection *bus;
-#ifdef HAVE_AUDIT
-        int audit_fd;
-#endif
-} Context;
-
-static usec_t get_startup_time(Context *c) {
-        const char
-                *interface = "org.freedesktop.systemd1.Manager",
-                *property = "StartupTimestamp";
-
-        DBusError error;
-        usec_t t = 0;
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusMessageIter iter, sub;
-
-        dbus_error_init(&error);
-
-        assert(c);
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.DBus.Properties",
-                              "Get"))) {
-                log_error("Could not allocate message.");
-                goto finish;
-        }
-
-        if (!dbus_message_append_args(m,
-                                      DBUS_TYPE_STRING, &interface,
-                                      DBUS_TYPE_STRING, &property,
-                                      DBUS_TYPE_INVALID)) {
-                log_error("Could not append arguments to message.");
-                goto finish;
-        }
-
-        if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
-                log_error("Failed to send command: %s", bus_error_message(&error));
-                goto finish;
-        }
-
-        if (!dbus_message_iter_init(reply, &iter) ||
-            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
-                log_error("Failed to parse reply.");
-                goto finish;
-        }
-
-        dbus_message_iter_recurse(&iter, &sub);
-
-        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64)  {
-                log_error("Failed to parse reply.");
-                goto finish;
-        }
-
-        dbus_message_iter_get_basic(&sub, &t);
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return t;
-}
-
-static int get_current_runlevel(Context *c) {
-        static const struct {
-                const int runlevel;
-                const char *special;
-        } table[] = {
-                /* The first target of this list that is active or has
-                 * a job scheduled wins. We prefer runlevels 5 and 3
-                 * here over the others, since these are the main
-                 * runlevels used on Fedora. It might make sense to
-                 * change the order on some distributions. */
-                { '5', SPECIAL_RUNLEVEL5_TARGET },
-                { '3', SPECIAL_RUNLEVEL3_TARGET },
-                { '4', SPECIAL_RUNLEVEL4_TARGET },
-                { '2', SPECIAL_RUNLEVEL2_TARGET },
-                { 'S', SPECIAL_RESCUE_TARGET },
-        };
-        const char
-                *interface = "org.freedesktop.systemd1.Unit",
-                *property = "ActiveState";
-
-        DBusMessage *m = NULL, *reply = NULL;
-        int r = 0;
-        unsigned i;
-        DBusError error;
-
-        assert(c);
-
-        dbus_error_init(&error);
-
-        for (i = 0; i < ELEMENTSOF(table); i++) {
-                const char *path = NULL, *state;
-                DBusMessageIter iter, sub;
-
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      "/org/freedesktop/systemd1",
-                                      "org.freedesktop.systemd1.Manager",
-                                      "GetUnit"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &table[i].special,
-                                              DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
-                        dbus_error_free(&error);
-                        continue;
-                }
-
-                if (!dbus_message_get_args(reply, &error,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_unref(m);
-                if (!(m = dbus_message_new_method_call(
-                                      "org.freedesktop.systemd1",
-                                      path,
-                                      "org.freedesktop.DBus.Properties",
-                                      "Get"))) {
-                        log_error("Could not allocate message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &interface,
-                                              DBUS_TYPE_STRING, &property,
-                                              DBUS_TYPE_INVALID)) {
-                        log_error("Could not append arguments to message.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                dbus_message_unref(reply);
-                if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
-                        log_error("Failed to send command: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                if (!dbus_message_iter_init(reply, &iter) ||
-                    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_recurse(&iter, &sub);
-
-                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
-                        log_error("Failed to parse reply.");
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_message_iter_get_basic(&sub, &state);
-
-                if (streq(state, "active") || streq(state, "reloading"))
-                        r = table[i].runlevel;
-
-                dbus_message_unref(m);
-                dbus_message_unref(reply);
-                m = reply = NULL;
-
-                if (r)
-                        break;
-        }
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int on_reboot(Context *c) {
-        int r = 0, q;
-        usec_t t;
-
-        assert(c);
-
-        /* We finished start-up, so let's write the utmp
-         * record and send the audit msg */
-
-#ifdef HAVE_AUDIT
-        if (c->audit_fd >= 0)
-                if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "init", NULL, NULL, NULL, 1) < 0) {
-                        log_error("Failed to send audit message: %m");
-                        r = -errno;
-                }
-#endif
-
-        /* If this call fails it will return 0, which
-         * utmp_put_reboot() will then fix to the current time */
-        t = get_startup_time(c);
-
-        if ((q = utmp_put_reboot(t)) < 0) {
-                log_error("Failed to write utmp record: %s", strerror(-q));
-                r = q;
-        }
-
-        return r;
-}
-
-static int on_shutdown(Context *c) {
-        int r = 0, q;
-
-        assert(c);
-
-        /* We started shut-down, so let's write the utmp
-         * record and send the audit msg */
-
-#ifdef HAVE_AUDIT
-        if (c->audit_fd >= 0)
-                if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "init", NULL, NULL, NULL, 1) < 0) {
-                        log_error("Failed to send audit message: %m");
-                        r = -errno;
-                }
-#endif
-
-        if ((q = utmp_put_shutdown()) < 0) {
-                log_error("Failed to write utmp record: %s", strerror(-q));
-                r = q;
-        }
-
-        return r;
-}
-
-static int on_runlevel(Context *c) {
-        int r = 0, q, previous, runlevel;
-
-        assert(c);
-
-        /* We finished changing runlevel, so let's write the
-         * utmp record and send the audit msg */
-
-        /* First, get last runlevel */
-        if ((q = utmp_get_runlevel(&previous, NULL)) < 0) {
-
-                if (q != -ESRCH && q != -ENOENT) {
-                        log_error("Failed to get current runlevel: %s", strerror(-q));
-                        return q;
-                }
-
-                /* Hmm, we didn't find any runlevel, that means we
-                 * have been rebooted */
-                r = on_reboot(c);
-                previous = 0;
-        }
-
-        /* Secondly, get new runlevel */
-        if ((runlevel = get_current_runlevel(c)) < 0)
-                return runlevel;
-
-        if (previous == runlevel)
-                return 0;
-
-#ifdef HAVE_AUDIT
-        if (c->audit_fd >= 0) {
-                char *s = NULL;
-
-                if (asprintf(&s, "old-level=%c new-level=%c",
-                             previous > 0 ? previous : 'N',
-                             runlevel > 0 ? runlevel : 'N') < 0)
-                        return -ENOMEM;
-
-                if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0) {
-                        log_error("Failed to send audit message: %m");
-                        r = -errno;
-                }
-
-                free(s);
-        }
-#endif
-
-        if ((q = utmp_put_runlevel(runlevel, previous)) < 0) {
-                log_error("Failed to write utmp record: %s", strerror(-q));
-                r = q;
-        }
-
-        return r;
-}
-
-int main(int argc, char *argv[]) {
-        int r;
-        DBusError error;
-        Context c;
-
-        dbus_error_init(&error);
-
-        zero(c);
-#ifdef HAVE_AUDIT
-        c.audit_fd = -1;
-#endif
-
-        if (getppid() != 1) {
-                log_error("This program should be invoked by init only.");
-                return EXIT_FAILURE;
-        }
-
-        if (argc != 2) {
-                log_error("This program requires one argument.");
-                return EXIT_FAILURE;
-        }
-
-        log_set_target(LOG_TARGET_AUTO);
-        log_parse_environment();
-        log_open();
-
-        umask(0022);
-
-#ifdef HAVE_AUDIT
-        if ((c.audit_fd = audit_open()) < 0 &&
-            /* If the kernel lacks netlink or audit support,
-             * don't worry about it. */
-            errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
-                log_error("Failed to connect to audit log: %m");
-#endif
-
-        if (bus_connect(DBUS_BUS_SYSTEM, &c.bus, NULL, &error) < 0) {
-                log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
-                r = -EIO;
-                goto finish;
-        }
-
-        log_debug("systemd-update-utmp running as pid %lu", (unsigned long) getpid());
-
-        if (streq(argv[1], "reboot"))
-                r = on_reboot(&c);
-        else if (streq(argv[1], "shutdown"))
-                r = on_shutdown(&c);
-        else if (streq(argv[1], "runlevel"))
-                r = on_runlevel(&c);
-        else {
-                log_error("Unknown command %s", argv[1]);
-                r = -EINVAL;
-        }
-
-        log_debug("systemd-update-utmp stopped as pid %lu", (unsigned long) getpid());
-
-finish:
-#ifdef HAVE_AUDIT
-        if (c.audit_fd >= 0)
-                audit_close(c.audit_fd);
-#endif
-
-        if (c.bus) {
-                dbus_connection_flush(c.bus);
-                dbus_connection_close(c.bus);
-                dbus_connection_unref(c.bus);
-        }
-
-        dbus_error_free(&error);
-        dbus_shutdown();
-
-        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
-}
diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c
new file mode 100644
index 0000000..ec07b92
--- /dev/null
+++ b/src/update-utmp/update-utmp.c
@@ -0,0 +1,423 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+  This file is part of systemd.
+
+  Copyright 2010 Lennart Poettering
+
+  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 <assert.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <dbus/dbus.h>
+
+#ifdef HAVE_AUDIT
+#include <libaudit.h>
+#endif
+
+#include "log.h"
+#include "macro.h"
+#include "util.h"
+#include "special.h"
+#include "utmp-wtmp.h"
+#include "dbus-common.h"
+
+typedef struct Context {
+        DBusConnection *bus;
+#ifdef HAVE_AUDIT
+        int audit_fd;
+#endif
+} Context;
+
+static usec_t get_startup_time(Context *c) {
+        const char
+                *interface = "org.freedesktop.systemd1.Manager",
+                *property = "StartupTimestamp";
+
+        DBusError error;
+        usec_t t = 0;
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusMessageIter iter, sub;
+
+        dbus_error_init(&error);
+
+        assert(c);
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.DBus.Properties",
+                              "Get"))) {
+                log_error("Could not allocate message.");
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &interface,
+                                      DBUS_TYPE_STRING, &property,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
+                log_error("Failed to send command: %s", bus_error_message(&error));
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
+                log_error("Failed to parse reply.");
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT64)  {
+                log_error("Failed to parse reply.");
+                goto finish;
+        }
+
+        dbus_message_iter_get_basic(&sub, &t);
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return t;
+}
+
+static int get_current_runlevel(Context *c) {
+        static const struct {
+                const int runlevel;
+                const char *special;
+        } table[] = {
+                /* The first target of this list that is active or has
+                 * a job scheduled wins. We prefer runlevels 5 and 3
+                 * here over the others, since these are the main
+                 * runlevels used on Fedora. It might make sense to
+                 * change the order on some distributions. */
+                { '5', SPECIAL_RUNLEVEL5_TARGET },
+                { '3', SPECIAL_RUNLEVEL3_TARGET },
+                { '4', SPECIAL_RUNLEVEL4_TARGET },
+                { '2', SPECIAL_RUNLEVEL2_TARGET },
+                { 'S', SPECIAL_RESCUE_TARGET },
+        };
+        const char
+                *interface = "org.freedesktop.systemd1.Unit",
+                *property = "ActiveState";
+
+        DBusMessage *m = NULL, *reply = NULL;
+        int r = 0;
+        unsigned i;
+        DBusError error;
+
+        assert(c);
+
+        dbus_error_init(&error);
+
+        for (i = 0; i < ELEMENTSOF(table); i++) {
+                const char *path = NULL, *state;
+                DBusMessageIter iter, sub;
+
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      "/org/freedesktop/systemd1",
+                                      "org.freedesktop.systemd1.Manager",
+                                      "GetUnit"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, &table[i].special,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
+                        dbus_error_free(&error);
+                        continue;
+                }
+
+                if (!dbus_message_get_args(reply, &error,
+                                           DBUS_TYPE_OBJECT_PATH, &path,
+                                           DBUS_TYPE_INVALID)) {
+                        log_error("Failed to parse reply: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_unref(m);
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      path,
+                                      "org.freedesktop.DBus.Properties",
+                                      "Get"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, &interface,
+                                              DBUS_TYPE_STRING, &property,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                dbus_message_unref(reply);
+                if (!(reply = dbus_connection_send_with_reply_and_block(c->bus, m, -1, &error))) {
+                        log_error("Failed to send command: %s", bus_error_message(&error));
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (!dbus_message_iter_init(reply, &iter) ||
+                    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&iter, &sub);
+
+                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_get_basic(&sub, &state);
+
+                if (streq(state, "active") || streq(state, "reloading"))
+                        r = table[i].runlevel;
+
+                dbus_message_unref(m);
+                dbus_message_unref(reply);
+                m = reply = NULL;
+
+                if (r)
+                        break;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int on_reboot(Context *c) {
+        int r = 0, q;
+        usec_t t;
+
+        assert(c);
+
+        /* We finished start-up, so let's write the utmp
+         * record and send the audit msg */
+
+#ifdef HAVE_AUDIT
+        if (c->audit_fd >= 0)
+                if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_BOOT, "init", NULL, NULL, NULL, 1) < 0) {
+                        log_error("Failed to send audit message: %m");
+                        r = -errno;
+                }
+#endif
+
+        /* If this call fails it will return 0, which
+         * utmp_put_reboot() will then fix to the current time */
+        t = get_startup_time(c);
+
+        if ((q = utmp_put_reboot(t)) < 0) {
+                log_error("Failed to write utmp record: %s", strerror(-q));
+                r = q;
+        }
+
+        return r;
+}
+
+static int on_shutdown(Context *c) {
+        int r = 0, q;
+
+        assert(c);
+
+        /* We started shut-down, so let's write the utmp
+         * record and send the audit msg */
+
+#ifdef HAVE_AUDIT
+        if (c->audit_fd >= 0)
+                if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_SHUTDOWN, "init", NULL, NULL, NULL, 1) < 0) {
+                        log_error("Failed to send audit message: %m");
+                        r = -errno;
+                }
+#endif
+
+        if ((q = utmp_put_shutdown()) < 0) {
+                log_error("Failed to write utmp record: %s", strerror(-q));
+                r = q;
+        }
+
+        return r;
+}
+
+static int on_runlevel(Context *c) {
+        int r = 0, q, previous, runlevel;
+
+        assert(c);
+
+        /* We finished changing runlevel, so let's write the
+         * utmp record and send the audit msg */
+
+        /* First, get last runlevel */
+        if ((q = utmp_get_runlevel(&previous, NULL)) < 0) {
+
+                if (q != -ESRCH && q != -ENOENT) {
+                        log_error("Failed to get current runlevel: %s", strerror(-q));
+                        return q;
+                }
+
+                /* Hmm, we didn't find any runlevel, that means we
+                 * have been rebooted */
+                r = on_reboot(c);
+                previous = 0;
+        }
+
+        /* Secondly, get new runlevel */
+        if ((runlevel = get_current_runlevel(c)) < 0)
+                return runlevel;
+
+        if (previous == runlevel)
+                return 0;
+
+#ifdef HAVE_AUDIT
+        if (c->audit_fd >= 0) {
+                char *s = NULL;
+
+                if (asprintf(&s, "old-level=%c new-level=%c",
+                             previous > 0 ? previous : 'N',
+                             runlevel > 0 ? runlevel : 'N') < 0)
+                        return -ENOMEM;
+
+                if (audit_log_user_message(c->audit_fd, AUDIT_SYSTEM_RUNLEVEL, s, NULL, NULL, NULL, 1) < 0) {
+                        log_error("Failed to send audit message: %m");
+                        r = -errno;
+                }
+
+                free(s);
+        }
+#endif
+
+        if ((q = utmp_put_runlevel(runlevel, previous)) < 0) {
+                log_error("Failed to write utmp record: %s", strerror(-q));
+                r = q;
+        }
+
+        return r;
+}
+
+int main(int argc, char *argv[]) {
+        int r;
+        DBusError error;
+        Context c;
+
+        dbus_error_init(&error);
+
+        zero(c);
+#ifdef HAVE_AUDIT
+        c.audit_fd = -1;
+#endif
+
+        if (getppid() != 1) {
+                log_error("This program should be invoked by init only.");
+                return EXIT_FAILURE;
+        }
+
+        if (argc != 2) {
+                log_error("This program requires one argument.");
+                return EXIT_FAILURE;
+        }
+
+        log_set_target(LOG_TARGET_AUTO);
+        log_parse_environment();
+        log_open();
+
+        umask(0022);
+
+#ifdef HAVE_AUDIT
+        if ((c.audit_fd = audit_open()) < 0 &&
+            /* If the kernel lacks netlink or audit support,
+             * don't worry about it. */
+            errno != EAFNOSUPPORT && errno != EPROTONOSUPPORT)
+                log_error("Failed to connect to audit log: %m");
+#endif
+
+        if (bus_connect(DBUS_BUS_SYSTEM, &c.bus, NULL, &error) < 0) {
+                log_error("Failed to get D-Bus connection: %s", bus_error_message(&error));
+                r = -EIO;
+                goto finish;
+        }
+
+        log_debug("systemd-update-utmp running as pid %lu", (unsigned long) getpid());
+
+        if (streq(argv[1], "reboot"))
+                r = on_reboot(&c);
+        else if (streq(argv[1], "shutdown"))
+                r = on_shutdown(&c);
+        else if (streq(argv[1], "runlevel"))
+                r = on_runlevel(&c);
+        else {
+                log_error("Unknown command %s", argv[1]);
+                r = -EINVAL;
+        }
+
+        log_debug("systemd-update-utmp stopped as pid %lu", (unsigned long) getpid());
+
+finish:
+#ifdef HAVE_AUDIT
+        if (c.audit_fd >= 0)
+                audit_close(c.audit_fd);
+#endif
+
+        if (c.bus) {
+                dbus_connection_flush(c.bus);
+                dbus_connection_close(c.bus);
+                dbus_connection_unref(c.bus);
+        }
+
+        dbus_error_free(&error);
+        dbus_shutdown();
+
+        return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}



More information about the systemd-commits mailing list