Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include "bus-util.h"
4 : : #include "bus-wait-for-units.h"
5 : : #include "hashmap.h"
6 : : #include "string-util.h"
7 : : #include "strv.h"
8 : : #include "unit-def.h"
9 : :
10 : : typedef struct WaitForItem {
11 : : BusWaitForUnits *parent;
12 : :
13 : : BusWaitForUnitsFlags flags;
14 : :
15 : : char *bus_path;
16 : :
17 : : sd_bus_slot *slot_get_all;
18 : : sd_bus_slot *slot_properties_changed;
19 : :
20 : : bus_wait_for_units_unit_callback unit_callback;
21 : : void *userdata;
22 : :
23 : : char *active_state;
24 : : uint32_t job_id;
25 : : char *clean_result;
26 : : } WaitForItem;
27 : :
28 : : typedef struct BusWaitForUnits {
29 : : sd_bus *bus;
30 : : sd_bus_slot *slot_disconnected;
31 : :
32 : : Hashmap *items;
33 : :
34 : : bus_wait_for_units_ready_callback ready_callback;
35 : : void *userdata;
36 : :
37 : : WaitForItem *current;
38 : :
39 : : BusWaitForUnitsState state;
40 : : bool has_failed:1;
41 : : } BusWaitForUnits;
42 : :
43 : 0 : static WaitForItem *wait_for_item_free(WaitForItem *item) {
44 : : int r;
45 : :
46 [ # # ]: 0 : if (!item)
47 : 0 : return NULL;
48 : :
49 [ # # ]: 0 : if (item->parent) {
50 [ # # # # : 0 : if (FLAGS_SET(item->flags, BUS_WAIT_REFFED) && item->bus_path && item->parent->bus) {
# # ]
51 : 0 : r = sd_bus_call_method_async(
52 : 0 : item->parent->bus,
53 : : NULL,
54 : : "org.freedesktop.systemd1",
55 : 0 : item->bus_path,
56 : : "org.freedesktop.systemd1.Unit",
57 : : "Unref",
58 : : NULL,
59 : : NULL,
60 : : NULL);
61 [ # # ]: 0 : if (r < 0)
62 [ # # ]: 0 : log_debug_errno(r, "Failed to drop reference to unit %s, ignoring: %m", item->bus_path);
63 : : }
64 : :
65 [ # # ]: 0 : assert_se(hashmap_remove(item->parent->items, item->bus_path) == item);
66 : :
67 [ # # ]: 0 : if (item->parent->current == item)
68 : 0 : item->parent->current = NULL;
69 : : }
70 : :
71 : 0 : sd_bus_slot_unref(item->slot_properties_changed);
72 : 0 : sd_bus_slot_unref(item->slot_get_all);
73 : :
74 : 0 : free(item->bus_path);
75 : 0 : free(item->active_state);
76 : 0 : free(item->clean_result);
77 : :
78 : 0 : return mfree(item);
79 : : }
80 : :
81 [ # # ]: 0 : DEFINE_TRIVIAL_CLEANUP_FUNC(WaitForItem*, wait_for_item_free);
82 : :
83 : 0 : static void bus_wait_for_units_clear(BusWaitForUnits *d) {
84 : : WaitForItem *item;
85 : :
86 [ # # ]: 0 : assert(d);
87 : :
88 : 0 : d->slot_disconnected = sd_bus_slot_unref(d->slot_disconnected);
89 : 0 : d->bus = sd_bus_unref(d->bus);
90 : :
91 [ # # ]: 0 : while ((item = hashmap_first(d->items))) {
92 : 0 : d->current = item;
93 : :
94 : 0 : item->unit_callback(d, item->bus_path, false, item->userdata);
95 : 0 : wait_for_item_free(item);
96 : : }
97 : :
98 : 0 : d->items = hashmap_free(d->items);
99 : 0 : }
100 : :
101 : 0 : static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
102 : 0 : BusWaitForUnits *d = userdata;
103 : :
104 [ # # ]: 0 : assert(m);
105 [ # # ]: 0 : assert(d);
106 : :
107 [ # # ]: 0 : log_error("Warning! D-Bus connection terminated.");
108 : :
109 : 0 : bus_wait_for_units_clear(d);
110 : :
111 [ # # ]: 0 : if (d->ready_callback)
112 : 0 : d->ready_callback(d, false, d->userdata);
113 : : else /* If no ready callback is specified close the connection so that the event loop exits */
114 : 0 : sd_bus_close(sd_bus_message_get_bus(m));
115 : :
116 : 0 : return 0;
117 : : }
118 : :
119 : 0 : int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret) {
120 : 0 : _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *d = NULL;
121 : : int r;
122 : :
123 [ # # ]: 0 : assert(bus);
124 [ # # ]: 0 : assert(ret);
125 : :
126 : 0 : d = new(BusWaitForUnits, 1);
127 [ # # ]: 0 : if (!d)
128 : 0 : return -ENOMEM;
129 : :
130 : 0 : *d = (BusWaitForUnits) {
131 : : .state = BUS_WAIT_SUCCESS,
132 : 0 : .bus = sd_bus_ref(bus),
133 : : };
134 : :
135 : 0 : r = sd_bus_match_signal_async(
136 : : bus,
137 : 0 : &d->slot_disconnected,
138 : : "org.freedesktop.DBus.Local",
139 : : NULL,
140 : : "org.freedesktop.DBus.Local",
141 : : "Disconnected",
142 : : match_disconnected, NULL, d);
143 [ # # ]: 0 : if (r < 0)
144 : 0 : return r;
145 : :
146 : 0 : *ret = TAKE_PTR(d);
147 : 0 : return 0;
148 : : }
149 : :
150 : 0 : BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d) {
151 [ # # ]: 0 : if (!d)
152 : 0 : return NULL;
153 : :
154 : 0 : bus_wait_for_units_clear(d);
155 : 0 : sd_bus_slot_unref(d->slot_disconnected);
156 : 0 : sd_bus_unref(d->bus);
157 : :
158 : 0 : return mfree(d);
159 : : }
160 : :
161 : 0 : static bool bus_wait_for_units_is_ready(BusWaitForUnits *d) {
162 [ # # ]: 0 : assert(d);
163 : :
164 [ # # ]: 0 : if (!d->bus) /* Disconnected? */
165 : 0 : return true;
166 : :
167 : 0 : return hashmap_isempty(d->items);
168 : : }
169 : :
170 : 0 : void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata) {
171 [ # # ]: 0 : assert(d);
172 : :
173 : 0 : d->ready_callback = callback;
174 : 0 : d->userdata = userdata;
175 : 0 : }
176 : :
177 : 0 : static void bus_wait_for_units_check_ready(BusWaitForUnits *d) {
178 [ # # ]: 0 : assert(d);
179 : :
180 [ # # ]: 0 : if (!bus_wait_for_units_is_ready(d))
181 : 0 : return;
182 : :
183 : 0 : d->state = d->has_failed ? BUS_WAIT_FAILURE : BUS_WAIT_SUCCESS;
184 : :
185 [ # # ]: 0 : if (d->ready_callback)
186 : 0 : d->ready_callback(d, d->state, d->userdata);
187 : : }
188 : :
189 : 0 : static void wait_for_item_check_ready(WaitForItem *item) {
190 : : BusWaitForUnits *d;
191 : :
192 [ # # ]: 0 : assert(item);
193 [ # # ]: 0 : assert_se(d = item->parent);
194 : :
195 [ # # ]: 0 : if (FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END)) {
196 : :
197 [ # # # # ]: 0 : if (item->clean_result && !streq(item->clean_result, "success"))
198 : 0 : d->has_failed = true;
199 : :
200 [ # # # # ]: 0 : if (!item->active_state || streq(item->active_state, "maintenance"))
201 : 0 : return;
202 : : }
203 : :
204 [ # # # # ]: 0 : if (FLAGS_SET(item->flags, BUS_WAIT_NO_JOB) && item->job_id != 0)
205 : 0 : return;
206 : :
207 [ # # ]: 0 : if (FLAGS_SET(item->flags, BUS_WAIT_FOR_INACTIVE)) {
208 : :
209 [ # # ]: 0 : if (streq_ptr(item->active_state, "failed"))
210 : 0 : d->has_failed = true;
211 [ # # ]: 0 : else if (!streq_ptr(item->active_state, "inactive"))
212 : 0 : return;
213 : : }
214 : :
215 [ # # ]: 0 : if (item->unit_callback) {
216 : 0 : d->current = item;
217 : 0 : item->unit_callback(d, item->bus_path, true, item->userdata);
218 : : }
219 : :
220 : 0 : wait_for_item_free(item);
221 : :
222 : 0 : bus_wait_for_units_check_ready(d);
223 : : }
224 : :
225 : 0 : static int property_map_job(
226 : : sd_bus *bus,
227 : : const char *member,
228 : : sd_bus_message *m,
229 : : sd_bus_error *error,
230 : : void *userdata) {
231 : :
232 : 0 : WaitForItem *item = userdata;
233 : : const char *path;
234 : : uint32_t id;
235 : : int r;
236 : :
237 [ # # ]: 0 : assert(item);
238 : :
239 : 0 : r = sd_bus_message_read(m, "(uo)", &id, &path);
240 [ # # ]: 0 : if (r < 0)
241 : 0 : return r;
242 : :
243 : 0 : item->job_id = id;
244 : 0 : return 0;
245 : : }
246 : :
247 : 0 : static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m) {
248 : :
249 : : static const struct bus_properties_map map[] = {
250 : : { "ActiveState", "s", NULL, offsetof(WaitForItem, active_state) },
251 : : { "Job", "(uo)", property_map_job, 0 },
252 : : { "CleanResult", "s", NULL, offsetof(WaitForItem, clean_result) },
253 : : {}
254 : : };
255 : :
256 : : int r;
257 : :
258 [ # # ]: 0 : assert(item);
259 [ # # ]: 0 : assert(m);
260 : :
261 : 0 : r = bus_message_map_all_properties(m, map, BUS_MAP_STRDUP, NULL, item);
262 [ # # ]: 0 : if (r < 0)
263 : 0 : return r;
264 : :
265 : 0 : wait_for_item_check_ready(item);
266 : 0 : return 0;
267 : : }
268 : :
269 : 0 : static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
270 : 0 : WaitForItem *item = userdata;
271 : : const char *interface;
272 : : int r;
273 : :
274 [ # # ]: 0 : assert(item);
275 : :
276 : 0 : r = sd_bus_message_read(m, "s", &interface);
277 [ # # ]: 0 : if (r < 0) {
278 [ # # ]: 0 : log_debug_errno(r, "Failed to parse PropertiesChanged signal: %m");
279 : 0 : return 0;
280 : : }
281 : :
282 [ # # ]: 0 : if (!streq(interface, "org.freedesktop.systemd1.Unit"))
283 : 0 : return 0;
284 : :
285 : 0 : r = wait_for_item_parse_properties(item, m);
286 [ # # ]: 0 : if (r < 0)
287 [ # # ]: 0 : log_debug_errno(r, "Failed to process PropertiesChanged signal: %m");
288 : :
289 : 0 : return 0;
290 : : }
291 : :
292 : 0 : static int on_get_all_properties(sd_bus_message *m, void *userdata, sd_bus_error *error) {
293 : 0 : WaitForItem *item = userdata;
294 : : int r;
295 : :
296 [ # # ]: 0 : assert(item);
297 : :
298 [ # # ]: 0 : if (sd_bus_error_is_set(error)) {
299 : 0 : BusWaitForUnits *d = item->parent;
300 : :
301 : 0 : d->has_failed = true;
302 : :
303 [ # # ]: 0 : log_debug_errno(sd_bus_error_get_errno(error), "GetAll() failed for %s: %s",
304 : : item->bus_path, error->message);
305 : :
306 : 0 : d->current = item;
307 : 0 : item->unit_callback(d, item->bus_path, false, item->userdata);
308 : 0 : wait_for_item_free(item);
309 : :
310 : 0 : bus_wait_for_units_check_ready(d);
311 : 0 : return 0;
312 : : }
313 : :
314 : 0 : r = wait_for_item_parse_properties(item, m);
315 [ # # ]: 0 : if (r < 0)
316 [ # # ]: 0 : log_debug_errno(r, "Failed to process GetAll method reply: %m");
317 : :
318 : 0 : return 0;
319 : : }
320 : :
321 : 0 : int bus_wait_for_units_add_unit(
322 : : BusWaitForUnits *d,
323 : : const char *unit,
324 : : BusWaitForUnitsFlags flags,
325 : : bus_wait_for_units_unit_callback callback,
326 : : void *userdata) {
327 : :
328 : 0 : _cleanup_(wait_for_item_freep) WaitForItem *item = NULL;
329 : : int r;
330 : :
331 [ # # ]: 0 : assert(d);
332 [ # # ]: 0 : assert(unit);
333 : :
334 [ # # ]: 0 : assert(flags != 0);
335 : :
336 : 0 : r = hashmap_ensure_allocated(&d->items, &string_hash_ops);
337 [ # # ]: 0 : if (r < 0)
338 : 0 : return r;
339 : :
340 : 0 : item = new(WaitForItem, 1);
341 [ # # ]: 0 : if (!item)
342 : 0 : return -ENOMEM;
343 : :
344 : 0 : *item = (WaitForItem) {
345 : : .flags = flags,
346 : 0 : .bus_path = unit_dbus_path_from_name(unit),
347 : : .unit_callback = callback,
348 : : .userdata = userdata,
349 : : .job_id = UINT32_MAX,
350 : : };
351 : :
352 [ # # ]: 0 : if (!item->bus_path)
353 : 0 : return -ENOMEM;
354 : :
355 [ # # ]: 0 : if (!FLAGS_SET(item->flags, BUS_WAIT_REFFED)) {
356 : 0 : r = sd_bus_call_method_async(
357 : : d->bus,
358 : : NULL,
359 : : "org.freedesktop.systemd1",
360 : 0 : item->bus_path,
361 : : "org.freedesktop.systemd1.Unit",
362 : : "Ref",
363 : : NULL,
364 : : NULL,
365 : : NULL);
366 [ # # ]: 0 : if (r < 0)
367 [ # # ]: 0 : return log_debug_errno(r, "Failed to add reference to unit %s: %m", unit);
368 : :
369 : 0 : item->flags |= BUS_WAIT_REFFED;
370 : : }
371 : :
372 : 0 : r = sd_bus_match_signal_async(
373 : : d->bus,
374 : 0 : &item->slot_properties_changed,
375 : : "org.freedesktop.systemd1",
376 : 0 : item->bus_path,
377 : : "org.freedesktop.DBus.Properties",
378 : : "PropertiesChanged",
379 : : on_properties_changed,
380 : : NULL,
381 : : item);
382 [ # # ]: 0 : if (r < 0)
383 [ # # ]: 0 : return log_debug_errno(r, "Failed to request match for PropertiesChanged signal: %m");
384 : :
385 : 0 : r = sd_bus_call_method_async(
386 : : d->bus,
387 : 0 : &item->slot_get_all,
388 : : "org.freedesktop.systemd1",
389 : 0 : item->bus_path,
390 : : "org.freedesktop.DBus.Properties",
391 : : "GetAll",
392 : : on_get_all_properties,
393 : : item,
394 [ # # ]: 0 : "s", FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END) ? NULL : "org.freedesktop.systemd1.Unit");
395 [ # # ]: 0 : if (r < 0)
396 [ # # ]: 0 : return log_debug_errno(r, "Failed to request properties of unit %s: %m", unit);
397 : :
398 : 0 : r = hashmap_put(d->items, item->bus_path, item);
399 [ # # ]: 0 : if (r < 0)
400 : 0 : return r;
401 : :
402 : 0 : d->state = BUS_WAIT_RUNNING;
403 : 0 : item->parent = d;
404 : 0 : TAKE_PTR(item);
405 : 0 : return 0;
406 : : }
407 : :
408 : 0 : int bus_wait_for_units_run(BusWaitForUnits *d) {
409 : : int r;
410 : :
411 [ # # ]: 0 : assert(d);
412 : :
413 [ # # ]: 0 : while (d->state == BUS_WAIT_RUNNING) {
414 : :
415 : 0 : r = sd_bus_process(d->bus, NULL);
416 [ # # ]: 0 : if (r < 0)
417 : 0 : return r;
418 [ # # ]: 0 : if (r > 0)
419 : 0 : continue;
420 : :
421 : 0 : r = sd_bus_wait(d->bus, (uint64_t) -1);
422 [ # # ]: 0 : if (r < 0)
423 : 0 : return r;
424 : : }
425 : :
426 : 0 : return d->state;
427 : : }
428 : :
429 : 0 : BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d) {
430 [ # # ]: 0 : assert(d);
431 : :
432 : 0 : return d->state;
433 : : }
|