From 2c6460336687c475aacbed7685d231b9643402e3 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 13 Mar 2014 15:38:18 +0000 Subject: [PATCH] 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 --- 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 ebd7fc85eb..2c62301d4d 100644 --- a/src/util/virdbus.c +++ b/src/util/virdbus.c @@ -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'", diff --git a/tests/virdbustest.c b/tests/virdbustest.c index 9a6c4c60ef..2269c8d1bf 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) -- GitLab