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