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