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