summaryrefslogtreecommitdiff
path: root/src/common/dbus-sdbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/dbus-sdbus.c')
-rw-r--r--src/common/dbus-sdbus.c1955
1 files changed, 1955 insertions, 0 deletions
diff --git a/src/common/dbus-sdbus.c b/src/common/dbus-sdbus.c
new file mode 100644
index 0000000..a2fb9e9
--- /dev/null
+++ b/src/common/dbus-sdbus.c
@@ -0,0 +1,1955 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/dbus-sdbus.h>
+
+#define BUS_SERVICE "org.freedesktop.DBus"
+#define BUS_PATH "/org/freedesktop/DBus"
+#define BUS_INTERFACE "org.freedesktop.DBus"
+#define BUS_NAME_CHANGED "NameOwnerChanged"
+#define BUS_GET_OWNER "GetNameOwner"
+
+/* XXX check these... */
+#define SDBUS_ERROR_FAILED "org.DBus.error.failed"
+
+/* D-Bus name request flags and reply statuses. */
+#define SDBUS_NAME_REQUEST_REPLACE 0x2
+#define SDBUS_NAME_REQUEST_DONTQ 0x4
+
+#define SDBUS_NAME_STATUS_OWNER 0x1
+#define SDBUS_NAME_STATUS_QUEUING 0x2
+#define SDBUS_NAME_STATUS_EXISTS 0x3
+#define SDBUS_NAME_STATUS_GOTIT 0x4
+
+#define SDBUS_NAME_STATUS_RELEASED 0x1
+#define SDBUS_NAME_STATUS_UNKNOWN 0x2
+#define SDBUS_NAME_STATUS_FOREIGN 0x3
+
+#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000))
+#define MSEC_TO_USEC(msec) ((uint64_t)(msec) * 1000)
+
+struct mrp_dbus_s {
+ char *address; /* bus address */
+ sd_bus *bus; /* actual D-BUS connection */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ mrp_subloop_t *sl; /* subloop for pumping the bus */
+ mrp_htbl_t *objects; /* object path (refcount) table */
+ mrp_htbl_t *methods; /* method handler table */
+ mrp_htbl_t *signals; /* signal handler table */
+ mrp_list_hook_t name_trackers; /* peer (name) watchers */
+ mrp_list_hook_t calls; /* pending calls */
+ uint32_t call_id; /* next call id */
+ const char *unique_name; /* our unique D-BUS address */
+ int priv; /* whether a private connection */
+ int signal_filter; /* if signal dispatching is set up */
+ int register_fallback; /* if the fallback object is set up */
+ mrp_refcnt_t refcnt; /* reference count */
+};
+
+
+struct mrp_dbus_msg_s {
+ sd_bus_message *msg; /* actual D-Bus message */
+ mrp_refcnt_t refcnt; /* reference count */
+ mrp_list_hook_t arrays; /* implicitly freed related arrays */
+};
+
+
+typedef struct {
+ mrp_dbus_type_t type;
+ mrp_list_hook_t hook;
+ void *items;
+ size_t nitem;
+} msg_array_t;
+
+/*
+ * Notes:
+ *
+ * At the moment we administer DBUS method and signal handlers
+ * in a very primitive way (subject to be changed later). For
+ * every bus instance we maintain two hash tables, one for methods
+ * and another for signals. Each method and signal handler is
+ * hashed in only by it's method/signal name to a linked list of
+ * method or signal handlers.
+ *
+ * When dispatching a method, we look up the chain with a matching
+ * method name, or the chain for "" in case a matching chain is
+ * not found, and invoke the handler which best matches the
+ * received message (by looking at the path, interface and name).
+ * Only one such handler is invoked at most.
+ *
+ * For signals we look up both the chain with a matching name and
+ * the chain for "" and invoke all signal handlers that match the
+ * received message (regardless of their return value).
+ */
+
+typedef struct {
+ char *path; /* object path */
+ int cnt; /* reference count */
+} object_t;
+
+typedef struct {
+ char *member; /* signal/method name */
+ mrp_list_hook_t handlers; /* handlers with matching member */
+} handler_list_t;
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char *sender;
+ char *path;
+ char *interface;
+ char *member;
+ mrp_dbus_handler_t handler;
+ void *user_data;
+} handler_t;
+
+#define method_t handler_t
+#define signal_t handler_t
+
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to name tracker list */
+ char *name; /* name to track */
+ mrp_dbus_name_cb_t cb; /* status change callback */
+ void *user_data; /* opaque callback user data */
+ int32_t qid; /* initial query ID */
+} name_tracker_t;
+
+
+typedef struct {
+ mrp_dbus_t *dbus; /* DBUS connection */
+ int32_t id; /* call id */
+ mrp_dbus_reply_cb_t cb; /* completion notification callback */
+ void *user_data; /* opaque callback data */
+ uint64_t serial; /* DBUS call */
+ mrp_list_hook_t hook; /* hook to list of pending calls */
+ sd_bus_message *msg; /* original message */
+} call_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml; /* mainloop for bus connection */
+ const char *address; /* address of bus */
+} bus_spec_t;
+
+static mrp_htbl_t *buses;
+
+
+
+static int dispatch_signal(sd_bus *b, int r, sd_bus_message *msg, void *data);
+static int dispatch_method(sd_bus *b, int r, sd_bus_message *msg, void *data);
+
+static void purge_name_trackers(mrp_dbus_t *dbus);
+static void purge_calls(mrp_dbus_t *dbus);
+static void handler_list_free_cb(void *key, void *entry);
+static void handler_free(handler_t *h);
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ void *data);
+static void call_free(call_t *call);
+static void object_free_cb(void *key, void *entry);
+
+
+static int purge_objects(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ object_t *o = (object_t *)entry;
+
+ MRP_UNUSED(key);
+
+ sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus);
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static int purge_filters(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ handler_list_t *l = (handler_list_t *)entry;
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ MRP_UNUSED(key);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_dbus_remove_filter(dbus,
+ h->sender, h->path, h->interface,
+ h->member, NULL);
+ }
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static void dbus_disconnect(mrp_dbus_t *dbus)
+{
+ if (dbus) {
+ mrp_htbl_remove(buses, dbus->bus, FALSE);
+
+ if (dbus->objects) {
+ mrp_htbl_foreach(dbus->signals, purge_objects, dbus);
+ mrp_htbl_destroy(dbus->objects, TRUE);
+ }
+
+ if (dbus->signals) {
+ mrp_htbl_foreach(dbus->signals, purge_filters, dbus);
+ mrp_htbl_destroy(dbus->signals, TRUE);
+ }
+ if (dbus->methods)
+ mrp_htbl_destroy(dbus->methods, TRUE);
+
+ purge_name_trackers(dbus);
+ purge_calls(dbus);
+
+ if (dbus->bus != NULL) {
+ if (dbus->signal_filter)
+ sd_bus_remove_filter(dbus->bus, dispatch_signal, dbus);
+ if (dbus->register_fallback)
+ sd_bus_remove_fallback(dbus->bus, "/", dispatch_method, dbus);
+ if (dbus->priv)
+ sd_bus_close(dbus->bus);
+ else
+ sd_bus_unref(dbus->bus);
+ }
+
+ mrp_free(dbus->address);
+ dbus->bus = NULL;
+ dbus->ml = NULL;
+
+ mrp_free(dbus);
+ }
+}
+
+
+static int bus_cmp(const void *key1, const void *key2)
+{
+ return key2 - key1;
+}
+
+
+static uint32_t bus_hash(const void *key)
+{
+ uint32_t h;
+
+ h = (ptrdiff_t)key;
+ h >>= 2 * sizeof(key);
+
+ return h;
+}
+
+
+static int find_bus_by_spec(void *key, void *object, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)object;
+ bus_spec_t *spec = (bus_spec_t *)user_data;
+
+ MRP_UNUSED(key);
+
+ if (dbus->ml == spec->ml && !strcmp(dbus->address, spec->address))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static mrp_dbus_t *dbus_get(mrp_mainloop_t *ml, const char *address)
+{
+ mrp_htbl_config_t hcfg;
+ bus_spec_t spec;
+
+ if (buses == NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = bus_cmp;
+ hcfg.hash = bus_hash;
+ hcfg.free = NULL;
+
+ buses = mrp_htbl_create(&hcfg);
+
+ return NULL;
+ }
+ else {
+ spec.ml = ml;
+ spec.address = address;
+
+ return mrp_htbl_find(buses, find_bus_by_spec, &spec);
+ }
+}
+
+
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ mrp_dbus_err_t *errp)
+{
+ mrp_htbl_config_t hcfg;
+ mrp_dbus_t *dbus;
+
+ if ((dbus = dbus_get(ml, address)) != NULL)
+ return mrp_dbus_ref(dbus);
+
+ if ((dbus = mrp_allocz(sizeof(*dbus))) == NULL)
+ return NULL;
+
+ mrp_list_init(&dbus->calls);
+ mrp_list_init(&dbus->name_trackers);
+ mrp_refcnt_init(&dbus->refcnt);
+
+ dbus->ml = ml;
+
+ mrp_dbus_error_init(errp);
+
+ /*
+ * connect to the bus
+ */
+
+ if (!strcmp(address, "system")) {
+ if (sd_bus_open_system(&dbus->bus) != 0)
+ goto fail;
+ }
+ else if (!strcmp(address, "session")) {
+ if (sd_bus_open_user(&dbus->bus) != 0)
+ goto fail;
+ }
+ else {
+ dbus->priv = TRUE;
+
+ if (sd_bus_new(&dbus->bus) != 0)
+ goto fail;
+ else {
+ if (sd_bus_set_address(dbus->bus, address) != 0)
+ goto fail;
+
+ if (sd_bus_start(dbus->bus) != 0)
+ goto fail;
+ }
+ }
+
+ dbus->address = mrp_strdup(address);
+ if (sd_bus_get_unique_name(dbus->bus, &dbus->unique_name) != 0)
+ goto fail;
+
+ /*
+ * set up with mainloop
+ */
+
+ if (!mrp_dbus_setup_with_mainloop(ml, dbus->bus))
+ goto fail;
+
+ /*
+ * set up our message dispatchers and take our name on the bus
+ */
+
+ if (sd_bus_add_filter(dbus->bus, dispatch_signal, dbus) != 0) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to set up signal dispatching.");
+ goto fail;
+ }
+ dbus->signal_filter = TRUE;
+
+ if (sd_bus_add_fallback(dbus->bus, "/", dispatch_method, dbus) != 0) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to set up method dispatching.");
+ goto fail;
+ }
+ dbus->register_fallback = TRUE;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = object_free_cb;
+
+ if ((dbus->objects = mrp_htbl_create(&hcfg)) == NULL) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to create DBUS object path table.");
+ goto fail;
+ }
+
+ hcfg.free = handler_list_free_cb;
+
+ if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to create DBUS method table.");
+ goto fail;
+ }
+
+ if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to create DBUS signal table.");
+ goto fail;
+ }
+
+
+ /*
+ * install handler for NameOwnerChanged for tracking clients/peers
+ */
+
+ if (!mrp_dbus_add_signal_handler(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, name_owner_change_cb,
+ NULL)) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to install NameOwnerChanged handler.");
+ goto fail;
+ }
+
+ /* install a 'safe' filter to avoid receiving all name change signals */
+ mrp_dbus_install_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, BUS_SERVICE, NULL);
+
+ mrp_list_init(&dbus->name_trackers);
+ dbus->call_id = 1;
+
+ if (mrp_htbl_insert(buses, dbus->bus, dbus))
+ return dbus;
+
+ fail:
+ dbus_disconnect(dbus);
+ return NULL;
+}
+
+
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus)
+{
+ return mrp_ref_obj(dbus, refcnt);
+}
+
+
+int mrp_dbus_unref(mrp_dbus_t *dbus)
+{
+ if (mrp_unref_obj(dbus, refcnt)) {
+ dbus_disconnect(dbus);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ int flags = SDBUS_NAME_REQUEST_REPLACE | SDBUS_NAME_REQUEST_DONTQ;
+ int status;
+
+ mrp_dbus_error_init(error);
+
+ status = sd_bus_request_name(dbus->bus, name, flags);
+
+ if (status == SDBUS_NAME_STATUS_OWNER || status == SDBUS_NAME_STATUS_GOTIT)
+ return TRUE;
+ else {
+ mrp_dbus_error_set(error, SDBUS_ERROR_FAILED, "failed to request name");
+
+ return FALSE;
+ }
+}
+
+
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ int status;
+
+ mrp_dbus_error_init(error);
+
+ status = sd_bus_release_name(dbus->bus, name);
+
+ if (status == SDBUS_NAME_STATUS_RELEASED)
+ return TRUE;
+ else {
+ mrp_dbus_error_set(error, SDBUS_ERROR_FAILED, "failed to release name");
+
+ return FALSE;
+ }
+}
+
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus)
+{
+ return dbus->unique_name;
+}
+
+
+static void name_owner_query_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ name_tracker_t *t = (name_tracker_t *)data;
+ const char *owner;
+ int state;
+
+ if (t->cb != NULL) { /* tracker still active */
+ t->qid = 0;
+ state = !mrp_dbus_msg_is_error(m);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &owner))
+ owner = "<unknown>";
+
+ t->cb(dbus, t->name, state, owner, t->user_data);
+ }
+ else /* already requested to delete */
+ mrp_free(t);
+}
+
+
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ const char *name, *prev, *next;
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ MRP_UNUSED(data);
+
+ if (mrp_dbus_msg_type(m) != MRP_DBUS_MESSAGE_TYPE_SIGNAL)
+ return FALSE;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &name) ||
+ !mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &prev) ||
+ !mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &next))
+ return FALSE;
+
+#if 0
+ /*
+ * Notes: XXX TODO
+ * In principle t->cb could call mrp_dbus_forget for some other D-BUS
+ * address than name. If that happened to be n (== p->hook.next) this
+ * would result in a crash or memory corruption in the next iteration
+ * of this loop (when handling n). We can easily get around this
+ * problem by
+ *
+ * 1. administering in mrp_dbus_t that we're handing a NameOwnerChange
+ * 2. checking for this in mrp_dbus_forget_name and if it is the case
+ * only marking the affected entry for deletion
+ * 3. removing entries marked for deletion in this loop (or just
+ * ignoring them and making another pass in the end removing any
+ * such entry).
+ */
+#endif
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (!strcmp(name, t->name))
+ t->cb(dbus, name, next && *next, next, t->user_data);
+ }
+
+ return TRUE;
+}
+
+
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ name_tracker_t *t;
+
+ if ((t = mrp_allocz(sizeof(*t))) != NULL) {
+ if ((t->name = mrp_strdup(name)) != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+
+ if (mrp_dbus_install_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, name,
+ NULL)) {
+ mrp_list_append(&dbus->name_trackers, &t->hook);
+
+ t->qid = mrp_dbus_call(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_GET_OWNER, 5000,
+ name_owner_query_cb, t,
+ MRP_DBUS_TYPE_STRING, t->name,
+ MRP_DBUS_TYPE_INVALID);
+ return TRUE;
+ }
+ else {
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_dbus_remove_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, name,
+ NULL);
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (t->cb == cb && t->user_data == user_data && !strcmp(t->name,name)) {
+ mrp_list_delete(&t->hook);
+ mrp_free(t->name);
+
+ if (!t->qid)
+ mrp_free(t);
+ else {
+ t->cb = NULL;
+ t->user_data = NULL;
+ t->name = NULL;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_name_trackers(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ mrp_list_delete(p);
+ mrp_dbus_remove_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, t->name,
+ NULL);
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+}
+
+
+static handler_t *handler_alloc(const char *sender, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_t *h;
+
+ if ((h = mrp_allocz(sizeof(*h))) != NULL) {
+ h->sender = mrp_strdup(sender);
+ h->path = mrp_strdup(path);
+ h->interface = mrp_strdup(interface);
+ h->member = mrp_strdup(member);
+
+ if ((path && !h->path) || !h->interface || !h->member) {
+ handler_free(h);
+ return NULL;
+ }
+
+ h->handler = handler;
+ h->user_data = user_data;
+
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static void handler_free(handler_t *h)
+{
+ if (h != NULL) {
+ mrp_free(h->sender);
+ mrp_free(h->path);
+ mrp_free(h->interface);
+ mrp_free(h->member);
+
+ mrp_free(h);
+ }
+}
+
+
+static handler_list_t *handler_list_alloc(const char *member)
+{
+ handler_list_t *l;
+
+ if ((l = mrp_allocz(sizeof(*l))) != NULL) {
+ if ((l->member = mrp_strdup(member)) != NULL)
+ mrp_list_init(&l->handlers);
+ else {
+ mrp_free(l);
+ l = NULL;
+ }
+ }
+
+ return l;
+}
+
+
+static inline void handler_list_free(handler_list_t *l)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_list_delete(p);
+ handler_free(h);
+ }
+
+ mrp_free(l->member);
+ mrp_free(l);
+}
+
+
+static void handler_list_free_cb(void *key, void *entry)
+{
+ MRP_UNUSED(key);
+
+ handler_list_free((handler_list_t *)entry);
+}
+
+
+static inline int handler_specificity(handler_t *h)
+{
+ int score = 0;
+
+ if (h->path && *h->path)
+ score |= 0x4;
+ if (h->interface && *h->interface)
+ score |= 0x2;
+ if (h->member && *h->member)
+ score |= 0x1;
+
+ return score;
+}
+
+
+static void handler_list_insert(handler_list_t *l, handler_t *handler)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+ int score;
+
+ score = handler_specificity(handler);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (score >= handler_specificity(h)) {
+ mrp_list_append(h->hook.prev, &handler->hook); /* add before h */
+ return;
+ }
+ }
+
+ mrp_list_append(&l->handlers, &handler->hook);
+}
+
+
+static handler_t *handler_list_lookup(handler_list_t *l, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (h->handler == handler && user_data == h->user_data &&
+ path && !strcmp(path, h->path) &&
+ interface && !strcmp(interface, h->interface) &&
+ member && !strcmp(member, h->member))
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static handler_t *handler_list_find(handler_list_t *l, const char *path,
+ const char *interface, const char *member)
+{
+#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field))
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h, path) && MATCHES(h, interface) && MATCHES(h, member))
+ return h;
+ }
+
+ return NULL;
+#undef MATCHES
+}
+
+
+static void object_free_cb(void *key, void *entry)
+{
+ object_t *o = (object_t *)entry;
+
+ MRP_UNUSED(key);
+
+ mrp_free(o->path);
+ mrp_free(o);
+}
+
+
+static object_t *object_add(mrp_dbus_t *dbus, const char *path)
+{
+ object_t *o;
+
+ o = mrp_alloc(sizeof(*o));
+
+ if (o != NULL) {
+ o->path = mrp_strdup(path);
+ o->cnt = 1;
+
+ if (o->path == NULL) {
+ mrp_free(o);
+ return NULL;
+ }
+
+ if (sd_bus_add_object(dbus->bus, o->path, dispatch_method, dbus) == 0) {
+ if (mrp_htbl_insert(dbus->objects, o->path, o))
+ return o;
+ else
+ sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus);
+ }
+
+ mrp_free(o->path);
+ mrp_free(o);
+ }
+
+ return NULL;
+}
+
+
+static object_t *object_lookup(mrp_dbus_t *dbus, const char *path)
+{
+ return mrp_htbl_lookup(dbus->objects, (void *)path);
+}
+
+
+static int object_ref(mrp_dbus_t *dbus, const char *path)
+{
+ object_t *o;
+
+ if ((o = object_lookup(dbus, path)) != NULL) {
+ o->cnt++;
+ return TRUE;
+ }
+
+ if (object_add(dbus, path) != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static void object_unref(mrp_dbus_t *dbus, const char *path)
+{
+ object_t *o;
+
+ if ((o = object_lookup(dbus, path)) != NULL) {
+ o->cnt--;
+
+ if (o->cnt <= 0) {
+ mrp_htbl_remove(dbus->objects, (void *)path, FALSE);
+ sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus);
+
+ mrp_free(o->path);
+ mrp_free(o);
+ }
+ }
+}
+
+
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if (!object_ref(dbus, path))
+ return FALSE;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) {
+ if ((methods = handler_list_alloc(member)) == NULL)
+ goto fail;
+
+ mrp_htbl_insert(dbus->methods, methods->member, methods);
+ }
+
+ m = handler_alloc(NULL, path, interface, member, handler, user_data);
+
+ if (m != NULL) {
+ handler_list_insert(methods, m);
+
+ return TRUE;
+ }
+
+ fail:
+ object_unref(dbus, path);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL)
+ return FALSE;
+
+ m = handler_list_lookup(methods, path, interface, member,
+ handler, user_data);
+ if (m != NULL) {
+ object_unref(dbus, path);
+ mrp_list_delete(&m->hook);
+ handler_free(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) {
+ if ((signals = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ if (!mrp_htbl_insert(dbus->signals, signals->member, signals)) {
+ handler_list_free(signals);
+ return FALSE;
+ }
+ }
+
+ s = handler_alloc(sender, path, interface, member, handler, user_data);
+ if (s != NULL) {
+ handler_list_insert(signals, s);
+ return TRUE;
+ }
+ else {
+ handler_free(s);
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, signals->member, TRUE);
+ return FALSE;
+ }
+}
+
+
+
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ MRP_UNUSED(sender);
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL)
+ return FALSE;
+
+ s = handler_list_lookup(signals, path, interface, member,
+ handler, user_data);
+ if (s != NULL) {
+ mrp_list_delete(&s->hook);
+ handler_free(s);
+
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, (void *)member, TRUE);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int success;
+
+
+ if (mrp_dbus_add_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data)) {
+ va_start(ap, member);
+ success = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ if (success)
+ return TRUE;
+ else
+ mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ status = mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ va_start(ap, member);
+ status &= mrp_dbus_remove_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+#define ADD_TAG(tag, value) do { \
+ if (value != NULL) { \
+ l = snprintf(p, n, "%s%s='%s'", p == filter ? "" : ",", \
+ tag, value); \
+ if (l >= n) \
+ return FALSE; \
+ n -= l; \
+ p += l; \
+ } \
+ } while (0)
+
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ if (sd_bus_add_match(dbus->bus, filter, NULL, NULL) != 0) {
+ mrp_log_error("Failed to install filter '%s'.", filter);
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+
+}
+
+
+int mrp_dbus_install_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ sd_bus_remove_match(dbus->bus, filter, NULL, NULL);
+
+ return TRUE;
+#undef ADD_TAG
+}
+
+
+int mrp_dbus_remove_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_remove_filterv(dbus, sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+static int element_size(mrp_dbus_type_t type)
+{
+ switch (type) {
+ case MRP_DBUS_TYPE_BYTE: return sizeof(char);
+ case MRP_DBUS_TYPE_BOOLEAN: return sizeof(uint32_t);
+ case MRP_DBUS_TYPE_INT16:
+ case MRP_DBUS_TYPE_UINT16: return sizeof(uint16_t);
+ case MRP_DBUS_TYPE_INT32:
+ case MRP_DBUS_TYPE_UINT32: return sizeof(uint32_t);
+ case MRP_DBUS_TYPE_INT64:
+ case MRP_DBUS_TYPE_UINT64: return sizeof(uint64_t);
+ case MRP_DBUS_TYPE_DOUBLE: return sizeof(double);
+ case MRP_DBUS_TYPE_STRING: return sizeof(char *);
+ case MRP_DBUS_TYPE_OBJECT_PATH: return sizeof(char *);
+ case MRP_DBUS_TYPE_SIGNATURE: return sizeof(char *);
+ default:
+ return FALSE;
+ }
+
+}
+
+
+static inline mrp_dbus_msg_t *create_message(sd_bus_message *msg, int ref)
+{
+ mrp_dbus_msg_t *m;
+
+ if (msg != NULL) {
+ if ((m = mrp_allocz(sizeof(*m))) != NULL) {
+ mrp_refcnt_init(&m->refcnt);
+ mrp_list_init(&m->arrays);
+ if (ref)
+ m->msg = sd_bus_message_ref(msg);
+ else
+ m->msg = msg;
+ }
+
+ return m;
+ }
+ else
+ return NULL;
+}
+
+
+static void free_msg_array(msg_array_t *a)
+{
+ void *ptr;
+ size_t esize, i;
+ int string;
+
+ if (a == NULL)
+ return;
+
+ mrp_list_delete(&a->hook);
+
+ if ((esize = element_size(a->type)) != 0) {
+ if (a->type == MRP_DBUS_TYPE_STRING ||
+ a->type == MRP_DBUS_TYPE_OBJECT_PATH ||
+ a->type == MRP_DBUS_TYPE_SIGNATURE)
+ string = TRUE;
+ else
+ string = FALSE;
+
+ if (string)
+ for (i = 0, ptr = a->items; i < a->nitem; i++, ptr += esize)
+ mrp_free(ptr);
+
+ mrp_free(a->items);
+ }
+ else
+ mrp_log_error("Hmm... looks like we have a corrupted implicit array.");
+
+ mrp_free(a);
+}
+
+
+static void free_message(mrp_dbus_msg_t *m)
+{
+ mrp_list_hook_t *p, *n;
+ msg_array_t *a;
+
+ mrp_list_foreach(&m->arrays, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ free_msg_array(a);
+ }
+
+ mrp_free(m);
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m)
+{
+ return mrp_ref_obj(m, refcnt);
+}
+
+
+int mrp_dbus_msg_unref(mrp_dbus_msg_t *m)
+{
+ if (mrp_unref_obj(m, refcnt)) {
+ sd_bus_message_unref(m->msg);
+ free_message(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static inline int verify_type(sd_bus_message *msg, int expected_type)
+{
+ uint8_t type;
+
+ if (sd_bus_message_get_type(msg, &type) != 0 || type == expected_type)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+static int dispatch_method(sd_bus *bus,int ret, sd_bus_message *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ const char *path = sd_bus_message_get_path(msg);
+ const char *interface = sd_bus_message_get_interface(msg);
+ const char *member = sd_bus_message_get_member(msg);
+ int r = FALSE;
+ handler_list_t *l;
+ handler_t *h;
+
+ MRP_UNUSED(bus);
+ MRP_UNUSED(ret);
+
+ if (!verify_type(msg, MRP_DBUS_MESSAGE_TYPE_METHOD_CALL) || !member)
+ return r;
+
+ mrp_debug("path='%s', interface='%s', member='%s')...",
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) {
+ retry:
+ if ((h = handler_list_find(l, path, interface, member)) != NULL) {
+ sd_bus_message_rewind(msg, TRUE);
+
+ if (m == NULL)
+ m = create_message(msg, TRUE);
+
+ if (h->handler(dbus, m, h->user_data))
+ r = TRUE;
+
+ goto out;
+ }
+ }
+ else {
+ if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL)
+ goto retry;
+ }
+
+ out:
+ if (!r)
+ mrp_debug("Unhandled method path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ mrp_dbus_msg_unref(m);
+
+ return r;
+}
+
+
+static int dispatch_signal(sd_bus *bus,int ret, sd_bus_message *msg, void *data)
+{
+#define MATCHES(h, field) (!*field || !h->field || !*h->field || \
+ !strcmp(field, h->field))
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ const char *path = sd_bus_message_get_path(msg);
+ const char *interface = sd_bus_message_get_interface(msg);
+ const char *member = sd_bus_message_get_member(msg);
+ mrp_list_hook_t *p, *n;
+ handler_list_t *l;
+ handler_t *h;
+ int retried = FALSE;
+ int handled = FALSE;
+
+ MRP_UNUSED(bus);
+ MRP_UNUSED(ret);
+
+ if (!verify_type(msg, MRP_DBUS_MESSAGE_TYPE_SIGNAL) || !member)
+ return FALSE;
+
+ mrp_debug("%s(path='%s', interface='%s', member='%s')...",
+ __FUNCTION__, SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->signals, (void *)member)) != NULL) {
+ retry:
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h,path) && MATCHES(h,interface) && MATCHES(h,member)) {
+ sd_bus_message_rewind(msg, TRUE);
+
+ if (m == NULL)
+ m = create_message(msg, TRUE);
+
+ h->handler(dbus, m, h->user_data);
+ handled = TRUE;
+ }
+ }
+ }
+
+ if (!retried) {
+ if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) {
+ retried = TRUE;
+ goto retry;
+ }
+ }
+
+ if (!handled)
+ mrp_debug("Unhandled signal path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ mrp_dbus_msg_unref(m);
+
+ return FALSE;
+#undef MATCHES
+#undef SAFESTR
+}
+
+
+static int append_args_strtype(mrp_dbus_msg_t *msg, const char *types,
+ va_list ap)
+{
+ MRP_UNUSED(msg);
+ MRP_UNUSED(types);
+ MRP_UNUSED(ap);
+
+ return FALSE;
+}
+
+
+static int append_args_inttype(sd_bus_message *msg, int type, va_list ap)
+{
+ void *vptr;
+ int atype, elen, i;
+ void **aptr;
+ int alen;
+ char stype[2] = { '\0', '\0' };
+ int r = 0;
+
+ (void)append_args_strtype;
+
+ while (type != MRP_DBUS_TYPE_INVALID) {
+ switch (type) {
+ case MRP_DBUS_TYPE_BYTE:
+ case MRP_DBUS_TYPE_BOOLEAN:
+ case MRP_DBUS_TYPE_INT16:
+ case MRP_DBUS_TYPE_UINT16:
+ case MRP_DBUS_TYPE_INT32:
+ case MRP_DBUS_TYPE_UINT32:
+ case MRP_DBUS_TYPE_INT64:
+ case MRP_DBUS_TYPE_UINT64:
+ case MRP_DBUS_TYPE_DOUBLE:
+ case MRP_DBUS_TYPE_STRING:
+ case MRP_DBUS_TYPE_OBJECT_PATH:
+ case MRP_DBUS_TYPE_SIGNATURE:
+ case MRP_DBUS_TYPE_UNIX_FD:
+ vptr = va_arg(ap, void *);
+ r = sd_bus_message_append_basic(msg, type, vptr);
+ break;
+
+ case MRP_DBUS_TYPE_ARRAY:
+ atype = va_arg(ap, int);
+ aptr = va_arg(ap, void **);
+ alen = va_arg(ap, int);
+
+ switch (atype) {
+#define LEN(_type, _size) case MRP_DBUS_TYPE_##_type: elen = _size; break
+ LEN(BYTE , sizeof(uint8_t));
+ LEN(BOOLEAN , sizeof(uint32_t));
+ LEN(INT16 , sizeof(int16_t));
+ LEN(UINT16 , sizeof(uint16_t));
+ LEN(INT32 , sizeof(int32_t));
+ LEN(UINT32 , sizeof(uint32_t));
+ LEN(INT64 , sizeof(int64_t));
+ LEN(UINT64 , sizeof(uint64_t));
+ LEN(DOUBLE , sizeof(double));
+ LEN(STRING , sizeof(const char *));
+ LEN(OBJECT_PATH, sizeof(const char *));
+ LEN(SIGNATURE , sizeof(const char *));
+ LEN(UNIX_FD , sizeof(int));
+#undef LEN
+ default:
+ return FALSE;
+ }
+
+ stype[0] = atype;
+ if (sd_bus_message_open_container(msg, type, stype) != 0)
+ return FALSE;
+ for (i = 0; i < alen; i++, aptr += elen)
+ if (sd_bus_message_append_basic(msg, atype, aptr) != 0)
+ return FALSE;
+ if (sd_bus_message_close_container(msg) != 0)
+ return FALSE;
+ else
+ return TRUE;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ type = va_arg(ap, int);
+ }
+
+ return (r == 0 ? TRUE : FALSE);
+}
+
+
+static int call_reply_cb(sd_bus *bus, int ret, sd_bus_message *msg,
+ void *user_data)
+{
+ call_t *call = (call_t *)user_data;
+ mrp_dbus_msg_t *reply = create_message(msg, TRUE);
+ sd_bus_error error;
+
+ MRP_UNUSED(bus);
+
+ call->serial = 0;
+ mrp_list_delete(&call->hook);
+
+ if (ret == 0) {
+ reply = create_message(msg, TRUE);
+ sd_bus_message_rewind(reply->msg, TRUE);
+ }
+ else {
+ sd_bus_message *err = NULL;
+
+ if (ret == ETIMEDOUT)
+ error = SD_BUS_ERROR_MAKE(MRP_DBUS_ERROR_TIMEOUT,
+ "D-Bus call timed out");
+ else
+ error = SD_BUS_ERROR_MAKE(MRP_DBUS_ERROR_FAILED,
+ "D-Bus call failed");
+
+ if (sd_bus_message_new_method_error(bus, call->msg, &error, &err) == 0)
+ reply = create_message(err, FALSE);
+ else
+ reply = NULL;
+ }
+
+ call->cb(call->dbus, reply, call->user_data);
+ call_free(call);
+ mrp_dbus_msg_unref(reply);
+
+ return TRUE;
+}
+
+
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data, int type, ...)
+{
+ va_list ap;
+ int32_t id;
+ call_t *call;
+ sd_bus_message *msg;
+ int success;
+
+ call = NULL;
+
+ if (sd_bus_message_new_method_call(dbus->bus, dest, path, interface, member,
+ &msg) != 0)
+ return 0;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (type == MRP_DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (cb == NULL) {
+ sd_bus_message_set_no_reply(msg, TRUE);
+ if (sd_bus_send(dbus->bus, msg, NULL) != 0)
+ goto fail;
+ sd_bus_message_unref(msg);
+ }
+ else {
+ if (sd_bus_send_with_reply(dbus->bus, msg, call_reply_cb, call,
+ timeout * 1000, &call->serial) != 0)
+ goto fail;
+
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->msg = msg;
+ }
+
+ return id;
+
+ fail:
+ sd_bus_message_unref(msg);
+ call_free(call);
+
+ return 0;
+}
+
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *m)
+{
+ /*bus_message_dump(m->msg);*/
+
+ if (sd_bus_send(dbus->bus, m->msg, NULL) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ if (call->id == id) {
+ mrp_list_delete(p);
+
+ sd_bus_send_with_reply_cancel(dbus->bus, call->serial);
+ call->serial = 0;
+
+ call_free(call);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, int type, ...)
+{
+ va_list ap;
+ sd_bus_message *rpl;
+ int success;
+
+ if (sd_bus_message_new_method_return(dbus->bus, m->msg, &rpl) != 0)
+ return FALSE;
+
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+
+ if (!success)
+ goto fail;
+
+ if (sd_bus_send(dbus->bus, rpl, NULL) != 0)
+ goto fail;
+
+ sd_bus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ sd_bus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ const char *errname, const char *errmsg, int type, ...)
+{
+ va_list ap;
+ sd_bus_message *rpl;
+ int success;
+ sd_bus_error err = SD_BUS_ERROR_NULL;;
+
+ sd_bus_error_set_const(&err, errname, errmsg);
+
+ if (sd_bus_message_new_method_error(dbus->bus, m->msg, &err, &rpl) != 0)
+ return FALSE;
+
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+
+ if (!success)
+ goto fail;
+
+ if (sd_bus_send(dbus->bus, rpl, NULL) != 0)
+ goto fail;
+
+ sd_bus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ sd_bus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+static void call_free(call_t *call)
+{
+ if (call != NULL) {
+ sd_bus_message_unref(call->msg);
+ mrp_free(call);
+ }
+}
+
+
+static void purge_calls(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ mrp_list_delete(&call->hook);
+
+ if (call->serial != 0)
+ sd_bus_send_with_reply_cancel(dbus->bus, call->serial);
+
+ mrp_free(call);
+ }
+}
+
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...)
+{
+ va_list ap;
+ sd_bus_message *msg;
+ int success;
+
+ if (sd_bus_message_new_signal(dbus->bus, path, interface, member,
+ &msg) != 0)
+ return 0;
+
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+
+ if (!success)
+ goto fail;
+
+ if (dest != NULL)
+ if (sd_bus_message_set_destination(msg, dest) != 0)
+ goto fail;
+
+ if (sd_bus_send(dbus->bus, msg, NULL) != 0)
+ goto fail;
+
+ sd_bus_message_unref(msg);
+
+ return TRUE;
+
+ fail:
+ sd_bus_message_unref(msg);
+
+ return 0;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *dbus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ sd_bus_message *msg;
+
+ if (sd_bus_message_new_method_call(dbus->bus, destination,
+ path, interface, member, &msg) == 0)
+ return create_message(msg, FALSE);
+ else
+ return NULL;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *dbus,
+ mrp_dbus_msg_t *msg)
+{
+ sd_bus_message *req, *rpl;
+
+ req = (sd_bus_message *)msg;
+
+ if (sd_bus_message_new_method_return(dbus->bus, req, &rpl) == 0)
+ return create_message(rpl, FALSE);
+ else
+ return NULL;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ mrp_dbus_err_t *err)
+{
+ sd_bus_message *req, *rpl;
+
+ req = m->msg;
+
+ if (sd_bus_message_new_method_error(dbus->bus, req, err, &rpl) == 0)
+ return create_message(rpl, FALSE);
+ else
+ return NULL;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *dbus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ sd_bus_message *msg = NULL;
+
+ if (sd_bus_message_new_signal(dbus->bus, path, interface, member,
+ &msg) == 0) {
+ if (destination != NULL) {
+ if (sd_bus_message_set_destination(msg, destination) != 0) {
+ sd_bus_message_unref(msg);
+ msg = NULL;
+ }
+ }
+ }
+
+ return create_message(msg, FALSE);
+}
+
+
+mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *m)
+{
+ uint8_t type;
+
+ if (sd_bus_message_get_type(m->msg, &type) == 0)
+ return (mrp_dbus_msg_type_t)type;
+ else
+ return MRP_DBUS_MESSAGE_TYPE_INVALID;
+}
+
+#define WRAP_GETTER(type, what) \
+ type mrp_dbus_msg_##what(mrp_dbus_msg_t *m) \
+ { \
+ return sd_bus_message_get_##what((sd_bus_message *)m->msg); \
+ } \
+ struct __mrp_dbus_allow_trailing_semicolon
+
+WRAP_GETTER(const char *, path);
+WRAP_GETTER(const char *, interface);
+WRAP_GETTER(const char *, member);
+WRAP_GETTER(const char *, destination);
+WRAP_GETTER(const char *, sender);
+
+#undef WRAP_GETTER
+
+
+int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ return sd_bus_message_open_container(m->msg, type, contents) == 0;
+}
+
+
+int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m)
+{
+ return sd_bus_message_close_container(m->msg) == 0;
+}
+
+
+int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ return sd_bus_message_append_basic(m->msg, type, valuep) == 0;
+}
+
+
+int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ return sd_bus_message_enter_container(m->msg, type, contents) == 1;
+}
+
+
+int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m)
+{
+ return sd_bus_message_exit_container(m->msg) == 1;
+}
+
+
+int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ return sd_bus_message_read_basic(m->msg, type, valuep) == 1;
+}
+
+
+int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type,
+ void **itemsp, size_t *nitemp)
+{
+ char sub[2] = { (char)type, '\0' };
+ msg_array_t *a;
+ int offs;
+ size_t esize;
+
+ if ((esize = element_size(type)) == 0)
+ return FALSE;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sub))
+ return FALSE;
+
+ if ((a = mrp_allocz(sizeof(*a))) == NULL)
+ goto fail;
+
+ a->type = type;
+ mrp_list_init(&a->hook);
+
+ offs = 0;
+ while (mrp_dbus_msg_arg_type(m, NULL) != MRP_DBUS_TYPE_INVALID) {
+ if (!mrp_realloc(a->items, offs + esize))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, type, a->items + offs))
+ goto fail;
+ else
+ a->nitem++;
+
+ offs += esize;
+ }
+
+ mrp_dbus_msg_exit_container(m);
+
+ mrp_list_append(&m->arrays, &a->hook);
+ *itemsp = a->items;
+ *nitemp = a->nitem;
+
+ return TRUE;
+
+ fail:
+ mrp_dbus_msg_exit_container(m);
+ free_msg_array(a);
+
+ return FALSE;
+}
+
+
+mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents)
+{
+ char type;
+
+ if (sd_bus_message_peek_type(m->msg, &type, contents) >= 0)
+ return (mrp_dbus_type_t)type;
+ else
+ return MRP_DBUS_TYPE_INVALID;
+}