From 4be391639f47b71ce021d8b5cb0f97c105a80156 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 11 Oct 2013 23:22:29 +0200 Subject: [PATCH] bus: properly implement logic for generating InterfacesAdded/InterfacesRemoved signals of ObjectManager --- src/libsystemd-bus/bus-objects.c | 202 +++++++++++++++++++++++++- src/libsystemd-bus/test-bus-objects.c | 51 ++++++- src/systemd/sd-bus.h | 8 +- 3 files changed, 251 insertions(+), 10 deletions(-) diff --git a/src/libsystemd-bus/bus-objects.c b/src/libsystemd-bus/bus-objects.c index 08261fc7c..1d32566c3 100644 --- a/src/libsystemd-bus/bus-objects.c +++ b/src/libsystemd-bus/bus-objects.c @@ -1897,12 +1897,206 @@ int sd_bus_emit_properties_changed( return sd_bus_emit_properties_changed_strv(bus, path, interface, names); } -int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interfaces, ...) { - return -ENOSYS; +static int interfaces_added_append_one_prefix( + sd_bus *bus, + sd_bus_message *m, + const char *prefix, + const char *path, + const char *interface, + bool require_fallback) { + + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL; + struct node_vtable *c; + struct node *n; + void *u = NULL; + int r; + + assert(bus); + assert(m); + assert(prefix); + assert(path); + assert(interface); + + n = hashmap_get(bus->nodes, prefix); + if (!n) + return 0; + + LIST_FOREACH(vtables, c, n->vtables) { + if (require_fallback && !c->is_fallback) + continue; + + if (streq(c->interface, interface)) + break; + } + + if (!c) + return 0; + + r = node_vtable_get_userdata(bus, path, c, &u); + if (r <= 0) + return r; + + r = sd_bus_message_append_basic(m, 's', interface); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "{sv}"); + if (r < 0) + return r; + + r = vtable_append_all_properties(bus, m,path, c, u, &error); + if (r < 0) + return r; + + if (sd_bus_error_is_set(&error)) + return bus_error_to_errno(&error); + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + return 1; +} + +static int interfaces_added_append_one( + sd_bus *bus, + sd_bus_message *m, + const char *path, + const char *interface) { + + char *prefix; + int r; + + assert(bus); + assert(m); + assert(path); + assert(interface); + + r = interfaces_added_append_one_prefix(bus, m, path, path, interface, false); + if (r != 0) + return r; + + prefix = alloca(strlen(path) + 1); + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { + r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true); + if (r != 0) + return r; + } + + return -ENOENT; } -int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interfaces, ...) { - return -ENOSYS; +int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) { + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + char **i; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (strv_isempty(interfaces)) + return 0; + + r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded", &m); + if (r < 0) + return r; + + r = sd_bus_message_append_basic(m, 'o', path); + if (r < 0) + return r; + + r = sd_bus_message_open_container(m, 'a', "{sa{sv}}"); + if (r < 0) + return r; + + STRV_FOREACH(i, interfaces) { + assert_return(interface_name_is_valid(*i), -EINVAL); + + r = sd_bus_message_open_container(m, 'e', "sa{sv}"); + if (r < 0) + return r; + + r = interfaces_added_append_one(bus, m, path, *i); + if (r < 0) + return r; + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return r; + + return sd_bus_send(bus, m, NULL); +} + +int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) { + _cleanup_strv_free_ char **interfaces = NULL; + va_list ap; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); + assert_return(!bus_pid_changed(bus), -ECHILD); + + va_start(ap, interface); + interfaces = strv_new_ap(interface, ap); + va_end(ap); + + if (!interfaces) + return -ENOMEM; + + return sd_bus_emit_interfaces_added_strv(bus, path, interfaces); +} + +int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces) { + _cleanup_bus_message_unref_ sd_bus_message *m = NULL; + int r; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); + assert_return(!bus_pid_changed(bus), -ECHILD); + + if (strv_isempty(interfaces)) + return 0; + + r = sd_bus_message_new_signal(bus, path, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved", &m); + if (r < 0) + return r; + + r = sd_bus_message_append_basic(m, 'o', path); + if (r < 0) + return r; + + r = sd_bus_message_append_strv(m, interfaces); + if (r < 0) + return r; + + return sd_bus_send(bus, m, NULL); +} + +int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) { + _cleanup_strv_free_ char **interfaces = NULL; + va_list ap; + + assert_return(bus, -EINVAL); + assert_return(object_path_is_valid(path), -EINVAL); + assert_return(BUS_IS_OPEN(bus->state), -ENOTCONN); + assert_return(!bus_pid_changed(bus), -ECHILD); + + va_start(ap, interface); + interfaces = strv_new_ap(interface, ap); + va_end(ap); + + if (!interfaces) + return -ENOMEM; + + return sd_bus_emit_interfaces_removed_strv(bus, path, interfaces); } int sd_bus_add_object_manager(sd_bus *bus, const char *path) { diff --git a/src/libsystemd-bus/test-bus-objects.c b/src/libsystemd-bus/test-bus-objects.c index 1c23c2136..2757f6932 100644 --- a/src/libsystemd-bus/test-bus-objects.c +++ b/src/libsystemd-bus/test-bus-objects.c @@ -39,7 +39,6 @@ * Add in: * * node hierarchy updates during dispatching - * emit_interfaces_added/emit_interfaces_removed * */ @@ -147,6 +146,28 @@ static int notify_test(sd_bus *bus, sd_bus_message *m, void *userdata) { return 1; } +static int emit_interfaces_added(sd_bus *bus, sd_bus_message *m, void *userdata) { + int r; + + assert_se(sd_bus_emit_interfaces_added(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0); + + r = sd_bus_reply_method_return(bus, m, NULL); + assert_se(r >= 0); + + return 1; +} + +static int emit_interfaces_removed(sd_bus *bus, sd_bus_message *m, void *userdata) { + int r; + + assert_se(sd_bus_emit_interfaces_removed(bus, m->path, "org.freedesktop.systemd.test", NULL) >= 0); + + r = sd_bus_reply_method_return(bus, m, NULL); + assert_se(r >= 0); + + return 1; +} + static const sd_bus_vtable vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_METHOD("AlterSomething", "s", "s", something_handler, 0), @@ -154,7 +175,9 @@ static const sd_bus_vtable vtable[] = { SD_BUS_WRITABLE_PROPERTY("Something", "s", get_handler, set_handler, 0, 0), SD_BUS_WRITABLE_PROPERTY("AutomaticStringProperty", "s", NULL, NULL, offsetof(struct context, automatic_string_property), 0), SD_BUS_WRITABLE_PROPERTY("AutomaticIntegerProperty", "u", NULL, NULL, offsetof(struct context, automatic_integer_property), 0), - SD_BUS_METHOD("NoOperation", "", "", NULL, 0), + SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0), + SD_BUS_METHOD("EmitInterfacesAdded", NULL, NULL, emit_interfaces_added, 0), + SD_BUS_METHOD("EmitInterfacesRemoved", NULL, NULL, emit_interfaces_removed, 0), SD_BUS_VTABLE_END }; @@ -385,6 +408,30 @@ static int client(struct context *c) { sd_bus_message_unref(reply); reply = NULL; + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, ""); + assert_se(r >= 0); + + r = sd_bus_process(bus, &reply); + assert_se(r > 0); + + assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")); + bus_message_dump(reply); + + sd_bus_message_unref(reply); + reply = NULL; + + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, ""); + assert_se(r >= 0); + + r = sd_bus_process(bus, &reply); + assert_se(r > 0); + + assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")); + bus_message_dump(reply); + + sd_bus_message_unref(reply); + reply = NULL; + r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, ""); assert_se(r >= 0); diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index e7ce56d7a..9b7ba5ad1 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -217,10 +217,10 @@ int sd_bus_emit_signal(sd_bus *bus, const char *path, const char *interface, con int sd_bus_emit_properties_changed_strv(sd_bus *bus, const char *path, const char *interface, char **names); int sd_bus_emit_properties_changed(sd_bus *bus, const char *path, const char *interface, const char *name, ...) _sd_sentinel_attr_; -int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces); /* MISSING */ -int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_attr_; /* MISSING */ -int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces); /* MISSING */ -int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_attr_; /* MISSING */ +int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces); +int sd_bus_emit_interfaces_added(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_attr_; +int sd_bus_emit_interfaces_removed_strv(sd_bus *bus, const char *path, char **interfaces); +int sd_bus_emit_interfaces_removed(sd_bus *bus, const char *path, const char *interface, ...) _sd_sentinel_attr_; /* Bus management */ -- 2.30.2