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