bus: implement object handler registry

This commit is contained in:
Lennart Poettering 2013-03-22 05:58:47 +01:00
parent 3cbb76ee34
commit a652755d2e
4 changed files with 290 additions and 26 deletions

View File

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

View File

@ -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_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_CALL)
return 0;
if (hashmap_isempty(bus->object_callbacks))
return 0;
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;
}
}
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 1;
}
static int process_message(sd_bus *bus, sd_bus_message *m) {
struct filter_callback *l;
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) {
struct reply_callback *c;
r = process_reply(bus, m);
if (r != 0)
return r;
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);
r = process_filter(bus, m);
if (r != 0)
return r;
r = c->callback(bus, 0, m, c->userdata);
free(c);
r = process_builtin(bus, m);
if (r != 0)
return r;
if (r != 0)
return r;
}
}
LIST_FOREACH(callbacks, l, bus->filter_callbacks) {
r = l->callback(bus, 0, m, l->userdata);
if (r != 0)
return r;
}
return process_builtin(bus, m);
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);
}

View File

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

View File

@ -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",