mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2018-07-03-v2' into staging
Monitor patches for 2018-07-03
# gpg: Signature made Tue 03 Jul 2018 22:20:13 BST
# gpg: using RSA key 3870B400EB918653
# 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-monitor-2018-07-03-v2: (32 commits)
qapi: Polish command flags documentation in qapi-code-gen.txt
monitor: Improve some comments
qmp: Clean up capability negotiation after commit 02130314d8
qobject: Let qobject_from_jsonf() fail instead of abort
qmp: Switch timestamp_put() to qdict_from_jsonf_nofail()
qmp: Add some comments around null responses
qmp: Simplify monitor_qmp_respond()
qmp: Replace get_qmp_greeting() by qmp_greeting()
qmp: Replace monitor_json_emitter{,raw}() by qmp_{queue,send}_response()
qmp: Use QDict * instead of QObject * for response objects
qmp: De-duplicate error response building
qobject: New qdict_from_jsonf_nofail()
monitor: Peel off @mon_global wrapper
monitor: Rename use_io_thr to use_io_thread
qmp: Don't let JSON errors jump the queue
qmp: Don't let malformed in-band commands jump the queue
tests/qmp-test: Demonstrate QMP errors jumping the queue
qmp: Simplify code around monitor_qmp_dispatch_one()
qmp: Always free QMPRequest with qmp_request_free()
qmp: Revert change to handle_qmp_command tracepoint
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
@@ -624,62 +624,48 @@ its return value.
|
||||
In rare cases, QAPI cannot express a type-safe representation of a
|
||||
corresponding Client JSON Protocol command. You then have to suppress
|
||||
generation of a marshalling function by including a key 'gen' with
|
||||
boolean value false, and instead write your own function. Please try
|
||||
to avoid adding new commands that rely on this, and instead use
|
||||
type-safe unions. For an example of this usage:
|
||||
boolean value false, and instead write your own function. For
|
||||
example:
|
||||
|
||||
{ 'command': 'netdev_add',
|
||||
'data': {'type': 'str', 'id': 'str'},
|
||||
'gen': false }
|
||||
|
||||
Please try to avoid adding new commands that rely on this, and instead
|
||||
use type-safe unions.
|
||||
|
||||
Normally, the QAPI schema is used to describe synchronous exchanges,
|
||||
where a response is expected. But in some cases, the action of a
|
||||
command is expected to change state in a way that a successful
|
||||
response is not possible (although the command will still return a
|
||||
normal dictionary error on failure). When a successful reply is not
|
||||
possible, the command expression should include the optional key
|
||||
possible, the command expression includes the optional key
|
||||
'success-response' with boolean value false. So far, only QGA makes
|
||||
use of this member.
|
||||
|
||||
A command can be declared to support Out-Of-Band (OOB) execution. By
|
||||
default, commands do not support OOB. To declare a command that
|
||||
supports it, the schema includes an extra 'allow-oob' field. For
|
||||
example:
|
||||
Key 'allow-oob' declares whether the command supports out-of-band
|
||||
(OOB) execution. It defaults to false. For example:
|
||||
|
||||
{ 'command': 'migrate_recover',
|
||||
'data': { 'uri': 'str' }, 'allow-oob': true }
|
||||
|
||||
To execute a command with out-of-band priority, the client specifies
|
||||
the "control" field in the request, with "run-oob" set to
|
||||
true. Example:
|
||||
See qmp-spec.txt for out-of-band execution syntax and semantics.
|
||||
|
||||
=> { "execute": "command-support-oob",
|
||||
"arguments": { ... },
|
||||
"control": { "run-oob": true } }
|
||||
<= { "return": { } }
|
||||
Commands supporting out-of-band execution can still be executed
|
||||
in-band.
|
||||
|
||||
Without it, even the commands that support out-of-band execution will
|
||||
still be run in-band.
|
||||
When a command is executed in-band, its handler runs in the main
|
||||
thread with the BQL held.
|
||||
|
||||
Under normal QMP command execution, the following apply to each
|
||||
command:
|
||||
When a command is executed out-of-band, its handler runs in a
|
||||
dedicated monitor I/O thread with the BQL *not* held.
|
||||
|
||||
- They are executed in order,
|
||||
- They run only in main thread of QEMU,
|
||||
- They run with the BQL held.
|
||||
An OOB-capable command handler must satisfy the following conditions:
|
||||
|
||||
When a command is executed with OOB, the following changes occur:
|
||||
|
||||
- They can be completed before a pending in-band command,
|
||||
- They run in a dedicated monitor thread,
|
||||
- They run with the BQL not held.
|
||||
|
||||
OOB command handlers must satisfy the following conditions:
|
||||
|
||||
- It terminates quickly,
|
||||
- It does not invoke system calls that may block,
|
||||
- It terminates quickly.
|
||||
- It does not invoke system calls that may block.
|
||||
- It does not access guest RAM that may block when userfaultfd is
|
||||
enabled for postcopy live migration,
|
||||
enabled for postcopy live migration.
|
||||
- It takes only "fast" locks, i.e. all critical sections protected by
|
||||
any lock it takes also satisfy the conditions for OOB command
|
||||
handler code.
|
||||
@@ -688,17 +674,18 @@ The restrictions on locking limit access to shared state. Such access
|
||||
requires synchronization, but OOB commands can't take the BQL or any
|
||||
other "slow" lock.
|
||||
|
||||
If in doubt, do not implement OOB execution support.
|
||||
When in doubt, do not implement OOB execution support.
|
||||
|
||||
A command may use the optional 'allow-preconfig' key to permit its execution
|
||||
at early runtime configuration stage (preconfig runstate).
|
||||
If not specified then a command defaults to 'allow-preconfig': false.
|
||||
Key 'allow-preconfig' declares whether the command is available before
|
||||
the machine is built. It defaults to false. For example:
|
||||
|
||||
An example of declaring a command that is enabled during preconfig:
|
||||
{ 'command': 'qmp_capabilities',
|
||||
'data': { '*enable': [ 'QMPCapability' ] },
|
||||
'allow-preconfig': true }
|
||||
|
||||
QMP is available before the machine is built only when QEMU was
|
||||
started with --preconfig.
|
||||
|
||||
=== Events ===
|
||||
|
||||
Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
|
||||
|
||||
@@ -52,13 +52,14 @@ Escape character is '^]'.
|
||||
"QMP": {
|
||||
"version": {
|
||||
"qemu": {
|
||||
"micro": 50,
|
||||
"minor": 6,
|
||||
"major": 1
|
||||
},
|
||||
"package": ""
|
||||
},
|
||||
"micro": 0,
|
||||
"minor": 0,
|
||||
"major": 3
|
||||
},
|
||||
"package": "v3.0.0"
|
||||
},
|
||||
"capabilities": [
|
||||
"oob"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
+54
-31
@@ -77,52 +77,65 @@ The greeting message format is:
|
||||
is the same of the query-version command)
|
||||
- The "capabilities" member specify the availability of features beyond the
|
||||
baseline specification; the order of elements in this array has no
|
||||
particular significance, so a client must search the entire array
|
||||
when looking for a particular capability
|
||||
particular significance.
|
||||
|
||||
2.2.1 Capabilities
|
||||
------------------
|
||||
|
||||
Currently supported capabilities are:
|
||||
|
||||
- "oob": the QMP server supports "Out-Of-Band" (OOB) command
|
||||
execution. For more details, please see the "run-oob" parameter in
|
||||
the "Issuing Commands" section below. Not all commands allow this
|
||||
"oob" execution. The "query-qmp-schema" command can be used to
|
||||
inspect which commands support "oob" execution.
|
||||
|
||||
QMP clients can get a list of supported QMP capabilities of the QMP
|
||||
server in the greeting message mentioned above. By default, all the
|
||||
capabilities are off. To enable any QMP capabilities, the QMP client
|
||||
needs to send the "qmp_capabilities" command with an extra parameter
|
||||
for the requested capabilities.
|
||||
- "oob": the QMP server supports "out-of-band" (OOB) command
|
||||
execution, as described in section "2.3.1 Out-of-band execution".
|
||||
|
||||
2.3 Issuing Commands
|
||||
--------------------
|
||||
|
||||
The format for command execution is:
|
||||
|
||||
{ "execute": json-string, "arguments": json-object, "id": json-value,
|
||||
"control": json-object }
|
||||
{ "execute": json-string, "arguments": json-object, "id": json-value }
|
||||
|
||||
or
|
||||
|
||||
{ "exec-oob": json-string, "arguments": json-object, "id": json-value }
|
||||
|
||||
Where,
|
||||
|
||||
- The "execute" member identifies the command to be executed by the Server
|
||||
- The "execute" or "exec-oob" member identifies the command to be
|
||||
executed by the server. The latter requests out-of-band execution.
|
||||
- The "arguments" member is used to pass any arguments required for the
|
||||
execution of the command, it is optional when no arguments are
|
||||
required. Each command documents what contents will be considered
|
||||
valid when handling the json-argument
|
||||
- The "id" member is a transaction identification associated with the
|
||||
command execution. It is required for all commands if the OOB -
|
||||
capability was enabled at startup, and optional otherwise. The same
|
||||
"id" field will be part of the response if provided. The "id" member
|
||||
can be any json-value, although most clients merely use a
|
||||
json-number incremented for each successive command
|
||||
- The "control" member is optional, and currently only used for
|
||||
out-of-band execution. The handling or response of an "oob" command
|
||||
can overtake prior in-band commands. To enable "oob" handling of a
|
||||
particular command, just provide a control field with: { "control":
|
||||
{ "run-oob": true } }
|
||||
command execution, it is optional and will be part of the response
|
||||
if provided. The "id" member can be any json-value. A json-number
|
||||
incremented for each successive command works fine.
|
||||
|
||||
2.3.1 Out-of-band execution
|
||||
---------------------------
|
||||
|
||||
The server normally reads, executes and responds to one command after
|
||||
the other. The client therefore receives command responses in issue
|
||||
order.
|
||||
|
||||
With out-of-band execution enabled via capability negotiation (section
|
||||
4.), the server reads and queues commands as they arrive. It executes
|
||||
commands from the queue one after the other. Commands executed
|
||||
out-of-band jump the queue: the command get executed right away,
|
||||
possibly overtaking prior in-band commands. The client may therefore
|
||||
receive such a command's response before responses from prior in-band
|
||||
commands.
|
||||
|
||||
To be able to match responses back to their commands, the client needs
|
||||
to pass "id" with out-of-band commands. Passing it with all commands
|
||||
is recommended for clients that accept capability "oob".
|
||||
|
||||
If the client sends in-band commands faster than the server can
|
||||
execute them, the server will eventually drop commands to limit the
|
||||
queue length. The sever sends event COMMAND_DROPPED then.
|
||||
|
||||
Only a few commands support out-of-band execution. The ones that do
|
||||
have "allow-oob": true in output of query-qmp-schema.
|
||||
|
||||
2.4 Commands Responses
|
||||
----------------------
|
||||
@@ -223,12 +236,13 @@ This section provides some examples of real QMP usage, in all of them
|
||||
3.1 Server greeting
|
||||
-------------------
|
||||
|
||||
S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 },
|
||||
"package": ""}, "capabilities": []}}
|
||||
S: { "QMP": {"version": {"qemu": {"micro": 0, "minor": 0, "major": 3},
|
||||
"package": "v3.0.0"}, "capabilities": ["oob"] } }
|
||||
|
||||
3.2 Client QMP negotiation
|
||||
--------------------------
|
||||
C: { "execute": "qmp_capabilities" }
|
||||
3.2 Capabilities negotiation
|
||||
----------------------------
|
||||
|
||||
C: { "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } }
|
||||
S: { "return": {}}
|
||||
|
||||
3.3 Simple 'stop' execution
|
||||
@@ -255,6 +269,15 @@ S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } }
|
||||
S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 },
|
||||
"event": "POWERDOWN" }
|
||||
|
||||
3.7 Out-of-band execution
|
||||
-------------------------
|
||||
|
||||
C: { "exec-oob": "migrate-pause", "id": 42 }
|
||||
S: { "id": 42,
|
||||
"error": { "class": "GenericError",
|
||||
"desc": "migrate-pause is currently only supported during postcopy-active state" } }
|
||||
|
||||
|
||||
4. Capabilities Negotiation
|
||||
===========================
|
||||
|
||||
|
||||
@@ -41,15 +41,15 @@ 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);
|
||||
QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp);
|
||||
QDict *qmp_error_response(Error *err);
|
||||
QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
||||
bool allow_oob);
|
||||
bool qmp_is_oob(QDict *dict);
|
||||
|
||||
typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque);
|
||||
|
||||
@@ -19,6 +19,8 @@ QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2);
|
||||
QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp)
|
||||
GCC_FMT_ATTR(1, 0);
|
||||
|
||||
QDict *qdict_from_jsonf_nofail(const char *string, ...) GCC_FMT_ATTR(1, 2);
|
||||
|
||||
QString *qobject_to_json(const QObject *obj);
|
||||
QString *qobject_to_json_pretty(const QObject *obj);
|
||||
|
||||
|
||||
+4
-19
@@ -46,7 +46,7 @@
|
||||
# Enumeration of capabilities to be advertised during initial client
|
||||
# connection, used for agreeing on particular QMP extension behaviors.
|
||||
#
|
||||
# @oob: QMP ability to support Out-Of-Band requests.
|
||||
# @oob: QMP ability to support out-of-band requests.
|
||||
# (Please refer to qmp-spec.txt for more information on OOB)
|
||||
#
|
||||
# Since: 2.12
|
||||
@@ -3454,6 +3454,9 @@
|
||||
# only be dropped when the oob capability is enabled.
|
||||
#
|
||||
# @id: The dropped command's "id" field.
|
||||
# FIXME Broken by design. Events are broadcast to all monitors. If
|
||||
# another monitor's client has a command with the same ID in flight,
|
||||
# the event will incorrectly claim that command was dropped.
|
||||
#
|
||||
# @reason: The reason why the command is dropped.
|
||||
#
|
||||
@@ -3469,24 +3472,6 @@
|
||||
{ 'event': 'COMMAND_DROPPED' ,
|
||||
'data': { 'id': 'any', 'reason': 'CommandDropReason' } }
|
||||
|
||||
##
|
||||
# @x-oob-test:
|
||||
#
|
||||
# Test OOB functionality. When sending this command with lock=true,
|
||||
# it'll try to hang the dispatcher. When sending it with lock=false,
|
||||
# it'll try to notify the locked thread to continue. Note: it should
|
||||
# only be used by QMP test program rather than anything else.
|
||||
#
|
||||
# Since: 2.12
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# { "execute": "x-oob-test",
|
||||
# "arguments": { "lock": true } }
|
||||
##
|
||||
{ 'command': 'x-oob-test', 'data' : { 'lock': 'bool' },
|
||||
'allow-oob': true }
|
||||
|
||||
##
|
||||
# @set-numa-node:
|
||||
#
|
||||
|
||||
+50
-47
@@ -20,13 +20,14 @@
|
||||
#include "qapi/qmp/qbool.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
|
||||
static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
|
||||
Error **errp)
|
||||
{
|
||||
const char *exec_key = NULL;
|
||||
const QDictEntry *ent;
|
||||
const char *arg_name;
|
||||
const QObject *arg_obj;
|
||||
bool has_exec_key = false;
|
||||
QDict *dict = NULL;
|
||||
QDict *dict;
|
||||
|
||||
dict = qobject_to(QDict, request);
|
||||
if (!dict) {
|
||||
@@ -39,27 +40,25 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
|
||||
arg_name = qdict_entry_key(ent);
|
||||
arg_obj = qdict_entry_value(ent);
|
||||
|
||||
if (!strcmp(arg_name, "execute")) {
|
||||
if (!strcmp(arg_name, "execute")
|
||||
|| (!strcmp(arg_name, "exec-oob") && allow_oob)) {
|
||||
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
|
||||
error_setg(errp,
|
||||
"QMP input member 'execute' must be a string");
|
||||
error_setg(errp, "QMP input member '%s' must be a string",
|
||||
arg_name);
|
||||
return NULL;
|
||||
}
|
||||
has_exec_key = true;
|
||||
if (exec_key) {
|
||||
error_setg(errp, "QMP input member '%s' clashes with '%s'",
|
||||
arg_name, exec_key);
|
||||
return NULL;
|
||||
}
|
||||
exec_key = arg_name;
|
||||
} else if (!strcmp(arg_name, "arguments")) {
|
||||
if (qobject_type(arg_obj) != QTYPE_QDICT) {
|
||||
error_setg(errp,
|
||||
"QMP input member 'arguments' must be an object");
|
||||
return NULL;
|
||||
}
|
||||
} else if (!strcmp(arg_name, "id")) {
|
||||
continue;
|
||||
} else if (!strcmp(arg_name, "control")) {
|
||||
if (qobject_type(arg_obj) != QTYPE_QDICT) {
|
||||
error_setg(errp,
|
||||
"QMP input member 'control' must be a dict");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
error_setg(errp, "QMP input member '%s' is unexpected",
|
||||
arg_name);
|
||||
@@ -67,7 +66,7 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_exec_key) {
|
||||
if (!exec_key) {
|
||||
error_setg(errp, "QMP input lacks member 'execute'");
|
||||
return NULL;
|
||||
}
|
||||
@@ -76,20 +75,27 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
|
||||
}
|
||||
|
||||
static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
||||
Error **errp)
|
||||
bool allow_oob, Error **errp)
|
||||
{
|
||||
Error *local_err = NULL;
|
||||
bool oob;
|
||||
const char *command;
|
||||
QDict *args, *dict;
|
||||
QmpCommand *cmd;
|
||||
QObject *ret = NULL;
|
||||
|
||||
dict = qmp_dispatch_check_obj(request, errp);
|
||||
dict = qmp_dispatch_check_obj(request, allow_oob, errp);
|
||||
if (!dict) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
command = qdict_get_str(dict, "execute");
|
||||
command = qdict_get_try_str(dict, "execute");
|
||||
oob = false;
|
||||
if (!command) {
|
||||
assert(allow_oob);
|
||||
command = qdict_get_str(dict, "exec-oob");
|
||||
oob = true;
|
||||
}
|
||||
cmd = qmp_find_command(cmds, command);
|
||||
if (cmd == NULL) {
|
||||
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
|
||||
@@ -101,6 +107,11 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
||||
command);
|
||||
return NULL;
|
||||
}
|
||||
if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
|
||||
error_setg(errp, "The command %s does not support OOB",
|
||||
command);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (runstate_check(RUN_STATE_PRECONFIG) &&
|
||||
!(cmd->options & QCO_ALLOW_PRECONFIG)) {
|
||||
@@ -122,6 +133,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
||||
} else if (cmd->options & QCO_NO_SUCCESS_RESP) {
|
||||
g_assert(!ret);
|
||||
} else if (!ret) {
|
||||
/* TODO turn into assertion */
|
||||
ret = QOBJECT(qdict_new());
|
||||
}
|
||||
|
||||
@@ -130,53 +142,44 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
||||
return ret;
|
||||
}
|
||||
|
||||
QObject *qmp_build_error_object(Error *err)
|
||||
QDict *qmp_error_response(Error *err)
|
||||
{
|
||||
return qobject_from_jsonf("{ 'class': %s, 'desc': %s }",
|
||||
QapiErrorClass_str(error_get_class(err)),
|
||||
error_get_pretty(err));
|
||||
QDict *rsp;
|
||||
|
||||
rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }",
|
||||
QapiErrorClass_str(error_get_class(err)),
|
||||
error_get_pretty(err));
|
||||
error_free(err);
|
||||
return rsp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect whether a request should be run out-of-band, by quickly
|
||||
* peeking at whether we have: { "control": { "run-oob": true } }. By
|
||||
* default commands are run in-band.
|
||||
* Does @qdict look like a command to be run out-of-band?
|
||||
*/
|
||||
bool qmp_is_oob(QDict *dict)
|
||||
{
|
||||
QBool *bool_obj;
|
||||
|
||||
dict = qdict_get_qdict(dict, "control");
|
||||
if (!dict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool_obj = qobject_to(QBool, qdict_get(dict, "run-oob"));
|
||||
if (!bool_obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return qbool_get_bool(bool_obj);
|
||||
return qdict_haskey(dict, "exec-oob")
|
||||
&& !qdict_haskey(dict, "execute");
|
||||
}
|
||||
|
||||
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request)
|
||||
QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
|
||||
bool allow_oob)
|
||||
{
|
||||
Error *err = NULL;
|
||||
QObject *ret;
|
||||
QDict *rsp;
|
||||
|
||||
ret = do_qmp_dispatch(cmds, request, &err);
|
||||
ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
|
||||
|
||||
rsp = qdict_new();
|
||||
if (err) {
|
||||
qdict_put_obj(rsp, "error", qmp_build_error_object(err));
|
||||
error_free(err);
|
||||
rsp = qmp_error_response(err);
|
||||
} else if (ret) {
|
||||
rsp = qdict_new();
|
||||
qdict_put_obj(rsp, "return", ret);
|
||||
} else {
|
||||
qobject_unref(rsp);
|
||||
return NULL;
|
||||
/* Can only happen for commands with QCO_NO_SUCCESS_RESP */
|
||||
rsp = NULL;
|
||||
}
|
||||
|
||||
return QOBJECT(rsp);
|
||||
return rsp;
|
||||
}
|
||||
|
||||
+5
-5
@@ -34,15 +34,15 @@ QMPEventFuncEmit qmp_event_get_func_emit(void)
|
||||
static void timestamp_put(QDict *qdict)
|
||||
{
|
||||
int err;
|
||||
QObject *obj;
|
||||
QDict *ts;
|
||||
qemu_timeval tv;
|
||||
|
||||
err = qemu_gettimeofday(&tv);
|
||||
/* Put -1 to indicate failure of getting host time */
|
||||
obj = qobject_from_jsonf("{ 'seconds': %lld, 'microseconds': %lld }",
|
||||
err < 0 ? -1LL : (long long)tv.tv_sec,
|
||||
err < 0 ? -1LL : (long long)tv.tv_usec);
|
||||
qdict_put_obj(qdict, "timestamp", obj);
|
||||
ts = qdict_from_jsonf_nofail("{ 'seconds': %lld, 'microseconds': %lld }",
|
||||
err < 0 ? -1LL : (long long)tv.tv_sec,
|
||||
err < 0 ? -1LL : (long long)tv.tv_usec);
|
||||
qdict_put(qdict, "timestamp", ts);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+7
-11
@@ -545,7 +545,7 @@ fail:
|
||||
#endif
|
||||
}
|
||||
|
||||
static int send_response(GAState *s, QObject *payload)
|
||||
static int send_response(GAState *s, QDict *payload)
|
||||
{
|
||||
const char *buf;
|
||||
QString *payload_qstr, *response_qstr;
|
||||
@@ -553,7 +553,7 @@ static int send_response(GAState *s, QObject *payload)
|
||||
|
||||
g_assert(payload && s->channel);
|
||||
|
||||
payload_qstr = qobject_to_json(payload);
|
||||
payload_qstr = qobject_to_json(QOBJECT(payload));
|
||||
if (!payload_qstr) {
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -581,12 +581,12 @@ static int send_response(GAState *s, QObject *payload)
|
||||
|
||||
static void process_command(GAState *s, QDict *req)
|
||||
{
|
||||
QObject *rsp = NULL;
|
||||
QDict *rsp;
|
||||
int ret;
|
||||
|
||||
g_assert(req);
|
||||
g_debug("processing command");
|
||||
rsp = qmp_dispatch(&ga_commands, QOBJECT(req));
|
||||
rsp = qmp_dispatch(&ga_commands, QOBJECT(req), false);
|
||||
if (rsp) {
|
||||
ret = send_response(s, rsp);
|
||||
if (ret < 0) {
|
||||
@@ -610,15 +610,13 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens)
|
||||
qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err));
|
||||
if (err || !qdict) {
|
||||
qobject_unref(qdict);
|
||||
qdict = qdict_new();
|
||||
if (!err) {
|
||||
g_warning("failed to parse event: unknown error");
|
||||
error_setg(&err, QERR_JSON_PARSING);
|
||||
} else {
|
||||
g_warning("failed to parse event: %s", error_get_pretty(err));
|
||||
}
|
||||
qdict_put_obj(qdict, "error", qmp_build_error_object(err));
|
||||
error_free(err);
|
||||
qdict = qmp_error_response(err);
|
||||
}
|
||||
|
||||
/* handle host->guest commands */
|
||||
@@ -627,13 +625,11 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens)
|
||||
} else {
|
||||
if (!qdict_haskey(qdict, "error")) {
|
||||
qobject_unref(qdict);
|
||||
qdict = qdict_new();
|
||||
g_warning("unrecognized payload format");
|
||||
error_setg(&err, QERR_UNSUPPORTED);
|
||||
qdict_put_obj(qdict, "error", qmp_build_error_object(err));
|
||||
error_free(err);
|
||||
qdict = qmp_error_response(err);
|
||||
}
|
||||
ret = send_response(s, QOBJECT(qdict));
|
||||
ret = send_response(s, qdict);
|
||||
if (ret < 0) {
|
||||
g_warning("error sending error response: %s", strerror(-ret));
|
||||
}
|
||||
|
||||
@@ -737,19 +737,3 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp)
|
||||
|
||||
return mem_info;
|
||||
}
|
||||
|
||||
static QemuSemaphore x_oob_test_sem;
|
||||
|
||||
static void __attribute__((constructor)) x_oob_test_init(void)
|
||||
{
|
||||
qemu_sem_init(&x_oob_test_sem, 0);
|
||||
}
|
||||
|
||||
void qmp_x_oob_test(bool lock, Error **errp)
|
||||
{
|
||||
if (lock) {
|
||||
qemu_sem_wait(&x_oob_test_sem);
|
||||
} else {
|
||||
qemu_sem_post(&x_oob_test_sem);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-5
@@ -59,10 +59,6 @@ QObject *qobject_from_json(const char *string, Error **errp)
|
||||
return qobject_from_jsonv(string, NULL, errp);
|
||||
}
|
||||
|
||||
/*
|
||||
* IMPORTANT: This function aborts on error, thus it must not
|
||||
* be used with untrusted arguments.
|
||||
*/
|
||||
QObject *qobject_from_jsonf(const char *string, ...)
|
||||
{
|
||||
QObject *obj;
|
||||
@@ -72,7 +68,24 @@ QObject *qobject_from_jsonf(const char *string, ...)
|
||||
obj = qobject_from_jsonv(string, &ap, &error_abort);
|
||||
va_end(ap);
|
||||
|
||||
assert(obj != NULL);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse @string as JSON object with %-escapes interpolated.
|
||||
* Abort on error. Do not use with untrusted @string.
|
||||
* Return the resulting QDict. It is never null.
|
||||
*/
|
||||
QDict *qdict_from_jsonf_nofail(const char *string, ...)
|
||||
{
|
||||
QDict *obj;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, string);
|
||||
obj = qobject_to(QDict, qobject_from_jsonv(string, &ap, &error_abort));
|
||||
va_end(ap);
|
||||
|
||||
assert(obj);
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,7 +144,7 @@
|
||||
{ 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
|
||||
{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true }
|
||||
|
||||
# Smoke test on Out-Of-Band and allow-preconfig-test
|
||||
# Smoke test on out-of-band and allow-preconfig-test
|
||||
{ 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true }
|
||||
|
||||
# For testing integer range flattening in opts-visitor. The following schema
|
||||
|
||||
+73
-32
@@ -135,16 +135,65 @@ static void test_qmp_protocol(void)
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
/* Tests for Out-Of-Band support. */
|
||||
/* Out-of-band tests */
|
||||
|
||||
char tmpdir[] = "/tmp/qmp-test-XXXXXX";
|
||||
char *fifo_name;
|
||||
|
||||
static void setup_blocking_cmd(void)
|
||||
{
|
||||
if (!mkdtemp(tmpdir)) {
|
||||
g_error("mkdtemp: %s", strerror(errno));
|
||||
}
|
||||
fifo_name = g_strdup_printf("%s/fifo", tmpdir);
|
||||
if (mkfifo(fifo_name, 0666)) {
|
||||
g_error("mkfifo: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static void cleanup_blocking_cmd(void)
|
||||
{
|
||||
unlink(fifo_name);
|
||||
rmdir(tmpdir);
|
||||
}
|
||||
|
||||
static void send_cmd_that_blocks(QTestState *s, const char *id)
|
||||
{
|
||||
qtest_async_qmp(s, "{ 'execute': 'blockdev-add', 'id': %s,"
|
||||
" 'arguments': {"
|
||||
" 'driver': 'blkdebug', 'node-name': %s,"
|
||||
" 'config': %s,"
|
||||
" 'image': { 'driver': 'null-co' } } }",
|
||||
id, id, fifo_name);
|
||||
}
|
||||
|
||||
static void unblock_blocked_cmd(void)
|
||||
{
|
||||
int fd = open(fifo_name, O_WRONLY);
|
||||
g_assert(fd >= 0);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void send_oob_cmd_that_fails(QTestState *s, const char *id)
|
||||
{
|
||||
qtest_async_qmp(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id);
|
||||
}
|
||||
|
||||
static void recv_cmd_id(QTestState *s, const char *id)
|
||||
{
|
||||
QDict *resp = qtest_qmp_receive(s);
|
||||
|
||||
g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id);
|
||||
qobject_unref(resp);
|
||||
}
|
||||
|
||||
static void test_qmp_oob(void)
|
||||
{
|
||||
QTestState *qts;
|
||||
QDict *resp, *q;
|
||||
int acks = 0;
|
||||
const QListEntry *entry;
|
||||
QList *capabilities;
|
||||
QString *qstr;
|
||||
const char *cmd_id;
|
||||
|
||||
qts = qtest_init_without_qmp_handshake(true, common_args);
|
||||
|
||||
@@ -179,43 +228,33 @@ static void test_qmp_oob(void)
|
||||
* Try any command that does not support OOB but with OOB flag. We
|
||||
* should get failure.
|
||||
*/
|
||||
resp = qtest_qmp(qts,
|
||||
"{ 'execute': 'query-cpus',"
|
||||
" 'control': { 'run-oob': true } }");
|
||||
resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }");
|
||||
g_assert(qdict_haskey(resp, "error"));
|
||||
qobject_unref(resp);
|
||||
|
||||
/*
|
||||
* First send the "x-oob-test" command with lock=true and
|
||||
* oob=false, it should hang the dispatcher and main thread;
|
||||
* later, we send another lock=false with oob=true to continue
|
||||
* that thread processing. Finally we should receive replies from
|
||||
* both commands.
|
||||
*/
|
||||
qtest_async_qmp(qts,
|
||||
"{ 'execute': 'x-oob-test',"
|
||||
" 'arguments': { 'lock': true }, "
|
||||
" 'id': 'lock-cmd'}");
|
||||
qtest_async_qmp(qts,
|
||||
"{ 'execute': 'x-oob-test', "
|
||||
" 'arguments': { 'lock': false }, "
|
||||
" 'control': { 'run-oob': true }, "
|
||||
" 'id': 'unlock-cmd' }");
|
||||
/* OOB command overtakes slow in-band command */
|
||||
setup_blocking_cmd();
|
||||
send_cmd_that_blocks(qts, "ib-blocks-1");
|
||||
qtest_async_qmp(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }");
|
||||
send_oob_cmd_that_fails(qts, "oob-1");
|
||||
recv_cmd_id(qts, "oob-1");
|
||||
unblock_blocked_cmd();
|
||||
recv_cmd_id(qts, "ib-blocks-1");
|
||||
recv_cmd_id(qts, "ib-quick-1");
|
||||
|
||||
/* Ignore all events. Wait for 2 acks */
|
||||
while (acks < 2) {
|
||||
resp = qtest_qmp_receive(qts);
|
||||
cmd_id = qdict_get_str(resp, "id");
|
||||
if (!g_strcmp0(cmd_id, "lock-cmd") ||
|
||||
!g_strcmp0(cmd_id, "unlock-cmd")) {
|
||||
acks++;
|
||||
}
|
||||
qobject_unref(resp);
|
||||
}
|
||||
/* Even malformed in-band command fails in-band */
|
||||
send_cmd_that_blocks(qts, "blocks-2");
|
||||
qtest_async_qmp(qts, "{ 'id': 'err-2' }");
|
||||
unblock_blocked_cmd();
|
||||
recv_cmd_id(qts, "blocks-2");
|
||||
recv_cmd_id(qts, "err-2");
|
||||
cleanup_blocking_cmd();
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
/* Query smoke tests */
|
||||
|
||||
static int query_error_class(const char *cmd)
|
||||
{
|
||||
static struct {
|
||||
@@ -392,6 +431,8 @@ static void add_query_tests(QmpSchema *schema)
|
||||
}
|
||||
}
|
||||
|
||||
/* Preconfig tests */
|
||||
|
||||
static void test_qmp_preconfig(void)
|
||||
{
|
||||
QDict *rsp, *ret;
|
||||
|
||||
@@ -227,6 +227,38 @@ static void test_qga_ping(gconstpointer fix)
|
||||
qobject_unref(ret);
|
||||
}
|
||||
|
||||
static void test_qga_invalid_id(gconstpointer fix)
|
||||
{
|
||||
const TestFixture *fixture = fix;
|
||||
QDict *ret, *error;
|
||||
const char *class;
|
||||
|
||||
ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}");
|
||||
g_assert_nonnull(ret);
|
||||
|
||||
error = qdict_get_qdict(ret, "error");
|
||||
class = qdict_get_try_str(error, "class");
|
||||
g_assert_cmpstr(class, ==, "GenericError");
|
||||
|
||||
qobject_unref(ret);
|
||||
}
|
||||
|
||||
static void test_qga_invalid_oob(gconstpointer fix)
|
||||
{
|
||||
const TestFixture *fixture = fix;
|
||||
QDict *ret, *error;
|
||||
const char *class;
|
||||
|
||||
ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}");
|
||||
g_assert_nonnull(ret);
|
||||
|
||||
error = qdict_get_qdict(ret, "error");
|
||||
class = qdict_get_try_str(error, "class");
|
||||
g_assert_cmpstr(class, ==, "GenericError");
|
||||
|
||||
qobject_unref(ret);
|
||||
}
|
||||
|
||||
static void test_qga_invalid_args(gconstpointer fix)
|
||||
{
|
||||
const TestFixture *fixture = fix;
|
||||
@@ -982,6 +1014,8 @@ int main(int argc, char **argv)
|
||||
g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops);
|
||||
g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read);
|
||||
g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time);
|
||||
g_test_add_data_func("/qga/invalid-id", &fix, test_qga_invalid_id);
|
||||
g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob);
|
||||
g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd);
|
||||
g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args);
|
||||
g_test_add_data_func("/qga/fsfreeze-status", &fix,
|
||||
|
||||
+10
-13
@@ -110,13 +110,13 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
|
||||
static void test_dispatch_cmd(void)
|
||||
{
|
||||
QDict *req = qdict_new();
|
||||
QObject *resp;
|
||||
QDict *resp;
|
||||
|
||||
qdict_put_str(req, "execute", "user_def_cmd");
|
||||
|
||||
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
|
||||
resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
|
||||
assert(resp != NULL);
|
||||
assert(!qdict_haskey(qobject_to(QDict, resp), "error"));
|
||||
assert(!qdict_haskey(resp, "error"));
|
||||
|
||||
qobject_unref(resp);
|
||||
qobject_unref(req);
|
||||
@@ -127,13 +127,13 @@ static void test_dispatch_cmd_failure(void)
|
||||
{
|
||||
QDict *req = qdict_new();
|
||||
QDict *args = qdict_new();
|
||||
QObject *resp;
|
||||
QDict *resp;
|
||||
|
||||
qdict_put_str(req, "execute", "user_def_cmd2");
|
||||
|
||||
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
|
||||
resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
|
||||
assert(resp != NULL);
|
||||
assert(qdict_haskey(qobject_to(QDict, resp), "error"));
|
||||
assert(qdict_haskey(resp, "error"));
|
||||
|
||||
qobject_unref(resp);
|
||||
qobject_unref(req);
|
||||
@@ -145,9 +145,9 @@ static void test_dispatch_cmd_failure(void)
|
||||
|
||||
qdict_put_str(req, "execute", "user_def_cmd");
|
||||
|
||||
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
|
||||
resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
|
||||
assert(resp != NULL);
|
||||
assert(qdict_haskey(qobject_to(QDict, resp), "error"));
|
||||
assert(qdict_haskey(resp, "error"));
|
||||
|
||||
qobject_unref(resp);
|
||||
qobject_unref(req);
|
||||
@@ -155,18 +155,15 @@ static void test_dispatch_cmd_failure(void)
|
||||
|
||||
static QObject *test_qmp_dispatch(QDict *req)
|
||||
{
|
||||
QObject *resp_obj;
|
||||
QDict *resp;
|
||||
QObject *ret;
|
||||
|
||||
resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req));
|
||||
assert(resp_obj);
|
||||
resp = qobject_to(QDict, resp_obj);
|
||||
resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
|
||||
assert(resp && !qdict_haskey(resp, "error"));
|
||||
ret = qdict_get(resp, "return");
|
||||
assert(ret);
|
||||
qobject_ref(ret);
|
||||
qobject_unref(resp_obj);
|
||||
qobject_unref(resp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user