]>
Commit | Line | Data |
---|---|---|
cccd2af6 LP |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
3 | #include "analyze.h" | |
4 | #include "analyze-dot.h" | |
5 | #include "bus-error.h" | |
6 | #include "bus-locator.h" | |
7 | #include "bus-unit-util.h" | |
8 | #include "glob-util.h" | |
9 | #include "terminal-util.h" | |
10 | ||
11 | static int graph_one_property( | |
12 | sd_bus *bus, | |
13 | const UnitInfo *u, | |
14 | const char *prop, | |
15 | const char *color, | |
16 | char *patterns[], | |
17 | char *from_patterns[], | |
18 | char *to_patterns[]) { | |
19 | ||
20 | _cleanup_strv_free_ char **units = NULL; | |
cccd2af6 | 21 | bool match_patterns; |
de010b0b | 22 | int r; |
cccd2af6 LP |
23 | |
24 | assert(u); | |
25 | assert(prop); | |
26 | assert(color); | |
27 | ||
28 | match_patterns = strv_fnmatch(patterns, u->id); | |
29 | ||
30 | if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id)) | |
31 | return 0; | |
32 | ||
33 | r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); | |
34 | if (r < 0) | |
35 | return r; | |
36 | ||
37 | STRV_FOREACH(unit, units) { | |
38 | bool match_patterns2; | |
39 | ||
40 | match_patterns2 = strv_fnmatch(patterns, *unit); | |
41 | ||
42 | if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit)) | |
43 | continue; | |
44 | ||
45 | if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) | |
46 | continue; | |
47 | ||
48 | printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color); | |
49 | } | |
50 | ||
51 | return 0; | |
52 | } | |
53 | ||
54 | static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) { | |
55 | int r; | |
56 | ||
57 | assert(bus); | |
58 | assert(u); | |
59 | ||
60 | if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) { | |
61 | r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns); | |
62 | if (r < 0) | |
63 | return r; | |
64 | } | |
65 | ||
66 | if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) { | |
67 | r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns); | |
68 | if (r < 0) | |
69 | return r; | |
70 | r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns); | |
71 | if (r < 0) | |
72 | return r; | |
73 | r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns); | |
74 | if (r < 0) | |
75 | return r; | |
76 | r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); | |
77 | if (r < 0) | |
78 | return r; | |
79 | } | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { | |
85 | _cleanup_strv_free_ char **expanded_patterns = NULL; | |
cccd2af6 LP |
86 | int r; |
87 | ||
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; | |
91 | ||
92 | if (strv_extend(&expanded_patterns, *pattern) < 0) | |
93 | return log_oom(); | |
94 | ||
95 | if (string_is_glob(*pattern)) | |
96 | continue; | |
97 | ||
98 | unit = unit_dbus_path_from_name(*pattern); | |
99 | if (!unit) | |
100 | return log_oom(); | |
101 | ||
102 | r = sd_bus_get_property_string( | |
103 | bus, | |
104 | "org.freedesktop.systemd1", | |
105 | unit, | |
106 | "org.freedesktop.systemd1.Unit", | |
107 | "Id", | |
108 | &error, | |
109 | &unit_id); | |
110 | if (r < 0) | |
111 | return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); | |
112 | ||
113 | if (!streq(*pattern, unit_id)) { | |
114 | if (strv_extend(&expanded_patterns, unit_id) < 0) | |
115 | return log_oom(); | |
116 | } | |
117 | } | |
118 | ||
119 | *ret = TAKE_PTR(expanded_patterns); /* do not free */ | |
120 | ||
121 | return 0; | |
122 | } | |
123 | ||
ef38bedb | 124 | int verb_dot(int argc, char *argv[], void *userdata) { |
cccd2af6 LP |
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; | |
131 | int r; | |
132 | UnitInfo u; | |
133 | ||
134 | r = acquire_bus(&bus, NULL); | |
135 | if (r < 0) | |
136 | return bus_log_connect_error(r, arg_transport); | |
137 | ||
138 | r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns); | |
139 | if (r < 0) | |
140 | return r; | |
141 | ||
142 | r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns); | |
143 | if (r < 0) | |
144 | return r; | |
145 | ||
146 | r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns); | |
147 | if (r < 0) | |
148 | return r; | |
149 | ||
150 | r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL); | |
151 | if (r < 0) | |
5475e963 | 152 | return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); |
cccd2af6 LP |
153 | |
154 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); | |
155 | if (r < 0) | |
156 | return bus_log_parse_error(r); | |
157 | ||
158 | printf("digraph systemd {\n"); | |
159 | ||
160 | while ((r = bus_parse_unit_info(reply, &u)) > 0) { | |
161 | ||
162 | r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns); | |
163 | if (r < 0) | |
164 | return r; | |
165 | } | |
166 | if (r < 0) | |
167 | return bus_log_parse_error(r); | |
168 | ||
169 | printf("}\n"); | |
170 | ||
171 | log_info(" Color legend: black = Requires\n" | |
172 | " dark blue = Requisite\n" | |
173 | " dark grey = Wants\n" | |
174 | " red = Conflicts\n" | |
175 | " green = After\n"); | |
176 | ||
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"); | |
180 | ||
181 | return 0; | |
182 | } |