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"
31 #include "bus-objects.h"
34 static int node_vtable_get_userdata(
37 struct node_vtable *c,
39 sd_bus_error *error) {
50 r = c->find(bus, path, c->interface, u, &u, error);
53 if (sd_bus_error_is_set(error))
54 return sd_bus_error_get_errno(error);
65 static void *vtable_property_convert_userdata(const sd_bus_vtable *p, void *u) {
68 return (uint8_t*) u + p->x.property.offset;
71 static int vtable_property_get_userdata(
74 struct vtable_member *p,
76 sd_bus_error *error) {
86 r = node_vtable_get_userdata(bus, path, p->parent, &u, error);
89 if (bus->nodes_modified)
92 *userdata = vtable_property_convert_userdata(p->vtable, u);
96 static int add_enumerated_to_set(
99 struct node_enumerator *first,
101 sd_bus_error *error) {
103 struct node_enumerator *c;
110 LIST_FOREACH(enumerators, c, first) {
111 char **children = NULL, **k;
113 if (bus->nodes_modified)
116 r = c->callback(bus, prefix, c->userdata, &children, error);
119 if (sd_bus_error_is_set(error))
120 return sd_bus_error_get_errno(error);
122 STRV_FOREACH(k, children) {
128 if (!object_path_is_valid(*k)){
134 if (!object_path_startswith(*k, prefix)) {
139 r = set_consume(s, *k);
152 static int add_subtree_to_set(
157 sd_bus_error *error) {
167 r = add_enumerated_to_set(bus, prefix, n->enumerators, s, error);
170 if (bus->nodes_modified)
173 LIST_FOREACH(siblings, i, n->child) {
176 if (!object_path_startswith(i->path, prefix))
183 r = set_consume(s, t);
184 if (r < 0 && r != -EEXIST)
187 r = add_subtree_to_set(bus, prefix, i, s, error);
190 if (bus->nodes_modified)
197 static int get_child_nodes(
202 sd_bus_error *error) {
212 s = set_new(string_hash_func, string_compare_func);
216 r = add_subtree_to_set(bus, prefix, n, s, error);
226 static int node_callbacks_run(
229 struct node_callback *first,
230 bool require_fallback,
231 bool *found_object) {
233 struct node_callback *c;
238 assert(found_object);
240 LIST_FOREACH(callbacks, c, first) {
241 _cleanup_bus_error_free_ sd_bus_error error_buffer = SD_BUS_ERROR_NULL;
243 if (bus->nodes_modified)
246 if (require_fallback && !c->is_fallback)
249 *found_object = true;
251 if (c->last_iteration == bus->iteration_counter)
254 c->last_iteration = bus->iteration_counter;
256 r = sd_bus_message_rewind(m, true);
260 r = c->callback(bus, m, c->userdata, &error_buffer);
261 r = bus_maybe_reply_error(m, r, &error_buffer);
269 #define CAPABILITY_SHIFT(x) (((x) >> __builtin_ctzll(_SD_BUS_VTABLE_CAPABILITY_MASK)) & 0xFFFF)
271 static int check_access(sd_bus *bus, sd_bus_message *m, struct vtable_member *c, sd_bus_error *error) {
272 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
281 /* If the entire bus is trusted let's grant access */
285 /* If the member is marked UNPRIVILEGED let's grant access */
286 if (c->vtable->flags & SD_BUS_VTABLE_UNPRIVILEGED)
289 /* If we are not connected to kdbus we cannot retrieve the
290 * effective capability set without race. Since we need this
291 * for a security decision we cannot use racy data, hence
292 * don't request it. */
294 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID|SD_BUS_CREDS_EFFECTIVE_CAPS, &creds);
296 r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_UID, &creds);
300 /* Check have the caller has the requested capability
301 * set. Note that the flags value contains the capability
302 * number plus one, which we need to subtract here. We do this
303 * so that we have 0 as special value for "default
305 cap = CAPABILITY_SHIFT(c->vtable->flags);
307 cap = CAPABILITY_SHIFT(c->parent->vtable[0].flags);
313 r = sd_bus_creds_has_effective_cap(creds, cap);
317 /* Caller has same UID as us, then let's grant access */
318 r = sd_bus_creds_get_uid(creds, &uid);
324 return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access to %s.%s() not permitted.", c->interface, c->member);
327 static int method_callbacks_run(
330 struct vtable_member *c,
331 bool require_fallback,
332 bool *found_object) {
334 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
335 const char *signature;
342 assert(found_object);
344 if (require_fallback && !c->parent->is_fallback)
347 r = check_access(bus, m, c, &error);
349 return bus_maybe_reply_error(m, r, &error);
351 r = node_vtable_get_userdata(bus, m->path, c->parent, &u, &error);
353 return bus_maybe_reply_error(m, r, &error);
354 if (bus->nodes_modified)
357 *found_object = true;
359 if (c->last_iteration == bus->iteration_counter)
362 c->last_iteration = bus->iteration_counter;
364 r = sd_bus_message_rewind(m, true);
368 signature = sd_bus_message_get_signature(m, true);
372 if (!streq(strempty(c->vtable->x.method.signature), signature))
373 return sd_bus_reply_method_errorf(
375 SD_BUS_ERROR_INVALID_ARGS,
376 "Invalid arguments '%s' to call %s.%s(), expecting '%s'.",
377 signature, c->interface, c->member, strempty(c->vtable->x.method.signature));
379 /* Keep track what the signature of the reply to this message
380 * should be, so that this can be enforced when sealing the
382 m->enforced_reply_signature = strempty(c->vtable->x.method.result);
384 if (c->vtable->x.method.handler) {
385 r = c->vtable->x.method.handler(bus, m, u, &error);
386 return bus_maybe_reply_error(m, r, &error);
389 /* If the method callback is NULL, make this a successful NOP */
390 r = sd_bus_reply_method_return(m, NULL);
397 static int invoke_property_get(
399 const sd_bus_vtable *v,
401 const char *interface,
402 const char *property,
403 sd_bus_message *reply,
405 sd_bus_error *error) {
417 if (v->x.property.get) {
418 r = v->x.property.get(bus, path, interface, property, reply, userdata, error);
421 if (sd_bus_error_is_set(error))
422 return sd_bus_error_get_errno(error);
426 /* Automatic handling if no callback is defined. */
428 if (streq(v->x.property.signature, "as"))
429 return sd_bus_message_append_strv(reply, *(char***) userdata);
431 assert(signature_is_single(v->x.property.signature, false));
432 assert(bus_type_is_basic(v->x.property.signature[0]));
434 switch (v->x.property.signature[0]) {
436 case SD_BUS_TYPE_STRING:
437 case SD_BUS_TYPE_SIGNATURE:
438 p = strempty(*(char**) userdata);
441 case SD_BUS_TYPE_OBJECT_PATH:
442 p = *(char**) userdata;
451 return sd_bus_message_append_basic(reply, v->x.property.signature[0], p);
454 static int invoke_property_set(
456 const sd_bus_vtable *v,
458 const char *interface,
459 const char *property,
460 sd_bus_message *value,
462 sd_bus_error *error) {
473 if (v->x.property.set) {
474 r = v->x.property.set(bus, path, interface, property, value, userdata, error);
477 if (sd_bus_error_is_set(error))
478 return sd_bus_error_get_errno(error);
482 /* Automatic handling if no callback is defined. */
484 assert(signature_is_single(v->x.property.signature, false));
485 assert(bus_type_is_basic(v->x.property.signature[0]));
487 switch (v->x.property.signature[0]) {
489 case SD_BUS_TYPE_STRING:
490 case SD_BUS_TYPE_OBJECT_PATH:
491 case SD_BUS_TYPE_SIGNATURE: {
495 r = sd_bus_message_read_basic(value, v->x.property.signature[0], &p);
503 free(*(char**) userdata);
504 *(char**) userdata = n;
510 r = sd_bus_message_read_basic(value, v->x.property.signature[0], userdata);
520 static int property_get_set_callbacks_run(
523 struct vtable_member *c,
524 bool require_fallback,
526 bool *found_object) {
528 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
529 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
536 assert(found_object);
538 if (require_fallback && !c->parent->is_fallback)
541 r = vtable_property_get_userdata(bus, m->path, c, &u, &error);
543 return bus_maybe_reply_error(m, r, &error);
544 if (bus->nodes_modified)
547 *found_object = true;
549 r = sd_bus_message_new_method_return(m, &reply);
554 /* Note that we do not protect against reexecution
555 * here (using the last_iteration check, see below),
556 * should the node tree have changed and we got called
557 * again. We assume that property Get() calls are
558 * ultimately without side-effects or if they aren't
559 * then at least idempotent. */
561 r = sd_bus_message_open_container(reply, 'v', c->vtable->x.property.signature);
565 /* Note that we do not do an access check here. Read
566 * access to properties is always unrestricted, since
567 * PropertiesChanged signals broadcast contents
570 r = invoke_property_get(bus, c->vtable, m->path, c->interface, c->member, reply, u, &error);
572 return bus_maybe_reply_error(m, r, &error);
574 if (bus->nodes_modified)
577 r = sd_bus_message_close_container(reply);
582 if (c->vtable->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
583 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Property '%s' is not writable.", c->member);
585 /* Avoid that we call the set routine more than once
586 * if the processing of this message got restarted
587 * because the node tree changed. */
588 if (c->last_iteration == bus->iteration_counter)
591 c->last_iteration = bus->iteration_counter;
593 r = sd_bus_message_enter_container(m, 'v', c->vtable->x.property.signature);
597 r = check_access(bus, m, c, &error);
599 return bus_maybe_reply_error(m, r, &error);
601 r = invoke_property_set(bus, c->vtable, m->path, c->interface, c->member, m, u, &error);
603 return bus_maybe_reply_error(m, r, &error);
605 if (bus->nodes_modified)
608 r = sd_bus_message_exit_container(m);
613 r = sd_bus_send(bus, reply, NULL);
620 static int vtable_append_all_properties(
622 sd_bus_message *reply,
624 struct node_vtable *c,
626 sd_bus_error *error) {
628 const sd_bus_vtable *v;
636 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
637 if (v->type != _SD_BUS_VTABLE_PROPERTY && v->type != _SD_BUS_VTABLE_WRITABLE_PROPERTY)
640 r = sd_bus_message_open_container(reply, 'e', "sv");
644 r = sd_bus_message_append(reply, "s", v->x.property.member);
648 r = sd_bus_message_open_container(reply, 'v', v->x.property.signature);
652 r = invoke_property_get(bus, v, path, c->interface, v->x.property.member, reply, vtable_property_convert_userdata(v, userdata), error);
655 if (bus->nodes_modified)
658 r = sd_bus_message_close_container(reply);
662 r = sd_bus_message_close_container(reply);
670 static int property_get_all_callbacks_run(
673 struct node_vtable *first,
674 bool require_fallback,
676 bool *found_object) {
678 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
679 struct node_vtable *c;
680 bool found_interface;
685 assert(found_object);
687 r = sd_bus_message_new_method_return(m, &reply);
691 r = sd_bus_message_open_container(reply, 'a', "{sv}");
695 found_interface = !iface ||
696 streq(iface, "org.freedesktop.DBus.Properties") ||
697 streq(iface, "org.freedesktop.DBus.Peer") ||
698 streq(iface, "org.freedesktop.DBus.Introspectable");
700 LIST_FOREACH(vtables, c, first) {
701 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
704 if (require_fallback && !c->is_fallback)
707 r = node_vtable_get_userdata(bus, m->path, c, &u, &error);
709 return bus_maybe_reply_error(m, r, &error);
710 if (bus->nodes_modified)
715 *found_object = true;
717 if (iface && !streq(c->interface, iface))
719 found_interface = true;
721 r = vtable_append_all_properties(bus, reply, m->path, c, u, &error);
723 return bus_maybe_reply_error(m, r, &error);
724 if (bus->nodes_modified)
728 if (!found_interface) {
729 r = sd_bus_reply_method_errorf(
731 SD_BUS_ERROR_UNKNOWN_INTERFACE,
732 "Unknown interface '%s'.", iface);
739 r = sd_bus_message_close_container(reply);
743 r = sd_bus_send(bus, reply, NULL);
750 static bool bus_node_with_object_manager(sd_bus *bus, struct node *n) {
754 if (n->object_manager)
758 return bus_node_with_object_manager(bus, n->parent);
763 static bool bus_node_exists(
767 bool require_fallback) {
769 struct node_vtable *c;
770 struct node_callback *k;
776 /* Tests if there's anything attached directly to this node
777 * for the specified path */
779 LIST_FOREACH(callbacks, k, n->callbacks) {
780 if (require_fallback && !k->is_fallback)
786 LIST_FOREACH(vtables, c, n->vtables) {
787 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
789 if (require_fallback && !c->is_fallback)
792 if (node_vtable_get_userdata(bus, path, c, NULL, &error) > 0)
794 if (bus->nodes_modified)
798 return !require_fallback && (n->enumerators || n->object_manager);
801 static int process_introspect(
805 bool require_fallback,
806 bool *found_object) {
808 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
809 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
810 _cleanup_set_free_free_ Set *s = NULL;
811 const char *previous_interface = NULL;
812 struct introspect intro;
813 struct node_vtable *c;
820 assert(found_object);
822 r = get_child_nodes(bus, m->path, n, &s, &error);
824 return bus_maybe_reply_error(m, r, &error);
825 if (bus->nodes_modified)
828 r = introspect_begin(&intro);
832 r = introspect_write_default_interfaces(&intro, bus_node_with_object_manager(bus, n));
836 empty = set_isempty(s);
838 LIST_FOREACH(vtables, c, n->vtables) {
839 if (require_fallback && !c->is_fallback)
842 r = node_vtable_get_userdata(bus, m->path, c, NULL, &error);
844 r = bus_maybe_reply_error(m, r, &error);
847 if (bus->nodes_modified) {
856 if (!streq_ptr(previous_interface, c->interface)) {
858 if (previous_interface)
859 fputs(" </interface>\n", intro.f);
861 fprintf(intro.f, " <interface name=\"%s\">\n", c->interface);
864 r = introspect_write_interface(&intro, c->vtable);
868 previous_interface = c->interface;
871 if (previous_interface)
872 fputs(" </interface>\n", intro.f);
875 /* Nothing?, let's see if we exist at all, and if not
876 * refuse to do anything */
877 r = bus_node_exists(bus, n, m->path, require_fallback);
880 if (bus->nodes_modified)
886 *found_object = true;
888 r = introspect_write_child_nodes(&intro, s, m->path);
892 r = introspect_finish(&intro, bus, m, &reply);
896 r = sd_bus_send(bus, reply, NULL);
903 introspect_free(&intro);
907 static int object_manager_serialize_path(
909 sd_bus_message *reply,
912 bool require_fallback,
913 sd_bus_error *error) {
915 const char *previous_interface = NULL;
916 bool found_something = false;
917 struct node_vtable *i;
927 n = hashmap_get(bus->nodes, prefix);
931 LIST_FOREACH(vtables, i, n->vtables) {
934 if (require_fallback && !i->is_fallback)
937 r = node_vtable_get_userdata(bus, path, i, &u, error);
940 if (bus->nodes_modified)
945 if (!found_something) {
947 /* Open the object part */
949 r = sd_bus_message_open_container(reply, 'e', "oa{sa{sv}}");
953 r = sd_bus_message_append(reply, "o", path);
957 r = sd_bus_message_open_container(reply, 'a', "{sa{sv}}");
961 found_something = true;
964 if (!streq_ptr(previous_interface, i->interface)) {
966 /* Maybe close the previous interface part */
968 if (previous_interface) {
969 r = sd_bus_message_close_container(reply);
973 r = sd_bus_message_close_container(reply);
978 /* Open the new interface part */
980 r = sd_bus_message_open_container(reply, 'e', "sa{sv}");
984 r = sd_bus_message_append(reply, "s", i->interface);
988 r = sd_bus_message_open_container(reply, 'a', "{sv}");
993 r = vtable_append_all_properties(bus, reply, path, i, u, error);
996 if (bus->nodes_modified)
999 previous_interface = i->interface;
1002 if (previous_interface) {
1003 r = sd_bus_message_close_container(reply);
1007 r = sd_bus_message_close_container(reply);
1012 if (found_something) {
1013 r = sd_bus_message_close_container(reply);
1017 r = sd_bus_message_close_container(reply);
1025 static int object_manager_serialize_path_and_fallbacks(
1027 sd_bus_message *reply,
1029 sd_bus_error *error) {
1039 /* First, add all vtables registered for this path */
1040 r = object_manager_serialize_path(bus, reply, path, path, false, error);
1043 if (bus->nodes_modified)
1046 /* Second, add fallback vtables registered for any of the prefixes */
1047 prefix = alloca(strlen(path) + 1);
1048 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
1049 r = object_manager_serialize_path(bus, reply, prefix, path, true, error);
1052 if (bus->nodes_modified)
1059 static int process_get_managed_objects(
1063 bool require_fallback,
1064 bool *found_object) {
1066 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1067 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1068 _cleanup_set_free_free_ Set *s = NULL;
1075 assert(found_object);
1077 if (!bus_node_with_object_manager(bus, n))
1080 r = get_child_nodes(bus, m->path, n, &s, &error);
1083 if (bus->nodes_modified)
1086 r = sd_bus_message_new_method_return(m, &reply);
1090 r = sd_bus_message_open_container(reply, 'a', "{oa{sa{sv}}}");
1094 empty = set_isempty(s);
1096 struct node_vtable *c;
1098 /* Hmm, so we have no children? Then let's check
1099 * whether we exist at all, i.e. whether at least one
1102 LIST_FOREACH(vtables, c, n->vtables) {
1104 if (require_fallback && !c->is_fallback)
1122 SET_FOREACH(path, s, i) {
1123 r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error);
1127 if (bus->nodes_modified)
1132 r = sd_bus_message_close_container(reply);
1136 r = sd_bus_send(bus, reply, NULL);
1143 static int object_find_and_run(
1147 bool require_fallback,
1148 bool *found_object) {
1151 struct vtable_member vtable_key, *v;
1157 assert(found_object);
1159 n = hashmap_get(bus->nodes, p);
1163 /* First, try object callbacks */
1164 r = node_callbacks_run(bus, m, n->callbacks, require_fallback, found_object);
1167 if (bus->nodes_modified)
1170 if (!m->interface || !m->member)
1173 /* Then, look for a known method */
1174 vtable_key.path = (char*) p;
1175 vtable_key.interface = m->interface;
1176 vtable_key.member = m->member;
1178 v = hashmap_get(bus->vtable_methods, &vtable_key);
1180 r = method_callbacks_run(bus, m, v, require_fallback, found_object);
1183 if (bus->nodes_modified)
1187 /* Then, look for a known property */
1188 if (streq(m->interface, "org.freedesktop.DBus.Properties")) {
1191 get = streq(m->member, "Get");
1193 if (get || streq(m->member, "Set")) {
1195 r = sd_bus_message_rewind(m, true);
1199 vtable_key.path = (char*) p;
1201 r = sd_bus_message_read(m, "ss", &vtable_key.interface, &vtable_key.member);
1203 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface and member parameters");
1205 v = hashmap_get(bus->vtable_properties, &vtable_key);
1207 r = property_get_set_callbacks_run(bus, m, v, require_fallback, get, found_object);
1212 } else if (streq(m->member, "GetAll")) {
1215 r = sd_bus_message_rewind(m, true);
1219 r = sd_bus_message_read(m, "s", &iface);
1221 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected interface parameter");
1226 r = property_get_all_callbacks_run(bus, m, n->vtables, require_fallback, iface, found_object);
1231 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1233 if (!isempty(sd_bus_message_get_signature(m, true)))
1234 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1236 r = process_introspect(bus, m, n, require_fallback, found_object);
1240 } else if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.ObjectManager", "GetManagedObjects")) {
1242 if (!isempty(sd_bus_message_get_signature(m, true)))
1243 return sd_bus_reply_method_errorf(m, SD_BUS_ERROR_INVALID_ARGS, "Expected no parameters");
1245 r = process_get_managed_objects(bus, m, n, require_fallback, found_object);
1250 if (bus->nodes_modified)
1253 if (!*found_object) {
1254 r = bus_node_exists(bus, n, m->path, require_fallback);
1258 *found_object = true;
1264 int bus_process_object(sd_bus *bus, sd_bus_message *m) {
1267 bool found_object = false;
1272 if (m->header->type != SD_BUS_MESSAGE_METHOD_CALL)
1275 if (hashmap_isempty(bus->nodes))
1281 pl = strlen(m->path);
1285 bus->nodes_modified = false;
1287 r = object_find_and_run(bus, m, m->path, false, &found_object);
1291 /* Look for fallback prefixes */
1292 OBJECT_PATH_FOREACH_PREFIX(prefix, m->path) {
1294 if (bus->nodes_modified)
1297 r = object_find_and_run(bus, m, prefix, true, &found_object);
1302 } while (bus->nodes_modified);
1307 if (sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Get") ||
1308 sd_bus_message_is_method_call(m, "org.freedesktop.DBus.Properties", "Set"))
1309 r = sd_bus_reply_method_errorf(
1311 SD_BUS_ERROR_UNKNOWN_PROPERTY,
1312 "Unknown property or interface.");
1314 r = sd_bus_reply_method_errorf(
1316 SD_BUS_ERROR_UNKNOWN_METHOD,
1317 "Unknown method '%s' or interface '%s'.", m->member, m->interface);
1325 static struct node *bus_node_allocate(sd_bus *bus, const char *path) {
1326 struct node *n, *parent;
1333 assert(path[0] == '/');
1335 n = hashmap_get(bus->nodes, path);
1339 r = hashmap_ensure_allocated(&bus->nodes, string_hash_func, string_compare_func);
1347 if (streq(path, "/"))
1350 e = strrchr(path, '/');
1353 p = strndupa(path, MAX(1, path - e));
1355 parent = bus_node_allocate(bus, p);
1362 n = new0(struct node, 1);
1369 r = hashmap_put(bus->nodes, s, n);
1377 LIST_PREPEND(siblings, parent->child, n);
1382 static void bus_node_gc(sd_bus *b, struct node *n) {
1395 assert(hashmap_remove(b->nodes, n->path) == n);
1398 LIST_REMOVE(siblings, n->parent->child, n);
1401 bus_node_gc(b, n->parent);
1405 static int bus_add_object(
1409 sd_bus_message_handler_t callback,
1412 struct node_callback *c;
1416 assert_return(bus, -EINVAL);
1417 assert_return(object_path_is_valid(path), -EINVAL);
1418 assert_return(callback, -EINVAL);
1419 assert_return(!bus_pid_changed(bus), -ECHILD);
1421 n = bus_node_allocate(bus, path);
1425 c = new0(struct node_callback, 1);
1432 c->callback = callback;
1433 c->userdata = userdata;
1434 c->is_fallback = fallback;
1436 LIST_PREPEND(callbacks, n->callbacks, c);
1437 bus->nodes_modified = true;
1443 bus_node_gc(bus, n);
1447 static int bus_remove_object(
1451 sd_bus_message_handler_t callback,
1454 struct node_callback *c;
1457 assert_return(bus, -EINVAL);
1458 assert_return(object_path_is_valid(path), -EINVAL);
1459 assert_return(callback, -EINVAL);
1460 assert_return(!bus_pid_changed(bus), -ECHILD);
1462 n = hashmap_get(bus->nodes, path);
1466 LIST_FOREACH(callbacks, c, n->callbacks)
1467 if (c->callback == callback && c->userdata == userdata && c->is_fallback == fallback)
1472 LIST_REMOVE(callbacks, n->callbacks, c);
1475 bus_node_gc(bus, n);
1476 bus->nodes_modified = true;
1481 _public_ int sd_bus_add_object(sd_bus *bus,
1483 sd_bus_message_handler_t callback,
1486 return bus_add_object(bus, false, path, callback, userdata);
1489 _public_ int sd_bus_remove_object(sd_bus *bus,
1491 sd_bus_message_handler_t callback,
1494 return bus_remove_object(bus, false, path, callback, userdata);
1497 _public_ int sd_bus_add_fallback(sd_bus *bus,
1499 sd_bus_message_handler_t callback,
1502 return bus_add_object(bus, true, prefix, callback, userdata);
1505 _public_ int sd_bus_remove_fallback(sd_bus *bus,
1507 sd_bus_message_handler_t callback,
1510 return bus_remove_object(bus, true, prefix, callback, userdata);
1513 static void free_node_vtable(sd_bus *bus, struct node_vtable *w) {
1519 if (w->interface && w->node && w->vtable) {
1520 const sd_bus_vtable *v;
1522 for (v = w->vtable; v->type != _SD_BUS_VTABLE_END; v++) {
1523 struct vtable_member *x = NULL;
1527 case _SD_BUS_VTABLE_METHOD: {
1528 struct vtable_member key;
1530 key.path = w->node->path;
1531 key.interface = w->interface;
1532 key.member = v->x.method.member;
1534 x = hashmap_remove(bus->vtable_methods, &key);
1538 case _SD_BUS_VTABLE_PROPERTY:
1539 case _SD_BUS_VTABLE_WRITABLE_PROPERTY: {
1540 struct vtable_member key;
1542 key.path = w->node->path;
1543 key.interface = w->interface;
1544 key.member = v->x.property.member;
1545 x = hashmap_remove(bus->vtable_properties, &key);
1557 static unsigned vtable_member_hash_func(const void *a) {
1558 const struct vtable_member *m = a;
1563 string_hash_func(m->path) ^
1564 string_hash_func(m->interface) ^
1565 string_hash_func(m->member);
1568 static int vtable_member_compare_func(const void *a, const void *b) {
1569 const struct vtable_member *x = a, *y = b;
1575 r = strcmp(x->path, y->path);
1579 r = strcmp(x->interface, y->interface);
1583 return strcmp(x->member, y->member);
1586 static int add_object_vtable_internal(
1589 const char *interface,
1590 const sd_bus_vtable *vtable,
1592 sd_bus_object_find_t find,
1595 struct node_vtable *c = NULL, *i, *existing = NULL;
1596 const sd_bus_vtable *v;
1600 assert_return(bus, -EINVAL);
1601 assert_return(object_path_is_valid(path), -EINVAL);
1602 assert_return(interface_name_is_valid(interface), -EINVAL);
1603 assert_return(vtable, -EINVAL);
1604 assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL);
1605 assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL);
1606 assert_return(!bus_pid_changed(bus), -ECHILD);
1607 assert_return(!streq(interface, "org.freedesktop.DBus.Properties") &&
1608 !streq(interface, "org.freedesktop.DBus.Introspectable") &&
1609 !streq(interface, "org.freedesktop.DBus.Peer") &&
1610 !streq(interface, "org.freedesktop.DBus.ObjectManager"), -EINVAL);
1612 r = hashmap_ensure_allocated(&bus->vtable_methods, vtable_member_hash_func, vtable_member_compare_func);
1616 r = hashmap_ensure_allocated(&bus->vtable_properties, vtable_member_hash_func, vtable_member_compare_func);
1620 n = bus_node_allocate(bus, path);
1624 LIST_FOREACH(vtables, i, n->vtables) {
1625 if (i->is_fallback != fallback) {
1630 if (streq(i->interface, interface)) {
1632 if (i->vtable == vtable) {
1641 c = new0(struct node_vtable, 1);
1648 c->is_fallback = fallback;
1650 c->userdata = userdata;
1653 c->interface = strdup(interface);
1654 if (!c->interface) {
1659 for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) {
1663 case _SD_BUS_VTABLE_METHOD: {
1664 struct vtable_member *m;
1666 if (!member_name_is_valid(v->x.method.member) ||
1667 !signature_is_valid(strempty(v->x.method.signature), false) ||
1668 !signature_is_valid(strempty(v->x.method.result), false) ||
1669 !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) ||
1670 v->flags & (SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY)) {
1675 m = new0(struct vtable_member, 1);
1683 m->interface = c->interface;
1684 m->member = v->x.method.member;
1687 r = hashmap_put(bus->vtable_methods, m, m);
1696 case _SD_BUS_VTABLE_WRITABLE_PROPERTY:
1698 if (!(v->x.property.set || bus_type_is_basic(v->x.property.signature[0]))) {
1705 case _SD_BUS_VTABLE_PROPERTY: {
1706 struct vtable_member *m;
1708 if (!member_name_is_valid(v->x.property.member) ||
1709 !signature_is_single(v->x.property.signature, false) ||
1710 !(v->x.property.get || bus_type_is_basic(v->x.property.signature[0]) || streq(v->x.property.signature, "as")) ||
1711 v->flags & SD_BUS_VTABLE_METHOD_NO_REPLY ||
1712 (v->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY && !(v->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)) ||
1713 (v->flags & SD_BUS_VTABLE_UNPRIVILEGED && v->type == _SD_BUS_VTABLE_PROPERTY)) {
1719 m = new0(struct vtable_member, 1);
1727 m->interface = c->interface;
1728 m->member = v->x.property.member;
1731 r = hashmap_put(bus->vtable_properties, m, m);
1740 case _SD_BUS_VTABLE_SIGNAL:
1742 if (!member_name_is_valid(v->x.signal.member) ||
1743 !signature_is_valid(strempty(v->x.signal.signature), false) ||
1744 v->flags & SD_BUS_VTABLE_UNPRIVILEGED) {
1757 LIST_INSERT_AFTER(vtables, n->vtables, existing, c);
1758 bus->nodes_modified = true;
1764 free_node_vtable(bus, c);
1766 bus_node_gc(bus, n);
1770 static int remove_object_vtable_internal(
1773 const char *interface,
1774 const sd_bus_vtable *vtable,
1776 sd_bus_object_find_t find,
1779 struct node_vtable *c;
1782 assert_return(bus, -EINVAL);
1783 assert_return(object_path_is_valid(path), -EINVAL);
1784 assert_return(interface_name_is_valid(interface), -EINVAL);
1785 assert_return(!bus_pid_changed(bus), -ECHILD);
1787 n = hashmap_get(bus->nodes, path);
1791 LIST_FOREACH(vtables, c, n->vtables)
1792 if (streq(c->interface, interface) &&
1793 c->is_fallback == fallback &&
1794 c->vtable == vtable &&
1796 c->userdata == userdata)
1802 LIST_REMOVE(vtables, n->vtables, c);
1804 free_node_vtable(bus, c);
1805 bus_node_gc(bus, n);
1807 bus->nodes_modified = true;
1812 _public_ int sd_bus_add_object_vtable(
1815 const char *interface,
1816 const sd_bus_vtable *vtable,
1819 return add_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1822 _public_ int sd_bus_remove_object_vtable(
1825 const char *interface,
1826 const sd_bus_vtable *vtable,
1829 return remove_object_vtable_internal(bus, path, interface, vtable, false, NULL, userdata);
1832 _public_ int sd_bus_add_fallback_vtable(
1835 const char *interface,
1836 const sd_bus_vtable *vtable,
1837 sd_bus_object_find_t find,
1840 return add_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1843 _public_ int sd_bus_remove_fallback_vtable(
1846 const char *interface,
1847 const sd_bus_vtable *vtable,
1848 sd_bus_object_find_t find,
1851 return remove_object_vtable_internal(bus, path, interface, vtable, true, find, userdata);
1854 _public_ int sd_bus_add_node_enumerator(
1857 sd_bus_node_enumerator_t callback,
1860 struct node_enumerator *c;
1864 assert_return(bus, -EINVAL);
1865 assert_return(object_path_is_valid(path), -EINVAL);
1866 assert_return(callback, -EINVAL);
1867 assert_return(!bus_pid_changed(bus), -ECHILD);
1869 n = bus_node_allocate(bus, path);
1873 c = new0(struct node_enumerator, 1);
1880 c->callback = callback;
1881 c->userdata = userdata;
1883 LIST_PREPEND(enumerators, n->enumerators, c);
1885 bus->nodes_modified = true;
1891 bus_node_gc(bus, n);
1895 _public_ int sd_bus_remove_node_enumerator(
1898 sd_bus_node_enumerator_t callback,
1901 struct node_enumerator *c;
1904 assert_return(bus, -EINVAL);
1905 assert_return(object_path_is_valid(path), -EINVAL);
1906 assert_return(callback, -EINVAL);
1907 assert_return(!bus_pid_changed(bus), -ECHILD);
1909 n = hashmap_get(bus->nodes, path);
1913 LIST_FOREACH(enumerators, c, n->enumerators)
1914 if (c->callback == callback && c->userdata == userdata)
1920 LIST_REMOVE(enumerators, n->enumerators, c);
1923 bus_node_gc(bus, n);
1925 bus->nodes_modified = true;
1930 static int emit_properties_changed_on_interface(
1934 const char *interface,
1935 bool require_fallback,
1938 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1939 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
1940 bool has_invalidating = false, has_changing = false;
1941 struct vtable_member key = {};
1942 struct node_vtable *c;
1953 n = hashmap_get(bus->nodes, prefix);
1957 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.Properties", "PropertiesChanged", &m);
1961 r = sd_bus_message_append(m, "s", interface);
1965 r = sd_bus_message_open_container(m, 'a', "{sv}");
1970 key.interface = interface;
1972 LIST_FOREACH(vtables, c, n->vtables) {
1973 if (require_fallback && !c->is_fallback)
1976 if (!streq(c->interface, interface))
1979 r = node_vtable_get_userdata(bus, path, c, &u, &error);
1982 if (bus->nodes_modified)
1987 STRV_FOREACH(property, names) {
1988 struct vtable_member *v;
1990 assert_return(member_name_is_valid(*property), -EINVAL);
1992 key.member = *property;
1993 v = hashmap_get(bus->vtable_properties, &key);
1997 /* If there are two vtables for the same
1998 * interface, let's handle this property when
1999 * we come to that vtable. */
2003 assert_return(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE, -EDOM);
2005 if (v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY) {
2006 has_invalidating = true;
2010 has_changing = true;
2012 r = sd_bus_message_open_container(m, 'e', "sv");
2016 r = sd_bus_message_append(m, "s", *property);
2020 r = sd_bus_message_open_container(m, 'v', v->vtable->x.property.signature);
2024 r = invoke_property_get(bus, v->vtable, m->path, interface, *property, m, vtable_property_convert_userdata(v->vtable, u), &error);
2027 if (bus->nodes_modified)
2030 r = sd_bus_message_close_container(m);
2034 r = sd_bus_message_close_container(m);
2040 if (!has_invalidating && !has_changing)
2043 r = sd_bus_message_close_container(m);
2047 r = sd_bus_message_open_container(m, 'a', "s");
2051 if (has_invalidating) {
2052 LIST_FOREACH(vtables, c, n->vtables) {
2053 if (require_fallback && !c->is_fallback)
2056 if (!streq(c->interface, interface))
2059 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2062 if (bus->nodes_modified)
2067 STRV_FOREACH(property, names) {
2068 struct vtable_member *v;
2070 key.member = *property;
2071 assert_se(v = hashmap_get(bus->vtable_properties, &key));
2072 assert(c == v->parent);
2074 if (!(v->vtable->flags & SD_BUS_VTABLE_PROPERTY_INVALIDATE_ONLY))
2077 r = sd_bus_message_append(m, "s", *property);
2084 r = sd_bus_message_close_container(m);
2088 r = sd_bus_send(bus, m, NULL);
2095 _public_ int sd_bus_emit_properties_changed_strv(
2098 const char *interface,
2101 BUS_DONT_DESTROY(bus);
2105 assert_return(bus, -EINVAL);
2106 assert_return(object_path_is_valid(path), -EINVAL);
2107 assert_return(interface_name_is_valid(interface), -EINVAL);
2108 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2109 assert_return(!bus_pid_changed(bus), -ECHILD);
2111 if (strv_isempty(names))
2115 bus->nodes_modified = false;
2117 r = emit_properties_changed_on_interface(bus, path, path, interface, false, names);
2120 if (bus->nodes_modified)
2123 prefix = alloca(strlen(path) + 1);
2124 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2125 r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, names);
2128 if (bus->nodes_modified)
2132 } while (bus->nodes_modified);
2137 _public_ int sd_bus_emit_properties_changed(
2140 const char *interface,
2141 const char *name, ...) {
2145 assert_return(bus, -EINVAL);
2146 assert_return(object_path_is_valid(path), -EINVAL);
2147 assert_return(interface_name_is_valid(interface), -EINVAL);
2148 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2149 assert_return(!bus_pid_changed(bus), -ECHILD);
2154 names = strv_from_stdarg_alloca(name);
2156 return sd_bus_emit_properties_changed_strv(bus, path, interface, names);
2159 static int interfaces_added_append_one_prefix(
2164 const char *interface,
2165 bool require_fallback) {
2167 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2168 bool found_interface = false;
2169 struct node_vtable *c;
2180 n = hashmap_get(bus->nodes, prefix);
2184 LIST_FOREACH(vtables, c, n->vtables) {
2185 if (require_fallback && !c->is_fallback)
2188 if (!streq(c->interface, interface))
2191 r = node_vtable_get_userdata(bus, path, c, &u, &error);
2194 if (bus->nodes_modified)
2199 if (!found_interface) {
2200 r = sd_bus_message_append_basic(m, 's', interface);
2204 r = sd_bus_message_open_container(m, 'a', "{sv}");
2208 found_interface = true;
2211 r = vtable_append_all_properties(bus, m, path, c, u, &error);
2214 if (bus->nodes_modified)
2218 if (found_interface) {
2219 r = sd_bus_message_close_container(m);
2224 return found_interface;
2227 static int interfaces_added_append_one(
2231 const char *interface) {
2241 r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false);
2244 if (bus->nodes_modified)
2247 prefix = alloca(strlen(path) + 1);
2248 OBJECT_PATH_FOREACH_PREFIX(prefix, path) {
2249 r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true);
2252 if (bus->nodes_modified)
2259 _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) {
2260 BUS_DONT_DESTROY(bus);
2262 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2266 assert_return(bus, -EINVAL);
2267 assert_return(object_path_is_valid(path), -EINVAL);
2268 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2269 assert_return(!bus_pid_changed(bus), -ECHILD);
2271 if (strv_isempty(interfaces))
2275 bus->nodes_modified = false;
2278 m = sd_bus_message_unref(m);
2280 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m);
2284 r = sd_bus_message_append_basic(m, 'o', path);
2288 r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
2292 STRV_FOREACH(i, interfaces) {
2293 assert_return(interface_name_is_valid(*i), -EINVAL);
2295 r = sd_bus_message_open_container(m, 'e', "sa{sv}");
2299 r = interfaces_added_append_one(bus, m, path, *i);
2303 if (bus->nodes_modified)
2306 r = sd_bus_message_close_container(m);
2311 if (bus->nodes_modified)
2314 r = sd_bus_message_close_container(m);
2318 } while (bus->nodes_modified);
2320 return sd_bus_send(bus, m, NULL);
2323 _public_ int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) {
2326 assert_return(bus, -EINVAL);
2327 assert_return(object_path_is_valid(path), -EINVAL);
2328 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2329 assert_return(!bus_pid_changed(bus), -ECHILD);
2331 interfaces = strv_from_stdarg_alloca(interface);
2333 return sd_bus_emit_interfaces_added_strv(bus, path, interfaces);
2336 _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) {
2337 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
2340 assert_return(bus, -EINVAL);
2341 assert_return(object_path_is_valid(path), -EINVAL);
2342 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2343 assert_return(!bus_pid_changed(bus), -ECHILD);
2345 if (strv_isempty(interfaces))
2348 r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m);
2352 r = sd_bus_message_append_basic(m, 'o', path);
2356 r = sd_bus_message_append_strv(m, interfaces);
2360 return sd_bus_send(bus, m, NULL);
2363 _public_ int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) {
2366 assert_return(bus, -EINVAL);
2367 assert_return(object_path_is_valid(path), -EINVAL);
2368 assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN);
2369 assert_return(!bus_pid_changed(bus), -ECHILD);
2371 interfaces = strv_from_stdarg_alloca(interface);
2373 return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces);
2376 _public_ int sd_bus_add_object_manager(sd_bus *bus, const char *path) {
2379 assert_return(bus, -EINVAL);
2380 assert_return(object_path_is_valid(path), -EINVAL);
2381 assert_return(!bus_pid_changed(bus), -ECHILD);
2383 n = bus_node_allocate(bus, path);
2387 n->object_manager = true;
2388 bus->nodes_modified = true;
2392 _public_ int sd_bus_remove_object_manager(sd_bus *bus, const char *path) {
2395 assert_return(bus, -EINVAL);
2396 assert_return(object_path_is_valid(path), -EINVAL);
2397 assert_return(!bus_pid_changed(bus), -ECHILD);
2399 n = hashmap_get(bus->nodes, path);
2403 if (!n->object_manager)
2406 n->object_manager = false;
2407 bus->nodes_modified = true;
2408 bus_node_gc(bus, n);