chiark / gitweb /
systemctl: fix parsing of LoadError property for systemctl show
[elogind.git] / src / systemctl.c
index f5a87fc701c254bf2f9e613a76f386a3195f3bcc..bb998d37e21b4c4e313adc065f069dd8231ae973 100644 (file)
@@ -33,7 +33,6 @@
 #include <sys/stat.h>
 #include <stddef.h>
 #include <sys/prctl.h>
-
 #include <dbus/dbus.h>
 
 #include "log.h"
 #include "bus-errors.h"
 #include "build.h"
 #include "unit-name.h"
+#include "pager.h"
+#include "spawn-agent.h"
+#include "install.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 bool arg_user = false;
-static bool arg_global = false;
+static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
 static bool arg_immediate = false;
 static bool arg_no_block = false;
 static bool arg_no_pager = false;
@@ -74,13 +75,14 @@ static bool arg_dry = false;
 static bool arg_quiet = false;
 static bool arg_full = false;
 static bool arg_force = false;
-static bool arg_defaults = false;
 static bool arg_ask_password = false;
 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,
@@ -108,14 +110,16 @@ static enum dot {
         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 private_bus = false;
 
-static pid_t pager_pid = 0;
-static pid_t agent_pid = 0;
-
-static int daemon_reload(DBusConnection *bus, char **args, unsigned n);
-static void pager_open(void);
+static int daemon_reload(DBusConnection *bus, char **args);
 
 static bool on_tty(void) {
         static int t = -1;
@@ -132,80 +136,26 @@ static bool on_tty(void) {
         return t;
 }
 
-static void spawn_ask_password_agent(void) {
-        pid_t parent;
-
-        if (agent_pid > 0)
-                return;
-
-        /* We check STDIN here, not STDOUT, since this is about input,
-         * not output */
-        if (!isatty(STDIN_FILENO))
-                return;
+static void pager_open_if_enabled(void) {
+        on_tty();
 
-        if (!arg_ask_password)
+        if (arg_no_pager)
                 return;
 
-        if (arg_user)
-                return;
+        pager_open();
+}
 
-        parent = getpid();
+static void agent_open_if_enabled(void) {
 
-        /* Spawns a temporary TTY agent, making sure it goes away when
-         * we go away */
+        /* Open the password agent as a child process if necessary */
 
-        if ((agent_pid = fork()) < 0)
+        if (!arg_ask_password)
                 return;
 
-        if (agent_pid == 0) {
-                /* In the child */
-
-                int fd;
-                bool stdout_is_tty, stderr_is_tty;
-
-                /* Make sure the agent goes away when the parent dies */
-                if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
-                        _exit(EXIT_FAILURE);
-
-                /* Check whether our parent died before we were able
-                 * to set the death signal */
-                if (getppid() != parent)
-                        _exit(EXIT_SUCCESS);
-
-                /* Don't leak fds to the agent */
-                close_all_fds(NULL, 0);
-
-                stdout_is_tty = isatty(STDOUT_FILENO);
-                stderr_is_tty = isatty(STDERR_FILENO);
-
-                if (!stdout_is_tty || !stderr_is_tty) {
-                        /* Detach from stdout/stderr. and reopen
-                         * /dev/tty for them. This is important to
-                         * ensure that when systemctl is started via
-                         * popen() or a similar call that expects to
-                         * read EOF we actually do generate EOF and
-                         * not delay this indefinitely by because we
-                         * keep an unused copy of stdin around. */
-                        if ((fd = open("/dev/tty", O_WRONLY)) < 0) {
-                                log_error("Failed to open /dev/tty: %m");
-                                _exit(EXIT_FAILURE);
-                        }
-
-                        if (!stdout_is_tty)
-                                dup2(fd, STDOUT_FILENO);
-
-                        if (!stderr_is_tty)
-                                dup2(fd, STDERR_FILENO);
-
-                        if (fd > 2)
-                                close(fd);
-                }
-
-                execl(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
+        if (arg_scope != UNIT_FILE_SYSTEM)
+                return;
 
-                log_error("Unable to execute agent: %m");
-                _exit(EXIT_FAILURE);
-        }
+        agent_open();
 }
 
 static const char *ansi_highlight(bool b) {
@@ -267,22 +217,6 @@ static int translate_bus_error_to_exit_status(int r, const DBusError *error) {
         return EXIT_FAILURE;
 }
 
-static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
-
-        assert(iter);
-        assert(data);
-
-        if (dbus_message_iter_get_arg_type(iter) != type)
-                return -EIO;
-
-        dbus_message_iter_get_basic(iter, data);
-
-        if (!dbus_message_iter_next(iter) != !next)
-                return -EIO;
-
-        return 0;
-}
-
 static void warn_wall(enum action action) {
         static const char *table[_ACTION_MAX] = {
                 [ACTION_HALT]      = "The system is going down for system halt NOW!",
@@ -319,6 +253,23 @@ static void warn_wall(enum action action) {
         utmp_wall(table[action], 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;
@@ -381,7 +332,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
         if (on_tty()) {
                 printf("%-25s %-6s %-*s %-*s %-*s", "UNIT", "LOAD",
                        active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
-                if (columns() >= 80+12 || arg_full)
+                if (columns() >= 80+12 || arg_full || !arg_no_pager)
                         printf(" %s\n", "DESCRIPTION");
                 else
                         printf("\n");
@@ -434,7 +385,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
                         if (u->job_id == 0)
                                 printf(" %-*s", job_len, "");
 
-                        if (arg_full)
+                        if (arg_full || !arg_no_pager)
                                 printf(" %s", u->description);
                         else
                                 printf(" %.*s", columns() - a - b - 1, u->description);
@@ -456,7 +407,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
         }
 }
 
-static int list_units(DBusConnection *bus, char **args, unsigned n) {
+static int list_units(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -468,7 +419,7 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
 
         assert(bus);
 
-        pager_open();
+        pager_open_if_enabled();
 
         if (!(m = dbus_message_new_method_call(
                               "org.freedesktop.systemd1",
@@ -563,6 +514,210 @@ finish:
         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 n_shown = 0;
+        const UnitFileList *u;
+
+        if (on_tty())
+                printf("%-25s %-6s\n", "UNIT FILE", "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(true);
+                        off = ansi_highlight(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, 25, 33);
+
+                printf("%-25s %s%-6s%s\n",
+                       e ? e : id,
+                       on, unit_file_state_to_string(u->state), off);
+
+                free(e);
+        }
+
+        if (on_tty())
+                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);
+
+        assert(bus);
+
+        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) {
+                        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 {
+                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\"]",
@@ -715,7 +870,7 @@ finish:
         return r;
 }
 
-static int dot(DBusConnection *bus, char **args, unsigned n) {
+static int dot(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -789,7 +944,7 @@ static int dot(DBusConnection *bus, char **args, unsigned n) {
                  "                 red       = Conflicts\n"
                  "                 green     = After\n");
 
-        if (isatty(fileno(stdout)))
+        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");
 
@@ -807,7 +962,7 @@ finish:
         return r;
 }
 
-static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
+static int list_jobs(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -818,7 +973,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
 
         assert(bus);
 
-        pager_open();
+        pager_open_if_enabled();
 
         if (!(m = dbus_message_new_method_call(
                               "org.freedesktop.systemd1",
@@ -845,7 +1000,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
 
         dbus_message_iter_recurse(&iter, &sub);
 
-        if (isatty(STDOUT_FILENO))
+        if (on_tty())
                 printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
 
         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
@@ -881,7 +1036,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
                 dbus_message_iter_next(&sub);
         }
 
-        if (isatty(STDOUT_FILENO))
+        if (on_tty())
                 printf("\n%u jobs listed.\n", k);
 
         r = 0;
@@ -898,18 +1053,18 @@ finish:
         return r;
 }
 
-static int load_unit(DBusConnection *bus, char **args, unsigned n) {
+static int load_unit(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
-        unsigned i;
+        char **name;
 
         dbus_error_init(&error);
 
         assert(bus);
         assert(args);
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
 
                 if (!(m = dbus_message_new_method_call(
                                       "org.freedesktop.systemd1",
@@ -922,7 +1077,7 @@ static int load_unit(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &args[i],
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
@@ -955,21 +1110,21 @@ finish:
         return r;
 }
 
-static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
+static int cancel_job(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
-        unsigned i;
+        char **name;
 
         dbus_error_init(&error);
 
         assert(bus);
         assert(args);
 
-        if (n <= 1)
-                return daemon_reload(bus, args, n);
+        if (strv_length(args) <= 1)
+                return daemon_reload(bus, args);
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
                 unsigned id;
                 const char *path;
 
@@ -983,7 +1138,7 @@ static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
                         goto finish;
                 }
 
-                if ((r = safe_atou(args[i], &id)) < 0) {
+                if ((r = safe_atou(*name, &id)) < 0) {
                         log_error("Failed to parse job id: %s", strerror(-r));
                         goto finish;
                 }
@@ -1331,8 +1486,8 @@ static int start_unit_one(
         }
 
         if (need_daemon_reload(bus, name))
-                log_warning("Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
-                            arg_user ? "--user" : "--system");
+                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;
@@ -1383,7 +1538,7 @@ static enum action verb_to_action(const char *verb) {
                 return ACTION_INVALID;
 }
 
-static int start_unit(DBusConnection *bus, char **args, unsigned n) {
+static int start_unit(DBusConnection *bus, char **args) {
 
         static const char * const table[_ACTION_MAX] = {
                 [ACTION_HALT] = SPECIAL_HALT_TARGET,
@@ -1401,20 +1556,21 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
         };
 
         int r, ret = 0;
-        unsigned i;
         const char *method, *mode, *one_name;
         Set *s = NULL;
         DBusError error;
+        char **name;
 
         dbus_error_init(&error);
 
         assert(bus);
 
-        spawn_ask_password_agent();
+        agent_open_if_enabled();
 
         if (arg_action == ACTION_SYSTEMCTL) {
                 method =
-                        streq(args[0], "stop")                  ? "StopUnit" :
+                        streq(args[0], "stop") ||
+                        streq(args[0], "condstop")              ? "StopUnit" :
                         streq(args[0], "reload")                ? "ReloadUnit" :
                         streq(args[0], "restart")               ? "RestartUnit" :
 
@@ -1469,8 +1625,8 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
                 if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0)
                         goto finish;
         } else {
-                for (i = 1; i < n; i++)
-                        if ((r = start_unit_one(bus, method, args[i], mode, &error, s)) != 0) {
+                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);
                         }
@@ -1491,7 +1647,7 @@ finish:
         return ret;
 }
 
-static int start_special(DBusConnection *bus, char **args, unsigned n) {
+static int start_special(DBusConnection *bus, char **args) {
         int r;
 
         assert(bus);
@@ -1503,9 +1659,9 @@ static int start_special(DBusConnection *bus, char **args, unsigned n) {
              streq(args[0], "reboot") ||
              streq(args[0], "kexec") ||
              streq(args[0], "exit")))
-                return daemon_reload(bus, args, n);
+                return daemon_reload(bus, args);
 
-        r = start_unit(bus, args, n);
+        r = start_unit(bus, args);
 
         if (r >= 0)
                 warn_wall(verb_to_action(args[0]));
@@ -1513,21 +1669,21 @@ static int start_special(DBusConnection *bus, char **args, unsigned n) {
         return r;
 }
 
-static int check_unit(DBusConnection *bus, char **args, unsigned n) {
+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;
-        unsigned i;
+        char **name;
 
         assert(bus);
         assert(args);
 
         dbus_error_init(&error);
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
                 const char *path = NULL;
                 const char *state;
                 DBusMessageIter iter, sub;
@@ -1543,7 +1699,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &args[i],
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
@@ -1558,6 +1714,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
 
                         dbus_error_free(&error);
                         dbus_message_unref(m);
+                        m = NULL;
                         continue;
                 }
 
@@ -1636,11 +1793,11 @@ finish:
         return r;
 }
 
-static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
+static int kill_unit(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         int r = 0;
         DBusError error;
-        unsigned i;
+        char **name;
 
         assert(bus);
         assert(args);
@@ -1653,7 +1810,7 @@ static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
         if (!arg_kill_mode)
                 arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
 
                 if (!(m = dbus_message_new_method_call(
                                       "org.freedesktop.systemd1",
@@ -1666,7 +1823,7 @@ static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &args[i],
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_STRING, &arg_kill_who,
                                               DBUS_TYPE_STRING, &arg_kill_mode,
                                               DBUS_TYPE_INT32, &arg_signal,
@@ -1728,7 +1885,7 @@ static void exec_status_info_free(ExecStatusInfo *i) {
 }
 
 static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) {
-        uint64_t start_timestamp, exit_timestamp;
+        uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
         DBusMessageIter sub2, sub3;
         const char*path;
         unsigned n;
@@ -1782,7 +1939,9 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i)
         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)
@@ -1803,6 +1962,7 @@ typedef struct UnitStatusInfo {
         const char *load_state;
         const char *active_state;
         const char *sub_state;
+        const char *unit_file_state;
 
         const char *description;
         const char *following;
@@ -1810,6 +1970,8 @@ typedef struct UnitStatusInfo {
         const char *path;
         const char *default_control_group;
 
+        const char *load_error;
+
         usec_t inactive_exit_timestamp;
         usec_t active_enter_timestamp;
         usec_t active_exit_timestamp;
@@ -1831,6 +1993,9 @@ typedef struct UnitStatusInfo {
 
         int exit_code, exit_status;
 
+        usec_t condition_timestamp;
+        bool condition_result;
+
         /* Socket */
         unsigned n_accepted;
         unsigned n_connections;
@@ -1877,7 +2042,11 @@ static void print_status_info(UnitStatusInfo *i) {
         } else
                 on = off = "";
 
-        if (i->path)
+        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);
@@ -1922,6 +2091,16 @@ static void print_status_info(UnitStatusInfo *i) {
         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)
@@ -2048,23 +2227,29 @@ static void print_status_info(UnitStatusInfo *i) {
 
                 printf("\t  CGroup: %s\n", i->default_control_group);
 
-                if ((c = columns()) > 18)
-                        c -= 18;
-                else
-                        c = 0;
+                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);
+                        show_cgroup_by_path(i->default_control_group, "\t\t  ", c);
+                }
         }
 
         if (i->need_daemon_reload)
                 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
                        ansi_highlight(true),
                        ansi_highlight(false),
-                       arg_user ? "--user" : "--system");
+                       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: {
@@ -2072,7 +2257,7 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
 
                 dbus_message_iter_get_basic(iter, &s);
 
-                if (s[0]) {
+                if (!isempty(s)) {
                         if (streq(name, "Id"))
                                 i->id = s;
                         else if (streq(name, "LoadState"))
@@ -2103,6 +2288,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 i->what = s;
                         else if (streq(name, "Following"))
                                 i->following = s;
+                        else if (streq(name, "UnitFileState"))
+                                i->unit_file_state = s;
                 }
 
                 break;
@@ -2117,6 +2304,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                         i->accept = b;
                 else if (streq(name, "NeedDaemonReload"))
                         i->need_daemon_reload = b;
+                else if (streq(name, "ConditionResult"))
+                        i->condition_result = b;
 
                 break;
         }
@@ -2174,6 +2363,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                         i->inactive_exit_timestamp = (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;
         }
@@ -2210,6 +2401,30 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
 
                 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;
@@ -2227,74 +2442,6 @@ static int print_property(const char *name, DBusMessageIter *iter) {
 
         switch (dbus_message_iter_get_arg_type(iter)) {
 
-        case DBUS_TYPE_STRING: {
-                const char *s;
-                dbus_message_iter_get_basic(iter, &s);
-
-                if (arg_all || s[0])
-                        printf("%s=%s\n", name, s);
-
-                return 0;
-        }
-
-        case DBUS_TYPE_BOOLEAN: {
-                dbus_bool_t b;
-                dbus_message_iter_get_basic(iter, &b);
-                printf("%s=%s\n", name, yes_no(b));
-
-                return 0;
-        }
-
-        case DBUS_TYPE_UINT64: {
-                uint64_t u;
-                dbus_message_iter_get_basic(iter, &u);
-
-                /* Yes, heuristics! But we can change this check
-                 * should it turn out to not be sufficient */
-
-                if (strstr(name, "Timestamp")) {
-                        char timestamp[FORMAT_TIMESTAMP_MAX], *t;
-
-                        if ((t = format_timestamp(timestamp, sizeof(timestamp), u)) || arg_all)
-                                printf("%s=%s\n", name, strempty(t));
-                } else if (strstr(name, "USec")) {
-                        char timespan[FORMAT_TIMESPAN_MAX];
-
-                        printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
-                } else
-                        printf("%s=%llu\n", name, (unsigned long long) u);
-
-                return 0;
-        }
-
-        case DBUS_TYPE_UINT32: {
-                uint32_t u;
-                dbus_message_iter_get_basic(iter, &u);
-
-                if (strstr(name, "UMask") || strstr(name, "Mode"))
-                        printf("%s=%04o\n", name, u);
-                else
-                        printf("%s=%u\n", name, (unsigned) u);
-
-                return 0;
-        }
-
-        case DBUS_TYPE_INT32: {
-                int32_t i;
-                dbus_message_iter_get_basic(iter, &i);
-
-                printf("%s=%i\n", name, (int) i);
-                return 0;
-        }
-
-        case DBUS_TYPE_DOUBLE: {
-                double d;
-                dbus_message_iter_get_basic(iter, &d);
-
-                printf("%s=%g\n", name, d);
-                return 0;
-        }
-
         case DBUS_TYPE_STRUCT: {
                 DBusMessageIter sub;
                 dbus_message_iter_recurse(iter, &sub);
@@ -2318,6 +2465,16 @@ static int print_property(const char *name, DBusMessageIter *iter) {
                         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;
                 }
 
@@ -2326,67 +2483,19 @@ static int print_property(const char *name, DBusMessageIter *iter) {
 
         case DBUS_TYPE_ARRAY:
 
-                if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
-                        DBusMessageIter sub;
-                        bool space = false;
+                if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "EnvironmentFiles")) {
+                        DBusMessageIter sub, sub2;
 
                         dbus_message_iter_recurse(iter, &sub);
-                        if (arg_all ||
-                            dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                                printf("%s=", name);
-
-                                while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                                        const char *s;
+                        while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
+                                const char *path;
+                                dbus_bool_t ignore;
 
-                                        assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
-                                        dbus_message_iter_get_basic(&sub, &s);
-                                        printf("%s%s", space ? " " : "", s);
+                                dbus_message_iter_recurse(&sub, &sub2);
 
-                                        space = true;
-                                        dbus_message_iter_next(&sub);
-                                }
-
-                                puts("");
-                        }
-
-                        return 0;
-
-                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
-                        DBusMessageIter sub;
-
-                        dbus_message_iter_recurse(iter, &sub);
-                        if (arg_all ||
-                            dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                                printf("%s=", name);
-
-                                while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                                        uint8_t u;
-
-                                        assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
-                                        dbus_message_iter_get_basic(&sub, &u);
-                                        printf("%02x", u);
-
-                                        dbus_message_iter_next(&sub);
-                                }
-
-                                puts("");
-                        }
-
-                        return 0;
-
-                } else 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=%s)\n", path, yes_no(ignore));
+                                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);
                         }
@@ -2451,7 +2560,7 @@ static int print_property(const char *name, DBusMessageIter *iter) {
 
                                         t = strv_join(info.argv, " ");
 
-                                        printf("%s={ path=%s ; argv[]=%s ; ignore=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
+                                        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),
@@ -2479,6 +2588,9 @@ static int print_property(const char *name, DBusMessageIter *iter) {
                 break;
         }
 
+        if (generic_print_property(name, iter, arg_all) > 0)
+                return 0;
+
         if (arg_all)
                 printf("%s=[unprintable]\n", name);
 
@@ -2607,12 +2719,12 @@ finish:
         return r;
 }
 
-static int show(DBusConnection *bus, char **args, unsigned n) {
+static int show(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         int r, ret = 0;
         DBusError error;
-        unsigned i;
         bool show_properties, new_line = false;
+        char **name;
 
         assert(bus);
         assert(args);
@@ -2622,9 +2734,9 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
         show_properties = !streq(args[0], "status");
 
         if (show_properties)
-                pager_open();
+                pager_open_if_enabled();
 
-        if (show_properties && n <= 1) {
+        if (show_properties && strv_length(args) <= 1) {
                 /* If not argument is specified inspect the manager
                  * itself */
 
@@ -2632,11 +2744,11 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                 goto finish;
         }
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
                 const char *path = NULL;
                 uint32_t id;
 
-                if (safe_atou32(args[i], &id) < 0) {
+                if (safe_atou32(*name, &id) < 0) {
 
                         /* Interpret as unit name */
 
@@ -2651,7 +2763,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                         }
 
                         if (!dbus_message_append_args(m,
-                                                      DBUS_TYPE_STRING, &args[i],
+                                                      DBUS_TYPE_STRING, name,
                                                       DBUS_TYPE_INVALID)) {
                                 log_error("Could not append arguments to message.");
                                 ret = -ENOMEM;
@@ -2680,7 +2792,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                 }
 
                                 if (!dbus_message_append_args(m,
-                                                              DBUS_TYPE_STRING, &args[i],
+                                                              DBUS_TYPE_STRING, name,
                                                               DBUS_TYPE_INVALID)) {
                                         log_error("Could not append arguments to message.");
                                         ret = -ENOMEM;
@@ -2782,225 +2894,7 @@ finish:
         return ret;
 }
 
-static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage *message, void *data) {
-        DBusError error;
-        DBusMessage *m = NULL, *reply = NULL;
-
-        assert(connection);
-        assert(message);
-
-        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", "UnitNew") ||
-                   dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
-                const char *id, *path;
-
-                if (!dbus_message_get_args(message, &error,
-                                           DBUS_TYPE_STRING, &id,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_INVALID))
-                        log_error("Failed to parse message: %s", bus_error_message(&error));
-                else if (streq(dbus_message_get_member(message), "UnitNew"))
-                        printf("Unit %s added.\n", id);
-                else
-                        printf("Unit %s removed.\n", id);
-
-        } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobNew") ||
-                   dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
-                uint32_t id;
-                const char *path, *result;
-
-                if (!dbus_message_get_args(message, &error,
-                                           DBUS_TYPE_UINT32, &id,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_STRING, &result,
-                                           DBUS_TYPE_INVALID))
-                        log_error("Failed to parse message: %s", bus_error_message(&error));
-                else if (streq(dbus_message_get_member(message), "JobNew"))
-                        printf("Job %u added.\n", id);
-                else
-                        printf("Job %u removed.\n", id);
-
-
-        } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
-
-                const char *path, *interface, *property = "Id";
-                DBusMessageIter iter, sub;
-
-                path = dbus_message_get_path(message);
-
-                if (!dbus_message_get_args(message, &error,
-                                          DBUS_TYPE_STRING, &interface,
-                                          DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse message: %s", bus_error_message(&error));
-                        goto finish;
-                }
-
-                if (!streq(interface, "org.freedesktop.systemd1.Job") &&
-                    !streq(interface, "org.freedesktop.systemd1.Unit"))
-                        goto finish;
-
-                if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              path,
-                              "org.freedesktop.DBus.Properties",
-                              "Get"))) {
-                        log_error("Could not allocate message.");
-                        goto oom;
-                }
-
-                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(connection, m, -1, &error))) {
-                        log_error("Failed to issue method call: %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 (streq(interface, "org.freedesktop.systemd1.Unit")) {
-                        const char *id;
-
-                        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
-                                log_error("Failed to parse reply.");
-                                goto finish;
-                        }
-
-                        dbus_message_iter_get_basic(&sub, &id);
-                        printf("Unit %s changed.\n", id);
-                } else {
-                        uint32_t id;
-
-                        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)  {
-                                log_error("Failed to parse reply.");
-                                goto finish;
-                        }
-
-                        dbus_message_iter_get_basic(&sub, &id);
-                        printf("Job %u changed.\n", id);
-                }
-        }
-
-finish:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
-oom:
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-        return DBUS_HANDLER_RESULT_NEED_MEMORY;
-}
-
-static int monitor(DBusConnection *bus, char **args, unsigned n) {
-        DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
-        int r;
-
-        dbus_error_init(&error);
-
-        if (!private_bus) {
-                dbus_bus_add_match(bus,
-                                   "type='signal',"
-                                   "sender='org.freedesktop.systemd1',"
-                                   "interface='org.freedesktop.systemd1.Manager',"
-                                   "path='/org/freedesktop/systemd1'",
-                                   &error);
-
-                if (dbus_error_is_set(&error)) {
-                        log_error("Failed to add match: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-
-                dbus_bus_add_match(bus,
-                                   "type='signal',"
-                                   "sender='org.freedesktop.systemd1',"
-                                   "interface='org.freedesktop.DBus.Properties',"
-                                   "member='PropertiesChanged'",
-                                   &error);
-
-                if (dbus_error_is_set(&error)) {
-                        log_error("Failed to add match: %s", bus_error_message(&error));
-                        r = -EIO;
-                        goto finish;
-                }
-        }
-
-        if (!dbus_connection_add_filter(bus, monitor_filter, NULL, NULL)) {
-                log_error("Failed to add filter.");
-                r = -ENOMEM;
-                goto finish;
-        }
-
-        if (!(m = dbus_message_new_method_call(
-                              "org.freedesktop.systemd1",
-                              "/org/freedesktop/systemd1",
-                              "org.freedesktop.systemd1.Manager",
-                              "Subscribe"))) {
-                log_error("Could not allocate 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;
-        }
-
-        while (dbus_connection_read_write_dispatch(bus, -1))
-                ;
-
-        r = 0;
-
-finish:
-
-        /* This is slightly dirty, since we don't undo the filter or the matches. */
-
-        if (m)
-                dbus_message_unref(m);
-
-        if (reply)
-                dbus_message_unref(reply);
-
-        dbus_error_free(&error);
-
-        return r;
-}
-
-static int dump(DBusConnection *bus, char **args, unsigned n) {
+static int dump(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -3008,7 +2902,7 @@ static int dump(DBusConnection *bus, char **args, unsigned n) {
 
         dbus_error_init(&error);
 
-        pager_open();
+        pager_open_if_enabled();
 
         if (!(m = dbus_message_new_method_call(
                               "org.freedesktop.systemd1",
@@ -3049,7 +2943,7 @@ finish:
         return r;
 }
 
-static int snapshot(DBusConnection *bus, char **args, unsigned n) {
+static int snapshot(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -3071,7 +2965,7 @@ static int snapshot(DBusConnection *bus, char **args, unsigned n) {
                 return -ENOMEM;
         }
 
-        if (n > 1)
+        if (strv_length(args) > 1)
                 name = args[1];
 
         if (!dbus_message_append_args(m,
@@ -3156,18 +3050,18 @@ finish:
         return r;
 }
 
-static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
+static int delete_snapshot(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         int r;
         DBusError error;
-        unsigned i;
+        char **name;
 
         assert(bus);
         assert(args);
 
         dbus_error_init(&error);
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
                 const char *path = NULL;
 
                 if (!(m = dbus_message_new_method_call(
@@ -3181,7 +3075,7 @@ static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, &args[i],
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
@@ -3239,7 +3133,7 @@ finish:
         return r;
 }
 
-static int daemon_reload(DBusConnection *bus, char **args, unsigned n) {
+static int daemon_reload(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
@@ -3285,6 +3179,13 @@ static int daemon_reload(DBusConnection *bus, char **args, unsigned n) {
                         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;
@@ -3304,19 +3205,19 @@ finish:
         return r;
 }
 
-static int reset_failed(DBusConnection *bus, char **args, unsigned n) {
+static int reset_failed(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
-        unsigned i;
         int r;
         DBusError error;
+        char **name;
 
         assert(bus);
         dbus_error_init(&error);
 
-        if (n <= 1)
-                return daemon_reload(bus, args, n);
+        if (strv_length(args) <= 1)
+                return daemon_reload(bus, args);
 
-        for (i = 1; i < n; i++) {
+        STRV_FOREACH(name, args+1) {
 
                 if (!(m = dbus_message_new_method_call(
                                       "org.freedesktop.systemd1",
@@ -3329,7 +3230,7 @@ static int reset_failed(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!dbus_message_append_args(m,
-                                              DBUS_TYPE_STRING, args + i,
+                                              DBUS_TYPE_STRING, name,
                                               DBUS_TYPE_INVALID)) {
                         log_error("Could not append arguments to message.");
                         r = -ENOMEM;
@@ -3361,7 +3262,7 @@ finish:
         return r;
 }
 
-static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
+static int show_enviroment(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         DBusMessageIter iter, sub, sub2;
@@ -3372,7 +3273,7 @@ static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
 
         dbus_error_init(&error);
 
-        pager_open();
+        pager_open_if_enabled();
 
         if (!(m = dbus_message_new_method_call(
                               "org.freedesktop.systemd1",
@@ -3445,13 +3346,13 @@ finish:
         return r;
 }
 
-static int set_environment(DBusConnection *bus, char **args, unsigned n) {
+static int set_environment(DBusConnection *bus, char **args) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         const char *method;
         DBusMessageIter iter, sub;
-        unsigned i;
+        char **name;
 
         dbus_error_init(&error);
 
@@ -3477,8 +3378,8 @@ static int set_environment(DBusConnection *bus, char **args, unsigned n) {
                 goto finish;
         }
 
-        for (i = 1; i < n; i++)
-                if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &args[i])) {
+        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;
@@ -3510,758 +3411,479 @@ finish:
         return r;
 }
 
-typedef struct {
-        char *name;
-        char *path;
-
-        char **aliases;
-        char **wanted_by;
-} InstallInfo;
-
-static Hashmap *will_install = NULL, *have_installed = NULL;
-static Set *remove_symlinks_to = NULL;
-static unsigned n_symlinks = 0;
-
-static void install_info_free(InstallInfo *i) {
-        assert(i);
-
-        free(i->name);
-        free(i->path);
-        strv_free(i->aliases);
-        strv_free(i->wanted_by);
-        free(i);
-}
-
-static void install_info_hashmap_free(Hashmap *m) {
-        InstallInfo *i;
-
-        while ((i = hashmap_steal_first(m)))
-                install_info_free(i);
-
-        hashmap_free(m);
-}
-
-static int install_info_add(const char *name) {
-        InstallInfo *i;
-        int r;
-
-        assert(will_install);
+static int enable_sysv_units(char **args) {
+        int r = 0;
 
-        if (!unit_name_is_valid_no_type(name, true)) {
-                log_warning("Unit name %s is not a valid unit name.", name);
-                return -EINVAL;
-        }
+#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX))
+        const char *verb = args[0];
+        unsigned f = 1, t = 1;
+        LookupPaths paths;
 
-        if (hashmap_get(have_installed, name) ||
-            hashmap_get(will_install, name))
+        if (arg_scope != UNIT_FILE_SYSTEM)
                 return 0;
 
-        if (!(i = new0(InstallInfo, 1))) {
-                r = -ENOMEM;
-                goto fail;
-        }
-
-        if (!(i->name = strdup(name))) {
-                r = -ENOMEM;
-                goto fail;
-        }
-
-        if ((r = hashmap_put(will_install, i->name, i)) < 0)
-                goto fail;
-
-        return 0;
-
-fail:
-        if (i)
-                install_info_free(i);
-
-        return r;
-}
-
-static int config_parse_also(
-                const char *filename,
-                unsigned line,
-                const char *section,
-                const char *lvalue,
-                const char *rvalue,
-                void *data,
-                void *userdata) {
-
-        char *w;
-        size_t l;
-        char *state;
-
-        assert(filename);
-        assert(lvalue);
-        assert(rvalue);
-
-        FOREACH_WORD_QUOTED(w, l, rvalue, state) {
-                char *n;
-                int r;
-
-                if (!(n = strndup(w, l)))
-                        return -ENOMEM;
-
-                if ((r = install_info_add(n)) < 0) {
-                        log_warning("Cannot install unit %s: %s", n, strerror(-r));
-                        free(n);
-                        return r;
-                }
-
-                free(n);
-        }
-
-        return 0;
-}
-
-static int mark_symlink_for_removal(const char *p) {
-        char *n;
-        int r;
-
-        assert(p);
-        assert(path_is_absolute(p));
-
-        if (!remove_symlinks_to)
+        if (!streq(verb, "enable") &&
+            !streq(verb, "disable") &&
+            !streq(verb, "is-enabled"))
                 return 0;
 
-        if (!(n = strdup(p)))
-                return -ENOMEM;
-
-        path_kill_slashes(n);
-
-        if ((r = set_put(remove_symlinks_to, n)) < 0) {
-                free(n);
-                return r == -EEXIST ? 0 : r;
-        }
-
-        return 0;
-}
-
-static int remove_marked_symlinks_fd(int fd, const char *config_path, const char *root, bool *deleted) {
-        int r = 0;
-        DIR *d;
-        struct dirent *de;
+        /* Processes all SysV units, and reshuffles the array so that
+         * afterwards only the native units remain */
 
-        assert(fd >= 0);
-        assert(root);
-        assert(deleted);
+        zero(paths);
+        r = lookup_paths_init(&paths, MANAGER_SYSTEM, false);
+        if (r < 0)
+                return r;
 
-        if (!(d = fdopendir(fd))) {
-                close_nointr_nofail(fd);
-                return -errno;
-        }
+        r = 0;
 
-        rewinddir(d);
+        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;
 
-        while ((de = readdir(d))) {
-                bool is_dir = false, is_link = false;
+                name = args[f];
 
-                if (ignore_file(de->d_name))
+                if (!endswith(name, ".service"))
                         continue;
 
-                if (de->d_type == DT_LNK)
-                        is_link = true;
-                else if (de->d_type == DT_DIR)
-                        is_dir = true;
-                else if (de->d_type == DT_UNKNOWN) {
-                        struct stat st;
-
-                        if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
-                                log_error("Failed to stat %s/%s: %m", root, de->d_name);
-
-                                if (r == 0)
-                                        r = -errno;
-                                continue;
-                        }
-
-                        is_link = S_ISLNK(st.st_mode);
-                        is_dir = S_ISDIR(st.st_mode);
-                } else
+                if (path_is_absolute(name))
                         continue;
 
-                if (is_dir) {
-                        int nfd, q;
-                        char *p;
+                STRV_FOREACH(k, paths.unit_path) {
+                        p = NULL;
 
-                        if ((nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0) {
-                                log_error("Failed to open %s/%s: %m", root, de->d_name);
-
-                                if (r == 0)
-                                        r = -errno;
-                                continue;
-                        }
+                        if (!isempty(arg_root))
+                                asprintf(&p, "%s/%s/%s", arg_root, *k, name);
+                        else
+                                asprintf(&p, "%s/%s", *k, name);
 
-                        if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
-                                log_error("Failed to allocate directory string.");
-                                close_nointr_nofail(nfd);
+                        if (!p) {
+                                log_error("No memory");
                                 r = -ENOMEM;
-                                break;
+                                goto finish;
                         }
 
-                        /* This will close nfd, regardless whether it succeeds or not */
-                        q = remove_marked_symlinks_fd(nfd, config_path, p, deleted);
+                        found_native = access(p, F_OK) >= 0;
                         free(p);
 
-                        if (r == 0)
-                                r = q;
-
-                } else if (is_link) {
-                        char *p, *dest, *c;
-                        int q;
-
-                        if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
-                                log_error("Failed to allocate symlink string.");
-                                r = -ENOMEM;
+                        if (found_native)
                                 break;
-                        }
-
-                        if ((q = readlink_and_make_absolute(p, &dest)) < 0) {
-                                log_error("Cannot read symlink %s: %s", p, strerror(-q));
-                                free(p);
-
-                                if (r == 0)
-                                        r = q;
-                                continue;
-                        }
-
-                        if ((c = canonicalize_file_name(dest))) {
-                                /* This might fail if the destination
-                                 * is already removed */
-
-                                free(dest);
-                                dest = c;
-                        }
-
-                        path_kill_slashes(dest);
-                        if (set_get(remove_symlinks_to, dest)) {
-
-                                if (!arg_quiet)
-                                        log_info("rm '%s'", p);
-
-                                if (unlink(p) < 0) {
-                                        log_error("Cannot unlink symlink %s: %m", p);
-
-                                        if (r == 0)
-                                                r = -errno;
-                                } else {
-                                        rmdir_parents(p, config_path);
-                                        path_kill_slashes(p);
-
-                                        if (!set_get(remove_symlinks_to, p)) {
-
-                                                if ((r = mark_symlink_for_removal(p)) < 0) {
-                                                        if (r == 0)
-                                                                r = q;
-                                                } else
-                                                        *deleted = true;
-                                        }
-                                }
-                        }
-
-                        free(p);
-                        free(dest);
-                }
-        }
-
-        closedir(d);
-
-        return r;
-}
-
-static int remove_marked_symlinks(const char *config_path) {
-        int fd, r = 0;
-        bool deleted;
-
-        assert(config_path);
-
-        if (set_size(remove_symlinks_to) <= 0)
-                return 0;
-
-        if ((fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0)
-                return -errno;
-
-        do {
-                int q, cfd;
-                deleted = false;
-
-                if ((cfd = dup(fd)) < 0) {
-                        r = -errno;
-                        break;
-                }
-
-                /* This takes possession of cfd and closes it */
-                if ((q = remove_marked_symlinks_fd(cfd, config_path, config_path, &deleted)) < 0) {
-                        if (r == 0)
-                                r = q;
-                }
-        } while (deleted);
-
-        close_nointr_nofail(fd);
-
-        return r;
-}
-
-static int create_symlink(const char *verb, const char *old_path, const char *new_path) {
-        int r;
-
-        assert(old_path);
-        assert(new_path);
-        assert(verb);
-
-        if (streq(verb, "enable")) {
-                char *dest;
-
-                mkdir_parents(new_path, 0755);
-
-                if (symlink(old_path, new_path) >= 0) {
-
-                        if (!arg_quiet)
-                                log_info("ln -s '%s' '%s'", old_path, new_path);
-
-                        return 0;
                 }
 
-                if (errno != EEXIST) {
-                        log_error("Cannot link %s to %s: %m", old_path, new_path);
-                        return -errno;
-                }
-
-                if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
-
-                        if (errno == EINVAL) {
-                                log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
-                                return -EEXIST;
-                        }
-
-                        log_error("readlink() failed: %s", strerror(-r));
-                        return r;
-                }
-
-                if (streq(dest, old_path)) {
-                        free(dest);
-                        return 0;
-                }
-
-                if (!arg_force) {
-                        log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
-                        free(dest);
-                        return -EEXIST;
-                }
-
-                free(dest);
-                unlink(new_path);
-
-                if (!arg_quiet)
-                        log_info("ln -s '%s' '%s'", old_path, new_path);
-
-                if (symlink(old_path, new_path) >= 0)
-                        return 0;
-
-                log_error("Cannot link %s to %s: %m", old_path, new_path);
-                return -errno;
-
-        } else if (streq(verb, "disable")) {
-                char *dest;
-
-                if ((r = mark_symlink_for_removal(old_path)) < 0)
-                        return r;
-
-                if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
-                        if (errno == ENOENT)
-                                return 0;
-
-                        if (errno == EINVAL) {
-                                log_warning("File %s not a symlink, ignoring.", old_path);
-                                return 0;
-                        }
-
-                        log_error("readlink() failed: %s", strerror(-r));
-                        return r;
-                }
-
-                if (!streq(dest, old_path)) {
-                        log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
-                        free(dest);
-                        return 0;
-                }
-
-                free(dest);
-
-                if ((r = mark_symlink_for_removal(new_path)) < 0)
-                        return r;
-
-                if (!arg_quiet)
-                        log_info("rm '%s'", new_path);
-
-                if (unlink(new_path) >= 0)
-                        return 0;
-
-                log_error("Cannot unlink %s: %m", new_path);
-                return -errno;
-
-        } else if (streq(verb, "is-enabled")) {
-                char *dest;
-
-                if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
-
-                        if (errno == ENOENT || errno == EINVAL)
-                                return 0;
-
-                        log_error("readlink() failed: %s", strerror(-r));
-                        return r;
-                }
-
-                if (streq(dest, old_path)) {
-                        free(dest);
-                        return 1;
-                }
-
-                return 0;
-        }
-
-        assert_not_reached("Unknown action.");
-}
-
-static int install_info_symlink_alias(const char *verb, InstallInfo *i, const char *config_path) {
-        char **s;
-        char *alias_path = NULL;
-        int r;
-
-        assert(verb);
-        assert(i);
-        assert(config_path);
-
-        STRV_FOREACH(s, i->aliases) {
-
-                free(alias_path);
-                if (!(alias_path = path_make_absolute(*s, config_path))) {
-                        log_error("Out of memory");
-                        r = -ENOMEM;
-                        goto finish;
-                }
-
-                if ((r = create_symlink(verb, i->path, alias_path)) != 0)
-                        goto finish;
-
-                if (streq(verb, "disable"))
-                        rmdir_parents(alias_path, config_path);
-        }
-        r = 0;
-
-finish:
-        free(alias_path);
-
-        return r;
-}
-
-static int install_info_symlink_wants(const char *verb, InstallInfo *i, const char *config_path) {
-        char **s;
-        char *alias_path = NULL;
-        int r;
-
-        assert(verb);
-        assert(i);
-        assert(config_path);
-
-        STRV_FOREACH(s, i->wanted_by) {
-                if (!unit_name_is_valid_no_type(*s, true)) {
-                        log_error("Invalid name %s.", *s);
-                        r = -EINVAL;
-                        goto finish;
-                }
-
-                free(alias_path);
-                alias_path = NULL;
+                if (found_native)
+                        continue;
 
-                if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
-                        log_error("Out of memory");
+                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;
                 }
 
-                if ((r = create_symlink(verb, i->path, alias_path)) != 0)
-                        goto finish;
+                p[strlen(p) - sizeof(".service") + 1] = 0;
+                found_sysv = access(p, F_OK) >= 0;
 
-                if (streq(verb, "disable"))
-                        rmdir_parents(alias_path, config_path);
-        }
-
-        r = 0;
-
-finish:
-        free(alias_path);
+                if (!found_sysv) {
+                        free(p);
+                        continue;
+                }
 
-        return r;
-}
+                /* Mark this entry, so that we don't try enabling it as native unit */
+                args[f] = (char*) "";
 
-static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo *i, const char *config_path) {
+                log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
 
-        const ConfigItem items[] = {
-                { "Alias",    config_parse_strv, &i->aliases,   "Install" },
-                { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
-                { "Also",     config_parse_also, NULL,          "Install" },
+                if (!isempty(arg_root))
+                        argv[c++] = q = strappend("--root=", arg_root);
 
-                { NULL, NULL, NULL, NULL }
-        };
+                argv[c++] = file_name_from_path(p);
+                argv[c++] =
+                        streq(verb, "enable") ? "on" :
+                        streq(verb, "disable") ? "off" : "--level=5";
+                argv[c] = NULL;
 
-        char **p;
-        char *filename = NULL;
-        FILE *f = NULL;
-        int r;
+                l = strv_join((char**)argv, " ");
+                if (!l) {
+                        log_error("No memory.");
+                        free(q);
+                        free(p);
+                        r = -ENOMEM;
+                        goto finish;
+                }
 
-        assert(paths);
-        assert(i);
+                log_info("Executing %s", l);
+                free(l);
 
-        STRV_FOREACH(p, paths->unit_path) {
-                int fd;
+                pid = fork();
+                if (pid < 0) {
+                        log_error("Failed to fork: %m");
+                        free(p);
+                        free(q);
+                        r = -errno;
+                        goto finish;
+                } else if (pid == 0) {
+                        /* Child */
 
-                if (!(filename = path_make_absolute(i->name, *p))) {
-                        log_error("Out of memory");
-                        return -ENOMEM;
+                        execv(argv[0], (char**) argv);
+                        _exit(EXIT_FAILURE);
                 }
 
-                /* Ensure that we don't follow symlinks */
-                if ((fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOCTTY)) >= 0)
-                        if ((f = fdopen(fd, "re")))
-                                break;
+                free(p);
+                free(q);
 
-                if (errno == ELOOP) {
-                        log_error("Refusing to operate on symlinks, please pass unit names or absolute paths to unit files.");
-                        free(filename);
-                        return -errno;
+                j = wait_for_terminate(pid, &status);
+                if (j < 0) {
+                        log_error("Failed to wait for child: %s", strerror(-r));
+                        r = j;
+                        goto finish;
                 }
 
-                if (errno != ENOENT) {
-                        log_error("Failed to open %s: %m", filename);
-                        free(filename);
-                        return -errno;
-                }
+                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");
+                                }
 
-                free(filename);
-                filename = NULL;
+                        } else if (status.si_status != 0) {
+                                r = -EINVAL;
+                                goto finish;
+                        }
+                } else {
+                        r = -EPROTO;
+                        goto finish;
+                }
         }
 
-        if (!f) {
-#if defined(TARGET_FEDORA) && defined (HAVE_SYSV_COMPAT)
+finish:
+        lookup_paths_free(&paths);
 
-                if (endswith(i->name, ".service")) {
-                        char *sysv;
-                        bool exists;
+        /* Drop all SysV units */
+        for (f = 1, t = 1; args[f]; f++) {
 
-                        if (asprintf(&sysv, SYSTEM_SYSVINIT_PATH "/%s", i->name) < 0) {
-                                log_error("Out of memory");
-                                return -ENOMEM;
-                        }
+                if (isempty(args[f]))
+                        continue;
 
-                        sysv[strlen(sysv) - sizeof(".service") + 1] = 0;
-                        exists = access(sysv, F_OK) >= 0;
+                args[t++] = args[f];
+        }
 
-                        if (exists) {
-                                pid_t pid;
-                                siginfo_t status;
+        args[t] = NULL;
 
-                                const char *argv[] = {
-                                        "/sbin/chkconfig",
-                                        NULL,
-                                        NULL,
-                                        NULL
-                                };
+#endif
+        return r;
+}
 
-                                log_info("%s is not a native service, redirecting to /sbin/chkconfig.", i->name);
+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;
 
-                                argv[1] = file_name_from_path(sysv);
-                                argv[2] =
-                                        streq(verb, "enable") ? "on" :
-                                        streq(verb, "disable") ? "off" : NULL;
+        dbus_error_init(&error);
 
-                                log_info("Executing %s %s %s", argv[0], argv[1], strempty(argv[2]));
+        r = enable_sysv_units(args);
+        if (r < 0)
+                return r;
 
-                                if ((pid = fork()) < 0) {
-                                        log_error("Failed to fork: %m");
-                                        free(sysv);
-                                        return -errno;
-                                } else if (pid == 0) {
-                                        execv(argv[0], (char**) argv);
-                                        _exit(EXIT_FAILURE);
-                                }
+        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");
 
-                                free(sysv);
+                if (r < 0) {
+                        log_error("Operation failed: %s", strerror(-r));
+                        goto finish;
+                }
 
-                                if ((r = wait_for_terminate(pid, &status)) < 0)
-                                        return r;
+                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);
+                }
 
-                                if (status.si_code == CLD_EXITED) {
+        } 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;
+                }
 
-                                        if (streq(verb, "is-enabled"))
-                                                return status.si_status == 0 ? 1 : 0;
+                dbus_message_iter_init_append(m, &iter);
 
-                                        if (status.si_status == 0)
-                                                n_symlinks ++;
+                r = bus_append_strv_iter(&iter, args+1);
+                if (r < 0) {
+                        log_error("Failed to append unit files.");
+                        goto finish;
+                }
 
-                                        return status.si_status == 0 ? 0 : -EINVAL;
+                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;
+                }
 
-                                } else
-                                        return -EPROTO;
-                        }
+                if (send_force) {
+                        b = arg_force;
 
-                        free(sysv);
+                        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) {
+                                log_error("Failed to append force boolean.");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
                 }
 
-#endif
+                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;
+                }
 
-                log_error("Couldn't find %s.", i->name);
-                return -ENOENT;
-        }
+                if (!dbus_message_iter_init(reply, &iter)) {
+                        log_error("Failed to initialize iterator.");
+                        goto finish;
+                }
 
-        i->path = filename;
+                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;
+                        }
 
-        if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
-                fclose(f);
-                return r;
-        }
+                        carries_install_info = b;
+                }
 
-        n_symlinks += strv_length(i->aliases);
-        n_symlinks += strv_length(i->wanted_by);
+                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;
+                }
 
-        fclose(f);
+                dbus_message_iter_recurse(&iter, &sub);
+                while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                        const char *type, *path, *source;
 
-        if ((r = install_info_symlink_alias(verb, i, config_path)) != 0)
-                return r;
+                        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+                                log_error("Failed to parse reply.");
+                                r = -EIO;
+                                goto finish;
+                        }
 
-        if ((r = install_info_symlink_wants(verb, i, config_path)) != 0)
-                return r;
+                        dbus_message_iter_recurse(&sub, &sub2);
 
-        if ((r = mark_symlink_for_removal(filename)) < 0)
-                return r;
+                        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 ((r = remove_marked_symlinks(config_path)) < 0)
-                return r;
+                        if (streq(type, "symlink"))
+                                log_info("ln -s '%s' '%s'", source, path);
+                        else
+                                log_info("rm '%s'", path);
 
-        return 0;
-}
+                        dbus_message_iter_next(&sub);
+                }
 
-static char *get_config_path(void) {
+                /* Try to reload if enabeld */
+                if (!arg_no_reload)
+                        r = daemon_reload(bus, args);
+        }
 
-        if (arg_user && arg_global)
-                return strdup(USER_CONFIG_UNIT_PATH);
+        if (carries_install_info == 0)
+                log_warning("Warning: unit files do not carry install information. No operation executed.");
 
-        if (arg_user) {
-                char *p;
+finish:
+        if (m)
+                dbus_message_unref(m);
 
-                if (user_config_home(&p) < 0)
-                        return NULL;
+        if (reply)
+                dbus_message_unref(reply);
 
-                return p;
-        }
+        unit_file_changes_free(changes, n_changes);
 
-        return strdup(SYSTEM_CONFIG_UNIT_PATH);
+        dbus_error_free(&error);
+        return r;
 }
 
-static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
+static int unit_is_enabled(DBusConnection *bus, char **args) {
         DBusError error;
         int r;
-        LookupPaths paths;
-        char *config_path = NULL;
-        unsigned j;
-        InstallInfo *i;
-        const char *verb = args[0];
+        DBusMessage *m = NULL, *reply = NULL;
+        bool enabled;
+        char **name;
 
         dbus_error_init(&error);
 
-        zero(paths);
-        if ((r = lookup_paths_init(&paths, arg_user ? MANAGER_USER : MANAGER_SYSTEM)) < 0) {
-                log_error("Failed to determine lookup paths: %s", strerror(-r));
-                goto finish;
-        }
+        r = enable_sysv_units(args);
+        if (r < 0)
+                return r;
 
-        if (!(config_path = get_config_path())) {
-                log_error("Failed to determine config path");
-                r = -ENOMEM;
-                goto finish;
-        }
+        enabled = r > 0;
 
-        will_install = hashmap_new(string_hash_func, string_compare_func);
-        have_installed = hashmap_new(string_hash_func, string_compare_func);
+        if (!bus || avoid_bus()) {
 
-        if (!will_install || !have_installed) {
-                log_error("Failed to allocate unit sets.");
-                r = -ENOMEM;
-                goto finish;
-        }
+                STRV_FOREACH(name, args+1) {
+                        UnitFileState state;
 
-        if (!arg_defaults && streq(verb, "disable"))
-                if (!(remove_symlinks_to = set_new(string_hash_func, string_compare_func))) {
-                        log_error("Failed to allocate symlink sets.");
-                        r = -ENOMEM;
-                        goto finish;
-                }
+                        state = unit_file_get_state(arg_scope, arg_root, *name);
+                        if (state < 0) {
+                                r = state;
+                                goto finish;
+                        }
 
-        for (j = 1; j < n; j++)
-                if ((r = install_info_add(args[j])) < 0) {
-                        log_warning("Cannot install unit %s: %s", args[j], strerror(-r));
-                        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));
                 }
 
-        r = 0;
+        } else {
+                STRV_FOREACH(name, args+1) {
+                        const char *s;
 
-        while ((i = hashmap_first(will_install))) {
-                int q;
+                        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;
+                        }
 
-                assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
+                        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 ((q = install_info_apply(verb, &paths, i, config_path)) != 0) {
+                        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 (q < 0) {
-                                if (r == 0)
-                                        r = q;
+                        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;
                         }
 
-                        /* In test mode and found something */
-                        r = 1;
-                        break;
-                }
-        }
+                        dbus_message_unref(m);
+                        dbus_message_unref(reply);
+                        m = reply = NULL;
 
-        if (streq(verb, "is-enabled"))
-                r = r > 0 ? 0 : -ENOENT;
-        else {
-                if (n_symlinks <= 0)
-                        log_warning("Unit files contain no applicable installation information. Ignoring.");
-
-                if (bus &&
-                    /* Don't try to reload anything if the user asked us to not do this */
-                    !arg_no_reload &&
-                    /* Don't try to reload anything when updating a unit globally */
-                    !arg_global &&
-                    /* Don't try to reload anything if we are called for system changes but the system wasn't booted with systemd */
-                    (arg_user || sd_booted() > 0) &&
-                    /* Don't try to reload anything if we are running in a chroot environment */
-                    (arg_user || running_in_chroot() <= 0) ) {
-                        int q;
-
-                        if ((q = daemon_reload(bus, args, n)) < 0)
-                                r = q;
+                        if (streq(s, "enabled") ||
+                            streq(s, "enabled-runtime") ||
+                            streq(s, "static"))
+                                enabled = true;
+
+                        if (!arg_quiet)
+                                puts(s);
                 }
         }
 
-finish:
-        install_info_hashmap_free(will_install);
-        install_info_hashmap_free(have_installed);
-
-        set_free_free(remove_symlinks_to);
+        r = enabled ? 0 : 1;
 
-        lookup_paths_free(&paths);
+finish:
+        if (m)
+                dbus_message_unref(m);
 
-        free(config_path);
+        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"
-               "Send control commands to or query the systemd manager.\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"
@@ -4273,27 +3895,30 @@ static int systemctl_help(void) {
                "                      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-pager       Do not pipe output into a pager.\n"
-               "     --system         Connect to system manager\n"
-               "     --user           Connect to user service manager\n"
-               "     --order          When generating graph for dot, show only order\n"
-               "     --require        When generating graph for dot, show only requirement\n"
                "     --no-wall        Don't send wall message before halt/power-off/reboot\n"
-               "     --global         Enable/disable unit files globally\n"
                "     --no-reload      When enabling/disabling unit files, don't reload daemon\n"
                "                      configuration\n"
+               "     --no-pager       Do not pipe output into a pager\n"
                "     --no-ask-password\n"
                "                      Do not ask for system passwords\n"
-               "     --kill-mode=MODE How to send signal\n"
-               "     --kill-who=WHO   Who to send signal to\n"
-               "  -s --signal=SIGNAL  Which signal to send\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"
-               "     --defaults       When disabling unit files, remove default symlinks only\n\n"
-               "Commands:\n"
-               "  list-units                      List units\n"
+               "     --root=PATH      Enable unit files in the specified root directory\n"
+               "     --runtime        Enable unit files only temporarily until next reboot\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"
@@ -4311,22 +3936,36 @@ static int systemctl_help(void) {
                "                                  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"
-               "  is-enabled [NAME...]            Check whether unit files are enabled\n"
-               "  load [NAME...]                  Load one or more units\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"
-               "  monitor                         Monitor unit/job changes\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"
+               "  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"
-               "  daemon-reload                   Reload systemd manager configuration\n"
-               "  daemon-reexec                   Reexecute systemd manager\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"
+               "  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"
@@ -4420,13 +4059,14 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_NO_WALL,
                 ARG_ORDER,
                 ARG_REQUIRE,
+                ARG_ROOT,
                 ARG_FULL,
                 ARG_NO_RELOAD,
-                ARG_DEFAULTS,
                 ARG_KILL_MODE,
                 ARG_KILL_WHO,
                 ARG_NO_ASK_PASSWORD,
-                ARG_FAILED
+                ARG_FAILED,
+                ARG_RUNTIME
         };
 
         static const struct option options[] = {
@@ -4448,13 +4088,16 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "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, 'f'           },
                 { "no-reload", no_argument,       NULL, ARG_NO_RELOAD },
-                { "defaults",  no_argument,       NULL, ARG_DEFAULTS  },
-                { "kill-mode", required_argument, NULL, ARG_KILL_MODE },
+                { "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   },
                 { NULL,        0,                 NULL, 0             }
         };
 
@@ -4466,7 +4109,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
         /* Only when running as systemctl we ask for passwords */
         arg_ask_password = true;
 
-        while ((c = getopt_long(argc, argv, "ht:p:aqfs:", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:P", options, NULL)) >= 0) {
 
                 switch (c) {
 
@@ -4513,11 +4156,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_USER:
-                        arg_user = true;
+                        arg_scope = UNIT_FILE_USER;
                         break;
 
                 case ARG_SYSTEM:
-                        arg_user = false;
+                        arg_scope = UNIT_FILE_SYSTEM;
+                        break;
+
+                case ARG_GLOBAL:
+                        arg_scope = UNIT_FILE_GLOBAL;
                         break;
 
                 case ARG_NO_BLOCK:
@@ -4540,6 +4187,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_dot = DOT_REQUIRE;
                         break;
 
+                case ARG_ROOT:
+                        arg_root = optarg;
+                        break;
+
                 case ARG_FULL:
                         arg_full = true;
                         break;
@@ -4560,15 +4211,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_no_reload = true;
                         break;
 
-                case ARG_GLOBAL:
-                        arg_global = true;
-                        arg_user = true;
-                        break;
-
-                case ARG_DEFAULTS:
-                        arg_defaults = true;
-                        break;
-
                 case ARG_KILL_WHO:
                         arg_kill_who = optarg;
                         break;
@@ -4588,6 +4230,19 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         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 '?':
                         return -EINVAL;
 
@@ -4597,6 +4252,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 }
         }
 
+        if (arg_transport != TRANSPORT_NORMAL && arg_scope != UNIT_FILE_SYSTEM) {
+                log_error("Cannot access user instance remotely.");
+                return -EINVAL;
+        }
+
         return 1;
 }
 
@@ -4699,10 +4359,10 @@ static int parse_time_spec(const char *t, usec_t *_u) {
 
         if (streq(t, "now"))
                 *_u = 0;
-        else if (t[0] == '+') {
+        else if (!strchr(t, ':')) {
                 uint64_t u;
 
-                if (safe_atou64(t + 1, &u) < 0)
+                if (safe_atou64(t, &u) < 0)
                         return -EINVAL;
 
                 *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
@@ -5117,7 +4777,7 @@ static int talk_upstart(void) {
                 goto finish;
         }
 
-        r = 0;
+        r = 1;
 
 finish:
         if (m)
@@ -5182,15 +4842,17 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                         EQUAL
                 } argc_cmp;
                 const int argc;
-                int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
+                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        },
@@ -5205,7 +4867,6 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "check",                 MORE,  2, check_unit        },
                 { "show",                  MORE,  1, show              },
                 { "status",                MORE,  2, show              },
-                { "monitor",               EQUAL, 1, monitor           },
                 { "dump",                  EQUAL, 1, dump              },
                 { "dot",                   EQUAL, 1, dot               },
                 { "snapshot",              LESS,  2, snapshot          },
@@ -5226,7 +4887,12 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "reset-failed",          MORE,  1, reset_failed      },
                 { "enable",                MORE,  2, enable_unit       },
                 { "disable",               MORE,  2, enable_unit       },
-                { "is-enabled",            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;
@@ -5291,15 +4957,28 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
          * enable/disable */
         if (!streq(verbs[i].verb, "enable") &&
             !streq(verbs[i].verb, "disable") &&
-            !bus) {
-                log_error("Failed to get D-Bus connection: %s", error->message);
-                return -EIO;
+            !streq(verbs[i].verb, "is-enable") &&
+            !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 && !avoid_bus()) {
+                        log_error("Failed to get D-Bus connection: %s", error->message);
+                        return -EIO;
+                }
         }
 
-        return verbs[i].dispatch(bus, argv + optind, left);
+        return verbs[i].dispatch(bus, argv + optind);
 }
 
-static int send_shutdownd(usec_t t, char mode, bool warn, const char *message) {
+static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
         int fd = -1;
         struct msghdr msghdr;
         struct iovec iovec;
@@ -5309,6 +4988,7 @@ static int send_shutdownd(usec_t t, char mode, bool warn, const char *message) {
         zero(c);
         c.elapse = t;
         c.mode = mode;
+        c.dry_run = dry_run;
         c.warn_wall = warn;
 
         if (message)
@@ -5320,7 +5000,7 @@ static int send_shutdownd(usec_t t, char mode, bool warn, const char *message) {
         zero(sockaddr);
         sockaddr.sa.sa_family = AF_UNIX;
         sockaddr.un.sun_path[0] = 0;
-        strncpy(sockaddr.un.sun_path+1, "/org/freedesktop/systemd1/shutdownd", sizeof(sockaddr.un.sun_path)-1);
+        strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
 
         zero(iovec);
         iovec.iov_base = (char*) &c;
@@ -5328,7 +5008,7 @@ static int send_shutdownd(usec_t t, char mode, bool warn, const char *message) {
 
         zero(msghdr);
         msghdr.msg_name = &sockaddr;
-        msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + 1 + sizeof("/org/freedesktop/systemd1/shutdownd") - 1;
+        msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
 
         msghdr.msg_iov = &iovec;
         msghdr.msg_iovlen = 1;
@@ -5346,7 +5026,7 @@ static int reload_with_fallback(DBusConnection *bus) {
 
         if (bus) {
                 /* First, try systemd via D-Bus. */
-                if (daemon_reload(bus, NULL, 0) > 0)
+                if (daemon_reload(bus, NULL) > 0)
                         return 0;
         }
 
@@ -5365,7 +5045,7 @@ static int start_with_fallback(DBusConnection *bus) {
 
         if (bus) {
                 /* First, try systemd via D-Bus. */
-                if (start_unit(bus, NULL, 0) >= 0)
+                if (start_unit(bus, NULL) >= 0)
                         goto done;
         }
 
@@ -5404,6 +5084,7 @@ static int halt_main(DBusConnection *bus) {
                                    arg_action == ACTION_HALT     ? 'H' :
                                    arg_action == ACTION_POWEROFF ? 'P' :
                                                                    'r',
+                                   arg_dry,
                                    !arg_no_wall,
                                    m);
                 free(m);
@@ -5465,8 +5146,9 @@ static int halt_main(DBusConnection *bus) {
 static int runlevel_main(void) {
         int r, runlevel, previous;
 
-        if ((r = utmp_get_runlevel(&runlevel, &previous)) < 0) {
-                printf("unknown\n");
+        r = utmp_get_runlevel(&runlevel, &previous);
+        if (r < 0) {
+                puts("unknown");
                 return r;
         }
 
@@ -5477,109 +5159,6 @@ static int runlevel_main(void) {
         return 0;
 }
 
-static void pager_open(void) {
-        int fd[2];
-        const char *pager;
-        pid_t parent_pid;
-
-        if (pager_pid > 0)
-                return;
-
-        if (!on_tty() || arg_no_pager)
-                return;
-
-        if ((pager = getenv("PAGER")))
-                if (!*pager || streq(pager, "cat"))
-                        return;
-
-        /* Determine and cache number of columns before we spawn the
-         * pager so that we get the value from the actual tty */
-        columns();
-
-        if (pipe(fd) < 0) {
-                log_error("Failed to create pager pipe: %m");
-                return;
-        }
-
-        parent_pid = getpid();
-
-        pager_pid = fork();
-        if (pager_pid < 0) {
-                log_error("Failed to fork pager: %m");
-                close_pipe(fd);
-                return;
-        }
-
-        /* In the child start the pager */
-        if (pager_pid == 0) {
-
-                dup2(fd[0], STDIN_FILENO);
-                close_pipe(fd);
-
-                setenv("LESS", "FRSX", 0);
-
-                /* Make sure the pager goes away when the parent dies */
-                if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
-                        _exit(EXIT_FAILURE);
-
-                /* Check whether our parent died before we were able
-                 * to set the death signal */
-                if (getppid() != parent_pid)
-                        _exit(EXIT_SUCCESS);
-
-                if (pager) {
-                        execlp(pager, pager, NULL);
-                        execl("/bin/sh", "sh", "-c", pager, NULL);
-                } else {
-                        /* Debian's alternatives command for pagers is
-                         * called 'pager'. Note that we do not call
-                         * sensible-pagers here, since that is just a
-                         * shell script that implements a logic that
-                         * is similar to this one anyway, but is
-                         * Debian-specific. */
-                        execlp("pager", "pager", NULL);
-
-                        execlp("less", "less", NULL);
-                        execlp("more", "more", NULL);
-                }
-
-                log_error("Unable to execute pager: %m");
-                _exit(EXIT_FAILURE);
-        }
-
-        /* Return in the parent */
-        if (dup2(fd[1], STDOUT_FILENO) < 0)
-                log_error("Failed to duplicate pager pipe: %m");
-
-        close_pipe(fd);
-}
-
-static void pager_close(void) {
-        siginfo_t dummy;
-
-        if (pager_pid <= 0)
-                return;
-
-        /* Inform pager that we are done */
-        fclose(stdout);
-        kill(pager_pid, SIGCONT);
-        wait_for_terminate(pager_pid, &dummy);
-        pager_pid = 0;
-}
-
-static void agent_close(void) {
-        siginfo_t dummy;
-
-        if (agent_pid <= 0)
-                return;
-
-        /* Inform agent that we are done */
-        kill(agent_pid, SIGTERM);
-        kill(agent_pid, SIGCONT);
-        wait_for_terminate(agent_pid, &dummy);
-        agent_pid = 0;
-}
-
 int main(int argc, char*argv[]) {
         int r, retval = EXIT_FAILURE;
         DBusConnection *bus = NULL;
@@ -5605,7 +5184,24 @@ int main(int argc, char*argv[]) {
                 goto finish;
         }
 
-        bus_connect(arg_user ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error);
+        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) {
 
@@ -5636,7 +5232,7 @@ int main(int argc, char*argv[]) {
                 break;
 
         case ACTION_CANCEL_SHUTDOWN:
-                r = send_shutdownd(0, 0, false, NULL);
+                r = send_shutdownd(0, 0, false, false, NULL);
                 break;
 
         case ACTION_INVALID:
@@ -5648,7 +5244,6 @@ int main(int argc, char*argv[]) {
         retval = r < 0 ? EXIT_FAILURE : r;
 
 finish:
-
         if (bus) {
                 dbus_connection_flush(bus);
                 dbus_connection_close(bus);