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