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