2009-11-11 16:42:41 +00:00
|
|
|
/*
|
|
|
|
* QObject JSON integration
|
|
|
|
*
|
|
|
|
* Copyright IBM, Corp. 2009
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
|
|
|
* See the COPYING.LIB file in the top-level directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-01-29 17:50:01 +00:00
|
|
|
#include "qemu/osdep.h"
|
2017-02-28 21:26:54 +00:00
|
|
|
#include "qapi/error.h"
|
2018-08-23 16:40:20 +00:00
|
|
|
#include "qapi/qmp/json-parser.h"
|
2012-12-17 17:19:43 +00:00
|
|
|
#include "qapi/qmp/qjson.h"
|
2018-02-01 11:18:35 +00:00
|
|
|
#include "qapi/qmp/qbool.h"
|
2018-02-01 11:18:39 +00:00
|
|
|
#include "qapi/qmp/qdict.h"
|
2018-02-01 11:18:38 +00:00
|
|
|
#include "qapi/qmp/qlist.h"
|
2018-02-01 11:18:36 +00:00
|
|
|
#include "qapi/qmp/qnum.h"
|
2018-02-01 11:18:40 +00:00
|
|
|
#include "qapi/qmp/qstring.h"
|
2016-03-20 17:16:19 +00:00
|
|
|
#include "qemu/unicode.h"
|
2009-11-11 16:42:41 +00:00
|
|
|
|
|
|
|
typedef struct JSONParsingState
|
|
|
|
{
|
|
|
|
JSONMessageParser parser;
|
|
|
|
QObject *result;
|
2017-02-28 21:26:54 +00:00
|
|
|
Error *err;
|
2009-11-11 16:42:41 +00:00
|
|
|
} JSONParsingState;
|
|
|
|
|
2018-08-23 16:40:01 +00:00
|
|
|
static void consume_json(void *opaque, QObject *json, Error *err)
|
2009-11-11 16:42:41 +00:00
|
|
|
{
|
2018-08-23 16:40:01 +00:00
|
|
|
JSONParsingState *s = opaque;
|
2017-02-28 21:26:54 +00:00
|
|
|
|
qjson: Fix qobject_from_json() & friends for multiple values
qobject_from_json() & friends use the consume_json() callback to
receive either a value or an error from the parser.
When they are fed a string that contains more than either one JSON
value or one JSON syntax error, consume_json() gets called multiple
times.
When the last call receives a value, qobject_from_json() returns that
value. Any other values are leaked.
When any call receives an error, qobject_from_json() sets the first
error received. Any other errors are thrown away.
When values follow errors, qobject_from_json() returns both a value
and sets an error. That's bad. Impact:
* block.c's parse_json_protocol() ignores and leaks the value. It's
used to to parse pseudo-filenames starting with "json:". The
pseudo-filenames can come from the user or from image meta-data such
as a QCOW2 image's backing file name.
* vl.c's parse_display_qapi() ignores and leaks the error. It's used
to parse the argument of command line option -display.
* vl.c's main() case QEMU_OPTION_blockdev ignores the error and leaves
it in @err. main() will then pass a pointer to a non-null Error *
to net_init_clients(), which is forbidden. It can lead to assertion
failure or other misbehavior.
* check-qjson.c's multiple_values() demonstrates the badness.
* The other callers are not affected since they only pass strings with
exactly one JSON value or, in the case of negative tests, one
error.
The impact on the _nofail() functions is relatively harmless. They
abort when any call receives an error. Else they return the last
value, and leak the others, if any.
Fix consume_json() as follows. On the first call, save value and
error as before. On subsequent calls, if any, don't save them. If
the first call saved a value, the next call, if any, replaces the
value by an "Expecting at most one JSON value" error. Take care not
to leak values or errors that aren't saved.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180823164025.12553-44-armbru@redhat.com>
2018-08-23 16:40:10 +00:00
|
|
|
assert(!json != !err);
|
|
|
|
assert(!s->result || !s->err);
|
|
|
|
|
|
|
|
if (s->result) {
|
|
|
|
qobject_unref(s->result);
|
|
|
|
s->result = NULL;
|
|
|
|
error_setg(&s->err, "Expecting at most one JSON value");
|
|
|
|
}
|
|
|
|
if (s->err) {
|
|
|
|
qobject_unref(json);
|
|
|
|
error_free(err);
|
|
|
|
return;
|
|
|
|
}
|
2018-08-23 16:40:01 +00:00
|
|
|
s->result = json;
|
qjson: Fix qobject_from_json() & friends for multiple values
qobject_from_json() & friends use the consume_json() callback to
receive either a value or an error from the parser.
When they are fed a string that contains more than either one JSON
value or one JSON syntax error, consume_json() gets called multiple
times.
When the last call receives a value, qobject_from_json() returns that
value. Any other values are leaked.
When any call receives an error, qobject_from_json() sets the first
error received. Any other errors are thrown away.
When values follow errors, qobject_from_json() returns both a value
and sets an error. That's bad. Impact:
* block.c's parse_json_protocol() ignores and leaks the value. It's
used to to parse pseudo-filenames starting with "json:". The
pseudo-filenames can come from the user or from image meta-data such
as a QCOW2 image's backing file name.
* vl.c's parse_display_qapi() ignores and leaks the error. It's used
to parse the argument of command line option -display.
* vl.c's main() case QEMU_OPTION_blockdev ignores the error and leaves
it in @err. main() will then pass a pointer to a non-null Error *
to net_init_clients(), which is forbidden. It can lead to assertion
failure or other misbehavior.
* check-qjson.c's multiple_values() demonstrates the badness.
* The other callers are not affected since they only pass strings with
exactly one JSON value or, in the case of negative tests, one
error.
The impact on the _nofail() functions is relatively harmless. They
abort when any call receives an error. Else they return the last
value, and leak the others, if any.
Fix consume_json() as follows. On the first call, save value and
error as before. On subsequent calls, if any, don't save them. If
the first call saved a value, the next call, if any, replaces the
value by an "Expecting at most one JSON value" error. Take care not
to leak values or errors that aren't saved.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180823164025.12553-44-armbru@redhat.com>
2018-08-23 16:40:10 +00:00
|
|
|
s->err = err;
|
2009-11-11 16:42:41 +00:00
|
|
|
}
|
|
|
|
|
2018-08-06 06:53:31 +00:00
|
|
|
/*
|
|
|
|
* Parse @string as JSON value.
|
|
|
|
* If @ap is non-null, interpolate %-escapes.
|
|
|
|
* Takes ownership of %p arguments.
|
|
|
|
* On success, return the JSON value.
|
|
|
|
* On failure, store an error through @errp and return NULL.
|
|
|
|
* Ownership of %p arguments becomes indeterminate then. To avoid
|
|
|
|
* leaks, callers passing %p must terminate on error, e.g. by passing
|
|
|
|
* &error_abort.
|
|
|
|
*/
|
|
|
|
static QObject *qobject_from_jsonv(const char *string, va_list *ap,
|
|
|
|
Error **errp)
|
2009-11-11 16:42:41 +00:00
|
|
|
{
|
|
|
|
JSONParsingState state = {};
|
|
|
|
|
2018-08-23 16:40:01 +00:00
|
|
|
json_message_parser_init(&state.parser, consume_json, &state, ap);
|
2009-11-11 16:42:41 +00:00
|
|
|
json_message_parser_feed(&state.parser, string, strlen(string));
|
|
|
|
json_message_parser_flush(&state.parser);
|
|
|
|
json_message_parser_destroy(&state.parser);
|
|
|
|
|
2018-08-23 16:40:14 +00:00
|
|
|
if (!state.result && !state.err) {
|
|
|
|
error_setg(&state.err, "Expecting a JSON value");
|
|
|
|
}
|
|
|
|
|
2017-02-28 21:26:54 +00:00
|
|
|
error_propagate(errp, state.err);
|
2009-11-11 16:42:41 +00:00
|
|
|
return state.result;
|
|
|
|
}
|
|
|
|
|
2017-02-28 21:26:58 +00:00
|
|
|
QObject *qobject_from_json(const char *string, Error **errp)
|
2009-11-19 01:05:24 +00:00
|
|
|
{
|
2017-02-28 21:26:58 +00:00
|
|
|
return qobject_from_jsonv(string, NULL, errp);
|
2009-11-19 01:05:24 +00:00
|
|
|
}
|
|
|
|
|
2018-08-06 06:53:28 +00:00
|
|
|
/*
|
|
|
|
* Parse @string as JSON value with %-escapes interpolated.
|
|
|
|
* Abort on error. Do not use with untrusted @string.
|
|
|
|
* Return the resulting QObject. It is never null.
|
|
|
|
*/
|
|
|
|
QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap)
|
|
|
|
{
|
|
|
|
va_list ap_copy;
|
|
|
|
QObject *obj;
|
|
|
|
|
|
|
|
/* va_copy() is needed when va_list is an array type */
|
|
|
|
va_copy(ap_copy, ap);
|
|
|
|
obj = qobject_from_jsonv(string, &ap_copy, &error_abort);
|
|
|
|
va_end(ap_copy);
|
|
|
|
|
|
|
|
assert(obj);
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2018-08-06 06:53:27 +00:00
|
|
|
/*
|
|
|
|
* Parse @string as JSON value with %-escapes interpolated.
|
|
|
|
* Abort on error. Do not use with untrusted @string.
|
|
|
|
* Return the resulting QObject. It is never null.
|
|
|
|
*/
|
|
|
|
QObject *qobject_from_jsonf_nofail(const char *string, ...)
|
2009-11-11 16:42:41 +00:00
|
|
|
{
|
2009-11-19 01:05:24 +00:00
|
|
|
QObject *obj;
|
2009-11-11 16:42:41 +00:00
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, string);
|
2018-08-06 06:53:28 +00:00
|
|
|
obj = qobject_from_vjsonf_nofail(string, ap);
|
2009-11-11 16:42:41 +00:00
|
|
|
va_end(ap);
|
|
|
|
|
2009-11-19 01:05:24 +00:00
|
|
|
return obj;
|
2009-11-11 16:42:41 +00:00
|
|
|
}
|
2009-11-11 18:01:22 +00:00
|
|
|
|
2018-08-06 06:53:28 +00:00
|
|
|
/*
|
|
|
|
* Parse @string as JSON object with %-escapes interpolated.
|
|
|
|
* Abort on error. Do not use with untrusted @string.
|
|
|
|
* Return the resulting QDict. It is never null.
|
|
|
|
*/
|
|
|
|
QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap)
|
|
|
|
{
|
|
|
|
QDict *qdict;
|
|
|
|
|
|
|
|
qdict = qobject_to(QDict, qobject_from_vjsonf_nofail(string, ap));
|
|
|
|
assert(qdict);
|
|
|
|
return qdict;
|
|
|
|
}
|
|
|
|
|
2018-07-03 08:53:47 +00:00
|
|
|
/*
|
|
|
|
* Parse @string as JSON object with %-escapes interpolated.
|
|
|
|
* Abort on error. Do not use with untrusted @string.
|
|
|
|
* Return the resulting QDict. It is never null.
|
|
|
|
*/
|
|
|
|
QDict *qdict_from_jsonf_nofail(const char *string, ...)
|
|
|
|
{
|
2018-08-06 06:53:28 +00:00
|
|
|
QDict *qdict;
|
2018-07-03 08:53:47 +00:00
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, string);
|
2018-08-06 06:53:28 +00:00
|
|
|
qdict = qdict_from_vjsonf_nofail(string, ap);
|
2018-07-03 08:53:47 +00:00
|
|
|
va_end(ap);
|
2018-08-06 06:53:28 +00:00
|
|
|
return qdict;
|
2018-07-03 08:53:47 +00:00
|
|
|
}
|
|
|
|
|
Add support for JSON pretty printing
The monitor does not pretty-print JSON output, so that everything
will be on a single line reply. When JSON docs get large this is
quite unpleasant to read. For the future command line capabilities
query ability, huge JSON docs will be available. This needs the
ability to pretty-print.
This introduces a new API qobject_to_json_pretty() that does
a minimal indentation of list and dict members. As an example,
this makes
{"QMP": {"version": {"micro": 50, "minor": 12, "package": "", "major": 0}, "capabilities": []}}
Output as
{
"QMP": {
"version": {
"micro": 50,
"minor": 12,
"package": "",
"major": 0
},
"capabilities": [
]
}
}
NB: this is not turned on for the QMP monitor.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2010-06-07 14:42:14 +00:00
|
|
|
static void to_json(const QObject *obj, QString *str, int pretty, int indent);
|
2009-11-11 18:01:22 +00:00
|
|
|
|
2020-04-15 08:30:45 +00:00
|
|
|
static void json_pretty_newline(QString *str, bool pretty, int indent)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (pretty) {
|
|
|
|
qstring_append(str, "\n");
|
|
|
|
for (i = 0; i < indent; i++) {
|
|
|
|
qstring_append(str, " ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Add support for JSON pretty printing
The monitor does not pretty-print JSON output, so that everything
will be on a single line reply. When JSON docs get large this is
quite unpleasant to read. For the future command line capabilities
query ability, huge JSON docs will be available. This needs the
ability to pretty-print.
This introduces a new API qobject_to_json_pretty() that does
a minimal indentation of list and dict members. As an example,
this makes
{"QMP": {"version": {"micro": 50, "minor": 12, "package": "", "major": 0}, "capabilities": []}}
Output as
{
"QMP": {
"version": {
"micro": 50,
"minor": 12,
"package": "",
"major": 0
},
"capabilities": [
]
}
}
NB: this is not turned on for the QMP monitor.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2010-06-07 14:42:14 +00:00
|
|
|
static void to_json(const QObject *obj, QString *str, int pretty, int indent)
|
2009-11-11 18:01:22 +00:00
|
|
|
{
|
|
|
|
switch (qobject_type(obj)) {
|
2015-04-29 21:35:05 +00:00
|
|
|
case QTYPE_QNULL:
|
|
|
|
qstring_append(str, "null");
|
|
|
|
break;
|
2017-06-07 16:35:58 +00:00
|
|
|
case QTYPE_QNUM: {
|
2018-02-24 15:40:29 +00:00
|
|
|
QNum *val = qobject_to(QNum, obj);
|
2017-06-07 16:35:58 +00:00
|
|
|
char *buffer = qnum_to_string(val);
|
2009-11-11 18:01:22 +00:00
|
|
|
qstring_append(str, buffer);
|
2017-06-07 16:35:58 +00:00
|
|
|
g_free(buffer);
|
2009-11-11 18:01:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QTYPE_QSTRING: {
|
2018-02-24 15:40:29 +00:00
|
|
|
QString *val = qobject_to(QString, obj);
|
2009-11-11 18:01:22 +00:00
|
|
|
const char *ptr;
|
2013-04-11 16:07:21 +00:00
|
|
|
int cp;
|
|
|
|
char buf[16];
|
|
|
|
char *end;
|
2009-11-11 18:01:22 +00:00
|
|
|
|
|
|
|
ptr = qstring_get_str(val);
|
|
|
|
qstring_append(str, "\"");
|
2013-04-11 16:07:21 +00:00
|
|
|
|
|
|
|
for (; *ptr; ptr = end) {
|
|
|
|
cp = mod_utf8_codepoint(ptr, 6, &end);
|
|
|
|
switch (cp) {
|
|
|
|
case '\"':
|
|
|
|
qstring_append(str, "\\\"");
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
qstring_append(str, "\\\\");
|
|
|
|
break;
|
|
|
|
case '\b':
|
|
|
|
qstring_append(str, "\\b");
|
|
|
|
break;
|
|
|
|
case '\f':
|
|
|
|
qstring_append(str, "\\f");
|
|
|
|
break;
|
|
|
|
case '\n':
|
|
|
|
qstring_append(str, "\\n");
|
|
|
|
break;
|
|
|
|
case '\r':
|
|
|
|
qstring_append(str, "\\r");
|
|
|
|
break;
|
|
|
|
case '\t':
|
|
|
|
qstring_append(str, "\\t");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
if (cp < 0) {
|
|
|
|
cp = 0xFFFD; /* replacement character */
|
2009-11-11 18:01:22 +00:00
|
|
|
}
|
2013-04-11 16:07:21 +00:00
|
|
|
if (cp > 0xFFFF) {
|
|
|
|
/* beyond BMP; need a surrogate pair */
|
|
|
|
snprintf(buf, sizeof(buf), "\\u%04X\\u%04X",
|
|
|
|
0xD800 + ((cp - 0x10000) >> 10),
|
|
|
|
0xDC00 + ((cp - 0x10000) & 0x3FF));
|
|
|
|
} else if (cp < 0x20 || cp >= 0x7F) {
|
|
|
|
snprintf(buf, sizeof(buf), "\\u%04X", cp);
|
|
|
|
} else {
|
|
|
|
buf[0] = cp;
|
|
|
|
buf[1] = 0;
|
2009-11-11 18:01:22 +00:00
|
|
|
}
|
2013-04-11 16:07:21 +00:00
|
|
|
qstring_append(str, buf);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-11-11 18:01:22 +00:00
|
|
|
qstring_append(str, "\"");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QTYPE_QDICT: {
|
2018-02-24 15:40:29 +00:00
|
|
|
QDict *val = qobject_to(QDict, obj);
|
2020-04-15 08:30:47 +00:00
|
|
|
const char *comma = pretty ? "," : ", ";
|
|
|
|
const char *sep = "";
|
|
|
|
const QDictEntry *entry;
|
|
|
|
QString *qkey;
|
2009-11-11 18:01:22 +00:00
|
|
|
|
|
|
|
qstring_append(str, "{");
|
2020-04-15 08:30:47 +00:00
|
|
|
|
|
|
|
for (entry = qdict_first(val);
|
|
|
|
entry;
|
|
|
|
entry = qdict_next(val, entry)) {
|
|
|
|
qstring_append(str, sep);
|
|
|
|
json_pretty_newline(str, pretty, indent + 1);
|
|
|
|
|
|
|
|
qkey = qstring_from_str(qdict_entry_key(entry));
|
|
|
|
to_json(QOBJECT(qkey), str, pretty, indent + 1);
|
|
|
|
qobject_unref(qkey);
|
|
|
|
|
|
|
|
qstring_append(str, ": ");
|
|
|
|
to_json(qdict_entry_value(entry), str, pretty, indent + 1);
|
|
|
|
sep = comma;
|
|
|
|
}
|
|
|
|
|
2020-04-15 08:30:45 +00:00
|
|
|
json_pretty_newline(str, pretty, indent);
|
2009-11-11 18:01:22 +00:00
|
|
|
qstring_append(str, "}");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QTYPE_QLIST: {
|
2018-02-24 15:40:29 +00:00
|
|
|
QList *val = qobject_to(QList, obj);
|
2020-04-15 08:30:46 +00:00
|
|
|
const char *comma = pretty ? "," : ", ";
|
|
|
|
const char *sep = "";
|
|
|
|
QListEntry *entry;
|
2009-11-11 18:01:22 +00:00
|
|
|
|
|
|
|
qstring_append(str, "[");
|
2020-04-15 08:30:46 +00:00
|
|
|
|
|
|
|
QLIST_FOREACH_ENTRY(val, entry) {
|
|
|
|
qstring_append(str, sep);
|
|
|
|
json_pretty_newline(str, pretty, indent + 1);
|
|
|
|
to_json(qlist_entry_obj(entry), str, pretty, indent + 1);
|
|
|
|
sep = comma;
|
|
|
|
}
|
|
|
|
|
2020-04-15 08:30:45 +00:00
|
|
|
json_pretty_newline(str, pretty, indent);
|
2009-11-11 18:01:22 +00:00
|
|
|
qstring_append(str, "]");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case QTYPE_QBOOL: {
|
2018-02-24 15:40:29 +00:00
|
|
|
QBool *val = qobject_to(QBool, obj);
|
2009-11-11 18:01:22 +00:00
|
|
|
|
2015-05-15 22:24:59 +00:00
|
|
|
if (qbool_get_bool(val)) {
|
2009-11-11 18:01:22 +00:00
|
|
|
qstring_append(str, "true");
|
|
|
|
} else {
|
|
|
|
qstring_append(str, "false");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2015-04-29 21:35:04 +00:00
|
|
|
default:
|
2013-07-08 14:14:21 +00:00
|
|
|
abort();
|
2009-11-11 18:01:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QString *qobject_to_json(const QObject *obj)
|
|
|
|
{
|
|
|
|
QString *str = qstring_new();
|
|
|
|
|
Add support for JSON pretty printing
The monitor does not pretty-print JSON output, so that everything
will be on a single line reply. When JSON docs get large this is
quite unpleasant to read. For the future command line capabilities
query ability, huge JSON docs will be available. This needs the
ability to pretty-print.
This introduces a new API qobject_to_json_pretty() that does
a minimal indentation of list and dict members. As an example,
this makes
{"QMP": {"version": {"micro": 50, "minor": 12, "package": "", "major": 0}, "capabilities": []}}
Output as
{
"QMP": {
"version": {
"micro": 50,
"minor": 12,
"package": "",
"major": 0
},
"capabilities": [
]
}
}
NB: this is not turned on for the QMP monitor.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>
2010-06-07 14:42:14 +00:00
|
|
|
to_json(obj, str, 0, 0);
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString *qobject_to_json_pretty(const QObject *obj)
|
|
|
|
{
|
|
|
|
QString *str = qstring_new();
|
|
|
|
|
|
|
|
to_json(obj, str, 1, 0);
|
2009-11-11 18:01:22 +00:00
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|