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(track, -EINVAL);
141 if (!bus->bus_client)
144 t = new0(sd_bus_track, 1);
149 t->handler = handler;
150 t->userdata = userdata;
151 t->bus = sd_bus_ref(bus);
153 LIST_PREPEND(tracks, bus->tracks, t);
156 bus_track_add_to_queue(t);
162 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
167 assert(track->n_ref > 0);
174 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
178 assert(track->n_ref > 0);
180 if (track->n_ref > 1) {
186 LIST_REMOVE(tracks, track->bus->tracks, track);
188 bus_track_remove_from_queue(track);
189 hashmap_free_with_destructor(track->names, track_item_free);
190 sd_bus_unref(track->bus);
194 static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
195 sd_bus_track *track = userdata;
196 const char *name, *old, *new;
202 r = sd_bus_message_read(message, "sss", &name, &old, &new);
206 bus_track_remove_name_fully(track, name);
210 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
211 _cleanup_(track_item_freep) struct track_item *n = NULL;
212 struct track_item *i;
216 assert_return(track, -EINVAL);
217 assert_return(service_name_is_valid(name), -EINVAL);
219 i = hashmap_get(track->names, name);
221 if (track->recursive) {
222 unsigned k = track->n_ref + 1;
224 if (k < track->n_ref) /* Check for overflow */
230 bus_track_remove_from_queue(track);
234 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
238 n = new0(struct track_item, 1);
241 n->name = strdup(name);
245 /* First, subscribe to this name */
246 match = MATCH_FOR_NAME(name);
248 bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
250 r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, NULL, track);
252 bus_track_add_to_queue(track);
256 r = hashmap_put(track->names, n->name, n);
258 bus_track_add_to_queue(track);
262 /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
263 track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */
264 r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
267 hashmap_remove(track->names, name);
268 bus_track_add_to_queue(track);
275 bus_track_remove_from_queue(track);
276 track->modified = true;
281 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
282 struct track_item *i;
284 assert_return(name, -EINVAL);
286 if (!track) /* Treat a NULL track object as an empty track object */
289 if (!track->recursive)
290 return bus_track_remove_name_fully(track, name);
292 i = hashmap_get(track->names, name);
301 return bus_track_remove_name_fully(track, name);
306 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
308 if (!track) /* Let's consider a NULL object equivalent to an empty object */
311 /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
312 * that this returns the number of names being watched, and multiple references to the same name are not
315 return hashmap_size(track->names);
318 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
319 assert_return(name, NULL);
321 if (!track) /* Let's consider a NULL object equivalent to an empty object */
324 return hashmap_get(track->names, (void*) name) ? name : NULL;
327 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
328 const char *n = NULL;
333 track->modified = false;
334 track->iterator = ITERATOR_FIRST;
336 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
340 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
341 const char *n = NULL;
349 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
353 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
356 assert_return(track, -EINVAL);
357 assert_return(m, -EINVAL);
359 if (sd_bus_message_get_bus(m) != track->bus)
362 sender = sd_bus_message_get_sender(m);
366 return sd_bus_track_add_name(track, sender);
369 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
372 assert_return(m, -EINVAL);
374 if (!track) /* Treat a NULL track object as an empty track object */
377 if (sd_bus_message_get_bus(m) != track->bus)
380 sender = sd_bus_message_get_sender(m);
384 return sd_bus_track_remove_name(track, sender);
387 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
388 assert_return(track, NULL);
393 void bus_track_dispatch(sd_bus_track *track) {
397 assert(track->handler);
399 bus_track_remove_from_queue(track);
401 sd_bus_track_ref(track);
403 r = track->handler(track, track->userdata);
405 log_debug_errno(r, "Failed to process track handler: %m");
407 bus_track_add_to_queue(track);
409 sd_bus_track_unref(track);
412 void bus_track_close(sd_bus_track *track) {
415 /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
416 * immediately, as we are closing now, but first flush out all names. */
419 return; /* We already closed this one, don't close it again. */
421 /* Remember that this one is closed now */
422 LIST_REMOVE(tracks, track->bus->tracks, track);
423 track->in_list = false;
425 /* If there's no name in this one anyway, we don't have to dispatch */
426 if (hashmap_isempty(track->names))
429 /* Let's flush out all names */
430 hashmap_clear_with_destructor(track->names, track_item_free);
434 bus_track_dispatch(track);
437 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
438 assert_return(track, NULL);
440 return track->userdata;
443 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
446 assert_return(track, NULL);
448 ret = track->userdata;
449 track->userdata = userdata;
454 _public_ int sd_bus_track_set_recursive(sd_bus_track *track, int b) {
455 assert_return(track, -EINVAL);
457 if (track->recursive == !!b)
460 if (!hashmap_isempty(track->names))
463 track->recursive = b;
467 _public_ int sd_bus_track_get_recursive(sd_bus_track *track) {
468 assert_return(track, -EINVAL);
470 return track->recursive;
473 _public_ int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m) {
476 assert_return(m, -EINVAL);
478 if (!track) /* Let's consider a NULL object equivalent to an empty object */
481 if (sd_bus_message_get_bus(m) != track->bus)
484 sender = sd_bus_message_get_sender(m);
488 return sd_bus_track_count_name(track, sender);
491 _public_ int sd_bus_track_count_name(sd_bus_track *track, const char *name) {
492 struct track_item *i;
494 assert_return(service_name_is_valid(name), -EINVAL);
496 if (!track) /* Let's consider a NULL object equivalent to an empty object */
499 i = hashmap_get(track->names, name);