chiark / gitweb /
systemctl: support force-reload and condrestart as aliases for reload-or-try-restart
[elogind.git] / src / systemctl.c
index d90471773ecb2b6c9fdc0ed0058a703c1e501df0..667aeaeb9642cd4ee40891c745d96f522681b46c 100644 (file)
@@ -47,9 +47,9 @@
 #include "list.h"
 
 static const char *arg_type = NULL;
-static const char *arg_property = NULL;
+static char **arg_property = NULL;
 static bool arg_all = false;
-static bool arg_replace = false;
+static bool arg_fail = false;
 static bool arg_session = false;
 static bool arg_no_block = false;
 static bool arg_immediate = false;
@@ -58,8 +58,9 @@ static bool arg_no_sync = false;
 static bool arg_no_wall = false;
 static bool arg_dry = false;
 static bool arg_quiet = false;
+static char arg_full = false;
 static char **arg_wall = NULL;
-enum action {
+static enum action {
         ACTION_INVALID,
         ACTION_SYSTEMCTL,
         ACTION_HALT,
@@ -77,9 +78,26 @@ enum action {
         ACTION_RUNLEVEL,
         _ACTION_MAX
 } arg_action = ACTION_SYSTEMCTL;
+static enum dot {
+        DOT_ALL,
+        DOT_ORDER,
+        DOT_REQUIRE
+} arg_dot = DOT_ALL;
 
 static bool private_bus = false;
 
+static const char *ansi_highlight(bool b) {
+        static int t = -1;
+
+        if (_unlikely_(t < 0))
+                t = isatty(STDOUT_FILENO) > 0;
+
+        if (!t)
+                return "";
+
+        return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF;
+}
+
 static bool error_is_no_service(DBusError *error) {
 
         assert(error);
@@ -183,10 +201,11 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
 
         dbus_message_iter_recurse(&iter, &sub);
 
-        printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
+        if (isatty(STDOUT_FILENO))
+                printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
 
         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
-                const char *id, *description, *load_state, *active_state, *sub_state, *unit_state, *job_type, *job_path, *dot;
+                const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path, *job_type, *job_path, *dot;
                 uint32_t job_id;
 
                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
@@ -202,7 +221,8 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
-                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0 ||
                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &job_id, true) < 0 ||
                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &job_type, true) < 0 ||
                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, false) < 0) {
@@ -213,17 +233,19 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
 
                 if ((!arg_type || ((dot = strrchr(id, '.')) &&
                                    streq(dot+1, arg_type))) &&
-                    (arg_all || !streq(active_state, "inactive"))) {
-
+                    (arg_all || !(streq(active_state, "inactive") || following[0]) || job_id > 0)) {
+                        char *e;
                         int a = 0, b = 0;
 
                         if (streq(active_state, "maintenance"))
-                                fputs(ANSI_HIGHLIGHT_ON, stdout);
+                                fputs(ansi_highlight(true), stdout);
 
-                        printf("%-45s %-6s %-12s %-12s%n", id, load_state, active_state, sub_state, &a);
+                        e = arg_full ? NULL : ellipsize(id, 45, 33);
+                        printf("%-45s %-6s %-12s %-12s%n", e ? e : id, load_state, active_state, sub_state, &a);
+                        free(e);
 
                         if (job_id != 0)
-                                printf(" %-15s%n", job_type, &b);
+                                printf(" => %-12s%n", job_type, &b);
                         else
                                 b = 1 + 15;
 
@@ -235,7 +257,7 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
                         }
 
                         if (streq(active_state, "maintenance"))
-                                fputs(ANSI_HIGHLIGHT_OFF, stdout);
+                                fputs(ansi_highlight(false), stdout);
 
                         fputs("\n", stdout);
                         k++;
@@ -244,10 +266,258 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
                 dbus_message_iter_next(&sub);
         }
 
-        if (arg_all)
-                printf("\n%u units listed.\n", k);
-        else
-                printf("\n%u live units listed. Pass --all to see dead units, too.\n", k);
+        if (isatty(STDOUT_FILENO)) {
+
+                printf("\nLOAD   = Load State, reflects whether the unit configuration was properly loaded.\n"
+                       "ACTIVE = Active State, the high-level unit activation state, i.e. generalization of the substate.\n"
+                       "SUB    = Substate, the low-level unit activation state, possible values depend on unit type.\n"
+                       "JOB    = Job, shows pending jobs for the unit.\n");
+
+                if (arg_all)
+                        printf("\n%u units listed.\n", k);
+                else
+                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", k);
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
+        static const char * const colors[] = {
+                "Requires",              "[color=\"black\"]",
+                "RequiresOverridable",   "[color=\"black\"]",
+                "Requisite",             "[color=\"darkblue\"]",
+                "RequisiteOverridable",  "[color=\"darkblue\"]",
+                "Wants",                 "[color=\"darkgrey\"]",
+                "Conflicts",             "[color=\"red\"]",
+                "After",                 "[color=\"green\"]"
+        };
+
+        const char *c = NULL;
+        unsigned i;
+
+        assert(name);
+        assert(prop);
+        assert(iter);
+
+        for (i = 0; i < ELEMENTSOF(colors); i += 2)
+                if (streq(colors[i], prop)) {
+                        c = colors[i+1];
+                        break;
+                }
+
+        if (!c)
+                return 0;
+
+        if (arg_dot != DOT_ALL)
+                if ((arg_dot == DOT_ORDER) != streq(prop, "After"))
+                        return 0;
+
+        switch (dbus_message_iter_get_arg_type(iter)) {
+
+        case DBUS_TYPE_ARRAY:
+
+                if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
+                        DBusMessageIter sub;
+
+                        dbus_message_iter_recurse(iter, &sub);
+
+                        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                                const char *s;
+
+                                assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
+                                dbus_message_iter_get_basic(&sub, &s);
+                                printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
+
+                                dbus_message_iter_next(&sub);
+                        }
+
+                        return 0;
+                }
+        }
+
+        return 0;
+}
+
+static int dot_one(DBusConnection *bus, const char *name, const char *path) {
+        DBusMessage *m = NULL, *reply = NULL;
+        const char *interface = "org.freedesktop.systemd1.Unit";
+        int r;
+        DBusError error;
+        DBusMessageIter iter, sub, sub2, sub3;
+
+        assert(bus);
+        assert(path);
+
+        dbus_error_init(&error);
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              path,
+                              "org.freedesktop.DBus.Properties",
+                              "GetAll"))) {
+                log_error("Could not allocate message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &interface,
+                                      DBUS_TYPE_INVALID)) {
+                log_error("Could not append arguments to message.");
+                r = -ENOMEM;
+                goto finish;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", error.message);
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                const char *prop;
+
+                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&sub, &sub2);
+
+                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&sub2, &sub3);
+
+                if (dot_one_property(name, prop, &sub3)) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_next(&sub);
+        }
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
+static int dot(DBusConnection *bus, char **args, unsigned n) {
+        DBusMessage *m = NULL, *reply = NULL;
+        DBusError error;
+        int r;
+        DBusMessageIter iter, sub, sub2;
+
+        dbus_error_init(&error);
+
+        assert(bus);
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              "ListUnits"))) {
+                log_error("Could not allocate message.");
+                return -ENOMEM;
+        }
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                log_error("Failed to issue method call: %s", error.message);
+                r = -EIO;
+                goto finish;
+        }
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+            dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
+                log_error("Failed to parse reply.");
+                r = -EIO;
+                goto finish;
+        }
+
+        printf("digraph systemd {\n");
+
+        dbus_message_iter_recurse(&iter, &sub);
+        while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+                const char *id, *description, *load_state, *active_state, *sub_state, *unit_path;
+
+                if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_iter_recurse(&sub, &sub2);
+
+                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0) {
+                        log_error("Failed to parse reply.");
+                        r = -EIO;
+                        goto finish;
+                }
+
+                if ((r = dot_one(bus, id, unit_path)) < 0)
+                        goto finish;
+
+                /* printf("\t\"%s\";\n", id); */
+                dbus_message_iter_next(&sub);
+        }
+
+        printf("}\n");
+
+        log_info("   Color legend: black     = Requires\n"
+                 "                 dark blue = Requisite\n"
+                 "                 dark grey = Wants\n"
+                 "                 red       = Conflicts\n"
+                 "                 green     = After\n");
+
+        if (isatty(fileno(stdout)))
+                log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
+                           "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n");
 
         r = 0;
 
@@ -304,6 +574,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
                 const char *name, *type, *state, *job_path, *unit_path;
                 uint32_t id;
+                char *e;
 
                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
                         log_error("Failed to parse reply.");
@@ -324,7 +595,10 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
                         goto finish;
                 }
 
-                printf("%4u %-45s %-17s %-7s\n", id, name, type, state);
+                e = arg_full ? NULL : ellipsize(name, 45, 33);
+                printf("%4u %-45s %-17s %-7s\n", id, e ? e : name, type, state);
+                free(e);
+
                 k++;
 
                 dbus_message_iter_next(&sub);
@@ -492,6 +766,77 @@ finish:
         return r;
 }
 
+static bool unit_need_daemon_reload(DBusConnection *bus, const char *unit) {
+        DBusMessage *m, *reply;
+        dbus_bool_t b = FALSE;
+        DBusMessageIter iter, sub;
+        const char
+                *interface = "org.freedesktop.systemd1.Unit",
+                *property = "NeedDaemonReload",
+                *path;
+
+        /* We ignore all errors here, since this is used to show a warning only */
+
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              "/org/freedesktop/systemd1",
+                              "org.freedesktop.systemd1.Manager",
+                              "GetUnit")))
+                goto finish;
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &unit,
+                                      DBUS_TYPE_INVALID))
+                goto finish;
+
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
+                goto finish;
+
+        if (!dbus_message_get_args(reply, NULL,
+                                   DBUS_TYPE_OBJECT_PATH, &path,
+                                   DBUS_TYPE_INVALID))
+                goto finish;
+
+        dbus_message_unref(m);
+        if (!(m = dbus_message_new_method_call(
+                              "org.freedesktop.systemd1",
+                              path,
+                              "org.freedesktop.DBus.Properties",
+                              "Get")))
+                goto finish;
+
+        if (!dbus_message_append_args(m,
+                                      DBUS_TYPE_STRING, &interface,
+                                      DBUS_TYPE_STRING, &property,
+                                      DBUS_TYPE_INVALID)) {
+                goto finish;
+        }
+
+        dbus_message_unref(reply);
+        if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
+                goto finish;
+
+        if (!dbus_message_iter_init(reply, &iter) ||
+            dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+                goto finish;
+
+        dbus_message_iter_recurse(&iter, &sub);
+
+        if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
+                goto finish;
+
+        dbus_message_iter_get_basic(&sub, &b);
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        return b;
+}
+
 typedef struct WaitData {
         Set *set;
         bool failed;
@@ -610,6 +955,7 @@ static int start_unit_one(
 
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
+        const char *path;
         int r;
 
         assert(bus);
@@ -653,18 +999,21 @@ static int start_unit_one(
                 goto finish;
         }
 
+        if (!dbus_message_get_args(reply, &error,
+                                   DBUS_TYPE_OBJECT_PATH, &path,
+                                   DBUS_TYPE_INVALID)) {
+                log_error("Failed to parse reply: %s", error.message);
+                r = -EIO;
+                goto finish;
+        }
+
+        if (unit_need_daemon_reload(bus, name))
+                log_warning("Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
+                            arg_session ? "--session" : "--system");
+
         if (!arg_no_block) {
-                const char *path;
                 char *p;
 
-                if (!dbus_message_get_args(reply, &error,
-                                           DBUS_TYPE_OBJECT_PATH, &path,
-                                           DBUS_TYPE_INVALID)) {
-                        log_error("Failed to parse reply: %s", error.message);
-                        r = -EIO;
-                        goto finish;
-                }
-
                 if (!(p = strdup(path))) {
                         log_error("Failed to duplicate path.");
                         r = -ENOMEM;
@@ -720,7 +1069,7 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
                 [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
                 [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
                 [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
-                [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_SERVICE,
+                [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
                 [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET
         };
 
@@ -733,17 +1082,22 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
 
         if (arg_action == ACTION_SYSTEMCTL) {
                 method =
-                        streq(args[0], "stop")    ? "StopUnit" :
-                        streq(args[0], "reload")  ? "ReloadUnit" :
-                        streq(args[0], "restart") ? "RestartUnit" :
-                                                    "StartUnit";
+                        streq(args[0], "stop")                  ? "StopUnit" :
+                        streq(args[0], "reload")                ? "ReloadUnit" :
+                        streq(args[0], "restart")               ? "RestartUnit" :
+                        streq(args[0], "try-restart")           ? "TryRestartUnit" :
+                        streq(args[0], "reload-or-restart")     ? "ReloadOrRestartUnit" :
+                        streq(args[0], "reload-or-try-restart") ||
+                        streq(args[0], "force-reload")          ||
+                        streq(args[0], "condrestart")           ? "ReloadOrTryRestartUnit" :
+                                                                  "StartUnit";
 
                 mode =
                         (streq(args[0], "isolate") ||
                          streq(args[0], "rescue")  ||
                          streq(args[0], "emergency")) ? "isolate" :
-                                          arg_replace ? "replace" :
-                                                        "fail";
+                                             arg_fail ? "fail" :
+                                                        "replace";
 
                 one_name = table[verb_to_action(args[0])];
 
@@ -794,12 +1148,17 @@ finish:
 }
 
 static int start_special(DBusConnection *bus, char **args, unsigned n) {
+        int r;
+
         assert(bus);
         assert(args);
 
-        warn_wall(verb_to_action(args[0]));
+        r = start_unit(bus, args, n);
 
-        return start_unit(bus, args, n);
+        if (r >= 0)
+                warn_wall(verb_to_action(args[0]));
+
+        return r;
 }
 
 static int check_unit(DBusConnection *bus, char **args, unsigned n) {
@@ -928,6 +1287,8 @@ typedef struct ExecStatusInfo {
         char *path;
         char **argv;
 
+        bool ignore;
+
         usec_t start_timestamp;
         usec_t exit_timestamp;
         pid_t pid;
@@ -952,6 +1313,7 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i)
         unsigned n;
         uint32_t pid;
         int32_t code, status;
+        dbus_bool_t ignore;
 
         assert(i);
         assert(i);
@@ -997,6 +1359,7 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i)
         }
 
         if (!dbus_message_iter_next(&sub2) ||
+            bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, true) < 0 ||
             bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp, true) < 0 ||
             bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp, true) < 0 ||
             bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, true) < 0 ||
@@ -1004,6 +1367,7 @@ static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i)
             bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &status, false) < 0)
                 return -EIO;
 
+        i->ignore = ignore;
         i->start_timestamp = (usec_t) start_timestamp;
         i->exit_timestamp = (usec_t) exit_timestamp;
         i->pid = (pid_t) pid;
@@ -1024,6 +1388,8 @@ typedef struct UnitStatusInfo {
         const char *fragment_path;
         const char *default_control_group;
 
+        bool need_daemon_reload;
+
         /* Service */
         pid_t main_pid;
         pid_t control_pid;
@@ -1054,7 +1420,6 @@ typedef struct UnitStatusInfo {
 
 static void print_status_info(UnitStatusInfo *i) {
         ExecStatusInfo *p;
-        int r;
 
         assert(i);
 
@@ -1071,18 +1436,25 @@ static void print_status_info(UnitStatusInfo *i) {
         if (i->fragment_path)
                 printf("\t  Loaded: %s (%s)\n", strna(i->load_state), i->fragment_path);
         else if (streq_ptr(i->load_state, "failed"))
-                printf("\t  Loaded: " ANSI_HIGHLIGHT_ON "%s" ANSI_HIGHLIGHT_OFF "\n", strna(i->load_state));
+                printf("\t  Loaded: %s%s%s\n",
+                       ansi_highlight(true),
+                       strna(i->load_state),
+                       ansi_highlight(false));
         else
                 printf("\t  Loaded: %s\n", strna(i->load_state));
 
         if (streq_ptr(i->active_state, "maintenance")) {
                         if (streq_ptr(i->active_state, i->sub_state))
-                                printf("\t  Active: " ANSI_HIGHLIGHT_ON "%s" ANSI_HIGHLIGHT_OFF "\n",
-                                       strna(i->active_state));
+                                printf("\t  Active: %s%s%s\n",
+                                       ansi_highlight(true),
+                                       strna(i->active_state),
+                                       ansi_highlight(false));
                         else
-                                printf("\t  Active: " ANSI_HIGHLIGHT_ON "%s (%s)" ANSI_HIGHLIGHT_OFF "\n",
+                                printf("\t  Active: %s%s (%s)%s\n",
+                                       ansi_highlight(true),
                                        strna(i->active_state),
-                                       strna(i->sub_state));
+                                       strna(i->sub_state),
+                                       ansi_highlight(false));
         } else {
                 if (streq_ptr(i->active_state, i->sub_state))
                         printf("\t  Active: %s\n",
@@ -1100,9 +1472,6 @@ static void print_status_info(UnitStatusInfo *i) {
         else if (i->what)
                 printf("\t    What: %s\n", i->what);
 
-        if (i->status_text)
-                printf("\t  Status: \"%s\"\n", i->status_text);
-
         if (i->accept)
                 printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
 
@@ -1153,8 +1522,7 @@ static void print_status_info(UnitStatusInfo *i) {
                                         printf("status=%i", i->exit_status);
                                 else
                                         printf("signal=%s", signal_to_string(i->exit_status));
-                                printf(")");
-                        }
+                                printf(")");                        }
                 }
 
                 if (i->main_pid > 0 && i->control_pid > 0)
@@ -1175,6 +1543,9 @@ static void print_status_info(UnitStatusInfo *i) {
                 printf("\n");
         }
 
+        if (i->status_text)
+                printf("\t  Status: \"%s\"\n", i->status_text);
+
         if (i->default_control_group) {
                 unsigned c;
 
@@ -1185,11 +1556,14 @@ static void print_status_info(UnitStatusInfo *i) {
                 else
                         c = 0;
 
-                if ((r = cg_init()) < 0)
-                        log_error("Failed to initialize libcg: %s", strerror(-r));
-                else
-                        show_cgroup_recursive(i->default_control_group, "\t\t  ", c);
+                show_cgroup_by_path(i->default_control_group, "\t\t  ", c);
         }
+
+        if (i->need_daemon_reload)
+                printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
+                       ansi_highlight(true),
+                       ansi_highlight(false),
+                       arg_session ? "--session" : "--system");
 }
 
 static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
@@ -1236,6 +1610,8 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
 
                 if (streq(name, "Accept"))
                         i->accept = b;
+                else if (streq(name, "NeedDaemonReload"))
+                        i->need_daemon_reload = b;
 
                 break;
         }
@@ -1328,7 +1704,7 @@ static int print_property(const char *name, DBusMessageIter *iter) {
         /* This is a low-level property printer, see
          * print_status_info() for the nicer output */
 
-        if (arg_property && !streq(name, arg_property))
+        if (arg_property && !strv_find(arg_property, name))
                 return 0;
 
         switch (dbus_message_iter_get_arg_type(iter)) {
@@ -1530,10 +1906,11 @@ static int print_property(const char *name, DBusMessageIter *iter) {
 
                                         t = strv_join(info.argv, " ");
 
-                                        printf("%s={ path=%s ; argv[]=%s start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
+                                        printf("%s={ path=%s ; argv[]=%s ; ignore=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
                                                name,
                                                strna(info.path),
                                                strna(t),
+                                               yes_no(info.ignore),
                                                strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
                                                strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
                                                (unsigned) info. pid,
@@ -2292,10 +2669,11 @@ static int clear_jobs(DBusConnection *bus, char **args, unsigned n) {
                 assert(arg_action == ACTION_SYSTEMCTL);
 
                 method =
-                        streq(args[0], "clear-jobs")    ? "ClearJobs" :
-                        streq(args[0], "daemon-reload") ? "Reload" :
-                        streq(args[0], "daemon-reexec") ? "Reexecute" :
-                                                          "Exit";
+                        streq(args[0], "clear-jobs")        ? "ClearJobs" :
+                        streq(args[0], "daemon-reload")     ? "Reload" :
+                        streq(args[0], "daemon-reexec")     ? "Reexecute" :
+                        streq(args[0], "reset-maintenance") ? "ResetMaintenance" :
+                                                              "Exit";
         }
 
         if (!(m = dbus_message_new_method_call(
@@ -2335,6 +2713,63 @@ finish:
         return r;
 }
 
+static int reset_maintenance(DBusConnection *bus, char **args, unsigned n) {
+        DBusMessage *m = NULL, *reply = NULL;
+        unsigned i;
+        int r;
+        DBusError error;
+
+        assert(bus);
+        dbus_error_init(&error);
+
+        if (n <= 1)
+                return clear_jobs(bus, args, n);
+
+        for (i = 1; i < n; i++) {
+
+                if (!(m = dbus_message_new_method_call(
+                                      "org.freedesktop.systemd1",
+                                      "/org/freedesktop/systemd1",
+                                      "org.freedesktop.systemd1.Manager",
+                                      "ResetMaintenanceUnit"))) {
+                        log_error("Could not allocate message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!dbus_message_append_args(m,
+                                              DBUS_TYPE_STRING, args + i,
+                                              DBUS_TYPE_INVALID)) {
+                        log_error("Could not append arguments to message.");
+                        r = -ENOMEM;
+                        goto finish;
+                }
+
+                if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
+                        log_error("Failed to issue method call: %s", error.message);
+                        r = -EIO;
+                        goto finish;
+                }
+
+                dbus_message_unref(m);
+                dbus_message_unref(reply);
+                m = reply = NULL;
+        }
+
+        r = 0;
+
+finish:
+        if (m)
+                dbus_message_unref(m);
+
+        if (reply)
+                dbus_message_unref(reply);
+
+        dbus_error_free(&error);
+
+        return r;
+}
+
 static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
@@ -2485,14 +2920,18 @@ finish:
 static int systemctl_help(void) {
 
         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
-               "Send control commands to the systemd manager.\n\n"
+               "Send control commands to or query the systemd manager.\n\n"
                "  -h --help          Show this help\n"
                "  -t --type=TYPE     List only units of a particular type\n"
                "  -p --property=NAME Show only properties by this name\n"
                "  -a --all           Show all units/properties, including dead/empty ones\n"
-               "     --replace       When installing a new job, replace existing conflicting ones\n"
+               "     --full          Don't ellipsize unit names.\n"
+               "     --fail          When installing a new job, fail if conflicting jobs are\n"
+               "                     pending\n"
                "     --system        Connect to system bus\n"
                "     --session       Connect to session bus\n"
+               "     --order         When generating graph for dot, show only order\n"
+               "     --require        When generating graph for dot, show only requirement\n"
                "  -q --quiet         Suppress output\n"
                "     --no-block      Do not wait until operation finished\n"
                "     --no-wall       Don't send wall message before halt/power-off/reboot\n\n"
@@ -2500,18 +2939,27 @@ static int systemctl_help(void) {
                "  list-units                      List units\n"
                "  start [NAME...]                 Start one or more units\n"
                "  stop [NAME...]                  Stop one or more units\n"
-               "  restart [NAME...]               Restart one or more units\n"
                "  reload [NAME...]                Reload one or more units\n"
+               "  restart [NAME...]               Start or restart one or more units\n"
+               "  try-restart [NAME...]           Restart one or more units if active\n"
+               "  reload-or-restart [NAME...]     Reload one or more units is possible,\n"
+               "                                  otherwise start or restart\n"
+               "  reload-or-try-restart [NAME...] Reload one or more units is possible,\n"
+               "                                  otherwise restart if active\n"
                "  isolate [NAME]                  Start one unit and stop all others\n"
-               "  check [NAME...]                 Check whether any of the passed units are active\n"
+               "  check [NAME...]                 Check whether units are active\n"
                "  status [NAME...]                Show status of one or more units\n"
-               "  show [NAME...|JOB...]           Show properties of one or more units/jobs/manager\n"
+               "  show [NAME...|JOB...]           Show properties of one or more\n"
+               "                                  units/jobs/manager\n"
+               "  reset-maintenance [NAME...]     Reset maintenance state for all, one\n"
+               "                                  or more units\n"
                "  load [NAME...]                  Load one or more units\n"
                "  list-jobs                       List jobs\n"
                "  cancel [JOB...]                 Cancel one or more jobs\n"
                "  clear-jobs                      Cancel all jobs\n"
                "  monitor                         Monitor unit/job changes\n"
                "  dump                            Dump server status\n"
+               "  dot                             Dump dependency graph for dot(1)\n"
                "  snapshot [NAME]                 Create a snapshot\n"
                "  delete [NAME...]                Remove one or more snapshots\n"
                "  daemon-reload                   Reload systemd manager configuration\n"
@@ -2523,9 +2971,9 @@ static int systemctl_help(void) {
                "  halt                            Shut down and halt the system\n"
                "  poweroff                        Shut down and power-off the system\n"
                "  reboot                          Shut down and reboot the system\n"
-               "  default                         Enter default mode\n"
-               "  rescue                          Enter rescue mode\n"
-               "  emergency                       Enter emergency mode\n",
+               "  rescue                          Enter system rescue mode\n"
+               "  emergency                       Enter system emergency mode\n"
+               "  default                         Enter system default mode\n",
                program_invocation_short_name);
 
         return 0;
@@ -2599,11 +3047,14 @@ static int runlevel_help(void) {
 static int systemctl_parse_argv(int argc, char *argv[]) {
 
         enum {
-                ARG_REPLACE = 0x100,
+                ARG_FAIL = 0x100,
                 ARG_SESSION,
                 ARG_SYSTEM,
                 ARG_NO_BLOCK,
-                ARG_NO_WALL
+                ARG_NO_WALL,
+                ARG_ORDER,
+                ARG_REQUIRE,
+                ARG_FULL
         };
 
         static const struct option options[] = {
@@ -2611,12 +3062,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "type",      required_argument, NULL, 't'          },
                 { "property",  required_argument, NULL, 'p'          },
                 { "all",       no_argument,       NULL, 'a'          },
-                { "replace",   no_argument,       NULL, ARG_REPLACE  },
+                { "full",      no_argument,       NULL, ARG_FULL     },
+                { "fail",      no_argument,       NULL, ARG_FAIL     },
                 { "session",   no_argument,       NULL, ARG_SESSION  },
                 { "system",    no_argument,       NULL, ARG_SYSTEM   },
                 { "no-block",  no_argument,       NULL, ARG_NO_BLOCK },
                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL  },
                 { "quiet",     no_argument,       NULL, 'q'          },
+                { "order",     no_argument,       NULL, ARG_ORDER    },
+                { "require",   no_argument,       NULL, ARG_REQUIRE  },
                 { NULL,        0,                 NULL, 0            }
         };
 
@@ -2637,21 +3091,28 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_type = optarg;
                         break;
 
-                case 'p':
-                        arg_property = optarg;
+                case 'p': {
+                        char **l;
+
+                        if (!(l = strv_append(arg_property, optarg)))
+                                return -ENOMEM;
+
+                        strv_free(arg_property);
+                        arg_property = l;
 
                         /* If the user asked for a particular
                          * property, show it to him, even if it is
                          * empty. */
                         arg_all = true;
                         break;
+                }
 
                 case 'a':
                         arg_all = true;
                         break;
 
-                case ARG_REPLACE:
-                        arg_replace = true;
+                case ARG_FAIL:
+                        arg_fail = true;
                         break;
 
                 case ARG_SESSION:
@@ -2670,6 +3131,18 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         arg_no_wall = true;
                         break;
 
+                case ARG_ORDER:
+                        arg_dot = DOT_ORDER;
+                        break;
+
+                case ARG_REQUIRE:
+                        arg_dot = DOT_REQUIRE;
+                        break;
+
+                case ARG_FULL:
+                        arg_full = true;
+                        break;
+
                 case 'q':
                         arg_quiet = true;
                         break;
@@ -3186,12 +3659,18 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) {
                 { "stop",              MORE,  2, start_unit      },
                 { "reload",            MORE,  2, start_unit      },
                 { "restart",           MORE,  2, start_unit      },
+                { "try-restart",       MORE,  2, start_unit      },
+                { "reload-or-restart", MORE,  2, start_unit      },
+                { "reload-or-try-restart", MORE, 2, start_unit   },
+                { "force-reload",      MORE,  2, start_unit      }, /* For compatibility with SysV */
+                { "condrestart",       MORE,  2, start_unit      }, /* For compatibility with RH */
                 { "isolate",           EQUAL, 2, start_unit      },
                 { "check",             MORE,  2, check_unit      },
                 { "show",              MORE,  1, show            },
                 { "status",            MORE,  2, show            },
                 { "monitor",           EQUAL, 1, monitor         },
                 { "dump",              EQUAL, 1, dump            },
+                { "dot",               EQUAL, 1, dot             },
                 { "snapshot",          LESS,  2, snapshot        },
                 { "delete",            MORE,  2, delete_snapshot },
                 { "daemon-reload",     EQUAL, 1, clear_jobs      },
@@ -3205,7 +3684,8 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) {
                 { "reboot",            EQUAL, 1, start_special   },
                 { "default",           EQUAL, 1, start_special   },
                 { "rescue",            EQUAL, 1, start_special   },
-                { "emergency",         EQUAL, 1, start_special   }
+                { "emergency",         EQUAL, 1, start_special   },
+                { "reset-maintenance", MORE,  1, reset_maintenance },
         };
 
         int left;
@@ -3292,26 +3772,29 @@ static int reload_with_fallback(DBusConnection *bus) {
 static int start_with_fallback(DBusConnection *bus) {
         int r;
 
-        warn_wall(arg_action);
 
         if (bus) {
                 /* First, try systemd via D-Bus. */
                 if ((r = start_unit(bus, NULL, 0)) > 0)
-                        return 0;
+                        goto done;
 
                 /* Hmm, talking to systemd via D-Bus didn't work. Then
                  * let's try to talk to Upstart via D-Bus. */
                 if ((r = talk_upstart()) > 0)
-                        return 0;
+                        goto done;
         }
 
         /* Nothing else worked, so let's try
          * /dev/initctl */
         if ((r = talk_initctl()) != 0)
-                return 0;
+                goto done;
 
         log_error("Failed to talk to init daemon.");
         return -EIO;
+
+done:
+        warn_wall(arg_action);
+        return 0;
 }
 
 static int halt_main(DBusConnection *bus) {
@@ -3455,5 +3938,7 @@ finish:
 
         dbus_shutdown();
 
+        strv_free(arg_property);
+
         return retval;
 }