]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-objects.c
tests: install `test` as part of BASICTOOLS (#7434)
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-objects.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
992c052c
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
b5efdb8a 21#include "alloc-util.h"
992c052c 22#include "bus-internal.h"
07630cea 23#include "bus-introspect.h"
992c052c 24#include "bus-message.h"
cf0fbc49 25#include "bus-objects.h"
992c052c 26#include "bus-signature.h"
19befb2d 27#include "bus-slot.h"
07630cea
LP
28#include "bus-type.h"
29#include "bus-util.h"
30#include "set.h"
31#include "string-util.h"
32#include "strv.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;
00590f82 42 void *u, *found_u;
992c052c
LP
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;
00590f82 54 r = c->find(bus, path, c->interface, u, &found_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 63 return r;
00590f82
AJ
64 } else
65 found_u = u;
992c052c
LP
66
67 if (userdata)
00590f82 68 *userdata = found_u;
992c052c
LP
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
9ed794a3 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) {
4afd3348 275 _cleanup_(sd_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
313cefa1 342 cap--;
adacb957 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
4afd3348 360 _cleanup_(sd_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
4afd3348
LP
583 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
584 _cleanup_(sd_bus_message_unrefp) 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 756 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
945c2931 757 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
992c052c
LP
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
4afd3348 784 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
992c052c 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 806 LIST_FOREACH(vtables, c, first) {
4afd3348 807 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
992c052c
LP
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
733e792b
AJ
834 if (!*found_object)
835 return 0;
836
992c052c
LP
837 if (!found_interface) {
838 r = sd_bus_reply_method_errorf(
df2d202e 839 m,
40ca29a1 840 SD_BUS_ERROR_UNKNOWN_INTERFACE,
992c052c
LP
841 "Unknown interface '%s'.", iface);
842 if (r < 0)
843 return r;
844
845 return 1;
846 }
847
848 r = sd_bus_message_close_container(reply);
849 if (r < 0)
850 return r;
851
852 r = sd_bus_send(bus, reply, NULL);
853 if (r < 0)
854 return r;
855
856 return 1;
857}
858
ff02f101 859static int bus_node_exists(
dfa92725
LP
860 sd_bus *bus,
861 struct node *n,
862 const char *path,
863 bool require_fallback) {
864
992c052c
LP
865 struct node_vtable *c;
866 struct node_callback *k;
ff02f101 867 int r;
992c052c
LP
868
869 assert(bus);
870 assert(n);
dfa92725 871 assert(path);
992c052c
LP
872
873 /* Tests if there's anything attached directly to this node
874 * for the specified path */
875
ff02f101
DH
876 if (!require_fallback && (n->enumerators || n->object_managers))
877 return true;
878
992c052c
LP
879 LIST_FOREACH(callbacks, k, n->callbacks) {
880 if (require_fallback && !k->is_fallback)
881 continue;
882
ff02f101 883 return 1;
992c052c
LP
884 }
885
886 LIST_FOREACH(vtables, c, n->vtables) {
4afd3348 887 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
992c052c
LP
888
889 if (require_fallback && !c->is_fallback)
890 continue;
891
ff02f101
DH
892 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
893 if (r != 0)
894 return r;
68313d3d 895 if (bus->nodes_modified)
ff02f101 896 return 0;
992c052c
LP
897 }
898
ff02f101 899 return 0;
992c052c
LP
900}
901
902static int process_introspect(
903 sd_bus *bus,
904 sd_bus_message *m,
905 struct node *n,
906 bool require_fallback,
907 bool *found_object) {
908
4afd3348
LP
909 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
910 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
992c052c 911 _cleanup_set_free_free_ Set *s = NULL;
718db961 912 const char *previous_interface = NULL;
992c052c
LP
913 struct introspect intro;
914 struct node_vtable *c;
915 bool empty;
916 int r;
917
918 assert(bus);
919 assert(m);
920 assert(n);
921 assert(found_object);
922
44eb1add 923 r = get_child_nodes(bus, m->path, n, 0, &s, &error);
992c052c 924 if (r < 0)
f00c3121 925 return bus_maybe_reply_error(m, r, &error);
68313d3d
LP
926 if (bus->nodes_modified)
927 return 0;
992c052c 928
7fb411f0 929 r = introspect_begin(&intro, bus->trusted);
992c052c
LP
930 if (r < 0)
931 return r;
932
943c3f94 933 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
992c052c
LP
934 if (r < 0)
935 return r;
936
937 empty = set_isempty(s);
938
939 LIST_FOREACH(vtables, c, n->vtables) {
940 if (require_fallback && !c->is_fallback)
941 continue;
942
f00c3121
LP
943 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
944 if (r < 0) {
945 r = bus_maybe_reply_error(m, r, &error);
946 goto finish;
947 }
948 if (bus->nodes_modified) {
949 r = 0;
950 goto finish;
951 }
992c052c
LP
952 if (r == 0)
953 continue;
954
955 empty = false;
956
6e8df5f0
LP
957 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
958 continue;
959
718db961
LP
960 if (!streq_ptr(previous_interface, c->interface)) {
961
962 if (previous_interface)
4b61c875 963 fputs_unlocked(" </interface>\n", intro.f);
718db961
LP
964
965 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
966 }
967
968 r = introspect_write_interface(&intro, c->vtable);
992c052c
LP
969 if (r < 0)
970 goto finish;
718db961
LP
971
972 previous_interface = c->interface;
992c052c
LP
973 }
974
718db961 975 if (previous_interface)
4b61c875 976 fputs_unlocked(" </interface>\n", intro.f);
718db961 977
992c052c
LP
978 if (empty) {
979 /* Nothing?, let's see if we exist at all, and if not
980 * refuse to do anything */
981 r = bus_node_exists(bus, n, m->path, require_fallback);
f2bfc6ba
LP
982 if (r <= 0) {
983 r = bus_maybe_reply_error(m, r, &error);
ff02f101 984 goto finish;
f2bfc6ba 985 }
ff02f101
DH
986 if (bus->nodes_modified) {
987 r = 0;
992c052c 988 goto finish;
ff02f101 989 }
992c052c
LP
990 }
991
992 *found_object = true;
993
994 r = introspect_write_child_nodes(&intro, s, m->path);
995 if (r < 0)
996 goto finish;
997
998 r = introspect_finish(&intro, bus, m, &reply);
999 if (r < 0)
1000 goto finish;
1001
1002 r = sd_bus_send(bus, reply, NULL);
1003 if (r < 0)
1004 goto finish;
1005
1006 r = 1;
1007
1008finish:
1009 introspect_free(&intro);
1010 return r;
1011}
1012
992c052c
LP
1013static int object_manager_serialize_path(
1014 sd_bus *bus,
1015 sd_bus_message *reply,
1016 const char *prefix,
1017 const char *path,
1018 bool require_fallback,
1019 sd_bus_error *error) {
1020
718db961
LP
1021 const char *previous_interface = NULL;
1022 bool found_something = false;
992c052c
LP
1023 struct node_vtable *i;
1024 struct node *n;
1025 int r;
1026
1027 assert(bus);
1028 assert(reply);
1029 assert(prefix);
1030 assert(path);
1031 assert(error);
1032
1033 n = hashmap_get(bus->nodes, prefix);
1034 if (!n)
1035 return 0;
1036
992c052c 1037 LIST_FOREACH(vtables, i, n->vtables) {
92db139e 1038 void *u;
992c052c
LP
1039
1040 if (require_fallback && !i->is_fallback)
1041 continue;
1042
f00c3121 1043 r = node_vtable_get_userdata(bus, path, i, &u, error);
92db139e
LP
1044 if (r < 0)
1045 return r;
68313d3d
LP
1046 if (bus->nodes_modified)
1047 return 0;
92db139e
LP
1048 if (r == 0)
1049 continue;
1050
1051 if (!found_something) {
718db961
LP
1052
1053 /* Open the object part */
1054
92db139e
LP
1055 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1056 if (r < 0)
1057 return r;
1058
1059 r = sd_bus_message_append(reply, "o", path);
1060 if (r < 0)
1061 return r;
1062
1063 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1064 if (r < 0)
1065 return r;
1066
c0e7906d
DH
1067 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
1068 if (r < 0)
1069 return r;
1070
1071 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
1072 if (r < 0)
1073 return r;
1074
1075 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
1076 if (r < 0)
1077 return r;
1078
1079 r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
1080 if (r < 0)
1081 return r;
1082
92db139e
LP
1083 found_something = true;
1084 }
1085
718db961
LP
1086 if (!streq_ptr(previous_interface, i->interface)) {
1087
1088 /* Maybe close the previous interface part */
1089
1090 if (previous_interface) {
1091 r = sd_bus_message_close_container(reply);
1092 if (r < 0)
1093 return r;
1094
1095 r = sd_bus_message_close_container(reply);
1096 if (r < 0)
1097 return r;
1098 }
1099
1100 /* Open the new interface part */
1101
1102 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1103 if (r < 0)
1104 return r;
1105
1106 r = sd_bus_message_append(reply, "s", i->interface);
1107 if (r < 0)
1108 return r;
1109
1110 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1111 if (r < 0)
1112 return r;
1113 }
1114
1115 r = vtable_append_all_properties(bus, reply, path, i, u, error);
992c052c
LP
1116 if (r < 0)
1117 return r;
68313d3d
LP
1118 if (bus->nodes_modified)
1119 return 0;
718db961
LP
1120
1121 previous_interface = i->interface;
1122 }
1123
1124 if (previous_interface) {
1125 r = sd_bus_message_close_container(reply);
1126 if (r < 0)
1127 return r;
1128
1129 r = sd_bus_message_close_container(reply);
1130 if (r < 0)
1131 return r;
992c052c
LP
1132 }
1133
92db139e
LP
1134 if (found_something) {
1135 r = sd_bus_message_close_container(reply);
1136 if (r < 0)
1137 return r;
992c052c 1138
92db139e
LP
1139 r = sd_bus_message_close_container(reply);
1140 if (r < 0)
1141 return r;
1142 }
992c052c
LP
1143
1144 return 1;
1145}
1146
1147static int object_manager_serialize_path_and_fallbacks(
1148 sd_bus *bus,
1149 sd_bus_message *reply,
1150 const char *path,
1151 sd_bus_error *error) {
1152
92e189e5 1153 char *prefix;
992c052c
LP
1154 int r;
1155
1156 assert(bus);
1157 assert(reply);
1158 assert(path);
1159 assert(error);
1160
1161 /* First, add all vtables registered for this path */
1162 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1163 if (r < 0)
1164 return r;
68313d3d
LP
1165 if (bus->nodes_modified)
1166 return 0;
992c052c
LP
1167
1168 /* Second, add fallback vtables registered for any of the prefixes */
92e189e5
LP
1169 prefix = alloca(strlen(path) + 1);
1170 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1171 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1172 if (r < 0)
1173 return r;
68313d3d
LP
1174 if (bus->nodes_modified)
1175 return 0;
992c052c
LP
1176 }
1177
1178 return 0;
1179}
1180
1181static int process_get_managed_objects(
1182 sd_bus *bus,
1183 sd_bus_message *m,
1184 struct node *n,
1185 bool require_fallback,
1186 bool *found_object) {
1187
4afd3348
LP
1188 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1189 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
992c052c 1190 _cleanup_set_free_free_ Set *s = NULL;
943c3f94
DH
1191 Iterator i;
1192 char *path;
992c052c
LP
1193 int r;
1194
1195 assert(bus);
1196 assert(m);
1197 assert(n);
1198 assert(found_object);
1199
943c3f94
DH
1200 /* Spec says, GetManagedObjects() is only implemented on the root of a
1201 * sub-tree. Therefore, we require a registered object-manager on
1202 * exactly the queried path, otherwise, we refuse to respond. */
1203
1204 if (require_fallback || !n->object_managers)
992c052c
LP
1205 return 0;
1206
44eb1add 1207 r = get_child_nodes(bus, m->path, n, CHILDREN_RECURSIVE, &s, &error);
992c052c 1208 if (r < 0)
f2bfc6ba 1209 return bus_maybe_reply_error(m, r, &error);
68313d3d
LP
1210 if (bus->nodes_modified)
1211 return 0;
992c052c 1212
df2d202e 1213 r = sd_bus_message_new_method_return(m, &reply);
992c052c
LP
1214 if (r < 0)
1215 return r;
1216
1217 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1218 if (r < 0)
1219 return r;
1220
943c3f94
DH
1221 SET_FOREACH(path, s, i) {
1222 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1223 if (r < 0)
f2bfc6ba 1224 return bus_maybe_reply_error(m, r, &error);
992c052c 1225
943c3f94 1226 if (bus->nodes_modified)
992c052c 1227 return 0;
992c052c
LP
1228 }
1229
1230 r = sd_bus_message_close_container(reply);
1231 if (r < 0)
1232 return r;
1233
1234 r = sd_bus_send(bus, reply, NULL);
1235 if (r < 0)
1236 return r;
1237
1238 return 1;
1239}
1240
1241static int object_find_and_run(
1242 sd_bus *bus,
1243 sd_bus_message *m,
1244 const char *p,
1245 bool require_fallback,
1246 bool *found_object) {
1247
1248 struct node *n;
1249 struct vtable_member vtable_key, *v;
1250 int r;
1251
1252 assert(bus);
1253 assert(m);
1254 assert(p);
1255 assert(found_object);
1256
1257 n = hashmap_get(bus->nodes, p);
1258 if (!n)
1259 return 0;
1260
1261 /* First, try object callbacks */
1262 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1263 if (r != 0)
1264 return r;
68313d3d
LP
1265 if (bus->nodes_modified)
1266 return 0;
992c052c
LP
1267
1268 if (!m->interface || !m->member)
1269 return 0;
1270
1271 /* Then, look for a known method */
1272 vtable_key.path = (char*) p;
1273 vtable_key.interface = m->interface;
1274 vtable_key.member = m->member;
1275
1276 v = hashmap_get(bus->vtable_methods, &vtable_key);
1277 if (v) {
1278 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1279 if (r != 0)
1280 return r;
68313d3d
LP
1281 if (bus->nodes_modified)
1282 return 0;
992c052c
LP
1283 }
1284
1285 /* Then, look for a known property */
1286 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1287 bool get = false;
1288
1289 get = streq(m->member, "Get");
1290
1291 if (get || streq(m->member, "Set")) {
1292
1293 r = sd_bus_message_rewind(m, true);
1294 if (r < 0)
1295 return r;
1296
1297 vtable_key.path = (char*) p;
1298
1299 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1300 if (r < 0)
0fc5ab90 1301 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
992c052c
LP
1302
1303 v = hashmap_get(bus->vtable_properties, &vtable_key);
1304 if (v) {
1305 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1306 if (r != 0)
1307 return r;
1308 }
1309
1310 } else if (streq(m->member, "GetAll")) {
1311 const char *iface;
1312
1313 r = sd_bus_message_rewind(m, true);
1314 if (r < 0)
1315 return r;
1316
1317 r = sd_bus_message_read(m, "s", &iface);
1318 if (r < 0)
0fc5ab90 1319 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
992c052c
LP
1320
1321 if (iface[0] == 0)
1322 iface = NULL;
1323
1324 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1325 if (r != 0)
1326 return r;
1327 }
1328
1329 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1330
0fc5ab90
LP
1331 if (!isempty(sd_bus_message_get_signature(m, true)))
1332 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1333
992c052c
LP
1334 r = process_introspect(bus, m, n, require_fallback, found_object);
1335 if (r != 0)
1336 return r;
1337
1338 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1339
0fc5ab90
LP
1340 if (!isempty(sd_bus_message_get_signature(m, true)))
1341 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1342
992c052c
LP
1343 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1344 if (r != 0)
1345 return r;
1346 }
1347
68313d3d
LP
1348 if (bus->nodes_modified)
1349 return 0;
1350
992c052c
LP
1351 if (!*found_object) {
1352 r = bus_node_exists(bus, n, m->path, require_fallback);
1353 if (r < 0)
f2bfc6ba 1354 return bus_maybe_reply_error(m, r, NULL);
ff02f101
DH
1355 if (bus->nodes_modified)
1356 return 0;
992c052c
LP
1357 if (r > 0)
1358 *found_object = true;
1359 }
1360
1361 return 0;
1362}
1363
1364int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1365 int r;
1366 size_t pl;
1367 bool found_object = false;
1368
1369 assert(bus);
1370 assert(m);
1371
09365592
LP
1372 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1373 return 0;
1374
40ca29a1 1375 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
992c052c
LP
1376 return 0;
1377
992c052c
LP
1378 if (hashmap_isempty(bus->nodes))
1379 return 0;
1380
f820cf99
LP
1381 /* Never respond to broadcast messages */
1382 if (bus->bus_client && !m->destination)
1383 return 0;
1384
adacb957
LP
1385 assert(m->path);
1386 assert(m->member);
1387
992c052c
LP
1388 pl = strlen(m->path);
1389 do {
92e189e5 1390 char prefix[pl+1];
992c052c
LP
1391
1392 bus->nodes_modified = false;
1393
1394 r = object_find_and_run(bus, m, m->path, false, &found_object);
1395 if (r != 0)
1396 return r;
1397
1398 /* Look for fallback prefixes */
92e189e5 1399 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
992c052c
LP
1400
1401 if (bus->nodes_modified)
1402 break;
1403
92e189e5 1404 r = object_find_and_run(bus, m, prefix, true, &found_object);
992c052c
LP
1405 if (r != 0)
1406 return r;
1407 }
1408
1409 } while (bus->nodes_modified);
1410
1411 if (!found_object)
1412 return 0;
1413
1414 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1415 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1416 r = sd_bus_reply_method_errorf(
df2d202e 1417 m,
40ca29a1 1418 SD_BUS_ERROR_UNKNOWN_PROPERTY,
992c052c
LP
1419 "Unknown property or interface.");
1420 else
1421 r = sd_bus_reply_method_errorf(
df2d202e 1422 m,
40ca29a1 1423 SD_BUS_ERROR_UNKNOWN_METHOD,
992c052c
LP
1424 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1425
1426 if (r < 0)
1427 return r;
1428
1429 return 1;
1430}
1431
1432static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1433 struct node *n, *parent;
1434 const char *e;
2fd069b1
ZJS
1435 _cleanup_free_ char *s = NULL;
1436 char *p;
992c052c
LP
1437 int r;
1438
1439 assert(bus);
1440 assert(path);
1441 assert(path[0] == '/');
1442
1443 n = hashmap_get(bus->nodes, path);
1444 if (n)
1445 return n;
1446
d5099efc 1447 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
992c052c
LP
1448 if (r < 0)
1449 return NULL;
1450
1451 s = strdup(path);
1452 if (!s)
1453 return NULL;
1454
1455 if (streq(path, "/"))
1456 parent = NULL;
1457 else {
1458 e = strrchr(path, '/');
1459 assert(e);
1460
7d67077f 1461 p = strndupa(path, MAX(1, e - path));
992c052c
LP
1462
1463 parent = bus_node_allocate(bus, p);
2fd069b1 1464 if (!parent)
992c052c 1465 return NULL;
992c052c
LP
1466 }
1467
1468 n = new0(struct node, 1);
1469 if (!n)
1470 return NULL;
1471
1472 n->parent = parent;
1473 n->path = s;
2fd069b1 1474 s = NULL; /* do not free */
992c052c 1475
8e050193 1476 r = hashmap_put(bus->nodes, n->path, n);
992c052c 1477 if (r < 0) {
2fd069b1 1478 free(n->path);
992c052c
LP
1479 free(n);
1480 return NULL;
1481 }
1482
1483 if (parent)
71fda00f 1484 LIST_PREPEND(siblings, parent->child, n);
992c052c
LP
1485
1486 return n;
1487}
1488
19befb2d 1489void bus_node_gc(sd_bus *b, struct node *n) {
992c052c
LP
1490 assert(b);
1491
1492 if (!n)
1493 return;
1494
1495 if (n->child ||
1496 n->callbacks ||
1497 n->vtables ||
1498 n->enumerators ||
19befb2d 1499 n->object_managers)
992c052c
LP
1500 return;
1501
85e55d14 1502 assert_se(hashmap_remove(b->nodes, n->path) == n);
992c052c
LP
1503
1504 if (n->parent)
71fda00f 1505 LIST_REMOVE(siblings, n->parent->child, n);
992c052c
LP
1506
1507 free(n->path);
1508 bus_node_gc(b, n->parent);
1509 free(n);
1510}
1511
2d5c8a27
DH
1512static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1513 struct node *n;
1514
1515 assert(bus);
1516 assert(path);
1517
1518 n = hashmap_get(bus->nodes, path);
1519 if (!n) {
1520 char *prefix;
1521
1522 prefix = alloca(strlen(path) + 1);
1523 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1524 n = hashmap_get(bus->nodes, prefix);
1525 if (n)
1526 break;
1527 }
1528 }
1529
1530 while (n && !n->object_managers)
1531 n = n->parent;
1532
1533 if (out)
1534 *out = n;
1535 return !!n;
1536}
1537
992c052c 1538static int bus_add_object(
dfa92725 1539 sd_bus *bus,
19befb2d 1540 sd_bus_slot **slot,
992c052c
LP
1541 bool fallback,
1542 const char *path,
1543 sd_bus_message_handler_t callback,
1544 void *userdata) {
1545
19befb2d 1546 sd_bus_slot *s;
992c052c
LP
1547 struct node *n;
1548 int r;
1549
dfa92725
LP
1550 assert_return(bus, -EINVAL);
1551 assert_return(object_path_is_valid(path), -EINVAL);
1552 assert_return(callback, -EINVAL);
1553 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c 1554
dfa92725 1555 n = bus_node_allocate(bus, path);
992c052c
LP
1556 if (!n)
1557 return -ENOMEM;
1558
19befb2d
LP
1559 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1560 if (!s) {
992c052c
LP
1561 r = -ENOMEM;
1562 goto fail;
1563 }
1564
19befb2d
LP
1565 s->node_callback.callback = callback;
1566 s->node_callback.is_fallback = fallback;
992c052c 1567
19befb2d
LP
1568 s->node_callback.node = n;
1569 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
68313d3d
LP
1570 bus->nodes_modified = true;
1571
19befb2d
LP
1572 if (slot)
1573 *slot = s;
1574
992c052c
LP
1575 return 0;
1576
1577fail:
19befb2d 1578 sd_bus_slot_unref(s);
dfa92725 1579 bus_node_gc(bus, n);
19befb2d 1580
992c052c
LP
1581 return r;
1582}
1583
19befb2d 1584_public_ int sd_bus_add_object(
992c052c 1585 sd_bus *bus,
19befb2d 1586 sd_bus_slot **slot,
992c052c
LP
1587 const char *path,
1588 sd_bus_message_handler_t callback,
1589 void *userdata) {
1590
19befb2d 1591 return bus_add_object(bus, slot, false, path, callback, userdata);
992c052c
LP
1592}
1593
19befb2d
LP
1594_public_ int sd_bus_add_fallback(
1595 sd_bus *bus,
1596 sd_bus_slot **slot,
1597 const char *prefix,
1598 sd_bus_message_handler_t callback,
1599 void *userdata) {
992c052c 1600
19befb2d 1601 return bus_add_object(bus, slot, true, prefix, callback, userdata);
992c052c
LP
1602}
1603
b826ab58 1604static void vtable_member_hash_func(const void *a, struct siphash *state) {
992c052c
LP
1605 const struct vtable_member *m = a;
1606
dfa92725
LP
1607 assert(m);
1608
b826ab58
TG
1609 string_hash_func(m->path, state);
1610 string_hash_func(m->interface, state);
1611 string_hash_func(m->member, state);
992c052c
LP
1612}
1613
1614static int vtable_member_compare_func(const void *a, const void *b) {
1615 const struct vtable_member *x = a, *y = b;
1616 int r;
1617
dfa92725
LP
1618 assert(x);
1619 assert(y);
1620
992c052c
LP
1621 r = strcmp(x->path, y->path);
1622 if (r != 0)
1623 return r;
1624
1625 r = strcmp(x->interface, y->interface);
1626 if (r != 0)
1627 return r;
1628
1629 return strcmp(x->member, y->member);
1630}
1631
d5099efc
MS
1632static const struct hash_ops vtable_member_hash_ops = {
1633 .hash = vtable_member_hash_func,
1634 .compare = vtable_member_compare_func
1635};
1636
992c052c
LP
1637static int add_object_vtable_internal(
1638 sd_bus *bus,
19befb2d 1639 sd_bus_slot **slot,
992c052c
LP
1640 const char *path,
1641 const char *interface,
1642 const sd_bus_vtable *vtable,
1643 bool fallback,
1644 sd_bus_object_find_t find,
1645 void *userdata) {
1646
2915234d 1647 sd_bus_slot *s = NULL;
19befb2d 1648 struct node_vtable *i, *existing = NULL;
992c052c
LP
1649 const sd_bus_vtable *v;
1650 struct node *n;
1651 int r;
1652
dfa92725
LP
1653 assert_return(bus, -EINVAL);
1654 assert_return(object_path_is_valid(path), -EINVAL);
1655 assert_return(interface_name_is_valid(interface), -EINVAL);
1656 assert_return(vtable, -EINVAL);
1657 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1658 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1659 assert_return(!bus_pid_changed(bus), -ECHILD);
718db961
LP
1660 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1661 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1662 !streq(interface, "org.freedesktop.DBus.Peer") &&
1663 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
992c052c 1664
d5099efc 1665 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
992c052c
LP
1666 if (r < 0)
1667 return r;
1668
d5099efc 1669 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
992c052c
LP
1670 if (r < 0)
1671 return r;
1672
1673 n = bus_node_allocate(bus, path);
1674 if (!n)
1675 return -ENOMEM;
1676
1677 LIST_FOREACH(vtables, i, n->vtables) {
992c052c
LP
1678 if (i->is_fallback != fallback) {
1679 r = -EPROTOTYPE;
1680 goto fail;
1681 }
718db961
LP
1682
1683 if (streq(i->interface, interface)) {
1684
1685 if (i->vtable == vtable) {
1686 r = -EEXIST;
1687 goto fail;
1688 }
1689
1690 existing = i;
1691 }
992c052c
LP
1692 }
1693
19befb2d
LP
1694 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1695 if (!s) {
992c052c
LP
1696 r = -ENOMEM;
1697 goto fail;
1698 }
1699
19befb2d
LP
1700 s->node_vtable.is_fallback = fallback;
1701 s->node_vtable.vtable = vtable;
1702 s->node_vtable.find = find;
992c052c 1703
19befb2d
LP
1704 s->node_vtable.interface = strdup(interface);
1705 if (!s->node_vtable.interface) {
992c052c
LP
1706 r = -ENOMEM;
1707 goto fail;
1708 }
1709
19befb2d 1710 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
992c052c
LP
1711
1712 switch (v->type) {
1713
1714 case _SD_BUS_VTABLE_METHOD: {
1715 struct vtable_member *m;
1716
77a874a3
LP
1717 if (!member_name_is_valid(v->x.method.member) ||
1718 !signature_is_valid(strempty(v->x.method.signature), false) ||
1719 !signature_is_valid(strempty(v->x.method.result), false) ||
1720 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
df98a87b 1721 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
992c052c
LP
1722 r = -EINVAL;
1723 goto fail;
1724 }
1725
1726 m = new0(struct vtable_member, 1);
1727 if (!m) {
1728 r = -ENOMEM;
1729 goto fail;
1730 }
1731
19befb2d 1732 m->parent = &s->node_vtable;
992c052c 1733 m->path = n->path;
19befb2d 1734 m->interface = s->node_vtable.interface;
77a874a3 1735 m->member = v->x.method.member;
992c052c
LP
1736 m->vtable = v;
1737
1738 r = hashmap_put(bus->vtable_methods, m, m);
1739 if (r < 0) {
1740 free(m);
1741 goto fail;
1742 }
1743
1744 break;
1745 }
1746
1747 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1748
77a874a3 1749 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
992c052c
LP
1750 r = -EINVAL;
1751 goto fail;
1752 }
9b772efb
LP
1753
1754 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1755 r = -EINVAL;
1756 goto fail;
1757 }
992c052c 1758
4831981d 1759 _fallthrough_;
992c052c
LP
1760 case _SD_BUS_VTABLE_PROPERTY: {
1761 struct vtable_member *m;
1762
77a874a3
LP
1763 if (!member_name_is_valid(v->x.property.member) ||
1764 !signature_is_single(v->x.property.signature, false) ||
c13c7de3 1765 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
33702051 1766 (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
df98a87b 1767 (!!(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 1768 ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
adacb957 1769 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
992c052c
LP
1770 r = -EINVAL;
1771 goto fail;
1772 }
1773
992c052c
LP
1774 m = new0(struct vtable_member, 1);
1775 if (!m) {
1776 r = -ENOMEM;
1777 goto fail;
1778 }
1779
19befb2d 1780 m->parent = &s->node_vtable;
992c052c 1781 m->path = n->path;
19befb2d 1782 m->interface = s->node_vtable.interface;
77a874a3 1783 m->member = v->x.property.member;
992c052c
LP
1784 m->vtable = v;
1785
1786 r = hashmap_put(bus->vtable_properties, m, m);
1787 if (r < 0) {
1788 free(m);
1789 goto fail;
1790 }
1791
1792 break;
1793 }
1794
1795 case _SD_BUS_VTABLE_SIGNAL:
1796
77a874a3 1797 if (!member_name_is_valid(v->x.signal.member) ||
adacb957
LP
1798 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1799 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
992c052c
LP
1800 r = -EINVAL;
1801 goto fail;
1802 }
1803
1804 break;
1805
1806 default:
1807 r = -EINVAL;
1808 goto fail;
1809 }
1810 }
1811
19befb2d
LP
1812 s->node_vtable.node = n;
1813 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
68313d3d
LP
1814 bus->nodes_modified = true;
1815
19befb2d
LP
1816 if (slot)
1817 *slot = s;
1818
992c052c
LP
1819 return 0;
1820
1821fail:
19befb2d 1822 sd_bus_slot_unref(s);
dfa92725
LP
1823 bus_node_gc(bus, n);
1824
19befb2d 1825 return r;
992c052c
LP
1826}
1827
d9f644e2 1828_public_ int sd_bus_add_object_vtable(
992c052c 1829 sd_bus *bus,
19befb2d 1830 sd_bus_slot **slot,
992c052c
LP
1831 const char *path,
1832 const char *interface,
1833 const sd_bus_vtable *vtable,
1834 void *userdata) {
1835
19befb2d 1836 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
992c052c
LP
1837}
1838
d9f644e2 1839_public_ int sd_bus_add_fallback_vtable(
992c052c 1840 sd_bus *bus,
19befb2d
LP
1841 sd_bus_slot **slot,
1842 const char *prefix,
718db961
LP
1843 const char *interface,
1844 const sd_bus_vtable *vtable,
1845 sd_bus_object_find_t find,
1846 void *userdata) {
992c052c 1847
19befb2d 1848 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
992c052c
LP
1849}
1850
d9f644e2 1851_public_ int sd_bus_add_node_enumerator(
992c052c 1852 sd_bus *bus,
19befb2d 1853 sd_bus_slot **slot,
992c052c
LP
1854 const char *path,
1855 sd_bus_node_enumerator_t callback,
1856 void *userdata) {
1857
19befb2d 1858 sd_bus_slot *s;
992c052c
LP
1859 struct node *n;
1860 int r;
1861
dfa92725
LP
1862 assert_return(bus, -EINVAL);
1863 assert_return(object_path_is_valid(path), -EINVAL);
1864 assert_return(callback, -EINVAL);
1865 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c
LP
1866
1867 n = bus_node_allocate(bus, path);
1868 if (!n)
1869 return -ENOMEM;
1870
19befb2d
LP
1871 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1872 if (!s) {
992c052c
LP
1873 r = -ENOMEM;
1874 goto fail;
1875 }
1876
19befb2d 1877 s->node_enumerator.callback = callback;
68313d3d 1878
19befb2d
LP
1879 s->node_enumerator.node = n;
1880 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
68313d3d
LP
1881 bus->nodes_modified = true;
1882
19befb2d
LP
1883 if (slot)
1884 *slot = s;
1885
992c052c
LP
1886 return 0;
1887
1888fail:
19befb2d 1889 sd_bus_slot_unref(s);
992c052c
LP
1890 bus_node_gc(bus, n);
1891
19befb2d 1892 return r;
992c052c
LP
1893}
1894
1895static int emit_properties_changed_on_interface(
1896 sd_bus *bus,
1897 const char *prefix,
1898 const char *path,
1899 const char *interface,
1900 bool require_fallback,
46525bfc 1901 bool *found_interface,
992c052c
LP
1902 char **names) {
1903
4afd3348
LP
1904 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1905 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
718db961
LP
1906 bool has_invalidating = false, has_changing = false;
1907 struct vtable_member key = {};
992c052c
LP
1908 struct node_vtable *c;
1909 struct node *n;
1910 char **property;
1911 void *u = NULL;
1912 int r;
1913
1914 assert(bus);
dfa92725 1915 assert(prefix);
992c052c
LP
1916 assert(path);
1917 assert(interface);
46525bfc 1918 assert(found_interface);
992c052c
LP
1919
1920 n = hashmap_get(bus->nodes, prefix);
1921 if (!n)
1922 return 0;
1923
151b9b96 1924 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
992c052c
LP
1925 if (r < 0)
1926 return r;
1927
1928 r = sd_bus_message_append(m, "s", interface);
1929 if (r < 0)
1930 return r;
1931
1932 r = sd_bus_message_open_container(m, 'a', "{sv}");
1933 if (r < 0)
1934 return r;
1935
1936 key.path = prefix;
1937 key.interface = interface;
1938
718db961
LP
1939 LIST_FOREACH(vtables, c, n->vtables) {
1940 if (require_fallback && !c->is_fallback)
1941 continue;
992c052c 1942
718db961 1943 if (!streq(c->interface, interface))
992c052c 1944 continue;
992c052c 1945
f00c3121 1946 r = node_vtable_get_userdata(bus, path, c, &u, &error);
992c052c
LP
1947 if (r < 0)
1948 return r;
718db961
LP
1949 if (bus->nodes_modified)
1950 return 0;
1951 if (r == 0)
1952 continue;
992c052c 1953
46525bfc
LP
1954 *found_interface = true;
1955
a03e4337
LP
1956 if (names) {
1957 /* If the caller specified a list of
1958 * properties we include exactly those in the
1959 * PropertiesChanged message */
992c052c 1960
a03e4337
LP
1961 STRV_FOREACH(property, names) {
1962 struct vtable_member *v;
992c052c 1963
a03e4337 1964 assert_return(member_name_is_valid(*property), -EINVAL);
718db961 1965
a03e4337
LP
1966 key.member = *property;
1967 v = hashmap_get(bus->vtable_properties, &key);
1968 if (!v)
1969 return -ENOENT;
1970
1971 /* If there are two vtables for the same
1972 * interface, let's handle this property when
1973 * we come to that vtable. */
1974 if (c != v->parent)
1975 continue;
992c052c 1976
a03e4337
LP
1977 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1978 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
992c052c 1979
a03e4337
LP
1980 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1981
1982 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1983 has_invalidating = true;
1984 continue;
1985 }
1986
1987 has_changing = true;
1988
1989 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1990 if (r < 0)
1991 return r;
1992 if (bus->nodes_modified)
1993 return 0;
718db961 1994 }
a03e4337
LP
1995 } else {
1996 const sd_bus_vtable *v;
718db961 1997
a03e4337
LP
1998 /* If the caller specified no properties list
1999 * we include all properties that are marked
2000 * as changing in the message. */
718db961 2001
a03e4337 2002 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
945c2931 2003 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
a03e4337 2004 continue;
718db961 2005
a03e4337
LP
2006 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2007 continue;
718db961 2008
a03e4337
LP
2009 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2010 has_invalidating = true;
2011 continue;
2012 }
718db961 2013
a03e4337
LP
2014 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2015 continue;
718db961 2016
a03e4337 2017 has_changing = true;
718db961 2018
a03e4337
LP
2019 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2020 if (r < 0)
2021 return r;
2022 if (bus->nodes_modified)
2023 return 0;
2024 }
718db961 2025 }
992c052c
LP
2026 }
2027
718db961
LP
2028 if (!has_invalidating && !has_changing)
2029 return 0;
2030
992c052c
LP
2031 r = sd_bus_message_close_container(m);
2032 if (r < 0)
2033 return r;
2034
2035 r = sd_bus_message_open_container(m, 'a', "s");
2036 if (r < 0)
2037 return r;
2038
2039 if (has_invalidating) {
718db961
LP
2040 LIST_FOREACH(vtables, c, n->vtables) {
2041 if (require_fallback && !c->is_fallback)
2042 continue;
992c052c 2043
718db961 2044 if (!streq(c->interface, interface))
992c052c
LP
2045 continue;
2046
f00c3121 2047 r = node_vtable_get_userdata(bus, path, c, &u, &error);
992c052c
LP
2048 if (r < 0)
2049 return r;
718db961
LP
2050 if (bus->nodes_modified)
2051 return 0;
2052 if (r == 0)
2053 continue;
2054
a03e4337
LP
2055 if (names) {
2056 STRV_FOREACH(property, names) {
2057 struct vtable_member *v;
718db961 2058
a03e4337
LP
2059 key.member = *property;
2060 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2061 assert(c == v->parent);
718db961 2062
a03e4337
LP
2063 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2064 continue;
718db961 2065
a03e4337
LP
2066 r = sd_bus_message_append(m, "s", *property);
2067 if (r < 0)
2068 return r;
2069 }
2070 } else {
2071 const sd_bus_vtable *v;
2072
2073 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
945c2931 2074 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
a03e4337
LP
2075 continue;
2076
2077 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2078 continue;
2079
2080 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2081 continue;
2082
2083 r = sd_bus_message_append(m, "s", v->x.property.member);
2084 if (r < 0)
2085 return r;
2086 }
718db961 2087 }
992c052c
LP
2088 }
2089 }
2090
2091 r = sd_bus_message_close_container(m);
2092 if (r < 0)
2093 return r;
2094
2095 r = sd_bus_send(bus, m, NULL);
2096 if (r < 0)
2097 return r;
2098
2099 return 1;
2100}
2101
d9f644e2 2102_public_ int sd_bus_emit_properties_changed_strv(
dfa92725
LP
2103 sd_bus *bus,
2104 const char *path,
2105 const char *interface,
2106 char **names) {
2107
8ce2afd6 2108 BUS_DONT_DESTROY(bus);
46525bfc 2109 bool found_interface = false;
92e189e5 2110 char *prefix;
992c052c
LP
2111 int r;
2112
dfa92725
LP
2113 assert_return(bus, -EINVAL);
2114 assert_return(object_path_is_valid(path), -EINVAL);
2115 assert_return(interface_name_is_valid(interface), -EINVAL);
dfa92725
LP
2116 assert_return(!bus_pid_changed(bus), -ECHILD);
2117
a3d59cd1
LP
2118 if (!BUS_IS_OPEN(bus->state))
2119 return -ENOTCONN;
a03e4337
LP
2120
2121 /* A non-NULL but empty names list means nothing needs to be
2122 generated. A NULL list OTOH indicates that all properties
2123 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2124 included in the PropertiesChanged message. */
2125 if (names && names[0] == NULL)
dfa92725 2126 return 0;
992c052c 2127
68313d3d
LP
2128 do {
2129 bus->nodes_modified = false;
992c052c 2130
46525bfc 2131 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
92e189e5
LP
2132 if (r != 0)
2133 return r;
68313d3d
LP
2134 if (bus->nodes_modified)
2135 continue;
2136
2137 prefix = alloca(strlen(path) + 1);
2138 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
46525bfc 2139 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
68313d3d
LP
2140 if (r != 0)
2141 return r;
2142 if (bus->nodes_modified)
2143 break;
2144 }
2145
2146 } while (bus->nodes_modified);
992c052c 2147
46525bfc 2148 return found_interface ? 0 : -ENOENT;
992c052c
LP
2149}
2150
d9f644e2 2151_public_ int sd_bus_emit_properties_changed(
dfa92725
LP
2152 sd_bus *bus,
2153 const char *path,
2154 const char *interface,
2155 const char *name, ...) {
2156
250a918d 2157 char **names;
992c052c 2158
dfa92725
LP
2159 assert_return(bus, -EINVAL);
2160 assert_return(object_path_is_valid(path), -EINVAL);
2161 assert_return(interface_name_is_valid(interface), -EINVAL);
dfa92725
LP
2162 assert_return(!bus_pid_changed(bus), -ECHILD);
2163
a3d59cd1
LP
2164 if (!BUS_IS_OPEN(bus->state))
2165 return -ENOTCONN;
2166
dfa92725
LP
2167 if (!name)
2168 return 0;
2169
250a918d 2170 names = strv_from_stdarg_alloca(name);
992c052c
LP
2171
2172 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2173}
2174
d95eb43e
DH
2175static int object_added_append_all_prefix(
2176 sd_bus *bus,
2177 sd_bus_message *m,
2178 Set *s,
2179 const char *prefix,
2180 const char *path,
2181 bool require_fallback) {
2182
2183 const char *previous_interface = NULL;
2184 struct node_vtable *c;
2185 struct node *n;
2186 int r;
2187
2188 assert(bus);
2189 assert(m);
2190 assert(s);
2191 assert(prefix);
2192 assert(path);
2193
2194 n = hashmap_get(bus->nodes, prefix);
2195 if (!n)
2196 return 0;
2197
2198 LIST_FOREACH(vtables, c, n->vtables) {
4afd3348 2199 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d95eb43e
DH
2200 void *u = NULL;
2201
2202 if (require_fallback && !c->is_fallback)
2203 continue;
2204
2205 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2206 if (r < 0)
2207 return r;
2208 if (bus->nodes_modified)
2209 return 0;
2210 if (r == 0)
2211 continue;
2212
2213 if (!streq_ptr(c->interface, previous_interface)) {
2214 /* If a child-node already handled this interface, we
2215 * skip it on any of its parents. The child vtables
2216 * always fully override any conflicting vtables of
2217 * any parent node. */
2218 if (set_get(s, c->interface))
2219 continue;
2220
2221 r = set_put(s, c->interface);
2222 if (r < 0)
2223 return r;
2224
2225 if (previous_interface) {
2226 r = sd_bus_message_close_container(m);
2227 if (r < 0)
2228 return r;
2229 r = sd_bus_message_close_container(m);
2230 if (r < 0)
2231 return r;
2232 }
2233
2234 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2235 if (r < 0)
2236 return r;
2237 r = sd_bus_message_append(m, "s", c->interface);
2238 if (r < 0)
2239 return r;
2240 r = sd_bus_message_open_container(m, 'a', "{sv}");
2241 if (r < 0)
2242 return r;
2243
2244 previous_interface = c->interface;
2245 }
2246
2247 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2248 if (r < 0)
2249 return r;
2250 if (bus->nodes_modified)
2251 return 0;
2252 }
2253
2254 if (previous_interface) {
2255 r = sd_bus_message_close_container(m);
2256 if (r < 0)
2257 return r;
2258 r = sd_bus_message_close_container(m);
2259 if (r < 0)
2260 return r;
2261 }
2262
2263 return 0;
2264}
2265
2266static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2267 _cleanup_set_free_ Set *s = NULL;
2268 char *prefix;
2269 int r;
2270
2271 assert(bus);
2272 assert(m);
2273 assert(path);
2274
2275 /*
2276 * This appends all interfaces registered on path @path. We first add
2277 * the builtin interfaces, which are always available and handled by
2278 * sd-bus. Then, we add all interfaces registered on the exact node,
2279 * followed by all fallback interfaces registered on any parent prefix.
2280 *
2281 * If an interface is registered multiple times on the same node with
2282 * different vtables, we merge all the properties across all vtables.
2283 * However, if a child node has the same interface registered as one of
2284 * its parent nodes has as fallback, we make the child overwrite the
2285 * parent instead of extending it. Therefore, we keep a "Set" of all
2286 * handled interfaces during parent traversal, so we skip interfaces on
2287 * a parent that were overwritten by a child.
2288 */
2289
2290 s = set_new(&string_hash_ops);
2291 if (!s)
2292 return -ENOMEM;
2293
2294 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2295 if (r < 0)
2296 return r;
2297 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2298 if (r < 0)
2299 return r;
2300 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2301 if (r < 0)
2302 return r;
2303 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2304 if (r < 0)
2305 return r;
2306
2307 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2308 if (r < 0)
2309 return r;
2310 if (bus->nodes_modified)
2311 return 0;
2312
2313 prefix = alloca(strlen(path) + 1);
2314 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2315 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2316 if (r < 0)
2317 return r;
2318 if (bus->nodes_modified)
2319 return 0;
2320 }
2321
2322 return 0;
2323}
2324
969a9685 2325_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
d95eb43e
DH
2326 BUS_DONT_DESTROY(bus);
2327
4afd3348 2328 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2d5c8a27 2329 struct node *object_manager;
d95eb43e
DH
2330 int r;
2331
2332 /*
2333 * This emits an InterfacesAdded signal on the given path, by iterating
2334 * all registered vtables and fallback vtables on the path. All
2335 * properties are queried and included in the signal.
2336 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2337 * explicit list of registered interfaces. However, unlike
2338 * interfaces_added(), this call can figure out the list of supported
2339 * interfaces itself. Furthermore, it properly adds the builtin
2340 * org.freedesktop.DBus.* interfaces.
2341 */
2342
2343 assert_return(bus, -EINVAL);
2344 assert_return(object_path_is_valid(path), -EINVAL);
2345 assert_return(!bus_pid_changed(bus), -ECHILD);
2346
2347 if (!BUS_IS_OPEN(bus->state))
2348 return -ENOTCONN;
2349
2d5c8a27
DH
2350 r = bus_find_parent_object_manager(bus, &object_manager, path);
2351 if (r < 0)
2352 return r;
2353 if (r == 0)
2354 return -ESRCH;
2355
d95eb43e
DH
2356 do {
2357 bus->nodes_modified = false;
2358 m = sd_bus_message_unref(m);
2359
2d5c8a27 2360 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
d95eb43e
DH
2361 if (r < 0)
2362 return r;
2363
2364 r = sd_bus_message_append_basic(m, 'o', path);
2365 if (r < 0)
2366 return r;
2367
2368 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2369 if (r < 0)
2370 return r;
2371
2372 r = object_added_append_all(bus, m, path);
2373 if (r < 0)
2374 return r;
2375
2376 if (bus->nodes_modified)
2377 continue;
2378
2379 r = sd_bus_message_close_container(m);
2380 if (r < 0)
2381 return r;
2382
2383 } while (bus->nodes_modified);
2384
2385 return sd_bus_send(bus, m, NULL);
2386}
2387
2388static int object_removed_append_all_prefix(
2389 sd_bus *bus,
2390 sd_bus_message *m,
2391 Set *s,
2392 const char *prefix,
2393 const char *path,
2394 bool require_fallback) {
2395
2396 const char *previous_interface = NULL;
2397 struct node_vtable *c;
2398 struct node *n;
2399 int r;
2400
2401 assert(bus);
2402 assert(m);
2403 assert(s);
2404 assert(prefix);
2405 assert(path);
2406
2407 n = hashmap_get(bus->nodes, prefix);
2408 if (!n)
2409 return 0;
2410
2411 LIST_FOREACH(vtables, c, n->vtables) {
4afd3348 2412 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d95eb43e
DH
2413 void *u = NULL;
2414
2415 if (require_fallback && !c->is_fallback)
2416 continue;
2417 if (streq_ptr(c->interface, previous_interface))
2418 continue;
2419
2420 /* If a child-node already handled this interface, we
2421 * skip it on any of its parents. The child vtables
2422 * always fully override any conflicting vtables of
2423 * any parent node. */
2424 if (set_get(s, c->interface))
2425 continue;
2426
2427 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2428 if (r < 0)
2429 return r;
2430 if (bus->nodes_modified)
2431 return 0;
2432 if (r == 0)
2433 continue;
2434
2435 r = set_put(s, c->interface);
2436 if (r < 0)
2437 return r;
2438
2439 r = sd_bus_message_append(m, "s", c->interface);
2440 if (r < 0)
2441 return r;
2442
2443 previous_interface = c->interface;
2444 }
2445
2446 return 0;
2447}
2448
2449static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2450 _cleanup_set_free_ Set *s = NULL;
2451 char *prefix;
2452 int r;
2453
2454 assert(bus);
2455 assert(m);
2456 assert(path);
2457
2458 /* see sd_bus_emit_object_added() for details */
2459
2460 s = set_new(&string_hash_ops);
2461 if (!s)
2462 return -ENOMEM;
2463
2464 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2465 if (r < 0)
2466 return r;
2467 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2468 if (r < 0)
2469 return r;
2470 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2471 if (r < 0)
2472 return r;
2473 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2474 if (r < 0)
2475 return r;
2476
2477 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2478 if (r < 0)
2479 return r;
2480 if (bus->nodes_modified)
2481 return 0;
2482
2483 prefix = alloca(strlen(path) + 1);
2484 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2485 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2486 if (r < 0)
2487 return r;
2488 if (bus->nodes_modified)
2489 return 0;
2490 }
2491
2492 return 0;
2493}
2494
969a9685 2495_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
d95eb43e
DH
2496 BUS_DONT_DESTROY(bus);
2497
4afd3348 2498 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2d5c8a27 2499 struct node *object_manager;
d95eb43e
DH
2500 int r;
2501
2502 /*
2503 * This is like sd_bus_emit_object_added(), but emits an
2504 * InterfacesRemoved signal on the given path. This only includes any
2505 * registered interfaces but skips the properties. Note that this will
2506 * call into the find() callbacks of any registered vtable. Therefore,
2507 * you must call this function before destroying/unlinking your object.
2508 * Otherwise, the list of interfaces will be incomplete. However, note
2509 * that this will *NOT* call into any property callback. Therefore, the
2510 * object might be in an "destructed" state, as long as we can find it.
2511 */
2512
2513 assert_return(bus, -EINVAL);
2514 assert_return(object_path_is_valid(path), -EINVAL);
2515 assert_return(!bus_pid_changed(bus), -ECHILD);
2516
2517 if (!BUS_IS_OPEN(bus->state))
2518 return -ENOTCONN;
2519
2d5c8a27
DH
2520 r = bus_find_parent_object_manager(bus, &object_manager, path);
2521 if (r < 0)
2522 return r;
2523 if (r == 0)
2524 return -ESRCH;
2525
d95eb43e
DH
2526 do {
2527 bus->nodes_modified = false;
2528 m = sd_bus_message_unref(m);
2529
2d5c8a27 2530 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
d95eb43e
DH
2531 if (r < 0)
2532 return r;
2533
2534 r = sd_bus_message_append_basic(m, 'o', path);
2535 if (r < 0)
2536 return r;
2537
2538 r = sd_bus_message_open_container(m, 'a', "s");
2539 if (r < 0)
2540 return r;
2541
2542 r = object_removed_append_all(bus, m, path);
2543 if (r < 0)
2544 return r;
2545
2546 if (bus->nodes_modified)
2547 continue;
2548
2549 r = sd_bus_message_close_container(m);
2550 if (r < 0)
2551 return r;
2552
2553 } while (bus->nodes_modified);
2554
2555 return sd_bus_send(bus, m, NULL);
2556}
2557
4be39163
LP
2558static int interfaces_added_append_one_prefix(
2559 sd_bus *bus,
2560 sd_bus_message *m,
2561 const char *prefix,
2562 const char *path,
2563 const char *interface,
2564 bool require_fallback) {
2565
4afd3348 2566 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
718db961 2567 bool found_interface = false;
4be39163
LP
2568 struct node_vtable *c;
2569 struct node *n;
2570 void *u = NULL;
2571 int r;
2572
2573 assert(bus);
2574 assert(m);
2575 assert(prefix);
2576 assert(path);
2577 assert(interface);
2578
2579 n = hashmap_get(bus->nodes, prefix);
2580 if (!n)
2581 return 0;
2582
2583 LIST_FOREACH(vtables, c, n->vtables) {
2584 if (require_fallback && !c->is_fallback)
2585 continue;
2586
718db961
LP
2587 if (!streq(c->interface, interface))
2588 continue;
4be39163 2589
f00c3121 2590 r = node_vtable_get_userdata(bus, path, c, &u, &error);
718db961
LP
2591 if (r < 0)
2592 return r;
2593 if (bus->nodes_modified)
2594 return 0;
2595 if (r == 0)
2596 continue;
4be39163 2597
718db961
LP
2598 if (!found_interface) {
2599 r = sd_bus_message_append_basic(m, 's', interface);
2600 if (r < 0)
2601 return r;
4be39163 2602
718db961
LP
2603 r = sd_bus_message_open_container(m, 'a', "{sv}");
2604 if (r < 0)
2605 return r;
4be39163 2606
718db961
LP
2607 found_interface = true;
2608 }
4be39163 2609
718db961
LP
2610 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2611 if (r < 0)
2612 return r;
2613 if (bus->nodes_modified)
2614 return 0;
2615 }
4be39163 2616
718db961
LP
2617 if (found_interface) {
2618 r = sd_bus_message_close_container(m);
2619 if (r < 0)
2620 return r;
2621 }
4be39163 2622
718db961 2623 return found_interface;
4be39163
LP
2624}
2625
2626static int interfaces_added_append_one(
2627 sd_bus *bus,
2628 sd_bus_message *m,
2629 const char *path,
2630 const char *interface) {
2631
2632 char *prefix;
2633 int r;
2634
2635 assert(bus);
2636 assert(m);
2637 assert(path);
2638 assert(interface);
2639
2640 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2641 if (r != 0)
2642 return r;
68313d3d
LP
2643 if (bus->nodes_modified)
2644 return 0;
4be39163
LP
2645
2646 prefix = alloca(strlen(path) + 1);
2647 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2648 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2649 if (r != 0)
2650 return r;
68313d3d
LP
2651 if (bus->nodes_modified)
2652 return 0;
4be39163
LP
2653 }
2654
2655 return -ENOENT;
992c052c
LP
2656}
2657
d9f644e2 2658_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
8ce2afd6
LP
2659 BUS_DONT_DESTROY(bus);
2660
4afd3348 2661 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2d5c8a27 2662 struct node *object_manager;
4be39163
LP
2663 char **i;
2664 int r;
2665
2666 assert_return(bus, -EINVAL);
2667 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2668 assert_return(!bus_pid_changed(bus), -ECHILD);
2669
a3d59cd1
LP
2670 if (!BUS_IS_OPEN(bus->state))
2671 return -ENOTCONN;
2672
4be39163
LP
2673 if (strv_isempty(interfaces))
2674 return 0;
2675
2d5c8a27
DH
2676 r = bus_find_parent_object_manager(bus, &object_manager, path);
2677 if (r < 0)
2678 return r;
2679 if (r == 0)
2680 return -ESRCH;
2681
68313d3d
LP
2682 do {
2683 bus->nodes_modified = false;
1b02f301 2684 m = sd_bus_message_unref(m);
4be39163 2685
2d5c8a27 2686 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
68313d3d
LP
2687 if (r < 0)
2688 return r;
4be39163 2689
68313d3d 2690 r = sd_bus_message_append_basic(m, 'o', path);
4be39163
LP
2691 if (r < 0)
2692 return r;
2693
68313d3d 2694 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
4be39163
LP
2695 if (r < 0)
2696 return r;
2697
68313d3d
LP
2698 STRV_FOREACH(i, interfaces) {
2699 assert_return(interface_name_is_valid(*i), -EINVAL);
2700
2701 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2702 if (r < 0)
2703 return r;
2704
2705 r = interfaces_added_append_one(bus, m, path, *i);
2706 if (r < 0)
2707 return r;
2708
2709 if (bus->nodes_modified)
2710 break;
2711
2712 r = sd_bus_message_close_container(m);
2713 if (r < 0)
2714 return r;
2715 }
2716
2717 if (bus->nodes_modified)
2718 continue;
2719
4be39163
LP
2720 r = sd_bus_message_close_container(m);
2721 if (r < 0)
2722 return r;
4be39163 2723
68313d3d 2724 } while (bus->nodes_modified);
4be39163
LP
2725
2726 return sd_bus_send(bus, m, NULL);
2727}
2728
d9f644e2 2729_public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
250a918d 2730 char **interfaces;
4be39163
LP
2731
2732 assert_return(bus, -EINVAL);
2733 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2734 assert_return(!bus_pid_changed(bus), -ECHILD);
2735
a3d59cd1
LP
2736 if (!BUS_IS_OPEN(bus->state))
2737 return -ENOTCONN;
2738
250a918d 2739 interfaces = strv_from_stdarg_alloca(interface);
4be39163
LP
2740
2741 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2742}
2743
d9f644e2 2744_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
4afd3348 2745 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2d5c8a27 2746 struct node *object_manager;
4be39163
LP
2747 int r;
2748
2749 assert_return(bus, -EINVAL);
2750 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2751 assert_return(!bus_pid_changed(bus), -ECHILD);
2752
a3d59cd1
LP
2753 if (!BUS_IS_OPEN(bus->state))
2754 return -ENOTCONN;
2755
4be39163
LP
2756 if (strv_isempty(interfaces))
2757 return 0;
2758
2d5c8a27
DH
2759 r = bus_find_parent_object_manager(bus, &object_manager, path);
2760 if (r < 0)
2761 return r;
2762 if (r == 0)
2763 return -ESRCH;
2764
2765 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
4be39163
LP
2766 if (r < 0)
2767 return r;
2768
2769 r = sd_bus_message_append_basic(m, 'o', path);
2770 if (r < 0)
2771 return r;
2772
2773 r = sd_bus_message_append_strv(m, interfaces);
2774 if (r < 0)
2775 return r;
2776
2777 return sd_bus_send(bus, m, NULL);
2778}
2779
d9f644e2 2780_public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
250a918d 2781 char **interfaces;
4be39163
LP
2782
2783 assert_return(bus, -EINVAL);
2784 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2785 assert_return(!bus_pid_changed(bus), -ECHILD);
2786
a3d59cd1
LP
2787 if (!BUS_IS_OPEN(bus->state))
2788 return -ENOTCONN;
2789
250a918d 2790 interfaces = strv_from_stdarg_alloca(interface);
4be39163
LP
2791
2792 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
992c052c
LP
2793}
2794
19befb2d
LP
2795_public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2796 sd_bus_slot *s;
992c052c 2797 struct node *n;
19befb2d 2798 int r;
992c052c 2799
dfa92725
LP
2800 assert_return(bus, -EINVAL);
2801 assert_return(object_path_is_valid(path), -EINVAL);
2802 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c
LP
2803
2804 n = bus_node_allocate(bus, path);
2805 if (!n)
2806 return -ENOMEM;
2807
19befb2d
LP
2808 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2809 if (!s) {
2810 r = -ENOMEM;
2811 goto fail;
2812 }
992c052c 2813
19befb2d
LP
2814 s->node_object_manager.node = n;
2815 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2816 bus->nodes_modified = true;
992c052c 2817
19befb2d
LP
2818 if (slot)
2819 *slot = s;
992c052c 2820
19befb2d 2821 return 0;
992c052c 2822
19befb2d
LP
2823fail:
2824 sd_bus_slot_unref(s);
992c052c 2825 bus_node_gc(bus, n);
dfa92725 2826
19befb2d 2827 return r;
992c052c 2828}