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