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