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