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