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