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