diff options
author | Lennart Poettering <lennart@poettering.net> | 2022-02-21 10:47:48 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2022-02-21 17:22:23 +0100 |
commit | cccd2af6f579fa63deafdde51014474f78d99958 (patch) | |
tree | 89d5c9e60fe1d6b0b46e506ddff2adee9a0cd50c /src | |
parent | 25eb70af8bdafb40ebe5850b9e1414e297677c1c (diff) | |
download | systemd-cccd2af6f579fa63deafdde51014474f78d99958.tar.gz |
analyze: split out "dot" verb
Diffstat (limited to 'src')
-rw-r--r-- | src/analyze/analyze-dot.c | 184 | ||||
-rw-r--r-- | src/analyze/analyze-dot.h | 4 | ||||
-rw-r--r-- | src/analyze/analyze.c | 187 | ||||
-rw-r--r-- | src/analyze/analyze.h | 10 | ||||
-rw-r--r-- | src/analyze/meson.build | 2 |
5 files changed, 204 insertions, 183 deletions
diff --git a/src/analyze/analyze-dot.c b/src/analyze/analyze-dot.c new file mode 100644 index 0000000000..08f9910368 --- /dev/null +++ b/src/analyze/analyze-dot.c @@ -0,0 +1,184 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-dot.h" +#include "bus-error.h" +#include "bus-locator.h" +#include "bus-unit-util.h" +#include "glob-util.h" +#include "terminal-util.h" + +static int graph_one_property( + sd_bus *bus, + const UnitInfo *u, + const char *prop, + const char *color, + char *patterns[], + char *from_patterns[], + char *to_patterns[]) { + + _cleanup_strv_free_ char **units = NULL; + char **unit; + int r; + bool match_patterns; + + assert(u); + assert(prop); + assert(color); + + match_patterns = strv_fnmatch(patterns, u->id); + + if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id)) + return 0; + + r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); + if (r < 0) + return r; + + STRV_FOREACH(unit, units) { + bool match_patterns2; + + match_patterns2 = strv_fnmatch(patterns, *unit); + + if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit)) + continue; + + if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) + continue; + + printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color); + } + + return 0; +} + +static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) { + int r; + + assert(bus); + assert(u); + + if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) { + r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + } + + if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) { + r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + } + + return 0; +} + +static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { + _cleanup_strv_free_ char **expanded_patterns = NULL; + char **pattern; + int r; + + STRV_FOREACH(pattern, patterns) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *unit = NULL, *unit_id = NULL; + + if (strv_extend(&expanded_patterns, *pattern) < 0) + return log_oom(); + + if (string_is_glob(*pattern)) + continue; + + unit = unit_dbus_path_from_name(*pattern); + if (!unit) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "Id", + &error, + &unit_id); + if (r < 0) + return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); + + if (!streq(*pattern, unit_id)) { + if (strv_extend(&expanded_patterns, unit_id) < 0) + return log_oom(); + } + } + + *ret = TAKE_PTR(expanded_patterns); /* do not free */ + + return 0; +} + +int dot(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_strv_free_ char **expanded_patterns = NULL; + _cleanup_strv_free_ char **expanded_from_patterns = NULL; + _cleanup_strv_free_ char **expanded_to_patterns = NULL; + int r; + UnitInfo u; + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns); + if (r < 0) + return r; + + r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns); + if (r < 0) + return r; + + r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns); + if (r < 0) + return r; + + r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL); + if (r < 0) + log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) + return bus_log_parse_error(r); + + printf("digraph systemd {\n"); + + while ((r = bus_parse_unit_info(reply, &u)) > 0) { + + r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns); + if (r < 0) + return r; + } + if (r < 0) + return bus_log_parse_error(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() && !arg_quiet) + log_notice("-- You probably want to process this output with graphviz' dot tool.\n" + "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n"); + + return 0; +} diff --git a/src/analyze/analyze-dot.h b/src/analyze/analyze-dot.h new file mode 100644 index 0000000000..9017c1878a --- /dev/null +++ b/src/analyze/analyze-dot.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int dot(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index f9790d99ee..8326cfa49e 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -15,6 +15,7 @@ #include "analyze.h" #include "analyze-calendar.h" #include "analyze-condition.h" +#include "analyze-dot.h" #include "analyze-dump.h" #include "analyze-elf.h" #include "analyze-filesystems.h" @@ -88,13 +89,8 @@ svg("</text>\n"); \ } while (false) -static enum dot { - DEP_ALL, - DEP_ORDER, - DEP_REQUIRE -} arg_dot = DEP_ALL; -static char **arg_dot_from_patterns = NULL; -static char **arg_dot_to_patterns = NULL; +DotMode arg_dot = DEP_ALL; +char **arg_dot_from_patterns = NULL, **arg_dot_to_patterns = NULL; static usec_t arg_fuzz = 0; PagerFlags arg_pager_flags = 0; BusTransport arg_transport = BUS_TRANSPORT_LOCAL; @@ -217,7 +213,7 @@ static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *in return 0; } -static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) { +int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; @@ -1204,181 +1200,6 @@ static int analyze_time(int argc, char *argv[], void *userdata) { return 0; } -static int graph_one_property( - sd_bus *bus, - const UnitInfo *u, - const char *prop, - const char *color, - char *patterns[], - char *from_patterns[], - char *to_patterns[]) { - - _cleanup_strv_free_ char **units = NULL; - char **unit; - int r; - bool match_patterns; - - assert(u); - assert(prop); - assert(color); - - match_patterns = strv_fnmatch(patterns, u->id); - - if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id)) - return 0; - - r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); - if (r < 0) - return r; - - STRV_FOREACH(unit, units) { - bool match_patterns2; - - match_patterns2 = strv_fnmatch(patterns, *unit); - - if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit)) - continue; - - if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) - continue; - - printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color); - } - - return 0; -} - -static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) { - int r; - - assert(bus); - assert(u); - - if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) { - r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - } - - if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) { - r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - } - - return 0; -} - -static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { - _cleanup_strv_free_ char **expanded_patterns = NULL; - char **pattern; - int r; - - STRV_FOREACH(pattern, patterns) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *unit = NULL, *unit_id = NULL; - - if (strv_extend(&expanded_patterns, *pattern) < 0) - return log_oom(); - - if (string_is_glob(*pattern)) - continue; - - unit = unit_dbus_path_from_name(*pattern); - if (!unit) - return log_oom(); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - unit, - "org.freedesktop.systemd1.Unit", - "Id", - &error, - &unit_id); - if (r < 0) - return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); - - if (!streq(*pattern, unit_id)) { - if (strv_extend(&expanded_patterns, unit_id) < 0) - return log_oom(); - } - } - - *ret = TAKE_PTR(expanded_patterns); /* do not free */ - - return 0; -} - -static int dot(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_strv_free_ char **expanded_patterns = NULL; - _cleanup_strv_free_ char **expanded_from_patterns = NULL; - _cleanup_strv_free_ char **expanded_to_patterns = NULL; - int r; - UnitInfo u; - - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns); - if (r < 0) - return r; - - r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns); - if (r < 0) - return r; - - r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns); - if (r < 0) - return r; - - r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL); - if (r < 0) - log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); - if (r < 0) - return bus_log_parse_error(r); - - printf("digraph systemd {\n"); - - while ((r = bus_parse_unit_info(reply, &u)) > 0) { - - r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns); - if (r < 0) - return r; - } - if (r < 0) - return bus_log_parse_error(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() && !arg_quiet) - log_notice("-- You probably want to process this output with graphviz' dot tool.\n" - "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n"); - - return 0; -} - static int cat_config(int argc, char *argv[], void *userdata) { char **arg, **list; int r; diff --git a/src/analyze/analyze.h b/src/analyze/analyze.h index 243e354458..1819b5d731 100644 --- a/src/analyze/analyze.h +++ b/src/analyze/analyze.h @@ -7,6 +7,14 @@ #include "pager.h" #include "time-util.h" +typedef enum DotMode { + DEP_ALL, + DEP_ORDER, + DEP_REQUIRE, +} DotMode; + +extern DotMode arg_dot; +extern char **arg_dot_from_patterns, **arg_dot_to_patterns; extern PagerFlags arg_pager_flags; extern BusTransport arg_transport; extern unsigned arg_iterations; @@ -15,4 +23,6 @@ extern bool arg_quiet; int acquire_bus(sd_bus **bus, bool *use_full_bus); +int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv); + void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan); diff --git a/src/analyze/meson.build b/src/analyze/meson.build index 2d62412641..922a343e69 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -5,6 +5,8 @@ systemd_analyze_sources = files(''' analyze-calendar.h analyze-condition.c analyze-condition.h + analyze-dot.c + analyze-dot.h analyze-dump.c analyze-dump.h analyze-elf.c |