1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 #include "bus-map-properties.h"
4 #include "bus-wait-for-units.h"
6 #include "string-util.h"
10 typedef struct WaitForItem
{
11 BusWaitForUnits
*parent
;
13 BusWaitForUnitsFlags flags
;
17 sd_bus_slot
*slot_get_all
;
18 sd_bus_slot
*slot_properties_changed
;
20 bus_wait_for_units_unit_callback unit_callback
;
28 typedef struct BusWaitForUnits
{
30 sd_bus_slot
*slot_disconnected
;
34 bus_wait_for_units_ready_callback ready_callback
;
39 BusWaitForUnitsState state
;
43 static WaitForItem
*wait_for_item_free(WaitForItem
*item
) {
50 if (FLAGS_SET(item
->flags
, BUS_WAIT_REFFED
) && item
->bus_path
&& item
->parent
->bus
) {
51 r
= sd_bus_call_method_async(
54 "org.freedesktop.systemd1",
56 "org.freedesktop.systemd1.Unit",
62 log_debug_errno(r
, "Failed to drop reference to unit %s, ignoring: %m", item
->bus_path
);
65 assert_se(hashmap_remove(item
->parent
->items
, item
->bus_path
) == item
);
67 if (item
->parent
->current
== item
)
68 item
->parent
->current
= NULL
;
71 sd_bus_slot_unref(item
->slot_properties_changed
);
72 sd_bus_slot_unref(item
->slot_get_all
);
75 free(item
->active_state
);
76 free(item
->clean_result
);
81 DEFINE_TRIVIAL_CLEANUP_FUNC(WaitForItem
*, wait_for_item_free
);
83 static void call_unit_callback_and_wait(BusWaitForUnits
*d
, WaitForItem
*item
, bool good
) {
86 if (item
->unit_callback
)
87 item
->unit_callback(d
, item
->bus_path
, good
, item
->userdata
);
89 wait_for_item_free(item
);
92 static void bus_wait_for_units_clear(BusWaitForUnits
*d
) {
97 d
->slot_disconnected
= sd_bus_slot_unref(d
->slot_disconnected
);
98 d
->bus
= sd_bus_unref(d
->bus
);
100 while ((item
= hashmap_first(d
->items
)))
101 call_unit_callback_and_wait(d
, item
, false);
103 d
->items
= hashmap_free(d
->items
);
106 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
107 BusWaitForUnits
*d
= userdata
;
112 log_error("Warning! D-Bus connection terminated.");
114 bus_wait_for_units_clear(d
);
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
));
124 int bus_wait_for_units_new(sd_bus
*bus
, BusWaitForUnits
**ret
) {
125 _cleanup_(bus_wait_for_units_freep
) BusWaitForUnits
*d
= NULL
;
131 d
= new(BusWaitForUnits
, 1);
135 *d
= (BusWaitForUnits
) {
136 .state
= BUS_WAIT_SUCCESS
,
137 .bus
= sd_bus_ref(bus
),
140 r
= sd_bus_match_signal_async(
142 &d
->slot_disconnected
,
143 "org.freedesktop.DBus.Local",
145 "org.freedesktop.DBus.Local",
147 match_disconnected
, NULL
, d
);
155 BusWaitForUnits
* bus_wait_for_units_free(BusWaitForUnits
*d
) {
159 bus_wait_for_units_clear(d
);
160 sd_bus_slot_unref(d
->slot_disconnected
);
161 sd_bus_unref(d
->bus
);
166 static bool bus_wait_for_units_is_ready(BusWaitForUnits
*d
) {
169 if (!d
->bus
) /* Disconnected? */
172 return hashmap_isempty(d
->items
);
175 void bus_wait_for_units_set_ready_callback(BusWaitForUnits
*d
, bus_wait_for_units_ready_callback callback
, void *userdata
) {
178 d
->ready_callback
= callback
;
179 d
->userdata
= userdata
;
182 static void bus_wait_for_units_check_ready(BusWaitForUnits
*d
) {
185 if (!bus_wait_for_units_is_ready(d
))
188 d
->state
= d
->has_failed
? BUS_WAIT_FAILURE
: BUS_WAIT_SUCCESS
;
190 if (d
->ready_callback
)
191 d
->ready_callback(d
, d
->state
, d
->userdata
);
194 static void wait_for_item_check_ready(WaitForItem
*item
) {
198 assert_se(d
= item
->parent
);
200 if (FLAGS_SET(item
->flags
, BUS_WAIT_FOR_MAINTENANCE_END
)) {
202 if (item
->clean_result
&& !streq(item
->clean_result
, "success"))
203 d
->has_failed
= true;
205 if (!item
->active_state
|| streq(item
->active_state
, "maintenance"))
209 if (FLAGS_SET(item
->flags
, BUS_WAIT_NO_JOB
) && item
->job_id
!= 0)
212 if (FLAGS_SET(item
->flags
, BUS_WAIT_FOR_INACTIVE
)) {
214 if (streq_ptr(item
->active_state
, "failed"))
215 d
->has_failed
= true;
216 else if (!streq_ptr(item
->active_state
, "inactive"))
220 call_unit_callback_and_wait(d
, item
, true);
221 bus_wait_for_units_check_ready(d
);
224 static int property_map_job(
231 WaitForItem
*item
= userdata
;
238 r
= sd_bus_message_read(m
, "(uo)", &id
, &path
);
246 static int wait_for_item_parse_properties(WaitForItem
*item
, sd_bus_message
*m
) {
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
) },
260 r
= bus_message_map_all_properties(m
, map
, BUS_MAP_STRDUP
, NULL
, item
);
264 wait_for_item_check_ready(item
);
268 static int on_properties_changed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
269 WaitForItem
*item
= userdata
;
270 const char *interface
;
275 r
= sd_bus_message_read(m
, "s", &interface
);
277 log_debug_errno(r
, "Failed to parse PropertiesChanged signal: %m");
281 if (!streq(interface
, "org.freedesktop.systemd1.Unit"))
284 r
= wait_for_item_parse_properties(item
, m
);
286 log_debug_errno(r
, "Failed to process PropertiesChanged signal: %m");
291 static int on_get_all_properties(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
292 WaitForItem
*item
= userdata
;
297 if (sd_bus_error_is_set(error
)) {
298 BusWaitForUnits
*d
= item
->parent
;
300 d
->has_failed
= true;
302 log_debug_errno(sd_bus_error_get_errno(error
), "GetAll() failed for %s: %s",
303 item
->bus_path
, error
->message
);
305 call_unit_callback_and_wait(d
, item
, false);
306 bus_wait_for_units_check_ready(d
);
310 r
= wait_for_item_parse_properties(item
, m
);
312 log_debug_errno(r
, "Failed to process GetAll method reply: %m");
317 int bus_wait_for_units_add_unit(
320 BusWaitForUnitsFlags flags
,
321 bus_wait_for_units_unit_callback callback
,
324 _cleanup_(wait_for_item_freep
) WaitForItem
*item
= NULL
;
332 r
= hashmap_ensure_allocated(&d
->items
, &string_hash_ops
);
336 item
= new(WaitForItem
, 1);
340 *item
= (WaitForItem
) {
342 .bus_path
= unit_dbus_path_from_name(unit
),
343 .unit_callback
= callback
,
344 .userdata
= userdata
,
345 .job_id
= UINT32_MAX
,
351 if (!FLAGS_SET(item
->flags
, BUS_WAIT_REFFED
)) {
352 r
= sd_bus_call_method_async(
355 "org.freedesktop.systemd1",
357 "org.freedesktop.systemd1.Unit",
363 return log_debug_errno(r
, "Failed to add reference to unit %s: %m", unit
);
365 item
->flags
|= BUS_WAIT_REFFED
;
368 r
= sd_bus_match_signal_async(
370 &item
->slot_properties_changed
,
371 "org.freedesktop.systemd1",
373 "org.freedesktop.DBus.Properties",
375 on_properties_changed
,
379 return log_debug_errno(r
, "Failed to request match for PropertiesChanged signal: %m");
381 r
= sd_bus_call_method_async(
384 "org.freedesktop.systemd1",
386 "org.freedesktop.DBus.Properties",
388 on_get_all_properties
,
390 "s", FLAGS_SET(item
->flags
, BUS_WAIT_FOR_MAINTENANCE_END
) ? NULL
: "org.freedesktop.systemd1.Unit");
392 return log_debug_errno(r
, "Failed to request properties of unit %s: %m", unit
);
394 r
= hashmap_put(d
->items
, item
->bus_path
, item
);
398 d
->state
= BUS_WAIT_RUNNING
;
404 int bus_wait_for_units_run(BusWaitForUnits
*d
) {
409 while (d
->state
== BUS_WAIT_RUNNING
) {
411 r
= sd_bus_process(d
->bus
, NULL
);
417 r
= sd_bus_wait(d
->bus
, (uint64_t) -1);
425 BusWaitForUnitsState
bus_wait_for_units_state(BusWaitForUnits
*d
) {