chiark / gitweb /
bus: add userdata API for bus name tracker objects
[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-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         Set *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         t = new0(sd_bus_track, 1);
94         if (!t)
95                 return -ENOMEM;
96
97         t->n_ref = 1;
98         t->handler = handler;
99         t->userdata = userdata;
100         t->bus = sd_bus_ref(bus);
101
102         bus_track_add_to_queue(t);
103
104         *track = t;
105         return 0;
106 }
107
108 _public_ sd_bus_track* sd_bus_track_ref(sd_bus_track *track) {
109         assert_return(track, NULL);
110
111         assert(track->n_ref > 0);
112
113         track->n_ref++;
114
115         return track;
116 }
117
118 _public_ sd_bus_track* sd_bus_track_unref(sd_bus_track *track) {
119         const char *n;
120
121         if (!track)
122                 return NULL;
123
124         assert(track->n_ref > 0);
125
126         if (track->n_ref > 1) {
127                 track->n_ref --;
128                 return NULL;
129         }
130
131         while ((n = set_first(track->names)))
132                 sd_bus_track_remove_name(track, n);
133
134         bus_track_remove_from_queue(track);
135         set_free(track->names);
136         sd_bus_unref(track->bus);
137         free(track);
138
139         return NULL;
140 }
141
142 static int on_name_owner_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
143         sd_bus_track *track = userdata;
144         const char *name, *old, *new;
145         int r;
146
147         assert(bus);
148         assert(message);
149         assert(track);
150
151         r = sd_bus_message_read(message, "sss", &name, &old, &new);
152         if (r < 0)
153                 return 0;
154
155         sd_bus_track_remove_name(track, name);
156         return 0;
157 }
158
159 _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
160         _cleanup_free_ char *n = NULL;
161         const char *match;
162         int r;
163
164         assert_return(track, -EINVAL);
165         assert_return(service_name_is_valid(name), -EINVAL);
166
167         r = set_ensure_allocated(&track->names, string_hash_func, string_compare_func);
168         if (r < 0)
169                 return r;
170
171         n = strdup(name);
172         if (!n)
173                 return -ENOMEM;
174
175         r = set_put(track->names, n);
176         if (r == -EEXIST)
177                 return 0;
178         if (r < 0)
179                 return r;
180
181         /* First, subscribe to this name */
182         match = MATCH_FOR_NAME(name);
183         r = sd_bus_add_match(track->bus, match, on_name_owner_changed, track);
184         if (r < 0) {
185                 set_remove(track->names, n);
186                 return r;
187         }
188
189         /* Second, check if it is currently existing, or maybe
190          * doesn't, or maybe disappeared already. */
191         r = sd_bus_get_owner(track->bus, name, 0, NULL);
192         if (r < 0) {
193                 set_remove(track->names, n);
194                 sd_bus_remove_match(track->bus, match, on_name_owner_changed, track);
195                 return r;
196         }
197
198         n = 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         const char *match;
208         _cleanup_free_ char *n = NULL;;
209
210         assert_return(name, -EINVAL);
211
212         if (!track)
213                 return 0;
214
215         n = set_remove(track->names, (char*) name);
216         if (!n)
217                 return 0;
218
219         match = MATCH_FOR_NAME(n);
220         sd_bus_remove_match(track->bus, match, on_name_owner_changed, track);
221
222         if (set_isempty(track->names))
223                 bus_track_add_to_queue(track);
224
225         track->modified = true;
226
227         return 1;
228 }
229
230 _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
231         if (!track)
232                 return 0;
233
234         return set_size(track->names);
235 }
236
237 _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
238         assert_return(track, NULL);
239         assert_return(name, NULL);
240
241         return set_get(track->names, (void*) name);
242 }
243
244 _public_ const char* sd_bus_track_first(sd_bus_track *track) {
245         if (!track)
246                 return NULL;
247
248         track->modified = false;
249         track->iterator = NULL;
250
251         return set_iterate(track->names, &track->iterator);
252 }
253
254 _public_ const char* sd_bus_track_next(sd_bus_track *track) {
255         if (!track)
256                 return NULL;
257
258         if (track->modified)
259                 return NULL;
260
261         return set_iterate(track->names, &track->iterator);
262 }
263
264 _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
265         const char *sender;
266
267         assert_return(track, -EINVAL);
268         assert_return(m, -EINVAL);
269
270         sender = sd_bus_message_get_sender(m);
271         if (!sender)
272                 return -EINVAL;
273
274         return sd_bus_track_add_name(track, sender);
275 }
276
277 _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
278         const char *sender;
279
280         assert_return(track, -EINVAL);
281         assert_return(m, -EINVAL);
282
283         sender = sd_bus_message_get_sender(m);
284         if (!sender)
285                 return -EINVAL;
286
287         return sd_bus_track_remove_name(track, sender);
288 }
289
290 _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
291         assert_return(track, NULL);
292
293         return track->bus;
294 }
295
296 void bus_track_dispatch(sd_bus_track *track) {
297         int r;
298
299         assert(track);
300         assert(track->in_queue);
301         assert(track->handler);
302
303         bus_track_remove_from_queue(track);
304
305         sd_bus_track_ref(track);
306
307         r = track->handler(track, track->userdata);
308         if (r < 0)
309                 log_debug("Failed to process track handler: %s", strerror(-r));
310         else if (r == 0)
311                 bus_track_add_to_queue(track);
312
313         sd_bus_track_unref(track);
314 }
315
316 _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
317         assert_return(track, NULL);
318
319         return track->userdata;
320 }
321
322 _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
323         void *ret;
324
325         assert_return(track, NULL);
326
327         ret = track->userdata;
328         track->userdata = userdata;
329
330         return ret;
331 }