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