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