]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-objects.c
macro: introduce TAKE_PTR() macro
[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)
0d536673 963 fputs(" </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)
0d536673 976 fputs(" </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
c7db1984 1372 if (bus->is_monitor)
09365592
LP
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;
ae2a15bc 1473 n->path = TAKE_PTR(s);
992c052c 1474
8e050193 1475 r = hashmap_put(bus->nodes, n->path, n);
992c052c 1476 if (r < 0) {
2fd069b1 1477 free(n->path);
5fecf46d 1478 return mfree(n);
992c052c
LP
1479 }
1480
1481 if (parent)
71fda00f 1482 LIST_PREPEND(siblings, parent->child, n);
992c052c
LP
1483
1484 return n;
1485}
1486
19befb2d 1487void bus_node_gc(sd_bus *b, struct node *n) {
992c052c
LP
1488 assert(b);
1489
1490 if (!n)
1491 return;
1492
1493 if (n->child ||
1494 n->callbacks ||
1495 n->vtables ||
1496 n->enumerators ||
19befb2d 1497 n->object_managers)
992c052c
LP
1498 return;
1499
85e55d14 1500 assert_se(hashmap_remove(b->nodes, n->path) == n);
992c052c
LP
1501
1502 if (n->parent)
71fda00f 1503 LIST_REMOVE(siblings, n->parent->child, n);
992c052c
LP
1504
1505 free(n->path);
1506 bus_node_gc(b, n->parent);
1507 free(n);
1508}
1509
2d5c8a27
DH
1510static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) {
1511 struct node *n;
1512
1513 assert(bus);
1514 assert(path);
1515
1516 n = hashmap_get(bus->nodes, path);
1517 if (!n) {
1518 char *prefix;
1519
1520 prefix = alloca(strlen(path) + 1);
1521 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1522 n = hashmap_get(bus->nodes, prefix);
1523 if (n)
1524 break;
1525 }
1526 }
1527
1528 while (n && !n->object_managers)
1529 n = n->parent;
1530
1531 if (out)
1532 *out = n;
1533 return !!n;
1534}
1535
992c052c 1536static int bus_add_object(
dfa92725 1537 sd_bus *bus,
19befb2d 1538 sd_bus_slot **slot,
992c052c
LP
1539 bool fallback,
1540 const char *path,
1541 sd_bus_message_handler_t callback,
1542 void *userdata) {
1543
19befb2d 1544 sd_bus_slot *s;
992c052c
LP
1545 struct node *n;
1546 int r;
1547
dfa92725 1548 assert_return(bus, -EINVAL);
45b1f410 1549 assert_return(bus = bus_resolve(bus), -ENOPKG);
dfa92725
LP
1550 assert_return(object_path_is_valid(path), -EINVAL);
1551 assert_return(callback, -EINVAL);
1552 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c 1553
dfa92725 1554 n = bus_node_allocate(bus, path);
992c052c
LP
1555 if (!n)
1556 return -ENOMEM;
1557
19befb2d
LP
1558 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1559 if (!s) {
992c052c
LP
1560 r = -ENOMEM;
1561 goto fail;
1562 }
1563
19befb2d
LP
1564 s->node_callback.callback = callback;
1565 s->node_callback.is_fallback = fallback;
992c052c 1566
19befb2d
LP
1567 s->node_callback.node = n;
1568 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
68313d3d
LP
1569 bus->nodes_modified = true;
1570
19befb2d
LP
1571 if (slot)
1572 *slot = s;
1573
992c052c
LP
1574 return 0;
1575
1576fail:
19befb2d 1577 sd_bus_slot_unref(s);
dfa92725 1578 bus_node_gc(bus, n);
19befb2d 1579
992c052c
LP
1580 return r;
1581}
1582
19befb2d 1583_public_ int sd_bus_add_object(
992c052c 1584 sd_bus *bus,
19befb2d 1585 sd_bus_slot **slot,
992c052c
LP
1586 const char *path,
1587 sd_bus_message_handler_t callback,
1588 void *userdata) {
1589
19befb2d 1590 return bus_add_object(bus, slot, false, path, callback, userdata);
992c052c
LP
1591}
1592
19befb2d
LP
1593_public_ int sd_bus_add_fallback(
1594 sd_bus *bus,
1595 sd_bus_slot **slot,
1596 const char *prefix,
1597 sd_bus_message_handler_t callback,
1598 void *userdata) {
992c052c 1599
19befb2d 1600 return bus_add_object(bus, slot, true, prefix, callback, userdata);
992c052c
LP
1601}
1602
b826ab58 1603static void vtable_member_hash_func(const void *a, struct siphash *state) {
992c052c
LP
1604 const struct vtable_member *m = a;
1605
dfa92725
LP
1606 assert(m);
1607
b826ab58
TG
1608 string_hash_func(m->path, state);
1609 string_hash_func(m->interface, state);
1610 string_hash_func(m->member, state);
992c052c
LP
1611}
1612
1613static int vtable_member_compare_func(const void *a, const void *b) {
1614 const struct vtable_member *x = a, *y = b;
1615 int r;
1616
dfa92725
LP
1617 assert(x);
1618 assert(y);
1619
992c052c
LP
1620 r = strcmp(x->path, y->path);
1621 if (r != 0)
1622 return r;
1623
1624 r = strcmp(x->interface, y->interface);
1625 if (r != 0)
1626 return r;
1627
1628 return strcmp(x->member, y->member);
1629}
1630
d5099efc
MS
1631static const struct hash_ops vtable_member_hash_ops = {
1632 .hash = vtable_member_hash_func,
1633 .compare = vtable_member_compare_func
1634};
1635
992c052c
LP
1636static int add_object_vtable_internal(
1637 sd_bus *bus,
19befb2d 1638 sd_bus_slot **slot,
992c052c
LP
1639 const char *path,
1640 const char *interface,
1641 const sd_bus_vtable *vtable,
1642 bool fallback,
1643 sd_bus_object_find_t find,
1644 void *userdata) {
1645
2915234d 1646 sd_bus_slot *s = NULL;
19befb2d 1647 struct node_vtable *i, *existing = NULL;
992c052c
LP
1648 const sd_bus_vtable *v;
1649 struct node *n;
1650 int r;
1651
dfa92725 1652 assert_return(bus, -EINVAL);
45b1f410 1653 assert_return(bus = bus_resolve(bus), -ENOPKG);
dfa92725
LP
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 1862 assert_return(bus, -EINVAL);
45b1f410 1863 assert_return(bus = bus_resolve(bus), -ENOPKG);
dfa92725
LP
1864 assert_return(object_path_is_valid(path), -EINVAL);
1865 assert_return(callback, -EINVAL);
1866 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c
LP
1867
1868 n = bus_node_allocate(bus, path);
1869 if (!n)
1870 return -ENOMEM;
1871
19befb2d
LP
1872 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1873 if (!s) {
992c052c
LP
1874 r = -ENOMEM;
1875 goto fail;
1876 }
1877
19befb2d 1878 s->node_enumerator.callback = callback;
68313d3d 1879
19befb2d
LP
1880 s->node_enumerator.node = n;
1881 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
68313d3d
LP
1882 bus->nodes_modified = true;
1883
19befb2d
LP
1884 if (slot)
1885 *slot = s;
1886
992c052c
LP
1887 return 0;
1888
1889fail:
19befb2d 1890 sd_bus_slot_unref(s);
992c052c
LP
1891 bus_node_gc(bus, n);
1892
19befb2d 1893 return r;
992c052c
LP
1894}
1895
1896static int emit_properties_changed_on_interface(
1897 sd_bus *bus,
1898 const char *prefix,
1899 const char *path,
1900 const char *interface,
1901 bool require_fallback,
46525bfc 1902 bool *found_interface,
992c052c
LP
1903 char **names) {
1904
4afd3348
LP
1905 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1906 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
718db961
LP
1907 bool has_invalidating = false, has_changing = false;
1908 struct vtable_member key = {};
992c052c
LP
1909 struct node_vtable *c;
1910 struct node *n;
1911 char **property;
1912 void *u = NULL;
1913 int r;
1914
1915 assert(bus);
dfa92725 1916 assert(prefix);
992c052c
LP
1917 assert(path);
1918 assert(interface);
46525bfc 1919 assert(found_interface);
992c052c
LP
1920
1921 n = hashmap_get(bus->nodes, prefix);
1922 if (!n)
1923 return 0;
1924
151b9b96 1925 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
992c052c
LP
1926 if (r < 0)
1927 return r;
1928
1929 r = sd_bus_message_append(m, "s", interface);
1930 if (r < 0)
1931 return r;
1932
1933 r = sd_bus_message_open_container(m, 'a', "{sv}");
1934 if (r < 0)
1935 return r;
1936
1937 key.path = prefix;
1938 key.interface = interface;
1939
718db961
LP
1940 LIST_FOREACH(vtables, c, n->vtables) {
1941 if (require_fallback && !c->is_fallback)
1942 continue;
992c052c 1943
718db961 1944 if (!streq(c->interface, interface))
992c052c 1945 continue;
992c052c 1946
f00c3121 1947 r = node_vtable_get_userdata(bus, path, c, &u, &error);
992c052c
LP
1948 if (r < 0)
1949 return r;
718db961
LP
1950 if (bus->nodes_modified)
1951 return 0;
1952 if (r == 0)
1953 continue;
992c052c 1954
46525bfc
LP
1955 *found_interface = true;
1956
a03e4337
LP
1957 if (names) {
1958 /* If the caller specified a list of
1959 * properties we include exactly those in the
1960 * PropertiesChanged message */
992c052c 1961
a03e4337
LP
1962 STRV_FOREACH(property, names) {
1963 struct vtable_member *v;
992c052c 1964
a03e4337 1965 assert_return(member_name_is_valid(*property), -EINVAL);
718db961 1966
a03e4337
LP
1967 key.member = *property;
1968 v = hashmap_get(bus->vtable_properties, &key);
1969 if (!v)
1970 return -ENOENT;
1971
1972 /* If there are two vtables for the same
1973 * interface, let's handle this property when
1974 * we come to that vtable. */
1975 if (c != v->parent)
1976 continue;
992c052c 1977
a03e4337
LP
1978 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1979 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
992c052c 1980
a03e4337
LP
1981 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1982
1983 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1984 has_invalidating = true;
1985 continue;
1986 }
1987
1988 has_changing = true;
1989
1990 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1991 if (r < 0)
1992 return r;
1993 if (bus->nodes_modified)
1994 return 0;
718db961 1995 }
a03e4337
LP
1996 } else {
1997 const sd_bus_vtable *v;
718db961 1998
a03e4337
LP
1999 /* If the caller specified no properties list
2000 * we include all properties that are marked
2001 * as changing in the message. */
718db961 2002
a03e4337 2003 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
945c2931 2004 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
a03e4337 2005 continue;
718db961 2006
a03e4337
LP
2007 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2008 continue;
718db961 2009
a03e4337
LP
2010 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
2011 has_invalidating = true;
2012 continue;
2013 }
718db961 2014
a03e4337
LP
2015 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2016 continue;
718db961 2017
a03e4337 2018 has_changing = true;
718db961 2019
a03e4337
LP
2020 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2021 if (r < 0)
2022 return r;
2023 if (bus->nodes_modified)
2024 return 0;
2025 }
718db961 2026 }
992c052c
LP
2027 }
2028
718db961
LP
2029 if (!has_invalidating && !has_changing)
2030 return 0;
2031
992c052c
LP
2032 r = sd_bus_message_close_container(m);
2033 if (r < 0)
2034 return r;
2035
2036 r = sd_bus_message_open_container(m, 'a', "s");
2037 if (r < 0)
2038 return r;
2039
2040 if (has_invalidating) {
718db961
LP
2041 LIST_FOREACH(vtables, c, n->vtables) {
2042 if (require_fallback && !c->is_fallback)
2043 continue;
992c052c 2044
718db961 2045 if (!streq(c->interface, interface))
992c052c
LP
2046 continue;
2047
f00c3121 2048 r = node_vtable_get_userdata(bus, path, c, &u, &error);
992c052c
LP
2049 if (r < 0)
2050 return r;
718db961
LP
2051 if (bus->nodes_modified)
2052 return 0;
2053 if (r == 0)
2054 continue;
2055
a03e4337
LP
2056 if (names) {
2057 STRV_FOREACH(property, names) {
2058 struct vtable_member *v;
718db961 2059
a03e4337
LP
2060 key.member = *property;
2061 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2062 assert(c == v->parent);
718db961 2063
a03e4337
LP
2064 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2065 continue;
718db961 2066
a03e4337
LP
2067 r = sd_bus_message_append(m, "s", *property);
2068 if (r < 0)
2069 return r;
2070 }
2071 } else {
2072 const sd_bus_vtable *v;
2073
2074 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
945c2931 2075 if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY))
a03e4337
LP
2076 continue;
2077
2078 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2079 continue;
2080
2081 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2082 continue;
2083
2084 r = sd_bus_message_append(m, "s", v->x.property.member);
2085 if (r < 0)
2086 return r;
2087 }
718db961 2088 }
992c052c
LP
2089 }
2090 }
2091
2092 r = sd_bus_message_close_container(m);
2093 if (r < 0)
2094 return r;
2095
2096 r = sd_bus_send(bus, m, NULL);
2097 if (r < 0)
2098 return r;
2099
2100 return 1;
2101}
2102
d9f644e2 2103_public_ int sd_bus_emit_properties_changed_strv(
dfa92725
LP
2104 sd_bus *bus,
2105 const char *path,
2106 const char *interface,
2107 char **names) {
2108
8ce2afd6 2109 BUS_DONT_DESTROY(bus);
46525bfc 2110 bool found_interface = false;
92e189e5 2111 char *prefix;
992c052c
LP
2112 int r;
2113
dfa92725 2114 assert_return(bus, -EINVAL);
45b1f410 2115 assert_return(bus = bus_resolve(bus), -ENOPKG);
dfa92725
LP
2116 assert_return(object_path_is_valid(path), -EINVAL);
2117 assert_return(interface_name_is_valid(interface), -EINVAL);
dfa92725
LP
2118 assert_return(!bus_pid_changed(bus), -ECHILD);
2119
a3d59cd1
LP
2120 if (!BUS_IS_OPEN(bus->state))
2121 return -ENOTCONN;
a03e4337
LP
2122
2123 /* A non-NULL but empty names list means nothing needs to be
2124 generated. A NULL list OTOH indicates that all properties
2125 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2126 included in the PropertiesChanged message. */
2127 if (names && names[0] == NULL)
dfa92725 2128 return 0;
992c052c 2129
68313d3d
LP
2130 do {
2131 bus->nodes_modified = false;
992c052c 2132
46525bfc 2133 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
92e189e5
LP
2134 if (r != 0)
2135 return r;
68313d3d
LP
2136 if (bus->nodes_modified)
2137 continue;
2138
2139 prefix = alloca(strlen(path) + 1);
2140 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
46525bfc 2141 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
68313d3d
LP
2142 if (r != 0)
2143 return r;
2144 if (bus->nodes_modified)
2145 break;
2146 }
2147
2148 } while (bus->nodes_modified);
992c052c 2149
46525bfc 2150 return found_interface ? 0 : -ENOENT;
992c052c
LP
2151}
2152
d9f644e2 2153_public_ int sd_bus_emit_properties_changed(
dfa92725
LP
2154 sd_bus *bus,
2155 const char *path,
2156 const char *interface,
2157 const char *name, ...) {
2158
250a918d 2159 char **names;
992c052c 2160
dfa92725 2161 assert_return(bus, -EINVAL);
45b1f410 2162 assert_return(bus = bus_resolve(bus), -ENOPKG);
dfa92725
LP
2163 assert_return(object_path_is_valid(path), -EINVAL);
2164 assert_return(interface_name_is_valid(interface), -EINVAL);
dfa92725
LP
2165 assert_return(!bus_pid_changed(bus), -ECHILD);
2166
a3d59cd1
LP
2167 if (!BUS_IS_OPEN(bus->state))
2168 return -ENOTCONN;
2169
dfa92725
LP
2170 if (!name)
2171 return 0;
2172
250a918d 2173 names = strv_from_stdarg_alloca(name);
992c052c
LP
2174
2175 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2176}
2177
d95eb43e
DH
2178static int object_added_append_all_prefix(
2179 sd_bus *bus,
2180 sd_bus_message *m,
2181 Set *s,
2182 const char *prefix,
2183 const char *path,
2184 bool require_fallback) {
2185
2186 const char *previous_interface = NULL;
2187 struct node_vtable *c;
2188 struct node *n;
2189 int r;
2190
2191 assert(bus);
2192 assert(m);
2193 assert(s);
2194 assert(prefix);
2195 assert(path);
2196
2197 n = hashmap_get(bus->nodes, prefix);
2198 if (!n)
2199 return 0;
2200
2201 LIST_FOREACH(vtables, c, n->vtables) {
4afd3348 2202 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d95eb43e
DH
2203 void *u = NULL;
2204
2205 if (require_fallback && !c->is_fallback)
2206 continue;
2207
2208 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2209 if (r < 0)
2210 return r;
2211 if (bus->nodes_modified)
2212 return 0;
2213 if (r == 0)
2214 continue;
2215
2216 if (!streq_ptr(c->interface, previous_interface)) {
2217 /* If a child-node already handled this interface, we
2218 * skip it on any of its parents. The child vtables
2219 * always fully override any conflicting vtables of
2220 * any parent node. */
2221 if (set_get(s, c->interface))
2222 continue;
2223
2224 r = set_put(s, c->interface);
2225 if (r < 0)
2226 return r;
2227
2228 if (previous_interface) {
2229 r = sd_bus_message_close_container(m);
2230 if (r < 0)
2231 return r;
2232 r = sd_bus_message_close_container(m);
2233 if (r < 0)
2234 return r;
2235 }
2236
2237 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2238 if (r < 0)
2239 return r;
2240 r = sd_bus_message_append(m, "s", c->interface);
2241 if (r < 0)
2242 return r;
2243 r = sd_bus_message_open_container(m, 'a', "{sv}");
2244 if (r < 0)
2245 return r;
2246
2247 previous_interface = c->interface;
2248 }
2249
2250 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2251 if (r < 0)
2252 return r;
2253 if (bus->nodes_modified)
2254 return 0;
2255 }
2256
2257 if (previous_interface) {
2258 r = sd_bus_message_close_container(m);
2259 if (r < 0)
2260 return r;
2261 r = sd_bus_message_close_container(m);
2262 if (r < 0)
2263 return r;
2264 }
2265
2266 return 0;
2267}
2268
2269static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2270 _cleanup_set_free_ Set *s = NULL;
2271 char *prefix;
2272 int r;
2273
2274 assert(bus);
2275 assert(m);
2276 assert(path);
2277
2278 /*
2279 * This appends all interfaces registered on path @path. We first add
2280 * the builtin interfaces, which are always available and handled by
2281 * sd-bus. Then, we add all interfaces registered on the exact node,
2282 * followed by all fallback interfaces registered on any parent prefix.
2283 *
2284 * If an interface is registered multiple times on the same node with
2285 * different vtables, we merge all the properties across all vtables.
2286 * However, if a child node has the same interface registered as one of
2287 * its parent nodes has as fallback, we make the child overwrite the
2288 * parent instead of extending it. Therefore, we keep a "Set" of all
2289 * handled interfaces during parent traversal, so we skip interfaces on
2290 * a parent that were overwritten by a child.
2291 */
2292
2293 s = set_new(&string_hash_ops);
2294 if (!s)
2295 return -ENOMEM;
2296
2297 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2298 if (r < 0)
2299 return r;
2300 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2301 if (r < 0)
2302 return r;
2303 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2304 if (r < 0)
2305 return r;
2306 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2307 if (r < 0)
2308 return r;
2309
2310 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2311 if (r < 0)
2312 return r;
2313 if (bus->nodes_modified)
2314 return 0;
2315
2316 prefix = alloca(strlen(path) + 1);
2317 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2318 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2319 if (r < 0)
2320 return r;
2321 if (bus->nodes_modified)
2322 return 0;
2323 }
2324
2325 return 0;
2326}
2327
969a9685 2328_public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
d95eb43e
DH
2329 BUS_DONT_DESTROY(bus);
2330
4afd3348 2331 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2d5c8a27 2332 struct node *object_manager;
d95eb43e
DH
2333 int r;
2334
2335 /*
2336 * This emits an InterfacesAdded signal on the given path, by iterating
2337 * all registered vtables and fallback vtables on the path. All
2338 * properties are queried and included in the signal.
2339 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2340 * explicit list of registered interfaces. However, unlike
2341 * interfaces_added(), this call can figure out the list of supported
2342 * interfaces itself. Furthermore, it properly adds the builtin
2343 * org.freedesktop.DBus.* interfaces.
2344 */
2345
2346 assert_return(bus, -EINVAL);
45b1f410 2347 assert_return(bus = bus_resolve(bus), -ENOPKG);
d95eb43e
DH
2348 assert_return(object_path_is_valid(path), -EINVAL);
2349 assert_return(!bus_pid_changed(bus), -ECHILD);
2350
2351 if (!BUS_IS_OPEN(bus->state))
2352 return -ENOTCONN;
2353
2d5c8a27
DH
2354 r = bus_find_parent_object_manager(bus, &object_manager, path);
2355 if (r < 0)
2356 return r;
2357 if (r == 0)
2358 return -ESRCH;
2359
d95eb43e
DH
2360 do {
2361 bus->nodes_modified = false;
2362 m = sd_bus_message_unref(m);
2363
2d5c8a27 2364 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
d95eb43e
DH
2365 if (r < 0)
2366 return r;
2367
2368 r = sd_bus_message_append_basic(m, 'o', path);
2369 if (r < 0)
2370 return r;
2371
2372 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2373 if (r < 0)
2374 return r;
2375
2376 r = object_added_append_all(bus, m, path);
2377 if (r < 0)
2378 return r;
2379
2380 if (bus->nodes_modified)
2381 continue;
2382
2383 r = sd_bus_message_close_container(m);
2384 if (r < 0)
2385 return r;
2386
2387 } while (bus->nodes_modified);
2388
2389 return sd_bus_send(bus, m, NULL);
2390}
2391
2392static int object_removed_append_all_prefix(
2393 sd_bus *bus,
2394 sd_bus_message *m,
2395 Set *s,
2396 const char *prefix,
2397 const char *path,
2398 bool require_fallback) {
2399
2400 const char *previous_interface = NULL;
2401 struct node_vtable *c;
2402 struct node *n;
2403 int r;
2404
2405 assert(bus);
2406 assert(m);
2407 assert(s);
2408 assert(prefix);
2409 assert(path);
2410
2411 n = hashmap_get(bus->nodes, prefix);
2412 if (!n)
2413 return 0;
2414
2415 LIST_FOREACH(vtables, c, n->vtables) {
4afd3348 2416 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
d95eb43e
DH
2417 void *u = NULL;
2418
2419 if (require_fallback && !c->is_fallback)
2420 continue;
2421 if (streq_ptr(c->interface, previous_interface))
2422 continue;
2423
2424 /* If a child-node already handled this interface, we
2425 * skip it on any of its parents. The child vtables
2426 * always fully override any conflicting vtables of
2427 * any parent node. */
2428 if (set_get(s, c->interface))
2429 continue;
2430
2431 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2432 if (r < 0)
2433 return r;
2434 if (bus->nodes_modified)
2435 return 0;
2436 if (r == 0)
2437 continue;
2438
2439 r = set_put(s, c->interface);
2440 if (r < 0)
2441 return r;
2442
2443 r = sd_bus_message_append(m, "s", c->interface);
2444 if (r < 0)
2445 return r;
2446
2447 previous_interface = c->interface;
2448 }
2449
2450 return 0;
2451}
2452
2453static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2454 _cleanup_set_free_ Set *s = NULL;
2455 char *prefix;
2456 int r;
2457
2458 assert(bus);
2459 assert(m);
2460 assert(path);
2461
2462 /* see sd_bus_emit_object_added() for details */
2463
2464 s = set_new(&string_hash_ops);
2465 if (!s)
2466 return -ENOMEM;
2467
2468 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2469 if (r < 0)
2470 return r;
2471 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2472 if (r < 0)
2473 return r;
2474 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2475 if (r < 0)
2476 return r;
2477 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2478 if (r < 0)
2479 return r;
2480
2481 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2482 if (r < 0)
2483 return r;
2484 if (bus->nodes_modified)
2485 return 0;
2486
2487 prefix = alloca(strlen(path) + 1);
2488 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2489 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2490 if (r < 0)
2491 return r;
2492 if (bus->nodes_modified)
2493 return 0;
2494 }
2495
2496 return 0;
2497}
2498
969a9685 2499_public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
d95eb43e
DH
2500 BUS_DONT_DESTROY(bus);
2501
4afd3348 2502 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2d5c8a27 2503 struct node *object_manager;
d95eb43e
DH
2504 int r;
2505
2506 /*
2507 * This is like sd_bus_emit_object_added(), but emits an
2508 * InterfacesRemoved signal on the given path. This only includes any
2509 * registered interfaces but skips the properties. Note that this will
2510 * call into the find() callbacks of any registered vtable. Therefore,
2511 * you must call this function before destroying/unlinking your object.
2512 * Otherwise, the list of interfaces will be incomplete. However, note
2513 * that this will *NOT* call into any property callback. Therefore, the
2514 * object might be in an "destructed" state, as long as we can find it.
2515 */
2516
2517 assert_return(bus, -EINVAL);
45b1f410 2518 assert_return(bus = bus_resolve(bus), -ENOPKG);
d95eb43e
DH
2519 assert_return(object_path_is_valid(path), -EINVAL);
2520 assert_return(!bus_pid_changed(bus), -ECHILD);
2521
2522 if (!BUS_IS_OPEN(bus->state))
2523 return -ENOTCONN;
2524
2d5c8a27
DH
2525 r = bus_find_parent_object_manager(bus, &object_manager, path);
2526 if (r < 0)
2527 return r;
2528 if (r == 0)
2529 return -ESRCH;
2530
d95eb43e
DH
2531 do {
2532 bus->nodes_modified = false;
2533 m = sd_bus_message_unref(m);
2534
2d5c8a27 2535 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
d95eb43e
DH
2536 if (r < 0)
2537 return r;
2538
2539 r = sd_bus_message_append_basic(m, 'o', path);
2540 if (r < 0)
2541 return r;
2542
2543 r = sd_bus_message_open_container(m, 'a', "s");
2544 if (r < 0)
2545 return r;
2546
2547 r = object_removed_append_all(bus, m, path);
2548 if (r < 0)
2549 return r;
2550
2551 if (bus->nodes_modified)
2552 continue;
2553
2554 r = sd_bus_message_close_container(m);
2555 if (r < 0)
2556 return r;
2557
2558 } while (bus->nodes_modified);
2559
2560 return sd_bus_send(bus, m, NULL);
2561}
2562
4be39163
LP
2563static int interfaces_added_append_one_prefix(
2564 sd_bus *bus,
2565 sd_bus_message *m,
2566 const char *prefix,
2567 const char *path,
2568 const char *interface,
2569 bool require_fallback) {
2570
4afd3348 2571 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
718db961 2572 bool found_interface = false;
4be39163
LP
2573 struct node_vtable *c;
2574 struct node *n;
2575 void *u = NULL;
2576 int r;
2577
2578 assert(bus);
2579 assert(m);
2580 assert(prefix);
2581 assert(path);
2582 assert(interface);
2583
2584 n = hashmap_get(bus->nodes, prefix);
2585 if (!n)
2586 return 0;
2587
2588 LIST_FOREACH(vtables, c, n->vtables) {
2589 if (require_fallback && !c->is_fallback)
2590 continue;
2591
718db961
LP
2592 if (!streq(c->interface, interface))
2593 continue;
4be39163 2594
f00c3121 2595 r = node_vtable_get_userdata(bus, path, c, &u, &error);
718db961
LP
2596 if (r < 0)
2597 return r;
2598 if (bus->nodes_modified)
2599 return 0;
2600 if (r == 0)
2601 continue;
4be39163 2602
718db961
LP
2603 if (!found_interface) {
2604 r = sd_bus_message_append_basic(m, 's', interface);
2605 if (r < 0)
2606 return r;
4be39163 2607
718db961
LP
2608 r = sd_bus_message_open_container(m, 'a', "{sv}");
2609 if (r < 0)
2610 return r;
4be39163 2611
718db961
LP
2612 found_interface = true;
2613 }
4be39163 2614
718db961
LP
2615 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2616 if (r < 0)
2617 return r;
2618 if (bus->nodes_modified)
2619 return 0;
2620 }
4be39163 2621
718db961
LP
2622 if (found_interface) {
2623 r = sd_bus_message_close_container(m);
2624 if (r < 0)
2625 return r;
2626 }
4be39163 2627
718db961 2628 return found_interface;
4be39163
LP
2629}
2630
2631static int interfaces_added_append_one(
2632 sd_bus *bus,
2633 sd_bus_message *m,
2634 const char *path,
2635 const char *interface) {
2636
2637 char *prefix;
2638 int r;
2639
2640 assert(bus);
2641 assert(m);
2642 assert(path);
2643 assert(interface);
2644
2645 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2646 if (r != 0)
2647 return r;
68313d3d
LP
2648 if (bus->nodes_modified)
2649 return 0;
4be39163
LP
2650
2651 prefix = alloca(strlen(path) + 1);
2652 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2653 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2654 if (r != 0)
2655 return r;
68313d3d
LP
2656 if (bus->nodes_modified)
2657 return 0;
4be39163
LP
2658 }
2659
2660 return -ENOENT;
992c052c
LP
2661}
2662
d9f644e2 2663_public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
8ce2afd6
LP
2664 BUS_DONT_DESTROY(bus);
2665
4afd3348 2666 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2d5c8a27 2667 struct node *object_manager;
4be39163
LP
2668 char **i;
2669 int r;
2670
2671 assert_return(bus, -EINVAL);
45b1f410 2672 assert_return(bus = bus_resolve(bus), -ENOPKG);
4be39163 2673 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2674 assert_return(!bus_pid_changed(bus), -ECHILD);
2675
a3d59cd1
LP
2676 if (!BUS_IS_OPEN(bus->state))
2677 return -ENOTCONN;
2678
4be39163
LP
2679 if (strv_isempty(interfaces))
2680 return 0;
2681
2d5c8a27
DH
2682 r = bus_find_parent_object_manager(bus, &object_manager, path);
2683 if (r < 0)
2684 return r;
2685 if (r == 0)
2686 return -ESRCH;
2687
68313d3d
LP
2688 do {
2689 bus->nodes_modified = false;
1b02f301 2690 m = sd_bus_message_unref(m);
4be39163 2691
2d5c8a27 2692 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
68313d3d
LP
2693 if (r < 0)
2694 return r;
4be39163 2695
68313d3d 2696 r = sd_bus_message_append_basic(m, 'o', path);
4be39163
LP
2697 if (r < 0)
2698 return r;
2699
68313d3d 2700 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
4be39163
LP
2701 if (r < 0)
2702 return r;
2703
68313d3d
LP
2704 STRV_FOREACH(i, interfaces) {
2705 assert_return(interface_name_is_valid(*i), -EINVAL);
2706
2707 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2708 if (r < 0)
2709 return r;
2710
2711 r = interfaces_added_append_one(bus, m, path, *i);
2712 if (r < 0)
2713 return r;
2714
2715 if (bus->nodes_modified)
2716 break;
2717
2718 r = sd_bus_message_close_container(m);
2719 if (r < 0)
2720 return r;
2721 }
2722
2723 if (bus->nodes_modified)
2724 continue;
2725
4be39163
LP
2726 r = sd_bus_message_close_container(m);
2727 if (r < 0)
2728 return r;
4be39163 2729
68313d3d 2730 } while (bus->nodes_modified);
4be39163
LP
2731
2732 return sd_bus_send(bus, m, NULL);
2733}
2734
d9f644e2 2735_public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
250a918d 2736 char **interfaces;
4be39163
LP
2737
2738 assert_return(bus, -EINVAL);
45b1f410 2739 assert_return(bus = bus_resolve(bus), -ENOPKG);
4be39163 2740 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2741 assert_return(!bus_pid_changed(bus), -ECHILD);
2742
a3d59cd1
LP
2743 if (!BUS_IS_OPEN(bus->state))
2744 return -ENOTCONN;
2745
250a918d 2746 interfaces = strv_from_stdarg_alloca(interface);
4be39163
LP
2747
2748 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2749}
2750
d9f644e2 2751_public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
4afd3348 2752 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
2d5c8a27 2753 struct node *object_manager;
4be39163
LP
2754 int r;
2755
2756 assert_return(bus, -EINVAL);
45b1f410 2757 assert_return(bus = bus_resolve(bus), -ENOPKG);
4be39163 2758 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2759 assert_return(!bus_pid_changed(bus), -ECHILD);
2760
a3d59cd1
LP
2761 if (!BUS_IS_OPEN(bus->state))
2762 return -ENOTCONN;
2763
4be39163
LP
2764 if (strv_isempty(interfaces))
2765 return 0;
2766
2d5c8a27
DH
2767 r = bus_find_parent_object_manager(bus, &object_manager, path);
2768 if (r < 0)
2769 return r;
2770 if (r == 0)
2771 return -ESRCH;
2772
2773 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
4be39163
LP
2774 if (r < 0)
2775 return r;
2776
2777 r = sd_bus_message_append_basic(m, 'o', path);
2778 if (r < 0)
2779 return r;
2780
2781 r = sd_bus_message_append_strv(m, interfaces);
2782 if (r < 0)
2783 return r;
2784
2785 return sd_bus_send(bus, m, NULL);
2786}
2787
d9f644e2 2788_public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
250a918d 2789 char **interfaces;
4be39163
LP
2790
2791 assert_return(bus, -EINVAL);
45b1f410 2792 assert_return(bus = bus_resolve(bus), -ENOPKG);
4be39163 2793 assert_return(object_path_is_valid(path), -EINVAL);
4be39163
LP
2794 assert_return(!bus_pid_changed(bus), -ECHILD);
2795
a3d59cd1
LP
2796 if (!BUS_IS_OPEN(bus->state))
2797 return -ENOTCONN;
2798
250a918d 2799 interfaces = strv_from_stdarg_alloca(interface);
4be39163
LP
2800
2801 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
992c052c
LP
2802}
2803
19befb2d
LP
2804_public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2805 sd_bus_slot *s;
992c052c 2806 struct node *n;
19befb2d 2807 int r;
992c052c 2808
dfa92725 2809 assert_return(bus, -EINVAL);
45b1f410 2810 assert_return(bus = bus_resolve(bus), -ENOPKG);
dfa92725
LP
2811 assert_return(object_path_is_valid(path), -EINVAL);
2812 assert_return(!bus_pid_changed(bus), -ECHILD);
992c052c
LP
2813
2814 n = bus_node_allocate(bus, path);
2815 if (!n)
2816 return -ENOMEM;
2817
19befb2d
LP
2818 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2819 if (!s) {
2820 r = -ENOMEM;
2821 goto fail;
2822 }
992c052c 2823
19befb2d
LP
2824 s->node_object_manager.node = n;
2825 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2826 bus->nodes_modified = true;
992c052c 2827
19befb2d
LP
2828 if (slot)
2829 *slot = s;
992c052c 2830
19befb2d 2831 return 0;
992c052c 2832
19befb2d
LP
2833fail:
2834 sd_bus_slot_unref(s);
992c052c 2835 bus_node_gc(bus, n);
dfa92725 2836
19befb2d 2837 return r;
992c052c 2838}