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