[libvirt] [PATCH 5/6] Introduce alternate way to encode/decode arrays in DBus messages

Daniel P. Berrange berrange at redhat.com
Thu Mar 20 12:28:44 UTC 2014


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 at 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




More information about the libvir-list mailing list