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