chiark / gitweb /
systemctl: sort 'list-units' output
[elogind.git] / src / systemctl.c
index 1e3385f8604074ff59ea09939cc2be1d722da66e..9a85383dbadf84114136c5eb4d383db342e35922 100644 (file)
@@ -96,18 +96,31 @@ static bool private_bus = false;
 
 static int daemon_reload(DBusConnection *bus, char **args, unsigned n);
 
-static const char *ansi_highlight(bool b) {
+static bool on_tty(void) {
         static int t = -1;
 
         if (_unlikely_(t < 0))
                 t = isatty(STDOUT_FILENO) > 0;
 
-        if (!t)
+        return t;
+}
+
+static const char *ansi_highlight(bool b) {
+
+        if (!on_tty())
                 return "";
 
         return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF;
 }
 
+static const char *ansi_highlight_green(bool b) {
+
+        if (!on_tty())
+                return "";
+
+        return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF;
+}
+
 static bool error_is_no_service(DBusError *error) {
 
         assert(error);
@@ -175,12 +188,43 @@ static void warn_wall(enum action action) {
         utmp_wall(table[action]);
 }
 
+struct unit_info {
+        const char *id;
+        const char *description;
+        const char *load_state;
+        const char *active_state;
+        const char *sub_state;
+        const char *following;
+        const char *unit_path;
+        uint32_t job_id;
+        const char *job_type;
+        const char *job_path;
+};
+
+static int compare_unit_info(const void *a, const void *b) {
+        const char *d1, *d2;
+        const struct unit_info *u = a, *v = b;
+
+        d1 = strrchr(u->id, '.');
+        d2 = strrchr(v->id, '.');
+
+        if (d1 && d2) {
+                int r;
+
+                if ((r = strcmp(d1, d2)) != 0)
+                        return r;
+        }
+
+        return strcmp(u->id, v->id);
+}
+
 static int list_units(DBusConnection *bus, char **args, unsigned n) {
         DBusMessage *m = NULL, *reply = NULL;
         DBusError error;
         int r;
         DBusMessageIter iter, sub, sub2;
-        unsigned k = 0;
+        unsigned c = 0, k, n_units = 0;
+        struct unit_info *unit_infos = NULL;
 
         dbus_error_init(&error);
 
@@ -211,12 +255,8 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
 
         dbus_message_iter_recurse(&iter, &sub);
 
-        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, *following, *unit_path, *job_type, *job_path, *dot;
-                uint32_t job_id;
+                struct unit_info *u;
 
                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
                         log_error("Failed to parse reply.");
@@ -224,56 +264,86 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
                         goto finish;
                 }
 
+                if (c >= n_units) {
+                        struct unit_info *w;
+
+                        n_units = MAX(2*c, 16);
+                        w = realloc(unit_infos, sizeof(struct unit_info) * n_units);
+
+                        if (!w) {
+                                log_error("Failed to allocate unit array.");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        unit_infos = w;
+                }
+
+                u = unit_infos+c;
+
                 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_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) {
+                if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->id, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->description, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->following, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
+                    bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
                         log_error("Failed to parse reply.");
                         r = -EIO;
                         goto finish;
                 }
 
-                if ((!arg_type || ((dot = strrchr(id, '.')) &&
+                dbus_message_iter_next(&sub);
+                c++;
+        }
+
+        qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
+
+        if (isatty(STDOUT_FILENO))
+                printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
+
+        for (k = 0; k < c; k++) {
+                const char *dot;
+                struct unit_info *u = unit_infos+k;
+
+                if ((!arg_type || ((dot = strrchr(u->id, '.')) &&
                                    streq(dot+1, arg_type))) &&
-                    (arg_all || !(streq(active_state, "inactive") || following[0]) || job_id > 0)) {
+                    (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0)) {
                         char *e;
                         int a = 0, b = 0;
+                        const char *on, *off;
 
-                        if (streq(active_state, "maintenance"))
-                                fputs(ansi_highlight(true), stdout);
+                        if (streq(u->active_state, "maintenance")) {
+                                on = ansi_highlight(true);
+                                off = ansi_highlight(false);
+                        } else
+                                on = off = "";
 
-                        e = arg_full ? NULL : ellipsize(id, 45, 33);
-                        printf("%-45s %-6s %-12s %-12s%n", e ? e : id, load_state, active_state, sub_state, &a);
+                        e = arg_full ? NULL : ellipsize(u->id, 45, 33);
+                        printf("%-45s %-6s %s%-12s %-12s%s%n", e ? e : u->id, u->load_state, on, u->active_state, u->sub_state, off, &a);
                         free(e);
 
-                        if (job_id != 0)
-                                printf(" => %-12s%n", job_type, &b);
+                        a -= strlen(on) + strlen(off);
+
+                        if (u->job_id != 0)
+                                printf(" => %-12s%n", u->job_type, &b);
                         else
                                 b = 1 + 15;
 
                         if (a + b + 2 < columns()) {
-                                if (job_id == 0)
+                                if (u->job_id == 0)
                                         printf("                ");
 
-                                printf(" %.*s", columns() - a - b - 2, description);
+                                printf(" %.*s", columns() - a - b - 2, u->description);
                         }
 
-                        if (streq(active_state, "maintenance"))
-                                fputs(ansi_highlight(false), stdout);
-
                         fputs("\n", stdout);
-                        k++;
                 }
-
-                dbus_message_iter_next(&sub);
         }
 
         if (isatty(STDOUT_FILENO)) {
@@ -284,9 +354,9 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) {
                        "JOB    = Job, shows pending jobs for the unit.\n");
 
                 if (arg_all)
-                        printf("\n%u units listed.\n", k);
+                        printf("\n%u units listed.\n", c);
                 else
-                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", k);
+                        printf("\n%u units listed. Pass --all to see inactive units, too.\n", c);
         }
 
         r = 0;
@@ -298,6 +368,8 @@ finish:
         if (reply)
                 dbus_message_unref(reply);
 
+        free(unit_infos);
+
         dbus_error_free(&error);
 
         return r;
@@ -311,6 +383,7 @@ static int dot_one_property(const char *name, const char *prop, DBusMessageIter
                 "RequisiteOverridable",  "[color=\"darkblue\"]",
                 "Wants",                 "[color=\"darkgrey\"]",
                 "Conflicts",             "[color=\"red\"]",
+                "ConflictedBy",          "[color=\"red\"]",
                 "After",                 "[color=\"green\"]"
         };
 
@@ -440,6 +513,8 @@ static int dot_one(DBusConnection *bus, const char *name, const char *path) {
                 dbus_message_iter_next(&sub);
         }
 
+        r = 0;
+
 finish:
         if (m)
                 dbus_message_unref(m);
@@ -489,7 +564,7 @@ static int dot(DBusConnection *bus, char **args, unsigned 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;
+                const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path;
 
                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
                         log_error("Failed to parse reply.");
@@ -504,6 +579,7 @@ static int dot(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_STRING, &following, 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;
@@ -780,7 +856,7 @@ finish:
 }
 
 static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
-        DBusMessage *m, *reply;
+        DBusMessage *m = NULL, *reply = NULL;
         dbus_bool_t b = FALSE;
         DBusMessageIter iter, sub;
         const char
@@ -1151,7 +1227,10 @@ static int start_unit(DBusConnection *bus, char **args, unsigned n) {
         }
 
         if (!arg_no_block)
-                r = wait_for_jobs(bus, s);
+                if ((r = wait_for_jobs(bus, s)) < 0)
+                        goto finish;
+
+        r = 1;
 
 finish:
         if (s)
@@ -1398,7 +1477,7 @@ typedef struct UnitStatusInfo {
 
         const char *description;
 
-        const char *fragment_path;
+        const char *path;
         const char *default_control_group;
 
         bool need_daemon_reload;
@@ -1433,6 +1512,7 @@ typedef struct UnitStatusInfo {
 
 static void print_status_info(UnitStatusInfo *i) {
         ExecStatusInfo *p;
+        const char *on, *off, *ss;
 
         assert(i);
 
@@ -1446,37 +1526,39 @@ static void print_status_info(UnitStatusInfo *i) {
 
         printf("\n");
 
-        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: %s%s%s\n",
-                       ansi_highlight(true),
-                       strna(i->load_state),
-                       ansi_highlight(false));
+        if (streq_ptr(i->load_state, "failed")) {
+                on = ansi_highlight(true);
+                off = ansi_highlight(false);
+        } else
+                on = off = "";
+
+        if (i->path)
+                printf("\t  Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
         else
-                printf("\t  Loaded: %s\n", strna(i->load_state));
+                printf("\t  Loaded: %s%s%s\n", on, strna(i->load_state), off);
+
+        ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
 
         if (streq_ptr(i->active_state, "maintenance")) {
-                        if (streq_ptr(i->active_state, i->sub_state))
-                                printf("\t  Active: %s%s%s\n",
-                                       ansi_highlight(true),
-                                       strna(i->active_state),
-                                       ansi_highlight(false));
-                        else
-                                printf("\t  Active: %s%s (%s)%s\n",
-                                       ansi_highlight(true),
-                                       strna(i->active_state),
-                                       strna(i->sub_state),
-                                       ansi_highlight(false));
-        } else {
-                if (streq_ptr(i->active_state, i->sub_state))
-                        printf("\t  Active: %s\n",
-                               strna(i->active_state));
-                else
-                        printf("\t  Active: %s (%s)\n",
-                               strna(i->active_state),
-                               strna(i->sub_state));
-        }
+                on = ansi_highlight(true);
+                off = ansi_highlight(false);
+        } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
+                on = ansi_highlight_green(true);
+                off = ansi_highlight_green(false);
+        } else
+                on = off = "";
+
+        if (ss)
+                printf("\t  Active: %s%s (%s)%s\n",
+                       on,
+                       strna(i->active_state),
+                       ss,
+                       off);
+        else
+                printf("\t  Active: %s%s%s\n",
+                       on,
+                       strna(i->active_state),
+                       off);
 
         if (i->sysfs_path)
                 printf("\t  Device: %s\n", i->sysfs_path);
@@ -1528,14 +1610,15 @@ static void print_status_info(UnitStatusInfo *i) {
                                         printf(" (%s)", t);
                                         free(t);
                                 }
-                        } else {
+                        } else if (i->exit_code > 0) {
                                 printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
 
                                 if (i->exit_code == CLD_EXITED)
                                         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)
@@ -1600,7 +1683,9 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn
                         else if (streq(name, "Description"))
                                 i->description = s;
                         else if (streq(name, "FragmentPath"))
-                                i->fragment_path = s;
+                                i->path = s;
+                        else if (streq(name, "SysVPath"))
+                                i->path = s;
                         else if (streq(name, "DefaultControlGroup"))
                                 i->default_control_group = s;
                         else if (streq(name, "StatusText"))
@@ -2095,7 +2180,9 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                 const char *path = NULL;
                 uint32_t id;
 
-                if (!show_properties || safe_atou32(args[i], &id) < 0) {
+                if (safe_atou32(args[i], &id) < 0) {
+
+                        /* Interpret as unit name */
 
                         if (!(m = dbus_message_new_method_call(
                                               "org.freedesktop.systemd1",
@@ -2151,7 +2238,9 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                 }
                         }
 
-                } else {
+                } else if (show_properties) {
+
+                        /* Interpret as job id */
 
                         if (!(m = dbus_message_new_method_call(
                                               "org.freedesktop.systemd1",
@@ -2171,6 +2260,33 @@ static int show(DBusConnection *bus, char **args, unsigned n) {
                                 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;
+                        }
+                } else {
+
+                        /* Interpret as PID */
+
+                        if (!(m = dbus_message_new_method_call(
+                                              "org.freedesktop.systemd1",
+                                              "/org/freedesktop/systemd1",
+                                              "org.freedesktop.systemd1.Manager",
+                                              "GetUnitByPID"))) {
+                                log_error("Could not allocate message.");
+                                r = -ENOMEM;
+                                goto finish;
+                        }
+
+                        if (!dbus_message_append_args(m,
+                                                      DBUS_TYPE_UINT32, &id,
+                                                      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;
@@ -3133,7 +3249,7 @@ static int remove_marked_symlinks_fd(int fd, const char *config_path, const char
                         free(p);
 
                         if (r == 0)
-                                q = r;
+                                r = q;
 
                 } else if (is_link) {
                         char *p, *dest, *c;
@@ -3652,7 +3768,7 @@ static int systemctl_help(void) {
                "                                  otherwise restart if active\n"
                "  isolate [NAME]                  Start one unit and stop all others\n"
                "  is-active [NAME...]             Check whether units are active\n"
-               "  status [NAME...]                Show runtime status of one or more units\n"
+               "  status [NAME...|PID...]         Show runtime status of one or more units\n"
                "  show [NAME...|JOB...]           Show properties of one or more\n"
                "                                  units/jobs or the manager\n"
                "  reset-maintenance [NAME...]     Reset maintenance state for all, one,\n"
@@ -3933,7 +4049,8 @@ static int halt_parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'p':
-                        arg_action = ACTION_POWEROFF;
+                        if (arg_action != ACTION_REBOOT)
+                                arg_action = ACTION_POWEROFF;
                         break;
 
                 case ARG_REBOOT:
@@ -4209,8 +4326,22 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_action = ACTION_POWEROFF;
                         return shutdown_parse_argv(argc, argv);
                 } else if (strstr(program_invocation_short_name, "init")) {
-                        arg_action = ACTION_INVALID;
-                        return telinit_parse_argv(argc, argv);
+
+                        if (sd_booted() > 0) {
+                                arg_action = ACTION_INVALID;
+                                return telinit_parse_argv(argc, argv);
+                        } else {
+                                /* Hmm, so some other init system is
+                                 * running, we need to forward this
+                                 * request to it. For now we simply
+                                 * guess that it is Upstart. */
+
+                                execv("/lib/upstart/telinit", argv);
+
+                                log_error("Couldn't find an alternative telinit implementation to spawn.");
+                                return -EIO;
+                        }
+
                 } else if (strstr(program_invocation_short_name, "runlevel")) {
                         arg_action = ACTION_RUNLEVEL;
                         return runlevel_parse_argv(argc, argv);
@@ -4426,7 +4557,6 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
         int left;
         unsigned i;
 
-        assert(bus);
         assert(argc >= 0);
         assert(argv);
         assert(error);
@@ -4495,11 +4625,10 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
 }
 
 static int reload_with_fallback(DBusConnection *bus) {
-        int r;
 
         if (bus) {
                 /* First, try systemd via D-Bus. */
-                if ((r = daemon_reload(bus, NULL, 0)) > 0)
+                if (daemon_reload(bus, NULL, 0) > 0)
                         return 0;
         }
 
@@ -4515,23 +4644,21 @@ static int reload_with_fallback(DBusConnection *bus) {
 }
 
 static int start_with_fallback(DBusConnection *bus) {
-        int r;
-
 
         if (bus) {
                 /* First, try systemd via D-Bus. */
-                if ((r = start_unit(bus, NULL, 0)) > 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)
+                if (start_unit(bus, NULL, 0) > 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 (talk_upstart() > 0)
+                goto done;
+
         /* Nothing else worked, so let's try
          * /dev/initctl */
-        if ((r = talk_initctl()) != 0)
+        if (talk_initctl() > 0)
                 goto done;
 
         log_error("Failed to talk to init daemon.");
@@ -4570,17 +4697,17 @@ static int halt_main(DBusConnection *bus) {
         switch (arg_action) {
 
         case ACTION_HALT:
-                log_info("Halting");
+                log_info("Halting.");
                 reboot(RB_HALT_SYSTEM);
                 break;
 
         case ACTION_POWEROFF:
-                log_info("Powering off");
+                log_info("Powering off.");
                 reboot(RB_POWER_OFF);
                 break;
 
         case ACTION_REBOOT:
-                log_info("Rebooting");
+                log_info("Rebooting.");
                 reboot(RB_AUTOBOOT);
                 break;
 
@@ -4596,7 +4723,7 @@ static int runlevel_main(void) {
         int r, runlevel, previous;
 
         if ((r = utmp_get_runlevel(&runlevel, &previous)) < 0) {
-                printf("unknown");
+                printf("unknown\n");
                 return r;
         }