]> git.proxmox.com Git - systemd.git/blob - src/libsystemd/sd-bus/bus-objects.c
Imported Upstream version 217
[systemd.git] / src / libsystemd / sd-bus / bus-objects.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/capability.h>
23
24 #include "strv.h"
25 #include "set.h"
26 #include "bus-internal.h"
27 #include "bus-message.h"
28 #include "bus-type.h"
29 #include "bus-signature.h"
30 #include "bus-introspect.h"
31 #include "bus-util.h"
32 #include "bus-slot.h"
33 #include "bus-objects.h"
34
35 static int node_vtable_get_userdata(
36 sd_bus *bus,
37 const char *path,
38 struct node_vtable *c,
39 void **userdata,
40 sd_bus_error *error) {
41
42 sd_bus_slot *s;
43 void *u;
44 int r;
45
46 assert(bus);
47 assert(path);
48 assert(c);
49
50 s = container_of(c, sd_bus_slot, node_vtable);
51 u = s->userdata;
52 if (c->find) {
53 bus->current_slot = sd_bus_slot_ref(s);
54 bus->current_userdata = u;
55 r = c->find(bus, path, c->interface, u, &u, error);
56 bus->current_userdata = NULL;
57 bus->current_slot = sd_bus_slot_unref(s);
58
59 if (r < 0)
60 return r;
61 if (sd_bus_error_is_set(error))
62 return -sd_bus_error_get_errno(error);
63 if (r == 0)
64 return r;
65 }
66
67 if (userdata)
68 *userdata = u;
69
70 return 1;
71 }
72
73 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
74 assert(p);
75
76 return (uint8_t*) u + p->x.property.offset;
77 }
78
79 static int vtable_property_get_userdata(
80 sd_bus *bus,
81 const char *path,
82 struct vtable_member *p,
83 void **userdata,
84 sd_bus_error *error) {
85
86 void *u;
87 int r;
88
89 assert(bus);
90 assert(path);
91 assert(p);
92 assert(userdata);
93
94 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
95 if (r <= 0)
96 return r;
97 if (bus->nodes_modified)
98 return 0;
99
100 *userdata = vtable_property_convert_userdata(p->vtable, u);
101 return 1;
102 }
103
104 static int add_enumerated_to_set(
105 sd_bus *bus,
106 const char *prefix,
107 struct node_enumerator *first,
108 Set *s,
109 sd_bus_error *error) {
110
111 struct node_enumerator *c;
112 int r;
113
114 assert(bus);
115 assert(prefix);
116 assert(s);
117
118 LIST_FOREACH(enumerators, c, first) {
119 char **children = NULL, **k;
120 sd_bus_slot *slot;
121
122 if (bus->nodes_modified)
123 return 0;
124
125 slot = container_of(c, sd_bus_slot, node_enumerator);
126
127 bus->current_slot = sd_bus_slot_ref(slot);
128 bus->current_userdata = slot->userdata;
129 r = c->callback(bus, prefix, slot->userdata, &children, error);
130 bus->current_userdata = NULL;
131 bus->current_slot = sd_bus_slot_unref(slot);
132
133 if (r < 0)
134 return r;
135 if (sd_bus_error_is_set(error))
136 return -sd_bus_error_get_errno(error);
137
138 STRV_FOREACH(k, children) {
139 if (r < 0) {
140 free(*k);
141 continue;
142 }
143
144 if (!object_path_is_valid(*k)){
145 free(*k);
146 r = -EINVAL;
147 continue;
148 }
149
150 if (!object_path_startswith(*k, prefix)) {
151 free(*k);
152 continue;
153 }
154
155 r = set_consume(s, *k);
156 if (r == -EEXIST)
157 r = 0;
158 }
159
160 free(children);
161 if (r < 0)
162 return r;
163 }
164
165 return 0;
166 }
167
168 static int add_subtree_to_set(
169 sd_bus *bus,
170 const char *prefix,
171 struct node *n,
172 Set *s,
173 sd_bus_error *error) {
174
175 struct node *i;
176 int r;
177
178 assert(bus);
179 assert(prefix);
180 assert(n);
181 assert(s);
182
183 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
184 if (r < 0)
185 return r;
186 if (bus->nodes_modified)
187 return 0;
188
189 LIST_FOREACH(siblings, i, n->child) {
190 char *t;
191
192 if (!object_path_startswith(i->path, prefix))
193 continue;
194
195 t = strdup(i->path);
196 if (!t)
197 return -ENOMEM;
198
199 r = set_consume(s, t);
200 if (r < 0 && r != -EEXIST)
201 return r;
202
203 r = add_subtree_to_set(bus, prefix, i, s, error);
204 if (r < 0)
205 return r;
206 if (bus->nodes_modified)
207 return 0;
208 }
209
210 return 0;
211 }
212
213 static int get_child_nodes(
214 sd_bus *bus,
215 const char *prefix,
216 struct node *n,
217 Set **_s,
218 sd_bus_error *error) {
219
220 Set *s = NULL;
221 int r;
222
223 assert(bus);
224 assert(prefix);
225 assert(n);
226 assert(_s);
227
228 s = set_new(&string_hash_ops);
229 if (!s)
230 return -ENOMEM;
231
232 r = add_subtree_to_set(bus, prefix, n, s, error);
233 if (r < 0) {
234 set_free_free(s);
235 return r;
236 }
237
238 *_s = s;
239 return 0;
240 }
241
242 static int node_callbacks_run(
243 sd_bus *bus,
244 sd_bus_message *m,
245 struct node_callback *first,
246 bool require_fallback,
247 bool *found_object) {
248
249 struct node_callback *c;
250 int r;
251
252 assert(bus);
253 assert(m);
254 assert(found_object);
255
256 LIST_FOREACH(callbacks, c, first) {
257 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
258 sd_bus_slot *slot;
259
260 if (bus->nodes_modified)
261 return 0;
262
263 if (require_fallback && !c->is_fallback)
264 continue;
265
266 *found_object = true;
267
268 if (c->last_iteration == bus->iteration_counter)
269 continue;
270
271 c->last_iteration = bus->iteration_counter;
272
273 r = sd_bus_message_rewind(m, true);
274 if (r < 0)
275 return r;
276
277 slot = container_of(c, sd_bus_slot, node_callback);
278
279 bus->current_slot = sd_bus_slot_ref(slot);
280 bus->current_handler = c->callback;
281 bus->current_userdata = slot->userdata;
282 r = c->callback(bus, m, slot->userdata, &error_buffer);
283 bus->current_userdata = NULL;
284 bus->current_handler = NULL;
285 bus->current_slot = sd_bus_slot_unref(slot);
286
287 r = bus_maybe_reply_error(m, r, &error_buffer);
288 if (r != 0)
289 return r;
290 }
291
292 return 0;
293 }
294
295 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
296
297 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
298 uint64_t cap;
299 int r;
300
301 assert(bus);
302 assert(m);
303 assert(c);
304
305 /* If the entire bus is trusted let's grant access */
306 if (bus->trusted)
307 return 0;
308
309 /* If the member is marked UNPRIVILEGED let's grant access */
310 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
311 return 0;
312
313 /* Check have the caller has the requested capability
314 * set. Note that the flags value contains the capability
315 * number plus one, which we need to subtract here. We do this
316 * so that we have 0 as special value for "default
317 * capability". */
318 cap = CAPABILITY_SHIFT(c->vtable->flags);
319 if (cap == 0)
320 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
321 if (cap == 0)
322 cap = CAP_SYS_ADMIN;
323 else
324 cap --;
325
326 r = sd_bus_query_sender_privilege(m, cap);
327 if (r < 0)
328 return r;
329 if (r > 0)
330 return 0;
331
332 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
333 }
334
335 static int method_callbacks_run(
336 sd_bus *bus,
337 sd_bus_message *m,
338 struct vtable_member *c,
339 bool require_fallback,
340 bool *found_object) {
341
342 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
343 const char *signature;
344 void *u;
345 int r;
346
347 assert(bus);
348 assert(m);
349 assert(c);
350 assert(found_object);
351
352 if (require_fallback && !c->parent->is_fallback)
353 return 0;
354
355 r = check_access(bus, m, c, &error);
356 if (r < 0)
357 return bus_maybe_reply_error(m, r, &error);
358
359 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
360 if (r <= 0)
361 return bus_maybe_reply_error(m, r, &error);
362 if (bus->nodes_modified)
363 return 0;
364
365 *found_object = true;
366
367 if (c->last_iteration == bus->iteration_counter)
368 return 0;
369
370 c->last_iteration = bus->iteration_counter;
371
372 r = sd_bus_message_rewind(m, true);
373 if (r < 0)
374 return r;
375
376 signature = sd_bus_message_get_signature(m, true);
377 if (!signature)
378 return -EINVAL;
379
380 if (!streq(strempty(c->vtable->x.method.signature), signature))
381 return sd_bus_reply_method_errorf(
382 m,
383 SD_BUS_ERROR_INVALID_ARGS,
384 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
385 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
386
387 /* Keep track what the signature of the reply to this message
388 * should be, so that this can be enforced when sealing the
389 * reply. */
390 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
391
392 if (c->vtable->x.method.handler) {
393 sd_bus_slot *slot;
394
395 slot = container_of(c->parent, sd_bus_slot, node_vtable);
396
397 bus->current_slot = sd_bus_slot_ref(slot);
398 bus->current_handler = c->vtable->x.method.handler;
399 bus->current_userdata = u;
400 r = c->vtable->x.method.handler(bus, m, u, &error);
401 bus->current_userdata = NULL;
402 bus->current_handler = NULL;
403 bus->current_slot = sd_bus_slot_unref(slot);
404
405 return bus_maybe_reply_error(m, r, &error);
406 }
407
408 /* If the method callback is NULL, make this a successful NOP */
409 r = sd_bus_reply_method_return(m, NULL);
410 if (r < 0)
411 return r;
412
413 return 1;
414 }
415
416 static int invoke_property_get(
417 sd_bus *bus,
418 sd_bus_slot *slot,
419 const sd_bus_vtable *v,
420 const char *path,
421 const char *interface,
422 const char *property,
423 sd_bus_message *reply,
424 void *userdata,
425 sd_bus_error *error) {
426
427 const void *p;
428 int r;
429
430 assert(bus);
431 assert(slot);
432 assert(v);
433 assert(path);
434 assert(interface);
435 assert(property);
436 assert(reply);
437
438 if (v->x.property.get) {
439
440 bus->current_slot = sd_bus_slot_ref(slot);
441 bus->current_userdata = userdata;
442 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
443 bus->current_userdata = NULL;
444 bus->current_slot = sd_bus_slot_unref(slot);
445
446 if (r < 0)
447 return r;
448 if (sd_bus_error_is_set(error))
449 return -sd_bus_error_get_errno(error);
450 return r;
451 }
452
453 /* Automatic handling if no callback is defined. */
454
455 if (streq(v->x.property.signature, "as"))
456 return sd_bus_message_append_strv(reply, *(char***) userdata);
457
458 assert(signature_is_single(v->x.property.signature, false));
459 assert(bus_type_is_basic(v->x.property.signature[0]));
460
461 switch (v->x.property.signature[0]) {
462
463 case SD_BUS_TYPE_STRING:
464 case SD_BUS_TYPE_SIGNATURE:
465 p = strempty(*(char**) userdata);
466 break;
467
468 case SD_BUS_TYPE_OBJECT_PATH:
469 p = *(char**) userdata;
470 assert(p);
471 break;
472
473 default:
474 p = userdata;
475 break;
476 }
477
478 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
479 }
480
481 static int invoke_property_set(
482 sd_bus *bus,
483 sd_bus_slot *slot,
484 const sd_bus_vtable *v,
485 const char *path,
486 const char *interface,
487 const char *property,
488 sd_bus_message *value,
489 void *userdata,
490 sd_bus_error *error) {
491
492 int r;
493
494 assert(bus);
495 assert(slot);
496 assert(v);
497 assert(path);
498 assert(interface);
499 assert(property);
500 assert(value);
501
502 if (v->x.property.set) {
503
504 bus->current_slot = sd_bus_slot_ref(slot);
505 bus->current_userdata = userdata;
506 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
507 bus->current_userdata = NULL;
508 bus->current_slot = sd_bus_slot_unref(slot);
509
510 if (r < 0)
511 return r;
512 if (sd_bus_error_is_set(error))
513 return -sd_bus_error_get_errno(error);
514 return r;
515 }
516
517 /* Automatic handling if no callback is defined. */
518
519 assert(signature_is_single(v->x.property.signature, false));
520 assert(bus_type_is_basic(v->x.property.signature[0]));
521
522 switch (v->x.property.signature[0]) {
523
524 case SD_BUS_TYPE_STRING:
525 case SD_BUS_TYPE_OBJECT_PATH:
526 case SD_BUS_TYPE_SIGNATURE: {
527 const char *p;
528 char *n;
529
530 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
531 if (r < 0)
532 return r;
533
534 n = strdup(p);
535 if (!n)
536 return -ENOMEM;
537
538 free(*(char**) userdata);
539 *(char**) userdata = n;
540
541 break;
542 }
543
544 default:
545 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
546 if (r < 0)
547 return r;
548
549 break;
550 }
551
552 return 1;
553 }
554
555 static int property_get_set_callbacks_run(
556 sd_bus *bus,
557 sd_bus_message *m,
558 struct vtable_member *c,
559 bool require_fallback,
560 bool is_get,
561 bool *found_object) {
562
563 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
564 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
565 sd_bus_slot *slot;
566 void *u = NULL;
567 int r;
568
569 assert(bus);
570 assert(m);
571 assert(c);
572 assert(found_object);
573
574 if (require_fallback && !c->parent->is_fallback)
575 return 0;
576
577 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
578 if (r <= 0)
579 return bus_maybe_reply_error(m, r, &error);
580 if (bus->nodes_modified)
581 return 0;
582
583 slot = container_of(c->parent, sd_bus_slot, node_vtable);
584
585 *found_object = true;
586
587 r = sd_bus_message_new_method_return(m, &reply);
588 if (r < 0)
589 return r;
590
591 if (is_get) {
592 /* Note that we do not protect against reexecution
593 * here (using the last_iteration check, see below),
594 * should the node tree have changed and we got called
595 * again. We assume that property Get() calls are
596 * ultimately without side-effects or if they aren't
597 * then at least idempotent. */
598
599 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
600 if (r < 0)
601 return r;
602
603 /* Note that we do not do an access check here. Read
604 * access to properties is always unrestricted, since
605 * PropertiesChanged signals broadcast contents
606 * anyway. */
607
608 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
609 if (r < 0)
610 return bus_maybe_reply_error(m, r, &error);
611
612 if (bus->nodes_modified)
613 return 0;
614
615 r = sd_bus_message_close_container(reply);
616 if (r < 0)
617 return r;
618
619 } else {
620 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
621 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
622
623 /* Avoid that we call the set routine more than once
624 * if the processing of this message got restarted
625 * because the node tree changed. */
626 if (c->last_iteration == bus->iteration_counter)
627 return 0;
628
629 c->last_iteration = bus->iteration_counter;
630
631 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
632 if (r < 0)
633 return r;
634
635 r = check_access(bus, m, c, &error);
636 if (r < 0)
637 return bus_maybe_reply_error(m, r, &error);
638
639 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
640 if (r < 0)
641 return bus_maybe_reply_error(m, r, &error);
642
643 if (bus->nodes_modified)
644 return 0;
645
646 r = sd_bus_message_exit_container(m);
647 if (r < 0)
648 return r;
649 }
650
651 r = sd_bus_send(bus, reply, NULL);
652 if (r < 0)
653 return r;
654
655 return 1;
656 }
657
658 static int vtable_append_one_property(
659 sd_bus *bus,
660 sd_bus_message *reply,
661 const char *path,
662 struct node_vtable *c,
663 const sd_bus_vtable *v,
664 void *userdata,
665 sd_bus_error *error) {
666
667 sd_bus_slot *slot;
668 int r;
669
670 assert(bus);
671 assert(reply);
672 assert(path);
673 assert(c);
674 assert(v);
675
676 r = sd_bus_message_open_container(reply, 'e', "sv");
677 if (r < 0)
678 return r;
679
680 r = sd_bus_message_append(reply, "s", v->x.property.member);
681 if (r < 0)
682 return r;
683
684 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
685 if (r < 0)
686 return r;
687
688 slot = container_of(c, sd_bus_slot, node_vtable);
689
690 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
691 if (r < 0)
692 return r;
693 if (bus->nodes_modified)
694 return 0;
695
696 r = sd_bus_message_close_container(reply);
697 if (r < 0)
698 return r;
699
700 r = sd_bus_message_close_container(reply);
701 if (r < 0)
702 return r;
703
704 return 0;
705 }
706
707 static int vtable_append_all_properties(
708 sd_bus *bus,
709 sd_bus_message *reply,
710 const char *path,
711 struct node_vtable *c,
712 void *userdata,
713 sd_bus_error *error) {
714
715 const sd_bus_vtable *v;
716 int r;
717
718 assert(bus);
719 assert(reply);
720 assert(path);
721 assert(c);
722
723 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
724 return 1;
725
726 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
727 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
728 continue;
729
730 if (v->flags & SD_BUS_VTABLE_HIDDEN)
731 continue;
732
733 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
734 if (r < 0)
735 return r;
736 if (bus->nodes_modified)
737 return 0;
738 }
739
740 return 1;
741 }
742
743 static int property_get_all_callbacks_run(
744 sd_bus *bus,
745 sd_bus_message *m,
746 struct node_vtable *first,
747 bool require_fallback,
748 const char *iface,
749 bool *found_object) {
750
751 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
752 struct node_vtable *c;
753 bool found_interface;
754 int r;
755
756 assert(bus);
757 assert(m);
758 assert(found_object);
759
760 r = sd_bus_message_new_method_return(m, &reply);
761 if (r < 0)
762 return r;
763
764 r = sd_bus_message_open_container(reply, 'a', "{sv}");
765 if (r < 0)
766 return r;
767
768 found_interface = !iface ||
769 streq(iface, "org.freedesktop.DBus.Properties") ||
770 streq(iface, "org.freedesktop.DBus.Peer") ||
771 streq(iface, "org.freedesktop.DBus.Introspectable");
772
773 LIST_FOREACH(vtables, c, first) {
774 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
775 void *u;
776
777 if (require_fallback && !c->is_fallback)
778 continue;
779
780 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
781 if (r < 0)
782 return bus_maybe_reply_error(m, r, &error);
783 if (bus->nodes_modified)
784 return 0;
785 if (r == 0)
786 continue;
787
788 *found_object = true;
789
790 if (iface && !streq(c->interface, iface))
791 continue;
792 found_interface = true;
793
794 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
795 if (r < 0)
796 return bus_maybe_reply_error(m, r, &error);
797 if (bus->nodes_modified)
798 return 0;
799 }
800
801 if (!found_interface) {
802 r = sd_bus_reply_method_errorf(
803 m,
804 SD_BUS_ERROR_UNKNOWN_INTERFACE,
805 "Unknown interface '%s'.", iface);
806 if (r < 0)
807 return r;
808
809 return 1;
810 }
811
812 r = sd_bus_message_close_container(reply);
813 if (r < 0)
814 return r;
815
816 r = sd_bus_send(bus, reply, NULL);
817 if (r < 0)
818 return r;
819
820 return 1;
821 }
822
823 static int bus_node_exists(
824 sd_bus *bus,
825 struct node *n,
826 const char *path,
827 bool require_fallback) {
828
829 struct node_vtable *c;
830 struct node_callback *k;
831 int r;
832
833 assert(bus);
834 assert(n);
835 assert(path);
836
837 /* Tests if there's anything attached directly to this node
838 * for the specified path */
839
840 if (!require_fallback && (n->enumerators || n->object_managers))
841 return true;
842
843 LIST_FOREACH(callbacks, k, n->callbacks) {
844 if (require_fallback && !k->is_fallback)
845 continue;
846
847 return 1;
848 }
849
850 LIST_FOREACH(vtables, c, n->vtables) {
851 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
852
853 if (require_fallback && !c->is_fallback)
854 continue;
855
856 r = node_vtable_get_userdata(bus, path, c, NULL, &error);
857 if (r != 0)
858 return r;
859 if (bus->nodes_modified)
860 return 0;
861 }
862
863 return 0;
864 }
865
866 static int process_introspect(
867 sd_bus *bus,
868 sd_bus_message *m,
869 struct node *n,
870 bool require_fallback,
871 bool *found_object) {
872
873 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
874 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
875 _cleanup_set_free_free_ Set *s = NULL;
876 const char *previous_interface = NULL;
877 struct introspect intro;
878 struct node_vtable *c;
879 bool empty;
880 int r;
881
882 assert(bus);
883 assert(m);
884 assert(n);
885 assert(found_object);
886
887 r = get_child_nodes(bus, m->path, n, &s, &error);
888 if (r < 0)
889 return bus_maybe_reply_error(m, r, &error);
890 if (bus->nodes_modified)
891 return 0;
892
893 r = introspect_begin(&intro, bus->trusted);
894 if (r < 0)
895 return r;
896
897 r = introspect_write_default_interfaces(&intro, !require_fallback && n->object_managers);
898 if (r < 0)
899 return r;
900
901 empty = set_isempty(s);
902
903 LIST_FOREACH(vtables, c, n->vtables) {
904 if (require_fallback && !c->is_fallback)
905 continue;
906
907 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
908 if (r < 0) {
909 r = bus_maybe_reply_error(m, r, &error);
910 goto finish;
911 }
912 if (bus->nodes_modified) {
913 r = 0;
914 goto finish;
915 }
916 if (r == 0)
917 continue;
918
919 empty = false;
920
921 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
922 continue;
923
924 if (!streq_ptr(previous_interface, c->interface)) {
925
926 if (previous_interface)
927 fputs(" </interface>\n", intro.f);
928
929 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
930 }
931
932 r = introspect_write_interface(&intro, c->vtable);
933 if (r < 0)
934 goto finish;
935
936 previous_interface = c->interface;
937 }
938
939 if (previous_interface)
940 fputs(" </interface>\n", intro.f);
941
942 if (empty) {
943 /* Nothing?, let's see if we exist at all, and if not
944 * refuse to do anything */
945 r = bus_node_exists(bus, n, m->path, require_fallback);
946 if (r <= 0)
947 goto finish;
948 if (bus->nodes_modified) {
949 r = 0;
950 goto finish;
951 }
952 }
953
954 *found_object = true;
955
956 r = introspect_write_child_nodes(&intro, s, m->path);
957 if (r < 0)
958 goto finish;
959
960 r = introspect_finish(&intro, bus, m, &reply);
961 if (r < 0)
962 goto finish;
963
964 r = sd_bus_send(bus, reply, NULL);
965 if (r < 0)
966 goto finish;
967
968 r = 1;
969
970 finish:
971 introspect_free(&intro);
972 return r;
973 }
974
975 static int object_manager_serialize_path(
976 sd_bus *bus,
977 sd_bus_message *reply,
978 const char *prefix,
979 const char *path,
980 bool require_fallback,
981 sd_bus_error *error) {
982
983 const char *previous_interface = NULL;
984 bool found_something = false;
985 struct node_vtable *i;
986 struct node *n;
987 int r;
988
989 assert(bus);
990 assert(reply);
991 assert(prefix);
992 assert(path);
993 assert(error);
994
995 n = hashmap_get(bus->nodes, prefix);
996 if (!n)
997 return 0;
998
999 LIST_FOREACH(vtables, i, n->vtables) {
1000 void *u;
1001
1002 if (require_fallback && !i->is_fallback)
1003 continue;
1004
1005 r = node_vtable_get_userdata(bus, path, i, &u, error);
1006 if (r < 0)
1007 return r;
1008 if (bus->nodes_modified)
1009 return 0;
1010 if (r == 0)
1011 continue;
1012
1013 if (!found_something) {
1014
1015 /* Open the object part */
1016
1017 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1018 if (r < 0)
1019 return r;
1020
1021 r = sd_bus_message_append(reply, "o", path);
1022 if (r < 0)
1023 return r;
1024
1025 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1026 if (r < 0)
1027 return r;
1028
1029 found_something = true;
1030 }
1031
1032 if (!streq_ptr(previous_interface, i->interface)) {
1033
1034 /* Maybe close the previous interface part */
1035
1036 if (previous_interface) {
1037 r = sd_bus_message_close_container(reply);
1038 if (r < 0)
1039 return r;
1040
1041 r = sd_bus_message_close_container(reply);
1042 if (r < 0)
1043 return r;
1044 }
1045
1046 /* Open the new interface part */
1047
1048 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1049 if (r < 0)
1050 return r;
1051
1052 r = sd_bus_message_append(reply, "s", i->interface);
1053 if (r < 0)
1054 return r;
1055
1056 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1057 if (r < 0)
1058 return r;
1059 }
1060
1061 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1062 if (r < 0)
1063 return r;
1064 if (bus->nodes_modified)
1065 return 0;
1066
1067 previous_interface = i->interface;
1068 }
1069
1070 if (previous_interface) {
1071 r = sd_bus_message_close_container(reply);
1072 if (r < 0)
1073 return r;
1074
1075 r = sd_bus_message_close_container(reply);
1076 if (r < 0)
1077 return r;
1078 }
1079
1080 if (found_something) {
1081 r = sd_bus_message_close_container(reply);
1082 if (r < 0)
1083 return r;
1084
1085 r = sd_bus_message_close_container(reply);
1086 if (r < 0)
1087 return r;
1088 }
1089
1090 return 1;
1091 }
1092
1093 static int object_manager_serialize_path_and_fallbacks(
1094 sd_bus *bus,
1095 sd_bus_message *reply,
1096 const char *path,
1097 sd_bus_error *error) {
1098
1099 char *prefix;
1100 int r;
1101
1102 assert(bus);
1103 assert(reply);
1104 assert(path);
1105 assert(error);
1106
1107 /* First, add all vtables registered for this path */
1108 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1109 if (r < 0)
1110 return r;
1111 if (bus->nodes_modified)
1112 return 0;
1113
1114 /* Second, add fallback vtables registered for any of the prefixes */
1115 prefix = alloca(strlen(path) + 1);
1116 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1117 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1118 if (r < 0)
1119 return r;
1120 if (bus->nodes_modified)
1121 return 0;
1122 }
1123
1124 return 0;
1125 }
1126
1127 static int process_get_managed_objects(
1128 sd_bus *bus,
1129 sd_bus_message *m,
1130 struct node *n,
1131 bool require_fallback,
1132 bool *found_object) {
1133
1134 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1135 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1136 _cleanup_set_free_free_ Set *s = NULL;
1137 Iterator i;
1138 char *path;
1139 int r;
1140
1141 assert(bus);
1142 assert(m);
1143 assert(n);
1144 assert(found_object);
1145
1146 /* Spec says, GetManagedObjects() is only implemented on the root of a
1147 * sub-tree. Therefore, we require a registered object-manager on
1148 * exactly the queried path, otherwise, we refuse to respond. */
1149
1150 if (require_fallback || !n->object_managers)
1151 return 0;
1152
1153 r = get_child_nodes(bus, m->path, n, &s, &error);
1154 if (r < 0)
1155 return r;
1156 if (bus->nodes_modified)
1157 return 0;
1158
1159 r = sd_bus_message_new_method_return(m, &reply);
1160 if (r < 0)
1161 return r;
1162
1163 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1164 if (r < 0)
1165 return r;
1166
1167 SET_FOREACH(path, s, i) {
1168 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1169 if (r < 0)
1170 return r;
1171
1172 if (bus->nodes_modified)
1173 return 0;
1174 }
1175
1176 r = sd_bus_message_close_container(reply);
1177 if (r < 0)
1178 return r;
1179
1180 r = sd_bus_send(bus, reply, NULL);
1181 if (r < 0)
1182 return r;
1183
1184 return 1;
1185 }
1186
1187 static int object_find_and_run(
1188 sd_bus *bus,
1189 sd_bus_message *m,
1190 const char *p,
1191 bool require_fallback,
1192 bool *found_object) {
1193
1194 struct node *n;
1195 struct vtable_member vtable_key, *v;
1196 int r;
1197
1198 assert(bus);
1199 assert(m);
1200 assert(p);
1201 assert(found_object);
1202
1203 n = hashmap_get(bus->nodes, p);
1204 if (!n)
1205 return 0;
1206
1207 /* First, try object callbacks */
1208 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1209 if (r != 0)
1210 return r;
1211 if (bus->nodes_modified)
1212 return 0;
1213
1214 if (!m->interface || !m->member)
1215 return 0;
1216
1217 /* Then, look for a known method */
1218 vtable_key.path = (char*) p;
1219 vtable_key.interface = m->interface;
1220 vtable_key.member = m->member;
1221
1222 v = hashmap_get(bus->vtable_methods, &vtable_key);
1223 if (v) {
1224 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1225 if (r != 0)
1226 return r;
1227 if (bus->nodes_modified)
1228 return 0;
1229 }
1230
1231 /* Then, look for a known property */
1232 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1233 bool get = false;
1234
1235 get = streq(m->member, "Get");
1236
1237 if (get || streq(m->member, "Set")) {
1238
1239 r = sd_bus_message_rewind(m, true);
1240 if (r < 0)
1241 return r;
1242
1243 vtable_key.path = (char*) p;
1244
1245 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1246 if (r < 0)
1247 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1248
1249 v = hashmap_get(bus->vtable_properties, &vtable_key);
1250 if (v) {
1251 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1252 if (r != 0)
1253 return r;
1254 }
1255
1256 } else if (streq(m->member, "GetAll")) {
1257 const char *iface;
1258
1259 r = sd_bus_message_rewind(m, true);
1260 if (r < 0)
1261 return r;
1262
1263 r = sd_bus_message_read(m, "s", &iface);
1264 if (r < 0)
1265 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1266
1267 if (iface[0] == 0)
1268 iface = NULL;
1269
1270 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1271 if (r != 0)
1272 return r;
1273 }
1274
1275 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1276
1277 if (!isempty(sd_bus_message_get_signature(m, true)))
1278 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1279
1280 r = process_introspect(bus, m, n, require_fallback, found_object);
1281 if (r != 0)
1282 return r;
1283
1284 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1285
1286 if (!isempty(sd_bus_message_get_signature(m, true)))
1287 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1288
1289 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1290 if (r != 0)
1291 return r;
1292 }
1293
1294 if (bus->nodes_modified)
1295 return 0;
1296
1297 if (!*found_object) {
1298 r = bus_node_exists(bus, n, m->path, require_fallback);
1299 if (r < 0)
1300 return r;
1301 if (bus->nodes_modified)
1302 return 0;
1303 if (r > 0)
1304 *found_object = true;
1305 }
1306
1307 return 0;
1308 }
1309
1310 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1311 int r;
1312 size_t pl;
1313 bool found_object = false;
1314
1315 assert(bus);
1316 assert(m);
1317
1318 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1319 return 0;
1320
1321 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1322 return 0;
1323
1324 if (hashmap_isempty(bus->nodes))
1325 return 0;
1326
1327 /* Never respond to broadcast messages */
1328 if (bus->bus_client && !m->destination)
1329 return 0;
1330
1331 assert(m->path);
1332 assert(m->member);
1333
1334 pl = strlen(m->path);
1335 do {
1336 char prefix[pl+1];
1337
1338 bus->nodes_modified = false;
1339
1340 r = object_find_and_run(bus, m, m->path, false, &found_object);
1341 if (r != 0)
1342 return r;
1343
1344 /* Look for fallback prefixes */
1345 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1346
1347 if (bus->nodes_modified)
1348 break;
1349
1350 r = object_find_and_run(bus, m, prefix, true, &found_object);
1351 if (r != 0)
1352 return r;
1353 }
1354
1355 } while (bus->nodes_modified);
1356
1357 if (!found_object)
1358 return 0;
1359
1360 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1361 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1362 r = sd_bus_reply_method_errorf(
1363 m,
1364 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1365 "Unknown property or interface.");
1366 else
1367 r = sd_bus_reply_method_errorf(
1368 m,
1369 SD_BUS_ERROR_UNKNOWN_METHOD,
1370 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1371
1372 if (r < 0)
1373 return r;
1374
1375 return 1;
1376 }
1377
1378 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1379 struct node *n, *parent;
1380 const char *e;
1381 _cleanup_free_ char *s = NULL;
1382 char *p;
1383 int r;
1384
1385 assert(bus);
1386 assert(path);
1387 assert(path[0] == '/');
1388
1389 n = hashmap_get(bus->nodes, path);
1390 if (n)
1391 return n;
1392
1393 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1394 if (r < 0)
1395 return NULL;
1396
1397 s = strdup(path);
1398 if (!s)
1399 return NULL;
1400
1401 if (streq(path, "/"))
1402 parent = NULL;
1403 else {
1404 e = strrchr(path, '/');
1405 assert(e);
1406
1407 p = strndupa(path, MAX(1, path - e));
1408
1409 parent = bus_node_allocate(bus, p);
1410 if (!parent)
1411 return NULL;
1412 }
1413
1414 n = new0(struct node, 1);
1415 if (!n)
1416 return NULL;
1417
1418 n->parent = parent;
1419 n->path = s;
1420 s = NULL; /* do not free */
1421
1422 r = hashmap_put(bus->nodes, n->path, n);
1423 if (r < 0) {
1424 free(n->path);
1425 free(n);
1426 return NULL;
1427 }
1428
1429 if (parent)
1430 LIST_PREPEND(siblings, parent->child, n);
1431
1432 return n;
1433 }
1434
1435 void bus_node_gc(sd_bus *b, struct node *n) {
1436 assert(b);
1437
1438 if (!n)
1439 return;
1440
1441 if (n->child ||
1442 n->callbacks ||
1443 n->vtables ||
1444 n->enumerators ||
1445 n->object_managers)
1446 return;
1447
1448 assert(hashmap_remove(b->nodes, n->path) == n);
1449
1450 if (n->parent)
1451 LIST_REMOVE(siblings, n->parent->child, n);
1452
1453 free(n->path);
1454 bus_node_gc(b, n->parent);
1455 free(n);
1456 }
1457
1458 static int bus_add_object(
1459 sd_bus *bus,
1460 sd_bus_slot **slot,
1461 bool fallback,
1462 const char *path,
1463 sd_bus_message_handler_t callback,
1464 void *userdata) {
1465
1466 sd_bus_slot *s;
1467 struct node *n;
1468 int r;
1469
1470 assert_return(bus, -EINVAL);
1471 assert_return(object_path_is_valid(path), -EINVAL);
1472 assert_return(callback, -EINVAL);
1473 assert_return(!bus_pid_changed(bus), -ECHILD);
1474
1475 n = bus_node_allocate(bus, path);
1476 if (!n)
1477 return -ENOMEM;
1478
1479 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1480 if (!s) {
1481 r = -ENOMEM;
1482 goto fail;
1483 }
1484
1485 s->node_callback.callback = callback;
1486 s->node_callback.is_fallback = fallback;
1487
1488 s->node_callback.node = n;
1489 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1490 bus->nodes_modified = true;
1491
1492 if (slot)
1493 *slot = s;
1494
1495 return 0;
1496
1497 fail:
1498 sd_bus_slot_unref(s);
1499 bus_node_gc(bus, n);
1500
1501 return r;
1502 }
1503
1504 _public_ int sd_bus_add_object(
1505 sd_bus *bus,
1506 sd_bus_slot **slot,
1507 const char *path,
1508 sd_bus_message_handler_t callback,
1509 void *userdata) {
1510
1511 return bus_add_object(bus, slot, false, path, callback, userdata);
1512 }
1513
1514 _public_ int sd_bus_add_fallback(
1515 sd_bus *bus,
1516 sd_bus_slot **slot,
1517 const char *prefix,
1518 sd_bus_message_handler_t callback,
1519 void *userdata) {
1520
1521 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1522 }
1523
1524 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1525 const struct vtable_member *m = a;
1526 uint8_t hash_key2[HASH_KEY_SIZE];
1527 unsigned long ret;
1528
1529 assert(m);
1530
1531 ret = string_hash_func(m->path, hash_key);
1532
1533 /* Use a slightly different hash key for the interface */
1534 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1535 hash_key2[0]++;
1536 ret ^= string_hash_func(m->interface, hash_key2);
1537
1538 /* And an even different one for the member */
1539 hash_key2[0]++;
1540 ret ^= string_hash_func(m->member, hash_key2);
1541
1542 return ret;
1543 }
1544
1545 static int vtable_member_compare_func(const void *a, const void *b) {
1546 const struct vtable_member *x = a, *y = b;
1547 int r;
1548
1549 assert(x);
1550 assert(y);
1551
1552 r = strcmp(x->path, y->path);
1553 if (r != 0)
1554 return r;
1555
1556 r = strcmp(x->interface, y->interface);
1557 if (r != 0)
1558 return r;
1559
1560 return strcmp(x->member, y->member);
1561 }
1562
1563 static const struct hash_ops vtable_member_hash_ops = {
1564 .hash = vtable_member_hash_func,
1565 .compare = vtable_member_compare_func
1566 };
1567
1568 static int add_object_vtable_internal(
1569 sd_bus *bus,
1570 sd_bus_slot **slot,
1571 const char *path,
1572 const char *interface,
1573 const sd_bus_vtable *vtable,
1574 bool fallback,
1575 sd_bus_object_find_t find,
1576 void *userdata) {
1577
1578 sd_bus_slot *s = NULL;
1579 struct node_vtable *i, *existing = NULL;
1580 const sd_bus_vtable *v;
1581 struct node *n;
1582 int r;
1583
1584 assert_return(bus, -EINVAL);
1585 assert_return(object_path_is_valid(path), -EINVAL);
1586 assert_return(interface_name_is_valid(interface), -EINVAL);
1587 assert_return(vtable, -EINVAL);
1588 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1589 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1590 assert_return(!bus_pid_changed(bus), -ECHILD);
1591 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1592 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1593 !streq(interface, "org.freedesktop.DBus.Peer") &&
1594 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1595
1596 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1597 if (r < 0)
1598 return r;
1599
1600 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1601 if (r < 0)
1602 return r;
1603
1604 n = bus_node_allocate(bus, path);
1605 if (!n)
1606 return -ENOMEM;
1607
1608 LIST_FOREACH(vtables, i, n->vtables) {
1609 if (i->is_fallback != fallback) {
1610 r = -EPROTOTYPE;
1611 goto fail;
1612 }
1613
1614 if (streq(i->interface, interface)) {
1615
1616 if (i->vtable == vtable) {
1617 r = -EEXIST;
1618 goto fail;
1619 }
1620
1621 existing = i;
1622 }
1623 }
1624
1625 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1626 if (!s) {
1627 r = -ENOMEM;
1628 goto fail;
1629 }
1630
1631 s->node_vtable.is_fallback = fallback;
1632 s->node_vtable.vtable = vtable;
1633 s->node_vtable.find = find;
1634
1635 s->node_vtable.interface = strdup(interface);
1636 if (!s->node_vtable.interface) {
1637 r = -ENOMEM;
1638 goto fail;
1639 }
1640
1641 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1642
1643 switch (v->type) {
1644
1645 case _SD_BUS_VTABLE_METHOD: {
1646 struct vtable_member *m;
1647
1648 if (!member_name_is_valid(v->x.method.member) ||
1649 !signature_is_valid(strempty(v->x.method.signature), false) ||
1650 !signature_is_valid(strempty(v->x.method.result), false) ||
1651 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1652 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1653 r = -EINVAL;
1654 goto fail;
1655 }
1656
1657 m = new0(struct vtable_member, 1);
1658 if (!m) {
1659 r = -ENOMEM;
1660 goto fail;
1661 }
1662
1663 m->parent = &s->node_vtable;
1664 m->path = n->path;
1665 m->interface = s->node_vtable.interface;
1666 m->member = v->x.method.member;
1667 m->vtable = v;
1668
1669 r = hashmap_put(bus->vtable_methods, m, m);
1670 if (r < 0) {
1671 free(m);
1672 goto fail;
1673 }
1674
1675 break;
1676 }
1677
1678 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1679
1680 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1681 r = -EINVAL;
1682 goto fail;
1683 }
1684
1685 /* Fall through */
1686
1687 case _SD_BUS_VTABLE_PROPERTY: {
1688 struct vtable_member *m;
1689
1690 if (!member_name_is_valid(v->x.property.member) ||
1691 !signature_is_single(v->x.property.signature, false) ||
1692 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1693 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1694 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1695 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1696 r = -EINVAL;
1697 goto fail;
1698 }
1699
1700 m = new0(struct vtable_member, 1);
1701 if (!m) {
1702 r = -ENOMEM;
1703 goto fail;
1704 }
1705
1706 m->parent = &s->node_vtable;
1707 m->path = n->path;
1708 m->interface = s->node_vtable.interface;
1709 m->member = v->x.property.member;
1710 m->vtable = v;
1711
1712 r = hashmap_put(bus->vtable_properties, m, m);
1713 if (r < 0) {
1714 free(m);
1715 goto fail;
1716 }
1717
1718 break;
1719 }
1720
1721 case _SD_BUS_VTABLE_SIGNAL:
1722
1723 if (!member_name_is_valid(v->x.signal.member) ||
1724 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1725 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1726 r = -EINVAL;
1727 goto fail;
1728 }
1729
1730 break;
1731
1732 default:
1733 r = -EINVAL;
1734 goto fail;
1735 }
1736 }
1737
1738 s->node_vtable.node = n;
1739 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1740 bus->nodes_modified = true;
1741
1742 if (slot)
1743 *slot = s;
1744
1745 return 0;
1746
1747 fail:
1748 sd_bus_slot_unref(s);
1749 bus_node_gc(bus, n);
1750
1751 return r;
1752 }
1753
1754 _public_ int sd_bus_add_object_vtable(
1755 sd_bus *bus,
1756 sd_bus_slot **slot,
1757 const char *path,
1758 const char *interface,
1759 const sd_bus_vtable *vtable,
1760 void *userdata) {
1761
1762 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1763 }
1764
1765 _public_ int sd_bus_add_fallback_vtable(
1766 sd_bus *bus,
1767 sd_bus_slot **slot,
1768 const char *prefix,
1769 const char *interface,
1770 const sd_bus_vtable *vtable,
1771 sd_bus_object_find_t find,
1772 void *userdata) {
1773
1774 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1775 }
1776
1777 _public_ int sd_bus_add_node_enumerator(
1778 sd_bus *bus,
1779 sd_bus_slot **slot,
1780 const char *path,
1781 sd_bus_node_enumerator_t callback,
1782 void *userdata) {
1783
1784 sd_bus_slot *s;
1785 struct node *n;
1786 int r;
1787
1788 assert_return(bus, -EINVAL);
1789 assert_return(object_path_is_valid(path), -EINVAL);
1790 assert_return(callback, -EINVAL);
1791 assert_return(!bus_pid_changed(bus), -ECHILD);
1792
1793 n = bus_node_allocate(bus, path);
1794 if (!n)
1795 return -ENOMEM;
1796
1797 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1798 if (!s) {
1799 r = -ENOMEM;
1800 goto fail;
1801 }
1802
1803 s->node_enumerator.callback = callback;
1804
1805 s->node_enumerator.node = n;
1806 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1807 bus->nodes_modified = true;
1808
1809 if (slot)
1810 *slot = s;
1811
1812 return 0;
1813
1814 fail:
1815 sd_bus_slot_unref(s);
1816 bus_node_gc(bus, n);
1817
1818 return r;
1819 }
1820
1821 static int emit_properties_changed_on_interface(
1822 sd_bus *bus,
1823 const char *prefix,
1824 const char *path,
1825 const char *interface,
1826 bool require_fallback,
1827 bool *found_interface,
1828 char **names) {
1829
1830 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1831 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1832 bool has_invalidating = false, has_changing = false;
1833 struct vtable_member key = {};
1834 struct node_vtable *c;
1835 struct node *n;
1836 char **property;
1837 void *u = NULL;
1838 int r;
1839
1840 assert(bus);
1841 assert(prefix);
1842 assert(path);
1843 assert(interface);
1844 assert(found_interface);
1845
1846 n = hashmap_get(bus->nodes, prefix);
1847 if (!n)
1848 return 0;
1849
1850 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1851 if (r < 0)
1852 return r;
1853
1854 r = sd_bus_message_append(m, "s", interface);
1855 if (r < 0)
1856 return r;
1857
1858 r = sd_bus_message_open_container(m, 'a', "{sv}");
1859 if (r < 0)
1860 return r;
1861
1862 key.path = prefix;
1863 key.interface = interface;
1864
1865 LIST_FOREACH(vtables, c, n->vtables) {
1866 if (require_fallback && !c->is_fallback)
1867 continue;
1868
1869 if (!streq(c->interface, interface))
1870 continue;
1871
1872 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1873 if (r < 0)
1874 return r;
1875 if (bus->nodes_modified)
1876 return 0;
1877 if (r == 0)
1878 continue;
1879
1880 *found_interface = true;
1881
1882 if (names) {
1883 /* If the caller specified a list of
1884 * properties we include exactly those in the
1885 * PropertiesChanged message */
1886
1887 STRV_FOREACH(property, names) {
1888 struct vtable_member *v;
1889
1890 assert_return(member_name_is_valid(*property), -EINVAL);
1891
1892 key.member = *property;
1893 v = hashmap_get(bus->vtable_properties, &key);
1894 if (!v)
1895 return -ENOENT;
1896
1897 /* If there are two vtables for the same
1898 * interface, let's handle this property when
1899 * we come to that vtable. */
1900 if (c != v->parent)
1901 continue;
1902
1903 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1904 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1905
1906 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1907
1908 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1909 has_invalidating = true;
1910 continue;
1911 }
1912
1913 has_changing = true;
1914
1915 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1916 if (r < 0)
1917 return r;
1918 if (bus->nodes_modified)
1919 return 0;
1920 }
1921 } else {
1922 const sd_bus_vtable *v;
1923
1924 /* If the caller specified no properties list
1925 * we include all properties that are marked
1926 * as changing in the message. */
1927
1928 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1929 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1930 continue;
1931
1932 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1933 continue;
1934
1935 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1936 has_invalidating = true;
1937 continue;
1938 }
1939
1940 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1941 continue;
1942
1943 has_changing = true;
1944
1945 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1946 if (r < 0)
1947 return r;
1948 if (bus->nodes_modified)
1949 return 0;
1950 }
1951 }
1952 }
1953
1954 if (!has_invalidating && !has_changing)
1955 return 0;
1956
1957 r = sd_bus_message_close_container(m);
1958 if (r < 0)
1959 return r;
1960
1961 r = sd_bus_message_open_container(m, 'a', "s");
1962 if (r < 0)
1963 return r;
1964
1965 if (has_invalidating) {
1966 LIST_FOREACH(vtables, c, n->vtables) {
1967 if (require_fallback && !c->is_fallback)
1968 continue;
1969
1970 if (!streq(c->interface, interface))
1971 continue;
1972
1973 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1974 if (r < 0)
1975 return r;
1976 if (bus->nodes_modified)
1977 return 0;
1978 if (r == 0)
1979 continue;
1980
1981 if (names) {
1982 STRV_FOREACH(property, names) {
1983 struct vtable_member *v;
1984
1985 key.member = *property;
1986 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1987 assert(c == v->parent);
1988
1989 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
1990 continue;
1991
1992 r = sd_bus_message_append(m, "s", *property);
1993 if (r < 0)
1994 return r;
1995 }
1996 } else {
1997 const sd_bus_vtable *v;
1998
1999 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2000 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2001 continue;
2002
2003 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2004 continue;
2005
2006 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2007 continue;
2008
2009 r = sd_bus_message_append(m, "s", v->x.property.member);
2010 if (r < 0)
2011 return r;
2012 }
2013 }
2014 }
2015 }
2016
2017 r = sd_bus_message_close_container(m);
2018 if (r < 0)
2019 return r;
2020
2021 r = sd_bus_send(bus, m, NULL);
2022 if (r < 0)
2023 return r;
2024
2025 return 1;
2026 }
2027
2028 _public_ int sd_bus_emit_properties_changed_strv(
2029 sd_bus *bus,
2030 const char *path,
2031 const char *interface,
2032 char **names) {
2033
2034 BUS_DONT_DESTROY(bus);
2035 bool found_interface = false;
2036 char *prefix;
2037 int r;
2038
2039 assert_return(bus, -EINVAL);
2040 assert_return(object_path_is_valid(path), -EINVAL);
2041 assert_return(interface_name_is_valid(interface), -EINVAL);
2042 assert_return(!bus_pid_changed(bus), -ECHILD);
2043
2044 if (!BUS_IS_OPEN(bus->state))
2045 return -ENOTCONN;
2046
2047 /* A non-NULL but empty names list means nothing needs to be
2048 generated. A NULL list OTOH indicates that all properties
2049 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2050 included in the PropertiesChanged message. */
2051 if (names && names[0] == NULL)
2052 return 0;
2053
2054 do {
2055 bus->nodes_modified = false;
2056
2057 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2058 if (r != 0)
2059 return r;
2060 if (bus->nodes_modified)
2061 continue;
2062
2063 prefix = alloca(strlen(path) + 1);
2064 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2065 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2066 if (r != 0)
2067 return r;
2068 if (bus->nodes_modified)
2069 break;
2070 }
2071
2072 } while (bus->nodes_modified);
2073
2074 return found_interface ? 0 : -ENOENT;
2075 }
2076
2077 _public_ int sd_bus_emit_properties_changed(
2078 sd_bus *bus,
2079 const char *path,
2080 const char *interface,
2081 const char *name, ...) {
2082
2083 char **names;
2084
2085 assert_return(bus, -EINVAL);
2086 assert_return(object_path_is_valid(path), -EINVAL);
2087 assert_return(interface_name_is_valid(interface), -EINVAL);
2088 assert_return(!bus_pid_changed(bus), -ECHILD);
2089
2090 if (!BUS_IS_OPEN(bus->state))
2091 return -ENOTCONN;
2092
2093 if (!name)
2094 return 0;
2095
2096 names = strv_from_stdarg_alloca(name);
2097
2098 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2099 }
2100
2101 static int interfaces_added_append_one_prefix(
2102 sd_bus *bus,
2103 sd_bus_message *m,
2104 const char *prefix,
2105 const char *path,
2106 const char *interface,
2107 bool require_fallback) {
2108
2109 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2110 bool found_interface = false;
2111 struct node_vtable *c;
2112 struct node *n;
2113 void *u = NULL;
2114 int r;
2115
2116 assert(bus);
2117 assert(m);
2118 assert(prefix);
2119 assert(path);
2120 assert(interface);
2121
2122 n = hashmap_get(bus->nodes, prefix);
2123 if (!n)
2124 return 0;
2125
2126 LIST_FOREACH(vtables, c, n->vtables) {
2127 if (require_fallback && !c->is_fallback)
2128 continue;
2129
2130 if (!streq(c->interface, interface))
2131 continue;
2132
2133 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2134 if (r < 0)
2135 return r;
2136 if (bus->nodes_modified)
2137 return 0;
2138 if (r == 0)
2139 continue;
2140
2141 if (!found_interface) {
2142 r = sd_bus_message_append_basic(m, 's', interface);
2143 if (r < 0)
2144 return r;
2145
2146 r = sd_bus_message_open_container(m, 'a', "{sv}");
2147 if (r < 0)
2148 return r;
2149
2150 found_interface = true;
2151 }
2152
2153 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2154 if (r < 0)
2155 return r;
2156 if (bus->nodes_modified)
2157 return 0;
2158 }
2159
2160 if (found_interface) {
2161 r = sd_bus_message_close_container(m);
2162 if (r < 0)
2163 return r;
2164 }
2165
2166 return found_interface;
2167 }
2168
2169 static int interfaces_added_append_one(
2170 sd_bus *bus,
2171 sd_bus_message *m,
2172 const char *path,
2173 const char *interface) {
2174
2175 char *prefix;
2176 int r;
2177
2178 assert(bus);
2179 assert(m);
2180 assert(path);
2181 assert(interface);
2182
2183 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2184 if (r != 0)
2185 return r;
2186 if (bus->nodes_modified)
2187 return 0;
2188
2189 prefix = alloca(strlen(path) + 1);
2190 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2191 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2192 if (r != 0)
2193 return r;
2194 if (bus->nodes_modified)
2195 return 0;
2196 }
2197
2198 return -ENOENT;
2199 }
2200
2201 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2202 BUS_DONT_DESTROY(bus);
2203
2204 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2205 char **i;
2206 int r;
2207
2208 assert_return(bus, -EINVAL);
2209 assert_return(object_path_is_valid(path), -EINVAL);
2210 assert_return(!bus_pid_changed(bus), -ECHILD);
2211
2212 if (!BUS_IS_OPEN(bus->state))
2213 return -ENOTCONN;
2214
2215 if (strv_isempty(interfaces))
2216 return 0;
2217
2218 do {
2219 bus->nodes_modified = false;
2220 m = sd_bus_message_unref(m);
2221
2222 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2223 if (r < 0)
2224 return r;
2225
2226 r = sd_bus_message_append_basic(m, 'o', path);
2227 if (r < 0)
2228 return r;
2229
2230 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2231 if (r < 0)
2232 return r;
2233
2234 STRV_FOREACH(i, interfaces) {
2235 assert_return(interface_name_is_valid(*i), -EINVAL);
2236
2237 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2238 if (r < 0)
2239 return r;
2240
2241 r = interfaces_added_append_one(bus, m, path, *i);
2242 if (r < 0)
2243 return r;
2244
2245 if (bus->nodes_modified)
2246 break;
2247
2248 r = sd_bus_message_close_container(m);
2249 if (r < 0)
2250 return r;
2251 }
2252
2253 if (bus->nodes_modified)
2254 continue;
2255
2256 r = sd_bus_message_close_container(m);
2257 if (r < 0)
2258 return r;
2259
2260 } while (bus->nodes_modified);
2261
2262 return sd_bus_send(bus, m, NULL);
2263 }
2264
2265 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2266 char **interfaces;
2267
2268 assert_return(bus, -EINVAL);
2269 assert_return(object_path_is_valid(path), -EINVAL);
2270 assert_return(!bus_pid_changed(bus), -ECHILD);
2271
2272 if (!BUS_IS_OPEN(bus->state))
2273 return -ENOTCONN;
2274
2275 interfaces = strv_from_stdarg_alloca(interface);
2276
2277 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2278 }
2279
2280 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2281 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2282 int r;
2283
2284 assert_return(bus, -EINVAL);
2285 assert_return(object_path_is_valid(path), -EINVAL);
2286 assert_return(!bus_pid_changed(bus), -ECHILD);
2287
2288 if (!BUS_IS_OPEN(bus->state))
2289 return -ENOTCONN;
2290
2291 if (strv_isempty(interfaces))
2292 return 0;
2293
2294 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2295 if (r < 0)
2296 return r;
2297
2298 r = sd_bus_message_append_basic(m, 'o', path);
2299 if (r < 0)
2300 return r;
2301
2302 r = sd_bus_message_append_strv(m, interfaces);
2303 if (r < 0)
2304 return r;
2305
2306 return sd_bus_send(bus, m, NULL);
2307 }
2308
2309 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2310 char **interfaces;
2311
2312 assert_return(bus, -EINVAL);
2313 assert_return(object_path_is_valid(path), -EINVAL);
2314 assert_return(!bus_pid_changed(bus), -ECHILD);
2315
2316 if (!BUS_IS_OPEN(bus->state))
2317 return -ENOTCONN;
2318
2319 interfaces = strv_from_stdarg_alloca(interface);
2320
2321 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2322 }
2323
2324 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2325 sd_bus_slot *s;
2326 struct node *n;
2327 int r;
2328
2329 assert_return(bus, -EINVAL);
2330 assert_return(object_path_is_valid(path), -EINVAL);
2331 assert_return(!bus_pid_changed(bus), -ECHILD);
2332
2333 n = bus_node_allocate(bus, path);
2334 if (!n)
2335 return -ENOMEM;
2336
2337 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2338 if (!s) {
2339 r = -ENOMEM;
2340 goto fail;
2341 }
2342
2343 s->node_object_manager.node = n;
2344 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2345 bus->nodes_modified = true;
2346
2347 if (slot)
2348 *slot = s;
2349
2350 return 0;
2351
2352 fail:
2353 sd_bus_slot_unref(s);
2354 bus_node_gc(bus, n);
2355
2356 return r;
2357 }