chiark / gitweb /
systemctl: drop uninteresting units immediately
[elogind.git] / src / systemctl / systemctl.c
index 2022f13233c55f34b8873d60b1ecaff9ef55e2d9..6c9fa763a756186841f86c088bf799041e812dec 100644 (file)
@@ -311,7 +311,7 @@ static bool output_show_unit(const UnitInfo *u, char **patterns) {
                               || u->following[0]) || u->job_id > 0);
 }
 
-static void output_units_list(const UnitInfo *unit_infos, unsigned c, char** patterns) {
+static void output_units_list(const UnitInfo *unit_infos, unsigned c) {
         unsigned id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len;
         const UnitInfo *u;
         unsigned n_shown = 0;
@@ -325,9 +325,6 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c, char** pat
         desc_len = 0;
 
         for (u = unit_infos; u < unit_infos + c; u++) {
-                if (!output_show_unit(u, patterns))
-                        continue;
-
                 max_id_len = MAX(max_id_len, strlen(u->id));
                 load_len = MAX(load_len, strlen(u->load_state));
                 active_len = MAX(active_len, strlen(u->active_state));
@@ -374,9 +371,6 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c, char** pat
                 const char *on_loaded, *off_loaded, *on = "";
                 const char *on_active, *off_active, *off = "";
 
-                if (!output_show_unit(u, patterns))
-                        continue;
-
                 if (!n_shown && !arg_no_legend) {
                         printf("%-*s %-*s %-*s %-*s ",
                                id_len, "UNIT",
@@ -454,7 +448,8 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c, char** pat
 static int get_unit_list(
                 sd_bus *bus,
                 sd_bus_message **_reply,
-                UnitInfo **_unit_infos) {
+                UnitInfo **_unit_infos,
+                char **patterns) {
 
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
@@ -486,6 +481,8 @@ static int get_unit_list(
                 return bus_log_parse_error(r);
 
         while ((r = bus_parse_unit_info(reply, &u)) > 0) {
+                if (!output_show_unit(&u, patterns))
+                        continue;
 
                 if (!GREEDY_REALLOC(unit_infos, size, c+1))
                         return log_oom();
@@ -515,12 +512,12 @@ static int list_units(sd_bus *bus, char **args) {
 
         pager_open_if_enabled();
 
-        r = get_unit_list(bus, &reply, &unit_infos);
+        r = get_unit_list(bus, &reply, &unit_infos, strv_skip_first(args));
         if (r < 0)
                 return r;
 
         qsort_safe(unit_infos, r, sizeof(UnitInfo), compare_unit_info);
-        output_units_list(unit_infos, r, strv_skip_first(args));
+        output_units_list(unit_infos, r);
 
         return 0;
 }
@@ -692,7 +689,7 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
 static int list_sockets(sd_bus *bus, char **args) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_free_ UnitInfo *unit_infos = NULL;
-        struct socket_info *socket_infos = NULL;
+        _cleanup_free_ struct socket_info *socket_infos = NULL;
         const UnitInfo *u;
         struct socket_info *s;
         unsigned cs = 0;
@@ -701,7 +698,7 @@ static int list_sockets(sd_bus *bus, char **args) {
 
         pager_open_if_enabled();
 
-        n = get_unit_list(bus, &reply, &unit_infos);
+        n = get_unit_list(bus, &reply, &unit_infos, strv_skip_first(args));
         if (n < 0)
                 return n;
 
@@ -709,9 +706,6 @@ static int list_sockets(sd_bus *bus, char **args) {
                 _cleanup_strv_free_ char **listening = NULL, **triggered = NULL;
                 int i, c;
 
-                if (!output_show_unit(u, strv_skip_first(args)))
-                        continue;
-
                 if (!endswith(u->id, ".socket"))
                         continue;
 
@@ -758,7 +752,6 @@ static int list_sockets(sd_bus *bus, char **args) {
                 if (s->own_triggered)
                         strv_free(s->triggered);
         }
-        free(socket_infos);
 
         return r;
 }
@@ -915,7 +908,7 @@ static int list_timers(sd_bus *bus, char **args) {
 
         pager_open_if_enabled();
 
-        n = get_unit_list(bus, &reply, &unit_infos);
+        n = get_unit_list(bus, &reply, &unit_infos, strv_skip_first(args));
         if (n < 0)
                 return n;
 
@@ -926,9 +919,6 @@ static int list_timers(sd_bus *bus, char **args) {
                 dual_timestamp next;
                 usec_t m;
 
-                if (!output_show_unit(u, strv_skip_first(args)))
-                        continue;
-
                 if (!endswith(u->id, ".timer"))
                         continue;
 
@@ -1014,7 +1004,7 @@ static bool output_show_unit_file(const UnitFileList *u, char **patterns) {
         return !arg_types || ((dot = strrchr(u->path, '.')) && strv_find(arg_types, dot+1));
 }
 
-static void output_unit_file_list(const UnitFileList *units, unsigned c, char **patterns) {
+static void output_unit_file_list(const UnitFileList *units, unsigned c) {
         unsigned max_id_len, id_cols, state_cols, n_shown = 0;
         const UnitFileList *u;
 
@@ -1022,9 +1012,6 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c, char **
         state_cols = sizeof("STATE")-1;
 
         for (u = units; u < units + c; u++) {
-                if (!output_show_unit_file(u, patterns))
-                        continue;
-
                 max_id_len = MAX(max_id_len, strlen(basename(u->path)));
                 state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
         }
@@ -1049,9 +1036,6 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c, char **
                 const char *on, *off;
                 const char *id;
 
-                if (!output_show_unit_file(u, patterns))
-                        continue;
-
                 n_shown++;
 
                 if (u->state == UNIT_FILE_MASKED ||
@@ -1083,6 +1067,8 @@ static int list_unit_files(sd_bus *bus, char **args) {
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_free_ UnitFileList *units = NULL;
+        UnitFileList *unit;
+        size_t size = 0;
         unsigned c = 0;
         const char *state;
         char *path;
@@ -1115,15 +1101,16 @@ static int list_unit_files(sd_bus *bus, char **args) {
                 }
 
                 HASHMAP_FOREACH(u, h, i) {
-                        memcpy(units + c++, u, sizeof(UnitFileList));
+                        if (!output_show_unit_file(u, strv_skip_first(args)))
+                                continue;
+
+                        units[c++] = *u;
                         free(u);
                 }
 
-                assert(c == n_units);
+                assert(c <= n_units);
                 hashmap_free(h);
         } else {
-                size_t size = 0;
-
                 r = sd_bus_call_method(
                                 bus,
                                 "org.freedesktop.systemd1",
@@ -1147,10 +1134,14 @@ static int list_unit_files(sd_bus *bus, char **args) {
                         if (!GREEDY_REALLOC(units, size, c + 1))
                                 return log_oom();
 
-                        units[c++] = (struct UnitFileList) {
+                        units[c] = (struct UnitFileList) {
                                 path,
                                 unit_file_state_from_string(state)
                         };
+
+                        if (output_show_unit_file(&units[c], strv_skip_first(args)))
+                                c ++;
+
                 }
                 if (r < 0)
                         return bus_log_parse_error(r);
@@ -1162,9 +1153,13 @@ static int list_unit_files(sd_bus *bus, char **args) {
 
         if (c > 0) {
                 qsort(units, c, sizeof(UnitFileList), compare_unit_file_list);
-                output_unit_file_list(units, c, strv_skip_first(args));
+                output_unit_file_list(units, c);
         }
 
+        if (avoid_bus())
+                for (unit = units; unit < units + c; unit++)
+                        free(unit->path);
+
         return 0;
 }
 
@@ -2146,9 +2141,10 @@ static enum action verb_to_action(const char *verb) {
 static int start_unit(sd_bus *bus, char **args) {
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_set_free_free_ Set *s = NULL;
-        const char *method, *mode, *one_name;
+        const char *method, *mode;
         char **name;
-        int r;
+        int r = 0;
+        char **names, *strv[] = {NULL, NULL}; /* at most one name */
 
         assert(bus);
 
@@ -2176,7 +2172,7 @@ static int start_unit(sd_bus *bus, char **args) {
                 mode = streq(args[0], "isolate") ? "isolate" :
                        action_table[action].mode ?: arg_job_mode;
 
-                one_name = action_table[action].target;
+                strv[0] = (char*) action_table[action].target;
         } else {
                 assert(arg_action < ELEMENTSOF(action_table));
                 assert(action_table[arg_action].target);
@@ -2184,9 +2180,14 @@ static int start_unit(sd_bus *bus, char **args) {
                 method = "StartUnit";
 
                 mode = action_table[arg_action].mode;
-                one_name = action_table[arg_action].target;
+                strv[0] = (char*) action_table[arg_action].target;
         }
 
+        if (strv[0])
+                names = strv;
+        else
+                names = args + 1;
+
         if (!arg_no_block) {
                 r = enable_wait_for_jobs(bus);
                 if (r < 0) {
@@ -2199,21 +2200,13 @@ static int start_unit(sd_bus *bus, char **args) {
                         return log_oom();
         }
 
-        if (one_name) {
-                r = start_unit_one(bus, method, one_name, mode, &error, s);
-                if (r < 0)
-                        r = translate_bus_error_to_exit_status(r, &error);
-        } else {
-                r = 0;
-
-                STRV_FOREACH(name, args+1) {
-                        int q;
+        STRV_FOREACH(name, names) {
+                int q;
 
-                        q = start_unit_one(bus, method, *name, mode, &error, s);
-                        if (q < 0) {
-                                r = translate_bus_error_to_exit_status(q, &error);
-                                sd_bus_error_free(&error);
-                        }
+                q = start_unit_one(bus, method, *name, mode, &error, s);
+                if (r == 0 && q < 0) {
+                        r = translate_bus_error_to_exit_status(q, &error);
+                        sd_bus_error_free(&error);
                 }
         }
 
@@ -2226,13 +2219,9 @@ static int start_unit(sd_bus *bus, char **args) {
 
                 /* When stopping units, warn if they can still be triggered by
                  * another active unit (socket, path, timer) */
-                if (!arg_quiet && streq(method, "StopUnit")) {
-                        if (one_name)
-                                check_triggering_units(bus, one_name);
-                        else
-                                STRV_FOREACH(name, args+1)
-                                        check_triggering_units(bus, *name);
-                }
+                if (!arg_quiet && streq(method, "StopUnit"))
+                        STRV_FOREACH(name, names)
+                                check_triggering_units(bus, *name);
         }
 
         return r;
@@ -3678,6 +3667,7 @@ static int get_unit_dbus_path_by_pid(
 
         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
+        char *u;
         int r;
 
         r = sd_bus_call_method(
@@ -3694,10 +3684,15 @@ static int get_unit_dbus_path_by_pid(
                 return r;
         }
 
-        r = sd_bus_message_read(reply, "o", unit);
+        r = sd_bus_message_read(reply, "o", &u);
         if (r < 0)
                 return bus_log_parse_error(r);
 
+        u = strdup(u);
+        if (!u)
+                return log_oom();
+
+        *unit = u;
         return 0;
 }
 
@@ -3714,7 +3709,7 @@ static int show_all(
         unsigned c;
         int r;
 
-        r = get_unit_list(bus, &reply, &unit_infos);
+        r = get_unit_list(bus, &reply, &unit_infos, NULL);
         if (r < 0)
                 return r;
 
@@ -3727,9 +3722,6 @@ static int show_all(
         for (u = unit_infos; u < unit_infos + c; u++) {
                 _cleanup_free_ char *p = NULL;
 
-                if (!output_show_unit(u, NULL))
-                        continue;
-
                 p = unit_dbus_path_from_name(u->id);
                 if (!p)
                         return log_oom();
@@ -3743,10 +3735,10 @@ static int show_all(
 }
 
 static int cat(sd_bus *bus, char **args) {
+        _cleanup_free_ char *unit = NULL, *n = NULL;
         int r = 0;
         char **name;
-
-        _cleanup_free_ char *unit = NULL, *n = NULL;
+        bool first = true;
 
         assert(bus);
         assert(args);
@@ -3754,9 +3746,9 @@ static int cat(sd_bus *bus, char **args) {
         pager_open_if_enabled();
 
         STRV_FOREACH(name, args+1) {
-                _cleanup_free_ char *fragment_path = NULL;
+                _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
                 _cleanup_strv_free_ char **dropin_paths = NULL;
-                sd_bus_error error;
+                _cleanup_free_ char *fragment_path = NULL;
                 char **path;
 
                 n = unit_name_mangle(*name);
@@ -3797,9 +3789,18 @@ static int cat(sd_bus *bus, char **args) {
                         continue;
                 }
 
+                if (first)
+                        first = false;
+                else
+                        puts("");
+
                 if (!isempty(fragment_path)) {
-                        fprintf(stdout, "# %s\n", fragment_path);
+                        printf("%s# %s%s\n",
+                               ansi_highlight_blue(),
+                               fragment_path,
+                               ansi_highlight_off());
                         fflush(stdout);
+
                         r = sendfile_full(STDOUT_FILENO, fragment_path);
                         if (r < 0) {
                                 log_warning("Failed to cat %s: %s", fragment_path, strerror(-r));
@@ -3808,10 +3809,13 @@ static int cat(sd_bus *bus, char **args) {
                 }
 
                 STRV_FOREACH(path, dropin_paths) {
-                        fprintf(stdout,   "%s# %s\n",
-                                isempty(fragment_path) && path == dropin_paths ? "" : "\n",
-                                *path);
+                        printf("%s%s# %s%s\n",
+                               isempty(fragment_path) && path == dropin_paths ? "" : "\n",
+                               ansi_highlight_blue(),
+                               *path,
+                               ansi_highlight_off());
                         fflush(stdout);
+
                         r = sendfile_full(STDOUT_FILENO, *path);
                         if (r < 0) {
                                 log_warning("Failed to cat %s: %s", *path, strerror(-r));
@@ -3820,7 +3824,7 @@ static int cat(sd_bus *bus, char **args) {
                 }
         }
 
-        return r;
+        return r < 0 ? r : 0;
 }
 
 static int show(sd_bus *bus, char **args) {
@@ -3870,8 +3874,10 @@ static int show(sd_bus *bus, char **args) {
                         } else {
                                 /* Interpret as PID */
                                 r = get_unit_dbus_path_by_pid(bus, id, &unit);
-                                if (r < 0)
+                                if (r < 0) {
                                         ret = r;
+                                        continue;
+                                }
                         }
 
                         show_one(args[0], bus, unit, show_properties, &new_line, &ellipsized);
@@ -4766,7 +4772,7 @@ static int unit_is_enabled(sd_bus *bus, char **args) {
                                         "GetUnitFileState",
                                         &error,
                                         &reply,
-                                        "s", name);
+                                        "s", *name);
                         if (r < 0) {
                                 log_error("Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
                                 return r;
@@ -4834,7 +4840,8 @@ static int systemctl_help(void) {
                "     --root=PATH      Enable unit files in the specified root directory\n"
                "  -n --lines=INTEGER  Number of journal entries to show\n"
                "  -o --output=STRING  Change journal output mode (short, short-monotonic,\n"
-               "                      verbose, export, json, json-pretty, json-sse, cat)\n\n"
+               "                      verbose, export, json, json-pretty, json-sse, cat)\n"
+               "     --plain          Print unit dependencies as a list instead of a tree\n\n"
                "Unit Commands:\n"
                "  list-units [PATTERN...]         List loaded units\n"
                "  list-sockets [PATTERN...]       List loaded sockets ordered by address\n"
@@ -5813,9 +5820,13 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 } argc_cmp;
                 const int argc;
                 int (* const dispatch)(sd_bus *bus, char **args);
+                const enum {
+                        NOBUS = 1,
+                        FORCE,
+                } bus;
         } verbs[] = {
                 { "list-units",            MORE,  0, list_units        },
-                { "list-unit-files",       MORE,  1, list_unit_files   },
+                { "list-unit-files",       MORE,  1, list_unit_files,  NOBUS },
                 { "list-sockets",          MORE,  1, list_sockets      },
                 { "list-timers",           MORE,  1, list_timers       },
                 { "list-jobs",             MORE,  1, list_jobs         },
@@ -5848,9 +5859,9 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "show-environment",      EQUAL, 1, show_environment  },
                 { "set-environment",       MORE,  2, set_environment   },
                 { "unset-environment",     MORE,  2, set_environment   },
-                { "halt",                  EQUAL, 1, start_special     },
-                { "poweroff",              EQUAL, 1, start_special     },
-                { "reboot",                EQUAL, 1, start_special     },
+                { "halt",                  EQUAL, 1, start_special,    FORCE },
+                { "poweroff",              EQUAL, 1, start_special,    FORCE },
+                { "reboot",                EQUAL, 1, start_special,    FORCE },
                 { "kexec",                 EQUAL, 1, start_special     },
                 { "suspend",               EQUAL, 1, start_special     },
                 { "hibernate",             EQUAL, 1, start_special     },
@@ -5860,53 +5871,50 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 { "emergency",             EQUAL, 1, start_special     },
                 { "exit",                  EQUAL, 1, start_special     },
                 { "reset-failed",          MORE,  1, reset_failed      },
-                { "enable",                MORE,  2, enable_unit       },
-                { "disable",               MORE,  2, enable_unit       },
-                { "is-enabled",            MORE,  2, unit_is_enabled   },
-                { "reenable",              MORE,  2, enable_unit       },
-                { "preset",                MORE,  2, enable_unit       },
-                { "mask",                  MORE,  2, enable_unit       },
-                { "unmask",                MORE,  2, enable_unit       },
-                { "link",                  MORE,  2, enable_unit       },
+                { "enable",                MORE,  2, enable_unit,      NOBUS },
+                { "disable",               MORE,  2, enable_unit,      NOBUS },
+                { "is-enabled",            MORE,  2, unit_is_enabled,  NOBUS },
+                { "reenable",              MORE,  2, enable_unit,      NOBUS },
+                { "preset",                MORE,  2, enable_unit,      NOBUS },
+                { "mask",                  MORE,  2, enable_unit,      NOBUS },
+                { "unmask",                MORE,  2, enable_unit,      NOBUS },
+                { "link",                  MORE,  2, enable_unit,      NOBUS },
                 { "switch-root",           MORE,  2, switch_root       },
                 { "list-dependencies",     LESS,  2, list_dependencies },
-                { "set-default",           EQUAL, 2, set_default       },
-                { "get-default",           EQUAL, 1, get_default       },
+                { "set-default",           EQUAL, 2, set_default,      NOBUS },
+                { "get-default",           EQUAL, 1, get_default,      NOBUS },
                 { "set-property",          MORE,  3, set_property      },
-        };
+                {}
+        }, *verb = verbs;
 
         int left;
-        unsigned i;
 
         assert(argc >= 0);
         assert(argv);
 
         left = argc - optind;
 
-        if (left <= 0)
-                /* Special rule: no arguments means "list-units" */
-                i = 0;
-        else {
+        /* Special rule: no arguments (left == 0) means "list-units" */
+        if (left > 0) {
                 if (streq(argv[optind], "help") && !argv[optind+1]) {
                         log_error("This command expects one or more "
                                   "unit names. Did you mean --help?");
                         return -EINVAL;
                 }
 
-                for (i = 0; i < ELEMENTSOF(verbs); i++)
-                        if (streq(argv[optind], verbs[i].verb))
-                                break;
+                for (; verb->verb; verb++)
+                        if (streq(argv[optind], verb->verb))
+                                goto found;
 
-                if (i >= ELEMENTSOF(verbs)) {
-                        log_error("Unknown operation '%s'.", argv[optind]);
-                        return -EINVAL;
-                }
+                log_error("Unknown operation '%s'.", argv[optind]);
+                return -EINVAL;
         }
+found:
 
-        switch (verbs[i].argc_cmp) {
+        switch (verb->argc_cmp) {
 
         case EQUAL:
-                if (left != verbs[i].argc) {
+                if (left != verb->argc) {
                         log_error("Invalid number of arguments.");
                         return -EINVAL;
                 }
@@ -5914,7 +5922,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 break;
 
         case MORE:
-                if (left < verbs[i].argc) {
+                if (left < verb->argc) {
                         log_error("Too few arguments.");
                         return -EINVAL;
                 }
@@ -5922,7 +5930,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
                 break;
 
         case LESS:
-                if (left > verbs[i].argc) {
+                if (left > verb->argc) {
                         log_error("Too many arguments.");
                         return -EINVAL;
                 }
@@ -5935,39 +5943,25 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
 
         /* Require a bus connection for all operations but
          * enable/disable */
-        if (!streq(verbs[i].verb, "enable") &&
-            !streq(verbs[i].verb, "disable") &&
-            !streq(verbs[i].verb, "is-enabled") &&
-            !streq(verbs[i].verb, "list-unit-files") &&
-            !streq(verbs[i].verb, "reenable") &&
-            !streq(verbs[i].verb, "preset") &&
-            !streq(verbs[i].verb, "mask") &&
-            !streq(verbs[i].verb, "unmask") &&
-            !streq(verbs[i].verb, "link") &&
-            !streq(verbs[i].verb, "set-default") &&
-            !streq(verbs[i].verb, "get-default")) {
+        if (verb->bus == NOBUS) {
+                if (!bus && !avoid_bus()) {
+                        log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
+                        return -EIO;
+                }
 
+        } else {
                 if (running_in_chroot() > 0) {
                         log_info("Running in chroot, ignoring request.");
                         return 0;
                 }
 
-                if (((!streq(verbs[i].verb, "reboot") &&
-                      !streq(verbs[i].verb, "halt") &&
-                      !streq(verbs[i].verb, "poweroff")) || arg_force <= 0) && !bus) {
-                        log_error("Failed to get D-Bus connection: %s", strerror (-bus_error));
-                        return -EIO;
-                }
-
-        } else {
-
-                if (!bus && !avoid_bus()) {
-                        log_error("Failed to get D-Bus connection: %s", strerror (-bus_error));
+                if ((verb->bus != FORCE || arg_force <= 0) && !bus) {
+                        log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
                         return -EIO;
                 }
         }
 
-        return verbs[i].dispatch(bus, argv + optind);
+        return verb->dispatch(bus, argv + optind);
 }
 
 static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {