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@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list