chiark / gitweb /
sd-bus: when bus operations are to be executed on direct connections always fail
[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 *bus, 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(bus);
151         assert(message);
152         assert(track);
153
154         r = sd_bus_message_read(message, "sss", &name, &old, &new);
155         if (r < 0)
156                 return 0;
157
158         sd_bus_track_remove_name(track, name);
159         return 0;
160 }
161
162 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
163         _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
164         _cleanup_free_ char *n = NULL;
165         const char *match;
166         int r;
167
168         assert_return(track, -EINVAL);
169         assert_return(service_name_is_valid(name), -EINVAL);
170
171         r = hashmap_ensure_allocated(&track->names, &string_hash_ops);
172         if (r < 0)
173                 return r;
174
175         n = strdup(name);
176         if (!n)
177                 return -ENOMEM;
178
179         /* First, subscribe to this name */
180         match = MATCH_FOR_NAME(n);
181         r = sd_bus_add_match(track->bus, &slot, match, on_name_owner_changed, track);
182         if (r < 0)
183                 return r;
184
185         r = hashmap_put(track->names, n, slot);
186         if (r == -EEXIST)
187                 return 0;
188         if (r < 0)
189                 return r;
190
191         /* Second, check if it is currently existing, or maybe
192          * doesn't, or maybe disappeared already. */
193         r = sd_bus_get_name_creds(track->bus, n, 0, NULL);
194         if (r < 0) {
195                 hashmap_remove(track->names, n);
196                 return r;
197         }
198
199         n = NULL;
200         slot = NULL;
201
202         bus_track_remove_from_queue(track);
203         track->modified = true;
204
205         return 1;
206 }
207
208 _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
209         _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
210         _cleanup_free_ char *n = NULL;
211
212         assert_return(name, -EINVAL);
213
214         if (!track)
215                 return 0;
216
217         slot = hashmap_remove2(track->names, (char*) name, (void**) &n);
218         if (!slot)
219                 return 0;
220
221         if (hashmap_isempty(track->names))
222                 bus_track_add_to_queue(track);
223
224         track->modified = true;
225
226         return 1;
227 }
228
229 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
230         if (!track)
231                 return 0;
232
233         return hashmap_size(track->names);
234 }
235
236 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
237         assert_return(track, NULL);
238         assert_return(name, NULL);
239
240         return hashmap_get(track->names, (void*) name) ? name : NULL;
241 }
242
243 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
244         const char *n = NULL;
245
246         if (!track)
247                 return NULL;
248
249         track->modified = false;
250         track->iterator = ITERATOR_FIRST;
251
252         hashmap_iterate(track->names, &track->iterator, (const void**) &n);
253         return n;
254 }
255
256 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
257         const char *n = NULL;
258
259         if (!track)
260                 return NULL;
261
262         if (track->modified)
263                 return NULL;
264
265         hashmap_iterate(track->names, &track->iterator, (const void**) &n);
266         return n;
267 }
268
269 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
270         const char *sender;
271
272         assert_return(track, -EINVAL);
273         assert_return(m, -EINVAL);
274
275         sender = sd_bus_message_get_sender(m);
276         if (!sender)
277                 return -EINVAL;
278
279         return sd_bus_track_add_name(track, sender);
280 }
281
282 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
283         const char *sender;
284
285         assert_return(track, -EINVAL);
286         assert_return(m, -EINVAL);
287
288         sender = sd_bus_message_get_sender(m);
289         if (!sender)
290                 return -EINVAL;
291
292         return sd_bus_track_remove_name(track, sender);
293 }
294
295 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
296         assert_return(track, NULL);
297
298         return track->bus;
299 }
300
301 void bus_track_dispatch(sd_bus_track *track) {
302         int r;
303
304         assert(track);
305         assert(track->in_queue);
306         assert(track->handler);
307
308         bus_track_remove_from_queue(track);
309
310         sd_bus_track_ref(track);
311
312         r = track->handler(track, track->userdata);
313         if (r < 0)
314                 log_debug_errno(r, "Failed to process track handler: %m");
315         else if (r == 0)
316                 bus_track_add_to_queue(track);
317
318         sd_bus_track_unref(track);
319 }
320
321 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
322         assert_return(track, NULL);
323
324         return track->userdata;
325 }
326
327 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
328         void *ret;
329
330         assert_return(track, NULL);
331
332         ret = track->userdata;
333         track->userdata = userdata;
334
335         return ret;
336 }