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 "alloc-util.h"
25 #include "bus-internal.h"
26 #include "bus-track.h"
33 sd_bus_track_handler_t handler;
36 LIST_FIELDS(sd_bus_track, queue);
42 #define MATCH_PREFIX \
44 "sender='org.freedesktop.DBus'," \
45 "path='/org/freedesktop/DBus'," \
46 "interface='org.freedesktop.DBus'," \
47 "member='NameOwnerChanged'," \
50 #define MATCH_SUFFIX \
53 #define MATCH_FOR_NAME(name) \
56 size_t _l = strlen(name); \
57 _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \
58 strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
62 static void bus_track_add_to_queue(sd_bus_track *track) {
71 LIST_PREPEND(queue, track->bus->track_queue, track);
72 track->in_queue = true;
75 static void bus_track_remove_from_queue(sd_bus_track *track) {
81 LIST_REMOVE(queue, track->bus->track_queue, track);
82 track->in_queue = false;
85 _public_ int sd_bus_track_new(
88 sd_bus_track_handler_t handler,
93 assert_return(bus, -EINVAL);
94 assert_return(track, -EINVAL);
99 t = new0(sd_bus_track, 1);
104 t->handler = handler;
105 t->userdata = userdata;
106 t->bus = sd_bus_ref(bus);
108 bus_track_add_to_queue(t);
114 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
119 assert(track->n_ref > 0);
126 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
132 assert(track->n_ref > 0);
134 if (track->n_ref > 1) {
139 while ((n = hashmap_first_key(track->names)))
140 sd_bus_track_remove_name(track, n);
142 bus_track_remove_from_queue(track);
143 hashmap_free(track->names);
144 sd_bus_unref(track->bus);
150 static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
151 sd_bus_track *track = userdata;
152 const char *name, *old, *new;
158 r = sd_bus_message_read(message, "sss", &name, &old, &new);
162 sd_bus_track_remove_name(track, name);
166 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
167 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
168 _cleanup_free_ char *n = NULL;
172 assert_return(track, -EINVAL);
173 assert_return(service_name_is_valid(name), -EINVAL);
175 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
183 /* First, subscribe to this name */
184 match = MATCH_FOR_NAME(n);
185 r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
189 r = hashmap_put(track->names, n, slot);
195 /* Second, check if it is currently existing, or maybe
196 * doesn't, or maybe disappeared already. */
197 r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
199 hashmap_remove(track->names, n);
206 bus_track_remove_from_queue(track);
207 track->modified = true;
212 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
213 _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL;
214 _cleanup_free_ char *n = NULL;
216 assert_return(name, -EINVAL);
221 slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
225 if (hashmap_isempty(track->names))
226 bus_track_add_to_queue(track);
228 track->modified = true;
233 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
237 return hashmap_size(track->names);
240 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
241 assert_return(track, NULL);
242 assert_return(name, NULL);
244 return hashmap_get(track->names, (void*) name) ? name : NULL;
247 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
248 const char *n = NULL;
253 track->modified = false;
254 track->iterator = ITERATOR_FIRST;
256 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
260 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
261 const char *n = NULL;
269 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
273 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
276 assert_return(track, -EINVAL);
277 assert_return(m, -EINVAL);
279 sender = sd_bus_message_get_sender(m);
283 return sd_bus_track_add_name(track, sender);
286 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
289 assert_return(track, -EINVAL);
290 assert_return(m, -EINVAL);
292 sender = sd_bus_message_get_sender(m);
296 return sd_bus_track_remove_name(track, sender);
299 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
300 assert_return(track, NULL);
305 void bus_track_dispatch(sd_bus_track *track) {
309 assert(track->in_queue);
310 assert(track->handler);
312 bus_track_remove_from_queue(track);
314 sd_bus_track_ref(track);
316 r = track->handler(track, track->userdata);
318 log_debug_errno(r, "Failed to process track handler: %m");
320 bus_track_add_to_queue(track);
322 sd_bus_track_unref(track);
325 #if 0 /// UNNEEDED by elogind
326 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
327 assert_return(track, NULL);
329 return track->userdata;
332 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
335 assert_return(track, NULL);
337 ret = track->userdata;
338 track->userdata = userdata;