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