chiark / gitweb /
systemctl: fix invalid free when enabling sysv services fails
[elogind.git] / src / systemctl / systemctl.c
index e529095f937fab86178d1bca09f3a9b02ca88b47..17dfff7887abca4f22b450447d00cb62d773bec9 100644 (file)
 #include "logs-show.h"
 #include "socket-util.h"
 #include "fileio.h"
+#include "copy.h"
 #include "env-util.h"
 #include "bus-util.h"
 #include "bus-message.h"
 #include "bus-error.h"
 #include "bus-errors.h"
+#include "mkdir.h"
 
 static char **arg_types = NULL;
 static char **arg_states = NULL;
 static char **arg_properties = NULL;
 static bool arg_all = false;
-static bool original_stdout_is_tty;
 static enum dependency {
         DEPENDENCY_FORWARD,
         DEPENDENCY_REVERSE,
@@ -98,9 +99,11 @@ static bool arg_ignore_inhibitors = false;
 static bool arg_dry = false;
 static bool arg_quiet = false;
 static bool arg_full = false;
+static bool arg_recursive = false;
 static int arg_force = 0;
 static bool arg_ask_password = true;
 static bool arg_runtime = false;
+static UnitFilePresetMode arg_preset_mode = UNIT_FILE_PRESET_FULL;
 static char **arg_wall = NULL;
 static const char *arg_kill_who = NULL;
 static int arg_signal = SIGTERM;
@@ -135,26 +138,11 @@ static char *arg_host = NULL;
 static unsigned arg_lines = 10;
 static OutputMode arg_output = OUTPUT_SHORT;
 static bool arg_plain = false;
-static const struct {
-        const char *verb;
-        const char *method;
-} unit_actions[] = {
-        { "start",                 "StartUnit" },
-        { "stop",                  "StopUnit" },
-        { "condstop",              "StopUnit" },
-        { "reload",                "ReloadUnit" },
-        { "restart",               "RestartUnit" },
-        { "try-restart",           "TryRestartUnit" },
-        { "condrestart",           "TryRestartUnit" },
-        { "reload-or-restart",     "ReloadOrRestartUnit" },
-        { "reload-or-try-restart", "ReloadOrTryRestartUnit" },
-        { "condreload",            "ReloadOrTryRestartUnit" },
-        { "force-reload",          "ReloadOrTryRestartUnit" }
-};
+
+static bool original_stdout_is_tty;
 
 static int daemon_reload(sd_bus *bus, char **args);
 static int halt_now(enum action a);
-
 static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet);
 
 static char** strv_skip_first(char **strv) {
@@ -257,7 +245,7 @@ static void warn_wall(enum action a) {
                 }
 
                 if (*p) {
-                        utmp_wall(p, NULL);
+                        utmp_wall(p, NULL, NULL);
                         return;
                 }
         }
@@ -265,7 +253,7 @@ static void warn_wall(enum action a) {
         if (!table[a])
                 return;
 
-        utmp_wall(table[a], NULL);
+        utmp_wall(table[a], NULL, NULL);
 }
 
 static bool avoid_bus(void) {
@@ -288,60 +276,81 @@ static bool avoid_bus(void) {
 static int compare_unit_info(const void *a, const void *b) {
         const UnitInfo *u = a, *v = b;
         const char *d1, *d2;
+        int r;
+
+        /* First, order by machine */
+        if (!u->machine && v->machine)
+                return -1;
+        if (u->machine && !v->machine)
+                return 1;
+        if (u->machine && v->machine) {
+                r = strcasecmp(u->machine, v->machine);
+                if (r != 0)
+                        return r;
+        }
 
+        /* Second, order by unit type */
         d1 = strrchr(u->id, '.');
         d2 = strrchr(v->id, '.');
-
         if (d1 && d2) {
-                int r;
-
                 r = strcasecmp(d1, d2);
                 if (r != 0)
                         return r;
         }
 
+        /* Third, order by name */
         return strcasecmp(u->id, v->id);
 }
 
 static bool output_show_unit(const UnitInfo *u, char **patterns) {
-        const char *dot;
-
-        if (!strv_isempty(arg_states))
-                return
-                        strv_contains(arg_states, u->load_state) ||
-                        strv_contains(arg_states, u->sub_state) ||
-                        strv_contains(arg_states, u->active_state);
-
         if (!strv_isempty(patterns)) {
                 char **pattern;
 
                 STRV_FOREACH(pattern, patterns)
                         if (fnmatch(*pattern, u->id, FNM_NOESCAPE) == 0)
-                                return true;
+                                goto next;
                 return false;
         }
 
-        return (!arg_types || ((dot = strrchr(u->id, '.')) &&
-                               strv_find(arg_types, dot+1))) &&
-                (arg_all || !(streq(u->active_state, "inactive")
-                              || u->following[0]) || u->job_id > 0);
+next:
+        if (arg_types) {
+                const char *dot;
+
+                dot = strrchr(u->id, '.');
+                if (!dot)
+                        return false;
+
+                if (!strv_find(arg_types, dot+1))
+                        return false;
+        }
+
+        if (arg_all)
+                return true;
+
+        if (u->job_id > 0)
+                return true;
+
+        if (streq(u->active_state, "inactive") || u->following[0])
+                return false;
+
+        return true;
 }
 
-static void output_units_list(const UnitInfo *unit_infos, unsigned c) {
-        unsigned id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len;
+static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
+        unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len;
         const UnitInfo *u;
         unsigned n_shown = 0;
         int job_count = 0;
 
-        max_id_len = sizeof("UNIT")-1;
-        load_len = sizeof("LOAD")-1;
-        active_len = sizeof("ACTIVE")-1;
-        sub_len = sizeof("SUB")-1;
-        job_len = sizeof("JOB")-1;
+        max_id_len = strlen("UNIT");
+        load_len = strlen("LOAD");
+        active_len = strlen("ACTIVE");
+        sub_len = strlen("SUB");
+        job_len = strlen("JOB");
         desc_len = 0;
 
         for (u = unit_infos; u < unit_infos + c; u++) {
-                max_id_len = MAX(max_id_len, strlen(u->id));
+                max_id_len = MAX(max_id_len, strlen(u->id) + (u->machine ? strlen(u->machine)+1 : 0));
                 load_len = MAX(load_len, strlen(u->load_state));
                 active_len = MAX(active_len, strlen(u->active_state));
                 sub_len = MAX(sub_len, strlen(u->sub_state));
@@ -350,13 +359,18 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) {
                         job_len = MAX(job_len, strlen(u->job_type));
                         job_count++;
                 }
+
+                if (!arg_no_legend &&
+                    (streq(u->active_state, "failed") ||
+                     STR_IN_SET(u->load_state, "error", "not-found", "masked")))
+                        circle_len = 2;
         }
 
         if (!arg_full && original_stdout_is_tty) {
                 unsigned basic_len;
 
                 id_len = MIN(max_id_len, 25u);
-                basic_len = 5 + id_len + 5 + active_len + sub_len;
+                basic_len = circle_len + 5 + id_len + 5 + active_len + sub_len;
 
                 if (job_count)
                         basic_len += job_len + 1;
@@ -383,11 +397,18 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) {
                 id_len = max_id_len;
 
         for (u = unit_infos; u < unit_infos + c; u++) {
-                _cleanup_free_ char *e = NULL;
-                const char *on_loaded, *off_loaded, *on = "";
-                const char *on_active, *off_active, *off = "";
+                _cleanup_free_ char *e = NULL, *j = NULL;
+                const char *on_loaded = "", *off_loaded = "";
+                const char *on_active = "", *off_active = "";
+                const char *on_circle = "", *off_circle = "";
+                const char *id;
+                bool circle = false;
 
                 if (!n_shown && !arg_no_legend) {
+
+                        if (circle_len > 0)
+                                fputs("  ", stdout);
+
                         printf("%-*s %-*s %-*s %-*s ",
                                id_len, "UNIT",
                                load_len, "LOAD",
@@ -405,23 +426,41 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) {
 
                 n_shown++;
 
-                if (streq(u->load_state, "error") ||
-                    streq(u->load_state, "not-found")) {
-                        on_loaded = on = ansi_highlight_red();
-                        off_loaded = off = ansi_highlight_off();
-                } else
-                        on_loaded = off_loaded = "";
+                if (STR_IN_SET(u->load_state, "error", "not-found", "masked")) {
+                        on_loaded = ansi_highlight_red();
+                        on_circle = ansi_highlight_yellow();
+                        off_loaded = off_circle = ansi_highlight_off();
+                        circle = true;
+                }
 
                 if (streq(u->active_state, "failed")) {
-                        on_active = on = ansi_highlight_red();
-                        off_active = off = ansi_highlight_off();
+                        on_circle = on_active = ansi_highlight_red();
+                        off_circle = off_active = ansi_highlight_off();
+                        circle = true;
+                }
+
+                if (u->machine) {
+                        j = strjoin(u->machine, ":", u->id, NULL);
+                        if (!j)
+                                return log_oom();
+
+                        id = j;
                 } else
-                        on_active = off_active = "";
+                        id = u->id;
+
+                if (arg_full) {
+                        e = ellipsize(id, id_len, 33);
+                        if (!e)
+                                return log_oom();
 
-                e = arg_full ? NULL : ellipsize(u->id, id_len, 33);
+                        id = e;
+                }
+
+                if (circle_len > 0)
+                        printf("%s%s%s ", on_circle, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_circle);
 
                 printf("%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s",
-                       on, id_len, e ? e : u->id, off,
+                       on_active, id_len, id, off_active,
                        on_loaded, load_len, u->load_state, off_loaded,
                        on_active, active_len, u->active_state,
                        sub_len, u->sub_state, off_active,
@@ -437,7 +476,8 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) {
                 const char *on, *off;
 
                 if (n_shown) {
-                        puts("\nLOAD   = Reflects whether the unit definition was properly loaded.\n"
+                        puts("\n"
+                             "LOAD   = 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.");
                         puts(job_count ? "JOB    = Pending job for the unit.\n" : "");
@@ -457,34 +497,45 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) {
                                "To show all installed unit files use 'systemctl list-unit-files'.\n",
                                on, n_shown, off);
         }
+
+        return 0;
 }
 
 static int get_unit_list(
                 sd_bus *bus,
-                sd_bus_message **_reply,
-                UnitInfo **_unit_infos,
-                char **patterns) {
+                const char *machine,
+                char **patterns,
+                UnitInfo **unit_infos,
+                int c,
+                sd_bus_message **_reply) {
 
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
-        _cleanup_free_ UnitInfo *unit_infos = NULL;
-        size_t size = 0;
-        int r, c = 0;
+        size_t size = c;
+        int r;
         UnitInfo u;
 
         assert(bus);
+        assert(unit_infos);
         assert(_reply);
-        assert(_unit_infos);
 
-        r = sd_bus_call_method(
+        r = sd_bus_message_new_method_call(
                         bus,
+                        &m,
                         "org.freedesktop.systemd1",
                         "/org/freedesktop/systemd1",
                         "org.freedesktop.systemd1.Manager",
-                        "ListUnits",
-                        &error,
-                        &reply,
-                        NULL);
+                        "ListUnitsFiltered");
+
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append_strv(m, arg_states);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
         if (r < 0) {
                 log_error("Failed to list units: %s", bus_error_message(&error, r));
                 return r;
@@ -495,13 +546,15 @@ static int get_unit_list(
                 return bus_log_parse_error(r);
 
         while ((r = bus_parse_unit_info(reply, &u)) > 0) {
+                u.machine = machine;
+
                 if (!output_show_unit(&u, patterns))
                         continue;
 
-                if (!GREEDY_REALLOC(unit_infos, size, c+1))
+                if (!GREEDY_REALLOC(*unit_infos, size, c+1))
                         return log_oom();
 
-                unit_infos[c++] = u;
+                (*unit_infos)[c++] = u;
         }
         if (r < 0)
                 return bus_log_parse_error(r);
@@ -513,27 +566,108 @@ static int get_unit_list(
         *_reply = reply;
         reply = NULL;
 
+        return c;
+}
+
+static void message_set_freep(Set **set) {
+        sd_bus_message *m;
+
+        while ((m = set_steal_first(*set)))
+                sd_bus_message_unref(m);
+
+        set_free(*set);
+}
+
+static int get_unit_list_recursive(
+                sd_bus *bus,
+                char **patterns,
+                UnitInfo **_unit_infos,
+                Set **_replies,
+                char ***_machines) {
+
+        _cleanup_free_ UnitInfo *unit_infos = NULL;
+        _cleanup_(message_set_freep) Set *replies;
+        sd_bus_message *reply;
+        int c, r;
+
+        assert(bus);
+        assert(_replies);
+        assert(_unit_infos);
+        assert(_machines);
+
+        replies = set_new(NULL);
+        if (!replies)
+                return log_oom();
+
+        c = get_unit_list(bus, NULL, patterns, &unit_infos, 0, &reply);
+        if (c < 0)
+                return c;
+
+        r = set_put(replies, reply);
+        if (r < 0) {
+                sd_bus_message_unref(reply);
+                return r;
+        }
+
+        if (arg_recursive) {
+                _cleanup_strv_free_ char **machines = NULL;
+                char **i;
+
+                r = sd_get_machine_names(&machines);
+                if (r < 0)
+                        return r;
+
+                STRV_FOREACH(i, machines) {
+                        _cleanup_bus_close_unref_ sd_bus *container = NULL;
+                        int k;
+
+                        r = sd_bus_open_system_container(&container, *i);
+                        if (r < 0) {
+                                log_error_errno(r, "Failed to connect to container %s: %m", *i);
+                                continue;
+                        }
+
+                        k = get_unit_list(container, *i, patterns, &unit_infos, c, &reply);
+                        if (k < 0)
+                                return k;
+
+                        c = k;
+
+                        r = set_put(replies, reply);
+                        if (r < 0) {
+                                sd_bus_message_unref(reply);
+                                return r;
+                        }
+                }
+
+                *_machines = machines;
+                machines = NULL;
+        } else
+                *_machines = NULL;
+
         *_unit_infos = unit_infos;
         unit_infos = NULL;
 
+        *_replies = replies;
+        replies = NULL;
+
         return c;
 }
 
 static int list_units(sd_bus *bus, char **args) {
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
+        _cleanup_(message_set_freep) Set *replies = NULL;
+        _cleanup_strv_free_ char **machines = NULL;
         int r;
 
         pager_open_if_enabled();
 
-        r = get_unit_list(bus, &reply, &unit_infos, strv_skip_first(args));
+        r = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines);
         if (r < 0)
                 return r;
 
         qsort_safe(unit_infos, r, sizeof(UnitInfo), compare_unit_info);
-        output_units_list(unit_infos, r);
-
-        return 0;
+        return output_units_list(unit_infos, r);
 }
 
 static int get_triggered_units(
@@ -610,6 +744,7 @@ static int get_listening(
 }
 
 struct socket_info {
+        const char *machine;
         const char* id;
 
         char* type;
@@ -630,6 +765,16 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_
         assert(a);
         assert(b);
 
+        if (!a->machine && b->machine)
+                return -1;
+        if (a->machine && !b->machine)
+                return 1;
+        if (a->machine && b->machine) {
+                o = strcasecmp(a->machine, b->machine);
+                if (o != 0)
+                        return o;
+        }
+
         o = strcmp(a->path, b->path);
         if (o == 0)
                 o = strcmp(a->type, b->type);
@@ -639,10 +784,10 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_
 
 static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
         struct socket_info *s;
-        unsigned pathlen = sizeof("LISTEN") - 1,
-                typelen = (sizeof("TYPE") - 1) * arg_show_types,
-                socklen = sizeof("UNIT") - 1,
-                servlen = sizeof("ACTIVATES") - 1;
+        unsigned pathlen = strlen("LISTEN"),
+                typelen = strlen("TYPE") * arg_show_types,
+                socklen = strlen("UNIT"),
+                servlen = strlen("ACTIVATES");
         const char *on, *off;
 
         for (s = socket_infos; s < socket_infos + cs; s++) {
@@ -652,7 +797,7 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
                 socklen = MAX(socklen, strlen(s->id));
                 if (arg_show_types)
                         typelen = MAX(typelen, strlen(s->type));
-                pathlen = MAX(pathlen, strlen(s->path));
+                pathlen = MAX(pathlen, strlen(s->path) + (s->machine ? strlen(s->machine)+1 : 0));
 
                 STRV_FOREACH(a, s->triggered)
                         tmp += strlen(*a) + 2*(a != s->triggered);
@@ -668,14 +813,24 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
                                "ACTIVATES");
 
                 for (s = socket_infos; s < socket_infos + cs; s++) {
+                        _cleanup_free_ char *j = NULL;
+                        const char *path;
                         char **a;
 
+                        if (s->machine) {
+                                j = strjoin(s->machine, ":", s->path, NULL);
+                                if (!j)
+                                        return log_oom();
+                                path = j;
+                        } else
+                                path = s->path;
+
                         if (arg_show_types)
                                 printf("%-*s %-*s %-*s",
-                                       pathlen, s->path, typelen, s->type, socklen, s->id);
+                                       pathlen, path, typelen, s->type, socklen, s->id);
                         else
                                 printf("%-*s %-*s",
-                                       pathlen, s->path, socklen, s->id);
+                                       pathlen, path, socklen, s->id);
                         STRV_FOREACH(a, s->triggered)
                                 printf("%s %s",
                                        a == s->triggered ? "" : ",", *a);
@@ -701,7 +856,8 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
 }
 
 static int list_sockets(sd_bus *bus, char **args) {
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_(message_set_freep) Set *replies = NULL;
+        _cleanup_strv_free_ char **machines = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
         _cleanup_free_ struct socket_info *socket_infos = NULL;
         const UnitInfo *u;
@@ -712,7 +868,7 @@ static int list_sockets(sd_bus *bus, char **args) {
 
         pager_open_if_enabled();
 
-        n = get_unit_list(bus, &reply, &unit_infos, strv_skip_first(args));
+        n = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines);
         if (n < 0)
                 return n;
 
@@ -740,6 +896,7 @@ static int list_sockets(sd_bus *bus, char **args) {
 
                 for (i = 0; i < c; i++)
                         socket_infos[cs + i] = (struct socket_info) {
+                                .machine = u->machine,
                                 .id = u->id,
                                 .type = listening[i*2],
                                 .path = listening[i*2 + 1],
@@ -815,16 +972,59 @@ static int get_next_elapse(
         return 0;
 }
 
+static int get_last_trigger(
+                sd_bus *bus,
+                const char *path,
+                usec_t *last) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        int r;
+
+        assert(bus);
+        assert(path);
+        assert(last);
+
+        r = sd_bus_get_property_trivial(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        path,
+                        "org.freedesktop.systemd1.Timer",
+                        "LastTriggerUSec",
+                        &error,
+                        't',
+                        last);
+        if (r < 0) {
+                log_error("Failed to get last trigger time: %s", bus_error_message(&error, r));
+                return r;
+        }
+
+        return 0;
+}
+
 struct timer_info {
+        const char* machine;
         const char* id;
         usec_t next_elapse;
+        usec_t last_trigger;
         char** triggered;
 };
 
 static int timer_info_compare(const struct timer_info *a, const struct timer_info *b) {
+        int o;
+
         assert(a);
         assert(b);
 
+        if (!a->machine && b->machine)
+                return -1;
+        if (a->machine && !b->machine)
+                return 1;
+        if (a->machine && b->machine) {
+                o = strcasecmp(a->machine, b->machine);
+                if (o != 0)
+                        return o;
+        }
+
         if (a->next_elapse < b->next_elapse)
                 return -1;
         if (a->next_elapse > b->next_elapse)
@@ -836,10 +1036,12 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf
 static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
         struct timer_info *t;
         unsigned
-                nextlen = sizeof("NEXT") - 1,
-                leftlen = sizeof("LEFT") - 1,
-                unitlen = sizeof("UNIT") - 1,
-                activatelen = sizeof("ACTIVATES") - 1;
+                nextlen = strlen("NEXT"),
+                leftlen = strlen("LEFT"),
+                lastlen = strlen("LAST"),
+                passedlen = strlen("PASSED"),
+                unitlen = strlen("UNIT"),
+                activatelen = strlen("ACTIVATES");
 
         const char *on, *off;
 
@@ -859,30 +1061,57 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
                         leftlen = MAX(leftlen, strlen(trel));
                 }
 
-                unitlen = MAX(unitlen, strlen(t->id));
+                if (t->last_trigger > 0) {
+                        char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
+
+                        format_timestamp(tstamp, sizeof(tstamp), t->last_trigger);
+                        lastlen = MAX(lastlen, strlen(tstamp) + 1);
+
+                        format_timestamp_relative(trel, sizeof(trel), t->last_trigger);
+                        passedlen = MAX(passedlen, strlen(trel));
+                }
+
+                unitlen = MAX(unitlen, strlen(t->id) + (t->machine ? strlen(t->machine)+1 : 0));
 
                 STRV_FOREACH(a, t->triggered)
                         ul += strlen(*a) + 2*(a != t->triggered);
+
                 activatelen = MAX(activatelen, ul);
         }
 
         if (n > 0) {
                 if (!arg_no_legend)
-                        printf("%-*s %-*s %-*s %s\n",
-                               nextlen, "NEXT",
-                               leftlen, "LEFT",
-                               unitlen, "UNIT",
-                                        "ACTIVATES");
+                        printf("%-*s %-*s %-*s %-*s %-*s %s\n",
+                               nextlen,   "NEXT",
+                               leftlen,   "LEFT",
+                               lastlen,   "LAST",
+                               passedlen, "PASSED",
+                               unitlen,   "UNIT",
+                                          "ACTIVATES");
 
                 for (t = timer_infos; t < timer_infos + n; t++) {
-                        char tstamp[FORMAT_TIMESTAMP_MAX] = "n/a", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
+                        _cleanup_free_ char *j = NULL;
+                        const char *unit;
+                        char tstamp1[FORMAT_TIMESTAMP_MAX] = "n/a", trel1[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
+                        char tstamp2[FORMAT_TIMESTAMP_MAX] = "n/a", trel2[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
                         char **a;
 
-                        format_timestamp(tstamp, sizeof(tstamp), t->next_elapse);
-                        format_timestamp_relative(trel, sizeof(trel), t->next_elapse);
+                        format_timestamp(tstamp1, sizeof(tstamp1), t->next_elapse);
+                        format_timestamp_relative(trel1, sizeof(trel1), t->next_elapse);
+
+                        format_timestamp(tstamp2, sizeof(tstamp2), t->last_trigger);
+                        format_timestamp_relative(trel2, sizeof(trel2), t->last_trigger);
 
-                        printf("%-*s %-*s %-*s",
-                               nextlen, tstamp, leftlen, trel, unitlen, t->id);
+                        if (t->machine) {
+                                j = strjoin(t->machine, ":", t->id, NULL);
+                                if (!j)
+                                        return log_oom();
+                                unit = j;
+                        } else
+                                unit = t->id;
+
+                        printf("%-*s %-*s %-*s %-*s %-*s",
+                               nextlen, tstamp1, leftlen, trel1, lastlen, tstamp2, passedlen, trel2, unitlen, unit);
 
                         STRV_FOREACH(a, t->triggered)
                                 printf("%s %s",
@@ -908,9 +1137,34 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
         return 0;
 }
 
-static int list_timers(sd_bus *bus, char **args) {
+static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) {
+        usec_t next_elapse;
 
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        assert(nw);
+        assert(next);
+
+        if (next->monotonic != USEC_INFINITY && next->monotonic > 0) {
+                usec_t converted;
+
+                if (next->monotonic > nw->monotonic)
+                        converted = nw->realtime + (next->monotonic - nw->monotonic);
+                else
+                        converted = nw->realtime - (nw->monotonic - next->monotonic);
+
+                if (next->realtime != USEC_INFINITY && next->realtime > 0)
+                        next_elapse = MIN(converted, next->realtime);
+                else
+                        next_elapse = converted;
+
+        } else
+                next_elapse = next->realtime;
+
+        return next_elapse;
+}
+
+static int list_timers(sd_bus *bus, char **args) {
+        _cleanup_(message_set_freep) Set *replies = NULL;
+        _cleanup_strv_free_ char **machines = NULL;
         _cleanup_free_ struct timer_info *timer_infos = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
         struct timer_info *t;
@@ -922,7 +1176,7 @@ static int list_timers(sd_bus *bus, char **args) {
 
         pager_open_if_enabled();
 
-        n = get_unit_list(bus, &reply, &unit_infos, strv_skip_first(args));
+        n = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines);
         if (n < 0)
                 return n;
 
@@ -930,8 +1184,8 @@ static int list_timers(sd_bus *bus, char **args) {
 
         for (u = unit_infos; u < unit_infos + n; u++) {
                 _cleanup_strv_free_ char **triggered = NULL;
-                dual_timestamp next;
-                usec_t m;
+                dual_timestamp next = {};
+                usec_t m, last = 0;
 
                 if (!endswith(u->id, ".timer"))
                         continue;
@@ -944,29 +1198,20 @@ static int list_timers(sd_bus *bus, char **args) {
                 if (r < 0)
                         goto cleanup;
 
-                if (next.monotonic != (usec_t) -1 && next.monotonic > 0) {
-                        usec_t converted;
-
-                        if (next.monotonic > nw.monotonic)
-                                converted = nw.realtime + (next.monotonic - nw.monotonic);
-                        else
-                                converted = nw.realtime - (nw.monotonic - next.monotonic);
-
-                        if (next.realtime != (usec_t) -1 && next.realtime > 0)
-                                m = MIN(converted, next.realtime);
-                        else
-                                m = converted;
-                } else
-                        m = next.realtime;
+                get_last_trigger(bus, u->unit_path, &last);
 
                 if (!GREEDY_REALLOC(timer_infos, size, c+1)) {
                         r = log_oom();
                         goto cleanup;
                 }
 
+                m = calc_next_elapse(&nw, &next);
+
                 timer_infos[c++] = (struct timer_info) {
+                        .machine = u->machine,
                         .id = u->id,
                         .next_elapse = m,
+                        .last_trigger = last,
                         .triggered = triggered,
                 };
 
@@ -1004,26 +1249,41 @@ static int compare_unit_file_list(const void *a, const void *b) {
 }
 
 static bool output_show_unit_file(const UnitFileList *u, char **patterns) {
-        const char *dot;
-
         if (!strv_isempty(patterns)) {
                 char **pattern;
 
                 STRV_FOREACH(pattern, patterns)
                         if (fnmatch(*pattern, basename(u->path), FNM_NOESCAPE) == 0)
-                                return true;
+                                goto next;
                 return false;
         }
 
-        return !arg_types || ((dot = strrchr(u->path, '.')) && strv_find(arg_types, dot+1));
+next:
+        if (!strv_isempty(arg_types)) {
+                const char *dot;
+
+                dot = strrchr(u->path, '.');
+                if (!dot)
+                        return false;
+
+                if (!strv_find(arg_types, dot+1))
+                        return false;
+        }
+
+        if (!strv_isempty(arg_states)) {
+                if (!strv_find(arg_states, unit_file_state_to_string(u->state)))
+                        return false;
+        }
+
+        return true;
 }
 
 static void output_unit_file_list(const UnitFileList *units, unsigned c) {
-        unsigned max_id_len, id_cols, state_cols, n_shown = 0;
+        unsigned max_id_len, id_cols, state_cols;
         const UnitFileList *u;
 
-        max_id_len = sizeof("UNIT FILE")-1;
-        state_cols = sizeof("STATE")-1;
+        max_id_len = strlen("UNIT FILE");
+        state_cols = strlen("STATE");
 
         for (u = units; u < units + c; u++) {
                 max_id_len = MAX(max_id_len, strlen(basename(u->path)));
@@ -1050,8 +1310,6 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
                 const char *on, *off;
                 const char *id;
 
-                n_shown++;
-
                 if (u->state == UNIT_FILE_MASKED ||
                     u->state == UNIT_FILE_MASKED_RUNTIME ||
                     u->state == UNIT_FILE_DISABLED ||
@@ -1074,7 +1332,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) {
         }
 
         if (!arg_no_legend)
-                printf("\n%u unit files listed.\n", n_shown);
+                printf("\n%u unit files listed.\n", c);
 }
 
 static int list_unit_files(sd_bus *bus, char **args) {
@@ -1096,20 +1354,21 @@ static int list_unit_files(sd_bus *bus, char **args) {
                 Iterator i;
                 unsigned n_units;
 
-                h = hashmap_new(string_hash_func, string_compare_func);
+                h = hashmap_new(&string_hash_ops);
                 if (!h)
                         return log_oom();
 
                 r = unit_file_get_list(arg_scope, arg_root, h);
                 if (r < 0) {
                         unit_file_list_free(h);
-                        log_error("Failed to get unit file list: %s", strerror(-r));
+                        log_error_errno(r, "Failed to get unit file list: %m");
                         return r;
                 }
 
                 n_units = hashmap_size(h);
+
                 units = new(UnitFileList, n_units);
-                if (!units) {
+                if (!units && n_units > 0) {
                         unit_file_list_free(h);
                         return log_oom();
                 }
@@ -1165,14 +1424,13 @@ static int list_unit_files(sd_bus *bus, char **args) {
                         return bus_log_parse_error(r);
         }
 
-        if (c > 0) {
-                qsort(units, c, sizeof(UnitFileList), compare_unit_file_list);
-                output_unit_file_list(units, c);
-        }
+        qsort_safe(units, c, sizeof(UnitFileList), compare_unit_file_list);
+        output_unit_file_list(units, c);
 
-        if (avoid_bus())
+        if (avoid_bus()) {
                 for (unit = units; unit < units + c; unit++)
                         free(unit->path);
+        }
 
         return 0;
 }
@@ -1191,7 +1449,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra
                                 printf("%s...\n",max_len % 2 ? "" : " ");
                                 return 0;
                         }
-                        printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERT : DRAW_TREE_SPACE));
+                        printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
                 }
                 len += 2;
 
@@ -1223,11 +1481,13 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha
                                        "RequiresOverridable\0"
                                        "Requisite\0"
                                        "RequisiteOverridable\0"
-                                       "Wants\0",
+                                       "Wants\0"
+                                       "BindsTo\0",
                 [DEPENDENCY_REVERSE] = "RequiredBy\0"
                                        "RequiredByOverridable\0"
                                        "WantedBy\0"
-                                       "PartOf\0",
+                                       "PartOf\0"
+                                       "BoundBy\0",
                 [DEPENDENCY_AFTER]   = "After\0",
                 [DEPENDENCY_BEFORE]  = "Before\0",
         };
@@ -1359,9 +1619,9 @@ static int list_dependencies_one(
 
                 state = check_one_unit(bus, *c, "activating\0active\0reloading\0", true);
                 if (state > 0)
-                        printf("%s%s%s", ansi_highlight_green(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off());
+                        printf("%s%s%s ", ansi_highlight_green(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off());
                 else
-                        printf("%s%s%s", ansi_highlight_red(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off());
+                        printf("%s%s%s ", ansi_highlight_red(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off());
 
                 r = list_dependencies_print(*c, level, branches, c[1] == NULL);
                 if (r < 0)
@@ -1402,57 +1662,291 @@ static int list_dependencies(sd_bus *bus, char **args) {
         return list_dependencies_one(bus, u, 0, &units, 0);
 }
 
-static int get_default(sd_bus *bus, char **args) {
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
-        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_free_ char *_path = NULL;
-        const char *path;
-        int r;
+struct machine_info {
+        bool is_host;
+        char *name;
+        char *state;
+        char *control_group;
+        uint32_t n_failed_units;
+        uint32_t n_jobs;
+        usec_t timestamp;
+};
 
-        if (!bus || avoid_bus()) {
-                r = unit_file_get_default(arg_scope, arg_root, &_path);
-                if (r < 0) {
-                        log_error("Failed to get default target: %s", strerror(-r));
-                        return r;
-                }
-                path = _path;
+static const struct bus_properties_map machine_info_property_map[] = {
+        { "SystemState",        "s", NULL, offsetof(struct machine_info, state)          },
+        { "NJobs",              "u", NULL, offsetof(struct machine_info, n_jobs)         },
+        { "NFailedUnits",       "u", NULL, offsetof(struct machine_info, n_failed_units) },
+        { "ControlGroup",       "s", NULL, offsetof(struct machine_info, control_group)  },
+        { "UserspaceTimestamp", "t", NULL, offsetof(struct machine_info, timestamp)      },
+        {}
+};
 
-        } else {
-                r = sd_bus_call_method(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                "GetDefaultTarget",
-                                &error,
-                                &reply,
-                                NULL);
-                if (r < 0) {
-                        log_error("Failed to get default target: %s", bus_error_message(&error, -r));
-                        return r;
-                }
+static void free_machines_list(struct machine_info *machine_infos, int n) {
+        int i;
 
-                r = sd_bus_message_read(reply, "s", &path);
-                if (r < 0)
-                        return bus_log_parse_error(r);
+        if (!machine_infos)
+                return;
+
+        for (i = 0; i < n; i++) {
+                free(machine_infos[i].name);
+                free(machine_infos[i].state);
+                free(machine_infos[i].control_group);
         }
 
-        if (path)
-                printf("%s\n", path);
+        free(machine_infos);
+}
 
-        return 0;
+static int compare_machine_info(const void *a, const void *b) {
+        const struct machine_info *u = a, *v = b;
+
+        if (u->is_host != v->is_host)
+                return u->is_host > v->is_host ? -1 : 1;
+
+        return strcasecmp(u->name, v->name);
 }
 
-static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_changes) {
-        unsigned i;
+static int get_machine_properties(sd_bus *bus, struct machine_info *mi) {
+        _cleanup_bus_close_unref_ sd_bus *container = NULL;
+        int r;
 
-        assert(changes || n_changes == 0);
+        assert(mi);
+
+        if (!bus) {
+                r = sd_bus_open_system_container(&container, mi->name);
+                if (r < 0)
+                        return r;
+
+                bus = container;
+        }
+
+        r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, mi);
+        if (r < 0)
+                return r;
+
+        return 0;
+}
+
+static bool output_show_machine(const char *name, char **patterns) {
+        char **i;
+
+        assert(name);
+
+        if (strv_isempty(patterns))
+                return true;
+
+        STRV_FOREACH(i, patterns)
+                if (fnmatch(*i, name, FNM_NOESCAPE) == 0)
+                        return true;
+
+        return false;
+}
+
+static int get_machine_list(
+                sd_bus *bus,
+                struct machine_info **_machine_infos,
+                char **patterns) {
+
+        struct machine_info *machine_infos = NULL;
+        _cleanup_strv_free_ char **m = NULL;
+        _cleanup_free_ char *hn = NULL;
+        size_t sz = 0;
+        char **i;
+        int c = 0;
+
+        hn = gethostname_malloc();
+        if (!hn)
+                return log_oom();
+
+        if (output_show_machine(hn, patterns)) {
+                if (!GREEDY_REALLOC0(machine_infos, sz, c+1))
+                        return log_oom();
+
+                machine_infos[c].is_host = true;
+                machine_infos[c].name = hn;
+                hn = NULL;
+
+                get_machine_properties(bus, &machine_infos[c]);
+                c++;
+        }
+
+        sd_get_machine_names(&m);
+        STRV_FOREACH(i, m) {
+                _cleanup_free_ char *class = NULL;
+
+                if (!output_show_machine(*i, patterns))
+                        continue;
+
+                sd_machine_get_class(*i, &class);
+                if (!streq_ptr(class, "container"))
+                        continue;
+
+                if (!GREEDY_REALLOC0(machine_infos, sz, c+1)) {
+                        free_machines_list(machine_infos, c);
+                        return log_oom();
+                }
+
+                machine_infos[c].is_host = false;
+                machine_infos[c].name = strdup(*i);
+                if (!machine_infos[c].name) {
+                        free_machines_list(machine_infos, c);
+                        return log_oom();
+                }
+
+                get_machine_properties(NULL, &machine_infos[c]);
+                c++;
+        }
+
+        *_machine_infos = machine_infos;
+        return c;
+}
+
+static void output_machines_list(struct machine_info *machine_infos, unsigned n) {
+        struct machine_info *m;
+        unsigned
+                circle_len = 0,
+                namelen = sizeof("NAME") - 1,
+                statelen = sizeof("STATE") - 1,
+                failedlen = sizeof("FAILED") - 1,
+                jobslen = sizeof("JOBS") - 1;
+
+        assert(machine_infos || n == 0);
+
+        for (m = machine_infos; m < machine_infos + n; m++) {
+                namelen = MAX(namelen, strlen(m->name) + (m->is_host ? sizeof(" (host)") - 1 : 0));
+                statelen = MAX(statelen, m->state ? strlen(m->state) : 0);
+                failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units));
+                jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs));
+
+                if (!arg_no_legend && !streq_ptr(m->state, "running"))
+                        circle_len = 2;
+        }
+
+        if (!arg_no_legend) {
+                if (circle_len > 0)
+                        fputs("  ", stdout);
+
+                printf("%-*s %-*s %-*s %-*s\n",
+                         namelen, "NAME",
+                        statelen, "STATE",
+                       failedlen, "FAILED",
+                         jobslen, "JOBS");
+        }
+
+        for (m = machine_infos; m < machine_infos + n; m++) {
+                const char *on_state = "", *off_state = "";
+                const char *on_failed = "", *off_failed = "";
+                bool circle = false;
+
+                if (streq_ptr(m->state, "degraded")) {
+                        on_state = ansi_highlight_red();
+                        off_state = ansi_highlight_off();
+                        circle = true;
+                } else if (!streq_ptr(m->state, "running")) {
+                        on_state = ansi_highlight_yellow();
+                        off_state = ansi_highlight_off();
+                        circle = true;
+                }
+
+                if (m->n_failed_units > 0) {
+                        on_failed = ansi_highlight_red();
+                        off_failed = ansi_highlight_off();
+                } else
+                        on_failed = off_failed = "";
+
+                if (circle_len > 0)
+                        printf("%s%s%s ", on_state, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_state);
+
+                if (m->is_host)
+                        printf("%-*s (host) %s%-*s%s %s%*u%s %*u\n",
+                               (int) (namelen - (sizeof(" (host)")-1)), strna(m->name),
+                               on_state, statelen, strna(m->state), off_state,
+                               on_failed, failedlen, m->n_failed_units, off_failed,
+                               jobslen, m->n_jobs);
+                else
+                        printf("%-*s %s%-*s%s %s%*u%s %*u\n",
+                               namelen, strna(m->name),
+                               on_state, statelen, strna(m->state), off_state,
+                               on_failed, failedlen, m->n_failed_units, off_failed,
+                               jobslen, m->n_jobs);
+        }
+
+        if (!arg_no_legend)
+                printf("\n%u machines listed.\n", n);
+}
+
+static int list_machines(sd_bus *bus, char **args) {
+        struct machine_info *machine_infos = NULL;
+        int r;
+
+        assert(bus);
+
+        if (geteuid() != 0) {
+                log_error("Must be root.");
+                return -EPERM;
+        }
+
+        pager_open_if_enabled();
+
+        r = get_machine_list(bus, &machine_infos, strv_skip_first(args));
+        if (r < 0)
+                return r;
+
+        qsort_safe(machine_infos, r, sizeof(struct machine_info), compare_machine_info);
+        output_machines_list(machine_infos, r);
+        free_machines_list(machine_infos, r);
+
+        return 0;
+}
+
+static int get_default(sd_bus *bus, char **args) {
+        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_free_ char *_path = NULL;
+        const char *path;
+        int r;
+
+        if (!bus || avoid_bus()) {
+                r = unit_file_get_default(arg_scope, arg_root, &_path);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to get default target: %m");
+                path = _path;
+
+        } else {
+                r = sd_bus_call_method(
+                                bus,
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                "GetDefaultTarget",
+                                &error,
+                                &reply,
+                                NULL);
+                if (r < 0) {
+                        log_error("Failed to get default target: %s", bus_error_message(&error, -r));
+                        return r;
+                }
+
+                r = sd_bus_message_read(reply, "s", &path);
+                if (r < 0)
+                        return bus_log_parse_error(r);
+        }
+
+        if (path)
+                printf("%s\n", path);
+
+        return 0;
+}
+
+static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_changes) {
+        unsigned i;
+
+        assert(changes || n_changes == 0);
 
         for (i = 0; i < n_changes; i++) {
                 if (changes[i].type == UNIT_FILE_SYMLINK)
-                        log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
+                        log_info("Created symlink from %s to %s.", changes[i].path, changes[i].source);
                 else
-                        log_info("rm '%s'", changes[i].path);
+                        log_info("Removed symlink %s.", changes[i].path);
         }
 }
 
@@ -1467,9 +1961,9 @@ static int deserialize_and_dump_unit_file_changes(sd_bus_message *m) {
         while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
                 if (!arg_quiet) {
                         if (streq(type, "symlink"))
-                                log_info("ln -s '%s' '%s'", source, path);
+                                log_info("Created symlink from %s to %s.", path, source);
                         else
-                                log_info("rm '%s'", path);
+                                log_info("Removed symlink %s.", path);
                 }
         }
         if (r < 0)
@@ -1493,29 +1987,37 @@ static int set_default(sd_bus *bus, char **args) {
                 return log_oom();
 
         if (!bus || avoid_bus()) {
-                r = unit_file_set_default(arg_scope, arg_root, unit, arg_force, &changes, &n_changes);
-                if (r < 0) {
-                        log_error("Failed to set default target: %s", strerror(-r));
-                        return r;
-                }
+                r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to set default target: %m");
 
                 if (!arg_quiet)
                         dump_unit_file_changes(changes, n_changes);
 
                 r = 0;
         } else {
-                _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+                _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
 
-                r = sd_bus_call_method(
+                r = sd_bus_message_new_method_call(
                                 bus,
+                                &m,
                                 "org.freedesktop.systemd1",
                                 "/org/freedesktop/systemd1",
                                 "org.freedesktop.systemd1.Manager",
-                                "SetDefaultTarget",
-                                &error,
-                                &reply,
-                                "sb", unit, arg_force);
+                                "SetDefaultTarget");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "sb", unit, 1);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_call(bus, m, 0, &error, &reply);
                 if (r < 0) {
                         log_error("Failed to set default target: %s", bus_error_message(&error, -r));
                         return r;
@@ -1525,7 +2027,7 @@ static int set_default(sd_bus *bus, char **args) {
                 if (r < 0)
                         return r;
 
-                /* Try to reload if enabeld */
+                /* Try to reload if enabled */
                 if (!arg_no_reload)
                         r = daemon_reload(bus, args);
                 else
@@ -1560,10 +2062,10 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipp
 
         pager_open_if_enabled();
 
-        id_len = sizeof("JOB")-1;
-        unit_len = sizeof("UNIT")-1;
-        type_len = sizeof("TYPE")-1;
-        state_len = sizeof("STATE")-1;
+        id_len = strlen("JOB");
+        unit_len = strlen("UNIT");
+        type_len = strlen("TYPE");
+        state_len = strlen("STATE");
 
         for (j = jobs; j < jobs + n; j++) {
                 uint32_t id = j->id;
@@ -1613,16 +2115,17 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipp
 }
 
 static bool output_show_job(struct job_info *job, char **patterns) {
-        if (!strv_isempty(patterns)) {
-                char **pattern;
+        char **pattern;
 
-                STRV_FOREACH(pattern, patterns)
-                        if (fnmatch(*pattern, job->name, FNM_NOESCAPE) == 0)
-                                return true;
-                return false;
-        }
+        assert(job);
 
-        return true;
+        if (strv_isempty(patterns))
+                return true;
+
+        STRV_FOREACH(pattern, patterns)
+                if (fnmatch(*pattern, job->name, FNM_NOESCAPE) == 0)
+                        return true;
+        return false;
 }
 
 static int list_jobs(sd_bus *bus, char **args) {
@@ -1681,6 +2184,7 @@ static int list_jobs(sd_bus *bus, char **args) {
 static int cancel_job(sd_bus *bus, char **args) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         char **name;
+        int r = 0;
 
         assert(args);
 
@@ -1688,31 +2192,41 @@ static int cancel_job(sd_bus *bus, char **args) {
                 return daemon_reload(bus, args);
 
         STRV_FOREACH(name, args+1) {
+                _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
                 uint32_t id;
-                int r;
+                int q;
 
-                r = safe_atou32(*name, &id);
-                if (r < 0) {
-                        log_error("Failed to parse job id \"%s\": %s", *name, strerror(-r));
-                        return r;
-                }
+                q = safe_atou32(*name, &id);
+                if (q < 0)
+                        return log_error_errno(q, "Failed to parse job id \"%s\": %m", *name);
 
-                r = sd_bus_call_method(
+                q = sd_bus_message_new_method_call(
                                 bus,
+                                &m,
                                 "org.freedesktop.systemd1",
                                 "/org/freedesktop/systemd1",
                                 "org.freedesktop.systemd1.Manager",
-                                "CancelJob",
-                                &error,
-                                NULL,
-                                "u", id);
-                if (r < 0) {
-                        log_error("Failed to cancel job %u: %s", (unsigned) id, bus_error_message(&error, r));
-                        return r;
+                                "CancelJob");
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+                if (q < 0)
+                        return bus_log_create_error(1);
+
+                q = sd_bus_message_append(m, "u", id);
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_call(bus, m, 0, &error, NULL);
+                if (q < 0) {
+                        log_error("Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q));
+                        if (r == 0)
+                                r = q;
                 }
         }
 
-        return 0;
+        return r;
 }
 
 static int need_daemon_reload(sd_bus *bus, const char *unit) {
@@ -1829,6 +2343,7 @@ static int enable_wait_for_jobs(sd_bus *bus) {
 
         r = sd_bus_add_match(
                         bus,
+                        NULL,
                         "type='signal',"
                         "sender='org.freedesktop.systemd1',"
                         "interface='org.freedesktop.systemd1.Manager',"
@@ -1870,9 +2385,19 @@ static int check_wait_response(WaitData *d) {
                 else if (streq(d->result, "canceled"))
                         log_error("Job for %s canceled.", strna(d->name));
                 else if (streq(d->result, "dependency"))
-                        log_error("A dependency job for %s failed. See 'journalctl -xn' for details.", strna(d->name));
-                else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
-                        log_error("Job for %s failed. See 'systemctl status %s' and 'journalctl -xn' for details.", strna(d->name), strna(d->name));
+                        log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
+                else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
+                        if (d->name) {
+                                bool quotes;
+
+                                quotes = chars_intersect(d->name, SHELL_NEED_QUOTES);
+
+                                log_error("Job for %s failed. See \"systemctl status %s%s%s\" and \"journalctl -xe\" for details.",
+                                          d->name,
+                                          quotes ? "'" : "", d->name, quotes ? "'" : "");
+                        } else
+                                log_error("Job failed. See \"journalctl -xe\" for details.");
+                }
         }
 
         if (streq(d->result, "timeout"))
@@ -1888,22 +2413,21 @@ static int check_wait_response(WaitData *d) {
 }
 
 static int wait_for_jobs(sd_bus *bus, Set *s) {
+        _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
         WaitData d = { .set = s };
         int r = 0, q;
 
         assert(bus);
         assert(s);
 
-        q = sd_bus_add_filter(bus, wait_filter, &d);
+        q = sd_bus_add_filter(bus, &slot, wait_filter, &d);
         if (q < 0)
                 return log_oom();
 
         while (!set_isempty(s)) {
                 q = bus_process_wait(bus);
-                if (q < 0) {
-                        log_error("Failed to wait for response: %s", strerror(-r));
-                        return q;
-                }
+                if (q < 0)
+                        return log_error_errno(q, "Failed to wait for response: %m");
 
                 if (d.result) {
                         q = check_wait_response(&d);
@@ -1922,10 +2446,6 @@ static int wait_for_jobs(sd_bus *bus, Set *s) {
                 d.result = NULL;
         }
 
-        q = sd_bus_remove_filter(bus, wait_filter, &d);
-        if (q < 0 && r == 0)
-                r = q;
-
         return r;
 }
 
@@ -2033,10 +2553,8 @@ static int check_triggering_units(
 
         STRV_FOREACH(i, triggered_by) {
                 r = check_one_unit(bus, *i, "active\0reloading\0", true);
-                if (r < 0) {
-                        log_error("Failed to check unit: %s", strerror(-r));
-                        return r;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "Failed to check unit: %m");
 
                 if (r == 0)
                         continue;
@@ -2052,6 +2570,23 @@ static int check_triggering_units(
         return 0;
 }
 
+static const struct {
+        const char *verb;
+        const char *method;
+} unit_actions[] = {
+        { "start",                 "StartUnit" },
+        { "stop",                  "StopUnit" },
+        { "condstop",              "StopUnit" },
+        { "reload",                "ReloadUnit" },
+        { "restart",               "RestartUnit" },
+        { "try-restart",           "TryRestartUnit" },
+        { "condrestart",           "TryRestartUnit" },
+        { "reload-or-restart",     "ReloadOrRestartUnit" },
+        { "reload-or-try-restart", "ReloadOrTryRestartUnit" },
+        { "condreload",            "ReloadOrTryRestartUnit" },
+        { "force-reload",          "ReloadOrTryRestartUnit" }
+};
+
 static const char *verb_to_method(const char *verb) {
        uint i;
 
@@ -2080,7 +2615,7 @@ static int start_unit_one(
                 sd_bus_error *error,
                 Set *s) {
 
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
         const char *path;
         int r;
 
@@ -2090,15 +2625,26 @@ static int start_unit_one(
         assert(error);
 
         log_debug("Calling manager for %s on %s, %s", method, name, mode);
-        r = sd_bus_call_method(
+
+        r = sd_bus_message_new_method_call(
                         bus,
+                        &m,
                         "org.freedesktop.systemd1",
                         "/org/freedesktop/systemd1",
                         "org.freedesktop.systemd1.Manager",
-                        method,
-                        error,
-                        &reply,
-                        "ss", name, mode);
+                        method);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "ss", name, mode);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_call(bus, m, 0, error, &reply);
         if (r < 0) {
                 const char *verb;
 
@@ -2139,7 +2685,6 @@ static int start_unit_one(
 
 static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) {
 
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
         char **name;
         int r = 0, i;
@@ -2155,21 +2700,20 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r
                         return log_oom();
 
                 if (string_is_glob(t))
-                        r = strv_push(&globs, t);
+                        r = strv_consume(&globs, t);
                 else
-                        r = strv_push(&mangled, t);
-                if (r < 0) {
-                        free(t);
+                        r = strv_consume(&mangled, t);
+                if (r < 0)
                         return log_oom();
-                }
         }
 
         /* Query the manager only if any of the names are a glob, since
          * this is fairly expensive */
         if (!strv_isempty(globs)) {
+                _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
                 _cleanup_free_ UnitInfo *unit_infos = NULL;
 
-                r = get_unit_list(bus, &reply, &unit_infos, globs);
+                r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
                 if (r < 0)
                         return r;
 
@@ -2180,6 +2724,7 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r
 
         *ret = mangled;
         mangled = NULL; /* do not free */
+
         return 0;
 }
 
@@ -2218,7 +2763,7 @@ static enum action verb_to_action(const char *verb) {
 static int start_unit(sd_bus *bus, char **args) {
         _cleanup_set_free_free_ Set *s = NULL;
         _cleanup_strv_free_ char **names = NULL;
-        const char *method, *mode, *one_name;
+        const char *method, *mode, *one_name, *suffix = NULL;
         char **name;
         int r = 0;
 
@@ -2231,8 +2776,11 @@ static int start_unit(sd_bus *bus, char **args) {
                 method = verb_to_method(args[0]);
                 action = verb_to_action(args[0]);
 
-                mode = streq(args[0], "isolate") ? "isolate" :
-                       action_table[action].mode ?: arg_job_mode;
+                if (streq(args[0], "isolate")) {
+                        mode = "isolate";
+                        suffix = ".target";
+                } else
+                        mode = action_table[action].mode ?: arg_job_mode;
 
                 one_name = action_table[action].target;
         } else {
@@ -2248,19 +2796,17 @@ static int start_unit(sd_bus *bus, char **args) {
         if (one_name)
                 names = strv_new(one_name, NULL);
         else {
-                r = expand_names(bus, args + 1, NULL, &names);
+                r = expand_names(bus, args + 1, suffix, &names);
                 if (r < 0)
-                        log_error("Failed to expand names: %s", strerror(-r));
+                        log_error_errno(r, "Failed to expand names: %m");
         }
 
         if (!arg_no_block) {
                 r = enable_wait_for_jobs(bus);
-                if (r < 0) {
-                        log_error("Could not watch jobs: %s", strerror(-r));
-                        return r;
-                }
+                if (r < 0)
+                        return log_error_errno(r, "Could not watch jobs: %m");
 
-                s = set_new(string_hash_func, string_compare_func);
+                s = set_new(&string_hash_ops);
                 if (!s)
                         return log_oom();
         }
@@ -2338,7 +2884,7 @@ static int reboot_with_logind(sd_bus *bus, enum action a) {
                         method,
                         &error,
                         NULL,
-                        "b", true);
+                        "b", arg_ask_password);
         if (r < 0)
                 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
 
@@ -2411,8 +2957,8 @@ static int check_inhibitors(sd_bus *bus, enum action a) {
                 get_process_comm(pid, &comm);
                 user = uid_to_name(uid);
 
-                log_warning("Operation inhibited by \"%s\" (PID %lu \"%s\", user %s), reason is \"%s\".",
-                            who, (unsigned long) pid, strna(comm), strna(user), why);
+                log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".",
+                            who, pid, strna(comm), strna(user), why);
 
                 c++;
         }
@@ -2517,10 +3063,8 @@ static int check_unit_generic(sd_bus *bus, int code, const char *good_states, ch
         assert(args);
 
         r = expand_names(bus, args, NULL, &names);
-        if (r < 0) {
-                log_error("Failed to expand names: %s", strerror(-r));
-                return r;
-        }
+        if (r < 0)
+                return log_error_errno(r, "Failed to expand names: %m");
 
         STRV_FOREACH(name, names) {
                 int state;
@@ -2558,21 +3102,32 @@ static int kill_unit(sd_bus *bus, char **args) {
 
         r = expand_names(bus, args + 1, NULL, &names);
         if (r < 0)
-                log_error("Failed to expand names: %s", strerror(-r));
+                log_error_errno(r, "Failed to expand names: %m");
 
         STRV_FOREACH(name, names) {
-                q = sd_bus_call_method(
+                _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+
+                q = sd_bus_message_new_method_call(
                                 bus,
+                                &m,
                                 "org.freedesktop.systemd1",
                                 "/org/freedesktop/systemd1",
                                 "org.freedesktop.systemd1.Manager",
-                                "KillUnit",
-                                &error,
-                                NULL,
-                                "ssi", *names, arg_kill_who, arg_signal);
+                                "KillUnit");
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_message_append(m, "ssi", *names, arg_kill_who, arg_signal);
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_call(bus, m, 0, &error, NULL);
                 if (q < 0) {
-                        log_error("Failed to kill unit %s: %s",
-                                  *names, bus_error_message(&error, r));
+                        log_error("Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
                         if (r == 0)
                                 r = q;
                 }
@@ -2665,6 +3220,7 @@ typedef struct UnitStatusInfo {
         const char *active_state;
         const char *sub_state;
         const char *unit_file_state;
+        const char *unit_file_preset;
 
         const char *description;
         const char *following;
@@ -2694,6 +3250,7 @@ typedef struct UnitStatusInfo {
         const char *status_text;
         const char *pid_file;
         bool running:1;
+        int status_errno;
 
         usec_t start_timestamp;
         usec_t exit_timestamp;
@@ -2705,7 +3262,14 @@ typedef struct UnitStatusInfo {
         bool failed_condition_trigger;
         bool failed_condition_negate;
         const char *failed_condition;
-        const char *failed_condition_param;
+        const char *failed_condition_parameter;
+
+        usec_t assert_timestamp;
+        bool assert_result;
+        bool failed_assert_trigger;
+        bool failed_assert_negate;
+        const char *failed_assert;
+        const char *failed_assert_parameter;
 
         /* Socket */
         unsigned n_accepted;
@@ -2732,7 +3296,7 @@ static void print_status_info(
                 bool *ellipsized) {
 
         ExecStatusInfo *p;
-        const char *on, *off, *ss;
+        const char *active_on, *active_off, *on, *off, *ss;
         usec_t timestamp;
         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
         char since2[FORMAT_TIMESTAMP_MAX], *s2;
@@ -2750,7 +3314,16 @@ static void print_status_info(
         /* This shows pretty information about a unit. See
          * print_property() for a low-level property printer */
 
-        printf("%s", strna(i->id));
+        if (streq_ptr(i->active_state, "failed")) {
+                active_on = ansi_highlight_red();
+                active_off = ansi_highlight_off();
+        } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
+                active_on = ansi_highlight_green();
+                active_off = ansi_highlight_off();
+        } else
+                active_on = active_off = "";
+
+        printf("%s%s%s %s", active_on, draw_special_char(DRAW_BLACK_CIRCLE), active_off, strna(i->id));
 
         if (i->description && !streq_ptr(i->id, i->description))
                 printf(" - %s", i->description);
@@ -2771,7 +3344,10 @@ static void print_status_info(
         if (i->load_error)
                 printf("   Loaded: %s%s%s (Reason: %s)\n",
                        on, strna(i->load_state), off, i->load_error);
-        else if (path && i->unit_file_state)
+        else if (path && !isempty(i->unit_file_state) && !isempty(i->unit_file_preset))
+                printf("   Loaded: %s%s%s (%s; %s; vendor preset: %s)\n",
+                       on, strna(i->load_state), off, path, i->unit_file_state, i->unit_file_preset);
+        else if (path && !isempty(i->unit_file_state))
                 printf("   Loaded: %s%s%s (%s; %s)\n",
                        on, strna(i->load_state), off, path, i->unit_file_state);
         else if (path)
@@ -2809,22 +3385,12 @@ static void print_status_info(
         }
 
         ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
-
-        if (streq_ptr(i->active_state, "failed")) {
-                on = ansi_highlight_red();
-                off = ansi_highlight_off();
-        } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
-                on = ansi_highlight_green();
-                off = ansi_highlight_off();
-        } else
-                on = off = "";
-
         if (ss)
                 printf("   Active: %s%s (%s)%s",
-                       on, strna(i->active_state), ss, off);
+                       active_on, strna(i->active_state), ss, active_off);
         else
                 printf("   Active: %s%s%s",
-                       on, strna(i->active_state), off);
+                       active_on, strna(i->active_state), active_off);
 
         if (!isempty(i->result) && !streq(i->result, "success"))
                 printf(" (Result: %s)", i->result);
@@ -2850,7 +3416,8 @@ static void print_status_info(
                 s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp);
                 s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
 
-                printf("           start condition failed at %s%s%s\n",
+                printf("Condition: start %scondition failed%s at %s%s%s\n",
+                       ansi_highlight_yellow(), ansi_highlight_off(),
                        s2, s1 ? "; " : "", s1 ? s1 : "");
                 if (i->failed_condition_trigger)
                         printf("           none of the trigger conditions were met\n");
@@ -2858,7 +3425,23 @@ static void print_status_info(
                         printf("           %s=%s%s was not met\n",
                                i->failed_condition,
                                i->failed_condition_negate ? "!" : "",
-                               i->failed_condition_param);
+                               i->failed_condition_parameter);
+        }
+
+        if (!i->assert_result && i->assert_timestamp > 0) {
+                s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp);
+                s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp);
+
+                printf("   Assert: start %sassertion failed%s at %s%s%s\n",
+                       ansi_highlight_red(), ansi_highlight_off(),
+                       s2, s1 ? "; " : "", s1 ? s1 : "");
+                if (i->failed_assert_trigger)
+                        printf("           none of the trigger assertions were met\n");
+                else if (i->failed_assert)
+                        printf("           %s=%s%s was not met\n",
+                               i->failed_assert,
+                               i->failed_assert_negate ? "!" : "",
+                               i->failed_assert_parameter);
         }
 
         if (i->sysfs_path)
@@ -2923,7 +3506,7 @@ static void print_status_info(
 
         if (i->main_pid > 0 || i->control_pid > 0) {
                 if (i->main_pid > 0) {
-                        printf(" Main PID: %u", (unsigned) i->main_pid);
+                        printf(" Main PID: "PID_FMT, i->main_pid);
 
                         if (i->running) {
                                 _cleanup_free_ char *comm = NULL;
@@ -2954,7 +3537,7 @@ static void print_status_info(
                 if (i->control_pid > 0) {
                         _cleanup_free_ char *c = NULL;
 
-                        printf(" %8s: %u", i->main_pid ? "" : " Control", (unsigned) i->control_pid);
+                        printf(" %8s: "PID_FMT, i->main_pid ? "" : " Control", i->control_pid);
 
                         get_process_comm(i->control_pid, &c);
                         if (c)
@@ -2966,9 +3549,12 @@ static void print_status_info(
 
         if (i->status_text)
                 printf("   Status: \"%s\"\n", i->status_text);
+        if (i->status_errno > 0)
+                printf("    Error: %i (%s)\n", i->status_errno, strerror(i->status_errno));
 
         if (i->control_group &&
-            (i->main_pid > 0 || i->control_pid > 0 || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0)) {
+            (i->main_pid > 0 || i->control_pid > 0 ||
+             ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_CONTAINER) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) {
                 unsigned c;
 
                 printf("   CGroup: %s\n", i->control_group);
@@ -2976,7 +3562,7 @@ static void print_status_info(
                 if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_CONTAINER) {
                         unsigned k = 0;
                         pid_t extra[2];
-                        char prefix[] = "           ";
+                        static const char prefix[] = "           ";
 
                         c = columns();
                         if (c > sizeof(prefix) - 1)
@@ -2990,13 +3576,11 @@ static void print_status_info(
                         if (i->control_pid > 0)
                                 extra[k++] = i->control_pid;
 
-                        show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix,
-                                                      c, false, extra, k, flags);
+                        show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, flags);
                 }
         }
 
         if (i->id && arg_transport == BUS_TRANSPORT_LOCAL) {
-                printf("\n");
                 show_journal_by_unit(stdout,
                                      i->id,
                                      arg_output,
@@ -3004,7 +3588,7 @@ static void print_status_info(
                                      i->inactive_exit_timestamp_monotonic,
                                      arg_lines,
                                      getuid(),
-                                     flags,
+                                     flags | OUTPUT_BEGIN_NEWLINE,
                                      arg_scope == UNIT_FILE_SYSTEM,
                                      ellipsized);
         }
@@ -3026,50 +3610,11 @@ static void show_unit_help(UnitStatusInfo *i) {
                 return;
         }
 
-        STRV_FOREACH(p, i->documentation) {
-
-                if (startswith(*p, "man:")) {
-                        const char *args[4] = { "man", NULL, NULL, NULL };
-                        _cleanup_free_ char *page = NULL, *section = NULL;
-                        char *e = NULL;
-                        pid_t pid;
-                        size_t k;
-
-                        k = strlen(*p);
-
-                        if ((*p)[k-1] == ')')
-                                e = strrchr(*p, '(');
-
-                        if (e) {
-                                page = strndup((*p) + 4, e - *p - 4);
-                                section = strndup(e + 1, *p + k - e - 2);
-                                if (!page || !section) {
-                                        log_oom();
-                                        return;
-                                }
-
-                                args[1] = section;
-                                args[2] = page;
-                        } else
-                                args[1] = *p + 4;
-
-                        pid = fork();
-                        if (pid < 0) {
-                                log_error("Failed to fork: %m");
-                                continue;
-                        }
-
-                        if (pid == 0) {
-                                /* Child */
-                                execvp(args[0], (char**) args);
-                                log_error("Failed to execute man: %m");
-                                _exit(EXIT_FAILURE);
-                        }
-
-                        wait_for_terminate(pid, NULL);
-                } else
+        STRV_FOREACH(p, i->documentation)
+                if (startswith(*p, "man:"))
+                        show_man_page(*p + 4, false);
+                else
                         log_info("Can't show: %s", *p);
-        }
 }
 
 static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *i, const char *contents) {
@@ -3127,6 +3672,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
                                 i->following = s;
                         else if (streq(name, "UnitFileState"))
                                 i->unit_file_state = s;
+                        else if (streq(name, "UnitFilePreset"))
+                                i->unit_file_preset = s;
                         else if (streq(name, "Result"))
                                 i->result = s;
                 }
@@ -3147,6 +3694,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
                         i->need_daemon_reload = b;
                 else if (streq(name, "ConditionResult"))
                         i->condition_result = b;
+                else if (streq(name, "AssertResult"))
+                        i->assert_result = b;
 
                 break;
         }
@@ -3187,6 +3736,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
                         i->exit_code = (int) j;
                 else if (streq(name, "ExecMainStatus"))
                         i->exit_status = (int) j;
+                else if (streq(name, "StatusErrno"))
+                        i->status_errno = (int) j;
 
                 break;
         }
@@ -3214,6 +3765,8 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
                         i->active_exit_timestamp = (usec_t) u;
                 else if (streq(name, "ConditionTimestamp"))
                         i->condition_timestamp = (usec_t) u;
+                else if (streq(name, "AssertTimestamp"))
+                        i->assert_timestamp = (usec_t) u;
 
                 break;
         }
@@ -3306,7 +3859,32 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *
                                         i->failed_condition = cond;
                                         i->failed_condition_trigger = trigger;
                                         i->failed_condition_negate = negate;
-                                        i->failed_condition_param = param;
+                                        i->failed_condition_parameter = param;
+                                }
+                        }
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        r = sd_bus_message_exit_container(m);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Asserts")) {
+                        const char *cond, *param;
+                        int trigger, negate;
+                        int32_t state;
+
+                        r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)");
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, &param, &state)) > 0) {
+                                log_debug("%s %d %d %s %d", cond, trigger, negate, param, state);
+                                if (state < 0 && (!trigger || !i->failed_assert)) {
+                                        i->failed_assert = cond;
+                                        i->failed_assert_trigger = trigger;
+                                        i->failed_assert_negate = negate;
+                                        i->failed_assert_parameter = param;
                                 }
                         }
                         if (r < 0)
@@ -3378,7 +3956,7 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
                                 return bus_log_parse_error(r);
 
                         if (u > 0)
-                                printf("%s=%u\n", name, (unsigned) u);
+                                printf("%s=%"PRIu32"\n", name, u);
                         else if (arg_all)
                                 printf("%s=\n", name);
 
@@ -3549,14 +4127,14 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
 
                                 tt = strv_join(info.argv, " ");
 
-                                printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
+                                printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }\n",
                                        name,
                                        strna(info.path),
                                        strna(tt),
                                        yes_no(info.ignore),
                                        strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
                                        strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
-                                       (unsigned) info. pid,
+                                       info.pid,
                                        sigchld_code_to_string(info.code),
                                        info.status,
                                        info.code == CLD_EXITED ? "" : "/",
@@ -3746,8 +4324,8 @@ static int show_one(
             streq(verb, "status")) {
                 /* According to LSB: "program not running" */
                 /* 0: program is running or service is OK
-                 * 1: program is dead and /var/run pid file exists
-                 * 2: program is dead and /var/lock lock file exists
+                 * 1: program is dead and /run PID file exists
+                 * 2: program is dead and /run/lock lock file exists
                  * 3: program is not running
                  * 4: program or service status is unknown
                  */
@@ -3785,7 +4363,7 @@ static int get_unit_dbus_path_by_pid(
                         &reply,
                         "u", pid);
         if (r < 0) {
-                log_error("Failed to get unit for PID %lu: %s", (unsigned long) pid, bus_error_message(&error, r));
+                log_error("Failed to get unit for PID "PID_FMT": %s", pid, bus_error_message(&error, r));
                 return r;
         }
 
@@ -3812,9 +4390,9 @@ static int show_all(
         _cleanup_free_ UnitInfo *unit_infos = NULL;
         const UnitInfo *u;
         unsigned c;
-        int r;
+        int r, ret = 0;
 
-        r = get_unit_list(bus, &reply, &unit_infos, NULL);
+        r = get_unit_list(bus, NULL, NULL, &unit_infos, 0, &reply);
         if (r < 0)
                 return r;
 
@@ -3832,105 +4410,76 @@ static int show_all(
                         return log_oom();
 
                 r = show_one(verb, bus, p, show_properties, new_line, ellipsized);
-                if (r != 0)
+                if (r < 0)
                         return r;
+                else if (r > 0 && ret == 0)
+                        ret = r;
         }
 
-        return 0;
+        return ret;
 }
 
-static int cat(sd_bus *bus, char **args) {
-        _cleanup_free_ char *unit = NULL;
-        _cleanup_strv_free_ char **names = NULL;
-        char **name;
-        bool first = true;
-        int r = 0;
+static int show_system_status(sd_bus *bus) {
+        char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX];
+        _cleanup_free_ char *hn = NULL;
+        struct machine_info mi = {};
+        const char *on, *off;
+        int r;
 
-        assert(bus);
-        assert(args);
+        hn = gethostname_malloc();
+        if (!hn)
+                return log_oom();
 
-        r = expand_names(bus, args + 1, NULL, &names);
+        r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &mi);
         if (r < 0)
-                log_error("Failed to expand names: %s", strerror(-r));
+                return log_error_errno(r, "Failed to read server status: %m");
 
-        pager_open_if_enabled();
-
-        STRV_FOREACH(name, names) {
-                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-                _cleanup_strv_free_ char **dropin_paths = NULL;
-                _cleanup_free_ char *fragment_path = NULL;
-                char **path;
-
-                unit = unit_dbus_path_from_name(*name);
-                if (!unit)
-                        return log_oom();
+        if (streq_ptr(mi.state, "degraded")) {
+                on = ansi_highlight_red();
+                off = ansi_highlight_off();
+        } else if (!streq_ptr(mi.state, "running")) {
+                on = ansi_highlight_yellow();
+                off = ansi_highlight_off();
+        } else
+                on = off = "";
 
-                if (need_daemon_reload(bus, *name) > 0)
-                        log_warning("Unit file of %s changed on disk. Run 'systemctl%s daemon-reload'.",
-                                    *name, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
+        printf("%s%s%s %s\n", on, draw_special_char(DRAW_BLACK_CIRCLE), off, arg_host ? arg_host : hn);
 
-                r = sd_bus_get_property_string(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                unit,
-                                "org.freedesktop.systemd1.Unit",
-                                "FragmentPath",
-                                &error,
-                                &fragment_path);
-                if (r < 0) {
-                        log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r));
-                        continue;
-                }
+        printf("    State: %s%s%s\n",
+               on, strna(mi.state), off);
 
-                r = sd_bus_get_property_strv(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                unit,
-                                "org.freedesktop.systemd1.Unit",
-                                "DropInPaths",
-                                &error,
-                                &dropin_paths);
-                if (r < 0) {
-                        log_warning("Failed to get DropInPaths: %s", bus_error_message(&error, r));
-                        continue;
-                }
+        printf("     Jobs: %u queued\n", mi.n_jobs);
+        printf("   Failed: %u units\n", mi.n_failed_units);
 
-                if (first)
-                        first = false;
-                else
-                        puts("");
+        printf("    Since: %s; %s\n",
+               format_timestamp(since2, sizeof(since2), mi.timestamp),
+               format_timestamp_relative(since1, sizeof(since1), mi.timestamp));
 
-                if (!isempty(fragment_path)) {
-                        printf("%s# %s%s\n",
-                               ansi_highlight_blue(),
-                               fragment_path,
-                               ansi_highlight_off());
-                        fflush(stdout);
+        printf("   CGroup: %s\n", mi.control_group ?: "/");
+        if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_CONTAINER) {
+                int flags =
+                        arg_all * OUTPUT_SHOW_ALL |
+                        (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
+                        on_tty() * OUTPUT_COLOR |
+                        !arg_quiet * OUTPUT_WARN_CUTOFF |
+                        arg_full * OUTPUT_FULL_WIDTH;
 
-                        r = sendfile_full(STDOUT_FILENO, fragment_path);
-                        if (r < 0) {
-                                log_warning("Failed to cat %s: %s", fragment_path, strerror(-r));
-                                continue;
-                        }
-                }
+                static const char prefix[] = "           ";
+                unsigned c;
 
-                STRV_FOREACH(path, dropin_paths) {
-                        printf("%s%s# %s%s\n",
-                               isempty(fragment_path) && path == dropin_paths ? "" : "\n",
-                               ansi_highlight_blue(),
-                               *path,
-                               ansi_highlight_off());
-                        fflush(stdout);
+                c = columns();
+                if (c > sizeof(prefix) - 1)
+                        c -= sizeof(prefix) - 1;
+                else
+                        c = 0;
 
-                        r = sendfile_full(STDOUT_FILENO, *path);
-                        if (r < 0) {
-                                log_warning("Failed to cat %s: %s", *path, strerror(-r));
-                                continue;
-                        }
-                }
+                show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, flags);
         }
 
-        return r < 0 ? r : 0;
+        free(mi.state);
+        free(mi.control_group);
+
+        return 0;
 }
 
 static int show(sd_bus *bus, char **args) {
@@ -3952,9 +4501,15 @@ static int show(sd_bus *bus, char **args) {
         if (show_properties && strv_length(args) <= 1)
                 return show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line, &ellipsized);
 
-        if (show_status && strv_length(args) <= 1)
-                ret = show_all(args[0], bus, false, &new_line, &ellipsized);
-        else {
+        if (show_status && strv_length(args) <= 1) {
+
+                pager_open_if_enabled();
+                show_system_status(bus);
+                new_line = true;
+
+                if (arg_all)
+                        ret = show_all(args[0], bus, false, &new_line, &ellipsized);
+        } else {
                 _cleanup_free_ char **patterns = NULL;
                 char **name;
 
@@ -3981,7 +4536,12 @@ static int show(sd_bus *bus, char **args) {
                                 }
                         }
 
-                        show_one(args[0], bus, unit, show_properties, &new_line, &ellipsized);
+                        r = show_one(args[0], bus, unit, show_properties,
+                                     &new_line, &ellipsized);
+                        if (r < 0)
+                                return r;
+                        else if (r > 0 && ret == 0)
+                                ret = r;
                 }
 
                 if (!strv_isempty(patterns)) {
@@ -3989,7 +4549,7 @@ static int show(sd_bus *bus, char **args) {
 
                         r = expand_names(bus, patterns, NULL, &names);
                         if (r < 0)
-                                log_error("Failed to expand names: %s", strerror(-r));
+                                log_error_errno(r, "Failed to expand names: %m");
 
                         STRV_FOREACH(name, names) {
                                 _cleanup_free_ char *unit;
@@ -3998,7 +4558,12 @@ static int show(sd_bus *bus, char **args) {
                                 if (!unit)
                                         return log_oom();
 
-                                show_one(args[0], bus, unit, show_properties, &new_line, &ellipsized);
+                                r = show_one(args[0], bus, unit, show_properties,
+                                             &new_line, &ellipsized);
+                                if (r < 0)
+                                        return r;
+                                else if (r > 0 && ret == 0)
+                                        ret = r;
                         }
                 }
         }
@@ -4009,162 +4574,98 @@ static int show(sd_bus *bus, char **args) {
         return ret;
 }
 
-static int append_assignment(sd_bus_message *m, const char *assignment) {
-        const char *eq;
-        char *field;
-        int r;
-
-        assert(m);
-        assert(assignment);
-
-        eq = strchr(assignment, '=');
-        if (!eq) {
-                log_error("Not an assignment: %s", assignment);
-                return -EINVAL;
-        }
+static int cat(sd_bus *bus, char **args) {
+        _cleanup_strv_free_ char **names = NULL;
+        char **name;
+        bool first = true;
+        int r = 0;
 
-        field = strndupa(assignment, eq - assignment);
-        eq ++;
+        assert(bus);
+        assert(args);
 
-        r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+        r = expand_names(bus, args + 1, NULL, &names);
         if (r < 0)
-                return bus_log_create_error(r);
+                log_error_errno(r, "Failed to expand names: %m");
 
-        if (streq(field, "CPUAccounting") ||
-            streq(field, "MemoryAccounting") ||
-            streq(field, "BlockIOAccounting")) {
+        pager_open_if_enabled();
 
-                r = parse_boolean(eq);
-                if (r < 0) {
-                        log_error("Failed to parse boolean assignment %s.", assignment);
-                        return -EINVAL;
-                }
+        STRV_FOREACH(name, names) {
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+                _cleanup_strv_free_ char **dropin_paths = NULL;
+                _cleanup_free_ char *fragment_path = NULL, *unit = NULL;
+                char **path;
 
-                r = sd_bus_message_append(m, "v", "b", r);
+                unit = unit_dbus_path_from_name(*name);
+                if (!unit)
+                        return log_oom();
 
-        } else if (streq(field, "MemoryLimit")) {
-                off_t bytes;
+                if (need_daemon_reload(bus, *name) > 0)
+                        log_warning("Unit file of %s changed on disk. Run 'systemctl%s daemon-reload'.",
+                                    *name, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
 
-                r = parse_bytes(eq, &bytes);
+                r = sd_bus_get_property_string(
+                                bus,
+                                "org.freedesktop.systemd1",
+                                unit,
+                                "org.freedesktop.systemd1.Unit",
+                                "FragmentPath",
+                                &error,
+                                &fragment_path);
                 if (r < 0) {
-                        log_error("Failed to parse bytes specification %s", assignment);
-                        return -EINVAL;
+                        log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r));
+                        continue;
                 }
 
-                r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes);
-
-        } else if (streq(field, "CPUShares") || streq(field, "BlockIOWeight")) {
-                uint64_t u;
-
-                r = safe_atou64(eq, &u);
+                r = sd_bus_get_property_strv(
+                                bus,
+                                "org.freedesktop.systemd1",
+                                unit,
+                                "org.freedesktop.systemd1.Unit",
+                                "DropInPaths",
+                                &error,
+                                &dropin_paths);
                 if (r < 0) {
-                        log_error("Failed to parse %s value %s.", field, eq);
-                        return -EINVAL;
+                        log_warning("Failed to get DropInPaths: %s", bus_error_message(&error, r));
+                        continue;
                 }
 
-                r = sd_bus_message_append(m, "v", "t", u);
-
-        } else if (streq(field, "DevicePolicy"))
-                r = sd_bus_message_append(m, "v", "s", eq);
-
-        else if (streq(field, "DeviceAllow")) {
-
-                if (isempty(eq))
-                        r = sd_bus_message_append(m, "v", "a(ss)", 0);
-                else {
-                        const char *path, *rwm;
-                        char *e;
+                if (first)
+                        first = false;
+                else
+                        puts("");
 
-                        e = strchr(eq, ' ');
-                        if (e) {
-                                path = strndupa(eq, e - eq);
-                                rwm = e+1;
-                        } else {
-                                path = eq;
-                                rwm = "";
-                        }
+                if (!isempty(fragment_path)) {
+                        printf("%s# %s%s\n",
+                               ansi_highlight_blue(),
+                               fragment_path,
+                               ansi_highlight_off());
+                        fflush(stdout);
 
-                        if (!path_startswith(path, "/dev")) {
-                                log_error("%s is not a device file in /dev.", path);
-                                return -EINVAL;
+                        r = copy_file_fd(fragment_path, STDOUT_FILENO);
+                        if (r < 0) {
+                                log_warning_errno(r, "Failed to cat %s: %m", fragment_path);
+                                continue;
                         }
-
-                        r = sd_bus_message_append(m, "v", "a(ss)", 1, path, rwm);
                 }
 
-        } else if (streq(field, "BlockIOReadBandwidth") || streq(field, "BlockIOWriteBandwidth")) {
-
-                if (isempty(eq))
-                        r = sd_bus_message_append(m, "v", "a(st)", 0);
-                else {
-                        const char *path, *bandwidth;
-                        off_t bytes;
-                        char *e;
-
-                        e = strchr(eq, ' ');
-                        if (e) {
-                                path = strndupa(eq, e - eq);
-                                bandwidth = e+1;
-                        } else {
-                                log_error("Failed to parse %s value %s.", field, eq);
-                                return -EINVAL;
-                        }
-
-                        if (!path_startswith(path, "/dev")) {
-                                log_error("%s is not a device file in /dev.", path);
-                                return -EINVAL;
-                        }
+                STRV_FOREACH(path, dropin_paths) {
+                        printf("%s%s# %s%s\n",
+                               isempty(fragment_path) && path == dropin_paths ? "" : "\n",
+                               ansi_highlight_blue(),
+                               *path,
+                               ansi_highlight_off());
+                        fflush(stdout);
 
-                        r = parse_bytes(bandwidth, &bytes);
+                        r = copy_file_fd(*path, STDOUT_FILENO);
                         if (r < 0) {
-                                log_error("Failed to parse byte value %s.", bandwidth);
-                                return -EINVAL;
+                                log_warning_errno(r, "Failed to cat %s: %m", *path);
+                                continue;
                         }
-
-                        r = sd_bus_message_append(m, "v", "a(st)", 1, path, (uint64_t) bytes);
                 }
+        }
 
-        } else if (streq(field, "BlockIODeviceWeight")) {
-
-                if (isempty(eq))
-                        r = sd_bus_message_append(m, "v", "a(st)", 0);
-                else {
-                        const char *path, *weight;
-                        uint64_t u;
-                        char *e;
-
-                        e = strchr(eq, ' ');
-                        if (e) {
-                                path = strndupa(eq, e - eq);
-                                weight = e+1;
-                        } else {
-                                log_error("Failed to parse %s value %s.", field, eq);
-                                return -EINVAL;
-                        }
-
-                        if (!path_startswith(path, "/dev")) {
-                                log_error("%s is not a device file in /dev.", path);
-                                return -EINVAL;
-                        }
-
-                        r = safe_atou64(weight, &u);
-                        if (r < 0) {
-                                log_error("Failed to parse %s value %s.", field, weight);
-                                return -EINVAL;
-                        }
-                        r = sd_bus_message_append(m, "v", "a(st)", path, u);
-                }
-
-        } else {
-                log_error("Unknown assignment %s.", assignment);
-                return -EINVAL;
-        }
-
-        if (r < 0)
-                return bus_log_create_error(r);
-
-        return 0;
-}
+        return r < 0 ? r : 0;
+}
 
 static int set_property(sd_bus *bus, char **args) {
         _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
@@ -4175,11 +4676,15 @@ static int set_property(sd_bus *bus, char **args) {
 
         r = sd_bus_message_new_method_call(
                         bus,
+                        &m,
                         "org.freedesktop.systemd1",
                         "/org/freedesktop/systemd1",
                         "org.freedesktop.systemd1.Manager",
-                        "SetUnitProperties",
-                        &m);
+                        "SetUnitProperties");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -4200,7 +4705,7 @@ static int set_property(sd_bus *bus, char **args) {
                 if (r < 0)
                         return bus_log_create_error(r);
 
-                r = append_assignment(m, *i);
+                r = bus_append_unit_property_assignment(m, *i);
                 if (r < 0)
                         return r;
 
@@ -4224,7 +4729,7 @@ static int set_property(sd_bus *bus, char **args) {
 
 static int snapshot(sd_bus *bus, char **args) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
         _cleanup_free_ char *n = NULL, *id = NULL;
         const char *path;
         int r;
@@ -4236,15 +4741,25 @@ static int snapshot(sd_bus *bus, char **args) {
         if (!n)
                 return log_oom();
 
-        r = sd_bus_call_method(
+        r = sd_bus_message_new_method_call(
                         bus,
+                        &m,
                         "org.freedesktop.systemd1",
                         "/org/freedesktop/systemd1",
                         "org.freedesktop.systemd1.Manager",
-                        "CreateSnapshot",
-                        &error,
-                        &reply,
-                        "sb", n, false);
+                        "CreateSnapshot");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_append(m, "sb", n, false);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_call(bus, m, 0, &error, &reply);
         if (r < 0) {
                 log_error("Failed to create snapshot: %s", bus_error_message(&error, r));
                 return r;
@@ -4277,27 +4792,39 @@ static int delete_snapshot(sd_bus *bus, char **args) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_strv_free_ char **names = NULL;
         char **name;
-        int r, q;
+        int r;
 
         assert(args);
 
         r = expand_names(bus, args + 1, ".snapshot", &names);
         if (r < 0)
-                log_error("Failed to expand names: %s", strerror(-r));
+                log_error_errno(r, "Failed to expand names: %m");
 
         STRV_FOREACH(name, names) {
-                q = sd_bus_call_method(
+                _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+                int q;
+
+                q = sd_bus_message_new_method_call(
                                 bus,
+                                &m,
                                 "org.freedesktop.systemd1",
                                 "/org/freedesktop/systemd1",
                                 "org.freedesktop.systemd1.Manager",
-                                "RemoveSnapshot",
-                                &error,
-                                NULL,
-                                "s", *name);
+                                "RemoveSnapshot");
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_message_append(m, "s", *name);
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_call(bus, m, 0, &error, NULL);
                 if (q < 0) {
-                        log_error("Failed to remove snapshot %s: %s",
-                                  *name, bus_error_message(&error, r));
+                        log_error("Failed to remove snapshot %s: %s", *name, bus_error_message(&error, q));
                         if (r == 0)
                                 r = q;
                 }
@@ -4308,6 +4835,7 @@ static int delete_snapshot(sd_bus *bus, char **args) {
 
 static int daemon_reload(sd_bus *bus, char **args) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
         const char *method;
         int r;
 
@@ -4331,16 +4859,21 @@ static int daemon_reload(sd_bus *bus, char **args) {
                                     /* "daemon-reload" */ "Reload";
         }
 
-        r = sd_bus_call_method(
+        r = sd_bus_message_new_method_call(
                         bus,
+                        &m,
                         "org.freedesktop.systemd1",
                         "/org/freedesktop/systemd1",
                         "org.freedesktop.systemd1.Manager",
-                        method,
-                        &error,
-                        NULL,
-                        NULL);
+                        method);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+        if (r < 0)
+                return bus_log_create_error(r);
 
+        r = sd_bus_call(bus, m, 0, &error, NULL);
         if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL)
                 /* There's always a fallback possible for
                  * legacy actions. */
@@ -4366,21 +4899,32 @@ static int reset_failed(sd_bus *bus, char **args) {
 
         r = expand_names(bus, args + 1, NULL, &names);
         if (r < 0)
-                log_error("Failed to expand names: %s", strerror(-r));
+                log_error_errno(r, "Failed to expand names: %m");
 
         STRV_FOREACH(name, names) {
-                q = sd_bus_call_method(
+                _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
+
+                q = sd_bus_message_new_method_call(
                                 bus,
+                                &m,
                                 "org.freedesktop.systemd1",
                                 "/org/freedesktop/systemd1",
                                 "org.freedesktop.systemd1.Manager",
-                                "ResetFailedUnit",
-                                &error,
-                                NULL,
-                                "s", *name);
+                                "ResetFailedUnit");
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_message_append(m, "s", *name);
+                if (q < 0)
+                        return bus_log_create_error(q);
+
+                q = sd_bus_call(bus, m, 0, &error, NULL);
                 if (q < 0) {
-                        log_error("Failed to reset failed state of unit %s: %s",
-                                  *name, bus_error_message(&error, r));
+                        log_error("Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q));
                         if (r == 0)
                                 r = q;
                 }
@@ -4429,8 +4973,8 @@ static int show_environment(sd_bus *bus, char **args) {
 
 static int switch_root(sd_bus *bus, char **args) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_free_ char *init = NULL;
-        const char *root;
+        _cleanup_free_ char *cmdline_init = NULL;
+        const char *root, *init;
         unsigned l;
         int r;
 
@@ -4443,20 +4987,33 @@ static int switch_root(sd_bus *bus, char **args) {
         root = args[1];
 
         if (l >= 3)
-                init = strdup(args[2]);
+                init = args[2];
         else {
-                parse_env_file("/proc/cmdline", WHITESPACE,
-                               "init", &init,
-                               NULL);
+                r = parse_env_file("/proc/cmdline", WHITESPACE,
+                                   "init", &cmdline_init,
+                                   NULL);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse /proc/cmdline: %m");
 
-                if (!init)
-                        init = strdup("");
+                init = cmdline_init;
         }
 
-        if (!init)
-                return log_oom();
+        if (isempty(init))
+                init = NULL;
+
+        if (init) {
+                const char *root_systemd_path = NULL, *root_init_path = NULL;
+
+                root_systemd_path = strappenda(root, "/" SYSTEMD_BINARY_PATH);
+                root_init_path = strappenda(root, "/", init);
+
+                /* If the passed init is actually the same as the
+                 * systemd binary, then let's suppress it. */
+                if (files_same(root_init_path, root_systemd_path) > 0)
+                        init = NULL;
+        }
 
-        log_debug("switching root - root: %s; init: %s", root, init);
+        log_debug("Switching root - root: %s; init: %s", root, strna(init));
 
         r = sd_bus_call_method(
                         bus,
@@ -4490,11 +5047,15 @@ static int set_environment(sd_bus *bus, char **args) {
 
         r = sd_bus_message_new_method_call(
                         bus,
+                        &m,
                         "org.freedesktop.systemd1",
                         "/org/freedesktop/systemd1",
                         "org.freedesktop.systemd1.Manager",
-                        method,
-                        &m);
+                        method);
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -4521,11 +5082,15 @@ static int import_environment(sd_bus *bus, char **args) {
 
         r = sd_bus_message_new_method_call(
                         bus,
+                        &m,
                         "org.freedesktop.systemd1",
                         "/org/freedesktop/systemd1",
                         "org.freedesktop.systemd1.Manager",
-                        "SetEnvironment",
-                        &m);
+                        "SetEnvironment");
+        if (r < 0)
+                return bus_log_create_error(r);
+
+        r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
         if (r < 0)
                 return bus_log_create_error(r);
 
@@ -4578,7 +5143,7 @@ static int enable_sysv_units(const char *verb, char **args) {
         int r = 0;
 
 #if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG)
-        unsigned f = 1, t = 1;
+        unsigned f = 0;
         _cleanup_lookup_paths_free_ LookupPaths paths = {};
 
         if (arg_scope != UNIT_FILE_SYSTEM)
@@ -4592,23 +5157,23 @@ static int enable_sysv_units(const char *verb, char **args) {
         /* Processes all SysV units, and reshuffles the array so that
          * afterwards only the native units remain */
 
-        r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, NULL, NULL, NULL);
+        r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, arg_root, NULL, NULL, NULL);
         if (r < 0)
                 return r;
 
         r = 0;
-        for (f = 0; args[f]; f++) {
+        while (args[f]) {
                 const char *name;
-                _cleanup_free_ char *p = NULL, *q = NULL;
+                _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL;
                 bool found_native = false, found_sysv;
                 unsigned c = 1;
                 const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
-                char **k, *l;
+                char **k;
                 int j;
                 pid_t pid;
                 siginfo_t status;
 
-                name = args[f];
+                name = args[f++];
 
                 if (!endswith(name, ".service"))
                         continue;
@@ -4617,20 +5182,13 @@ static int enable_sysv_units(const char *verb, char **args) {
                         continue;
 
                 STRV_FOREACH(k, paths.unit_path) {
-                        if (!isempty(arg_root))
-                                asprintf(&p, "%s/%s/%s", arg_root, *k, name);
-                        else
-                                asprintf(&p, "%s/%s", *k, name);
-
-                        if (!p) {
-                                r = log_oom();
-                                goto finish;
-                        }
+                        _cleanup_free_ char *path = NULL;
 
-                        found_native = access(p, F_OK) >= 0;
-                        free(p);
-                        p = NULL;
+                        path = path_join(arg_root, *k, name);
+                        if (!path)
+                                return log_oom();
 
+                        found_native = access(path, F_OK) >= 0;
                         if (found_native)
                                 break;
                 }
@@ -4638,24 +5196,15 @@ static int enable_sysv_units(const char *verb, char **args) {
                 if (found_native)
                         continue;
 
-                if (!isempty(arg_root))
-                        asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
-                else
-                        asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name);
-                if (!p) {
-                        r = log_oom();
-                        goto finish;
-                }
+                p = path_join(arg_root, SYSTEM_SYSVINIT_PATH, name);
+                if (!p)
+                        return log_oom();
 
-                p[strlen(p) - sizeof(".service") + 1] = 0;
+                p[strlen(p) - strlen(".service")] = 0;
                 found_sysv = access(p, F_OK) >= 0;
-
                 if (!found_sysv)
                         continue;
 
-                /* Mark this entry, so that we don't try enabling it as native unit */
-                args[f] = (char*) "";
-
                 log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
 
                 if (!isempty(arg_root))
@@ -4668,20 +5217,15 @@ static int enable_sysv_units(const char *verb, char **args) {
                 argv[c] = NULL;
 
                 l = strv_join((char**)argv, " ");
-                if (!l) {
-                        r = log_oom();
-                        goto finish;
-                }
+                if (!l)
+                        return log_oom();
 
                 log_info("Executing %s", l);
-                free(l);
 
                 pid = fork();
-                if (pid < 0) {
-                        log_error("Failed to fork: %m");
-                        r = -errno;
-                        goto finish;
-                } else if (pid == 0) {
+                if (pid < 0)
+                        return log_error_errno(errno, "Failed to fork: %m");
+                else if (pid == 0) {
                         /* Child */
 
                         execv(argv[0], (char**) argv);
@@ -4690,9 +5234,8 @@ static int enable_sysv_units(const char *verb, char **args) {
 
                 j = wait_for_terminate(pid, &status);
                 if (j < 0) {
-                        log_error("Failed to wait for child: %s", strerror(-r));
-                        r = j;
-                        goto finish;
+                        log_error_errno(r, "Failed to wait for child: %m");
+                        return j;
                 }
 
                 if (status.si_code == CLD_EXITED) {
@@ -4706,28 +5249,16 @@ static int enable_sysv_units(const char *verb, char **args) {
                                                 puts("disabled");
                                 }
 
-                        } else if (status.si_status != 0) {
-                                r = -EINVAL;
-                                goto finish;
-                        }
-                } else {
-                        r = -EPROTO;
-                        goto finish;
-                }
-        }
-
-finish:
-        /* Drop all SysV units */
-        for (f = 0, t = 0; args[f]; f++) {
-
-                if (isempty(args[f]))
-                        continue;
+                        } else if (status.si_status != 0)
+                                return -EINVAL;
+                } else
+                        return -EPROTO;
 
-                args[t++] = args[f];
+                /* Remove this entry, so that we don't try enabling it as native unit */
+                assert(f > 0 && streq(args[f-1], name));
+                assert_se(strv_remove(args + f - 1, name));
         }
 
-        args[t] = NULL;
-
 #endif
         return r;
 }
@@ -4800,7 +5331,7 @@ static int enable_unit(sd_bus *bus, char **args) {
                 } else if (streq(verb, "link"))
                         r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
                 else if (streq(verb, "preset")) {
-                        r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
+                        r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes);
                         carries_install_info = r;
                 } else if (streq(verb, "mask"))
                         r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
@@ -4810,182 +5341,883 @@ static int enable_unit(sd_bus *bus, char **args) {
                         assert_not_reached("Unknown verb");
 
                 if (r < 0) {
-                        log_error("Operation failed: %s", strerror(-r));
-                        goto finish;
+                        log_error_errno(r, "Operation failed: %m");
+                        goto finish;
+                }
+
+                if (!arg_quiet)
+                        dump_unit_file_changes(changes, n_changes);
+
+                r = 0;
+        } else {
+                _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+                int expect_carries_install_info = false;
+                bool send_force = true, send_preset_mode = false;
+                const char *method;
+
+                if (streq(verb, "enable")) {
+                        method = "EnableUnitFiles";
+                        expect_carries_install_info = true;
+                } else if (streq(verb, "disable")) {
+                        method = "DisableUnitFiles";
+                        send_force = false;
+                } else if (streq(verb, "reenable")) {
+                        method = "ReenableUnitFiles";
+                        expect_carries_install_info = true;
+                } else if (streq(verb, "link"))
+                        method = "LinkUnitFiles";
+                else if (streq(verb, "preset")) {
+
+                        if (arg_preset_mode != UNIT_FILE_PRESET_FULL) {
+                                method = "PresetUnitFilesWithMode";
+                                send_preset_mode = true;
+                        } else
+                                method = "PresetUnitFiles";
+
+                        expect_carries_install_info = true;
+                } else if (streq(verb, "mask"))
+                        method = "MaskUnitFiles";
+                else if (streq(verb, "unmask")) {
+                        method = "UnmaskUnitFiles";
+                        send_force = false;
+                } else
+                        assert_not_reached("Unknown verb");
+
+                r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                method);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append_strv(m, names);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                if (send_preset_mode) {
+                        r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode));
+                        if (r < 0)
+                                return bus_log_create_error(r);
+                }
+
+                r = sd_bus_message_append(m, "b", arg_runtime);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                if (send_force) {
+                        r = sd_bus_message_append(m, "b", arg_force);
+                        if (r < 0)
+                                return bus_log_create_error(r);
+                }
+
+                r = sd_bus_call(bus, m, 0, &error, &reply);
+                if (r < 0) {
+                        log_error("Failed to execute operation: %s", bus_error_message(&error, r));
+                        return r;
+                }
+
+                if (expect_carries_install_info) {
+                        r = sd_bus_message_read(reply, "b", &carries_install_info);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+                }
+
+                r = deserialize_and_dump_unit_file_changes(reply);
+                if (r < 0)
+                        return r;
+
+                /* Try to reload if enabled */
+                if (!arg_no_reload)
+                        r = daemon_reload(bus, args);
+                else
+                        r = 0;
+        }
+
+        if (carries_install_info == 0)
+                log_warning("The unit files have no [Install] section. They are not meant to be enabled\n"
+                            "using systemctl.\n"
+                            "Possible reasons for having this kind of units are:\n"
+                            "1) A unit may be statically enabled by being symlinked from another unit's\n"
+                            "   .wants/ or .requires/ directory.\n"
+                            "2) A unit's purpose may be to act as a helper for some other unit which has\n"
+                            "   a requirement dependency on it.\n"
+                            "3) A unit may be started when needed via activation (socket, path, timer,\n"
+                            "   D-Bus, udev, scripted systemctl call, ...).\n");
+
+finish:
+        unit_file_changes_free(changes, n_changes);
+
+        return r;
+}
+
+static int add_dependency(sd_bus *bus, char **args) {
+        _cleanup_strv_free_ char **names = NULL;
+        _cleanup_free_ char *target = NULL;
+        const char *verb = args[0];
+        UnitDependency dep;
+        int r = 0;
+
+        if (!args[1])
+                return 0;
+
+        target = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".target");
+        if (!target)
+                return log_oom();
+
+        r = mangle_names(args+2, &names);
+        if (r < 0)
+                return r;
+
+        if (streq(verb, "add-wants"))
+                dep = UNIT_WANTS;
+        else if (streq(verb, "add-requires"))
+                dep = UNIT_REQUIRES;
+        else
+                assert_not_reached("Unknown verb");
+
+        if (!bus || avoid_bus()) {
+                UnitFileChange *changes = NULL;
+                unsigned n_changes = 0;
+
+                r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes);
+
+                if (r < 0)
+                        return log_error_errno(r, "Can't add dependency: %m");
+
+                if (!arg_quiet)
+                        dump_unit_file_changes(changes, n_changes);
+
+                unit_file_changes_free(changes, n_changes);
+
+        } else {
+                _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                "AddDependencyUnitFiles");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append_strv(m, names);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(m, "ssbb", target, unit_dependency_to_string(dep), arg_runtime, arg_force);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_call(bus, m, 0, &error, &reply);
+                if (r < 0) {
+                        log_error("Failed to execute operation: %s", bus_error_message(&error, r));
+                        return r;
+                }
+
+                r = deserialize_and_dump_unit_file_changes(reply);
+                if (r < 0)
+                        return r;
+
+                if (!arg_no_reload)
+                        r = daemon_reload(bus, args);
+                else
+                        r = 0;
+        }
+
+        return r;
+}
+
+static int preset_all(sd_bus *bus, char **args) {
+        UnitFileChange *changes = NULL;
+        unsigned n_changes = 0;
+        int r;
+
+        if (!bus || avoid_bus()) {
+
+                r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes);
+                if (r < 0) {
+                        log_error_errno(r, "Operation failed: %m");
+                        goto finish;
+                }
+
+                if (!arg_quiet)
+                        dump_unit_file_changes(changes, n_changes);
+
+                r = 0;
+
+        } else {
+                _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+
+                r = sd_bus_message_new_method_call(
+                                bus,
+                                &m,
+                                "org.freedesktop.systemd1",
+                                "/org/freedesktop/systemd1",
+                                "org.freedesktop.systemd1.Manager",
+                                "PresetAllUnitFiles");
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_message_append(
+                                m,
+                                "sbb",
+                                unit_file_preset_mode_to_string(arg_preset_mode),
+                                arg_runtime,
+                                arg_force);
+                if (r < 0)
+                        return bus_log_create_error(r);
+
+                r = sd_bus_call(bus, m, 0, &error, &reply);
+                if (r < 0) {
+                        log_error("Failed to execute operation: %s", bus_error_message(&error, r));
+                        return r;
+                }
+
+                r = deserialize_and_dump_unit_file_changes(reply);
+                if (r < 0)
+                        return r;
+
+                if (!arg_no_reload)
+                        r = daemon_reload(bus, args);
+                else
+                        r = 0;
+        }
+
+finish:
+        unit_file_changes_free(changes, n_changes);
+
+        return r;
+}
+
+static int unit_is_enabled(sd_bus *bus, char **args) {
+
+        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+        _cleanup_strv_free_ char **names = NULL;
+        bool enabled;
+        char **name;
+        int r;
+
+        r = mangle_names(args+1, &names);
+        if (r < 0)
+                return r;
+
+        r = enable_sysv_units(args[0], names);
+        if (r < 0)
+                return r;
+
+        enabled = r > 0;
+
+        if (!bus || avoid_bus()) {
+
+                STRV_FOREACH(name, names) {
+                        UnitFileState state;
+
+                        state = unit_file_get_state(arg_scope, arg_root, *name);
+                        if (state < 0)
+                                return log_error_errno(state, "Failed to get unit file state for %s: %m", *name);
+
+                        if (state == UNIT_FILE_ENABLED ||
+                            state == UNIT_FILE_ENABLED_RUNTIME ||
+                            state == UNIT_FILE_STATIC ||
+                            state == UNIT_FILE_INDIRECT)
+                                enabled = true;
+
+                        if (!arg_quiet)
+                                puts(unit_file_state_to_string(state));
+                }
+
+        } else {
+                STRV_FOREACH(name, names) {
+                        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+                        const char *s;
+
+                        r = sd_bus_call_method(
+                                        bus,
+                                        "org.freedesktop.systemd1",
+                                        "/org/freedesktop/systemd1",
+                                        "org.freedesktop.systemd1.Manager",
+                                        "GetUnitFileState",
+                                        &error,
+                                        &reply,
+                                        "s", *name);
+                        if (r < 0) {
+                                log_error("Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
+                                return r;
+                        }
+
+                        r = sd_bus_message_read(reply, "s", &s);
+                        if (r < 0)
+                                return bus_log_parse_error(r);
+
+                        if (STR_IN_SET(s, "enabled", "enabled-runtime", "static", "indirect"))
+                                enabled = true;
+
+                        if (!arg_quiet)
+                                puts(s);
+                }
+        }
+
+        return !enabled;
+}
+
+static int is_system_running(sd_bus *bus, char **args) {
+        _cleanup_free_ char *state = NULL;
+        int r;
+
+        r = sd_bus_get_property_string(
+                        bus,
+                        "org.freedesktop.systemd1",
+                        "/org/freedesktop/systemd1",
+                        "org.freedesktop.systemd1.Manager",
+                        "SystemState",
+                        NULL,
+                        &state);
+        if (r < 0) {
+                if (!arg_quiet)
+                        puts("unknown");
+                return 0;
+        }
+
+        if (!arg_quiet)
+                puts(state);
+
+        return streq(state, "running") ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **unit_path) {
+        char **p;
+
+        assert(lp);
+        assert(unit_name);
+        assert(unit_path);
+
+        STRV_FOREACH(p, lp->unit_path) {
+                char *path;
+
+                path = path_join(arg_root, *p, unit_name);
+                if (!path)
+                        return log_oom();
+
+                if (access(path, F_OK) == 0) {
+                        *unit_path = path;
+                        return 1;
+                }
+
+                free(path);
+        }
+
+        return 0;
+}
+
+static int create_edit_temp_file(const char *new_path, const char *original_path, char **ret_tmp_fn) {
+        int r;
+        char *t;
+
+        assert(new_path);
+        assert(original_path);
+        assert(ret_tmp_fn);
+
+        t = tempfn_random(new_path);
+        if (!t)
+                return log_oom();
+
+        r = mkdir_parents(new_path, 0755);
+        if (r < 0) {
+                log_error_errno(r, "Failed to create directories for %s: %m", new_path);
+                free(t);
+                return r;
+        }
+
+        r = copy_file(original_path, t, 0, 0644);
+        if (r == -ENOENT) {
+                r = touch(t);
+                if (r < 0) {
+                        log_error_errno(r, "Failed to create temporary file %s: %m", t);
+                        free(t);
+                        return r;
+                }
+        } else if (r < 0) {
+                log_error_errno(r, "Failed to copy %s to %s: %m", original_path, t);
+                free(t);
+                return r;
+        }
+
+        *ret_tmp_fn = t;
+
+        return 0;
+}
+
+static int get_drop_in_to_edit(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_path) {
+        char *tmp_new_path;
+        char *tmp;
+
+        assert(unit_name);
+        assert(ret_path);
+
+        switch (arg_scope) {
+                case UNIT_FILE_SYSTEM:
+                        tmp = strappenda(arg_runtime ? "/run/systemd/system/" : SYSTEM_CONFIG_UNIT_PATH "/", unit_name, ".d/override.conf");
+                        break;
+                case UNIT_FILE_GLOBAL:
+                        tmp = strappenda(arg_runtime ? "/run/systemd/user/" : USER_CONFIG_UNIT_PATH "/", unit_name, ".d/override.conf");
+                        break;
+                case UNIT_FILE_USER:
+                        assert(user_home);
+                        assert(user_runtime);
+
+                        tmp = strappenda(arg_runtime ? user_runtime : user_home, "/", unit_name, ".d/override.conf");
+                        break;
+                default:
+                        assert_not_reached("Invalid scope");
+        }
+
+        tmp_new_path = path_join(arg_root, tmp, NULL);
+        if (!tmp_new_path)
+                return log_oom();
+
+        *ret_path = tmp_new_path;
+
+        return 0;
+}
+
+static int unit_file_create_drop_in(const char *unit_name, const char *user_home, const char *user_runtime, char **ret_new_path, char **ret_tmp_path) {
+        char *tmp_new_path;
+        char *tmp_tmp_path;
+        int r;
+
+        assert(unit_name);
+        assert(ret_new_path);
+        assert(ret_tmp_path);
+
+        r = get_drop_in_to_edit(unit_name, user_home, user_runtime, &tmp_new_path);
+        if (r < 0)
+                return r;
+
+        r = create_edit_temp_file(tmp_new_path, tmp_new_path, &tmp_tmp_path);
+        if (r < 0) {
+                free(tmp_new_path);
+                return r;
+        }
+
+        *ret_new_path = tmp_new_path;
+        *ret_tmp_path = tmp_tmp_path;
+
+        return 0;
+}
+
+static bool unit_is_editable(const char *unit_name, const char *fragment_path, const char *user_home) {
+        bool editable = true;
+        const char *invalid_path;
+
+        assert(unit_name);
+
+        if (!arg_runtime)
+                return true;
+
+        switch (arg_scope) {
+                case UNIT_FILE_SYSTEM:
+                        if (path_startswith(fragment_path, "/etc/systemd/system")) {
+                                editable = false;
+                                invalid_path = "/etc/systemd/system";
+                        } else if (path_startswith(fragment_path, SYSTEM_CONFIG_UNIT_PATH)) {
+                                editable = false;
+                                invalid_path = SYSTEM_CONFIG_UNIT_PATH;
+                        }
+                        break;
+                case UNIT_FILE_GLOBAL:
+                        if (path_startswith(fragment_path, "/etc/systemd/user")) {
+                                editable = false;
+                                invalid_path = "/etc/systemd/user";
+                        } else if (path_startswith(fragment_path, USER_CONFIG_UNIT_PATH)) {
+                                editable = false;
+                                invalid_path = USER_CONFIG_UNIT_PATH;
+                        }
+                        break;
+                case UNIT_FILE_USER:
+                        assert(user_home);
+
+                        if (path_startswith(fragment_path, "/etc/systemd/user")) {
+                                editable = false;
+                                invalid_path = "/etc/systemd/user";
+                        } else if (path_startswith(fragment_path, USER_CONFIG_UNIT_PATH)) {
+                                editable = false;
+                                invalid_path = USER_CONFIG_UNIT_PATH;
+                        } else if (path_startswith(fragment_path, user_home)) {
+                                editable = false;
+                                invalid_path = user_home;
+                        }
+                        break;
+                default:
+                        assert_not_reached("Invalid scope");
+        }
+
+        if (!editable)
+                log_error("%s ignored: cannot temporarily edit units from %s", unit_name, invalid_path);
+
+        return editable;
+}
+
+static int get_copy_to_edit(const char *unit_name, const char *fragment_path, const char *user_home, const char *user_runtime, char **ret_path) {
+        char *tmp_new_path;
+
+        assert(unit_name);
+        assert(ret_path);
+
+        if (!unit_is_editable(unit_name, fragment_path, user_home))
+                return -EINVAL;
+
+        switch (arg_scope) {
+                case UNIT_FILE_SYSTEM:
+                        tmp_new_path = path_join(arg_root, arg_runtime ? "/run/systemd/system/" : SYSTEM_CONFIG_UNIT_PATH, unit_name);
+                        break;
+                case UNIT_FILE_GLOBAL:
+                        tmp_new_path = path_join(arg_root, arg_runtime ? "/run/systemd/user/" : USER_CONFIG_UNIT_PATH, unit_name);
+                        break;
+                case UNIT_FILE_USER:
+                        assert(user_home);
+                        assert(user_runtime);
+
+                        tmp_new_path = path_join(arg_root, arg_runtime ? user_runtime : user_home, unit_name);
+                        break;
+                default:
+                        assert_not_reached("Invalid scope");
+        }
+        if (!tmp_new_path)
+                return log_oom();
+
+        *ret_path = tmp_new_path;
+
+        return 0;
+}
+
+static int unit_file_create_copy(const char *unit_name,
+                                 const char *fragment_path,
+                                 const char *user_home,
+                                 const char *user_runtime,
+                                 char **ret_new_path,
+                                 char **ret_tmp_path) {
+        char *tmp_new_path;
+        char *tmp_tmp_path;
+        int r;
+
+        assert(fragment_path);
+        assert(unit_name);
+        assert(ret_new_path);
+        assert(ret_tmp_path);
+
+        r = get_copy_to_edit(unit_name, fragment_path, user_home, user_runtime, &tmp_new_path);
+        if (r < 0)
+                return r;
+
+        if (!path_equal(fragment_path, tmp_new_path) && access(tmp_new_path, F_OK) == 0) {
+                char response;
+
+                r = ask_char(&response, "yn", "%s already exists, are you sure to overwrite it with %s? [(y)es, (n)o] ", tmp_new_path, fragment_path);
+                if (r < 0) {
+                        free(tmp_new_path);
+                        return r;
                 }
+                if (response != 'y') {
+                        log_warning("%s ignored", unit_name);
+                        free(tmp_new_path);
+                        return -1;
+                }
+        }
 
-                if (!arg_quiet)
-                        dump_unit_file_changes(changes, n_changes);
+        r = create_edit_temp_file(tmp_new_path, fragment_path, &tmp_tmp_path);
+        if (r < 0) {
+                log_error_errno(r, "Failed to create temporary file for %s: %m", tmp_new_path);
+                free(tmp_new_path);
+                return r;
+        }
 
-                r = 0;
-        } else {
-                _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
-                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-                int expect_carries_install_info = false;
-                bool send_force = true;
-                const char *method;
+        *ret_new_path = tmp_new_path;
+        *ret_tmp_path = tmp_tmp_path;
 
-                if (streq(verb, "enable")) {
-                        method = "EnableUnitFiles";
-                        expect_carries_install_info = true;
-                } else if (streq(verb, "disable")) {
-                        method = "DisableUnitFiles";
-                        send_force = false;
-                } else if (streq(verb, "reenable")) {
-                        method = "ReenableUnitFiles";
-                        expect_carries_install_info = true;
-                } else if (streq(verb, "link"))
-                        method = "LinkUnitFiles";
-                else if (streq(verb, "preset")) {
-                        method = "PresetUnitFiles";
-                        expect_carries_install_info = true;
-                } else if (streq(verb, "mask"))
-                        method = "MaskUnitFiles";
-                else if (streq(verb, "unmask")) {
-                        method = "UnmaskUnitFiles";
-                        send_force = false;
-                } else
-                        assert_not_reached("Unknown verb");
+        return 0;
+}
 
-                r = sd_bus_message_new_method_call(
-                                bus,
-                                "org.freedesktop.systemd1",
-                                "/org/freedesktop/systemd1",
-                                "org.freedesktop.systemd1.Manager",
-                                method,
-                                &m);
-                if (r < 0)
-                        return bus_log_create_error(r);
+static int run_editor(char **paths) {
+        pid_t pid;
+        int r;
 
-                r = sd_bus_message_append_strv(m, names);
-                if (r < 0)
-                        return bus_log_create_error(r);
+        assert(paths);
 
-                r = sd_bus_message_append(m, "b", arg_runtime);
-                if (r < 0)
-                        return bus_log_create_error(r);
+        pid = fork();
+        if (pid < 0) {
+                log_error_errno(errno, "Failed to fork: %m");
+                return -errno;
+        }
 
-                if (send_force) {
-                        r = sd_bus_message_append(m, "b", arg_force);
-                        if (r < 0)
-                                return bus_log_create_error(r);
-                }
+        if (pid == 0) {
+                const char **args;
+                char **backup_editors = STRV_MAKE("nano", "vim", "vi");
+                char *editor;
+                char **tmp_path, **original_path, **p;
+                unsigned i = 1;
+                size_t argc;
 
-                r = sd_bus_call(bus, m, 0, &error, &reply);
-                if (r < 0) {
-                        log_error("Failed to execute operation: %s", bus_error_message(&error, r));
-                        return r;
+                argc = strv_length(paths)/2 + 1;
+                args = newa(const char*, argc + 1);
+
+                args[0] = NULL;
+                STRV_FOREACH_PAIR(original_path, tmp_path, paths) {
+                        args[i] = *tmp_path;
+                        i++;
                 }
+                args[argc] = NULL;
 
-                if (expect_carries_install_info) {
-                        r = sd_bus_message_read(reply, "b", &carries_install_info);
-                        if (r < 0)
-                                return bus_log_parse_error(r);
+                /* SYSTEMD_EDITOR takes precedence over EDITOR which takes precedence over VISUAL
+                 * If neither SYSTEMD_EDITOR nor EDITOR nor VISUAL are present,
+                 * we try to execute well known editors
+                 */
+                editor = getenv("SYSTEMD_EDITOR");
+                if (!editor)
+                        editor = getenv("EDITOR");
+                if (!editor)
+                        editor = getenv("VISUAL");
+
+                if (!isempty(editor)) {
+                        args[0] = editor;
+                        execvp(editor, (char* const*) args);
                 }
 
-                r = deserialize_and_dump_unit_file_changes(reply);
-                if (r < 0)
-                        return r;
+                STRV_FOREACH(p, backup_editors) {
+                        args[0] = *p;
+                        execvp(*p, (char* const*) args);
+                        /* We do not fail if the editor doesn't exist
+                         * because we want to try each one of them before
+                         * failing.
+                         */
+                        if (errno != ENOENT) {
+                                log_error("Failed to execute %s: %m", editor);
+                                _exit(EXIT_FAILURE);
+                        }
+                }
 
-                /* Try to reload if enabeld */
-                if (!arg_no_reload)
-                        r = daemon_reload(bus, args);
-                else
-                        r = 0;
+                log_error("Cannot edit unit(s): No editor available. Please set either SYSTEMD_EDITOR or EDITOR or VISUAL environment variable");
+                _exit(EXIT_FAILURE);
         }
 
-        if (carries_install_info == 0)
-                log_warning("The unit files have no [Install] section. They are not meant to be enabled\n"
-                            "using systemctl.\n"
-                            "Possible reasons for having this kind of units are:\n"
-                            "1) A unit may be statically enabled by being symlinked from another unit's\n"
-                            "   .wants/ or .requires/ directory.\n"
-                            "2) A unit's purpose may be to act as a helper for some other unit which has\n"
-                            "   a requirement dependency on it.\n"
-                            "3) A unit may be started when needed via activation (socket, path, timer,\n"
-                            "   D-Bus, udev, scripted systemctl call, ...).\n");
-
-finish:
-        unit_file_changes_free(changes, n_changes);
+        r = wait_for_terminate_and_warn("editor", pid, true);
+        if (r < 0)
+                return log_error_errno(r, "Failed to wait for child: %m");
 
         return r;
 }
 
-static int unit_is_enabled(sd_bus *bus, char **args) {
-
-        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
-        _cleanup_strv_free_ char **names = NULL;
-        bool enabled;
+static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) {
+        _cleanup_free_ char *user_home = NULL;
+        _cleanup_free_ char *user_runtime = NULL;
         char **name;
         int r;
 
-        r = mangle_names(args+1, &names);
-        if (r < 0)
-                return r;
+        assert(names);
+        assert(paths);
 
-        r = enable_sysv_units(args[0], names);
-        if (r < 0)
-                return r;
+        if (arg_scope == UNIT_FILE_USER) {
+                r = user_config_home(&user_home);
+                if (r < 0)
+                        return log_oom();
+                else if (r == 0) {
+                        log_error("Cannot edit units for the user instance: home directory unknown");
+                        return -1;
+                }
 
-        enabled = r > 0;
+                r = user_runtime_dir(&user_runtime);
+                if (r < 0)
+                        return log_oom();
+                else if (r == 0) {
+                        log_error("Cannot edit units for the user instance: runtime directory unknown");
+                        return -1;
+                }
+        }
 
         if (!bus || avoid_bus()) {
+                _cleanup_lookup_paths_free_ LookupPaths lp = {};
+
+                /* If there is no bus, we try to find the units by testing each available directory
+                 * according to the scope.
+                 */
+                r = lookup_paths_init(&lp,
+                                arg_scope == UNIT_FILE_SYSTEM ? SYSTEMD_SYSTEM : SYSTEMD_USER,
+                                arg_scope == UNIT_FILE_USER,
+                                arg_root,
+                                NULL, NULL, NULL);
+                if (r < 0) {
+                        log_error_errno(r, "Failed get lookup paths: %m");
+                        return r;
+                }
 
                 STRV_FOREACH(name, names) {
-                        UnitFileState state;
+                        _cleanup_free_ char *path = NULL;
+                        char *new_path, *tmp_path;
 
-                        state = unit_file_get_state(arg_scope, arg_root, *name);
-                        if (state < 0) {
-                                log_error("Failed to get unit file state for %s: %s", *name, strerror(-state));
-                                return state;
+                        r = unit_file_find_path(&lp, *name, &path);
+                        if (r < 0)
+                                return r;
+                        if (r == 0) {
+                                log_warning("%s ignored: not found", *name);
+                                continue;
                         }
 
-                        if (state == UNIT_FILE_ENABLED ||
-                            state == UNIT_FILE_ENABLED_RUNTIME ||
-                            state == UNIT_FILE_STATIC)
-                                enabled = true;
+                        if (arg_full)
+                                r = unit_file_create_copy(*name, path, user_home, user_runtime, &new_path, &tmp_path);
+                        else
+                                r = unit_file_create_drop_in(*name, user_home, user_runtime, &new_path, &tmp_path);
 
-                        if (!arg_quiet)
-                                puts(unit_file_state_to_string(state));
-                }
+                        if (r < 0)
+                                continue;
+
+                        r = strv_push(paths, new_path);
+                        if (r < 0)
+                                return log_oom();
 
+                        r = strv_push(paths, tmp_path);
+                        if (r < 0)
+                                return log_oom();
+                }
         } else {
                 STRV_FOREACH(name, names) {
-                        _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
-                        const char *s;
+                        _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
+                        _cleanup_free_ char *fragment_path = NULL;
+                        _cleanup_free_ char *unit = NULL;
+                        char *new_path, *tmp_path;
 
-                        r = sd_bus_call_method(
+                        unit = unit_dbus_path_from_name(*name);
+                        if (!unit)
+                                return log_oom();
+
+                        if (need_daemon_reload(bus, *name) > 0) {
+                                log_warning("%s ignored: unit file changed on disk. Run 'systemctl%s daemon-reload'.",
+                                        *name, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
+                                continue;
+                        }
+
+                        r = sd_bus_get_property_string(
                                         bus,
                                         "org.freedesktop.systemd1",
-                                        "/org/freedesktop/systemd1",
-                                        "org.freedesktop.systemd1.Manager",
-                                        "GetUnitFileState",
+                                        unit,
+                                        "org.freedesktop.systemd1.Unit",
+                                        "FragmentPath",
                                         &error,
-                                        &reply,
-                                        "s", *name);
+                                        &fragment_path);
                         if (r < 0) {
-                                log_error("Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
-                                return r;
+                                log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r));
+                                continue;
                         }
 
-                        r = sd_bus_message_read(reply, "s", &s);
+                        if (isempty(fragment_path)) {
+                                log_warning("%s ignored: not found", *name);
+                                continue;
+                        }
+
+                        if (arg_full)
+                                r = unit_file_create_copy(*name, fragment_path, user_home, user_runtime, &new_path, &tmp_path);
+                        else
+                                r = unit_file_create_drop_in(*name, user_home, user_runtime, &new_path, &tmp_path);
                         if (r < 0)
-                                return bus_log_parse_error(r);
+                                continue;
 
-                        if (streq(s, "enabled") ||
-                            streq(s, "enabled-runtime") ||
-                            streq(s, "static"))
-                                enabled = true;
+                        r = strv_push(paths, new_path);
+                        if (r < 0)
+                                return log_oom();
 
-                        if (!arg_quiet)
-                                puts(s);
+                        r = strv_push(paths, tmp_path);
+                        if (r < 0)
+                                return log_oom();
                 }
         }
 
-        return !enabled;
+        return 0;
+}
+
+static int edit(sd_bus *bus, char **args) {
+        _cleanup_strv_free_ char **names = NULL;
+        _cleanup_strv_free_ char **paths = NULL;
+        char **original, **tmp;
+        int r;
+
+        assert(args);
+
+        if (!on_tty()) {
+                log_error("Cannot edit units if we are not on a tty");
+                return -EINVAL;
+        }
+
+        if (arg_transport != BUS_TRANSPORT_LOCAL) {
+                log_error("Cannot remotely edit units");
+                return -EINVAL;
+        }
+
+        r = expand_names(bus, args + 1, NULL, &names);
+        if (r < 0)
+                return log_error_errno(r, "Failed to expand names: %m");
+
+        if (!names) {
+                log_error("No unit name found by expanding names");
+                return -ENOENT;
+        }
+
+        r = find_paths_to_edit(bus, names, &paths);
+        if (r < 0)
+                return r;
+
+        if (strv_isempty(paths)) {
+                log_error("Cannot find any units to edit");
+                return -ENOENT;
+        }
+
+        r = run_editor(paths);
+        if (r < 0)
+                goto end;
+
+        STRV_FOREACH_PAIR(original, tmp, paths) {
+                /* If the temporary file is empty we ignore it.
+                 * It's useful if the user wants to cancel its modification
+                 */
+                if (null_or_empty_path(*tmp)) {
+                        log_warning("Edition of %s canceled: temporary file empty", *original);
+                        continue;
+                }
+                r = rename(*tmp, *original);
+                if (r < 0) {
+                        r = log_error_errno(errno, "Failed to rename %s to %s: %m", *tmp, *original);
+                        goto end;
+                }
+        }
+
+        if (!arg_no_reload && bus && !avoid_bus())
+                r = daemon_reload(bus, args);
+
+end:
+        STRV_FOREACH_PAIR(original, tmp, paths)
+                unlink_noerrno(*tmp);
+
+        return r;
 }
 
-static int systemctl_help(void) {
+static void systemctl_help(void) {
 
         pager_open_if_enabled();
 
@@ -5006,6 +6238,7 @@ static int systemctl_help(void) {
                "                      ones. To list all units installed on the system, use\n"
                "                      the 'list-unit-files' command instead.\n"
                "  -l --full           Don't ellipsize unit names on output\n"
+               "  -r --recursive      Show unit list of host and local containers\n"
                "     --reverse        Show reverse dependencies with 'list-dependencies'\n"
                "     --job-mode=MODE  Specify how to deal with already queued jobs, when\n"
                "                      queueing a new job\n"
@@ -5027,6 +6260,8 @@ static int systemctl_help(void) {
                "     --runtime        Enable unit files only temporarily until next reboot\n"
                "  -f --force          When enabling unit files, override existing symlinks\n"
                "                      When shutting down, execute action immediately\n"
+               "     --preset-mode=   Specifies whether fully apply presets, or only enable,\n"
+               "                      or only disable\n"
                "     --root=PATH      Enable unit files in the specified root directory\n"
                "  -n --lines=INTEGER  Number of journal entries to show\n"
                "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
@@ -5047,15 +6282,15 @@ static int systemctl_help(void) {
                "                                  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"
-               "  is-failed NAME...               Check whether units are failed\n"
-               "  status [NAME...|PID...]         Show runtime status of one or more units\n"
-               "  show [NAME...|JOB...]           Show properties of one or more\n"
+               "  is-active PATTERN...            Check whether units are active\n"
+               "  is-failed PATTERN...            Check whether units are failed\n"
+               "  status [PATTERN...|PID...]      Show runtime status of one or more units\n"
+               "  show [PATTERN...|JOB...]        Show properties of one or more\n"
                "                                  units/jobs or the manager\n"
-               "  cat NAME...                     Show files and drop-ins of one or more units\n"
+               "  cat PATTERN...                  Show files and drop-ins of one or more units\n"
                "  set-property NAME ASSIGNMENT... Sets one or more properties of a unit\n"
-               "  help NAME...|PID...             Show manual for one or more units\n"
-               "  reset-failed [NAME...]          Reset failed state for all, one, or more\n"
+               "  help PATTERN...|PID...          Show manual for one or more units\n"
+               "  reset-failed [PATTERN...]       Reset failed state for all, one, or more\n"
                "                                  units\n"
                "  list-dependencies [NAME]        Recursively show units which are required\n"
                "                                  or wanted by this unit or by which this\n"
@@ -5067,13 +6302,23 @@ static int systemctl_help(void) {
                "  reenable NAME...                Reenable one or more unit files\n"
                "  preset NAME...                  Enable/disable one or more unit files\n"
                "                                  based on preset configuration\n"
+               "  preset-all                      Enable/disable all unit files based on\n"
+               "                                  preset configuration\n"
                "  is-enabled NAME...              Check whether unit files are enabled\n\n"
                "  mask NAME...                    Mask one or more units\n"
                "  unmask NAME...                  Unmask one or more units\n"
                "  link PATH...                    Link one or more units files into\n"
                "                                  the search path\n"
+               "  add-wants TARGET NAME...        Add 'Wants' dependency for the target\n"
+               "                                  on specified one or more units\n"
+               "  add-requires TARGET NAME...     Add 'Requires' dependency for the target\n"
+               "                                  on specified one or more units\n"
                "  get-default                     Get the name of the default target\n"
-               "  set-default NAME                Set the default target\n\n"
+               "  set-default NAME                Set the default target\n"
+               "  edit NAME...                    Edit one or more unit files\n"
+               "\n"
+               "Machine Commands:\n"
+               "  list-machines [PATTERN...]      List local containers and host\n\n"
                "Job Commands:\n"
                "  list-jobs [PATTERN...]          List jobs\n"
                "  cancel [JOB...]                 Cancel all, one, or more jobs\n\n"
@@ -5089,6 +6334,7 @@ static int systemctl_help(void) {
                "  daemon-reload                   Reload systemd manager configuration\n"
                "  daemon-reexec                   Reexecute systemd manager\n\n"
                "System Commands:\n"
+               "  is-system-running               Check whether system is fully running\n"
                "  default                         Enter system default mode\n"
                "  rescue                          Enter system rescue mode\n"
                "  emergency                       Enter system emergency mode\n"
@@ -5102,12 +6348,9 @@ static int systemctl_help(void) {
                "  hibernate                       Hibernate the system\n"
                "  hybrid-sleep                    Hibernate and suspend the system\n",
                program_invocation_short_name);
-
-        return 0;
 }
 
-static int halt_help(void) {
-
+static void halt_help(void) {
         printf("%s [OPTIONS...]%s\n\n"
                "%s the system.\n\n"
                "     --help      Show this help\n"
@@ -5123,12 +6366,9 @@ static int halt_help(void) {
                arg_action == ACTION_REBOOT   ? "Reboot" :
                arg_action == ACTION_POWEROFF ? "Power off" :
                                                "Halt");
-
-        return 0;
 }
 
-static int shutdown_help(void) {
-
+static void shutdown_help(void) {
         printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
                "Shut down the system.\n\n"
                "     --help      Show this help\n"
@@ -5140,12 +6380,9 @@ static int shutdown_help(void) {
                "     --no-wall   Don't send wall message before halt/power-off/reboot\n"
                "  -c             Cancel a pending shutdown\n",
                program_invocation_short_name);
-
-        return 0;
 }
 
-static int telinit_help(void) {
-
+static void telinit_help(void) {
         printf("%s [OPTIONS...] {COMMAND}\n\n"
                "Send control commands to the init daemon.\n\n"
                "     --help      Show this help\n"
@@ -5158,32 +6395,26 @@ static int telinit_help(void) {
                "  q, Q           Reload init daemon configuration\n"
                "  u, U           Reexecute init daemon\n",
                program_invocation_short_name);
-
-        return 0;
 }
 
-static int runlevel_help(void) {
-
+static void runlevel_help(void) {
         printf("%s [OPTIONS...]\n\n"
                "Prints the previous and current runlevel of the init system.\n\n"
                "     --help      Show this help\n",
                program_invocation_short_name);
-
-        return 0;
 }
 
-static int help_types(void) {
+static void help_types(void) {
         int i;
         const char *t;
 
-        puts("Available unit types:");
+        if (!arg_no_legend)
+                puts("Available unit types:");
         for (i = 0; i < _UNIT_TYPE_MAX; i++) {
                 t = unit_type_to_string(i);
                 if (t)
                         puts(t);
         }
-
-        return 0;
 }
 
 static int systemctl_parse_argv(int argc, char *argv[]) {
@@ -5213,7 +6444,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_FORCE,
                 ARG_PLAIN,
                 ARG_STATE,
-                ARG_JOB_MODE
+                ARG_JOB_MODE,
+                ARG_PRESET_MODE,
         };
 
         static const struct option options[] = {
@@ -5254,6 +6486,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "output",              required_argument, NULL, 'o'                     },
                 { "plain",               no_argument,       NULL, ARG_PLAIN               },
                 { "state",               required_argument, NULL, ARG_STATE               },
+                { "recursive",           no_argument,       NULL, 'r'                     },
+                { "preset-mode",         required_argument, NULL, ARG_PRESET_MODE         },
                 {}
         };
 
@@ -5262,12 +6496,13 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:i", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir", options, NULL)) >= 0)
 
                 switch (c) {
 
                 case 'h':
-                        return systemctl_help();
+                        systemctl_help();
+                        return 0;
 
                 case ARG_VERSION:
                         puts(PACKAGE_STRING);
@@ -5275,7 +6510,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         return 0;
 
                 case 't': {
-                        char *word, *state;
+                        const char *word, *state;
                         size_t size;
 
                         FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
@@ -5324,7 +6559,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                                 if (!arg_properties)
                                         return log_oom();
                         } else {
-                                char *word, *state;
+                                const char *word, *state;
                                 size_t size;
 
                                 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
@@ -5334,10 +6569,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                                         if (!prop)
                                                 return log_oom();
 
-                                        if (strv_push(&arg_properties, prop) < 0) {
-                                                free(prop);
+                                        if (strv_consume(&arg_properties, prop) < 0)
                                                 return log_oom();
-                                        }
                                 }
                         }
 
@@ -5496,7 +6729,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case ARG_STATE: {
-                        char *word, *state;
+                        const char *word, *state;
                         size_t size;
 
                         FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
@@ -5506,21 +6739,37 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                                 if (!s)
                                         return log_oom();
 
-                                if (strv_push(&arg_states, s) < 0) {
-                                        free(s);
+                                if (strv_consume(&arg_states, s) < 0)
                                         return log_oom();
-                                }
                         }
                         break;
                 }
 
+                case 'r':
+                        if (geteuid() != 0) {
+                                log_error("--recursive requires root privileges.");
+                                return -EPERM;
+                        }
+
+                        arg_recursive = true;
+                        break;
+
+                case ARG_PRESET_MODE:
+
+                        arg_preset_mode = unit_file_preset_mode_from_string(optarg);
+                        if (arg_preset_mode < 0) {
+                                log_error("Failed to parse preset mode: %s.", optarg);
+                                return -EINVAL;
+                        }
+
+                        break;
+
                 case '?':
                         return -EINVAL;
 
                 default:
                         assert_not_reached("Unhandled option");
                 }
-        }
 
         if (arg_transport != BUS_TRANSPORT_LOCAL && arg_scope != UNIT_FILE_SYSTEM) {
                 log_error("Cannot access user instance remotely.");
@@ -5560,11 +6809,12 @@ static int halt_parse_argv(int argc, char *argv[]) {
                 if (runlevel == '0' || runlevel == '6')
                         arg_force = 2;
 
-        while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0)
                 switch (c) {
 
                 case ARG_HELP:
-                        return halt_help();
+                        halt_help();
+                        return 0;
 
                 case ARG_HALT:
                         arg_action = ACTION_HALT;
@@ -5607,15 +6857,11 @@ static int halt_parse_argv(int argc, char *argv[]) {
                 default:
                         assert_not_reached("Unhandled option");
                 }
-        }
 
-        if (arg_action == ACTION_REBOOT && argc == optind + 1) {
-                r = write_string_file(REBOOT_PARAM_FILE, argv[optind]);
-                if (r < 0) {
-                        log_error("Failed to write reboot param to "
-                                  REBOOT_PARAM_FILE": %s", strerror(-r));
+        if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
+                r = update_reboot_param_file(argc == optind + 1 ? argv[optind] : NULL);
+                if (r < 0)
                         return r;
-                }
         } else if (optind < argc) {
                 log_error("Too many arguments.");
                 return -EINVAL;
@@ -5695,11 +6941,12 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0)
                 switch (c) {
 
                 case ARG_HELP:
-                        return shutdown_help();
+                        shutdown_help();
+                        return 0;
 
                 case 'H':
                         arg_action = ACTION_HALT;
@@ -5748,7 +6995,6 @@ static int shutdown_parse_argv(int argc, char *argv[]) {
                 default:
                         assert_not_reached("Unhandled option");
                 }
-        }
 
         if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) {
                 r = parse_time_spec(argv[optind], &arg_when);
@@ -5809,11 +7055,12 @@ static int telinit_parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
                 switch (c) {
 
                 case ARG_HELP:
-                        return telinit_help();
+                        telinit_help();
+                        return 0;
 
                 case ARG_NO_WALL:
                         arg_no_wall = true;
@@ -5825,10 +7072,10 @@ static int telinit_parse_argv(int argc, char *argv[]) {
                 default:
                         assert_not_reached("Unhandled option");
                 }
-        }
 
         if (optind >= argc) {
-                telinit_help();
+                log_error("%s: required argument missing.",
+                          program_invocation_short_name);
                 return -EINVAL;
         }
 
@@ -5874,11 +7121,12 @@ static int runlevel_parse_argv(int argc, char *argv[]) {
         assert(argc >= 0);
         assert(argv);
 
-        while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
+        while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
                 switch (c) {
 
                 case ARG_HELP:
-                        return runlevel_help();
+                        runlevel_help();
+                        return 0;
 
                 case '?':
                         return -EINVAL;
@@ -5886,7 +7134,6 @@ static int runlevel_parse_argv(int argc, char *argv[]) {
                 default:
                         assert_not_reached("Unhandled option");
                 }
-        }
 
         if (optind < argc) {
                 log_error("Too many arguments.");
@@ -5985,16 +7232,13 @@ static int talk_initctl(void) {
                 if (errno == ENOENT)
                         return 0;
 
-                log_error("Failed to open "INIT_FIFO": %m");
+                log_error_errno(errno, "Failed to open "INIT_FIFO": %m");
                 return -errno;
         }
 
-        errno = 0;
-        r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
-        if (r) {
-                log_error("Failed to write to "INIT_FIFO": %m");
-                return errno > 0 ? -errno : -EIO;
-        }
+        r = loop_write(fd, &request, sizeof(request), false);
+        if (r < 0)
+                return log_error_errno(r, "Failed to write to "INIT_FIFO": %m");
 
         return 1;
 }
@@ -6020,6 +7264,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "list-sockets",          MORE,  1, list_sockets      },
                 { "list-timers",           MORE,  1, list_timers       },
                 { "list-jobs",             MORE,  1, list_jobs         },
+                { "list-machines",         MORE,  1, list_machines     },
                 { "clear-jobs",            EQUAL, 1, daemon_reload     },
                 { "cancel",                MORE,  2, cancel_job        },
                 { "start",                 MORE,  2, start_unit        },
@@ -6067,6 +7312,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "is-enabled",            MORE,  2, unit_is_enabled,  NOBUS },
                 { "reenable",              MORE,  2, enable_unit,      NOBUS },
                 { "preset",                MORE,  2, enable_unit,      NOBUS },
+                { "preset-all",            EQUAL, 1, preset_all,       NOBUS },
                 { "mask",                  MORE,  2, enable_unit,      NOBUS },
                 { "unmask",                MORE,  2, enable_unit,      NOBUS },
                 { "link",                  MORE,  2, enable_unit,      NOBUS },
@@ -6075,6 +7321,10 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "set-default",           EQUAL, 2, set_default,      NOBUS },
                 { "get-default",           EQUAL, 1, get_default,      NOBUS },
                 { "set-property",          MORE,  3, set_property      },
+                { "is-system-running",     EQUAL, 1, is_system_running },
+                { "add-wants",             MORE,  3, add_dependency,   NOBUS },
+                { "add-requires",          MORE,  3, add_dependency,   NOBUS },
+                { "edit",                  MORE,  2, edit,             NOBUS },
                 {}
         }, *verb = verbs;
 
@@ -6136,7 +7386,7 @@ found:
          * enable/disable */
         if (verb->bus == NOBUS) {
                 if (!bus && !avoid_bus()) {
-                        log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
+                        log_error_errno(bus_error, "Failed to get D-Bus connection: %m");
                         return -EIO;
                 }
 
@@ -6147,7 +7397,7 @@ found:
                 }
 
                 if ((verb->bus != FORCE || arg_force <= 0) && !bus) {
-                        log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
+                        log_error_errno(bus_error, "Failed to get D-Bus connection: %m");
                         return -EIO;
                 }
         }
@@ -6177,7 +7427,7 @@ static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const ch
         struct msghdr msghdr = {
                 .msg_name = &sockaddr,
                 .msg_namelen = offsetof(struct sockaddr_un, sun_path)
-                               + sizeof("/run/systemd/shutdownd") - 1,
+                               + strlen("/run/systemd/shutdownd"),
                 .msg_iov = iovec,
                 .msg_iovlen = 1,
         };
@@ -6211,10 +7461,8 @@ static int reload_with_fallback(sd_bus *bus) {
         /* Nothing else worked, so let's try signals */
         assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
 
-        if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
-                log_error("kill() failed: %m");
-                return -errno;
-        }
+        if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0)
+                return log_error_errno(errno, "kill() failed: %m");
 
         return 0;
 }
@@ -6242,8 +7490,13 @@ done:
 
 static int halt_now(enum action a) {
 
-/* Make sure C-A-D is handled by the kernel from this
-         * point on... */
+        /* The kernel will automaticall flush ATA disks and suchlike
+         * on reboot(), but the file systems need to be synce'd
+         * explicitly in advance. */
+        sync();
+
+        /* Make sure C-A-D is handled by the kernel from this point
+         * on... */
         reboot(RB_ENABLE_CAD);
 
         switch (a) {
@@ -6320,7 +7573,7 @@ static int halt_main(sd_bus *bus) {
                                    m);
 
                 if (r < 0)
-                        log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
+                        log_warning_errno(r, "Failed to talk to shutdownd, proceeding with immediate shutdown: %m");
                 else {
                         char date[FORMAT_TIMESTAMP_MAX];
 
@@ -6339,7 +7592,7 @@ static int halt_main(sd_bus *bus) {
                 else {
                         r = utmp_put_shutdown();
                         if (r < 0)
-                                log_warning("Failed to write utmp record: %s", strerror(-r));
+                                log_warning_errno(r, "Failed to write utmp record: %m");
                 }
         }
 
@@ -6347,7 +7600,7 @@ static int halt_main(sd_bus *bus) {
                 return 0;
 
         r = halt_now(arg_action);
-        log_error("Failed to reboot: %s", strerror(-r));
+        log_error_errno(r, "Failed to reboot: %m");
 
         return r;
 }
@@ -6369,7 +7622,7 @@ static int runlevel_main(void) {
 }
 
 int main(int argc, char*argv[]) {
-        _cleanup_bus_unref_ sd_bus *bus = NULL;
+        _cleanup_bus_close_unref_ sd_bus *bus = NULL;
         int r;
 
         setlocale(LC_ALL, "");
@@ -6445,7 +7698,7 @@ int main(int argc, char*argv[]) {
 
                 r = send_shutdownd(arg_when, SD_SHUTDOWN_NONE, false, !arg_no_wall, m);
                 if (r < 0)
-                        log_warning("Failed to talk to shutdownd, shutdown hasn't been cancelled: %s", strerror(-r));
+                        log_warning_errno(r, "Failed to talk to shutdownd, shutdown hasn't been cancelled: %m");
                 break;
         }