chiark / gitweb /
systemctl: make sure the tty agent does not retain a copy of stdio
[elogind.git] / src / systemctl.c
index 9da8303b2e997d8b1df80e66a91f5c249bf30709..66903aba28a5a1c276e081fa65a9a713be0e93ea 100644 (file)
@@ -31,6 +31,8 @@
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <stddef.h>
+#include <sys/prctl.h>
 
 #include <dbus/dbus.h>
 
 #include "sd-daemon.h"
 #include "shutdownd.h"
 #include "exit-status.h"
+#include "bus-errors.h"
+#include "build.h"
+#include "unit-name.h"
 
 static const char *arg_type = NULL;
 static char **arg_property = NULL;
 static bool arg_all = false;
 static bool arg_fail = false;
-static bool arg_session = false;
+static bool arg_user = false;
 static bool arg_global = false;
 static bool arg_immediate = false;
 static bool arg_no_block = false;
+static bool arg_no_pager = false;
 static bool arg_no_wtmp = false;
 static bool arg_no_sync = false;
 static bool arg_no_wall = false;
@@ -69,12 +75,11 @@ static bool arg_quiet = false;
 static bool arg_full = false;
 static bool arg_force = false;
 static bool arg_defaults = false;
-static bool arg_sysv_compat = false; /* this is undocumented, and
-                                      * exists simply to make
-                                      * implementation of SysV
-                                      * compatible shell glue
-                                      * easier */
+static bool arg_ask_password = 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 usec_t arg_when = 0;
 static enum action {
         ACTION_INVALID,
@@ -82,6 +87,8 @@ static enum action {
         ACTION_HALT,
         ACTION_POWEROFF,
         ACTION_REBOOT,
+        ACTION_KEXEC,
+        ACTION_EXIT,
         ACTION_RUNLEVEL2,
         ACTION_RUNLEVEL3,
         ACTION_RUNLEVEL4,
@@ -103,17 +110,94 @@ static enum dot {
 
 static bool private_bus = false;
 
+static pid_t pager_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;
 
+        /* 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
+         * when run in our own pager. */
+
         if (_unlikely_(t < 0))
                 t = isatty(STDOUT_FILENO) > 0;
 
         return t;
 }
 
+static void spawn_ask_password_agent(void) {
+        pid_t parent, child;
+
+        /* We check STDIN here, not STDOUT, since this is about input,
+         * not output */
+        if (!isatty(STDIN_FILENO))
+                return;
+
+        if (!arg_ask_password)
+                return;
+
+        parent = getpid();
+
+        /* Spawns a temporary TTY agent, making sure it goes away when
+         * we go away */
+
+        if ((child = fork()) < 0)
+                return;
+
+        if (child == 0) {
+                /* In the child */
+                const char * const args[] = {
+                        SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH,
+                        "--watch",
+                        NULL
+                };
+
+                int fd;
+
+                /* 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);
+
+                /* 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);
+                }
+
+                close(STDIN_FILENO);
+                close(STDOUT_FILENO);
+                close(STDERR_FILENO);
+
+                dup2(fd, STDIN_FILENO);
+                dup2(fd, STDOUT_FILENO);
+                dup2(fd, STDERR_FILENO);
+
+                if (fd > 2)
+                        close(fd);
+
+                execv(args[0], (char **) args);
+                _exit(EXIT_FAILURE);
+        }
+}
+
 static const char *ansi_highlight(bool b) {
 
         if (!on_tty())
@@ -130,8 +214,7 @@ static const char *ansi_highlight_green(bool b) {
         return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF;
 }
 
-static bool error_is_no_service(DBusError *error) {
-
+static bool error_is_no_service(const DBusError *error) {
         assert(error);
 
         if (!dbus_error_is_set(error))
@@ -146,6 +229,34 @@ static bool error_is_no_service(DBusError *error) {
         return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
 }
 
+static int translate_bus_error_to_exit_status(int r, const DBusError *error) {
+        assert(error);
+
+        if (!dbus_error_is_set(error))
+                return r;
+
+        if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED) ||
+            dbus_error_has_name(error, BUS_ERROR_ONLY_BY_DEPENDENCY) ||
+            dbus_error_has_name(error, BUS_ERROR_NO_ISOLATION) ||
+            dbus_error_has_name(error, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE))
+                return EXIT_NOPERMISSION;
+
+        if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT))
+                return EXIT_NOTINSTALLED;
+
+        if (dbus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) ||
+            dbus_error_has_name(error, BUS_ERROR_NOT_SUPPORTED))
+                return EXIT_NOTIMPLEMENTED;
+
+        if (dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED))
+                return EXIT_NOTCONFIGURED;
+
+        if (r != 0)
+                return r;
+
+        return EXIT_FAILURE;
+}
+
 static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
 
         assert(iter);
@@ -167,6 +278,7 @@ static void warn_wall(enum action action) {
                 [ACTION_HALT]      = "The system is going down for system halt NOW!",
                 [ACTION_REBOOT]    = "The system is going down for reboot NOW!",
                 [ACTION_POWEROFF]  = "The system is going down for power-off NOW!",
+                [ACTION_KEXEC]     = "The system is going down for kexec reboot NOW!",
                 [ACTION_RESCUE]    = "The system is going down to rescue mode NOW!",
                 [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!"
         };
@@ -183,7 +295,7 @@ static void warn_wall(enum action action) {
                 }
 
                 if (*p) {
-                        utmp_wall(p);
+                        utmp_wall(p, NULL);
                         free(p);
                         return;
                 }
@@ -194,7 +306,7 @@ static void warn_wall(enum action action) {
         if (!table[action])
                 return;
 
-        utmp_wall(table[action]);
+        utmp_wall(table[action], NULL);
 }
 
 struct unit_info {
@@ -227,18 +339,124 @@ 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) {
+        const char *dot;
+
+        return (!arg_type || ((dot = strrchr(u->id, '.')) &&
+                              streq(dot+1, arg_type))) &&
+                (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0);
+}
+
+static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
+        unsigned active_len, sub_len, job_len, n_shown = 0;
+        const struct unit_info *u;
+
+        active_len = sizeof("ACTIVE")-1;
+        sub_len = sizeof("SUB")-1;
+        job_len = sizeof("JOB")-1;
+
+        for (u = unit_infos; u < unit_infos + c; u++) {
+                if (!output_show_job(u))
+                        continue;
+
+                active_len = MAX(active_len, strlen(u->active_state));
+                sub_len = MAX(sub_len, strlen(u->sub_state));
+                if (u->job_id != 0)
+                        job_len = MAX(job_len, strlen(u->job_type));
+        }
+
+        if (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)
+                        printf(" %s\n", "DESCRIPTION");
+                else
+                        printf("\n");
+        }
+
+        for (u = unit_infos; u < unit_infos + c; u++) {
+                char *e;
+                int a = 0, b = 0;
+                const char *on_loaded, *off_loaded;
+                const char *on_active, *off_active;
+
+                if (!output_show_job(u))
+                        continue;
+
+                n_shown++;
+
+                if (!streq(u->load_state, "loaded") &&
+                    !streq(u->load_state, "banned")) {
+                        on_loaded = ansi_highlight(true);
+                        off_loaded = ansi_highlight(false);
+                } else
+                        on_loaded = off_loaded = "";
+
+                if (streq(u->active_state, "failed")) {
+                        on_active = ansi_highlight(true);
+                        off_active = ansi_highlight(false);
+                } else
+                        on_active = off_active = "";
+
+                e = arg_full ? NULL : ellipsize(u->id, 25, 33);
+
+                printf("%-25s %s%-6s%s %s%-*s %-*s%s%n",
+                       e ? e : u->id,
+                       on_loaded, u->load_state, off_loaded,
+                       on_active, active_len, u->active_state,
+                       sub_len, u->sub_state, off_active,
+                       &a);
+
+                free(e);
+
+                a -= strlen(on_loaded) + strlen(off_loaded);
+                a -= strlen(on_active) + strlen(off_active);
+
+                if (u->job_id != 0)
+                        printf(" %-*s", job_len, u->job_type);
+                else
+                        b = 1 + job_len;
+
+                if (a + b + 1 < columns()) {
+                        if (u->job_id == 0)
+                                printf(" %-*s", job_len, "");
+
+                        if (arg_full)
+                                printf(" %s", u->description);
+                        else
+                                printf(" %.*s", columns() - a - b - 1, u->description);
+                }
+
+                fputs("\n", stdout);
+        }
+
+        if (on_tty()) {
+                printf("\nLOAD   = Reflects whether the unit definition was properly loaded.\n"
+                       "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
+                       "SUB    = The low-level unit activation state, values depend on unit type.\n"
+                       "JOB    = Pending job for the unit.\n");
+
+                if (arg_all)
+                        printf("\n%u units listed.\n", n_shown);
+                else
+                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown);
+        }
+}
+
 static int list_units(DBusConnection *bus, char **args, unsigned n) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessageIter iter, sub, sub2;
-        unsigned c = 0, k, n_units = 0;
+        unsigned c = 0, n_units = 0;
         struct unit_info *unit_infos = NULL;
 
         dbus_error_init(&error);
 
         assert(bus);
 
+        pager_open();
+
         if (!(m = dbus_message_new_method_call(
                               "org.freedesktop.systemd1",
                               "/org/freedesktop/systemd1",
@@ -249,7 +467,7 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -311,61 +529,9 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
                 c++;
         }
 
-        qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
-
-        if (isatty(STDOUT_FILENO))
-                printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
-
-        for (k = 0; k < c; k++) {
-                const char *dot;
-                struct unit_info *u = unit_infos+k;
-
-                if ((!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)) {
-                        char *e;
-                        int a = 0, b = 0;
-                        const char *on, *off;
-
-                        if (streq(u->active_state, "maintenance")) {
-                                on = ansi_highlight(true);
-                                off = ansi_highlight(false);
-                        } else
-                                on = off = "";
-
-                        e = arg_full ? NULL : ellipsize(u->id, 45, 33);
-                        printf("%-45s %-6s %s%-12s %-12s%s%n", e ? e : u->id, u->load_state, on, u->active_state, u->sub_state, off, &a);
-                        free(e);
-
-                        a -= strlen(on) + strlen(off);
-
-                        if (u->job_id != 0)
-                                printf(" => %-12s%n", u->job_type, &b);
-                        else
-                                b = 1 + 15;
-
-                        if (a + b + 2 < columns()) {
-                                if (u->job_id == 0)
-                                        printf("                ");
-
-                                printf(" %.*s", columns() - a - b - 2, u->description);
-                        }
-
-                        fputs("\n", stdout);
-                }
-        }
-
-        if (isatty(STDOUT_FILENO)) {
-
-                printf("\nLOAD   = Reflects whether the unit definition was properly loaded.\n"
-                       "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
-                       "SUB    = The low-level unit activation state, values depend on unit type.\n"
-                       "JOB    = Pending job for the unit.\n");
-
-                if (arg_all)
-                        printf("\n%u units listed.\n", c);
-                else
-                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", c);
+        if (c > 0) {
+                qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
+                output_units_list(unit_infos, c);
         }
 
         r = 0;
@@ -473,7 +639,7 @@ static int dot_one(DBusConnection *bus, const char *name, const char *path) {
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -556,7 +722,7 @@ static int dot(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -639,6 +805,8 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
 
         assert(bus);
 
+        pager_open();
+
         if (!(m = dbus_message_new_method_call(
                               "org.freedesktop.systemd1",
                               "/org/freedesktop/systemd1",
@@ -649,7 +817,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -664,7 +832,8 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
 
         dbus_message_iter_recurse(&iter, &sub);
 
-        printf("%4s %-45s %-17s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
+        if (isatty(STDOUT_FILENO))
+                printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
 
         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
                 const char *name, *type, *state, *job_path, *unit_path;
@@ -690,8 +859,8 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
                         goto finish;
                 }
 
-                e = arg_full ? NULL : ellipsize(name, 45, 33);
-                printf("%4u %-45s %-17s %-7s\n", id, e ? e : name, type, state);
+                e = arg_full ? NULL : ellipsize(name, 25, 33);
+                printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state);
                 free(e);
 
                 k++;
@@ -699,7 +868,9 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
                 dbus_message_iter_next(&sub);
         }
 
-        printf("\n%u jobs listed.\n", k);
+        if (isatty(STDOUT_FILENO))
+                printf("\n%u jobs listed.\n", k);
+
         r = 0;
 
 finish:
@@ -746,7 +917,7 @@ static int load_unit(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", error.message);
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -814,7 +985,7 @@ static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", error.message);
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -822,7 +993,7 @@ static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
                 if (!dbus_message_get_args(reply, &error,
                                            DBUS_TYPE_OBJECT_PATH, &path,
                                            DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", error.message);
+                        log_error("Failed to parse reply: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -840,7 +1011,7 @@ static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
 
                 dbus_message_unref(reply);
                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", error.message);
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -969,7 +1140,7 @@ static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *me
                                            DBUS_TYPE_OBJECT_PATH, &path,
                                            DBUS_TYPE_BOOLEAN, &success,
                                            DBUS_TYPE_INVALID))
-                        log_error("Failed to parse message: %s", error.message);
+                        log_error("Failed to parse message: %s", bus_error_message(&error));
                 else {
                         char *p;
 
@@ -1003,7 +1174,7 @@ static int enable_wait_for_jobs(DBusConnection *bus) {
                            &error);
 
         if (dbus_error_is_set(&error)) {
-                log_error("Failed to add match: %s", error.message);
+                log_error("Failed to add match: %s", bus_error_message(&error));
                 dbus_error_free(&error);
                 return -EIO;
         }
@@ -1034,7 +1205,7 @@ static int wait_for_jobs(DBusConnection *bus, Set *s) {
                 ;
 
         if (!arg_quiet && d.failed)
-                log_error("Job failed, see logs for details.");
+                log_error("Job failed. See system logs and 'systemctl status' for details.");
 
         r = d.failed ? -EIO : 0;
 
@@ -1049,10 +1220,10 @@ static int start_unit_one(
                 const char *method,
                 const char *name,
                 const char *mode,
+                DBusError *error,
                 Set *s) {
 
         DBusMessage *m = NULL, *reply = NULL;
-        DBusError error;
         const char *path;
         int r;
 
@@ -1060,10 +1231,9 @@ static int start_unit_one(
         assert(method);
         assert(name);
         assert(mode);
+        assert(error);
         assert(arg_no_block || s);
 
-        dbus_error_init(&error);
-
         if (!(m = dbus_message_new_method_call(
                               "org.freedesktop.systemd1",
                               "/org/freedesktop/systemd1",
@@ -1083,31 +1253,31 @@ static int start_unit_one(
                 goto finish;
         }
 
-        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error))) {
 
-                if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
+                if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(error)) {
                         /* There's always a fallback possible for
                          * legacy actions. */
-                        r = 0;
+                        r = -EADDRNOTAVAIL;
                         goto finish;
                 }
 
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(error));
                 r = -EIO;
                 goto finish;
         }
 
-        if (!dbus_message_get_args(reply, &error,
+        if (!dbus_message_get_args(reply, error,
                                    DBUS_TYPE_OBJECT_PATH, &path,
                                    DBUS_TYPE_INVALID)) {
-                log_error("Failed to parse reply: %s", error.message);
+                log_error("Failed to parse reply: %s", bus_error_message(error));
                 r = -EIO;
                 goto finish;
         }
 
         if (need_daemon_reload(bus, name))
                 log_warning("Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
-                            arg_session ? "--session" : "--system");
+                            arg_user ? "--user" : "--system");
 
         if (!arg_no_block) {
                 char *p;
@@ -1125,7 +1295,7 @@ static int start_unit_one(
                 }
         }
 
-        r = 1;
+        r = 0;
 
 finish:
         if (m)
@@ -1134,8 +1304,6 @@ finish:
         if (reply)
                 dbus_message_unref(reply);
 
-        dbus_error_free(&error);
-
         return r;
 }
 
@@ -1146,12 +1314,16 @@ static enum action verb_to_action(const char *verb) {
                 return ACTION_POWEROFF;
         else if (streq(verb, "reboot"))
                 return ACTION_REBOOT;
+        else if (streq(verb, "kexec"))
+                return ACTION_KEXEC;
         else if (streq(verb, "rescue"))
                 return ACTION_RESCUE;
         else if (streq(verb, "emergency"))
                 return ACTION_EMERGENCY;
         else if (streq(verb, "default"))
                 return ACTION_DEFAULT;
+        else if (streq(verb, "exit"))
+                return ACTION_EXIT;
         else
                 return ACTION_INVALID;
 }
@@ -1162,32 +1334,39 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
                 [ACTION_HALT] = SPECIAL_HALT_TARGET,
                 [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
                 [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
+                [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET,
                 [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
                 [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
                 [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
                 [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
                 [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
                 [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
-                [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET
+                [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
+                [ACTION_EXIT] = SPECIAL_EXIT_TARGET
         };
 
-        int r;
+        int r, ret = 0;
         unsigned i;
         const char *method, *mode, *one_name;
         Set *s = NULL;
+        DBusError error;
+
+        dbus_error_init(&error);
 
         assert(bus);
 
+        spawn_ask_password_agent();
+
         if (arg_action == ACTION_SYSTEMCTL) {
                 method =
                         streq(args[0], "stop")                  ? "StopUnit" :
                         streq(args[0], "reload")                ? "ReloadUnit" :
                         streq(args[0], "restart")               ? "RestartUnit" :
-                        streq(args[0], "try-restart")           ? "TryRestartUnit" :
+                        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], "force-reload")          ||
-                        streq(args[0], "condrestart")           ? "ReloadOrTryRestartUnit" :
+                        streq(args[0], "force-reload")          ? "ReloadOrTryRestartUnit" :
                                                                   "StartUnit";
 
                 mode =
@@ -1206,46 +1385,52 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
                 method = "StartUnit";
 
                 mode = (arg_action == ACTION_EMERGENCY ||
-                        arg_action == ACTION_RESCUE) ? "isolate" : "replace";
+                        arg_action == ACTION_RESCUE ||
+                        arg_action == ACTION_RUNLEVEL2 ||
+                        arg_action == ACTION_RUNLEVEL3 ||
+                        arg_action == ACTION_RUNLEVEL4 ||
+                        arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace";
 
                 one_name = table[arg_action];
         }
 
         if (!arg_no_block) {
-                if ((r = enable_wait_for_jobs(bus)) < 0) {
-                        log_error("Could not watch jobs: %s", strerror(-r));
+                if ((ret = enable_wait_for_jobs(bus)) < 0) {
+                        log_error("Could not watch jobs: %s", strerror(-ret));
                         goto finish;
                 }
 
                 if (!(s = set_new(string_hash_func, string_compare_func))) {
                         log_error("Failed to allocate set.");
-                        r = -ENOMEM;
+                        ret = -ENOMEM;
                         goto finish;
                 }
         }
 
-        r = 0;
-
         if (one_name) {
-                if ((r = start_unit_one(bus, method, one_name, mode, s)) <= 0)
+                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, s)) < 0)
-                                goto finish;
+                        if ((r = start_unit_one(bus, method, args[i], mode, &error, s)) != 0) {
+                                ret = translate_bus_error_to_exit_status(r, &error);
+                                dbus_error_free(&error);
+                        }
         }
 
         if (!arg_no_block)
-                if ((r = wait_for_jobs(bus, s)) < 0)
+                if ((r = wait_for_jobs(bus, s)) < 0) {
+                        ret = r;
                         goto finish;
-
-        r = 1;
+                }
 
 finish:
         if (s)
                 set_free_free(s);
 
-        return r;
+        dbus_error_free(&error);
+
+        return ret;
 }
 
 static int start_special(DBusConnection *bus, char **args, unsigned n) {
@@ -1254,6 +1439,14 @@ static int start_special(DBusConnection *bus, char **args, unsigned n) {
         assert(bus);
         assert(args);
 
+        if (arg_force &&
+            (streq(args[0], "halt") ||
+             streq(args[0], "poweroff") ||
+             streq(args[0], "reboot") ||
+             streq(args[0], "kexec") ||
+             streq(args[0], "exit")))
+                return daemon_reload(bus, args, n);
+
         r = start_unit(bus, args, n);
 
         if (r >= 0)
@@ -1267,7 +1460,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
         const char
                 *interface = "org.freedesktop.systemd1.Unit",
                 *property = "ActiveState";
-        int r = -EADDRNOTAVAIL;
+        int r = 3; /* According to LSB: "program is not running" */
         DBusError error;
         unsigned i;
 
@@ -1306,13 +1499,14 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
                                 puts("unknown");
 
                         dbus_error_free(&error);
+                        dbus_message_unref(m);
                         continue;
                 }
 
                 if (!dbus_message_get_args(reply, &error,
                                            DBUS_TYPE_OBJECT_PATH, &path,
                                            DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", error.message);
+                        log_error("Failed to parse reply: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -1339,7 +1533,7 @@ static int check_unit(DBusConnection *bus, char **args, unsigned n) {
 
                 dbus_message_unref(reply);
                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", error.message);
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -1384,7 +1578,74 @@ finish:
         return r;
 }
 
+static int kill_unit(DBusConnection *bus, char **args, unsigned n) {
+        DBusMessage *m = NULL, *reply = NULL;
+        int r = 0;
+        DBusError error;
+        unsigned i;
+
+        assert(bus);
+        assert(args);
+
+        dbus_error_init(&error);
+
+        if (!arg_kill_who)
+                arg_kill_who = "all";
+
+        if (!arg_kill_mode)
+                arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
+
+        for (i = 1; i < n; i++) {
+
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      "/org/freedesktop/systemd1",
+                                      "org.freedesktop.systemd1.Manager",
+                                      "KillUnit"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, &args[i],
+                                              DBUS_TYPE_STRING, &arg_kill_who,
+                                              DBUS_TYPE_STRING, &arg_kill_mode,
+                                              DBUS_TYPE_INT32, &arg_signal,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                        dbus_error_free(&error);
+                        r = -EIO;
+                }
+
+                dbus_message_unref(m);
+
+                if (reply)
+                        dbus_message_unref(reply);
+                m = reply = NULL;
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
 typedef struct ExecStatusInfo {
+        char *name;
+
         char *path;
         char **argv;
 
@@ -1402,6 +1663,7 @@ typedef struct ExecStatusInfo {
 static void exec_status_info_free(ExecStatusInfo *i) {
         assert(i);
 
+        free(i->name);
         free(i->path);
         strv_free(i->argv);
         free(i);
@@ -1485,6 +1747,7 @@ typedef struct UnitStatusInfo {
         const char *sub_state;
 
         const char *description;
+        const char *following;
 
         const char *path;
         const char *default_control_group;
@@ -1501,7 +1764,9 @@ typedef struct UnitStatusInfo {
         pid_t control_pid;
         const char *status_text;
         bool running:1;
+#ifdef HAVE_SYSV_COMPAT
         bool is_sysv:1;
+#endif
 
         usec_t start_timestamp;
         usec_t exit_timestamp;
@@ -1544,7 +1809,11 @@ static void print_status_info(UnitStatusInfo *i) {
 
         printf("\n");
 
-        if (streq_ptr(i->load_state, "failed")) {
+        if (i->following)
+                printf("\t  Follow: unit currently follows state of %s\n", i->following);
+
+        if (streq_ptr(i->load_state, "failed") ||
+            streq_ptr(i->load_state, "banned")) {
                 on = ansi_highlight(true);
                 off = ansi_highlight(false);
         } else
@@ -1557,7 +1826,7 @@ static void print_status_info(UnitStatusInfo *i) {
 
         ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
 
-        if (streq_ptr(i->active_state, "maintenance")) {
+        if (streq_ptr(i->active_state, "failed")) {
                 on = ansi_highlight(true);
                 off = ansi_highlight(false);
         } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
@@ -1581,7 +1850,7 @@ static void print_status_info(UnitStatusInfo *i) {
         timestamp = (streq_ptr(i->active_state, "active")      ||
                      streq_ptr(i->active_state, "reloading"))   ? i->active_enter_timestamp :
                     (streq_ptr(i->active_state, "inactive")    ||
-                     streq_ptr(i->active_state, "maintenance")) ? i->inactive_enter_timestamp :
+                     streq_ptr(i->active_state, "failed"))      ? i->inactive_enter_timestamp :
                     streq_ptr(i->active_state, "activating")    ? i->inactive_exit_timestamp :
                                                                   i->active_exit_timestamp;
 
@@ -1589,17 +1858,17 @@ static void print_status_info(UnitStatusInfo *i) {
         s2 = format_timestamp(since2, sizeof(since2), timestamp);
 
         if (s1)
-                printf(" since [%s; %s]\n", s2, s1);
+                printf(" since %s; %s\n", s2, s1);
         else if (s2)
-                printf(" since [%s]\n", s2);
+                printf(" since %s\n", s2);
         else
                 printf("\n");
 
         if (i->sysfs_path)
                 printf("\t  Device: %s\n", i->sysfs_path);
-        else if (i->where)
+        if (i->where)
                 printf("\t   Where: %s\n", i->where);
-        else if (i->what)
+        if (i->what)
                 printf("\t    What: %s\n", i->what);
 
         if (i->accept)
@@ -1607,26 +1876,49 @@ static void print_status_info(UnitStatusInfo *i) {
 
         LIST_FOREACH(exec, p, i->exec) {
                 char *t;
+                bool good;
 
                 /* Only show exited processes here */
                 if (p->code == 0)
                         continue;
 
                 t = strv_join(p->argv, " ");
-                printf("\t Process: %u (%s, code=%s, ", p->pid, strna(t), sigchld_code_to_string(p->code));
+                printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
                 free(t);
 
+#ifdef HAVE_SYSV_COMPAT
+                if (i->is_sysv)
+                        good = is_clean_exit_lsb(p->code, p->status);
+                else
+#endif
+                        good = is_clean_exit(p->code, p->status);
+
+                if (!good) {
+                        on = ansi_highlight(true);
+                        off = ansi_highlight(false);
+                } else
+                        on = off = "";
+
+                printf("%s(code=%s, ", on, sigchld_code_to_string(p->code));
+
                 if (p->code == CLD_EXITED) {
                         const char *c;
 
                         printf("status=%i", p->status);
 
+#ifdef HAVE_SYSV_COMPAT
                         if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+#else
+                        if ((c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD)))
+#endif
                                 printf("/%s", c);
 
                 } else
                         printf("signal=%s", signal_to_string(p->status));
-                printf(")\n");
+
+                printf(")%s\n", off);
+
+                on = off = NULL;
 
                 if (i->main_pid == p->pid &&
                     i->start_timestamp == p->start_timestamp &&
@@ -1659,7 +1951,11 @@ static void print_status_info(UnitStatusInfo *i) {
 
                                         printf("status=%i", i->exit_status);
 
+#ifdef HAVE_SYSV_COMPAT
                                         if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
+#else
+                                        if ((c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD)))
+#endif
                                                 printf("/%s", c);
 
                                 } else
@@ -1706,7 +2002,7 @@ static void print_status_info(UnitStatusInfo *i) {
                 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
                        ansi_highlight(true),
                        ansi_highlight(false),
-                       arg_session ? "--session" : "--system");
+                       arg_user ? "--user" : "--system");
 }
 
 static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
@@ -1731,10 +2027,13 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 i->description = s;
                         else if (streq(name, "FragmentPath"))
                                 i->path = s;
+#ifdef HAVE_SYSV_COMPAT
                         else if (streq(name, "SysVPath")) {
                                 i->is_sysv = true;
                                 i->path = s;
-                        } else if (streq(name, "DefaultControlGroup"))
+                        }
+#endif
+                        else if (streq(name, "DefaultControlGroup"))
                                 i->default_control_group = s;
                         else if (streq(name, "StatusText"))
                                 i->status_text = s;
@@ -1744,6 +2043,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 i->where = s;
                         else if (streq(name, "What"))
                                 i->what = s;
+                        else if (streq(name, "Following"))
+                                i->following = s;
                 }
 
                 break;
@@ -1833,6 +2134,11 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                                 if (!(info = new0(ExecStatusInfo, 1)))
                                         return -ENOMEM;
 
+                                if (!(info->name = strdup(name))) {
+                                        free(info);
+                                        return -ENOMEM;
+                                }
+
                                 if ((r = exec_status_info_deserialize(&sub, info)) < 0) {
                                         free(info);
                                         return r;
@@ -1923,6 +2229,14 @@ static int print_property(const char *name, DBusMessageIter *iter) {
                 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);
@@ -2094,7 +2408,7 @@ static int print_property(const char *name, DBusMessageIter *iter) {
         return 0;
 }
 
-static int show_one(DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
+static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
         DBusMessage *m = NULL, *reply = NULL;
         const char *interface = "";
         int r;
@@ -2129,7 +2443,7 @@ static int show_one(DBusConnection *bus, const char *path, bool show_properties,
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -2190,19 +2504,14 @@ static int show_one(DBusConnection *bus, const char *path, bool show_properties,
 
         r = 0;
 
-        if (!show_properties) {
-                if (arg_sysv_compat &&
-                    !streq_ptr(info.active_state, "active") &&
-                    !streq_ptr(info.active_state, "reloading")) {
+        if (!show_properties)
+                print_status_info(&info);
 
-                        /* If the SysV compatibility mode is on, we
-                         * will refuse to run "status" on units that
-                         * aren't active */
-                        log_error("Unit not active.");
-                        r = -EADDRNOTAVAIL;
-                } else
-                        print_status_info(&info);
-        }
+        if (!streq_ptr(info.active_state, "active") &&
+            !streq_ptr(info.active_state, "reloading") &&
+            streq(verb, "status"))
+                /* According to LSB: "program not running" */
+                r = 3;
 
         while ((p = info.exec)) {
                 LIST_REMOVE(ExecStatusInfo, exec, info.exec, p);
@@ -2223,7 +2532,7 @@ finish:
 
 static int show(DBusConnection *bus, char **args, unsigned n) {
         DBusMessage *m = NULL, *reply = NULL;
-        int r;
+        int r, ret = 0;
         DBusError error;
         unsigned i;
         bool show_properties, new_line = false;
@@ -2235,11 +2544,14 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
 
         show_properties = !streq(args[0], "status");
 
+        if (show_properties)
+                pager_open();
+
         if (show_properties && n <= 1) {
                 /* If not argument is specified inspect the manager
                  * itself */
 
-                r = show_one(bus, "/org/freedesktop/systemd1", show_properties, &new_line);
+                ret = show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line);
                 goto finish;
         }
 
@@ -2257,7 +2569,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                               "org.freedesktop.systemd1.Manager",
                                               "LoadUnit"))) {
                                 log_error("Could not allocate message.");
-                                r = -ENOMEM;
+                                ret = -ENOMEM;
                                 goto finish;
                         }
 
@@ -2265,15 +2577,15 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                                       DBUS_TYPE_STRING, &args[i],
                                                       DBUS_TYPE_INVALID)) {
                                 log_error("Could not append arguments to message.");
-                                r = -ENOMEM;
+                                ret = -ENOMEM;
                                 goto finish;
                         }
 
                         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
 
                                 if (!dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
-                                        log_error("Failed to issue method call: %s", error.message);
-                                        r = -EIO;
+                                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+                                        ret = -EIO;
                                         goto finish;
                                 }
 
@@ -2286,7 +2598,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                                       "org.freedesktop.systemd1.Manager",
                                                       "GetUnit"))) {
                                         log_error("Could not allocate message.");
-                                        r = -ENOMEM;
+                                        ret = -ENOMEM;
                                         goto finish;
                                 }
 
@@ -2294,13 +2606,17 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                                               DBUS_TYPE_STRING, &args[i],
                                                               DBUS_TYPE_INVALID)) {
                                         log_error("Could not append arguments to message.");
-                                        r = -ENOMEM;
+                                        ret = -ENOMEM;
                                         goto finish;
                                 }
 
                                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                                        log_error("Failed to issue method call: %s", error.message);
-                                        r = -EIO;
+                                        log_error("Failed to issue method call: %s", bus_error_message(&error));
+
+                                        if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT))
+                                                ret = 4; /* According to LSB: "program or service status is unknown" */
+                                        else
+                                                ret = -EIO;
                                         goto finish;
                                 }
                         }
@@ -2315,7 +2631,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                               "org.freedesktop.systemd1.Manager",
                                               "GetJob"))) {
                                 log_error("Could not allocate message.");
-                                r = -ENOMEM;
+                                ret = -ENOMEM;
                                 goto finish;
                         }
 
@@ -2323,13 +2639,13 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                                       DBUS_TYPE_UINT32, &id,
                                                       DBUS_TYPE_INVALID)) {
                                 log_error("Could not append arguments to message.");
-                                r = -ENOMEM;
+                                ret = -ENOMEM;
                                 goto finish;
                         }
 
                         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                                log_error("Failed to issue method call: %s", error.message);
-                                r = -EIO;
+                                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                                ret = -EIO;
                                 goto finish;
                         }
                 } else {
@@ -2342,7 +2658,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                               "org.freedesktop.systemd1.Manager",
                                               "GetUnitByPID"))) {
                                 log_error("Could not allocate message.");
-                                r = -ENOMEM;
+                                ret = -ENOMEM;
                                 goto finish;
                         }
 
@@ -2350,13 +2666,13 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                                       DBUS_TYPE_UINT32, &id,
                                                       DBUS_TYPE_INVALID)) {
                                 log_error("Could not append arguments to message.");
-                                r = -ENOMEM;
+                                ret = -ENOMEM;
                                 goto finish;
                         }
 
                         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                                log_error("Failed to issue method call: %s", error.message);
-                                r = -EIO;
+                                log_error("Failed to issue method call: %s", bus_error_message(&error));
+                                ret = -EIO;
                                 goto finish;
                         }
                 }
@@ -2364,21 +2680,19 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                 if (!dbus_message_get_args(reply, &error,
                                            DBUS_TYPE_OBJECT_PATH, &path,
                                            DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", error.message);
-                        r = -EIO;
+                        log_error("Failed to parse reply: %s", bus_error_message(&error));
+                        ret = -EIO;
                         goto finish;
                 }
 
-                if ((r = show_one(bus, path, show_properties, &new_line)) < 0)
-                        goto finish;
+                if ((r = show_one(args[0], bus, path, show_properties, &new_line)) != 0)
+                        ret = r;
 
                 dbus_message_unref(m);
                 dbus_message_unref(reply);
                 m = reply = NULL;
         }
 
-        r = 0;
-
 finish:
         if (m)
                 dbus_message_unref(m);
@@ -2388,7 +2702,7 @@ finish:
 
         dbus_error_free(&error);
 
-        return r;
+        return ret;
 }
 
 static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage *message, void *data) {
@@ -2417,7 +2731,7 @@ static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage
                                            DBUS_TYPE_STRING, &id,
                                            DBUS_TYPE_OBJECT_PATH, &path,
                                            DBUS_TYPE_INVALID))
-                        log_error("Failed to parse message: %s", error.message);
+                        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
@@ -2432,7 +2746,7 @@ static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage
                                            DBUS_TYPE_UINT32, &id,
                                            DBUS_TYPE_OBJECT_PATH, &path,
                                            DBUS_TYPE_INVALID))
-                        log_error("Failed to parse message: %s", error.message);
+                        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
@@ -2449,7 +2763,7 @@ static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage
                 if (!dbus_message_get_args(message, &error,
                                           DBUS_TYPE_STRING, &interface,
                                           DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse message: %s", error.message);
+                        log_error("Failed to parse message: %s", bus_error_message(&error));
                         goto finish;
                 }
 
@@ -2475,7 +2789,7 @@ static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage
                 }
 
                 if (!(reply = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", error.message);
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
                         goto finish;
                 }
 
@@ -2547,7 +2861,7 @@ static int monitor(DBusConnection *bus, char **args, unsigned n) {
                                    &error);
 
                 if (dbus_error_is_set(&error)) {
-                        log_error("Failed to add match: %s", error.message);
+                        log_error("Failed to add match: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -2560,7 +2874,7 @@ static int monitor(DBusConnection *bus, char **args, unsigned n) {
                                    &error);
 
                 if (dbus_error_is_set(&error)) {
-                        log_error("Failed to add match: %s", error.message);
+                        log_error("Failed to add match: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -2583,7 +2897,7 @@ static int monitor(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -2616,6 +2930,8 @@ static int dump(DBusConnection *bus, char **args, unsigned n) {
 
         dbus_error_init(&error);
 
+        pager_open();
+
         if (!(m = dbus_message_new_method_call(
                               "org.freedesktop.systemd1",
                               "/org/freedesktop/systemd1",
@@ -2626,7 +2942,7 @@ static int dump(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -2634,7 +2950,7 @@ static int dump(DBusConnection *bus, char **args, unsigned n) {
         if (!dbus_message_get_args(reply, &error,
                                    DBUS_TYPE_STRING, &text,
                                    DBUS_TYPE_INVALID)) {
-                log_error("Failed to parse reply: %s", error.message);
+                log_error("Failed to parse reply: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -2690,7 +3006,7 @@ static int snapshot(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -2698,7 +3014,7 @@ static int snapshot(DBusConnection *bus, char **args, unsigned n) {
         if (!dbus_message_get_args(reply, &error,
                                    DBUS_TYPE_OBJECT_PATH, &path,
                                    DBUS_TYPE_INVALID)) {
-                log_error("Failed to parse reply: %s", error.message);
+                log_error("Failed to parse reply: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -2724,7 +3040,7 @@ static int snapshot(DBusConnection *bus, char **args, unsigned n) {
 
         dbus_message_unref(reply);
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -2795,7 +3111,7 @@ static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", error.message);
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -2803,7 +3119,7 @@ static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
                 if (!dbus_message_get_args(reply, &error,
                                            DBUS_TYPE_OBJECT_PATH, &path,
                                            DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", error.message);
+                        log_error("Failed to parse reply: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -2821,7 +3137,7 @@ static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
 
                 dbus_message_unref(reply);
                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", error.message);
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -2861,12 +3177,16 @@ static int daemon_reload(DBusConnection *bus, char **args, unsigned n) {
                 assert(arg_action == ACTION_SYSTEMCTL);
 
                 method =
-                        streq(args[0], "clear-jobs")        ||
-                        streq(args[0], "cancel")            ? "ClearJobs" :
-                        streq(args[0], "daemon-reexec")     ? "Reexecute" :
-                        streq(args[0], "reset-maintenance") ? "ResetMaintenance" :
-                        streq(args[0], "daemon-exit")       ? "Exit" :
-                                                              "Reload";
+                        streq(args[0], "clear-jobs")    ||
+                        streq(args[0], "cancel")        ? "ClearJobs" :
+                        streq(args[0], "daemon-reexec") ? "Reexecute" :
+                        streq(args[0], "reset-failed")  ? "ResetFailed" :
+                        streq(args[0], "halt")          ? "Halt" :
+                        streq(args[0], "poweroff")      ? "PowerOff" :
+                        streq(args[0], "reboot")        ? "Reboot" :
+                        streq(args[0], "kexec")         ? "KExec" :
+                        streq(args[0], "exit")          ? "Exit" :
+                                    /* "daemon-reload" */ "Reload";
         }
 
         if (!(m = dbus_message_new_method_call(
@@ -2883,16 +3203,16 @@ static int daemon_reload(DBusConnection *bus, char **args, unsigned n) {
                 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
                         /* There's always a fallback possible for
                          * legacy actions. */
-                        r = 0;
+                        r = -EADDRNOTAVAIL;
                         goto finish;
                 }
 
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
 
-        r = 1;
+        r = 0;
 
 finish:
         if (m)
@@ -2906,7 +3226,7 @@ finish:
         return r;
 }
 
-static int reset_maintenance(DBusConnection *bus, char **args, unsigned n) {
+static int reset_failed(DBusConnection *bus, char **args, unsigned n) {
         DBusMessage *m = NULL, *reply = NULL;
         unsigned i;
         int r;
@@ -2924,7 +3244,7 @@ static int reset_maintenance(DBusConnection *bus, char **args, unsigned n) {
                                       "org.freedesktop.systemd1",
                                       "/org/freedesktop/systemd1",
                                       "org.freedesktop.systemd1.Manager",
-                                      "ResetMaintenanceUnit"))) {
+                                      "ResetFailedUnit"))) {
                         log_error("Could not allocate message.");
                         r = -ENOMEM;
                         goto finish;
@@ -2939,7 +3259,7 @@ static int reset_maintenance(DBusConnection *bus, char **args, unsigned n) {
                 }
 
                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                        log_error("Failed to issue method call: %s", error.message);
+                        log_error("Failed to issue method call: %s", bus_error_message(&error));
                         r = -EIO;
                         goto finish;
                 }
@@ -2974,6 +3294,8 @@ static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
 
         dbus_error_init(&error);
 
+        pager_open();
+
         if (!(m = dbus_message_new_method_call(
                               "org.freedesktop.systemd1",
                               "/org/freedesktop/systemd1",
@@ -2993,7 +3315,7 @@ static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -3091,7 +3413,7 @@ static int set_environment(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -3120,6 +3442,7 @@ typedef struct {
 
 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);
@@ -3140,28 +3463,16 @@ static void install_info_hashmap_free(Hashmap *m) {
         hashmap_free(m);
 }
 
-static bool unit_name_valid(const char *name) {
-
-        /* This is a minimal version of unit_name_valid() from
-         * unit-name.c */
-
-        if (!*name)
-                return false;
-
-        if (ignore_file(name))
-                return false;
-
-        return true;
-}
-
 static int install_info_add(const char *name) {
         InstallInfo *i;
         int r;
 
         assert(will_install);
 
-        if (!unit_name_valid(name))
+        if (!unit_name_is_valid_no_type(name, true)) {
+                log_warning("Unit name %s is not a valid unit name.", name);
                 return -EINVAL;
+        }
 
         if (hashmap_get(have_installed, name) ||
             hashmap_get(will_install, name))
@@ -3213,11 +3524,13 @@ static int config_parse_also(
                 if (!(n = strndup(w, l)))
                         return -ENOMEM;
 
-                r = install_info_add(n);
-                free(n);
-
-                if (r < 0)
+                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;
@@ -3542,12 +3855,6 @@ static int install_info_symlink_alias(const char *verb, InstallInfo *i, const ch
 
         STRV_FOREACH(s, i->aliases) {
 
-                if (!unit_name_valid(*s)) {
-                        log_error("Invalid name %s.", *s);
-                        r = -EINVAL;
-                        goto finish;
-                }
-
                 free(alias_path);
                 if (!(alias_path = path_make_absolute(*s, config_path))) {
                         log_error("Out of memory");
@@ -3561,7 +3868,6 @@ static int install_info_symlink_alias(const char *verb, InstallInfo *i, const ch
                 if (streq(verb, "disable"))
                         rmdir_parents(alias_path, config_path);
         }
-
         r = 0;
 
 finish:
@@ -3580,7 +3886,7 @@ static int install_info_symlink_wants(const char *verb, InstallInfo *i, const ch
         assert(config_path);
 
         STRV_FOREACH(s, i->wanted_by) {
-                if (!unit_name_valid(*s)) {
+                if (!unit_name_is_valid_no_type(*s, true)) {
                         log_error("Invalid name %s.", *s);
                         r = -EINVAL;
                         goto finish;
@@ -3658,6 +3964,68 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
         }
 
         if (!f) {
+#if defined(TARGET_FEDORA) && defined (HAVE_SYSV_COMPAT)
+
+                if (endswith(i->name, ".service")) {
+                        char *sysv;
+                        bool exists;
+
+                        if (asprintf(&sysv, SYSTEM_SYSVINIT_PATH "/%s", i->name) < 0) {
+                                log_error("Out of memory");
+                                return -ENOMEM;
+                        }
+
+                        sysv[strlen(sysv) - sizeof(".service") + 1] = 0;
+                        exists = access(sysv, F_OK) >= 0;
+
+                        if (exists) {
+                                pid_t pid;
+                                siginfo_t status;
+
+                                const char *argv[] = {
+                                        "/sbin/chkconfig",
+                                        NULL,
+                                        NULL,
+                                        NULL
+                                };
+
+                                log_info("%s is not a native service, redirecting to /sbin/chkconfig.", i->name);
+
+                                argv[1] = file_name_from_path(sysv);
+                                argv[2] =
+                                        streq(verb, "enable") ? "on" :
+                                        streq(verb, "disable") ? "off" : NULL;
+
+                                log_info("Executing %s %s %s", argv[0], argv[1], strempty(argv[2]));
+
+                                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);
+                                }
+
+                                free(sysv);
+
+                                if ((r = wait_for_terminate(pid, &status)) < 0)
+                                        return r;
+
+                                if (status.si_code == CLD_EXITED) {
+                                        if (status.si_status == 0 && (streq(verb, "enable") || streq(verb, "disable")))
+                                                n_symlinks ++;
+
+                                        return status.si_status == 0 ? 0 : -EINVAL;
+                                } else
+                                        return -EPROTO;
+                        }
+
+                        free(sysv);
+                }
+
+#endif
+
                 log_error("Couldn't find %s.", i->name);
                 return -ENOENT;
         }
@@ -3669,6 +4037,9 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
                 return r;
         }
 
+        n_symlinks += strv_length(i->aliases);
+        n_symlinks += strv_length(i->wanted_by);
+
         fclose(f);
 
         if ((r = install_info_symlink_alias(verb, i, config_path)) != 0)
@@ -3688,13 +4059,13 @@ static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo
 
 static char *get_config_path(void) {
 
-        if (arg_session && arg_global)
-                return strdup(SESSION_CONFIG_UNIT_PATH);
+        if (arg_user && arg_global)
+                return strdup(USER_CONFIG_UNIT_PATH);
 
-        if (arg_session) {
+        if (arg_user) {
                 char *p;
 
-                if (session_config_home(&p) < 0)
+                if (user_config_home(&p) < 0)
                         return NULL;
 
                 return p;
@@ -3715,7 +4086,7 @@ static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
         dbus_error_init(&error);
 
         zero(paths);
-        if ((r = lookup_paths_init(&paths, arg_session ? MANAGER_SESSION : MANAGER_SYSTEM)) < 0) {
+        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;
         }
@@ -3743,8 +4114,10 @@ static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
                 }
 
         for (j = 1; j < n; j++)
-                if ((r = install_info_add(args[j])) < 0)
+                if ((r = install_info_add(args[j])) < 0) {
+                        log_warning("Cannot install unit %s: %s", args[j], strerror(-r));
                         goto finish;
+                }
 
         while ((i = hashmap_first(will_install))) {
                 int q;
@@ -3767,19 +4140,24 @@ static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
 
         if (streq(verb, "is-enabled"))
                 r = r > 0 ? 0 : -ENOENT;
-        else 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_session || sd_booted() > 0) &&
-                 /* Don't try to reload anything if we are running in a chroot environment */
-                 (arg_session || running_in_chroot() <= 0) ) {
-                int q;
+        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 ((q = daemon_reload(bus, args, n)) < 0)
+                                r = q;
+                }
         }
 
 finish:
@@ -3799,25 +4177,33 @@ static int systemctl_help(void) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
                "Send control commands to or query the systemd manager.\n\n"
-               "  -h --help          Show this help\n"
-               "  -t --type=TYPE     List only units of a particular type\n"
-               "  -p --property=NAME Show only properties by this name\n"
-               "  -a --all           Show all units/properties, including dead/empty ones\n"
-               "     --full          Don't ellipsize unit names on output\n"
-               "     --fail          When queueing a new job, fail if conflicting jobs are\n"
-               "                     pending\n"
-               "  -q --quiet         Suppress output\n"
-               "     --no-block      Do not wait until operation finished\n"
-               "     --system        Connect to system bus\n"
-               "     --session       Connect to session bus\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"
-               "     --force         When enabling unit files, override existing symlinks\n"
-               "     --defaults      When disabling unit files, remove default symlinks only\n\n"
+               "  -h --help           Show this help\n"
+               "     --version        Show package version\n"
+               "  -t --type=TYPE      List only units of a particular type\n"
+               "  -p --property=NAME  Show only properties by this name\n"
+               "  -a --all            Show all units/properties, including dead/empty ones\n"
+               "     --full           Don't ellipsize unit names on output\n"
+               "     --fail           When queueing a new job, fail if conflicting jobs are\n"
+               "                      pending\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-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"
+               "  -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"
                "  start [NAME...]                 Start (activate) one or more units\n"
@@ -3830,12 +4216,13 @@ static int systemctl_help(void) {
                "  reload-or-try-restart [NAME...] Reload one or more units is possible,\n"
                "                                  otherwise restart if active\n"
                "  isolate [NAME]                  Start one unit and stop all others\n"
+               "  kill [NAME...]                  Send signal to processes of a unit\n"
                "  is-active [NAME...]             Check whether units are active\n"
                "  status [NAME...|PID...]         Show runtime status of one or more units\n"
                "  show [NAME...|JOB...]           Show properties of one or more\n"
                "                                  units/jobs or the manager\n"
-               "  reset-maintenance [NAME...]     Reset maintenance state for all, one,\n"
-               "                                  or more units\n"
+               "  reset-failed [NAME...]          Reset failed state for all, one, or more\n"
+               "                                  units\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"
@@ -3849,16 +4236,17 @@ static int systemctl_help(void) {
                "  delete [NAME...]                Remove one or more snapshots\n"
                "  daemon-reload                   Reload systemd manager configuration\n"
                "  daemon-reexec                   Reexecute systemd manager\n"
-               "  daemon-exit                     Ask the systemd manager to quit\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"
+               "  default                         Enter system default mode\n"
+               "  rescue                          Enter system rescue mode\n"
+               "  emergency                       Enter system emergency mode\n"
                "  halt                            Shut down and halt the system\n"
                "  poweroff                        Shut down and power-off the system\n"
                "  reboot                          Shut down and reboot the system\n"
-               "  rescue                          Enter system rescue mode\n"
-               "  emergency                       Enter system emergency mode\n"
-               "  default                         Enter system default mode\n",
+               "  kexec                           Shut down and reboot the system with kexec\n"
+               "  exit                            Ask for user instance termination\n",
                program_invocation_short_name);
 
         return 0;
@@ -3934,39 +4322,47 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
 
         enum {
                 ARG_FAIL = 0x100,
-                ARG_SESSION,
+                ARG_VERSION,
+                ARG_USER,
                 ARG_SYSTEM,
                 ARG_GLOBAL,
                 ARG_NO_BLOCK,
+                ARG_NO_PAGER,
                 ARG_NO_WALL,
                 ARG_ORDER,
                 ARG_REQUIRE,
                 ARG_FULL,
-                ARG_FORCE,
                 ARG_NO_RELOAD,
                 ARG_DEFAULTS,
-                ARG_SYSV_COMPAT
+                ARG_KILL_MODE,
+                ARG_KILL_WHO,
+                ARG_NO_ASK_PASSWORD
         };
 
         static const struct option options[] = {
                 { "help",      no_argument,       NULL, 'h'           },
+                { "version",   no_argument,       NULL, ARG_VERSION   },
                 { "type",      required_argument, NULL, 't'           },
                 { "property",  required_argument, NULL, 'p'           },
                 { "all",       no_argument,       NULL, 'a'           },
                 { "full",      no_argument,       NULL, ARG_FULL      },
                 { "fail",      no_argument,       NULL, ARG_FAIL      },
-                { "session",   no_argument,       NULL, ARG_SESSION   },
+                { "user",      no_argument,       NULL, ARG_USER      },
                 { "system",    no_argument,       NULL, ARG_SYSTEM    },
                 { "global",    no_argument,       NULL, ARG_GLOBAL    },
                 { "no-block",  no_argument,       NULL, ARG_NO_BLOCK  },
+                { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL   },
                 { "quiet",     no_argument,       NULL, 'q'           },
                 { "order",     no_argument,       NULL, ARG_ORDER     },
                 { "require",   no_argument,       NULL, ARG_REQUIRE   },
-                { "force",     no_argument,       NULL, ARG_FORCE     },
+                { "force",     no_argument,       NULL, 'f'           },
                 { "no-reload", no_argument,       NULL, ARG_NO_RELOAD },
                 { "defaults",  no_argument,       NULL, ARG_DEFAULTS  },
-                { "sysv-compat", no_argument,     NULL, ARG_SYSV_COMPAT },
+                { "kill-mode", required_argument, NULL, ARG_KILL_MODE },
+                { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
+                { "signal",    required_argument, NULL, 's'           },
+                { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
                 { NULL,        0,                 NULL, 0             }
         };
 
@@ -3975,7 +4371,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "ht:p:aq", options, NULL)) >= 0) {
+        /* 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) {
 
                 switch (c) {
 
@@ -3983,6 +4382,12 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         systemctl_help();
                         return 0;
 
+                case ARG_VERSION:
+                        puts(PACKAGE_STRING);
+                        puts(DISTRIBUTION);
+                        puts(SYSTEMD_FEATURES);
+                        return 0;
+
                 case 't':
                         arg_type = optarg;
                         break;
@@ -4011,18 +4416,22 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_fail = true;
                         break;
 
-                case ARG_SESSION:
-                        arg_session = true;
+                case ARG_USER:
+                        arg_user = true;
                         break;
 
                 case ARG_SYSTEM:
-                        arg_session = false;
+                        arg_user = false;
                         break;
 
                 case ARG_NO_BLOCK:
                         arg_no_block = true;
                         break;
 
+                case ARG_NO_PAGER:
+                        arg_no_pager = true;
+                        break;
+
                 case ARG_NO_WALL:
                         arg_no_wall = true;
                         break;
@@ -4043,7 +4452,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_quiet = true;
                         break;
 
-                case ARG_FORCE:
+                case 'f':
                         arg_force = true;
                         break;
 
@@ -4053,15 +4462,30 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
 
                 case ARG_GLOBAL:
                         arg_global = true;
-                        arg_session = true;
+                        arg_user = true;
                         break;
 
                 case ARG_DEFAULTS:
                         arg_defaults = true;
                         break;
 
-                case ARG_SYSV_COMPAT:
-                        arg_sysv_compat = true;
+                case ARG_KILL_WHO:
+                        arg_kill_who = optarg;
+                        break;
+
+                case ARG_KILL_MODE:
+                        arg_kill_mode = optarg;
+                        break;
+
+                case 's':
+                        if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
+                                log_error("Failed to parse signal string %s.", optarg);
+                                return -EINVAL;
+                        }
+                        break;
+
+                case ARG_NO_ASK_PASSWORD:
+                        arg_ask_password = false;
                         break;
 
                 case '?':
@@ -4526,7 +4950,7 @@ static int talk_upstart(void) {
                         goto finish;
                 }
 
-                log_error("Failed to connect to Upstart bus: %s", error.message);
+                log_error("Failed to connect to Upstart bus: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
@@ -4566,16 +4990,16 @@ static int talk_upstart(void) {
         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
 
                 if (error_is_no_service(&error)) {
-                        r = 0;
+                        r = -EADDRNOTAVAIL;
                         goto finish;
                 }
 
-                log_error("Failed to issue method call: %s", error.message);
+                log_error("Failed to issue method call: %s", bus_error_message(&error));
                 r = -EIO;
                 goto finish;
         }
 
-        r = 1;
+        r = 0;
 
 finish:
         if (m)
@@ -4585,6 +5009,7 @@ finish:
                 dbus_message_unref(reply);
 
         if (bus) {
+                dbus_connection_flush(bus);
                 dbus_connection_close(bus);
                 dbus_connection_unref(bus);
         }
@@ -4656,6 +5081,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "force-reload",          MORE,  2, start_unit        }, /* For compatibility with SysV */
                 { "condrestart",           MORE,  2, start_unit        }, /* For compatibility with RH */
                 { "isolate",               EQUAL, 2, start_unit        },
+                { "kill",                  MORE,  2, kill_unit         },
                 { "is-active",             MORE,  2, check_unit        },
                 { "check",                 MORE,  2, check_unit        },
                 { "show",                  MORE,  1, show              },
@@ -4667,17 +5093,18 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
                 { "delete",                MORE,  2, delete_snapshot   },
                 { "daemon-reload",         EQUAL, 1, daemon_reload     },
                 { "daemon-reexec",         EQUAL, 1, daemon_reload     },
-                { "daemon-exit",           EQUAL, 1, daemon_reload     },
                 { "show-environment",      EQUAL, 1, show_enviroment   },
                 { "set-environment",       MORE,  2, set_environment   },
                 { "unset-environment",     MORE,  2, set_environment   },
                 { "halt",                  EQUAL, 1, start_special     },
                 { "poweroff",              EQUAL, 1, start_special     },
                 { "reboot",                EQUAL, 1, start_special     },
+                { "kexec",                 EQUAL, 1, start_special     },
                 { "default",               EQUAL, 1, start_special     },
                 { "rescue",                EQUAL, 1, start_special     },
                 { "emergency",             EQUAL, 1, start_special     },
-                { "reset-maintenance",     MORE,  1, reset_maintenance },
+                { "exit",                  EQUAL, 1, start_special     },
+                { "reset-failed",          MORE,  1, reset_failed      },
                 { "enable",                MORE,  2, enable_unit       },
                 { "disable",               MORE,  2, enable_unit       },
                 { "is-enabled",            MORE,  2, enable_unit       }
@@ -4758,11 +5185,6 @@ static int send_shutdownd(usec_t t, char mode, bool warn, const char *message) {
         struct msghdr msghdr;
         struct iovec iovec;
         union sockaddr_union sockaddr;
-        struct ucred *ucred;
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(struct ucred))];
-        } control;
         struct shutdownd_command c;
 
         zero(c);
@@ -4785,24 +5207,12 @@ static int send_shutdownd(usec_t t, char mode, bool warn, const char *message) {
         iovec.iov_base = (char*) &c;
         iovec.iov_len = sizeof(c);
 
-        zero(control);
-        control.cmsghdr.cmsg_level = SOL_SOCKET;
-        control.cmsghdr.cmsg_type = SCM_CREDENTIALS;
-        control.cmsghdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
-
-        ucred = (struct ucred*) CMSG_DATA(&control.cmsghdr);
-        ucred->pid = getpid();
-        ucred->uid = getuid();
-        ucred->gid = getgid();
-
         zero(msghdr);
         msghdr.msg_name = &sockaddr;
-        msghdr.msg_namelen = sizeof(sa_family_t) + 1 + sizeof("/org/freedesktop/systemd1/shutdownd") - 1;
+        msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + 1 + sizeof("/org/freedesktop/systemd1/shutdownd") - 1;
 
         msghdr.msg_iov = &iovec;
         msghdr.msg_iovlen = 1;
-        msghdr.msg_control = &control;
-        msghdr.msg_controllen = control.cmsghdr.cmsg_len;
 
         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
                 close_nointr_nofail(fd);
@@ -4836,7 +5246,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) >= 0)
                         goto done;
         }
 
@@ -4862,7 +5272,7 @@ static int halt_main(DBusConnection *bus) {
         int r;
 
         if (geteuid() != 0) {
-                log_error("Must to be root.");
+                log_error("Must be root.");
                 return -EPERM;
         }
 
@@ -4948,8 +5358,87 @@ 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) {
+        siginfo_t dummy;
+
+        if (pager_pid <= 0)
+                return;
+
+        /* Inform pager that we are done */
+        fclose(stdout);
+        wait_for_terminate(pager_pid, &dummy);
+        pager_pid = 0;
+}
+
 int main(int argc, char*argv[]) {
-        int r, retval = 1;
+        int r, retval = EXIT_FAILURE;
         DBusConnection *bus = NULL;
         DBusError error;
 
@@ -4961,30 +5450,30 @@ int main(int argc, char*argv[]) {
         if ((r = parse_argv(argc, argv)) < 0)
                 goto finish;
         else if (r == 0) {
-                retval = 0;
+                retval = EXIT_SUCCESS;
                 goto finish;
         }
 
         /* /sbin/runlevel doesn't need to communicate via D-Bus, so
          * let's shortcut this */
         if (arg_action == ACTION_RUNLEVEL) {
-                retval = runlevel_main() < 0;
+                r = runlevel_main();
+                retval = r < 0 ? EXIT_FAILURE : r;
                 goto finish;
         }
 
-        bus_connect(arg_session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error);
+        bus_connect(arg_user ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error);
 
         switch (arg_action) {
 
-        case ACTION_SYSTEMCTL: {
-                retval = systemctl_main(bus, argc, argv, &error) < 0;
+        case ACTION_SYSTEMCTL:
+                r = systemctl_main(bus, argc, argv, &error);
                 break;
-        }
 
         case ACTION_HALT:
         case ACTION_POWEROFF:
         case ACTION_REBOOT:
-                retval = halt_main(bus) < 0;
+                r = halt_main(bus);
                 break;
 
         case ACTION_RUNLEVEL2:
@@ -4994,16 +5483,16 @@ int main(int argc, char*argv[]) {
         case ACTION_RESCUE:
         case ACTION_EMERGENCY:
         case ACTION_DEFAULT:
-                retval = start_with_fallback(bus) < 0;
+                r = start_with_fallback(bus);
                 break;
 
         case ACTION_RELOAD:
         case ACTION_REEXEC:
-                retval = reload_with_fallback(bus) < 0;
+                r = reload_with_fallback(bus);
                 break;
 
         case ACTION_CANCEL_SHUTDOWN:
-                retval = send_shutdownd(0, 0, false, NULL) < 0;
+                r = send_shutdownd(0, 0, false, NULL);
                 break;
 
         case ACTION_INVALID:
@@ -5012,9 +5501,12 @@ int main(int argc, char*argv[]) {
                 assert_not_reached("Unknown action");
         }
 
+        retval = r < 0 ? EXIT_FAILURE : r;
+
 finish:
 
         if (bus) {
+                dbus_connection_flush(bus);
                 dbus_connection_close(bus);
                 dbus_connection_unref(bus);
         }
@@ -5025,5 +5517,7 @@ finish:
 
         strv_free(arg_property);
 
+        pager_close();
+
         return retval;
 }