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