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