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