]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-objects.c
Merge pull request #1012 from gentoo-root/master
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-objects.c
CommitLineData
992c052c
LP
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 "strv.h"
23#include "set.h"
24#include "bus-internal.h"
25#include "bus-message.h"
26#include "bus-type.h"
27#include "bus-signature.h"
28#include "bus-introspect.h"
19befb2d
LP
29#include "bus-slot.h"
30#include "bus-objects.h"
992c052c
LP
31
32static int node_vtable_get_userdata(
33 sd_bus *bus,
34 const char *path,
35 struct node_vtable *c,
f00c3121
LP
36 void **userdata,
37 sd_bus_error *error) {
992c052c 38
19befb2d 39 sd_bus_slot *s;
992c052c
LP
40 void *u;
41 int r;
42
43 assert(bus);
44 assert(path);
45 assert(c);
46
19befb2d
LP
47 s = container_of(c, sd_bus_slot, node_vtable);
48 u = s->userdata;
992c052c 49 if (c->find) {
1b64f838 50 bus->current_slot = sd_bus_slot_ref(s);
caa82984 51 bus->current_userdata = u;
f00c3121 52 r = c->find(bus, path, c->interface, u, &u, error);
caa82984 53 bus->current_userdata = NULL;
1b64f838 54 bus->current_slot = sd_bus_slot_unref(s);
19befb2d 55
f00c3121
LP
56 if (r < 0)
57 return r;
58 if (sd_bus_error_is_set(error))
5958d089 59 return -sd_bus_error_get_errno(error);
f00c3121 60 if (r == 0)
992c052c
LP
61 return r;
62 }
63
64 if (userdata)
65 *userdata = u;
66
67 return 1;
68}
69
09c8a7c6 70static void *vtable_method_convert_userdata(const sd_bus_vtable *p, void *u) {
71 assert(p);
72
73 return (uint8_t*) u + p->x.method.offset;
74}
75
992c052c
LP
76static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
77 assert(p);
78
77a874a3 79 return (uint8_t*) u + p->x.property.offset;
992c052c
LP
80}
81
82static int vtable_property_get_userdata(
83 sd_bus *bus,
84 const char *path,
85 struct vtable_member *p,
f00c3121
LP
86 void **userdata,
87 sd_bus_error *error) {
992c052c
LP
88
89 void *u;
90 int r;
91
92 assert(bus);
93 assert(path);
94 assert(p);
95 assert(userdata);
96
f00c3121 97 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
992c052c
LP
98 if (r <= 0)
99 return r;
68313d3d
LP
100 if (bus->nodes_modified)
101 return 0;
992c052c
LP
102
103 *userdata = vtable_property_convert_userdata(p->vtable, u);
104 return 1;
105}
106
dfa92725
LP
107static int add_enumerated_to_set(
108 sd_bus *bus,
109 const char *prefix,
110 struct node_enumerator *first,
f00c3121
LP
111 Set *s,
112 sd_bus_error *error) {
dfa92725 113
992c052c
LP
114 struct node_enumerator *c;
115 int r;
116
117 assert(bus);
118 assert(prefix);
119 assert(s);
120
121 LIST_FOREACH(enumerators, c, first) {
122 char **children = NULL, **k;
1b64f838 123 sd_bus_slot *slot;
992c052c 124
68313d3d
LP
125 if (bus->nodes_modified)
126 return 0;
127
1b64f838
LP
128 slot = container_of(c, sd_bus_slot, node_enumerator);
129
130 bus->current_slot = sd_bus_slot_ref(slot);
caa82984 131 bus->current_userdata = slot->userdata;
1b64f838 132 r = c->callback(bus, prefix, slot->userdata, &children, error);
caa82984 133 bus->current_userdata = NULL;
1b64f838 134 bus->current_slot = sd_bus_slot_unref(slot);
19befb2d 135
992c052c
LP
136 if (r < 0)
137 return r;
f00c3121 138 if (sd_bus_error_is_set(error))
5958d089 139 return -sd_bus_error_get_errno(error);
992c052c
LP
140
141 STRV_FOREACH(k, children) {
142 if (r < 0) {
143 free(*k);
144 continue;
145 }
146
5d66866d 147 if (!object_path_is_valid(*k)){
992c052c
LP
148 free(*k);
149 r = -EINVAL;
150 continue;
151 }
152
5d66866d
LP
153 if (!object_path_startswith(*k, prefix)) {
154 free(*k);
155 continue;
156 }
157
992c052c 158 r = set_consume(s, *k);
52c7f2b2
LP
159 if (r == -EEXIST)
160 r = 0;
992c052c
LP
161 }
162
163 free(children);
164 if (r < 0)
165 return r;
166 }
167
168 return 0;
169}
170
dfa92725
LP
171static int add_subtree_to_set(
172 sd_bus *bus,
173 const char *prefix,
174 struct node *n,
2d5c8a27 175 bool skip_subhierarchies,
f00c3121
LP
176 Set *s,
177 sd_bus_error *error) {
dfa92725 178
992c052c
LP
179 struct node *i;
180 int r;
181
182 assert(bus);
183 assert(prefix);
184 assert(n);
185 assert(s);
186
f00c3121 187 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
992c052c
LP
188 if (r < 0)
189 return r;
68313d3d
LP
190 if (bus->nodes_modified)
191 return 0;
992c052c
LP
192
193 LIST_FOREACH(siblings, i, n->child) {
194 char *t;
195
5d66866d
LP
196 if (!object_path_startswith(i->path, prefix))
197 continue;
198
992c052c
LP
199 t = strdup(i->path);
200 if (!t)
201 return -ENOMEM;
202
203 r = set_consume(s, t);
204 if (r < 0 && r != -EEXIST)
205 return r;
206
2d5c8a27
DH
207 if (!skip_subhierarchies || !i->object_managers) {
208 r = add_subtree_to_set(bus, prefix, i, skip_subhierarchies, s, error);
209 if (r < 0)
210 return r;
211 if (bus->nodes_modified)
212 return 0;
213 }
992c052c
LP
214 }
215
216 return 0;
217}
218
dfa92725
LP
219static int get_child_nodes(
220 sd_bus *bus,
221 const char *prefix,
222 struct node *n,
2d5c8a27 223 bool skip_subhierarchies,
f00c3121
LP
224 Set **_s,
225 sd_bus_error *error) {
dfa92725 226
992c052c
LP
227 Set *s = NULL;
228 int r;
229
230 assert(bus);
dfa92725 231 assert(prefix);
992c052c
LP
232 assert(n);
233 assert(_s);
234
d5099efc 235 s = set_new(&string_hash_ops);
992c052c
LP
236 if (!s)
237 return -ENOMEM;
238
2d5c8a27 239 r = add_subtree_to_set(bus, prefix, n, skip_subhierarchies, s, error);
992c052c
LP
240 if (r < 0) {
241 set_free_free(s);
242 return r;
243 }
244
245 *_s = s;
246 return 0;
247}
248
249static int node_callbacks_run(
250 sd_bus *bus,
251 sd_bus_message *m,
252 struct node_callback *first,
253 bool require_fallback,
254 bool *found_object) {
255
256 struct node_callback *c;
257 int r;
258
259 assert(bus);
260 assert(m);
261 assert(found_object);
262
263 LIST_FOREACH(callbacks, c, first) {
ebcf1f97 264 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
1b64f838 265 sd_bus_slot *slot;
ebcf1f97 266
68313d3d
LP
267 if (bus->nodes_modified)
268 return 0;
269
992c052c
LP
270 if (require_fallback && !c->is_fallback)
271 continue;
272
273 *found_object = true;
274
275 if (c->last_iteration == bus->iteration_counter)
276 continue;
277
68313d3d
LP
278 c->last_iteration = bus->iteration_counter;
279
992c052c
LP
280 r = sd_bus_message_rewind(m, true);
281 if (r < 0)
282 return r;
283
1b64f838
LP
284 slot = container_of(c, sd_bus_slot, node_callback);
285
286 bus->current_slot = sd_bus_slot_ref(slot);
caa82984
LP
287 bus->current_handler = c->callback;
288 bus->current_userdata = slot->userdata;
19070062 289 r = c->callback(m, slot->userdata, &error_buffer);
caa82984
LP
290 bus->current_userdata = NULL;
291 bus->current_handler = NULL;
1b64f838 292 bus->current_slot = sd_bus_slot_unref(slot);
19befb2d 293
ebcf1f97 294 r = bus_maybe_reply_error(m, r, &error_buffer);
992c052c
LP
295 if (r != 0)
296 return r;
297 }
298
299 return 0;
300}
301
adacb957
LP
302#define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
303
304static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
adacb957 305 uint64_t cap;
adacb957
LP
306 int r;
307
308 assert(bus);
309 assert(m);
310 assert(c);
311
312 /* If the entire bus is trusted let's grant access */
313 if (bus->trusted)
314 return 0;
315
316 /* If the member is marked UNPRIVILEGED let's grant access */
317 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
318 return 0;
319
adacb957
LP
320 /* Check have the caller has the requested capability
321 * set. Note that the flags value contains the capability
322 * number plus one, which we need to subtract here. We do this
323 * so that we have 0 as special value for "default
324 * capability". */
325 cap = CAPABILITY_SHIFT(c->vtable->flags);
326 if (cap == 0)
327 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
328 if (cap == 0)
329 cap = CAP_SYS_ADMIN;
330 else
331 cap --;
332
def9a7aa
LP
333 r = sd_bus_query_sender_privilege(m, cap);
334 if (r < 0)
335 return r;
adacb957 336 if (r > 0)
def9a7aa 337 return 0;
adacb957
LP
338
339 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
340}
341
992c052c
LP
342static int method_callbacks_run(
343 sd_bus *bus,
344 sd_bus_message *m,
345 struct vtable_member *c,
346 bool require_fallback,
347 bool *found_object) {
348
f00c3121 349 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
992c052c
LP
350 const char *signature;
351 void *u;
352 int r;
353
354 assert(bus);
355 assert(m);
356 assert(c);
357 assert(found_object);
358
359 if (require_fallback && !c->parent->is_fallback)
360 return 0;
361
adacb957
LP
362 r = check_access(bus, m, c, &error);
363 if (r < 0)
364 return bus_maybe_reply_error(m, r, &error);
365
f00c3121 366 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
992c052c 367 if (r <= 0)
f00c3121 368 return bus_maybe_reply_error(m, r, &error);
68313d3d
LP
369 if (bus->nodes_modified)
370 return 0;
992c052c 371
09c8a7c6 372 u = vtable_method_convert_userdata(c->vtable, u);
373
992c052c
LP
374 *found_object = true;
375
68313d3d
LP
376 if (c->last_iteration == bus->iteration_counter)
377 return 0;
378
379 c->last_iteration = bus->iteration_counter;
380
992c052c
LP
381 r = sd_bus_message_rewind(m, true);
382 if (r < 0)
383 return r;
384
40ca29a1
LP
385 signature = sd_bus_message_get_signature(m, true);
386 if (!signature)
387 return -EINVAL;
992c052c 388
f00c3121 389 if (!streq(strempty(c->vtable->x.method.signature), signature))
86b8d289
LP
390 return sd_bus_reply_method_errorf(
391 m,
392 SD_BUS_ERROR_INVALID_ARGS,
393 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
394 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
992c052c 395
6717d473
LP
396 /* Keep track what the signature of the reply to this message
397 * should be, so that this can be enforced when sealing the
398 * reply. */
399 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
400
ebcf1f97 401 if (c->vtable->x.method.handler) {
1b64f838
LP
402 sd_bus_slot *slot;
403
404 slot = container_of(c->parent, sd_bus_slot, node_vtable);
19befb2d 405
1b64f838 406 bus->current_slot = sd_bus_slot_ref(slot);
caa82984
LP
407 bus->current_handler = c->vtable->x.method.handler;
408 bus->current_userdata = u;
19070062 409 r = c->vtable->x.method.handler(m, u, &error);
caa82984
LP
410 bus->current_userdata = NULL;
411 bus->current_handler = NULL;
1b64f838 412 bus->current_slot = sd_bus_slot_unref(slot);
19befb2d 413
f00c3121 414 return bus_maybe_reply_error(m, r, &error);
ebcf1f97 415 }
992c052c
LP
416
417 /* If the method callback is NULL, make this a successful NOP */
df2d202e 418 r = sd_bus_reply_method_return(m, NULL);
992c052c
LP
419 if (r < 0)
420 return r;
421
422 return 1;
423}
424
425static int invoke_property_get(
426 sd_bus *bus,
19befb2d 427 sd_bus_slot *slot,
992c052c
LP
428 const sd_bus_vtable *v,
429 const char *path,
430 const char *interface,
431 const char *property,
ebcf1f97
LP
432 sd_bus_message *reply,
433 void *userdata,
434 sd_bus_error *error) {
992c052c 435
af8601fa 436 const void *p;
f00c3121 437 int r;
992c052c
LP
438
439 assert(bus);
19befb2d 440 assert(slot);
992c052c 441 assert(v);
dfa92725
LP
442 assert(path);
443 assert(interface);
444 assert(property);
ebcf1f97 445 assert(reply);
992c052c 446
f00c3121 447 if (v->x.property.get) {
19befb2d 448
1b64f838 449 bus->current_slot = sd_bus_slot_ref(slot);
caa82984 450 bus->current_userdata = userdata;
f00c3121 451 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
caa82984 452 bus->current_userdata = NULL;
1b64f838 453 bus->current_slot = sd_bus_slot_unref(slot);
19befb2d 454
f00c3121
LP
455 if (r < 0)
456 return r;
457 if (sd_bus_error_is_set(error))
5958d089 458 return -sd_bus_error_get_errno(error);
f00c3121
LP
459 return r;
460 }
992c052c
LP
461
462 /* Automatic handling if no callback is defined. */
463
c13c7de3 464 if (streq(v->x.property.signature, "as"))
ebcf1f97 465 return sd_bus_message_append_strv(reply, *(char***) userdata);
c13c7de3 466
77a874a3
LP
467 assert(signature_is_single(v->x.property.signature, false));
468 assert(bus_type_is_basic(v->x.property.signature[0]));
992c052c 469
77a874a3 470 switch (v->x.property.signature[0]) {
992c052c
LP
471
472 case SD_BUS_TYPE_STRING:
28d6633a
LP
473 case SD_BUS_TYPE_SIGNATURE:
474 p = strempty(*(char**) userdata);
af8601fa
KS
475 break;
476
992c052c 477 case SD_BUS_TYPE_OBJECT_PATH:
992c052c 478 p = *(char**) userdata;
28d6633a 479 assert(p);
992c052c
LP
480 break;
481
482 default:
483 p = userdata;
484 break;
485 }
486
ebcf1f97 487 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
992c052c
LP
488}
489
490static int invoke_property_set(
491 sd_bus *bus,
19befb2d 492 sd_bus_slot *slot,
992c052c
LP
493 const sd_bus_vtable *v,
494 const char *path,
495 const char *interface,
496 const char *property,
497 sd_bus_message *value,
ebcf1f97
LP
498 void *userdata,
499 sd_bus_error *error) {
992c052c
LP
500
501 int r;
502
503 assert(bus);
19befb2d 504 assert(slot);
992c052c 505 assert(v);
dfa92725
LP
506 assert(path);
507 assert(interface);
508 assert(property);
509 assert(value);
992c052c 510
f00c3121 511 if (v->x.property.set) {
19befb2d 512
1b64f838 513 bus->current_slot = sd_bus_slot_ref(slot);
caa82984 514 bus->current_userdata = userdata;
f00c3121 515 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
caa82984 516 bus->current_userdata = NULL;
1b64f838 517 bus->current_slot = sd_bus_slot_unref(slot);
19befb2d 518
f00c3121
LP
519 if (r < 0)
520 return r;
521 if (sd_bus_error_is_set(error))
5958d089 522 return -sd_bus_error_get_errno(error);
f00c3121
LP
523 return r;
524 }
992c052c
LP
525
526 /* Automatic handling if no callback is defined. */
527
77a874a3
LP
528 assert(signature_is_single(v->x.property.signature, false));
529 assert(bus_type_is_basic(v->x.property.signature[0]));
992c052c 530
77a874a3 531 switch (v->x.property.signature[0]) {
992c052c
LP
532
533 case SD_BUS_TYPE_STRING:
534 case SD_BUS_TYPE_OBJECT_PATH:
535 case SD_BUS_TYPE_SIGNATURE: {
536 const char *p;
537 char *n;
538
77a874a3 539 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
992c052c
LP
540 if (r < 0)
541 return r;
542
543 n = strdup(p);
544 if (!n)
545 return -ENOMEM;
546
547 free(*(char**) userdata);
548 *(char**) userdata = n;
549
550 break;
551 }
552
553 default:
77a874a3 554 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
992c052c
LP
555 if (r < 0)
556 return r;
557
558 break;
559 }
560
561 return 1;
562}
563
564static int property_get_set_callbacks_run(
565 sd_bus *bus,
566 sd_bus_message *m,
567 struct vtable_member *c,
568 bool require_fallback,
569 bool is_get,
570 bool *found_object) {
571
992c052c 572 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
ebcf1f97 573 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
19befb2d 574 sd_bus_slot *slot;
c94d7fc3 575 void *u = NULL;
992c052c
LP
576 int r;
577
578 assert(bus);
579 assert(m);
dfa92725 580 assert(c);
992c052c
LP
581 assert(found_object);
582
583 if (require_fallback && !c->parent->is_fallback)
584 return 0;
585
f00c3121 586 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
992c052c 587 if (r <= 0)
f00c3121 588 return bus_maybe_reply_error(m, r, &error);
68313d3d
LP
589 if (bus->nodes_modified)
590 return 0;
992c052c 591
19befb2d
LP
592 slot = container_of(c->parent, sd_bus_slot, node_vtable);
593
992c052c
LP
594 *found_object = true;
595
df2d202e 596 r = sd_bus_message_new_method_return(m, &reply);
992c052c
LP
597 if (r < 0)
598 return r;
599
992c052c 600 if (is_get) {
68313d3d
LP
601 /* Note that we do not protect against reexecution
602 * here (using the last_iteration check, see below),
603 * should the node tree have changed and we got called
604 * again. We assume that property Get() calls are
605 * ultimately without side-effects or if they aren't
606 * then at least idempotent. */
607
77a874a3 608 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
992c052c
LP
609 if (r < 0)
610 return r;
611
adacb957
LP
612 /* Note that we do not do an access check here. Read
613 * access to properties is always unrestricted, since
614 * PropertiesChanged signals broadcast contents
615 * anyway. */
616
19befb2d 617 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
992c052c 618 if (r < 0)
f00c3121 619 return bus_maybe_reply_error(m, r, &error);
992c052c 620
68313d3d
LP
621 if (bus->nodes_modified)
622 return 0;
623
992c052c
LP
624 r = sd_bus_message_close_container(reply);
625 if (r < 0)
626 return r;
627
628 } else {
0ca454d4
LP
629 const char *signature = NULL;
630 char type = 0;
631
992c052c 632 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
ebcf1f97 633 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
68313d3d 634
ebcf1f97
LP
635 /* Avoid that we call the set routine more than once
636 * if the processing of this message got restarted
637 * because the node tree changed. */
638 if (c->last_iteration == bus->iteration_counter)
639 return 0;
992c052c 640
ebcf1f97 641 c->last_iteration = bus->iteration_counter;
992c052c 642
0ca454d4
LP
643 r = sd_bus_message_peek_type(m, &type, &signature);
644 if (r < 0)
645 return r;
646
647 if (type != 'v' || !streq(strempty(signature), strempty(c->vtable->x.property.signature)))
648 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Incorrect parameters for property '%s', expected '%s', got '%s'.", c->member, strempty(c->vtable->x.property.signature), strempty(signature));
649
ebcf1f97
LP
650 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
651 if (r < 0)
652 return r;
992c052c 653
adacb957
LP
654 r = check_access(bus, m, c, &error);
655 if (r < 0)
656 return bus_maybe_reply_error(m, r, &error);
657
19befb2d 658 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
ebcf1f97 659 if (r < 0)
f00c3121 660 return bus_maybe_reply_error(m, r, &error);
992c052c 661
68313d3d
LP
662 if (bus->nodes_modified)
663 return 0;
664
992c052c
LP
665 r = sd_bus_message_exit_container(m);
666 if (r < 0)
667 return r;
668 }
669
670 r = sd_bus_send(bus, reply, NULL);
671 if (r < 0)
672 return r;
673
674 return 1;
675}
676
a03e4337
LP
677static int vtable_append_one_property(
678 sd_bus *bus,
679 sd_bus_message *reply,
680 const char *path,
681 struct node_vtable *c,
682 const sd_bus_vtable *v,
683 void *userdata,
684 sd_bus_error *error) {
685
19befb2d 686 sd_bus_slot *slot;
a03e4337
LP
687 int r;
688
689 assert(bus);
690 assert(reply);
691 assert(path);
692 assert(c);
693 assert(v);
694
695 r = sd_bus_message_open_container(reply, 'e', "sv");
696 if (r < 0)
697 return r;
698
699 r = sd_bus_message_append(reply, "s", v->x.property.member);
700 if (r < 0)
701 return r;
702
703 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
704 if (r < 0)
705 return r;
706
19befb2d
LP
707 slot = container_of(c, sd_bus_slot, node_vtable);
708
709 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
a03e4337
LP
710 if (r < 0)
711 return r;
712 if (bus->nodes_modified)
713 return 0;
714
715 r = sd_bus_message_close_container(reply);
716 if (r < 0)
717 return r;
718
719 r = sd_bus_message_close_container(reply);
720 if (r < 0)
721 return r;
722
723 return 0;
724}
725
992c052c
LP
726static int vtable_append_all_properties(
727 sd_bus *bus,
728 sd_bus_message *reply,
729 const char *path,
730 struct node_vtable *c,
731 void *userdata,
732 sd_bus_error *error) {
733
734 const sd_bus_vtable *v;
735 int r;
736
737 assert(bus);
738 assert(reply);
dfa92725 739 assert(path);
992c052c
LP
740 assert(c);
741
6e8df5f0
LP
742 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
743 return 1;
744
992c052c
LP
745 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
746 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
747 continue;
748
6e8df5f0
LP
749 if (v->flags & SD_BUS_VTABLE_HIDDEN)
750 continue;
751
a03e4337 752 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
f00c3121
LP
753 if (r < 0)
754 return r;
68313d3d
LP
755 if (bus->nodes_modified)
756 return 0;
992c052c
LP
757 }
758
759 return 1;
760}
761
762static int property_get_all_callbacks_run(
763 sd_bus *bus,
764 sd_bus_message *m,
765 struct node_vtable *first,
766 bool require_fallback,
767 const char *iface,
768 bool *found_object) {
769
770 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
771 struct node_vtable *c;
bd037038 772 bool found_interface;
992c052c
LP
773 int r;
774
775 assert(bus);
776 assert(m);
777 assert(found_object);
778
df2d202e 779 r = sd_bus_message_new_method_return(m, &reply);
992c052c
LP
780 if (r < 0)
781 return r;
782
783 r = sd_bus_message_open_container(reply, 'a', "{sv}");
784 if (r < 0)
785 return r;
786
38911893 787 found_interface = !iface ||
bd037038
LP
788 streq(iface, "org.freedesktop.DBus.Properties") ||
789 streq(iface, "org.freedesktop.DBus.Peer") ||
790 streq(iface, "org.freedesktop.DBus.Introspectable");
791
992c052c
LP
792 LIST_FOREACH(vtables, c, first) {
793 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
794 void *u;
795
796 if (require_fallback && !c->is_fallback)
797 continue;
798
f00c3121 799 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
992c052c 800 if (r < 0)
f00c3121 801 return bus_maybe_reply_error(m, r, &error);
68313d3d
LP
802 if (bus->nodes_modified)
803 return 0;
992c052c
LP
804 if (r == 0)
805 continue;
806
807 *found_object = true;
808
809 if (iface && !streq(c->interface, iface))
810 continue;
811 found_interface = true;
812
992c052c
LP
813 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
814 if (r < 0)
f00c3121 815 return bus_maybe_reply_error(m, r, &error);
68313d3d
LP
816 if (bus->nodes_modified)
817 return 0;
992c052c
LP
818 }
819
820 if (!found_interface) {
821 r = sd_bus_reply_method_errorf(
df2d202e 822 m,
40ca29a1 823 SD_BUS_ERROR_UNKNOWN_INTERFACE,
992c052c
LP
824 "Unknown interface '%s'.", iface);
825 if (r < 0)
826 return r;
827
828 return 1;
829 }
830
831 r = sd_bus_message_close_container(reply);
832 if (r < 0)
833 return r;
834
835 r = sd_bus_send(bus, reply, NULL);
836 if (r < 0)
837 return r;
838
839 return 1;
840}
841
ff02f101 842static int bus_node_exists(
dfa92725
LP
843 sd_bus *bus,
844 struct node *n,
845 const char *path,
846 bool require_fallback) {
847
992c052c
LP
848 struct node_vtable *c;
849 struct node_callback *k;
ff02f101 850 int r;
992c052c
LP
851
852 assert(bus);
853 assert(n);
dfa92725 854 assert(path);
992c052c
LP
855
856 /* Tests if there's anything attached directly to this node
857 * for the specified path */
858
ff02f101
DH
859 if (!require_fallback && (n->enumerators || n->object_managers))
860 return true;
861
992c052c
LP
862 LIST_FOREACH(callbacks, k, n->callbacks) {
863 if (require_fallback && !k->is_fallback)
864 continue;
865
ff02f101 866 return 1;
992c052c
LP
867 }
868
869 LIST_FOREACH(vtables, c, n->vtables) {
f00c3121 870 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
992c052c
LP
871
872 if (require_fallback && !c->is_fallback)
873 continue;
874
ff02f101
DH
875 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
876 if (r != 0)
877 return r;
68313d3d 878 if (bus->nodes_modified)
ff02f101 879 return 0;
992c052c
LP
880 }
881
ff02f101 882 return 0;
992c052c
LP
883}
884
885static int process_introspect(
886 sd_bus *bus,
887 sd_bus_message *m,
888 struct node *n,
889 bool require_fallback,
890 bool *found_object) {
891
f00c3121 892 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
992c052c
LP
893 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
894 _cleanup_set_free_free_ Set *s = NULL;
718db961 895 const char *previous_interface = NULL;
992c052c
LP
896 struct introspect intro;
897 struct node_vtable *c;
898 bool empty;
899 int r;
900
901 assert(bus);
902 assert(m);
903 assert(n);
904 assert(found_object);
905
2d5c8a27 906 r = get_child_nodes(bus, m->path, n, false, &s, &error);
992c052c 907 if (r < 0)
f00c3121 908 return bus_maybe_reply_error(m, r, &error);
68313d3d
LP
909 if (bus->nodes_modified)
910 return 0;
992c052c 911
7fb411f0 912 r = introspect_begin(&intro, bus->trusted);
992c052c
LP
913 if (r < 0)
914 return r;
915
943c3f94 916 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
992c052c
LP
917 if (r < 0)
918 return r;
919
920 empty = set_isempty(s);
921
922 LIST_FOREACH(vtables, c, n->vtables) {
923 if (require_fallback && !c->is_fallback)
924 continue;
925
f00c3121
LP
926 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
927 if (r < 0) {
928 r = bus_maybe_reply_error(m, r, &error);
929 goto finish;
930 }
931 if (bus->nodes_modified) {
932 r = 0;
933 goto finish;
934 }
992c052c
LP
935 if (r == 0)
936 continue;
937
938 empty = false;
939
6e8df5f0
LP
940 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
941 continue;
942
718db961
LP
943 if (!streq_ptr(previous_interface, c->interface)) {
944
945 if (previous_interface)
946 fputs(" </interface>\n", intro.f);
947
948 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
949 }
950
951 r = introspect_write_interface(&intro, c->vtable);
992c052c
LP
952 if (r < 0)
953 goto finish;
718db961
LP
954
955 previous_interface = c->interface;
992c052c
LP
956 }
957
718db961
LP
958 if (previous_interface)
959 fputs(" </interface>\n", intro.f);
960
992c052c
LP
961 if (empty) {
962 /* Nothing?, let's see if we exist at all, and if not
963 * refuse to do anything */
964 r = bus_node_exists(bus, n, m->path, require_fallback);
ff02f101
DH
965 if (r <= 0)
966 goto finish;
967 if (bus->nodes_modified) {
968 r = 0;
992c052c 969 goto finish;
ff02f101 970 }
992c052c
LP
971 }
972
973 *found_object = true;
974
975 r = introspect_write_child_nodes(&intro, s, m->path);
976 if (r < 0)
977 goto finish;
978
979 r = introspect_finish(&intro, bus, m, &reply);
980 if (r < 0)
981 goto finish;
982
983 r = sd_bus_send(bus, reply, NULL);
984 if (r < 0)
985 goto finish;
986
987 r = 1;
988
989finish:
990 introspect_free(&intro);
991 return r;
992}
993
992c052c
LP
994static int object_manager_serialize_path(
995 sd_bus *bus,
996 sd_bus_message *reply,
997 const char *prefix,
998 const char *path,
999 bool require_fallback,
1000 sd_bus_error *error) {
1001
718db961
LP
1002 const char *previous_interface = NULL;
1003 bool found_something = false;
992c052c
LP
1004 struct node_vtable *i;
1005 struct node *n;
1006 int r;
1007
1008 assert(bus);
1009 assert(reply);
1010 assert(prefix);
1011 assert(path);
1012 assert(error);
1013
1014 n = hashmap_get(bus->nodes, prefix);
1015 if (!n)
1016 return 0;
1017
992c052c 1018 LIST_FOREACH(vtables, i, n->vtables) {
92db139e 1019 void *u;
992c052c
LP
1020
1021 if (require_fallback && !i->is_fallback)
1022 continue;
1023
f00c3121 1024 r = node_vtable_get_userdata(bus, path, i, &u, error);
92db139e
LP
1025 if (r < 0)
1026 return r;
68313d3d
LP
1027 if (bus->nodes_modified)
1028 return 0;
92db139e
LP
1029 if (r == 0)
1030 continue;
1031
1032 if (!found_something) {
718db961
LP
1033
1034 /* Open the object part */
1035
92db139e
LP
1036 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1037 if (r < 0)
1038 return r;
1039
1040 r = sd_bus_message_append(reply, "o", path);
1041 if (r < 0)
1042 return r;
1043
1044 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1045 if (r < 0)
1046 return r;
1047
1048 found_something = true;
1049 }
1050
718db961
LP
1051 if (!streq_ptr(previous_interface, i->interface)) {
1052
1053 /* Maybe close the previous interface part */
1054
1055 if (previous_interface) {
1056 r = sd_bus_message_close_container(reply);
1057 if (r < 0)
1058 return r;
1059
1060 r = sd_bus_message_close_container(reply);
1061 if (r < 0)
1062 return r;
1063 }
1064
1065 /* Open the new interface part */
1066
1067 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1068 if (r < 0)
1069 return r;
1070
1071 r = sd_bus_message_append(reply, "s", i->interface);
1072 if (r < 0)
1073 return r;
1074
1075 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1076 if (r < 0)
1077 return r;
1078 }
1079
1080 r = vtable_append_all_properties(bus, reply, path, i, u, error);
992c052c
LP
1081 if (r < 0)
1082 return r;
68313d3d
LP
1083 if (bus->nodes_modified)
1084 return 0;
718db961
LP
1085
1086 previous_interface = i->interface;
1087 }
1088
1089 if (previous_interface) {
1090 r = sd_bus_message_close_container(reply);
1091 if (r < 0)
1092 return r;
1093
1094 r = sd_bus_message_close_container(reply);
1095 if (r < 0)
1096 return r;
992c052c
LP
1097 }
1098
92db139e
LP
1099 if (found_something) {
1100 r = sd_bus_message_close_container(reply);
1101 if (r < 0)
1102 return r;
992c052c 1103
92db139e
LP
1104 r = sd_bus_message_close_container(reply);
1105 if (r < 0)
1106 return r;
1107 }
992c052c
LP
1108
1109 return 1;
1110}
1111
1112static int object_manager_serialize_path_and_fallbacks(
1113 sd_bus *bus,
1114 sd_bus_message *reply,
1115 const char *path,
1116 sd_bus_error *error) {
1117
92e189e5 1118 char *prefix;
992c052c
LP
1119 int r;
1120
1121 assert(bus);
1122 assert(reply);
1123 assert(path);
1124 assert(error);
1125
1126 /* First, add all vtables registered for this path */
1127 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1128 if (r < 0)
1129 return r;
68313d3d
LP
1130 if (bus->nodes_modified)
1131 return 0;
992c052c
LP
1132
1133 /* Second, add fallback vtables registered for any of the prefixes */
92e189e5
LP
1134 prefix = alloca(strlen(path) + 1);
1135 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1136 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1137 if (r < 0)
1138 return r;
68313d3d
LP
1139 if (bus->nodes_modified)
1140 return 0;
992c052c
LP
1141 }
1142
1143 return 0;
1144}
1145
1146static int process_get_managed_objects(
1147 sd_bus *bus,
1148 sd_bus_message *m,
1149 struct node *n,
1150 bool require_fallback,
1151 bool *found_object) {
1152
f00c3121 1153 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
992c052c
LP
1154 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1155 _cleanup_set_free_free_ Set *s = NULL;
943c3f94
DH
1156 Iterator i;
1157 char *path;
992c052c
LP
1158 int r;
1159
1160 assert(bus);
1161 assert(m);
1162 assert(n);
1163 assert(found_object);
1164
943c3f94
DH
1165 /* Spec says, GetManagedObjects() is only implemented on the root of a
1166 * sub-tree. Therefore, we require a registered object-manager on
1167 * exactly the queried path, otherwise, we refuse to respond. */
1168
1169 if (require_fallback || !n->object_managers)
992c052c
LP
1170 return 0;
1171
2d5c8a27 1172 r = get_child_nodes(bus, m->path, n, true, &s, &error);
992c052c
LP
1173 if (r < 0)
1174 return r;
68313d3d
LP
1175 if (bus->nodes_modified)
1176 return 0;
992c052c 1177
df2d202e 1178 r = sd_bus_message_new_method_return(m, &reply);
992c052c
LP
1179 if (r < 0)
1180 return r;
1181
1182 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1183 if (r < 0)
1184 return r;
1185
943c3f94
DH
1186 SET_FOREACH(path, s, i) {
1187 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1188 if (r < 0)
1189 return r;
992c052c 1190
943c3f94 1191 if (bus->nodes_modified)
992c052c 1192 return 0;
992c052c
LP
1193 }
1194
1195 r = sd_bus_message_close_container(reply);
1196 if (r < 0)
1197 return r;
1198
1199 r = sd_bus_send(bus, reply, NULL);
1200 if (r < 0)
1201 return r;
1202
1203 return 1;
1204}
1205
1206static int object_find_and_run(
1207 sd_bus *bus,
1208 sd_bus_message *m,
1209 const char *p,
1210 bool require_fallback,
1211 bool *found_object) {
1212
1213 struct node *n;
1214 struct vtable_member vtable_key, *v;
1215 int r;
1216
1217 assert(bus);
1218 assert(m);
1219 assert(p);
1220 assert(found_object);
1221
1222 n = hashmap_get(bus->nodes, p);
1223 if (!n)
1224 return 0;
1225
1226 /* First, try object callbacks */
1227 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1228 if (r != 0)
1229 return r;
68313d3d
LP
1230 if (bus->nodes_modified)
1231 return 0;
992c052c
LP
1232
1233 if (!m->interface || !m->member)
1234 return 0;
1235
1236 /* Then, look for a known method */
1237 vtable_key.path = (char*) p;
1238 vtable_key.interface = m->interface;
1239 vtable_key.member = m->member;
1240
1241 v = hashmap_get(bus->vtable_methods, &vtable_key);
1242 if (v) {
1243 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1244 if (r != 0)
1245 return r;
68313d3d
LP
1246 if (bus->nodes_modified)
1247 return 0;
992c052c
LP
1248 }
1249
1250 /* Then, look for a known property */
1251 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1252 bool get = false;
1253
1254 get = streq(m->member, "Get");
1255
1256 if (get || streq(m->member, "Set")) {
1257
1258 r = sd_bus_message_rewind(m, true);
1259 if (r < 0)
1260 return r;
1261
1262 vtable_key.path = (char*) p;
1263
1264 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1265 if (r < 0)
0fc5ab90 1266 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
992c052c
LP
1267
1268 v = hashmap_get(bus->vtable_properties, &vtable_key);
1269 if (v) {
1270 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1271 if (r != 0)
1272 return r;
1273 }
1274
1275 } else if (streq(m->member, "GetAll")) {
1276 const char *iface;
1277
1278 r = sd_bus_message_rewind(m, true);
1279 if (r < 0)
1280 return r;
1281
1282 r = sd_bus_message_read(m, "s", &iface);
1283 if (r < 0)
0fc5ab90 1284 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
992c052c
LP
1285
1286 if (iface[0] == 0)
1287 iface = NULL;
1288
1289 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1290 if (r != 0)
1291 return r;
1292 }
1293
1294 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1295
0fc5ab90
LP
1296 if (!isempty(sd_bus_message_get_signature(m, true)))
1297 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1298
992c052c
LP
1299 r = process_introspect(bus, m, n, require_fallback, found_object);
1300 if (r != 0)
1301 return r;
1302
1303 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1304
0fc5ab90
LP
1305 if (!isempty(sd_bus_message_get_signature(m, true)))
1306 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1307
992c052c
LP
1308 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1309 if (r != 0)
1310 return r;
1311 }
1312
68313d3d
LP
1313 if (bus->nodes_modified)
1314 return 0;
1315
992c052c
LP
1316 if (!*found_object) {
1317 r = bus_node_exists(bus, n, m->path, require_fallback);
1318 if (r < 0)
1319 return r;
ff02f101
DH
1320 if (bus->nodes_modified)
1321 return 0;
992c052c
LP
1322 if (r > 0)
1323 *found_object = true;
1324 }
1325
1326 return 0;
1327}
1328
1329int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1330 int r;
1331 size_t pl;
1332 bool found_object = false;
1333
1334 assert(bus);
1335 assert(m);
1336
09365592
LP
1337 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1338 return 0;
1339
40ca29a1 1340 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
992c052c
LP
1341 return 0;
1342
992c052c
LP
1343 if (hashmap_isempty(bus->nodes))
1344 return 0;
1345
f820cf99
LP
1346 /* Never respond to broadcast messages */
1347 if (bus->bus_client && !m->destination)
1348 return 0;
1349
adacb957
LP
1350 assert(m->path);
1351 assert(m->member);
1352
992c052c
LP
1353 pl = strlen(m->path);
1354 do {
92e189e5 1355 char prefix[pl+1];
992c052c
LP
1356
1357 bus->nodes_modified = false;
1358
1359 r = object_find_and_run(bus, m, m->path, false, &found_object);
1360 if (r != 0)
1361 return r;
1362
1363 /* Look for fallback prefixes */
92e189e5 1364 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
992c052c
LP
1365
1366 if (bus->nodes_modified)
1367 break;
1368
92e189e5 1369 r = object_find_and_run(bus, m, prefix, true, &found_object);
992c052c
LP
1370 if (r != 0)
1371 return r;
1372 }
1373
1374 } while (bus->nodes_modified);
1375
1376 if (!found_object)
1377 return 0;
1378
1379 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1380 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1381 r = sd_bus_reply_method_errorf(
df2d202e 1382 m,
40ca29a1 1383 SD_BUS_ERROR_UNKNOWN_PROPERTY,
992c052c
LP
1384 "Unknown property or interface.");
1385 else
1386 r = sd_bus_reply_method_errorf(
df2d202e 1387 m,
40ca29a1 1388 SD_BUS_ERROR_UNKNOWN_METHOD,
992c052c
LP
1389 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1390
1391 if (r < 0)
1392 return r;
1393
1394 return 1;
1395}
1396
1397static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1398 struct node *n, *parent;
1399 const char *e;
2fd069b1
ZJS
1400 _cleanup_free_ char *s = NULL;
1401 char *p;
992c052c
LP
1402 int r;
1403
1404 assert(bus);
1405 assert(path);
1406 assert(path[0] == '/');
1407
1408 n = hashmap_get(bus->nodes, path);
1409 if (n)
1410 return n;
1411
d5099efc 1412 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
992c052c
LP
1413 if (r < 0)
1414 return NULL;
1415
1416 s = strdup(path);
1417 if (!s)
1418 return NULL;
1419
1420 if (streq(path, "/"))
1421 parent = NULL;
1422 else {
1423 e = strrchr(path, '/');
1424 assert(e);
1425
7d67077f 1426 p = strndupa(path, MAX(1, e - path));
992c052c
LP
1427
1428 parent = bus_node_allocate(bus, p);
2fd069b1 1429 if (!parent)
992c052c 1430 return NULL;
992c052c
LP
1431 }
1432
1433 n = new0(struct node, 1);
1434 if (!n)
1435 return NULL;
1436
1437 n->parent = parent;
1438 n->path = s;
2fd069b1 1439 s = NULL; /* do not free */
992c052c 1440
8e050193 1441 r = hashmap_put(bus->nodes, n->path, n);
992c052c 1442 if (r < 0) {
2fd069b1 1443 free(n->path);
992c052c
LP
1444 free(n);
1445 return NULL;
1446 }
1447
1448 if (parent)
71fda00f 1449 LIST_PREPEND(siblings, parent->child, n);
992c052c
LP
1450
1451 return n;
1452}
1453
19befb2d 1454void bus_node_gc(sd_bus *b, struct node *n) {
992c052c
LP
1455 assert(b);
1456
1457 if (!n)
1458 return;
1459
1460 if (n->child ||
1461 n->callbacks ||
1462 n->vtables ||
1463 n->enumerators ||
19befb2d 1464 n->object_managers)
992c052c
LP
1465 return;
1466
1467 assert(hashmap_remove(b->nodes, n->path) == n);
1468
1469 if (n->parent)
71fda00f 1470 LIST_REMOVE(siblings, n->parent->child, n);
992c052c
LP
1471
1472 free(n->path);
1473 bus_node_gc(b, n->parent);
1474 free(n);
1475}
1476
2d5c8a27
DH
1477static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1478 struct node *n;
1479
1480 assert(bus);
1481 assert(path);
1482
1483 n = hashmap_get(bus->nodes, path);
1484 if (!n) {
1485 char *prefix;
1486
1487 prefix = alloca(strlen(path) + 1);
1488 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1489 n = hashmap_get(bus->nodes, prefix);
1490 if (n)
1491 break;
1492 }
1493 }
1494
1495 while (n && !n->object_managers)
1496 n = n->parent;
1497
1498 if (out)
1499 *out = n;
1500 return !!n;
1501}
1502
992c052c 1503static int bus_add_object(
dfa92725 1504 sd_bus *bus,
19befb2d 1505 sd_bus_slot **slot,
992c052c
LP
1506 bool fallback,
1507 const char *path,
1508 sd_bus_message_handler_t callback,
1509 void *userdata) {
1510
19befb2d 1511 sd_bus_slot *s;
992c052c
LP
1512 struct node *n;
1513 int r;
1514
dfa92725
LP
1515 assert_return(bus, -EINVAL);
1516 assert_return(object_path_is_valid(path), -EINVAL);
1517 assert_return(callback, -EINVAL);
1518 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c 1519
dfa92725 1520 n = bus_node_allocate(bus, path);
992c052c
LP
1521 if (!n)
1522 return -ENOMEM;
1523
19befb2d
LP
1524 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1525 if (!s) {
992c052c
LP
1526 r = -ENOMEM;
1527 goto fail;
1528 }
1529
19befb2d
LP
1530 s->node_callback.callback = callback;
1531 s->node_callback.is_fallback = fallback;
992c052c 1532
19befb2d
LP
1533 s->node_callback.node = n;
1534 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
68313d3d
LP
1535 bus->nodes_modified = true;
1536
19befb2d
LP
1537 if (slot)
1538 *slot = s;
1539
992c052c
LP
1540 return 0;
1541
1542fail:
19befb2d 1543 sd_bus_slot_unref(s);
dfa92725 1544 bus_node_gc(bus, n);
19befb2d 1545
992c052c
LP
1546 return r;
1547}
1548
19befb2d 1549_public_ int sd_bus_add_object(
992c052c 1550 sd_bus *bus,
19befb2d 1551 sd_bus_slot **slot,
992c052c
LP
1552 const char *path,
1553 sd_bus_message_handler_t callback,
1554 void *userdata) {
1555
19befb2d 1556 return bus_add_object(bus, slot, false, path, callback, userdata);
992c052c
LP
1557}
1558
19befb2d
LP
1559_public_ int sd_bus_add_fallback(
1560 sd_bus *bus,
1561 sd_bus_slot **slot,
1562 const char *prefix,
1563 sd_bus_message_handler_t callback,
1564 void *userdata) {
992c052c 1565
19befb2d 1566 return bus_add_object(bus, slot, true, prefix, callback, userdata);
992c052c
LP
1567}
1568
9bf3b535 1569static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
992c052c 1570 const struct vtable_member *m = a;
9bf3b535
LP
1571 uint8_t hash_key2[HASH_KEY_SIZE];
1572 unsigned long ret;
992c052c 1573
dfa92725
LP
1574 assert(m);
1575
9bf3b535
LP
1576 ret = string_hash_func(m->path, hash_key);
1577
1578 /* Use a slightly different hash key for the interface */
1579 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1580 hash_key2[0]++;
1581 ret ^= string_hash_func(m->interface, hash_key2);
1582
1583 /* And an even different one for the member */
1584 hash_key2[0]++;
1585 ret ^= string_hash_func(m->member, hash_key2);
1586
1587 return ret;
992c052c
LP
1588}
1589
1590static int vtable_member_compare_func(const void *a, const void *b) {
1591 const struct vtable_member *x = a, *y = b;
1592 int r;
1593
dfa92725
LP
1594 assert(x);
1595 assert(y);
1596
992c052c
LP
1597 r = strcmp(x->path, y->path);
1598 if (r != 0)
1599 return r;
1600
1601 r = strcmp(x->interface, y->interface);
1602 if (r != 0)
1603 return r;
1604
1605 return strcmp(x->member, y->member);
1606}
1607
d5099efc
MS
1608static const struct hash_ops vtable_member_hash_ops = {
1609 .hash = vtable_member_hash_func,
1610 .compare = vtable_member_compare_func
1611};
1612
992c052c
LP
1613static int add_object_vtable_internal(
1614 sd_bus *bus,
19befb2d 1615 sd_bus_slot **slot,
992c052c
LP
1616 const char *path,
1617 const char *interface,
1618 const sd_bus_vtable *vtable,
1619 bool fallback,
1620 sd_bus_object_find_t find,
1621 void *userdata) {
1622
2915234d 1623 sd_bus_slot *s = NULL;
19befb2d 1624 struct node_vtable *i, *existing = NULL;
992c052c
LP
1625 const sd_bus_vtable *v;
1626 struct node *n;
1627 int r;
1628
dfa92725
LP
1629 assert_return(bus, -EINVAL);
1630 assert_return(object_path_is_valid(path), -EINVAL);
1631 assert_return(interface_name_is_valid(interface), -EINVAL);
1632 assert_return(vtable, -EINVAL);
1633 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1634 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1635 assert_return(!bus_pid_changed(bus), -ECHILD);
718db961
LP
1636 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1637 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1638 !streq(interface, "org.freedesktop.DBus.Peer") &&
1639 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
992c052c 1640
d5099efc 1641 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
992c052c
LP
1642 if (r < 0)
1643 return r;
1644
d5099efc 1645 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
992c052c
LP
1646 if (r < 0)
1647 return r;
1648
1649 n = bus_node_allocate(bus, path);
1650 if (!n)
1651 return -ENOMEM;
1652
1653 LIST_FOREACH(vtables, i, n->vtables) {
992c052c
LP
1654 if (i->is_fallback != fallback) {
1655 r = -EPROTOTYPE;
1656 goto fail;
1657 }
718db961
LP
1658
1659 if (streq(i->interface, interface)) {
1660
1661 if (i->vtable == vtable) {
1662 r = -EEXIST;
1663 goto fail;
1664 }
1665
1666 existing = i;
1667 }
992c052c
LP
1668 }
1669
19befb2d
LP
1670 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1671 if (!s) {
992c052c
LP
1672 r = -ENOMEM;
1673 goto fail;
1674 }
1675
19befb2d
LP
1676 s->node_vtable.is_fallback = fallback;
1677 s->node_vtable.vtable = vtable;
1678 s->node_vtable.find = find;
992c052c 1679
19befb2d
LP
1680 s->node_vtable.interface = strdup(interface);
1681 if (!s->node_vtable.interface) {
992c052c
LP
1682 r = -ENOMEM;
1683 goto fail;
1684 }
1685
19befb2d 1686 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
992c052c
LP
1687
1688 switch (v->type) {
1689
1690 case _SD_BUS_VTABLE_METHOD: {
1691 struct vtable_member *m;
1692
77a874a3
LP
1693 if (!member_name_is_valid(v->x.method.member) ||
1694 !signature_is_valid(strempty(v->x.method.signature), false) ||
1695 !signature_is_valid(strempty(v->x.method.result), false) ||
1696 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
df98a87b 1697 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
992c052c
LP
1698 r = -EINVAL;
1699 goto fail;
1700 }
1701
1702 m = new0(struct vtable_member, 1);
1703 if (!m) {
1704 r = -ENOMEM;
1705 goto fail;
1706 }
1707
19befb2d 1708 m->parent = &s->node_vtable;
992c052c 1709 m->path = n->path;
19befb2d 1710 m->interface = s->node_vtable.interface;
77a874a3 1711 m->member = v->x.method.member;
992c052c
LP
1712 m->vtable = v;
1713
1714 r = hashmap_put(bus->vtable_methods, m, m);
1715 if (r < 0) {
1716 free(m);
1717 goto fail;
1718 }
1719
1720 break;
1721 }
1722
1723 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1724
77a874a3 1725 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
992c052c
LP
1726 r = -EINVAL;
1727 goto fail;
1728 }
9b772efb
LP
1729
1730 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1731 r = -EINVAL;
1732 goto fail;
1733 }
992c052c
LP
1734
1735 /* Fall through */
1736
1737 case _SD_BUS_VTABLE_PROPERTY: {
1738 struct vtable_member *m;
1739
77a874a3
LP
1740 if (!member_name_is_valid(v->x.property.member) ||
1741 !signature_is_single(v->x.property.signature, false) ||
c13c7de3 1742 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
992c052c 1743 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
df98a87b 1744 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
adacb957 1745 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
992c052c
LP
1746 r = -EINVAL;
1747 goto fail;
1748 }
1749
992c052c
LP
1750 m = new0(struct vtable_member, 1);
1751 if (!m) {
1752 r = -ENOMEM;
1753 goto fail;
1754 }
1755
19befb2d 1756 m->parent = &s->node_vtable;
992c052c 1757 m->path = n->path;
19befb2d 1758 m->interface = s->node_vtable.interface;
77a874a3 1759 m->member = v->x.property.member;
992c052c
LP
1760 m->vtable = v;
1761
1762 r = hashmap_put(bus->vtable_properties, m, m);
1763 if (r < 0) {
1764 free(m);
1765 goto fail;
1766 }
1767
1768 break;
1769 }
1770
1771 case _SD_BUS_VTABLE_SIGNAL:
1772
77a874a3 1773 if (!member_name_is_valid(v->x.signal.member) ||
adacb957
LP
1774 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1775 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
992c052c
LP
1776 r = -EINVAL;
1777 goto fail;
1778 }
1779
1780 break;
1781
1782 default:
1783 r = -EINVAL;
1784 goto fail;
1785 }
1786 }
1787
19befb2d
LP
1788 s->node_vtable.node = n;
1789 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
68313d3d
LP
1790 bus->nodes_modified = true;
1791
19befb2d
LP
1792 if (slot)
1793 *slot = s;
1794
992c052c
LP
1795 return 0;
1796
1797fail:
19befb2d 1798 sd_bus_slot_unref(s);
dfa92725
LP
1799 bus_node_gc(bus, n);
1800
19befb2d 1801 return r;
992c052c
LP
1802}
1803
d9f644e2 1804_public_ int sd_bus_add_object_vtable(
992c052c 1805 sd_bus *bus,
19befb2d 1806 sd_bus_slot **slot,
992c052c
LP
1807 const char *path,
1808 const char *interface,
1809 const sd_bus_vtable *vtable,
1810 void *userdata) {
1811
19befb2d 1812 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
992c052c
LP
1813}
1814
d9f644e2 1815_public_ int sd_bus_add_fallback_vtable(
992c052c 1816 sd_bus *bus,
19befb2d
LP
1817 sd_bus_slot **slot,
1818 const char *prefix,
718db961
LP
1819 const char *interface,
1820 const sd_bus_vtable *vtable,
1821 sd_bus_object_find_t find,
1822 void *userdata) {
992c052c 1823
19befb2d 1824 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
992c052c
LP
1825}
1826
d9f644e2 1827_public_ int sd_bus_add_node_enumerator(
992c052c 1828 sd_bus *bus,
19befb2d 1829 sd_bus_slot **slot,
992c052c
LP
1830 const char *path,
1831 sd_bus_node_enumerator_t callback,
1832 void *userdata) {
1833
19befb2d 1834 sd_bus_slot *s;
992c052c
LP
1835 struct node *n;
1836 int r;
1837
dfa92725
LP
1838 assert_return(bus, -EINVAL);
1839 assert_return(object_path_is_valid(path), -EINVAL);
1840 assert_return(callback, -EINVAL);
1841 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c
LP
1842
1843 n = bus_node_allocate(bus, path);
1844 if (!n)
1845 return -ENOMEM;
1846
19befb2d
LP
1847 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1848 if (!s) {
992c052c
LP
1849 r = -ENOMEM;
1850 goto fail;
1851 }
1852
19befb2d 1853 s->node_enumerator.callback = callback;
68313d3d 1854
19befb2d
LP
1855 s->node_enumerator.node = n;
1856 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
68313d3d
LP
1857 bus->nodes_modified = true;
1858
19befb2d
LP
1859 if (slot)
1860 *slot = s;
1861
992c052c
LP
1862 return 0;
1863
1864fail:
19befb2d 1865 sd_bus_slot_unref(s);
992c052c
LP
1866 bus_node_gc(bus, n);
1867
19befb2d 1868 return r;
992c052c
LP
1869}
1870
1871static int emit_properties_changed_on_interface(
1872 sd_bus *bus,
1873 const char *prefix,
1874 const char *path,
1875 const char *interface,
1876 bool require_fallback,
46525bfc 1877 bool *found_interface,
992c052c
LP
1878 char **names) {
1879
f00c3121 1880 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
992c052c 1881 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
718db961
LP
1882 bool has_invalidating = false, has_changing = false;
1883 struct vtable_member key = {};
992c052c
LP
1884 struct node_vtable *c;
1885 struct node *n;
1886 char **property;
1887 void *u = NULL;
1888 int r;
1889
1890 assert(bus);
dfa92725 1891 assert(prefix);
992c052c
LP
1892 assert(path);
1893 assert(interface);
46525bfc 1894 assert(found_interface);
992c052c
LP
1895
1896 n = hashmap_get(bus->nodes, prefix);
1897 if (!n)
1898 return 0;
1899
151b9b96 1900 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
992c052c
LP
1901 if (r < 0)
1902 return r;
1903
1904 r = sd_bus_message_append(m, "s", interface);
1905 if (r < 0)
1906 return r;
1907
1908 r = sd_bus_message_open_container(m, 'a', "{sv}");
1909 if (r < 0)
1910 return r;
1911
1912 key.path = prefix;
1913 key.interface = interface;
1914
718db961
LP
1915 LIST_FOREACH(vtables, c, n->vtables) {
1916 if (require_fallback && !c->is_fallback)
1917 continue;
992c052c 1918
718db961 1919 if (!streq(c->interface, interface))
992c052c 1920 continue;
992c052c 1921
f00c3121 1922 r = node_vtable_get_userdata(bus, path, c, &u, &error);
992c052c
LP
1923 if (r < 0)
1924 return r;
718db961
LP
1925 if (bus->nodes_modified)
1926 return 0;
1927 if (r == 0)
1928 continue;
992c052c 1929
46525bfc
LP
1930 *found_interface = true;
1931
a03e4337
LP
1932 if (names) {
1933 /* If the caller specified a list of
1934 * properties we include exactly those in the
1935 * PropertiesChanged message */
992c052c 1936
a03e4337
LP
1937 STRV_FOREACH(property, names) {
1938 struct vtable_member *v;
992c052c 1939
a03e4337 1940 assert_return(member_name_is_valid(*property), -EINVAL);
718db961 1941
a03e4337
LP
1942 key.member = *property;
1943 v = hashmap_get(bus->vtable_properties, &key);
1944 if (!v)
1945 return -ENOENT;
1946
1947 /* If there are two vtables for the same
1948 * interface, let's handle this property when
1949 * we come to that vtable. */
1950 if (c != v->parent)
1951 continue;
992c052c 1952
a03e4337
LP
1953 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1954 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
992c052c 1955
a03e4337
LP
1956 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1957
1958 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1959 has_invalidating = true;
1960 continue;
1961 }
1962
1963 has_changing = true;
1964
1965 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1966 if (r < 0)
1967 return r;
1968 if (bus->nodes_modified)
1969 return 0;
718db961 1970 }
a03e4337
LP
1971 } else {
1972 const sd_bus_vtable *v;
718db961 1973
a03e4337
LP
1974 /* If the caller specified no properties list
1975 * we include all properties that are marked
1976 * as changing in the message. */
718db961 1977
a03e4337
LP
1978 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1979 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1980 continue;
718db961 1981
a03e4337
LP
1982 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1983 continue;
718db961 1984
a03e4337
LP
1985 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1986 has_invalidating = true;
1987 continue;
1988 }
718db961 1989
a03e4337
LP
1990 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1991 continue;
718db961 1992
a03e4337 1993 has_changing = true;
718db961 1994
a03e4337
LP
1995 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1996 if (r < 0)
1997 return r;
1998 if (bus->nodes_modified)
1999 return 0;
2000 }
718db961 2001 }
992c052c
LP
2002 }
2003
718db961
LP
2004 if (!has_invalidating && !has_changing)
2005 return 0;
2006
992c052c
LP
2007 r = sd_bus_message_close_container(m);
2008 if (r < 0)
2009 return r;
2010
2011 r = sd_bus_message_open_container(m, 'a', "s");
2012 if (r < 0)
2013 return r;
2014
2015 if (has_invalidating) {
718db961
LP
2016 LIST_FOREACH(vtables, c, n->vtables) {
2017 if (require_fallback && !c->is_fallback)
2018 continue;
992c052c 2019
718db961 2020 if (!streq(c->interface, interface))
992c052c
LP
2021 continue;
2022
f00c3121 2023 r = node_vtable_get_userdata(bus, path, c, &u, &error);
992c052c
LP
2024 if (r < 0)
2025 return r;
718db961
LP
2026 if (bus->nodes_modified)
2027 return 0;
2028 if (r == 0)
2029 continue;
2030
a03e4337
LP
2031 if (names) {
2032 STRV_FOREACH(property, names) {
2033 struct vtable_member *v;
718db961 2034
a03e4337
LP
2035 key.member = *property;
2036 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2037 assert(c == v->parent);
718db961 2038
a03e4337
LP
2039 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2040 continue;
718db961 2041
a03e4337
LP
2042 r = sd_bus_message_append(m, "s", *property);
2043 if (r < 0)
2044 return r;
2045 }
2046 } else {
2047 const sd_bus_vtable *v;
2048
2049 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2050 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2051 continue;
2052
2053 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2054 continue;
2055
2056 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2057 continue;
2058
2059 r = sd_bus_message_append(m, "s", v->x.property.member);
2060 if (r < 0)
2061 return r;
2062 }
718db961 2063 }
992c052c
LP
2064 }
2065 }
2066
2067 r = sd_bus_message_close_container(m);
2068 if (r < 0)
2069 return r;
2070
2071 r = sd_bus_send(bus, m, NULL);
2072 if (r < 0)
2073 return r;
2074
2075 return 1;
2076}
2077
d9f644e2 2078_public_ int sd_bus_emit_properties_changed_strv(
dfa92725
LP
2079 sd_bus *bus,
2080 const char *path,
2081 const char *interface,
2082 char **names) {
2083
8ce2afd6 2084 BUS_DONT_DESTROY(bus);
46525bfc 2085 bool found_interface = false;
92e189e5 2086 char *prefix;
992c052c
LP
2087 int r;
2088
dfa92725
LP
2089 assert_return(bus, -EINVAL);
2090 assert_return(object_path_is_valid(path), -EINVAL);
2091 assert_return(interface_name_is_valid(interface), -EINVAL);
dfa92725
LP
2092 assert_return(!bus_pid_changed(bus), -ECHILD);
2093
a3d59cd1
LP
2094 if (!BUS_IS_OPEN(bus->state))
2095 return -ENOTCONN;
a03e4337
LP
2096
2097 /* A non-NULL but empty names list means nothing needs to be
2098 generated. A NULL list OTOH indicates that all properties
2099 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2100 included in the PropertiesChanged message. */
2101 if (names && names[0] == NULL)
dfa92725 2102 return 0;
992c052c 2103
68313d3d
LP
2104 do {
2105 bus->nodes_modified = false;
992c052c 2106
46525bfc 2107 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
92e189e5
LP
2108 if (r != 0)
2109 return r;
68313d3d
LP
2110 if (bus->nodes_modified)
2111 continue;
2112
2113 prefix = alloca(strlen(path) + 1);
2114 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
46525bfc 2115 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
68313d3d
LP
2116 if (r != 0)
2117 return r;
2118 if (bus->nodes_modified)
2119 break;
2120 }
2121
2122 } while (bus->nodes_modified);
992c052c 2123
46525bfc 2124 return found_interface ? 0 : -ENOENT;
992c052c
LP
2125}
2126
d9f644e2 2127_public_ int sd_bus_emit_properties_changed(
dfa92725
LP
2128 sd_bus *bus,
2129 const char *path,
2130 const char *interface,
2131 const char *name, ...) {
2132
250a918d 2133 char **names;
992c052c 2134
dfa92725
LP
2135 assert_return(bus, -EINVAL);
2136 assert_return(object_path_is_valid(path), -EINVAL);
2137 assert_return(interface_name_is_valid(interface), -EINVAL);
dfa92725
LP
2138 assert_return(!bus_pid_changed(bus), -ECHILD);
2139
a3d59cd1
LP
2140 if (!BUS_IS_OPEN(bus->state))
2141 return -ENOTCONN;
2142
dfa92725
LP
2143 if (!name)
2144 return 0;
2145
250a918d 2146 names = strv_from_stdarg_alloca(name);
992c052c
LP
2147
2148 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2149}
2150
d95eb43e
DH
2151static int object_added_append_all_prefix(
2152 sd_bus *bus,
2153 sd_bus_message *m,
2154 Set *s,
2155 const char *prefix,
2156 const char *path,
2157 bool require_fallback) {
2158
2159 const char *previous_interface = NULL;
2160 struct node_vtable *c;
2161 struct node *n;
2162 int r;
2163
2164 assert(bus);
2165 assert(m);
2166 assert(s);
2167 assert(prefix);
2168 assert(path);
2169
2170 n = hashmap_get(bus->nodes, prefix);
2171 if (!n)
2172 return 0;
2173
2174 LIST_FOREACH(vtables, c, n->vtables) {
2175 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2176 void *u = NULL;
2177
2178 if (require_fallback && !c->is_fallback)
2179 continue;
2180
2181 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2182 if (r < 0)
2183 return r;
2184 if (bus->nodes_modified)
2185 return 0;
2186 if (r == 0)
2187 continue;
2188
2189 if (!streq_ptr(c->interface, previous_interface)) {
2190 /* If a child-node already handled this interface, we
2191 * skip it on any of its parents. The child vtables
2192 * always fully override any conflicting vtables of
2193 * any parent node. */
2194 if (set_get(s, c->interface))
2195 continue;
2196
2197 r = set_put(s, c->interface);
2198 if (r < 0)
2199 return r;
2200
2201 if (previous_interface) {
2202 r = sd_bus_message_close_container(m);
2203 if (r < 0)
2204 return r;
2205 r = sd_bus_message_close_container(m);
2206 if (r < 0)
2207 return r;
2208 }
2209
2210 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2211 if (r < 0)
2212 return r;
2213 r = sd_bus_message_append(m, "s", c->interface);
2214 if (r < 0)
2215 return r;
2216 r = sd_bus_message_open_container(m, 'a', "{sv}");
2217 if (r < 0)
2218 return r;
2219
2220 previous_interface = c->interface;
2221 }
2222
2223 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2224 if (r < 0)
2225 return r;
2226 if (bus->nodes_modified)
2227 return 0;
2228 }
2229
2230 if (previous_interface) {
2231 r = sd_bus_message_close_container(m);
2232 if (r < 0)
2233 return r;
2234 r = sd_bus_message_close_container(m);
2235 if (r < 0)
2236 return r;
2237 }
2238
2239 return 0;
2240}
2241
2242static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2243 _cleanup_set_free_ Set *s = NULL;
2244 char *prefix;
2245 int r;
2246
2247 assert(bus);
2248 assert(m);
2249 assert(path);
2250
2251 /*
2252 * This appends all interfaces registered on path @path. We first add
2253 * the builtin interfaces, which are always available and handled by
2254 * sd-bus. Then, we add all interfaces registered on the exact node,
2255 * followed by all fallback interfaces registered on any parent prefix.
2256 *
2257 * If an interface is registered multiple times on the same node with
2258 * different vtables, we merge all the properties across all vtables.
2259 * However, if a child node has the same interface registered as one of
2260 * its parent nodes has as fallback, we make the child overwrite the
2261 * parent instead of extending it. Therefore, we keep a "Set" of all
2262 * handled interfaces during parent traversal, so we skip interfaces on
2263 * a parent that were overwritten by a child.
2264 */
2265
2266 s = set_new(&string_hash_ops);
2267 if (!s)
2268 return -ENOMEM;
2269
2270 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2271 if (r < 0)
2272 return r;
2273 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2274 if (r < 0)
2275 return r;
2276 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2277 if (r < 0)
2278 return r;
2279 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2280 if (r < 0)
2281 return r;
2282
2283 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2284 if (r < 0)
2285 return r;
2286 if (bus->nodes_modified)
2287 return 0;
2288
2289 prefix = alloca(strlen(path) + 1);
2290 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2291 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2292 if (r < 0)
2293 return r;
2294 if (bus->nodes_modified)
2295 return 0;
2296 }
2297
2298 return 0;
2299}
2300
969a9685 2301_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
d95eb43e
DH
2302 BUS_DONT_DESTROY(bus);
2303
2304 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2d5c8a27 2305 struct node *object_manager;
d95eb43e
DH
2306 int r;
2307
2308 /*
2309 * This emits an InterfacesAdded signal on the given path, by iterating
2310 * all registered vtables and fallback vtables on the path. All
2311 * properties are queried and included in the signal.
2312 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2313 * explicit list of registered interfaces. However, unlike
2314 * interfaces_added(), this call can figure out the list of supported
2315 * interfaces itself. Furthermore, it properly adds the builtin
2316 * org.freedesktop.DBus.* interfaces.
2317 */
2318
2319 assert_return(bus, -EINVAL);
2320 assert_return(object_path_is_valid(path), -EINVAL);
2321 assert_return(!bus_pid_changed(bus), -ECHILD);
2322
2323 if (!BUS_IS_OPEN(bus->state))
2324 return -ENOTCONN;
2325
2d5c8a27
DH
2326 r = bus_find_parent_object_manager(bus, &object_manager, path);
2327 if (r < 0)
2328 return r;
2329 if (r == 0)
2330 return -ESRCH;
2331
d95eb43e
DH
2332 do {
2333 bus->nodes_modified = false;
2334 m = sd_bus_message_unref(m);
2335
2d5c8a27 2336 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
d95eb43e
DH
2337 if (r < 0)
2338 return r;
2339
2340 r = sd_bus_message_append_basic(m, 'o', path);
2341 if (r < 0)
2342 return r;
2343
2344 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2345 if (r < 0)
2346 return r;
2347
2348 r = object_added_append_all(bus, m, path);
2349 if (r < 0)
2350 return r;
2351
2352 if (bus->nodes_modified)
2353 continue;
2354
2355 r = sd_bus_message_close_container(m);
2356 if (r < 0)
2357 return r;
2358
2359 } while (bus->nodes_modified);
2360
2361 return sd_bus_send(bus, m, NULL);
2362}
2363
2364static int object_removed_append_all_prefix(
2365 sd_bus *bus,
2366 sd_bus_message *m,
2367 Set *s,
2368 const char *prefix,
2369 const char *path,
2370 bool require_fallback) {
2371
2372 const char *previous_interface = NULL;
2373 struct node_vtable *c;
2374 struct node *n;
2375 int r;
2376
2377 assert(bus);
2378 assert(m);
2379 assert(s);
2380 assert(prefix);
2381 assert(path);
2382
2383 n = hashmap_get(bus->nodes, prefix);
2384 if (!n)
2385 return 0;
2386
2387 LIST_FOREACH(vtables, c, n->vtables) {
2388 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2389 void *u = NULL;
2390
2391 if (require_fallback && !c->is_fallback)
2392 continue;
2393 if (streq_ptr(c->interface, previous_interface))
2394 continue;
2395
2396 /* If a child-node already handled this interface, we
2397 * skip it on any of its parents. The child vtables
2398 * always fully override any conflicting vtables of
2399 * any parent node. */
2400 if (set_get(s, c->interface))
2401 continue;
2402
2403 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2404 if (r < 0)
2405 return r;
2406 if (bus->nodes_modified)
2407 return 0;
2408 if (r == 0)
2409 continue;
2410
2411 r = set_put(s, c->interface);
2412 if (r < 0)
2413 return r;
2414
2415 r = sd_bus_message_append(m, "s", c->interface);
2416 if (r < 0)
2417 return r;
2418
2419 previous_interface = c->interface;
2420 }
2421
2422 return 0;
2423}
2424
2425static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2426 _cleanup_set_free_ Set *s = NULL;
2427 char *prefix;
2428 int r;
2429
2430 assert(bus);
2431 assert(m);
2432 assert(path);
2433
2434 /* see sd_bus_emit_object_added() for details */
2435
2436 s = set_new(&string_hash_ops);
2437 if (!s)
2438 return -ENOMEM;
2439
2440 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2441 if (r < 0)
2442 return r;
2443 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2444 if (r < 0)
2445 return r;
2446 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2447 if (r < 0)
2448 return r;
2449 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2450 if (r < 0)
2451 return r;
2452
2453 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2454 if (r < 0)
2455 return r;
2456 if (bus->nodes_modified)
2457 return 0;
2458
2459 prefix = alloca(strlen(path) + 1);
2460 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2461 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2462 if (r < 0)
2463 return r;
2464 if (bus->nodes_modified)
2465 return 0;
2466 }
2467
2468 return 0;
2469}
2470
969a9685 2471_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
d95eb43e
DH
2472 BUS_DONT_DESTROY(bus);
2473
2474 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2d5c8a27 2475 struct node *object_manager;
d95eb43e
DH
2476 int r;
2477
2478 /*
2479 * This is like sd_bus_emit_object_added(), but emits an
2480 * InterfacesRemoved signal on the given path. This only includes any
2481 * registered interfaces but skips the properties. Note that this will
2482 * call into the find() callbacks of any registered vtable. Therefore,
2483 * you must call this function before destroying/unlinking your object.
2484 * Otherwise, the list of interfaces will be incomplete. However, note
2485 * that this will *NOT* call into any property callback. Therefore, the
2486 * object might be in an "destructed" state, as long as we can find it.
2487 */
2488
2489 assert_return(bus, -EINVAL);
2490 assert_return(object_path_is_valid(path), -EINVAL);
2491 assert_return(!bus_pid_changed(bus), -ECHILD);
2492
2493 if (!BUS_IS_OPEN(bus->state))
2494 return -ENOTCONN;
2495
2d5c8a27
DH
2496 r = bus_find_parent_object_manager(bus, &object_manager, path);
2497 if (r < 0)
2498 return r;
2499 if (r == 0)
2500 return -ESRCH;
2501
d95eb43e
DH
2502 do {
2503 bus->nodes_modified = false;
2504 m = sd_bus_message_unref(m);
2505
2d5c8a27 2506 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
d95eb43e
DH
2507 if (r < 0)
2508 return r;
2509
2510 r = sd_bus_message_append_basic(m, 'o', path);
2511 if (r < 0)
2512 return r;
2513
2514 r = sd_bus_message_open_container(m, 'a', "s");
2515 if (r < 0)
2516 return r;
2517
2518 r = object_removed_append_all(bus, m, path);
2519 if (r < 0)
2520 return r;
2521
2522 if (bus->nodes_modified)
2523 continue;
2524
2525 r = sd_bus_message_close_container(m);
2526 if (r < 0)
2527 return r;
2528
2529 } while (bus->nodes_modified);
2530
2531 return sd_bus_send(bus, m, NULL);
2532}
2533
4be39163
LP
2534static int interfaces_added_append_one_prefix(
2535 sd_bus *bus,
2536 sd_bus_message *m,
2537 const char *prefix,
2538 const char *path,
2539 const char *interface,
2540 bool require_fallback) {
2541
2542 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
718db961 2543 bool found_interface = false;
4be39163
LP
2544 struct node_vtable *c;
2545 struct node *n;
2546 void *u = NULL;
2547 int r;
2548
2549 assert(bus);
2550 assert(m);
2551 assert(prefix);
2552 assert(path);
2553 assert(interface);
2554
2555 n = hashmap_get(bus->nodes, prefix);
2556 if (!n)
2557 return 0;
2558
2559 LIST_FOREACH(vtables, c, n->vtables) {
2560 if (require_fallback && !c->is_fallback)
2561 continue;
2562
718db961
LP
2563 if (!streq(c->interface, interface))
2564 continue;
4be39163 2565
f00c3121 2566 r = node_vtable_get_userdata(bus, path, c, &u, &error);
718db961
LP
2567 if (r < 0)
2568 return r;
2569 if (bus->nodes_modified)
2570 return 0;
2571 if (r == 0)
2572 continue;
4be39163 2573
718db961
LP
2574 if (!found_interface) {
2575 r = sd_bus_message_append_basic(m, 's', interface);
2576 if (r < 0)
2577 return r;
4be39163 2578
718db961
LP
2579 r = sd_bus_message_open_container(m, 'a', "{sv}");
2580 if (r < 0)
2581 return r;
4be39163 2582
718db961
LP
2583 found_interface = true;
2584 }
4be39163 2585
718db961
LP
2586 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2587 if (r < 0)
2588 return r;
2589 if (bus->nodes_modified)
2590 return 0;
2591 }
4be39163 2592
718db961
LP
2593 if (found_interface) {
2594 r = sd_bus_message_close_container(m);
2595 if (r < 0)
2596 return r;
2597 }
4be39163 2598
718db961 2599 return found_interface;
4be39163
LP
2600}
2601
2602static int interfaces_added_append_one(
2603 sd_bus *bus,
2604 sd_bus_message *m,
2605 const char *path,
2606 const char *interface) {
2607
2608 char *prefix;
2609 int r;
2610
2611 assert(bus);
2612 assert(m);
2613 assert(path);
2614 assert(interface);
2615
2616 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2617 if (r != 0)
2618 return r;
68313d3d
LP
2619 if (bus->nodes_modified)
2620 return 0;
4be39163
LP
2621
2622 prefix = alloca(strlen(path) + 1);
2623 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2624 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2625 if (r != 0)
2626 return r;
68313d3d
LP
2627 if (bus->nodes_modified)
2628 return 0;
4be39163
LP
2629 }
2630
2631 return -ENOENT;
992c052c
LP
2632}
2633
d9f644e2 2634_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
8ce2afd6
LP
2635 BUS_DONT_DESTROY(bus);
2636
4be39163 2637 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2d5c8a27 2638 struct node *object_manager;
4be39163
LP
2639 char **i;
2640 int r;
2641
2642 assert_return(bus, -EINVAL);
2643 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2644 assert_return(!bus_pid_changed(bus), -ECHILD);
2645
a3d59cd1
LP
2646 if (!BUS_IS_OPEN(bus->state))
2647 return -ENOTCONN;
2648
4be39163
LP
2649 if (strv_isempty(interfaces))
2650 return 0;
2651
2d5c8a27
DH
2652 r = bus_find_parent_object_manager(bus, &object_manager, path);
2653 if (r < 0)
2654 return r;
2655 if (r == 0)
2656 return -ESRCH;
2657
68313d3d
LP
2658 do {
2659 bus->nodes_modified = false;
1b02f301 2660 m = sd_bus_message_unref(m);
4be39163 2661
2d5c8a27 2662 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
68313d3d
LP
2663 if (r < 0)
2664 return r;
4be39163 2665
68313d3d 2666 r = sd_bus_message_append_basic(m, 'o', path);
4be39163
LP
2667 if (r < 0)
2668 return r;
2669
68313d3d 2670 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
4be39163
LP
2671 if (r < 0)
2672 return r;
2673
68313d3d
LP
2674 STRV_FOREACH(i, interfaces) {
2675 assert_return(interface_name_is_valid(*i), -EINVAL);
2676
2677 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2678 if (r < 0)
2679 return r;
2680
2681 r = interfaces_added_append_one(bus, m, path, *i);
2682 if (r < 0)
2683 return r;
2684
2685 if (bus->nodes_modified)
2686 break;
2687
2688 r = sd_bus_message_close_container(m);
2689 if (r < 0)
2690 return r;
2691 }
2692
2693 if (bus->nodes_modified)
2694 continue;
2695
4be39163
LP
2696 r = sd_bus_message_close_container(m);
2697 if (r < 0)
2698 return r;
4be39163 2699
68313d3d 2700 } while (bus->nodes_modified);
4be39163
LP
2701
2702 return sd_bus_send(bus, m, NULL);
2703}
2704
d9f644e2 2705_public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
250a918d 2706 char **interfaces;
4be39163
LP
2707
2708 assert_return(bus, -EINVAL);
2709 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2710 assert_return(!bus_pid_changed(bus), -ECHILD);
2711
a3d59cd1
LP
2712 if (!BUS_IS_OPEN(bus->state))
2713 return -ENOTCONN;
2714
250a918d 2715 interfaces = strv_from_stdarg_alloca(interface);
4be39163
LP
2716
2717 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2718}
2719
d9f644e2 2720_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
4be39163 2721 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2d5c8a27 2722 struct node *object_manager;
4be39163
LP
2723 int r;
2724
2725 assert_return(bus, -EINVAL);
2726 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2727 assert_return(!bus_pid_changed(bus), -ECHILD);
2728
a3d59cd1
LP
2729 if (!BUS_IS_OPEN(bus->state))
2730 return -ENOTCONN;
2731
4be39163
LP
2732 if (strv_isempty(interfaces))
2733 return 0;
2734
2d5c8a27
DH
2735 r = bus_find_parent_object_manager(bus, &object_manager, path);
2736 if (r < 0)
2737 return r;
2738 if (r == 0)
2739 return -ESRCH;
2740
2741 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
4be39163
LP
2742 if (r < 0)
2743 return r;
2744
2745 r = sd_bus_message_append_basic(m, 'o', path);
2746 if (r < 0)
2747 return r;
2748
2749 r = sd_bus_message_append_strv(m, interfaces);
2750 if (r < 0)
2751 return r;
2752
2753 return sd_bus_send(bus, m, NULL);
2754}
2755
d9f644e2 2756_public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
250a918d 2757 char **interfaces;
4be39163
LP
2758
2759 assert_return(bus, -EINVAL);
2760 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2761 assert_return(!bus_pid_changed(bus), -ECHILD);
2762
a3d59cd1
LP
2763 if (!BUS_IS_OPEN(bus->state))
2764 return -ENOTCONN;
2765
250a918d 2766 interfaces = strv_from_stdarg_alloca(interface);
4be39163
LP
2767
2768 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
992c052c
LP
2769}
2770
19befb2d
LP
2771_public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2772 sd_bus_slot *s;
992c052c 2773 struct node *n;
19befb2d 2774 int r;
992c052c 2775
dfa92725
LP
2776 assert_return(bus, -EINVAL);
2777 assert_return(object_path_is_valid(path), -EINVAL);
2778 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c
LP
2779
2780 n = bus_node_allocate(bus, path);
2781 if (!n)
2782 return -ENOMEM;
2783
19befb2d
LP
2784 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2785 if (!s) {
2786 r = -ENOMEM;
2787 goto fail;
2788 }
992c052c 2789
19befb2d
LP
2790 s->node_object_manager.node = n;
2791 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2792 bus->nodes_modified = true;
992c052c 2793
19befb2d
LP
2794 if (slot)
2795 *slot = s;
992c052c 2796
19befb2d 2797 return 0;
992c052c 2798
19befb2d
LP
2799fail:
2800 sd_bus_slot_unref(s);
992c052c 2801 bus_node_gc(bus, n);
dfa92725 2802
19befb2d 2803 return r;
992c052c 2804}