[libvirt] [PATCH] Introduce virDBusCallMethod & virDBusMessageRead methods

Daniel P. Berrange berrange at redhat.com
Wed Jul 17 12:12:16 UTC 2013


From: "Daniel P. Berrange" <berrange at redhat.com>

Doing DBus method calls using libdbus.so is tedious in the
extreme. systemd developers came up with a nice high level
API for DBus method calls (sd_bus_call_method). While
systemd doesn't use libdbus.so, their API design can easily
be ported to libdbus.so.

This patch thus introduces methods virDBusCallMethod &
virDBusMessageRead, which are based on the code used for
sd_bus_call_method and sd_bus_message_read. This code in
systemd is under the LGPLv2+, so we're license compatible.

This code is probably pretty unintelligible unless you are
familiar with the DBus type system. So I added some API
docs trying to explain how to use them, as well as test
cases to validate that I didn't screw up the adaptation
from the original systemd code.

NB: this is being done as a pre-requisite to updating
libvirt's cgroups code to talk to systemd via DBus.

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 .gitignore               |   1 +
 src/libvirt_private.syms |   4 +
 src/util/virdbus.c       | 958 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/util/virdbus.h       |  11 +
 src/util/virdbuspriv.h   |  43 +++
 tests/Makefile.am        |   6 +
 tests/virdbustest.c      | 391 +++++++++++++++++++
 7 files changed, 1413 insertions(+), 1 deletion(-)
 create mode 100644 src/util/virdbuspriv.h
 create mode 100644 tests/virdbustest.c

diff --git a/.gitignore b/.gitignore
index 3efc2e4..851c6e4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -191,6 +191,7 @@
 /tests/virbitmaptest
 /tests/virbuftest
 /tests/vircgrouptest
+/tests/virdbustest
 /tests/virdrivermoduletest
 /tests/virendiantest
 /tests/virhashtest
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 0ab7632..f337637 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1269,8 +1269,12 @@ virConfWriteMem;
 
 
 # util/virdbus.h
+virDBusCallMethod;
 virDBusGetSessionBus;
 virDBusGetSystemBus;
+virDBusMessageDecode;
+virDBusMessageEncode;
+virDBusMessageRead;
 
 
 # util/virdnsmasq.h
diff --git a/src/util/virdbus.c b/src/util/virdbus.c
index 52b6ca9..8c2c783 100644
--- a/src/util/virdbus.c
+++ b/src/util/virdbus.c
@@ -21,11 +21,12 @@
 
 #include <config.h>
 
-#include "virdbus.h"
+#include "virdbuspriv.h"
 #include "viralloc.h"
 #include "virerror.h"
 #include "virlog.h"
 #include "virthread.h"
+#include "virstring.h"
 
 #define VIR_FROM_THIS VIR_FROM_DBUS
 
@@ -223,6 +224,940 @@ static void virDBusToggleWatch(DBusWatch *watch,
     (void)virEventUpdateHandle(info->watch, flags);
 }
 
+# define VIR_DBUS_TYPE_STACK_MAX_DEPTH 32
+
+static const char virDBusBasicTypes[] = {
+    DBUS_TYPE_BYTE,
+    DBUS_TYPE_BOOLEAN,
+    DBUS_TYPE_INT16,
+    DBUS_TYPE_UINT16,
+    DBUS_TYPE_INT32,
+    DBUS_TYPE_UINT32,
+    DBUS_TYPE_INT64,
+    DBUS_TYPE_UINT64,
+    DBUS_TYPE_DOUBLE,
+    DBUS_TYPE_STRING,
+    DBUS_TYPE_OBJECT_PATH,
+    DBUS_TYPE_SIGNATURE,
+    DBUS_TYPE_UNIX_FD
+};
+
+static bool virDBusIsBasicType(char c) {
+    return !!memchr(virDBusBasicTypes, c, ARRAY_CARDINALITY(virDBusBasicTypes));
+}
+
+/*
+ * All code related to virDBusMessageIterEncode and
+ * virDBusMessageIterDecode is derived from systemd
+ * bus_message_append_ap()/message_read_ap() in
+ * bus-message.c under the terms of the LGPLv2+
+ */
+static int
+virDBusSignatureLengthInternal(const char *s,
+                               bool allowDict,
+                               unsigned arrayDepth,
+                               unsigned structDepth,
+                               size_t *l)
+{
+    if (virDBusIsBasicType(*s) || *s == DBUS_TYPE_VARIANT) {
+        *l = 1;
+        return 0;
+    }
+
+    if (*s == DBUS_TYPE_ARRAY) {
+        size_t t;
+
+        if (arrayDepth >= VIR_DBUS_TYPE_STACK_MAX_DEPTH) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Signature '%s' too deeply nested"),
+                           s);
+            return -1;
+        }
+
+        if (virDBusSignatureLengthInternal(s + 1, true, arrayDepth+1, structDepth, &t) < 0)
+            return -1;
+
+        *l = t + 1;
+        return 0;
+    }
+
+    if (*s == DBUS_STRUCT_BEGIN_CHAR) {
+        const char *p = s + 1;
+
+        if (structDepth >= VIR_DBUS_TYPE_STACK_MAX_DEPTH) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Signature '%s' too deeply nested"),
+                           s);
+            return -1;
+        }
+
+        while (*p != DBUS_STRUCT_END_CHAR) {
+            size_t t;
+
+            if (virDBusSignatureLengthInternal(p, false, arrayDepth, structDepth+1, &t) < 0)
+                return -1;
+
+            p += t;
+        }
+
+        *l = p - s + 1;
+        return 0;
+    }
+
+    if (*s == DBUS_DICT_ENTRY_BEGIN_CHAR && allowDict) {
+        const char *p = s + 1;
+        unsigned n = 0;
+        if (structDepth >= VIR_DBUS_TYPE_STACK_MAX_DEPTH) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Signature '%s' too deeply nested"),
+                           s);
+            return -1;
+        }
+
+        while (*p != DBUS_DICT_ENTRY_END_CHAR) {
+            size_t t;
+
+            if (n == 0 && !virDBusIsBasicType(*p)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Dict entry in signature '%s' must be a basic type"),
+                               s);
+                return -1;
+            }
+
+            if (virDBusSignatureLengthInternal(p, false, arrayDepth, structDepth+1, &t) < 0)
+                return -1;
+
+            p += t;
+            n++;
+        }
+
+        if (n != 2) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Dict entry in signature '%s' is wrong size"),
+                           s);
+            return -1;
+        }
+
+        *l = p - s + 1;
+        return 0;
+    }
+
+    virReportError(VIR_ERR_INTERNAL_ERROR,
+                   _("Unexpected signature '%s'"), s);
+    return -1;
+}
+
+
+static int virDBusSignatureLength(const char *s, size_t *l)
+{
+    return virDBusSignatureLengthInternal(s, true, 0, 0, l);
+}
+
+
+
+/* Ideally, we'd just call ourselves recursively on every
+ * complex type. However, the state of a va_list that is
+ * passed to a function is undefined after that function
+ * returns. This means we need to docode the va_list linearly
+ * in a single stackframe. We hence implement our own
+ * home-grown stack in an array. */
+
+typedef struct _virDBusTypeStack virDBusTypeStack;
+struct _virDBusTypeStack {
+    const char *types;
+    size_t nstruct;
+    size_t narray;
+    DBusMessageIter *iter;
+};
+
+static int virDBusTypeStackPush(virDBusTypeStack **stack,
+                                size_t *nstack,
+                                DBusMessageIter *iter,
+                                const char *types,
+                                size_t nstruct,
+                                size_t narray)
+{
+    if (*nstack >= VIR_DBUS_TYPE_STACK_MAX_DEPTH) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("DBus type too deeply nested"));
+        return -1;
+    }
+
+    if (VIR_EXPAND_N(*stack, *nstack, 1) < 0)
+        return -1;
+
+    (*stack)[(*nstack) - 1].iter = iter;
+    (*stack)[(*nstack) - 1].types = types;
+    (*stack)[(*nstack) - 1].nstruct = nstruct;
+    (*stack)[(*nstack) - 1].narray = narray;
+    VIR_DEBUG("Pushed '%s'", types);
+    return 0;
+}
+
+
+static int virDBusTypeStackPop(virDBusTypeStack **stack,
+                               size_t *nstack,
+                               DBusMessageIter **iter,
+                               const char **types,
+                               size_t *nstruct,
+                               size_t *narray)
+{
+    if (*nstack == 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("DBus type stack is empty"));
+        return -1;
+    }
+
+    *iter = (*stack)[(*nstack) - 1].iter;
+    *types = (*stack)[(*nstack) - 1].types;
+    *nstruct = (*stack)[(*nstack) - 1].nstruct;
+    *narray = (*stack)[(*nstack) - 1].narray;
+    VIR_DEBUG("Popped '%s'", *types);
+    VIR_SHRINK_N(*stack, *nstack, 1);
+
+    return 0;
+}
+
+
+static void virDBusTypeStackFree(virDBusTypeStack **stack,
+                                 size_t *nstack)
+{
+    size_t i;
+    /* The iter in the first level of the stack is the
+     * root iter which must not be freed
+     */
+    for (i = 1; i < *nstack; i++) {
+        VIR_FREE((*stack)[i].iter);
+    }
+    VIR_FREE(*stack);
+}
+
+
+# define SET_NEXT_VAL(dbustype, vargtype, sigtype)                      \
+    do {                                                                \
+        dbustype x = (dbustype)va_arg(args, vargtype);                  \
+        if (!dbus_message_iter_append_basic(iter, sigtype, &x)) {       \
+            virReportError(VIR_ERR_INTERNAL_ERROR,                      \
+                           _("Cannot append basic type %s"), #vargtype); \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+static int
+virDBusMessageIterEncode(DBusMessageIter *rootiter,
+                         const char *types,
+                         va_list args)
+{
+    int ret = -1;
+    size_t narray;
+    size_t nstruct;
+    virDBusTypeStack *stack = NULL;
+    size_t nstack = 0;
+    size_t siglen;
+    char *contsig = NULL;
+    const char *vsig;
+    DBusMessageIter *newiter = NULL;
+    DBusMessageIter *iter = rootiter;
+
+    VIR_DEBUG("rootiter=%p types=%s", rootiter, types);
+
+    if (!types)
+        return 0;
+
+    narray = (size_t)-1;
+    nstruct = strlen(types);
+
+    for (;;) {
+        const char *t;
+
+        VIR_DEBUG("Loop stack=%zu array=%zu struct=%zu type='%s'",
+                  nstack, narray, nstruct, types);
+        if (narray == 0 ||
+            (narray == (size_t)-1 &&
+             nstruct == 0)) {
+            DBusMessageIter *thisiter = iter;
+            VIR_DEBUG("Popping iter=%p", iter);
+            if (nstack == 0)
+                break;
+            if (virDBusTypeStackPop(&stack, &nstack, &iter,
+                                    &types, &nstruct, &narray) < 0)
+                goto cleanup;
+            VIR_DEBUG("Popped iter=%p", iter);
+
+            if (!dbus_message_iter_close_container(iter, thisiter)) {
+                if (thisiter != rootiter)
+                    VIR_FREE(thisiter);
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Cannot close container iterator"));
+                goto cleanup;
+            }
+            if (thisiter != rootiter)
+                VIR_FREE(thisiter);
+            continue;
+        }
+
+        t = types;
+        if (narray != (size_t)-1) {
+            narray--;
+        } else {
+            types++;
+            nstruct--;
+        }
+
+        switch (*t) {
+        case DBUS_TYPE_BYTE:
+            SET_NEXT_VAL(unsigned char, int, *t);
+            break;
+
+        case DBUS_TYPE_BOOLEAN:
+            SET_NEXT_VAL(dbus_bool_t, int, *t);
+            break;
+
+        case DBUS_TYPE_INT16:
+            SET_NEXT_VAL(dbus_int16_t, int, *t);
+            break;
+
+        case DBUS_TYPE_UINT16:
+            SET_NEXT_VAL(dbus_uint16_t, unsigned int, *t);
+            break;
+
+        case DBUS_TYPE_INT32:
+            SET_NEXT_VAL(dbus_int32_t, int, *t);
+            break;
+
+        case DBUS_TYPE_UINT32:
+            SET_NEXT_VAL(dbus_uint32_t, unsigned int, *t);
+            break;
+
+        case DBUS_TYPE_INT64:
+            SET_NEXT_VAL(dbus_int64_t, long long, *t);
+            break;
+
+        case DBUS_TYPE_UINT64:
+            SET_NEXT_VAL(dbus_uint64_t, unsigned long long, *t);
+            break;
+
+        case DBUS_TYPE_DOUBLE:
+            SET_NEXT_VAL(double, double, *t);
+            break;
+
+        case DBUS_TYPE_STRING:
+        case DBUS_TYPE_OBJECT_PATH:
+        case DBUS_TYPE_SIGNATURE:
+            SET_NEXT_VAL(char *, char *, *t);
+            break;
+
+        case DBUS_TYPE_ARRAY:
+            if (virDBusSignatureLength(t + 1, &siglen) < 0)
+                goto cleanup;
+
+            if (VIR_STRNDUP(contsig, t + 1, siglen) < 0)
+                goto cleanup;
+
+            if (narray == (size_t)-1) {
+                types += siglen;
+                nstruct -= siglen;
+            }
+
+            if (VIR_ALLOC(newiter) < 0)
+                goto cleanup;
+            VIR_DEBUG("Contsig '%s' '%zu'", contsig, siglen);
+            if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+                                                  contsig, newiter))
+                goto cleanup;
+            if (virDBusTypeStackPush(&stack, &nstack,
+                                     iter, types,
+                                     nstruct, narray) < 0)
+                goto cleanup;
+            VIR_FREE(contsig);
+            iter = newiter;
+            newiter = NULL;
+            types = t + 1;
+            nstruct = siglen;
+            narray = va_arg(args, size_t);
+            break;
+
+        case DBUS_TYPE_VARIANT:
+            vsig = va_arg(args, const char *);
+            if (!vsig) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Missing variant type signature"));
+                goto cleanup;
+            }
+            if (VIR_ALLOC(newiter) < 0)
+                goto cleanup;
+            if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                                  vsig, newiter))
+                goto cleanup;
+            if (virDBusTypeStackPush(&stack, &nstack,
+                                     iter, types,
+                                     nstruct, narray) < 0)
+                goto cleanup;
+            iter = newiter;
+            newiter = NULL;
+            types = vsig;
+            nstruct = strlen(types);
+            narray = (size_t)-1;
+            break;
+
+        case DBUS_STRUCT_BEGIN_CHAR:
+        case DBUS_DICT_ENTRY_BEGIN_CHAR:
+            if (virDBusSignatureLength(t, &siglen) < 0)
+                goto cleanup;
+
+            if (VIR_STRNDUP(contsig, t + 1, siglen - 1) < 0)
+                goto cleanup;
+
+            if (VIR_ALLOC(newiter) < 0)
+                goto cleanup;
+            VIR_DEBUG("Contsig '%s' '%zu'", contsig, siglen);
+            if (!dbus_message_iter_open_container(iter,
+                                                  *t == DBUS_STRUCT_BEGIN_CHAR ?
+                                                  DBUS_TYPE_STRUCT : DBUS_TYPE_DICT_ENTRY,
+                                                  NULL, newiter))
+                goto cleanup;
+            if (narray == (size_t)-1) {
+                types += siglen - 1;
+                nstruct -= siglen - 1;
+            }
+
+            if (virDBusTypeStackPush(&stack, &nstack,
+                                     iter, types,
+                                     nstruct, narray) < 0)
+                goto cleanup;
+            VIR_FREE(contsig);
+            iter = newiter;
+            newiter = NULL;
+            types = t + 1;
+            nstruct = siglen - 2;
+            narray = (size_t)-1;
+
+            break;
+
+        default:
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unknown type in signature '%s'"),
+                           types);
+        }
+    }
+
+    ret = 0;
+
+cleanup:
+    virDBusTypeStackFree(&stack, &nstack);
+    VIR_FREE(contsig);
+    VIR_FREE(newiter);
+    return ret;
+}
+# undef SET_NEXT_VAL
+
+
+# define GET_NEXT_VAL(dbustype, vargtype)                               \
+    do {                                                                \
+        dbustype *x = (dbustype *)va_arg(args, vargtype *);             \
+        dbus_message_iter_get_basic(iter, x);                           \
+    } while (0)
+
+
+static int
+virDBusMessageIterDecode(DBusMessageIter *rootiter,
+                         const char *types,
+                         va_list args)
+{
+    int ret = -1;
+    size_t narray;
+    size_t nstruct;
+    virDBusTypeStack *stack = NULL;
+    size_t nstack = 0;
+    size_t siglen;
+    char *contsig = NULL;
+    const char *vsig;
+    DBusMessageIter *newiter = NULL;
+    DBusMessageIter *iter = rootiter;
+
+    VIR_DEBUG("rootiter=%p types=%s", rootiter, types);
+
+    if (!types)
+        return 0;
+
+    narray = (size_t)-1;
+    nstruct = strlen(types);
+
+    for (;;) {
+        const char *t;
+        bool advanceiter = true;
+
+        VIR_DEBUG("Loop stack=%zu array=%zu struct=%zu type='%s'",
+                  nstack, narray, nstruct, types);
+        if (narray == 0 ||
+            (narray == (size_t)-1 &&
+             nstruct == 0)) {
+            DBusMessageIter *thisiter = iter;
+            VIR_DEBUG("Popping iter=%p", iter);
+            if (nstack == 0)
+                break;
+            if (virDBusTypeStackPop(&stack, &nstack, &iter,
+                                    &types, &nstruct, &narray) < 0)
+                goto cleanup;
+            VIR_DEBUG("Popped iter=%p types=%s", iter, types);
+            if (thisiter != rootiter)
+                VIR_FREE(thisiter);
+            if (!(narray == 0 ||
+                  (narray == (size_t)-1 &&
+                   nstruct == 0)) &&
+                !dbus_message_iter_next(iter)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Not enough fields in message for signature"));
+                goto cleanup;
+            }
+            continue;
+        }
+
+        t = types;
+        if (narray != (size_t)-1) {
+            narray--;
+        } else {
+            types++;
+            nstruct--;
+        }
+
+        switch (*t) {
+        case DBUS_TYPE_BYTE:
+            GET_NEXT_VAL(unsigned char, int);
+            break;
+
+        case DBUS_TYPE_BOOLEAN:
+            GET_NEXT_VAL(dbus_bool_t, int);
+            break;
+
+        case DBUS_TYPE_INT16:
+            GET_NEXT_VAL(dbus_int16_t, int);
+            break;
+
+        case DBUS_TYPE_UINT16:
+            GET_NEXT_VAL(dbus_uint16_t, unsigned int);
+            break;
+
+        case DBUS_TYPE_INT32:
+            GET_NEXT_VAL(dbus_uint32_t, int);
+            break;
+
+        case DBUS_TYPE_UINT32:
+            GET_NEXT_VAL(dbus_uint32_t, unsigned int);
+            break;
+
+        case DBUS_TYPE_INT64:
+            GET_NEXT_VAL(dbus_uint64_t, long long);
+            break;
+
+        case DBUS_TYPE_UINT64:
+            GET_NEXT_VAL(dbus_uint64_t, unsigned long long);
+            break;
+
+        case DBUS_TYPE_DOUBLE:
+            GET_NEXT_VAL(double, double);
+            break;
+
+        case DBUS_TYPE_STRING:
+        case DBUS_TYPE_OBJECT_PATH:
+        case DBUS_TYPE_SIGNATURE:
+            do {
+                char **x = (char **)va_arg(args, char **);
+                char *s;
+                dbus_message_iter_get_basic(iter, &s);
+                if (VIR_STRDUP(*x, s) < 0)
+                    goto cleanup;
+            } while (0);
+            break;
+
+        case DBUS_TYPE_ARRAY:
+            advanceiter = false;
+            if (virDBusSignatureLength(t + 1, &siglen) < 0)
+                goto cleanup;
+
+            if (VIR_STRNDUP(contsig, t + 1, siglen) < 0)
+                goto cleanup;
+
+            if (narray == (size_t)-1) {
+                types += siglen;
+                nstruct -= siglen;
+            }
+
+            if (VIR_ALLOC(newiter) < 0)
+                goto cleanup;
+            VIR_DEBUG("Contsig '%s' '%zu' '%s'", contsig, siglen, types);
+            dbus_message_iter_recurse(iter, newiter);
+            if (virDBusTypeStackPush(&stack, &nstack,
+                                     iter, types,
+                                     nstruct, narray) < 0)
+                goto cleanup;
+            VIR_FREE(contsig);
+            iter = newiter;
+            newiter = NULL;
+            types = t + 1;
+            nstruct = siglen;
+            narray = va_arg(args, size_t);
+            break;
+
+        case DBUS_TYPE_VARIANT:
+            advanceiter = false;
+            vsig = va_arg(args, const char *);
+            if (!vsig) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Missing variant type signature"));
+                goto cleanup;
+            }
+            if (VIR_ALLOC(newiter) < 0)
+                goto cleanup;
+            dbus_message_iter_recurse(iter, newiter);
+            if (virDBusTypeStackPush(&stack, &nstack,
+                                     iter, types,
+                                     nstruct, narray) < 0) {
+                VIR_DEBUG("Push failed");
+                goto cleanup;
+            }
+            iter = newiter;
+            newiter = NULL;
+            types = vsig;
+            nstruct = strlen(types);
+            narray = (size_t)-1;
+            break;
+
+        case DBUS_STRUCT_BEGIN_CHAR:
+        case DBUS_DICT_ENTRY_BEGIN_CHAR:
+            advanceiter = false;
+            if (virDBusSignatureLength(t, &siglen) < 0)
+                goto cleanup;
+
+            if (VIR_STRNDUP(contsig, t + 1, siglen - 1) < 0)
+                goto cleanup;
+
+            if (VIR_ALLOC(newiter) < 0)
+                goto cleanup;
+            VIR_DEBUG("Contsig '%s' '%zu'", contsig, siglen);
+            dbus_message_iter_recurse(iter, newiter);
+            if (narray == (size_t)-1) {
+                types += siglen - 1;
+                nstruct -= siglen - 1;
+            }
+
+            if (virDBusTypeStackPush(&stack, &nstack,
+                                     iter, types,
+                                     nstruct, narray) < 0)
+                goto cleanup;
+            VIR_FREE(contsig);
+            iter = newiter;
+            newiter = NULL;
+            types = t + 1;
+            nstruct = siglen - 2;
+            narray = (size_t)-1;
+
+            break;
+
+        default:
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unknown type in signature '%s'"),
+                           types);
+        }
+
+        VIR_DEBUG("After stack=%zu array=%zu struct=%zu type='%s'",
+                  nstack, narray, nstruct, types);
+        if (advanceiter &&
+            !(narray == 0 ||
+              (narray == (size_t)-1 &&
+               nstruct == 0)) &&
+            !dbus_message_iter_next(iter)) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Not enough fields in message for signature"));
+            goto cleanup;
+        }
+    }
+
+    if (dbus_message_iter_has_next(iter)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Too many fields in message for signature"));
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    virDBusTypeStackFree(&stack, &nstack);
+    VIR_FREE(contsig);
+    VIR_FREE(newiter);
+    return ret;
+}
+# undef GET_NEXT_VAL
+
+int
+virDBusMessageEncodeArgs(DBusMessage* msg,
+                         const char *types,
+                         va_list args)
+{
+    DBusMessageIter iter;
+    int ret = -1;
+
+    memset(&iter, 0, sizeof(iter));
+
+    dbus_message_iter_init_append(msg, &iter);
+
+    ret = virDBusMessageIterEncode(&iter, types, args);
+
+    return ret;
+}
+
+
+int virDBusMessageDecodeArgs(DBusMessage* msg,
+                             const char *types,
+                             va_list args)
+{
+    DBusMessageIter iter;
+    int ret = -1;
+
+    if (!dbus_message_iter_init(msg, &iter)) {
+        if (*types != '\0') {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("No args present for signature %s"),
+                           types);
+        } else {
+            ret = 0;
+        }
+        goto cleanup;
+    }
+
+    ret = virDBusMessageIterDecode(&iter, types, args);
+
+cleanup:
+    return ret;
+}
+
+
+int virDBusMessageEncode(DBusMessage* msg,
+                         const char *types,
+                         ...)
+{
+    int ret;
+    va_list args;
+    va_start(args, types);
+    ret = virDBusMessageEncodeArgs(msg, types, args);
+    va_end(args);
+    return ret;
+}
+
+
+int virDBusMessageDecode(DBusMessage* msg,
+                         const char *types,
+                         ...)
+{
+    int ret;
+    va_list args;
+    va_start(args, types);
+    ret = virDBusMessageDecodeArgs(msg, types, args);
+    va_end(args);
+    return ret;
+}
+
+/**
+ * @conn: a DBus connection
+ * @replyout: pointer to receive reply message, or NULL
+ * @destination: bus identifier of the target service
+ * @path: object path of the target service
+ * @interface: the interface of the object
+ * @member: the name of the method in the interface
+ * @types: type signature for following method arguments
+ * @...: method arguments
+ *
+ * This invokes a method on a remote service on the
+ * DBus bus @conn. The @destination, @path, @interface
+ * and @member parameters identify the object method to
+ * be invoked. The optional @replyout parameter will be
+ * filled with any reply to the method call. The
+ * virDBusMethodReply method can be used to decode the
+ * return values.
+ *
+ * The @types parameter is a DBus signature describing
+ * the method call parameters which will be provided
+ * as variadic args. Each character in @types must
+ * correspond to one of the following DBus codes for
+ * basic types:
+ *
+ * 'y' - 8-bit byte, promoted to an 'int'
+ * 'b' - bool value, promoted to an 'int'
+ * 'n' - 16-bit signed integer, promoted to an 'int'
+ * 'q' - 16-bit unsigned integer, promoted to an 'int'
+ * 'i' - 32-bit signed integer, passed as an 'int'
+ * 'u' - 32-bit unsigned integer, passed as an 'int'
+ * 'x' - 64-bit signed integer, passed as a 'long long'
+ * 't' - 64-bit unsigned integer, passed as an 'unsigned long long'
+ * 'd' - 8-byte floating point, passed as a 'double'
+ * 's' - NULL terminated string, in UTF-8
+ * 'o' - NULL terminated string, representing a valid object path
+ * 'g' - NULL terminated string, representing a valid type signature
+ *
+ * or use one of the compound types
+ *
+ * 'a' - array of values
+ * 'v' - a variadic type.
+ * '(' - start of a struct
+ * ')' - end of a struct
+ * '{' - start of a dictionary entry (pair of types)
+ * '}' - start of a dictionary entry (pair of types)
+ *
+ * Passing values in variadic args for basic types is
+ * simple, the value is just passed directly using the
+ * corresponding C type listed against the type code
+ * above. Note how any integer value smaller than an
+ * 'int' is promoted to an 'int' by the C rules for
+ * variadic args.
+ *
+ * Passing values in variadic args for compound types
+ * requires a little further explanation.
+ *
+ * - Variant: the first arg is a string containing
+ *   the type signature for the values to be stored
+ *   inside the variant. This is then followed by
+ *   the values corresponding to the type signature
+ *   in the normal manner.
+ *
+ * - Array: when 'a' appears in a type signature, it
+ *   must be followed by a single type describing the
+ *   array element type. For example 'as' is an array
+ *   of strings. 'a(is)' is an array of structs, each
+ *   struct containing an int and a string.
+ *
+ *   The first variadic arg for an array, is an 'int'
+ *   specifying the number of elements in the array.
+ *   This is then followed by the values for the array
+ *
+ * - Struct: when a '(' appears in a type signature,
+ *   it must be followed by one or more types describing
+ *   the elements in the array, terminated by a ')'.
+ *
+ * - Dict entry: when a '{' appears in a type signature it
+ *   must be followed by exactly two types, one describing
+ *   the type of the hash key, the other describing the
+ *   type of the hash entry. The hash key type must be
+ *   a basic type, not a compound type.
+ *
+ * Example signatures, with their corresponding variadic
+ * args:
+ *
+ * - "biiss" - some basic types
+ *
+ *     (true, 7, 42, "hello", "world")
+ *
+ * - "as" - an array with a basic type element
+ *
+ *     (3, "one", "two", "three")
+ *
+ * - "a(is)" - an array with a struct element
+ *
+ *     (3, 1, "one", 2, "two", 3, "three")
+ *
+ * - "svs" - some basic types with a variant as an int
+ *
+ *     ("hello", "i", 3, "world")
+ *
+ * - "svs" - some basic types with a variant as an array of ints
+ *
+ *     ("hello", "ai", 4, 1, 2, 3, 4, "world")
+ *
+ * - "a{ss}" - a hash table (aka array + dict entry)
+ *
+ *     (3, "title", "Mr", "forename", "Joe", "surname", "Bloggs")
+ *
+ * - "a{sv}" - a hash table (aka array + dict entry)
+ *
+ *     (3, "email", "s", "joe at blogs.com", "age", "i", 35,
+ *      "address", "as", 3, "Some house", "Some road", "some city")
+ */
+
+int virDBusCallMethod(DBusConnection *conn,
+                      DBusMessage **replyout,
+                      const char *destination,
+                      const char *path,
+                      const char *interface,
+                      const char *member,
+                      const char *types, ...)
+{
+    DBusMessage *call = NULL;
+    DBusMessage *reply = NULL;
+    DBusError error;
+    int ret = -1;
+    va_list args;
+
+    dbus_error_init(&error);
+
+    if (!(call = dbus_message_new_method_call(destination,
+                                              path,
+                                              interface,
+                                              member))) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    va_start(args, types);
+    ret = virDBusMessageEncodeArgs(call, types, args);
+    va_end(args);
+    if (ret < 0)
+        goto cleanup;
+
+    ret = -1;
+
+    if (!(reply = dbus_connection_send_with_reply_and_block(conn,
+                                                            call,
+                                                            30 * 1000,
+                                                            &error))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Cannot send to %s.%s on path %s with interface %s: %s"),
+                       destination, member, path, interface, NULLSTR(error.message));
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    dbus_error_free(&error);
+    if (call)
+        dbus_message_unref(call);
+    if (reply) {
+        if (ret == 0 && replyout)
+            *replyout = reply;
+        else
+            dbus_message_unref(reply);
+    }
+    return ret;
+}
+
+
+/**
+ * virDBusMessageRead:
+ * @msg: the reply to decode
+ * @types: type signature for following return values
+ * @...: pointers in which to store return values
+ *
+ * The @types type signature is the same format as
+ * that used for the virDBusCallMethod. The difference
+ * is that each variadic parameter must be a pointer to
+ * be filled with the values. eg instead of passing an
+ * 'int', pass an 'int *'.
+ *
+ */
+int virDBusMessageRead(DBusMessage *msg,
+                       const char *types, ...)
+{
+    va_list args;
+    int ret;
+
+    va_start(args, types);
+    ret = virDBusMessageDecodeArgs(msg, types, args);
+    va_end(args);
+
+    dbus_message_unref(msg);
+    return ret;
+}
+
+
 #else /* ! WITH_DBUS */
 DBusConnection *virDBusGetSystemBus(void)
 {
@@ -237,4 +1172,25 @@ DBusConnection *virDBusGetSessionBus(void)
                    "%s", _("DBus support not compiled into this binary"));
     return NULL;
 }
+
+int virDBusCallMethod(DBusConnection *conn ATTRIBUTE_UNUSED,
+                      const char *destination ATTRIBUTE_UNUSED,
+                      const char *path ATTRIBUTE_UNUSED,
+                      const char *interface ATTRIBUTE_UNUSED,
+                      const char *member ATTRIBUTE_UNUSED,
+                      const char *types ATTRIBUTE_UNUSED, ...)
+{
+    virReportError(VIR_ERR_INTERNAL_ERROR,
+                   "%s", _("DBus support not compiled into this binary"));
+    return -1;
+}
+
+int virDBusMessageRead(DBusMessage *msg ATTRIBUTE_UNUSED,
+                       const char *types ATTRIBUTE_UNUSED, ...)
+{
+    virReportError(VIR_ERR_INTERNAL_ERROR,
+                   "%s", _("DBus support not compiled into this binary"));
+    return -1;
+}
+
 #endif /* ! WITH_DBUS */
diff --git a/src/util/virdbus.h b/src/util/virdbus.h
index a73e293..69a32d8 100644
--- a/src/util/virdbus.h
+++ b/src/util/virdbus.h
@@ -27,10 +27,21 @@
 #  include <dbus/dbus.h>
 # else
 #  define DBusConnection void
+#  define DBusMesssage void
 # endif
 # include "internal.h"
 
 DBusConnection *virDBusGetSystemBus(void);
 DBusConnection *virDBusGetSessionBus(void);
 
+int virDBusCallMethod(DBusConnection *conn,
+                      DBusMessage **reply,
+                      const char *destination,
+                      const char *path,
+                      const char *interface,
+                      const char *member,
+                      const char *types, ...);
+int virDBusMessageRead(DBusMessage *msg,
+                       const char *types, ...);
+
 #endif /* __VIR_DBUS_H__ */
diff --git a/src/util/virdbuspriv.h b/src/util/virdbuspriv.h
new file mode 100644
index 0000000..751536f
--- /dev/null
+++ b/src/util/virdbuspriv.h
@@ -0,0 +1,43 @@
+/*
+ * virdbuspriv.h: internal APIs for testing DBus code
+ *
+ * Copyright (C) 2012-2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VIR_DBUS_PRIV_H__
+# define __VIR_DBUS_PRIV_H__
+
+# include "virdbus.h"
+
+int virDBusMessageEncodeArgs(DBusMessage* msg,
+                             const char *types,
+                             va_list args);
+
+int virDBusMessageDecodeArgs(DBusMessage* msg,
+                             const char *types,
+                             va_list args);
+
+int virDBusMessageEncode(DBusMessage* msg,
+                         const char *types,
+                         ...);
+
+int virDBusMessageDecode(DBusMessage* msg,
+                         const char *types,
+                         ...);
+
+#endif /* __VIR_DBUS_PRIV_H__ */
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 4c49151..9eaa9d8 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -116,6 +116,7 @@ test_programs = virshtest sockettest \
 	virauthconfigtest \
 	virbitmaptest \
 	vircgrouptest \
+	virdbustest \
 	virendiantest \
 	viridentitytest \
 	virkeycodetest \
@@ -637,6 +638,11 @@ vircgroupmock_la_CFLAGS = $(AM_CFLAGS)
 vircgroupmock_la_LDFLAGS = -module -avoid-version \
         -rpath /evil/libtool/hack/to/force/shared/lib/creation
 
+virdbustest_SOURCES = \
+	virdbustest.c testutils.h testutils.c
+virdbustest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS)
+virdbustest_LDADD = $(LDADDS)
+
 
 viruritest_SOURCES = \
 	viruritest.c testutils.h testutils.c
diff --git a/tests/virdbustest.c b/tests/virdbustest.c
new file mode 100644
index 0000000..13cd8cf
--- /dev/null
+++ b/tests/virdbustest.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#include "virdbuspriv.h"
+#include "virlog.h"
+#include "testutils.h"
+
+#define VERIFY(t, a, b, f)                                              \
+    do {                                                                \
+        VIR_DEBUG("Compare " t " '" f "' to '" f "'", a, b);            \
+        if (a != b) {                                                   \
+            fprintf(stderr, "Failed to round-trip " t " '" f "' to '" f "'\n", a, b); \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+#define VERIFY_STR(t, a, b, f)                                          \
+    do {                                                                \
+        VIR_DEBUG("Compare " t " '" f "' to '" f "'", a, b);            \
+        if (STRNEQ(a, b)) {                                             \
+            fprintf(stderr, "Failed to round-trip " t " '" f "' to '" f "'\n", a, b); \
+            goto cleanup;                                               \
+        }                                                               \
+    } while (0)
+
+static int testMessageSimple(const void *args ATTRIBUTE_UNUSED)
+{
+    DBusMessage *msg = NULL;
+    int ret = -1;
+    unsigned char in_byte = 200, out_byte = 0;
+    bool in_bool = true, out_bool = false;
+    int in_int16 = 12000, out_int16 = 0;
+    unsigned int in_uint16 = 32000, out_uint16 = 0;
+    int in_int32 = 100000000, out_int32 = 0;
+    unsigned int in_uint32 = 200000000, out_uint32 = 0;
+    long long in_int64 = 1000000000000, out_int64 = 0;
+    unsigned long long in_uint64 = 2000000000000, out_uint64 = 0;
+    double in_double = 3.14159265359, out_double = 0;;
+    const char *in_string = "Hello World";
+    char *out_string = NULL;
+    const char *in_objectpath = "/org/libvirt/test";
+    char *out_objectpath = NULL;
+    const char *in_signature = "ybnqiuxtdsog";
+    char *out_signature = NULL;
+
+    if (!(msg = dbus_message_new_method_call("org.libvirt.test",
+                                             "/org/libvirt/test",
+                                             "org.libvirt.test.astrochicken",
+                                             "cluck"))) {
+        VIR_DEBUG("Failed to allocate method call");
+        goto cleanup;
+    }
+
+    if (virDBusMessageEncode(msg,
+                             "ybnqiuxtdsog",
+                             in_byte, in_bool,
+                             in_int16, in_uint16,
+                             in_int32, in_uint32,
+                             in_int64, in_uint64,
+                             in_double, in_string,
+                             in_objectpath, in_signature) < 0) {
+        VIR_DEBUG("Failed to encode arguments");
+        goto cleanup;
+    }
+
+    if (virDBusMessageDecode(msg,
+                             "ybnqiuxtdsog",
+                             &out_byte, &out_bool,
+                             &out_int16, &out_uint16,
+                             &out_int32, &out_uint32,
+                             &out_int64, &out_uint64,
+                             &out_double, &out_string,
+                             &out_objectpath, &out_signature) < 0) {
+        VIR_DEBUG("Failed to decode arguments");
+        goto cleanup;
+    }
+
+    VERIFY("byte", in_byte, out_byte, "%d");
+    VERIFY("bool", in_bool, out_bool, "%d");
+    VERIFY("int16", in_int16, out_int16, "%d");
+    VERIFY("uint16", in_int16, out_int16, "%d");
+    VERIFY("int32", in_int32, out_int32, "%d");
+    VERIFY("uint32", in_int32, out_int32, "%d");
+    VERIFY("int64", in_int64, out_int64, "%lld");
+    VERIFY("uint64", in_int64, out_int64, "%lld");
+    VERIFY("double", in_double, out_double, "%lf");
+    VERIFY_STR("string", in_string, out_string, "%s");
+    VERIFY_STR("objectpath", in_objectpath, out_objectpath, "%s");
+    VERIFY_STR("signature", in_signature, out_signature, "%s");
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(out_string);
+    VIR_FREE(out_signature);
+    VIR_FREE(out_objectpath);
+    dbus_message_unref(msg);
+    return ret;
+}
+
+
+static int testMessageVariant(const void *args ATTRIBUTE_UNUSED)
+{
+    DBusMessage *msg = NULL;
+    int ret = -1;
+    const char *in_str1 = "Hello";
+    int in_int32 = 100000000, out_int32 = 0;
+    const char *in_str2 = "World";
+    char *out_str1 = NULL, *out_str2 = NULL;
+
+    if (!(msg = dbus_message_new_method_call("org.libvirt.test",
+                                             "/org/libvirt/test",
+                                             "org.libvirt.test.astrochicken",
+                                             "cluck"))) {
+        VIR_DEBUG("Failed to allocate method call");
+        goto cleanup;
+    }
+
+    if (virDBusMessageEncode(msg,
+                             "svs",
+                             in_str1,
+                             "i", in_int32,
+                             in_str2) < 0) {
+        VIR_DEBUG("Failed to encode arguments");
+        goto cleanup;
+    }
+
+    if (virDBusMessageDecode(msg,
+                             "svs",
+                             &out_str1,
+                             "i", &out_int32,
+                             &out_str2) < 0) {
+        VIR_DEBUG("Failed to decode arguments");
+        goto cleanup;
+    }
+
+
+    VERIFY_STR("str1", in_str1, out_str1, "%s");
+    VERIFY("int32", in_int32, out_int32, "%d");
+    VERIFY_STR("str2", in_str2, out_str2, "%s");
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(out_str1);
+    VIR_FREE(out_str2);
+    dbus_message_unref(msg);
+    return ret;
+}
+
+static int testMessageArray(const void *args ATTRIBUTE_UNUSED)
+{
+    DBusMessage *msg = NULL;
+    int ret = -1;
+    const char *in_str1 = "Hello";
+    size_t arraylen = 3;
+    int in_int32a = 100000000, out_int32a = 0;
+    int in_int32b = 200000000, out_int32b = 0;
+    int in_int32c = 300000000, out_int32c = 0;
+    const char *in_str2 = "World";
+    char *out_str1 = NULL, *out_str2 = NULL;
+
+    if (!(msg = dbus_message_new_method_call("org.libvirt.test",
+                                             "/org/libvirt/test",
+                                             "org.libvirt.test.astrochicken",
+                                             "cluck"))) {
+        VIR_DEBUG("Failed to allocate method call");
+        goto cleanup;
+    }
+
+    if (virDBusMessageEncode(msg,
+                             "sais",
+                             in_str1,
+                             arraylen, in_int32a, in_int32b, in_int32c,
+                             in_str2) < 0) {
+        VIR_DEBUG("Failed to encode arguments");
+        goto cleanup;
+    }
+
+    if (virDBusMessageDecode(msg,
+                             "sais",
+                             &out_str1,
+                             arraylen, &out_int32a, &out_int32b, &out_int32c,
+                             &out_str2) < 0) {
+        VIR_DEBUG("Failed to decode arguments");
+        goto cleanup;
+    }
+
+
+    VERIFY_STR("str1", in_str1, out_str1, "%s");
+    VERIFY("int32a", in_int32a, out_int32a, "%d");
+    VERIFY("int32b", in_int32b, out_int32b, "%d");
+    VERIFY("int32c", in_int32c, out_int32c, "%d");
+    VERIFY_STR("str2", in_str2, out_str2, "%s");
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(out_str1);
+    VIR_FREE(out_str2);
+    dbus_message_unref(msg);
+    return ret;
+}
+
+static int testMessageStruct(const void *args ATTRIBUTE_UNUSED)
+{
+    DBusMessage *msg = NULL;
+    int ret = -1;
+    unsigned char in_byte = 200, out_byte = 0;
+    bool in_bool = true, out_bool = false;
+    int in_int16 = 12000, out_int16 = 0;
+    unsigned int in_uint16 = 32000, out_uint16 = 0;
+    int in_int32 = 100000000, out_int32 = 0;
+    unsigned int in_uint32 = 200000000, out_uint32 = 0;
+    long long in_int64 = 1000000000000, out_int64 = 0;
+    unsigned long long in_uint64 = 2000000000000, out_uint64 = 0;
+    double in_double = 3.14159265359, out_double = 0;;
+    const char *in_string = "Hello World";
+    char *out_string = NULL;
+    const char *in_objectpath = "/org/libvirt/test";
+    char *out_objectpath = NULL;
+    const char *in_signature = "ybnqiuxtdsog";
+    char *out_signature = NULL;
+
+    if (!(msg = dbus_message_new_method_call("org.libvirt.test",
+                                             "/org/libvirt/test",
+                                             "org.libvirt.test.astrochicken",
+                                             "cluck"))) {
+        VIR_DEBUG("Failed to allocate method call");
+        goto cleanup;
+    }
+
+    if (virDBusMessageEncode(msg,
+                             "ybn(qiuxtds)og",
+                             in_byte, in_bool,
+                             in_int16, in_uint16,
+                             in_int32, in_uint32,
+                             in_int64, in_uint64,
+                             in_double, in_string,
+                             in_objectpath, in_signature) < 0) {
+        VIR_DEBUG("Failed to encode arguments");
+        goto cleanup;
+    }
+
+    if (virDBusMessageDecode(msg,
+                             "ybn(qiuxtds)og",
+                             &out_byte, &out_bool,
+                             &out_int16, &out_uint16,
+                             &out_int32, &out_uint32,
+                             &out_int64, &out_uint64,
+                             &out_double, &out_string,
+                             &out_objectpath, &out_signature) < 0) {
+        VIR_DEBUG("Failed to decode arguments");
+        goto cleanup;
+    }
+
+    VERIFY("byte", in_byte, out_byte, "%d");
+    VERIFY("bool", in_bool, out_bool, "%d");
+    VERIFY("int16", in_int16, out_int16, "%d");
+    VERIFY("uint16", in_int16, out_int16, "%d");
+    VERIFY("int32", in_int32, out_int32, "%d");
+    VERIFY("uint32", in_int32, out_int32, "%d");
+    VERIFY("int64", in_int64, out_int64, "%lld");
+    VERIFY("uint64", in_int64, out_int64, "%lld");
+    VERIFY("double", in_double, out_double, "%lf");
+    VERIFY_STR("string", in_string, out_string, "%s");
+    VERIFY_STR("objectpath", in_objectpath, out_objectpath, "%s");
+    VERIFY_STR("signature", in_signature, out_signature, "%s");
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(out_string);
+    VIR_FREE(out_signature);
+    VIR_FREE(out_objectpath);
+    dbus_message_unref(msg);
+    return ret;
+}
+
+
+static int testMessageDict(const void *args ATTRIBUTE_UNUSED)
+{
+    DBusMessage *msg = NULL;
+    int ret = -1;
+    const char *in_str1 = "Hello";
+    size_t arraylen = 3;
+    int in_int32a = 100000000, out_int32a = 0;
+    const char *in_key1 = "turnover";
+    int in_int32b = 200000000, out_int32b = 0;
+    const char *in_key2 = "revenue";
+    int in_int32c = 300000000, out_int32c = 0;
+    const char *in_key3 = "debt";
+    const char *in_str2 = "World";
+    char *out_str1 = NULL, *out_str2 = NULL;
+    char *out_key1 = NULL, *out_key2 = NULL, *out_key3 = NULL;
+
+    if (!(msg = dbus_message_new_method_call("org.libvirt.test",
+                                             "/org/libvirt/test",
+                                             "org.libvirt.test.astrochicken",
+                                             "cluck"))) {
+        VIR_DEBUG("Failed to allocate method call");
+        goto cleanup;
+    }
+
+    if (virDBusMessageEncode(msg,
+                             "sa{si}s",
+                             in_str1,
+                             arraylen,
+                             in_key1, in_int32a,
+                             in_key2, in_int32b,
+                             in_key3, in_int32c,
+                             in_str2) < 0) {
+        VIR_DEBUG("Failed to encode arguments");
+        goto cleanup;
+    }
+
+    if (virDBusMessageDecode(msg,
+                             "sa{si}s",
+                             &out_str1,
+                             arraylen,
+                             &out_key1, &out_int32a,
+                             &out_key2, &out_int32b,
+                             &out_key3, &out_int32c,
+                             &out_str2) < 0) {
+        VIR_DEBUG("Failed to decode arguments");
+        goto cleanup;
+    }
+
+
+    VERIFY_STR("str1", in_str1, out_str1, "%s");
+    VERIFY("int32a", in_int32a, out_int32a, "%d");
+    VERIFY("int32b", in_int32b, out_int32b, "%d");
+    VERIFY("int32c", in_int32c, out_int32c, "%d");
+    VERIFY_STR("key1", in_key1, out_key1, "%s");
+    VERIFY_STR("key1", in_key2, out_key2, "%s");
+    VERIFY_STR("key1", in_key3, out_key3, "%s");
+    VERIFY_STR("str2", in_str2, out_str2, "%s");
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(out_str1);
+    VIR_FREE(out_str2);
+    VIR_FREE(out_key1);
+    VIR_FREE(out_key2);
+    VIR_FREE(out_key3);
+    dbus_message_unref(msg);
+    return ret;
+}
+
+
+static int
+mymain(void)
+{
+    int ret = 0;
+
+    if (virtTestRun("Test message simple ", 1, testMessageSimple, NULL) < 0)
+        ret = -1;
+    if (virtTestRun("Test message variant ", 1, testMessageVariant, NULL) < 0)
+        ret = -1;
+    if (virtTestRun("Test message array ", 1, testMessageArray, NULL) < 0)
+        ret = -1;
+    if (virtTestRun("Test message struct ", 1, testMessageStruct, NULL) < 0)
+        ret = -1;
+    if (virtTestRun("Test message dict ", 1, testMessageDict, NULL) < 0)
+        ret = -1;
+    return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIRT_TEST_MAIN(mymain)
-- 
1.8.1.4




More information about the libvir-list mailing list