]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-objects.c
sd-bus: refuse properties that claim to be both writable and constant at the same...
[thirdparty/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 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1686 r = -EINVAL;
1687 goto fail;
1688 }
1689
1690 /* Fall through */
1691
1692 case _SD_BUS_VTABLE_PROPERTY: {
1693 struct vtable_member *m;
1694
1695 if (!member_name_is_valid(v->x.property.member) ||
1696 !signature_is_single(v->x.property.signature, false) ||
1697 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1698 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1699 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1700 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1701 r = -EINVAL;
1702 goto fail;
1703 }
1704
1705 m = new0(struct vtable_member, 1);
1706 if (!m) {
1707 r = -ENOMEM;
1708 goto fail;
1709 }
1710
1711 m->parent = &s->node_vtable;
1712 m->path = n->path;
1713 m->interface = s->node_vtable.interface;
1714 m->member = v->x.property.member;
1715 m->vtable = v;
1716
1717 r = hashmap_put(bus->vtable_properties, m, m);
1718 if (r < 0) {
1719 free(m);
1720 goto fail;
1721 }
1722
1723 break;
1724 }
1725
1726 case _SD_BUS_VTABLE_SIGNAL:
1727
1728 if (!member_name_is_valid(v->x.signal.member) ||
1729 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1730 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1731 r = -EINVAL;
1732 goto fail;
1733 }
1734
1735 break;
1736
1737 default:
1738 r = -EINVAL;
1739 goto fail;
1740 }
1741 }
1742
1743 s->node_vtable.node = n;
1744 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1745 bus->nodes_modified = true;
1746
1747 if (slot)
1748 *slot = s;
1749
1750 return 0;
1751
1752 fail:
1753 sd_bus_slot_unref(s);
1754 bus_node_gc(bus, n);
1755
1756 return r;
1757 }
1758
1759 _public_ int sd_bus_add_object_vtable(
1760 sd_bus *bus,
1761 sd_bus_slot **slot,
1762 const char *path,
1763 const char *interface,
1764 const sd_bus_vtable *vtable,
1765 void *userdata) {
1766
1767 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1768 }
1769
1770 _public_ int sd_bus_add_fallback_vtable(
1771 sd_bus *bus,
1772 sd_bus_slot **slot,
1773 const char *prefix,
1774 const char *interface,
1775 const sd_bus_vtable *vtable,
1776 sd_bus_object_find_t find,
1777 void *userdata) {
1778
1779 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1780 }
1781
1782 _public_ int sd_bus_add_node_enumerator(
1783 sd_bus *bus,
1784 sd_bus_slot **slot,
1785 const char *path,
1786 sd_bus_node_enumerator_t callback,
1787 void *userdata) {
1788
1789 sd_bus_slot *s;
1790 struct node *n;
1791 int r;
1792
1793 assert_return(bus, -EINVAL);
1794 assert_return(object_path_is_valid(path), -EINVAL);
1795 assert_return(callback, -EINVAL);
1796 assert_return(!bus_pid_changed(bus), -ECHILD);
1797
1798 n = bus_node_allocate(bus, path);
1799 if (!n)
1800 return -ENOMEM;
1801
1802 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1803 if (!s) {
1804 r = -ENOMEM;
1805 goto fail;
1806 }
1807
1808 s->node_enumerator.callback = callback;
1809
1810 s->node_enumerator.node = n;
1811 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1812 bus->nodes_modified = true;
1813
1814 if (slot)
1815 *slot = s;
1816
1817 return 0;
1818
1819 fail:
1820 sd_bus_slot_unref(s);
1821 bus_node_gc(bus, n);
1822
1823 return r;
1824 }
1825
1826 static int emit_properties_changed_on_interface(
1827 sd_bus *bus,
1828 const char *prefix,
1829 const char *path,
1830 const char *interface,
1831 bool require_fallback,
1832 bool *found_interface,
1833 char **names) {
1834
1835 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1836 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1837 bool has_invalidating = false, has_changing = false;
1838 struct vtable_member key = {};
1839 struct node_vtable *c;
1840 struct node *n;
1841 char **property;
1842 void *u = NULL;
1843 int r;
1844
1845 assert(bus);
1846 assert(prefix);
1847 assert(path);
1848 assert(interface);
1849 assert(found_interface);
1850
1851 n = hashmap_get(bus->nodes, prefix);
1852 if (!n)
1853 return 0;
1854
1855 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1856 if (r < 0)
1857 return r;
1858
1859 r = sd_bus_message_append(m, "s", interface);
1860 if (r < 0)
1861 return r;
1862
1863 r = sd_bus_message_open_container(m, 'a', "{sv}");
1864 if (r < 0)
1865 return r;
1866
1867 key.path = prefix;
1868 key.interface = interface;
1869
1870 LIST_FOREACH(vtables, c, n->vtables) {
1871 if (require_fallback && !c->is_fallback)
1872 continue;
1873
1874 if (!streq(c->interface, interface))
1875 continue;
1876
1877 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1878 if (r < 0)
1879 return r;
1880 if (bus->nodes_modified)
1881 return 0;
1882 if (r == 0)
1883 continue;
1884
1885 *found_interface = true;
1886
1887 if (names) {
1888 /* If the caller specified a list of
1889 * properties we include exactly those in the
1890 * PropertiesChanged message */
1891
1892 STRV_FOREACH(property, names) {
1893 struct vtable_member *v;
1894
1895 assert_return(member_name_is_valid(*property), -EINVAL);
1896
1897 key.member = *property;
1898 v = hashmap_get(bus->vtable_properties, &key);
1899 if (!v)
1900 return -ENOENT;
1901
1902 /* If there are two vtables for the same
1903 * interface, let's handle this property when
1904 * we come to that vtable. */
1905 if (c != v->parent)
1906 continue;
1907
1908 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1909 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1910
1911 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1912
1913 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1914 has_invalidating = true;
1915 continue;
1916 }
1917
1918 has_changing = true;
1919
1920 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1921 if (r < 0)
1922 return r;
1923 if (bus->nodes_modified)
1924 return 0;
1925 }
1926 } else {
1927 const sd_bus_vtable *v;
1928
1929 /* If the caller specified no properties list
1930 * we include all properties that are marked
1931 * as changing in the message. */
1932
1933 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1934 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1935 continue;
1936
1937 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1938 continue;
1939
1940 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1941 has_invalidating = true;
1942 continue;
1943 }
1944
1945 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1946 continue;
1947
1948 has_changing = true;
1949
1950 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1951 if (r < 0)
1952 return r;
1953 if (bus->nodes_modified)
1954 return 0;
1955 }
1956 }
1957 }
1958
1959 if (!has_invalidating && !has_changing)
1960 return 0;
1961
1962 r = sd_bus_message_close_container(m);
1963 if (r < 0)
1964 return r;
1965
1966 r = sd_bus_message_open_container(m, 'a', "s");
1967 if (r < 0)
1968 return r;
1969
1970 if (has_invalidating) {
1971 LIST_FOREACH(vtables, c, n->vtables) {
1972 if (require_fallback && !c->is_fallback)
1973 continue;
1974
1975 if (!streq(c->interface, interface))
1976 continue;
1977
1978 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1979 if (r < 0)
1980 return r;
1981 if (bus->nodes_modified)
1982 return 0;
1983 if (r == 0)
1984 continue;
1985
1986 if (names) {
1987 STRV_FOREACH(property, names) {
1988 struct vtable_member *v;
1989
1990 key.member = *property;
1991 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1992 assert(c == v->parent);
1993
1994 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
1995 continue;
1996
1997 r = sd_bus_message_append(m, "s", *property);
1998 if (r < 0)
1999 return r;
2000 }
2001 } else {
2002 const sd_bus_vtable *v;
2003
2004 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2005 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2006 continue;
2007
2008 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2009 continue;
2010
2011 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2012 continue;
2013
2014 r = sd_bus_message_append(m, "s", v->x.property.member);
2015 if (r < 0)
2016 return r;
2017 }
2018 }
2019 }
2020 }
2021
2022 r = sd_bus_message_close_container(m);
2023 if (r < 0)
2024 return r;
2025
2026 r = sd_bus_send(bus, m, NULL);
2027 if (r < 0)
2028 return r;
2029
2030 return 1;
2031 }
2032
2033 _public_ int sd_bus_emit_properties_changed_strv(
2034 sd_bus *bus,
2035 const char *path,
2036 const char *interface,
2037 char **names) {
2038
2039 BUS_DONT_DESTROY(bus);
2040 bool found_interface = false;
2041 char *prefix;
2042 int r;
2043
2044 assert_return(bus, -EINVAL);
2045 assert_return(object_path_is_valid(path), -EINVAL);
2046 assert_return(interface_name_is_valid(interface), -EINVAL);
2047 assert_return(!bus_pid_changed(bus), -ECHILD);
2048
2049 if (!BUS_IS_OPEN(bus->state))
2050 return -ENOTCONN;
2051
2052 /* A non-NULL but empty names list means nothing needs to be
2053 generated. A NULL list OTOH indicates that all properties
2054 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2055 included in the PropertiesChanged message. */
2056 if (names && names[0] == NULL)
2057 return 0;
2058
2059 do {
2060 bus->nodes_modified = false;
2061
2062 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2063 if (r != 0)
2064 return r;
2065 if (bus->nodes_modified)
2066 continue;
2067
2068 prefix = alloca(strlen(path) + 1);
2069 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2070 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2071 if (r != 0)
2072 return r;
2073 if (bus->nodes_modified)
2074 break;
2075 }
2076
2077 } while (bus->nodes_modified);
2078
2079 return found_interface ? 0 : -ENOENT;
2080 }
2081
2082 _public_ int sd_bus_emit_properties_changed(
2083 sd_bus *bus,
2084 const char *path,
2085 const char *interface,
2086 const char *name, ...) {
2087
2088 char **names;
2089
2090 assert_return(bus, -EINVAL);
2091 assert_return(object_path_is_valid(path), -EINVAL);
2092 assert_return(interface_name_is_valid(interface), -EINVAL);
2093 assert_return(!bus_pid_changed(bus), -ECHILD);
2094
2095 if (!BUS_IS_OPEN(bus->state))
2096 return -ENOTCONN;
2097
2098 if (!name)
2099 return 0;
2100
2101 names = strv_from_stdarg_alloca(name);
2102
2103 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2104 }
2105
2106 static int interfaces_added_append_one_prefix(
2107 sd_bus *bus,
2108 sd_bus_message *m,
2109 const char *prefix,
2110 const char *path,
2111 const char *interface,
2112 bool require_fallback) {
2113
2114 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2115 bool found_interface = false;
2116 struct node_vtable *c;
2117 struct node *n;
2118 void *u = NULL;
2119 int r;
2120
2121 assert(bus);
2122 assert(m);
2123 assert(prefix);
2124 assert(path);
2125 assert(interface);
2126
2127 n = hashmap_get(bus->nodes, prefix);
2128 if (!n)
2129 return 0;
2130
2131 LIST_FOREACH(vtables, c, n->vtables) {
2132 if (require_fallback && !c->is_fallback)
2133 continue;
2134
2135 if (!streq(c->interface, interface))
2136 continue;
2137
2138 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2139 if (r < 0)
2140 return r;
2141 if (bus->nodes_modified)
2142 return 0;
2143 if (r == 0)
2144 continue;
2145
2146 if (!found_interface) {
2147 r = sd_bus_message_append_basic(m, 's', interface);
2148 if (r < 0)
2149 return r;
2150
2151 r = sd_bus_message_open_container(m, 'a', "{sv}");
2152 if (r < 0)
2153 return r;
2154
2155 found_interface = true;
2156 }
2157
2158 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2159 if (r < 0)
2160 return r;
2161 if (bus->nodes_modified)
2162 return 0;
2163 }
2164
2165 if (found_interface) {
2166 r = sd_bus_message_close_container(m);
2167 if (r < 0)
2168 return r;
2169 }
2170
2171 return found_interface;
2172 }
2173
2174 static int interfaces_added_append_one(
2175 sd_bus *bus,
2176 sd_bus_message *m,
2177 const char *path,
2178 const char *interface) {
2179
2180 char *prefix;
2181 int r;
2182
2183 assert(bus);
2184 assert(m);
2185 assert(path);
2186 assert(interface);
2187
2188 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2189 if (r != 0)
2190 return r;
2191 if (bus->nodes_modified)
2192 return 0;
2193
2194 prefix = alloca(strlen(path) + 1);
2195 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2196 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2197 if (r != 0)
2198 return r;
2199 if (bus->nodes_modified)
2200 return 0;
2201 }
2202
2203 return -ENOENT;
2204 }
2205
2206 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2207 BUS_DONT_DESTROY(bus);
2208
2209 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2210 char **i;
2211 int r;
2212
2213 assert_return(bus, -EINVAL);
2214 assert_return(object_path_is_valid(path), -EINVAL);
2215 assert_return(!bus_pid_changed(bus), -ECHILD);
2216
2217 if (!BUS_IS_OPEN(bus->state))
2218 return -ENOTCONN;
2219
2220 if (strv_isempty(interfaces))
2221 return 0;
2222
2223 do {
2224 bus->nodes_modified = false;
2225 m = sd_bus_message_unref(m);
2226
2227 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2228 if (r < 0)
2229 return r;
2230
2231 r = sd_bus_message_append_basic(m, 'o', path);
2232 if (r < 0)
2233 return r;
2234
2235 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2236 if (r < 0)
2237 return r;
2238
2239 STRV_FOREACH(i, interfaces) {
2240 assert_return(interface_name_is_valid(*i), -EINVAL);
2241
2242 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2243 if (r < 0)
2244 return r;
2245
2246 r = interfaces_added_append_one(bus, m, path, *i);
2247 if (r < 0)
2248 return r;
2249
2250 if (bus->nodes_modified)
2251 break;
2252
2253 r = sd_bus_message_close_container(m);
2254 if (r < 0)
2255 return r;
2256 }
2257
2258 if (bus->nodes_modified)
2259 continue;
2260
2261 r = sd_bus_message_close_container(m);
2262 if (r < 0)
2263 return r;
2264
2265 } while (bus->nodes_modified);
2266
2267 return sd_bus_send(bus, m, NULL);
2268 }
2269
2270 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2271 char **interfaces;
2272
2273 assert_return(bus, -EINVAL);
2274 assert_return(object_path_is_valid(path), -EINVAL);
2275 assert_return(!bus_pid_changed(bus), -ECHILD);
2276
2277 if (!BUS_IS_OPEN(bus->state))
2278 return -ENOTCONN;
2279
2280 interfaces = strv_from_stdarg_alloca(interface);
2281
2282 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2283 }
2284
2285 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2286 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2287 int r;
2288
2289 assert_return(bus, -EINVAL);
2290 assert_return(object_path_is_valid(path), -EINVAL);
2291 assert_return(!bus_pid_changed(bus), -ECHILD);
2292
2293 if (!BUS_IS_OPEN(bus->state))
2294 return -ENOTCONN;
2295
2296 if (strv_isempty(interfaces))
2297 return 0;
2298
2299 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2300 if (r < 0)
2301 return r;
2302
2303 r = sd_bus_message_append_basic(m, 'o', path);
2304 if (r < 0)
2305 return r;
2306
2307 r = sd_bus_message_append_strv(m, interfaces);
2308 if (r < 0)
2309 return r;
2310
2311 return sd_bus_send(bus, m, NULL);
2312 }
2313
2314 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2315 char **interfaces;
2316
2317 assert_return(bus, -EINVAL);
2318 assert_return(object_path_is_valid(path), -EINVAL);
2319 assert_return(!bus_pid_changed(bus), -ECHILD);
2320
2321 if (!BUS_IS_OPEN(bus->state))
2322 return -ENOTCONN;
2323
2324 interfaces = strv_from_stdarg_alloca(interface);
2325
2326 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2327 }
2328
2329 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2330 sd_bus_slot *s;
2331 struct node *n;
2332 int r;
2333
2334 assert_return(bus, -EINVAL);
2335 assert_return(object_path_is_valid(path), -EINVAL);
2336 assert_return(!bus_pid_changed(bus), -ECHILD);
2337
2338 n = bus_node_allocate(bus, path);
2339 if (!n)
2340 return -ENOMEM;
2341
2342 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2343 if (!s) {
2344 r = -ENOMEM;
2345 goto fail;
2346 }
2347
2348 s->node_object_manager.node = n;
2349 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2350 bus->nodes_modified = true;
2351
2352 if (slot)
2353 *slot = s;
2354
2355 return 0;
2356
2357 fail:
2358 sd_bus_slot_unref(s);
2359 bus_node_gc(bus, n);
2360
2361 return r;
2362 }