1 /* SPDX-License-Identifier: LGPL-2.1+ */
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 bus_wait_for_units_clear(BusWaitForUnits
*d
) {
88 d
->slot_disconnected
= sd_bus_slot_unref(d
->slot_disconnected
);
89 d
->bus
= sd_bus_unref(d
->bus
);
91 while ((item
= hashmap_first(d
->items
))) {
94 item
->unit_callback(d
, item
->bus_path
, false, item
->userdata
);
95 wait_for_item_free(item
);
98 d
->items
= hashmap_free(d
->items
);
101 static int match_disconnected(sd_bus_message
*m
, void *userdata
, sd_bus_error
*error
) {
102 BusWaitForUnits
*d
= userdata
;
107 log_error("Warning! D-Bus connection terminated.");
109 bus_wait_for_units_clear(d
);
111 if (d
->ready_callback
)
112 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 sd_bus_close(sd_bus_message_get_bus(m
));
119 int bus_wait_for_units_new(sd_bus
*bus
, BusWaitForUnits
**ret
) {
120 _cleanup_(bus_wait_for_units_freep
) BusWaitForUnits
*d
= NULL
;
126 d
= new(BusWaitForUnits
, 1);
130 *d
= (BusWaitForUnits
) {
131 .state
= BUS_WAIT_SUCCESS
,
132 .bus
= sd_bus_ref(bus
),
135 r
= sd_bus_match_signal_async(
137 &d
->slot_disconnected
,
138 "org.freedesktop.DBus.Local",
140 "org.freedesktop.DBus.Local",
142 match_disconnected
, NULL
, d
);
150 BusWaitForUnits
* bus_wait_for_units_free(BusWaitForUnits
*d
) {
154 bus_wait_for_units_clear(d
);
155 sd_bus_slot_unref(d
->slot_disconnected
);
156 sd_bus_unref(d
->bus
);
161 static bool bus_wait_for_units_is_ready(BusWaitForUnits
*d
) {
164 if (!d
->bus
) /* Disconnected? */
167 return hashmap_isempty(d
->items
);
170 void bus_wait_for_units_set_ready_callback(BusWaitForUnits
*d
, bus_wait_for_units_ready_callback callback
, void *userdata
) {
173 d
->ready_callback
= callback
;
174 d
->userdata
= userdata
;
177 static void bus_wait_for_units_check_ready(BusWaitForUnits
*d
) {
180 if (!bus_wait_for_units_is_ready(d
))
183 d
->state
= d
->has_failed
? BUS_WAIT_FAILURE
: BUS_WAIT_SUCCESS
;
185 if (d
->ready_callback
)
186 d
->ready_callback(d
, d
->state
, d
->userdata
);
189 static void wait_for_item_check_ready(WaitForItem
*item
) {
193 assert(d
= item
->parent
);
195 if (FLAGS_SET(item
->flags
, BUS_WAIT_FOR_MAINTENANCE_END
)) {
197 if (item
->clean_result
&& !streq(item
->clean_result
, "success"))
198 d
->has_failed
= true;
200 if (!item
->active_state
|| streq(item
->active_state
, "maintenance"))
204 if (FLAGS_SET(item
->flags
, BUS_WAIT_NO_JOB
) && item
->job_id
!= 0)
207 if (FLAGS_SET(item
->flags
, BUS_WAIT_FOR_INACTIVE
)) {
209 if (streq_ptr(item
->active_state
, "failed"))
210 d
->has_failed
= true;
211 else if (!streq_ptr(item
->active_state
, "inactive"))
215 if (item
->unit_callback
) {
217 item
->unit_callback(d
, item
->bus_path
, true, item
->userdata
);
220 wait_for_item_free(item
);
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
*error
) {
293 WaitForItem
*item
= userdata
;
298 if (sd_bus_error_is_set(error
)) {
299 BusWaitForUnits
*d
= item
->parent
;
301 d
->has_failed
= true;
303 log_debug_errno(sd_bus_error_get_errno(error
), "GetAll() failed for %s: %s",
304 item
->bus_path
, error
->message
);
307 item
->unit_callback(d
, item
->bus_path
, false, item
->userdata
);
308 wait_for_item_free(item
);
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_t) -1);
429 BusWaitForUnitsState
bus_wait_for_units_state(BusWaitForUnits
*d
) {