From 411975ce63b28194b21b964268efaa04b19cbb37 Mon Sep 17 00:00:00 2001 From: Kevin Kuehler Date: Tue, 10 Dec 2019 01:58:01 -0800 Subject: [PATCH 1/3] shared/bus-util: Don't replace exsting strv Change the behavior of string arrays in a bus property map. Previously, passing the same strv pointer to more than one map entry would result in the old strv being freed and overwritten. With this change, an existing strv pointer is appended to. This is important if we want to create one strv comprised of multiple dependencies. This makes it so callers don't have to create one strv per dependency and subsequently merge them into one strv. --- src/shared/bus-util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index aea46d3119..10c05eba18 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1127,7 +1127,7 @@ static int map_basic(sd_bus *bus, const char *member, sd_bus_message *m, unsigne if (r < 0) return r; - return strv_free_and_replace(*p, l); + return strv_extend_strv(p, l, false); } case SD_BUS_TYPE_BOOLEAN: { From e9c387c8293c57d1c773fc80d23239350eb3b370 Mon Sep 17 00:00:00 2001 From: Kevin Kuehler Date: Sun, 8 Dec 2019 15:23:27 -0800 Subject: [PATCH 2/3] systemctl: Add --with-dependencies flag Will print a unit and all of its dependencies. Works with cat, status, list-units, and list-unit-files. This flag can also be used in conjunction with --reverse, --before, and --after. We also vastly simplify the list_dependencies_get_dependencies logic. Instead of using 5 strvs and merging them into one, use one strv and have the bus append all the map values to it. Fixes #9273 --- src/systemctl/systemctl.c | 227 +++++++++++++++++++++++++------------- 1 file changed, 148 insertions(+), 79 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 6a0e59a4d7..0873045173 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -117,6 +117,7 @@ static bool arg_dry_run = false; static bool arg_quiet = false; static bool arg_full = false; static bool arg_recursive = false; +static bool arg_with_dependencies = false; static bool arg_show_transaction = false; static int arg_force = 0; static bool arg_ask_password = false; @@ -799,6 +800,107 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r return 0; } +static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***ret) { + _cleanup_strv_free_ char **deps = NULL; + + static const struct bus_properties_map map[_DEPENDENCY_MAX][6] = { + [DEPENDENCY_FORWARD] = { + { "Requires", "as", NULL, 0 }, + { "Requisite", "as", NULL, 0 }, + { "Wants", "as", NULL, 0 }, + { "ConsistsOf", "as", NULL, 0 }, + { "BindsTo", "as", NULL, 0 }, + {} + }, + [DEPENDENCY_REVERSE] = { + { "RequiredBy", "as", NULL, 0 }, + { "RequisiteOf", "as", NULL, 0 }, + { "WantedBy", "as", NULL, 0 }, + { "PartOf", "as", NULL, 0 }, + { "BoundBy", "as", NULL, 0 }, + {} + }, + [DEPENDENCY_AFTER] = { + { "After", "as", NULL, 0 }, + {} + }, + [DEPENDENCY_BEFORE] = { + { "Before", "as", NULL, 0 }, + {} + }, + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *dbus_path = NULL; + int r; + + assert(bus); + assert(name); + assert(ret); + + dbus_path = unit_dbus_path_from_name(name); + if (!dbus_path) + return log_oom(); + + r = bus_map_all_properties(bus, + "org.freedesktop.systemd1", + dbus_path, + map[arg_dependency], + BUS_MAP_STRDUP, + &error, + NULL, + &deps); + if (r < 0) + return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r)); + + *ret = TAKE_PTR(deps); + + return 0; +} + +static int append_unit_dependencies(sd_bus *bus, char **names, char ***ret) { + _cleanup_strv_free_ char **with_deps = NULL; + char **name; + + assert(bus); + assert(ret); + + STRV_FOREACH(name, names) { + _cleanup_strv_free_ char **deps = NULL; + + if (strv_extend(&with_deps, *name) < 0) + return log_oom(); + + (void) list_dependencies_get_dependencies(bus, *name, &deps); + + if (strv_extend_strv(&with_deps, deps, true) < 0) + return log_oom(); + } + + *ret = TAKE_PTR(with_deps); + + return 0; +} + +static int maybe_extend_with_unit_dependencies(sd_bus *bus, char ***list) { + assert(bus); + assert(list); + + if (arg_with_dependencies) { + int r; + _cleanup_strv_free_ char **list_with_deps = NULL; + + r = append_unit_dependencies(bus, *list, &list_with_deps); + if (r < 0) + return log_error_errno(r, "Failed to append unit dependencies: %m"); + + strv_free(*list); + *list = TAKE_PTR(list_with_deps); + } + + return 0; +} + static int list_units(int argc, char *argv[], void *userdata) { _cleanup_free_ UnitInfo *unit_infos = NULL; _cleanup_(message_set_freep) Set *replies = NULL; @@ -812,9 +914,21 @@ static int list_units(int argc, char *argv[], void *userdata) { (void) pager_open(arg_pager_flags); - r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); - if (r < 0) - return r; + if (arg_with_dependencies) { + _cleanup_strv_free_ char **names = NULL; + + r = append_unit_dependencies(bus, strv_skip(argv, 1), &names); + if (r < 0) + return r; + + r = get_unit_list_recursive(bus, names, &unit_infos, &replies, &machines); + if (r < 0) + return r; + } else { + r = get_unit_list_recursive(bus, strv_skip(argv, 1), &unit_infos, &replies, &machines); + if (r < 0) + return r; + } typesafe_qsort(unit_infos, r, compare_unit_info); return output_units_list(unit_infos, r); @@ -1571,9 +1685,21 @@ static int list_unit_files(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_create_error(r); - r = sd_bus_message_append_strv(m, strv_skip(argv, 1)); - if (r < 0) - return bus_log_create_error(r); + if (arg_with_dependencies) { + _cleanup_strv_free_ char **names_with_deps = NULL; + + r = append_unit_dependencies(bus, strv_skip(argv, 1), &names_with_deps); + if (r < 0) + return log_error_errno(r, "Failed to append unit dependencies: %m"); + + r = sd_bus_message_append_strv(m, names_with_deps); + if (r < 0) + return bus_log_create_error(r); + } else { + r = sd_bus_message_append_strv(m, strv_skip(argv, 1)); + if (r < 0) + return bus_log_create_error(r); + } r = sd_bus_call(bus, m, 0, &error, &reply); if (r < 0 && sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD)) { @@ -1677,79 +1803,6 @@ static int list_dependencies_print(const char *name, int level, unsigned branche return 0; } -static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) { - struct DependencyStatusInfo { - char **dep[5]; - } info = {}; - - static const struct bus_properties_map map[_DEPENDENCY_MAX][6] = { - [DEPENDENCY_FORWARD] = { - { "Requires", "as", NULL, offsetof(struct DependencyStatusInfo, dep[0]) }, - { "Requisite", "as", NULL, offsetof(struct DependencyStatusInfo, dep[1]) }, - { "Wants", "as", NULL, offsetof(struct DependencyStatusInfo, dep[2]) }, - { "ConsistsOf", "as", NULL, offsetof(struct DependencyStatusInfo, dep[3]) }, - { "BindsTo", "as", NULL, offsetof(struct DependencyStatusInfo, dep[4]) }, - {} - }, - [DEPENDENCY_REVERSE] = { - { "RequiredBy", "as", NULL, offsetof(struct DependencyStatusInfo, dep[0]) }, - { "RequisiteOf", "as", NULL, offsetof(struct DependencyStatusInfo, dep[1]) }, - { "WantedBy", "as", NULL, offsetof(struct DependencyStatusInfo, dep[2]) }, - { "PartOf", "as", NULL, offsetof(struct DependencyStatusInfo, dep[3]) }, - { "BoundBy", "as", NULL, offsetof(struct DependencyStatusInfo, dep[4]) }, - {} - }, - [DEPENDENCY_AFTER] = { - { "After", "as", NULL, offsetof(struct DependencyStatusInfo, dep[0]) }, - {} - }, - [DEPENDENCY_BEFORE] = { - { "Before", "as", NULL, offsetof(struct DependencyStatusInfo, dep[0]) }, - {} - }, - }; - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_strv_free_ char **ret = NULL; - _cleanup_free_ char *dbus_path = NULL; - int i, r; - - assert(bus); - assert(name); - assert(deps); - - dbus_path = unit_dbus_path_from_name(name); - if (!dbus_path) - return log_oom(); - - r = bus_map_all_properties(bus, - "org.freedesktop.systemd1", - dbus_path, - map[arg_dependency], - BUS_MAP_STRDUP, - &error, - NULL, - &info); - if (r < 0) - return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r)); - - if (IN_SET(arg_dependency, DEPENDENCY_AFTER, DEPENDENCY_BEFORE)) { - *deps = info.dep[0]; - return 0; - } - - for (i = 0; i < 5; i++) { - r = strv_extend_strv(&ret, info.dep[i], true); - if (r < 0) - return log_oom(); - info.dep[i] = strv_free(info.dep[i]); - } - - *deps = TAKE_PTR(ret); - - return 0; -} - static int list_dependencies_compare(char * const *a, char * const *b) { if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET) return 1; @@ -5909,6 +5962,10 @@ static int show(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to expand names: %m"); + r = maybe_extend_with_unit_dependencies(bus, &names); + if (r < 0) + return r; + STRV_FOREACH(name, names) { _cleanup_free_ char *path; @@ -5959,6 +6016,10 @@ static int cat(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to expand names: %m"); + r = maybe_extend_with_unit_dependencies(bus, &names); + if (r < 0) + return r; + (void) pager_open(arg_pager_flags); STRV_FOREACH(name, names) { @@ -7945,6 +8006,8 @@ static int systemctl_help(void) { " -l --full Don't ellipsize unit names on output\n" " -r --recursive Show unit list of host and local containers\n" " --reverse Show reverse dependencies with 'list-dependencies'\n" + " --with-dependencies Show unit dependencies with 'status', 'cat',\n" + " 'list-units', and 'list-unit-files'.\n" " --job-mode=MODE Specify how to deal with already queued jobs, when\n" " queueing a new job\n" " -T --show-transaction When enqueuing a unit job, show full transaction\n" @@ -8235,6 +8298,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_BOOT_LOADER_ENTRY, ARG_NOW, ARG_MESSAGE, + ARG_WITH_DEPENDENCIES, ARG_WAIT, ARG_WHAT, }; @@ -8281,6 +8345,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "plain", no_argument, NULL, ARG_PLAIN }, { "state", required_argument, NULL, ARG_STATE }, { "recursive", no_argument, NULL, 'r' }, + { "with-dependencies", no_argument, NULL, ARG_WITH_DEPENDENCIES }, { "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, { "boot-loader-menu", required_argument, NULL, ARG_BOOT_LOADER_MENU }, @@ -8641,6 +8706,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_show_transaction = true; break; + case ARG_WITH_DEPENDENCIES: + arg_with_dependencies = true; + break; + case ARG_WHAT: { const char *p; From a602a0b44b9eb9af0027d054dd24e405a658e375 Mon Sep 17 00:00:00 2001 From: Kevin Kuehler Date: Mon, 9 Dec 2019 01:40:47 -0800 Subject: [PATCH 3/3] man: Document systemctl --with-dependencies switch --- man/systemctl.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/man/systemctl.xml b/man/systemctl.xml index 3d86f7dffa..5828477e8d 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1592,6 +1592,22 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + + + When used with status, + cat, list-units, and + list-unit-files, those commands print all + specified units and the dependencies of those units. + + Options , + , + may be used to change what types of dependencies + are shown. + + +