1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include "bus-internal.h"
25 #include "bus-track.h"
30 sd_bus_track_handler_t handler;
33 LIST_FIELDS(sd_bus_track, queue);
39 #define MATCH_PREFIX \
41 "sender='org.freedesktop.DBus'," \
42 "path='/org/freedesktop/DBus'," \
43 "interface='org.freedesktop.DBus'," \
44 "member='NameOwnerChanged'," \
47 #define MATCH_SUFFIX \
50 #define MATCH_FOR_NAME(name) \
53 size_t _l = strlen(name); \
54 _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \
55 strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
59 static void bus_track_add_to_queue(sd_bus_track *track) {
68 LIST_PREPEND(queue, track->bus->track_queue, track);
69 track->in_queue = true;
72 static void bus_track_remove_from_queue(sd_bus_track *track) {
78 LIST_REMOVE(queue, track->bus->track_queue, track);
79 track->in_queue = false;
82 _public_ int sd_bus_track_new(
85 sd_bus_track_handler_t handler,
90 assert_return(bus, -EINVAL);
91 assert_return(track, -EINVAL);
96 t = new0(sd_bus_track, 1);
101 t->handler = handler;
102 t->userdata = userdata;
103 t->bus = sd_bus_ref(bus);
105 bus_track_add_to_queue(t);
111 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
112 assert_return(track, NULL);
114 assert(track->n_ref > 0);
121 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
127 assert(track->n_ref > 0);
129 if (track->n_ref > 1) {
134 while ((n = hashmap_first_key(track->names)))
135 sd_bus_track_remove_name(track, n);
137 bus_track_remove_from_queue(track);
138 hashmap_free(track->names);
139 sd_bus_unref(track->bus);
145 static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
146 sd_bus_track *track = userdata;
147 const char *name, *old, *new;
153 r = sd_bus_message_read(message, "sss", &name, &old, &new);
157 sd_bus_track_remove_name(track, name);
161 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
162 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
163 _cleanup_free_ char *n = NULL;
167 assert_return(track, -EINVAL);
168 assert_return(service_name_is_valid(name), -EINVAL);
170 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
178 /* First, subscribe to this name */
179 match = MATCH_FOR_NAME(n);
180 r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
184 r = hashmap_put(track->names, n, slot);
190 /* Second, check if it is currently existing, or maybe
191 * doesn't, or maybe disappeared already. */
192 r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
194 hashmap_remove(track->names, n);
201 bus_track_remove_from_queue(track);
202 track->modified = true;
207 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
208 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
209 _cleanup_free_ char *n = NULL;
211 assert_return(name, -EINVAL);
216 slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
220 if (hashmap_isempty(track->names))
221 bus_track_add_to_queue(track);
223 track->modified = true;
228 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
232 return hashmap_size(track->names);
235 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
236 assert_return(track, NULL);
237 assert_return(name, NULL);
239 return hashmap_get(track->names, (void*) name) ? name : NULL;
242 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
243 const char *n = NULL;
248 track->modified = false;
249 track->iterator = ITERATOR_FIRST;
251 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
255 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
256 const char *n = NULL;
264 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
268 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
271 assert_return(track, -EINVAL);
272 assert_return(m, -EINVAL);
274 sender = sd_bus_message_get_sender(m);
278 return sd_bus_track_add_name(track, sender);
281 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
284 assert_return(track, -EINVAL);
285 assert_return(m, -EINVAL);
287 sender = sd_bus_message_get_sender(m);
291 return sd_bus_track_remove_name(track, sender);
294 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
295 assert_return(track, NULL);
300 void bus_track_dispatch(sd_bus_track *track) {
304 assert(track->in_queue);
305 assert(track->handler);
307 bus_track_remove_from_queue(track);
309 sd_bus_track_ref(track);
311 r = track->handler(track, track->userdata);
313 log_debug_errno(r, "Failed to process track handler: %m");
315 bus_track_add_to_queue(track);
317 sd_bus_track_unref(track);
320 /// UNNEEDED by elogind
322 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
323 assert_return(track, NULL);
325 return track->userdata;
328 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
331 assert_return(track, NULL);
333 ret = track->userdata;
334 track->userdata = userdata;