]>
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, | |
de91848c MY |
16 | char **patterns, |
17 | char **from_patterns, | |
18 | char **to_patterns) { | |
cccd2af6 LP |
19 | |
20 | _cleanup_strv_free_ char **units = NULL; | |
cccd2af6 | 21 | bool match_patterns; |
de010b0b | 22 | int r; |
cccd2af6 | 23 | |
de91848c | 24 | assert(bus); |
cccd2af6 LP |
25 | assert(u); |
26 | assert(prop); | |
27 | assert(color); | |
28 | ||
29 | match_patterns = strv_fnmatch(patterns, u->id); | |
30 | ||
31 | if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id)) | |
32 | return 0; | |
33 | ||
34 | r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); | |
35 | if (r < 0) | |
36 | return r; | |
37 | ||
38 | STRV_FOREACH(unit, units) { | |
39 | bool match_patterns2; | |
40 | ||
41 | match_patterns2 = strv_fnmatch(patterns, *unit); | |
42 | ||
43 | if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit)) | |
44 | continue; | |
45 | ||
46 | if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) | |
47 | continue; | |
48 | ||
49 | printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color); | |
50 | } | |
51 | ||
52 | return 0; | |
53 | } | |
54 | ||
de91848c MY |
55 | static int graph_one( |
56 | sd_bus *bus, | |
57 | const UnitInfo *u, | |
58 | char **patterns, | |
59 | char **from_patterns, | |
60 | char **to_patterns) { | |
61 | ||
cccd2af6 LP |
62 | int r; |
63 | ||
64 | assert(bus); | |
65 | assert(u); | |
66 | ||
67 | if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) { | |
68 | r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns); | |
69 | if (r < 0) | |
70 | return r; | |
71 | } | |
72 | ||
73 | if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) { | |
74 | r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns); | |
75 | if (r < 0) | |
76 | return r; | |
de91848c | 77 | |
cccd2af6 LP |
78 | r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns); |
79 | if (r < 0) | |
80 | return r; | |
de91848c | 81 | |
147e7b44 MY |
82 | r = graph_one_property(bus, u, "BindsTo", "gold", patterns, from_patterns, to_patterns); |
83 | if (r < 0) | |
84 | return r; | |
85 | ||
cccd2af6 LP |
86 | r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns); |
87 | if (r < 0) | |
88 | return r; | |
de91848c | 89 | |
cccd2af6 LP |
90 | r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); |
91 | if (r < 0) | |
92 | return r; | |
93 | } | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { | |
99 | _cleanup_strv_free_ char **expanded_patterns = NULL; | |
cccd2af6 LP |
100 | int r; |
101 | ||
de91848c MY |
102 | assert(bus); |
103 | assert(ret); | |
104 | ||
cccd2af6 LP |
105 | STRV_FOREACH(pattern, patterns) { |
106 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
107 | _cleanup_free_ char *unit = NULL, *unit_id = NULL; | |
108 | ||
109 | if (strv_extend(&expanded_patterns, *pattern) < 0) | |
110 | return log_oom(); | |
111 | ||
112 | if (string_is_glob(*pattern)) | |
113 | continue; | |
114 | ||
115 | unit = unit_dbus_path_from_name(*pattern); | |
116 | if (!unit) | |
117 | return log_oom(); | |
118 | ||
119 | r = sd_bus_get_property_string( | |
120 | bus, | |
121 | "org.freedesktop.systemd1", | |
122 | unit, | |
123 | "org.freedesktop.systemd1.Unit", | |
124 | "Id", | |
125 | &error, | |
126 | &unit_id); | |
127 | if (r < 0) | |
128 | return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); | |
129 | ||
de91848c | 130 | if (!streq(*pattern, unit_id)) |
cccd2af6 LP |
131 | if (strv_extend(&expanded_patterns, unit_id) < 0) |
132 | return log_oom(); | |
cccd2af6 LP |
133 | } |
134 | ||
135 | *ret = TAKE_PTR(expanded_patterns); /* do not free */ | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
ef38bedb | 140 | int verb_dot(int argc, char *argv[], void *userdata) { |
cccd2af6 LP |
141 | _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; |
142 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
143 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
144 | _cleanup_strv_free_ char **expanded_patterns = NULL; | |
145 | _cleanup_strv_free_ char **expanded_from_patterns = NULL; | |
146 | _cleanup_strv_free_ char **expanded_to_patterns = NULL; | |
cccd2af6 | 147 | UnitInfo u; |
de91848c | 148 | int r; |
cccd2af6 LP |
149 | |
150 | r = acquire_bus(&bus, NULL); | |
151 | if (r < 0) | |
152 | return bus_log_connect_error(r, arg_transport); | |
153 | ||
154 | r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns); | |
155 | if (r < 0) | |
156 | return r; | |
157 | ||
158 | r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns); | |
159 | if (r < 0) | |
160 | return r; | |
161 | ||
162 | r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns); | |
163 | if (r < 0) | |
164 | return r; | |
165 | ||
166 | r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL); | |
167 | if (r < 0) | |
5475e963 | 168 | return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); |
cccd2af6 LP |
169 | |
170 | r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); | |
171 | if (r < 0) | |
172 | return bus_log_parse_error(r); | |
173 | ||
174 | printf("digraph systemd {\n"); | |
175 | ||
176 | while ((r = bus_parse_unit_info(reply, &u)) > 0) { | |
177 | ||
178 | r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns); | |
179 | if (r < 0) | |
180 | return r; | |
181 | } | |
182 | if (r < 0) | |
183 | return bus_log_parse_error(r); | |
184 | ||
185 | printf("}\n"); | |
186 | ||
187 | log_info(" Color legend: black = Requires\n" | |
188 | " dark blue = Requisite\n" | |
147e7b44 | 189 | " gold = BindsTo\n" |
cccd2af6 LP |
190 | " dark grey = Wants\n" |
191 | " red = Conflicts\n" | |
192 | " green = After\n"); | |
193 | ||
194 | if (on_tty() && !arg_quiet) | |
195 | log_notice("-- You probably want to process this output with graphviz' dot tool.\n" | |
196 | "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n"); | |
197 | ||
198 | return 0; | |
199 | } |