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