1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2013 Lennart Poettering
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.
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.
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/>.
22 #include <sys/capability.h>
26 #include "bus-internal.h"
27 #include "bus-message.h"
29 #include "bus-signature.h"
30 #include "bus-introspect.h"
33 #include "bus-objects.h"
35 static int node_vtable_get_userdata(
38 struct node_vtable *c,
40 sd_bus_error *error) {
50 s = container_of(c, sd_bus_slot, node_vtable);
53 bus->current_slot = sd_bus_slot_ref(s);
54 bus->current_userdata = u;
55 r = c->find(bus, path, c->interface, u, &u, error);
56 bus->current_userdata = NULL;
57 bus->current_slot = sd_bus_slot_unref(s);
61 if (sd_bus_error_is_set(error))
62 return -sd_bus_error_get_errno(error);
73 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
76 return (uint8_t*) u + p->x.property.offset;
79 static int vtable_property_get_userdata(
82 struct vtable_member *p,
84 sd_bus_error *error) {
94 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
97 if (bus->nodes_modified)
100 *userdata = vtable_property_convert_userdata(p->vtable, u);
104 static int add_enumerated_to_set(
107 struct node_enumerator *first,
109 sd_bus_error *error) {
111 struct node_enumerator *c;
118 LIST_FOREACH(enumerators, c, first) {
119 char **children = NULL, **k;
122 if (bus->nodes_modified)
125 slot = container_of(c, sd_bus_slot, node_enumerator);
127 bus->current_slot = sd_bus_slot_ref(slot);
128 bus->current_userdata = slot->userdata;
129 r = c->callback(bus, prefix, slot->userdata, &children, error);
130 bus->current_userdata = NULL;
131 bus->current_slot = sd_bus_slot_unref(slot);
135 if (sd_bus_error_is_set(error))
136 return -sd_bus_error_get_errno(error);
138 STRV_FOREACH(k, children) {
144 if (!object_path_is_valid(*k)){
150 if (!object_path_startswith(*k, prefix)) {
155 r = set_consume(s, *k);
168 static int add_subtree_to_set(
173 sd_bus_error *error) {
183 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
186 if (bus->nodes_modified)
189 LIST_FOREACH(siblings, i, n->child) {
192 if (!object_path_startswith(i->path, prefix))
199 r = set_consume(s, t);
200 if (r < 0 && r != -EEXIST)
203 r = add_subtree_to_set(bus, prefix, i, s, error);
206 if (bus->nodes_modified)
213 static int get_child_nodes(
218 sd_bus_error *error) {
228 s = set_new(string_hash_func, string_compare_func);
232 r = add_subtree_to_set(bus, prefix, n, s, error);
242 static int node_callbacks_run(
245 struct node_callback *first,
246 bool require_fallback,
247 bool *found_object) {
249 struct node_callback *c;
254 assert(found_object);
256 LIST_FOREACH(callbacks, c, first) {
257 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
260 if (bus->nodes_modified)
263 if (require_fallback && !c->is_fallback)
266 *found_object = true;
268 if (c->last_iteration == bus->iteration_counter)
271 c->last_iteration = bus->iteration_counter;
273 r = sd_bus_message_rewind(m, true);
277 slot = container_of(c, sd_bus_slot, node_callback);
279 bus->current_slot = sd_bus_slot_ref(slot);
280 bus->current_handler = c->callback;
281 bus->current_userdata = slot->userdata;
282 r = c->callback(bus, m, slot->userdata, &error_buffer);
283 bus->current_userdata = NULL;
284 bus->current_handler = NULL;
285 bus->current_slot = sd_bus_slot_unref(slot);
287 r = bus_maybe_reply_error(m, r, &error_buffer);
295 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
297 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
305 /* If the entire bus is trusted let's grant access */
309 /* If the member is marked UNPRIVILEGED let's grant access */
310 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
313 /* Check have the caller has the requested capability
314 * set. Note that the flags value contains the capability
315 * number plus one, which we need to subtract here. We do this
316 * so that we have 0 as special value for "default
318 cap = CAPABILITY_SHIFT(c->vtable->flags);
320 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
326 r = sd_bus_query_sender_privilege(m, cap);
332 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
335 static int method_callbacks_run(
338 struct vtable_member *c,
339 bool require_fallback,
340 bool *found_object) {
342 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
343 const char *signature;
350 assert(found_object);
352 if (require_fallback && !c->parent->is_fallback)
355 r = check_access(bus, m, c, &error);
357 return bus_maybe_reply_error(m, r, &error);
359 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
361 return bus_maybe_reply_error(m, r, &error);
362 if (bus->nodes_modified)
365 *found_object = true;
367 if (c->last_iteration == bus->iteration_counter)
370 c->last_iteration = bus->iteration_counter;
372 r = sd_bus_message_rewind(m, true);
376 signature = sd_bus_message_get_signature(m, true);
380 if (!streq(strempty(c->vtable->x.method.signature), signature))
381 return sd_bus_reply_method_errorf(
383 SD_BUS_ERROR_INVALID_ARGS,
384 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
385 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
387 /* Keep track what the signature of the reply to this message
388 * should be, so that this can be enforced when sealing the
390 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
392 if (c->vtable->x.method.handler) {
395 slot = container_of(c->parent, sd_bus_slot, node_vtable);
397 bus->current_slot = sd_bus_slot_ref(slot);
398 bus->current_handler = c->vtable->x.method.handler;
399 bus->current_userdata = u;
400 r = c->vtable->x.method.handler(bus, m, u, &error);
401 bus->current_userdata = NULL;
402 bus->current_handler = NULL;
403 bus->current_slot = sd_bus_slot_unref(slot);
405 return bus_maybe_reply_error(m, r, &error);
408 /* If the method callback is NULL, make this a successful NOP */
409 r = sd_bus_reply_method_return(m, NULL);
416 static int invoke_property_get(
419 const sd_bus_vtable *v,
421 const char *interface,
422 const char *property,
423 sd_bus_message *reply,
425 sd_bus_error *error) {
438 if (v->x.property.get) {
440 bus->current_slot = sd_bus_slot_ref(slot);
441 bus->current_userdata = userdata;
442 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
443 bus->current_userdata = NULL;
444 bus->current_slot = sd_bus_slot_unref(slot);
448 if (sd_bus_error_is_set(error))
449 return -sd_bus_error_get_errno(error);
453 /* Automatic handling if no callback is defined. */
455 if (streq(v->x.property.signature, "as"))
456 return sd_bus_message_append_strv(reply, *(char***) userdata);
458 assert(signature_is_single(v->x.property.signature, false));
459 assert(bus_type_is_basic(v->x.property.signature[0]));
461 switch (v->x.property.signature[0]) {
463 case SD_BUS_TYPE_STRING:
464 case SD_BUS_TYPE_SIGNATURE:
465 p = strempty(*(char**) userdata);
468 case SD_BUS_TYPE_OBJECT_PATH:
469 p = *(char**) userdata;
478 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
481 static int invoke_property_set(
484 const sd_bus_vtable *v,
486 const char *interface,
487 const char *property,
488 sd_bus_message *value,
490 sd_bus_error *error) {
502 if (v->x.property.set) {
504 bus->current_slot = sd_bus_slot_ref(slot);
505 bus->current_userdata = userdata;
506 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
507 bus->current_userdata = NULL;
508 bus->current_slot = sd_bus_slot_unref(slot);
512 if (sd_bus_error_is_set(error))
513 return -sd_bus_error_get_errno(error);
517 /* Automatic handling if no callback is defined. */
519 assert(signature_is_single(v->x.property.signature, false));
520 assert(bus_type_is_basic(v->x.property.signature[0]));
522 switch (v->x.property.signature[0]) {
524 case SD_BUS_TYPE_STRING:
525 case SD_BUS_TYPE_OBJECT_PATH:
526 case SD_BUS_TYPE_SIGNATURE: {
530 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
538 free(*(char**) userdata);
539 *(char**) userdata = n;
545 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
555 static int property_get_set_callbacks_run(
558 struct vtable_member *c,
559 bool require_fallback,
561 bool *found_object) {
563 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
564 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
572 assert(found_object);
574 if (require_fallback && !c->parent->is_fallback)
577 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
579 return bus_maybe_reply_error(m, r, &error);
580 if (bus->nodes_modified)
583 slot = container_of(c->parent, sd_bus_slot, node_vtable);
585 *found_object = true;
587 r = sd_bus_message_new_method_return(m, &reply);
592 /* Note that we do not protect against reexecution
593 * here (using the last_iteration check, see below),
594 * should the node tree have changed and we got called
595 * again. We assume that property Get() calls are
596 * ultimately without side-effects or if they aren't
597 * then at least idempotent. */
599 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
603 /* Note that we do not do an access check here. Read
604 * access to properties is always unrestricted, since
605 * PropertiesChanged signals broadcast contents
608 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
610 return bus_maybe_reply_error(m, r, &error);
612 if (bus->nodes_modified)
615 r = sd_bus_message_close_container(reply);
620 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
621 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
623 /* Avoid that we call the set routine more than once
624 * if the processing of this message got restarted
625 * because the node tree changed. */
626 if (c->last_iteration == bus->iteration_counter)
629 c->last_iteration = bus->iteration_counter;
631 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
635 r = check_access(bus, m, c, &error);
637 return bus_maybe_reply_error(m, r, &error);
639 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
641 return bus_maybe_reply_error(m, r, &error);
643 if (bus->nodes_modified)
646 r = sd_bus_message_exit_container(m);
651 r = sd_bus_send(bus, reply, NULL);
658 static int vtable_append_one_property(
660 sd_bus_message *reply,
662 struct node_vtable *c,
663 const sd_bus_vtable *v,
665 sd_bus_error *error) {
676 r = sd_bus_message_open_container(reply, 'e', "sv");
680 r = sd_bus_message_append(reply, "s", v->x.property.member);
684 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
688 slot = container_of(c, sd_bus_slot, node_vtable);
690 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
693 if (bus->nodes_modified)
696 r = sd_bus_message_close_container(reply);
700 r = sd_bus_message_close_container(reply);
707 static int vtable_append_all_properties(
709 sd_bus_message *reply,
711 struct node_vtable *c,
713 sd_bus_error *error) {
715 const sd_bus_vtable *v;
723 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
726 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
727 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
730 if (v->flags & SD_BUS_VTABLE_HIDDEN)
733 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
736 if (bus->nodes_modified)
743 static int property_get_all_callbacks_run(
746 struct node_vtable *first,
747 bool require_fallback,
749 bool *found_object) {
751 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
752 struct node_vtable *c;
753 bool found_interface;
758 assert(found_object);
760 r = sd_bus_message_new_method_return(m, &reply);
764 r = sd_bus_message_open_container(reply, 'a', "{sv}");
768 found_interface = !iface ||
769 streq(iface, "org.freedesktop.DBus.Properties") ||
770 streq(iface, "org.freedesktop.DBus.Peer") ||
771 streq(iface, "org.freedesktop.DBus.Introspectable");
773 LIST_FOREACH(vtables, c, first) {
774 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
777 if (require_fallback && !c->is_fallback)
780 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
782 return bus_maybe_reply_error(m, r, &error);
783 if (bus->nodes_modified)
788 *found_object = true;
790 if (iface && !streq(c->interface, iface))
792 found_interface = true;
794 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
796 return bus_maybe_reply_error(m, r, &error);
797 if (bus->nodes_modified)
801 if (!found_interface) {
802 r = sd_bus_reply_method_errorf(
804 SD_BUS_ERROR_UNKNOWN_INTERFACE,
805 "Unknown interface '%s'.", iface);
812 r = sd_bus_message_close_container(reply);
816 r = sd_bus_send(bus, reply, NULL);
823 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
827 if (n->object_managers)
831 return bus_node_with_object_manager(bus, n->parent);
836 static bool bus_node_exists(
840 bool require_fallback) {
842 struct node_vtable *c;
843 struct node_callback *k;
849 /* Tests if there's anything attached directly to this node
850 * for the specified path */
852 LIST_FOREACH(callbacks, k, n->callbacks) {
853 if (require_fallback && !k->is_fallback)
859 LIST_FOREACH(vtables, c, n->vtables) {
860 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
862 if (require_fallback && !c->is_fallback)
865 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
867 if (bus->nodes_modified)
871 return !require_fallback && (n->enumerators || n->object_managers);
874 static int process_introspect(
878 bool require_fallback,
879 bool *found_object) {
881 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
882 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
883 _cleanup_set_free_free_ Set *s = NULL;
884 const char *previous_interface = NULL;
885 struct introspect intro;
886 struct node_vtable *c;
893 assert(found_object);
895 r = get_child_nodes(bus, m->path, n, &s, &error);
897 return bus_maybe_reply_error(m, r, &error);
898 if (bus->nodes_modified)
901 r = introspect_begin(&intro, bus->trusted);
905 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
909 empty = set_isempty(s);
911 LIST_FOREACH(vtables, c, n->vtables) {
912 if (require_fallback && !c->is_fallback)
915 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
917 r = bus_maybe_reply_error(m, r, &error);
920 if (bus->nodes_modified) {
929 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
932 if (!streq_ptr(previous_interface, c->interface)) {
934 if (previous_interface)
935 fputs(" </interface>\n", intro.f);
937 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
940 r = introspect_write_interface(&intro, c->vtable);
944 previous_interface = c->interface;
947 if (previous_interface)
948 fputs(" </interface>\n", intro.f);
951 /* Nothing?, let's see if we exist at all, and if not
952 * refuse to do anything */
953 r = bus_node_exists(bus, n, m->path, require_fallback);
956 if (bus->nodes_modified)
962 *found_object = true;
964 r = introspect_write_child_nodes(&intro, s, m->path);
968 r = introspect_finish(&intro, bus, m, &reply);
972 r = sd_bus_send(bus, reply, NULL);
979 introspect_free(&intro);
983 static int object_manager_serialize_path(
985 sd_bus_message *reply,
988 bool require_fallback,
989 sd_bus_error *error) {
991 const char *previous_interface = NULL;
992 bool found_something = false;
993 struct node_vtable *i;
1003 n = hashmap_get(bus->nodes, prefix);
1007 LIST_FOREACH(vtables, i, n->vtables) {
1010 if (require_fallback && !i->is_fallback)
1013 r = node_vtable_get_userdata(bus, path, i, &u, error);
1016 if (bus->nodes_modified)
1021 if (!found_something) {
1023 /* Open the object part */
1025 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1029 r = sd_bus_message_append(reply, "o", path);
1033 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1037 found_something = true;
1040 if (!streq_ptr(previous_interface, i->interface)) {
1042 /* Maybe close the previous interface part */
1044 if (previous_interface) {
1045 r = sd_bus_message_close_container(reply);
1049 r = sd_bus_message_close_container(reply);
1054 /* Open the new interface part */
1056 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1060 r = sd_bus_message_append(reply, "s", i->interface);
1064 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1069 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1072 if (bus->nodes_modified)
1075 previous_interface = i->interface;
1078 if (previous_interface) {
1079 r = sd_bus_message_close_container(reply);
1083 r = sd_bus_message_close_container(reply);
1088 if (found_something) {
1089 r = sd_bus_message_close_container(reply);
1093 r = sd_bus_message_close_container(reply);
1101 static int object_manager_serialize_path_and_fallbacks(
1103 sd_bus_message *reply,
1105 sd_bus_error *error) {
1115 /* First, add all vtables registered for this path */
1116 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1119 if (bus->nodes_modified)
1122 /* Second, add fallback vtables registered for any of the prefixes */
1123 prefix = alloca(strlen(path) + 1);
1124 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1125 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1128 if (bus->nodes_modified)
1135 static int process_get_managed_objects(
1139 bool require_fallback,
1140 bool *found_object) {
1142 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1143 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1144 _cleanup_set_free_free_ Set *s = NULL;
1151 assert(found_object);
1153 if (!bus_node_with_object_manager(bus, n))
1156 r = get_child_nodes(bus, m->path, n, &s, &error);
1159 if (bus->nodes_modified)
1162 r = sd_bus_message_new_method_return(m, &reply);
1166 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1170 empty = set_isempty(s);
1172 struct node_vtable *c;
1174 /* Hmm, so we have no children? Then let's check
1175 * whether we exist at all, i.e. whether at least one
1178 LIST_FOREACH(vtables, c, n->vtables) {
1180 if (require_fallback && !c->is_fallback)
1198 SET_FOREACH(path, s, i) {
1199 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1203 if (bus->nodes_modified)
1208 r = sd_bus_message_close_container(reply);
1212 r = sd_bus_send(bus, reply, NULL);
1219 static int object_find_and_run(
1223 bool require_fallback,
1224 bool *found_object) {
1227 struct vtable_member vtable_key, *v;
1233 assert(found_object);
1235 n = hashmap_get(bus->nodes, p);
1239 /* First, try object callbacks */
1240 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1243 if (bus->nodes_modified)
1246 if (!m->interface || !m->member)
1249 /* Then, look for a known method */
1250 vtable_key.path = (char*) p;
1251 vtable_key.interface = m->interface;
1252 vtable_key.member = m->member;
1254 v = hashmap_get(bus->vtable_methods, &vtable_key);
1256 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1259 if (bus->nodes_modified)
1263 /* Then, look for a known property */
1264 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1267 get = streq(m->member, "Get");
1269 if (get || streq(m->member, "Set")) {
1271 r = sd_bus_message_rewind(m, true);
1275 vtable_key.path = (char*) p;
1277 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1279 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1281 v = hashmap_get(bus->vtable_properties, &vtable_key);
1283 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1288 } else if (streq(m->member, "GetAll")) {
1291 r = sd_bus_message_rewind(m, true);
1295 r = sd_bus_message_read(m, "s", &iface);
1297 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1302 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1307 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1309 if (!isempty(sd_bus_message_get_signature(m, true)))
1310 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1312 r = process_introspect(bus, m, n, require_fallback, found_object);
1316 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1318 if (!isempty(sd_bus_message_get_signature(m, true)))
1319 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1321 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1326 if (bus->nodes_modified)
1329 if (!*found_object) {
1330 r = bus_node_exists(bus, n, m->path, require_fallback);
1334 *found_object = true;
1340 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1343 bool found_object = false;
1348 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1351 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1354 if (hashmap_isempty(bus->nodes))
1357 /* Never respond to broadcast messages */
1358 if (bus->bus_client && !m->destination)
1364 pl = strlen(m->path);
1368 bus->nodes_modified = false;
1370 r = object_find_and_run(bus, m, m->path, false, &found_object);
1374 /* Look for fallback prefixes */
1375 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1377 if (bus->nodes_modified)
1380 r = object_find_and_run(bus, m, prefix, true, &found_object);
1385 } while (bus->nodes_modified);
1390 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1391 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1392 r = sd_bus_reply_method_errorf(
1394 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1395 "Unknown property or interface.");
1397 r = sd_bus_reply_method_errorf(
1399 SD_BUS_ERROR_UNKNOWN_METHOD,
1400 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1408 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1409 struct node *n, *parent;
1411 _cleanup_free_ char *s = NULL;
1417 assert(path[0] == '/');
1419 n = hashmap_get(bus->nodes, path);
1423 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1431 if (streq(path, "/"))
1434 e = strrchr(path, '/');
1437 p = strndupa(path, MAX(1, path - e));
1439 parent = bus_node_allocate(bus, p);
1444 n = new0(struct node, 1);
1450 s = NULL; /* do not free */
1452 r = hashmap_put(bus->nodes, n->path, n);
1460 LIST_PREPEND(siblings, parent->child, n);
1465 void bus_node_gc(sd_bus *b, struct node *n) {
1478 assert(hashmap_remove(b->nodes, n->path) == n);
1481 LIST_REMOVE(siblings, n->parent->child, n);
1484 bus_node_gc(b, n->parent);
1488 static int bus_add_object(
1493 sd_bus_message_handler_t callback,
1500 assert_return(bus, -EINVAL);
1501 assert_return(object_path_is_valid(path), -EINVAL);
1502 assert_return(callback, -EINVAL);
1503 assert_return(!bus_pid_changed(bus), -ECHILD);
1505 n = bus_node_allocate(bus, path);
1509 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1515 s->node_callback.callback = callback;
1516 s->node_callback.is_fallback = fallback;
1518 s->node_callback.node = n;
1519 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1520 bus->nodes_modified = true;
1528 sd_bus_slot_unref(s);
1529 bus_node_gc(bus, n);
1534 _public_ int sd_bus_add_object(
1538 sd_bus_message_handler_t callback,
1541 return bus_add_object(bus, slot, false, path, callback, userdata);
1544 _public_ int sd_bus_add_fallback(
1548 sd_bus_message_handler_t callback,
1551 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1554 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1555 const struct vtable_member *m = a;
1556 uint8_t hash_key2[HASH_KEY_SIZE];
1561 ret = string_hash_func(m->path, hash_key);
1563 /* Use a slightly different hash key for the interface */
1564 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1566 ret ^= string_hash_func(m->interface, hash_key2);
1568 /* And an even different one for the member */
1570 ret ^= string_hash_func(m->member, hash_key2);
1575 static int vtable_member_compare_func(const void *a, const void *b) {
1576 const struct vtable_member *x = a, *y = b;
1582 r = strcmp(x->path, y->path);
1586 r = strcmp(x->interface, y->interface);
1590 return strcmp(x->member, y->member);
1593 static int add_object_vtable_internal(
1597 const char *interface,
1598 const sd_bus_vtable *vtable,
1600 sd_bus_object_find_t find,
1603 sd_bus_slot *s = NULL;
1604 struct node_vtable *i, *existing = NULL;
1605 const sd_bus_vtable *v;
1609 assert_return(bus, -EINVAL);
1610 assert_return(object_path_is_valid(path), -EINVAL);
1611 assert_return(interface_name_is_valid(interface), -EINVAL);
1612 assert_return(vtable, -EINVAL);
1613 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1614 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1615 assert_return(!bus_pid_changed(bus), -ECHILD);
1616 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1617 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1618 !streq(interface, "org.freedesktop.DBus.Peer") &&
1619 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1621 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1625 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1629 n = bus_node_allocate(bus, path);
1633 LIST_FOREACH(vtables, i, n->vtables) {
1634 if (i->is_fallback != fallback) {
1639 if (streq(i->interface, interface)) {
1641 if (i->vtable == vtable) {
1650 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1656 s->node_vtable.is_fallback = fallback;
1657 s->node_vtable.vtable = vtable;
1658 s->node_vtable.find = find;
1660 s->node_vtable.interface = strdup(interface);
1661 if (!s->node_vtable.interface) {
1666 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1670 case _SD_BUS_VTABLE_METHOD: {
1671 struct vtable_member *m;
1673 if (!member_name_is_valid(v->x.method.member) ||
1674 !signature_is_valid(strempty(v->x.method.signature), false) ||
1675 !signature_is_valid(strempty(v->x.method.result), false) ||
1676 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1677 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1682 m = new0(struct vtable_member, 1);
1688 m->parent = &s->node_vtable;
1690 m->interface = s->node_vtable.interface;
1691 m->member = v->x.method.member;
1694 r = hashmap_put(bus->vtable_methods, m, m);
1703 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1705 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1712 case _SD_BUS_VTABLE_PROPERTY: {
1713 struct vtable_member *m;
1715 if (!member_name_is_valid(v->x.property.member) ||
1716 !signature_is_single(v->x.property.signature, false) ||
1717 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1718 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1719 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1720 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1725 m = new0(struct vtable_member, 1);
1731 m->parent = &s->node_vtable;
1733 m->interface = s->node_vtable.interface;
1734 m->member = v->x.property.member;
1737 r = hashmap_put(bus->vtable_properties, m, m);
1746 case _SD_BUS_VTABLE_SIGNAL:
1748 if (!member_name_is_valid(v->x.signal.member) ||
1749 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1750 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1763 s->node_vtable.node = n;
1764 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1765 bus->nodes_modified = true;
1773 sd_bus_slot_unref(s);
1774 bus_node_gc(bus, n);
1779 _public_ int sd_bus_add_object_vtable(
1783 const char *interface,
1784 const sd_bus_vtable *vtable,
1787 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1790 _public_ int sd_bus_add_fallback_vtable(
1794 const char *interface,
1795 const sd_bus_vtable *vtable,
1796 sd_bus_object_find_t find,
1799 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1802 _public_ int sd_bus_add_node_enumerator(
1806 sd_bus_node_enumerator_t callback,
1813 assert_return(bus, -EINVAL);
1814 assert_return(object_path_is_valid(path), -EINVAL);
1815 assert_return(callback, -EINVAL);
1816 assert_return(!bus_pid_changed(bus), -ECHILD);
1818 n = bus_node_allocate(bus, path);
1822 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1828 s->node_enumerator.callback = callback;
1830 s->node_enumerator.node = n;
1831 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1832 bus->nodes_modified = true;
1840 sd_bus_slot_unref(s);
1841 bus_node_gc(bus, n);
1846 static int emit_properties_changed_on_interface(
1850 const char *interface,
1851 bool require_fallback,
1852 bool *found_interface,
1855 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1856 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1857 bool has_invalidating = false, has_changing = false;
1858 struct vtable_member key = {};
1859 struct node_vtable *c;
1869 assert(found_interface);
1871 n = hashmap_get(bus->nodes, prefix);
1875 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1879 r = sd_bus_message_append(m, "s", interface);
1883 r = sd_bus_message_open_container(m, 'a', "{sv}");
1888 key.interface = interface;
1890 LIST_FOREACH(vtables, c, n->vtables) {
1891 if (require_fallback && !c->is_fallback)
1894 if (!streq(c->interface, interface))
1897 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1900 if (bus->nodes_modified)
1905 *found_interface = true;
1908 /* If the caller specified a list of
1909 * properties we include exactly those in the
1910 * PropertiesChanged message */
1912 STRV_FOREACH(property, names) {
1913 struct vtable_member *v;
1915 assert_return(member_name_is_valid(*property), -EINVAL);
1917 key.member = *property;
1918 v = hashmap_get(bus->vtable_properties, &key);
1922 /* If there are two vtables for the same
1923 * interface, let's handle this property when
1924 * we come to that vtable. */
1928 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1929 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1931 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1933 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1934 has_invalidating = true;
1938 has_changing = true;
1940 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1943 if (bus->nodes_modified)
1947 const sd_bus_vtable *v;
1949 /* If the caller specified no properties list
1950 * we include all properties that are marked
1951 * as changing in the message. */
1953 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1954 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1957 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1960 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1961 has_invalidating = true;
1965 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1968 has_changing = true;
1970 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1973 if (bus->nodes_modified)
1979 if (!has_invalidating && !has_changing)
1982 r = sd_bus_message_close_container(m);
1986 r = sd_bus_message_open_container(m, 'a', "s");
1990 if (has_invalidating) {
1991 LIST_FOREACH(vtables, c, n->vtables) {
1992 if (require_fallback && !c->is_fallback)
1995 if (!streq(c->interface, interface))
1998 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2001 if (bus->nodes_modified)
2007 STRV_FOREACH(property, names) {
2008 struct vtable_member *v;
2010 key.member = *property;
2011 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2012 assert(c == v->parent);
2014 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2017 r = sd_bus_message_append(m, "s", *property);
2022 const sd_bus_vtable *v;
2024 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2025 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2028 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2031 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2034 r = sd_bus_message_append(m, "s", v->x.property.member);
2042 r = sd_bus_message_close_container(m);
2046 r = sd_bus_send(bus, m, NULL);
2053 _public_ int sd_bus_emit_properties_changed_strv(
2056 const char *interface,
2059 BUS_DONT_DESTROY(bus);
2060 bool found_interface = false;
2064 assert_return(bus, -EINVAL);
2065 assert_return(object_path_is_valid(path), -EINVAL);
2066 assert_return(interface_name_is_valid(interface), -EINVAL);
2067 assert_return(!bus_pid_changed(bus), -ECHILD);
2069 if (!BUS_IS_OPEN(bus->state))
2072 /* A non-NULL but empty names list means nothing needs to be
2073 generated. A NULL list OTOH indicates that all properties
2074 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2075 included in the PropertiesChanged message. */
2076 if (names && names[0] == NULL)
2080 bus->nodes_modified = false;
2082 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2085 if (bus->nodes_modified)
2088 prefix = alloca(strlen(path) + 1);
2089 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2090 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2093 if (bus->nodes_modified)
2097 } while (bus->nodes_modified);
2099 return found_interface ? 0 : -ENOENT;
2102 _public_ int sd_bus_emit_properties_changed(
2105 const char *interface,
2106 const char *name, ...) {
2110 assert_return(bus, -EINVAL);
2111 assert_return(object_path_is_valid(path), -EINVAL);
2112 assert_return(interface_name_is_valid(interface), -EINVAL);
2113 assert_return(!bus_pid_changed(bus), -ECHILD);
2115 if (!BUS_IS_OPEN(bus->state))
2121 names = strv_from_stdarg_alloca(name);
2123 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2126 static int interfaces_added_append_one_prefix(
2131 const char *interface,
2132 bool require_fallback) {
2134 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2135 bool found_interface = false;
2136 struct node_vtable *c;
2147 n = hashmap_get(bus->nodes, prefix);
2151 LIST_FOREACH(vtables, c, n->vtables) {
2152 if (require_fallback && !c->is_fallback)
2155 if (!streq(c->interface, interface))
2158 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2161 if (bus->nodes_modified)
2166 if (!found_interface) {
2167 r = sd_bus_message_append_basic(m, 's', interface);
2171 r = sd_bus_message_open_container(m, 'a', "{sv}");
2175 found_interface = true;
2178 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2181 if (bus->nodes_modified)
2185 if (found_interface) {
2186 r = sd_bus_message_close_container(m);
2191 return found_interface;
2194 static int interfaces_added_append_one(
2198 const char *interface) {
2208 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2211 if (bus->nodes_modified)
2214 prefix = alloca(strlen(path) + 1);
2215 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2216 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2219 if (bus->nodes_modified)
2226 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2227 BUS_DONT_DESTROY(bus);
2229 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2233 assert_return(bus, -EINVAL);
2234 assert_return(object_path_is_valid(path), -EINVAL);
2235 assert_return(!bus_pid_changed(bus), -ECHILD);
2237 if (!BUS_IS_OPEN(bus->state))
2240 if (strv_isempty(interfaces))
2244 bus->nodes_modified = false;
2245 m = sd_bus_message_unref(m);
2247 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2251 r = sd_bus_message_append_basic(m, 'o', path);
2255 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2259 STRV_FOREACH(i, interfaces) {
2260 assert_return(interface_name_is_valid(*i), -EINVAL);
2262 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2266 r = interfaces_added_append_one(bus, m, path, *i);
2270 if (bus->nodes_modified)
2273 r = sd_bus_message_close_container(m);
2278 if (bus->nodes_modified)
2281 r = sd_bus_message_close_container(m);
2285 } while (bus->nodes_modified);
2287 return sd_bus_send(bus, m, NULL);
2290 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2293 assert_return(bus, -EINVAL);
2294 assert_return(object_path_is_valid(path), -EINVAL);
2295 assert_return(!bus_pid_changed(bus), -ECHILD);
2297 if (!BUS_IS_OPEN(bus->state))
2300 interfaces = strv_from_stdarg_alloca(interface);
2302 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2305 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2306 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2309 assert_return(bus, -EINVAL);
2310 assert_return(object_path_is_valid(path), -EINVAL);
2311 assert_return(!bus_pid_changed(bus), -ECHILD);
2313 if (!BUS_IS_OPEN(bus->state))
2316 if (strv_isempty(interfaces))
2319 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2323 r = sd_bus_message_append_basic(m, 'o', path);
2327 r = sd_bus_message_append_strv(m, interfaces);
2331 return sd_bus_send(bus, m, NULL);
2334 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2337 assert_return(bus, -EINVAL);
2338 assert_return(object_path_is_valid(path), -EINVAL);
2339 assert_return(!bus_pid_changed(bus), -ECHILD);
2341 if (!BUS_IS_OPEN(bus->state))
2344 interfaces = strv_from_stdarg_alloca(interface);
2346 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2349 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2354 assert_return(bus, -EINVAL);
2355 assert_return(object_path_is_valid(path), -EINVAL);
2356 assert_return(!bus_pid_changed(bus), -ECHILD);
2358 n = bus_node_allocate(bus, path);
2362 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2368 s->node_object_manager.node = n;
2369 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2370 bus->nodes_modified = true;
2378 sd_bus_slot_unref(s);
2379 bus_node_gc(bus, n);