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