diff --git a/man/systemctl.xml b/man/systemctl.xml
index ab67d78287..ceec7b0479 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.
+
+
+
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: {
diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c
index 3e8eb10468..64c1401afc 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);
@@ -1588,9 +1702,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)) {
@@ -1694,79 +1820,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;
@@ -5926,6 +5979,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;
@@ -5976,6 +6033,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) {
@@ -7966,6 +8027,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"
@@ -8259,6 +8322,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,
};
@@ -8305,6 +8369,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 },
@@ -8665,6 +8730,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;