diff options
-rw-r--r-- | man/systemctl.xml | 11 | ||||
-rw-r--r-- | src/shared/util.c | 2 | ||||
-rw-r--r-- | src/shared/util.h | 1 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 227 |
4 files changed, 239 insertions, 2 deletions
diff --git a/man/systemctl.xml b/man/systemctl.xml index 48c5a1c738..f223383716 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -997,6 +997,17 @@ dependencies.</para></listitem> </varlistentry> <varlistentry> + <term><command>list-dependencies [NAME]</command></term> + + <listitem><para> + Shows required and wanted units of given unit. + Target units are recursively expanded. + When <option>--all</option> is passed + dependency tree is generated for all + subsidiary units.</para> + </listitem> + </varlistentry> + <varlistentry> <term><command>snapshot [NAME]</command></term> <listitem><para>Create a snapshot. If diff --git a/src/shared/util.c b/src/shared/util.c index 8e1409671f..d5de59fce9 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -5648,12 +5648,14 @@ const char *draw_special_char(DrawSpecialChar ch) { [DRAW_TREE_VERT] = "\342\224\202 ", /* │ */ [DRAW_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ [DRAW_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ + [DRAW_TREE_SPACE] = " ", /* */ [DRAW_TRIANGULAR_BULLET] = "\342\200\243 ", /* ‣ */ }, /* ASCII fallback */ { [DRAW_TREE_VERT] = "| ", [DRAW_TREE_BRANCH] = "|-", [DRAW_TREE_RIGHT] = "`-", + [DRAW_TREE_SPACE] = " ", [DRAW_TRIANGULAR_BULLET] = "> ", } }; diff --git a/src/shared/util.h b/src/shared/util.h index bbf744e900..cdaff45772 100644 --- a/src/shared/util.h +++ b/src/shared/util.h @@ -555,6 +555,7 @@ typedef enum DrawSpecialChar { DRAW_TREE_VERT, DRAW_TREE_BRANCH, DRAW_TREE_RIGHT, + DRAW_TREE_SPACE, DRAW_TRIANGULAR_BULLET, _DRAW_SPECIAL_CHAR_MAX } DrawSpecialChar; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index a2ad0deb5a..442179d340 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -710,6 +710,226 @@ static int list_unit_files(DBusConnection *bus, char **args) { return 0; } +static int list_dependencies_print(const char *name, int level, unsigned int branches, bool last) { + int i; + _cleanup_free_ char *n = NULL; + size_t len = 0; + size_t max_len = MAX(columns(),20); + + for (i = level - 1; i >= 0; i--) { + len += 2; + if(len > max_len - 3 && !arg_full) { + printf("%s...\n",max_len % 2 ? "" : " "); + return 0; + } + printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERT : DRAW_TREE_SPACE)); + } + len += 2; + if(len > max_len - 3 && !arg_full) { + printf("%s...\n",max_len % 2 ? "" : " "); + return 0; + } + printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH)); + + if(arg_full){ + printf("%s\n", name); + return 0; + } + + n = ellipsize(name, max_len-len, 100); + if(!n) + return log_oom(); + + printf("%s\n", n); + return 0; +} + +static int list_dependencies_get_dependencies(DBusConnection *bus, const char *name, char ***deps) { + static const char * const dependencies[] = { + "Requires", + "RequiresOverridable", + "Requisite", + "RequisiteOverridable", + "Wants" + }; + + _cleanup_free_ char *path; + const char *interface = "org.freedesktop.systemd1.Unit"; + + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub, sub2, sub3; + + int r = 0; + unsigned int i; + + char **ret = NULL; + char **c; + + assert(bus); + assert(name); + assert(deps); + + path = unit_dbus_path_from_name(name); + if (path == NULL) { + r = -EINVAL; + goto finish; + } + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + goto finish; + + if (!dbus_message_iter_init(reply, &iter) || + dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY || + dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&iter, &sub); + + while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { + const char *prop; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + r = -EIO; + goto finish; + } + + dbus_message_iter_recurse(&sub2, &sub3); + + dbus_message_iter_next(&sub); + + for (i = 0; i < ELEMENTSOF(dependencies); i++) + if (streq(dependencies[i], prop)) { + break; + } + + if (i == ELEMENTSOF(dependencies)) + continue; + + if (dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_ARRAY) { + if (dbus_message_iter_get_element_type(&sub3) == DBUS_TYPE_STRING) { + DBusMessageIter sub4; + dbus_message_iter_recurse(&sub3, &sub4); + + while (dbus_message_iter_get_arg_type(&sub4) != DBUS_TYPE_INVALID) { + const char *s; + + assert(dbus_message_iter_get_arg_type(&sub4) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&sub4, &s); + c = strv_append(ret, s); + if (c == NULL) { + r = log_oom(); + goto finish; + } + strv_free(ret); + ret = c; + dbus_message_iter_next(&sub4); + } + } + } + } +finish: + if (r < 0) + strv_freep(&ret); + *deps = ret; + return r; +} + +static int list_dependencies_compare(const void *_a, const void *_b) { + const char **a = (const char**) _a, **b = (const char**) _b; + if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET) + return 1; + if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET) + return -1; + return strcasecmp(*a, *b); +} + +static int list_dependencies_one(DBusConnection *bus, const char *name, int level, char **units, unsigned int branches) { + char **deps = NULL; + char **c; + char **u = NULL; + int r = 0; + + u = strv_append(units, name); + if(!u) + return log_oom(); + + r = list_dependencies_get_dependencies(bus, name, &deps); + if (r < 0) + goto finish; + + qsort(deps, strv_length(deps), sizeof (char*), list_dependencies_compare); + + STRV_FOREACH(c, deps) { + if (strv_contains(u, *c)) { + r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1); + if(r < 0) + goto finish; + continue; + } + + r = list_dependencies_print(*c, level, branches, c[1] == NULL); + if(r < 0) + goto finish; + + 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) + goto finish; + } + } + r = 0; +finish: + strv_free(deps); + strv_free(u); + + return r; +} + +static int list_dependencies(DBusConnection *bus, char **args) { + int r = 0; + _cleanup_free_ char *unit = NULL; + + assert(bus); + assert(args[1]); + + unit = unit_name_mangle(args[1]); + if (!unit) + return log_oom(); + + pager_open_if_enabled(); + printf("%s\n", unit); + r = list_dependencies_one(bus, unit, 0, NULL, 0); + return r; +} + static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) { static const char * const colors[] = { @@ -4063,7 +4283,7 @@ static int systemctl_help(void) { " 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" - " help [NAME...|PID...] Show manual for one or more units\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" " set-cgroup [NAME] [CGROUP...] Add unit to a control group\n" @@ -4072,7 +4292,9 @@ static int systemctl_help(void) { " Set control group attribute\n" " unset-cgroup-attr [NAME] [ATTR...]\n" " Unset control group attribute\n" - " load [NAME...] Load one or more units\n\n" + " load [NAME...] Load one or more units\n" + " list-dependencies [NAME] Recursively show units which are required\n" + " or wanted by this unit\n\n" "Unit File Commands:\n" " list-unit-files List installed unit files\n" " enable [NAME...] Enable one or more unit files\n" @@ -5088,6 +5310,7 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "unmask", MORE, 2, enable_unit }, { "link", MORE, 2, enable_unit }, { "switch-root", MORE, 2, switch_root }, + { "list-dependencies", EQUAL, 2, list_dependencies }, }; int left; |