Introduce alternate way to encode/decode arrays in DBus messages

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@redhat.com>
This commit is contained in:
Daniel P. Berrange 2014-03-13 15:38:18 +00:00
parent 217ac43e03
commit 2c64603366
2 changed files with 201 additions and 13 deletions

View File

@ -458,7 +458,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;
}
@ -480,7 +480,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;
@ -503,16 +503,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,
@ -521,6 +530,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;
@ -546,6 +557,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;
@ -618,12 +631,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;
@ -646,7 +679,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:
@ -712,8 +747,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;
}
}
@ -741,7 +777,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); \
@ -756,6 +801,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;
@ -782,6 +830,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;
@ -851,7 +901,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)
@ -862,6 +921,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;
@ -869,6 +940,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;
@ -887,7 +966,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:
@ -947,8 +1033,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'",

View File

@ -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)