X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=16dfddb8e5856610ed7fb01d525be300145ceb56;hb=4c49ab0e7a7f9f14f4308476257cd3548d0b4694;hp=c6ebf9b0113226f0a4b8dcd18073c745ff6d79b7;hpb=2b6bf07dd23bb467099d213c97b3875c5e453491;p=elogind.git diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index c6ebf9b01..16dfddb8e 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -37,6 +37,7 @@ #include #include #include +#include #include "sd-daemon.h" #include "sd-shutdown.h" @@ -140,6 +141,12 @@ static int halt_now(enum action a); static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet); +static char** strv_skip_first(char **strv) { + if (strv_length(strv) > 0) + return strv + 1; + return NULL; +} + static void pager_open_if_enabled(void) { if (arg_no_pager) @@ -280,7 +287,7 @@ static int compare_unit_info(const void *a, const void *b) { return strcasecmp(u->id, v->id); } -static bool output_show_unit(const UnitInfo *u) { +static bool output_show_unit(const UnitInfo *u, char **patterns) { const char *dot; if (!strv_isempty(arg_states)) @@ -289,13 +296,22 @@ static bool output_show_unit(const UnitInfo *u) { strv_contains(arg_states, u->sub_state) || strv_contains(arg_states, u->active_state); + if (!strv_isempty(patterns)) { + char **pattern; + + STRV_FOREACH(pattern, patterns) + if (fnmatch(*pattern, u->id, FNM_NOESCAPE) == 0) + return true; + return false; + } + return (!arg_types || ((dot = strrchr(u->id, '.')) && strv_find(arg_types, dot+1))) && (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0); } -static void output_units_list(const UnitInfo *unit_infos, unsigned c) { +static void output_units_list(const UnitInfo *unit_infos, unsigned c, char** patterns) { unsigned id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len; const UnitInfo *u; unsigned n_shown = 0; @@ -309,7 +325,7 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) { desc_len = 0; for (u = unit_infos; u < unit_infos + c; u++) { - if (!output_show_unit(u)) + if (!output_show_unit(u, patterns)) continue; max_id_len = MAX(max_id_len, strlen(u->id)); @@ -358,7 +374,7 @@ static void output_units_list(const UnitInfo *unit_infos, unsigned c) { const char *on_loaded, *off_loaded, *on = ""; const char *on_active, *off_active, *off = ""; - if (!output_show_unit(u)) + if (!output_show_unit(u, patterns)) continue; if (!n_shown && !arg_no_legend) { @@ -504,7 +520,7 @@ static int list_units(sd_bus *bus, char **args) { return r; qsort_safe(unit_infos, r, sizeof(UnitInfo), compare_unit_info); - output_units_list(unit_infos, r); + output_units_list(unit_infos, r, strv_skip_first(args)); return 0; } @@ -693,7 +709,7 @@ 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)) + if (!output_show_unit(u, strv_skip_first(args))) continue; if (!endswith(u->id, ".socket")) @@ -910,7 +926,7 @@ static int list_timers(sd_bus *bus, char **args) { dual_timestamp next; usec_t m; - if (!output_show_unit(u)) + if (!output_show_unit(u, strv_skip_first(args))) continue; if (!endswith(u->id, ".timer")) @@ -983,13 +999,22 @@ static int compare_unit_file_list(const void *a, const void *b) { return strcasecmp(basename(u->path), basename(v->path)); } -static bool output_show_unit_file(const UnitFileList *u) { +static bool output_show_unit_file(const UnitFileList *u, char **patterns) { const char *dot; + if (!strv_isempty(patterns)) { + char **pattern; + + STRV_FOREACH(pattern, patterns) + if (fnmatch(*pattern, basename(u->path), FNM_NOESCAPE) == 0) + return true; + return false; + } + return !arg_types || ((dot = strrchr(u->path, '.')) && strv_find(arg_types, dot+1)); } -static void output_unit_file_list(const UnitFileList *units, unsigned c) { +static void output_unit_file_list(const UnitFileList *units, unsigned c, char **patterns) { unsigned max_id_len, id_cols, state_cols, n_shown = 0; const UnitFileList *u; @@ -997,7 +1022,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { state_cols = sizeof("STATE")-1; for (u = units; u < units + c; u++) { - if (!output_show_unit_file(u)) + if (!output_show_unit_file(u, patterns)) continue; max_id_len = MAX(max_id_len, strlen(basename(u->path))); @@ -1024,7 +1049,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { const char *on, *off; const char *id; - if (!output_show_unit_file(u)) + if (!output_show_unit_file(u, patterns)) continue; n_shown++; @@ -1137,7 +1162,7 @@ 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); + output_unit_file_list(units, c, strv_skip_first(args)); } return 0; @@ -1511,7 +1536,7 @@ struct job_info { const char *name, *type, *state; }; -static void output_jobs_list(const struct job_info* jobs, unsigned n) { +static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipped) { unsigned id_len, unit_len, type_len, state_len; const struct job_info *j; const char *on, *off; @@ -1523,7 +1548,7 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n) { on = ansi_highlight_green(); off = ansi_highlight_off(); - printf("%sNo jobs running.%s\n", on, off); + printf("%sNo jobs %s.%s\n", on, skipped ? "listed" : "running", off); return; } @@ -1581,6 +1606,19 @@ static void output_jobs_list(const struct job_info* jobs, unsigned n) { } } +static bool output_show_job(struct job_info *job, char **patterns) { + if (!strv_isempty(patterns)) { + char **pattern; + + STRV_FOREACH(pattern, patterns) + if (fnmatch(*pattern, job->name, FNM_NOESCAPE) == 0) + return true; + return false; + } + + return true; +} + static int list_jobs(sd_bus *bus, char **args) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; @@ -1590,6 +1628,7 @@ static int list_jobs(sd_bus *bus, char **args) { unsigned c = 0; uint32_t id; int r; + bool skipped = false; r = sd_bus_call_method( bus, @@ -1610,16 +1649,17 @@ static int list_jobs(sd_bus *bus, char **args) { return bus_log_parse_error(r); while ((r = sd_bus_message_read(reply, "(usssoo)", &id, &name, &type, &state, &job_path, &unit_path)) > 0) { + struct job_info job = { id, name, type, state }; + + if (!output_show_job(&job, strv_skip_first(args))) { + skipped = true; + continue; + } if (!GREEDY_REALLOC(jobs, size, c + 1)) return log_oom(); - jobs[c++] = (struct job_info) { - id, - name, - type, - state - }; + jobs[c++] = job; } if (r < 0) return bus_log_parse_error(r); @@ -1628,7 +1668,7 @@ static int list_jobs(sd_bus *bus, char **args) { if (r < 0) return bus_log_parse_error(r); - output_jobs_list(jobs, c); + output_jobs_list(jobs, c, skipped); return r; } @@ -1803,59 +1843,85 @@ static int enable_wait_for_jobs(sd_bus *bus) { return 0; } +static int bus_process_wait(sd_bus *bus) { + int r; + + for (;;) { + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + if (r > 0) + return 0; + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) + return r; + } +} + +static int check_wait_response(WaitData *d) { + int r = 0; + + assert(d->result); + + if (!arg_quiet) { + if (streq(d->result, "timeout")) + log_error("Job for %s timed out.", strna(d->name)); + else if (streq(d->result, "canceled")) + log_error("Job for %s canceled.", strna(d->name)); + else if (streq(d->result, "dependency")) + log_error("A dependency job for %s failed. See 'journalctl -xn' for details.", strna(d->name)); + else if (!streq(d->result, "done") && !streq(d->result, "skipped")) + log_error("Job for %s failed. See 'systemctl status %s' and 'journalctl -xn' for details.", strna(d->name), strna(d->name)); + } + + if (streq(d->result, "timeout")) + r = -ETIME; + else if (streq(d->result, "canceled")) + r = -ECANCELED; + else if (streq(d->result, "dependency")) + r = -EIO; + else if (!streq(d->result, "done") && !streq(d->result, "skipped")) + r = -EIO; + + return r; +} + static int wait_for_jobs(sd_bus *bus, Set *s) { WaitData d = { .set = s }; - int r; + int r = 0, q; assert(bus); assert(s); - r = sd_bus_add_filter(bus, wait_filter, &d); - if (r < 0) + q = sd_bus_add_filter(bus, wait_filter, &d); + if (q < 0) return log_oom(); while (!set_isempty(s)) { - for (;;) { - r = sd_bus_process(bus, NULL); - if (r < 0) - return r; - if (r > 0) - break; - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) - return r; - } - - if (!d.result) - goto free_name; + q = bus_process_wait(bus); + if (q < 0) + return q; - if (!arg_quiet) { - if (streq(d.result, "timeout")) - log_error("Job for %s timed out.", strna(d.name)); - else if (streq(d.result, "canceled")) - log_error("Job for %s canceled.", strna(d.name)); - else if (streq(d.result, "dependency")) - log_error("A dependency job for %s failed. See 'journalctl -xn' for details.", strna(d.name)); - else if (!streq(d.result, "done") && !streq(d.result, "skipped")) - log_error("Job for %s failed. See 'systemctl status %s' and 'journalctl -xn' for details.", strna(d.name), strna(d.name)); + if (d.result) { + q = check_wait_response(&d); + /* Return the first error as it is most likely to be + * meaningful. */ + if (q < 0 && r == 0) + r = q; } - if (streq_ptr(d.result, "timeout")) - r = -ETIME; - else if (streq_ptr(d.result, "canceled")) - r = -ECANCELED; - else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped")) - r = -EIO; + free(d.name); + d.name = NULL; free(d.result); d.result = NULL; - - free_name: - free(d.name); - d.name = NULL; } - return sd_bus_remove_filter(bus, wait_filter, &d); + q = sd_bus_remove_filter(bus, wait_filter, &d); + if (q < 0 && r == 0) + r = q; + + return r; } static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet) { @@ -3661,7 +3727,7 @@ static int show_all( for (u = unit_infos; u < unit_infos + c; u++) { _cleanup_free_ char *p = NULL; - if (!output_show_unit(u)) + if (!output_show_unit(u, NULL)) continue; p = unit_dbus_path_from_name(u->id); @@ -4770,9 +4836,9 @@ static int systemctl_help(void) { " -o --output=STRING Change journal output mode (short, short-monotonic,\n" " verbose, export, json, json-pretty, json-sse, cat)\n\n" "Unit Commands:\n" - " list-units List loaded units\n" - " list-sockets List loaded sockets ordered by address\n" - " list-timers List loaded timers ordered by next elapse\n" + " list-units [PATTERN...] List loaded units\n" + " list-sockets [PATTERN...] List loaded sockets ordered by address\n" + " list-timers [PATTERN...] List loaded timers ordered by next elapse\n" " start [NAME...] Start (activate) one or more units\n" " stop [NAME...] Stop (deactivate) one or more units\n" " reload [NAME...] Reload one or more units\n" @@ -4799,7 +4865,7 @@ static int systemctl_help(void) { " or wanted by this unit or by which this\n" " unit is required or wanted\n\n" "Unit File Commands:\n" - " list-unit-files List installed unit files\n" + " list-unit-files [PATTERN...] List installed unit files\n" " enable [NAME...] Enable one or more unit files\n" " disable [NAME...] Disable one or more unit files\n" " reenable [NAME...] Reenable one or more unit files\n" @@ -4813,7 +4879,7 @@ static int systemctl_help(void) { " get-default Get the name of the default target\n" " set-default NAME Set the default target\n\n" "Job Commands:\n" - " list-jobs List jobs\n" + " list-jobs [PATTERN...] List jobs\n" " cancel [JOB...] Cancel all, one, or more jobs\n\n" "Snapshot Commands:\n" " snapshot [NAME] Create a snapshot\n" @@ -5749,11 +5815,11 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) { const int argc; int (* const dispatch)(sd_bus *bus, char **args); } verbs[] = { - { "list-units", LESS, 1, list_units }, - { "list-unit-files", EQUAL, 1, list_unit_files }, - { "list-sockets", LESS, 1, list_sockets }, - { "list-timers", LESS, 1, list_timers }, - { "list-jobs", EQUAL, 1, list_jobs }, + { "list-units", MORE, 0, list_units }, + { "list-unit-files", MORE, 1, list_unit_files }, + { "list-sockets", MORE, 1, list_sockets }, + { "list-timers", MORE, 1, list_timers }, + { "list-jobs", MORE, 1, list_jobs }, { "clear-jobs", EQUAL, 1, daemon_reload }, { "cancel", MORE, 2, cancel_job }, { "start", MORE, 2, start_unit },