This reverts commit bf114decb34f21cd225ead6dc4d929d35a8c5fe5.
Jansson cannot parse QEMU's quirky JSON.
Revert back to yajl.
https://bugzilla.redhat.com/show_bug.cgi?id=1614569
Signed-off-by: Ján Tomko <jtomko(a)redhat.com>
---
src/util/virjson.c | 529 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 528 insertions(+), 1 deletion(-)
diff --git a/src/util/virjson.c b/src/util/virjson.c
index 80274bc6c5..608ba85d67 100644
--- a/src/util/virjson.c
+++ b/src/util/virjson.c
@@ -29,6 +29,22 @@
#include "virstring.h"
#include "virutil.h"
+#if WITH_YAJL
+# include <yajl/yajl_gen.h>
+# include <yajl/yajl_parse.h>
+
+# ifdef WITH_YAJL2
+# define yajl_size_t size_t
+# define VIR_YAJL_STATUS_OK(status) ((status) == yajl_status_ok)
+# else
+# define yajl_size_t unsigned int
+# define yajl_complete_parse yajl_parse_complete
+# define VIR_YAJL_STATUS_OK(status) \
+ ((status) == yajl_status_ok || (status) == yajl_status_insufficient_data)
+# endif
+
+#endif
+
/* XXX fixme */
#define VIR_FROM_THIS VIR_FROM_NONE
@@ -72,6 +88,23 @@ struct _virJSONValue {
};
+typedef struct _virJSONParserState virJSONParserState;
+typedef virJSONParserState *virJSONParserStatePtr;
+struct _virJSONParserState {
+ virJSONValuePtr value;
+ char *key;
+};
+
+typedef struct _virJSONParser virJSONParser;
+typedef virJSONParser *virJSONParserPtr;
+struct _virJSONParser {
+ virJSONValuePtr head;
+ virJSONParserStatePtr state;
+ size_t nstate;
+ int wrap;
+};
+
+
virJSONType
virJSONValueGetType(const virJSONValue *value)
{
@@ -1458,7 +1491,501 @@ virJSONValueCopy(const virJSONValue *in)
}
-#if WITH_JANSSON
+#if WITH_YAJL
+static int
+virJSONParserInsertValue(virJSONParserPtr parser,
+ virJSONValuePtr value)
+{
+ if (!parser->head) {
+ parser->head = value;
+ } else {
+ virJSONParserStatePtr state;
+ if (!parser->nstate) {
+ VIR_DEBUG("got a value to insert without a container");
+ return -1;
+ }
+
+ state = &parser->state[parser->nstate-1];
+
+ switch (state->value->type) {
+ case VIR_JSON_TYPE_OBJECT: {
+ if (!state->key) {
+ VIR_DEBUG("missing key when inserting object value");
+ return -1;
+ }
+
+ if (virJSONValueObjectAppend(state->value,
+ state->key,
+ value) < 0)
+ return -1;
+
+ VIR_FREE(state->key);
+ } break;
+
+ case VIR_JSON_TYPE_ARRAY: {
+ if (state->key) {
+ VIR_DEBUG("unexpected key when inserting array value");
+ return -1;
+ }
+
+ if (virJSONValueArrayAppend(state->value,
+ value) < 0)
+ return -1;
+ } break;
+
+ default:
+ VIR_DEBUG("unexpected value type, not a container");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+virJSONParserHandleNull(void *ctx)
+{
+ virJSONParserPtr parser = ctx;
+ virJSONValuePtr value = virJSONValueNewNull();
+
+ VIR_DEBUG("parser=%p", parser);
+
+ if (!value)
+ return 0;
+
+ if (virJSONParserInsertValue(parser, value) < 0) {
+ virJSONValueFree(value);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int
+virJSONParserHandleBoolean(void *ctx,
+ int boolean_)
+{
+ virJSONParserPtr parser = ctx;
+ virJSONValuePtr value = virJSONValueNewBoolean(boolean_);
+
+ VIR_DEBUG("parser=%p boolean=%d", parser, boolean_);
+
+ if (!value)
+ return 0;
+
+ if (virJSONParserInsertValue(parser, value) < 0) {
+ virJSONValueFree(value);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int
+virJSONParserHandleNumber(void *ctx,
+ const char *s,
+ yajl_size_t l)
+{
+ virJSONParserPtr parser = ctx;
+ char *str;
+ virJSONValuePtr value;
+
+ if (VIR_STRNDUP(str, s, l) < 0)
+ return -1;
+ value = virJSONValueNewNumber(str);
+ VIR_FREE(str);
+
+ VIR_DEBUG("parser=%p str=%s", parser, str);
+
+ if (!value)
+ return 0;
+
+ if (virJSONParserInsertValue(parser, value) < 0) {
+ virJSONValueFree(value);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int
+virJSONParserHandleString(void *ctx,
+ const unsigned char *stringVal,
+ yajl_size_t stringLen)
+{
+ virJSONParserPtr parser = ctx;
+ virJSONValuePtr value = virJSONValueNewStringLen((const char *)stringVal,
+ stringLen);
+
+ VIR_DEBUG("parser=%p str=%p", parser, (const char *)stringVal);
+
+ if (!value)
+ return 0;
+
+ if (virJSONParserInsertValue(parser, value) < 0) {
+ virJSONValueFree(value);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static int
+virJSONParserHandleMapKey(void *ctx,
+ const unsigned char *stringVal,
+ yajl_size_t stringLen)
+{
+ virJSONParserPtr parser = ctx;
+ virJSONParserStatePtr state;
+
+ VIR_DEBUG("parser=%p key=%p", parser, (const char *)stringVal);
+
+ if (!parser->nstate)
+ return 0;
+
+ state = &parser->state[parser->nstate-1];
+ if (state->key)
+ return 0;
+ if (VIR_STRNDUP(state->key, (const char *)stringVal, stringLen) < 0)
+ return 0;
+ return 1;
+}
+
+
+static int
+virJSONParserHandleStartMap(void *ctx)
+{
+ virJSONParserPtr parser = ctx;
+ virJSONValuePtr value = virJSONValueNewObject();
+
+ VIR_DEBUG("parser=%p", parser);
+
+ if (!value)
+ return 0;
+
+ if (virJSONParserInsertValue(parser, value) < 0) {
+ virJSONValueFree(value);
+ return 0;
+ }
+
+ if (VIR_REALLOC_N(parser->state,
+ parser->nstate + 1) < 0) {
+ return 0;
+ }
+
+ parser->state[parser->nstate].value = value;
+ parser->state[parser->nstate].key = NULL;
+ parser->nstate++;
+
+ return 1;
+}
+
+
+static int
+virJSONParserHandleEndMap(void *ctx)
+{
+ virJSONParserPtr parser = ctx;
+ virJSONParserStatePtr state;
+
+ VIR_DEBUG("parser=%p", parser);
+
+ if (!parser->nstate)
+ return 0;
+
+ state = &(parser->state[parser->nstate-1]);
+ if (state->key) {
+ VIR_FREE(state->key);
+ return 0;
+ }
+
+ VIR_DELETE_ELEMENT(parser->state, parser->nstate - 1, parser->nstate);
+
+ return 1;
+}
+
+
+static int
+virJSONParserHandleStartArray(void *ctx)
+{
+ virJSONParserPtr parser = ctx;
+ virJSONValuePtr value = virJSONValueNewArray();
+
+ VIR_DEBUG("parser=%p", parser);
+
+ if (!value)
+ return 0;
+
+ if (virJSONParserInsertValue(parser, value) < 0) {
+ virJSONValueFree(value);
+ return 0;
+ }
+
+ if (VIR_REALLOC_N(parser->state,
+ parser->nstate + 1) < 0)
+ return 0;
+
+ parser->state[parser->nstate].value = value;
+ parser->state[parser->nstate].key = NULL;
+ parser->nstate++;
+
+ return 1;
+}
+
+
+static int
+virJSONParserHandleEndArray(void *ctx)
+{
+ virJSONParserPtr parser = ctx;
+ virJSONParserStatePtr state;
+
+ VIR_DEBUG("parser=%p", parser);
+
+ if (!(parser->nstate - parser->wrap))
+ return 0;
+
+ state = &(parser->state[parser->nstate-1]);
+ if (state->key) {
+ VIR_FREE(state->key);
+ return 0;
+ }
+
+ VIR_DELETE_ELEMENT(parser->state, parser->nstate - 1, parser->nstate);
+
+ return 1;
+}
+
+
+static const yajl_callbacks parserCallbacks = {
+ virJSONParserHandleNull,
+ virJSONParserHandleBoolean,
+ NULL,
+ NULL,
+ virJSONParserHandleNumber,
+ virJSONParserHandleString,
+ virJSONParserHandleStartMap,
+ virJSONParserHandleMapKey,
+ virJSONParserHandleEndMap,
+ virJSONParserHandleStartArray,
+ virJSONParserHandleEndArray
+};
+
+
+/* XXX add an incremental streaming parser - yajl trivially supports it */
+virJSONValuePtr
+virJSONValueFromString(const char *jsonstring)
+{
+ yajl_handle hand;
+ virJSONParser parser = { NULL, NULL, 0, 0 };
+ virJSONValuePtr ret = NULL;
+ int rc;
+ size_t len = strlen(jsonstring);
+# ifndef WITH_YAJL2
+ yajl_parser_config cfg = { 0, 1 }; /* Match yajl 2 default behavior */
+ VIR_AUTOPTR(virJSONValue) tmp = NULL;
+# endif
+
+ VIR_DEBUG("string=%s", jsonstring);
+
+# ifdef WITH_YAJL2
+ hand = yajl_alloc(&parserCallbacks, NULL, &parser);
+# else
+ hand = yajl_alloc(&parserCallbacks, &cfg, NULL, &parser);
+# endif
+ if (!hand) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to create JSON parser"));
+ goto cleanup;
+ }
+
+ /* Yajl 2 is nice enough to default to rejecting trailing garbage.
+ * Yajl 1.0.12 has yajl_get_bytes_consumed to make that detection
+ * simpler. But we're stuck with yajl 1.0.7 on RHEL 6, which
+ * happily quits parsing at the end of a valid JSON construct,
+ * with no visibility into how much more input remains. Wrapping
+ * things in an array forces yajl to confess the truth. */
+# ifdef WITH_YAJL2
+ rc = yajl_parse(hand, (const unsigned char *)jsonstring, len);
+# else
+ rc = yajl_parse(hand, (const unsigned char *)"[", 1);
+ parser.wrap = 1;
+ if (VIR_YAJL_STATUS_OK(rc))
+ rc = yajl_parse(hand, (const unsigned char *)jsonstring, len);
+ parser.wrap = 0;
+ if (VIR_YAJL_STATUS_OK(rc))
+ rc = yajl_parse(hand, (const unsigned char *)"]", 1);
+# endif
+ if (!VIR_YAJL_STATUS_OK(rc) ||
+ yajl_complete_parse(hand) != yajl_status_ok) {
+ unsigned char *errstr = yajl_get_error(hand, 1,
+ (const unsigned char*)jsonstring,
+ strlen(jsonstring));
+
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse json %s: %s"),
+ jsonstring, (const char*) errstr);
+ yajl_free_error(hand, errstr);
+ virJSONValueFree(parser.head);
+ goto cleanup;
+ }
+
+ if (parser.nstate != 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse json %s: unterminated
string/map/array"),
+ jsonstring);
+ virJSONValueFree(parser.head);
+ } else {
+ ret = parser.head;
+# ifndef WITH_YAJL2
+ /* Undo the array wrapping above */
+ tmp = ret;
+ ret = NULL;
+ if (virJSONValueArraySize(tmp) > 1)
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse json %s: too many items present"),
+ jsonstring);
+ else
+ ret = virJSONValueArraySteal(tmp, 0);
+# endif
+ }
+
+ cleanup:
+ yajl_free(hand);
+
+ if (parser.nstate) {
+ size_t i;
+ for (i = 0; i < parser.nstate; i++)
+ VIR_FREE(parser.state[i].key);
+ VIR_FREE(parser.state);
+ }
+
+ VIR_DEBUG("result=%p", ret);
+
+ return ret;
+}
+
+
+static int
+virJSONValueToStringOne(virJSONValuePtr object,
+ yajl_gen g)
+{
+ size_t i;
+
+ VIR_DEBUG("object=%p type=%d gen=%p", object, object->type, g);
+
+ switch (object->type) {
+ case VIR_JSON_TYPE_OBJECT:
+ if (yajl_gen_map_open(g) != yajl_gen_status_ok)
+ return -1;
+ for (i = 0; i < object->data.object.npairs; i++) {
+ if (yajl_gen_string(g,
+ (unsigned char *)object->data.object.pairs[i].key,
+ strlen(object->data.object.pairs[i].key))
+ != yajl_gen_status_ok)
+ return -1;
+ if (virJSONValueToStringOne(object->data.object.pairs[i].value, g) <
0)
+ return -1;
+ }
+ if (yajl_gen_map_close(g) != yajl_gen_status_ok)
+ return -1;
+ break;
+ case VIR_JSON_TYPE_ARRAY:
+ if (yajl_gen_array_open(g) != yajl_gen_status_ok)
+ return -1;
+ for (i = 0; i < object->data.array.nvalues; i++) {
+ if (virJSONValueToStringOne(object->data.array.values[i], g) < 0)
+ return -1;
+ }
+ if (yajl_gen_array_close(g) != yajl_gen_status_ok)
+ return -1;
+ break;
+
+ case VIR_JSON_TYPE_STRING:
+ if (yajl_gen_string(g, (unsigned char *)object->data.string,
+ strlen(object->data.string)) != yajl_gen_status_ok)
+ return -1;
+ break;
+
+ case VIR_JSON_TYPE_NUMBER:
+ if (yajl_gen_number(g, object->data.number,
+ strlen(object->data.number)) != yajl_gen_status_ok)
+ return -1;
+ break;
+
+ case VIR_JSON_TYPE_BOOLEAN:
+ if (yajl_gen_bool(g, object->data.boolean) != yajl_gen_status_ok)
+ return -1;
+ break;
+
+ case VIR_JSON_TYPE_NULL:
+ if (yajl_gen_null(g) != yajl_gen_status_ok)
+ return -1;
+ break;
+
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+char *
+virJSONValueToString(virJSONValuePtr object,
+ bool pretty)
+{
+ yajl_gen g;
+ const unsigned char *str;
+ char *ret = NULL;
+ yajl_size_t len;
+# ifndef WITH_YAJL2
+ yajl_gen_config conf = { pretty ? 1 : 0, pretty ? " " : " "};
+# endif
+
+ VIR_DEBUG("object=%p", object);
+
+# ifdef WITH_YAJL2
+ g = yajl_gen_alloc(NULL);
+ if (g) {
+ yajl_gen_config(g, yajl_gen_beautify, pretty ? 1 : 0);
+ yajl_gen_config(g, yajl_gen_indent_string, pretty ? " " : "
");
+ yajl_gen_config(g, yajl_gen_validate_utf8, 1);
+ }
+# else
+ g = yajl_gen_alloc(&conf, NULL);
+# endif
+ if (!g) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to create JSON formatter"));
+ goto cleanup;
+ }
+
+ if (virJSONValueToStringOne(object, g) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ if (yajl_gen_get_buf(g, &str, &len) != yajl_gen_status_ok) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ ignore_value(VIR_STRDUP(ret, (const char *)str));
+
+ cleanup:
+ yajl_gen_free(g);
+
+ VIR_DEBUG("result=%s", NULLSTR(ret));
+
+ return ret;
+}
+
+
+#elif WITH_JANSSON
# include <jansson.h>
static virJSONValuePtr
--
2.16.1