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(sizeof(MATCH_PREFIX)-1+_l+sizeof(MATCH_SUFFIX)); \
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 = set_first(track->names)))
132 sd_bus_track_remove_name(track, n);
134 bus_track_remove_from_queue(track);
135 set_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_free_ char *n = NULL;
164 assert_return(track, -EINVAL);
165 assert_return(service_name_is_valid(name), -EINVAL);
167 r = set_ensure_allocated(&track->names, string_hash_func, string_compare_func);
175 r = set_put(track->names, n);
181 /* First, subscribe to this name */
182 match = MATCH_FOR_NAME(name);
183 r = sd_bus_add_match(track->bus, match, on_name_owner_changed, track);
185 set_remove(track->names, n);
189 /* Second, check if it is currently existing, or maybe
190 * doesn't, or maybe disappeared already. */
191 r = sd_bus_get_owner(track->bus, name, 0, NULL);
193 set_remove(track->names, n);
194 sd_bus_remove_match(track->bus, match, on_name_owner_changed, track);
200 bus_track_remove_from_queue(track);
201 track->modified = true;
206 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
208 _cleanup_free_ char *n = NULL;;
210 assert_return(name, -EINVAL);
215 n = set_remove(track->names, (char*) name);
219 match = MATCH_FOR_NAME(n);
220 sd_bus_remove_match(track->bus, match, on_name_owner_changed, track);
222 if (set_isempty(track->names))
223 bus_track_add_to_queue(track);
225 track->modified = true;
230 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
234 return set_size(track->names);
237 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
238 assert_return(track, NULL);
239 assert_return(name, NULL);
241 return set_get(track->names, (void*) name);
244 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
248 track->modified = false;
249 track->iterator = NULL;
251 return set_iterate(track->names, &track->iterator);
254 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
261 return set_iterate(track->names, &track->iterator);
264 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
267 assert_return(track, -EINVAL);
268 assert_return(m, -EINVAL);
270 sender = sd_bus_message_get_sender(m);
274 return sd_bus_track_add_name(track, sender);
277 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
280 assert_return(track, -EINVAL);
281 assert_return(m, -EINVAL);
283 sender = sd_bus_message_get_sender(m);
287 return sd_bus_track_remove_name(track, sender);
290 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
291 assert_return(track, NULL);
296 void bus_track_dispatch(sd_bus_track *track) {
300 assert(track->in_queue);
301 assert(track->handler);
303 bus_track_remove_from_queue(track);
305 sd_bus_track_ref(track);
307 r = track->handler(track, track->userdata);
309 log_debug("Failed to process track handler: %s", strerror(-r));
311 bus_track_add_to_queue(track);
313 sd_bus_track_unref(track);