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