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