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