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) {
115 assert_return(track, NULL);
117 assert(track->n_ref > 0);
124 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
130 assert(track->n_ref > 0);
132 if (track->n_ref > 1) {
137 while ((n = hashmap_first_key(track->names)))
138 sd_bus_track_remove_name(track, n);
140 bus_track_remove_from_queue(track);
141 hashmap_free(track->names);
142 sd_bus_unref(track->bus);
148 static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
149 sd_bus_track *track = userdata;
150 const char *name, *old, *new;
156 r = sd_bus_message_read(message, "sss", &name, &old, &new);
160 sd_bus_track_remove_name(track, name);
164 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
165 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
166 _cleanup_free_ char *n = NULL;
170 assert_return(track, -EINVAL);
171 assert_return(service_name_is_valid(name), -EINVAL);
173 r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
181 /* First, subscribe to this name */
182 match = MATCH_FOR_NAME(n);
183 r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
187 r = hashmap_put(track->names, n, slot);
193 /* Second, check if it is currently existing, or maybe
194 * doesn't, or maybe disappeared already. */
195 r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
197 hashmap_remove(track->names, n);
204 bus_track_remove_from_queue(track);
205 track->modified = true;
210 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
211 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
212 _cleanup_free_ char *n = NULL;
214 assert_return(name, -EINVAL);
219 slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
223 if (hashmap_isempty(track->names))
224 bus_track_add_to_queue(track);
226 track->modified = true;
231 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
235 return hashmap_size(track->names);
238 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
239 assert_return(track, NULL);
240 assert_return(name, NULL);
242 return hashmap_get(track->names, (void*) name) ? name : NULL;
245 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
246 const char *n = NULL;
251 track->modified = false;
252 track->iterator = ITERATOR_FIRST;
254 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
258 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
259 const char *n = NULL;
267 hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
271 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
274 assert_return(track, -EINVAL);
275 assert_return(m, -EINVAL);
277 sender = sd_bus_message_get_sender(m);
281 return sd_bus_track_add_name(track, sender);
284 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
287 assert_return(track, -EINVAL);
288 assert_return(m, -EINVAL);
290 sender = sd_bus_message_get_sender(m);
294 return sd_bus_track_remove_name(track, sender);
297 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
298 assert_return(track, NULL);
303 void bus_track_dispatch(sd_bus_track *track) {
307 assert(track->in_queue);
308 assert(track->handler);
310 bus_track_remove_from_queue(track);
312 sd_bus_track_ref(track);
314 r = track->handler(track, track->userdata);
316 log_debug_errno(r, "Failed to process track handler: %m");
318 bus_track_add_to_queue(track);
320 sd_bus_track_unref(track);
323 #if 0 /// UNNEEDED by elogind
324 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
325 assert_return(track, NULL);
327 return track->userdata;
330 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
333 assert_return(track, NULL);
335 ret = track->userdata;
336 track->userdata = userdata;