1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
6 #include "bus-internal.h"
8 #include "string-util.h"
18 unsigned n_adding
; /* are we in the process of adding a new name? */
20 sd_bus_track_handler_t handler
;
23 LIST_FIELDS(sd_bus_track
, queue
);
25 bool in_list
:1; /* In bus->tracks? */
26 bool in_queue
:1; /* In bus->track_queue? */
29 sd_bus_destroy_t destroy_callback
;
31 LIST_FIELDS(sd_bus_track
, tracks
);
34 #define MATCH_FOR_NAME(name) \
35 strjoina("type='signal'," \
36 "sender='org.freedesktop.DBus'," \
37 "path='/org/freedesktop/DBus'," \
38 "interface='org.freedesktop.DBus'," \
39 "member='NameOwnerChanged'," \
42 static struct track_item
* track_item_free(struct track_item
*i
) {
46 sd_bus_slot_unref(i
->slot
);
51 DEFINE_PRIVATE_TRIVIAL_UNREF_FUNC(struct track_item
, track_item
, track_item_free
);
52 DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item
*, track_item_unref
);
53 DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(track_item_hash_ops
, char, string_hash_func
, string_compare_func
,
54 struct track_item
, track_item_free
);
56 static void bus_track_add_to_queue(sd_bus_track
*track
) {
59 /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
62 /* Already in the queue? */
66 /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
67 * until the addition is complete. */
68 if (track
->n_adding
> 0)
71 /* still referenced? */
72 if (hashmap_size(track
->names
) > 0)
75 /* Nothing to call? */
83 LIST_PREPEND(queue
, track
->bus
->track_queue
, track
);
84 track
->in_queue
= true;
87 static void bus_track_remove_from_queue(sd_bus_track
*track
) {
93 LIST_REMOVE(queue
, track
->bus
->track_queue
, track
);
94 track
->in_queue
= false;
97 static int bus_track_remove_name_fully(sd_bus_track
*track
, const char *name
) {
103 i
= hashmap_remove(track
->names
, name
);
109 bus_track_add_to_queue(track
);
111 track
->modified
= true;
115 _public_
int sd_bus_track_new(
117 sd_bus_track
**track
,
118 sd_bus_track_handler_t handler
,
123 assert_return(bus
, -EINVAL
);
124 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
125 assert_return(track
, -EINVAL
);
127 if (!bus
->bus_client
)
130 t
= new0(sd_bus_track
, 1);
135 t
->handler
= handler
;
136 t
->userdata
= userdata
;
137 t
->bus
= sd_bus_ref(bus
);
139 LIST_PREPEND(tracks
, bus
->tracks
, t
);
142 bus_track_add_to_queue(t
);
148 static sd_bus_track
*track_free(sd_bus_track
*track
) {
152 LIST_REMOVE(tracks
, track
->bus
->tracks
, track
);
154 bus_track_remove_from_queue(track
);
155 track
->names
= hashmap_free(track
->names
);
156 track
->bus
= sd_bus_unref(track
->bus
);
158 if (track
->destroy_callback
)
159 track
->destroy_callback(track
->userdata
);
164 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_track
, sd_bus_track
, track_free
);
166 static int on_name_owner_changed(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
167 sd_bus_track
*track
= ASSERT_PTR(userdata
);
173 r
= sd_bus_message_read(message
, "sss", &name
, NULL
, NULL
);
177 bus_track_remove_name_fully(track
, name
);
181 _public_
int sd_bus_track_add_name(sd_bus_track
*track
, const char *name
) {
182 _cleanup_(track_item_unrefp
) struct track_item
*n
= NULL
;
183 struct track_item
*i
;
187 assert_return(track
, -EINVAL
);
188 assert_return(service_name_is_valid(name
), -EINVAL
);
190 i
= hashmap_get(track
->names
, name
);
192 if (track
->recursive
) {
193 assert(i
->n_ref
> 0);
195 /* Manual overflow check (instead of a DEFINE_TRIVIAL_REF_FUNC() helper or so), so
196 * that we can return a proper error, given this is almost always called in a
197 * directly client controllable way, and thus better should never hit an assertion
199 if (i
->n_ref
>= UINT_MAX
)
205 bus_track_remove_from_queue(track
);
209 r
= hashmap_ensure_allocated(&track
->names
, &track_item_hash_ops
);
213 n
= new(struct track_item
, 1);
217 *n
= (struct track_item
) {
221 n
->name
= strdup(name
);
225 /* First, subscribe to this name */
226 match
= MATCH_FOR_NAME(name
);
228 bus_track_remove_from_queue(track
); /* don't dispatch this while we work in it */
230 r
= sd_bus_add_match_async(track
->bus
, &n
->slot
, match
, on_name_owner_changed
, NULL
, track
);
232 bus_track_add_to_queue(track
);
236 r
= hashmap_put(track
->names
, n
->name
, n
);
238 bus_track_add_to_queue(track
);
242 /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
243 track
->n_adding
++; /* again, make sure this isn't dispatch while we are working in it */
244 r
= sd_bus_get_name_creds(track
->bus
, name
, 0, NULL
);
247 hashmap_remove(track
->names
, name
);
248 bus_track_add_to_queue(track
);
254 bus_track_remove_from_queue(track
);
255 track
->modified
= true;
260 _public_
int sd_bus_track_remove_name(sd_bus_track
*track
, const char *name
) {
261 struct track_item
*i
;
263 assert_return(name
, -EINVAL
);
265 if (!track
) /* Treat a NULL track object as an empty track object */
268 i
= hashmap_get(track
->names
, name
);
272 assert(i
->n_ref
>= 1);
274 return bus_track_remove_name_fully(track
, name
);
281 _public_
unsigned sd_bus_track_count(sd_bus_track
*track
) {
283 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
286 /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
287 * that this returns the number of names being watched, and multiple references to the same name are not
290 return hashmap_size(track
->names
);
293 _public_
const char* sd_bus_track_contains(sd_bus_track
*track
, const char *name
) {
294 assert_return(name
, NULL
);
296 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
299 return hashmap_contains(track
->names
, name
) ? name
: NULL
;
302 _public_
const char* sd_bus_track_first(sd_bus_track
*track
) {
303 const char *n
= NULL
;
308 track
->modified
= false;
309 track
->iterator
= ITERATOR_FIRST
;
311 (void) hashmap_iterate(track
->names
, &track
->iterator
, NULL
, (const void**) &n
);
315 _public_
const char* sd_bus_track_next(sd_bus_track
*track
) {
316 const char *n
= NULL
;
324 (void) hashmap_iterate(track
->names
, &track
->iterator
, NULL
, (const void**) &n
);
328 _public_
int sd_bus_track_add_sender(sd_bus_track
*track
, sd_bus_message
*m
) {
331 assert_return(track
, -EINVAL
);
332 assert_return(m
, -EINVAL
);
334 if (sd_bus_message_get_bus(m
) != track
->bus
)
337 sender
= sd_bus_message_get_sender(m
);
341 return sd_bus_track_add_name(track
, sender
);
344 _public_
int sd_bus_track_remove_sender(sd_bus_track
*track
, sd_bus_message
*m
) {
347 assert_return(m
, -EINVAL
);
349 if (!track
) /* Treat a NULL track object as an empty track object */
352 if (sd_bus_message_get_bus(m
) != track
->bus
)
355 sender
= sd_bus_message_get_sender(m
);
359 return sd_bus_track_remove_name(track
, sender
);
362 _public_ sd_bus
* sd_bus_track_get_bus(sd_bus_track
*track
) {
363 assert_return(track
, NULL
);
368 void bus_track_dispatch(sd_bus_track
*track
) {
372 assert(track
->handler
);
374 bus_track_remove_from_queue(track
);
376 sd_bus_track_ref(track
);
378 r
= track
->handler(track
, track
->userdata
);
380 log_debug_errno(r
, "Failed to process track handler: %m");
382 bus_track_add_to_queue(track
);
384 sd_bus_track_unref(track
);
387 void bus_track_close(sd_bus_track
*track
) {
390 /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
391 * immediately, as we are closing now, but first flush out all names. */
394 return; /* We already closed this one, don't close it again. */
396 /* Remember that this one is closed now */
397 LIST_REMOVE(tracks
, track
->bus
->tracks
, track
);
398 track
->in_list
= false;
400 /* If there's no name in this one anyway, we don't have to dispatch */
401 if (hashmap_isempty(track
->names
))
404 /* Let's flush out all names */
405 hashmap_clear(track
->names
);
409 bus_track_dispatch(track
);
412 _public_
void *sd_bus_track_get_userdata(sd_bus_track
*track
) {
413 assert_return(track
, NULL
);
415 return track
->userdata
;
418 _public_
void *sd_bus_track_set_userdata(sd_bus_track
*track
, void *userdata
) {
421 assert_return(track
, NULL
);
423 ret
= track
->userdata
;
424 track
->userdata
= userdata
;
429 _public_
int sd_bus_track_set_destroy_callback(sd_bus_track
*track
, sd_bus_destroy_t callback
) {
430 assert_return(track
, -EINVAL
);
432 track
->destroy_callback
= callback
;
436 _public_
int sd_bus_track_get_destroy_callback(sd_bus_track
*track
, sd_bus_destroy_t
*ret
) {
437 assert_return(track
, -EINVAL
);
440 *ret
= track
->destroy_callback
;
442 return !!track
->destroy_callback
;
445 _public_
int sd_bus_track_set_recursive(sd_bus_track
*track
, int b
) {
446 assert_return(track
, -EINVAL
);
448 if (track
->recursive
== !!b
)
451 if (!hashmap_isempty(track
->names
))
454 track
->recursive
= b
;
458 _public_
int sd_bus_track_get_recursive(sd_bus_track
*track
) {
459 assert_return(track
, -EINVAL
);
461 return track
->recursive
;
464 _public_
int sd_bus_track_count_sender(sd_bus_track
*track
, sd_bus_message
*m
) {
467 assert_return(m
, -EINVAL
);
469 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
472 if (sd_bus_message_get_bus(m
) != track
->bus
)
475 sender
= sd_bus_message_get_sender(m
);
479 return sd_bus_track_count_name(track
, sender
);
482 _public_
int sd_bus_track_count_name(sd_bus_track
*track
, const char *name
) {
483 struct track_item
*i
;
485 assert_return(service_name_is_valid(name
), -EINVAL
);
487 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
490 i
= hashmap_get(track
->names
, name
);