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