2 This file is part of systemd.
4 Copyright 2013 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include "alloc-util.h"
23 #include "bus-internal.h"
24 #include "bus-track.h"
31 sd_bus_track_handler_t handler;
34 LIST_FIELDS(sd_bus_track, queue);
40 #define MATCH_PREFIX \
42 "sender='org.freedesktop.DBus'," \
43 "path='/org/freedesktop/DBus'," \
44 "interface='org.freedesktop.DBus'," \
45 "member='NameOwnerChanged'," \
48 #define MATCH_SUFFIX \
51 #define MATCH_FOR_NAME(name) \
54 size_t _l = strlen(name); \
55 _x = alloca(strlen(MATCH_PREFIX)+_l+strlen(MATCH_SUFFIX)+1); \
56 strcpy(stpcpy(stpcpy(_x, MATCH_PREFIX), name), MATCH_SUFFIX); \
60 static void bus_track_add_to_queue(sd_bus_track *track) {
69 LIST_PREPEND(queue, track->bus->track_queue, track);
70 track->in_queue = true;
73 static void bus_track_remove_from_queue(sd_bus_track *track) {
79 LIST_REMOVE(queue, track->bus->track_queue, track);
80 track->in_queue = false;
83 _public_ int sd_bus_track_new(
86 sd_bus_track_handler_t handler,
91 assert_return(bus, -EINVAL);
92 assert_return(track, -EINVAL);
97 t = new0(sd_bus_track, 1);
102 t->handler = handler;
103 t->userdata = userdata;
104 t->bus = sd_bus_ref(bus);
106 bus_track_add_to_queue(t);
112 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
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_(sd_bus_slot_unrefp) 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_(sd_bus_slot_unrefp) 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;