On 10/14/2014 03:29 AM, Peter Krempa wrote:
Our qemu monitor code has a converter from key-value pairs to a json
value object. I want to re-use the code later and having it part of the
monitor command generator is inflexible. Split it out into a separate
helper.
---
src/libvirt_private.syms | 2 +
src/qemu/qemu_monitor_json.c | 161 +--------------------------------
src/util/virjson.c | 211 +++++++++++++++++++++++++++++++++++++++++++
src/util/virjson.h | 5 +
4 files changed, 220 insertions(+), 159 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index d6265ac..314b2b8 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1503,6 +1503,8 @@ virJSONValueObjectAppendNumberLong;
virJSONValueObjectAppendNumberUint;
virJSONValueObjectAppendNumberUlong;
virJSONValueObjectAppendString;
+virJSONValueObjectCreate;
+virJSONValueObjectCreateVArgs;
virJSONValueObjectGet;
virJSONValueObjectGetBoolean;
virJSONValueObjectGetKey;
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 7f23aae..2967193 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -431,7 +431,6 @@ qemuMonitorJSONMakeCommandRaw(bool wrap, const char *cmdname, ...)
virJSONValuePtr obj;
virJSONValuePtr jargs = NULL;
va_list args;
- char *key;
va_start(args, cmdname);
@@ -442,164 +441,8 @@ qemuMonitorJSONMakeCommandRaw(bool wrap, const char *cmdname, ...)
cmdname) < 0)
goto error;
- while ((key = va_arg(args, char *)) != NULL) {
- int ret;
- char type;
-
- if (strlen(key) < 3) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("argument key '%s' is too short, missing type
prefix"),
- key);
- goto error;
- }
-
- /* Keys look like s:name the first letter is a type code:
- * Explanation of type codes:
- * s: string value, must be non-null
- * S: string value, omitted if null
- *
- * i: signed integer value
- * j: signed integer value, error if negative
- * z: signed integer value, omitted if zero
- * y: signed integer value, omitted if zero, error if negative
- *
- * I: signed long integer value
- * J: signed long integer value, error if negative
- * Z: signed long integer value, omitted if zero
- * Y: signed long integer value, omitted if zero, error if negative
- *
- * u: unsigned integer value
- * p: unsigned integer value, omitted if zero
- *
- * U: unsigned long integer value (see below for quirks)
- * P: unsigned long integer value, omitted if zero
- *
- * b: boolean value
- * B: boolean value, omitted if false
- *
- * d: double precision floating point number
- * n: json null value
- * a: json array
- */
- type = key[0];
- key += 2;
-
- if (!jargs &&
- !(jargs = virJSONValueNewObject()))
- goto error;
-
- /* This doesn't support maps, but no command uses those. */
- switch (type) {
- case 'S':
- case 's': {
- char *val = va_arg(args, char *);
- if (!val) {
- if (type == 'S')
- continue;
-
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("argument key '%s' must not have null
value"),
- key);
- goto error;
- }
- ret = virJSONValueObjectAppendString(jargs, key, val);
- } break;
-
- case 'z':
- case 'y':
- case 'j':
- case 'i': {
- int val = va_arg(args, int);
-
- if (val < 0 && (type == 'j' || type == 'y')) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("argument key '%s' must not be
negative"),
- key);
- goto error;
- }
-
- if (!val && (type == 'z' || type == 'y'))
- continue;
-
- ret = virJSONValueObjectAppendNumberInt(jargs, key, val);
- } break;
-
- case 'p':
- case 'u': {
- unsigned int val = va_arg(args, unsigned int);
-
- if (!val && type == 'p')
- continue;
-
- ret = virJSONValueObjectAppendNumberUint(jargs, key, val);
- } break;
-
- case 'Z':
- case 'Y':
- case 'J':
- case 'I': {
- long long val = va_arg(args, long long);
-
- if (val < 0 && (type == 'J' || type == 'Y')) {
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("argument key '%s' must not be
negative"),
- key);
- goto error;
- }
-
- if (!val && (type == 'Z' || type == 'Y'))
- continue;
-
- ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
- } break;
-
- case 'P':
- case 'U': {
- /* qemu silently truncates numbers larger than LLONG_MAX,
- * so passing the full range of unsigned 64 bit integers
- * is not safe here. Pass them as signed 64 bit integers
- * instead.
- */
- long long val = va_arg(args, long long);
-
- if (!val && type == 'P')
- continue;
-
- ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
- } break;
-
- case 'd': {
- double val = va_arg(args, double);
- ret = virJSONValueObjectAppendNumberDouble(jargs, key, val);
- } break;
-
- case 'B':
- case 'b': {
- int val = va_arg(args, int);
-
- if (!val && type == 'B')
- continue;
-
- ret = virJSONValueObjectAppendBoolean(jargs, key, val);
- } break;
-
- case 'n': {
- ret = virJSONValueObjectAppendNull(jargs, key);
- } break;
-
- case 'a': {
- virJSONValuePtr val = va_arg(args, virJSONValuePtr);
- ret = virJSONValueObjectAppend(jargs, key, val);
- } break;
-
- default:
- virReportError(VIR_ERR_INTERNAL_ERROR,
- _("unsupported data type '%c' for arg
'%s'"), type, key - 2);
- goto error;
- }
- if (ret < 0)
- goto error;
- }
+ if (virJSONValueObjectCreateVArgs(&jargs, args) < 0)
+ goto error;
if (jargs &&
virJSONValueObjectAppend(obj, wrap ? "data" : "arguments",
jargs) < 0)
diff --git a/src/util/virjson.c b/src/util/virjson.c
index ec83b2f..0dfeaed 100644
--- a/src/util/virjson.c
+++ b/src/util/virjson.c
@@ -63,6 +63,217 @@ struct _virJSONParser {
};
+/**
+ * virJSONValueObjectCreateVArgs:
+ * @obj: returns the created JSON object
+ * @...: a key-value argument pairs, terminated by NULL
+ *
+ * Creates a JSON value object filled with key-value pairs supplied as variable
+ * argument list.
+ *
+ * Keys look like s:name the first letter is a type code:
+ * Explanation of type codes:
+ * s: string value, must be non-null
+ * S: string value, omitted if null
+ *
+ * i: signed integer value
+ * j: signed integer value, error if negative
+ * z: signed integer value, omitted if zero
+ * y: signed integer value, omitted if zero, error if negative
+ *
+ * I: signed long integer value
+ * J: signed long integer value, error if negative
+ * Z: signed long integer value, omitted if zero
+ * Y: signed long integer value, omitted if zero, error if negative
+ *
+ * u: unsigned integer value
+ * p: unsigned integer value, omitted if zero
+ *
+ * U: unsigned long integer value (see below for quirks)
+ * P: unsigned long integer value, omitted if zero
+ *
+ * b: boolean value
+ * B: boolean value, omitted if false
+ *
+ * d: double precision floating point number
+ * n: json null value
+ * a: json array
+ *
+ * The value corresponds to the selected type.
+ *
+ * Returns -1 on error. 1 on success, if at least one key:pair was valid 0
+ * in case of no error but nothing was filled (@obj will be NULL).
+ */
+int
+virJSONValueObjectCreateVArgs(virJSONValuePtr *obj, va_list args)
+{
+ virJSONValuePtr jargs = NULL;
+ char type;
+ char *key;
+ int ret = -1;
+ int rc = -2; /* -2 is a sentinel to check if at least one entry was added */
+
+ *obj = NULL;
+
+ if (!(jargs = virJSONValueNewObject()))
+ goto cleanup;
+
+ while ((key = va_arg(args, char *)) != NULL) {
+
+ if (strlen(key) < 3) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("argument key '%s' is too short, missing type
prefix"),
+ key);
+ goto cleanup;
+ }
+
+ type = key[0];
+ key += 2;
+
+ /* This doesn't support maps, but no command uses those. */
+ switch (type) {
+ case 'S':
+ case 's': {
+ char *val = va_arg(args, char *);
+ if (!val) {
+ if (type == 'S')
+ continue;
+
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("argument key '%s' must not have null
value"),
+ key);
+ goto cleanup;
+ }
+ rc = virJSONValueObjectAppendString(jargs, key, val);
+ } break;
+
+ case 'z':
+ case 'y':
+ case 'j':
+ case 'i': {
+ int val = va_arg(args, int);
+
+ if (val < 0 && (type == 'j' || type == 'y')) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("argument key '%s' must not be
negative"),
+ key);
+ goto cleanup;
+ }
+
+ if (!val && (type == 'z' || type == 'y'))
+ continue;
+
+ rc = virJSONValueObjectAppendNumberInt(jargs, key, val);
+ } break;
+
+ case 'p':
+ case 'u': {
+ unsigned int val = va_arg(args, unsigned int);
+
+ if (!val && type == 'p')
+ continue;
+
+ rc = virJSONValueObjectAppendNumberUint(jargs, key, val);
+ } break;
+
+ case 'Z':
+ case 'Y':
+ case 'J':
+ case 'I': {
+ long long val = va_arg(args, long long);
+
+ if (val < 0 && (type == 'J' || type == 'Y')) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("argument key '%s' must not be
negative"),
+ key);
+ goto cleanup;
+ }
+
+ if (!val && (type == 'Z' || type == 'Y'))
+ continue;
+
+ rc = virJSONValueObjectAppendNumberLong(jargs, key, val);
+ } break;
+
+ case 'P':
+ case 'U': {
+ /* qemu silently truncates numbers larger than LLONG_MAX,
+ * so passing the full range of unsigned 64 bit integers
+ * is not safe here. Pass them as signed 64 bit integers
+ * instead.
+ */
+ long long val = va_arg(args, long long);
+
+ if (!val && type == 'P')
+ continue;
+
+ rc = virJSONValueObjectAppendNumberLong(jargs, key, val);
+ } break;
+
+ case 'd': {
+ double val = va_arg(args, double);
+ rc = virJSONValueObjectAppendNumberDouble(jargs, key, val);
+ } break;
+
+ case 'B':
+ case 'b': {
+ int val = va_arg(args, int);
+
+ if (!val && type == 'B')
+ continue;
+
+ rc = virJSONValueObjectAppendBoolean(jargs, key, val);
+ } break;
+
+ case 'n': {
+ rc = virJSONValueObjectAppendNull(jargs, key);
+ } break;
+
+ case 'a': {
+ virJSONValuePtr val = va_arg(args, virJSONValuePtr);
+ rc = virJSONValueObjectAppend(jargs, key, val);
+ } break;
+
+ default:
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unsupported data type '%c' for arg
'%s'"), type, key - 2);
+ goto cleanup;
+ }
+
+ if (rc < 0)
+ goto cleanup;
+ }
+
+ /* verify that we added at least one key-value pair */
+ if (rc == -2) {
I didn't check any of the virJSAONValue* functions called, but if any
return -2 for whatever reason "sometime" in the future, then this logic
is flawed.
However, at this point you should have an array size/count right? And if
0, then we added something (eg, npairs count should be incremented)
thus virJSONValueObjectKeysNumber() should return > 0
+ ret = 0;
+ goto cleanup;
+ }
+
+ *obj = jargs;
+ jargs = NULL;
+ ret = 1;
+
+ cleanup:
+ virJSONValueFree(jargs);
+ return ret;
+}
+
+
+int
+virJSONValueObjectCreate(virJSONValuePtr *obj, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, obj);
+ ret = virJSONValueObjectCreateVArgs(obj, args);
+ va_end(args);
+
+ return ret;
+}
+
+
void
virJSONValueFree(virJSONValuePtr value)
{
diff --git a/src/util/virjson.h b/src/util/virjson.h
index 9487729..be603ae 100644
--- a/src/util/virjson.h
+++ b/src/util/virjson.h
@@ -26,6 +26,8 @@
# include "internal.h"
+# include <stdarg.h>
+
typedef enum {
VIR_JSON_TYPE_OBJECT,
@@ -79,6 +81,9 @@ struct _virJSONValue {
void virJSONValueFree(virJSONValuePtr value);
+int virJSONValueObjectCreate(virJSONValuePtr *obj, ...);
+int virJSONValueObjectCreateVArgs(virJSONValuePtr *obj, va_list args);
Add an ATTRIBUTE_NONNULL(1) for this definition, since you deref in
function.
ACK with adjustments
John
+
virJSONValuePtr virJSONValueNewString(const char *data);
virJSONValuePtr virJSONValueNewStringLen(const char *data, size_t length);
virJSONValuePtr virJSONValueNewNumberInt(int data);