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:
Peter Maydell
2017-03-06 10:18:33 +00:00
45 changed files with 1079 additions and 769 deletions
+1
View File
@@ -1404,6 +1404,7 @@ F: qmp.c
F: monitor.c
F: docs/*qmp-*
F: scripts/qmp/
F: tests/qmp-test.c
T: git git://repo.or.cz/qemu/armbru.git qapi-next
Register API
+1 -1
View File
@@ -278,7 +278,7 @@ static SocketAddress *nbd_config(BDRVNBDState *s, QDict *options, Error **errp)
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);
if (local_err) {
error_propagate(errp, local_err);
+1 -1
View File
@@ -474,7 +474,7 @@ static NFSServer *nfs_config(QDict *options, Error **errp)
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);
if (local_error) {
error_propagate(errp, local_error);
+1 -1
View File
@@ -601,7 +601,7 @@ static InetSocketAddress *ssh_config(QDict *options, Error **errp)
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);
if (local_error) {
error_propagate(errp, local_error);
+1 -1
View File
@@ -1138,7 +1138,7 @@ Example:
Visitor *v;
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);
if (err) {
goto out;
+5
View File
@@ -326,7 +326,12 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name,
return;
}
}
visit_check_list(v, &err);
visit_end_list(v, NULL);
if (err) {
error_propagate(errp, err);
return;
}
break;
}
default:
+1
View File
@@ -16,6 +16,7 @@ extern Monitor *cur_mon;
bool monitor_cur_is_qmp(void);
void monitor_init_qmp_commands(void);
void monitor_init(Chardev *chr, int flags);
void monitor_cleanup(void);
+14 -8
View File
@@ -34,18 +34,24 @@ typedef struct QmpCommand
bool enabled;
} QmpCommand;
void qmp_register_command(const char *name, QmpCommandFunc *fn,
QmpCommandOptions options);
void qmp_unregister_command(const char *name);
QmpCommand *qmp_find_command(const char *name);
QObject *qmp_dispatch(QObject *request);
void qmp_disable_command(const char *name);
void qmp_enable_command(const char *name);
typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList;
void qmp_register_command(QmpCommandList *cmds, const char *name,
QmpCommandFunc *fn, QmpCommandOptions options);
void qmp_unregister_command(QmpCommandList *cmds, const char *name);
QmpCommand *qmp_find_command(QmpCommandList *cmds, 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);
const char *qmp_command_name(const QmpCommand *cmd);
bool qmp_has_success_response(const QmpCommand *cmd);
QObject *qmp_build_error_object(Error *err);
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
-9
View File
@@ -82,15 +82,6 @@
#define QERR_QGA_COMMAND_FAILED \
"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 \
"Could not set password"
+36 -4
View File
@@ -1,6 +1,7 @@
/*
* Input Visitor
*
* Copyright (C) 2017 Red Hat, Inc.
* Copyright IBM, Corp. 2011
*
* Authors:
@@ -20,11 +21,42 @@
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
* dictionary; otherwise excess input is ignored.
* A QObject input visitor visit builds a QAPI object from a QObject.
* 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
+31 -4
View File
@@ -19,11 +19,38 @@
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
* collect the result of the visit.
* A QObject output visitor visit builds a QObject from QAPI Object.
* 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);
+5 -2
View File
@@ -61,6 +61,9 @@ struct Visitor
/* Must be set */
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 */
void (*end_list)(Visitor *v, void **list);
@@ -102,8 +105,8 @@ struct Visitor
/* Must be set to visit explicit null values. */
void (*type_null)(Visitor *v, const char *name, Error **errp);
/* Must be set for input visitors, optional otherwise. The core
* takes care of the return type in the public interface. */
/* Must be set for input visitors to visit structs, optional otherwise.
The core takes care of the return type in the public interface. */
void (*optional)(Visitor *v, const char *name, bool *present);
/* Must be set */
+13 -6
View File
@@ -66,12 +66,6 @@
* object, @name is the key associated with the value; and when
* 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;
* they allocate *@obj during input visits, leave it unchanged on
* 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);
/*
* 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.
*
-2
View File
@@ -42,7 +42,6 @@ static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
typedef enum {
MODULE_INIT_BLOCK,
MODULE_INIT_OPTS,
MODULE_INIT_QAPI,
MODULE_INIT_QOM,
MODULE_INIT_TRACE,
MODULE_INIT_MAX
@@ -50,7 +49,6 @@ typedef enum {
#define block_init(function) module_init(function, MODULE_INIT_BLOCK)
#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 trace_init(function) module_init(function, MODULE_INIT_TRACE)
+65 -117
View File
@@ -165,7 +165,7 @@ typedef struct {
* When command qmp_capabilities succeeds, we go into command
* mode.
*/
bool in_command_mode; /* are we in command mode? */
QmpCommandList *commands;
} MonitorQMP;
/*
@@ -221,6 +221,8 @@ static int mon_refcount;
static mon_cmd_t mon_cmds[];
static mon_cmd_t info_cmds[];
QmpCommandList qmp_commands, qmp_cap_negotiation_commands;
Monitor *cur_mon;
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);
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));
}
}
@@ -563,11 +566,6 @@ static void monitor_qapi_event_init(void)
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 monitor_data_init(Monitor *mon)
@@ -919,7 +917,7 @@ CommandInfoList *qmp_query_commands(Error **errp)
{
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;
}
@@ -973,44 +971,67 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data,
static void qmp_unregister_commands_hack(void)
{
#ifndef CONFIG_SPICE
qmp_unregister_command("query-spice");
qmp_unregister_command(&qmp_commands, "query-spice");
#endif
#ifndef TARGET_I386
qmp_unregister_command("rtc-reset-reinjection");
qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection");
#endif
#ifndef TARGET_S390X
qmp_unregister_command("dump-skeys");
qmp_unregister_command(&qmp_commands, "dump-skeys");
#endif
#ifndef TARGET_ARM
qmp_unregister_command("query-gic-capabilities");
qmp_unregister_command(&qmp_commands, "query-gic-capabilities");
#endif
#if !defined(TARGET_S390X) && !defined(TARGET_I386)
qmp_unregister_command("query-cpu-model-expansion");
qmp_unregister_command(&qmp_commands, "query-cpu-model-expansion");
#endif
#if !defined(TARGET_S390X)
qmp_unregister_command("query-cpu-model-baseline");
qmp_unregister_command("query-cpu-model-comparison");
qmp_unregister_command(&qmp_commands, "query-cpu-model-baseline");
qmp_unregister_command(&qmp_commands, "query-cpu-model-comparison");
#endif
#if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \
&& !defined(TARGET_S390X)
qmp_unregister_command("query-cpu-definitions");
qmp_unregister_command(&qmp_commands, "query-cpu-definitions");
#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);
qmp_register_command("device_add", qmp_device_add,
qmp_register_command(&qmp_commands, "device_add", qmp_device_add,
QCO_NO_OPTIONS);
qmp_register_command("netdev_add", qmp_netdev_add,
qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add,
QCO_NO_OPTIONS);
/* call it after the rest of qapi_init() */
register_module_init(qmp_unregister_commands_hack, MODULE_INIT_QAPI);
qmp_unregister_commands_hack();
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 */
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;
}
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)
{
QObject *req, *rsp = NULL, *id = NULL;
QDict *qdict = NULL;
const char *cmd_name;
Monitor *mon = cur_mon;
Error *err = NULL;
@@ -3772,24 +3716,28 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
goto err_out;
}
qdict = qmp_check_input_obj(req, &err);
if (!qdict) {
goto err_out;
qdict = qobject_to_qdict(req);
if (qdict) {
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:
if (err) {
qdict = qdict_new();
@@ -3886,7 +3834,7 @@ static void monitor_qmp_event(void *opaque, int event)
switch (event) {
case CHR_EVENT_OPENED:
mon->qmp.in_command_mode = false;
mon->qmp.commands = &qmp_cap_negotiation_commands;
data = get_qmp_greeting();
monitor_json_emitter(mon, data);
qobject_decref(data);
+12
View File
@@ -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
opts_end_list(Visitor *v, void **obj)
{
@@ -528,6 +538,7 @@ opts_visitor_new(const QemuOpts *opts)
{
OptsVisitor *ov;
assert(opts);
ov = g_malloc0(sizeof *ov);
ov->visitor.type = VISITOR_INPUT;
@@ -538,6 +549,7 @@ opts_visitor_new(const QemuOpts *opts)
ov->visitor.start_list = &opts_start_list;
ov->visitor.next_list = &opts_next_list;
ov->visitor.check_list = &opts_check_list;
ov->visitor.end_list = &opts_end_list;
ov->visitor.type_int64 = &opts_type_int64;
+8
View File
@@ -90,6 +90,14 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t 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)
{
trace_visit_end_list(v, obj);
+18 -11
View File
@@ -30,8 +30,7 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
dict = qobject_to_qdict(request);
if (!dict) {
error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT,
"request is not a dictionary");
error_setg(errp, "Expected '%s' in QMP input", "object");
return NULL;
}
@@ -42,26 +41,34 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
if (!strcmp(arg_name, "execute")) {
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute",
"string");
error_setg(errp, "QMP input object member '%s' expects '%s'",
"execute", "string");
return NULL;
}
has_exec_key = true;
} else if (strcmp(arg_name, "arguments")) {
error_setg(errp, QERR_QMP_EXTRA_MEMBER, arg_name);
} else if (!strcmp(arg_name, "arguments")) {
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;
}
}
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 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;
const char *command;
@@ -75,7 +82,7 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp)
}
command = qdict_get_str(dict, "execute");
cmd = qmp_find_command(command);
cmd = qmp_find_command(cmds, command);
if (cmd == NULL) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"The command %s has not been found", command);
@@ -115,13 +122,13 @@ QObject *qmp_build_error_object(Error *err)
error_get_pretty(err));
}
QObject *qmp_dispatch(QObject *request)
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request)
{
Error *err = NULL;
QObject *ret;
QDict *rsp;
ret = do_qmp_dispatch(request, &err);
ret = do_qmp_dispatch(cmds, request, &err);
rsp = qdict_new();
if (err) {
+18 -19
View File
@@ -15,11 +15,8 @@
#include "qemu/osdep.h"
#include "qapi/qmp/dispatch.h"
static QTAILQ_HEAD(QmpCommandList, QmpCommand) qmp_commands =
QTAILQ_HEAD_INITIALIZER(qmp_commands);
void qmp_register_command(const char *name, QmpCommandFunc *fn,
QmpCommandOptions options)
void qmp_register_command(QmpCommandList *cmds, const char *name,
QmpCommandFunc *fn, QmpCommandOptions options)
{
QmpCommand *cmd = g_malloc0(sizeof(*cmd));
@@ -27,22 +24,22 @@ void qmp_register_command(const char *name, QmpCommandFunc *fn,
cmd->fn = fn;
cmd->enabled = true;
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);
}
QmpCommand *qmp_find_command(const char *name)
QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name)
{
QmpCommand *cmd;
QTAILQ_FOREACH(cmd, &qmp_commands, node) {
QTAILQ_FOREACH(cmd, cmds, node) {
if (strcmp(cmd->name, name) == 0) {
return cmd;
}
@@ -50,11 +47,12 @@ QmpCommand *qmp_find_command(const char *name)
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;
QTAILQ_FOREACH(cmd, &qmp_commands, node) {
QTAILQ_FOREACH(cmd, cmds, node) {
if (strcmp(cmd->name, name) == 0) {
cmd->enabled = enabled;
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)
@@ -87,11 +85,12 @@ bool qmp_has_success_response(const QmpCommand *cmd)
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;
QTAILQ_FOREACH(cmd, &qmp_commands, node) {
QTAILQ_FOREACH(cmd, cmds, node) {
fn(cmd, opaque);
}
}
+135 -72
View File
@@ -21,21 +21,19 @@
#include "qapi/qmp/types.h"
#include "qapi/qmp/qerror.h"
#define QIV_STACK_SIZE 1024
typedef struct StackObject
{
QObject *obj; /* Object being visited */
typedef struct StackObject {
const char *name; /* Name of @obj in its parent, if any */
QObject *obj; /* QDict or QList being visited */
void *qapi; /* sanity check that caller uses same pointer */
GHashTable *h; /* If obj is dict: unvisited keys */
const QListEntry *entry; /* If obj is list: unvisited tail */
GHashTable *h; /* If @obj is QDict: unvisited keys */
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;
struct QObjectInputVisitor
{
struct QObjectInputVisitor {
Visitor visitor;
/* Root of visit at visitor creation. */
@@ -45,8 +43,7 @@ struct QObjectInputVisitor
* QDict or QList). */
QSLIST_HEAD(, StackObject) stack;
/* True to reject parse in visit_end_struct() if unvisited keys remain. */
bool strict;
GString *errname; /* Accumulator for full_name() */
};
static QObjectInputVisitor *to_qiv(Visitor *v)
@@ -54,9 +51,51 @@ static QObjectInputVisitor *to_qiv(Visitor *v)
return container_of(v, QObjectInputVisitor, visitor);
}
static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
const char *name,
bool consume, Error **errp)
static const char *full_name_nth(QObjectInputVisitor *qiv, const char *name,
int n)
{
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;
QObject *qobj;
@@ -80,22 +119,37 @@ static QObject *qobject_input_get_object(QObjectInputVisitor *qiv,
bool removed = g_hash_table_remove(tos->h, name);
assert(removed);
}
if (!ret) {
error_setg(errp, QERR_MISSING_PARAMETER, name);
}
} else {
assert(qobject_type(qobj) == QTYPE_QLIST);
assert(!name);
ret = qlist_entry_obj(tos->entry);
assert(ret);
if (tos->entry) {
ret = qlist_entry_obj(tos->entry);
if (consume) {
tos->entry = qlist_next(tos->entry);
}
} else {
ret = NULL;
}
if (consume) {
tos->entry = qlist_next(tos->entry);
tos->index++;
}
}
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)
{
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,
QObject *obj, void *qapi,
Error **errp)
const char *name,
QObject *obj, void *qapi)
{
GHashTable *h;
StackObject *tos = g_new0(StackObject, 1);
assert(obj);
tos->name = name;
tos->obj = obj;
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);
qdict_iter(qobject_to_qdict(obj), qdict_add_key, 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->index = -1;
}
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);
StackObject *tos = QSLIST_FIRST(&qiv->stack);
GHashTableIter iter;
const char *key;
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);
if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
error_setg(errp, QERR_QMP_EXTRA_MEMBER, key);
}
}
g_hash_table_iter_init(&iter, tos->h);
if (g_hash_table_iter_next(&iter, (void **)&key, NULL)) {
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);
QObject *qobj = qobject_input_get_object(qiv, name, true, errp);
Error *err = NULL;
if (obj) {
*obj = NULL;
@@ -179,16 +231,12 @@ static void qobject_input_start_struct(Visitor *v, const char *name, void **obj,
return;
}
if (qobject_type(qobj) != QTYPE_QDICT) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"QDict");
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "object");
return;
}
qobject_input_push(qiv, qobj, obj, &err);
if (err) {
error_propagate(errp, err);
return;
}
qobject_input_push(qiv, name, qobj, obj);
if (obj) {
*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);
const QListEntry *entry;
if (list) {
*list = NULL;
}
if (!qobj) {
return;
}
if (qobject_type(qobj) != QTYPE_QLIST) {
if (list) {
*list = NULL;
}
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"list");
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "array");
return;
}
entry = qobject_input_push(qiv, qobj, list, errp);
if (list) {
if (entry) {
*list = g_malloc0(size);
} else {
*list = NULL;
}
entry = qobject_input_push(qiv, name, qobj, list);
if (entry && list) {
*list = g_malloc0(size);
}
}
@@ -230,15 +274,30 @@ static GenericList *qobject_input_next_list(Visitor *v, GenericList *tail,
size_t size)
{
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;
}
tail->next = g_malloc0(size);
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,
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);
if (!qint) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"integer");
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "integer");
return;
}
@@ -291,8 +350,8 @@ static void qobject_input_type_uint64(Visitor *v, const char *name,
}
qint = qobject_to_qint(qobj);
if (!qint) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"integer");
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "integer");
return;
}
@@ -311,8 +370,8 @@ static void qobject_input_type_bool(Visitor *v, const char *name, bool *obj,
}
qbool = qobject_to_qbool(qobj);
if (!qbool) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"boolean");
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "boolean");
return;
}
@@ -332,8 +391,8 @@ static void qobject_input_type_str(Visitor *v, const char *name, char **obj,
}
qstr = qobject_to_qstring(qobj);
if (!qstr) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"string");
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "string");
return;
}
@@ -363,8 +422,8 @@ static void qobject_input_type_number(Visitor *v, const char *name, double *obj,
return;
}
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"number");
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "number");
}
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) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"null");
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
full_name(qiv, name), "null");
}
}
static void qobject_input_optional(Visitor *v, const char *name, bool *present)
{
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) {
*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)
{
QObjectInputVisitor *qiv = to_qiv(v);
while (!QSLIST_EMPTY(&qiv->stack)) {
StackObject *tos = QSLIST_FIRST(&qiv->stack);
@@ -421,10 +481,13 @@ static void qobject_input_free(Visitor *v)
}
qobject_decref(qiv->root);
if (qiv->errname) {
g_string_free(qiv->errname, TRUE);
}
g_free(qiv);
}
Visitor *qobject_input_visitor_new(QObject *obj, bool strict)
Visitor *qobject_input_visitor_new(QObject *obj)
{
QObjectInputVisitor *v;
@@ -437,6 +500,7 @@ Visitor *qobject_input_visitor_new(QObject *obj, bool strict)
v->visitor.end_struct = qobject_input_pop;
v->visitor.start_list = qobject_input_start_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.start_alternate = qobject_input_start_alternate;
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.optional = qobject_input_optional;
v->visitor.free = qobject_input_free;
v->strict = strict;
v->root = obj;
qobject_incref(obj);

Some files were not shown because too many files have changed in this diff Show More