1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "analyze-dot.h"
6 #include "bus-locator.h"
7 #include "bus-unit-util.h"
9 #include "terminal-util.h"
11 static int graph_one_property(
17 char *from_patterns
[],
18 char *to_patterns
[]) {
20 _cleanup_strv_free_
char **units
= NULL
;
28 match_patterns
= strv_fnmatch(patterns
, u
->id
);
30 if (!strv_isempty(from_patterns
) && !match_patterns
&& !strv_fnmatch(from_patterns
, u
->id
))
33 r
= bus_get_unit_property_strv(bus
, u
->unit_path
, prop
, &units
);
37 STRV_FOREACH(unit
, units
) {
40 match_patterns2
= strv_fnmatch(patterns
, *unit
);
42 if (!strv_isempty(to_patterns
) && !match_patterns2
&& !strv_fnmatch(to_patterns
, *unit
))
45 if (!strv_isempty(patterns
) && !match_patterns
&& !match_patterns2
)
48 printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u
->id
, *unit
, color
);
54 static int graph_one(sd_bus
*bus
, const UnitInfo
*u
, char *patterns
[], char *from_patterns
[], char *to_patterns
[]) {
60 if (IN_SET(arg_dot
, DEP_ORDER
, DEP_ALL
)) {
61 r
= graph_one_property(bus
, u
, "After", "green", patterns
, from_patterns
, to_patterns
);
66 if (IN_SET(arg_dot
, DEP_REQUIRE
, DEP_ALL
)) {
67 r
= graph_one_property(bus
, u
, "Requires", "black", patterns
, from_patterns
, to_patterns
);
70 r
= graph_one_property(bus
, u
, "Requisite", "darkblue", patterns
, from_patterns
, to_patterns
);
73 r
= graph_one_property(bus
, u
, "Wants", "grey66", patterns
, from_patterns
, to_patterns
);
76 r
= graph_one_property(bus
, u
, "Conflicts", "red", patterns
, from_patterns
, to_patterns
);
84 static int expand_patterns(sd_bus
*bus
, char **patterns
, char ***ret
) {
85 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
88 STRV_FOREACH(pattern
, patterns
) {
89 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
90 _cleanup_free_
char *unit
= NULL
, *unit_id
= NULL
;
92 if (strv_extend(&expanded_patterns
, *pattern
) < 0)
95 if (string_is_glob(*pattern
))
98 unit
= unit_dbus_path_from_name(*pattern
);
102 r
= sd_bus_get_property_string(
104 "org.freedesktop.systemd1",
106 "org.freedesktop.systemd1.Unit",
111 return log_error_errno(r
, "Failed to get ID: %s", bus_error_message(&error
, r
));
113 if (!streq(*pattern
, unit_id
)) {
114 if (strv_extend(&expanded_patterns
, unit_id
) < 0)
119 *ret
= TAKE_PTR(expanded_patterns
); /* do not free */
124 int verb_dot(int argc
, char *argv
[], void *userdata
) {
125 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
*reply
= NULL
;
126 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
127 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
128 _cleanup_strv_free_
char **expanded_patterns
= NULL
;
129 _cleanup_strv_free_
char **expanded_from_patterns
= NULL
;
130 _cleanup_strv_free_
char **expanded_to_patterns
= NULL
;
134 r
= acquire_bus(&bus
, NULL
);
136 return bus_log_connect_error(r
, arg_transport
);
138 r
= expand_patterns(bus
, strv_skip(argv
, 1), &expanded_patterns
);
142 r
= expand_patterns(bus
, arg_dot_from_patterns
, &expanded_from_patterns
);
146 r
= expand_patterns(bus
, arg_dot_to_patterns
, &expanded_to_patterns
);
150 r
= bus_call_method(bus
, bus_systemd_mgr
, "ListUnits", &error
, &reply
, NULL
);
152 return log_error_errno(r
, "Failed to list units: %s", bus_error_message(&error
, r
));
154 r
= sd_bus_message_enter_container(reply
, SD_BUS_TYPE_ARRAY
, "(ssssssouso)");
156 return bus_log_parse_error(r
);
158 printf("digraph systemd {\n");
160 while ((r
= bus_parse_unit_info(reply
, &u
)) > 0) {
162 r
= graph_one(bus
, &u
, expanded_patterns
, expanded_from_patterns
, expanded_to_patterns
);
167 return bus_log_parse_error(r
);
171 log_info(" Color legend: black = Requires\n"
172 " dark blue = Requisite\n"
173 " dark grey = Wants\n"
177 if (on_tty() && !arg_quiet
)
178 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
179 "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n");