From: "Daniel P. Berrange" <berrange(a)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(a)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(a)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(a)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