Currently the DBus helper APIs require the values for an array
to be passed inline in the variadic argument list. This change
introduces support for passing arrays using a pointer to a plain
C array of the basic type. This is of particular benefit for
decoding messages when you don't know how many array elements
are being received.
Signed-off-by: Daniel P. Berrange <berrange(a)redhat.com>
---
src/util/virdbus.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++------
tests/virdbustest.c | 93 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 201 insertions(+), 13 deletions(-)
diff --git a/src/util/virdbus.c b/src/util/virdbus.c
index 8a978e5..d208646 100644
--- a/src/util/virdbus.c
+++ b/src/util/virdbus.c
@@ -456,7 +456,7 @@ static int virDBusTypeStackPush(virDBusTypeStack **stack,
(*stack)[(*nstack) - 1].types = types;
(*stack)[(*nstack) - 1].nstruct = nstruct;
(*stack)[(*nstack) - 1].narray = narray;
- VIR_DEBUG("Pushed '%s'", types);
+ VIR_DEBUG("Pushed types='%s' nstruct=%zu narray=%zu", types,
nstruct, narray);
return 0;
}
@@ -478,7 +478,7 @@ static int virDBusTypeStackPop(virDBusTypeStack **stack,
*types = (*stack)[(*nstack) - 1].types;
*nstruct = (*stack)[(*nstack) - 1].nstruct;
*narray = (*stack)[(*nstack) - 1].narray;
- VIR_DEBUG("Popped '%s'", *types);
+ VIR_DEBUG("Popped types='%s' nstruct=%zu narray=%zu", *types,
*nstruct, *narray);
VIR_SHRINK_N(*stack, *nstack, 1);
return 0;
@@ -501,16 +501,25 @@ static void virDBusTypeStackFree(virDBusTypeStack **stack,
# define SET_NEXT_VAL(dbustype, vargtype, sigtype, fmt) \
do { \
- dbustype x = (dbustype)va_arg(args, vargtype); \
+ dbustype x; \
+ if (arrayref) { \
+ vargtype *valarray = arrayptr; \
+ x = (dbustype)*valarray; \
+ valarray++; \
+ arrayptr = valarray; \
+ } else { \
+ 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); \
+ _("Cannot append basic type %s"), #vargtype);\
goto cleanup; \
} \
- VIR_DEBUG("Appended basic type '" #dbustype "' varg
'" #vargtype \
+ 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,
@@ -519,6 +528,8 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
int ret = -1;
size_t narray;
size_t nstruct;
+ bool arrayref = false;
+ void *arrayptr = NULL;
virDBusTypeStack *stack = NULL;
size_t nstack = 0;
size_t siglen;
@@ -544,6 +555,8 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
(narray == (size_t)-1 &&
nstruct == 0)) {
DBusMessageIter *thisiter = iter;
+ arrayref = false;
+ arrayptr = NULL;
VIR_DEBUG("Popping iter=%p", iter);
if (nstack == 0)
break;
@@ -616,12 +629,32 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
break;
case DBUS_TYPE_ARRAY:
+ arrayptr = NULL;
+ if (t[1] == '&') {
+ VIR_DEBUG("Got array ref");
+ t++;
+ types++;
+ nstruct--;
+ arrayref = true;
+ } else {
+ VIR_DEBUG("Got array non-ref");
+ arrayref = false;
+ }
+
if (virDBusSignatureLength(t + 1, &siglen) < 0)
goto cleanup;
if (VIR_STRNDUP(contsig, t + 1, siglen) < 0)
goto cleanup;
+ if (arrayref && (strlen(contsig) > 1 ||
+ !virDBusIsBasicType(*contsig))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Got array ref but '%s' is not a single
basic type"),
+ contsig);
+ goto cleanup;
+ }
+
if (narray == (size_t)-1) {
types += siglen;
nstruct -= siglen;
@@ -644,7 +677,9 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
newiter = NULL;
types = t + 1;
nstruct = siglen;
- narray = va_arg(args, int);
+ narray = (size_t)va_arg(args, int);
+ if (arrayref)
+ arrayptr = va_arg(args, void *);
break;
case DBUS_TYPE_VARIANT:
@@ -710,8 +745,9 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
- _("Unknown type in signature '%s'"),
- types);
+ _("Unknown type '%c' in signature
'%s'"),
+ *t, types);
+ goto cleanup;
}
}
@@ -739,7 +775,16 @@ cleanup:
# define GET_NEXT_VAL(dbustype, vargtype, fmt) \
do { \
- dbustype *x = (dbustype *)va_arg(args, vargtype *); \
+ dbustype *x; \
+ if (arrayref) { \
+ vargtype **xptrptr = arrayptr; \
+ if (VIR_EXPAND_N(*xptrptr, *narrayptr, 1) < 0) \
+ goto cleanup; \
+ x = (dbustype *)(*xptrptr + (*narrayptr - 1)); \
+ VIR_DEBUG("Expanded to %zu", *narrayptr); \
+ } else { \
+ 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);
\
@@ -754,6 +799,9 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
int ret = -1;
size_t narray;
size_t nstruct;
+ bool arrayref = false;
+ void *arrayptr = NULL;
+ size_t *narrayptr = 0;
virDBusTypeStack *stack = NULL;
size_t nstack = 0;
size_t siglen;
@@ -780,6 +828,8 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
(narray == (size_t)-1 &&
nstruct == 0)) {
DBusMessageIter *thisiter = iter;
+ arrayref = false;
+ arrayptr = NULL;
VIR_DEBUG("Popping iter=%p", iter);
if (nstack == 0)
break;
@@ -849,7 +899,16 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
case DBUS_TYPE_OBJECT_PATH:
case DBUS_TYPE_SIGNATURE:
do {
- char **x = (char **)va_arg(args, char **);
+ char **x;
+ if (arrayref) {
+ char ***xptrptr = arrayptr;
+ if (VIR_EXPAND_N(*xptrptr, *narrayptr, 1) < 0)
+ goto cleanup;
+ x = (char **)(*xptrptr + (*narrayptr - 1));
+ VIR_DEBUG("Expanded to %zu", *narrayptr);
+ } else {
+ x = (char **)va_arg(args, char **);
+ }
char *s;
dbus_message_iter_get_basic(iter, &s);
if (VIR_STRDUP(*x, s) < 0)
@@ -860,6 +919,18 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
break;
case DBUS_TYPE_ARRAY:
+ arrayptr = NULL;
+ if (t[1] == '&') {
+ VIR_DEBUG("Got array ref");
+ t++;
+ types++;
+ nstruct--;
+ arrayref = true;
+ } else {
+ VIR_DEBUG("Got array non-ref");
+ arrayref = false;
+ }
+
advanceiter = false;
if (virDBusSignatureLength(t + 1, &siglen) < 0)
goto cleanup;
@@ -867,6 +938,14 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
if (VIR_STRNDUP(contsig, t + 1, siglen) < 0)
goto cleanup;
+ if (arrayref && (strlen(contsig) > 1 ||
+ !virDBusIsBasicType(*contsig))) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Got array ref but '%s' is not a single
basic type"),
+ contsig);
+ goto cleanup;
+ }
+
if (narray == (size_t)-1) {
types += siglen;
nstruct -= siglen;
@@ -885,7 +964,14 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
newiter = NULL;
types = t + 1;
nstruct = siglen;
- narray = va_arg(args, int);
+ if (arrayref) {
+ narrayptr = va_arg(args, size_t *);
+ arrayptr = va_arg(args, void *);
+ *narrayptr = 0;
+ *(char **)arrayptr = NULL;
+ } else {
+ narray = va_arg(args, int);
+ }
break;
case DBUS_TYPE_VARIANT:
@@ -945,8 +1031,17 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
- _("Unknown type in signature '%s'"),
- types);
+ _("Unknown type '%c' in signature
'%s'"),
+ *t, types);
+ goto cleanup;
+ }
+
+ if (arrayref) {
+ if (*t == '&' ||
+ dbus_message_iter_has_next(iter))
+ narray = 1;
+ else
+ narray = 0;
}
VIR_DEBUG("After stack=%zu array=%zu struct=%zu type='%s'",
diff --git a/tests/virdbustest.c b/tests/virdbustest.c
index 9a6c4c6..2269c8d 100644
--- a/tests/virdbustest.c
+++ b/tests/virdbustest.c
@@ -228,6 +228,97 @@ cleanup:
return ret;
}
+static int testMessageArrayRef(const void *args ATTRIBUTE_UNUSED)
+{
+ DBusMessage *msg = NULL;
+ int ret = -1;
+ const char *in_str1 = "Hello";
+ int in_int32[] = {
+ 100000000, 2000000000, -2000000000
+ };
+ const char *in_strv1[] = {
+ "Fishfood",
+ };
+ const char *in_strv2[] = {
+ "Hello", "World",
+ };
+ int *out_int32 = NULL;
+ size_t out_nint32 = 0;
+ char **out_strv1 = NULL;
+ char **out_strv2 = NULL;
+ size_t out_nstrv1 = 0;
+ size_t out_nstrv2 = 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,
+ "sa&sa&ia&ss",
+ in_str1,
+ 1, in_strv1,
+ 3, in_int32,
+ 2, in_strv2,
+ in_str2) < 0) {
+ VIR_DEBUG("Failed to encode arguments");
+ goto cleanup;
+ }
+
+ if (virDBusMessageDecode(msg,
+ "sa&sa&ia&ss",
+ &out_str1,
+ &out_nstrv1, &out_strv1,
+ &out_nint32, &out_int32,
+ &out_nstrv2, &out_strv2,
+ &out_str2) < 0) {
+ VIR_DEBUG("Failed to decode arguments");
+ goto cleanup;
+ }
+
+
+ VERIFY_STR("str1", in_str1, out_str1, "%s");
+ if (out_nstrv1 != 1) {
+ fprintf(stderr, "Expected 1 string, but got %zu\n",
+ out_nstrv1);
+ goto cleanup;
+ }
+ VERIFY_STR("strv1[0]", in_strv1[0], out_strv1[0], "%s");
+
+ if (out_nint32 != 3) {
+ fprintf(stderr, "Expected 3 integers, but got %zu\n",
+ out_nint32);
+ goto cleanup;
+ }
+ VERIFY("int32a", in_int32[0], out_int32[0], "%d");
+ VERIFY("int32b", in_int32[1], out_int32[1], "%d");
+ VERIFY("int32c", in_int32[2], out_int32[2], "%d");
+
+ if (out_nstrv2 != 2) {
+ fprintf(stderr, "Expected 2 strings, but got %zu\n",
+ out_nstrv2);
+ goto cleanup;
+ }
+ VERIFY_STR("strv2[0]", in_strv2[0], out_strv2[0], "%s");
+ VERIFY_STR("strv2[1]", in_strv2[1], out_strv2[1], "%s");
+
+ VERIFY_STR("str2", in_str2, out_str2, "%s");
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(out_int32);
+ 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;
@@ -385,6 +476,8 @@ mymain(void)
ret = -1;
if (virtTestRun("Test message array ", testMessageArray, NULL) < 0)
ret = -1;
+ if (virtTestRun("Test message array ref ", testMessageArrayRef, NULL) <
0)
+ ret = -1;
if (virtTestRun("Test message struct ", testMessageStruct, NULL) < 0)
ret = -1;
if (virtTestRun("Test message dict ", testMessageDict, NULL) < 0)
--
1.8.5.3