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);
82 DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
84 static void bus_track_add_to_queue(sd_bus_track *track) {
87 /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
90 /* Already in the queue? */
94 /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
95 * until the addition is complete. */
96 if (track->n_adding > 0)
99 /* still referenced? */
100 if (hashmap_size(track->names) > 0)
103 /* Nothing to call? */
107 /* Already closed? */
111 LIST_PREPEND(queue, track->bus->track_queue, track);
112 track->in_queue = true;
115 static void bus_track_remove_from_queue(sd_bus_track *track) {
118 if (!track->in_queue)
121 LIST_REMOVE(queue, track->bus->track_queue, track);
122 track->in_queue = false;
125 static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) {
126 struct track_item *i;
131 i = hashmap_remove(track->names, name);
137 bus_track_add_to_queue(track);
139 track->modified = true;
143 _public_ int sd_bus_track_new(
145 sd_bus_track **track,
146 sd_bus_track_handler_t handler,
151 assert_return(bus, -EINVAL);
152 assert_return(track, -EINVAL);
154 if (!bus->bus_client)
157 t = new0(sd_bus_track, 1);
162 t->handler = handler;
163 t->userdata = userdata;
164 t->bus = sd_bus_ref(bus);
166 LIST_PREPEND(tracks, bus->tracks, t);
169 bus_track_add_to_queue(t);
175 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
180 assert(track->n_ref > 0);
187 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
188 struct track_item *i;
193 assert(track->n_ref > 0);
195 if (track->n_ref > 1) {
200 while ((i = hashmap_steal_first(track->names)))
204 LIST_REMOVE(tracks, track->bus->tracks, track);
206 bus_track_remove_from_queue(track);
207 hashmap_free(track->names);
208 sd_bus_unref(track->bus);
214 static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
215 sd_bus_track *track = userdata;
216 const char *name, *old, *new;
222 r = sd_bus_message_read(message, "sss", &name, &old, &new);
226 bus_track_remove_name_fully(track, name);
230 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
231 _cleanup_(track_item_freep) struct track_item *n = NULL;
232 struct track_item *i;
236 assert_return(track, -EINVAL);
237 assert_return(service_name_is_valid(name), -EINVAL);
239 i = hashmap_get(track->names, name);
241 if (track->recursive) {
242 unsigned k = track->n_ref + 1;
244 if (k < track->n_ref) /* Check for overflow */
250 bus_track_remove_from_queue(track);
254 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
258 n = new0(struct track_item, 1);
261 n->name = strdup(name);
265 /* First, subscribe to this name */
266 match = MATCH_FOR_NAME(name);
268 bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
270 track->n_adding++; /* make sure we aren't dispatched while we synchronously add this match */
271 r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track);
274 bus_track_add_to_queue(track);
278 r = hashmap_put(track->names, n->name, n);
280 bus_track_add_to_queue(track);
284 /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
285 track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */
286 r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
289 hashmap_remove(track->names, name);
290 bus_track_add_to_queue(track);
297 bus_track_remove_from_queue(track);
298 track->modified = true;
303 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
304 struct track_item *i;
306 assert_return(name, -EINVAL);
308 if (!track) /* Treat a NULL track object as an empty track object */
311 if (!track->recursive)
312 return bus_track_remove_name_fully(track, name);
314 i = hashmap_get(track->names, name);
323 return bus_track_remove_name_fully(track, name);
328 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
330 if (!track) /* Let's consider a NULL object equivalent to an empty object */
333 /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
334 * that this returns the number of names being watched, and multiple references to the same name are not
337 return hashmap_size(track->names);
340 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
341 assert_return(name, NULL);
343 if (!track) /* Let's consider a NULL object equivalent to an empty object */
346 return hashmap_get(track->names, (void*) name) ? name : NULL;
349 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
350 const char *n = NULL;
355 track->modified = false;
356 track->iterator = ITERATOR_FIRST;
358 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
362 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
363 const char *n = NULL;
371 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
375 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
378 assert_return(track, -EINVAL);
379 assert_return(m, -EINVAL);
381 if (sd_bus_message_get_bus(m) != track->bus)
384 sender = sd_bus_message_get_sender(m);
388 return sd_bus_track_add_name(track, sender);
391 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
394 assert_return(m, -EINVAL);
396 if (!track) /* Treat a NULL track object as an empty track object */
399 if (sd_bus_message_get_bus(m) != track->bus)
402 sender = sd_bus_message_get_sender(m);
406 return sd_bus_track_remove_name(track, sender);
409 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
410 assert_return(track, NULL);
415 void bus_track_dispatch(sd_bus_track *track) {
419 assert(track->handler);
421 bus_track_remove_from_queue(track);
423 sd_bus_track_ref(track);
425 r = track->handler(track, track->userdata);
427 log_debug_errno(r, "Failed to process track handler: %m");
429 bus_track_add_to_queue(track);
431 sd_bus_track_unref(track);
434 #if 0 /// UNNEEDED by elogind
435 void bus_track_close(sd_bus_track *track) {
436 struct track_item *i;
440 /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
441 * immediately, as we are closing now, but first flush out all names. */
444 return; /* We already closed this one, don't close it again. */
446 /* Remember that this one is closed now */
447 LIST_REMOVE(tracks, track->bus->tracks, track);
448 track->in_list = false;
450 /* If there's no name in this one anyway, we don't have to dispatch */
451 if (hashmap_isempty(track->names))
454 /* Let's flush out all names */
455 while ((i = hashmap_steal_first(track->names)))
460 bus_track_dispatch(track);
463 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
464 assert_return(track, NULL);
466 return track->userdata;
469 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
472 assert_return(track, NULL);
474 ret = track->userdata;
475 track->userdata = userdata;
481 _public_ int sd_bus_track_set_recursive(sd_bus_track *track, int b) {
482 assert_return(track, -EINVAL);
484 if (track->recursive == !!b)
487 if (!hashmap_isempty(track->names))
490 track->recursive = b;
494 _public_ int sd_bus_track_get_recursive(sd_bus_track *track) {
495 assert_return(track, -EINVAL);
497 return track->recursive;
500 _public_ int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m) {
503 assert_return(m, -EINVAL);
505 if (!track) /* Let's consider a NULL object equivalent to an empty object */
508 if (sd_bus_message_get_bus(m) != track->bus)
511 sender = sd_bus_message_get_sender(m);
515 return sd_bus_track_count_name(track, sender);
518 _public_ int sd_bus_track_count_name(sd_bus_track *track, const char *name) {
519 struct track_item *i;
521 assert_return(service_name_is_valid(name), -EINVAL);
523 if (!track) /* Let's consider a NULL object equivalent to an empty object */
526 i = hashmap_get(track->names, name);