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