]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-objects.c
hashmap: introduce hash_ops to make struct Hashmap smaller
[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 bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
824 assert(bus);
825 assert(n);
826
827 if (n->object_managers)
828 return true;
829
830 if (n->parent)
831 return bus_node_with_object_manager(bus, n->parent);
832
833 return false;
834 }
835
836 static bool bus_node_exists(
837 sd_bus *bus,
838 struct node *n,
839 const char *path,
840 bool require_fallback) {
841
842 struct node_vtable *c;
843 struct node_callback *k;
844
845 assert(bus);
846 assert(n);
847 assert(path);
848
849 /* Tests if there's anything attached directly to this node
850 * for the specified path */
851
852 LIST_FOREACH(callbacks, k, n->callbacks) {
853 if (require_fallback && !k->is_fallback)
854 continue;
855
856 return true;
857 }
858
859 LIST_FOREACH(vtables, c, n->vtables) {
860 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
861
862 if (require_fallback && !c->is_fallback)
863 continue;
864
865 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
866 return true;
867 if (bus->nodes_modified)
868 return false;
869 }
870
871 return !require_fallback && (n->enumerators || n->object_managers);
872 }
873
874 static int process_introspect(
875 sd_bus *bus,
876 sd_bus_message *m,
877 struct node *n,
878 bool require_fallback,
879 bool *found_object) {
880
881 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
882 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
883 _cleanup_set_free_free_ Set *s = NULL;
884 const char *previous_interface = NULL;
885 struct introspect intro;
886 struct node_vtable *c;
887 bool empty;
888 int r;
889
890 assert(bus);
891 assert(m);
892 assert(n);
893 assert(found_object);
894
895 r = get_child_nodes(bus, m->path, n, &s, &error);
896 if (r < 0)
897 return bus_maybe_reply_error(m, r, &error);
898 if (bus->nodes_modified)
899 return 0;
900
901 r = introspect_begin(&intro, bus->trusted);
902 if (r < 0)
903 return r;
904
905 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
906 if (r < 0)
907 return r;
908
909 empty = set_isempty(s);
910
911 LIST_FOREACH(vtables, c, n->vtables) {
912 if (require_fallback && !c->is_fallback)
913 continue;
914
915 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
916 if (r < 0) {
917 r = bus_maybe_reply_error(m, r, &error);
918 goto finish;
919 }
920 if (bus->nodes_modified) {
921 r = 0;
922 goto finish;
923 }
924 if (r == 0)
925 continue;
926
927 empty = false;
928
929 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
930 continue;
931
932 if (!streq_ptr(previous_interface, c->interface)) {
933
934 if (previous_interface)
935 fputs(" </interface>\n", intro.f);
936
937 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
938 }
939
940 r = introspect_write_interface(&intro, c->vtable);
941 if (r < 0)
942 goto finish;
943
944 previous_interface = c->interface;
945 }
946
947 if (previous_interface)
948 fputs(" </interface>\n", intro.f);
949
950 if (empty) {
951 /* Nothing?, let's see if we exist at all, and if not
952 * refuse to do anything */
953 r = bus_node_exists(bus, n, m->path, require_fallback);
954 if (r < 0)
955 return r;
956 if (bus->nodes_modified)
957 return 0;
958 if (r == 0)
959 goto finish;
960 }
961
962 *found_object = true;
963
964 r = introspect_write_child_nodes(&intro, s, m->path);
965 if (r < 0)
966 goto finish;
967
968 r = introspect_finish(&intro, bus, m, &reply);
969 if (r < 0)
970 goto finish;
971
972 r = sd_bus_send(bus, reply, NULL);
973 if (r < 0)
974 goto finish;
975
976 r = 1;
977
978 finish:
979 introspect_free(&intro);
980 return r;
981 }
982
983 static int object_manager_serialize_path(
984 sd_bus *bus,
985 sd_bus_message *reply,
986 const char *prefix,
987 const char *path,
988 bool require_fallback,
989 sd_bus_error *error) {
990
991 const char *previous_interface = NULL;
992 bool found_something = false;
993 struct node_vtable *i;
994 struct node *n;
995 int r;
996
997 assert(bus);
998 assert(reply);
999 assert(prefix);
1000 assert(path);
1001 assert(error);
1002
1003 n = hashmap_get(bus->nodes, prefix);
1004 if (!n)
1005 return 0;
1006
1007 LIST_FOREACH(vtables, i, n->vtables) {
1008 void *u;
1009
1010 if (require_fallback && !i->is_fallback)
1011 continue;
1012
1013 r = node_vtable_get_userdata(bus, path, i, &u, error);
1014 if (r < 0)
1015 return r;
1016 if (bus->nodes_modified)
1017 return 0;
1018 if (r == 0)
1019 continue;
1020
1021 if (!found_something) {
1022
1023 /* Open the object part */
1024
1025 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1026 if (r < 0)
1027 return r;
1028
1029 r = sd_bus_message_append(reply, "o", path);
1030 if (r < 0)
1031 return r;
1032
1033 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1034 if (r < 0)
1035 return r;
1036
1037 found_something = true;
1038 }
1039
1040 if (!streq_ptr(previous_interface, i->interface)) {
1041
1042 /* Maybe close the previous interface part */
1043
1044 if (previous_interface) {
1045 r = sd_bus_message_close_container(reply);
1046 if (r < 0)
1047 return r;
1048
1049 r = sd_bus_message_close_container(reply);
1050 if (r < 0)
1051 return r;
1052 }
1053
1054 /* Open the new interface part */
1055
1056 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1057 if (r < 0)
1058 return r;
1059
1060 r = sd_bus_message_append(reply, "s", i->interface);
1061 if (r < 0)
1062 return r;
1063
1064 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1065 if (r < 0)
1066 return r;
1067 }
1068
1069 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1070 if (r < 0)
1071 return r;
1072 if (bus->nodes_modified)
1073 return 0;
1074
1075 previous_interface = i->interface;
1076 }
1077
1078 if (previous_interface) {
1079 r = sd_bus_message_close_container(reply);
1080 if (r < 0)
1081 return r;
1082
1083 r = sd_bus_message_close_container(reply);
1084 if (r < 0)
1085 return r;
1086 }
1087
1088 if (found_something) {
1089 r = sd_bus_message_close_container(reply);
1090 if (r < 0)
1091 return r;
1092
1093 r = sd_bus_message_close_container(reply);
1094 if (r < 0)
1095 return r;
1096 }
1097
1098 return 1;
1099 }
1100
1101 static int object_manager_serialize_path_and_fallbacks(
1102 sd_bus *bus,
1103 sd_bus_message *reply,
1104 const char *path,
1105 sd_bus_error *error) {
1106
1107 char *prefix;
1108 int r;
1109
1110 assert(bus);
1111 assert(reply);
1112 assert(path);
1113 assert(error);
1114
1115 /* First, add all vtables registered for this path */
1116 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1117 if (r < 0)
1118 return r;
1119 if (bus->nodes_modified)
1120 return 0;
1121
1122 /* Second, add fallback vtables registered for any of the prefixes */
1123 prefix = alloca(strlen(path) + 1);
1124 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1125 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1126 if (r < 0)
1127 return r;
1128 if (bus->nodes_modified)
1129 return 0;
1130 }
1131
1132 return 0;
1133 }
1134
1135 static int process_get_managed_objects(
1136 sd_bus *bus,
1137 sd_bus_message *m,
1138 struct node *n,
1139 bool require_fallback,
1140 bool *found_object) {
1141
1142 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1143 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1144 _cleanup_set_free_free_ Set *s = NULL;
1145 bool empty;
1146 int r;
1147
1148 assert(bus);
1149 assert(m);
1150 assert(n);
1151 assert(found_object);
1152
1153 if (!bus_node_with_object_manager(bus, n))
1154 return 0;
1155
1156 r = get_child_nodes(bus, m->path, n, &s, &error);
1157 if (r < 0)
1158 return r;
1159 if (bus->nodes_modified)
1160 return 0;
1161
1162 r = sd_bus_message_new_method_return(m, &reply);
1163 if (r < 0)
1164 return r;
1165
1166 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1167 if (r < 0)
1168 return r;
1169
1170 empty = set_isempty(s);
1171 if (empty) {
1172 struct node_vtable *c;
1173
1174 /* Hmm, so we have no children? Then let's check
1175 * whether we exist at all, i.e. whether at least one
1176 * vtable exists. */
1177
1178 LIST_FOREACH(vtables, c, n->vtables) {
1179
1180 if (require_fallback && !c->is_fallback)
1181 continue;
1182
1183 if (r < 0)
1184 return r;
1185 if (r == 0)
1186 continue;
1187
1188 empty = false;
1189 break;
1190 }
1191
1192 if (empty)
1193 return 0;
1194 } else {
1195 Iterator i;
1196 char *path;
1197
1198 SET_FOREACH(path, s, i) {
1199 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1200 if (r < 0)
1201 return r;
1202
1203 if (bus->nodes_modified)
1204 return 0;
1205 }
1206 }
1207
1208 r = sd_bus_message_close_container(reply);
1209 if (r < 0)
1210 return r;
1211
1212 r = sd_bus_send(bus, reply, NULL);
1213 if (r < 0)
1214 return r;
1215
1216 return 1;
1217 }
1218
1219 static int object_find_and_run(
1220 sd_bus *bus,
1221 sd_bus_message *m,
1222 const char *p,
1223 bool require_fallback,
1224 bool *found_object) {
1225
1226 struct node *n;
1227 struct vtable_member vtable_key, *v;
1228 int r;
1229
1230 assert(bus);
1231 assert(m);
1232 assert(p);
1233 assert(found_object);
1234
1235 n = hashmap_get(bus->nodes, p);
1236 if (!n)
1237 return 0;
1238
1239 /* First, try object callbacks */
1240 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1241 if (r != 0)
1242 return r;
1243 if (bus->nodes_modified)
1244 return 0;
1245
1246 if (!m->interface || !m->member)
1247 return 0;
1248
1249 /* Then, look for a known method */
1250 vtable_key.path = (char*) p;
1251 vtable_key.interface = m->interface;
1252 vtable_key.member = m->member;
1253
1254 v = hashmap_get(bus->vtable_methods, &vtable_key);
1255 if (v) {
1256 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1257 if (r != 0)
1258 return r;
1259 if (bus->nodes_modified)
1260 return 0;
1261 }
1262
1263 /* Then, look for a known property */
1264 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1265 bool get = false;
1266
1267 get = streq(m->member, "Get");
1268
1269 if (get || streq(m->member, "Set")) {
1270
1271 r = sd_bus_message_rewind(m, true);
1272 if (r < 0)
1273 return r;
1274
1275 vtable_key.path = (char*) p;
1276
1277 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1278 if (r < 0)
1279 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1280
1281 v = hashmap_get(bus->vtable_properties, &vtable_key);
1282 if (v) {
1283 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1284 if (r != 0)
1285 return r;
1286 }
1287
1288 } else if (streq(m->member, "GetAll")) {
1289 const char *iface;
1290
1291 r = sd_bus_message_rewind(m, true);
1292 if (r < 0)
1293 return r;
1294
1295 r = sd_bus_message_read(m, "s", &iface);
1296 if (r < 0)
1297 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1298
1299 if (iface[0] == 0)
1300 iface = NULL;
1301
1302 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1303 if (r != 0)
1304 return r;
1305 }
1306
1307 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1308
1309 if (!isempty(sd_bus_message_get_signature(m, true)))
1310 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1311
1312 r = process_introspect(bus, m, n, require_fallback, found_object);
1313 if (r != 0)
1314 return r;
1315
1316 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1317
1318 if (!isempty(sd_bus_message_get_signature(m, true)))
1319 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1320
1321 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1322 if (r != 0)
1323 return r;
1324 }
1325
1326 if (bus->nodes_modified)
1327 return 0;
1328
1329 if (!*found_object) {
1330 r = bus_node_exists(bus, n, m->path, require_fallback);
1331 if (r < 0)
1332 return r;
1333 if (r > 0)
1334 *found_object = true;
1335 }
1336
1337 return 0;
1338 }
1339
1340 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1341 int r;
1342 size_t pl;
1343 bool found_object = false;
1344
1345 assert(bus);
1346 assert(m);
1347
1348 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1349 return 0;
1350
1351 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1352 return 0;
1353
1354 if (hashmap_isempty(bus->nodes))
1355 return 0;
1356
1357 /* Never respond to broadcast messages */
1358 if (bus->bus_client && !m->destination)
1359 return 0;
1360
1361 assert(m->path);
1362 assert(m->member);
1363
1364 pl = strlen(m->path);
1365 do {
1366 char prefix[pl+1];
1367
1368 bus->nodes_modified = false;
1369
1370 r = object_find_and_run(bus, m, m->path, false, &found_object);
1371 if (r != 0)
1372 return r;
1373
1374 /* Look for fallback prefixes */
1375 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1376
1377 if (bus->nodes_modified)
1378 break;
1379
1380 r = object_find_and_run(bus, m, prefix, true, &found_object);
1381 if (r != 0)
1382 return r;
1383 }
1384
1385 } while (bus->nodes_modified);
1386
1387 if (!found_object)
1388 return 0;
1389
1390 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1391 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1392 r = sd_bus_reply_method_errorf(
1393 m,
1394 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1395 "Unknown property or interface.");
1396 else
1397 r = sd_bus_reply_method_errorf(
1398 m,
1399 SD_BUS_ERROR_UNKNOWN_METHOD,
1400 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1401
1402 if (r < 0)
1403 return r;
1404
1405 return 1;
1406 }
1407
1408 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1409 struct node *n, *parent;
1410 const char *e;
1411 _cleanup_free_ char *s = NULL;
1412 char *p;
1413 int r;
1414
1415 assert(bus);
1416 assert(path);
1417 assert(path[0] == '/');
1418
1419 n = hashmap_get(bus->nodes, path);
1420 if (n)
1421 return n;
1422
1423 r = hashmap_ensure_allocated(&bus->nodes, &string_hash_ops);
1424 if (r < 0)
1425 return NULL;
1426
1427 s = strdup(path);
1428 if (!s)
1429 return NULL;
1430
1431 if (streq(path, "/"))
1432 parent = NULL;
1433 else {
1434 e = strrchr(path, '/');
1435 assert(e);
1436
1437 p = strndupa(path, MAX(1, path - e));
1438
1439 parent = bus_node_allocate(bus, p);
1440 if (!parent)
1441 return NULL;
1442 }
1443
1444 n = new0(struct node, 1);
1445 if (!n)
1446 return NULL;
1447
1448 n->parent = parent;
1449 n->path = s;
1450 s = NULL; /* do not free */
1451
1452 r = hashmap_put(bus->nodes, n->path, n);
1453 if (r < 0) {
1454 free(n->path);
1455 free(n);
1456 return NULL;
1457 }
1458
1459 if (parent)
1460 LIST_PREPEND(siblings, parent->child, n);
1461
1462 return n;
1463 }
1464
1465 void bus_node_gc(sd_bus *b, struct node *n) {
1466 assert(b);
1467
1468 if (!n)
1469 return;
1470
1471 if (n->child ||
1472 n->callbacks ||
1473 n->vtables ||
1474 n->enumerators ||
1475 n->object_managers)
1476 return;
1477
1478 assert(hashmap_remove(b->nodes, n->path) == n);
1479
1480 if (n->parent)
1481 LIST_REMOVE(siblings, n->parent->child, n);
1482
1483 free(n->path);
1484 bus_node_gc(b, n->parent);
1485 free(n);
1486 }
1487
1488 static int bus_add_object(
1489 sd_bus *bus,
1490 sd_bus_slot **slot,
1491 bool fallback,
1492 const char *path,
1493 sd_bus_message_handler_t callback,
1494 void *userdata) {
1495
1496 sd_bus_slot *s;
1497 struct node *n;
1498 int r;
1499
1500 assert_return(bus, -EINVAL);
1501 assert_return(object_path_is_valid(path), -EINVAL);
1502 assert_return(callback, -EINVAL);
1503 assert_return(!bus_pid_changed(bus), -ECHILD);
1504
1505 n = bus_node_allocate(bus, path);
1506 if (!n)
1507 return -ENOMEM;
1508
1509 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1510 if (!s) {
1511 r = -ENOMEM;
1512 goto fail;
1513 }
1514
1515 s->node_callback.callback = callback;
1516 s->node_callback.is_fallback = fallback;
1517
1518 s->node_callback.node = n;
1519 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1520 bus->nodes_modified = true;
1521
1522 if (slot)
1523 *slot = s;
1524
1525 return 0;
1526
1527 fail:
1528 sd_bus_slot_unref(s);
1529 bus_node_gc(bus, n);
1530
1531 return r;
1532 }
1533
1534 _public_ int sd_bus_add_object(
1535 sd_bus *bus,
1536 sd_bus_slot **slot,
1537 const char *path,
1538 sd_bus_message_handler_t callback,
1539 void *userdata) {
1540
1541 return bus_add_object(bus, slot, false, path, callback, userdata);
1542 }
1543
1544 _public_ int sd_bus_add_fallback(
1545 sd_bus *bus,
1546 sd_bus_slot **slot,
1547 const char *prefix,
1548 sd_bus_message_handler_t callback,
1549 void *userdata) {
1550
1551 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1552 }
1553
1554 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1555 const struct vtable_member *m = a;
1556 uint8_t hash_key2[HASH_KEY_SIZE];
1557 unsigned long ret;
1558
1559 assert(m);
1560
1561 ret = string_hash_func(m->path, hash_key);
1562
1563 /* Use a slightly different hash key for the interface */
1564 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1565 hash_key2[0]++;
1566 ret ^= string_hash_func(m->interface, hash_key2);
1567
1568 /* And an even different one for the member */
1569 hash_key2[0]++;
1570 ret ^= string_hash_func(m->member, hash_key2);
1571
1572 return ret;
1573 }
1574
1575 static int vtable_member_compare_func(const void *a, const void *b) {
1576 const struct vtable_member *x = a, *y = b;
1577 int r;
1578
1579 assert(x);
1580 assert(y);
1581
1582 r = strcmp(x->path, y->path);
1583 if (r != 0)
1584 return r;
1585
1586 r = strcmp(x->interface, y->interface);
1587 if (r != 0)
1588 return r;
1589
1590 return strcmp(x->member, y->member);
1591 }
1592
1593 static const struct hash_ops vtable_member_hash_ops = {
1594 .hash = vtable_member_hash_func,
1595 .compare = vtable_member_compare_func
1596 };
1597
1598 static int add_object_vtable_internal(
1599 sd_bus *bus,
1600 sd_bus_slot **slot,
1601 const char *path,
1602 const char *interface,
1603 const sd_bus_vtable *vtable,
1604 bool fallback,
1605 sd_bus_object_find_t find,
1606 void *userdata) {
1607
1608 sd_bus_slot *s = NULL;
1609 struct node_vtable *i, *existing = NULL;
1610 const sd_bus_vtable *v;
1611 struct node *n;
1612 int r;
1613
1614 assert_return(bus, -EINVAL);
1615 assert_return(object_path_is_valid(path), -EINVAL);
1616 assert_return(interface_name_is_valid(interface), -EINVAL);
1617 assert_return(vtable, -EINVAL);
1618 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1619 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1620 assert_return(!bus_pid_changed(bus), -ECHILD);
1621 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1622 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1623 !streq(interface, "org.freedesktop.DBus.Peer") &&
1624 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1625
1626 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1627 if (r < 0)
1628 return r;
1629
1630 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1631 if (r < 0)
1632 return r;
1633
1634 n = bus_node_allocate(bus, path);
1635 if (!n)
1636 return -ENOMEM;
1637
1638 LIST_FOREACH(vtables, i, n->vtables) {
1639 if (i->is_fallback != fallback) {
1640 r = -EPROTOTYPE;
1641 goto fail;
1642 }
1643
1644 if (streq(i->interface, interface)) {
1645
1646 if (i->vtable == vtable) {
1647 r = -EEXIST;
1648 goto fail;
1649 }
1650
1651 existing = i;
1652 }
1653 }
1654
1655 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1656 if (!s) {
1657 r = -ENOMEM;
1658 goto fail;
1659 }
1660
1661 s->node_vtable.is_fallback = fallback;
1662 s->node_vtable.vtable = vtable;
1663 s->node_vtable.find = find;
1664
1665 s->node_vtable.interface = strdup(interface);
1666 if (!s->node_vtable.interface) {
1667 r = -ENOMEM;
1668 goto fail;
1669 }
1670
1671 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1672
1673 switch (v->type) {
1674
1675 case _SD_BUS_VTABLE_METHOD: {
1676 struct vtable_member *m;
1677
1678 if (!member_name_is_valid(v->x.method.member) ||
1679 !signature_is_valid(strempty(v->x.method.signature), false) ||
1680 !signature_is_valid(strempty(v->x.method.result), false) ||
1681 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1682 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1683 r = -EINVAL;
1684 goto fail;
1685 }
1686
1687 m = new0(struct vtable_member, 1);
1688 if (!m) {
1689 r = -ENOMEM;
1690 goto fail;
1691 }
1692
1693 m->parent = &s->node_vtable;
1694 m->path = n->path;
1695 m->interface = s->node_vtable.interface;
1696 m->member = v->x.method.member;
1697 m->vtable = v;
1698
1699 r = hashmap_put(bus->vtable_methods, m, m);
1700 if (r < 0) {
1701 free(m);
1702 goto fail;
1703 }
1704
1705 break;
1706 }
1707
1708 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1709
1710 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1711 r = -EINVAL;
1712 goto fail;
1713 }
1714
1715 /* Fall through */
1716
1717 case _SD_BUS_VTABLE_PROPERTY: {
1718 struct vtable_member *m;
1719
1720 if (!member_name_is_valid(v->x.property.member) ||
1721 !signature_is_single(v->x.property.signature, false) ||
1722 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1723 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1724 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1725 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1726 r = -EINVAL;
1727 goto fail;
1728 }
1729
1730 m = new0(struct vtable_member, 1);
1731 if (!m) {
1732 r = -ENOMEM;
1733 goto fail;
1734 }
1735
1736 m->parent = &s->node_vtable;
1737 m->path = n->path;
1738 m->interface = s->node_vtable.interface;
1739 m->member = v->x.property.member;
1740 m->vtable = v;
1741
1742 r = hashmap_put(bus->vtable_properties, m, m);
1743 if (r < 0) {
1744 free(m);
1745 goto fail;
1746 }
1747
1748 break;
1749 }
1750
1751 case _SD_BUS_VTABLE_SIGNAL:
1752
1753 if (!member_name_is_valid(v->x.signal.member) ||
1754 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1755 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1756 r = -EINVAL;
1757 goto fail;
1758 }
1759
1760 break;
1761
1762 default:
1763 r = -EINVAL;
1764 goto fail;
1765 }
1766 }
1767
1768 s->node_vtable.node = n;
1769 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1770 bus->nodes_modified = true;
1771
1772 if (slot)
1773 *slot = s;
1774
1775 return 0;
1776
1777 fail:
1778 sd_bus_slot_unref(s);
1779 bus_node_gc(bus, n);
1780
1781 return r;
1782 }
1783
1784 _public_ int sd_bus_add_object_vtable(
1785 sd_bus *bus,
1786 sd_bus_slot **slot,
1787 const char *path,
1788 const char *interface,
1789 const sd_bus_vtable *vtable,
1790 void *userdata) {
1791
1792 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1793 }
1794
1795 _public_ int sd_bus_add_fallback_vtable(
1796 sd_bus *bus,
1797 sd_bus_slot **slot,
1798 const char *prefix,
1799 const char *interface,
1800 const sd_bus_vtable *vtable,
1801 sd_bus_object_find_t find,
1802 void *userdata) {
1803
1804 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1805 }
1806
1807 _public_ int sd_bus_add_node_enumerator(
1808 sd_bus *bus,
1809 sd_bus_slot **slot,
1810 const char *path,
1811 sd_bus_node_enumerator_t callback,
1812 void *userdata) {
1813
1814 sd_bus_slot *s;
1815 struct node *n;
1816 int r;
1817
1818 assert_return(bus, -EINVAL);
1819 assert_return(object_path_is_valid(path), -EINVAL);
1820 assert_return(callback, -EINVAL);
1821 assert_return(!bus_pid_changed(bus), -ECHILD);
1822
1823 n = bus_node_allocate(bus, path);
1824 if (!n)
1825 return -ENOMEM;
1826
1827 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1828 if (!s) {
1829 r = -ENOMEM;
1830 goto fail;
1831 }
1832
1833 s->node_enumerator.callback = callback;
1834
1835 s->node_enumerator.node = n;
1836 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1837 bus->nodes_modified = true;
1838
1839 if (slot)
1840 *slot = s;
1841
1842 return 0;
1843
1844 fail:
1845 sd_bus_slot_unref(s);
1846 bus_node_gc(bus, n);
1847
1848 return r;
1849 }
1850
1851 static int emit_properties_changed_on_interface(
1852 sd_bus *bus,
1853 const char *prefix,
1854 const char *path,
1855 const char *interface,
1856 bool require_fallback,
1857 bool *found_interface,
1858 char **names) {
1859
1860 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1861 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1862 bool has_invalidating = false, has_changing = false;
1863 struct vtable_member key = {};
1864 struct node_vtable *c;
1865 struct node *n;
1866 char **property;
1867 void *u = NULL;
1868 int r;
1869
1870 assert(bus);
1871 assert(prefix);
1872 assert(path);
1873 assert(interface);
1874 assert(found_interface);
1875
1876 n = hashmap_get(bus->nodes, prefix);
1877 if (!n)
1878 return 0;
1879
1880 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1881 if (r < 0)
1882 return r;
1883
1884 r = sd_bus_message_append(m, "s", interface);
1885 if (r < 0)
1886 return r;
1887
1888 r = sd_bus_message_open_container(m, 'a', "{sv}");
1889 if (r < 0)
1890 return r;
1891
1892 key.path = prefix;
1893 key.interface = interface;
1894
1895 LIST_FOREACH(vtables, c, n->vtables) {
1896 if (require_fallback && !c->is_fallback)
1897 continue;
1898
1899 if (!streq(c->interface, interface))
1900 continue;
1901
1902 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1903 if (r < 0)
1904 return r;
1905 if (bus->nodes_modified)
1906 return 0;
1907 if (r == 0)
1908 continue;
1909
1910 *found_interface = true;
1911
1912 if (names) {
1913 /* If the caller specified a list of
1914 * properties we include exactly those in the
1915 * PropertiesChanged message */
1916
1917 STRV_FOREACH(property, names) {
1918 struct vtable_member *v;
1919
1920 assert_return(member_name_is_valid(*property), -EINVAL);
1921
1922 key.member = *property;
1923 v = hashmap_get(bus->vtable_properties, &key);
1924 if (!v)
1925 return -ENOENT;
1926
1927 /* If there are two vtables for the same
1928 * interface, let's handle this property when
1929 * we come to that vtable. */
1930 if (c != v->parent)
1931 continue;
1932
1933 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1934 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1935
1936 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1937
1938 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1939 has_invalidating = true;
1940 continue;
1941 }
1942
1943 has_changing = true;
1944
1945 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1946 if (r < 0)
1947 return r;
1948 if (bus->nodes_modified)
1949 return 0;
1950 }
1951 } else {
1952 const sd_bus_vtable *v;
1953
1954 /* If the caller specified no properties list
1955 * we include all properties that are marked
1956 * as changing in the message. */
1957
1958 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1959 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1960 continue;
1961
1962 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1963 continue;
1964
1965 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1966 has_invalidating = true;
1967 continue;
1968 }
1969
1970 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1971 continue;
1972
1973 has_changing = true;
1974
1975 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1976 if (r < 0)
1977 return r;
1978 if (bus->nodes_modified)
1979 return 0;
1980 }
1981 }
1982 }
1983
1984 if (!has_invalidating && !has_changing)
1985 return 0;
1986
1987 r = sd_bus_message_close_container(m);
1988 if (r < 0)
1989 return r;
1990
1991 r = sd_bus_message_open_container(m, 'a', "s");
1992 if (r < 0)
1993 return r;
1994
1995 if (has_invalidating) {
1996 LIST_FOREACH(vtables, c, n->vtables) {
1997 if (require_fallback && !c->is_fallback)
1998 continue;
1999
2000 if (!streq(c->interface, interface))
2001 continue;
2002
2003 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2004 if (r < 0)
2005 return r;
2006 if (bus->nodes_modified)
2007 return 0;
2008 if (r == 0)
2009 continue;
2010
2011 if (names) {
2012 STRV_FOREACH(property, names) {
2013 struct vtable_member *v;
2014
2015 key.member = *property;
2016 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2017 assert(c == v->parent);
2018
2019 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2020 continue;
2021
2022 r = sd_bus_message_append(m, "s", *property);
2023 if (r < 0)
2024 return r;
2025 }
2026 } else {
2027 const sd_bus_vtable *v;
2028
2029 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2030 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2031 continue;
2032
2033 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2034 continue;
2035
2036 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2037 continue;
2038
2039 r = sd_bus_message_append(m, "s", v->x.property.member);
2040 if (r < 0)
2041 return r;
2042 }
2043 }
2044 }
2045 }
2046
2047 r = sd_bus_message_close_container(m);
2048 if (r < 0)
2049 return r;
2050
2051 r = sd_bus_send(bus, m, NULL);
2052 if (r < 0)
2053 return r;
2054
2055 return 1;
2056 }
2057
2058 _public_ int sd_bus_emit_properties_changed_strv(
2059 sd_bus *bus,
2060 const char *path,
2061 const char *interface,
2062 char **names) {
2063
2064 BUS_DONT_DESTROY(bus);
2065 bool found_interface = false;
2066 char *prefix;
2067 int r;
2068
2069 assert_return(bus, -EINVAL);
2070 assert_return(object_path_is_valid(path), -EINVAL);
2071 assert_return(interface_name_is_valid(interface), -EINVAL);
2072 assert_return(!bus_pid_changed(bus), -ECHILD);
2073
2074 if (!BUS_IS_OPEN(bus->state))
2075 return -ENOTCONN;
2076
2077 /* A non-NULL but empty names list means nothing needs to be
2078 generated. A NULL list OTOH indicates that all properties
2079 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2080 included in the PropertiesChanged message. */
2081 if (names && names[0] == NULL)
2082 return 0;
2083
2084 do {
2085 bus->nodes_modified = false;
2086
2087 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2088 if (r != 0)
2089 return r;
2090 if (bus->nodes_modified)
2091 continue;
2092
2093 prefix = alloca(strlen(path) + 1);
2094 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2095 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2096 if (r != 0)
2097 return r;
2098 if (bus->nodes_modified)
2099 break;
2100 }
2101
2102 } while (bus->nodes_modified);
2103
2104 return found_interface ? 0 : -ENOENT;
2105 }
2106
2107 _public_ int sd_bus_emit_properties_changed(
2108 sd_bus *bus,
2109 const char *path,
2110 const char *interface,
2111 const char *name, ...) {
2112
2113 char **names;
2114
2115 assert_return(bus, -EINVAL);
2116 assert_return(object_path_is_valid(path), -EINVAL);
2117 assert_return(interface_name_is_valid(interface), -EINVAL);
2118 assert_return(!bus_pid_changed(bus), -ECHILD);
2119
2120 if (!BUS_IS_OPEN(bus->state))
2121 return -ENOTCONN;
2122
2123 if (!name)
2124 return 0;
2125
2126 names = strv_from_stdarg_alloca(name);
2127
2128 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2129 }
2130
2131 static int interfaces_added_append_one_prefix(
2132 sd_bus *bus,
2133 sd_bus_message *m,
2134 const char *prefix,
2135 const char *path,
2136 const char *interface,
2137 bool require_fallback) {
2138
2139 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2140 bool found_interface = false;
2141 struct node_vtable *c;
2142 struct node *n;
2143 void *u = NULL;
2144 int r;
2145
2146 assert(bus);
2147 assert(m);
2148 assert(prefix);
2149 assert(path);
2150 assert(interface);
2151
2152 n = hashmap_get(bus->nodes, prefix);
2153 if (!n)
2154 return 0;
2155
2156 LIST_FOREACH(vtables, c, n->vtables) {
2157 if (require_fallback && !c->is_fallback)
2158 continue;
2159
2160 if (!streq(c->interface, interface))
2161 continue;
2162
2163 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2164 if (r < 0)
2165 return r;
2166 if (bus->nodes_modified)
2167 return 0;
2168 if (r == 0)
2169 continue;
2170
2171 if (!found_interface) {
2172 r = sd_bus_message_append_basic(m, 's', interface);
2173 if (r < 0)
2174 return r;
2175
2176 r = sd_bus_message_open_container(m, 'a', "{sv}");
2177 if (r < 0)
2178 return r;
2179
2180 found_interface = true;
2181 }
2182
2183 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2184 if (r < 0)
2185 return r;
2186 if (bus->nodes_modified)
2187 return 0;
2188 }
2189
2190 if (found_interface) {
2191 r = sd_bus_message_close_container(m);
2192 if (r < 0)
2193 return r;
2194 }
2195
2196 return found_interface;
2197 }
2198
2199 static int interfaces_added_append_one(
2200 sd_bus *bus,
2201 sd_bus_message *m,
2202 const char *path,
2203 const char *interface) {
2204
2205 char *prefix;
2206 int r;
2207
2208 assert(bus);
2209 assert(m);
2210 assert(path);
2211 assert(interface);
2212
2213 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2214 if (r != 0)
2215 return r;
2216 if (bus->nodes_modified)
2217 return 0;
2218
2219 prefix = alloca(strlen(path) + 1);
2220 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2221 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2222 if (r != 0)
2223 return r;
2224 if (bus->nodes_modified)
2225 return 0;
2226 }
2227
2228 return -ENOENT;
2229 }
2230
2231 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2232 BUS_DONT_DESTROY(bus);
2233
2234 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2235 char **i;
2236 int r;
2237
2238 assert_return(bus, -EINVAL);
2239 assert_return(object_path_is_valid(path), -EINVAL);
2240 assert_return(!bus_pid_changed(bus), -ECHILD);
2241
2242 if (!BUS_IS_OPEN(bus->state))
2243 return -ENOTCONN;
2244
2245 if (strv_isempty(interfaces))
2246 return 0;
2247
2248 do {
2249 bus->nodes_modified = false;
2250 m = sd_bus_message_unref(m);
2251
2252 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2253 if (r < 0)
2254 return r;
2255
2256 r = sd_bus_message_append_basic(m, 'o', path);
2257 if (r < 0)
2258 return r;
2259
2260 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2261 if (r < 0)
2262 return r;
2263
2264 STRV_FOREACH(i, interfaces) {
2265 assert_return(interface_name_is_valid(*i), -EINVAL);
2266
2267 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2268 if (r < 0)
2269 return r;
2270
2271 r = interfaces_added_append_one(bus, m, path, *i);
2272 if (r < 0)
2273 return r;
2274
2275 if (bus->nodes_modified)
2276 break;
2277
2278 r = sd_bus_message_close_container(m);
2279 if (r < 0)
2280 return r;
2281 }
2282
2283 if (bus->nodes_modified)
2284 continue;
2285
2286 r = sd_bus_message_close_container(m);
2287 if (r < 0)
2288 return r;
2289
2290 } while (bus->nodes_modified);
2291
2292 return sd_bus_send(bus, m, NULL);
2293 }
2294
2295 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2296 char **interfaces;
2297
2298 assert_return(bus, -EINVAL);
2299 assert_return(object_path_is_valid(path), -EINVAL);
2300 assert_return(!bus_pid_changed(bus), -ECHILD);
2301
2302 if (!BUS_IS_OPEN(bus->state))
2303 return -ENOTCONN;
2304
2305 interfaces = strv_from_stdarg_alloca(interface);
2306
2307 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2308 }
2309
2310 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2311 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2312 int r;
2313
2314 assert_return(bus, -EINVAL);
2315 assert_return(object_path_is_valid(path), -EINVAL);
2316 assert_return(!bus_pid_changed(bus), -ECHILD);
2317
2318 if (!BUS_IS_OPEN(bus->state))
2319 return -ENOTCONN;
2320
2321 if (strv_isempty(interfaces))
2322 return 0;
2323
2324 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2325 if (r < 0)
2326 return r;
2327
2328 r = sd_bus_message_append_basic(m, 'o', path);
2329 if (r < 0)
2330 return r;
2331
2332 r = sd_bus_message_append_strv(m, interfaces);
2333 if (r < 0)
2334 return r;
2335
2336 return sd_bus_send(bus, m, NULL);
2337 }
2338
2339 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2340 char **interfaces;
2341
2342 assert_return(bus, -EINVAL);
2343 assert_return(object_path_is_valid(path), -EINVAL);
2344 assert_return(!bus_pid_changed(bus), -ECHILD);
2345
2346 if (!BUS_IS_OPEN(bus->state))
2347 return -ENOTCONN;
2348
2349 interfaces = strv_from_stdarg_alloca(interface);
2350
2351 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2352 }
2353
2354 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2355 sd_bus_slot *s;
2356 struct node *n;
2357 int r;
2358
2359 assert_return(bus, -EINVAL);
2360 assert_return(object_path_is_valid(path), -EINVAL);
2361 assert_return(!bus_pid_changed(bus), -ECHILD);
2362
2363 n = bus_node_allocate(bus, path);
2364 if (!n)
2365 return -ENOMEM;
2366
2367 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2368 if (!s) {
2369 r = -ENOMEM;
2370 goto fail;
2371 }
2372
2373 s->node_object_manager.node = n;
2374 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2375 bus->nodes_modified = true;
2376
2377 if (slot)
2378 *slot = s;
2379
2380 return 0;
2381
2382 fail:
2383 sd_bus_slot_unref(s);
2384 bus_node_gc(bus, n);
2385
2386 return r;
2387 }