From a652755d2e8c2ddb31eb281f4585a3edbb333338 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 22 Mar 2013 05:58:47 +0100 Subject: [PATCH] bus: implement object handler registry --- src/libsystemd-bus/bus-internal.h | 9 ++ src/libsystemd-bus/sd-bus.c | 223 ++++++++++++++++++++++++++--- src/libsystemd-bus/sd-bus.h | 16 ++- src/libsystemd-bus/test-bus-chat.c | 58 ++++++++ 4 files changed, 285 insertions(+), 21 deletions(-) diff --git a/src/libsystemd-bus/bus-internal.h b/src/libsystemd-bus/bus-internal.h index 56514aebb..82f60849d 100644 --- a/src/libsystemd-bus/bus-internal.h +++ b/src/libsystemd-bus/bus-internal.h @@ -48,6 +48,14 @@ struct filter_callback { LIST_FIELDS(struct filter_callback, callbacks); }; +struct object_callback { + sd_message_handler_t callback; + void *userdata; + + char *path; + bool is_fallback; +}; + enum bus_state { BUS_OPENING, BUS_AUTHENTICATING, @@ -81,6 +89,7 @@ struct sd_bus { Prioq *reply_callbacks_prioq; Hashmap *reply_callbacks; LIST_HEAD(struct filter_callback, filter_callbacks); + Hashmap *object_callbacks; union { struct sockaddr sa; diff --git a/src/libsystemd-bus/sd-bus.c b/src/libsystemd-bus/sd-bus.c index 73774ba30..88f17a260 100644 --- a/src/libsystemd-bus/sd-bus.c +++ b/src/libsystemd-bus/sd-bus.c @@ -40,6 +40,7 @@ static int bus_poll(sd_bus *bus, bool need_more, uint64_t timeout_usec); static void bus_free(sd_bus *b) { struct filter_callback *f; + struct object_callback *c; unsigned i; assert(b); @@ -68,6 +69,13 @@ static void bus_free(sd_bus *b) { free(f); } + while ((c = hashmap_steal_first(b->object_callbacks))) { + free(c->path); + free(c); + } + + hashmap_free(b->object_callbacks); + free(b); } @@ -1552,6 +1560,43 @@ static int process_timeout(sd_bus *bus) { return r < 0 ? r : 1; } +static int process_reply(sd_bus *bus, sd_bus_message *m) { + struct reply_callback *c; + int r; + + assert(bus); + assert(m); + + if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_RETURN && + m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_ERROR) + return 0; + + c = hashmap_remove(bus->reply_callbacks, &m->reply_serial); + if (!c) + return 0; + + if (c->timeout != 0) + prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); + + r = c->callback(bus, 0, m, c->userdata); + free(c); + + return r; +} + +static int process_filter(sd_bus *bus, sd_bus_message *m) { + struct filter_callback *l; + int r; + + LIST_FOREACH(callbacks, l, bus->filter_callbacks) { + r = l->callback(bus, 0, m, l->userdata); + if (r != 0) + return r; + } + + return 0; +} + static int process_builtin(sd_bus *bus, sd_bus_message *m) { _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; int r; @@ -1603,36 +1648,90 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) { return 1; } -static int process_message(sd_bus *bus, sd_bus_message *m) { - struct filter_callback *l; +static int process_object(sd_bus *bus, sd_bus_message *m) { + _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_INIT; + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + struct object_callback *c; + char *p; int r; + bool found = false; assert(bus); assert(m); - if (m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_RETURN || m->header->type == SD_BUS_MESSAGE_TYPE_METHOD_ERROR) { - struct reply_callback *c; + if (m->header->type != SD_BUS_MESSAGE_TYPE_METHOD_CALL) + return 0; - c = hashmap_remove(bus->reply_callbacks, &m->reply_serial); - if (c) { - if (c->timeout != 0) - prioq_remove(bus->reply_callbacks_prioq, c, &c->prioq_idx); + if (hashmap_isempty(bus->object_callbacks)) + return 0; - r = c->callback(bus, 0, m, c->userdata); - free(c); + c = hashmap_get(bus->object_callbacks, m->path); + if (c) { + r = c->callback(bus, 0, m, c->userdata); + if (r != 0) + return r; + + found = true; + } + /* Look for fallback prefixes */ + p = strdupa(m->path); + for (;;) { + char *e; + + e = strrchr(p, '/'); + if (e == p || !e) + break; + + *e = 0; + + c = hashmap_get(bus->object_callbacks, p); + if (c && c->is_fallback) { + r = c->callback(bus, 0, m, c->userdata); if (r != 0) return r; + + found = true; } } - LIST_FOREACH(callbacks, l, bus->filter_callbacks) { - r = l->callback(bus, 0, m, l->userdata); - if (r != 0) - return r; - } + if (!found) + return 0; + + sd_bus_error_set(&error, + "org.freedesktop.DBus.Error.UnknownMethod", + "Unknown method '%s' or interface '%s'.", m->member, m->interface); + + r = sd_bus_message_new_method_error(bus, m, &error, &reply); + if (r < 0) + return r; + + r = sd_bus_send(bus, reply, NULL); + if (r < 0) + return r; - return process_builtin(bus, m); + return 1; +} + +static int process_message(sd_bus *bus, sd_bus_message *m) { + int r; + + assert(bus); + assert(m); + + r = process_reply(bus, m); + if (r != 0) + return r; + + r = process_filter(bus, m); + if (r != 0) + return r; + + r = process_builtin(bus, m); + if (r != 0) + return r; + + return process_object(bus, m); } int sd_bus_process(sd_bus *bus, sd_bus_message **ret) { @@ -1874,3 +1973,95 @@ int sd_bus_remove_filter(sd_bus *bus, sd_message_handler_t callback, void *userd return 0; } + +static int bus_add_object( + sd_bus *bus, + bool fallback, + const char *path, + sd_message_handler_t callback, + void *userdata) { + + struct object_callback *c; + int r; + + if (!bus) + return -EINVAL; + if (!path) + return -EINVAL; + if (!callback) + return -EINVAL; + + r = hashmap_ensure_allocated(&bus->object_callbacks, string_hash_func, string_compare_func); + if (r < 0) + return r; + + c = new(struct object_callback, 1); + if (!c) + return -ENOMEM; + + c->path = strdup(path); + if (!path) { + free(c); + return -ENOMEM; + } + + c->callback = callback; + c->userdata = userdata; + c->is_fallback = fallback; + + r = hashmap_put(bus->object_callbacks, c->path, c); + if (r < 0) { + free(c->path); + free(c); + return r; + } + + return 0; +} + +static int bus_remove_object( + sd_bus *bus, + bool fallback, + const char *path, + sd_message_handler_t callback, + void *userdata) { + + struct object_callback *c; + + if (!bus) + return -EINVAL; + if (!path) + return -EINVAL; + if (!callback) + return -EINVAL; + + c = hashmap_get(bus->object_callbacks, path); + if (!c) + return 0; + + if (c->callback != callback || c->userdata != userdata || c->is_fallback != fallback) + return 0; + + assert_se(c == hashmap_remove(bus->object_callbacks, c->path)); + + free(c->path); + free(c); + + return 1; +} + +int sd_bus_add_object(sd_bus *bus, const char *path, sd_message_handler_t callback, void *userdata) { + return bus_add_object(bus, false, path, callback, userdata); +} + +int sd_bus_remove_object(sd_bus *bus, const char *path, sd_message_handler_t callback, void *userdata) { + return bus_remove_object(bus, false, path, callback, userdata); +} + +int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata) { + return bus_add_object(bus, true, prefix, callback, userdata); +} + +int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata) { + return bus_remove_object(bus, true, prefix, callback, userdata); +} diff --git a/src/libsystemd-bus/sd-bus.h b/src/libsystemd-bus/sd-bus.h index 73710d151..b82caec5f 100644 --- a/src/libsystemd-bus/sd-bus.h +++ b/src/libsystemd-bus/sd-bus.h @@ -28,15 +28,15 @@ #include "sd-bus-protocol.h" /* TODO: - * * - make unix fd passing work - * - add page donation logic - * - api for appending/reading fixed arrays - * - merge busctl into systemctl or so? - * - add object handlers * - implicitly add stub introspection calls * - implement unix exec protocol * - server side + * + * Later: + * - add page donation logic + * - api for appending/reading fixed arrays + * - merge busctl into systemctl or so? */ typedef struct sd_bus sd_bus; @@ -80,6 +80,12 @@ int sd_bus_flush(sd_bus *bus); int sd_bus_add_filter(sd_bus *bus, sd_message_handler_t callback, void *userdata); int sd_bus_remove_filter(sd_bus *bus, sd_message_handler_t callback, void *userdata); +int sd_bus_add_object(sd_bus *bus, const char *path, sd_message_handler_t callback, void *userdata); +int sd_bus_remove_object(sd_bus *bus, const char *path, sd_message_handler_t callback, void *userdata); + +int sd_bus_add_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata); +int sd_bus_remove_fallback(sd_bus *bus, const char *prefix, sd_message_handler_t callback, void *userdata); + /* Message object */ int sd_bus_message_new_signal(sd_bus *bus, const char *path, const char *interface, const char *member, sd_bus_message **m); diff --git a/src/libsystemd-bus/test-bus-chat.c b/src/libsystemd-bus/test-bus-chat.c index 23b00c720..92fdd13fa 100644 --- a/src/libsystemd-bus/test-bus-chat.c +++ b/src/libsystemd-bus/test-bus-chat.c @@ -31,6 +31,37 @@ #include "bus-message.h" #include "bus-error.h" +static int object_callback(sd_bus *bus, int error, sd_bus_message *m, void *userdata) { + int r; + + assert(bus); + + if (error != 0) + return 0; + + if (sd_bus_message_is_method_call(m, "org.object.test", "Foobar")) { + _cleanup_bus_message_unref_ sd_bus_message *reply = NULL; + + log_info("Invoked Foobar() on %s", sd_bus_message_get_path(m)); + + r = sd_bus_message_new_method_return(bus, m, &reply); + if (r < 0) { + log_error("Failed to allocate return: %s", strerror(-r)); + return r; + } + + r = sd_bus_send(bus, reply, NULL); + if (r < 0) { + log_error("Failed to send reply: %s", strerror(-r)); + return r; + } + + return 1; + } + + return 0; +} + static int server_init(sd_bus **_bus) { sd_bus *bus = NULL; sd_id128_t id; @@ -67,6 +98,12 @@ static int server_init(sd_bus **_bus) { goto fail; } + r = sd_bus_add_fallback(bus, "/foo/bar", object_callback, NULL); + if (r < 0) { + log_error("Failed to add object: %s", strerror(-r)); + goto fail; + } + *_bus = bus; return 0; @@ -294,6 +331,27 @@ static void* client2(void*p) { goto finish; } + r = sd_bus_message_new_method_call( + bus, + "org.freedesktop.systemd.test", + "/foo/bar/waldo/piep", + "org.object.test", + "Foobar", + &m); + if (r < 0) { + log_error("Failed to allocate method call: %s", strerror(-r)); + goto finish; + } + + r = sd_bus_send(bus, m, NULL); + if (r < 0) { + log_error("Failed to issue method call: %s", bus_error_message(&error, -r)); + goto finish; + } + + sd_bus_message_unref(m); + m = NULL; + r = sd_bus_message_new_method_call( bus, "org.freedesktop.systemd.test", -- 2.30.2