On Thu, Jul 18, 2013 at 04:33:06PM -0600, Eric Blake wrote: > On 07/18/2013 07:27 AM, Daniel P. Berrange wrote: > > From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> > > > > 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. > > > > Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx> > > Failed to build. > > util/virdbus.c:1119:9: error: implicit declaration of function > 'virReportDBusServiceError' [-Werror=implicit-function-declaration] > virReportDBusServiceError(error.message ? error.message : > "unknown error", > ^ > util/virdbus.c:1119:9: error: nested extern declaration of > 'virReportDBusServiceError' [-Werror=nested-externs] Opps, yes, stuff slipped into the following patch. > > + > > + if (virDBusSignatureLengthInternal(s + 1, true, arrayDepth+1, structDepth, &t) < 0) > > Inconsistent spacing around '+'. > > > + > > + while (*p != DBUS_STRUCT_END_CHAR) { > > + size_t t; > > + > > + if (virDBusSignatureLengthInternal(p, false, arrayDepth, structDepth+1, &t) < 0) > > and again, probably also a long line worth wrapping. > > > > + > > + if (virDBusSignatureLengthInternal(p, false, arrayDepth, structDepth+1, &t) < 0) > > Recurring theme on '+'; I'll quit pointing them out. All changed. > > > + > > +/* 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 > > d/docode/decode/ > > > + * in a single stackframe. We hence implement our own > > + * home-grown stack in an array. */ > > Is it also possible to use va_copy, or is that risking stack explosion > because we'd have to copy the list for each recursion? To be honest, this code is hard enough to understand without trying to change the way systemd developers wrote it. So I just took the approach of following their design without trying to improve it myself, since it'd be more likely that I'd screw it up :-) > > + > > +/** > > + * 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 *'. > > Are variants/structs/dicts allowed on decoding? If so, an example of > how to interleave intermediate types alongside pointers for receiving > contents may make sense. Yes, with the limitation that you must know exactly the nesting you are expecting to get back. ie must know the array length, must know the variant sub-type, etc. If you have unpredictable complex types then you'll have to use dbus apis the hard way. Fortunately this is mostly a problem for DBus servers, clients typically only need to deal with very simple out parameters. > > + > > + if (virDBusMessageDecode(msg, > > + "svs", > > + &out_str1, > > + "i", &out_int32, > > + &out_str2) < 0) { > > Ah, so you CAN pass a variant to decode. I guess DBus is smart enough > to error out if any type signature fails to match the actual message > being decoded (that is, if I pass ["v", "i", &out_int32], but the > message was encoded with ["v", "s", "hello"], then I get a proper > message about type mismatch rather than some random integer which > happens to represent 32-bits of the pointer value of "hello")? In fact, > it is worth a negative test, to prove that we handle mismatch in the > client's arguments vs. the message being decoded? I'll see about doing some negative tests. > > + > > + if (virDBusMessageDecode(msg, > > + "sais", > > + &out_str1, > > + 3, &out_int32a, &out_int32b, &out_int32c, > > + &out_str2) < 0) { > > Likewise, what happens if our array is too small or too large compared > to what we are decoding? Too small is probably an error; does too large > work and just leave the extra pointers unmodified? As above, it just won't work - you need to know the size upfront. > Overall, it looks pretty clean; I'm okay if you just post an interdiff > for the change you make to fix compilation instead of a full-blown v2. Oddly I can't get the test failure you did. Can you test this updated patch below where I've added more debugging and send the output of LIBVIRT_DEBUG=1 VIR_TEST_DEBUG=1 ./virdbustest Daniel >From d920df83f4966a088d6bb88b27b5ce11b546db2c Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> Date: Fri, 12 Jul 2013 11:13:04 +0100 Subject: [PATCH] Introduce virDBusCallMethod & virDBusMessageRead methods 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. Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx> --- .gitignore | 1 + include/libvirt/virterror.h | 1 + src/libvirt_private.syms | 4 + src/util/virdbus.c | 985 +++++++++++++++++++++++++++++++++++++++++++- src/util/virdbus.h | 11 + src/util/virdbuspriv.h | 45 ++ src/util/virerror.c | 6 + src/util/virerror.h | 11 + tests/Makefile.am | 13 + tests/virdbustest.c | 393 ++++++++++++++++++ 10 files changed, 1469 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/include/libvirt/virterror.h b/include/libvirt/virterror.h index 5f78856..3ef73a1 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -294,6 +294,7 @@ typedef enum { VIR_ERR_RESOURCE_BUSY = 87, /* resource is already in use */ VIR_ERR_ACCESS_DENIED = 88, /* operation on the object/resource was denied */ + VIR_ERR_DBUS_SERVICE = 89, /* error from a dbus service */ } virErrorNumber; /** diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 542424d..0128264 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1270,8 +1270,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..47648e9 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,966 @@ 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 decode 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, fmt) \ + 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; \ + } \ + VIR_DEBUG("Appended basic type '" #dbustype "' varg '" #vargtype \ + "' sig '%c' val '" fmt "'", sigtype, (vargtype)x); \ + } 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, "%d"); + break; + + case DBUS_TYPE_BOOLEAN: + SET_NEXT_VAL(dbus_bool_t, int, *t, "%d"); + break; + + case DBUS_TYPE_INT16: + SET_NEXT_VAL(dbus_int16_t, int, *t, "%d"); + break; + + case DBUS_TYPE_UINT16: + SET_NEXT_VAL(dbus_uint16_t, unsigned int, *t, "%d"); + break; + + case DBUS_TYPE_INT32: + SET_NEXT_VAL(dbus_int32_t, int, *t, "%d"); + break; + + case DBUS_TYPE_UINT32: + SET_NEXT_VAL(dbus_uint32_t, unsigned int, *t, "%u"); + break; + + case DBUS_TYPE_INT64: + SET_NEXT_VAL(dbus_int64_t, long long, *t, "%lld"); + break; + + case DBUS_TYPE_UINT64: + SET_NEXT_VAL(dbus_uint64_t, unsigned long long, *t, "%llu"); + break; + + case DBUS_TYPE_DOUBLE: + SET_NEXT_VAL(double, double, *t, "%lf"); + break; + + case DBUS_TYPE_STRING: + case DBUS_TYPE_OBJECT_PATH: + case DBUS_TYPE_SIGNATURE: + SET_NEXT_VAL(char *, char *, *t, "%s"); + 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, int); + 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, fmt) \ + do { \ + dbustype *x = (dbustype *)va_arg(args, vargtype *); \ + dbus_message_iter_get_basic(iter, x); \ + VIR_DEBUG("Read basic type '" #dbustype "' varg '" #vargtype \ + "' val '" fmt "'", (vargtype)*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, "%d"); + break; + + case DBUS_TYPE_BOOLEAN: + GET_NEXT_VAL(dbus_bool_t, int, "%d"); + break; + + case DBUS_TYPE_INT16: + GET_NEXT_VAL(dbus_int16_t, int, "%d"); + break; + + case DBUS_TYPE_UINT16: + GET_NEXT_VAL(dbus_uint16_t, unsigned int, "%d"); + break; + + case DBUS_TYPE_INT32: + GET_NEXT_VAL(dbus_uint32_t, int, "%d"); + break; + + case DBUS_TYPE_UINT32: + GET_NEXT_VAL(dbus_uint32_t, unsigned int, "%u"); + break; + + case DBUS_TYPE_INT64: + GET_NEXT_VAL(dbus_uint64_t, long long, "%lld"); + break; + + case DBUS_TYPE_UINT64: + GET_NEXT_VAL(dbus_uint64_t, unsigned long long, "%llu"); + break; + + case DBUS_TYPE_DOUBLE: + GET_NEXT_VAL(double, double, "%lf"); + 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, int); + 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; +} + +# define VIR_DBUS_METHOD_CALL_TIMEOUT_MILLIS 30 * 1000 + +/** + * virDBusCallMethod: + * @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' - NUL-terminated string, in UTF-8 + * 'o' - NUL-terminated string, representing a valid object path + * 'g' - NUL-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@xxxxxxxxx", "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, + VIR_DBUS_METHOD_CALL_TIMEOUT_MILLIS, + &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; + } + + if (dbus_set_error_from_message(&error, + reply)) { + virReportDBusServiceError(error.message ? error.message : "unknown error", + error.name); + 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 +1198,26 @@ DBusConnection *virDBusGetSessionBus(void) "%s", _("DBus support not compiled into this binary")); return NULL; } + +int virDBusCallMethod(DBusConnection *conn ATTRIBUTE_UNUSED, + DBusMessage **reply 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..c04fd10 100644 --- a/src/util/virdbus.h +++ b/src/util/virdbus.h @@ -27,10 +27,21 @@ # include <dbus/dbus.h> # else # define DBusConnection void +# define DBusMessage 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..a4ff655 --- /dev/null +++ b/src/util/virdbuspriv.h @@ -0,0 +1,45 @@ +/* + * 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" + +# include <stdarg.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/src/util/virerror.c b/src/util/virerror.c index ce3ab85..b8572da 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -1243,6 +1243,12 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("access denied: %s"); break; + case VIR_ERR_DBUS_SERVICE: + if (info == NULL) + errmsg = _("error from service"); + else + errmsg = _("error from service: %s"); + break; } return errmsg; } diff --git a/src/util/virerror.h b/src/util/virerror.h index 332a5eb..6ea456b 100644 --- a/src/util/virerror.h +++ b/src/util/virerror.h @@ -145,6 +145,17 @@ void virReportSystemErrorFull(int domcode, 0, 0, \ (fmt), __VA_ARGS__) +# define virReportDBusServiceError(message, name) \ + virRaiseErrorFull(__FILE__, __FUNCTION__, __LINE__, \ + VIR_FROM_THIS, \ + VIR_ERR_DBUS_SERVICE, \ + VIR_ERR_ERROR, \ + __FUNCTION__, \ + name, \ + NULL, \ + 0, 0, \ + "%s", message); + void virReportOOMErrorFull(int domcode, const char *filename, const char *funcname, diff --git a/tests/Makefile.am b/tests/Makefile.am index 4c49151..1748ed1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -128,6 +128,11 @@ test_programs = virshtest sockettest \ fchosttest \ $(NULL) +if WITH_DBUS +test_programs += virdbustest +endif + + if WITH_GNUTLS test_programs += virnettlscontexttest endif @@ -637,6 +642,14 @@ vircgroupmock_la_CFLAGS = $(AM_CFLAGS) vircgroupmock_la_LDFLAGS = -module -avoid-version \ -rpath /evil/libtool/hack/to/force/shared/lib/creation +if WITH_DBUS +virdbustest_SOURCES = \ + virdbustest.c testutils.h testutils.c +virdbustest_CFLAGS = $(AM_CFLAGS) $(DBUS_CFLAGS) +virdbustest_LDADD = $(LDADDS) +else +EXTRA_DIST += virdbustest.c +endif viruritest_SOURCES = \ viruritest.c testutils.h testutils.c diff --git a/tests/virdbustest.c b/tests/virdbustest.c new file mode 100644 index 0000000..9506f86 --- /dev/null +++ b/tests/virdbustest.c @@ -0,0 +1,393 @@ +/* + * 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@xxxxxxxxxx> + */ + +#include <config.h> + +#include <stdlib.h> + +#include "virdbuspriv.h" +#include "virlog.h" +#include "testutils.h" + +#define VERIFY(typname, valorig, valnew, fmt) \ + do { \ + VIR_DEBUG("Compare " typname " '" fmt "' to '" \ + fmt "'", valorig, valnew); \ + if (valorig != valnew) { \ + fprintf(stderr, "Failed to round-trip " typname " '" \ + fmt "' to '" fmt "'\n", valorig, valnew); \ + goto cleanup; \ + } \ + } while (0) + +#define VERIFY_STR(typname, valorig, valnew, fmt) \ + do { \ + VIR_DEBUG("Compare " typname " '" fmt "' to '" \ + fmt "'", valorig, valnew); \ + if (STRNEQ(valorig, valnew)) { \ + fprintf(stderr, "Failed to round-trip " typname " '" \ + fmt "' to '" fmt "'\n", valorig, valnew); \ + 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"; + int in_int32a = 1000000000, out_int32a = 0; + int in_int32b = 2000000000, out_int32b = 0; + int in_int32c = 3000000000, 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, + (long long)3, in_int32a, in_int32b, in_int32c, + in_str2) < 0) { + VIR_DEBUG("Failed to encode arguments"); + goto cleanup; + } + + if (virDBusMessageDecode(msg, + "sais", + &out_str1, + 3, &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"; + 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, + 3, + 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, + 3, + &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 -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :| -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list