Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include "sd-bus.h"
4 :
5 : #include "alloc-util.h"
6 : #include "bus-internal.h"
7 : #include "bus-track.h"
8 : #include "bus-util.h"
9 :
10 : struct track_item {
11 : unsigned n_ref;
12 : char *name;
13 : sd_bus_slot *slot;
14 : };
15 :
16 : struct sd_bus_track {
17 : unsigned n_ref;
18 : unsigned n_adding; /* are we in the process of adding a new name? */
19 : sd_bus *bus;
20 : sd_bus_track_handler_t handler;
21 : void *userdata;
22 : Hashmap *names;
23 : LIST_FIELDS(sd_bus_track, queue);
24 : Iterator iterator;
25 : bool in_list:1; /* In bus->tracks? */
26 : bool in_queue:1; /* In bus->track_queue? */
27 : bool modified:1;
28 : bool recursive:1;
29 : sd_bus_destroy_t destroy_callback;
30 :
31 : LIST_FIELDS(sd_bus_track, tracks);
32 : };
33 :
34 : #define MATCH_FOR_NAME(name) \
35 : strjoina("type='signal'," \
36 : "sender='org.freedesktop.DBus'," \
37 : "path='/org/freedesktop/DBus'," \
38 : "interface='org.freedesktop.DBus'," \
39 : "member='NameOwnerChanged'," \
40 : "arg0='", name, "'")
41 :
42 2 : static struct track_item* track_item_free(struct track_item *i) {
43 :
44 2 : if (!i)
45 0 : return NULL;
46 :
47 2 : sd_bus_slot_unref(i->slot);
48 2 : free(i->name);
49 2 : return mfree(i);
50 : }
51 :
52 2 : DEFINE_TRIVIAL_CLEANUP_FUNC(struct track_item*, track_item_free);
53 1 : DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(track_item_hash_ops, char, string_hash_func, string_compare_func,
54 : struct track_item, track_item_free);
55 :
56 4 : static void bus_track_add_to_queue(sd_bus_track *track) {
57 4 : assert(track);
58 :
59 : /* Adds the bus track object to the queue of objects we should dispatch next, subject to a number of
60 : * conditions. */
61 :
62 : /* Already in the queue? */
63 4 : if (track->in_queue)
64 0 : return;
65 :
66 : /* if we are currently in the process of adding a new name, then let's not enqueue this just yet, let's wait
67 : * until the addition is complete. */
68 4 : if (track->n_adding > 0)
69 0 : return;
70 :
71 : /* still referenced? */
72 4 : if (hashmap_size(track->names) > 0)
73 0 : return;
74 :
75 : /* Nothing to call? */
76 4 : if (!track->handler)
77 0 : return;
78 :
79 : /* Already closed? */
80 4 : if (!track->in_list)
81 1 : return;
82 :
83 3 : LIST_PREPEND(queue, track->bus->track_queue, track);
84 3 : track->in_queue = true;
85 : }
86 :
87 8 : static void bus_track_remove_from_queue(sd_bus_track *track) {
88 8 : assert(track);
89 :
90 8 : if (!track->in_queue)
91 5 : return;
92 :
93 3 : LIST_REMOVE(queue, track->bus->track_queue, track);
94 3 : track->in_queue = false;
95 : }
96 :
97 1 : static int bus_track_remove_name_fully(sd_bus_track *track, const char *name) {
98 : struct track_item *i;
99 :
100 1 : assert(track);
101 1 : assert(name);
102 :
103 1 : i = hashmap_remove(track->names, name);
104 1 : if (!i)
105 0 : return 0;
106 :
107 1 : track_item_free(i);
108 :
109 1 : bus_track_add_to_queue(track);
110 :
111 1 : track->modified = true;
112 1 : return 1;
113 : }
114 :
115 2 : _public_ int sd_bus_track_new(
116 : sd_bus *bus,
117 : sd_bus_track **track,
118 : sd_bus_track_handler_t handler,
119 : void *userdata) {
120 :
121 : sd_bus_track *t;
122 :
123 2 : assert_return(bus, -EINVAL);
124 2 : assert_return(bus = bus_resolve(bus), -ENOPKG);
125 2 : assert_return(track, -EINVAL);
126 :
127 2 : if (!bus->bus_client)
128 0 : return -EINVAL;
129 :
130 2 : t = new0(sd_bus_track, 1);
131 2 : if (!t)
132 0 : return -ENOMEM;
133 :
134 2 : t->n_ref = 1;
135 2 : t->handler = handler;
136 2 : t->userdata = userdata;
137 2 : t->bus = sd_bus_ref(bus);
138 :
139 2 : LIST_PREPEND(tracks, bus->tracks, t);
140 2 : t->in_list = true;
141 :
142 2 : bus_track_add_to_queue(t);
143 :
144 2 : *track = t;
145 2 : return 0;
146 : }
147 :
148 2 : static sd_bus_track *track_free(sd_bus_track *track) {
149 2 : assert(track);
150 :
151 2 : if (track->in_list)
152 1 : LIST_REMOVE(tracks, track->bus->tracks, track);
153 :
154 2 : bus_track_remove_from_queue(track);
155 2 : track->names = hashmap_free(track->names);
156 2 : track->bus = sd_bus_unref(track->bus);
157 :
158 2 : if (track->destroy_callback)
159 0 : track->destroy_callback(track->userdata);
160 :
161 2 : return mfree(track);
162 : }
163 :
164 2359 : DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_track, sd_bus_track, track_free);
165 :
166 1 : static int on_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error) {
167 1 : sd_bus_track *track = userdata;
168 : const char *name, *old, *new;
169 : int r;
170 :
171 1 : assert(message);
172 1 : assert(track);
173 :
174 1 : r = sd_bus_message_read(message, "sss", &name, &old, &new);
175 1 : if (r < 0)
176 0 : return 0;
177 :
178 1 : bus_track_remove_name_fully(track, name);
179 1 : return 0;
180 : }
181 :
182 2 : _public_ int sd_bus_track_add_name(sd_bus_track *track, const char *name) {
183 2 : _cleanup_(track_item_freep) struct track_item *n = NULL;
184 : struct track_item *i;
185 : const char *match;
186 : int r;
187 :
188 2 : assert_return(track, -EINVAL);
189 2 : assert_return(service_name_is_valid(name), -EINVAL);
190 :
191 2 : i = hashmap_get(track->names, name);
192 2 : if (i) {
193 0 : if (track->recursive) {
194 0 : unsigned k = track->n_ref + 1;
195 :
196 0 : if (k < track->n_ref) /* Check for overflow */
197 0 : return -EOVERFLOW;
198 :
199 0 : track->n_ref = k;
200 : }
201 :
202 0 : bus_track_remove_from_queue(track);
203 0 : return 0;
204 : }
205 :
206 2 : r = hashmap_ensure_allocated(&track->names, &track_item_hash_ops);
207 2 : if (r < 0)
208 0 : return r;
209 :
210 2 : n = new0(struct track_item, 1);
211 2 : if (!n)
212 0 : return -ENOMEM;
213 2 : n->name = strdup(name);
214 2 : if (!n->name)
215 0 : return -ENOMEM;
216 :
217 : /* First, subscribe to this name */
218 14 : match = MATCH_FOR_NAME(name);
219 :
220 2 : bus_track_remove_from_queue(track); /* don't dispatch this while we work in it */
221 :
222 2 : r = sd_bus_add_match_async(track->bus, &n->slot, match, on_name_owner_changed, NULL, track);
223 2 : if (r < 0) {
224 0 : bus_track_add_to_queue(track);
225 0 : return r;
226 : }
227 :
228 2 : r = hashmap_put(track->names, n->name, n);
229 2 : if (r < 0) {
230 0 : bus_track_add_to_queue(track);
231 0 : return r;
232 : }
233 :
234 : /* Second, check if it is currently existing, or maybe doesn't, or maybe disappeared already. */
235 2 : track->n_adding++; /* again, make sure this isn't dispatch while we are working in it */
236 2 : r = sd_bus_get_name_creds(track->bus, name, 0, NULL);
237 2 : track->n_adding--;
238 2 : if (r < 0) {
239 0 : hashmap_remove(track->names, name);
240 0 : bus_track_add_to_queue(track);
241 0 : return r;
242 : }
243 :
244 2 : n->n_ref = 1;
245 2 : n = NULL;
246 :
247 2 : bus_track_remove_from_queue(track);
248 2 : track->modified = true;
249 :
250 2 : return 1;
251 : }
252 :
253 0 : _public_ int sd_bus_track_remove_name(sd_bus_track *track, const char *name) {
254 : struct track_item *i;
255 :
256 0 : assert_return(name, -EINVAL);
257 :
258 0 : if (!track) /* Treat a NULL track object as an empty track object */
259 0 : return 0;
260 :
261 0 : if (!track->recursive)
262 0 : return bus_track_remove_name_fully(track, name);
263 :
264 0 : i = hashmap_get(track->names, name);
265 0 : if (!i)
266 0 : return -EUNATCH;
267 0 : if (i->n_ref <= 0)
268 0 : return -EUNATCH;
269 :
270 0 : i->n_ref--;
271 :
272 0 : if (i->n_ref <= 0)
273 0 : return bus_track_remove_name_fully(track, name);
274 :
275 0 : return 1;
276 : }
277 :
278 18800 : _public_ unsigned sd_bus_track_count(sd_bus_track *track) {
279 :
280 18800 : if (!track) /* Let's consider a NULL object equivalent to an empty object */
281 18800 : return 0;
282 :
283 : /* This signature really should have returned an int, so that we can propagate errors. But well, ... Also, note
284 : * that this returns the number of names being watched, and multiple references to the same name are not
285 : * counted. */
286 :
287 0 : return hashmap_size(track->names);
288 : }
289 :
290 0 : _public_ const char* sd_bus_track_contains(sd_bus_track *track, const char *name) {
291 0 : assert_return(name, NULL);
292 :
293 0 : if (!track) /* Let's consider a NULL object equivalent to an empty object */
294 0 : return NULL;
295 :
296 0 : return hashmap_get(track->names, (void*) name) ? name : NULL;
297 : }
298 :
299 1190 : _public_ const char* sd_bus_track_first(sd_bus_track *track) {
300 1190 : const char *n = NULL;
301 :
302 1190 : if (!track)
303 1190 : return NULL;
304 :
305 0 : track->modified = false;
306 0 : track->iterator = ITERATOR_FIRST;
307 :
308 0 : (void) hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
309 0 : return n;
310 : }
311 :
312 0 : _public_ const char* sd_bus_track_next(sd_bus_track *track) {
313 0 : const char *n = NULL;
314 :
315 0 : if (!track)
316 0 : return NULL;
317 :
318 0 : if (track->modified)
319 0 : return NULL;
320 :
321 0 : (void) hashmap_iterate(track->names, &track->iterator, NULL, (const void**) &n);
322 0 : return n;
323 : }
324 :
325 0 : _public_ int sd_bus_track_add_sender(sd_bus_track *track, sd_bus_message *m) {
326 : const char *sender;
327 :
328 0 : assert_return(track, -EINVAL);
329 0 : assert_return(m, -EINVAL);
330 :
331 0 : if (sd_bus_message_get_bus(m) != track->bus)
332 0 : return -EINVAL;
333 :
334 0 : sender = sd_bus_message_get_sender(m);
335 0 : if (!sender)
336 0 : return -EINVAL;
337 :
338 0 : return sd_bus_track_add_name(track, sender);
339 : }
340 :
341 0 : _public_ int sd_bus_track_remove_sender(sd_bus_track *track, sd_bus_message *m) {
342 : const char *sender;
343 :
344 0 : assert_return(m, -EINVAL);
345 :
346 0 : if (!track) /* Treat a NULL track object as an empty track object */
347 0 : return 0;
348 :
349 0 : if (sd_bus_message_get_bus(m) != track->bus)
350 0 : return -EINVAL;
351 :
352 0 : sender = sd_bus_message_get_sender(m);
353 0 : if (!sender)
354 0 : return -EINVAL;
355 :
356 0 : return sd_bus_track_remove_name(track, sender);
357 : }
358 :
359 2 : _public_ sd_bus* sd_bus_track_get_bus(sd_bus_track *track) {
360 2 : assert_return(track, NULL);
361 :
362 2 : return track->bus;
363 : }
364 :
365 2 : void bus_track_dispatch(sd_bus_track *track) {
366 : int r;
367 :
368 2 : assert(track);
369 2 : assert(track->handler);
370 :
371 2 : bus_track_remove_from_queue(track);
372 :
373 2 : sd_bus_track_ref(track);
374 :
375 2 : r = track->handler(track, track->userdata);
376 2 : if (r < 0)
377 0 : log_debug_errno(r, "Failed to process track handler: %m");
378 2 : else if (r == 0)
379 1 : bus_track_add_to_queue(track);
380 :
381 2 : sd_bus_track_unref(track);
382 2 : }
383 :
384 1 : void bus_track_close(sd_bus_track *track) {
385 1 : assert(track);
386 :
387 : /* Called whenever our bus connected is closed. If so, and our track object is non-empty, dispatch it
388 : * immediately, as we are closing now, but first flush out all names. */
389 :
390 1 : if (!track->in_list)
391 0 : return; /* We already closed this one, don't close it again. */
392 :
393 : /* Remember that this one is closed now */
394 1 : LIST_REMOVE(tracks, track->bus->tracks, track);
395 1 : track->in_list = false;
396 :
397 : /* If there's no name in this one anyway, we don't have to dispatch */
398 1 : if (hashmap_isempty(track->names))
399 0 : return;
400 :
401 : /* Let's flush out all names */
402 1 : hashmap_clear(track->names);
403 :
404 : /* Invoke handler */
405 1 : if (track->handler)
406 1 : bus_track_dispatch(track);
407 : }
408 :
409 0 : _public_ void *sd_bus_track_get_userdata(sd_bus_track *track) {
410 0 : assert_return(track, NULL);
411 :
412 0 : return track->userdata;
413 : }
414 :
415 0 : _public_ void *sd_bus_track_set_userdata(sd_bus_track *track, void *userdata) {
416 : void *ret;
417 :
418 0 : assert_return(track, NULL);
419 :
420 0 : ret = track->userdata;
421 0 : track->userdata = userdata;
422 :
423 0 : return ret;
424 : }
425 :
426 0 : _public_ int sd_bus_track_set_destroy_callback(sd_bus_track *track, sd_bus_destroy_t callback) {
427 0 : assert_return(track, -EINVAL);
428 :
429 0 : track->destroy_callback = callback;
430 0 : return 0;
431 : }
432 :
433 0 : _public_ int sd_bus_track_get_destroy_callback(sd_bus_track *track, sd_bus_destroy_t *ret) {
434 0 : assert_return(track, -EINVAL);
435 :
436 0 : if (ret)
437 0 : *ret = track->destroy_callback;
438 :
439 0 : return !!track->destroy_callback;
440 : }
441 :
442 0 : _public_ int sd_bus_track_set_recursive(sd_bus_track *track, int b) {
443 0 : assert_return(track, -EINVAL);
444 :
445 0 : if (track->recursive == !!b)
446 0 : return 0;
447 :
448 0 : if (!hashmap_isempty(track->names))
449 0 : return -EBUSY;
450 :
451 0 : track->recursive = b;
452 0 : return 0;
453 : }
454 :
455 0 : _public_ int sd_bus_track_get_recursive(sd_bus_track *track) {
456 0 : assert_return(track, -EINVAL);
457 :
458 0 : return track->recursive;
459 : }
460 :
461 0 : _public_ int sd_bus_track_count_sender(sd_bus_track *track, sd_bus_message *m) {
462 : const char *sender;
463 :
464 0 : assert_return(m, -EINVAL);
465 :
466 0 : if (!track) /* Let's consider a NULL object equivalent to an empty object */
467 0 : return 0;
468 :
469 0 : if (sd_bus_message_get_bus(m) != track->bus)
470 0 : return -EINVAL;
471 :
472 0 : sender = sd_bus_message_get_sender(m);
473 0 : if (!sender)
474 0 : return -EINVAL;
475 :
476 0 : return sd_bus_track_count_name(track, sender);
477 : }
478 :
479 0 : _public_ int sd_bus_track_count_name(sd_bus_track *track, const char *name) {
480 : struct track_item *i;
481 :
482 0 : assert_return(service_name_is_valid(name), -EINVAL);
483 :
484 0 : if (!track) /* Let's consider a NULL object equivalent to an empty object */
485 0 : return 0;
486 :
487 0 : i = hashmap_get(track->names, name);
488 0 : if (!i)
489 0 : return 0;
490 :
491 0 : return i->n_ref;
492 : }
|