]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/analyze/analyze-dot.c
NEWS: finalize for v256~rc3
[thirdparty/systemd.git] / src / analyze / analyze-dot.c
CommitLineData
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
11static 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
55static 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
98static 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 140int 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}