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