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