QAPI patches for 2017-02-28
-----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJYu8qfAAoJEDhwtADrkYZT5U0P/3FWhZDhXal/X3DHazFQzUku g6jgJ82n6SGoTKYCjwEVJkB6COHDHenMEDdtkA2+tlF77R8QWhihQwTcWG65zpNo qRq2wIza9ZuLiT7zryJpcjcCNeLuj+iwG2/tf6MdP/9p4pp31afh+0TjwblZd30K 25TeqNzqPPr7PvX6XeH0lBZ3ceQEp7q6GngUzg8mwvQtQox3lxlopVS7Q3nWxOZP 1AY5RIt6AE8EJKCU5tfcQS4RU82JbWJJB5AbepQk6y7Cvab07qGGsGTK7smLhuu8 KIsV/OTRgJl9KlRgjO+qZIc2lX0M/mXe2qmQpkd9+7/0cEjxqmyfsE1M1XKMC4Cd 86Z7GcGfdtN0U/FDXm/22Rmo6IbxvdvKiZV+YxQJDuW/1KnqlelsrCwHMRhS9VSa wv9mrpQLHEx3E7gdda8703E5ulfAZ+Qh7eD9Nv5DfLbN9+kNo5b72tqXkzx1kpyN tveTuKgGwTGh8FWMfyW/yICuSuROJKgLRr4SMHbEOZ9L+mgIoW8aHKZPAROLn5bc wqIB6vwGYAwLEZv5w61/ZWdlvSxwdQynComwg0h6LvkeBAJSjfdCjnyhy7gTXV0R X/1q7fkOXGzDYl/Als74mV+OkMaMhhb+iL+IXhlE4Mv712xsgWObaHXdRB9AcFIq QteZv/e3RaXq8vtdm1/r =7Na7 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-02-28' into staging QAPI patches for 2017-02-28 # gpg: Signature made Sun 05 Mar 2017 08:21:51 GMT # gpg: using RSA key 0x3870B400EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" # Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653 * remotes/armbru/tags/pull-qapi-2017-02-28: (27 commits) qapi: Improve qobject visitor documentation qapi: Fix object input visit beyond end of list tests: Cover input visit beyond end of list qapi: Make input visitors detect unvisited list tails test-qobject-input-visitor: Cover missing nested struct member tests: Cover partial input visit of list test-string-input-visitor: Improve list coverage test-string-input-visitor: Tear down existing test automatically tests-qobject-input-strict: Merge into test-qobject-input-visitor qapi: Drop unused non-strict qobject input visitor test-qobject-input-visitor: Use strict visitor qom: Make object_property_set_qobject()'s input visitor strict qapi: Make string input and opts visitor require non-null input qapi: Drop string input visitor method optional() qapi: Improve qobject input visitor error reporting qapi: Make QObject input visitor set *list reliably qapi: Clean up after commit 3d344c2 qapi: Improve a QObject input visitor error message qmp: Eliminate silly QERR_QMP_* macros qmp: Drop duplicated QMP command object checks ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
fbddc2e560
@ -1404,6 +1404,7 @@ F: qmp.c
|
|||||||
F: monitor.c
|
F: monitor.c
|
||||||
F: docs/*qmp-*
|
F: docs/*qmp-*
|
||||||
F: scripts/qmp/
|
F: scripts/qmp/
|
||||||
|
F: tests/qmp-test.c
|
||||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||||
|
|
||||||
Register API
|
Register API
|
||||||
|
@ -278,7 +278,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, Error **errp)
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
iv = qobject_input_visitor_new(crumpled_addr, true);
|
iv = qobject_input_visitor_new(crumpled_addr);
|
||||||
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
|
visit_type_SocketAddress(iv, NULL, &saddr, &local_err);
|
||||||
if (local_err) {
|
if (local_err) {
|
||||||
error_propagate(errp, local_err);
|
error_propagate(errp, local_err);
|
||||||
|
@ -474,7 +474,7 @@ static NFSServer *nfs_config(QDict *options, Error **errp)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
iv = qobject_input_visitor_new(crumpled_addr, true);
|
iv = qobject_input_visitor_new(crumpled_addr);
|
||||||
visit_type_NFSServer(iv, NULL, &server, &local_error);
|
visit_type_NFSServer(iv, NULL, &server, &local_error);
|
||||||
if (local_error) {
|
if (local_error) {
|
||||||
error_propagate(errp, local_error);
|
error_propagate(errp, local_error);
|
||||||
|
@ -601,7 +601,7 @@ static InetSocketAddress *ssh_config(QDict *options, Error **errp)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
iv = qobject_input_visitor_new(crumpled_addr, true);
|
iv = qobject_input_visitor_new(crumpled_addr);
|
||||||
visit_type_InetSocketAddress(iv, NULL, &inet, &local_error);
|
visit_type_InetSocketAddress(iv, NULL, &inet, &local_error);
|
||||||
if (local_error) {
|
if (local_error) {
|
||||||
error_propagate(errp, local_error);
|
error_propagate(errp, local_error);
|
||||||
|
@ -1138,7 +1138,7 @@ Example:
|
|||||||
Visitor *v;
|
Visitor *v;
|
||||||
UserDefOneList *arg1 = NULL;
|
UserDefOneList *arg1 = NULL;
|
||||||
|
|
||||||
v = qobject_input_visitor_new(QOBJECT(args), true);
|
v = qobject_input_visitor_new(QOBJECT(args));
|
||||||
visit_start_struct(v, NULL, NULL, 0, &err);
|
visit_start_struct(v, NULL, NULL, 0, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -326,7 +326,12 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
visit_check_list(v, &err);
|
||||||
visit_end_list(v, NULL);
|
visit_end_list(v, NULL);
|
||||||
|
if (err) {
|
||||||
|
error_propagate(errp, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -16,6 +16,7 @@ extern Monitor *cur_mon;
|
|||||||
|
|
||||||
bool monitor_cur_is_qmp(void);
|
bool monitor_cur_is_qmp(void);
|
||||||
|
|
||||||
|
void monitor_init_qmp_commands(void);
|
||||||
void monitor_init(Chardev *chr, int flags);
|
void monitor_init(Chardev *chr, int flags);
|
||||||
void monitor_cleanup(void);
|
void monitor_cleanup(void);
|
||||||
|
|
||||||
|
@ -34,18 +34,24 @@ typedef struct QmpCommand
|
|||||||
bool enabled;
|
bool enabled;
|
||||||
} QmpCommand;
|
} QmpCommand;
|
||||||
|
|
||||||
void qmp_register_command(const char *name, QmpCommandFunc *fn,
|
typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList;
|
||||||
QmpCommandOptions options);
|
|
||||||
void qmp_unregister_command(const char *name);
|
void qmp_register_command(QmpCommandList *cmds, const char *name,
|
||||||
QmpCommand *qmp_find_command(const char *name);
|
QmpCommandFunc *fn, QmpCommandOptions options);
|
||||||
QObject *qmp_dispatch(QObject *request);
|
void qmp_unregister_command(QmpCommandList *cmds, const char *name);
|
||||||
void qmp_disable_command(const char *name);
|
QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name);
|
||||||
void qmp_enable_command(const char *name);
|
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request);
|
||||||
|
void qmp_disable_command(QmpCommandList *cmds, const char *name);
|
||||||
|
void qmp_enable_command(QmpCommandList *cmds, const char *name);
|
||||||
|
|
||||||
bool qmp_command_is_enabled(const QmpCommand *cmd);
|
bool qmp_command_is_enabled(const QmpCommand *cmd);
|
||||||
const char *qmp_command_name(const QmpCommand *cmd);
|
const char *qmp_command_name(const QmpCommand *cmd);
|
||||||
bool qmp_has_success_response(const QmpCommand *cmd);
|
bool qmp_has_success_response(const QmpCommand *cmd);
|
||||||
QObject *qmp_build_error_object(Error *err);
|
QObject *qmp_build_error_object(Error *err);
|
||||||
|
|
||||||
typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque);
|
typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque);
|
||||||
void qmp_for_each_command(qmp_cmd_callback_fn fn, void *opaque);
|
|
||||||
|
void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn,
|
||||||
|
void *opaque);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -82,15 +82,6 @@
|
|||||||
#define QERR_QGA_COMMAND_FAILED \
|
#define QERR_QGA_COMMAND_FAILED \
|
||||||
"Guest agent command failed, error was '%s'"
|
"Guest agent command failed, error was '%s'"
|
||||||
|
|
||||||
#define QERR_QMP_BAD_INPUT_OBJECT \
|
|
||||||
"Expected '%s' in QMP input"
|
|
||||||
|
|
||||||
#define QERR_QMP_BAD_INPUT_OBJECT_MEMBER \
|
|
||||||
"QMP input object member '%s' expects '%s'"
|
|
||||||
|
|
||||||
#define QERR_QMP_EXTRA_MEMBER \
|
|
||||||
"QMP input object member '%s' is unexpected"
|
|
||||||
|
|
||||||
#define QERR_SET_PASSWD_FAILED \
|
#define QERR_SET_PASSWD_FAILED \
|
||||||
"Could not set password"
|
"Could not set password"
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Input Visitor
|
* Input Visitor
|
||||||
*
|
*
|
||||||
|
* Copyright (C) 2017 Red Hat, Inc.
|
||||||
* Copyright IBM, Corp. 2011
|
* Copyright IBM, Corp. 2011
|
||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
@ -20,11 +21,42 @@
|
|||||||
typedef struct QObjectInputVisitor QObjectInputVisitor;
|
typedef struct QObjectInputVisitor QObjectInputVisitor;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return a new input visitor that converts a QObject to a QAPI object.
|
* Create a QObject input visitor for @obj
|
||||||
*
|
*
|
||||||
* Set @strict to reject a parse that doesn't consume all keys of a
|
* A QObject input visitor visit builds a QAPI object from a QObject.
|
||||||
* dictionary; otherwise excess input is ignored.
|
* This simultaneously walks the QAPI object being built and the
|
||||||
|
* QObject. The latter walk starts at @obj.
|
||||||
|
*
|
||||||
|
* visit_type_FOO() creates an instance of QAPI type FOO. The visited
|
||||||
|
* QObject must match FOO. QDict matches struct/union types, QList
|
||||||
|
* matches list types, QString matches type 'str' and enumeration
|
||||||
|
* types, QInt matches integer types, QFloat matches type 'number',
|
||||||
|
* QBool matches type 'bool'. Type 'any' is matched by QObject. A
|
||||||
|
* QAPI alternate type is matched when one of its member types is.
|
||||||
|
*
|
||||||
|
* visit_start_struct() ... visit_end_struct() visits a QDict and
|
||||||
|
* creates a QAPI struct/union. Visits in between visit the
|
||||||
|
* dictionary members. visit_optional() is true when the QDict has
|
||||||
|
* this member. visit_check_struct() fails if unvisited members
|
||||||
|
* remain.
|
||||||
|
*
|
||||||
|
* visit_start_list() ... visit_end_list() visits a QList and creates
|
||||||
|
* a QAPI list. Visits in between visit list members, one after the
|
||||||
|
* other. visit_next_list() returns NULL when all QList members have
|
||||||
|
* been visited. visit_check_list() fails if unvisited members
|
||||||
|
* remain.
|
||||||
|
*
|
||||||
|
* visit_start_alternate() ... visit_end_alternate() visits a QObject
|
||||||
|
* and creates a QAPI alternate. The visit in between visits the same
|
||||||
|
* QObject and initializes the alternate member that is in use.
|
||||||
|
*
|
||||||
|
* Error messages refer to parts of @obj in JavaScript/Python syntax.
|
||||||
|
* For example, 'a.b[2]' refers to the second member of the QList
|
||||||
|
* member 'b' of the QDict member 'a' of QDict @obj.
|
||||||
|
*
|
||||||
|
* The caller is responsible for freeing the visitor with
|
||||||
|
* visit_free().
|
||||||
*/
|
*/
|
||||||
Visitor *qobject_input_visitor_new(QObject *obj, bool strict);
|
Visitor *qobject_input_visitor_new(QObject *obj);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -19,11 +19,38 @@
|
|||||||
|
|
||||||
typedef struct QObjectOutputVisitor QObjectOutputVisitor;
|
typedef struct QObjectOutputVisitor QObjectOutputVisitor;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Create a new QObject output visitor.
|
* Create a QObject output visitor for @obj
|
||||||
*
|
*
|
||||||
* If everything else succeeds, pass @result to visit_complete() to
|
* A QObject output visitor visit builds a QObject from QAPI Object.
|
||||||
* collect the result of the visit.
|
* This simultaneously walks the QAPI object and the QObject being
|
||||||
|
* built. The latter walk starts at @obj.
|
||||||
|
*
|
||||||
|
* visit_type_FOO() creates a QObject for QAPI type FOO. It creates a
|
||||||
|
* QDict for struct/union types, a QList for list types, QString for
|
||||||
|
* type 'str' and enumeration types, QInt for integer types, QFloat
|
||||||
|
* for type 'number', QBool for type 'bool'. For type 'any', it
|
||||||
|
* increments the QObject's reference count. For QAPI alternate
|
||||||
|
* types, it creates the QObject for the member that is in use.
|
||||||
|
*
|
||||||
|
* visit_start_struct() ... visit_end_struct() visits a QAPI
|
||||||
|
* struct/union and creates a QDict. Visits in between visit the
|
||||||
|
* members. visit_optional() is true when the struct/union has this
|
||||||
|
* member. visit_check_struct() does nothing.
|
||||||
|
*
|
||||||
|
* visit_start_list() ... visit_end_list() visits a QAPI list and
|
||||||
|
* creates a QList. Visits in between visit list members, one after
|
||||||
|
* the other. visit_next_list() returns NULL when all QAPI list
|
||||||
|
* members have been visited. visit_check_list() does nothing.
|
||||||
|
*
|
||||||
|
* visit_start_alternate() ... visit_end_alternate() visits a QAPI
|
||||||
|
* alternate. The visit in between creates the QObject for the
|
||||||
|
* alternate member that is in use.
|
||||||
|
*
|
||||||
|
* Errors are not expected to happen.
|
||||||
|
*
|
||||||
|
* The caller is responsible for freeing the visitor with
|
||||||
|
* visit_free().
|
||||||
*/
|
*/
|
||||||
Visitor *qobject_output_visitor_new(QObject **result);
|
Visitor *qobject_output_visitor_new(QObject **result);
|
||||||
|
|
||||||
|
@ -61,6 +61,9 @@ struct Visitor
|
|||||||
/* Must be set */
|
/* Must be set */
|
||||||
GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size);
|
GenericList *(*next_list)(Visitor *v, GenericList *tail, size_t size);
|
||||||
|
|
||||||
|
/* Optional; intended for input visitors */
|
||||||
|
void (*check_list)(Visitor *v, Error **errp);
|
||||||
|
|
||||||
/* Must be set */
|
/* Must be set */
|
||||||
void (*end_list)(Visitor *v, void **list);
|
void (*end_list)(Visitor *v, void **list);
|
||||||
|
|
||||||
@ -102,8 +105,8 @@ struct Visitor
|
|||||||
/* Must be set to visit explicit null values. */
|
/* Must be set to visit explicit null values. */
|
||||||
void (*type_null)(Visitor *v, const char *name, Error **errp);
|
void (*type_null)(Visitor *v, const char *name, Error **errp);
|
||||||
|
|
||||||
/* Must be set for input visitors, optional otherwise. The core
|
/* Must be set for input visitors to visit structs, optional otherwise.
|
||||||
* takes care of the return type in the public interface. */
|
The core takes care of the return type in the public interface. */
|
||||||
void (*optional)(Visitor *v, const char *name, bool *present);
|
void (*optional)(Visitor *v, const char *name, bool *present);
|
||||||
|
|
||||||
/* Must be set */
|
/* Must be set */
|
||||||
|
@ -66,12 +66,6 @@
|
|||||||
* object, @name is the key associated with the value; and when
|
* object, @name is the key associated with the value; and when
|
||||||
* visiting a member of a list, @name is NULL.
|
* visiting a member of a list, @name is NULL.
|
||||||
*
|
*
|
||||||
* FIXME: Clients must pass NULL for @name when visiting a member of a
|
|
||||||
* list, but this leads to poor error messages; it might be nicer to
|
|
||||||
* require a non-NULL name such as "key.0" for '{ "key": [ "value" ]
|
|
||||||
* }' if an error is encountered on "value" (or to have the visitor
|
|
||||||
* core auto-generate the nicer name).
|
|
||||||
*
|
|
||||||
* The visit_type_FOO() functions expect a non-null @obj argument;
|
* The visit_type_FOO() functions expect a non-null @obj argument;
|
||||||
* they allocate *@obj during input visits, leave it unchanged on
|
* they allocate *@obj during input visits, leave it unchanged on
|
||||||
* output visits, and recursively free any resources during a dealloc
|
* output visits, and recursively free any resources during a dealloc
|
||||||
@ -375,6 +369,19 @@ void visit_start_list(Visitor *v, const char *name, GenericList **list,
|
|||||||
*/
|
*/
|
||||||
GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size);
|
GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare for completing a list visit.
|
||||||
|
*
|
||||||
|
* @errp obeys typical error usage, and reports failures such as
|
||||||
|
* unvisited list tail remaining in the input stream.
|
||||||
|
*
|
||||||
|
* Should be called prior to visit_end_list() if all other
|
||||||
|
* intermediate visit steps were successful, to allow the visitor one
|
||||||
|
* last chance to report errors. May be skipped on a cleanup path,
|
||||||
|
* where there is no need to check for further errors.
|
||||||
|
*/
|
||||||
|
void visit_check_list(Visitor *v, Error **errp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Complete a list visit started earlier.
|
* Complete a list visit started earlier.
|
||||||
*
|
*
|
||||||
|
@ -42,7 +42,6 @@ static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
MODULE_INIT_BLOCK,
|
MODULE_INIT_BLOCK,
|
||||||
MODULE_INIT_OPTS,
|
MODULE_INIT_OPTS,
|
||||||
MODULE_INIT_QAPI,
|
|
||||||
MODULE_INIT_QOM,
|
MODULE_INIT_QOM,
|
||||||
MODULE_INIT_TRACE,
|
MODULE_INIT_TRACE,
|
||||||
MODULE_INIT_MAX
|
MODULE_INIT_MAX
|
||||||
@ -50,7 +49,6 @@ typedef enum {
|
|||||||
|
|
||||||
#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
|
#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
|
||||||
#define opts_init(function) module_init(function, MODULE_INIT_OPTS)
|
#define opts_init(function) module_init(function, MODULE_INIT_OPTS)
|
||||||
#define qapi_init(function) module_init(function, MODULE_INIT_QAPI)
|
|
||||||
#define type_init(function) module_init(function, MODULE_INIT_QOM)
|
#define type_init(function) module_init(function, MODULE_INIT_QOM)
|
||||||
#define trace_init(function) module_init(function, MODULE_INIT_TRACE)
|
#define trace_init(function) module_init(function, MODULE_INIT_TRACE)
|
||||||
|
|
||||||
|
182
monitor.c
182
monitor.c
@ -165,7 +165,7 @@ typedef struct {
|
|||||||
* When command qmp_capabilities succeeds, we go into command
|
* When command qmp_capabilities succeeds, we go into command
|
||||||
* mode.
|
* mode.
|
||||||
*/
|
*/
|
||||||
bool in_command_mode; /* are we in command mode? */
|
QmpCommandList *commands;
|
||||||
} MonitorQMP;
|
} MonitorQMP;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -221,6 +221,8 @@ static int mon_refcount;
|
|||||||
static mon_cmd_t mon_cmds[];
|
static mon_cmd_t mon_cmds[];
|
||||||
static mon_cmd_t info_cmds[];
|
static mon_cmd_t info_cmds[];
|
||||||
|
|
||||||
|
QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
|
||||||
|
|
||||||
Monitor *cur_mon;
|
Monitor *cur_mon;
|
||||||
|
|
||||||
static QEMUClockType event_clock_type = QEMU_CLOCK_REALTIME;
|
static QEMUClockType event_clock_type = QEMU_CLOCK_REALTIME;
|
||||||
@ -414,7 +416,8 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict)
|
|||||||
|
|
||||||
trace_monitor_protocol_event_emit(event, qdict);
|
trace_monitor_protocol_event_emit(event, qdict);
|
||||||
QLIST_FOREACH(mon, &mon_list, entry) {
|
QLIST_FOREACH(mon, &mon_list, entry) {
|
||||||
if (monitor_is_qmp(mon) && mon->qmp.in_command_mode) {
|
if (monitor_is_qmp(mon)
|
||||||
|
&& mon->qmp.commands != &qmp_cap_negotiation_commands) {
|
||||||
monitor_json_emitter(mon, QOBJECT(qdict));
|
monitor_json_emitter(mon, QOBJECT(qdict));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -563,11 +566,6 @@ static void monitor_qapi_event_init(void)
|
|||||||
qmp_event_set_func_emit(monitor_qapi_event_queue);
|
qmp_event_set_func_emit(monitor_qapi_event_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_qmp_capabilities(Error **errp)
|
|
||||||
{
|
|
||||||
cur_mon->qmp.in_command_mode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_hmp_command(Monitor *mon, const char *cmdline);
|
static void handle_hmp_command(Monitor *mon, const char *cmdline);
|
||||||
|
|
||||||
static void monitor_data_init(Monitor *mon)
|
static void monitor_data_init(Monitor *mon)
|
||||||
@ -919,7 +917,7 @@ CommandInfoList *qmp_query_commands(Error **errp)
|
|||||||
{
|
{
|
||||||
CommandInfoList *list = NULL;
|
CommandInfoList *list = NULL;
|
||||||
|
|
||||||
qmp_for_each_command(query_commands_cb, &list);
|
qmp_for_each_command(cur_mon->qmp.commands, query_commands_cb, &list);
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@ -973,44 +971,67 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
|
|||||||
static void qmp_unregister_commands_hack(void)
|
static void qmp_unregister_commands_hack(void)
|
||||||
{
|
{
|
||||||
#ifndef CONFIG_SPICE
|
#ifndef CONFIG_SPICE
|
||||||
qmp_unregister_command("query-spice");
|
qmp_unregister_command(&qmp_commands, "query-spice");
|
||||||
#endif
|
#endif
|
||||||
#ifndef TARGET_I386
|
#ifndef TARGET_I386
|
||||||
qmp_unregister_command("rtc-reset-reinjection");
|
qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
|
||||||
#endif
|
#endif
|
||||||
#ifndef TARGET_S390X
|
#ifndef TARGET_S390X
|
||||||
qmp_unregister_command("dump-skeys");
|
qmp_unregister_command(&qmp_commands, "dump-skeys");
|
||||||
#endif
|
#endif
|
||||||
#ifndef TARGET_ARM
|
#ifndef TARGET_ARM
|
||||||
qmp_unregister_command("query-gic-capabilities");
|
qmp_unregister_command(&qmp_commands, "query-gic-capabilities");
|
||||||
#endif
|
#endif
|
||||||
#if !defined(TARGET_S390X) && !defined(TARGET_I386)
|
#if !defined(TARGET_S390X) && !defined(TARGET_I386)
|
||||||
qmp_unregister_command("query-cpu-model-expansion");
|
qmp_unregister_command(&qmp_commands, "query-cpu-model-expansion");
|
||||||
#endif
|
#endif
|
||||||
#if !defined(TARGET_S390X)
|
#if !defined(TARGET_S390X)
|
||||||
qmp_unregister_command("query-cpu-model-baseline");
|
qmp_unregister_command(&qmp_commands, "query-cpu-model-baseline");
|
||||||
qmp_unregister_command("query-cpu-model-comparison");
|
qmp_unregister_command(&qmp_commands, "query-cpu-model-comparison");
|
||||||
#endif
|
#endif
|
||||||
#if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
|
#if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
|
||||||
&& !defined(TARGET_S390X)
|
&& !defined(TARGET_S390X)
|
||||||
qmp_unregister_command("query-cpu-definitions");
|
qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qmp_init_marshal(void)
|
void monitor_init_qmp_commands(void)
|
||||||
{
|
{
|
||||||
qmp_register_command("query-qmp-schema", qmp_query_qmp_schema,
|
/*
|
||||||
|
* Two command lists:
|
||||||
|
* - qmp_commands contains all QMP commands
|
||||||
|
* - qmp_cap_negotiation_commands contains just
|
||||||
|
* "qmp_capabilities", to enforce capability negotiation
|
||||||
|
*/
|
||||||
|
|
||||||
|
qmp_init_marshal(&qmp_commands);
|
||||||
|
|
||||||
|
qmp_register_command(&qmp_commands, "query-qmp-schema",
|
||||||
|
qmp_query_qmp_schema,
|
||||||
QCO_NO_OPTIONS);
|
QCO_NO_OPTIONS);
|
||||||
qmp_register_command("device_add", qmp_device_add,
|
qmp_register_command(&qmp_commands, "device_add", qmp_device_add,
|
||||||
QCO_NO_OPTIONS);
|
QCO_NO_OPTIONS);
|
||||||
qmp_register_command("netdev_add", qmp_netdev_add,
|
qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
|
||||||
QCO_NO_OPTIONS);
|
QCO_NO_OPTIONS);
|
||||||
|
|
||||||
/* call it after the rest of qapi_init() */
|
qmp_unregister_commands_hack();
|
||||||
register_module_init(qmp_unregister_commands_hack, MODULE_INIT_QAPI);
|
|
||||||
|
QTAILQ_INIT(&qmp_cap_negotiation_commands);
|
||||||
|
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
|
||||||
|
qmp_marshal_qmp_capabilities, QCO_NO_OPTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
qapi_init(qmp_init_marshal);
|
void qmp_qmp_capabilities(Error **errp)
|
||||||
|
{
|
||||||
|
if (cur_mon->qmp.commands == &qmp_commands) {
|
||||||
|
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
|
||||||
|
"Capabilities negotiation is already complete, command "
|
||||||
|
"ignored");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_mon->qmp.commands = &qmp_commands;
|
||||||
|
}
|
||||||
|
|
||||||
/* set the current CPU defined by the user */
|
/* set the current CPU defined by the user */
|
||||||
int monitor_set_cpu(int cpu_index)
|
int monitor_set_cpu(int cpu_index)
|
||||||
@ -3679,87 +3700,10 @@ static int monitor_can_read(void *opaque)
|
|||||||
return (mon->suspend_cnt == 0) ? 1 : 0;
|
return (mon->suspend_cnt == 0) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool invalid_qmp_mode(const Monitor *mon, const char *cmd,
|
|
||||||
Error **errp)
|
|
||||||
{
|
|
||||||
bool is_cap = g_str_equal(cmd, "qmp_capabilities");
|
|
||||||
|
|
||||||
if (is_cap && mon->qmp.in_command_mode) {
|
|
||||||
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
|
|
||||||
"Capabilities negotiation is already complete, command "
|
|
||||||
"'%s' ignored", cmd);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!is_cap && !mon->qmp.in_command_mode) {
|
|
||||||
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
|
|
||||||
"Expecting capabilities negotiation with "
|
|
||||||
"'qmp_capabilities' before command '%s'", cmd);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Input object checking rules
|
|
||||||
*
|
|
||||||
* 1. Input object must be a dict
|
|
||||||
* 2. The "execute" key must exist
|
|
||||||
* 3. The "execute" key must be a string
|
|
||||||
* 4. If the "arguments" key exists, it must be a dict
|
|
||||||
* 5. If the "id" key exists, it can be anything (ie. json-value)
|
|
||||||
* 6. Any argument not listed above is considered invalid
|
|
||||||
*/
|
|
||||||
static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp)
|
|
||||||
{
|
|
||||||
const QDictEntry *ent;
|
|
||||||
int has_exec_key = 0;
|
|
||||||
QDict *input_dict;
|
|
||||||
|
|
||||||
input_dict = qobject_to_qdict(input_obj);
|
|
||||||
if (!input_dict) {
|
|
||||||
error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT, "object");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (ent = qdict_first(input_dict); ent; ent = qdict_next(input_dict, ent)){
|
|
||||||
const char *arg_name = qdict_entry_key(ent);
|
|
||||||
const QObject *arg_obj = qdict_entry_value(ent);
|
|
||||||
|
|
||||||
if (!strcmp(arg_name, "execute")) {
|
|
||||||
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
|
|
||||||
error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
|
|
||||||
"execute", "string");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
has_exec_key = 1;
|
|
||||||
} else if (!strcmp(arg_name, "arguments")) {
|
|
||||||
if (qobject_type(arg_obj) != QTYPE_QDICT) {
|
|
||||||
error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
|
|
||||||
"arguments", "object");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else if (!strcmp(arg_name, "id")) {
|
|
||||||
/* Any string is acceptable as "id", so nothing to check */
|
|
||||||
} else {
|
|
||||||
error_setg(errp, QERR_QMP_EXTRA_MEMBER, arg_name);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_exec_key) {
|
|
||||||
error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return input_dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
|
static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
|
||||||
{
|
{
|
||||||
QObject *req, *rsp = NULL, *id = NULL;
|
QObject *req, *rsp = NULL, *id = NULL;
|
||||||
QDict *qdict = NULL;
|
QDict *qdict = NULL;
|
||||||
const char *cmd_name;
|
|
||||||
Monitor *mon = cur_mon;
|
Monitor *mon = cur_mon;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
@ -3772,24 +3716,28 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
|
|||||||
goto err_out;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
qdict = qmp_check_input_obj(req, &err);
|
qdict = qobject_to_qdict(req);
|
||||||
if (!qdict) {
|
if (qdict) {
|
||||||
goto err_out;
|
id = qdict_get(qdict, "id");
|
||||||
|
qobject_incref(id);
|
||||||
|
qdict_del(qdict, "id");
|
||||||
|
} /* else will fail qmp_dispatch() */
|
||||||
|
|
||||||
|
rsp = qmp_dispatch(cur_mon->qmp.commands, req);
|
||||||
|
|
||||||
|
if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
|
||||||
|
qdict = qdict_get_qdict(qobject_to_qdict(rsp), "error");
|
||||||
|
if (qdict
|
||||||
|
&& !g_strcmp0(qdict_get_try_str(qdict, "class"),
|
||||||
|
QapiErrorClass_lookup[ERROR_CLASS_COMMAND_NOT_FOUND])) {
|
||||||
|
/* Provide a more useful error message */
|
||||||
|
qdict_del(qdict, "desc");
|
||||||
|
qdict_put(qdict, "desc",
|
||||||
|
qstring_from_str("Expecting capabilities negotiation"
|
||||||
|
" with 'qmp_capabilities'"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id = qdict_get(qdict, "id");
|
|
||||||
qobject_incref(id);
|
|
||||||
qdict_del(qdict, "id");
|
|
||||||
|
|
||||||
cmd_name = qdict_get_str(qdict, "execute");
|
|
||||||
trace_handle_qmp_command(mon, cmd_name);
|
|
||||||
|
|
||||||
if (invalid_qmp_mode(mon, cmd_name, &err)) {
|
|
||||||
goto err_out;
|
|
||||||
}
|
|
||||||
|
|
||||||
rsp = qmp_dispatch(req);
|
|
||||||
|
|
||||||
err_out:
|
err_out:
|
||||||
if (err) {
|
if (err) {
|
||||||
qdict = qdict_new();
|
qdict = qdict_new();
|
||||||
@ -3886,7 +3834,7 @@ static void monitor_qmp_event(void *opaque, int event)
|
|||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case CHR_EVENT_OPENED:
|
case CHR_EVENT_OPENED:
|
||||||
mon->qmp.in_command_mode = false;
|
mon->qmp.commands = &qmp_cap_negotiation_commands;
|
||||||
data = get_qmp_greeting();
|
data = get_qmp_greeting();
|
||||||
monitor_json_emitter(mon, data);
|
monitor_json_emitter(mon, data);
|
||||||
qobject_decref(data);
|
qobject_decref(data);
|
||||||
|
@ -272,6 +272,16 @@ opts_next_list(Visitor *v, GenericList *tail, size_t size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
opts_check_list(Visitor *v, Error **errp)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* FIXME should set error when unvisited elements remain. Mostly
|
||||||
|
* harmless, as the generated visits always visit all elements.
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
opts_end_list(Visitor *v, void **obj)
|
opts_end_list(Visitor *v, void **obj)
|
||||||
{
|
{
|
||||||
@ -528,6 +538,7 @@ opts_visitor_new(const QemuOpts *opts)
|
|||||||
{
|
{
|
||||||
OptsVisitor *ov;
|
OptsVisitor *ov;
|
||||||
|
|
||||||
|
assert(opts);
|
||||||
ov = g_malloc0(sizeof *ov);
|
ov = g_malloc0(sizeof *ov);
|
||||||
|
|
||||||
ov->visitor.type = VISITOR_INPUT;
|
ov->visitor.type = VISITOR_INPUT;
|
||||||
@ -538,6 +549,7 @@ opts_visitor_new(const QemuOpts *opts)
|
|||||||
|
|
||||||
ov->visitor.start_list = &opts_start_list;
|
ov->visitor.start_list = &opts_start_list;
|
||||||
ov->visitor.next_list = &opts_next_list;
|
ov->visitor.next_list = &opts_next_list;
|
||||||
|
ov->visitor.check_list = &opts_check_list;
|
||||||
ov->visitor.end_list = &opts_end_list;
|
ov->visitor.end_list = &opts_end_list;
|
||||||
|
|
||||||
ov->visitor.type_int64 = &opts_type_int64;
|
ov->visitor.type_int64 = &opts_type_int64;
|
||||||
|
@ -90,6 +90,14 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size)
|
|||||||
return v->next_list(v, tail, size);
|
return v->next_list(v, tail, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void visit_check_list(Visitor *v, Error **errp)
|
||||||
|
{
|
||||||
|
trace_visit_check_list(v);
|
||||||
|
if (v->check_list) {
|
||||||
|
v->check_list(v, errp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void visit_end_list(Visitor *v, void **obj)
|
void visit_end_list(Visitor *v, void **obj)
|
||||||
{
|
{
|
||||||
trace_visit_end_list(v, obj);
|
trace_visit_end_list(v, obj);
|
||||||
|
@ -30,8 +30,7 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
|
|||||||
|
|
||||||
dict = qobject_to_qdict(request);
|
dict = qobject_to_qdict(request);
|
||||||
if (!dict) {
|
if (!dict) {
|
||||||
error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT,
|
error_setg(errp, "Expected '%s' in QMP input", "object");
|
||||||
"request is not a dictionary");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,26 +41,34 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
|
|||||||
|
|
||||||
if (!strcmp(arg_name, "execute")) {
|
if (!strcmp(arg_name, "execute")) {
|
||||||
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
|
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
|
||||||
error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute",
|
error_setg(errp, "QMP input object member '%s' expects '%s'",
|
||||||
"string");
|
"execute", "string");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
has_exec_key = true;
|
has_exec_key = true;
|
||||||
} else if (strcmp(arg_name, "arguments")) {
|
} else if (!strcmp(arg_name, "arguments")) {
|
||||||
error_setg(errp, QERR_QMP_EXTRA_MEMBER, arg_name);
|
if (qobject_type(arg_obj) != QTYPE_QDICT) {
|
||||||
|
error_setg(errp, "QMP input object member '%s' expects '%s'",
|
||||||
|
"arguments", "object");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error_setg(errp, "QMP input object member '%s' is unexpected",
|
||||||
|
arg_name);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!has_exec_key) {
|
if (!has_exec_key) {
|
||||||
error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute");
|
error_setg(errp, "Expected '%s' in QMP input", "execute");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QObject *do_qmp_dispatch(QObject *request, Error **errp)
|
static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
||||||
|
Error **errp)
|
||||||
{
|
{
|
||||||
Error *local_err = NULL;
|
Error *local_err = NULL;
|
||||||
const char *command;
|
const char *command;
|
||||||
@ -75,7 +82,7 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
command = qdict_get_str(dict, "execute");
|
command = qdict_get_str(dict, "execute");
|
||||||
cmd = qmp_find_command(command);
|
cmd = qmp_find_command(cmds, command);
|
||||||
if (cmd == NULL) {
|
if (cmd == NULL) {
|
||||||
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
|
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
|
||||||
"The command %s has not been found", command);
|
"The command %s has not been found", command);
|
||||||
@ -115,13 +122,13 @@ QObject *qmp_build_error_object(Error *err)
|
|||||||
error_get_pretty(err));
|
error_get_pretty(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
QObject *qmp_dispatch(QObject *request)
|
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request)
|
||||||
{
|
{
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
QObject *ret;
|
QObject *ret;
|
||||||
QDict *rsp;
|
QDict *rsp;
|
||||||
|
|
||||||
ret = do_qmp_dispatch(request, &err);
|
ret = do_qmp_dispatch(cmds, request, &err);
|
||||||
|
|
||||||
rsp = qdict_new();
|
rsp = qdict_new();
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -15,11 +15,8 @@
|
|||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qapi/qmp/dispatch.h"
|
#include "qapi/qmp/dispatch.h"
|
||||||
|
|
||||||
static QTAILQ_HEAD(QmpCommandList, QmpCommand) qmp_commands =
|
void qmp_register_command(QmpCommandList *cmds, const char *name,
|
||||||
QTAILQ_HEAD_INITIALIZER(qmp_commands);
|
QmpCommandFunc *fn, QmpCommandOptions options)
|
||||||
|
|
||||||
void qmp_register_command(const char *name, QmpCommandFunc *fn,
|
|
||||||
QmpCommandOptions options)
|
|
||||||
{
|
{
|
||||||
QmpCommand *cmd = g_malloc0(sizeof(*cmd));
|
QmpCommand *cmd = g_malloc0(sizeof(*cmd));
|
||||||
|
|
||||||
@ -27,22 +24,22 @@ void qmp_register_command(const char *name, QmpCommandFunc *fn,
|
|||||||
cmd->fn = fn;
|
cmd->fn = fn;
|
||||||
cmd->enabled = true;
|
cmd->enabled = true;
|
||||||
cmd->options = options;
|
cmd->options = options;
|
||||||
QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node);
|
QTAILQ_INSERT_TAIL(cmds, cmd, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_unregister_command(const char *name)
|
void qmp_unregister_command(QmpCommandList *cmds, const char *name)
|
||||||
{
|
{
|
||||||
QmpCommand *cmd = qmp_find_command(name);
|
QmpCommand *cmd = qmp_find_command(cmds, name);
|
||||||
|
|
||||||
QTAILQ_REMOVE(&qmp_commands, cmd, node);
|
QTAILQ_REMOVE(cmds, cmd, node);
|
||||||
g_free(cmd);
|
g_free(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
QmpCommand *qmp_find_command(const char *name)
|
QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name)
|
||||||
{
|
{
|
||||||
QmpCommand *cmd;
|
QmpCommand *cmd;
|
||||||
|
|
||||||
QTAILQ_FOREACH(cmd, &qmp_commands, node) {
|
QTAILQ_FOREACH(cmd, cmds, node) {
|
||||||
if (strcmp(cmd->name, name) == 0) {
|
if (strcmp(cmd->name, name) == 0) {
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
@ -50,11 +47,12 @@ QmpCommand *qmp_find_command(const char *name)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qmp_toggle_command(const char *name, bool enabled)
|
static void qmp_toggle_command(QmpCommandList *cmds, const char *name,
|
||||||
|
bool enabled)
|
||||||
{
|
{
|
||||||
QmpCommand *cmd;
|
QmpCommand *cmd;
|
||||||
|
|
||||||
QTAILQ_FOREACH(cmd, &qmp_commands, node) {
|
QTAILQ_FOREACH(cmd, cmds, node) {
|
||||||
if (strcmp(cmd->name, name) == 0) {
|
if (strcmp(cmd->name, name) == 0) {
|
||||||
cmd->enabled = enabled;
|
cmd->enabled = enabled;
|
||||||
return;
|
return;
|
||||||
@ -62,14 +60,14 @@ static void qmp_toggle_command(const char *name, bool enabled)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_disable_command(const char *name)
|
void qmp_disable_command(QmpCommandList *cmds, const char *name)
|
||||||
{
|
{
|
||||||
qmp_toggle_command(name, false);
|
qmp_toggle_command(cmds, name, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_enable_command(const char *name)
|
void qmp_enable_command(QmpCommandList *cmds, const char *name)
|
||||||
{
|
{
|
||||||
qmp_toggle_command(name, true);
|
qmp_toggle_command(cmds, name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool qmp_command_is_enabled(const QmpCommand *cmd)
|
bool qmp_command_is_enabled(const QmpCommand *cmd)
|
||||||
@ -87,11 +85,12 @@ bool qmp_has_success_response(const QmpCommand *cmd)
|
|||||||
return !(cmd->options & QCO_NO_SUCCESS_RESP);
|
return !(cmd->options & QCO_NO_SUCCESS_RESP);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qmp_for_each_command(qmp_cmd_callback_fn fn, void *opaque)
|
void qmp_for_each_command(QmpCommandList *cmds, qmp_cmd_callback_fn fn,
|
||||||
|
void *opaque)
|
||||||
{
|
{
|
||||||
QmpCommand *cmd;
|
QmpCommand *cmd;
|
||||||
|
|
||||||
QTAILQ_FOREACH(cmd, &qmp_commands, node) {
|
QTAILQ_FOREACH(cmd, cmds, node) {
|
||||||
fn(cmd, opaque);
|
fn(cmd, opaque);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,21 +21,19 @@
|
|||||||
#include "qapi/qmp/types.h"
|
#include "qapi/qmp/types.h"
|
||||||
#include "qapi/qmp/qerror.h"
|
#include "qapi/qmp/qerror.h"
|
||||||
|
|
||||||
#define QIV_STACK_SIZE 1024
|
typedef struct StackObject {
|
||||||
|
const char *name; /* Name of @obj in its parent, if any */
|
||||||
typedef struct StackObject
|
QObject *obj; /* QDict or QList being visited */
|
||||||
{
|
|
||||||
QObject *obj; /* Object being visited */
|
|
||||||
void *qapi; /* sanity check that caller uses same pointer */
|
void *qapi; /* sanity check that caller uses same pointer */
|
||||||
|
|
||||||
GHashTable *h; /* If obj is dict: unvisited keys */
|
GHashTable *h; /* If @obj is QDict: unvisited keys */
|
||||||
const QListEntry *entry; /* If obj is list: unvisited tail */
|
const QListEntry *entry; /* If @obj is QList: unvisited tail */
|
||||||
|
unsigned index; /* If @obj is QList: list index of @entry */
|
||||||
|
|
||||||
QSLIST_ENTRY(StackObject) node;
|
QSLIST_ENTRY(StackObject) node; /* parent */
|
||||||
} StackObject;
|
} StackObject;
|
||||||
|
|
||||||
struct QObjectInputVisitor
|
struct QObjectInputVisitor {
|
||||||
{
|
|
||||||
Visitor visitor;
|
Visitor visitor;
|
||||||
|
|
||||||
/* Root of visit at visitor creation. */
|
/* Root of visit at visitor creation. */
|
||||||
@ -45,8 +43,7 @@ struct QObjectInputVisitor
|
|||||||
* QDict or QList). */
|
* QDict or QList). */
|
||||||
QSLIST_HEAD(, StackObject) stack;
|
QSLIST_HEAD(, StackObject) stack;
|
||||||
|
|
||||||
/* True to reject parse in visit_end_struct() if unvisited keys remain. */
|
GString *errname; /* Accumulator for full_name() */
|
||||||
bool strict;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static QObjectInputVisitor *to_qiv(Visitor *v)
|
static QObjectInputVisitor *to_qiv(Visitor *v)
|
||||||
@ -54,9 +51,51 @@ static QObjectInputVisitor *to_qiv(Visitor *v)
|
|||||||
return container_of(v, QObjectInputVisitor, visitor);
|
return container_of(v, QObjectInputVisitor, visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
|
static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name,
|
||||||
const char *name,
|
int n)
|
||||||
bool consume, Error **errp)
|
{
|
||||||
|
StackObject *so;
|
||||||
|
char buf[32];
|
||||||
|
|
||||||
|
if (qiv->errname) {
|
||||||
|
g_string_truncate(qiv->errname, 0);
|
||||||
|
} else {
|
||||||
|
qiv->errname = g_string_new("");
|
||||||
|
}
|
||||||
|
|
||||||
|
QSLIST_FOREACH(so , &qiv->stack, node) {
|
||||||
|
if (n) {
|
||||||
|
n--;
|
||||||
|
} else if (qobject_type(so->obj) == QTYPE_QDICT) {
|
||||||
|
g_string_prepend(qiv->errname, name ?: "<anonymous>");
|
||||||
|
g_string_prepend_c(qiv->errname, '.');
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), "[%u]", so->index);
|
||||||
|
g_string_prepend(qiv->errname, buf);
|
||||||
|
}
|
||||||
|
name = so->name;
|
||||||
|
}
|
||||||
|
assert(!n);
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
g_string_prepend(qiv->errname, name);
|
||||||
|
} else if (qiv->errname->str[0] == '.') {
|
||||||
|
g_string_erase(qiv->errname, 0, 1);
|
||||||
|
} else if (!qiv->errname->str[0]) {
|
||||||
|
return "<anonymous>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return qiv->errname->str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *full_name(QObjectInputVisitor *qiv, const char *name)
|
||||||
|
{
|
||||||
|
return full_name_nth(qiv, name, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QObject *qobject_input_try_get_object(QObjectInputVisitor *qiv,
|
||||||
|
const char *name,
|
||||||
|
bool consume)
|
||||||
{
|
{
|
||||||
StackObject *tos;
|
StackObject *tos;
|
||||||
QObject *qobj;
|
QObject *qobj;
|
||||||
@ -80,22 +119,37 @@ static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
|
|||||||
bool removed = g_hash_table_remove(tos->h, name);
|
bool removed = g_hash_table_remove(tos->h, name);
|
||||||
assert(removed);
|
assert(removed);
|
||||||
}
|
}
|
||||||
if (!ret) {
|
|
||||||
error_setg(errp, QERR_MISSING_PARAMETER, name);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
assert(qobject_type(qobj) == QTYPE_QLIST);
|
assert(qobject_type(qobj) == QTYPE_QLIST);
|
||||||
assert(!name);
|
assert(!name);
|
||||||
ret = qlist_entry_obj(tos->entry);
|
if (tos->entry) {
|
||||||
assert(ret);
|
ret = qlist_entry_obj(tos->entry);
|
||||||
|
if (consume) {
|
||||||
|
tos->entry = qlist_next(tos->entry);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = NULL;
|
||||||
|
}
|
||||||
if (consume) {
|
if (consume) {
|
||||||
tos->entry = qlist_next(tos->entry);
|
tos->index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
|
||||||
|
const char *name,
|
||||||
|
bool consume, Error **errp)
|
||||||
|
{
|
||||||
|
QObject *obj = qobject_input_try_get_object(qiv, name, consume);
|
||||||
|
|
||||||
|
if (!obj) {
|
||||||
|
error_setg(errp, QERR_MISSING_PARAMETER, full_name(qiv, name));
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
static void qdict_add_key(const char *key, QObject *obj, void *opaque)
|
static void qdict_add_key(const char *key, QObject *obj, void *opaque)
|
||||||
{
|
{
|
||||||
GHashTable *h = opaque;
|
GHashTable *h = opaque;
|
||||||
@ -103,22 +157,25 @@ static void qdict_add_key(const char *key, QObject *obj, void *opaque)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv,
|
static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv,
|
||||||
QObject *obj, void *qapi,
|
const char *name,
|
||||||
Error **errp)
|
QObject *obj, void *qapi)
|
||||||
{
|
{
|
||||||
GHashTable *h;
|
GHashTable *h;
|
||||||
StackObject *tos = g_new0(StackObject, 1);
|
StackObject *tos = g_new0(StackObject, 1);
|
||||||
|
|
||||||
assert(obj);
|
assert(obj);
|
||||||
|
tos->name = name;
|
||||||
tos->obj = obj;
|
tos->obj = obj;
|
||||||
tos->qapi = qapi;
|
tos->qapi = qapi;
|
||||||
|
|
||||||
if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) {
|
if (qobject_type(obj) == QTYPE_QDICT) {
|
||||||
h = g_hash_table_new(g_str_hash, g_str_equal);
|
h = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
|
qdict_iter(qobject_to_qdict(obj), qdict_add_key, h);
|
||||||
tos->h = h;
|
tos->h = h;
|
||||||
} else if (qobject_type(obj) == QTYPE_QLIST) {
|
} else {
|
||||||
|
assert(qobject_type(obj) == QTYPE_QLIST);
|
||||||
tos->entry = qlist_first(qobject_to_qlist(obj));
|
tos->entry = qlist_first(qobject_to_qlist(obj));
|
||||||
|
tos->index = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
|
QSLIST_INSERT_HEAD(&qiv->stack, tos, node);
|
||||||
@ -130,19 +187,15 @@ static void qobject_input_check_struct(Visitor *v, Error **errp)
|
|||||||
{
|
{
|
||||||
QObjectInputVisitor *qiv = to_qiv(v);
|
QObjectInputVisitor *qiv = to_qiv(v);
|
||||||
StackObject *tos = QSLIST_FIRST(&qiv->stack);
|
StackObject *tos = QSLIST_FIRST(&qiv->stack);
|
||||||
|
GHashTableIter iter;
|
||||||
|
const char *key;
|
||||||
|
|
||||||
assert(tos && !tos->entry);
|
assert(tos && !tos->entry);
|
||||||
if (qiv->strict) {
|
|
||||||
GHashTable *const top_ht = tos->h;
|
|
||||||
if (top_ht) {
|
|
||||||
GHashTableIter iter;
|
|
||||||
const char *key;
|
|
||||||
|
|
||||||
g_hash_table_iter_init(&iter, top_ht);
|
g_hash_table_iter_init(&iter, tos->h);
|
||||||
if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
|
if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
|
||||||
error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
|
error_setg(errp, "Parameter '%s' is unexpected",
|
||||||
}
|
full_name(qiv, key));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +223,6 @@ static void qobject_input_start_struct(Visitor *v, const char *name, void **obj,
|
|||||||
{
|
{
|
||||||
QObjectInputVisitor *qiv = to_qiv(v);
|
QObjectInputVisitor *qiv = to_qiv(v);
|
||||||
QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
|
QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
|
||||||
Error *err = NULL;
|
|
||||||
|
|
||||||
if (obj) {
|
if (obj) {
|
||||||
*obj = NULL;
|
*obj = NULL;
|
||||||
@ -179,16 +231,12 @@ static void qobject_input_start_struct(Visitor *v, const char *name, void **obj,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (qobject_type(qobj) != QTYPE_QDICT) {
|
if (qobject_type(qobj) != QTYPE_QDICT) {
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||||
"QDict");
|
full_name(qiv, name), "object");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qobject_input_push(qiv, qobj, obj, &err);
|
qobject_input_push(qiv, name, qobj, obj);
|
||||||
if (err) {
|
|
||||||
error_propagate(errp, err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (obj) {
|
if (obj) {
|
||||||
*obj = g_malloc0(size);
|
*obj = g_malloc0(size);
|
||||||
@ -204,25 +252,21 @@ static void qobject_input_start_list(Visitor *v, const char *name,
|
|||||||
QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
|
QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
|
||||||
const QListEntry *entry;
|
const QListEntry *entry;
|
||||||
|
|
||||||
|
if (list) {
|
||||||
|
*list = NULL;
|
||||||
|
}
|
||||||
if (!qobj) {
|
if (!qobj) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (qobject_type(qobj) != QTYPE_QLIST) {
|
if (qobject_type(qobj) != QTYPE_QLIST) {
|
||||||
if (list) {
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||||
*list = NULL;
|
full_name(qiv, name), "array");
|
||||||
}
|
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
||||||
"list");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
entry = qobject_input_push(qiv, qobj, list, errp);
|
entry = qobject_input_push(qiv, name, qobj, list);
|
||||||
if (list) {
|
if (entry && list) {
|
||||||
if (entry) {
|
*list = g_malloc0(size);
|
||||||
*list = g_malloc0(size);
|
|
||||||
} else {
|
|
||||||
*list = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,15 +274,30 @@ static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail,
|
|||||||
size_t size)
|
size_t size)
|
||||||
{
|
{
|
||||||
QObjectInputVisitor *qiv = to_qiv(v);
|
QObjectInputVisitor *qiv = to_qiv(v);
|
||||||
StackObject *so = QSLIST_FIRST(&qiv->stack);
|
StackObject *tos = QSLIST_FIRST(&qiv->stack);
|
||||||
|
|
||||||
if (!so->entry) {
|
assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
|
||||||
|
|
||||||
|
if (!tos->entry) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
tail->next = g_malloc0(size);
|
tail->next = g_malloc0(size);
|
||||||
return tail->next;
|
return tail->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qobject_input_check_list(Visitor *v, Error **errp)
|
||||||
|
{
|
||||||
|
QObjectInputVisitor *qiv = to_qiv(v);
|
||||||
|
StackObject *tos = QSLIST_FIRST(&qiv->stack);
|
||||||
|
|
||||||
|
assert(tos && tos->obj && qobject_type(tos->obj) == QTYPE_QLIST);
|
||||||
|
|
||||||
|
if (tos->entry) {
|
||||||
|
error_setg(errp, "Only %u list elements expected in %s",
|
||||||
|
tos->index + 1, full_name_nth(qiv, NULL, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void qobject_input_start_alternate(Visitor *v, const char *name,
|
static void qobject_input_start_alternate(Visitor *v, const char *name,
|
||||||
GenericAlternate **obj, size_t size,
|
GenericAlternate **obj, size_t size,
|
||||||
@ -270,8 +329,8 @@ static void qobject_input_type_int64(Visitor *v, const char *name, int64_t *obj,
|
|||||||
}
|
}
|
||||||
qint = qobject_to_qint(qobj);
|
qint = qobject_to_qint(qobj);
|
||||||
if (!qint) {
|
if (!qint) {
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||||
"integer");
|
full_name(qiv, name), "integer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,8 +350,8 @@ static void qobject_input_type_uint64(Visitor *v, const char *name,
|
|||||||
}
|
}
|
||||||
qint = qobject_to_qint(qobj);
|
qint = qobject_to_qint(qobj);
|
||||||
if (!qint) {
|
if (!qint) {
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||||
"integer");
|
full_name(qiv, name), "integer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,8 +370,8 @@ static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj,
|
|||||||
}
|
}
|
||||||
qbool = qobject_to_qbool(qobj);
|
qbool = qobject_to_qbool(qobj);
|
||||||
if (!qbool) {
|
if (!qbool) {
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||||
"boolean");
|
full_name(qiv, name), "boolean");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,8 +391,8 @@ static void qobject_input_type_str(Visitor *v, const char *name, char **obj,
|
|||||||
}
|
}
|
||||||
qstr = qobject_to_qstring(qobj);
|
qstr = qobject_to_qstring(qobj);
|
||||||
if (!qstr) {
|
if (!qstr) {
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||||
"string");
|
full_name(qiv, name), "string");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,8 +422,8 @@ static void qobject_input_type_number(Visitor *v, const char *name, double *obj,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||||
"number");
|
full_name(qiv, name), "number");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj,
|
static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj,
|
||||||
@ -392,15 +451,15 @@ static void qobject_input_type_null(Visitor *v, const char *name, Error **errp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (qobject_type(qobj) != QTYPE_QNULL) {
|
if (qobject_type(qobj) != QTYPE_QNULL) {
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||||
"null");
|
full_name(qiv, name), "null");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qobject_input_optional(Visitor *v, const char *name, bool *present)
|
static void qobject_input_optional(Visitor *v, const char *name, bool *present)
|
||||||
{
|
{
|
||||||
QObjectInputVisitor *qiv = to_qiv(v);
|
QObjectInputVisitor *qiv = to_qiv(v);
|
||||||
QObject *qobj = qobject_input_get_object(qiv, name, false, NULL);
|
QObject *qobj = qobject_input_try_get_object(qiv, name, false);
|
||||||
|
|
||||||
if (!qobj) {
|
if (!qobj) {
|
||||||
*present = false;
|
*present = false;
|
||||||
@ -413,6 +472,7 @@ static void qobject_input_optional(Visitor *v, const char *name, bool *present)
|
|||||||
static void qobject_input_free(Visitor *v)
|
static void qobject_input_free(Visitor *v)
|
||||||
{
|
{
|
||||||
QObjectInputVisitor *qiv = to_qiv(v);
|
QObjectInputVisitor *qiv = to_qiv(v);
|
||||||
|
|
||||||
while (!QSLIST_EMPTY(&qiv->stack)) {
|
while (!QSLIST_EMPTY(&qiv->stack)) {
|
||||||
StackObject *tos = QSLIST_FIRST(&qiv->stack);
|
StackObject *tos = QSLIST_FIRST(&qiv->stack);
|
||||||
|
|
||||||
@ -421,10 +481,13 @@ static void qobject_input_free(Visitor *v)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qobject_decref(qiv->root);
|
qobject_decref(qiv->root);
|
||||||
|
if (qiv->errname) {
|
||||||
|
g_string_free(qiv->errname, TRUE);
|
||||||
|
}
|
||||||
g_free(qiv);
|
g_free(qiv);
|
||||||
}
|
}
|
||||||
|
|
||||||
Visitor *qobject_input_visitor_new(QObject *obj, bool strict)
|
Visitor *qobject_input_visitor_new(QObject *obj)
|
||||||
{
|
{
|
||||||
QObjectInputVisitor *v;
|
QObjectInputVisitor *v;
|
||||||
|
|
||||||
@ -437,6 +500,7 @@ Visitor *qobject_input_visitor_new(QObject *obj, bool strict)
|
|||||||
v->visitor.end_struct = qobject_input_pop;
|
v->visitor.end_struct = qobject_input_pop;
|
||||||
v->visitor.start_list = qobject_input_start_list;
|
v->visitor.start_list = qobject_input_start_list;
|
||||||
v->visitor.next_list = qobject_input_next_list;
|
v->visitor.next_list = qobject_input_next_list;
|
||||||
|
v->visitor.check_list = qobject_input_check_list;
|
||||||
v->visitor.end_list = qobject_input_pop;
|
v->visitor.end_list = qobject_input_pop;
|
||||||
v->visitor.start_alternate = qobject_input_start_alternate;
|
v->visitor.start_alternate = qobject_input_start_alternate;
|
||||||
v->visitor.type_int64 = qobject_input_type_int64;
|
v->visitor.type_int64 = qobject_input_type_int64;
|
||||||
@ -448,7 +512,6 @@ Visitor *qobject_input_visitor_new(QObject *obj, bool strict)
|
|||||||
v->visitor.type_null = qobject_input_type_null;
|
v->visitor.type_null = qobject_input_type_null;
|
||||||
v->visitor.optional = qobject_input_optional;
|
v->visitor.optional = qobject_input_optional;
|
||||||
v->visitor.free = qobject_input_free;
|
v->visitor.free = qobject_input_free;
|
||||||
v->strict = strict;
|
|
||||||
|
|
||||||
v->root = obj;
|
v->root = obj;
|
||||||
qobject_incref(obj);
|
qobject_incref(obj);
|
||||||
|
@ -170,6 +170,35 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
|
|||||||
return tail->next;
|
return tail->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void check_list(Visitor *v, Error **errp)
|
||||||
|
{
|
||||||
|
const StringInputVisitor *siv = to_siv(v);
|
||||||
|
Range *r;
|
||||||
|
GList *cur_range;
|
||||||
|
|
||||||
|
if (!siv->ranges || !siv->cur_range) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = siv->cur_range->data;
|
||||||
|
if (!r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!range_contains(r, siv->cur)) {
|
||||||
|
cur_range = g_list_next(siv->cur_range);
|
||||||
|
if (!cur_range) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
r = cur_range->data;
|
||||||
|
if (!r) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error_setg(errp, "Range contains too many values");
|
||||||
|
}
|
||||||
|
|
||||||
static void end_list(Visitor *v, void **obj)
|
static void end_list(Visitor *v, void **obj)
|
||||||
{
|
{
|
||||||
StringInputVisitor *siv = to_siv(v);
|
StringInputVisitor *siv = to_siv(v);
|
||||||
@ -182,12 +211,6 @@ static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
|
|||||||
{
|
{
|
||||||
StringInputVisitor *siv = to_siv(v);
|
StringInputVisitor *siv = to_siv(v);
|
||||||
|
|
||||||
if (!siv->string) {
|
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
||||||
"integer");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parse_str(siv, name, errp) < 0) {
|
if (parse_str(siv, name, errp) < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -242,13 +265,7 @@ static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
|
|||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
|
|
||||||
if (siv->string) {
|
parse_option_size(name, siv->string, &val, &err);
|
||||||
parse_option_size(name, siv->string, &val, &err);
|
|
||||||
} else {
|
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
||||||
"size");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (err) {
|
if (err) {
|
||||||
error_propagate(errp, err);
|
error_propagate(errp, err);
|
||||||
return;
|
return;
|
||||||
@ -262,19 +279,17 @@ static void parse_type_bool(Visitor *v, const char *name, bool *obj,
|
|||||||
{
|
{
|
||||||
StringInputVisitor *siv = to_siv(v);
|
StringInputVisitor *siv = to_siv(v);
|
||||||
|
|
||||||
if (siv->string) {
|
if (!strcasecmp(siv->string, "on") ||
|
||||||
if (!strcasecmp(siv->string, "on") ||
|
!strcasecmp(siv->string, "yes") ||
|
||||||
!strcasecmp(siv->string, "yes") ||
|
!strcasecmp(siv->string, "true")) {
|
||||||
!strcasecmp(siv->string, "true")) {
|
*obj = true;
|
||||||
*obj = true;
|
return;
|
||||||
return;
|
}
|
||||||
}
|
if (!strcasecmp(siv->string, "off") ||
|
||||||
if (!strcasecmp(siv->string, "off") ||
|
!strcasecmp(siv->string, "no") ||
|
||||||
!strcasecmp(siv->string, "no") ||
|
!strcasecmp(siv->string, "false")) {
|
||||||
!strcasecmp(siv->string, "false")) {
|
*obj = false;
|
||||||
*obj = false;
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||||
@ -285,13 +300,8 @@ static void parse_type_str(Visitor *v, const char *name, char **obj,
|
|||||||
Error **errp)
|
Error **errp)
|
||||||
{
|
{
|
||||||
StringInputVisitor *siv = to_siv(v);
|
StringInputVisitor *siv = to_siv(v);
|
||||||
if (siv->string) {
|
|
||||||
*obj = g_strdup(siv->string);
|
*obj = g_strdup(siv->string);
|
||||||
} else {
|
|
||||||
*obj = NULL;
|
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
|
||||||
"string");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_type_number(Visitor *v, const char *name, double *obj,
|
static void parse_type_number(Visitor *v, const char *name, double *obj,
|
||||||
@ -302,10 +312,8 @@ static void parse_type_number(Visitor *v, const char *name, double *obj,
|
|||||||
double val;
|
double val;
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
if (siv->string) {
|
val = strtod(siv->string, &endp);
|
||||||
val = strtod(siv->string, &endp);
|
if (errno || endp == siv->string || *endp) {
|
||||||
}
|
|
||||||
if (!siv->string || errno || endp == siv->string || *endp) {
|
|
||||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||||
"number");
|
"number");
|
||||||
return;
|
return;
|
||||||
@ -314,18 +322,6 @@ static void parse_type_number(Visitor *v, const char *name, double *obj,
|
|||||||
*obj = val;
|
*obj = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_optional(Visitor *v, const char *name, bool *present)
|
|
||||||
{
|
|
||||||
StringInputVisitor *siv = to_siv(v);
|
|
||||||
|
|
||||||
if (!siv->string) {
|
|
||||||
*present = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
*present = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void string_input_free(Visitor *v)
|
static void string_input_free(Visitor *v)
|
||||||
{
|
{
|
||||||
StringInputVisitor *siv = to_siv(v);
|
StringInputVisitor *siv = to_siv(v);
|
||||||
@ -339,6 +335,7 @@ Visitor *string_input_visitor_new(const char *str)
|
|||||||
{
|
{
|
||||||
StringInputVisitor *v;
|
StringInputVisitor *v;
|
||||||
|
|
||||||
|
assert(str);
|
||||||
v = g_malloc0(sizeof(*v));
|
v = g_malloc0(sizeof(*v));
|
||||||
|
|
||||||
v->visitor.type = VISITOR_INPUT;
|
v->visitor.type = VISITOR_INPUT;
|
||||||
@ -350,8 +347,8 @@ Visitor *string_input_visitor_new(const char *str)
|
|||||||
v->visitor.type_number = parse_type_number;
|
v->visitor.type_number = parse_type_number;
|
||||||
v->visitor.start_list = start_list;
|
v->visitor.start_list = start_list;
|
||||||
v->visitor.next_list = next_list;
|
v->visitor.next_list = next_list;
|
||||||
|
v->visitor.check_list = check_list;
|
||||||
v->visitor.end_list = end_list;
|
v->visitor.end_list = end_list;
|
||||||
v->visitor.optional = parse_optional;
|
|
||||||
v->visitor.free = string_input_free;
|
v->visitor.free = string_input_free;
|
||||||
|
|
||||||
v->string = str;
|
v->string = str;
|
||||||
|
@ -8,6 +8,7 @@ visit_end_struct(void *v, void *obj) "v=%p obj=%p"
|
|||||||
|
|
||||||
visit_start_list(void *v, const char *name, void *obj, size_t size) "v=%p name=%s obj=%p size=%zu"
|
visit_start_list(void *v, const char *name, void *obj, size_t size) "v=%p name=%s obj=%p size=%zu"
|
||||||
visit_next_list(void *v, void *tail, size_t size) "v=%p tail=%p size=%zu"
|
visit_next_list(void *v, void *tail, size_t size) "v=%p tail=%p size=%zu"
|
||||||
|
visit_check_list(void *v) "v=%p"
|
||||||
visit_end_list(void *v, void *obj) "v=%p obj=%p"
|
visit_end_list(void *v, void *obj) "v=%p obj=%p"
|
||||||
|
|
||||||
visit_start_alternate(void *v, const char *name, void *obj, size_t size, bool promote_int) "v=%p name=%s obj=%p size=%zu promote_int=%d"
|
visit_start_alternate(void *v, const char *name, void *obj, size_t size, bool promote_int) "v=%p name=%s obj=%p size=%zu promote_int=%d"
|
||||||
|
@ -75,7 +75,7 @@ struct GuestAgentInfo *qmp_guest_info(Error **errp)
|
|||||||
GuestAgentInfo *info = g_new0(GuestAgentInfo, 1);
|
GuestAgentInfo *info = g_new0(GuestAgentInfo, 1);
|
||||||
|
|
||||||
info->version = g_strdup(QEMU_VERSION);
|
info->version = g_strdup(QEMU_VERSION);
|
||||||
qmp_for_each_command(qmp_command_info, info);
|
qmp_for_each_command(&ga_commands, qmp_command_info, info);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +18,9 @@
|
|||||||
|
|
||||||
typedef struct GAState GAState;
|
typedef struct GAState GAState;
|
||||||
typedef struct GACommandState GACommandState;
|
typedef struct GACommandState GACommandState;
|
||||||
|
|
||||||
extern GAState *ga_state;
|
extern GAState *ga_state;
|
||||||
|
extern QmpCommandList ga_commands;
|
||||||
|
|
||||||
GList *ga_command_blacklist_init(GList *blacklist);
|
GList *ga_command_blacklist_init(GList *blacklist);
|
||||||
void ga_command_state_init(GAState *s, GACommandState *cs);
|
void ga_command_state_init(GAState *s, GACommandState *cs);
|
||||||
|
19
qga/main.c
19
qga/main.c
@ -92,6 +92,7 @@ struct GAState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct GAState *ga_state;
|
struct GAState *ga_state;
|
||||||
|
QmpCommandList ga_commands;
|
||||||
|
|
||||||
/* commands that are safe to issue while filesystems are frozen */
|
/* commands that are safe to issue while filesystems are frozen */
|
||||||
static const char *ga_freeze_whitelist[] = {
|
static const char *ga_freeze_whitelist[] = {
|
||||||
@ -370,7 +371,7 @@ static void ga_disable_non_whitelisted(QmpCommand *cmd, void *opaque)
|
|||||||
}
|
}
|
||||||
if (!whitelisted) {
|
if (!whitelisted) {
|
||||||
g_debug("disabling command: %s", name);
|
g_debug("disabling command: %s", name);
|
||||||
qmp_disable_command(name);
|
qmp_disable_command(&ga_commands, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -383,7 +384,7 @@ static void ga_enable_non_blacklisted(QmpCommand *cmd, void *opaque)
|
|||||||
if (g_list_find_custom(blacklist, name, ga_strcmp) == NULL &&
|
if (g_list_find_custom(blacklist, name, ga_strcmp) == NULL &&
|
||||||
!qmp_command_is_enabled(cmd)) {
|
!qmp_command_is_enabled(cmd)) {
|
||||||
g_debug("enabling command: %s", name);
|
g_debug("enabling command: %s", name);
|
||||||
qmp_enable_command(name);
|
qmp_enable_command(&ga_commands, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -420,7 +421,7 @@ void ga_set_frozen(GAState *s)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* disable all non-whitelisted (for frozen state) commands */
|
/* disable all non-whitelisted (for frozen state) commands */
|
||||||
qmp_for_each_command(ga_disable_non_whitelisted, NULL);
|
qmp_for_each_command(&ga_commands, ga_disable_non_whitelisted, NULL);
|
||||||
g_warning("disabling logging due to filesystem freeze");
|
g_warning("disabling logging due to filesystem freeze");
|
||||||
ga_disable_logging(s);
|
ga_disable_logging(s);
|
||||||
s->frozen = true;
|
s->frozen = true;
|
||||||
@ -456,7 +457,7 @@ void ga_unset_frozen(GAState *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* enable all disabled, non-blacklisted commands */
|
/* enable all disabled, non-blacklisted commands */
|
||||||
qmp_for_each_command(ga_enable_non_blacklisted, s->blacklist);
|
qmp_for_each_command(&ga_commands, ga_enable_non_blacklisted, s->blacklist);
|
||||||
s->frozen = false;
|
s->frozen = false;
|
||||||
if (!ga_delete_file(s->state_filepath_isfrozen)) {
|
if (!ga_delete_file(s->state_filepath_isfrozen)) {
|
||||||
g_warning("unable to delete %s, fsfreeze may not function properly",
|
g_warning("unable to delete %s, fsfreeze may not function properly",
|
||||||
@ -555,7 +556,7 @@ static void process_command(GAState *s, QDict *req)
|
|||||||
|
|
||||||
g_assert(req);
|
g_assert(req);
|
||||||
g_debug("processing command");
|
g_debug("processing command");
|
||||||
rsp = qmp_dispatch(QOBJECT(req));
|
rsp = qmp_dispatch(&ga_commands, QOBJECT(req));
|
||||||
if (rsp) {
|
if (rsp) {
|
||||||
ret = send_response(s, rsp);
|
ret = send_response(s, rsp);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -1119,7 +1120,7 @@ static void config_parse(GAConfig *config, int argc, char **argv)
|
|||||||
break;
|
break;
|
||||||
case 'b': {
|
case 'b': {
|
||||||
if (is_help_option(optarg)) {
|
if (is_help_option(optarg)) {
|
||||||
qmp_for_each_command(ga_print_cmd, NULL);
|
qmp_for_each_command(&ga_commands, ga_print_cmd, NULL);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
config->blacklist = g_list_concat(config->blacklist,
|
config->blacklist = g_list_concat(config->blacklist,
|
||||||
@ -1247,7 +1248,7 @@ static int run_agent(GAState *s, GAConfig *config)
|
|||||||
s->deferred_options.log_filepath = config->log_filepath;
|
s->deferred_options.log_filepath = config->log_filepath;
|
||||||
}
|
}
|
||||||
ga_disable_logging(s);
|
ga_disable_logging(s);
|
||||||
qmp_for_each_command(ga_disable_non_whitelisted, NULL);
|
qmp_for_each_command(&ga_commands, ga_disable_non_whitelisted, NULL);
|
||||||
} else {
|
} else {
|
||||||
if (config->daemonize) {
|
if (config->daemonize) {
|
||||||
become_daemon(config->pid_filepath);
|
become_daemon(config->pid_filepath);
|
||||||
@ -1277,7 +1278,7 @@ static int run_agent(GAState *s, GAConfig *config)
|
|||||||
s->blacklist = config->blacklist;
|
s->blacklist = config->blacklist;
|
||||||
do {
|
do {
|
||||||
g_debug("disabling command: %s", (char *)l->data);
|
g_debug("disabling command: %s", (char *)l->data);
|
||||||
qmp_disable_command(l->data);
|
qmp_disable_command(&ga_commands, l->data);
|
||||||
l = g_list_next(l);
|
l = g_list_next(l);
|
||||||
} while (l);
|
} while (l);
|
||||||
}
|
}
|
||||||
@ -1321,7 +1322,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
|
config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL;
|
||||||
|
|
||||||
module_call_init(MODULE_INIT_QAPI);
|
qga_qmp_init_marshal(&ga_commands);
|
||||||
|
|
||||||
init_dfl_pathnames();
|
init_dfl_pathnames();
|
||||||
config_load(config);
|
config_load(config);
|
||||||
|
2
qmp.c
2
qmp.c
@ -675,7 +675,7 @@ void qmp_object_add(const char *type, const char *id,
|
|||||||
pdict = qdict_new();
|
pdict = qdict_new();
|
||||||
}
|
}
|
||||||
|
|
||||||
v = qobject_input_visitor_new(QOBJECT(pdict), true);
|
v = qobject_input_visitor_new(QOBJECT(pdict));
|
||||||
obj = user_creatable_add_type(type, id, pdict, v, errp);
|
obj = user_creatable_add_type(type, id, pdict, v, errp);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
if (obj) {
|
if (obj) {
|
||||||
|
@ -22,8 +22,8 @@ void object_property_set_qobject(Object *obj, QObject *value,
|
|||||||
const char *name, Error **errp)
|
const char *name, Error **errp)
|
||||||
{
|
{
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
/* TODO: Should we reject, rather than ignore, excess input? */
|
|
||||||
v = qobject_input_visitor_new(value, false);
|
v = qobject_input_visitor_new(value);
|
||||||
object_property_set(obj, v, name, errp);
|
object_property_set(obj, v, name, errp);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ def gen_marshal(name, arg_type, boxed, ret_type):
|
|||||||
push_indent()
|
push_indent()
|
||||||
|
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
v = qobject_input_visitor_new(QOBJECT(args), true);
|
v = qobject_input_visitor_new(QOBJECT(args));
|
||||||
visit_start_struct(v, NULL, NULL, 0, &err);
|
visit_start_struct(v, NULL, NULL, 0, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
goto out;
|
goto out;
|
||||||
@ -198,7 +198,8 @@ def gen_register_command(name, success_response):
|
|||||||
options = 'QCO_NO_SUCCESS_RESP'
|
options = 'QCO_NO_SUCCESS_RESP'
|
||||||
|
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
qmp_register_command("%(name)s", qmp_marshal_%(c_name)s, %(opts)s);
|
qmp_register_command(cmds, "%(name)s",
|
||||||
|
qmp_marshal_%(c_name)s, %(opts)s);
|
||||||
''',
|
''',
|
||||||
name=name, c_name=c_name(name),
|
name=name, c_name=c_name(name),
|
||||||
opts=options)
|
opts=options)
|
||||||
@ -208,14 +209,15 @@ def gen_register_command(name, success_response):
|
|||||||
def gen_registry(registry):
|
def gen_registry(registry):
|
||||||
ret = mcgen('''
|
ret = mcgen('''
|
||||||
|
|
||||||
static void qmp_init_marshal(void)
|
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds)
|
||||||
{
|
{
|
||||||
''')
|
QTAILQ_INIT(cmds);
|
||||||
|
|
||||||
|
''',
|
||||||
|
c_prefix=c_name(prefix, protect=False))
|
||||||
ret += registry
|
ret += registry
|
||||||
ret += mcgen('''
|
ret += mcgen('''
|
||||||
}
|
}
|
||||||
|
|
||||||
qapi_init(qmp_init_marshal);
|
|
||||||
''')
|
''')
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@ -291,7 +293,6 @@ fdef.write(mcgen('''
|
|||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/module.h"
|
#include "qemu/module.h"
|
||||||
#include "qapi/qmp/types.h"
|
#include "qapi/qmp/types.h"
|
||||||
#include "qapi/qmp/dispatch.h"
|
|
||||||
#include "qapi/visitor.h"
|
#include "qapi/visitor.h"
|
||||||
#include "qapi/qobject-output-visitor.h"
|
#include "qapi/qobject-output-visitor.h"
|
||||||
#include "qapi/qobject-input-visitor.h"
|
#include "qapi/qobject-input-visitor.h"
|
||||||
@ -306,10 +307,12 @@ fdef.write(mcgen('''
|
|||||||
fdecl.write(mcgen('''
|
fdecl.write(mcgen('''
|
||||||
#include "%(prefix)sqapi-types.h"
|
#include "%(prefix)sqapi-types.h"
|
||||||
#include "qapi/qmp/qdict.h"
|
#include "qapi/qmp/qdict.h"
|
||||||
|
#include "qapi/qmp/dispatch.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
|
|
||||||
|
void %(c_prefix)sqmp_init_marshal(QmpCommandList *cmds);
|
||||||
''',
|
''',
|
||||||
prefix=prefix))
|
prefix=prefix, c_prefix=c_name(prefix, protect=False)))
|
||||||
|
|
||||||
schema = QAPISchema(input_file)
|
schema = QAPISchema(input_file)
|
||||||
gen = QAPISchemaGenCommandVisitor()
|
gen = QAPISchemaGenCommandVisitor()
|
||||||
|
@ -64,7 +64,7 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaVisitor):
|
|||||||
# generate C
|
# generate C
|
||||||
# TODO can generate awfully long lines
|
# TODO can generate awfully long lines
|
||||||
jsons.extend(self._jsons)
|
jsons.extend(self._jsons)
|
||||||
name = prefix + 'qmp_schema_json'
|
name = c_name(prefix, protect=False) + 'qmp_schema_json'
|
||||||
self.decl = mcgen('''
|
self.decl = mcgen('''
|
||||||
extern const char %(c_name)s[];
|
extern const char %(c_name)s[];
|
||||||
''',
|
''',
|
||||||
|
@ -133,6 +133,9 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
visit_check_list(v, &err);
|
||||||
|
}
|
||||||
visit_end_list(v, (void **)obj);
|
visit_end_list(v, (void **)obj);
|
||||||
if (err && visit_is_input(v)) {
|
if (err && visit_is_input(v)) {
|
||||||
qapi_free_%(c_name)s(*obj);
|
qapi_free_%(c_name)s(*obj);
|
||||||
|
@ -346,7 +346,7 @@ static void cpu_model_from_info(S390CPUModel *model, const CpuModelInfo *info,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (qdict) {
|
if (qdict) {
|
||||||
visitor = qobject_input_visitor_new(info->props, true);
|
visitor = qobject_input_visitor_new(info->props);
|
||||||
visit_start_struct(visitor, NULL, NULL, 0, errp);
|
visit_start_struct(visitor, NULL, NULL, 0, errp);
|
||||||
if (*errp) {
|
if (*errp) {
|
||||||
object_unref(obj);
|
object_unref(obj);
|
||||||
|
@ -28,7 +28,6 @@ check-unit-y += tests/test-clone-visitor$(EXESUF)
|
|||||||
gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c
|
gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c
|
||||||
check-unit-y += tests/test-qobject-input-visitor$(EXESUF)
|
check-unit-y += tests/test-qobject-input-visitor$(EXESUF)
|
||||||
gcov-files-test-qobject-input-visitor-y = qapi/qobject-input-visitor.c
|
gcov-files-test-qobject-input-visitor-y = qapi/qobject-input-visitor.c
|
||||||
check-unit-y += tests/test-qobject-input-strict$(EXESUF)
|
|
||||||
check-unit-y += tests/test-qmp-commands$(EXESUF)
|
check-unit-y += tests/test-qmp-commands$(EXESUF)
|
||||||
gcov-files-test-qmp-commands-y = qapi/qmp-dispatch.c
|
gcov-files-test-qmp-commands-y = qapi/qmp-dispatch.c
|
||||||
check-unit-y += tests/test-string-input-visitor$(EXESUF)
|
check-unit-y += tests/test-string-input-visitor$(EXESUF)
|
||||||
@ -133,7 +132,9 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
|
|||||||
# All QTests for now are POSIX-only, but the dependencies are
|
# All QTests for now are POSIX-only, but the dependencies are
|
||||||
# really in libqtest, not in the testcases themselves.
|
# really in libqtest, not in the testcases themselves.
|
||||||
|
|
||||||
check-qtest-generic-y = tests/device-introspect-test$(EXESUF)
|
check-qtest-generic-y = tests/qmp-test$(EXESUF)
|
||||||
|
gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c
|
||||||
|
check-qtest-generic-y += tests/device-introspect-test$(EXESUF)
|
||||||
gcov-files-generic-y = qdev-monitor.c qmp.c
|
gcov-files-generic-y = qdev-monitor.c qmp.c
|
||||||
|
|
||||||
gcov-files-ipack-y += hw/ipack/ipack.c
|
gcov-files-ipack-y += hw/ipack/ipack.c
|
||||||
@ -487,7 +488,7 @@ test-obj-y = tests/check-qint.o tests/check-qstring.o tests/check-qdict.o \
|
|||||||
tests/test-coroutine.o tests/test-string-output-visitor.o \
|
tests/test-coroutine.o tests/test-string-output-visitor.o \
|
||||||
tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \
|
tests/test-string-input-visitor.o tests/test-qobject-output-visitor.o \
|
||||||
tests/test-clone-visitor.o \
|
tests/test-clone-visitor.o \
|
||||||
tests/test-qobject-input-visitor.o tests/test-qobject-input-strict.o \
|
tests/test-qobject-input-visitor.o \
|
||||||
tests/test-qmp-commands.o tests/test-visitor-serialization.o \
|
tests/test-qmp-commands.o tests/test-visitor-serialization.o \
|
||||||
tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \
|
tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \
|
||||||
tests/test-opts-visitor.o tests/test-qmp-event.o \
|
tests/test-opts-visitor.o tests/test-qmp-event.o \
|
||||||
@ -596,7 +597,6 @@ tests/test-qmp-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y)
|
|||||||
tests/test-qobject-output-visitor$(EXESUF): tests/test-qobject-output-visitor.o $(test-qapi-obj-y)
|
tests/test-qobject-output-visitor$(EXESUF): tests/test-qobject-output-visitor.o $(test-qapi-obj-y)
|
||||||
tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y)
|
tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y)
|
||||||
tests/test-qobject-input-visitor$(EXESUF): tests/test-qobject-input-visitor.o $(test-qapi-obj-y)
|
tests/test-qobject-input-visitor$(EXESUF): tests/test-qobject-input-visitor.o $(test-qapi-obj-y)
|
||||||
tests/test-qobject-input-strict$(EXESUF): tests/test-qobject-input-strict.o $(test-qapi-obj-y)
|
|
||||||
tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y)
|
tests/test-qmp-commands$(EXESUF): tests/test-qmp-commands.o tests/test-qmp-marshal.o $(test-qapi-obj-y)
|
||||||
tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y)
|
tests/test-visitor-serialization$(EXESUF): tests/test-visitor-serialization.o $(test-qapi-obj-y)
|
||||||
tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y)
|
tests/test-opts-visitor$(EXESUF): tests/test-opts-visitor.o $(test-qapi-obj-y)
|
||||||
@ -653,6 +653,7 @@ libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o
|
|||||||
libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o
|
libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o
|
||||||
libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
|
libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
|
||||||
|
|
||||||
|
tests/qmp-test$(EXESUF): tests/qmp-test.o
|
||||||
tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o
|
tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o
|
||||||
tests/rtc-test$(EXESUF): tests/rtc-test.o
|
tests/rtc-test$(EXESUF): tests/rtc-test.o
|
||||||
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
|
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
|
||||||
|
@ -47,7 +47,7 @@ static void qnull_visit_test(void)
|
|||||||
|
|
||||||
g_assert(qnull_.refcnt == 1);
|
g_assert(qnull_.refcnt == 1);
|
||||||
obj = qnull();
|
obj = qnull();
|
||||||
v = qobject_input_visitor_new(obj, true);
|
v = qobject_input_visitor_new(obj);
|
||||||
qobject_decref(obj);
|
qobject_decref(obj);
|
||||||
visit_type_null(v, NULL, &error_abort);
|
visit_type_null(v, NULL, &error_abort);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
|
@ -149,7 +149,7 @@ void qtest_add_abrt_handler(GHookFunc fn, const void *data)
|
|||||||
g_hook_prepend(&abrt_hooks, hook);
|
g_hook_prepend(&abrt_hooks, hook);
|
||||||
}
|
}
|
||||||
|
|
||||||
QTestState *qtest_init(const char *extra_args)
|
QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
|
||||||
{
|
{
|
||||||
QTestState *s;
|
QTestState *s;
|
||||||
int sock, qmpsock, i;
|
int sock, qmpsock, i;
|
||||||
@ -204,10 +204,6 @@ QTestState *qtest_init(const char *extra_args)
|
|||||||
s->irq_level[i] = false;
|
s->irq_level[i] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read the QMP greeting and then do the handshake */
|
|
||||||
qtest_qmp_discard_response(s, "");
|
|
||||||
qtest_qmp_discard_response(s, "{ 'execute': 'qmp_capabilities' }");
|
|
||||||
|
|
||||||
if (getenv("QTEST_STOP")) {
|
if (getenv("QTEST_STOP")) {
|
||||||
kill(s->qemu_pid, SIGSTOP);
|
kill(s->qemu_pid, SIGSTOP);
|
||||||
}
|
}
|
||||||
@ -219,6 +215,17 @@ QTestState *qtest_init(const char *extra_args)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTestState *qtest_init(const char *extra_args)
|
||||||
|
{
|
||||||
|
QTestState *s = qtest_init_without_qmp_handshake(extra_args);
|
||||||
|
|
||||||
|
/* Read the QMP greeting and then do the handshake */
|
||||||
|
qtest_qmp_discard_response(s, "");
|
||||||
|
qtest_qmp_discard_response(s, "{ 'execute': 'qmp_capabilities' }");
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
void qtest_quit(QTestState *s)
|
void qtest_quit(QTestState *s)
|
||||||
{
|
{
|
||||||
qtest_instances = g_list_remove(qtest_instances, s);
|
qtest_instances = g_list_remove(qtest_instances, s);
|
||||||
@ -442,14 +449,20 @@ void qmp_fd_sendv(int fd, const char *fmt, va_list ap)
|
|||||||
if (qobj) {
|
if (qobj) {
|
||||||
int log = getenv("QTEST_LOG") != NULL;
|
int log = getenv("QTEST_LOG") != NULL;
|
||||||
QString *qstr = qobject_to_json(qobj);
|
QString *qstr = qobject_to_json(qobj);
|
||||||
const char *str = qstring_get_str(qstr);
|
const char *str;
|
||||||
size_t size = qstring_get_length(qstr);
|
|
||||||
|
/*
|
||||||
|
* BUG: QMP doesn't react to input until it sees a newline, an
|
||||||
|
* object, or an array. Work-around: give it a newline.
|
||||||
|
*/
|
||||||
|
qstring_append_chr(qstr, '\n');
|
||||||
|
str = qstring_get_str(qstr);
|
||||||
|
|
||||||
if (log) {
|
if (log) {
|
||||||
fprintf(stderr, "%s", str);
|
fprintf(stderr, "%s", str);
|
||||||
}
|
}
|
||||||
/* Send QMP request */
|
/* Send QMP request */
|
||||||
socket_send(fd, str, size);
|
socket_send(fd, str, qstring_get_length(qstr));
|
||||||
|
|
||||||
QDECREF(qstr);
|
QDECREF(qstr);
|
||||||
qobject_decref(qobj);
|
qobject_decref(qobj);
|
||||||
|
@ -31,6 +31,14 @@ extern QTestState *global_qtest;
|
|||||||
*/
|
*/
|
||||||
QTestState *qtest_init(const char *extra_args);
|
QTestState *qtest_init(const char *extra_args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* qtest_init_without_qmp_handshake:
|
||||||
|
* @extra_args: other arguments to pass to QEMU.
|
||||||
|
*
|
||||||
|
* Returns: #QTestState instance.
|
||||||
|
*/
|
||||||
|
QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* qtest_quit:
|
* qtest_quit:
|
||||||
* @s: #QTestState instance to operate on.
|
* @s: #QTestState instance to operate on.
|
||||||
|
139
tests/qmp-test.c
Normal file
139
tests/qmp-test.c
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* QMP protocol test cases
|
||||||
|
*
|
||||||
|
* Copyright (c) 2017 Red Hat Inc.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Markus Armbruster <armbru@redhat.com>,
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
#include "libqtest.h"
|
||||||
|
#include "qapi-visit.h"
|
||||||
|
#include "qapi/error.h"
|
||||||
|
#include "qapi/qobject-input-visitor.h"
|
||||||
|
#include "qapi/visitor.h"
|
||||||
|
|
||||||
|
const char common_args[] = "-nodefaults -machine none";
|
||||||
|
|
||||||
|
static const char *get_error_class(QDict *resp)
|
||||||
|
{
|
||||||
|
QDict *error = qdict_get_qdict(resp, "error");
|
||||||
|
const char *desc = qdict_get_try_str(error, "desc");
|
||||||
|
|
||||||
|
g_assert(desc);
|
||||||
|
return error ? qdict_get_try_str(error, "class") : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_version(QObject *version)
|
||||||
|
{
|
||||||
|
Visitor *v;
|
||||||
|
VersionInfo *vinfo;
|
||||||
|
|
||||||
|
g_assert(version);
|
||||||
|
v = qobject_input_visitor_new(version);
|
||||||
|
visit_type_VersionInfo(v, "version", &vinfo, &error_abort);
|
||||||
|
qapi_free_VersionInfo(vinfo);
|
||||||
|
visit_free(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_malformed(void)
|
||||||
|
{
|
||||||
|
QDict *resp;
|
||||||
|
|
||||||
|
/* Not even a dictionary */
|
||||||
|
resp = qmp("null");
|
||||||
|
g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* No "execute" key */
|
||||||
|
resp = qmp("{}");
|
||||||
|
g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* "execute" isn't a string */
|
||||||
|
resp = qmp("{ 'execute': true }");
|
||||||
|
g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* "arguments" isn't a dictionary */
|
||||||
|
resp = qmp("{ 'execute': 'no-such-cmd', 'arguments': [] }");
|
||||||
|
g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* extra key */
|
||||||
|
resp = qmp("{ 'execute': 'no-such-cmd', 'extra': true }");
|
||||||
|
g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
|
||||||
|
QDECREF(resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_qmp_protocol(void)
|
||||||
|
{
|
||||||
|
QDict *resp, *q, *ret;
|
||||||
|
QList *capabilities;
|
||||||
|
|
||||||
|
global_qtest = qtest_init_without_qmp_handshake(common_args);
|
||||||
|
|
||||||
|
/* Test greeting */
|
||||||
|
resp = qmp_receive();
|
||||||
|
q = qdict_get_qdict(resp, "QMP");
|
||||||
|
g_assert(q);
|
||||||
|
test_version(qdict_get(q, "version"));
|
||||||
|
capabilities = qdict_get_qlist(q, "capabilities");
|
||||||
|
g_assert(capabilities && qlist_empty(capabilities));
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* Test valid command before handshake */
|
||||||
|
resp = qmp("{ 'execute': 'query-version' }");
|
||||||
|
g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound");
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* Test malformed commands before handshake */
|
||||||
|
test_malformed();
|
||||||
|
|
||||||
|
/* Test handshake */
|
||||||
|
resp = qmp("{ 'execute': 'qmp_capabilities' }");
|
||||||
|
ret = qdict_get_qdict(resp, "return");
|
||||||
|
g_assert(ret && !qdict_size(ret));
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* Test repeated handshake */
|
||||||
|
resp = qmp("{ 'execute': 'qmp_capabilities' }");
|
||||||
|
g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound");
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* Test valid command */
|
||||||
|
resp = qmp("{ 'execute': 'query-version' }");
|
||||||
|
test_version(qdict_get(resp, "return"));
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* Test malformed commands */
|
||||||
|
test_malformed();
|
||||||
|
|
||||||
|
/* Test 'id' */
|
||||||
|
resp = qmp("{ 'execute': 'query-name', 'id': 'cookie#1' }");
|
||||||
|
ret = qdict_get_qdict(resp, "return");
|
||||||
|
g_assert(ret);
|
||||||
|
g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1");
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
/* Test command failure with 'id' */
|
||||||
|
resp = qmp("{ 'execute': 'human-monitor-command', 'id': 2 }");
|
||||||
|
g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
|
||||||
|
g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2);
|
||||||
|
QDECREF(resp);
|
||||||
|
|
||||||
|
qtest_end();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
g_test_init(&argc, &argv, NULL);
|
||||||
|
|
||||||
|
qtest_add_func("qmp/protocol", test_qmp_protocol);
|
||||||
|
|
||||||
|
return g_test_run();
|
||||||
|
}
|
@ -172,6 +172,81 @@ expect_u64_max(OptsVisitorFixture *f, gconstpointer test_data)
|
|||||||
|
|
||||||
/* test cases */
|
/* test cases */
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_opts_range_unvisited(void)
|
||||||
|
{
|
||||||
|
intList *list = NULL;
|
||||||
|
intList *tail;
|
||||||
|
QemuOpts *opts;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
opts = qemu_opts_parse(qemu_find_opts("userdef"), "ilist=0-2", false,
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
v = opts_visitor_new(opts);
|
||||||
|
|
||||||
|
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||||
|
|
||||||
|
/* Would be simpler if the visitor genuinely supported virtual walks */
|
||||||
|
visit_start_list(v, "ilist", (GenericList **)&list, sizeof(*list),
|
||||||
|
&error_abort);
|
||||||
|
tail = list;
|
||||||
|
visit_type_int(v, NULL, &tail->value, &error_abort);
|
||||||
|
g_assert_cmpint(tail->value, ==, 0);
|
||||||
|
tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*list));
|
||||||
|
g_assert(tail);
|
||||||
|
visit_type_int(v, NULL, &tail->value, &error_abort);
|
||||||
|
g_assert_cmpint(tail->value, ==, 1);
|
||||||
|
tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*list));
|
||||||
|
g_assert(tail);
|
||||||
|
visit_check_list(v, &error_abort); /* BUG: unvisited tail not reported */
|
||||||
|
visit_end_list(v, (void **)&list);
|
||||||
|
|
||||||
|
visit_check_struct(v, &error_abort);
|
||||||
|
visit_end_struct(v, NULL);
|
||||||
|
|
||||||
|
qapi_free_intList(list);
|
||||||
|
visit_free(v);
|
||||||
|
qemu_opts_del(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_opts_range_beyond(void)
|
||||||
|
{
|
||||||
|
Error *err = NULL;
|
||||||
|
intList *list = NULL;
|
||||||
|
intList *tail;
|
||||||
|
QemuOpts *opts;
|
||||||
|
Visitor *v;
|
||||||
|
int64_t val;
|
||||||
|
|
||||||
|
opts = qemu_opts_parse(qemu_find_opts("userdef"), "ilist=0", false,
|
||||||
|
&error_abort);
|
||||||
|
|
||||||
|
v = opts_visitor_new(opts);
|
||||||
|
|
||||||
|
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||||
|
|
||||||
|
/* Would be simpler if the visitor genuinely supported virtual walks */
|
||||||
|
visit_start_list(v, "ilist", (GenericList **)&list, sizeof(*list),
|
||||||
|
&error_abort);
|
||||||
|
tail = list;
|
||||||
|
visit_type_int(v, NULL, &tail->value, &error_abort);
|
||||||
|
g_assert_cmpint(tail->value, ==, 0);
|
||||||
|
tail = (intList *)visit_next_list(v, (GenericList *)tail, sizeof(*tail));
|
||||||
|
g_assert(!tail);
|
||||||
|
visit_type_int(v, NULL, &val, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_end_list(v, (void **)&list);
|
||||||
|
|
||||||
|
visit_check_struct(v, &error_abort);
|
||||||
|
visit_end_struct(v, NULL);
|
||||||
|
|
||||||
|
qapi_free_intList(list);
|
||||||
|
visit_free(v);
|
||||||
|
qemu_opts_del(opts);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@ -263,6 +338,11 @@ main(int argc, char **argv)
|
|||||||
add_test("/visitor/opts/i64/range/2big/full", &expect_fail,
|
add_test("/visitor/opts/i64/range/2big/full", &expect_fail,
|
||||||
"i64=-0x8000000000000000-0x7fffffffffffffff");
|
"i64=-0x8000000000000000-0x7fffffffffffffff");
|
||||||
|
|
||||||
|
g_test_add_func("/visitor/opts/range/unvisited",
|
||||||
|
test_opts_range_unvisited);
|
||||||
|
g_test_add_func("/visitor/opts/range/beyond",
|
||||||
|
test_opts_range_beyond);
|
||||||
|
|
||||||
g_test_run();
|
g_test_run();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -213,7 +213,7 @@ static void test_qga_invalid_args(gconstpointer fix)
|
|||||||
desc = qdict_get_try_str(error, "desc");
|
desc = qdict_get_try_str(error, "desc");
|
||||||
|
|
||||||
g_assert_cmpstr(class, ==, "GenericError");
|
g_assert_cmpstr(class, ==, "GenericError");
|
||||||
g_assert_cmpstr(desc, ==, "QMP input object member 'foo' is unexpected");
|
g_assert_cmpstr(desc, ==, "Parameter 'foo' is unexpected");
|
||||||
|
|
||||||
QDECREF(ret);
|
QDECREF(ret);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#include "tests/test-qapi-types.h"
|
#include "tests/test-qapi-types.h"
|
||||||
#include "tests/test-qapi-visit.h"
|
#include "tests/test-qapi-visit.h"
|
||||||
|
|
||||||
|
static QmpCommandList qmp_commands;
|
||||||
|
|
||||||
void qmp_user_def_cmd(Error **errp)
|
void qmp_user_def_cmd(Error **errp)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -94,7 +96,7 @@ static void test_dispatch_cmd(void)
|
|||||||
|
|
||||||
qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
|
qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
|
||||||
|
|
||||||
resp = qmp_dispatch(QOBJECT(req));
|
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
|
||||||
assert(resp != NULL);
|
assert(resp != NULL);
|
||||||
assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
|
assert(!qdict_haskey(qobject_to_qdict(resp), "error"));
|
||||||
|
|
||||||
@ -111,7 +113,7 @@ static void test_dispatch_cmd_failure(void)
|
|||||||
|
|
||||||
qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
|
qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd2")));
|
||||||
|
|
||||||
resp = qmp_dispatch(QOBJECT(req));
|
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
|
||||||
assert(resp != NULL);
|
assert(resp != NULL);
|
||||||
assert(qdict_haskey(qobject_to_qdict(resp), "error"));
|
assert(qdict_haskey(qobject_to_qdict(resp), "error"));
|
||||||
|
|
||||||
@ -125,7 +127,7 @@ static void test_dispatch_cmd_failure(void)
|
|||||||
|
|
||||||
qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
|
qdict_put_obj(req, "execute", QOBJECT(qstring_from_str("user_def_cmd")));
|
||||||
|
|
||||||
resp = qmp_dispatch(QOBJECT(req));
|
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
|
||||||
assert(resp != NULL);
|
assert(resp != NULL);
|
||||||
assert(qdict_haskey(qobject_to_qdict(resp), "error"));
|
assert(qdict_haskey(qobject_to_qdict(resp), "error"));
|
||||||
|
|
||||||
@ -139,7 +141,7 @@ static QObject *test_qmp_dispatch(QDict *req)
|
|||||||
QDict *resp;
|
QDict *resp;
|
||||||
QObject *ret;
|
QObject *ret;
|
||||||
|
|
||||||
resp_obj = qmp_dispatch(QOBJECT(req));
|
resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req));
|
||||||
assert(resp_obj);
|
assert(resp_obj);
|
||||||
resp = qobject_to_qdict(resp_obj);
|
resp = qobject_to_qdict(resp_obj);
|
||||||
assert(resp && !qdict_haskey(resp, "error"));
|
assert(resp && !qdict_haskey(resp, "error"));
|
||||||
@ -244,7 +246,7 @@ static void test_dealloc_partial(void)
|
|||||||
ud2_dict = qdict_new();
|
ud2_dict = qdict_new();
|
||||||
qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));
|
qdict_put_obj(ud2_dict, "string0", QOBJECT(qstring_from_str(text)));
|
||||||
|
|
||||||
v = qobject_input_visitor_new(QOBJECT(ud2_dict), true);
|
v = qobject_input_visitor_new(QOBJECT(ud2_dict));
|
||||||
visit_type_UserDefTwo(v, NULL, &ud2, &err);
|
visit_type_UserDefTwo(v, NULL, &ud2, &err);
|
||||||
visit_free(v);
|
visit_free(v);
|
||||||
QDECREF(ud2_dict);
|
QDECREF(ud2_dict);
|
||||||
@ -273,7 +275,7 @@ int main(int argc, char **argv)
|
|||||||
g_test_add_func("/0.15/dealloc_types", test_dealloc_types);
|
g_test_add_func("/0.15/dealloc_types", test_dealloc_types);
|
||||||
g_test_add_func("/0.15/dealloc_partial", test_dealloc_partial);
|
g_test_add_func("/0.15/dealloc_partial", test_dealloc_partial);
|
||||||
|
|
||||||
module_call_init(MODULE_INIT_QAPI);
|
test_qmp_init_marshal(&qmp_commands);
|
||||||
g_test_run();
|
g_test_run();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1,381 +0,0 @@
|
|||||||
/*
|
|
||||||
* QObject Input Visitor unit-tests (strict mode).
|
|
||||||
*
|
|
||||||
* Copyright (C) 2011-2012, 2015 Red Hat Inc.
|
|
||||||
*
|
|
||||||
* Authors:
|
|
||||||
* Luiz Capitulino <lcapitulino@redhat.com>
|
|
||||||
* Paolo Bonzini <pbonzini@redhat.com>
|
|
||||||
*
|
|
||||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
||||||
* See the COPYING file in the top-level directory.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "qemu/osdep.h"
|
|
||||||
|
|
||||||
#include "qemu-common.h"
|
|
||||||
#include "qapi/error.h"
|
|
||||||
#include "qapi/qobject-input-visitor.h"
|
|
||||||
#include "test-qapi-types.h"
|
|
||||||
#include "test-qapi-visit.h"
|
|
||||||
#include "qapi/qmp/types.h"
|
|
||||||
#include "qapi/qmp/qjson.h"
|
|
||||||
#include "test-qmp-introspect.h"
|
|
||||||
#include "qmp-introspect.h"
|
|
||||||
#include "qapi-visit.h"
|
|
||||||
|
|
||||||
typedef struct TestInputVisitorData {
|
|
||||||
QObject *obj;
|
|
||||||
Visitor *qiv;
|
|
||||||
} TestInputVisitorData;
|
|
||||||
|
|
||||||
static void validate_teardown(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
qobject_decref(data->obj);
|
|
||||||
data->obj = NULL;
|
|
||||||
|
|
||||||
if (data->qiv) {
|
|
||||||
visit_free(data->qiv);
|
|
||||||
data->qiv = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The various test_init functions are provided instead of a test setup
|
|
||||||
function so that the JSON string used by the tests are kept in the test
|
|
||||||
functions (and not in main()). */
|
|
||||||
static Visitor *validate_test_init_internal(TestInputVisitorData *data,
|
|
||||||
const char *json_string,
|
|
||||||
va_list *ap)
|
|
||||||
{
|
|
||||||
validate_teardown(data, NULL);
|
|
||||||
|
|
||||||
data->obj = qobject_from_jsonv(json_string, ap);
|
|
||||||
g_assert(data->obj);
|
|
||||||
|
|
||||||
data->qiv = qobject_input_visitor_new(data->obj, true);
|
|
||||||
g_assert(data->qiv);
|
|
||||||
return data->qiv;
|
|
||||||
}
|
|
||||||
|
|
||||||
static GCC_FMT_ATTR(2, 3)
|
|
||||||
Visitor *validate_test_init(TestInputVisitorData *data,
|
|
||||||
const char *json_string, ...)
|
|
||||||
{
|
|
||||||
Visitor *v;
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, json_string);
|
|
||||||
v = validate_test_init_internal(data, json_string, &ap);
|
|
||||||
va_end(ap);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* similar to validate_test_init(), but does not expect a string
|
|
||||||
* literal/format json_string argument and so can be used for
|
|
||||||
* programatically generated strings (and we can't pass in programatically
|
|
||||||
* generated strings via %s format parameters since qobject_from_jsonv()
|
|
||||||
* will wrap those in double-quotes and treat the entire object as a
|
|
||||||
* string)
|
|
||||||
*/
|
|
||||||
static Visitor *validate_test_init_raw(TestInputVisitorData *data,
|
|
||||||
const char *json_string)
|
|
||||||
{
|
|
||||||
return validate_test_init_internal(data, json_string, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void test_validate_struct(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
TestStruct *p = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo' }");
|
|
||||||
|
|
||||||
visit_type_TestStruct(v, NULL, &p, &error_abort);
|
|
||||||
g_free(p->string);
|
|
||||||
g_free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_struct_nested(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefTwo *udp = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "{ 'string0': 'string0', "
|
|
||||||
"'dict1': { 'string1': 'string1', "
|
|
||||||
"'dict2': { 'userdef': { 'integer': 42, "
|
|
||||||
"'string': 'string' }, 'string': 'string2'}}}");
|
|
||||||
|
|
||||||
visit_type_UserDefTwo(v, NULL, &udp, &error_abort);
|
|
||||||
qapi_free_UserDefTwo(udp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_list(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefOneList *head = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44 } ]");
|
|
||||||
|
|
||||||
visit_type_UserDefOneList(v, NULL, &head, &error_abort);
|
|
||||||
qapi_free_UserDefOneList(head);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_union_native_list(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefNativeListUnion *tmp = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "{ 'type': 'integer', 'data' : [ 1, 2 ] }");
|
|
||||||
|
|
||||||
visit_type_UserDefNativeListUnion(v, NULL, &tmp, &error_abort);
|
|
||||||
qapi_free_UserDefNativeListUnion(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_union_flat(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefFlatUnion *tmp = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data,
|
|
||||||
"{ 'enum1': 'value1', "
|
|
||||||
"'integer': 41, "
|
|
||||||
"'string': 'str', "
|
|
||||||
"'boolean': true }");
|
|
||||||
|
|
||||||
visit_type_UserDefFlatUnion(v, NULL, &tmp, &error_abort);
|
|
||||||
qapi_free_UserDefFlatUnion(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_alternate(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefAlternate *tmp = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "42");
|
|
||||||
|
|
||||||
visit_type_UserDefAlternate(v, NULL, &tmp, &error_abort);
|
|
||||||
qapi_free_UserDefAlternate(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_fail_struct(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
TestStruct *p = NULL;
|
|
||||||
Error *err = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }");
|
|
||||||
|
|
||||||
visit_type_TestStruct(v, NULL, &p, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
g_assert(!p);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_fail_struct_nested(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefTwo *udp = NULL;
|
|
||||||
Error *err = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}");
|
|
||||||
|
|
||||||
visit_type_UserDefTwo(v, NULL, &udp, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
g_assert(!udp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_fail_struct_missing(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
Error *err = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
QObject *any;
|
|
||||||
GenericAlternate *alt;
|
|
||||||
bool present;
|
|
||||||
int en;
|
|
||||||
int64_t i64;
|
|
||||||
uint32_t u32;
|
|
||||||
int8_t i8;
|
|
||||||
char *str;
|
|
||||||
double dbl;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "{}");
|
|
||||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
|
||||||
visit_start_struct(v, "struct", NULL, 0, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_start_list(v, "list", NULL, 0, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_start_alternate(v, "alternate", &alt, sizeof(*alt), false, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_optional(v, "optional", &present);
|
|
||||||
g_assert(!present);
|
|
||||||
visit_type_enum(v, "enum", &en, EnumOne_lookup, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_type_int(v, "i64", &i64, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_type_uint32(v, "u32", &u32, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_type_int8(v, "i8", &i8, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_type_str(v, "i8", &str, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_type_number(v, "dbl", &dbl, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_type_any(v, "any", &any, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_type_null(v, "null", &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_end_struct(v, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_fail_list(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefOneList *head = NULL;
|
|
||||||
Error *err = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]");
|
|
||||||
|
|
||||||
visit_type_UserDefOneList(v, NULL, &head, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
g_assert(!head);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_fail_union_native_list(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefNativeListUnion *tmp = NULL;
|
|
||||||
Error *err = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data,
|
|
||||||
"{ 'type': 'integer', 'data' : [ 'string' ] }");
|
|
||||||
|
|
||||||
visit_type_UserDefNativeListUnion(v, NULL, &tmp, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
g_assert(!tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_fail_union_flat(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefFlatUnion *tmp = NULL;
|
|
||||||
Error *err = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "{ 'string': 'c', 'integer': 41, 'boolean': true }");
|
|
||||||
|
|
||||||
visit_type_UserDefFlatUnion(v, NULL, &tmp, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
g_assert(!tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefFlatUnion2 *tmp = NULL;
|
|
||||||
Error *err = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
/* test situation where discriminator field ('enum1' here) is missing */
|
|
||||||
v = validate_test_init(data, "{ 'integer': 42, 'string': 'c', 'string1': 'd', 'string2': 'e' }");
|
|
||||||
|
|
||||||
visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
g_assert(!tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_fail_alternate(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
UserDefAlternate *tmp;
|
|
||||||
Visitor *v;
|
|
||||||
Error *err = NULL;
|
|
||||||
|
|
||||||
v = validate_test_init(data, "3.14");
|
|
||||||
|
|
||||||
visit_type_UserDefAlternate(v, NULL, &tmp, &err);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
g_assert(!tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_test_validate_qmp_introspect(TestInputVisitorData *data,
|
|
||||||
const char *schema_json)
|
|
||||||
{
|
|
||||||
SchemaInfoList *schema = NULL;
|
|
||||||
Visitor *v;
|
|
||||||
|
|
||||||
v = validate_test_init_raw(data, schema_json);
|
|
||||||
|
|
||||||
visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
|
|
||||||
g_assert(schema);
|
|
||||||
|
|
||||||
qapi_free_SchemaInfoList(schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_validate_qmp_introspect(TestInputVisitorData *data,
|
|
||||||
const void *unused)
|
|
||||||
{
|
|
||||||
do_test_validate_qmp_introspect(data, test_qmp_schema_json);
|
|
||||||
do_test_validate_qmp_introspect(data, qmp_schema_json);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void validate_test_add(const char *testpath,
|
|
||||||
TestInputVisitorData *data,
|
|
||||||
void (*test_func)(TestInputVisitorData *data, const void *user_data))
|
|
||||||
{
|
|
||||||
g_test_add(testpath, TestInputVisitorData, data, NULL, test_func,
|
|
||||||
validate_teardown);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
TestInputVisitorData testdata;
|
|
||||||
|
|
||||||
g_test_init(&argc, &argv, NULL);
|
|
||||||
|
|
||||||
validate_test_add("/visitor/input-strict/pass/struct",
|
|
||||||
&testdata, test_validate_struct);
|
|
||||||
validate_test_add("/visitor/input-strict/pass/struct-nested",
|
|
||||||
&testdata, test_validate_struct_nested);
|
|
||||||
validate_test_add("/visitor/input-strict/pass/list",
|
|
||||||
&testdata, test_validate_list);
|
|
||||||
validate_test_add("/visitor/input-strict/pass/union-flat",
|
|
||||||
&testdata, test_validate_union_flat);
|
|
||||||
validate_test_add("/visitor/input-strict/pass/alternate",
|
|
||||||
&testdata, test_validate_alternate);
|
|
||||||
validate_test_add("/visitor/input-strict/pass/union-native-list",
|
|
||||||
&testdata, test_validate_union_native_list);
|
|
||||||
validate_test_add("/visitor/input-strict/fail/struct",
|
|
||||||
&testdata, test_validate_fail_struct);
|
|
||||||
validate_test_add("/visitor/input-strict/fail/struct-nested",
|
|
||||||
&testdata, test_validate_fail_struct_nested);
|
|
||||||
validate_test_add("/visitor/input-strict/fail/struct-missing",
|
|
||||||
&testdata, test_validate_fail_struct_missing);
|
|
||||||
validate_test_add("/visitor/input-strict/fail/list",
|
|
||||||
&testdata, test_validate_fail_list);
|
|
||||||
validate_test_add("/visitor/input-strict/fail/union-flat",
|
|
||||||
&testdata, test_validate_fail_union_flat);
|
|
||||||
validate_test_add("/visitor/input-strict/fail/union-flat-no-discriminator",
|
|
||||||
&testdata, test_validate_fail_union_flat_no_discrim);
|
|
||||||
validate_test_add("/visitor/input-strict/fail/alternate",
|
|
||||||
&testdata, test_validate_fail_alternate);
|
|
||||||
validate_test_add("/visitor/input-strict/fail/union-native-list",
|
|
||||||
&testdata, test_validate_fail_union_native_list);
|
|
||||||
validate_test_add("/visitor/input-strict/pass/qmp-introspect",
|
|
||||||
&testdata, test_validate_qmp_introspect);
|
|
||||||
|
|
||||||
g_test_run();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -5,6 +5,7 @@
|
|||||||
*
|
*
|
||||||
* Authors:
|
* Authors:
|
||||||
* Luiz Capitulino <lcapitulino@redhat.com>
|
* Luiz Capitulino <lcapitulino@redhat.com>
|
||||||
|
* Paolo Bonzini <pbonzini@redhat.com>
|
||||||
*
|
*
|
||||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
* See the COPYING file in the top-level directory.
|
* See the COPYING file in the top-level directory.
|
||||||
@ -19,6 +20,9 @@
|
|||||||
#include "test-qapi-visit.h"
|
#include "test-qapi-visit.h"
|
||||||
#include "qapi/qmp/types.h"
|
#include "qapi/qmp/types.h"
|
||||||
#include "qapi/qmp/qjson.h"
|
#include "qapi/qmp/qjson.h"
|
||||||
|
#include "test-qmp-introspect.h"
|
||||||
|
#include "qmp-introspect.h"
|
||||||
|
#include "qapi-visit.h"
|
||||||
|
|
||||||
typedef struct TestInputVisitorData {
|
typedef struct TestInputVisitorData {
|
||||||
QObject *obj;
|
QObject *obj;
|
||||||
@ -49,7 +53,7 @@ static Visitor *visitor_input_test_init_internal(TestInputVisitorData *data,
|
|||||||
data->obj = qobject_from_jsonv(json_string, ap);
|
data->obj = qobject_from_jsonv(json_string, ap);
|
||||||
g_assert(data->obj);
|
g_assert(data->obj);
|
||||||
|
|
||||||
data->qiv = qobject_input_visitor_new(data->obj, false);
|
data->qiv = qobject_input_visitor_new(data->obj);
|
||||||
g_assert(data->qiv);
|
g_assert(data->qiv);
|
||||||
return data->qiv;
|
return data->qiv;
|
||||||
}
|
}
|
||||||
@ -290,14 +294,14 @@ static void test_visitor_in_null(TestInputVisitorData *data,
|
|||||||
* when input is not null.
|
* when input is not null.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
v = visitor_input_test_init(data, "{ 'a': null, 'b': '' }");
|
v = visitor_input_test_init(data, "{ 'a': null, 'b': '', 'c': null }");
|
||||||
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||||
visit_type_null(v, "a", &error_abort);
|
visit_type_null(v, "a", &error_abort);
|
||||||
visit_type_str(v, "a", &tmp, &err);
|
|
||||||
g_assert(!tmp);
|
|
||||||
error_free_or_abort(&err);
|
|
||||||
visit_type_null(v, "b", &err);
|
visit_type_null(v, "b", &err);
|
||||||
error_free_or_abort(&err);
|
error_free_or_abort(&err);
|
||||||
|
visit_type_str(v, "c", &tmp, &err);
|
||||||
|
g_assert(!tmp);
|
||||||
|
error_free_or_abort(&err);
|
||||||
visit_check_struct(v, &error_abort);
|
visit_check_struct(v, &error_abort);
|
||||||
visit_end_struct(v, NULL);
|
visit_end_struct(v, NULL);
|
||||||
}
|
}
|
||||||
@ -833,6 +837,230 @@ static void test_visitor_in_wrong_type(TestInputVisitorData *data,
|
|||||||
error_free_or_abort(&err);
|
error_free_or_abort(&err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_struct(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
TestStruct *p = NULL;
|
||||||
|
Error *err = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "{ 'integer': -42, 'boolean': true, 'string': 'foo', 'extra': 42 }");
|
||||||
|
|
||||||
|
visit_type_TestStruct(v, NULL, &p, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
g_assert(!p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_struct_nested(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
UserDefTwo *udp = NULL;
|
||||||
|
Error *err = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "{ 'string0': 'string0', 'dict1': { 'string1': 'string1', 'dict2': { 'userdef1': { 'integer': 42, 'string': 'string', 'extra': [42, 23, {'foo':'bar'}] }, 'string2': 'string2'}}}");
|
||||||
|
|
||||||
|
visit_type_UserDefTwo(v, NULL, &udp, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
g_assert(!udp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_struct_in_list(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
UserDefOneList *head = NULL;
|
||||||
|
Error *err = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "[ { 'string': 'string0', 'integer': 42 }, { 'string': 'string1', 'integer': 43 }, { 'string': 'string2', 'integer': 44, 'extra': 'ggg' } ]");
|
||||||
|
|
||||||
|
visit_type_UserDefOneList(v, NULL, &head, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
g_assert(!head);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_struct_missing(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
Error *err = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
QObject *any;
|
||||||
|
GenericAlternate *alt;
|
||||||
|
bool present;
|
||||||
|
int en;
|
||||||
|
int64_t i64;
|
||||||
|
uint32_t u32;
|
||||||
|
int8_t i8;
|
||||||
|
char *str;
|
||||||
|
double dbl;
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "{ 'sub': [ {} ] }");
|
||||||
|
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||||
|
visit_start_struct(v, "struct", NULL, 0, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_start_list(v, "list", NULL, 0, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_start_alternate(v, "alternate", &alt, sizeof(*alt), false, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_optional(v, "optional", &present);
|
||||||
|
g_assert(!present);
|
||||||
|
visit_type_enum(v, "enum", &en, EnumOne_lookup, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_type_int(v, "i64", &i64, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_type_uint32(v, "u32", &u32, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_type_int8(v, "i8", &i8, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_type_str(v, "i8", &str, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_type_number(v, "dbl", &dbl, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_type_any(v, "any", &any, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_type_null(v, "null", &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_start_list(v, "sub", NULL, 0, &error_abort);
|
||||||
|
visit_start_struct(v, NULL, NULL, 0, &error_abort);
|
||||||
|
visit_type_int(v, "i64", &i64, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_end_struct(v, NULL);
|
||||||
|
visit_end_list(v, NULL);
|
||||||
|
visit_end_struct(v, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_list(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
int64_t i64 = -1;
|
||||||
|
Error *err = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
/* Unvisited list tail */
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "[ 1, 2, 3 ]");
|
||||||
|
|
||||||
|
visit_start_list(v, NULL, NULL, 0, &error_abort);
|
||||||
|
visit_type_int(v, NULL, &i64, &error_abort);
|
||||||
|
g_assert_cmpint(i64, ==, 1);
|
||||||
|
visit_type_int(v, NULL, &i64, &error_abort);
|
||||||
|
g_assert_cmpint(i64, ==, 2);
|
||||||
|
visit_check_list(v, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_end_list(v, NULL);
|
||||||
|
|
||||||
|
/* Visit beyond end of list */
|
||||||
|
v = visitor_input_test_init(data, "[]");
|
||||||
|
|
||||||
|
visit_start_list(v, NULL, NULL, 0, &error_abort);
|
||||||
|
visit_type_int(v, NULL, &i64, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_end_list(v, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_list_nested(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
int64_t i64 = -1;
|
||||||
|
Error *err = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
/* Unvisited nested list tail */
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "[ 0, [ 1, 2, 3 ] ]");
|
||||||
|
|
||||||
|
visit_start_list(v, NULL, NULL, 0, &error_abort);
|
||||||
|
visit_type_int(v, NULL, &i64, &error_abort);
|
||||||
|
g_assert_cmpint(i64, ==, 0);
|
||||||
|
visit_start_list(v, NULL, NULL, 0, &error_abort);
|
||||||
|
visit_type_int(v, NULL, &i64, &error_abort);
|
||||||
|
g_assert_cmpint(i64, ==, 1);
|
||||||
|
visit_check_list(v, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_end_list(v, NULL);
|
||||||
|
visit_check_list(v, &error_abort);
|
||||||
|
visit_end_list(v, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_union_native_list(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
UserDefNativeListUnion *tmp = NULL;
|
||||||
|
Error *err = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data,
|
||||||
|
"{ 'type': 'integer', 'data' : [ 'string' ] }");
|
||||||
|
|
||||||
|
visit_type_UserDefNativeListUnion(v, NULL, &tmp, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
g_assert(!tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_union_flat(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
UserDefFlatUnion *tmp = NULL;
|
||||||
|
Error *err = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "{ 'string': 'c', 'integer': 41, 'boolean': true }");
|
||||||
|
|
||||||
|
visit_type_UserDefFlatUnion(v, NULL, &tmp, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
g_assert(!tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_union_flat_no_discrim(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
UserDefFlatUnion2 *tmp = NULL;
|
||||||
|
Error *err = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
/* test situation where discriminator field ('enum1' here) is missing */
|
||||||
|
v = visitor_input_test_init(data, "{ 'integer': 42, 'string': 'c', 'string1': 'd', 'string2': 'e' }");
|
||||||
|
|
||||||
|
visit_type_UserDefFlatUnion2(v, NULL, &tmp, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
g_assert(!tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_fail_alternate(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
UserDefAlternate *tmp;
|
||||||
|
Visitor *v;
|
||||||
|
Error *err = NULL;
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "3.14");
|
||||||
|
|
||||||
|
visit_type_UserDefAlternate(v, NULL, &tmp, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
g_assert(!tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_test_visitor_in_qmp_introspect(TestInputVisitorData *data,
|
||||||
|
const char *schema_json)
|
||||||
|
{
|
||||||
|
SchemaInfoList *schema = NULL;
|
||||||
|
Visitor *v;
|
||||||
|
|
||||||
|
v = visitor_input_test_init_raw(data, schema_json);
|
||||||
|
|
||||||
|
visit_type_SchemaInfoList(v, NULL, &schema, &error_abort);
|
||||||
|
g_assert(schema);
|
||||||
|
|
||||||
|
qapi_free_SchemaInfoList(schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_visitor_in_qmp_introspect(TestInputVisitorData *data,
|
||||||
|
const void *unused)
|
||||||
|
{
|
||||||
|
do_test_visitor_in_qmp_introspect(data, test_qmp_schema_json);
|
||||||
|
do_test_visitor_in_qmp_introspect(data, qmp_schema_json);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
g_test_init(&argc, &argv, NULL);
|
g_test_init(&argc, &argv, NULL);
|
||||||
@ -893,6 +1121,28 @@ int main(int argc, char **argv)
|
|||||||
NULL, test_visitor_in_native_list_string);
|
NULL, test_visitor_in_native_list_string);
|
||||||
input_visitor_test_add("/visitor/input/native_list/number",
|
input_visitor_test_add("/visitor/input/native_list/number",
|
||||||
NULL, test_visitor_in_native_list_number);
|
NULL, test_visitor_in_native_list_number);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/struct",
|
||||||
|
NULL, test_visitor_in_fail_struct);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/struct-nested",
|
||||||
|
NULL, test_visitor_in_fail_struct_nested);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/struct-in-list",
|
||||||
|
NULL, test_visitor_in_fail_struct_in_list);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/struct-missing",
|
||||||
|
NULL, test_visitor_in_fail_struct_missing);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/list",
|
||||||
|
NULL, test_visitor_in_fail_list);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/list-nested",
|
||||||
|
NULL, test_visitor_in_fail_list_nested);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/union-flat",
|
||||||
|
NULL, test_visitor_in_fail_union_flat);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/union-flat-no-discriminator",
|
||||||
|
NULL, test_visitor_in_fail_union_flat_no_discrim);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/alternate",
|
||||||
|
NULL, test_visitor_in_fail_alternate);
|
||||||
|
input_visitor_test_add("/visitor/input/fail/union-native-list",
|
||||||
|
NULL, test_visitor_in_fail_union_native_list);
|
||||||
|
input_visitor_test_add("/visitor/input/qmp-introspect",
|
||||||
|
NULL, test_visitor_in_qmp_introspect);
|
||||||
|
|
||||||
g_test_run();
|
g_test_run();
|
||||||
|
|
||||||
|
@ -39,6 +39,8 @@ static
|
|||||||
Visitor *visitor_input_test_init(TestInputVisitorData *data,
|
Visitor *visitor_input_test_init(TestInputVisitorData *data,
|
||||||
const char *string)
|
const char *string)
|
||||||
{
|
{
|
||||||
|
visitor_input_teardown(data, NULL);
|
||||||
|
|
||||||
data->v = string_input_visitor_new(string);
|
data->v = string_input_visitor_new(string);
|
||||||
g_assert(data->v);
|
g_assert(data->v);
|
||||||
return data->v;
|
return data->v;
|
||||||
@ -57,43 +59,138 @@ static void test_visitor_in_int(TestInputVisitorData *data,
|
|||||||
g_assert(!err);
|
g_assert(!err);
|
||||||
g_assert_cmpint(res, ==, value);
|
g_assert_cmpint(res, ==, value);
|
||||||
|
|
||||||
visitor_input_teardown(data, unused);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, "not an int");
|
v = visitor_input_test_init(data, "not an int");
|
||||||
|
|
||||||
visit_type_int(v, NULL, &res, &err);
|
visit_type_int(v, NULL, &res, &err);
|
||||||
error_free_or_abort(&err);
|
error_free_or_abort(&err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void check_ilist(Visitor *v, int64_t *expected, size_t n)
|
||||||
|
{
|
||||||
|
int64List *res = NULL;
|
||||||
|
int64List *tail;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
visit_type_int64List(v, NULL, &res, &error_abort);
|
||||||
|
tail = res;
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
g_assert(tail);
|
||||||
|
g_assert_cmpint(tail->value, ==, expected[i]);
|
||||||
|
tail = tail->next;
|
||||||
|
}
|
||||||
|
g_assert(!tail);
|
||||||
|
|
||||||
|
qapi_free_int64List(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_ulist(Visitor *v, uint64_t *expected, size_t n)
|
||||||
|
{
|
||||||
|
uint64List *res = NULL;
|
||||||
|
uint64List *tail;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* BUG: unsigned numbers above INT64_MAX don't work */
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
if (expected[i] > INT64_MAX) {
|
||||||
|
Error *err = NULL;
|
||||||
|
visit_type_uint64List(v, NULL, &res, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
visit_type_uint64List(v, NULL, &res, &error_abort);
|
||||||
|
tail = res;
|
||||||
|
for (i = 0; i < n; i++) {
|
||||||
|
g_assert(tail);
|
||||||
|
g_assert_cmpuint(tail->value, ==, expected[i]);
|
||||||
|
tail = tail->next;
|
||||||
|
}
|
||||||
|
g_assert(!tail);
|
||||||
|
|
||||||
|
qapi_free_uint64List(res);
|
||||||
|
}
|
||||||
|
|
||||||
static void test_visitor_in_intList(TestInputVisitorData *data,
|
static void test_visitor_in_intList(TestInputVisitorData *data,
|
||||||
const void *unused)
|
const void *unused)
|
||||||
{
|
{
|
||||||
int64_t value[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20};
|
/* Note: the visitor *sorts* ranges *unsigned* */
|
||||||
int16List *res = NULL, *tmp;
|
int64_t expect1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20 };
|
||||||
|
int64_t expect2[] = { 32767, -32768, -32767 };
|
||||||
|
int64_t expect3[] = { INT64_MAX, INT64_MIN };
|
||||||
|
uint64_t expect4[] = { UINT64_MAX };
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
int64List *res = NULL;
|
||||||
|
int64List *tail;
|
||||||
Visitor *v;
|
Visitor *v;
|
||||||
int i = 0;
|
int64_t val;
|
||||||
|
|
||||||
|
/* Valid lists */
|
||||||
|
|
||||||
v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8");
|
v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8");
|
||||||
|
check_ilist(v, expect1, ARRAY_SIZE(expect1));
|
||||||
|
|
||||||
visit_type_int16List(v, NULL, &res, &error_abort);
|
v = visitor_input_test_init(data, "32767,-32768--32767");
|
||||||
tmp = res;
|
check_ilist(v, expect2, ARRAY_SIZE(expect2));
|
||||||
while (i < sizeof(value) / sizeof(value[0])) {
|
|
||||||
g_assert(tmp);
|
|
||||||
g_assert_cmpint(tmp->value, ==, value[i++]);
|
|
||||||
tmp = tmp->next;
|
|
||||||
}
|
|
||||||
g_assert(!tmp);
|
|
||||||
|
|
||||||
qapi_free_int16List(res);
|
v = visitor_input_test_init(data,
|
||||||
|
"-9223372036854775808,9223372036854775807");
|
||||||
|
check_ilist(v, expect3, ARRAY_SIZE(expect3));
|
||||||
|
|
||||||
visitor_input_teardown(data, unused);
|
v = visitor_input_test_init(data, "18446744073709551615");
|
||||||
|
check_ulist(v, expect4, ARRAY_SIZE(expect4));
|
||||||
|
|
||||||
|
/* Empty list is invalid (weird) */
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "");
|
||||||
|
visit_type_int64List(v, NULL, &res, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
|
||||||
|
/* Not a list */
|
||||||
|
|
||||||
v = visitor_input_test_init(data, "not an int list");
|
v = visitor_input_test_init(data, "not an int list");
|
||||||
|
|
||||||
visit_type_int16List(v, NULL, &res, &err);
|
visit_type_int64List(v, NULL, &res, &err);
|
||||||
error_free_or_abort(&err);
|
error_free_or_abort(&err);
|
||||||
g_assert(!res);
|
g_assert(!res);
|
||||||
|
|
||||||
|
/* Unvisited list tail */
|
||||||
|
|
||||||
|
v = visitor_input_test_init(data, "0,2-3");
|
||||||
|
|
||||||
|
/* Would be simpler if the visitor genuinely supported virtual walks */
|
||||||
|
visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res),
|
||||||
|
&error_abort);
|
||||||
|
tail = res;
|
||||||
|
visit_type_int64(v, NULL, &tail->value, &error_abort);
|
||||||
|
g_assert_cmpint(tail->value, ==, 0);
|
||||||
|
tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res));
|
||||||
|
g_assert(tail);
|
||||||
|
visit_type_int64(v, NULL, &tail->value, &error_abort);
|
||||||
|
g_assert_cmpint(tail->value, ==, 2);
|
||||||
|
tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res));
|
||||||
|
g_assert(tail);
|
||||||
|
|
||||||
|
visit_check_list(v, &err);
|
||||||
|
error_free_or_abort(&err);
|
||||||
|
visit_end_list(v, (void **)&res);
|
||||||
|
|
||||||
|
qapi_free_int64List(res);
|
||||||
|
|
||||||
|
/* Visit beyond end of list */
|
||||||
|
v = visitor_input_test_init(data, "0");
|
||||||
|
|
||||||
|
visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res),
|
||||||
|
&error_abort);
|
||||||
|
tail = res;
|
||||||
|
visit_type_int64(v, NULL, &tail->value, &err);
|
||||||
|
g_assert_cmpint(tail->value, ==, 0);
|
||||||
|
visit_type_int64(v, NULL, &val, &err);
|
||||||
|
g_assert_cmpint(val, ==, 1); /* BUG */
|
||||||
|
visit_check_list(v, &error_abort);
|
||||||
|
visit_end_list(v, (void **)&res);
|
||||||
|
|
||||||
|
qapi_free_int64List(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_visitor_in_bool(TestInputVisitorData *data,
|
static void test_visitor_in_bool(TestInputVisitorData *data,
|
||||||
@ -108,35 +205,30 @@ static void test_visitor_in_bool(TestInputVisitorData *data,
|
|||||||
visit_type_bool(v, NULL, &res, &err);
|
visit_type_bool(v, NULL, &res, &err);
|
||||||
g_assert(!err);
|
g_assert(!err);
|
||||||
g_assert_cmpint(res, ==, true);
|
g_assert_cmpint(res, ==, true);
|
||||||
visitor_input_teardown(data, unused);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, "yes");
|
v = visitor_input_test_init(data, "yes");
|
||||||
|
|
||||||
visit_type_bool(v, NULL, &res, &err);
|
visit_type_bool(v, NULL, &res, &err);
|
||||||
g_assert(!err);
|
g_assert(!err);
|
||||||
g_assert_cmpint(res, ==, true);
|
g_assert_cmpint(res, ==, true);
|
||||||
visitor_input_teardown(data, unused);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, "on");
|
v = visitor_input_test_init(data, "on");
|
||||||
|
|
||||||
visit_type_bool(v, NULL, &res, &err);
|
visit_type_bool(v, NULL, &res, &err);
|
||||||
g_assert(!err);
|
g_assert(!err);
|
||||||
g_assert_cmpint(res, ==, true);
|
g_assert_cmpint(res, ==, true);
|
||||||
visitor_input_teardown(data, unused);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, "false");
|
v = visitor_input_test_init(data, "false");
|
||||||
|
|
||||||
visit_type_bool(v, NULL, &res, &err);
|
visit_type_bool(v, NULL, &res, &err);
|
||||||
g_assert(!err);
|
g_assert(!err);
|
||||||
g_assert_cmpint(res, ==, false);
|
g_assert_cmpint(res, ==, false);
|
||||||
visitor_input_teardown(data, unused);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, "no");
|
v = visitor_input_test_init(data, "no");
|
||||||
|
|
||||||
visit_type_bool(v, NULL, &res, &err);
|
visit_type_bool(v, NULL, &res, &err);
|
||||||
g_assert(!err);
|
g_assert(!err);
|
||||||
g_assert_cmpint(res, ==, false);
|
g_assert_cmpint(res, ==, false);
|
||||||
visitor_input_teardown(data, unused);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, "off");
|
v = visitor_input_test_init(data, "off");
|
||||||
|
|
||||||
@ -190,8 +282,6 @@ static void test_visitor_in_enum(TestInputVisitorData *data,
|
|||||||
visit_type_EnumOne(v, NULL, &res, &err);
|
visit_type_EnumOne(v, NULL, &res, &err);
|
||||||
g_assert(!err);
|
g_assert(!err);
|
||||||
g_assert_cmpint(i, ==, res);
|
g_assert_cmpint(i, ==, res);
|
||||||
|
|
||||||
visitor_input_teardown(data, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,30 +314,24 @@ static void test_visitor_in_fuzz(TestInputVisitorData *data,
|
|||||||
|
|
||||||
v = visitor_input_test_init(data, buf);
|
v = visitor_input_test_init(data, buf);
|
||||||
visit_type_int(v, NULL, &ires, NULL);
|
visit_type_int(v, NULL, &ires, NULL);
|
||||||
visitor_input_teardown(data, NULL);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, buf);
|
v = visitor_input_test_init(data, buf);
|
||||||
visit_type_intList(v, NULL, &ilres, NULL);
|
visit_type_intList(v, NULL, &ilres, NULL);
|
||||||
qapi_free_intList(ilres);
|
qapi_free_intList(ilres);
|
||||||
visitor_input_teardown(data, NULL);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, buf);
|
v = visitor_input_test_init(data, buf);
|
||||||
visit_type_bool(v, NULL, &bres, NULL);
|
visit_type_bool(v, NULL, &bres, NULL);
|
||||||
visitor_input_teardown(data, NULL);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, buf);
|
v = visitor_input_test_init(data, buf);
|
||||||
visit_type_number(v, NULL, &nres, NULL);
|
visit_type_number(v, NULL, &nres, NULL);
|
||||||
visitor_input_teardown(data, NULL);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, buf);
|
v = visitor_input_test_init(data, buf);
|
||||||
sres = NULL;
|
sres = NULL;
|
||||||
visit_type_str(v, NULL, &sres, NULL);
|
visit_type_str(v, NULL, &sres, NULL);
|
||||||
g_free(sres);
|
g_free(sres);
|
||||||
visitor_input_teardown(data, NULL);
|
|
||||||
|
|
||||||
v = visitor_input_test_init(data, buf);
|
v = visitor_input_test_init(data, buf);
|
||||||
visit_type_EnumOne(v, NULL, &eres, NULL);
|
visit_type_EnumOne(v, NULL, &eres, NULL);
|
||||||
visitor_input_teardown(data, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1040,7 +1040,7 @@ static void qmp_deserialize(void **native_out, void *datap,
|
|||||||
obj = qobject_from_json(qstring_get_str(output_json));
|
obj = qobject_from_json(qstring_get_str(output_json));
|
||||||
|
|
||||||
QDECREF(output_json);
|
QDECREF(output_json);
|
||||||
d->qiv = qobject_input_visitor_new(obj, true);
|
d->qiv = qobject_input_visitor_new(obj);
|
||||||
qobject_decref(obj_orig);
|
qobject_decref(obj_orig);
|
||||||
qobject_decref(obj);
|
qobject_decref(obj);
|
||||||
visit(d->qiv, native_out, errp);
|
visit(d->qiv, native_out, errp);
|
||||||
|
@ -65,7 +65,6 @@ xen_remap_bucket(uint64_t index) "index %#"PRIx64
|
|||||||
xen_map_cache_return(void* ptr) "%p"
|
xen_map_cache_return(void* ptr) "%p"
|
||||||
|
|
||||||
# monitor.c
|
# monitor.c
|
||||||
handle_qmp_command(void *mon, const char *cmd_name) "mon %p cmd_name \"%s\""
|
|
||||||
monitor_protocol_event_handler(uint32_t event, void *qdict) "event=%d data=%p"
|
monitor_protocol_event_handler(uint32_t event, void *qdict) "event=%d data=%p"
|
||||||
monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p"
|
monitor_protocol_event_emit(uint32_t event, void *data) "event=%d data=%p"
|
||||||
monitor_protocol_event_queue(uint32_t event, void *qdict, uint64_t rate) "event=%d data=%p rate=%" PRId64
|
monitor_protocol_event_queue(uint32_t event, void *qdict, uint64_t rate) "event=%d data=%p rate=%" PRId64
|
||||||
|
2
vl.c
2
vl.c
@ -2988,7 +2988,7 @@ int main(int argc, char **argv, char **envp)
|
|||||||
qemu_init_exec_dir(argv[0]);
|
qemu_init_exec_dir(argv[0]);
|
||||||
|
|
||||||
module_call_init(MODULE_INIT_QOM);
|
module_call_init(MODULE_INIT_QOM);
|
||||||
module_call_init(MODULE_INIT_QAPI);
|
monitor_init_qmp_commands();
|
||||||
|
|
||||||
qemu_add_opts(&qemu_drive_opts);
|
qemu_add_opts(&qemu_drive_opts);
|
||||||
qemu_add_drive_opts(&qemu_legacy_drive_opts);
|
qemu_add_drive_opts(&qemu_legacy_drive_opts);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user