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);
93 t = new0(sd_bus_track, 1);
99 t->userdata = userdata;
100 t->bus = sd_bus_ref(bus);
102 bus_track_add_to_queue(t);
108 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
109 assert_return(track, NULL);
111 assert(track->n_ref > 0);
118 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
124 assert(track->n_ref > 0);
126 if (track->n_ref > 1) {
131 while ((n = hashmap_first_key(track->names)))
132 sd_bus_track_remove_name(track, n);
134 bus_track_remove_from_queue(track);
135 hashmap_free(track->names);
136 sd_bus_unref(track->bus);
142 static int on_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
143 sd_bus_track *track = userdata;
144 const char *name, *old, *new;
151 r = sd_bus_message_read(message, "sss", &name, &old, &new);
155 sd_bus_track_remove_name(track, name);
159 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
160 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
161 _cleanup_free_ char *n = NULL;
165 assert_return(track, -EINVAL);
166 assert_return(service_name_is_valid(name), -EINVAL);
168 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
176 /* First, subscribe to this name */
177 match = MATCH_FOR_NAME(n);
178 r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
182 r = hashmap_put(track->names, n, slot);
188 /* Second, check if it is currently existing, or maybe
189 * doesn't, or maybe disappeared already. */
190 r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
192 hashmap_remove(track->names, n);
199 bus_track_remove_from_queue(track);
200 track->modified = true;
205 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
206 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
207 _cleanup_free_ char *n = NULL;
209 assert_return(name, -EINVAL);
214 slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
218 if (hashmap_isempty(track->names))
219 bus_track_add_to_queue(track);
221 track->modified = true;
226 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
230 return hashmap_size(track->names);
233 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
234 assert_return(track, NULL);
235 assert_return(name, NULL);
237 return hashmap_get(track->names, (void*) name) ? name : NULL;
240 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
241 const char *n = NULL;
246 track->modified = false;
247 track->iterator = ITERATOR_FIRST;
249 hashmap_iterate(track->names, &track->iterator, (const void**) &n);
253 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
254 const char *n = NULL;
262 hashmap_iterate(track->names, &track->iterator, (const void**) &n);
266 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
269 assert_return(track, -EINVAL);
270 assert_return(m, -EINVAL);
272 sender = sd_bus_message_get_sender(m);
276 return sd_bus_track_add_name(track, sender);
279 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
282 assert_return(track, -EINVAL);
283 assert_return(m, -EINVAL);
285 sender = sd_bus_message_get_sender(m);
289 return sd_bus_track_remove_name(track, sender);
292 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
293 assert_return(track, NULL);
298 void bus_track_dispatch(sd_bus_track *track) {
302 assert(track->in_queue);
303 assert(track->handler);
305 bus_track_remove_from_queue(track);
307 sd_bus_track_ref(track);
309 r = track->handler(track, track->userdata);
311 log_debug_errno(r, "Failed to process track handler: %m");
313 bus_track_add_to_queue(track);
315 sd_bus_track_unref(track);
318 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
319 assert_return(track, NULL);
321 return track->userdata;
324 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
327 assert_return(track, NULL);
329 ret = track->userdata;
330 track->userdata = userdata;