chiark / gitweb /
tree-wide: make ++/-- usage consistent WRT spacing
[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
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         if (!bus->bus_client)
95                 return -EINVAL;
96
97         t = new0(sd_bus_track, 1);
98         if (!t)
99                 return -ENOMEM;
100
101         t->n_ref = 1;
102         t->handler = handler;
103         t->userdata = userdata;
104         t->bus = sd_bus_ref(bus);
105
106         bus_track_add_to_queue(t);
107
108         *track = t;
109         return 0;
110 }
111
112 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
113
114         if (!track)
115                 return 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_(sd_bus_slot_unrefp) 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_(sd_bus_slot_unrefp) 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