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