diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index 7cc34b6f1a..e5f6dc7f8c 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -95,6 +95,20 @@ _expr_; \ }) +#define ASSERT_NONNEG(expr) \ + ({ \ + typeof(expr) _expr_ = (expr), _zero = 0; \ + assert(_expr_ >= _zero); \ + _expr_; \ + }) + +#define ASSERT_SE_NONNEG(expr) \ + ({ \ + typeof(expr) _expr_ = (expr), _zero = 0; \ + assert_se(_expr_ >= _zero); \ + _expr_; \ + }) + #define assert_cc(expr) static_assert(expr, #expr) diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 909dfe4d3a..2ad7a9993d 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -1044,6 +1044,7 @@ static int object_manager_serialize_path( const char *prefix, const char *path, bool require_fallback, + bool *found_object_manager, sd_bus_error *error) { const char *previous_interface = NULL; @@ -1055,12 +1056,16 @@ static int object_manager_serialize_path( assert(reply); assert(prefix); assert(path); + assert(found_object_manager); assert(error); n = hashmap_get(bus->nodes, prefix); if (!n) return 0; + if (!require_fallback && n->object_managers) + *found_object_manager = true; + LIST_FOREACH(vtables, i, n->vtables) { void *u; @@ -1103,9 +1108,12 @@ static int object_manager_serialize_path( if (r < 0) return r; - r = sd_bus_message_append(reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0); - if (r < 0) - return r; + if (*found_object_manager) { + r = sd_bus_message_append( + reply, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0); + if (r < 0) + return r; + } found_something = true; } @@ -1180,6 +1188,7 @@ static int object_manager_serialize_path_and_fallbacks( _cleanup_free_ char *prefix = NULL; size_t pl; int r; + bool found_object_manager = false; assert(bus); assert(reply); @@ -1187,7 +1196,7 @@ static int object_manager_serialize_path_and_fallbacks( assert(error); /* First, add all vtables registered for this path */ - r = object_manager_serialize_path(bus, reply, path, path, false, error); + r = object_manager_serialize_path(bus, reply, path, path, false, &found_object_manager, error); if (r < 0) return r; if (bus->nodes_modified) @@ -1201,7 +1210,7 @@ static int object_manager_serialize_path_and_fallbacks( return -ENOMEM; OBJECT_PATH_FOREACH_PREFIX(prefix, path) { - r = object_manager_serialize_path(bus, reply, prefix, path, true, error); + r = object_manager_serialize_path(bus, reply, prefix, path, true, &found_object_manager, error); if (r < 0) return r; if (bus->nodes_modified) @@ -1548,13 +1557,18 @@ void bus_node_gc(sd_bus *b, struct node *n) { free(n); } -static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path) { +static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const char *path, bool* path_has_object_manager) { struct node *n; assert(bus); assert(path); + assert(path_has_object_manager); n = hashmap_get(bus->nodes, path); + + if (n) + *path_has_object_manager = n->object_managers; + if (!n) { _cleanup_free_ char *prefix = NULL; size_t pl; @@ -2423,7 +2437,7 @@ static int object_added_append_all_prefix( return 0; } -static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { +static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path, bool path_has_object_manager) { _cleanup_ordered_set_free_ OrderedSet *s = NULL; _cleanup_free_ char *prefix = NULL; size_t pl; @@ -2461,9 +2475,11 @@ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *p r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0); if (r < 0) return r; - r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0); - if (r < 0) - return r; + if (path_has_object_manager){ + r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0); + if (r < 0) + return r; + } r = object_added_append_all_prefix(bus, m, s, path, path, false); if (r < 0) @@ -2512,7 +2528,8 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; - r = bus_find_parent_object_manager(bus, &object_manager, path); + bool path_has_object_manager = false; + r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager); if (r < 0) return r; if (r == 0) @@ -2536,7 +2553,7 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { if (r < 0) return r; - r = object_added_append_all(bus, m, path); + r = object_added_append_all(bus, m, path, path_has_object_manager); if (r < 0) return r; @@ -2612,7 +2629,7 @@ static int object_removed_append_all_prefix( return 0; } -static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { +static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path, bool path_has_object_manager) { _cleanup_ordered_set_free_ OrderedSet *s = NULL; _cleanup_free_ char *prefix = NULL; size_t pl; @@ -2637,9 +2654,12 @@ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.Properties"); if (r < 0) return r; - r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager"); - if (r < 0) - return r; + + if (path_has_object_manager){ + r = sd_bus_message_append(m, "s", "org.freedesktop.DBus.ObjectManager"); + if (r < 0) + return r; + } r = object_removed_append_all_prefix(bus, m, s, path, path, false); if (r < 0) @@ -2688,7 +2708,8 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; - r = bus_find_parent_object_manager(bus, &object_manager, path); + bool path_has_object_manager = false; + r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager); if (r < 0) return r; if (r == 0) @@ -2712,7 +2733,7 @@ _public_ int sd_bus_emit_object_removed(sd_bus *bus, const char *path) { if (r < 0) return r; - r = object_removed_append_all(bus, m, path); + r = object_removed_append_all(bus, m, path, path_has_object_manager); if (r < 0) return r; @@ -2849,7 +2870,8 @@ _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, ch if (strv_isempty(interfaces)) return 0; - r = bus_find_parent_object_manager(bus, &object_manager, path); + bool path_has_object_manager = false; + r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager); if (r < 0) return r; if (r == 0) @@ -2936,7 +2958,8 @@ _public_ int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, if (strv_isempty(interfaces)) return 0; - r = bus_find_parent_object_manager(bus, &object_manager, path); + bool path_has_object_manager = false; + r = bus_find_parent_object_manager(bus, &object_manager, path, &path_has_object_manager); if (r < 0) return r; if (r == 0) diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index 6c8d5db693..949ef07c34 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -165,6 +165,12 @@ static int emit_object_added(sd_bus_message *m, void *userdata, sd_bus_error *er return 1; } +static int emit_object_with_manager_added(sd_bus_message *m, void *userdata, sd_bus_error *error) { + assert_se(sd_bus_emit_object_added(sd_bus_message_get_bus(m), "/value/a") >= 0); + + return ASSERT_SE_NONNEG(sd_bus_reply_method_return(m, NULL)); +} + static int emit_object_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { int r; @@ -187,6 +193,7 @@ static const sd_bus_vtable vtable[] = { SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0), SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0), SD_BUS_METHOD("EmitObjectAdded", NULL, NULL, emit_object_added, 0), + SD_BUS_METHOD("EmitObjectWithManagerAdded", NULL, NULL, emit_object_with_manager_added, 0), SD_BUS_METHOD("EmitObjectRemoved", NULL, NULL, emit_object_removed, 0), SD_BUS_VTABLE_END }; @@ -463,6 +470,41 @@ static int client(struct context *c) { sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER); + /* Check that /value/b does not have ObjectManager interface but /value/a does */ + assert_se(sd_bus_message_rewind(reply, 1) > 0); + assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{oa{sa{sv}}}") > 0); + while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "oa{sa{sv}}")) > 0) { + const char *path = NULL; + assert_se(sd_bus_message_read_basic(reply, 'o', &path) > 0); + if (STR_IN_SET(path, "/value/b", "/value/a")) { + /* Check that there is no object manager interface here */ + bool found_object_manager_interface = false; + assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sa{sv}}") > 0); + while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}")) > 0) { + const char *interface_name = NULL; + assert_se(sd_bus_message_read_basic(reply, 's', &interface_name) > 0); + + if (streq(interface_name, "org.freedesktop.DBus.ObjectManager")) { + assert_se(!streq(path, "/value/b")); + found_object_manager_interface = true; + } + + assert_se(sd_bus_message_skip(reply, "a{sv}") >= 0); + assert_se(sd_bus_message_exit_container(reply) >= 0); + } + assert_se(sd_bus_message_exit_container(reply) >= 0); + + if (streq(path, "/value/a")) { + /* ObjectManager must be here */ + assert_se(found_object_manager_interface); + } + + } else + assert_se(sd_bus_message_skip(reply, "a{sa{sv}}") >= 0); + + assert_se(sd_bus_message_exit_container(reply) >= 0); + } + reply = sd_bus_message_unref(reply); r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, NULL); @@ -518,6 +560,54 @@ static int client(struct context *c) { assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")); sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER); + /* Check if /value/a/x does not have org.freedesktop.DBus.ObjectManager */ + assert_se(sd_bus_message_rewind(reply, 1) >= 0); + const char* should_be_value_a_x = NULL; + assert_se(sd_bus_message_read_basic(reply, 'o', &should_be_value_a_x) > 0); + assert_se(streq(should_be_value_a_x, "/value/a/x")); + assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sa{sv}}") > 0); + while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}")) > 0) { + const char* interface_name = NULL; + assert_se(sd_bus_message_read_basic(reply, 's', &interface_name) > 0); + + assert(!streq(interface_name, "org.freedesktop.DBus.ObjectManager")); + + assert_se(sd_bus_message_skip(reply, "a{sv}") >= 0); + + assert_se(sd_bus_message_exit_container(reply) >= 0); + } + + reply = sd_bus_message_unref(reply); + + assert_se(sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectWithManagerAdded", &error, NULL, NULL) >= 0); + + assert_se(sd_bus_process(bus, &reply) > 0); + + assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")); + sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER); + + /* Check if /value/a has org.freedesktop.DBus.ObjectManager */ + assert_se(sd_bus_message_rewind(reply, 1) >= 0); + const char* should_be_value_a = NULL; + bool found_object_manager = false; + assert_se(sd_bus_message_read_basic(reply, 'o', &should_be_value_a) > 0); + assert_se(streq(should_be_value_a, "/value/a")); + assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sa{sv}}") > 0); + while (ASSERT_SE_NONNEG(sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}")) > 0) { + const char* interface_name = NULL; + assert_se(sd_bus_message_read_basic(reply, 's', &interface_name)); + + if (streq(interface_name, "org.freedesktop.DBus.ObjectManager")) { + found_object_manager = true; + break; + } + + assert_se(sd_bus_message_skip(reply, "a{sv}") >= 0); + + assert_se(sd_bus_message_exit_container(reply) >= 0); + } + assert_se(found_object_manager); + reply = sd_bus_message_unref(reply); r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &error, NULL, NULL); @@ -529,6 +619,18 @@ static int client(struct context *c) { assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")); sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER); + /* Check if /value/a/x does not have org.freedesktop.DBus.ObjectManager */ + assert_se(sd_bus_message_rewind(reply, 1) >= 0); + should_be_value_a_x = NULL; + assert_se(sd_bus_message_read_basic(reply, 'o', &should_be_value_a_x) > 0); + assert_se(streq(should_be_value_a_x, "/value/a/x")); + assert_se(sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s") > 0); + const char* deleted_interface_name = NULL; + while (ASSERT_SE_NONNEG(sd_bus_message_read_basic(reply, 's', &deleted_interface_name)) > 0) { + assert(!streq(deleted_interface_name, "org.freedesktop.DBus.ObjectManager")); + } + assert_se(sd_bus_message_exit_container(reply) >= 0); + reply = sd_bus_message_unref(reply); r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, NULL);