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 *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
146 sd_bus_track *track = userdata;
147 const char *name, *old, *new;
154 r = sd_bus_message_read(message, "sss", &name, &old, &new);
158 sd_bus_track_remove_name(track, name);
162 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
163 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
164 _cleanup_free_ char *n = NULL;
168 assert_return(track, -EINVAL);
169 assert_return(service_name_is_valid(name), -EINVAL);
171 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
179 /* First, subscribe to this name */
180 match = MATCH_FOR_NAME(n);
181 r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
185 r = hashmap_put(track->names, n, slot);
191 /* Second, check if it is currently existing, or maybe
192 * doesn't, or maybe disappeared already. */
193 r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
195 hashmap_remove(track->names, n);
202 bus_track_remove_from_queue(track);
203 track->modified = true;
208 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
209 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
210 _cleanup_free_ char *n = NULL;
212 assert_return(name, -EINVAL);
217 slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
221 if (hashmap_isempty(track->names))
222 bus_track_add_to_queue(track);
224 track->modified = true;
229 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
233 return hashmap_size(track->names);
236 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
237 assert_return(track, NULL);
238 assert_return(name, NULL);
240 return hashmap_get(track->names, (void*) name) ? name : NULL;
243 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
244 const char *n = NULL;
249 track->modified = false;
250 track->iterator = ITERATOR_FIRST;
252 hashmap_iterate(track->names, &track->iterator, (const void**) &n);
256 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
257 const char *n = NULL;
265 hashmap_iterate(track->names, &track->iterator, (const void**) &n);
269 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
272 assert_return(track, -EINVAL);
273 assert_return(m, -EINVAL);
275 sender = sd_bus_message_get_sender(m);
279 return sd_bus_track_add_name(track, sender);
282 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
285 assert_return(track, -EINVAL);
286 assert_return(m, -EINVAL);
288 sender = sd_bus_message_get_sender(m);
292 return sd_bus_track_remove_name(track, sender);
295 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
296 assert_return(track, NULL);
301 void bus_track_dispatch(sd_bus_track *track) {
305 assert(track->in_queue);
306 assert(track->handler);
308 bus_track_remove_from_queue(track);
310 sd_bus_track_ref(track);
312 r = track->handler(track, track->userdata);
314 log_debug_errno(r, "Failed to process track handler: %m");
316 bus_track_add_to_queue(track);
318 sd_bus_track_unref(track);
321 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
322 assert_return(track, NULL);
324 return track->userdata;
327 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
330 assert_return(track, NULL);
332 ret = track->userdata;
333 track->userdata = userdata;