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