From 98c5bbc85dc05c9dbccc1d289000dc554892d0d1 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 18 Dec 2017 20:10:13 +0100 Subject: [PATCH] sd-bus: add APIs to request/release names asynchronously They do the same thing as their synchronous counterparts, but only enqueue the operation, thus removing synchronization points during service initialization. If the callback function is passed as NULL we'll fallback to generic implementations of the reply handlers, that terminate the connection if the requested name cannot be acquired, under the assumption that not being able to acquire the name is a technical problem. --- src/libsystemd/libsystemd.sym | 2 + src/libsystemd/sd-bus/bus-control.c | 250 ++++++++++++++++++++++++--- src/libsystemd/sd-bus/bus-internal.h | 2 + src/libsystemd/sd-bus/sd-bus.c | 2 +- src/systemd/sd-bus.h | 2 + 5 files changed, 237 insertions(+), 21 deletions(-) diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 4229e0aeb1..9acaf07833 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -535,4 +535,6 @@ LIBSYSTEMD_237 { global: sd_bus_set_watch_bind; sd_bus_get_watch_bind; + sd_bus_request_name_async; + sd_bus_release_name_async; } LIBSYSTEMD_236; diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index e1e75da679..4038001318 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -57,14 +57,18 @@ _public_ int sd_bus_get_unique_name(sd_bus *bus, const char **unique) { return 0; } -_public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - uint32_t ret, param = 0; - int r; +static int validate_request_name_parameters( + sd_bus *bus, + const char *name, + uint64_t flags, + uint32_t *ret_param) { + + uint32_t param = 0; + + assert(bus); + assert(name); + assert(ret_param); - assert_return(bus, -EINVAL); - assert_return(name, -EINVAL); - assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(!(flags & ~(SD_BUS_NAME_ALLOW_REPLACEMENT|SD_BUS_NAME_REPLACE_EXISTING|SD_BUS_NAME_QUEUE)), -EINVAL); assert_return(service_name_is_valid(name), -EINVAL); assert_return(name[0] != ':', -EINVAL); @@ -86,6 +90,28 @@ _public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) if (!(flags & SD_BUS_NAME_QUEUE)) param |= BUS_NAME_DO_NOT_QUEUE; + *ret_param = param; + + return 0; +} + +_public_ int sd_bus_request_name( + sd_bus *bus, + const char *name, + uint64_t flags) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + uint32_t ret, param = 0; + int r; + + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = validate_request_name_parameters(bus, name, flags, ¶m); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.DBus", @@ -104,26 +130,112 @@ _public_ int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags) if (r < 0) return r; - if (ret == BUS_NAME_ALREADY_OWNER) + switch (ret) { + + case BUS_NAME_ALREADY_OWNER: return -EALREADY; - else if (ret == BUS_NAME_EXISTS) + + case BUS_NAME_EXISTS: return -EEXIST; - else if (ret == BUS_NAME_IN_QUEUE) + + case BUS_NAME_IN_QUEUE: return 0; - else if (ret == BUS_NAME_PRIMARY_OWNER) + + case BUS_NAME_PRIMARY_OWNER: return 1; + } return -EIO; } -_public_ int sd_bus_release_name(sd_bus *bus, const char *name) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; +static int default_request_name_handler( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) { + uint32_t ret; int r; + assert(m); + + if (sd_bus_message_is_method_error(m, NULL)) { + log_debug_errno(sd_bus_message_get_errno(m), + "Unable to request name, failing connection: %s", + sd_bus_message_get_error(m)->message); + + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; + } + + r = sd_bus_message_read(m, "u", &ret); + if (r < 0) + return r; + + switch (ret) { + + case BUS_NAME_ALREADY_OWNER: + log_debug("Already owner of requested service name, ignoring."); + return 1; + + case BUS_NAME_IN_QUEUE: + log_debug("In queue for requested service name."); + return 1; + + case BUS_NAME_PRIMARY_OWNER: + log_debug("Successfully acquired requested service name."); + return 1; + + case BUS_NAME_EXISTS: + log_debug("Requested service name already owned, failing connection."); + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; + } + + log_debug("Unexpected response from RequestName(), failing connection."); + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; +} + +_public_ int sd_bus_request_name_async( + sd_bus *bus, + sd_bus_slot **ret_slot, + const char *name, + uint64_t flags, + sd_bus_message_handler_t callback, + void *userdata) { + + uint32_t param = 0; + int r; + assert_return(bus, -EINVAL); assert_return(name, -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); + + r = validate_request_name_parameters(bus, name, flags, ¶m); + if (r < 0) + return r; + + return sd_bus_call_method_async( + bus, + ret_slot, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "RequestName", + callback ?: default_request_name_handler, + userdata, + "su", + name, + param); +} + +static int validate_release_name_parameters( + sd_bus *bus, + const char *name) { + + assert(bus); + assert(name); + assert_return(service_name_is_valid(name), -EINVAL); assert_return(name[0] != ':', -EINVAL); @@ -137,6 +249,25 @@ _public_ int sd_bus_release_name(sd_bus *bus, const char *name) { if (!BUS_IS_OPEN(bus->state)) return -ENOTCONN; + return 0; +} + +_public_ int sd_bus_release_name( + sd_bus *bus, + const char *name) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + uint32_t ret; + int r; + + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = validate_release_name_parameters(bus, name); + if (r < 0) + return r; + r = sd_bus_call_method( bus, "org.freedesktop.DBus", @@ -153,14 +284,93 @@ _public_ int sd_bus_release_name(sd_bus *bus, const char *name) { r = sd_bus_message_read(reply, "u", &ret); if (r < 0) return r; - if (ret == BUS_NAME_NON_EXISTENT) - return -ESRCH; - if (ret == BUS_NAME_NOT_OWNER) - return -EADDRINUSE; - if (ret == BUS_NAME_RELEASED) - return 0; - return -EINVAL; + switch (ret) { + + case BUS_NAME_NON_EXISTENT: + return -ESRCH; + + case BUS_NAME_NOT_OWNER: + return -EADDRINUSE; + + case BUS_NAME_RELEASED: + return 0; + } + + return -EIO; +} + +static int default_release_name_handler( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) { + + uint32_t ret; + int r; + + assert(m); + + if (sd_bus_message_is_method_error(m, NULL)) { + log_debug_errno(sd_bus_message_get_errno(m), + "Unable to release name, failing connection: %s", + sd_bus_message_get_error(m)->message); + + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; + } + + r = sd_bus_message_read(m, "u", &ret); + if (r < 0) + return r; + + switch (ret) { + + case BUS_NAME_NON_EXISTENT: + log_debug("Name asked to release is not taken currently, ignoring."); + return 1; + + case BUS_NAME_NOT_OWNER: + log_debug("Name asked to release is owned by somebody else, ignoring."); + return 1; + + case BUS_NAME_RELEASED: + log_debug("Name successfully released."); + return 1; + } + + log_debug("Unexpected response from ReleaseName(), failing connection."); + bus_enter_closing(sd_bus_message_get_bus(m)); + return 1; +} + +_public_ int sd_bus_release_name_async( + sd_bus *bus, + sd_bus_slot **ret_slot, + const char *name, + sd_bus_message_handler_t callback, + void *userdata) { + + int r; + + assert_return(bus, -EINVAL); + assert_return(name, -EINVAL); + assert_return(!bus_pid_changed(bus), -ECHILD); + + r = validate_release_name_parameters(bus, name); + if (r < 0) + return r; + + return sd_bus_call_method_async( + bus, + ret_slot, + "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus", + "ReleaseName", + callback ?: default_release_name_handler, + userdata, + "s", + name); } _public_ int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable) { diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 79012b0ad0..bf395b1f22 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -407,3 +407,5 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, sd_bus_error *error); if (!assert_log(expr, #expr)) \ return sd_bus_error_set_errno(error, r); \ } while (false) + +void bus_enter_closing(sd_bus *bus); diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 2c20ad6ebf..d551035cd6 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -1354,7 +1354,7 @@ _public_ sd_bus* sd_bus_flush_close_unref(sd_bus *bus) { return sd_bus_unref(bus); } -static void bus_enter_closing(sd_bus *bus) { +void bus_enter_closing(sd_bus *bus) { assert(bus); if (!IN_SET(bus->state, BUS_WATCH_BIND, BUS_OPENING, BUS_AUTHENTICATING, BUS_HELLO, BUS_RUNNING)) diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 66bc48842b..9cebafd304 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -302,7 +302,9 @@ int sd_bus_message_rewind(sd_bus_message *m, int complete); int sd_bus_get_unique_name(sd_bus *bus, const char **unique); int sd_bus_request_name(sd_bus *bus, const char *name, uint64_t flags); +int sd_bus_request_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, uint64_t flags, sd_bus_message_handler_t callback, void *userdata); int sd_bus_release_name(sd_bus *bus, const char *name); +int sd_bus_release_name_async(sd_bus *bus, sd_bus_slot **ret_slot, const char *name, sd_bus_message_handler_t callback, void *userdata); int sd_bus_list_names(sd_bus *bus, char ***acquired, char ***activatable); /* free the results */ int sd_bus_get_name_creds(sd_bus *bus, const char *name, uint64_t mask, sd_bus_creds **creds); /* unref the result! */ int sd_bus_get_name_machine_id(sd_bus *bus, const char *name, sd_id128_t *machine);