chiark / gitweb /
Prep v221: Update and clean up build system to sync with upstream
[elogind.git] / src / libelogind / 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         if (!bus->bus_client)
94                 return -EINVAL;
95
96         t = new0(sd_bus_track, 1);
97         if (!t)
98                 return -ENOMEM;
99
100         t->n_ref = 1;
101         t->handler = handler;
102         t->userdata = userdata;
103         t->bus = sd_bus_ref(bus);
104
105         bus_track_add_to_queue(t);
106
107         *track = t;
108         return 0;
109 }
110
111 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
112         assert_return(track, NULL);
113
114         assert(track->n_ref > 0);
115
116         track->n_ref++;
117
118         return track;
119 }
120
121 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
122         const char *n;
123
124         if (!track)
125                 return NULL;
126
127         assert(track->n_ref > 0);
128
129         if (track->n_ref > 1) {
130                 track->n_ref --;
131                 return NULL;
132         }
133
134         while ((n = hashmap_first_key(track->names)))
135                 sd_bus_track_remove_name(track, n);
136
137         bus_track_remove_from_queue(track);
138         hashmap_free(track->names);
139         sd_bus_unref(track->bus);
140         free(track);
141
142         return NULL;
143 }
144
145 static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
146         sd_bus_track *track = userdata;
147         const char *name, *old, *new;
148         int r;
149
150         assert(message);
151         assert(track);
152
153         r = sd_bus_message_read(message, "sss", &name, &old, &new);
154         if (r < 0)
155                 return 0;
156
157         sd_bus_track_remove_name(track, name);
158         return 0;
159 }
160
161 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
162         _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
163         _cleanup_free_ char *n = NULL;
164         const char *match;
165         int r;
166
167         assert_return(track, -EINVAL);
168         assert_return(service_name_is_valid(name), -EINVAL);
169
170         r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
171         if (r < 0)
172                 return r;
173
174         n = strdup(name);
175         if (!n)
176                 return -ENOMEM;
177
178         /* First, subscribe to this name */
179         match = MATCH_FOR_NAME(n);
180         r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
181         if (r < 0)
182                 return r;
183
184         r = hashmap_put(track->names, n, slot);
185         if (r == -EEXIST)
186                 return 0;
187         if (r < 0)
188                 return r;
189
190         /* Second, check if it is currently existing, or maybe
191          * doesn't, or maybe disappeared already. */
192         r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
193         if (r < 0) {
194                 hashmap_remove(track->names, n);
195                 return r;
196         }
197
198         n = NULL;
199         slot = NULL;
200
201         bus_track_remove_from_queue(track);
202         track->modified = true;
203
204         return 1;
205 }
206
207 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
208         _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
209         _cleanup_free_ char *n = NULL;
210
211         assert_return(name, -EINVAL);
212
213         if (!track)
214                 return 0;
215
216         slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
217         if (!slot)
218                 return 0;
219
220         if (hashmap_isempty(track->names))
221                 bus_track_add_to_queue(track);
222
223         track->modified = true;
224
225         return 1;
226 }
227
228 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
229         if (!track)
230                 return 0;
231
232         return hashmap_size(track->names);
233 }
234
235 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
236         assert_return(track, NULL);
237         assert_return(name, NULL);
238
239         return hashmap_get(track->names, (void*) name) ? name : NULL;
240 }
241
242 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
243         const char *n = NULL;
244
245         if (!track)
246                 return NULL;
247
248         track->modified = false;
249         track->iterator = ITERATOR_FIRST;
250
251         hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
252         return n;
253 }
254
255 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
256         const char *n = NULL;
257
258         if (!track)
259                 return NULL;
260
261         if (track->modified)
262                 return NULL;
263
264         hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
265         return n;
266 }
267
268 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
269         const char *sender;
270
271         assert_return(track, -EINVAL);
272         assert_return(m, -EINVAL);
273
274         sender = sd_bus_message_get_sender(m);
275         if (!sender)
276                 return -EINVAL;
277
278         return sd_bus_track_add_name(track, sender);
279 }
280
281 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
282         const char *sender;
283
284         assert_return(track, -EINVAL);
285         assert_return(m, -EINVAL);
286
287         sender = sd_bus_message_get_sender(m);
288         if (!sender)
289                 return -EINVAL;
290
291         return sd_bus_track_remove_name(track, sender);
292 }
293
294 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
295         assert_return(track, NULL);
296
297         return track->bus;
298 }
299
300 void bus_track_dispatch(sd_bus_track *track) {
301         int r;
302
303         assert(track);
304         assert(track->in_queue);
305         assert(track->handler);
306
307         bus_track_remove_from_queue(track);
308
309         sd_bus_track_ref(track);
310
311         r = track->handler(track, track->userdata);
312         if (r < 0)
313                 log_debug_errno(r, "Failed to process track handler: %m");
314         else if (r == 0)
315                 bus_track_add_to_queue(track);
316
317         sd_bus_track_unref(track);
318 }
319
320 /// UNNEEDED by elogind
321 #if 0
322 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
323         assert_return(track, NULL);
324
325         return track->userdata;
326 }
327
328 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
329         void *ret;
330
331         assert_return(track, NULL);
332
333         ret = track->userdata;
334         track->userdata = userdata;
335
336         return ret;
337 }
338 #endif // 0