X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fsystemctl%2Fsystemctl.c;h=1ab27336d7097fc05c12cd8ef367f3871cd2a142;hp=2e1863675f7f3ce3b58ebdb17bd5a0f02f3885bc;hb=d08e75edf9d44540d9744349fc0c47dfb4a7eec8;hpb=4561be3a64534a911ee405ffb51950a624a0cd3f diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 2e1863675..1ab27336d 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" @@ -70,6 +71,7 @@ #include "bus-util.h" #include "bus-message.h" #include "bus-error.h" +#include "bus-errors.h" static char **arg_types = NULL; static char **arg_states = NULL; @@ -81,6 +83,7 @@ static enum dependency { DEPENDENCY_REVERSE, DEPENDENCY_AFTER, DEPENDENCY_BEFORE, + _DEPENDENCY_MAX } arg_dependency = DEPENDENCY_FORWARD; static const char *arg_job_mode = "replace"; static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; @@ -136,6 +139,14 @@ static bool arg_plain = false; static int daemon_reload(sd_bus *bus, char **args); 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) @@ -194,7 +205,7 @@ static int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) return EXIT_NOTINSTALLED; if (sd_bus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) || - sd_bus_error_has_name(error, BUS_ERROR_NOT_SUPPORTED)) + sd_bus_error_has_name(error, SD_BUS_ERROR_NOT_SUPPORTED)) return EXIT_NOTIMPLEMENTED; if (sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) @@ -276,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)) @@ -285,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; @@ -305,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)); @@ -354,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) { @@ -500,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; } @@ -689,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")) @@ -906,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")) @@ -976,16 +996,25 @@ static int compare_unit_file_list(const void *a, const void *b) { return r; } - return strcasecmp(path_get_file_name(u->path), path_get_file_name(v->path)); + 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; @@ -993,10 +1022,10 @@ 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(path_get_file_name(u->path))); + max_id_len = MAX(max_id_len, strlen(basename(u->path))); state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state))); } @@ -1020,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++; @@ -1037,7 +1066,7 @@ static void output_unit_file_list(const UnitFileList *units, unsigned c) { } else on = off = ""; - id = path_get_file_name(u->path); + id = basename(u->path); e = arg_full ? NULL : ellipsize(id, id_cols, 33); @@ -1133,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; @@ -1149,7 +1178,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra for (i = level - 1; i >= 0; i--) { len += 2; - if(len > max_len - 3 && !arg_full) { + if (len > max_len - 3 && !arg_full) { printf("%s...\n",max_len % 2 ? "" : " "); return 0; } @@ -1157,7 +1186,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra } len += 2; - if(len > max_len - 3 && !arg_full) { + if (len > max_len - 3 && !arg_full) { printf("%s...\n",max_len % 2 ? "" : " "); return 0; } @@ -1171,7 +1200,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra } n = ellipsize(name, max_len-len, 100); - if(!n) + if (!n) return log_oom(); printf("%s\n", n); @@ -1180,7 +1209,7 @@ static int list_dependencies_print(const char *name, int level, unsigned int bra static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) { - static const char *dependencies[] = { + static const char *dependencies[_DEPENDENCY_MAX] = { [DEPENDENCY_FORWARD] = "Requires\0" "RequiresOverridable\0" "Requisite\0" @@ -1203,7 +1232,7 @@ static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, cha assert(bus); assert(name); assert(deps); - assert(arg_dependency < ELEMENTSOF(dependencies)); + assert_cc(ELEMENTSOF(dependencies) == _DEPENDENCY_MAX); path = unit_dbus_path_from_name(name); if (!path) @@ -1308,6 +1337,8 @@ static int list_dependencies_one( qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); STRV_FOREACH(c, deps) { + int state; + if (strv_contains(u, *c)) { if (!arg_plain) { r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); @@ -1317,13 +1348,19 @@ static int list_dependencies_one( continue; } + state = check_one_unit(bus, *c, "activating\0active\0reloading\0", true); + if (state > 0) + printf("%s%s%s", ansi_highlight_green(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off()); + else + printf("%s%s%s", ansi_highlight_red(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off()); + r = list_dependencies_print(*c, level, branches, c[1] == NULL); if (r < 0) return r; if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) { r = list_dependencies_one(bus, *c, level + 1, &u, (branches << 1) | (c[1] == NULL ? 0 : 1)); - if(r < 0) + if (r < 0) return r; } } @@ -1400,12 +1437,106 @@ static int get_default(sd_bus *bus, char **args) { return 0; } +static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_changes) { + unsigned i; + + assert(changes || n_changes == 0); + + for (i = 0; i < n_changes; i++) { + if (changes[i].type == UNIT_FILE_SYMLINK) + log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path); + else + log_info("rm '%s'", changes[i].path); + } +} + +static int deserialize_and_dump_unit_file_changes(sd_bus_message *m) { + const char *type, *path, *source; + int r; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) { + if (!arg_quiet) { + if (streq(type, "symlink")) + log_info("ln -s '%s' '%s'", source, path); + else + log_info("rm '%s'", path); + } + } + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + return 0; +} + +static int set_default(sd_bus *bus, char **args) { + _cleanup_free_ char *unit = NULL; + UnitFileChange *changes = NULL; + unsigned n_changes = 0; + int r; + + unit = unit_name_mangle_with_suffix(args[1], ".target"); + if (!unit) + return log_oom(); + + if (!bus || avoid_bus()) { + r = unit_file_set_default(arg_scope, arg_root, unit, arg_force, &changes, &n_changes); + if (r < 0) { + log_error("Failed to set default target: %s", strerror(-r)); + return r; + } + + if (!arg_quiet) + dump_unit_file_changes(changes, n_changes); + + r = 0; + } else { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "SetDefaultTarget", + &error, + &reply, + "sb", unit, arg_force); + if (r < 0) { + log_error("Failed to set default target: %s", bus_error_message(&error, -r)); + return r; + } + + r = deserialize_and_dump_unit_file_changes(reply); + if (r < 0) + return r; + + /* Try to reload if enabeld */ + if (!arg_no_reload) + r = daemon_reload(bus, args); + else + r = 0; + } + + unit_file_changes_free(changes, n_changes); + + return r; +} + struct job_info { uint32_t id; 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; @@ -1417,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; } @@ -1475,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; @@ -1484,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, @@ -1504,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); @@ -1522,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; } @@ -1616,7 +1762,7 @@ typedef struct WaitData { char *result; } WaitData; -static int wait_filter(sd_bus *bus, sd_bus_message *m, void *data) { +static int wait_filter(sd_bus *bus, sd_bus_message *m, void *data, sd_bus_error *error) { WaitData *d = data; assert(bus); @@ -1669,7 +1815,7 @@ static int wait_filter(sd_bus *bus, sd_bus_message *m, void *data) { } #endif - log_error("Failed to parse message."); + bus_log_parse_error(r); } return 0; @@ -1697,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) { @@ -2039,7 +2211,7 @@ static int start_unit(sd_bus *bus, char **args) { q = start_unit_one(bus, method, *name, mode, &error, s); if (q < 0) { - r = translate_bus_error_to_exit_status(r, &error); + r = translate_bus_error_to_exit_status(q, &error); sd_bus_error_free(&error); } } @@ -2582,7 +2754,7 @@ static void print_status_info( last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir)); - printf("%s%s", path_get_file_name(*dropin), last ? "\n" : ", "); + printf("%s%s", basename(*dropin), last ? "\n" : ", "); } } @@ -3499,16 +3671,13 @@ static int show_one( return r; } -static int show_one_by_pid( - const char *verb, +static int get_unit_dbus_path_by_pid( sd_bus *bus, uint32_t pid, - bool *new_line, - bool *ellipsized) { + char **unit) { _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; - const char *path = NULL; int r; r = sd_bus_call_method( @@ -3525,11 +3694,11 @@ static int show_one_by_pid( return r; } - r = sd_bus_message_read(reply, "o", &path); + r = sd_bus_message_read(reply, "o", unit); if (r < 0) return bus_log_parse_error(r); - return show_one(verb, bus, path, false, new_line, ellipsized); + return 0; } static int show_all( @@ -3549,6 +3718,8 @@ static int show_all( if (r < 0) return r; + pager_open_if_enabled(); + c = (unsigned) r; qsort_safe(unit_infos, c, sizeof(UnitInfo), compare_unit_info); @@ -3556,15 +3727,13 @@ 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); if (!p) return log_oom(); - printf("%s -> '%s'\n", u->id, p); - r = show_one(verb, bus, p, show_properties, new_line, ellipsized); if (r != 0) return r; @@ -3573,6 +3742,87 @@ static int show_all( return 0; } +static int cat(sd_bus *bus, char **args) { + int r = 0; + char **name; + + _cleanup_free_ char *unit = NULL, *n = NULL; + + assert(bus); + assert(args); + + pager_open_if_enabled(); + + STRV_FOREACH(name, args+1) { + _cleanup_free_ char *fragment_path = NULL; + _cleanup_strv_free_ char **dropin_paths = NULL; + sd_bus_error error; + char **path; + + n = unit_name_mangle(*name); + if (!n) + return log_oom(); + + unit = unit_dbus_path_from_name(n); + if (!unit) + return log_oom(); + + if (need_daemon_reload(bus, n) > 0) + log_warning("Unit file of %s changed on disk. Run 'systemctl%s daemon-reload'.", + n, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "FragmentPath", + &error, + &fragment_path); + if (r < 0) { + log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r)); + continue; + } + + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "DropInPaths", + &error, + &dropin_paths); + if (r < 0) { + log_warning("Failed to get DropInPaths: %s", bus_error_message(&error, r)); + continue; + } + + if (!isempty(fragment_path)) { + fprintf(stdout, "# %s\n", fragment_path); + fflush(stdout); + r = sendfile_full(STDOUT_FILENO, fragment_path); + if (r < 0) { + log_warning("Failed to cat %s: %s", fragment_path, strerror(-r)); + continue; + } + } + + STRV_FOREACH(path, dropin_paths) { + fprintf(stdout, "%s# %s\n", + isempty(fragment_path) && path == dropin_paths ? "" : "\n", + *path); + fflush(stdout); + r = sendfile_full(STDOUT_FILENO, *path); + if (r < 0) { + log_warning("Failed to cat %s: %s", *path, strerror(-r)); + continue; + } + } + } + + return r; +} + static int show(sd_bus *bus, char **args) { int r, ret = 0; bool show_properties, show_status, new_line = false; @@ -3597,41 +3847,34 @@ static int show(sd_bus *bus, char **args) { ret = show_all(args[0], bus, false, &new_line, &ellipsized); else STRV_FOREACH(name, args+1) { + _cleanup_free_ char *unit = NULL; uint32_t id; if (safe_atou32(*name, &id) < 0) { - _cleanup_free_ char *p = NULL, *n = NULL; + _cleanup_free_ char *n = NULL; /* Interpret as unit name */ n = unit_name_mangle(*name); if (!n) return log_oom(); - p = unit_dbus_path_from_name(n); - if (!p) + unit = unit_dbus_path_from_name(n); + if (!unit) return log_oom(); - r = show_one(args[0], bus, p, show_properties, &new_line, &ellipsized); - if (r != 0) - ret = r; - } else if (show_properties) { - _cleanup_free_ char *p = NULL; - /* Interpret as job id */ - if (asprintf(&p, "/org/freedesktop/systemd1/job/%u", id) < 0) + if (asprintf(&unit, "/org/freedesktop/systemd1/job/%u", id) < 0) return log_oom(); - r = show_one(args[0], bus, p, show_properties, &new_line, &ellipsized); - if (r != 0) - ret = r; - } else { /* Interpret as PID */ - r = show_one_by_pid(args[0], bus, id, &new_line, &ellipsized); - if (r != 0) + r = get_unit_dbus_path_by_pid(bus, id, &unit); + if (r < 0) ret = r; } + + show_one(args[0], bus, unit, show_properties, &new_line, &ellipsized); } if (ellipsized && !arg_quiet) @@ -4227,7 +4470,7 @@ static int enable_sysv_units(const char *verb, char **args) { if (!isempty(arg_root)) argv[c++] = q = strappend("--root=", arg_root); - argv[c++] = path_get_file_name(p); + argv[c++] = basename(p); argv[c++] = streq(verb, "enable") ? "on" : streq(verb, "disable") ? "off" : "--level=5"; @@ -4331,12 +4574,10 @@ static int mangle_names(char **original_names, char ***mangled_names) { } static int enable_unit(sd_bus *bus, char **args) { - _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; - _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_strv_free_ char **mangled_names = NULL; const char *verb = args[0]; UnitFileChange *changes = NULL; - unsigned n_changes = 0, i; + unsigned n_changes = 0; int carries_install_info = -1; int r; @@ -4369,8 +4610,6 @@ static int enable_unit(sd_bus *bus, char **args) { r = unit_file_mask(arg_scope, arg_runtime, arg_root, mangled_names, arg_force, &changes, &n_changes); else if (streq(verb, "unmask")) r = unit_file_unmask(arg_scope, arg_runtime, arg_root, mangled_names, &changes, &n_changes); - else if (streq(verb, "set-default")) - r = unit_file_set_default(arg_scope, arg_root, args[1], &changes, &n_changes); else assert_not_reached("Unknown verb"); @@ -4379,20 +4618,16 @@ static int enable_unit(sd_bus *bus, char **args) { goto finish; } - if (!arg_quiet) { - for (i = 0; i < n_changes; i++) { - if (changes[i].type == UNIT_FILE_SYMLINK) - log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path); - else - log_info("rm '%s'", changes[i].path); - } - } + if (!arg_quiet) + dump_unit_file_changes(changes, n_changes); r = 0; } else { - const char *method, *type, *path, *source; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL; + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; int expect_carries_install_info = false; bool send_force = true; + const char *method; if (streq(verb, "enable")) { method = "EnableUnitFiles"; @@ -4413,8 +4648,6 @@ static int enable_unit(sd_bus *bus, char **args) { else if (streq(verb, "unmask")) { method = "UnmaskUnitFiles"; send_force = false; - } else if (streq(verb, "set-default")) { - method = "SetDefaultTarget"; } else assert_not_reached("Unknown verb"); @@ -4454,24 +4687,9 @@ static int enable_unit(sd_bus *bus, char **args) { return bus_log_parse_error(r); } - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(sss)"); + r = deserialize_and_dump_unit_file_changes(reply); if (r < 0) - return bus_log_parse_error(r); - - while ((r = sd_bus_message_read(reply, "(sss)", &type, &path, &source)) > 0) { - if (!arg_quiet) { - if (streq(type, "symlink")) - log_info("ln -s '%s' '%s'", source, path); - else - log_info("rm '%s'", path); - } - } - if (r < 0) - return bus_log_parse_error(r); - - r = sd_bus_message_exit_container(reply); - if (r < 0) - return bus_log_parse_error(r); + return r; /* Try to reload if enabeld */ if (!arg_no_reload) @@ -4591,14 +4809,10 @@ static int systemctl_help(void) { " -a --all Show all loaded units/properties, including dead/empty\n" " ones. To list all units installed on the system, use\n" " the 'list-unit-files' command instead.\n" - " --reverse Show reverse dependencies with 'list-dependencies'\n" " -l --full Don't ellipsize unit names on output\n" - " --fail When queueing a new job, fail if conflicting jobs are\n" - " pending\n" - " --irreversible When queueing a new job, make sure it cannot be implicitly\n" - " cancelled\n" - " --ignore-dependencies\n" - " When queueing a new job, ignore all its dependencies\n" + " --reverse Show reverse dependencies with 'list-dependencies'\n" + " --job-mode=MODE Specify how to deal with already queued jobs, when\n" + " queueing a new job\n" " --show-types When showing sockets, explicitly show their type\n" " -i --ignore-inhibitors\n" " When shutting down or sleeping, ignore inhibitors\n" @@ -4622,57 +4836,57 @@ 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" - " 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" - " restart [NAME...] Start or restart one or more units\n" - " try-restart [NAME...] Restart one or more units if active\n" - " reload-or-restart [NAME...] Reload one or more units if possible,\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" + " restart NAME... Start or restart one or more units\n" + " try-restart NAME... Restart one or more units if active\n" + " reload-or-restart NAME... Reload one or more units if possible,\n" " otherwise start or restart\n" - " reload-or-try-restart [NAME...] Reload one or more units if possible,\n" + " reload-or-try-restart NAME... Reload one or more units if possible,\n" " otherwise restart if active\n" - " isolate [NAME] Start one unit and stop all others\n" - " kill [NAME...] Send signal to processes of a unit\n" - " is-active [NAME...] Check whether units are active\n" - " is-failed [NAME...] Check whether units are failed\n" + " isolate NAME Start one unit and stop all others\n" + " kill NAME... Send signal to processes of a unit\n" + " is-active NAME... Check whether units are active\n" + " is-failed NAME... Check whether units are failed\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" - " set-property [NAME] [ASSIGNMENT...]\n" - " Sets one or more properties of a unit\n" - " help [NAME...|PID...] Show manual for one or more units\n" + " cat NAME... Show files and drop-ins of one or more units\n" + " set-property NAME ASSIGNMENT... Sets one or more properties of a unit\n" + " help NAME...|PID... Show manual for one or more units\n" " reset-failed [NAME...] Reset failed state for all, one, or more\n" " units\n" " list-dependencies [NAME] Recursively show units which are required\n" " 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" - " 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" - " preset [NAME...] Enable/disable one or more 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" + " preset NAME... Enable/disable one or more unit files\n" " based on preset configuration\n" - " is-enabled [NAME...] Check whether unit files are enabled\n\n" - " mask [NAME...] Mask one or more units\n" - " unmask [NAME...] Unmask one or more units\n" - " link [PATH...] Link one or more units files into\n" + " is-enabled NAME... Check whether unit files are enabled\n\n" + " mask NAME... Mask one or more units\n" + " unmask NAME... Unmask one or more units\n" + " link PATH... Link one or more units files into\n" " the search path\n" " 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" - " delete [NAME...] Remove one or more snapshots\n\n" + " delete NAME... Remove one or more snapshots\n\n" "Environment Commands:\n" " show-environment Dump environment\n" - " set-environment [NAME=VALUE...] Set one or more environment variables\n" - " unset-environment [NAME...] Unset one or more environment variables\n\n" + " set-environment NAME=VALUE... Set one or more environment variables\n" + " unset-environment NAME... Unset one or more environment variables\n\n" "Manager Lifecycle Commands:\n" " daemon-reload Reload systemd manager configuration\n" " daemon-reexec Reexecute systemd manager\n\n" @@ -4685,7 +4899,7 @@ static int systemctl_help(void) { " reboot [ARG] Shut down and reboot the system\n" " kexec Shut down and reboot the system with kexec\n" " exit Request user instance exit\n" - " switch-root [ROOT] [INIT] Change to a different root file system\n" + " switch-root ROOT [INIT] Change to a different root file system\n" " suspend Suspend the system\n" " hibernate Hibernate the system\n" " hybrid-sleep Hibernate and suspend the system\n", @@ -4765,7 +4979,7 @@ static int help_types(void) { const char *t; puts("Available unit types:"); - for(i = 0; i < _UNIT_TYPE_MAX; i++) { + for (i = 0; i < _UNIT_TYPE_MAX; i++) { t = unit_type_to_string(i); if (t) puts(t); @@ -4800,7 +5014,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_RUNTIME, ARG_FORCE, ARG_PLAIN, - ARG_STATE + ARG_STATE, + ARG_JOB_MODE }; static const struct option options[] = { @@ -4815,9 +5030,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "show-types", no_argument, NULL, ARG_SHOW_TYPES }, { "failed", no_argument, NULL, ARG_FAILED }, /* compatibility only */ { "full", no_argument, NULL, 'l' }, - { "fail", no_argument, NULL, ARG_FAIL }, - { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, - { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, + { "job-mode", required_argument, NULL, ARG_JOB_MODE }, + { "fail", no_argument, NULL, ARG_FAIL }, /* compatibility only */ + { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, /* compatibility only */ + { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */ { "ignore-inhibitors", no_argument, NULL, 'i' }, { "user", no_argument, NULL, ARG_USER }, { "system", no_argument, NULL, ARG_SYSTEM }, @@ -4955,6 +5171,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_show_types = true; break; + case ARG_JOB_MODE: + arg_job_mode = optarg; + break; + case ARG_FAIL: arg_job_mode = "fail"; break; @@ -5593,12 +5813,16 @@ 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", 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, NOBUS }, + { "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 }, @@ -5618,6 +5842,7 @@ static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) { { "check", MORE, 2, check_unit_active }, { "is-failed", MORE, 2, check_unit_failed }, { "show", MORE, 1, show }, + { "cat", MORE, 2, cat }, { "status", MORE, 1, show }, { "help", MORE, 2, show }, { "snapshot", LESS, 2, snapshot }, @@ -5627,9 +5852,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 }, @@ -5639,53 +5864,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, enable_unit }, - { "get-default", LESS, 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; } @@ -5693,7 +5915,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; } @@ -5701,7 +5923,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; } @@ -5714,39 +5936,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) {