analyze: split out "dot" verb
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
int dot(int argc, char *argv[], void *userdata);
|
||||
+4
-183
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user