chiark / gitweb /
loginctl: implement more verbs
[elogind.git] / src / systemctl.c
index 66903aba28a5a1c276e081fa65a9a713be0e93ea..840e2ffc2d2f235b61983378c298b4265db54524 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"
 
 static const char *arg_type = NULL;
 static char **arg_property = NULL;
 static bool arg_all = false;
-static bool arg_fail = false;
+static const char *arg_job_mode = "replace";
 static bool arg_user = false;
 static bool arg_global = false;
 static bool arg_immediate = false;
@@ -76,10 +76,12 @@ 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 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,
@@ -107,13 +109,18 @@ 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 bool on_tty(void) {
         static int t = -1;
@@ -121,7 +128,7 @@ static bool on_tty(void) {
         /* Note that this is invoked relatively early, before we start
          * the pager. That means the value we return reflects whether
          * we originally were started on a tty, not if we currently
-         * are. But this is intended, since we want color, and so on
+         * are. But this is intended, since we want colour and so on
          * when run in our own pager. */
 
         if (_unlikely_(t < 0))
@@ -130,8 +137,18 @@ static bool on_tty(void) {
         return t;
 }
 
+static void pager_open_if_enabled(void) {
+        on_tty();
+
+        if (!arg_no_pager)
+                pager_open();
+}
+
 static void spawn_ask_password_agent(void) {
-        pid_t parent, child;
+        pid_t parent;
+
+        if (agent_pid > 0)
+                return;
 
         /* We check STDIN here, not STDOUT, since this is about input,
          * not output */
@@ -141,23 +158,22 @@ static void spawn_ask_password_agent(void) {
         if (!arg_ask_password)
                 return;
 
+        if (arg_user)
+                return;
+
         parent = getpid();
 
         /* Spawns a temporary TTY agent, making sure it goes away when
          * we go away */
 
-        if ((child = fork()) < 0)
+        if ((agent_pid = fork()) < 0)
                 return;
 
-        if (child == 0) {
+        if (agent_pid == 0) {
                 /* In the child */
-                const char * const args[] = {
-                        SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH,
-                        "--watch",
-                        NULL
-                };
 
                 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)
@@ -171,29 +187,35 @@ static void spawn_ask_password_agent(void) {
                 /* Don't leak fds to the agent */
                 close_all_fds(NULL, 0);
 
-                /* Detach from stdin/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_RDWR|O_CLOEXEC|O_NONBLOCK)) < 0) {
-                        log_error("Failed to open /dev/tty: %m");
-                        _exit(EXIT_FAILURE);
-                }
+                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);
+                        }
 
-                close(STDIN_FILENO);
-                close(STDOUT_FILENO);
-                close(STDERR_FILENO);
+                        if (!stdout_is_tty)
+                                dup2(fd, STDOUT_FILENO);
 
-                dup2(fd, STDIN_FILENO);
-                dup2(fd, STDOUT_FILENO);
-                dup2(fd, STDERR_FILENO);
+                        if (!stderr_is_tty)
+                                dup2(fd, STDERR_FILENO);
 
-                if (fd > 2)
-                        close(fd);
+                        if (fd > 2)
+                                close(fd);
+                }
+
+                execl(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL);
 
-                execv(args[0], (char **) args);
+                log_error("Unable to execute agent: %m");
                 _exit(EXIT_FAILURE);
         }
 }
@@ -257,22 +279,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!",
@@ -339,9 +345,12 @@ static int compare_unit_info(const void *a, const void *b) {
         return strcasecmp(u->id, v->id);
 }
 
-static bool output_show_job(const struct unit_info *u) {
+static bool output_show_unit(const struct unit_info *u) {
         const char *dot;
 
+        if (arg_failed)
+                return streq(u->active_state, "failed");
+
         return (!arg_type || ((dot = strrchr(u->id, '.')) &&
                               streq(dot+1, arg_type))) &&
                 (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0);
@@ -356,7 +365,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
         job_len = sizeof("JOB")-1;
 
         for (u = unit_infos; u < unit_infos + c; u++) {
-                if (!output_show_job(u))
+                if (!output_show_unit(u))
                         continue;
 
                 active_len = MAX(active_len, strlen(u->active_state));
@@ -368,7 +377,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");
@@ -380,7 +389,7 @@ static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
                 const char *on_loaded, *off_loaded;
                 const char *on_active, *off_active;
 
-                if (!output_show_job(u))
+                if (!output_show_unit(u))
                         continue;
 
                 n_shown++;
@@ -421,7 +430,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);
@@ -455,7 +464,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",
@@ -776,7 +785,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");
 
@@ -805,7 +814,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",
@@ -832,7 +841,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) {
@@ -868,7 +877,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;
@@ -1108,7 +1117,7 @@ finish:
 
 typedef struct WaitData {
         Set *set;
-        bool failed;
+        char *result;
 } WaitData;
 
 static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
@@ -1132,26 +1141,52 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
 
         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
                 uint32_t id;
-                const char *path;
+                const char *path, *result;
                 dbus_bool_t success = true;
 
-                if (!dbus_message_get_args(message, &error,
-                                           DBUS_TYPE_UINT32, &id,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_BOOLEAN, &success,
-                                           DBUS_TYPE_INVALID))
-                        log_error("Failed to parse message: %s", bus_error_message(&error));
-                else {
+                if (dbus_message_get_args(message, &error,
+                                          DBUS_TYPE_UINT32, &id,
+                                          DBUS_TYPE_OBJECT_PATH, &path,
+                                          DBUS_TYPE_STRING, &result,
+                                          DBUS_TYPE_INVALID)) {
+                        char *p;
+
+                        if ((p = set_remove(d->set, (char*) path)))
+                                free(p);
+
+                        if (*result)
+                                d->result = strdup(result);
+
+                        goto finish;
+                }
+#ifndef LEGACY
+                dbus_error_free(&error);
+
+                if (dbus_message_get_args(message, &error,
+                                          DBUS_TYPE_UINT32, &id,
+                                          DBUS_TYPE_OBJECT_PATH, &path,
+                                          DBUS_TYPE_BOOLEAN, &success,
+                                          DBUS_TYPE_INVALID)) {
                         char *p;
 
+                        /* Compatibility with older systemd versions <
+                         * 19 during upgrades. This should be dropped
+                         * one day */
+
                         if ((p = set_remove(d->set, (char*) path)))
                                 free(p);
 
                         if (!success)
-                                d->failed = true;
+                                d->result = strdup("failed");
+
+                        goto finish;
                 }
+#endif
+
+                log_error("Failed to parse message: %s", bus_error_message(&error));
         }
 
+finish:
         dbus_error_free(&error);
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
@@ -1192,7 +1227,6 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) {
 
         zero(d);
         d.set = s;
-        d.failed = false;
 
         if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) {
                 log_error("Failed to add filter.");
@@ -1204,10 +1238,27 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) {
                dbus_connection_read_write_dispatch(bus, -1))
                 ;
 
-        if (!arg_quiet && d.failed)
-                log_error("Job failed. See system logs and 'systemctl status' for details.");
+        if (!arg_quiet && d.result) {
+                if (streq(d.result, "timeout"))
+                        log_error("Job timed out.");
+                else if (streq(d.result, "canceled"))
+                        log_error("Job canceled.");
+                else if (streq(d.result, "dependency"))
+                        log_error("A dependency job failed. See system logs for details.");
+                else if (!streq(d.result, "done") && !streq(d.result, "skipped"))
+                        log_error("Job failed. See system logs and 'systemctl status' for details.");
+        }
+
+        if (streq_ptr(d.result, "timeout"))
+                r = -ETIME;
+        else if (streq_ptr(d.result, "canceled"))
+                r = -ECANCELED;
+        else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped"))
+                r = -EIO;
+        else
+                r = 0;
 
-        r = d.failed ? -EIO : 0;
+        free(d.result);
 
 finish:
         /* This is slightly dirty, since we don't undo the filter registration. */
@@ -1359,22 +1410,26 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
 
         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" :
+
                         streq(args[0], "try-restart")           ||
                         streq(args[0], "condrestart")           ? "TryRestartUnit" :
+
                         streq(args[0], "reload-or-restart")     ? "ReloadOrRestartUnit" :
+
                         streq(args[0], "reload-or-try-restart") ||
+                        streq(args[0], "condreload") ||
+
                         streq(args[0], "force-reload")          ? "ReloadOrTryRestartUnit" :
                                                                   "StartUnit";
 
                 mode =
                         (streq(args[0], "isolate") ||
                          streq(args[0], "rescue")  ||
-                         streq(args[0], "emergency")) ? "isolate" :
-                                             arg_fail ? "fail" :
-                                                        "replace";
+                         streq(args[0], "emergency")) ? "isolate" : arg_job_mode;
 
                 one_name = table[verb_to_action(args[0])];
 
@@ -1500,6 +1555,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
 
                         dbus_error_free(&error);
                         dbus_message_unref(m);
+                        m = NULL;
                         continue;
                 }
 
@@ -1670,7 +1726,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;
@@ -1724,7 +1780,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)
@@ -1773,6 +1831,9 @@ typedef struct UnitStatusInfo {
 
         int exit_code, exit_status;
 
+        usec_t condition_timestamp;
+        bool condition_result;
+
         /* Socket */
         unsigned n_accepted;
         unsigned n_connections;
@@ -1864,6 +1925,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)
@@ -1990,12 +2061,14 @@ 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)
@@ -2007,6 +2080,10 @@ static void print_status_info(UnitStatusInfo *i) {
 
 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: {
@@ -2014,7 +2091,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"))
@@ -2059,6 +2136,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;
         }
@@ -2116,6 +2195,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;
         }
@@ -2169,74 +2250,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);
@@ -2268,50 +2281,21 @@ 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;
-
-                        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;
-
-                                        assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
-                                        dbus_message_iter_get_basic(&sub, &s);
-                                        printf("%s%s", space ? " " : "", s);
-
-                                        space = true;
-                                        dbus_message_iter_next(&sub);
-                                }
-
-                                puts("");
-                        }
-
-                        return 0;
-
-                } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
-                        DBusMessageIter sub;
+                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) {
-                                        uint8_t u;
+                        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_BYTE);
-                                        dbus_message_iter_get_basic(&sub, &u);
-                                        printf("%02x", u);
+                                dbus_message_iter_recurse(&sub, &sub2);
 
-                                        dbus_message_iter_next(&sub);
-                                }
+                                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));
 
-                                puts("");
+                                dbus_message_iter_next(&sub);
                         }
 
                         return 0;
@@ -2402,6 +2386,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);
 
@@ -2545,7 +2532,7 @@ 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 not argument is specified inspect the manager
@@ -2737,8 +2724,7 @@ static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage
                 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")) {
+        } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobNew")) {
                 uint32_t id;
                 const char *path;
 
@@ -2747,10 +2733,22 @@ static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage
                                            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), "JobNew"))
+                else
                         printf("Job %u added.\n", id);
+
+
+        } else if (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
-                        printf("Job %u removed.\n", id);
+                        printf("Job %u removed (result=%s).\n", id, result);
 
 
         } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
@@ -2930,7 +2928,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",
@@ -3207,6 +3205,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;
@@ -3294,7 +3299,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",
@@ -3505,6 +3510,7 @@ static int config_parse_also(
                 unsigned line,
                 const char *section,
                 const char *lvalue,
+                int ltype,
                 const char *rvalue,
                 void *data,
                 void *userdata) {
@@ -3723,8 +3729,14 @@ static int remove_marked_symlinks(const char *config_path) {
         return r;
 }
 
-static int create_symlink(const char *verb, const char *old_path, const char *new_path) {
+static int create_symlink(const char *verb, const char *orig_old_path, const char *new_path) {
         int r;
+       const char *old_path;
+
+       if (arg_root)
+               old_path = orig_old_path+strlen(arg_root);
+       else
+               old_path = orig_old_path;
 
         assert(old_path);
         assert(new_path);
@@ -3838,6 +3850,7 @@ static int create_symlink(const char *verb, const char *old_path, const char *ne
                         return 1;
                 }
 
+                free(dest);
                 return 0;
         }
 
@@ -3919,11 +3932,11 @@ finish:
 static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo *i, const char *config_path) {
 
         const ConfigItem items[] = {
-                { "Alias",    config_parse_strv, &i->aliases,   "Install" },
-                { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
-                { "Also",     config_parse_also, NULL,          "Install" },
+                { "Alias",    config_parse_strv, 0, &i->aliases,   "Install" },
+                { "WantedBy", config_parse_strv, 0, &i->wanted_by, "Install" },
+                { "Also",     config_parse_also, 0, NULL,          "Install" },
 
-                { NULL, NULL, NULL, NULL }
+                { NULL, NULL, 0, NULL, NULL }
         };
 
         char **p;
@@ -3936,12 +3949,23 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
 
         STRV_FOREACH(p, paths->unit_path) {
                 int fd;
+               char *path, *should_free;
+
+               if (arg_root)
+                       should_free = path = strappend(arg_root, *p);
+               else {
+                       should_free = NULL;
+                       path = *p;
+               }
 
-                if (!(filename = path_make_absolute(i->name, *p))) {
+                if (!(filename = path_make_absolute(i->name, path))) {
                         log_error("Out of memory");
                         return -ENOMEM;
                 }
 
+               if (should_free)
+                       free(should_free);
+
                 /* 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")))
@@ -3964,7 +3988,7 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
         }
 
         if (!f) {
-#if defined(TARGET_FEDORA) && defined (HAVE_SYSV_COMPAT)
+#if (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX)) && defined (HAVE_SYSV_COMPAT)
 
                 if (endswith(i->name, ".service")) {
                         char *sysv;
@@ -3976,7 +4000,14 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
                         }
 
                         sysv[strlen(sysv) - sizeof(".service") + 1] = 0;
-                        exists = access(sysv, F_OK) >= 0;
+                        if (arg_root) {
+                                char *tmp_path;
+
+                                tmp_path = strappend (arg_root, sysv);
+                                exists = access (tmp_path, F_OK) >= 0;
+                                free (tmp_path);
+                       } else
+                                exists = access(sysv, F_OK) >= 0;
 
                         if (exists) {
                                 pid_t pid;
@@ -3986,6 +4017,7 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
                                         "/sbin/chkconfig",
                                         NULL,
                                         NULL,
+                                        NULL,
                                         NULL
                                 };
 
@@ -3994,9 +4026,11 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
                                 argv[1] = file_name_from_path(sysv);
                                 argv[2] =
                                         streq(verb, "enable") ? "on" :
-                                        streq(verb, "disable") ? "off" : NULL;
+                                        streq(verb, "disable") ? "off" : "--level=5";
+                                if (arg_root)
+                                        argv[3] = strappend("--root=", arg_root);
 
-                                log_info("Executing %s %s %s", argv[0], argv[1], strempty(argv[2]));
+                               log_info("Executing %s %s %s %s", argv[0], argv[1], strempty(argv[2]), strempty(argv[3]));
 
                                 if ((pid = fork()) < 0) {
                                         log_error("Failed to fork: %m");
@@ -4013,10 +4047,15 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
                                         return r;
 
                                 if (status.si_code == CLD_EXITED) {
-                                        if (status.si_status == 0 && (streq(verb, "enable") || streq(verb, "disable")))
+
+                                        if (streq(verb, "is-enabled"))
+                                                return status.si_status == 0 ? 1 : 0;
+
+                                        if (status.si_status == 0)
                                                 n_symlinks ++;
 
                                         return status.si_status == 0 ? 0 : -EINVAL;
+
                                 } else
                                         return -EPROTO;
                         }
@@ -4037,6 +4076,16 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
                 return r;
         }
 
+        /* Consider unit files stored in /lib and /usr always enabled
+         * if they have no [Install] data. */
+        if (streq(verb, "is-enabled") &&
+            strv_isempty(i->aliases) &&
+            strv_isempty(i->wanted_by) &&
+            !path_startswith(filename, "/etc")) {
+                fclose(f);
+                return 1;
+        }
+
         n_symlinks += strv_length(i->aliases);
         n_symlinks += strv_length(i->wanted_by);
 
@@ -4058,20 +4107,24 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
 }
 
 static char *get_config_path(void) {
+        char *ret;
 
         if (arg_user && arg_global)
-                return strdup(USER_CONFIG_UNIT_PATH);
+                ret = strdup(USER_CONFIG_UNIT_PATH);
 
         if (arg_user) {
-                char *p;
-
-                if (user_config_home(&p) < 0)
+                if (user_config_home(&ret) < 0)
                         return NULL;
-
-                return p;
         }
 
-        return strdup(SYSTEM_CONFIG_UNIT_PATH);
+        ret = strdup(SYSTEM_CONFIG_UNIT_PATH);
+        if (arg_root) {
+                char *p;
+                p = strappend (arg_root, ret);
+                free (ret);
+                return p;
+        } else
+                return ret;
 }
 
 static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
@@ -4119,6 +4172,8 @@ static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
                         goto finish;
                 }
 
+        r = 0;
+
         while ((i = hashmap_first(will_install))) {
                 int q;
 
@@ -4152,7 +4207,7 @@ static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
                     /* 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) ) {
+                    (arg_user || running_in_chroot() <= 0 || arg_root) ) {
                         int q;
 
                         if ((q = daemon_reload(bus, args, n)) < 0)
@@ -4182,25 +4237,31 @@ static int systemctl_help(void) {
                "  -t --type=TYPE      List only units of a particular type\n"
                "  -p --property=NAME  Show only properties by this name\n"
                "  -a --all            Show all units/properties, including dead/empty ones\n"
+               "     --failed         Show only failed units\n"
                "     --full           Don't ellipsize unit names on output\n"
                "     --fail           When queueing a new job, fail if conflicting jobs are\n"
                "                      pending\n"
+               "     --ignore-dependencies\n"
+               "                      When queueing a new job, ignore all its dependencies\n"
+               "     --kill-who=WHO   Who to send signal to\n"
+               "  -s --signal=SIGNAL  Which signal to send\n"
+               "  -H --host=[USER@]HOST\n"
+               "                      Show information for remote host\n"
+               "  -P --privileged     Acquire privileges before execution\n"
                "  -q --quiet          Suppress output\n"
                "     --no-block       Do not wait until operation finished\n"
-               "     --no-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"
+               "     --root=path      Use <root> as the root file system\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"
@@ -4322,6 +4383,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
 
         enum {
                 ARG_FAIL = 0x100,
+                ARG_IGNORE_DEPENDENCIES,
                 ARG_VERSION,
                 ARG_USER,
                 ARG_SYSTEM,
@@ -4331,12 +4393,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_NO_ASK_PASSWORD,
+                ARG_FAILED
         };
 
         static const struct option options[] = {
@@ -4345,8 +4409,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "type",      required_argument, NULL, 't'           },
                 { "property",  required_argument, NULL, 'p'           },
                 { "all",       no_argument,       NULL, 'a'           },
+                { "failed",    no_argument,       NULL, ARG_FAILED    },
                 { "full",      no_argument,       NULL, ARG_FULL      },
                 { "fail",      no_argument,       NULL, ARG_FAIL      },
+                { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
                 { "user",      no_argument,       NULL, ARG_USER      },
                 { "system",    no_argument,       NULL, ARG_SYSTEM    },
                 { "global",    no_argument,       NULL, ARG_GLOBAL    },
@@ -4356,13 +4422,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'           },
                 { NULL,        0,                 NULL, 0             }
         };
 
@@ -4374,7 +4443,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) {
 
@@ -4413,7 +4482,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_FAIL:
-                        arg_fail = true;
+                        arg_job_mode = "fail";
+                        break;
+
+                case ARG_IGNORE_DEPENDENCIES:
+                        arg_job_mode = "ignore-dependencies";
                         break;
 
                 case ARG_USER:
@@ -4444,10 +4517,18 @@ 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;
 
+                case ARG_FAILED:
+                        arg_failed = true;
+                        break;
+
                 case 'q':
                         arg_quiet = true;
                         break;
@@ -4488,6 +4569,15 @@ 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 '?':
                         return -EINVAL;
 
@@ -4497,6 +4587,11 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 }
         }
 
+        if (arg_transport != TRANSPORT_NORMAL && arg_user) {
+                log_error("Cannot access user instance remotely.");
+                return -EINVAL;
+        }
+
         return 1;
 }
 
@@ -4599,10 +4694,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;
@@ -4643,6 +4738,18 @@ static int parse_time_spec(const char *t, usec_t *_u) {
         return 0;
 }
 
+static bool kexec_loaded(void) {
+       bool loaded = false;
+       char *s;
+
+       if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
+               if (s[0] == '1')
+                       loaded = true;
+               free(s);
+       }
+       return loaded;
+}
+
 static int shutdown_parse_argv(int argc, char *argv[]) {
 
         enum {
@@ -4680,7 +4787,10 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'r':
-                        arg_action = ACTION_REBOOT;
+                        if (kexec_loaded())
+                                arg_action = ACTION_KEXEC;
+                        else
+                                arg_action = ACTION_REBOOT;
                         break;
 
                 case 'h':
@@ -4873,7 +4983,10 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_action = ACTION_POWEROFF;
                         return halt_parse_argv(argc, argv);
                 } else if (strstr(program_invocation_short_name, "reboot")) {
-                        arg_action = ACTION_REBOOT;
+                        if (kexec_loaded())
+                                arg_action = ACTION_KEXEC;
+                        else
+                                arg_action = ACTION_REBOOT;
                         return halt_parse_argv(argc, argv);
                 } else if (strstr(program_invocation_short_name, "shutdown")) {
                         arg_action = ACTION_POWEROFF;
@@ -5073,12 +5186,14 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "cancel",                MORE,  2, cancel_job        },
                 { "start",                 MORE,  2, start_unit        },
                 { "stop",                  MORE,  2, start_unit        },
+                { "condstop",              MORE,  2, start_unit        }, /* For compatibility with ALTLinux */
                 { "reload",                MORE,  2, start_unit        },
                 { "restart",               MORE,  2, start_unit        },
                 { "try-restart",           MORE,  2, start_unit        },
                 { "reload-or-restart",     MORE,  2, start_unit        },
                 { "reload-or-try-restart", MORE,  2, start_unit        },
                 { "force-reload",          MORE,  2, start_unit        }, /* For compatibility with SysV */
+                { "condreload",            MORE,  2, start_unit        }, /* For compatibility with ALTLinux */
                 { "condrestart",           MORE,  2, start_unit        }, /* For compatibility with RH */
                 { "isolate",               EQUAL, 2, start_unit        },
                 { "kill",                  MORE,  2, kill_unit         },
@@ -5170,17 +5285,23 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
 
         /* Require a bus connection for all operations but
          * 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;
+        if (!streq(verbs[i].verb, "enable") && !streq(verbs[i].verb, "disable")) {
+
+                if (running_in_chroot() > 0) {
+                        log_info("Running in chroot, ignoring request.");
+                        return 0;
+                }
+
+                if (!bus) {
+                        log_error("Failed to get D-Bus connection: %s", error->message);
+                        return -EIO;
+                }
         }
 
         return verbs[i].dispatch(bus, argv + optind, left);
 }
 
-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;
@@ -5190,6 +5311,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)
@@ -5201,7 +5323,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;
@@ -5209,7 +5331,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;
@@ -5285,6 +5407,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);
@@ -5358,83 +5481,17 @@ static int runlevel_main(void) {
         return 0;
 }
 
-static void pager_open(void) {
-        int fd[2];
-        const char *pager;
-
-        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;
-        }
-
-        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);
-
-                prctl(PR_SET_PDEATHSIG, SIGTERM);
-
-                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) {
+static void agent_close(void) {
         siginfo_t dummy;
 
-        if (pager_pid <= 0)
+        if (agent_pid <= 0)
                 return;
 
-        /* Inform pager that we are done */
-        fclose(stdout);
-        wait_for_terminate(pager_pid, &dummy);
-        pager_pid = 0;
+        /* 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[]) {
@@ -5462,7 +5519,22 @@ 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 (arg_transport == TRANSPORT_NORMAL)
+                bus_connect(arg_user ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &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) {
 
@@ -5473,6 +5545,7 @@ int main(int argc, char*argv[]) {
         case ACTION_HALT:
         case ACTION_POWEROFF:
         case ACTION_REBOOT:
+        case ACTION_KEXEC:
                 r = halt_main(bus);
                 break;
 
@@ -5492,7 +5565,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:
@@ -5518,6 +5591,7 @@ finish:
         strv_free(arg_property);
 
         pager_close();
+        agent_close();
 
         return retval;
 }