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