]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd/sd-bus/bus-objects.c
Merge pull request #1159 from AnchorCat/polkit-details/v2
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-objects.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "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-util.h"
30 #include "bus-slot.h"
31 #include "bus-objects.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_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_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_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
582 _cleanup_bus_message_unref_ 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_bus_message_unref_ 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_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_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_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
905 _cleanup_bus_message_unref_ 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_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1166 _cleanup_bus_message_unref_ 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 unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1582 const struct vtable_member *m = a;
1583 uint8_t hash_key2[HASH_KEY_SIZE];
1584 unsigned long ret;
1585
1586 assert(m);
1587
1588 ret = string_hash_func(m->path, hash_key);
1589
1590 /* Use a slightly different hash key for the interface */
1591 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1592 hash_key2[0]++;
1593 ret ^= string_hash_func(m->interface, hash_key2);
1594
1595 /* And an even different one for the member */
1596 hash_key2[0]++;
1597 ret ^= string_hash_func(m->member, hash_key2);
1598
1599 return ret;
1600 }
1601
1602 static int vtable_member_compare_func(const void *a, const void *b) {
1603 const struct vtable_member *x = a, *y = b;
1604 int r;
1605
1606 assert(x);
1607 assert(y);
1608
1609 r = strcmp(x->path, y->path);
1610 if (r != 0)
1611 return r;
1612
1613 r = strcmp(x->interface, y->interface);
1614 if (r != 0)
1615 return r;
1616
1617 return strcmp(x->member, y->member);
1618 }
1619
1620 static const struct hash_ops vtable_member_hash_ops = {
1621 .hash = vtable_member_hash_func,
1622 .compare = vtable_member_compare_func
1623 };
1624
1625 static int add_object_vtable_internal(
1626 sd_bus *bus,
1627 sd_bus_slot **slot,
1628 const char *path,
1629 const char *interface,
1630 const sd_bus_vtable *vtable,
1631 bool fallback,
1632 sd_bus_object_find_t find,
1633 void *userdata) {
1634
1635 sd_bus_slot *s = NULL;
1636 struct node_vtable *i, *existing = NULL;
1637 const sd_bus_vtable *v;
1638 struct node *n;
1639 int r;
1640
1641 assert_return(bus, -EINVAL);
1642 assert_return(object_path_is_valid(path), -EINVAL);
1643 assert_return(interface_name_is_valid(interface), -EINVAL);
1644 assert_return(vtable, -EINVAL);
1645 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1646 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1647 assert_return(!bus_pid_changed(bus), -ECHILD);
1648 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1649 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1650 !streq(interface, "org.freedesktop.DBus.Peer") &&
1651 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1652
1653 r = hashmap_ensure_allocated(&bus->vtable_methods, &vtable_member_hash_ops);
1654 if (r < 0)
1655 return r;
1656
1657 r = hashmap_ensure_allocated(&bus->vtable_properties, &vtable_member_hash_ops);
1658 if (r < 0)
1659 return r;
1660
1661 n = bus_node_allocate(bus, path);
1662 if (!n)
1663 return -ENOMEM;
1664
1665 LIST_FOREACH(vtables, i, n->vtables) {
1666 if (i->is_fallback != fallback) {
1667 r = -EPROTOTYPE;
1668 goto fail;
1669 }
1670
1671 if (streq(i->interface, interface)) {
1672
1673 if (i->vtable == vtable) {
1674 r = -EEXIST;
1675 goto fail;
1676 }
1677
1678 existing = i;
1679 }
1680 }
1681
1682 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1683 if (!s) {
1684 r = -ENOMEM;
1685 goto fail;
1686 }
1687
1688 s->node_vtable.is_fallback = fallback;
1689 s->node_vtable.vtable = vtable;
1690 s->node_vtable.find = find;
1691
1692 s->node_vtable.interface = strdup(interface);
1693 if (!s->node_vtable.interface) {
1694 r = -ENOMEM;
1695 goto fail;
1696 }
1697
1698 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1699
1700 switch (v->type) {
1701
1702 case _SD_BUS_VTABLE_METHOD: {
1703 struct vtable_member *m;
1704
1705 if (!member_name_is_valid(v->x.method.member) ||
1706 !signature_is_valid(strempty(v->x.method.signature), false) ||
1707 !signature_is_valid(strempty(v->x.method.result), false) ||
1708 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1709 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1710 r = -EINVAL;
1711 goto fail;
1712 }
1713
1714 m = new0(struct vtable_member, 1);
1715 if (!m) {
1716 r = -ENOMEM;
1717 goto fail;
1718 }
1719
1720 m->parent = &s->node_vtable;
1721 m->path = n->path;
1722 m->interface = s->node_vtable.interface;
1723 m->member = v->x.method.member;
1724 m->vtable = v;
1725
1726 r = hashmap_put(bus->vtable_methods, m, m);
1727 if (r < 0) {
1728 free(m);
1729 goto fail;
1730 }
1731
1732 break;
1733 }
1734
1735 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1736
1737 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1738 r = -EINVAL;
1739 goto fail;
1740 }
1741
1742 if (v->flags & SD_BUS_VTABLE_PROPERTY_CONST) {
1743 r = -EINVAL;
1744 goto fail;
1745 }
1746
1747 /* Fall through */
1748
1749 case _SD_BUS_VTABLE_PROPERTY: {
1750 struct vtable_member *m;
1751
1752 if (!member_name_is_valid(v->x.property.member) ||
1753 !signature_is_single(v->x.property.signature, false) ||
1754 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1755 (v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY) ||
1756 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1757 ((v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) && (v->flags & SD_BUS_VTABLE_PROPERTY_EXPLICIT)) ||
1758 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1759 r = -EINVAL;
1760 goto fail;
1761 }
1762
1763 m = new0(struct vtable_member, 1);
1764 if (!m) {
1765 r = -ENOMEM;
1766 goto fail;
1767 }
1768
1769 m->parent = &s->node_vtable;
1770 m->path = n->path;
1771 m->interface = s->node_vtable.interface;
1772 m->member = v->x.property.member;
1773 m->vtable = v;
1774
1775 r = hashmap_put(bus->vtable_properties, m, m);
1776 if (r < 0) {
1777 free(m);
1778 goto fail;
1779 }
1780
1781 break;
1782 }
1783
1784 case _SD_BUS_VTABLE_SIGNAL:
1785
1786 if (!member_name_is_valid(v->x.signal.member) ||
1787 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1788 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1789 r = -EINVAL;
1790 goto fail;
1791 }
1792
1793 break;
1794
1795 default:
1796 r = -EINVAL;
1797 goto fail;
1798 }
1799 }
1800
1801 s->node_vtable.node = n;
1802 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1803 bus->nodes_modified = true;
1804
1805 if (slot)
1806 *slot = s;
1807
1808 return 0;
1809
1810 fail:
1811 sd_bus_slot_unref(s);
1812 bus_node_gc(bus, n);
1813
1814 return r;
1815 }
1816
1817 _public_ int sd_bus_add_object_vtable(
1818 sd_bus *bus,
1819 sd_bus_slot **slot,
1820 const char *path,
1821 const char *interface,
1822 const sd_bus_vtable *vtable,
1823 void *userdata) {
1824
1825 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1826 }
1827
1828 _public_ int sd_bus_add_fallback_vtable(
1829 sd_bus *bus,
1830 sd_bus_slot **slot,
1831 const char *prefix,
1832 const char *interface,
1833 const sd_bus_vtable *vtable,
1834 sd_bus_object_find_t find,
1835 void *userdata) {
1836
1837 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1838 }
1839
1840 _public_ int sd_bus_add_node_enumerator(
1841 sd_bus *bus,
1842 sd_bus_slot **slot,
1843 const char *path,
1844 sd_bus_node_enumerator_t callback,
1845 void *userdata) {
1846
1847 sd_bus_slot *s;
1848 struct node *n;
1849 int r;
1850
1851 assert_return(bus, -EINVAL);
1852 assert_return(object_path_is_valid(path), -EINVAL);
1853 assert_return(callback, -EINVAL);
1854 assert_return(!bus_pid_changed(bus), -ECHILD);
1855
1856 n = bus_node_allocate(bus, path);
1857 if (!n)
1858 return -ENOMEM;
1859
1860 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1861 if (!s) {
1862 r = -ENOMEM;
1863 goto fail;
1864 }
1865
1866 s->node_enumerator.callback = callback;
1867
1868 s->node_enumerator.node = n;
1869 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1870 bus->nodes_modified = true;
1871
1872 if (slot)
1873 *slot = s;
1874
1875 return 0;
1876
1877 fail:
1878 sd_bus_slot_unref(s);
1879 bus_node_gc(bus, n);
1880
1881 return r;
1882 }
1883
1884 static int emit_properties_changed_on_interface(
1885 sd_bus *bus,
1886 const char *prefix,
1887 const char *path,
1888 const char *interface,
1889 bool require_fallback,
1890 bool *found_interface,
1891 char **names) {
1892
1893 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1894 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1895 bool has_invalidating = false, has_changing = false;
1896 struct vtable_member key = {};
1897 struct node_vtable *c;
1898 struct node *n;
1899 char **property;
1900 void *u = NULL;
1901 int r;
1902
1903 assert(bus);
1904 assert(prefix);
1905 assert(path);
1906 assert(interface);
1907 assert(found_interface);
1908
1909 n = hashmap_get(bus->nodes, prefix);
1910 if (!n)
1911 return 0;
1912
1913 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1914 if (r < 0)
1915 return r;
1916
1917 r = sd_bus_message_append(m, "s", interface);
1918 if (r < 0)
1919 return r;
1920
1921 r = sd_bus_message_open_container(m, 'a', "{sv}");
1922 if (r < 0)
1923 return r;
1924
1925 key.path = prefix;
1926 key.interface = interface;
1927
1928 LIST_FOREACH(vtables, c, n->vtables) {
1929 if (require_fallback && !c->is_fallback)
1930 continue;
1931
1932 if (!streq(c->interface, interface))
1933 continue;
1934
1935 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1936 if (r < 0)
1937 return r;
1938 if (bus->nodes_modified)
1939 return 0;
1940 if (r == 0)
1941 continue;
1942
1943 *found_interface = true;
1944
1945 if (names) {
1946 /* If the caller specified a list of
1947 * properties we include exactly those in the
1948 * PropertiesChanged message */
1949
1950 STRV_FOREACH(property, names) {
1951 struct vtable_member *v;
1952
1953 assert_return(member_name_is_valid(*property), -EINVAL);
1954
1955 key.member = *property;
1956 v = hashmap_get(bus->vtable_properties, &key);
1957 if (!v)
1958 return -ENOENT;
1959
1960 /* If there are two vtables for the same
1961 * interface, let's handle this property when
1962 * we come to that vtable. */
1963 if (c != v->parent)
1964 continue;
1965
1966 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1967 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1968
1969 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1970
1971 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1972 has_invalidating = true;
1973 continue;
1974 }
1975
1976 has_changing = true;
1977
1978 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1979 if (r < 0)
1980 return r;
1981 if (bus->nodes_modified)
1982 return 0;
1983 }
1984 } else {
1985 const sd_bus_vtable *v;
1986
1987 /* If the caller specified no properties list
1988 * we include all properties that are marked
1989 * as changing in the message. */
1990
1991 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1992 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1993 continue;
1994
1995 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1996 continue;
1997
1998 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1999 has_invalidating = true;
2000 continue;
2001 }
2002
2003 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
2004 continue;
2005
2006 has_changing = true;
2007
2008 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
2009 if (r < 0)
2010 return r;
2011 if (bus->nodes_modified)
2012 return 0;
2013 }
2014 }
2015 }
2016
2017 if (!has_invalidating && !has_changing)
2018 return 0;
2019
2020 r = sd_bus_message_close_container(m);
2021 if (r < 0)
2022 return r;
2023
2024 r = sd_bus_message_open_container(m, 'a', "s");
2025 if (r < 0)
2026 return r;
2027
2028 if (has_invalidating) {
2029 LIST_FOREACH(vtables, c, n->vtables) {
2030 if (require_fallback && !c->is_fallback)
2031 continue;
2032
2033 if (!streq(c->interface, interface))
2034 continue;
2035
2036 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2037 if (r < 0)
2038 return r;
2039 if (bus->nodes_modified)
2040 return 0;
2041 if (r == 0)
2042 continue;
2043
2044 if (names) {
2045 STRV_FOREACH(property, names) {
2046 struct vtable_member *v;
2047
2048 key.member = *property;
2049 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2050 assert(c == v->parent);
2051
2052 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2053 continue;
2054
2055 r = sd_bus_message_append(m, "s", *property);
2056 if (r < 0)
2057 return r;
2058 }
2059 } else {
2060 const sd_bus_vtable *v;
2061
2062 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2063 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2064 continue;
2065
2066 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2067 continue;
2068
2069 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2070 continue;
2071
2072 r = sd_bus_message_append(m, "s", v->x.property.member);
2073 if (r < 0)
2074 return r;
2075 }
2076 }
2077 }
2078 }
2079
2080 r = sd_bus_message_close_container(m);
2081 if (r < 0)
2082 return r;
2083
2084 r = sd_bus_send(bus, m, NULL);
2085 if (r < 0)
2086 return r;
2087
2088 return 1;
2089 }
2090
2091 _public_ int sd_bus_emit_properties_changed_strv(
2092 sd_bus *bus,
2093 const char *path,
2094 const char *interface,
2095 char **names) {
2096
2097 BUS_DONT_DESTROY(bus);
2098 bool found_interface = false;
2099 char *prefix;
2100 int r;
2101
2102 assert_return(bus, -EINVAL);
2103 assert_return(object_path_is_valid(path), -EINVAL);
2104 assert_return(interface_name_is_valid(interface), -EINVAL);
2105 assert_return(!bus_pid_changed(bus), -ECHILD);
2106
2107 if (!BUS_IS_OPEN(bus->state))
2108 return -ENOTCONN;
2109
2110 /* A non-NULL but empty names list means nothing needs to be
2111 generated. A NULL list OTOH indicates that all properties
2112 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2113 included in the PropertiesChanged message. */
2114 if (names && names[0] == NULL)
2115 return 0;
2116
2117 do {
2118 bus->nodes_modified = false;
2119
2120 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2121 if (r != 0)
2122 return r;
2123 if (bus->nodes_modified)
2124 continue;
2125
2126 prefix = alloca(strlen(path) + 1);
2127 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2128 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2129 if (r != 0)
2130 return r;
2131 if (bus->nodes_modified)
2132 break;
2133 }
2134
2135 } while (bus->nodes_modified);
2136
2137 return found_interface ? 0 : -ENOENT;
2138 }
2139
2140 _public_ int sd_bus_emit_properties_changed(
2141 sd_bus *bus,
2142 const char *path,
2143 const char *interface,
2144 const char *name, ...) {
2145
2146 char **names;
2147
2148 assert_return(bus, -EINVAL);
2149 assert_return(object_path_is_valid(path), -EINVAL);
2150 assert_return(interface_name_is_valid(interface), -EINVAL);
2151 assert_return(!bus_pid_changed(bus), -ECHILD);
2152
2153 if (!BUS_IS_OPEN(bus->state))
2154 return -ENOTCONN;
2155
2156 if (!name)
2157 return 0;
2158
2159 names = strv_from_stdarg_alloca(name);
2160
2161 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2162 }
2163
2164 static int object_added_append_all_prefix(
2165 sd_bus *bus,
2166 sd_bus_message *m,
2167 Set *s,
2168 const char *prefix,
2169 const char *path,
2170 bool require_fallback) {
2171
2172 const char *previous_interface = NULL;
2173 struct node_vtable *c;
2174 struct node *n;
2175 int r;
2176
2177 assert(bus);
2178 assert(m);
2179 assert(s);
2180 assert(prefix);
2181 assert(path);
2182
2183 n = hashmap_get(bus->nodes, prefix);
2184 if (!n)
2185 return 0;
2186
2187 LIST_FOREACH(vtables, c, n->vtables) {
2188 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2189 void *u = NULL;
2190
2191 if (require_fallback && !c->is_fallback)
2192 continue;
2193
2194 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2195 if (r < 0)
2196 return r;
2197 if (bus->nodes_modified)
2198 return 0;
2199 if (r == 0)
2200 continue;
2201
2202 if (!streq_ptr(c->interface, previous_interface)) {
2203 /* If a child-node already handled this interface, we
2204 * skip it on any of its parents. The child vtables
2205 * always fully override any conflicting vtables of
2206 * any parent node. */
2207 if (set_get(s, c->interface))
2208 continue;
2209
2210 r = set_put(s, c->interface);
2211 if (r < 0)
2212 return r;
2213
2214 if (previous_interface) {
2215 r = sd_bus_message_close_container(m);
2216 if (r < 0)
2217 return r;
2218 r = sd_bus_message_close_container(m);
2219 if (r < 0)
2220 return r;
2221 }
2222
2223 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2224 if (r < 0)
2225 return r;
2226 r = sd_bus_message_append(m, "s", c->interface);
2227 if (r < 0)
2228 return r;
2229 r = sd_bus_message_open_container(m, 'a', "{sv}");
2230 if (r < 0)
2231 return r;
2232
2233 previous_interface = c->interface;
2234 }
2235
2236 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2237 if (r < 0)
2238 return r;
2239 if (bus->nodes_modified)
2240 return 0;
2241 }
2242
2243 if (previous_interface) {
2244 r = sd_bus_message_close_container(m);
2245 if (r < 0)
2246 return r;
2247 r = sd_bus_message_close_container(m);
2248 if (r < 0)
2249 return r;
2250 }
2251
2252 return 0;
2253 }
2254
2255 static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2256 _cleanup_set_free_ Set *s = NULL;
2257 char *prefix;
2258 int r;
2259
2260 assert(bus);
2261 assert(m);
2262 assert(path);
2263
2264 /*
2265 * This appends all interfaces registered on path @path. We first add
2266 * the builtin interfaces, which are always available and handled by
2267 * sd-bus. Then, we add all interfaces registered on the exact node,
2268 * followed by all fallback interfaces registered on any parent prefix.
2269 *
2270 * If an interface is registered multiple times on the same node with
2271 * different vtables, we merge all the properties across all vtables.
2272 * However, if a child node has the same interface registered as one of
2273 * its parent nodes has as fallback, we make the child overwrite the
2274 * parent instead of extending it. Therefore, we keep a "Set" of all
2275 * handled interfaces during parent traversal, so we skip interfaces on
2276 * a parent that were overwritten by a child.
2277 */
2278
2279 s = set_new(&string_hash_ops);
2280 if (!s)
2281 return -ENOMEM;
2282
2283 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);
2284 if (r < 0)
2285 return r;
2286 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);
2287 if (r < 0)
2288 return r;
2289 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);
2290 if (r < 0)
2291 return r;
2292 r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);
2293 if (r < 0)
2294 return r;
2295
2296 r = object_added_append_all_prefix(bus, m, s, path, path, false);
2297 if (r < 0)
2298 return r;
2299 if (bus->nodes_modified)
2300 return 0;
2301
2302 prefix = alloca(strlen(path) + 1);
2303 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2304 r = object_added_append_all_prefix(bus, m, s, prefix, path, true);
2305 if (r < 0)
2306 return r;
2307 if (bus->nodes_modified)
2308 return 0;
2309 }
2310
2311 return 0;
2312 }
2313
2314 _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) {
2315 BUS_DONT_DESTROY(bus);
2316
2317 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2318 struct node *object_manager;
2319 int r;
2320
2321 /*
2322 * This emits an InterfacesAdded signal on the given path, by iterating
2323 * all registered vtables and fallback vtables on the path. All
2324 * properties are queried and included in the signal.
2325 * This call is equivalent to sd_bus_emit_interfaces_added() with an
2326 * explicit list of registered interfaces. However, unlike
2327 * interfaces_added(), this call can figure out the list of supported
2328 * interfaces itself. Furthermore, it properly adds the builtin
2329 * org.freedesktop.DBus.* interfaces.
2330 */
2331
2332 assert_return(bus, -EINVAL);
2333 assert_return(object_path_is_valid(path), -EINVAL);
2334 assert_return(!bus_pid_changed(bus), -ECHILD);
2335
2336 if (!BUS_IS_OPEN(bus->state))
2337 return -ENOTCONN;
2338
2339 r = bus_find_parent_object_manager(bus, &object_manager, path);
2340 if (r < 0)
2341 return r;
2342 if (r == 0)
2343 return -ESRCH;
2344
2345 do {
2346 bus->nodes_modified = false;
2347 m = sd_bus_message_unref(m);
2348
2349 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2350 if (r < 0)
2351 return r;
2352
2353 r = sd_bus_message_append_basic(m, 'o', path);
2354 if (r < 0)
2355 return r;
2356
2357 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2358 if (r < 0)
2359 return r;
2360
2361 r = object_added_append_all(bus, m, path);
2362 if (r < 0)
2363 return r;
2364
2365 if (bus->nodes_modified)
2366 continue;
2367
2368 r = sd_bus_message_close_container(m);
2369 if (r < 0)
2370 return r;
2371
2372 } while (bus->nodes_modified);
2373
2374 return sd_bus_send(bus, m, NULL);
2375 }
2376
2377 static int object_removed_append_all_prefix(
2378 sd_bus *bus,
2379 sd_bus_message *m,
2380 Set *s,
2381 const char *prefix,
2382 const char *path,
2383 bool require_fallback) {
2384
2385 const char *previous_interface = NULL;
2386 struct node_vtable *c;
2387 struct node *n;
2388 int r;
2389
2390 assert(bus);
2391 assert(m);
2392 assert(s);
2393 assert(prefix);
2394 assert(path);
2395
2396 n = hashmap_get(bus->nodes, prefix);
2397 if (!n)
2398 return 0;
2399
2400 LIST_FOREACH(vtables, c, n->vtables) {
2401 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2402 void *u = NULL;
2403
2404 if (require_fallback && !c->is_fallback)
2405 continue;
2406 if (streq_ptr(c->interface, previous_interface))
2407 continue;
2408
2409 /* If a child-node already handled this interface, we
2410 * skip it on any of its parents. The child vtables
2411 * always fully override any conflicting vtables of
2412 * any parent node. */
2413 if (set_get(s, c->interface))
2414 continue;
2415
2416 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2417 if (r < 0)
2418 return r;
2419 if (bus->nodes_modified)
2420 return 0;
2421 if (r == 0)
2422 continue;
2423
2424 r = set_put(s, c->interface);
2425 if (r < 0)
2426 return r;
2427
2428 r = sd_bus_message_append(m, "s", c->interface);
2429 if (r < 0)
2430 return r;
2431
2432 previous_interface = c->interface;
2433 }
2434
2435 return 0;
2436 }
2437
2438 static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) {
2439 _cleanup_set_free_ Set *s = NULL;
2440 char *prefix;
2441 int r;
2442
2443 assert(bus);
2444 assert(m);
2445 assert(path);
2446
2447 /* see sd_bus_emit_object_added() for details */
2448
2449 s = set_new(&string_hash_ops);
2450 if (!s)
2451 return -ENOMEM;
2452
2453 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Peer");
2454 if (r < 0)
2455 return r;
2456 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Introspectable");
2457 if (r < 0)
2458 return r;
2459 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties");
2460 if (r < 0)
2461 return r;
2462 r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager");
2463 if (r < 0)
2464 return r;
2465
2466 r = object_removed_append_all_prefix(bus, m, s, path, path, false);
2467 if (r < 0)
2468 return r;
2469 if (bus->nodes_modified)
2470 return 0;
2471
2472 prefix = alloca(strlen(path) + 1);
2473 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2474 r = object_removed_append_all_prefix(bus, m, s, prefix, path, true);
2475 if (r < 0)
2476 return r;
2477 if (bus->nodes_modified)
2478 return 0;
2479 }
2480
2481 return 0;
2482 }
2483
2484 _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) {
2485 BUS_DONT_DESTROY(bus);
2486
2487 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2488 struct node *object_manager;
2489 int r;
2490
2491 /*
2492 * This is like sd_bus_emit_object_added(), but emits an
2493 * InterfacesRemoved signal on the given path. This only includes any
2494 * registered interfaces but skips the properties. Note that this will
2495 * call into the find() callbacks of any registered vtable. Therefore,
2496 * you must call this function before destroying/unlinking your object.
2497 * Otherwise, the list of interfaces will be incomplete. However, note
2498 * that this will *NOT* call into any property callback. Therefore, the
2499 * object might be in an "destructed" state, as long as we can find it.
2500 */
2501
2502 assert_return(bus, -EINVAL);
2503 assert_return(object_path_is_valid(path), -EINVAL);
2504 assert_return(!bus_pid_changed(bus), -ECHILD);
2505
2506 if (!BUS_IS_OPEN(bus->state))
2507 return -ENOTCONN;
2508
2509 r = bus_find_parent_object_manager(bus, &object_manager, path);
2510 if (r < 0)
2511 return r;
2512 if (r == 0)
2513 return -ESRCH;
2514
2515 do {
2516 bus->nodes_modified = false;
2517 m = sd_bus_message_unref(m);
2518
2519 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2520 if (r < 0)
2521 return r;
2522
2523 r = sd_bus_message_append_basic(m, 'o', path);
2524 if (r < 0)
2525 return r;
2526
2527 r = sd_bus_message_open_container(m, 'a', "s");
2528 if (r < 0)
2529 return r;
2530
2531 r = object_removed_append_all(bus, m, path);
2532 if (r < 0)
2533 return r;
2534
2535 if (bus->nodes_modified)
2536 continue;
2537
2538 r = sd_bus_message_close_container(m);
2539 if (r < 0)
2540 return r;
2541
2542 } while (bus->nodes_modified);
2543
2544 return sd_bus_send(bus, m, NULL);
2545 }
2546
2547 static int interfaces_added_append_one_prefix(
2548 sd_bus *bus,
2549 sd_bus_message *m,
2550 const char *prefix,
2551 const char *path,
2552 const char *interface,
2553 bool require_fallback) {
2554
2555 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2556 bool found_interface = false;
2557 struct node_vtable *c;
2558 struct node *n;
2559 void *u = NULL;
2560 int r;
2561
2562 assert(bus);
2563 assert(m);
2564 assert(prefix);
2565 assert(path);
2566 assert(interface);
2567
2568 n = hashmap_get(bus->nodes, prefix);
2569 if (!n)
2570 return 0;
2571
2572 LIST_FOREACH(vtables, c, n->vtables) {
2573 if (require_fallback && !c->is_fallback)
2574 continue;
2575
2576 if (!streq(c->interface, interface))
2577 continue;
2578
2579 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2580 if (r < 0)
2581 return r;
2582 if (bus->nodes_modified)
2583 return 0;
2584 if (r == 0)
2585 continue;
2586
2587 if (!found_interface) {
2588 r = sd_bus_message_append_basic(m, 's', interface);
2589 if (r < 0)
2590 return r;
2591
2592 r = sd_bus_message_open_container(m, 'a', "{sv}");
2593 if (r < 0)
2594 return r;
2595
2596 found_interface = true;
2597 }
2598
2599 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2600 if (r < 0)
2601 return r;
2602 if (bus->nodes_modified)
2603 return 0;
2604 }
2605
2606 if (found_interface) {
2607 r = sd_bus_message_close_container(m);
2608 if (r < 0)
2609 return r;
2610 }
2611
2612 return found_interface;
2613 }
2614
2615 static int interfaces_added_append_one(
2616 sd_bus *bus,
2617 sd_bus_message *m,
2618 const char *path,
2619 const char *interface) {
2620
2621 char *prefix;
2622 int r;
2623
2624 assert(bus);
2625 assert(m);
2626 assert(path);
2627 assert(interface);
2628
2629 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2630 if (r != 0)
2631 return r;
2632 if (bus->nodes_modified)
2633 return 0;
2634
2635 prefix = alloca(strlen(path) + 1);
2636 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2637 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2638 if (r != 0)
2639 return r;
2640 if (bus->nodes_modified)
2641 return 0;
2642 }
2643
2644 return -ENOENT;
2645 }
2646
2647 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2648 BUS_DONT_DESTROY(bus);
2649
2650 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2651 struct node *object_manager;
2652 char **i;
2653 int r;
2654
2655 assert_return(bus, -EINVAL);
2656 assert_return(object_path_is_valid(path), -EINVAL);
2657 assert_return(!bus_pid_changed(bus), -ECHILD);
2658
2659 if (!BUS_IS_OPEN(bus->state))
2660 return -ENOTCONN;
2661
2662 if (strv_isempty(interfaces))
2663 return 0;
2664
2665 r = bus_find_parent_object_manager(bus, &object_manager, path);
2666 if (r < 0)
2667 return r;
2668 if (r == 0)
2669 return -ESRCH;
2670
2671 do {
2672 bus->nodes_modified = false;
2673 m = sd_bus_message_unref(m);
2674
2675 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2676 if (r < 0)
2677 return r;
2678
2679 r = sd_bus_message_append_basic(m, 'o', path);
2680 if (r < 0)
2681 return r;
2682
2683 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2684 if (r < 0)
2685 return r;
2686
2687 STRV_FOREACH(i, interfaces) {
2688 assert_return(interface_name_is_valid(*i), -EINVAL);
2689
2690 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2691 if (r < 0)
2692 return r;
2693
2694 r = interfaces_added_append_one(bus, m, path, *i);
2695 if (r < 0)
2696 return r;
2697
2698 if (bus->nodes_modified)
2699 break;
2700
2701 r = sd_bus_message_close_container(m);
2702 if (r < 0)
2703 return r;
2704 }
2705
2706 if (bus->nodes_modified)
2707 continue;
2708
2709 r = sd_bus_message_close_container(m);
2710 if (r < 0)
2711 return r;
2712
2713 } while (bus->nodes_modified);
2714
2715 return sd_bus_send(bus, m, NULL);
2716 }
2717
2718 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2719 char **interfaces;
2720
2721 assert_return(bus, -EINVAL);
2722 assert_return(object_path_is_valid(path), -EINVAL);
2723 assert_return(!bus_pid_changed(bus), -ECHILD);
2724
2725 if (!BUS_IS_OPEN(bus->state))
2726 return -ENOTCONN;
2727
2728 interfaces = strv_from_stdarg_alloca(interface);
2729
2730 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2731 }
2732
2733 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2734 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2735 struct node *object_manager;
2736 int r;
2737
2738 assert_return(bus, -EINVAL);
2739 assert_return(object_path_is_valid(path), -EINVAL);
2740 assert_return(!bus_pid_changed(bus), -ECHILD);
2741
2742 if (!BUS_IS_OPEN(bus->state))
2743 return -ENOTCONN;
2744
2745 if (strv_isempty(interfaces))
2746 return 0;
2747
2748 r = bus_find_parent_object_manager(bus, &object_manager, path);
2749 if (r < 0)
2750 return r;
2751 if (r == 0)
2752 return -ESRCH;
2753
2754 r = sd_bus_message_new_signal(bus, &m, object_manager->path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2755 if (r < 0)
2756 return r;
2757
2758 r = sd_bus_message_append_basic(m, 'o', path);
2759 if (r < 0)
2760 return r;
2761
2762 r = sd_bus_message_append_strv(m, interfaces);
2763 if (r < 0)
2764 return r;
2765
2766 return sd_bus_send(bus, m, NULL);
2767 }
2768
2769 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2770 char **interfaces;
2771
2772 assert_return(bus, -EINVAL);
2773 assert_return(object_path_is_valid(path), -EINVAL);
2774 assert_return(!bus_pid_changed(bus), -ECHILD);
2775
2776 if (!BUS_IS_OPEN(bus->state))
2777 return -ENOTCONN;
2778
2779 interfaces = strv_from_stdarg_alloca(interface);
2780
2781 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2782 }
2783
2784 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2785 sd_bus_slot *s;
2786 struct node *n;
2787 int r;
2788
2789 assert_return(bus, -EINVAL);
2790 assert_return(object_path_is_valid(path), -EINVAL);
2791 assert_return(!bus_pid_changed(bus), -ECHILD);
2792
2793 n = bus_node_allocate(bus, path);
2794 if (!n)
2795 return -ENOMEM;
2796
2797 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2798 if (!s) {
2799 r = -ENOMEM;
2800 goto fail;
2801 }
2802
2803 s->node_object_manager.node = n;
2804 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2805 bus->nodes_modified = true;
2806
2807 if (slot)
2808 *slot = s;
2809
2810 return 0;
2811
2812 fail:
2813 sd_bus_slot_unref(s);
2814 bus_node_gc(bus, n);
2815
2816 return r;
2817 }