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