chiark / gitweb /
systemctl: allow multiple arguments to --type
[elogind.git] / src / systemctl / systemctl.c
index 6a4c968f9857a441b72c1850eb812f479f60d78e..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;
 }
@@ -1193,14 +1278,11 @@ static int enable_wait_for_jobs(DBusConnection *bus) {
 
 static int wait_for_jobs(DBusConnection *bus, Set *s) {
         int r = 0;
-        WaitData d;
+        WaitData d = { .set = s };
 
         assert(bus);
         assert(s);
 
-        zero(d);
-        d.set = s;
-
         if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL))
                 return log_oom();
 
@@ -2349,7 +2431,7 @@ static void print_status_info(UnitStatusInfo *i) {
                                         return;
                                 }
 
-                                printf("%s\n %*s  %s ", dir, maxlen, "",
+                                printf("%s\n %*s  %s", dir, maxlen, "",
                                        draw_special_char(DRAW_TREE_RIGHT));
                         }
 
@@ -2864,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)) {
@@ -3021,9 +3103,8 @@ static int print_property(const char *name, DBusMessageIter *iter) {
 
                         dbus_message_iter_recurse(iter, &sub);
                         while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
-                                ExecStatusInfo info;
+                                ExecStatusInfo info = {};
 
-                                zero(info);
                                 if (exec_status_info_deserialize(&sub, &info) >= 0) {
                                         char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
                                         char _cleanup_free_ *t;
@@ -3070,14 +3151,12 @@ static int show_one(const char *verb, DBusConnection *bus, const char *path, boo
         const char *interface = "";
         int r;
         DBusMessageIter iter, sub, sub2, sub3;
-        UnitStatusInfo info;
+        UnitStatusInfo info = {};
         ExecStatusInfo *p;
 
         assert(path);
         assert(new_line);
 
-        zero(info);
-
         r = bus_method_call_with_reply(
                         bus,
                         "org.freedesktop.systemd1",
@@ -3654,7 +3733,7 @@ static int enable_sysv_units(char **args) {
 #if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG)
         const char *verb = args[0];
         unsigned f = 1, t = 1;
-        LookupPaths paths;
+        LookupPaths paths = {};
 
         if (arg_scope != UNIT_FILE_SYSTEM)
                 return 0;
@@ -3667,7 +3746,6 @@ static int enable_sysv_units(char **args) {
         /* Processes all SysV units, and reshuffles the array so that
          * afterwards only the native units remain */
 
-        zero(paths);
         r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, NULL, NULL, NULL);
         if (r < 0)
                 return r;
@@ -4426,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;
-
-                        FOREACH_WORD_SEPARATOR(word, size, source, ",", state) {
-                                char _cleanup_free_ *prop;
-                                char **tmp;
+                        if (isempty(optarg) && !arg_properties) {
+                                arg_properties = strv_new(NULL, NULL);
+                                if (!arg_properties)
+                                        return log_oom();
+                        } else {
+                                char *word, *state;
+                                size_t size;
 
-                                prop = strndup(word, size);
-                                if (!prop)
-                                        return -ENOMEM;
+                                FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
+                                        char *prop;
 
-                                tmp = strv_append(arg_property, prop);
-                                if (!tmp)
-                                        return -ENOMEM;
+                                        prop = strndup(word, size);
+                                        if (!prop)
+                                                return log_oom();
 
-                                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
@@ -4716,7 +4816,7 @@ static int parse_time_spec(const char *t, usec_t *_u) {
         } else {
                 char *e = NULL;
                 long hour, minute;
-                struct tm tm;
+                struct tm tm = {};
                 time_t s;
                 usec_t n;
 
@@ -4732,7 +4832,6 @@ static int parse_time_spec(const char *t, usec_t *_u) {
                 n = now(CLOCK_REALTIME);
                 s = (time_t) (n / USEC_PER_SEC);
 
-                zero(tm);
                 assert_se(localtime_r(&s, &tm));
 
                 tm.tm_hour = (int) hour;
@@ -5134,7 +5233,7 @@ finish:
 }
 
 static int talk_initctl(void) {
-        struct init_request request = {0};
+        struct init_request request = {};
         int r;
         int _cleanup_close_ fd = -1;
         char rl;
@@ -5339,41 +5438,38 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError
 
 static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
         int _cleanup_close_ fd;
-        struct msghdr msghdr;
-        struct iovec iovec[2];
-        union sockaddr_union sockaddr;
-        struct sd_shutdown_command c;
+        struct sd_shutdown_command c = {
+                .usec = t,
+                .mode = mode,
+                .dry_run = dry_run,
+                .warn_wall = warn,
+        };
+        union sockaddr_union sockaddr = {
+                .un.sun_family = AF_UNIX,
+                .un.sun_path = "/run/systemd/shutdownd",
+        };
+        struct iovec iovec[2] = {
+                {.iov_base = (char*) &c,
+                 .iov_len = offsetof(struct sd_shutdown_command, wall_message),
+                }
+        };
+        struct msghdr msghdr = {
+                .msg_name = &sockaddr,
+                .msg_namelen = offsetof(struct sockaddr_un, sun_path)
+                               + sizeof("/run/systemd/shutdownd") - 1,
+                .msg_iov = iovec,
+                .msg_iovlen = 1,
+        };
 
         fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
         if (fd < 0)
                 return -errno;
 
-        zero(c);
-        c.usec = t;
-        c.mode = mode;
-        c.dry_run = dry_run;
-        c.warn_wall = warn;
-
-        zero(sockaddr);
-        sockaddr.sa.sa_family = AF_UNIX;
-        strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
-
-        zero(msghdr);
-        msghdr.msg_name = &sockaddr;
-        msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
-
-        zero(iovec);
-        iovec[0].iov_base = (char*) &c;
-        iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
-
-        if (isempty(message))
-                msghdr.msg_iovlen = 1;
-        else {
+        if (!isempty(message)) {
                 iovec[1].iov_base = (char*) message;
                 iovec[1].iov_len = strlen(message);
-                msghdr.msg_iovlen = 2;
+                msghdr.msg_iovlen++;
         }
-        msghdr.msg_iov = iovec;
 
         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0)
                 return -errno;
@@ -5651,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();