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"
36 sd_bus_track_handler_t handler;
39 LIST_FIELDS(sd_bus_track, queue);
46 #define MATCH_PREFIX \
48 "sender='org.freedesktop.DBus'," \
49 "path='/org/freedesktop/DBus'," \
50 "interface='org.freedesktop.DBus'," \
51 "member='NameOwnerChanged'," \
54 #define MATCH_SUFFIX \
57 #define MATCH_FOR_NAME(name) \
60 size_t _l = strlen(name); \
61 _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \
62 strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
66 static struct track_item* track_item_free(struct track_item *i) {
71 sd_bus_slot_unref(i->slot);
78 DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
80 static void bus_track_add_to_queue(sd_bus_track *track) {
89 LIST_PREPEND(queue, track->bus->track_queue, track);
90 track->in_queue = true;
93 static void bus_track_remove_from_queue(sd_bus_track *track) {
99 LIST_REMOVE(queue, track->bus->track_queue, track);
100 track->in_queue = false;
103 static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) {
104 struct track_item *i;
109 i = hashmap_remove(track->names, name);
115 if (hashmap_isempty(track->names))
116 bus_track_add_to_queue(track);
118 track->modified = true;
122 _public_ int sd_bus_track_new(
124 sd_bus_track **track,
125 sd_bus_track_handler_t handler,
130 assert_return(bus, -EINVAL);
131 assert_return(track, -EINVAL);
133 if (!bus->bus_client)
136 t = new0(sd_bus_track, 1);
141 t->handler = handler;
142 t->userdata = userdata;
143 t->bus = sd_bus_ref(bus);
145 bus_track_add_to_queue(t);
151 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
156 assert(track->n_ref > 0);
163 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
164 struct track_item *i;
169 assert(track->n_ref > 0);
171 if (track->n_ref > 1) {
176 while ((i = hashmap_steal_first(track->names)))
179 bus_track_remove_from_queue(track);
180 hashmap_free(track->names);
181 sd_bus_unref(track->bus);
187 static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
188 sd_bus_track *track = userdata;
189 const char *name, *old, *new;
195 r = sd_bus_message_read(message, "sss", &name, &old, &new);
199 bus_track_remove_name_fully(track, name);
203 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
204 _cleanup_(track_item_freep) struct track_item *n = NULL;
205 struct track_item *i;
209 assert_return(track, -EINVAL);
210 assert_return(service_name_is_valid(name), -EINVAL);
212 i = hashmap_get(track->names, name);
214 if (track->recursive) {
215 unsigned k = track->n_ref + 1;
217 if (k < track->n_ref) /* Check for overflow */
223 bus_track_remove_from_queue(track);
227 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
231 n = new0(struct track_item, 1);
234 n->name = strdup(name);
238 /* First, subscribe to this name */
239 match = MATCH_FOR_NAME(name);
240 r = sd_bus_add_match(track->bus, &n->slot, match, on_name_owner_changed, track);
244 r = hashmap_put(track->names, n->name, n);
248 /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
249 r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
251 hashmap_remove(track->names, name);
258 bus_track_remove_from_queue(track);
259 track->modified = true;
264 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
265 struct track_item *i;
267 assert_return(name, -EINVAL);
269 if (!track) /* Treat a NULL track object as an empty track object */
272 if (!track->recursive)
273 return bus_track_remove_name_fully(track, name);
275 i = hashmap_get(track->names, name);
284 return bus_track_remove_name_fully(track, name);
289 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
291 if (!track) /* Let's consider a NULL object equivalent to an empty object */
294 /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
295 * that this returns the number of names being watched, and multiple references to the same name are not
298 return hashmap_size(track->names);
301 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
302 assert_return(name, NULL);
304 if (!track) /* Let's consider a NULL object equivalent to an empty object */
307 return hashmap_get(track->names, (void*) name) ? name : NULL;
310 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
311 const char *n = NULL;
316 track->modified = false;
317 track->iterator = ITERATOR_FIRST;
319 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
323 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
324 const char *n = NULL;
332 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
336 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
339 assert_return(track, -EINVAL);
340 assert_return(m, -EINVAL);
342 if (sd_bus_message_get_bus(m) != track->bus)
345 sender = sd_bus_message_get_sender(m);
349 return sd_bus_track_add_name(track, sender);
352 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
355 assert_return(m, -EINVAL);
357 if (!track) /* Treat a NULL track object as an empty track object */
360 if (sd_bus_message_get_bus(m) != track->bus)
363 sender = sd_bus_message_get_sender(m);
367 return sd_bus_track_remove_name(track, sender);
370 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
371 assert_return(track, NULL);
376 void bus_track_dispatch(sd_bus_track *track) {
380 assert(track->in_queue);
381 assert(track->handler);
383 bus_track_remove_from_queue(track);
385 sd_bus_track_ref(track);
387 r = track->handler(track, track->userdata);
389 log_debug_errno(r, "Failed to process track handler: %m");
391 bus_track_add_to_queue(track);
393 sd_bus_track_unref(track);
396 #if 0 /// UNNEEDED by elogind
397 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
398 assert_return(track, NULL);
400 return track->userdata;
403 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
406 assert_return(track, NULL);
408 ret = track->userdata;
409 track->userdata = userdata;
415 _public_ int sd_bus_track_set_recursive(sd_bus_track *track, int b) {
416 assert_return(track, -EINVAL);
418 if (track->recursive == !!b)
421 if (!hashmap_isempty(track->names))
424 track->recursive = b;
428 _public_ int sd_bus_track_get_recursive(sd_bus_track *track) {
429 assert_return(track, -EINVAL);
431 return track->recursive;
434 _public_ int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m) {
437 assert_return(m, -EINVAL);
439 if (!track) /* Let's consider a NULL object equivalent to an empty object */
442 if (sd_bus_message_get_bus(m) != track->bus)
445 sender = sd_bus_message_get_sender(m);
449 return sd_bus_track_count_name(track, sender);
452 _public_ int sd_bus_track_count_name(sd_bus_track *track, const char *name) {
453 struct track_item *i;
455 assert_return(service_name_is_valid(name), -EINVAL);
457 if (!track) /* Let's consider a NULL object equivalent to an empty object */
460 i = hashmap_get(track->names, name);