common: initial version of DBUS low-level and transport abstractions.
authorKrisztian Litkey <krisztian.litkey@intel.com>
Mon, 4 Jun 2012 15:24:18 +0000 (18:24 +0300)
committerKrisztian Litkey <krisztian.litkey@intel.com>
Mon, 4 Jun 2012 15:31:18 +0000 (18:31 +0300)
configure.ac
src/Makefile.am
src/common/dbus-transport.c [new file with mode: 0644]
src/common/dbus-transport.h [new file with mode: 0644]
src/common/dbus.c [new file with mode: 0644]
src/common/dbus.h [new file with mode: 0644]
src/common/tests/Makefile.am
src/common/transport.c
src/common/transport.h
src/daemon/murphy.conf

index 7801c37..5743f29 100644 (file)
@@ -85,15 +85,6 @@ PKG_CHECK_MODULES(GLIB, glib-2.0)
 AC_SUBST(GLIB_CFLAGS)
 AC_SUBST(GLIB_LIBS)
 
-# Check for low-level DBUS libs.
-PKG_CHECK_MODULES(DBUS, dbus-1 >= 0.70)
-AC_SUBST(DBUS_CFLAGS)
-AC_SUBST(DBUS_LIBS)
-
-DBUS_SESSION_DIR="`pkg-config --variable session_bus_services_dir dbus-1`"
-AC_SUBST(DBUS_SESSION_DIR)
-
-
 # Check and enable extra compiler warnings if they are supported.
 AC_ARG_ENABLE(extra-warnings,
               [  --enable-extra-warnings         enable extra compiler warnings],
@@ -113,6 +104,27 @@ fi
 AC_SUBST(WARNING_CFLAGS)
 
 
+# Check if DBUS was enabled.
+AC_ARG_ENABLE(dbus,
+              [  --enable-dbus         enable D-BUS support],
+             [enable_dbus=$enableval], [enable_dbus=no])
+
+if test "$enable_dbus" = "yes"; then
+    PKG_CHECK_MODULES(DBUS, dbus-1 >= 0.70)
+
+    DBUS_SESSION_DIR="`pkg-config --variable session_bus_services_dir dbus-1`"
+    AC_SUBST(DBUS_SESSION_DIR)
+else
+    AC_MSG_NOTICE([D-Bus support is disabled.])
+fi
+
+AM_CONDITIONAL(DBUS_ENABLED, [test "$enable_dbus" = "yes"])
+AC_SUBST(DBUS_ENABLED)
+AC_SUBST(DBUS_CFLAGS)
+AC_SUBST(DBUS_LIBS)
+
+
+
 # Set up murphy CFLAGS and LIBS.
 MURPHY_CFLAGS="$GLIB_CFLAGS $DBUS_CFLAGS"
 MURPHY_LIBS="$GLIB_LIBS $DBUS_LIBS"
index 5284e26..2119183 100644 (file)
@@ -40,24 +40,23 @@ libmurphy_common_la_HEADERS =               \
                common/msg.h            \
                common/transport.h
 
-
-libmurphy_common_la_SOURCES =          \
-               common/log.c            \
-               common/mm.c             \
-               common/hashtbl.c        \
-               common/mainloop.c       \
-               common/utils.c          \
-               common/file-utils.c     \
-               common/msg.c            \
-               common/transport.c      \
-               common/stream-transport.c \
+libmurphy_common_la_SOURCES =                  \
+               common/log.c                    \
+               common/mm.c                     \
+               common/hashtbl.c                \
+               common/mainloop.c               \
+               common/utils.c                  \
+               common/file-utils.c             \
+               common/msg.c                    \
+               common/transport.c              \
+               common/stream-transport.c       \
                common/dgram-transport.c
 
 libmurphy_common_la_CFLAGS  =          \
                $(AM_CFLAGS)
 
-libmurphy_common_la_LDFLAGS =          \
-               -Wl,-version-script=linker-script.common \
+libmurphy_common_la_LDFLAGS =                                  \
+               -Wl,-version-script=linker-script.common        \
                -version-info @MURPHY_VERSION_INFO@
 
 libmurphy_common_la_LIBADD  =          \
@@ -110,7 +109,7 @@ libmurphy_core_la_LDFLAGS =         \
                -Wl,-version-script=linker-script.core \
                -version-info @MURPHY_VERSION_INFO@
 
-libmurphy_core_la_LIBADD  =    \
+libmurphy_core_la_LIBADD  =            \
                libmurphy-common.la -ldl
 
 libmurphy_core_la_DEPENDENCIES = linker-script.core
@@ -123,6 +122,58 @@ clean-linker-script::
        -rm -f linker-script.core
 
 ###################################
+# murphy dbus library
+#
+
+if DBUS_ENABLED
+lib_LTLIBRARIES += libmurphy-dbus.la
+EXTRA_DIST      += common/murphy-dbus.pc
+pkgconfig_DATA  += common/murphy-dbus.pc
+
+libmurphy_dbush_ladir      =           \
+               $(includedir)/murphy
+
+libmurphy_dbush_la_HEADERS =           \
+               common/dbus.h           \
+               common/dbus-transport.h
+
+libmurphy_dbus_ladir      =            \
+               $(includedir)/murphy/common
+
+libmurphy_dbus_la_HEADERS =            \
+               common/dbus.h           \
+               common/dbus-transport.h
+
+libmurphy_dbus_la_SOURCES =            \
+               common/dbus.c           \
+               common/dbus-glue.c      \
+               common/dbus-transport.c
+
+libmurphy_dbus_la_CFLAGS  =            \
+               $(AM_CFLAGS)            \
+               $(DBUS_CFLAGS)
+
+libmurphy_dbus_la_LDFLAGS =            \
+               -Wl,-version-script=linker-script.dbus \
+               -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_dbus_la_LIBADD  =            \
+               -lrt $(DBUS_LIBS)
+
+libmurphy_dbus_la_DEPENDENCIES = linker-script.dbus
+
+libdbusincludedir      = $(includedir)/murphy/dbus
+libdbusinclude_HEADERS = $(libmurphy_dbus_la_HEADERS)
+
+# linker script generation
+linker-script.dbus: $(libmurphy_dbus_la_HEADERS)
+       $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q -o $@ $^
+
+clean-linker-script::
+       -rm -f linker-script.dbus
+endif
+
+###################################
 # murphy plugins
 #
 
@@ -153,9 +204,10 @@ plugin_LTLIBRARIES    += plugin-test.la
 endif
 
 # dbus plugin
+if DBUS_ENABLED
 DBUS_PLUGIN_SOURCES = plugins/plugin-dbus.c
 DBUS_PLUGIN_CFLAGS  = $(DBUS_CFLAGS)
-DBUS_PLUGIN_LIBS    = $(DBUS_LIBS)
+DBUS_PLUGIN_LIBS    = libmurphy-dbus.la
 
 if BUILTIN_PLUGIN_DBUS
 BUILTIN_PLUGINS += $(DBUS_PLUGIN_SOURCES)
@@ -169,6 +221,7 @@ plugin_dbus_la_LIBADD  = $(DBUS_PLUGIN_LIBS)
 
 plugin_LTLIBRARIES    += plugin-dbus.la
 endif
+endif
 
 # glib plugin
 GLIB_PLUGIN_SOURCES = plugins/plugin-glib.c
@@ -247,6 +300,10 @@ murphy_console_LDADD  =                    \
                libmurphy-common.la     \
                $(BUILTIN_LIBS)
 
+if DBUS_ENABLED
+murphy_console_LDADD +=        libmurphy-dbus.la
+endif
+
 murphy_console_LDFLAGS = -rdynamic -lreadline
 
 #end
diff --git a/src/common/dbus-transport.c b/src/common/dbus-transport.c
new file mode 100644 (file)
index 0000000..c0c574e
--- /dev/null
@@ -0,0 +1,1516 @@
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/dbus.h>
+#include <murphy/common/dbus-transport.h>
+
+#define DBUS  "dbus"
+#define DBUSL 4
+
+#define TRANSPORT_PATH       "/murphy/transport"
+#define TRANSPORT_INTERFACE  "Murphy.Transport"
+#define TRANSPORT_MESSAGE    "DeliverMessage"
+#define TRANSPORT_CUSTOM     "DeliverCustom"
+#define TRANSPORT_RAW        "DeliverRaw"
+#define TRANSPORT_METHOD     "DeliverMessage"
+
+#define ANY_ADDRESS          "any"
+
+typedef struct {
+    MRP_TRANSPORT_PUBLIC_FIELDS;         /* common transport fields */
+    mrp_dbus_t     *dbus;                /* D-BUS connection */
+    int             bound : 1;           /* whether bound to an address */
+    mrp_dbusaddr_t  local;               /* address we're bound to */
+    mrp_dbusaddr_t  remote;              /* address we're connected to */
+} dbus_t;
+
+
+static uint32_t nauto;                   /* for autobinding */
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+static int dbus_data_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+static int dbus_raw_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+
+#if 0
+static int dbus_recv_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+#endif
+
+static DBusMessage *msg_encode(const char *sender_id, mrp_msg_t *msg);
+static mrp_msg_t *msg_decode(DBusMessage *m, const char **sender_id);
+
+static DBusMessage *data_encode(const char *sender_id,
+                               void *data, uint16_t tag);
+static void *data_decode(DBusMessage *m, uint16_t *tag, const char **sender_id);
+
+
+
+
+static socklen_t parse_address(const char *str, mrp_dbusaddr_t *addr,
+                              socklen_t size)
+{
+    const char *p, *e;
+    char       *q;
+    size_t      l, n;
+
+    if (size < sizeof(*addr)) {
+       errno = EINVAL;
+       return FALSE;
+    }
+
+    if (strncmp(str, DBUS":", DBUSL + 1))
+       return 0;
+    else
+       str += DBUSL + 1;
+    
+    /*
+     * The format of the address is
+     *     dbus:[bus-address]@address/path
+     * eg.
+     *     dbus:[session]@:1.33/client1, or
+     *     dbus:[unix:abstract=/tmp/dbus-Xx2Kpi...a572]@:1.33/client1
+     */
+
+    p = str;
+    q = addr->db_fqa;
+    l = sizeof(addr->db_fqa);
+    
+    /* get bus address */
+    if (*p != '[') {
+       errno = EINVAL;
+       return 0;
+    }
+    else
+       p++;
+
+    e = strchr(p, ']');
+
+    if (e == NULL) {
+       errno = EINVAL;
+       return 0;
+    }
+    
+    n = e - p;
+    if (n >= l) {
+       errno = ENAMETOOLONG;
+       return 0;
+    }
+
+    /* save bus address */
+    strncpy(q, p, n);
+    q[n] = '\0';
+    addr->db_bus = q;
+
+    q += n + 1;
+    l -= n + 1;
+    p  = e + 1;
+    
+    /* get (local or remote) address on bus */
+    if (*p != '@')
+       addr->db_addr = ANY_ADDRESS;
+    else {
+       p++;
+       e = strchr(p, '/');
+
+       if (e == NULL) {
+           errno = EINVAL;
+           return 0;
+       }
+       
+       n = e - p;
+       if (n >= l) {
+           errno = ENAMETOOLONG;
+           return 0;
+       }
+
+       /* save address on bus */
+       strncpy(q, p, n);
+       q[n] = '\0';
+       addr->db_addr = q;
+       
+       q += n + 1;
+       l -= n + 1;
+       p  = e;
+    }
+    
+    /* get object path */
+    if (*p != '/') {
+       errno = EINVAL;
+       return 0;
+    }
+
+    n = snprintf(q, l, "%s%s", TRANSPORT_PATH, p);
+    if (n >= l) {
+       errno = ENAMETOOLONG;
+       return 0;
+    }
+
+    addr->db_path   = q;
+    addr->db_family = MRP_AF_DBUS;
+
+    return sizeof(*addr);
+}
+
+
+static mrp_dbusaddr_t *copy_address(mrp_dbusaddr_t *dst, mrp_dbusaddr_t *src)
+{
+    char   *p, *q;
+    size_t  l, n;
+
+    dst->db_family = src->db_family;
+
+    /* copy bus address */
+    p = src->db_bus;
+    q = dst->db_fqa;
+    l = sizeof(dst->db_fqa);
+
+    n = strlen(p);
+    if (l < n + 1) {
+       errno = EOVERFLOW;
+       return NULL;
+    }
+
+    dst->db_bus = q;
+    memcpy(q, p, n + 1);
+    q += n + 1;
+    l -= n + 1;
+
+    /* copy address */
+    p = src->db_addr;
+    n = strlen(p);
+    if (l < n + 1) {
+       errno = EOVERFLOW;
+       return NULL;
+    }
+
+    dst->db_addr = q;
+    memcpy(q, p, n + 1);
+    q += n + 1;
+    l -= n + 1;
+
+    /* copy path */
+    p = src->db_path;
+    n = strlen(p);
+    if (l < n + 1) {
+       errno = EOVERFLOW;
+       return NULL;
+    }
+
+    dst->db_path = q;
+    memcpy(q, p, n + 1);
+    q += n + 1;
+    l -= n + 1;
+
+    return dst;
+}
+
+
+static inline int check_address(mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+    mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addr;
+    
+    return (a->db_family = MRP_AF_DBUS && addrlen == sizeof(*a));
+}
+
+
+static size_t peer_address(mrp_sockaddr_t *addrp, const char *sender,
+                          const char *path)
+{
+    mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+    const char     *p;
+    char           *q;
+    int             l, n;
+
+    q = addr->db_fqa;
+    l = sizeof(addr->db_fqa);
+    p = ANY_ADDRESS;
+    n = 3;
+
+    addr->db_bus = q;
+    memcpy(q, p, n + 1);
+    q += n + 1;
+    l -= n + 1;
+    
+    addr->db_addr = q;
+    p = sender;
+    n = strlen(sender);
+    
+    if (l < n + 1)
+       return 0;
+    
+    memcpy(q, p, n + 1);
+    q += n + 1;
+    l -= n + 1;
+
+    addr->db_path = q;
+    p = path;
+    n = strlen(p);
+
+    if (l < n + 1)
+       return 0;
+    
+    memcpy(q, p, n + 1);
+
+    return sizeof(addrp);
+}
+
+
+static socklen_t dbus_resolve(const char *str, mrp_sockaddr_t *addr,
+                             socklen_t size, const char **typep)
+{
+    socklen_t len;
+    
+    len = parse_address(str, (mrp_dbusaddr_t *)addr, size);
+    
+    if (len > 0) {
+       if (typep != NULL)
+           *typep = DBUS;
+    }
+
+    return len;
+}
+
+
+static int dbus_open(mrp_transport_t *mt)
+{
+    MRP_UNUSED(mt);
+    
+    return TRUE;
+}
+
+
+static int dbus_createfrom(mrp_transport_t *mt, void *conn)
+{
+    dbus_t     *t    = (dbus_t *)mt;
+    mrp_dbus_t *dbus = (mrp_dbus_t *)conn;
+
+    t->dbus = mrp_dbus_ref(dbus);
+
+    if (t->dbus != NULL)
+       return TRUE;
+    else
+       return FALSE;
+}
+
+
+static int dbus_bind(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+                    socklen_t addrlen)
+{
+    dbus_t          *t    = (dbus_t *)mt;
+    mrp_dbus_t      *dbus = NULL;
+    mrp_dbusaddr_t  *addr = (mrp_dbusaddr_t *)addrp;
+    int            (*cb)(mrp_dbus_t *, DBusMessage *, void *);
+    const char      *method;
+    
+    if (t->bound) {
+       errno = EINVAL;
+       goto fail;
+    }
+
+    if (!check_address(addrp, addrlen)) {
+       errno = EINVAL;
+       goto fail;
+    }
+    
+    if (t->dbus == NULL) {
+       dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+           
+       if (dbus == NULL) {
+           errno = ECONNRESET;
+           goto fail;
+       }
+       else {
+           t->dbus = dbus;
+
+           if (addr->db_addr != NULL && strcmp(addr->db_addr, ANY_ADDRESS)) {
+               if (!mrp_dbus_acquire_name(t->dbus, addr->db_addr, NULL)) {
+                   errno = EADDRINUSE; /* XXX TODO, should check error... */
+                   goto fail;
+               }
+           }
+       }
+    }
+    else {
+       /* XXX TODO: should check given address against address of the bus */
+    }
+    
+    copy_address(&t->local, addr);
+    
+    if (t->flags & MRP_TRANSPORT_MODE_CUSTOM) {
+       method = TRANSPORT_CUSTOM;
+       cb     = dbus_data_cb;
+    }
+    else if (t->flags & MRP_TRANSPORT_MODE_RAW) {
+       method = TRANSPORT_RAW;
+       cb     = dbus_raw_cb;
+    }
+    else {
+       method = TRANSPORT_MESSAGE;
+       cb     = dbus_msg_cb;
+    }
+
+    if (!mrp_dbus_export_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+                               method, cb, t)) {
+       errno = EIO;
+       goto fail;
+    }
+    else {
+       t->bound = TRUE;
+       return TRUE;
+    }
+    
+ fail:
+    if (dbus != NULL) {
+       mrp_dbus_unref(dbus);
+       t->dbus = NULL;
+    }
+    
+    return FALSE;
+}
+
+
+static int dbus_autobind(mrp_transport_t *mt, mrp_sockaddr_t *addrp)
+{
+    mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addrp;
+    char            astr[MRP_SOCKADDR_SIZE];
+    mrp_sockaddr_t  addr;
+    socklen_t       alen;
+    
+    snprintf(astr, sizeof(astr), "dbus:[%s]/auto/%u", a->db_bus, nauto++);
+    
+    alen = dbus_resolve(astr, &addr, sizeof(addr), NULL);
+
+    if (alen > 0)
+       return dbus_bind(mt, &addr, alen);
+    else
+       return FALSE;
+}
+
+
+static void dbus_close(mrp_transport_t *mt)
+{
+    dbus_t         *t = (dbus_t *)mt;
+    mrp_dbusaddr_t *addr;
+    const char     *method;
+    int           (*cb)(mrp_dbus_t *, DBusMessage *, void *);
+    
+    if (t->bound) {
+       if (t->flags & MRP_TRANSPORT_MODE_CUSTOM) {
+           method = TRANSPORT_CUSTOM;
+           cb     = dbus_data_cb;
+       }
+       else if (t->flags & MRP_TRANSPORT_MODE_RAW) {
+           method = TRANSPORT_RAW;
+           cb     = dbus_raw_cb;
+       }
+       else {
+           method = TRANSPORT_MESSAGE;
+           cb     = dbus_msg_cb;
+       }
+
+       addr = (mrp_dbusaddr_t *)&t->local;
+       mrp_dbus_remove_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+                              method, cb, t);
+    }
+    
+    mrp_dbus_unref(t->dbus);
+    t->dbus = NULL;
+}
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+    mrp_transport_t *mt = (mrp_transport_t *)user_data;
+    mrp_sockaddr_t   addr;
+    socklen_t        alen;
+    const char      *sender_path;
+    mrp_msg_t       *msg;
+    
+    MRP_UNUSED(dbus);
+
+    msg = msg_decode(dmsg, &sender_path);
+
+    if (msg != NULL) {
+       if (mt->connected) {
+           MRP_TRANSPORT_BUSY(mt, {
+                   mt->evt.recvmsg(mt, msg, mt->user_data);
+               });
+       }
+       else {
+           peer_address(&addr, dbus_message_get_sender(dmsg), sender_path);
+           alen = sizeof(addr);
+
+           MRP_TRANSPORT_BUSY(mt, {
+                   mt->evt.recvmsgfrom(mt, msg, &addr, alen,
+                                       mt->user_data);
+               });
+       }
+           
+       mrp_msg_unref(msg);
+       
+       mt->check_destroy(mt);
+    }
+    else {
+       mrp_log_error("Failed to decode message.");
+    }
+    
+    return TRUE;
+}
+
+
+static int dbus_data_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+    mrp_transport_t *mt = (mrp_transport_t *)user_data;
+    mrp_sockaddr_t   addr;
+    socklen_t        alen;
+    const char      *sender_path;
+    uint16_t         tag;
+    void            *decoded;
+    
+    MRP_UNUSED(dbus);
+
+    decoded = data_decode(dmsg, &tag, &sender_path);
+       
+    if (decoded != NULL) {
+       if (mt->connected) {
+           MRP_TRANSPORT_BUSY(mt, {
+                   mt->evt.recvdata(mt, decoded, tag, mt->user_data);
+               });
+       }
+       else {
+           peer_address(&addr, dbus_message_get_sender(dmsg), sender_path);
+           alen = sizeof(addr);
+
+           MRP_TRANSPORT_BUSY(mt, {
+                   mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen,
+                                        mt->user_data);
+               });
+       }
+
+       mt->check_destroy(mt);
+    }
+    
+    return TRUE;
+}
+
+
+static int dbus_raw_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+    MRP_UNUSED(dbus);
+    MRP_UNUSED(dmsg);
+    MRP_UNUSED(user_data);
+
+    return FALSE;
+}
+
+
+#if 0
+static int dbus_recv_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+    mrp_transport_t *mt = (mrp_transport_t *)user_data;
+    dbus_t          *t = (dbus_t *)mt;
+    mrp_sockaddr_t   addr;
+    socklen_t        alen;
+    const char      *sender_path;
+    uint16_t         tag;
+    void            *decoded;
+    mrp_msg_t       *msg;
+    
+    MRP_UNUSED(dbus);
+
+    if (t->flags & MRP_TRANSPORT_MODE_CUSTOM) {
+       decoded = data_decode(dmsg, &tag, &sender_path);
+       
+       if (decoded != NULL) {
+           if (mt->connected) {
+               MRP_TRANSPORT_BUSY(mt, {
+                       mt->evt.recvdata(mt, decoded, tag, mt->user_data);
+                   });
+           }
+           else {
+               peer_address(&addr, dbus_message_get_sender(dmsg), sender_path);
+               alen = sizeof(addr);
+
+               MRP_TRANSPORT_BUSY(mt, {
+                       mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen,
+                                            mt->user_data);
+                   });
+           }
+
+           mt->check_destroy(mt);
+       }
+    }
+    else if (t->flags & MRP_TRANSPORT_MODE_RAW) {
+       mrp_log_error("%s(): XXX TODO: implement raw mode", __FUNCTION__);
+    }
+    else {
+       msg = msg_decode(dmsg, &sender_path);
+
+       if (msg != NULL) {
+           if (mt->connected) {
+               MRP_TRANSPORT_BUSY(mt, {
+                       mt->evt.recvmsg(mt, msg, mt->user_data);
+                   });
+           }
+           else {
+               peer_address(&addr, dbus_message_get_sender(dmsg), sender_path);
+               alen = sizeof(addr);
+
+               MRP_TRANSPORT_BUSY(mt, {
+                       mt->evt.recvmsgfrom(mt, msg, &addr, alen,
+                                           mt->user_data);
+                   });
+           }
+           
+           mrp_msg_unref(msg);
+           mt->check_destroy(mt);
+       }
+       else {
+           mrp_log_error("Failed to decode message.");
+       }
+    }
+    
+    return TRUE;
+}
+#endif
+
+
+static int dbus_connect(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+                       socklen_t addrlen)
+{
+    dbus_t         *t    = (dbus_t *)mt;
+    mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+
+    if (!check_address(addrp, addrlen)) {
+       errno = EINVAL;
+       return FALSE;
+    }
+
+    if (t->dbus == NULL) {
+       t->dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+       
+       if (t->dbus == NULL) {
+           errno = ECONNRESET;
+           return FALSE;
+       }
+    }
+    else {
+       /* XXX TODO: check given address against address of the bus */
+    }
+    
+    if (!t->bound)
+       if (!dbus_autobind(mt, addrp))
+           return FALSE;
+    
+    copy_address(&t->remote, addr);
+    
+    return TRUE;
+}
+
+
+static int dbus_disconnect(mrp_transport_t *mt)
+{
+    dbus_t *t = (dbus_t *)mt;
+    
+    mrp_clear(&t->remote);
+    
+    return TRUE;
+}
+
+
+static int dbus_sendmsgto(mrp_transport_t *mt, mrp_msg_t *msg,
+                         mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+    dbus_t         *t    = (dbus_t *)mt;
+    mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+    DBusMessage    *m;
+    int             success;
+
+    if (check_address(addrp, addrlen)) {
+       if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+           return FALSE;
+       
+       m = msg_encode(t->local.db_path, msg);
+
+       if (m != NULL) {
+           if (dbus_message_set_destination(m, addr->db_addr)     &&
+               dbus_message_set_path(m, addr->db_path)            &&
+               dbus_message_set_interface(m, TRANSPORT_INTERFACE) &&
+               dbus_message_set_member(m, TRANSPORT_MESSAGE)) {
+               if (mrp_dbus_send_msg(t->dbus, m))
+                   success = TRUE;
+               else {
+                   errno   = EIO;
+                   success = FALSE;
+               }
+           }
+           else {
+               errno   = ECOMM;
+               success = FALSE;
+           }
+
+           dbus_message_unref(m);
+       }
+       else
+           success = FALSE;
+    }
+    else {
+       errno   = EINVAL;
+       success = FALSE;
+    }
+
+    return success;
+}
+
+
+static int dbus_sendmsg(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+    dbus_t         *t    = (dbus_t *)mt;
+    mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+    socklen_t       alen = sizeof(t->remote);
+    
+    return dbus_sendmsgto(mt, msg, addr, alen);
+}
+
+
+static int dbus_sendrawto(mrp_transport_t *mt, void *data, size_t size,
+                         mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+    MRP_UNUSED(mt);
+    MRP_UNUSED(data);
+    MRP_UNUSED(size);
+    MRP_UNUSED(addr);
+    MRP_UNUSED(addrlen);
+
+    return FALSE;
+}
+
+
+static int dbus_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+    dbus_t         *t    = (dbus_t *)mt;
+    mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+    socklen_t       alen = sizeof(t->remote);
+    
+    return dbus_sendrawto(mt, data, size, addr, alen);
+}
+
+
+static int dbus_senddatato(mrp_transport_t *mt, void *data, uint16_t tag,
+                          mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+    dbus_t         *t    = (dbus_t *)mt;
+    mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+    DBusMessage    *m;
+    int             success;
+
+    if (check_address(addrp, addrlen)) {
+       if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+           return FALSE;
+
+       m = data_encode(t->local.db_path, data, tag);
+
+       if (m != NULL) {
+           if (dbus_message_set_destination(m, addr->db_addr)     &&
+               dbus_message_set_path(m, addr->db_path)            &&
+               dbus_message_set_interface(m, TRANSPORT_INTERFACE) &&
+               dbus_message_set_member(m, TRANSPORT_CUSTOM)) {
+               if (mrp_dbus_send_msg(t->dbus, m))
+                   success = TRUE;
+               else {
+                   errno   = EIO;
+                   success = FALSE;
+               }
+           }
+           else {
+               errno   = ECOMM;
+               success = FALSE;
+           }
+           
+           dbus_message_unref(m);
+       }
+       else
+           success = FALSE;
+    }
+    else {
+       errno   = EINVAL;
+       success = FALSE;
+    }
+
+    return success;
+}
+
+
+static int dbus_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+    dbus_t         *t    = (dbus_t *)mt;
+    mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+    socklen_t       alen = sizeof(t->remote);
+
+    return dbus_senddatato(mt, data, tag, addr, alen);
+}
+
+
+static const char *get_array_signature(uint16_t type)
+{
+#define MAP(from, to) \
+    case MRP_MSG_FIELD_##from:                 \
+       return DBUS_TYPE_##to##_AS_STRING;
+    
+    switch (type) {
+       MAP(STRING, STRING);
+       MAP(BOOL  , BOOLEAN);
+       MAP(UINT8 , UINT16);
+       MAP(SINT8 , INT16);
+       MAP(UINT16, UINT16);
+       MAP(SINT16, INT16);
+       MAP(UINT32, UINT32);
+       MAP(SINT32, INT32);
+       MAP(UINT64, UINT64);
+       MAP(SINT64, INT64);
+       MAP(DOUBLE, DOUBLE);
+    default:
+       return NULL;
+    }
+}
+
+
+static DBusMessage *msg_encode(const char *sender_id, mrp_msg_t *msg)
+{
+    /*
+     * Notes: There is a type mismatch between our and DBUS types for
+     *        8-bit integers (D-BUS does not have a signed 8-bit type)
+     *        and boolean types (D-BUS has uint32_t booleans, C99 fails
+     *        to specify the type and gcc uses a signed char). The
+     *        QUIRKY versions of the macros take care of these mismatches.
+     */
+    
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _val)                         \
+       case MRP_MSG_FIELD_##_mtype:                                    \
+           type = DBUS_TYPE_##_dtype;                                  \
+           vptr = &(_val);                                             \
+                                                                       \
+           if (!dbus_message_iter_append_basic(_i, type, vptr))        \
+               goto fail;                                              \
+           break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mval, _dval)                 \
+       case MRP_MSG_FIELD_##_mtype:                                    \
+           type  = DBUS_TYPE_##_dtype;                                 \
+           _dval = _mval;                                              \
+           vptr  = &_dval;                                             \
+                                                                       \
+           if (!dbus_message_iter_append_basic(_i, type, vptr))        \
+               goto fail;                                              \
+           break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _val)                           \
+               case MRP_MSG_FIELD_##_mtype:                              \
+                   type = DBUS_TYPE_##_dtype;                            \
+                   vptr = &_val;                                         \
+                                                                         \
+                   if (!dbus_message_iter_append_basic(_i, type, vptr))  \
+                       goto fail;                                        \
+                   break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar)                   \
+               case MRP_MSG_FIELD_##_mtype:                              \
+                   type  = DBUS_TYPE_##_dtype;                           \
+                   _dvar = _mvar;                                        \
+                   vptr  = &_dvar;                                       \
+                                                                         \
+                   if (!dbus_message_iter_append_basic(_i, type, vptr))  \
+                       goto fail;                                        \
+                   break
+
+    DBusMessage     *m;
+    mrp_list_hook_t *p, *n;
+    mrp_msg_field_t *f;
+    uint16_t         base;
+    uint32_t         asize, i;
+    DBusMessageIter  im, ia;
+    int              type;
+    void            *vptr;
+    dbus_bool_t      bln;
+    uint16_t         u16;
+    int16_t          s16;
+
+    m = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+    
+    if (m == NULL)
+       return NULL;
+    
+    dbus_message_iter_init_append(m, &im);
+
+    if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_OBJECT_PATH, &sender_id))
+       goto fail;
+
+    if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &msg->nfield))
+       goto fail;
+    
+    mrp_list_foreach(&msg->fields, p, n) {
+       f = mrp_list_entry(p, typeof(*f), hook);
+       
+       if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->tag) ||
+           !dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->type))
+           goto fail;
+               
+       switch (f->type) {
+           BASIC_SIMPLE(&im, STRING, STRING , f->str);
+           BASIC_QUIRKY(&im, BOOL  , BOOLEAN, f->bln, bln);
+           BASIC_QUIRKY(&im, UINT8 , UINT16 , f->u8 , u16);
+           BASIC_QUIRKY(&im, SINT8 ,  INT16 , f->s8 , s16);
+           BASIC_SIMPLE(&im, UINT16, UINT16 , f->u16);
+           BASIC_SIMPLE(&im, SINT16,  INT16 , f->s16);
+           BASIC_SIMPLE(&im, UINT32, UINT32 , f->u32);
+           BASIC_SIMPLE(&im, SINT32,  INT32 , f->s32);
+           BASIC_SIMPLE(&im, UINT64, UINT64 , f->u64);
+           BASIC_SIMPLE(&im, SINT64,  INT64 , f->s64);
+           BASIC_SIMPLE(&im, DOUBLE, DOUBLE , f->dbl);
+           
+       case MRP_MSG_FIELD_BLOB:
+           mrp_log_error("%s(): XXX TODO: implement blob encoding...",
+                         __FUNCTION__);
+           goto fail;
+           
+       default:
+           if (f->type & MRP_MSG_FIELD_ARRAY) {
+               base  = f->type & ~(MRP_MSG_FIELD_ARRAY);
+               asize = f->size[0];
+               
+               if (!dbus_message_iter_append_basic(&im,
+                                                   DBUS_TYPE_UINT32, &asize))
+                   goto fail;
+
+               if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+                                                     get_array_signature(base),
+                                                     &ia))
+                   goto fail;
+
+               for (i = 0; i < asize; i++) {
+                   switch (base) {
+                       ARRAY_SIMPLE(&ia, STRING, STRING , f->astr[i]);
+                       ARRAY_QUIRKY(&ia, BOOL  , BOOLEAN, f->abln[i], bln);
+                       ARRAY_QUIRKY(&ia, UINT8 , UINT16 , f->au8[i] , u16);
+                       ARRAY_QUIRKY(&ia, SINT8 ,  INT16 , f->as8[i] , s16);
+                       ARRAY_SIMPLE(&ia, UINT16, UINT16 , f->au16[i]);
+                       ARRAY_SIMPLE(&ia, SINT16,  INT16 , f->as16[i]);
+                       ARRAY_SIMPLE(&ia, UINT32, UINT32 , f->au32[i]);
+                       ARRAY_SIMPLE(&ia, SINT32,  INT32 , f->as32[i]);
+                       ARRAY_SIMPLE(&ia, UINT64, UINT64 , f->au64[i]);
+                       ARRAY_SIMPLE(&ia, DOUBLE, DOUBLE , f->adbl[i]);
+                       
+                   case MRP_MSG_FIELD_BLOB:
+                       goto fail;
+                           
+                   default:
+                       goto fail;
+                   }
+               }
+               
+               if (!dbus_message_iter_close_container(&im, &ia))
+                   goto fail;
+           }
+           else
+               goto fail;
+       }
+    }
+
+    return m;
+    
+ fail:
+    if (m != NULL)
+       dbus_message_unref(m);
+    
+    errno = ECOMM;
+    
+    return FALSE;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_msg_t *msg_decode(DBusMessage *m, const char **sender_id)
+{
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _var)                            \
+       case MRP_MSG_FIELD_##_mtype:                                       \
+           if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype)  \
+               goto fail;                                                 \
+           dbus_message_iter_get_basic(_i, &(_var));                      \
+           dbus_message_iter_next(_i);                                    \
+                                                                          \
+           if (!mrp_msg_append(msg, tag, type, (_var)))                   \
+               goto fail;                                                 \
+           break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar)                    \
+       case MRP_MSG_FIELD_##_mtype:                                       \
+           if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype)  \
+               goto fail;                                                 \
+           dbus_message_iter_get_basic(_i, &(_dvar));                     \
+           dbus_message_iter_next(_i);                                    \
+                                                                          \
+           _mvar = _dvar;                                                 \
+           if (!mrp_msg_append(msg, tag, type, (_mvar)))                  \
+               goto fail;                                                 \
+           break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _var)                            \
+       case MRP_MSG_FIELD_##_mtype:                                       \
+           if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype)  \
+               goto fail;                                                 \
+           dbus_message_iter_get_basic(_i, &(_var));                      \
+           dbus_message_iter_next(_i);                                    \
+           break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar)                    \
+       case MRP_MSG_FIELD_##_mtype:                                       \
+           if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype)  \
+               goto fail;                                                 \
+           dbus_message_iter_get_basic(_i, &(_dvar));                     \
+           dbus_message_iter_next(_i);                                    \
+                                                                          \
+           _mvar = _dvar;                                                 \
+           break
+
+#define APPEND_ARRAY(_type, _var)                                      \
+               case MRP_MSG_FIELD_##_type:                             \
+                   if (!mrp_msg_append(msg, tag,                       \
+                                       MRP_MSG_FIELD_ARRAY |           \
+                                       MRP_MSG_FIELD_##_type,          \
+                                       n, _var))                       \
+                       goto fail;                                      \
+                   break
+
+    mrp_msg_t       *msg;
+    mrp_msg_value_t  v;
+    uint16_t         u16;
+    int16_t          s16;
+    uint32_t         u32;
+    DBusMessageIter  im, ia;
+    uint16_t         nfield, tag, type, base, i;
+    uint32_t         n, j;
+    const char      *sender;
+
+    if (!dbus_message_iter_init(m, &im))
+       goto fail;
+
+    if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_OBJECT_PATH)
+       goto fail;
+
+    dbus_message_iter_get_basic(&im, &sender);
+    dbus_message_iter_next(&im);
+    
+    if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+       goto fail;
+
+    dbus_message_iter_get_basic(&im, &nfield);
+    dbus_message_iter_next(&im);
+
+    msg = mrp_msg_create_empty();
+    
+    if (msg == NULL)
+       goto fail;
+
+    for (i = 0; i < nfield; i++) {
+       if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+           goto fail;
+
+       dbus_message_iter_get_basic(&im, &tag);
+       dbus_message_iter_next(&im);
+
+       if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+           goto fail;
+       
+       dbus_message_iter_get_basic(&im, &type);
+       dbus_message_iter_next(&im);
+
+       switch (type) {
+           BASIC_SIMPLE(&im, STRING, STRING , v.str);
+           BASIC_QUIRKY(&im, BOOL  , BOOLEAN, v.bln, u32);
+           BASIC_QUIRKY(&im, UINT8 , UINT16 , v.u8 , u16);
+           BASIC_QUIRKY(&im, SINT8 ,  INT16 , v.s8 , s16);
+           BASIC_SIMPLE(&im, UINT16, UINT16 , v.u16);
+           BASIC_SIMPLE(&im, SINT16,  INT16 , v.s16);
+           BASIC_SIMPLE(&im, UINT32, UINT32 , v.u32);
+           BASIC_SIMPLE(&im, SINT32,  INT32 , v.s32);
+           BASIC_SIMPLE(&im, UINT64, UINT64 , v.u64);
+           BASIC_SIMPLE(&im, SINT64,  INT64 , v.s64);
+           BASIC_SIMPLE(&im, DOUBLE, DOUBLE , v.dbl);
+           
+       case MRP_MSG_FIELD_BLOB:
+           mrp_log_error("%s(): XXX TODO: implement blob decoding...",
+                         __FUNCTION__);
+           goto fail;
+           
+       default:
+           if (!(type & MRP_MSG_FIELD_ARRAY))
+               goto fail;
+           
+           base = type & ~(MRP_MSG_FIELD_ARRAY);
+               
+           if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT32)
+               goto fail;
+               
+           dbus_message_iter_get_basic(&im, &n);
+           dbus_message_iter_next(&im);
+
+           if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_ARRAY)
+               goto fail;
+           dbus_message_iter_recurse(&im, &ia);
+           dbus_message_iter_next(&im);
+
+           {
+               char     *astr[n];
+               uint32_t  dbln[n];
+               bool      abln[n];
+               uint8_t   au8 [n];
+               int8_t    as8 [n];
+               uint16_t  au16[n];
+               int16_t   as16[n];
+               uint32_t  au32[n];
+               int32_t   as32[n];
+               uint64_t  au64[n];
+               int64_t   as64[n];
+               double    adbl[n];
+
+               for (j = 0; j < n; j++) {
+                   switch (base) {
+                       ARRAY_SIMPLE(&ia, STRING, STRING , astr[j]);
+                       ARRAY_QUIRKY(&ia, BOOL  , BOOLEAN, abln[j], dbln[j]);
+                       ARRAY_QUIRKY(&ia, UINT8 , UINT16 , au8[j] , au16[j]);
+                       ARRAY_QUIRKY(&ia, SINT8 ,  INT16 , as8[j] , as16[j]);
+                       ARRAY_SIMPLE(&ia, UINT16, UINT16 , au16[j]);
+                       ARRAY_SIMPLE(&ia, SINT16,  INT16 , as16[j]);
+                       ARRAY_SIMPLE(&ia, UINT32, UINT32 , au32[j]);
+                       ARRAY_SIMPLE(&ia, SINT32,  INT32 , as32[j]);
+                       ARRAY_SIMPLE(&ia, UINT64, UINT64 , au64[j]);
+                       ARRAY_SIMPLE(&ia, SINT64,  INT64 , as64[j]);
+                       ARRAY_SIMPLE(&ia, DOUBLE, DOUBLE , adbl[j]);
+                   default:
+                       goto fail;
+                   }
+               }
+
+               switch (base) {
+                   APPEND_ARRAY(STRING, astr);
+                   APPEND_ARRAY(BOOL  , abln);
+                   APPEND_ARRAY(UINT8 , au8 );
+                   APPEND_ARRAY(SINT8 , as8 );
+                   APPEND_ARRAY(UINT16, au16);
+                   APPEND_ARRAY(SINT16, as16);
+                   APPEND_ARRAY(UINT32, au32);
+                   APPEND_ARRAY(SINT32, as32);
+                   APPEND_ARRAY(UINT64, au64);
+                   APPEND_ARRAY(SINT64, as64);
+                   APPEND_ARRAY(DOUBLE, adbl);
+               default:
+                   goto fail;
+               }
+           }
+       }
+    }
+
+    if (sender_id != NULL)
+       *sender_id = sender;
+
+    return msg;
+    
+ fail:
+    mrp_msg_unref(msg);
+    errno = EBADMSG;
+    
+    return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+#undef APPEND_ARRAY
+}
+
+
+static DBusMessage *data_encode(const char *sender_id, void *data, uint16_t tag)
+{
+#define BASIC_SIMPLE(_mtype, _dtype, _val)                             \
+       case MRP_MSG_FIELD_##_mtype:                                    \
+           type = DBUS_TYPE_##_dtype;                                  \
+           vptr = &(_val);                                             \
+                                                                       \
+           if (!dbus_message_iter_append_basic(&im, type, vptr))       \
+               goto fail;                                              \
+           break
+
+#define BASIC_QUIRKY(_mtype, _dtype, _mval, _dval)                     \
+       case MRP_MSG_FIELD_##_mtype:                                    \
+           type  = DBUS_TYPE_##_dtype;                                 \
+           _dval = _mval;                                              \
+           vptr  = &_dval;                                             \
+                                                                       \
+           if (!dbus_message_iter_append_basic(&im, type, vptr))       \
+               goto fail;                                              \
+           break
+
+#define ARRAY_SIMPLE(_mtype, _dtype, _val)                               \
+               case MRP_MSG_FIELD_##_mtype:                              \
+                   type = DBUS_TYPE_##_dtype;                            \
+                   vptr = &_val;                                         \
+                                                                         \
+                   if (!dbus_message_iter_append_basic(&ia, type, vptr)) \
+                       goto fail;                                        \
+                   break
+
+#define ARRAY_QUIRKY(_mtype, _dtype, _mvar, _dvar)                       \
+               case MRP_MSG_FIELD_##_mtype:                              \
+                   type  = DBUS_TYPE_##_dtype;                           \
+                   _dvar = _mvar;                                        \
+                   vptr = &_dvar;                                        \
+                                                                         \
+                   if (!dbus_message_iter_append_basic(&ia, type, vptr)) \
+                       goto fail;                                        \
+                   break
+
+    DBusMessage       *m;
+    mrp_data_descr_t  *descr;
+    mrp_data_member_t *fields, *f;
+    int                nfield;
+    uint16_t           type, base;
+    mrp_msg_value_t   *v;
+    void              *vptr;
+    uint32_t           n, j;
+    int                i;
+    DBusMessageIter    im, ia;
+    uint16_t           u16;
+    int16_t            s16;
+    uint32_t           bln;
+
+    m = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+    
+    if (m == NULL)
+       return NULL;
+    
+    descr = mrp_msg_find_type(tag);
+    
+    if (descr == NULL)
+       goto fail;
+
+    fields = descr->fields;
+    nfield = descr->nfield;
+    
+    dbus_message_iter_init_append(m, &im);
+
+    if (!dbus_message_iter_append_basic(&im,
+                                       DBUS_TYPE_OBJECT_PATH, &sender_id))
+       goto fail;
+
+    if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &tag))
+       goto fail;
+
+    if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &nfield))
+       goto fail;
+
+    for (i = 0, f = fields; i < nfield; i++, f++) {
+       if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->tag) ||
+           !dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->type))
+           goto fail;
+
+       v = (mrp_msg_value_t *)(data + f->offs);
+       
+       switch (f->type) {
+           BASIC_SIMPLE(STRING, STRING , v->str);
+           BASIC_QUIRKY(BOOL  , BOOLEAN, v->bln, bln);
+           BASIC_QUIRKY(UINT8 , UINT16 , v->u8 , u16);
+           BASIC_QUIRKY(SINT8 ,  INT16 , v->s8 , s16);
+           BASIC_SIMPLE(UINT16, UINT16 , v->u16);
+           BASIC_SIMPLE(SINT16,  INT16 , v->s16);
+           BASIC_SIMPLE(UINT32, UINT32 , v->u32);
+           BASIC_SIMPLE(SINT32,  INT32 , v->s32);
+           BASIC_SIMPLE(UINT64, UINT64 , v->u64);
+           BASIC_SIMPLE(SINT64,  INT64 , v->s64);
+           BASIC_SIMPLE(DOUBLE, DOUBLE , v->dbl);
+
+       default:
+           if (!(f->type & MRP_MSG_FIELD_ARRAY))
+               goto fail;
+           
+           base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+           n    = mrp_data_get_array_size(data, descr, i);
+               
+           if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT32, &n))
+               goto fail;
+           
+           if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+                                                 get_array_signature(base),
+                                                 &ia))
+               goto fail;
+           
+           for (j = 0; j < n; j++) {
+               switch (base) {
+                   ARRAY_SIMPLE(STRING, STRING , v->astr[j]);
+                   ARRAY_QUIRKY(BOOL  , BOOLEAN, v->abln[j], bln);
+                   ARRAY_QUIRKY(UINT8 , UINT16 , v->au8[j] , u16);
+                   ARRAY_QUIRKY(SINT8 ,  INT16 , v->as8[j] , s16);
+                   ARRAY_SIMPLE(UINT16, UINT16 , v->au16[j]);
+                   ARRAY_SIMPLE(SINT16,  INT16 , v->as16[j]);
+                   ARRAY_SIMPLE(UINT32, UINT32 , v->au32[j]);
+                   ARRAY_SIMPLE(SINT32,  INT32 , v->as32[j]);
+                   ARRAY_SIMPLE(UINT64, UINT64 , v->au64[j]);
+                   ARRAY_SIMPLE(DOUBLE, DOUBLE , v->adbl[j]);
+                   
+               case MRP_MSG_FIELD_BLOB:
+                   goto fail;
+                   
+               default:
+                   goto fail;
+               }
+           }
+           
+           if (!dbus_message_iter_close_container(&im, &ia))
+               goto fail;
+       }
+    }
+    
+    return m;
+
+ fail:
+    if (m != NULL)
+       dbus_message_unref(m);
+
+    errno = ECOMM;
+
+    return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+                                     uint16_t tag)
+{
+    mrp_data_member_t *f;
+    int                i;
+
+    for (i = 0, f = fields; i < nfield; i++, f++)
+       if (f->tag == tag)
+           return f;
+    
+    return NULL;
+}
+
+
+static void *data_decode(DBusMessage *m, uint16_t *tagp, const char **sender_id)
+{
+#define HANDLE_SIMPLE(_i, _mtype, _dtype, _var)                                   \
+       case MRP_MSG_FIELD_##_mtype:                                       \
+           if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype)  \
+               goto fail;                                                 \
+           dbus_message_iter_get_basic(_i, &(_var));                      \
+           dbus_message_iter_next(_i);                                    \
+           break
+
+#define HANDLE_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar)                           \
+       case MRP_MSG_FIELD_##_mtype:                                       \
+           if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype)  \
+               goto fail;                                                 \
+           dbus_message_iter_get_basic(_i, &(_dvar));                     \
+           dbus_message_iter_next(_i);                                    \
+                                                                          \
+           _mvar = _dvar;                                                 \
+           break
+
+    void              *data;
+    mrp_data_descr_t  *descr;
+    mrp_data_member_t *fields, *f;
+    int                nfield;
+    uint16_t           tag, type, base;
+    mrp_msg_value_t   *v;
+    uint32_t           n, j, size;
+    int                i;
+    DBusMessageIter    im, ia;
+    const char        *sender;
+    uint32_t           u32;
+    uint16_t           u16;
+    int16_t            s16;
+
+    data = NULL;
+
+    if (!dbus_message_iter_init(m, &im))
+       goto fail;
+
+    if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_OBJECT_PATH)
+       goto fail;
+
+    dbus_message_iter_get_basic(&im, &sender);
+    dbus_message_iter_next(&im);
+    
+    if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+       goto fail;
+
+    dbus_message_iter_get_basic(&im, &tag);
+    dbus_message_iter_next(&im);
+
+    descr = mrp_msg_find_type(tag);
+    
+    if (descr == NULL)
+       goto fail;
+
+    *tagp = tag;
+
+    if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+       goto fail;
+
+    dbus_message_iter_get_basic(&im, &nfield);
+    dbus_message_iter_next(&im);
+
+    if (nfield != descr->nfield)
+       goto fail;
+
+    fields = descr->fields;
+    data   = mrp_allocz(descr->size);
+
+    if (MRP_UNLIKELY(data == NULL))
+       goto fail;
+    
+    for (i = 0; i < nfield; i++) {
+       if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+           goto fail;
+
+       dbus_message_iter_get_basic(&im, &tag);
+       dbus_message_iter_next(&im);
+       
+       if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+           goto fail;
+
+       dbus_message_iter_get_basic(&im, &type);
+       dbus_message_iter_next(&im);
+
+       f = member_type(fields, nfield, tag);
+
+       if (MRP_UNLIKELY(f == NULL))
+           goto fail;
+       
+       v = (mrp_msg_value_t *)(data + f->offs);
+
+       switch (type) {
+           HANDLE_SIMPLE(&im, STRING, STRING , v->str);
+           HANDLE_QUIRKY(&im, BOOL  , BOOLEAN, v->bln, u32);
+           HANDLE_QUIRKY(&im, UINT8 , UINT16 , v->u8 , u16);
+           HANDLE_QUIRKY(&im, SINT8 ,  INT16 , v->s8 , s16);
+           HANDLE_SIMPLE(&im, UINT16, UINT16 , v->u16);
+           HANDLE_SIMPLE(&im, SINT16,  INT16 , v->s16);
+           HANDLE_SIMPLE(&im, UINT32, UINT32 , v->u32);
+           HANDLE_SIMPLE(&im, SINT32,  INT32 , v->s32);
+           HANDLE_SIMPLE(&im, UINT64, UINT64 , v->u64);
+           HANDLE_SIMPLE(&im, SINT64,  INT64 , v->s64);
+           HANDLE_SIMPLE(&im, DOUBLE, DOUBLE , v->dbl);
+           
+       default:
+           if (!(f->type & MRP_MSG_FIELD_ARRAY))
+               goto fail;
+
+           base = type & ~(MRP_MSG_FIELD_ARRAY);
+               
+           if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT32)
+               goto fail;
+               
+           dbus_message_iter_get_basic(&im, &n);
+           dbus_message_iter_next(&im);
+
+           if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_ARRAY)
+               goto fail;
+           
+           dbus_message_iter_recurse(&im, &ia);
+           dbus_message_iter_next(&im);
+
+           size = n;
+
+           switch (base) {
+           case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break;
+           case MRP_MSG_FIELD_BOOL:   size *= sizeof(*v->abln); break;
+           case MRP_MSG_FIELD_UINT8:  size *= sizeof(*v->au8);  break;
+           case MRP_MSG_FIELD_SINT8:  size *= sizeof(*v->as8);  break;
+           case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break;
+           case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break;
+           case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break;
+           case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break;
+           case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break;
+           case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break;
+           case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break;
+           default:
+               goto fail;
+           }
+
+           v->aany = mrp_allocz(size);
+           if (v->aany == NULL)
+               goto fail;
+
+           for (j = 0; j < n; j++) {
+               uint32_t  dbln[n];
+               uint16_t  au16[n];
+               int16_t   as16[n];
+               
+               switch (base) {
+                   HANDLE_SIMPLE(&ia, STRING, STRING , v->astr[j]);
+                   HANDLE_QUIRKY(&ia, BOOL  , BOOLEAN, v->abln[j], dbln[j]);
+                   HANDLE_QUIRKY(&ia, UINT8 , UINT16 , v->au8[j] , au16[j]);
+                   HANDLE_QUIRKY(&ia, SINT8 ,  INT16 , v->as8[j] , as16[j]);
+                   HANDLE_SIMPLE(&ia, UINT16, UINT16 , v->au16[j]);
+                   HANDLE_SIMPLE(&ia, SINT16,  INT16 , v->as16[j]);
+                   HANDLE_SIMPLE(&ia, UINT32, UINT32 , v->au32[j]);
+                   HANDLE_SIMPLE(&ia, SINT32,  INT32 , v->as32[j]);
+                   HANDLE_SIMPLE(&ia, UINT64, UINT64 , v->au64[j]);
+                   HANDLE_SIMPLE(&ia, SINT64,  INT64 , v->as64[j]);
+                   HANDLE_SIMPLE(&ia, DOUBLE, DOUBLE , v->adbl[j]);
+               }
+
+               if (base == MRP_MSG_FIELD_STRING) {
+                   v->astr[j] = mrp_strdup(v->astr[j]);
+                   if (v->astr[j] == NULL)
+                       goto fail;
+               }
+           }
+       }
+
+       if (f->type == MRP_MSG_FIELD_STRING) {
+           v->str = mrp_strdup(v->str);
+           if (v->str == NULL)
+               goto fail;
+       }
+    }
+
+    if (sender_id != NULL)
+       *sender_id = sender;
+
+    return data;
+    
+ fail:
+    mrp_data_free(data, tag);
+    errno = EBADMSG;
+    
+    return NULL;
+}
+
+
+
+
+MRP_REGISTER_TRANSPORT(dbus, DBUS, dbus_t, dbus_resolve,
+                      dbus_open, dbus_createfrom, dbus_close,
+                      dbus_bind, NULL, NULL,
+                      dbus_connect, dbus_disconnect,
+                      dbus_sendmsg, dbus_sendmsgto,
+                      dbus_sendraw, dbus_sendrawto,
+                      dbus_senddata, dbus_senddatato);
+
diff --git a/src/common/dbus-transport.h b/src/common/dbus-transport.h
new file mode 100644 (file)
index 0000000..95ebd06
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef __MURPHY_DBUS_TRANSPORT_H__
+#define __MURPHY_DBUS_TRANSPORT_H__
+
+#include <murphy/common/transport.h>
+
+#define MRP_AF_DBUS 0xDB
+
+#define MRP_DBUSADDR_BASE                                              \
+    __SOCKADDR_COMMON(db_);                                            \
+    char *db_bus;                               /* D-BUS bus address */ \
+    char *db_addr;                             /* address on bus */    \
+    char *db_path                              /* instance path */     \
+
+typedef struct {
+    MRP_DBUSADDR_BASE;
+} _mrp_dbusaddr_base_t;
+
+
+typedef struct {
+    MRP_DBUSADDR_BASE;
+    char db_fqa[MRP_SOCKADDR_SIZE - sizeof(_mrp_dbusaddr_base_t)];
+} mrp_dbusaddr_t;
+
+
+
+#endif /* __MURPHY_DBUS_TRANSPORT_H__ */
diff --git a/src/common/dbus.c b/src/common/dbus.c
new file mode 100644 (file)
index 0000000..ecc5389
--- /dev/null
@@ -0,0 +1,1316 @@
+#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.h>
+
+
+#define DBUS_ADMIN_SERVICE   "org.freedesktop.DBus"
+#define DBUS_ADMIN_INTERFACE "org.freedesktop.DBus"
+#define DBUS_ADMIN_PATH      "/org/freedesktop/DBus"
+#define DBUS_NAME_CHANGED    "NameOwnerChanged"
+
+
+struct mrp_dbus_s {
+    DBusConnection  *conn;               /* actual D-BUS connection */
+    mrp_mainloop_t  *ml;                 /* murphy mainloop */
+    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 */
+    mrp_refcnt_t     refcnt;             /* reference count */
+};
+
+
+/*
+ * 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            *member;
+    mrp_list_hook_t  handlers;
+} 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 */
+    DBusPendingCall     *pend;           /* pending DBUS call */
+    mrp_list_hook_t      hook;           /* hook to list of pending calls */
+} call_t;
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+                                        DBusMessage *msg, void *data);
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+                                        DBusMessage *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, DBusMessage *msg, void *data);
+static void call_free(call_t *call);
+
+
+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;
+
+    (void)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;
+}
+
+
+void dbus_disconnect(mrp_dbus_t *dbus)
+{
+    if (dbus) {
+       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);
+
+       if (dbus->conn != NULL) {
+           dbus_connection_remove_filter(dbus->conn, dispatch_signal, dbus);
+           dbus_connection_unregister_object_path(dbus->conn, "/");
+           if (dbus->priv)
+               dbus_connection_close(dbus->conn);
+           dbus_connection_unref(dbus->conn);
+       }
+
+       purge_name_trackers(dbus);
+       purge_calls(dbus);
+       
+       dbus->conn = NULL;
+       dbus->ml   = NULL;
+       
+       mrp_free(dbus);
+    }
+}
+
+
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+                            DBusError *errp)
+{
+    static struct DBusObjectPathVTable vtable = {
+        .message_function = dispatch_method
+    };
+    
+    mrp_htbl_config_t  hcfg;
+    mrp_dbus_t        *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"))
+       dbus->conn = dbus_bus_get(DBUS_BUS_SYSTEM, errp);
+    else if (!strcmp(address, "session"))
+       dbus->conn = dbus_bus_get(DBUS_BUS_SESSION, errp);
+    else {
+       dbus->conn = dbus_connection_open_private(address, errp);
+       dbus->priv = TRUE;
+
+       if (dbus->conn == NULL || !dbus_bus_register(dbus->conn, errp))
+           goto fail;
+    }
+
+    if (dbus->conn == NULL)
+       goto fail;
+
+    dbus->unique_name = dbus_bus_get_unique_name(dbus->conn);
+
+    /*
+     * set up with mainloop
+     */
+
+    if (!mrp_dbus_setup_connection(ml, dbus->conn))
+       goto fail;
+    
+    /*
+     * set up our message dispatchers and take our name on the bus
+     */
+
+    if (!dbus_connection_add_filter(dbus->conn, dispatch_signal, dbus, NULL)) {
+       dbus_set_error(errp, DBUS_ERROR_FAILED,
+                      "Failed to set up signal dispatching.");
+       goto fail;
+    }
+    
+    if (!dbus_connection_register_fallback(dbus->conn, "/", &vtable, dbus)) {
+       dbus_set_error(errp, DBUS_ERROR_FAILED,
+                      "Failed to set up method dispatching.");
+       goto fail;
+    }
+
+    mrp_clear(&hcfg);
+    hcfg.comp = mrp_string_comp;
+    hcfg.hash = mrp_string_hash;
+    hcfg.free = handler_list_free_cb;
+    
+    if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) {
+       dbus_set_error(errp, DBUS_ERROR_FAILED,
+                      "Failed to create DBUS method table.");
+       goto fail;
+    }
+
+    if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) {
+       dbus_set_error(errp, DBUS_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, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+                                    DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+                                    name_owner_change_cb, NULL)) {
+       dbus_set_error(errp, DBUS_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,
+                           DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+                           DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+                           DBUS_ADMIN_SERVICE, NULL);
+    
+    mrp_list_init(&dbus->name_trackers);
+    dbus->call_id = 1;
+
+    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, DBusError *error)
+{
+    int flags, status;
+
+    mrp_dbus_error_init(error);
+    
+    flags  = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE;
+    status = dbus_bus_request_name(dbus->conn, name, flags, error);
+
+    if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+       return TRUE;
+    else {
+       if (status == DBUS_REQUEST_NAME_REPLY_EXISTS) {
+           dbus_error_free(error);
+           dbus_set_error(error, DBUS_ERROR_FAILED, "name already taken");
+       }
+       return FALSE;
+    }
+}
+
+
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, DBusError *error)
+{
+    mrp_dbus_error_init(error);
+    
+    if (dbus_bus_release_name(dbus->conn, name, error) != -1)
+       return TRUE;
+    else
+       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, DBusMessage *msg, void *data)
+{
+    name_tracker_t *t = (name_tracker_t *)data;
+    int             state;
+
+    if (t->cb != NULL) {                /* tracker still active */
+       t->qid = 0;
+       state  = dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN;
+       t->cb(dbus, t->name, state, t->user_data);
+    }
+    else                                /* already requested to delete */ 
+       mrp_free(t);
+}
+
+
+static int name_owner_change_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *data)
+{
+    const char      *name, *prev, *next;
+    mrp_list_hook_t *p, *n;
+    name_tracker_t  *t;
+
+    (void)data;
+
+    if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+       return FALSE;
+
+    if (!dbus_message_get_args(msg, NULL,
+                              DBUS_TYPE_STRING, &name,
+                              DBUS_TYPE_STRING, &prev,
+                              DBUS_TYPE_STRING, &next,
+                              DBUS_TYPE_INVALID))
+       return FALSE;
+    
+    /*
+     * 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) adminstering 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).
+     */
+    
+    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, 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,
+                                       DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+                                       DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+                                       name, NULL)) {
+               mrp_list_append(&dbus->name_trackers, &t->hook);
+               
+               t->qid = mrp_dbus_call(dbus,
+                                      DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+                                      DBUS_ADMIN_SERVICE, "GetNameOwner", 5000,
+                                      name_owner_query_cb, t,
+                                      DBUS_TYPE_STRING, &t->name,
+                                      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,
+                          DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+                          DBUS_ADMIN_SERVICE, DBUS_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, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+                              DBUS_ADMIN_SERVICE, DBUS_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 (!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)
+{
+    (void)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
+}
+
+
+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 ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) {
+       if ((methods = handler_list_alloc(member)) == NULL)
+           return FALSE;
+
+       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;
+    }
+    else
+       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) {
+       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;
+
+    (void)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;
+    DBusError error;
+    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);
+
+    dbus_error_init(&error);
+    dbus_bus_add_match(dbus->conn, filter, &error);
+
+    if (dbus_error_is_set(&error)) {
+       mrp_log_error("Failed to install filter '%s' (error: %s).", filter,
+                     mrp_dbus_errmsg(&error));
+       dbus_error_free(&error);
+
+       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);
+
+    dbus_bus_remove_match(dbus->conn, filter, 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 DBusHandlerResult dispatch_method(DBusConnection *c,
+                                        DBusMessage *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+    const char *path      = dbus_message_get_path(msg);
+    const char *interface = dbus_message_get_interface(msg);
+    const char *member    = dbus_message_get_member(msg);
+    
+    mrp_dbus_t     *dbus = (mrp_dbus_t *)data;
+    handler_list_t *l;
+    handler_t      *h;
+
+    (void)c;
+
+    if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL || !member)
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+#if 0
+    mrp_debug("path='%s', interface='%s', member='%s')..."
+             SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+#endif
+    
+    if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) {
+    retry:
+       if ((h = handler_list_find(l, path, interface, member)) != NULL) {
+           if (h->handler(dbus, msg, h->user_data))
+               return DBUS_HANDLER_RESULT_HANDLED;
+           else
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+       }
+    }
+    else {
+       if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL)
+           goto retry;
+    }
+
+    mrp_log_info("Unhandled method path=%s, %s.%s.", SAFESTR(path),
+                SAFESTR(interface), SAFESTR(member));
+    
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+                                        DBusMessage *msg, void *data)
+{
+#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field))
+    const char *path      = dbus_message_get_path(msg);
+    const char *interface = dbus_message_get_interface(msg);
+    const char *member    = dbus_message_get_member(msg);
+    
+    mrp_dbus_t      *dbus = (mrp_dbus_t *)data;
+    mrp_list_hook_t *p, *n;
+    handler_list_t  *l;
+    handler_t       *h;
+    int              retried = FALSE;
+    int              handled = FALSE;
+
+    (void)c;
+
+    if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL || !member)
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+#if 0
+    mrp_log_info("%s(path='%s', interface='%s', member='%s')...",
+                __FUNCTION__,
+                SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+#endif
+    
+    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)) {
+               h->handler(dbus, msg, h->user_data);
+               handled = TRUE;
+           }
+       }
+    }
+    
+    if (!retried) {
+       if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) {
+           retried = TRUE;
+           goto retry;
+       }
+    }
+    
+    if (!handled)
+       mrp_log_info("Unhandled signal path=%s, %s.%s.", SAFESTR(path),
+                    SAFESTR(interface), SAFESTR(member));
+    
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+#undef MATCHES
+#undef SAFESTR
+}
+
+
+static void call_reply_cb(DBusPendingCall *pend, void *user_data)
+{
+    call_t      *call = (call_t *)user_data;
+    DBusMessage *reply;
+
+    reply = dbus_pending_call_steal_reply(pend);
+
+    call->pend = NULL;
+    mrp_list_delete(&call->hook);
+
+    call->cb(call->dbus, reply, call->user_data);
+
+    dbus_message_unref(reply);
+    dbus_pending_call_unref(pend);
+    
+    call_free(call);
+}
+
+
+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;
+    DBusMessage     *msg;
+    DBusPendingCall *pend;
+    int              success;
+    
+    call = NULL;
+    pend = NULL;
+    
+    msg = dbus_message_new_method_call(dest, path, interface, member);
+
+    if (msg == NULL)
+       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 == DBUS_TYPE_INVALID)
+       success = TRUE;
+    else {
+       va_start(ap, type);
+       success = dbus_message_append_args_valist(msg, type, ap);
+       va_end(ap);
+    }
+
+    if (!success)
+       goto fail;
+       
+    if (cb == NULL) {
+       dbus_message_set_no_reply(msg, TRUE);
+       if (!dbus_connection_send(dbus->conn, msg, NULL))
+           goto fail;
+    }
+    else {
+       if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+           goto fail;
+
+       if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+           goto fail;
+    }
+    
+    if (cb != NULL) {
+       mrp_list_append(&dbus->calls, &call->hook);
+       call->pend = pend;
+    }
+
+    dbus_message_unref(msg);
+    
+    return id;
+
+ fail:
+    if (pend != NULL)
+       dbus_pending_call_unref(pend);
+    
+    if(msg != NULL)
+       dbus_message_unref(msg);
+
+    call_free(call);
+
+    return 0;
+}
+
+
+int32_t mrp_dbus_send(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, DBusMessage *msg)
+{
+    int32_t          id;
+    call_t          *call;
+    DBusPendingCall *pend;
+    int              method;
+
+    call = NULL;
+    pend = NULL;
+    
+    if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_SIGNAL) {
+       if (cb != NULL)
+           goto fail;
+       else
+           method = FALSE;
+    }
+    else
+       method = TRUE;
+    
+    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 (!dbus_message_set_destination(msg, dest))
+       goto fail;
+    if (!dbus_message_set_path(msg, path))
+       goto fail;
+    if (!dbus_message_set_interface(msg, interface))
+       goto fail;
+    if (!dbus_message_set_member(msg, member))
+       goto fail;
+    
+    if (cb == NULL) {
+       if (method)
+           dbus_message_set_no_reply(msg, TRUE);
+       if (!dbus_connection_send(dbus->conn, msg, NULL))
+           goto fail;
+    }
+    else {
+       if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+           goto fail;
+       
+       if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+           goto fail;
+    }
+    
+    if (cb != NULL) {
+       mrp_list_append(&dbus->calls, &call->hook);
+       call->pend = pend;
+    }
+
+    dbus_message_unref(msg);
+    
+    return id;
+
+ fail:
+    if (pend != NULL)
+       dbus_pending_call_unref(pend);
+    
+    if(msg != NULL)
+       dbus_message_unref(msg);
+
+    call_free(call);
+
+    return 0;
+}
+
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, DBusMessage *msg)
+{
+    return dbus_connection_send(dbus->conn, msg, NULL);
+}
+
+
+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);
+           
+           dbus_pending_call_cancel(call->pend);
+           dbus_pending_call_unref(call->pend);
+           call->pend = NULL;
+
+           call_free(call);
+           return TRUE;
+       }
+    }
+
+    return FALSE;
+}
+
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, DBusMessage *msg, int type, ...)
+{
+    va_list      ap;
+    DBusMessage *rpl;
+    int          success;
+    
+    rpl = dbus_message_new_method_return(msg);
+
+    if (rpl == NULL)
+       return FALSE;
+    
+    if (type == DBUS_TYPE_INVALID)
+       success = TRUE;
+    else {
+       va_start(ap, type);
+       success = dbus_message_append_args_valist(rpl, type, ap);
+       va_end(ap);
+    }
+
+    if (!success)
+       goto fail;
+       
+    if (!dbus_connection_send(dbus->conn, rpl, NULL))
+       goto fail;
+    
+    dbus_message_unref(rpl);
+    
+    return TRUE;
+
+ fail:
+    if(rpl != NULL)
+       dbus_message_unref(rpl);
+    
+    return FALSE;
+}
+
+
+static void call_free(call_t *call)
+{
+    if (call != NULL)
+       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->pend != NULL)
+           dbus_pending_call_unref(call->pend);
+       
+       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;
+    DBusMessage *msg;
+    int          success;
+    
+    msg = dbus_message_new_signal(path, interface, member);
+
+    if (msg == NULL)
+       return 0;
+    
+    if (type == DBUS_TYPE_INVALID)
+       success = TRUE;
+    else {
+       va_start(ap, type);
+       success = dbus_message_append_args_valist(msg, type, ap);
+       va_end(ap);
+    }
+
+    if (!success)
+       goto fail;
+       
+    if (dest && *dest && !dbus_message_set_destination(msg, dest))
+       goto fail;
+
+    if (!dbus_connection_send(dbus->conn, msg, NULL))
+       goto fail;
+    
+    dbus_message_unref(msg);
+    
+    return TRUE;
+
+ fail:
+    /*
+     * XXX TODO: Hmm... IIRC, libdbus unrefs messages upon failure. If it
+     *           was really so, this would corrupt/crash. Check this from
+     *           libdbus code.
+     */
+    if(msg != NULL)
+       dbus_message_unref(msg);
+
+    return 0;
+}
diff --git a/src/common/dbus.h b/src/common/dbus.h
new file mode 100644 (file)
index 0000000..92ce917
--- /dev/null
@@ -0,0 +1,141 @@
+#ifndef __MURPHY_DBUS_H__
+#define __MURPHY_DBUS_H__
+
+#include <dbus/dbus.h>
+
+#define MRP_AF_DBUS 0xDB
+
+/** Our D-BUS (connection) abstraction. */
+struct mrp_dbus_s;
+typedef struct mrp_dbus_s mrp_dbus_t;
+
+/** D-BUS method or signal callback type. */
+typedef int (*mrp_dbus_handler_t)(mrp_dbus_t *, DBusMessage *, void *);
+
+/** Create a new connection to the given bus. */
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+                            DBusError *errp);
+
+/** Set up a DBusConnection with a mainloop. */
+int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn);
+
+/** Increase the reference count of the given DBus (connection). */
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus);
+
+/** Decrease the reference count of the given DBus (connection). */
+int mrp_dbus_unref(mrp_dbus_t *dbus);
+
+/** Acquire the given name on the given bus (connection). */
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name, DBusError *error);
+
+/** Release the given name on the given bus (connection). */
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, DBusError *error);
+
+typedef void (*mrp_dbus_name_cb_t)(mrp_dbus_t *, const char *, int, void *);
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+                        mrp_dbus_name_cb_t cb, void *user_data);
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+                        mrp_dbus_name_cb_t cb, void *user_data);
+
+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);
+
+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);
+
+MRP_NULLTERM 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, ...);
+
+MRP_NULLTERM 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, ...);
+
+MRP_NULLTERM int mrp_dbus_install_filter(mrp_dbus_t *dbus,
+                                        const char *sender,
+                                        const char *path,
+                                        const char *interface,
+                                        const char *member, ...);
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus,
+                            const char *sender,
+                            const char *path,
+                            const char *interface,
+                            const char *member,
+                            va_list ap);
+
+MRP_NULLTERM int mrp_dbus_remove_filter(mrp_dbus_t *dbus,
+                                       const char *sender,
+                                       const char *path,
+                                       const char *interface,
+                                       const char *member, ...);
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus,
+                           const char *sender,
+                           const char *path,
+                           const char *interface,
+                           const char *member,
+                           va_list ap);
+
+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);
+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);
+
+typedef void (*mrp_dbus_reply_cb_t)(mrp_dbus_t *dbus, DBusMessage *reply,
+                                   void *user_data);
+
+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 dbus_type, ...);
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id);
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, DBusMessage *msg, int type, ...);
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+                   const char *interface, const char *member, int type, ...);
+
+int32_t mrp_dbus_send(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,
+                     DBusMessage *msg);
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, DBusMessage *msg);
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus);
+
+static inline void mrp_dbus_error_init(DBusError *error)
+{
+    /*
+     * Prevent libdbus error messages for NULL DBusError's...
+     */
+    if (error != NULL)
+       dbus_error_init(error);
+}
+
+
+static inline const char *mrp_dbus_errmsg(DBusError *err)
+{
+    if (err && dbus_error_is_set(err))
+       return err->message;
+    else
+       return "unknown DBUS error";
+}
+
+
+#endif /* __MURPHY_DBUS_H__ */
index 702d299..378d177 100644 (file)
@@ -1,6 +1,9 @@
 AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir)
 
-noinst_PROGRAMS = mm-test hash-test mainloop-test msg-test transport-test
+noinst_PROGRAMS  = mm-test hash-test msg-test transport-test
+if DBUS_ENABLED
+noinst_PROGRAMS += mainloop-test dbus-test
+endif
 
 # memory management test
 mm_test_SOURCES = mm-test.c
@@ -27,3 +30,11 @@ transport_test_SOURCES = transport-test.c
 transport_test_CFLAGS  = $(AM_CFLAGS)
 transport_test_LDADD   = ../../libmurphy-common.la
 
+if DBUS_ENABLED
+transport_test_LDADD  += ../../libmurphy-dbus.la
+
+# DBUS test
+dbus_test_SOURCES = dbus-test.c
+dbus_test_CFLAGS  = $(AM_CFLAGS) $(DBUS_CFLAGS)
+dbus_test_LDADD   = ../../libmurphy-dbus.la ../../libmurphy-common.la
+endif
index eb87a6d..f598c4b 100644 (file)
@@ -262,7 +262,7 @@ mrp_transport_t *mrp_transport_accept(mrp_transport_t *lt,
                    t = NULL;
                }
                else {
-                   
+                   t->connected = TRUE;
                }
            });
     }
@@ -328,8 +328,10 @@ int mrp_transport_connect(mrp_transport_t *t, mrp_sockaddr_t *addr,
 
        purge_destroyed(t);
     }
-    else
+    else {
+       errno  = EISCONN;
        result = FALSE;
+    }
 
     return result;
 }
index 0dc0a69..5628c3c 100644 (file)
@@ -59,11 +59,14 @@ typedef struct mrp_transport_s mrp_transport_t;
  * transport socket address
  */
 
+#define MRP_SOCKADDR_SIZE 256
+
 typedef union {
     struct sockaddr     any;
     struct sockaddr_in  ipv4;
     struct sockaddr_in6 ipv6;
     struct sockaddr_un  unx;
+    char                data[MRP_SOCKADDR_SIZE];
 } mrp_sockaddr_t;
 
 
@@ -261,31 +264,35 @@ struct mrp_transport_s {
 
 
 #ifndef __MRP_TRANSPORT_DISABLE_CODE_CHECK__
-#  define __TRANSPORT_CHK_BLOCK(...) do {                              \
-       static int __warned = 0;                                        \
-                                                                       \
-    if (MRP_UNLIKELY(__warned == 0 &&                                  \
-                    strstr(#__VA_ARGS__, "return") != NULL)) {         \
-       mrp_log_error("********************* WARNING *********************"); \
-       mrp_log_error("* You seem to directly do a return from a block   *"); \
-       mrp_log_error("* of code protected by MRP_TRANSPORT_BUSY. Are    *"); \
-       mrp_log_error("* you absolutely sure you know what you are doing *"); \
-       mrp_log_error("* and that you are also doing it correctly ?      *"); \
-       mrp_log_error("***************************************************"); \
-       mrp_log_error("The suspicious code block is located at: ");          \
-       mrp_log_error("  %s@%s:%d", __FUNCTION__, __FILE__, __LINE__);       \
-       mrp_log_error("and it looks like this:");                            \
-       mrp_log_error("---------------------------------------------");      \
-       mrp_log_error("%s", #__VA_ARGS__);                                   \
-       mrp_log_error("---------------------------------------------");      \
-       mrp_log_error("If you understand what MRP_TRANSPORT_BUSY does and"); \
-       mrp_log_error("how, and you are sure about the corretness of your"); \
-       mrp_log_error("code you can disable this error message by");         \
-       mrp_log_error("#defining __MRP_TRANSPORT_DISABLE_CODE_CHECK__");     \
-       mrp_log_error("when compiling %s.", __FILE__);                       \
-       __warned = 1;                                                        \
-    }                                                                       \
- } while (0)
+#  define W mrp_log_error
+#  define __TRANSPORT_CHK_BLOCK(...) do {                                 \
+       static int __checked = FALSE, __warned = FALSE;                    \
+                                                                          \
+       if (MRP_UNLIKELY(!__checked)) {                                    \
+           __checked = TRUE;                                              \
+           if (MRP_UNLIKELY(!__warned &&                                  \
+                            strstr(#__VA_ARGS__, "return") != NULL)) {    \
+               W("*********************** WARNING ********************"); \
+               W("* You seem to directly do a return from a block of *"); \
+               W("* code protected by MRP_TRANSPORT_BUSY. Are you    *"); \
+               W("* absolutely sure you know what you are doing and  *"); \
+               W("* that you are also doing it correctly ?           *"); \
+               W("****************************************************"); \
+               W("The suspicious code block is located at: ");            \
+               W("  %s@%s:%d", __FUNCTION__, __FILE__, __LINE__);         \
+               W("and it looks like this:");                              \
+               W("---------------------------------------------");        \
+               W("%s", #__VA_ARGS__);                                     \
+               W("---------------------------------------------");        \
+               W("If you understand what MRP_TRANSPORT_BUSY does and");   \
+               W("how, and you are sure about the corretness of your");   \
+               W("code you can disable this error message by");           \
+               W("#defining __MRP_TRANSPORT_DISABLE_CODE_CHECK__");       \
+               W("when compiling %s.", __FILE__);                         \
+               __warned = TRUE;                                           \
+           }                                                              \
+       }                                                                  \
+    } while (0)
 #else
 #  define __TRANSPORT_CHK_BLOCK(...) do { } while (0)
 #endif
index 738c52d..9193663 100644 (file)
@@ -1,6 +1,8 @@
-try-load-plugin console # address="tcp4:127.0.0.1:3000"
+# try-load-plugin dbus
+try-load-plugin console        # address="tcp4:127.0.0.1:3000"
                         # address="udp4:127.0.0.1:3000"
-                        # address="unxstrm:@/murphyd"
+                        # address="unxs:@/murphyd"
+                       # address="dbus:[session]@murphy.org/console
 
 # load two instances of the test plugin
 if plugin-exists test