chiark / gitweb /
systemctl: allow multiple arguments to --type
[elogind.git] / src / systemctl / systemctl.c
index fd9f580dc2e6438b4fe9222d44cee62c4ddd90c3..0e6087c62a278c4a0b72cd4d88a60756fa48723e 100644 (file)
@@ -68,9 +68,9 @@
 #include "socket-util.h"
 #include "fileio.h"
 
-static const char *arg_type = NULL;
-static const char *arg_load_state = NULL;
-static char **arg_property = NULL;
+static char **arg_types = NULL;
+static char **arg_load_states = NULL;
+static char **arg_properties = NULL;
 static bool arg_all = false;
 static const char *arg_job_mode = "replace";
 static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
@@ -295,9 +295,9 @@ static bool output_show_unit(const struct unit_info *u) {
         if (arg_failed)
                 return streq(u->active_state, "failed");
 
-        return (!arg_type || ((dot = strrchr(u->id, '.')) &&
-                              streq(dot+1, arg_type))) &&
-                (!arg_load_state || streq(u->load_state, arg_load_state)) &&
+        return (!arg_types || ((dot = strrchr(u->id, '.')) &&
+                               strv_find(arg_types, dot+1))) &&
+                (!arg_load_states || strv_find(arg_load_states, u->load_state)) &&
                 (arg_all || !(streq(u->active_state, "inactive")
                               || u->following[0]) || u->job_id > 0);
 }
@@ -524,7 +524,7 @@ static int compare_unit_file_list(const void *a, const void *b) {
 static bool output_show_unit_file(const UnitFileList *u) {
         const char *dot;
 
-        return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type));
+        return !arg_types || ((dot = strrchr(u->path, '.')) && strv_find(arg_types, dot+1));
 }
 
 static void output_unit_file_list(const UnitFileList *units, unsigned c) {
@@ -895,13 +895,84 @@ static int list_dependencies(DBusConnection *bus, char **args) {
         return list_dependencies_one(bus, u, 0, NULL, 0);
 }
 
+struct job_info {
+        uint32_t id;
+        char *name, *type, *state;
+};
+
+static void list_jobs_print(struct job_info* jobs, size_t n) {
+        size_t i;
+        struct job_info *j;
+        const char *on, *off;
+        bool shorten = false;
+
+        assert(n == 0 || jobs);
+
+        if (n == 0) {
+                on = ansi_highlight_green(true);
+                off = ansi_highlight_green(false);
+
+                printf("%sNo jobs running.%s\n", on, off);
+                return;
+        }
+
+        pager_open_if_enabled();
+
+        {
+                /* JOB UNIT TYPE STATE */
+                unsigned l0 = 3, l1 = 4, l2 = 4, l3 = 5;
+
+                for (i = 0, j = jobs; i < n; i++, j++) {
+                        assert(j->name && j->type && j->state);
+                        l0 = MAX(l0, decimal_str_max(j->id));
+                        l1 = MAX(l1, strlen(j->name));
+                        l2 = MAX(l2, strlen(j->type));
+                        l3 = MAX(l3, strlen(j->state));
+                }
+
+                if (!arg_full && l0 + 1 + l1 + l2 + 1 + l3 > columns()) {
+                        l1 = MAX(33u, columns() - l0 - l2 - l3 - 3);
+                        shorten = true;
+                }
+
+                if (on_tty())
+                        printf("%*s %-*s %-*s %-*s\n",
+                               l0, "JOB",
+                               l1, "UNIT",
+                               l2, "TYPE",
+                               l3, "STATE");
+
+                for (i = 0, j = jobs; i < n; i++, j++) {
+                        char _cleanup_free_ *e = NULL;
+
+                        if (streq(j->state, "running")) {
+                                on = ansi_highlight(true);
+                                off = ansi_highlight(false);
+                        } else
+                                on = off = "";
+
+                        e = shorten ? ellipsize(j->name, l1, 33) : NULL;
+                        printf("%*u %s%-*s%s %-*s %s%-*s%s\n",
+                               l0, j->id,
+                               on, l1, e ? e : j->name, off,
+                               l2, j->type,
+                               on, l3, j->state, off);
+                }
+        }
+
+        on = ansi_highlight(true);
+        off = ansi_highlight(false);
+
+        if (on_tty())
+                printf("\n%s%zu jobs listed%s.\n", on, n, off);
+}
+
 static int list_jobs(DBusConnection *bus, char **args) {
         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
         DBusMessageIter iter, sub, sub2;
-        unsigned k = 0;
         int r;
-
-        pager_open_if_enabled();
+        struct job_info *jobs = NULL;
+        size_t size = 0, used = 0;
 
         r = bus_method_call_with_reply(
                         bus,
@@ -924,13 +995,9 @@ static int list_jobs(DBusConnection *bus, char **args) {
 
         dbus_message_iter_recurse(&iter, &sub);
 
-        if (on_tty())
-                printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
-
         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
                 const char *name, *type, *state, *job_path, *unit_path;
                 uint32_t id;
-                char _cleanup_free_ *e = NULL;
 
                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
                         log_error("Failed to parse reply.");
@@ -946,19 +1013,37 @@ static int list_jobs(DBusConnection *bus, char **args) {
                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
                         log_error("Failed to parse reply.");
-                        return -EIO;
+                        r = -EIO;
+                        goto finish;
                 }
 
-                e = arg_full ? NULL : ellipsize(name, 25, 33);
-                printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state);
+                if (!greedy_realloc((void**) &jobs, &size,
+                                    sizeof(struct job_info) * (used + 1))) {
+                        r = log_oom();
+                        goto finish;
+                }
 
-                k++;
+                jobs[used++] = (struct job_info) { id,
+                                                   strdup(name),
+                                                   strdup(type),
+                                                   strdup(state) };
+                if (!jobs[used-1].name || !jobs[used-1].type || !jobs[used-1].state) {
+                        r = log_oom();
+                        goto finish;
+                }
 
                 dbus_message_iter_next(&sub);
         }
 
-        if (on_tty())
-                printf("\n%u jobs listed.\n", k);
+        list_jobs_print(jobs, used);
+
+ finish:
+        while (used--) {
+                free(jobs[used].name);
+                free(jobs[used].type);
+                free(jobs[used].state);
+        }
+        free(jobs);
 
         return 0;
 }
@@ -2861,7 +2946,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 && !strv_find(arg_property, name))
+        if (arg_properties && !strv_find(arg_properties, name))
                 return 0;
 
         switch (dbus_message_iter_get_arg_type(iter)) {
@@ -4419,45 +4504,67 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         puts(SYSTEMD_FEATURES);
                         return 0;
 
-                case 't':
-                        if (streq(optarg, "help")) {
-                                help_types();
-                                return 0;
-                        }
+                case 't': {
+                        char *word, *state;
+                        size_t size;
 
-                        if (unit_type_from_string(optarg) >= 0) {
-                                arg_type = optarg;
-                                break;
-                        }
-                        if (unit_load_state_from_string(optarg) >= 0) {
-                                arg_load_state = optarg;
-                                break;
+                        FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
+                                char _cleanup_free_ *type;
+
+                                type = strndup(word, size);
+                                if (!type)
+                                        return -ENOMEM;
+
+                                if (streq(type, "help")) {
+                                        help_types();
+                                        return 0;
+                                }
+
+                                if (unit_type_from_string(type) >= 0) {
+                                        if (strv_push(&arg_types, type))
+                                                return log_oom();
+                                        type = NULL;
+                                        continue;
+                                }
+
+                                if (unit_load_state_from_string(optarg) >= 0) {
+                                        if (strv_push(&arg_load_states, type))
+                                                return log_oom();
+                                        type = NULL;
+                                        continue;
+                                }
+
+                                log_error("Unkown unit type or load state '%s'.", type);
+                                log_info("Use -t help to see a list of allowed values.");
+                                return -EINVAL;
                         }
-                        log_error("Unkown unit type or load state '%s'.",
-                                  optarg);
-                        log_info("Use -t help to see a list of allowed values.");
-                        return -EINVAL;
+
+                        break;
+                }
+
                 case 'p': {
-                        char *word, *state;
-                        size_t size;
                         /* Make sure that if the empty property list
                            was specified, we won't show any properties. */
-                        const char *source = isempty(optarg) ? " " : optarg;
+                        if (isempty(optarg) && !arg_properties) {
+                                arg_properties = strv_new(NULL, NULL);
+                                if (!arg_properties)
+                                        return log_oom();
+                        } else {
+                                char *word, *state;
+                                size_t size;
 
-                        FOREACH_WORD_SEPARATOR(word, size, source, ",", state) {
-                                char _cleanup_free_ *prop;
-                                char **tmp;
+                                FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
+                                        char *prop;
 
-                                prop = strndup(word, size);
-                                if (!prop)
-                                        return -ENOMEM;
+                                        prop = strndup(word, size);
+                                        if (!prop)
+                                                return log_oom();
 
-                                tmp = strv_append(arg_property, prop);
-                                if (!tmp)
-                                        return -ENOMEM;
-
-                                strv_free(arg_property);
-                                arg_property = tmp;
+                                        if (strv_push(&arg_properties, prop)) {
+                                                free(prop);
+                                                return log_oom();
+                                        }
+                                }
                         }
 
                         /* If the user asked for a particular
@@ -5640,7 +5747,9 @@ finish:
 
         dbus_shutdown();
 
-        strv_free(arg_property);
+        strv_free(arg_types);
+        strv_free(arg_load_states);
+        strv_free(arg_properties);
 
         pager_close();
         ask_password_agent_close();