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