2 This file is part of systemd.
4 Copyright 2013 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "alloc-util.h"
23 #include "bus-internal.h"
24 #include "bus-track.h"
35 unsigned n_adding
; /* are we in the process of adding a new name? */
37 sd_bus_track_handler_t handler
;
40 LIST_FIELDS(sd_bus_track
, queue
);
42 bool in_list
:1; /* In bus->tracks? */
43 bool in_queue
:1; /* In bus->track_queue? */
47 LIST_FIELDS(sd_bus_track
, tracks
);
50 #define MATCH_PREFIX \
52 "sender='org.freedesktop.DBus'," \
53 "path='/org/freedesktop/DBus'," \
54 "interface='org.freedesktop.DBus'," \
55 "member='NameOwnerChanged'," \
58 #define MATCH_SUFFIX \
61 #define MATCH_FOR_NAME(name) \
64 size_t _l = strlen(name); \
65 _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \
66 strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
70 static struct track_item
* track_item_free(struct track_item
*i
) {
75 sd_bus_slot_unref(i
->slot
);
80 DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item
*, track_item_free
);
82 static void bus_track_add_to_queue(sd_bus_track
*track
) {
85 /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
88 /* Already in the queue? */
92 /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
93 * until the addition is complete. */
94 if (track
->n_adding
> 0)
97 /* still referenced? */
98 if (hashmap_size(track
->names
) > 0)
101 /* Nothing to call? */
105 /* Already closed? */
109 LIST_PREPEND(queue
, track
->bus
->track_queue
, track
);
110 track
->in_queue
= true;
113 static void bus_track_remove_from_queue(sd_bus_track
*track
) {
116 if (!track
->in_queue
)
119 LIST_REMOVE(queue
, track
->bus
->track_queue
, track
);
120 track
->in_queue
= false;
123 static int bus_track_remove_name_fully(sd_bus_track
*track
, const char *name
) {
124 struct track_item
*i
;
129 i
= hashmap_remove(track
->names
, name
);
135 bus_track_add_to_queue(track
);
137 track
->modified
= true;
141 _public_
int sd_bus_track_new(
143 sd_bus_track
**track
,
144 sd_bus_track_handler_t handler
,
149 assert_return(bus
, -EINVAL
);
150 assert_return(track
, -EINVAL
);
152 if (!bus
->bus_client
)
155 t
= new0(sd_bus_track
, 1);
160 t
->handler
= handler
;
161 t
->userdata
= userdata
;
162 t
->bus
= sd_bus_ref(bus
);
164 LIST_PREPEND(tracks
, bus
->tracks
, t
);
167 bus_track_add_to_queue(t
);
173 _public_ sd_bus_track
* sd_bus_track_ref(sd_bus_track
*track
) {
178 assert(track
->n_ref
> 0);
185 _public_ sd_bus_track
* sd_bus_track_unref(sd_bus_track
*track
) {
186 struct track_item
*i
;
191 assert(track
->n_ref
> 0);
193 if (track
->n_ref
> 1) {
198 while ((i
= hashmap_steal_first(track
->names
)))
202 LIST_REMOVE(tracks
, track
->bus
->tracks
, track
);
204 bus_track_remove_from_queue(track
);
205 hashmap_free(track
->names
);
206 sd_bus_unref(track
->bus
);
210 static int on_name_owner_changed(sd_bus_message
*message
, void *userdata
, sd_bus_error
*error
) {
211 sd_bus_track
*track
= userdata
;
212 const char *name
, *old
, *new;
218 r
= sd_bus_message_read(message
, "sss", &name
, &old
, &new);
222 bus_track_remove_name_fully(track
, name
);
226 _public_
int sd_bus_track_add_name(sd_bus_track
*track
, const char *name
) {
227 _cleanup_(track_item_freep
) struct track_item
*n
= NULL
;
228 struct track_item
*i
;
232 assert_return(track
, -EINVAL
);
233 assert_return(service_name_is_valid(name
), -EINVAL
);
235 i
= hashmap_get(track
->names
, name
);
237 if (track
->recursive
) {
238 unsigned k
= track
->n_ref
+ 1;
240 if (k
< track
->n_ref
) /* Check for overflow */
246 bus_track_remove_from_queue(track
);
250 r
= hashmap_ensure_allocated(&track
->names
, &string_hash_ops
);
254 n
= new0(struct track_item
, 1);
257 n
->name
= strdup(name
);
261 /* First, subscribe to this name */
262 match
= MATCH_FOR_NAME(name
);
264 bus_track_remove_from_queue(track
); /* don't dispatch this while we work in it */
266 track
->n_adding
++; /* make sure we aren't dispatched while we synchronously add this match */
267 r
= sd_bus_add_match(track
->bus
, &n
->slot
, match
, on_name_owner_changed
, track
);
270 bus_track_add_to_queue(track
);
274 r
= hashmap_put(track
->names
, n
->name
, n
);
276 bus_track_add_to_queue(track
);
280 /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
281 track
->n_adding
++; /* again, make sure this isn't dispatch while we are working in it */
282 r
= sd_bus_get_name_creds(track
->bus
, name
, 0, NULL
);
285 hashmap_remove(track
->names
, name
);
286 bus_track_add_to_queue(track
);
293 bus_track_remove_from_queue(track
);
294 track
->modified
= true;
299 _public_
int sd_bus_track_remove_name(sd_bus_track
*track
, const char *name
) {
300 struct track_item
*i
;
302 assert_return(name
, -EINVAL
);
304 if (!track
) /* Treat a NULL track object as an empty track object */
307 if (!track
->recursive
)
308 return bus_track_remove_name_fully(track
, name
);
310 i
= hashmap_get(track
->names
, name
);
319 return bus_track_remove_name_fully(track
, name
);
324 _public_
unsigned sd_bus_track_count(sd_bus_track
*track
) {
326 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
329 /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
330 * that this returns the number of names being watched, and multiple references to the same name are not
333 return hashmap_size(track
->names
);
336 _public_
const char* sd_bus_track_contains(sd_bus_track
*track
, const char *name
) {
337 assert_return(name
, NULL
);
339 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
342 return hashmap_get(track
->names
, (void*) name
) ? name
: NULL
;
345 _public_
const char* sd_bus_track_first(sd_bus_track
*track
) {
346 const char *n
= NULL
;
351 track
->modified
= false;
352 track
->iterator
= ITERATOR_FIRST
;
354 hashmap_iterate(track
->names
, &track
->iterator
, NULL
, (const void**) &n
);
358 _public_
const char* sd_bus_track_next(sd_bus_track
*track
) {
359 const char *n
= NULL
;
367 hashmap_iterate(track
->names
, &track
->iterator
, NULL
, (const void**) &n
);
371 _public_
int sd_bus_track_add_sender(sd_bus_track
*track
, sd_bus_message
*m
) {
374 assert_return(track
, -EINVAL
);
375 assert_return(m
, -EINVAL
);
377 if (sd_bus_message_get_bus(m
) != track
->bus
)
380 sender
= sd_bus_message_get_sender(m
);
384 return sd_bus_track_add_name(track
, sender
);
387 _public_
int sd_bus_track_remove_sender(sd_bus_track
*track
, sd_bus_message
*m
) {
390 assert_return(m
, -EINVAL
);
392 if (!track
) /* Treat a NULL track object as an empty track object */
395 if (sd_bus_message_get_bus(m
) != track
->bus
)
398 sender
= sd_bus_message_get_sender(m
);
402 return sd_bus_track_remove_name(track
, sender
);
405 _public_ sd_bus
* sd_bus_track_get_bus(sd_bus_track
*track
) {
406 assert_return(track
, NULL
);
411 void bus_track_dispatch(sd_bus_track
*track
) {
415 assert(track
->handler
);
417 bus_track_remove_from_queue(track
);
419 sd_bus_track_ref(track
);
421 r
= track
->handler(track
, track
->userdata
);
423 log_debug_errno(r
, "Failed to process track handler: %m");
425 bus_track_add_to_queue(track
);
427 sd_bus_track_unref(track
);
430 void bus_track_close(sd_bus_track
*track
) {
431 struct track_item
*i
;
435 /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
436 * immediately, as we are closing now, but first flush out all names. */
439 return; /* We already closed this one, don't close it again. */
441 /* Remember that this one is closed now */
442 LIST_REMOVE(tracks
, track
->bus
->tracks
, track
);
443 track
->in_list
= false;
445 /* If there's no name in this one anyway, we don't have to dispatch */
446 if (hashmap_isempty(track
->names
))
449 /* Let's flush out all names */
450 while ((i
= hashmap_steal_first(track
->names
)))
455 bus_track_dispatch(track
);
458 _public_
void *sd_bus_track_get_userdata(sd_bus_track
*track
) {
459 assert_return(track
, NULL
);
461 return track
->userdata
;
464 _public_
void *sd_bus_track_set_userdata(sd_bus_track
*track
, void *userdata
) {
467 assert_return(track
, NULL
);
469 ret
= track
->userdata
;
470 track
->userdata
= userdata
;
475 _public_
int sd_bus_track_set_recursive(sd_bus_track
*track
, int b
) {
476 assert_return(track
, -EINVAL
);
478 if (track
->recursive
== !!b
)
481 if (!hashmap_isempty(track
->names
))
484 track
->recursive
= b
;
488 _public_
int sd_bus_track_get_recursive(sd_bus_track
*track
) {
489 assert_return(track
, -EINVAL
);
491 return track
->recursive
;
494 _public_
int sd_bus_track_count_sender(sd_bus_track
*track
, sd_bus_message
*m
) {
497 assert_return(m
, -EINVAL
);
499 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
502 if (sd_bus_message_get_bus(m
) != track
->bus
)
505 sender
= sd_bus_message_get_sender(m
);
509 return sd_bus_track_count_name(track
, sender
);
512 _public_
int sd_bus_track_count_name(sd_bus_track
*track
, const char *name
) {
513 struct track_item
*i
;
515 assert_return(service_name_is_valid(name
), -EINVAL
);
517 if (!track
) /* Let's consider a NULL object equivalent to an empty object */
520 i
= hashmap_get(track
->names
, name
);