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 r = c->find(bus, path, c->interface, u, &u, error);
55 bus->current_slot = sd_bus_slot_unref(s);
59 if (sd_bus_error_is_set(error))
60 return -sd_bus_error_get_errno(error);
71 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
74 return (uint8_t*) u + p->x.property.offset;
77 static int vtable_property_get_userdata(
80 struct vtable_member *p,
82 sd_bus_error *error) {
92 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
95 if (bus->nodes_modified)
98 *userdata = vtable_property_convert_userdata(p->vtable, u);
102 static int add_enumerated_to_set(
105 struct node_enumerator *first,
107 sd_bus_error *error) {
109 struct node_enumerator *c;
116 LIST_FOREACH(enumerators, c, first) {
117 char **children = NULL, **k;
120 if (bus->nodes_modified)
123 slot = container_of(c, sd_bus_slot, node_enumerator);
125 bus->current_slot = sd_bus_slot_ref(slot);
126 r = c->callback(bus, prefix, slot->userdata, &children, error);
127 bus->current_slot = sd_bus_slot_unref(slot);
131 if (sd_bus_error_is_set(error))
132 return -sd_bus_error_get_errno(error);
134 STRV_FOREACH(k, children) {
140 if (!object_path_is_valid(*k)){
146 if (!object_path_startswith(*k, prefix)) {
151 r = set_consume(s, *k);
164 static int add_subtree_to_set(
169 sd_bus_error *error) {
179 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
182 if (bus->nodes_modified)
185 LIST_FOREACH(siblings, i, n->child) {
188 if (!object_path_startswith(i->path, prefix))
195 r = set_consume(s, t);
196 if (r < 0 && r != -EEXIST)
199 r = add_subtree_to_set(bus, prefix, i, s, error);
202 if (bus->nodes_modified)
209 static int get_child_nodes(
214 sd_bus_error *error) {
224 s = set_new(string_hash_func, string_compare_func);
228 r = add_subtree_to_set(bus, prefix, n, s, error);
238 static int node_callbacks_run(
241 struct node_callback *first,
242 bool require_fallback,
243 bool *found_object) {
245 struct node_callback *c;
250 assert(found_object);
252 LIST_FOREACH(callbacks, c, first) {
253 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
256 if (bus->nodes_modified)
259 if (require_fallback && !c->is_fallback)
262 *found_object = true;
264 if (c->last_iteration == bus->iteration_counter)
267 c->last_iteration = bus->iteration_counter;
269 r = sd_bus_message_rewind(m, true);
273 slot = container_of(c, sd_bus_slot, node_callback);
275 bus->current_slot = sd_bus_slot_ref(slot);
276 r = c->callback(bus, m, slot->userdata, &error_buffer);
277 bus->current_slot = sd_bus_slot_unref(slot);
279 r = bus_maybe_reply_error(m, r, &error_buffer);
287 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
289 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
290 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
298 /* If the entire bus is trusted let's grant access */
302 /* If the member is marked UNPRIVILEGED let's grant access */
303 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
306 /* Check have the caller has the requested capability
307 * set. Note that the flags value contains the capability
308 * number plus one, which we need to subtract here. We do this
309 * so that we have 0 as special value for "default
311 cap = CAPABILITY_SHIFT(c->vtable->flags);
313 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
319 r = sd_bus_query_sender_privilege(m, cap);
325 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
328 static int method_callbacks_run(
331 struct vtable_member *c,
332 bool require_fallback,
333 bool *found_object) {
335 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
336 const char *signature;
343 assert(found_object);
345 if (require_fallback && !c->parent->is_fallback)
348 r = check_access(bus, m, c, &error);
350 return bus_maybe_reply_error(m, r, &error);
352 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
354 return bus_maybe_reply_error(m, r, &error);
355 if (bus->nodes_modified)
358 *found_object = true;
360 if (c->last_iteration == bus->iteration_counter)
363 c->last_iteration = bus->iteration_counter;
365 r = sd_bus_message_rewind(m, true);
369 signature = sd_bus_message_get_signature(m, true);
373 if (!streq(strempty(c->vtable->x.method.signature), signature))
374 return sd_bus_reply_method_errorf(
376 SD_BUS_ERROR_INVALID_ARGS,
377 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
378 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
380 /* Keep track what the signature of the reply to this message
381 * should be, so that this can be enforced when sealing the
383 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
385 if (c->vtable->x.method.handler) {
388 slot = container_of(c->parent, sd_bus_slot, node_vtable);
390 bus->current_slot = sd_bus_slot_ref(slot);
391 r = c->vtable->x.method.handler(bus, m, u, &error);
392 bus->current_slot = sd_bus_slot_unref(slot);
394 return bus_maybe_reply_error(m, r, &error);
397 /* If the method callback is NULL, make this a successful NOP */
398 r = sd_bus_reply_method_return(m, NULL);
405 static int invoke_property_get(
408 const sd_bus_vtable *v,
410 const char *interface,
411 const char *property,
412 sd_bus_message *reply,
414 sd_bus_error *error) {
427 if (v->x.property.get) {
429 bus->current_slot = sd_bus_slot_ref(slot);
430 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
431 bus->current_slot = sd_bus_slot_unref(slot);
435 if (sd_bus_error_is_set(error))
436 return -sd_bus_error_get_errno(error);
440 /* Automatic handling if no callback is defined. */
442 if (streq(v->x.property.signature, "as"))
443 return sd_bus_message_append_strv(reply, *(char***) userdata);
445 assert(signature_is_single(v->x.property.signature, false));
446 assert(bus_type_is_basic(v->x.property.signature[0]));
448 switch (v->x.property.signature[0]) {
450 case SD_BUS_TYPE_STRING:
451 case SD_BUS_TYPE_SIGNATURE:
452 p = strempty(*(char**) userdata);
455 case SD_BUS_TYPE_OBJECT_PATH:
456 p = *(char**) userdata;
465 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
468 static int invoke_property_set(
471 const sd_bus_vtable *v,
473 const char *interface,
474 const char *property,
475 sd_bus_message *value,
477 sd_bus_error *error) {
489 if (v->x.property.set) {
491 bus->current_slot = sd_bus_slot_ref(slot);
492 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
493 bus->current_slot = sd_bus_slot_unref(slot);
497 if (sd_bus_error_is_set(error))
498 return -sd_bus_error_get_errno(error);
502 /* Automatic handling if no callback is defined. */
504 assert(signature_is_single(v->x.property.signature, false));
505 assert(bus_type_is_basic(v->x.property.signature[0]));
507 switch (v->x.property.signature[0]) {
509 case SD_BUS_TYPE_STRING:
510 case SD_BUS_TYPE_OBJECT_PATH:
511 case SD_BUS_TYPE_SIGNATURE: {
515 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
523 free(*(char**) userdata);
524 *(char**) userdata = n;
530 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
540 static int property_get_set_callbacks_run(
543 struct vtable_member *c,
544 bool require_fallback,
546 bool *found_object) {
548 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
549 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
557 assert(found_object);
559 if (require_fallback && !c->parent->is_fallback)
562 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
564 return bus_maybe_reply_error(m, r, &error);
565 if (bus->nodes_modified)
568 slot = container_of(c->parent, sd_bus_slot, node_vtable);
570 *found_object = true;
572 r = sd_bus_message_new_method_return(m, &reply);
577 /* Note that we do not protect against reexecution
578 * here (using the last_iteration check, see below),
579 * should the node tree have changed and we got called
580 * again. We assume that property Get() calls are
581 * ultimately without side-effects or if they aren't
582 * then at least idempotent. */
584 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
588 /* Note that we do not do an access check here. Read
589 * access to properties is always unrestricted, since
590 * PropertiesChanged signals broadcast contents
593 r = invoke_property_get(bus, slot, c->vtable, m->path, c->interface, c->member, reply, u, &error);
595 return bus_maybe_reply_error(m, r, &error);
597 if (bus->nodes_modified)
600 r = sd_bus_message_close_container(reply);
605 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
606 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
608 /* Avoid that we call the set routine more than once
609 * if the processing of this message got restarted
610 * because the node tree changed. */
611 if (c->last_iteration == bus->iteration_counter)
614 c->last_iteration = bus->iteration_counter;
616 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
620 r = check_access(bus, m, c, &error);
622 return bus_maybe_reply_error(m, r, &error);
624 r = invoke_property_set(bus, slot, c->vtable, m->path, c->interface, c->member, m, u, &error);
626 return bus_maybe_reply_error(m, r, &error);
628 if (bus->nodes_modified)
631 r = sd_bus_message_exit_container(m);
636 r = sd_bus_send(bus, reply, NULL);
643 static int vtable_append_one_property(
645 sd_bus_message *reply,
647 struct node_vtable *c,
648 const sd_bus_vtable *v,
650 sd_bus_error *error) {
661 r = sd_bus_message_open_container(reply, 'e', "sv");
665 r = sd_bus_message_append(reply, "s", v->x.property.member);
669 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
673 slot = container_of(c, sd_bus_slot, node_vtable);
675 r = invoke_property_get(bus, slot, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
678 if (bus->nodes_modified)
681 r = sd_bus_message_close_container(reply);
685 r = sd_bus_message_close_container(reply);
692 static int vtable_append_all_properties(
694 sd_bus_message *reply,
696 struct node_vtable *c,
698 sd_bus_error *error) {
700 const sd_bus_vtable *v;
708 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
711 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
712 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
715 if (v->flags & SD_BUS_VTABLE_HIDDEN)
718 r = vtable_append_one_property(bus, reply, path, c, v, userdata, error);
721 if (bus->nodes_modified)
728 static int property_get_all_callbacks_run(
731 struct node_vtable *first,
732 bool require_fallback,
734 bool *found_object) {
736 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
737 struct node_vtable *c;
738 bool found_interface;
743 assert(found_object);
745 r = sd_bus_message_new_method_return(m, &reply);
749 r = sd_bus_message_open_container(reply, 'a', "{sv}");
753 found_interface = !iface ||
754 streq(iface, "org.freedesktop.DBus.Properties") ||
755 streq(iface, "org.freedesktop.DBus.Peer") ||
756 streq(iface, "org.freedesktop.DBus.Introspectable");
758 LIST_FOREACH(vtables, c, first) {
759 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
762 if (require_fallback && !c->is_fallback)
765 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
767 return bus_maybe_reply_error(m, r, &error);
768 if (bus->nodes_modified)
773 *found_object = true;
775 if (iface && !streq(c->interface, iface))
777 found_interface = true;
779 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
781 return bus_maybe_reply_error(m, r, &error);
782 if (bus->nodes_modified)
786 if (!found_interface) {
787 r = sd_bus_reply_method_errorf(
789 SD_BUS_ERROR_UNKNOWN_INTERFACE,
790 "Unknown interface '%s'.", iface);
797 r = sd_bus_message_close_container(reply);
801 r = sd_bus_send(bus, reply, NULL);
808 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
812 if (n->object_managers)
816 return bus_node_with_object_manager(bus, n->parent);
821 static bool bus_node_exists(
825 bool require_fallback) {
827 struct node_vtable *c;
828 struct node_callback *k;
834 /* Tests if there's anything attached directly to this node
835 * for the specified path */
837 LIST_FOREACH(callbacks, k, n->callbacks) {
838 if (require_fallback && !k->is_fallback)
844 LIST_FOREACH(vtables, c, n->vtables) {
845 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
847 if (require_fallback && !c->is_fallback)
850 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
852 if (bus->nodes_modified)
856 return !require_fallback && (n->enumerators || n->object_managers);
859 static int process_introspect(
863 bool require_fallback,
864 bool *found_object) {
866 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
867 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
868 _cleanup_set_free_free_ Set *s = NULL;
869 const char *previous_interface = NULL;
870 struct introspect intro;
871 struct node_vtable *c;
878 assert(found_object);
880 r = get_child_nodes(bus, m->path, n, &s, &error);
882 return bus_maybe_reply_error(m, r, &error);
883 if (bus->nodes_modified)
886 r = introspect_begin(&intro, bus->trusted);
890 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
894 empty = set_isempty(s);
896 LIST_FOREACH(vtables, c, n->vtables) {
897 if (require_fallback && !c->is_fallback)
900 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
902 r = bus_maybe_reply_error(m, r, &error);
905 if (bus->nodes_modified) {
914 if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN)
917 if (!streq_ptr(previous_interface, c->interface)) {
919 if (previous_interface)
920 fputs(" </interface>\n", intro.f);
922 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
925 r = introspect_write_interface(&intro, c->vtable);
929 previous_interface = c->interface;
932 if (previous_interface)
933 fputs(" </interface>\n", intro.f);
936 /* Nothing?, let's see if we exist at all, and if not
937 * refuse to do anything */
938 r = bus_node_exists(bus, n, m->path, require_fallback);
941 if (bus->nodes_modified)
947 *found_object = true;
949 r = introspect_write_child_nodes(&intro, s, m->path);
953 r = introspect_finish(&intro, bus, m, &reply);
957 r = sd_bus_send(bus, reply, NULL);
964 introspect_free(&intro);
968 static int object_manager_serialize_path(
970 sd_bus_message *reply,
973 bool require_fallback,
974 sd_bus_error *error) {
976 const char *previous_interface = NULL;
977 bool found_something = false;
978 struct node_vtable *i;
988 n = hashmap_get(bus->nodes, prefix);
992 LIST_FOREACH(vtables, i, n->vtables) {
995 if (require_fallback && !i->is_fallback)
998 r = node_vtable_get_userdata(bus, path, i, &u, error);
1001 if (bus->nodes_modified)
1006 if (!found_something) {
1008 /* Open the object part */
1010 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
1014 r = sd_bus_message_append(reply, "o", path);
1018 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
1022 found_something = true;
1025 if (!streq_ptr(previous_interface, i->interface)) {
1027 /* Maybe close the previous interface part */
1029 if (previous_interface) {
1030 r = sd_bus_message_close_container(reply);
1034 r = sd_bus_message_close_container(reply);
1039 /* Open the new interface part */
1041 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
1045 r = sd_bus_message_append(reply, "s", i->interface);
1049 r = sd_bus_message_open_container(reply, 'a', "{sv}");
1054 r = vtable_append_all_properties(bus, reply, path, i, u, error);
1057 if (bus->nodes_modified)
1060 previous_interface = i->interface;
1063 if (previous_interface) {
1064 r = sd_bus_message_close_container(reply);
1068 r = sd_bus_message_close_container(reply);
1073 if (found_something) {
1074 r = sd_bus_message_close_container(reply);
1078 r = sd_bus_message_close_container(reply);
1086 static int object_manager_serialize_path_and_fallbacks(
1088 sd_bus_message *reply,
1090 sd_bus_error *error) {
1100 /* First, add all vtables registered for this path */
1101 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1104 if (bus->nodes_modified)
1107 /* Second, add fallback vtables registered for any of the prefixes */
1108 prefix = alloca(strlen(path) + 1);
1109 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1110 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1113 if (bus->nodes_modified)
1120 static int process_get_managed_objects(
1124 bool require_fallback,
1125 bool *found_object) {
1127 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1128 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1129 _cleanup_set_free_free_ Set *s = NULL;
1136 assert(found_object);
1138 if (!bus_node_with_object_manager(bus, n))
1141 r = get_child_nodes(bus, m->path, n, &s, &error);
1144 if (bus->nodes_modified)
1147 r = sd_bus_message_new_method_return(m, &reply);
1151 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1155 empty = set_isempty(s);
1157 struct node_vtable *c;
1159 /* Hmm, so we have no children? Then let's check
1160 * whether we exist at all, i.e. whether at least one
1163 LIST_FOREACH(vtables, c, n->vtables) {
1165 if (require_fallback && !c->is_fallback)
1183 SET_FOREACH(path, s, i) {
1184 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1188 if (bus->nodes_modified)
1193 r = sd_bus_message_close_container(reply);
1197 r = sd_bus_send(bus, reply, NULL);
1204 static int object_find_and_run(
1208 bool require_fallback,
1209 bool *found_object) {
1212 struct vtable_member vtable_key, *v;
1218 assert(found_object);
1220 n = hashmap_get(bus->nodes, p);
1224 /* First, try object callbacks */
1225 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1228 if (bus->nodes_modified)
1231 if (!m->interface || !m->member)
1234 /* Then, look for a known method */
1235 vtable_key.path = (char*) p;
1236 vtable_key.interface = m->interface;
1237 vtable_key.member = m->member;
1239 v = hashmap_get(bus->vtable_methods, &vtable_key);
1241 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1244 if (bus->nodes_modified)
1248 /* Then, look for a known property */
1249 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1252 get = streq(m->member, "Get");
1254 if (get || streq(m->member, "Set")) {
1256 r = sd_bus_message_rewind(m, true);
1260 vtable_key.path = (char*) p;
1262 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1264 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1266 v = hashmap_get(bus->vtable_properties, &vtable_key);
1268 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1273 } else if (streq(m->member, "GetAll")) {
1276 r = sd_bus_message_rewind(m, true);
1280 r = sd_bus_message_read(m, "s", &iface);
1282 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1287 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1292 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1294 if (!isempty(sd_bus_message_get_signature(m, true)))
1295 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1297 r = process_introspect(bus, m, n, require_fallback, found_object);
1301 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1303 if (!isempty(sd_bus_message_get_signature(m, true)))
1304 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1306 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1311 if (bus->nodes_modified)
1314 if (!*found_object) {
1315 r = bus_node_exists(bus, n, m->path, require_fallback);
1319 *found_object = true;
1325 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1328 bool found_object = false;
1333 if (bus->hello_flags & KDBUS_HELLO_MONITOR)
1336 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1339 if (hashmap_isempty(bus->nodes))
1342 /* Never respond to broadcast messages */
1343 if (bus->bus_client && !m->destination)
1349 pl = strlen(m->path);
1353 bus->nodes_modified = false;
1355 r = object_find_and_run(bus, m, m->path, false, &found_object);
1359 /* Look for fallback prefixes */
1360 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1362 if (bus->nodes_modified)
1365 r = object_find_and_run(bus, m, prefix, true, &found_object);
1370 } while (bus->nodes_modified);
1375 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1376 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1377 r = sd_bus_reply_method_errorf(
1379 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1380 "Unknown property or interface.");
1382 r = sd_bus_reply_method_errorf(
1384 SD_BUS_ERROR_UNKNOWN_METHOD,
1385 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1393 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1394 struct node *n, *parent;
1396 _cleanup_free_ char *s = NULL;
1402 assert(path[0] == '/');
1404 n = hashmap_get(bus->nodes, path);
1408 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1416 if (streq(path, "/"))
1419 e = strrchr(path, '/');
1422 p = strndupa(path, MAX(1, path - e));
1424 parent = bus_node_allocate(bus, p);
1429 n = new0(struct node, 1);
1435 s = NULL; /* do not free */
1437 r = hashmap_put(bus->nodes, n->path, n);
1445 LIST_PREPEND(siblings, parent->child, n);
1450 void bus_node_gc(sd_bus *b, struct node *n) {
1463 assert(hashmap_remove(b->nodes, n->path) == n);
1466 LIST_REMOVE(siblings, n->parent->child, n);
1469 bus_node_gc(b, n->parent);
1473 static int bus_add_object(
1478 sd_bus_message_handler_t callback,
1485 assert_return(bus, -EINVAL);
1486 assert_return(object_path_is_valid(path), -EINVAL);
1487 assert_return(callback, -EINVAL);
1488 assert_return(!bus_pid_changed(bus), -ECHILD);
1490 n = bus_node_allocate(bus, path);
1494 s = bus_slot_allocate(bus, !slot, BUS_NODE_CALLBACK, sizeof(struct node_callback), userdata);
1500 s->node_callback.callback = callback;
1501 s->node_callback.is_fallback = fallback;
1503 s->node_callback.node = n;
1504 LIST_PREPEND(callbacks, n->callbacks, &s->node_callback);
1505 bus->nodes_modified = true;
1513 sd_bus_slot_unref(s);
1514 bus_node_gc(bus, n);
1519 _public_ int sd_bus_add_object(
1523 sd_bus_message_handler_t callback,
1526 return bus_add_object(bus, slot, false, path, callback, userdata);
1529 _public_ int sd_bus_add_fallback(
1533 sd_bus_message_handler_t callback,
1536 return bus_add_object(bus, slot, true, prefix, callback, userdata);
1539 static unsigned long vtable_member_hash_func(const void *a, const uint8_t hash_key[HASH_KEY_SIZE]) {
1540 const struct vtable_member *m = a;
1541 uint8_t hash_key2[HASH_KEY_SIZE];
1546 ret = string_hash_func(m->path, hash_key);
1548 /* Use a slightly different hash key for the interface */
1549 memcpy(hash_key2, hash_key, HASH_KEY_SIZE);
1551 ret ^= string_hash_func(m->interface, hash_key2);
1553 /* And an even different one for the member */
1555 ret ^= string_hash_func(m->member, hash_key2);
1560 static int vtable_member_compare_func(const void *a, const void *b) {
1561 const struct vtable_member *x = a, *y = b;
1567 r = strcmp(x->path, y->path);
1571 r = strcmp(x->interface, y->interface);
1575 return strcmp(x->member, y->member);
1578 static int add_object_vtable_internal(
1582 const char *interface,
1583 const sd_bus_vtable *vtable,
1585 sd_bus_object_find_t find,
1588 sd_bus_slot *s = NULL;
1589 struct node_vtable *i, *existing = NULL;
1590 const sd_bus_vtable *v;
1594 assert_return(bus, -EINVAL);
1595 assert_return(object_path_is_valid(path), -EINVAL);
1596 assert_return(interface_name_is_valid(interface), -EINVAL);
1597 assert_return(vtable, -EINVAL);
1598 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1599 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1600 assert_return(!bus_pid_changed(bus), -ECHILD);
1601 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1602 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1603 !streq(interface, "org.freedesktop.DBus.Peer") &&
1604 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1606 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1610 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1614 n = bus_node_allocate(bus, path);
1618 LIST_FOREACH(vtables, i, n->vtables) {
1619 if (i->is_fallback != fallback) {
1624 if (streq(i->interface, interface)) {
1626 if (i->vtable == vtable) {
1635 s = bus_slot_allocate(bus, !slot, BUS_NODE_VTABLE, sizeof(struct node_vtable), userdata);
1641 s->node_vtable.is_fallback = fallback;
1642 s->node_vtable.vtable = vtable;
1643 s->node_vtable.find = find;
1645 s->node_vtable.interface = strdup(interface);
1646 if (!s->node_vtable.interface) {
1651 for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1655 case _SD_BUS_VTABLE_METHOD: {
1656 struct vtable_member *m;
1658 if (!member_name_is_valid(v->x.method.member) ||
1659 !signature_is_valid(strempty(v->x.method.signature), false) ||
1660 !signature_is_valid(strempty(v->x.method.result), false) ||
1661 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1662 v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) {
1667 m = new0(struct vtable_member, 1);
1673 m->parent = &s->node_vtable;
1675 m->interface = s->node_vtable.interface;
1676 m->member = v->x.method.member;
1679 r = hashmap_put(bus->vtable_methods, m, m);
1688 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1690 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1697 case _SD_BUS_VTABLE_PROPERTY: {
1698 struct vtable_member *m;
1700 if (!member_name_is_valid(v->x.property.member) ||
1701 !signature_is_single(v->x.property.signature, false) ||
1702 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1703 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1704 (!!(v->flags & SD_BUS_VTABLE_PROPERTY_CONST) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE) + !!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) > 1 ||
1705 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1710 m = new0(struct vtable_member, 1);
1716 m->parent = &s->node_vtable;
1718 m->interface = s->node_vtable.interface;
1719 m->member = v->x.property.member;
1722 r = hashmap_put(bus->vtable_properties, m, m);
1731 case _SD_BUS_VTABLE_SIGNAL:
1733 if (!member_name_is_valid(v->x.signal.member) ||
1734 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1735 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1748 s->node_vtable.node = n;
1749 LIST_INSERT_AFTER(vtables, n->vtables, existing, &s->node_vtable);
1750 bus->nodes_modified = true;
1758 sd_bus_slot_unref(s);
1759 bus_node_gc(bus, n);
1764 _public_ int sd_bus_add_object_vtable(
1768 const char *interface,
1769 const sd_bus_vtable *vtable,
1772 return add_object_vtable_internal(bus, slot, path, interface, vtable, false, NULL, userdata);
1775 _public_ int sd_bus_add_fallback_vtable(
1779 const char *interface,
1780 const sd_bus_vtable *vtable,
1781 sd_bus_object_find_t find,
1784 return add_object_vtable_internal(bus, slot, prefix, interface, vtable, true, find, userdata);
1787 _public_ int sd_bus_add_node_enumerator(
1791 sd_bus_node_enumerator_t callback,
1798 assert_return(bus, -EINVAL);
1799 assert_return(object_path_is_valid(path), -EINVAL);
1800 assert_return(callback, -EINVAL);
1801 assert_return(!bus_pid_changed(bus), -ECHILD);
1803 n = bus_node_allocate(bus, path);
1807 s = bus_slot_allocate(bus, !slot, BUS_NODE_ENUMERATOR, sizeof(struct node_enumerator), userdata);
1813 s->node_enumerator.callback = callback;
1815 s->node_enumerator.node = n;
1816 LIST_PREPEND(enumerators, n->enumerators, &s->node_enumerator);
1817 bus->nodes_modified = true;
1825 sd_bus_slot_unref(s);
1826 bus_node_gc(bus, n);
1831 static int emit_properties_changed_on_interface(
1835 const char *interface,
1836 bool require_fallback,
1837 bool *found_interface,
1840 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1841 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1842 bool has_invalidating = false, has_changing = false;
1843 struct vtable_member key = {};
1844 struct node_vtable *c;
1854 assert(found_interface);
1856 n = hashmap_get(bus->nodes, prefix);
1860 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.Properties", "PropertiesChanged");
1864 r = sd_bus_message_append(m, "s", interface);
1868 r = sd_bus_message_open_container(m, 'a', "{sv}");
1873 key.interface = interface;
1875 LIST_FOREACH(vtables, c, n->vtables) {
1876 if (require_fallback && !c->is_fallback)
1879 if (!streq(c->interface, interface))
1882 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1885 if (bus->nodes_modified)
1890 *found_interface = true;
1893 /* If the caller specified a list of
1894 * properties we include exactly those in the
1895 * PropertiesChanged message */
1897 STRV_FOREACH(property, names) {
1898 struct vtable_member *v;
1900 assert_return(member_name_is_valid(*property), -EINVAL);
1902 key.member = *property;
1903 v = hashmap_get(bus->vtable_properties, &key);
1907 /* If there are two vtables for the same
1908 * interface, let's handle this property when
1909 * we come to that vtable. */
1913 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE ||
1914 v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION, -EDOM);
1916 assert_return(!(v->vtable->flags & SD_BUS_VTABLE_HIDDEN), -EDOM);
1918 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1919 has_invalidating = true;
1923 has_changing = true;
1925 r = vtable_append_one_property(bus, m, m->path, c, v->vtable, u, &error);
1928 if (bus->nodes_modified)
1932 const sd_bus_vtable *v;
1934 /* If the caller specified no properties list
1935 * we include all properties that are marked
1936 * as changing in the message. */
1938 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1939 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
1942 if (v->flags & SD_BUS_VTABLE_HIDDEN)
1945 if (v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION) {
1946 has_invalidating = true;
1950 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE))
1953 has_changing = true;
1955 r = vtable_append_one_property(bus, m, m->path, c, v, u, &error);
1958 if (bus->nodes_modified)
1964 if (!has_invalidating && !has_changing)
1967 r = sd_bus_message_close_container(m);
1971 r = sd_bus_message_open_container(m, 'a', "s");
1975 if (has_invalidating) {
1976 LIST_FOREACH(vtables, c, n->vtables) {
1977 if (require_fallback && !c->is_fallback)
1980 if (!streq(c->interface, interface))
1983 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1986 if (bus->nodes_modified)
1992 STRV_FOREACH(property, names) {
1993 struct vtable_member *v;
1995 key.member = *property;
1996 assert_se(v = hashmap_get(bus->vtable_properties, &key));
1997 assert(c == v->parent);
1999 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2002 r = sd_bus_message_append(m, "s", *property);
2007 const sd_bus_vtable *v;
2009 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
2010 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
2013 if (v->flags & SD_BUS_VTABLE_HIDDEN)
2016 if (!(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION))
2019 r = sd_bus_message_append(m, "s", v->x.property.member);
2027 r = sd_bus_message_close_container(m);
2031 r = sd_bus_send(bus, m, NULL);
2038 _public_ int sd_bus_emit_properties_changed_strv(
2041 const char *interface,
2044 BUS_DONT_DESTROY(bus);
2045 bool found_interface = false;
2049 assert_return(bus, -EINVAL);
2050 assert_return(object_path_is_valid(path), -EINVAL);
2051 assert_return(interface_name_is_valid(interface), -EINVAL);
2052 assert_return(!bus_pid_changed(bus), -ECHILD);
2054 if (!BUS_IS_OPEN(bus->state))
2057 /* A non-NULL but empty names list means nothing needs to be
2058 generated. A NULL list OTOH indicates that all properties
2059 that are set to EMITS_CHANGE or EMITS_INVALIDATION shall be
2060 included in the PropertiesChanged message. */
2061 if (names && names[0] == NULL)
2065 bus->nodes_modified = false;
2067 r = emit_properties_changed_on_interface(bus, path, path, interface, false, &found_interface, names);
2070 if (bus->nodes_modified)
2073 prefix = alloca(strlen(path) + 1);
2074 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2075 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names);
2078 if (bus->nodes_modified)
2082 } while (bus->nodes_modified);
2084 return found_interface ? 0 : -ENOENT;
2087 _public_ int sd_bus_emit_properties_changed(
2090 const char *interface,
2091 const char *name, ...) {
2095 assert_return(bus, -EINVAL);
2096 assert_return(object_path_is_valid(path), -EINVAL);
2097 assert_return(interface_name_is_valid(interface), -EINVAL);
2098 assert_return(!bus_pid_changed(bus), -ECHILD);
2100 if (!BUS_IS_OPEN(bus->state))
2106 names = strv_from_stdarg_alloca(name);
2108 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2111 static int interfaces_added_append_one_prefix(
2116 const char *interface,
2117 bool require_fallback) {
2119 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2120 bool found_interface = false;
2121 struct node_vtable *c;
2132 n = hashmap_get(bus->nodes, prefix);
2136 LIST_FOREACH(vtables, c, n->vtables) {
2137 if (require_fallback && !c->is_fallback)
2140 if (!streq(c->interface, interface))
2143 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2146 if (bus->nodes_modified)
2151 if (!found_interface) {
2152 r = sd_bus_message_append_basic(m, 's', interface);
2156 r = sd_bus_message_open_container(m, 'a', "{sv}");
2160 found_interface = true;
2163 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2166 if (bus->nodes_modified)
2170 if (found_interface) {
2171 r = sd_bus_message_close_container(m);
2176 return found_interface;
2179 static int interfaces_added_append_one(
2183 const char *interface) {
2193 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2196 if (bus->nodes_modified)
2199 prefix = alloca(strlen(path) + 1);
2200 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2201 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2204 if (bus->nodes_modified)
2211 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2212 BUS_DONT_DESTROY(bus);
2214 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2218 assert_return(bus, -EINVAL);
2219 assert_return(object_path_is_valid(path), -EINVAL);
2220 assert_return(!bus_pid_changed(bus), -ECHILD);
2222 if (!BUS_IS_OPEN(bus->state))
2225 if (strv_isempty(interfaces))
2229 bus->nodes_modified = false;
2230 m = sd_bus_message_unref(m);
2232 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded");
2236 r = sd_bus_message_append_basic(m, 'o', path);
2240 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2244 STRV_FOREACH(i, interfaces) {
2245 assert_return(interface_name_is_valid(*i), -EINVAL);
2247 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2251 r = interfaces_added_append_one(bus, m, path, *i);
2255 if (bus->nodes_modified)
2258 r = sd_bus_message_close_container(m);
2263 if (bus->nodes_modified)
2266 r = sd_bus_message_close_container(m);
2270 } while (bus->nodes_modified);
2272 return sd_bus_send(bus, m, NULL);
2275 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2278 assert_return(bus, -EINVAL);
2279 assert_return(object_path_is_valid(path), -EINVAL);
2280 assert_return(!bus_pid_changed(bus), -ECHILD);
2282 if (!BUS_IS_OPEN(bus->state))
2285 interfaces = strv_from_stdarg_alloca(interface);
2287 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2290 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2291 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2294 assert_return(bus, -EINVAL);
2295 assert_return(object_path_is_valid(path), -EINVAL);
2296 assert_return(!bus_pid_changed(bus), -ECHILD);
2298 if (!BUS_IS_OPEN(bus->state))
2301 if (strv_isempty(interfaces))
2304 r = sd_bus_message_new_signal(bus, &m, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved");
2308 r = sd_bus_message_append_basic(m, 'o', path);
2312 r = sd_bus_message_append_strv(m, interfaces);
2316 return sd_bus_send(bus, m, NULL);
2319 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2322 assert_return(bus, -EINVAL);
2323 assert_return(object_path_is_valid(path), -EINVAL);
2324 assert_return(!bus_pid_changed(bus), -ECHILD);
2326 if (!BUS_IS_OPEN(bus->state))
2329 interfaces = strv_from_stdarg_alloca(interface);
2331 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2334 _public_ int sd_bus_add_object_manager(sd_bus *bus, sd_bus_slot **slot, const char *path) {
2339 assert_return(bus, -EINVAL);
2340 assert_return(object_path_is_valid(path), -EINVAL);
2341 assert_return(!bus_pid_changed(bus), -ECHILD);
2343 n = bus_node_allocate(bus, path);
2347 s = bus_slot_allocate(bus, !slot, BUS_NODE_OBJECT_MANAGER, sizeof(struct node_object_manager), NULL);
2353 s->node_object_manager.node = n;
2354 LIST_PREPEND(object_managers, n->object_managers, &s->node_object_manager);
2355 bus->nodes_modified = true;
2363 sd_bus_slot_unref(s);
2364 bus_node_gc(bus, n);