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