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