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