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
graph_one_property(sd_bus * bus,const UnitInfo * u,const char * prop,const char * color,char * patterns[],char * from_patterns[],char * to_patterns[])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;
21 bool match_patterns;
22 int r;
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
graph_one(sd_bus * bus,const UnitInfo * u,char * patterns[],char * from_patterns[],char * to_patterns[])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
expand_patterns(sd_bus * bus,char ** patterns,char *** ret)84 static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) {
85 _cleanup_strv_free_ char **expanded_patterns = NULL;
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
verb_dot(int argc,char * argv[],void * userdata)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;
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)
152 log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
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 }
183