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