From cb4d9834305406b5f1978e268df1cdc01e519d89 Mon Sep 17 00:00:00 2001 From: Simon Peeters Date: Thu, 7 Feb 2013 02:44:50 +0100 Subject: [PATCH 5/5] move systemctl dot to systemd-analyze depgraph --- TODO | 2 - man/systemctl.xml | 44 ------- man/systemd-analyze.xml | 40 ++++++ shell-completion/systemd-bash-completion.sh | 4 +- shell-completion/systemd-zsh-completion.zsh | 8 +- src/analyze/systemd-analyze.c | 188 ++++++++++++++++++++++++++- src/systemctl/systemctl.c | 191 ---------------------------- 7 files changed, 230 insertions(+), 247 deletions(-) diff --git a/TODO b/TODO index 2c8aad3..9997a4a 100644 --- a/TODO +++ b/TODO @@ -74,8 +74,6 @@ Features: * introduce ExecCondition= in services -* if we have systemd-analyze in C "systemctl dot" should move there too - * EFI: - fsck hookup for the ESP mount is missing - write man page for efi boot generator diff --git a/man/systemctl.xml b/man/systemctl.xml index 3842645..51792cf 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -272,32 +272,6 @@ - - - - When used in - conjunction with the - dot command (see - below), selects which dependencies are - shown in the dependency graph. If - is passed - only dependencies of type - After= or - Before= are - shown. If - is passed only dependencies of type - Requires=, - RequiresOverridable=, - Requisite=, - RequisiteOverridable=, - Wants= and - Conflicts= are - shown. If neither is passed, shows - dependencies of all these - types. - - - Don't send wall @@ -979,24 +953,6 @@ applications. - dot - - Generate textual - dependency graph description in dot - format for further processing with the - GraphViz - dot1 - tool. Use a command line like - systemctl dot | dot -Tsvg > - systemd.svg to generate a - graphical dependency tree. Unless - or - is passed - the generated graph will show both - ordering and requirement - dependencies. - - list-dependencies [NAME] Shows required and diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index c2ff9cc..0fe5f15 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -57,6 +57,9 @@ systemd-analyze OPTIONS plot > file.svg + + systemd-analyze OPTIONS depgraph + @@ -90,6 +93,17 @@ been started at what time, highlighting the time they spent on initialization. + systemd-analyze depgraph + Generate textual dependency graph description in dot + format for further processing with the GraphViz + dot1 + tool. Use a command line like systemd-analyze + depgraph | dot -Tsvg > systemd.svg to generate + a graphical dependency tree. Unless + or + is passed the generated graph will show both ordering + and requirement dependencies. + If no command is passed systemd-analyze time is implied. @@ -116,6 +130,32 @@ of user sessions instead of the system manager. + + + + + + When used in + conjunction with the + depgraph command (see + above), selects which dependencies are + shown in the dependency graph. If + is passed + only dependencies of type + After= or + Before= are + shown. If + is passed only dependencies of type + Requires=, + RequiresOverridable=, + Requisite=, + RequisiteOverridable=, + Wants= and + Conflicts= are + shown. If neither is passed, shows + dependencies of all these + types. + diff --git a/shell-completion/systemd-bash-completion.sh b/shell-completion/systemd-bash-completion.sh index 52dc72b..1132b3c 100644 --- a/shell-completion/systemd-bash-completion.sh +++ b/shell-completion/systemd-bash-completion.sh @@ -60,7 +60,7 @@ _systemctl () { local -A OPTS=( [STANDALONE]='--all -a --defaults --fail --ignore-dependencies --failed --force -f --full --global --help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall - --order --require --quiet -q --privileged -P --system --user --version --runtime' + --quiet -q --privileged -P --system --user --version --runtime' [ARG]='--host -H --kill-mode --kill-who --property -p --signal -s --type -t --root' ) @@ -113,7 +113,7 @@ _systemctl () { [JOBS]='cancel' [SNAPSHOTS]='delete' [ENVS]='set-environment unset-environment' - [STANDALONE]='daemon-reexec daemon-reload default dot dump + [STANDALONE]='daemon-reexec daemon-reload default dump emergency exit halt hibernate hybrid-sleep kexec list-jobs list-units list-unit-files poweroff reboot rescue show-environment suspend' diff --git a/shell-completion/systemd-zsh-completion.zsh b/shell-completion/systemd-zsh-completion.zsh index d5fb850..177a564 100644 --- a/shell-completion/systemd-zsh-completion.zsh +++ b/shell-completion/systemd-zsh-completion.zsh @@ -27,8 +27,6 @@ _ctls() '--no-legend[Do not print a legend, i.e. the column headers and the footer with hints]' \ '--no-pager[Do not pipe output into a pager]' \ '--no-ask-password[Do not ask for system passwords]' \ - '--order[When generating graph for dot, show only order]' \ - '--require[When generating graph for dot, show only requirement]' \ '--system[Connect to system manager]' \ '--user[Connect to user service manager]' \ '--global[Enable/disable unit files globally]' \ @@ -136,6 +134,8 @@ _ctls() _arguments \ {-h,--help}'[Show help text.]' \ '--user[Shows performance data of user sessions instead of the system manager.]' \ + '--order[When generating graph for dot, show only order]' \ + '--require[When generating graph for dot, show only requirement]' \ '*::systemd-analyze commands:_systemd_analyze_command' ;; systemd-ask-password) @@ -293,6 +293,7 @@ _systemd_analyze_command(){ 'time:Print the time taken to start' 'blame:prints a list of all running units, ordered by the time they took to initialize' 'plot:prints an SVG graphic detailing which system services have been started at what time' + 'dot:Dump dependency graph for dot(1)' ) if (( CURRENT == 1 )); then @@ -352,7 +353,6 @@ _outputmodes() { "list-jobs:List jobs" "cancel:Cancel all, one, or more jobs" "dump:Dump server status" - "dot:Dump dependency graph for dot(1)" "snapshot:Create a snapshot" "delete:Remove one or more snapshots" "show-environment:Dump environment" @@ -575,7 +575,7 @@ done (( $+functions[_systemctl_link] )) || _systemctl_link() { _files } # no systemctl completion for: -# [STANDALONE]='daemon-reexec daemon-reload default dot dump +# [STANDALONE]='daemon-reexec daemon-reload default dump # emergency exit halt kexec list-jobs list-units # list-unit-files poweroff reboot rescue show-environment' # [NAME]='snapshot load' diff --git a/src/analyze/systemd-analyze.c b/src/analyze/systemd-analyze.c index 9acf35a..16ac095 100644 --- a/src/analyze/systemd-analyze.c +++ b/src/analyze/systemd-analyze.c @@ -46,6 +46,11 @@ } while(false) static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; +static enum depgraph { + DEP_ALL, + DEP_ORDER, + DEP_REQUIRE +} arg_depgraph = DEP_ALL; double scale_x = 0.1; // pixels per ms double scale_y = 20.0; @@ -532,6 +537,166 @@ static int analyze_time(DBusConnection *bus) return 0; } +static int graph_one_property(const char *name, const char *prop, DBusMessageIter *iter) { + + static const char * const colors[] = { + "Requires", "[color=\"black\"]", + "RequiresOverridable", "[color=\"black\"]", + "Requisite", "[color=\"darkblue\"]", + "RequisiteOverridable", "[color=\"darkblue\"]", + "Wants", "[color=\"grey66\"]", + "Conflicts", "[color=\"red\"]", + "ConflictedBy", "[color=\"red\"]", + "After", "[color=\"green\"]" + }; + + const char *c = NULL; + unsigned i; + + assert(name); + assert(prop); + assert(iter); + + for (i = 0; i < ELEMENTSOF(colors); i += 2) + if (streq(colors[i], prop)) { + c = colors[i+1]; + break; + } + + if (!c) + return 0; + + if (arg_depgraph != DEP_ALL) + if ((arg_depgraph == DEP_ORDER) != streq(prop, "After")) + return 0; + + if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY && + dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { + DBusMessageIter sub; + + dbus_message_iter_recurse(iter, &sub); + + for (dbus_message_iter_recurse(iter, &sub); + dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID; + dbus_message_iter_next(&sub)) { + const char *s; + + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); + dbus_message_iter_get_basic(&sub, &s); + printf("\t\"%s\"->\"%s\" %s;\n", name, s, c); + } + } + + return 0; +} + +static int graph_one(DBusConnection *bus, const struct unit_info *u) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + const char *interface = "org.freedesktop.systemd1.Unit"; + int r; + DBusMessageIter iter, sub, sub2, sub3; + + assert(bus); + assert(u); + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + u->unit_path, + "org.freedesktop.DBus.Properties", + "GetAll", + &reply, + NULL, + DBUS_TYPE_STRING, &interface, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + 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."); + return -EIO; + } + + for (dbus_message_iter_recurse(&iter, &sub); + dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID; + dbus_message_iter_next(&sub)) { + const char *prop; + + assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY); + dbus_message_iter_recurse(&sub, &sub2); + + if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 || + dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { + log_error("Failed to parse reply."); + return -EIO; + } + + dbus_message_iter_recurse(&sub2, &sub3); + r = graph_one_property(u->id, prop, &sub3); + if (r < 0) + return r; + } + + return 0; +} + +static int depgraph(DBusConnection *bus) { + _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; + DBusMessageIter iter, sub; + int r; + + r = bus_method_call_with_reply( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "ListUnits", + &reply, + NULL, + DBUS_TYPE_INVALID); + if (r < 0) + return r; + + 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_STRUCT) { + log_error("Failed to parse reply."); + return -EIO; + } + + printf("digraph systemd {\n"); + + for (dbus_message_iter_recurse(&iter, &sub); + dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID; + dbus_message_iter_next(&sub)) { + struct unit_info u; + + r = bus_parse_unit_info(&sub, &u); + if (r < 0) + return -EIO; + + r = graph_one(bus, &u); + if (r < 0) + return r; + } + + printf("}\n"); + + log_info(" Color legend: black = Requires\n" + " dark blue = Requisite\n" + " dark grey = Wants\n" + " red = Conflicts\n" + " green = After\n"); + + if (on_tty()) + log_notice("-- You probably want to process this output with graphviz' dot tool.\n" + "-- Try a shell pipeline like 'systemd-analyze depgraph | dot -Tsvg > systemd.svg'!\n"); + + return 0; +} + static void analyze_help(void) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" @@ -539,11 +704,14 @@ static void analyze_help(void) " -h --help Show this help\n" " --version Show package version\n" " --system Connect to system manager\n" - " --user Connect to user service manager\n\n" + " --user Connect to user service manager\n" + " --order When generating a dependency graph, show only order\n" + " --require When generating a dependencygraph, show only requirement\n\n" "Commands:\n" - " time print time spent in the kernel before reaching userspace\n" - " blame print list of running units ordered by time to init\n" - " plot output SVG graphic showing service initialization\n\n", + " time Print time spent in the kernel before reaching userspace\n" + " blame Print list of running units ordered by time to init\n" + " plot Output SVG graphic showing service initialization\n" + " depgraph Dump dependency graph (in dot(1) format)\n\n", program_invocation_short_name); } @@ -551,6 +719,8 @@ static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, + ARG_ORDER, + ARG_REQUIRE, ARG_USER, ARG_SYSTEM }; @@ -558,6 +728,8 @@ static int parse_argv(int argc, char *argv[]) static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, + { "order", no_argument, NULL, ARG_ORDER }, + { "require", no_argument, NULL, ARG_REQUIRE }, { "user", no_argument, NULL, ARG_USER }, { "system", no_argument, NULL, ARG_SYSTEM }, { NULL, 0, NULL, 0 } @@ -580,6 +752,12 @@ static int parse_argv(int argc, char *argv[]) case ARG_SYSTEM: arg_scope = UNIT_FILE_SYSTEM; break; + case ARG_ORDER: + arg_depgraph = DEP_ORDER; + break; + case ARG_REQUIRE: + arg_depgraph = DEP_REQUIRE; + break; case -1: return 1; case '?': @@ -614,6 +792,8 @@ int main(int argc, char *argv[]) { r = analyze_blame(bus); else if (streq(argv[optind], "plot")) r = analyze_plot(bus); + else if (streq(argv[optind], "depgraph")) + r = depgraph(bus); else log_error("Unknown operation '%s'.", argv[optind]); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 54c8ebc..12e343e 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -116,11 +116,6 @@ static enum action { ACTION_CANCEL_SHUTDOWN, _ACTION_MAX } arg_action = ACTION_SYSTEMCTL; -static enum dot { - DOT_ALL, - DOT_ORDER, - DOT_REQUIRE -} arg_dot = DOT_ALL; static enum transport { TRANSPORT_NORMAL, TRANSPORT_SSH, @@ -891,176 +886,6 @@ static int list_dependencies(DBusConnection *bus, char **args) { return list_dependencies_one(bus, u, 0, NULL, 0); } -static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) { - - static const char * const colors[] = { - "Requires", "[color=\"black\"]", - "RequiresOverridable", "[color=\"black\"]", - "Requisite", "[color=\"darkblue\"]", - "RequisiteOverridable", "[color=\"darkblue\"]", - "Wants", "[color=\"grey66\"]", - "Conflicts", "[color=\"red\"]", - "ConflictedBy", "[color=\"red\"]", - "After", "[color=\"green\"]" - }; - - const char *c = NULL; - unsigned i; - - assert(name); - assert(prop); - assert(iter); - - for (i = 0; i < ELEMENTSOF(colors); i += 2) - if (streq(colors[i], prop)) { - c = colors[i+1]; - break; - } - - if (!c) - return 0; - - if (arg_dot != DOT_ALL) - if ((arg_dot == DOT_ORDER) != streq(prop, "After")) - return 0; - - switch (dbus_message_iter_get_arg_type(iter)) { - - case DBUS_TYPE_ARRAY: - - if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) { - DBusMessageIter sub; - - dbus_message_iter_recurse(iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *s; - - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING); - dbus_message_iter_get_basic(&sub, &s); - printf("\t\"%s\"->\"%s\" %s;\n", name, s, c); - - dbus_message_iter_next(&sub); - } - - return 0; - } - } - - return 0; -} - -static int dot_one(DBusConnection *bus, const struct unit_info *u) { - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - const char *interface = "org.freedesktop.systemd1.Unit"; - int r; - DBusMessageIter iter, sub, sub2, sub3; - - assert(bus); - assert(u); - - r = bus_method_call_with_reply( - bus, - "org.freedesktop.systemd1", - u->unit_path, - "org.freedesktop.DBus.Properties", - "GetAll", - &reply, - NULL, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID); - if (r < 0) - return r; - - 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."); - return -EIO; - } - - dbus_message_iter_recurse(&iter, &sub); - - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - const char *prop; - - assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY); - dbus_message_iter_recurse(&sub, &sub2); - - if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0 || - dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) { - log_error("Failed to parse reply."); - return -EIO; - } - - dbus_message_iter_recurse(&sub2, &sub3); - r = dot_one_property(u->id, prop, &sub3); - if (r < 0) - return r; - - dbus_message_iter_next(&sub); - } - - return 0; -} - -static int dot(DBusConnection *bus, char **args) { - _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; - DBusMessageIter iter, sub; - int r; - - r = bus_method_call_with_reply( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "ListUnits", - &reply, - NULL, - DBUS_TYPE_INVALID); - if (r < 0) - return r; - - 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_STRUCT) { - log_error("Failed to parse reply."); - return -EIO; - } - - printf("digraph systemd {\n"); - - dbus_message_iter_recurse(&iter, &sub); - while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) { - struct unit_info u; - - r = bus_parse_unit_info(&sub, &u); - if (r < 0) - return -EIO; - - r = dot_one(bus, &u); - if (r < 0) - return r; - - /* printf("\t\"%s\";\n", u.id); */ - dbus_message_iter_next(&sub); - } - - printf("}\n"); - - log_info(" Color legend: black = Requires\n" - " dark blue = Requisite\n" - " dark grey = Wants\n" - " red = Conflicts\n" - " green = After\n"); - - if (on_tty()) - log_notice("-- You probably want to process this output with graphviz' dot tool.\n" - "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n"); - - return 0; -} - static int list_jobs(DBusConnection *bus, char **args) { _cleanup_dbus_message_unref_ DBusMessage *reply = NULL; DBusMessageIter iter, sub, sub2; @@ -4276,8 +4101,6 @@ static int systemctl_help(void) { " --no-pager Do not pipe output into a pager\n" " --no-ask-password\n" " Do not ask for system passwords\n" - " --order When generating graph for dot, show only order\n" - " --require When generating graph for dot, show only requirement\n" " --system Connect to system manager\n" " --user Connect to user service manager\n" " --global Enable/disable unit files globally\n" @@ -4337,7 +4160,6 @@ static int systemctl_help(void) { " cancel [JOB...] Cancel all, one, or more jobs\n\n" "Status Commands:\n" " dump Dump server status\n" - " dot Dump dependency graph for dot(1)\n\n" "Snapshot Commands:\n" " snapshot [NAME] Create a snapshot\n" " delete [NAME...] Remove one or more snapshots\n\n" @@ -4460,8 +4282,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_NO_LEGEND, ARG_NO_PAGER, ARG_NO_WALL, - ARG_ORDER, - ARG_REQUIRE, ARG_ROOT, ARG_FULL, ARG_NO_RELOAD, @@ -4491,8 +4311,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "no-wall", no_argument, NULL, ARG_NO_WALL }, { "quiet", no_argument, NULL, 'q' }, - { "order", no_argument, NULL, ARG_ORDER }, - { "require", no_argument, NULL, ARG_REQUIRE }, { "root", required_argument, NULL, ARG_ROOT }, { "force", no_argument, NULL, ARG_FORCE }, { "no-reload", no_argument, NULL, ARG_NO_RELOAD }, @@ -4599,14 +4417,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_no_wall = true; break; - case ARG_ORDER: - arg_dot = DOT_ORDER; - break; - - case ARG_REQUIRE: - arg_dot = DOT_REQUIRE; - break; - case ARG_ROOT: arg_root = optarg; break; @@ -5307,7 +5117,6 @@ static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError { "status", MORE, 2, show }, { "help", MORE, 2, show }, { "dump", EQUAL, 1, dump }, - { "dot", EQUAL, 1, dot }, { "snapshot", LESS, 2, snapshot }, { "delete", MORE, 2, delete_snapshot }, { "daemon-reload", EQUAL, 1, daemon_reload }, -- 1.8.1