chiark / gitweb /
048178550b5fb5a50cff72ae16842cea31bd6929
[elogind.git] / src / libsystemd / sd-bus / bus-track.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include "sd-bus.h"
23 #include "bus-util.h"
24 #include "bus-internal.h"
25 #include "bus-track.h"
26
27 struct sd_bus_track {
28         unsigned n_ref;
29         sd_bus *bus;
30         sd_bus_track_handler_t handler;
31         void *userdata;
32         Hashmap *names;
33         LIST_FIELDS(sd_bus_track, queue);
34         Iterator iterator;
35         bool in_queue;
36         bool modified;
37 };
38
39 #define MATCH_PREFIX                                        \
40         "type='signal',"                                    \
41         "sender='org.freedesktop.DBus',"                    \
42         "path='/org/freedesktop/DBus',"                     \
43         "interface='org.freedesktop.DBus',"                 \
44         "member='NameOwnerChanged',"                        \
45         "arg0='"
46
47 #define MATCH_SUFFIX \
48         "'"
49
50 #define MATCH_FOR_NAME(name)                                            \
51         ({                                                              \
52                 char *_x;                                               \
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); \
56                 _x;                                                     \
57         })
58
59 static void bus_track_add_to_queue(sd_bus_track *track) {
60         assert(track);
61
62         if (track->in_queue)
63                 return;
64
65         if (!track->handler)
66                 return;
67
68         LIST_PREPEND(queue, track->bus->track_queue, track);
69         track->in_queue = true;
70 }
71
72 static void bus_track_remove_from_queue(sd_bus_track *track) {
73         assert(track);
74
75         if (!track->in_queue)
76                 return;
77
78         LIST_REMOVE(queue, track->bus->track_queue, track);
79         track->in_queue = false;
80 }
81
82 _public_ int sd_bus_track_new(
83                 sd_bus *bus,
84                 sd_bus_track **track,
85                 sd_bus_track_handler_t handler,
86                 void *userdata) {
87
88         sd_bus_track *t;
89
90         assert_return(bus, -EINVAL);
91         assert_return(track, -EINVAL);
92
93         t = new0(sd_bus_track, 1);
94         if (!t)
95                 return -ENOMEM;
96
97         t->n_ref = 1;
98         t->handler = handler;
99         t->userdata = userdata;
100         t->bus = sd_bus_ref(bus);
101
102         bus_track_add_to_queue(t);
103
104         *track = t;
105         return 0;
106 }
107
108 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
109         assert_return(track, NULL);
110
111         assert(track->n_ref > 0);
112
113         track->n_ref++;
114
115         return track;
116 }
117
118 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
119         const char *n;
120
121         if (!track)
122                 return NULL;
123
124         assert(track->n_ref > 0);
125
126         if (track->n_ref > 1) {
127                 track->n_ref --;
128                 return NULL;
129         }
130
131         while ((n = hashmap_first_key(track->names)))
132                 sd_bus_track_remove_name(track, n);
133
134         bus_track_remove_from_queue(track);
135         hashmap_free(track->names);
136         sd_bus_unref(track->bus);
137         free(track);
138
139         return NULL;
140 }
141
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;
145         int r;
146
147         assert(bus);
148         assert(message);
149         assert(track);
150
151         r = sd_bus_message_read(message, "sss", &name, &old, &new);
152         if (r < 0)
153                 return 0;
154
155         sd_bus_track_remove_name(track, name);
156         return 0;
157 }
158
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;
162         const char *match;
163         int r;
164
165         assert_return(track, -EINVAL);
166         assert_return(service_name_is_valid(name), -EINVAL);
167
168         r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
169         if (r < 0)
170                 return r;
171
172         n = strdup(name);
173         if (!n)
174                 return -ENOMEM;
175
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);
179         if (r < 0)
180                 return r;
181
182         r = hashmap_put(track->names, n, slot);
183         if (r == -EEXIST)
184                 return 0;
185         if (r < 0)
186                 return r;
187
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);
191         if (r < 0) {
192                 hashmap_remove(track->names, n);
193                 return r;
194         }
195
196         n = NULL;
197         slot = NULL;
198
199         bus_track_remove_from_queue(track);
200         track->modified = true;
201
202         return 1;
203 }
204
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;
208
209         assert_return(name, -EINVAL);
210
211         if (!track)
212                 return 0;
213
214         slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
215         if (!slot)
216                 return 0;
217
218         if (hashmap_isempty(track->names))
219                 bus_track_add_to_queue(track);
220
221         track->modified = true;
222
223         return 1;
224 }
225
226 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
227         if (!track)
228                 return 0;
229
230         return hashmap_size(track->names);
231 }
232
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);
236
237         return hashmap_get(track->names, (void*) name) ? name : NULL;
238 }
239
240 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
241         const char *n = NULL;
242
243         if (!track)
244                 return NULL;
245
246         track->modified = false;
247         track->iterator = ITERATOR_FIRST;
248
249         hashmap_iterate(track->names, &track->iterator, (const void**) &n);
250         return n;
251 }
252
253 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
254         const char *n = NULL;
255
256         if (!track)
257                 return NULL;
258
259         if (track->modified)
260                 return NULL;
261
262         hashmap_iterate(track->names, &track->iterator, (const void**) &n);
263         return n;
264 }
265
266 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
267         const char *sender;
268
269         assert_return(track, -EINVAL);
270         assert_return(m, -EINVAL);
271
272         sender = sd_bus_message_get_sender(m);
273         if (!sender)
274                 return -EINVAL;
275
276         return sd_bus_track_add_name(track, sender);
277 }
278
279 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
280         const char *sender;
281
282         assert_return(track, -EINVAL);
283         assert_return(m, -EINVAL);
284
285         sender = sd_bus_message_get_sender(m);
286         if (!sender)
287                 return -EINVAL;
288
289         return sd_bus_track_remove_name(track, sender);
290 }
291
292 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
293         assert_return(track, NULL);
294
295         return track->bus;
296 }
297
298 void bus_track_dispatch(sd_bus_track *track) {
299         int r;
300
301         assert(track);
302         assert(track->in_queue);
303         assert(track->handler);
304
305         bus_track_remove_from_queue(track);
306
307         sd_bus_track_ref(track);
308
309         r = track->handler(track, track->userdata);
310         if (r < 0)
311                 log_debug_errno(r, "Failed to process track handler: %m");
312         else if (r == 0)
313                 bus_track_add_to_queue(track);
314
315         sd_bus_track_unref(track);
316 }
317
318 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
319         assert_return(track, NULL);
320
321         return track->userdata;
322 }
323
324 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
325         void *ret;
326
327         assert_return(track, NULL);
328
329         ret = track->userdata;
330         track->userdata = userdata;
331
332         return ret;
333 }