[libvirt] [PATCH 6/7] Support passing dict by reference for dbus messages

Daniel P. Berrange berrange at redhat.com
Wed Sep 10 14:20:59 UTC 2014


Currently DBus dict values must be passed inline

   virDBusMessageEncode("a{ss}",
                        3,
                        "key1", "val1",
                        "key2", "val2",
                        "key3", "val3");
   virDBusMessageDecode("a{ss}",
                        3,
                        &key1, &val1,
                        &key2, &val2,
                        &key3, &val3);

This allows them to be passed by reference

   const char **dictin = {
      "key1", "val1",
      "key2", "val2",
      "key3", "val3"
   };
   char **dictout;
   size_t ndictout;

   virDBusMessageEncode("a&{ss}",
                        ARRAY_CARDINALITY(dict) / 2,
                        dictin);
   virDBusMessageDecode("a&{ss}",
                        &ndictout,
                        &dictout);

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 src/util/virdbus.c   | 274 +++++++++++++++++++++++++++++++++++----------------
 src/util/virstring.c |  14 +++
 src/util/virstring.h |   2 +
 tests/virdbustest.c  | 218 +++++++++++++++++++++++++++++++++++++++-
 4 files changed, 423 insertions(+), 85 deletions(-)

diff --git a/src/util/virdbus.c b/src/util/virdbus.c
index a63338a..dc3a535 100644
--- a/src/util/virdbus.c
+++ b/src/util/virdbus.c
@@ -313,15 +313,18 @@ virDBusSignatureLengthInternal(const char *s,
                                bool allowDict,
                                unsigned arrayDepth,
                                unsigned structDepth,
-                               size_t *l)
+                               size_t *skiplen,
+                               size_t *siglen)
 {
     if (virDBusIsBasicType(*s) || *s == DBUS_TYPE_VARIANT) {
-        *l = 1;
+        *skiplen = *siglen = 1;
         return 0;
     }
 
     if (*s == DBUS_TYPE_ARRAY) {
-        size_t t;
+        size_t skiplencont;
+        size_t siglencont;
+        bool arrayref = false;
 
         if (arrayDepth >= VIR_DBUS_TYPE_STACK_MAX_DEPTH) {
             virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -330,14 +333,23 @@ virDBusSignatureLengthInternal(const char *s,
             return -1;
         }
 
+        if (*(s + 1) == '&') {
+            arrayref = true;
+            s++;
+        }
+
         if (virDBusSignatureLengthInternal(s + 1,
                                            true,
                                            arrayDepth + 1,
                                            structDepth,
-                                           &t) < 0)
+                                           &skiplencont,
+                                           &siglencont) < 0)
             return -1;
 
-        *l = t + 1;
+        *skiplen = skiplencont + 1;
+        *siglen = siglencont + 1;
+        if (arrayref)
+            (*skiplen)++;
         return 0;
     }
 
@@ -351,20 +363,25 @@ virDBusSignatureLengthInternal(const char *s,
             return -1;
         }
 
+        *skiplen = *siglen = 2;
+
         while (*p != DBUS_STRUCT_END_CHAR) {
-            size_t t;
+            size_t skiplencont;
+            size_t siglencont;
 
             if (virDBusSignatureLengthInternal(p,
                                                false,
                                                arrayDepth,
                                                structDepth + 1,
-                                               &t) < 0)
+                                               &skiplencont,
+                                               &siglencont) < 0)
                 return -1;
 
-            p += t;
+            p += skiplencont;
+            *skiplen += skiplencont;
+            *siglen += siglencont;
         }
 
-        *l = p - s + 1;
         return 0;
     }
 
@@ -378,8 +395,11 @@ virDBusSignatureLengthInternal(const char *s,
             return -1;
         }
 
+        *skiplen = *siglen = 2;
+
         while (*p != DBUS_DICT_ENTRY_END_CHAR) {
-            size_t t;
+            size_t skiplencont;
+            size_t siglencont;
 
             if (n == 0 && !virDBusIsBasicType(*p)) {
                 virReportError(VIR_ERR_INTERNAL_ERROR,
@@ -392,10 +412,13 @@ virDBusSignatureLengthInternal(const char *s,
                                                false,
                                                arrayDepth,
                                                structDepth + 1,
-                                               &t) < 0)
+                                               &skiplencont,
+                                               &siglencont) < 0)
                 return -1;
 
-            p += t;
+            p += skiplencont;
+            *skiplen += skiplencont;
+            *siglen += siglencont;
             n++;
         }
 
@@ -406,7 +429,6 @@ virDBusSignatureLengthInternal(const char *s,
             return -1;
         }
 
-        *l = p - s + 1;
         return 0;
     }
 
@@ -416,12 +438,40 @@ virDBusSignatureLengthInternal(const char *s,
 }
 
 
-static int virDBusSignatureLength(const char *s, size_t *l)
+static int virDBusSignatureLength(const char *s, size_t *skiplen, size_t *siglen)
 {
-    return virDBusSignatureLengthInternal(s, true, 0, 0, l);
+    return virDBusSignatureLengthInternal(s, true, 0, 0, skiplen, siglen);
 }
 
 
+static char *virDBusCopyContainerSignature(const char *sig,
+                                           size_t *skiplen,
+                                           size_t *siglen)
+{
+    size_t i, j;
+    char *contsig;
+    bool isGroup;
+
+    isGroup = (sig[0] == DBUS_STRUCT_BEGIN_CHAR ||
+               sig[0] == DBUS_DICT_ENTRY_BEGIN_CHAR);
+
+    if (virDBusSignatureLength(isGroup ? sig : sig + 1, skiplen, siglen) < 0)
+        return NULL;
+
+    if (VIR_ALLOC_N(contsig, *siglen + 1) < 0)
+        return NULL;
+
+    for (i = 0, j = 0; i < *skiplen && j < *siglen; i++) {
+        if (sig[i + 1] == '&')
+            continue;
+        contsig[j] = sig[i + 1];
+        j++;
+    }
+    contsig[*siglen] = '\0';
+    VIR_DEBUG("Extracted '%s' from '%s'", contsig, sig);
+    return contsig;
+}
+
 
 /* Ideally, we'd just call ourselves recursively on every
  * complex type. However, the state of a va_list that is
@@ -458,7 +508,8 @@ static int virDBusTypeStackPush(virDBusTypeStack **stack,
     (*stack)[(*nstack) - 1].types = types;
     (*stack)[(*nstack) - 1].nstruct = nstruct;
     (*stack)[(*nstack) - 1].narray = narray;
-    VIR_DEBUG("Pushed types='%s' nstruct=%zu narray=%zu", types, nstruct, narray);
+    VIR_DEBUG("Pushed types='%s' nstruct=%zu narray=%zd",
+              types, nstruct, (ssize_t)narray);
     return 0;
 }
 
@@ -480,7 +531,8 @@ static int virDBusTypeStackPop(virDBusTypeStack **stack,
     *types = (*stack)[(*nstack) - 1].types;
     *nstruct = (*stack)[(*nstack) - 1].nstruct;
     *narray = (*stack)[(*nstack) - 1].narray;
-    VIR_DEBUG("Popped types='%s' nstruct=%zu narray=%zu", *types, *nstruct, *narray);
+    VIR_DEBUG("Popped types='%s' nstruct=%zu narray=%zd",
+              *types, *nstruct, (ssize_t)*narray);
     VIR_SHRINK_N(*stack, *nstack, 1);
 
     return 0;
@@ -501,6 +553,28 @@ static void virDBusTypeStackFree(virDBusTypeStack **stack,
 }
 
 
+static bool
+virDBusIsAllowedRefType(const char *sig)
+{
+    if (*sig == '{') {
+        if (strlen(sig) != 4)
+            return false;
+        if (!virDBusIsBasicType(sig[1]) ||
+            !virDBusIsBasicType(sig[2]) ||
+            sig[1] != sig[2])
+            return false;
+        if (sig[3] != '}')
+            return false;
+    } else {
+        if (strlen(sig) != 1)
+            return false;
+        if (!virDBusIsBasicType(sig[0]))
+            return false;
+    }
+    return true;
+}
+
+
 # define SET_NEXT_VAL(dbustype, vargtype, sigtype, fmt)                 \
     do {                                                                \
         dbustype x;                                                     \
@@ -535,6 +609,7 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
     virDBusTypeStack *stack = NULL;
     size_t nstack = 0;
     size_t siglen;
+    size_t skiplen;
     char *contsig = NULL;
     const char *vsig;
     DBusMessageIter *newiter = NULL;
@@ -551,14 +626,17 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
     for (;;) {
         const char *t;
 
-        VIR_DEBUG("Loop stack=%zu array=%zu struct=%zu type='%s'",
-                  nstack, narray, nstruct, types);
+        VIR_DEBUG("Loop nstack=%zu narray=%zd nstruct=%zu types='%s'",
+                  nstack, (ssize_t)narray, nstruct, types);
         if (narray == 0 ||
             (narray == (size_t)-1 &&
              nstruct == 0)) {
             DBusMessageIter *thisiter = iter;
-            arrayref = false;
-            arrayptr = NULL;
+            if (*types != '}') {
+                VIR_DEBUG("Reset array ref");
+                arrayref = false;
+                arrayptr = NULL;
+            }
             VIR_DEBUG("Popping iter=%p", iter);
             if (nstack == 0)
                 break;
@@ -643,28 +721,25 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
                 arrayref = false;
             }
 
-            if (virDBusSignatureLength(t + 1, &siglen) < 0)
+            if (!(contsig = virDBusCopyContainerSignature(t, &skiplen, &siglen)))
                 goto cleanup;
 
-            if (VIR_STRNDUP(contsig, t + 1, siglen) < 0)
-                goto cleanup;
-
-            if (arrayref && (strlen(contsig) > 1 ||
-                             !virDBusIsBasicType(*contsig))) {
+            if (arrayref && !virDBusIsAllowedRefType(contsig)) {
                 virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("Got array ref but '%s' is not a single basic type"),
+                               _("Got array ref but '%s' is not a single basic type "
+                                 "or dict with matching key+value type"),
                                contsig);
                 goto cleanup;
             }
 
             if (narray == (size_t)-1) {
-                types += siglen;
-                nstruct -= siglen;
+                types += skiplen;
+                nstruct -= skiplen;
             }
 
             if (VIR_ALLOC(newiter) < 0)
                 goto cleanup;
-            VIR_DEBUG("Contsig '%s' '%zu'", contsig, siglen);
+            VIR_DEBUG("Contsig '%s' skip='%zu' len='%zu'", contsig, skiplen, siglen);
             if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
                                                   contsig, newiter))
                 goto cleanup;
@@ -678,7 +753,7 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
             iter = newiter;
             newiter = NULL;
             types = t + 1;
-            nstruct = siglen;
+            nstruct = skiplen;
             narray = (size_t)va_arg(args, int);
             if (arrayref)
                 arrayptr = va_arg(args, void *);
@@ -711,23 +786,20 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
 
         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)
+            if (!(contsig = virDBusCopyContainerSignature(t, &skiplen, &siglen)))
                 goto cleanup;
 
             if (VIR_ALLOC(newiter) < 0)
                 goto cleanup;
-            VIR_DEBUG("Contsig '%s' '%zu'", contsig, siglen);
+            VIR_DEBUG("Contsig '%s' skip='%zu' len='%zu'", contsig, skiplen, 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;
+                types += skiplen - 1;
+                nstruct -= skiplen - 1;
             }
 
             if (virDBusTypeStackPush(&stack, &nstack,
@@ -740,15 +812,15 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
             iter = newiter;
             newiter = NULL;
             types = t + 1;
-            nstruct = siglen - 2;
+            nstruct = skiplen - 2;
             narray = (size_t)-1;
 
             break;
 
         default:
             virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Unknown type '%c' in signature '%s'"),
-                           *t, types);
+                           _("Unknown type '%x' in signature '%s'"),
+                           (int)*t, types);
             goto cleanup;
         }
     }
@@ -779,6 +851,7 @@ virDBusMessageIterEncode(DBusMessageIter *rootiter,
     do {                                                                \
         dbustype *x;                                                    \
         if (arrayref) {                                                 \
+            VIR_DEBUG("Use arrayref");                                  \
             vargtype **xptrptr = arrayptr;                              \
             if (VIR_EXPAND_N(*xptrptr, *narrayptr, 1) < 0)              \
                 goto cleanup;                                           \
@@ -806,6 +879,7 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
     size_t *narrayptr = 0;
     virDBusTypeStack *stack = NULL;
     size_t nstack = 0;
+    size_t skiplen;
     size_t siglen;
     char *contsig = NULL;
     const char *vsig;
@@ -824,14 +898,12 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
         const char *t;
         bool advanceiter = true;
 
-        VIR_DEBUG("Loop stack=%zu array=%zu struct=%zu type='%s'",
-                  nstack, narray, nstruct, types);
+        VIR_DEBUG("Loop nstack=%zu narray=%zd nstruct=%zu type='%s'",
+                  nstack, (ssize_t)narray, nstruct, types);
         if (narray == 0 ||
             (narray == (size_t)-1 &&
              nstruct == 0)) {
             DBusMessageIter *thisiter = iter;
-            arrayref = false;
-            arrayptr = NULL;
             VIR_DEBUG("Popping iter=%p", iter);
             if (nstack == 0)
                 break;
@@ -839,8 +911,20 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
                                     &types, &nstruct, &narray) < 0)
                 goto cleanup;
             VIR_DEBUG("Popped iter=%p types=%s", iter, types);
+            if (strchr(types, '}') == NULL) {
+                arrayref = false;
+                arrayptr = NULL;
+                VIR_DEBUG("Clear array ref flag");
+            }
             if (thisiter != rootiter)
                 VIR_FREE(thisiter);
+            if (arrayref) {
+                if (!dbus_message_iter_has_next(iter))
+                    narray = 0;
+                else
+                    narray = 1;
+                VIR_DEBUG("Pop set narray=%zd", (ssize_t)narray);
+            }
             if (!(narray == 0 ||
                   (narray == (size_t)-1 &&
                    nstruct == 0)) &&
@@ -854,7 +938,8 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
 
         t = types;
         if (narray != (size_t)-1) {
-            narray--;
+            if (!arrayref)
+                narray--;
         } else {
             types++;
             nstruct--;
@@ -934,28 +1019,25 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
             }
 
             advanceiter = false;
-            if (virDBusSignatureLength(t + 1, &siglen) < 0)
-                goto cleanup;
-
-            if (VIR_STRNDUP(contsig, t + 1, siglen) < 0)
+            if (!(contsig = virDBusCopyContainerSignature(t, &skiplen, &siglen)))
                 goto cleanup;
 
-            if (arrayref && (strlen(contsig) > 1 ||
-                             !virDBusIsBasicType(*contsig))) {
+            if (arrayref && !virDBusIsAllowedRefType(contsig)) {
                 virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("Got array ref but '%s' is not a single basic type"),
+                               _("Got array ref but '%s' is not a single basic type / dict"),
                                contsig);
                 goto cleanup;
             }
 
             if (narray == (size_t)-1) {
-                types += siglen;
-                nstruct -= siglen;
+                types += skiplen;
+                nstruct -= skiplen;
             }
 
             if (VIR_ALLOC(newiter) < 0)
                 goto cleanup;
-            VIR_DEBUG("Contsig '%s' '%zu' '%s'", contsig, siglen, types);
+            VIR_DEBUG("Array contsig='%s' skip=%'zu' len='%zu' types='%s'",
+                      contsig, skiplen, siglen, types);
             dbus_message_iter_recurse(iter, newiter);
             if (virDBusTypeStackPush(&stack, &nstack,
                                      iter, types,
@@ -965,7 +1047,7 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
             iter = newiter;
             newiter = NULL;
             types = t + 1;
-            nstruct = siglen;
+            nstruct = skiplen;
             if (arrayref) {
                 narrayptr = va_arg(args, size_t *);
                 arrayptr = va_arg(args, void *);
@@ -1003,19 +1085,17 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
         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)
+            if (!(contsig = virDBusCopyContainerSignature(t, &skiplen, &siglen)))
                 goto cleanup;
 
             if (VIR_ALLOC(newiter) < 0)
                 goto cleanup;
-            VIR_DEBUG("Contsig '%s' '%zu'", contsig, siglen);
+            VIR_DEBUG("Dict/struct contsig='%s' skip='%zu' len='%zu' types='%s'",
+                      contsig, skiplen, siglen, types);
             dbus_message_iter_recurse(iter, newiter);
             if (narray == (size_t)-1) {
-                types += siglen - 1;
-                nstruct -= siglen - 1;
+                types += skiplen - 1;
+                nstruct -= skiplen - 1;
             }
 
             if (virDBusTypeStackPush(&stack, &nstack,
@@ -1026,7 +1106,7 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
             iter = newiter;
             newiter = NULL;
             types = t + 1;
-            nstruct = siglen - 2;
+            nstruct = skiplen - 2;
             narray = (size_t)-1;
 
             break;
@@ -1038,24 +1118,32 @@ virDBusMessageIterDecode(DBusMessageIter *rootiter,
             goto cleanup;
         }
 
+        VIR_DEBUG("After nstack=%zu narray=%zd nstruct=%zu types='%s'",
+                  nstack, (ssize_t)narray, nstruct, types);
+
         if (arrayref) {
-            if (*t == '&' ||
-                dbus_message_iter_has_next(iter))
-                narray = 1;
-            else
+            if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_INVALID) {
                 narray = 0;
-        }
-
-        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;
+            } else {
+                if (advanceiter)
+                    dbus_message_iter_next(iter);
+                if (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_INVALID) {
+                    narray = 0;
+                } else {
+                    narray = 1;
+                }
+            }
+            VIR_DEBUG("Set narray=%zd", (ssize_t)narray);
+        } else {
+            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;
+            }
         }
     }
 
@@ -1217,7 +1305,27 @@ int virDBusMessageDecode(DBusMessage* msg,
  *
  *   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
+ *   This is then followed by additional variadic args,
+ *   one for each element of the array.
+ *
+ * - Array reference: when 'a' appears in a type signature,
+ *   followed by '&', this signifies an array passed by
+ *   reference.
+ *
+ *   Array references may only be used when the
+ *   element values are basic types, or a dict
+ *   entry where both keys and values are using
+ *   the same basic type.
+ *
+ *   The first variadic arg for an array, is an 'int'
+ *   specifying the number of elements in the array.
+ *   When the element is a basic type, the second
+ *   variadic arg is a pointer to an array containing
+ *   the element values. When the element is a dict
+ *   entry, the second variadic arg is a pointer to
+ *   an array containing the dict keys, and the
+ *   third variadic arg is a pointer to an array
+ *   containing the dict values.
  *
  * - Struct: when a '(' appears in a type signature,
  *   it must be followed by one or more types describing
diff --git a/src/util/virstring.c b/src/util/virstring.c
index b14f785..43eab91 100644
--- a/src/util/virstring.c
+++ b/src/util/virstring.c
@@ -205,6 +205,20 @@ virStringFreeListCount(char **strings,
 }
 
 
+size_t virStringListLen(const char **strings)
+{
+    size_t i = 0;
+
+    if (!strings)
+        return 0;
+
+    while (strings[i] != NULL)
+        i++;
+
+    return i;
+}
+
+
 bool
 virStringArrayHasString(char **strings, const char *needle)
 {
diff --git a/src/util/virstring.h b/src/util/virstring.h
index 267fbd0..b82ef2a 100644
--- a/src/util/virstring.h
+++ b/src/util/virstring.h
@@ -44,6 +44,8 @@ char *virStringJoin(const char **strings,
 void virStringFreeList(char **strings);
 void virStringFreeListCount(char **strings, size_t count);
 
+size_t virStringListLen(const char **strings);
+
 bool virStringArrayHasString(char **strings, const char *needle);
 
 char *virArgvToString(const char *const *argv);
diff --git a/tests/virdbustest.c b/tests/virdbustest.c
index 0079b41..002f904 100644
--- a/tests/virdbustest.c
+++ b/tests/virdbustest.c
@@ -228,6 +228,99 @@ static int testMessageArray(const void *args ATTRIBUTE_UNUSED)
     return ret;
 }
 
+static int testMessageEmptyArrayRef(const void *args ATTRIBUTE_UNUSED)
+{
+    DBusMessage *msg = NULL;
+    int ret = -1;
+    const char *in_strv1[] = {};
+    size_t out_nstrv1;
+    char **out_strv1 = 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,
+                             "a&s",
+                             0, in_strv1) < 0) {
+        VIR_DEBUG("Failed to encode arguments");
+        goto cleanup;
+    }
+
+    if (virDBusMessageDecode(msg,
+                             "a&s",
+                             &out_nstrv1, &out_strv1) < 0) {
+        VIR_DEBUG("Failed to decode arguments");
+        goto cleanup;
+    }
+
+
+    if (out_nstrv1 != 0) {
+        fprintf(stderr, "Expected 0 string, but got %zu\n",
+                out_nstrv1);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    dbus_message_unref(msg);
+    return ret;
+}
+
+static int testMessageSingleArrayRef(const void *args ATTRIBUTE_UNUSED)
+{
+    DBusMessage *msg = NULL;
+    int ret = -1;
+    const char *in_strv1[] = {
+        "Fishfood",
+    };
+    char **out_strv1 = NULL;
+    size_t out_nstrv1 = 0;
+
+    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,
+                             "a&s",
+                             1, in_strv1) < 0) {
+        VIR_DEBUG("Failed to encode arguments");
+        goto cleanup;
+    }
+
+    if (virDBusMessageDecode(msg,
+                             "a&s",
+                             &out_nstrv1, &out_strv1) < 0) {
+        VIR_DEBUG("Failed to decode arguments");
+        goto cleanup;
+    }
+
+
+    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");
+
+    ret = 0;
+
+ cleanup:
+    if (out_strv1)
+        VIR_FREE(out_strv1[0]);
+    dbus_message_unref(msg);
+    return ret;
+}
+
 static int testMessageArrayRef(const void *args ATTRIBUTE_UNUSED)
 {
     DBusMessage *msg = NULL;
@@ -426,7 +519,7 @@ static int testMessageDict(const void *args ATTRIBUTE_UNUSED)
     }
 
     if (virDBusMessageEncode(msg,
-                             "sa{si}s",
+                             "(sa{si}s)",
                              in_str1,
                              3,
                              in_key1, in_int32a,
@@ -438,7 +531,7 @@ static int testMessageDict(const void *args ATTRIBUTE_UNUSED)
     }
 
     if (virDBusMessageDecode(msg,
-                             "sa{si}s",
+                             "(sa{si}s)",
                              &out_str1,
                              3,
                              &out_key1, &out_int32a,
@@ -471,6 +564,119 @@ static int testMessageDict(const void *args ATTRIBUTE_UNUSED)
     return ret;
 }
 
+static int testMessageDictRef(const void *args ATTRIBUTE_UNUSED)
+{
+    DBusMessage *msg = NULL;
+    int ret = -1;
+    const char *in_str1 = "Hello";
+    const char *in_strv1[] = {
+        "Fruit1", "Apple",
+        "Fruit2", "Orange",
+        "Fruit3", "Kiwi",
+    };
+    const char *in_str2 = "World";
+    char *out_str1 = NULL;
+    size_t out_nint32 = 0;
+    char **out_strv1 = NULL;
+    char *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&{ss}s)",
+                             in_str1,
+                             3, in_strv1,
+                             in_str2) < 0) {
+        VIR_DEBUG("Failed to encode arguments");
+        goto cleanup;
+    }
+
+    if (virDBusMessageDecode(msg,
+                             "(sa&{ss}s)",
+                             &out_str1,
+                             &out_nint32,
+                             &out_strv1,
+                             &out_str2) < 0) {
+        VIR_DEBUG("Failed to decode arguments: '%s'", virGetLastErrorMessage());
+        goto cleanup;
+    }
+
+
+    VERIFY_STR("str1", in_str1, out_str1, "%s");
+    VERIFY_STR("strv1[0]", in_strv1[0], out_strv1[0], "%s");
+    VERIFY_STR("strv1[1]", in_strv1[1], out_strv1[1], "%s");
+    VERIFY_STR("strv1[2]", in_strv1[2], out_strv1[2], "%s");
+    VERIFY_STR("strv1[3]", in_strv1[3], out_strv1[3], "%s");
+    VERIFY_STR("strv1[4]", in_strv1[4], out_strv1[4], "%s");
+    VERIFY_STR("strv1[5]", in_strv1[5], out_strv1[5], "%s");
+    VERIFY_STR("str2", in_str2, out_str2, "%s");
+
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(out_str1);
+    VIR_FREE(out_str2);
+    if (out_strv1) {
+        VIR_FREE(out_strv1[0]);
+        VIR_FREE(out_strv1[1]);
+        VIR_FREE(out_strv1[2]);
+        VIR_FREE(out_strv1[3]);
+        VIR_FREE(out_strv1[4]);
+        VIR_FREE(out_strv1[5]);
+    }
+    VIR_FREE(out_strv1);
+    dbus_message_unref(msg);
+    return ret;
+}
+
+static int testMessageEmptyDictRef(const void *args ATTRIBUTE_UNUSED)
+{
+    DBusMessage *msg = NULL;
+    int ret = -1;
+    const char *in_strv1[] = {};
+    size_t out_nint32 = 0;
+    char **out_strv1 = 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,
+                             "a&{ss}",
+                             0, in_strv1) < 0) {
+        VIR_DEBUG("Failed to encode arguments");
+        goto cleanup;
+    }
+
+    if (virDBusMessageDecode(msg,
+                             "a&{ss}",
+                             &out_nint32,
+                             &out_strv1) < 0) {
+        VIR_DEBUG("Failed to decode arguments: '%s'", virGetLastErrorMessage());
+        goto cleanup;
+    }
+
+    if (out_nint32 != 0) {
+        fprintf(stderr, "Unexpected dict entries\n");
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    dbus_message_unref(msg);
+    return ret;
+}
 
 static int
 mymain(void)
@@ -483,12 +689,20 @@ mymain(void)
         ret = -1;
     if (virtTestRun("Test message array ", testMessageArray, NULL) < 0)
         ret = -1;
+    if (virtTestRun("Test message array empty ref ", testMessageEmptyArrayRef, NULL) < 0)
+        ret = -1;
+    if (virtTestRun("Test message array single ref ", testMessageSingleArrayRef, 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)
         ret = -1;
+    if (virtTestRun("Test message dict empty ref ", testMessageEmptyDictRef, NULL) < 0)
+        ret = -1;
+    if (virtTestRun("Test message dict ref ", testMessageDictRef, NULL) < 0)
+        ret = -1;
     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 
-- 
1.9.3




More information about the libvir-list mailing list