1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "bus-map-properties.h"
5 #include "bus-wait-for-units.h"
7 #include "string-util.h"
11 typedef struct WaitForItem
{
12 BusWaitForUnits
*parent
;
14 BusWaitForUnitsFlags flags
;
18 sd_bus_slot
*slot_get_all
;
19 sd_bus_slot
*slot_properties_changed
;
21 bus_wait_for_units_unit_callback unit_callback
;
29 typedef struct BusWaitForUnits
{
31 sd_bus_slot
*slot_disconnected
;
35 bus_wait_for_units_ready_callback ready_callback
;
40 BusWaitForUnitsState state
;
44 static WaitForItem
*wait_for_item_free(WaitForItem
*item
) {
51 if (FLAGS_SET(item
->flags
, BUS_WAIT_REFFED
) && item
->bus_path
&& item
->parent
->bus
) {
52 r
= sd_bus_call_method_async(
55 "org.freedesktop.systemd1",
57 "org.freedesktop.systemd1.Unit",
63 log_debug_errno(r
, "Failed to drop reference to unit %s, ignoring: %m", item
->bus_path
);
66 assert_se(hashmap_remove(item
->parent
->items
, item
->bus_path
) == item
);
68 if (item
->parent
->current
== item
)
69 item
->parent
->current
= NULL
;
72 sd_bus_slot_unref(item
->slot_properties_changed
);
73 sd_bus_slot_unref(item
->slot_get_all
);
76 free(item
->active_state
);
77 free(item
->clean_result
);
82 DEFINE_TRIVIAL_CLEANUP_FUNC(WaitForItem
*, wait_for_item_free
);
84 static void call_unit_callback_and_wait(BusWaitForUnits
*d
, WaitForItem
*item
, bool good
) {
87 if (item
->unit_callback
)
88 item
->unit_callback(d
, item
->bus_path
, good
, item
->userdata
);
90 wait_for_item_free(item
);
93 static void bus_wait_for_units_clear(BusWaitForUnits
*d
) {
98 d
->slot_disconnected
= sd_bus_slot_unref(d
->slot_disconnected
);
99 d
->bus
= sd_bus_unref(d
->bus
);
101 while ((item
= hashmap_first(d
->items
)))
102 call_unit_callback_and_wait(d
, item
, false);
104 d
->items
= hashmap_free(d
->items
);
107 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
108 BusWaitForUnits
*d
= userdata
;
113 log_error("Warning! D-Bus connection terminated.");
115 bus_wait_for_units_clear(d
);
117 if (d
->ready_callback
)
118 d
->ready_callback(d
, false, d
->userdata
);
119 else /* If no ready callback is specified close the connection so that the event loop exits */
120 sd_bus_close(sd_bus_message_get_bus(m
));
125 int bus_wait_for_units_new(sd_bus
*bus
, BusWaitForUnits
**ret
) {
126 _cleanup_(bus_wait_for_units_freep
) BusWaitForUnits
*d
= NULL
;
132 d
= new(BusWaitForUnits
, 1);
136 *d
= (BusWaitForUnits
) {
137 .state
= BUS_WAIT_SUCCESS
,
138 .bus
= sd_bus_ref(bus
),
141 r
= sd_bus_match_signal_async(
143 &d
->slot_disconnected
,
144 "org.freedesktop.DBus.Local",
146 "org.freedesktop.DBus.Local",
148 match_disconnected
, NULL
, d
);
156 BusWaitForUnits
* bus_wait_for_units_free(BusWaitForUnits
*d
) {
160 bus_wait_for_units_clear(d
);
161 sd_bus_slot_unref(d
->slot_disconnected
);
162 sd_bus_unref(d
->bus
);
167 static bool bus_wait_for_units_is_ready(BusWaitForUnits
*d
) {
170 if (!d
->bus
) /* Disconnected? */
173 return hashmap_isempty(d
->items
);
176 void bus_wait_for_units_set_ready_callback(BusWaitForUnits
*d
, bus_wait_for_units_ready_callback callback
, void *userdata
) {
179 d
->ready_callback
= callback
;
180 d
->userdata
= userdata
;
183 static void bus_wait_for_units_check_ready(BusWaitForUnits
*d
) {
186 if (!bus_wait_for_units_is_ready(d
))
189 d
->state
= d
->has_failed
? BUS_WAIT_FAILURE
: BUS_WAIT_SUCCESS
;
191 if (d
->ready_callback
)
192 d
->ready_callback(d
, d
->state
, d
->userdata
);
195 static void wait_for_item_check_ready(WaitForItem
*item
) {
199 assert_se(d
= item
->parent
);
201 if (FLAGS_SET(item
->flags
, BUS_WAIT_FOR_MAINTENANCE_END
)) {
203 if (item
->clean_result
&& !streq(item
->clean_result
, "success"))
204 d
->has_failed
= true;
206 if (!item
->active_state
|| streq(item
->active_state
, "maintenance"))
210 if (FLAGS_SET(item
->flags
, BUS_WAIT_NO_JOB
) && item
->job_id
!= 0)
213 if (FLAGS_SET(item
->flags
, BUS_WAIT_FOR_INACTIVE
)) {
215 if (streq_ptr(item
->active_state
, "failed"))
216 d
->has_failed
= true;
217 else if (!streq_ptr(item
->active_state
, "inactive"))
221 call_unit_callback_and_wait(d
, item
, true);
222 bus_wait_for_units_check_ready(d
);
225 static int property_map_job(
232 WaitForItem
*item
= userdata
;
239 r
= sd_bus_message_read(m
, "(uo)", &id
, &path
);
247 static int wait_for_item_parse_properties(WaitForItem
*item
, sd_bus_message
*m
) {
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
) },
261 r
= bus_message_map_all_properties(m
, map
, BUS_MAP_STRDUP
, NULL
, item
);
265 wait_for_item_check_ready(item
);
269 static int on_properties_changed(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
270 WaitForItem
*item
= userdata
;
271 const char *interface
;
276 r
= sd_bus_message_read(m
, "s", &interface
);
278 log_debug_errno(r
, "Failed to parse PropertiesChanged signal: %m");
282 if (!streq(interface
, "org.freedesktop.systemd1.Unit"))
285 r
= wait_for_item_parse_properties(item
, m
);
287 log_debug_errno(r
, "Failed to process PropertiesChanged signal: %m");
292 static int on_get_all_properties(sd_bus_message
*m
, void *userdata
, sd_bus_error
*ret_error
) {
293 WaitForItem
*item
= userdata
;
294 const sd_bus_error
*e
;
299 e
= sd_bus_message_get_error(m
);
301 BusWaitForUnits
*d
= item
->parent
;
303 d
->has_failed
= true;
305 r
= sd_bus_error_get_errno(e
);
306 log_debug_errno(r
, "GetAll() failed for %s: %s",
307 item
->bus_path
, bus_error_message(e
, r
));
309 call_unit_callback_and_wait(d
, item
, false);
310 bus_wait_for_units_check_ready(d
);
314 r
= wait_for_item_parse_properties(item
, m
);
316 log_debug_errno(r
, "Failed to process GetAll method reply: %m");
321 int bus_wait_for_units_add_unit(
324 BusWaitForUnitsFlags flags
,
325 bus_wait_for_units_unit_callback callback
,
328 _cleanup_(wait_for_item_freep
) WaitForItem
*item
= NULL
;
336 r
= hashmap_ensure_allocated(&d
->items
, &string_hash_ops
);
340 item
= new(WaitForItem
, 1);
344 *item
= (WaitForItem
) {
346 .bus_path
= unit_dbus_path_from_name(unit
),
347 .unit_callback
= callback
,
348 .userdata
= userdata
,
349 .job_id
= UINT32_MAX
,
355 if (!FLAGS_SET(item
->flags
, BUS_WAIT_REFFED
)) {
356 r
= sd_bus_call_method_async(
359 "org.freedesktop.systemd1",
361 "org.freedesktop.systemd1.Unit",
367 return log_debug_errno(r
, "Failed to add reference to unit %s: %m", unit
);
369 item
->flags
|= BUS_WAIT_REFFED
;
372 r
= sd_bus_match_signal_async(
374 &item
->slot_properties_changed
,
375 "org.freedesktop.systemd1",
377 "org.freedesktop.DBus.Properties",
379 on_properties_changed
,
383 return log_debug_errno(r
, "Failed to request match for PropertiesChanged signal: %m");
385 r
= sd_bus_call_method_async(
388 "org.freedesktop.systemd1",
390 "org.freedesktop.DBus.Properties",
392 on_get_all_properties
,
394 "s", FLAGS_SET(item
->flags
, BUS_WAIT_FOR_MAINTENANCE_END
) ? NULL
: "org.freedesktop.systemd1.Unit");
396 return log_debug_errno(r
, "Failed to request properties of unit %s: %m", unit
);
398 r
= hashmap_put(d
->items
, item
->bus_path
, item
);
402 d
->state
= BUS_WAIT_RUNNING
;
408 int bus_wait_for_units_run(BusWaitForUnits
*d
) {
413 while (d
->state
== BUS_WAIT_RUNNING
) {
415 r
= sd_bus_process(d
->bus
, NULL
);
421 r
= sd_bus_wait(d
->bus
, UINT64_MAX
);
429 BusWaitForUnitsState
bus_wait_for_units_state(BusWaitForUnits
*d
) {