1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2013 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include "alloc-util.h"
24 #include "bus-internal.h"
25 #include "bus-track.h"
36 unsigned n_adding
; /* are we in the process of adding a new name? */
38 sd_bus_track_handler_t handler
;
41 LIST_FIELDS(sd_bus_track
, queue
);
43 bool in_list
:1; /* In bus->tracks? */
44 bool in_queue
:1; /* In bus->track_queue? */
48 LIST_FIELDS(sd_bus_track
, tracks
);
51 #define MATCH_FOR_NAME(name) \
52 strjoina("type='signal'," \
53 "sender='org.freedesktop.DBus'," \
54 "path='/org/freedesktop/DBus'," \
55 "interface='org.freedesktop.DBus'," \
56 "member='NameOwnerChanged'," \
59 static struct track_item
* track_item_free(struct track_item
*i
) {
64 sd_bus_slot_unref(i
->slot
);
69 DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item
*, track_item_free
);
71 static void bus_track_add_to_queue(sd_bus_track
*track
) {
74 /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
77 /* Already in the queue? */
81 /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
82 * until the addition is complete. */
83 if (track
->n_adding
> 0)
86 /* still referenced? */
87 if (hashmap_size(track
->names
) > 0)
90 /* Nothing to call? */
98 LIST_PREPEND(queue
, track
->bus
->track_queue
, track
);
99 track
->in_queue
= true;
102 static void bus_track_remove_from_queue(sd_bus_track
*track
) {
105 if (!track
->in_queue
)
108 LIST_REMOVE(queue
, track
->bus
->track_queue
, track
);
109 track
->in_queue
= false;
112 static int bus_track_remove_name_fully(sd_bus_track
*track
, const char *name
) {
113 struct track_item
*i
;
118 i
= hashmap_remove(track
->names
, name
);
124 bus_track_add_to_queue(track
);
126 track
->modified
= true;
130 _public_
int sd_bus_track_new(
132 sd_bus_track
**track
,
133 sd_bus_track_handler_t handler
,
138 assert_return(bus
, -EINVAL
);
139 assert_return(bus
= bus_resolve(bus
), -ENOPKG
);
140 assert_return(track
, -EINVAL
);
142 if (!bus
->bus_client
)
145 t
= new0(sd_bus_track
, 1);
150 t
->handler
= handler
;
151 t
->userdata
= userdata
;
152 t
->bus
= sd_bus_ref(bus
);
154 LIST_PREPEND(tracks
, bus
->tracks
, t
);
157 bus_track_add_to_queue(t
);
163 _public_ sd_bus_track
* sd_bus_track_ref(sd_bus_track
*track
) {
168 assert(track
->n_ref
> 0);
175 _public_ sd_bus_track
* sd_bus_track_unref(sd_bus_track
*track
) {
179 assert(track
->n_ref
> 0);
181 if (track
->n_ref
> 1) {
187 LIST_REMOVE(tracks
, track
->bus
->tracks
, track
);
189 bus_track_remove_from_queue(track
);
190 hashmap_free_with_destructor(track
->names
, track_item_free
);
191 sd_bus_unref(track
->bus
);
195 static int on_name_owner_changed(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
196 sd_bus_track
*track
= userdata
;
197 const char *name
, *old
, *new;
203 r
= sd_bus_message_read(message
, "sss", &name
, &old
, &new);
207 bus_track_remove_name_fully(track
, name
);
211 _public_
int sd_bus_track_add_name(sd_bus_track
*track
, const char *name
) {
212 _cleanup_(track_item_freep
) struct track_item
*n
= NULL
;
213 struct track_item
*i
;
217 assert_return(track
, -EINVAL
);
218 assert_return(service_name_is_valid(name
), -EINVAL
);
220 i
= hashmap_get(track
->names
, name
);
222 if (track
->recursive
) {
223 unsigned k
= track
->n_ref
+ 1;
225 if (k
< track
->n_ref
) /* Check for overflow */
231 bus_track_remove_from_queue(track
);
235 r
= hashmap_ensure_allocated(&track
->names
, &string_hash_ops
);
239 n
= new0(struct track_item
, 1);
242 n
->name
= strdup(name
);
246 /* First, subscribe to this name */
247 match
= MATCH_FOR_NAME(name
);
249 bus_track_remove_from_queue(track
); /* don't dispatch this while we work in it */
251 r
= sd_bus_add_match_async(track
->bus
, &n
->slot
, match
, on_name_owner_changed
, NULL
, track
);
253 bus_track_add_to_queue(track
);
257 r
= hashmap_put(track
->names
, n
->name
, n
);
259 bus_track_add_to_queue(track
);
263 /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
264 track
->n_adding
++; /* again, make sure this isn't dispatch while we are working in it */
265 r
= sd_bus_get_name_creds(track
->bus
, name
, 0, NULL
);
268 hashmap_remove(track
->names
, name
);
269 bus_track_add_to_queue(track
);
276 bus_track_remove_from_queue(track
);
277 track
->modified
= true;
282 _public_
int sd_bus_track_remove_name(sd_bus_track
*track
, const char *name
) {
283 struct track_item
*i
;
285 assert_return(name
, -EINVAL
);
287 if (!track
) /* Treat a NULL track object as an empty track object */
290 if (!track
->recursive
)
291 return bus_track_remove_name_fully(track
, name
);
293 i
= hashmap_get(track
->names
, name
);
302 return bus_track_remove_name_fully(track
, name
);
307 _public_
unsigned sd_bus_track_count(sd_bus_track
*track
) {
309 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
312 /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
313 * that this returns the number of names being watched, and multiple references to the same name are not
316 return hashmap_size(track
->names
);
319 _public_
const char* sd_bus_track_contains(sd_bus_track
*track
, const char *name
) {
320 assert_return(name
, NULL
);
322 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
325 return hashmap_get(track
->names
, (void*) name
) ? name
: NULL
;
328 _public_
const char* sd_bus_track_first(sd_bus_track
*track
) {
329 const char *n
= NULL
;
334 track
->modified
= false;
335 track
->iterator
= ITERATOR_FIRST
;
337 hashmap_iterate(track
->names
, &track
->iterator
, NULL
, (const void**) &n
);
341 _public_
const char* sd_bus_track_next(sd_bus_track
*track
) {
342 const char *n
= NULL
;
350 hashmap_iterate(track
->names
, &track
->iterator
, NULL
, (const void**) &n
);
354 _public_
int sd_bus_track_add_sender(sd_bus_track
*track
, sd_bus_message
*m
) {
357 assert_return(track
, -EINVAL
);
358 assert_return(m
, -EINVAL
);
360 if (sd_bus_message_get_bus(m
) != track
->bus
)
363 sender
= sd_bus_message_get_sender(m
);
367 return sd_bus_track_add_name(track
, sender
);
370 _public_
int sd_bus_track_remove_sender(sd_bus_track
*track
, sd_bus_message
*m
) {
373 assert_return(m
, -EINVAL
);
375 if (!track
) /* Treat a NULL track object as an empty track object */
378 if (sd_bus_message_get_bus(m
) != track
->bus
)
381 sender
= sd_bus_message_get_sender(m
);
385 return sd_bus_track_remove_name(track
, sender
);
388 _public_ sd_bus
* sd_bus_track_get_bus(sd_bus_track
*track
) {
389 assert_return(track
, NULL
);
394 void bus_track_dispatch(sd_bus_track
*track
) {
398 assert(track
->handler
);
400 bus_track_remove_from_queue(track
);
402 sd_bus_track_ref(track
);
404 r
= track
->handler(track
, track
->userdata
);
406 log_debug_errno(r
, "Failed to process track handler: %m");
408 bus_track_add_to_queue(track
);
410 sd_bus_track_unref(track
);
413 void bus_track_close(sd_bus_track
*track
) {
416 /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
417 * immediately, as we are closing now, but first flush out all names. */
420 return; /* We already closed this one, don't close it again. */
422 /* Remember that this one is closed now */
423 LIST_REMOVE(tracks
, track
->bus
->tracks
, track
);
424 track
->in_list
= false;
426 /* If there's no name in this one anyway, we don't have to dispatch */
427 if (hashmap_isempty(track
->names
))
430 /* Let's flush out all names */
431 hashmap_clear_with_destructor(track
->names
, track_item_free
);
435 bus_track_dispatch(track
);
438 _public_
void *sd_bus_track_get_userdata(sd_bus_track
*track
) {
439 assert_return(track
, NULL
);
441 return track
->userdata
;
444 _public_
void *sd_bus_track_set_userdata(sd_bus_track
*track
, void *userdata
) {
447 assert_return(track
, NULL
);
449 ret
= track
->userdata
;
450 track
->userdata
= userdata
;
455 _public_
int sd_bus_track_set_recursive(sd_bus_track
*track
, int b
) {
456 assert_return(track
, -EINVAL
);
458 if (track
->recursive
== !!b
)
461 if (!hashmap_isempty(track
->names
))
464 track
->recursive
= b
;
468 _public_
int sd_bus_track_get_recursive(sd_bus_track
*track
) {
469 assert_return(track
, -EINVAL
);
471 return track
->recursive
;
474 _public_
int sd_bus_track_count_sender(sd_bus_track
*track
, sd_bus_message
*m
) {
477 assert_return(m
, -EINVAL
);
479 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
482 if (sd_bus_message_get_bus(m
) != track
->bus
)
485 sender
= sd_bus_message_get_sender(m
);
489 return sd_bus_track_count_name(track
, sender
);
492 _public_
int sd_bus_track_count_name(sd_bus_track
*track
, const char *name
) {
493 struct track_item
*i
;
495 assert_return(service_name_is_valid(name
), -EINVAL
);
497 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
500 i
= hashmap_get(track
->names
, name
);