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.
This commit is contained in:
Lennart Poettering 2017-12-18 20:10:13 +01:00
parent e8bd7b092f
commit 98c5bbc85d
5 changed files with 237 additions and 21 deletions

View File

@ -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;

View File

@ -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, &param);
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, &param);
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) {

View File

@ -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);

View File

@ -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))

View File

@ -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);