qapi patches for 2018-03-27, 2.12-rc1

- Marc-André Lureau: qmp-test: fix response leak
 - Eric Blake: tests: Silence false positive warning on generated test name
 - Laurent Vivier: 0/4 (partial) coccinelle: re-run scripts from scripst/coccinelle
 - Peter Xu: 0/8 Monitor: some oob related patches (fixes, new param, tests)
 - Satheesh Rajendran: hmp.c: Revert hmp_info_cpus output format change
 -----BEGIN PGP SIGNATURE-----
 Comment: Public key at http://people.redhat.com/eblake/eblake.gpg
 
 iQEcBAABCAAGBQJaumDMAAoJEKeha0olJ0NqY34IAJ1AJLZeqtb9HIPDrL/iydXQ
 qrMPxfz/fbcjMbomYvltvCYB3GMQw88YVtTuu2kS5aZsr6vLhpw2ZjGrb+a0llFM
 lF6oERg/6VmfQNaeqcHEzORIdG6/GEctxoWS07f+NgSrjdxNNHyU+i/LGvvA5K4m
 x7akPw3DZIF/oBLDlFgtl80KtOf8vQ8QitCIp2nsURQFLHvjVSoXHGUfhoiUUgNA
 89rF62Repq9xZXSsFr+Jpp4/bPGxA717sixXSxFUDuTVIZGnffbt4wVxLi4TKSx1
 1Ho6psPK3d82rQ9OeIc6CqulElLMPGn7aNwR1VR6ig0sB409K5PQJybJDqQD/C4=
 =ld/c
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/ericb/tags/pull-qapi-2018-03-27-v2' into staging

qapi patches for 2018-03-27, 2.12-rc1

- Marc-André Lureau: qmp-test: fix response leak
- Eric Blake: tests: Silence false positive warning on generated test name
- Laurent Vivier: 0/4 (partial) coccinelle: re-run scripts from scripst/coccinelle
- Peter Xu: 0/8 Monitor: some oob related patches (fixes, new param, tests)
- Satheesh Rajendran: hmp.c: Revert hmp_info_cpus output format change

# gpg: Signature made Tue 27 Mar 2018 16:18:36 BST
# gpg:                using RSA key A7A16B4A2527436A
# gpg: Good signature from "Eric Blake <eblake@redhat.com>"
# gpg:                 aka "Eric Blake (Free Software Programmer) <ebb9@byu.net>"
# gpg:                 aka "[jpeg image of size 6874]"
# Primary key fingerprint: 71C2 CC22 B1C4 6029 27D2  F3AA A7A1 6B4A 2527 436A

* remotes/ericb/tags/pull-qapi-2018-03-27-v2:
  hmp.c: Revert hmp_info_cpus output format change
  tests: qmp-test: add test for new "x-oob"
  tests: Add parameter to qtest_init_without_qmp_handshake
  monitor: new parameter "x-oob"
  qmp: cleanup qmp queues properly
  tests: add oob-test for qapi-schema
  tests: let qapi-schema tests detect oob
  qapi: restrict allow-oob value to be "true"
  qmp: fix qmp_capabilities error regression
  qdict: remove useless cast
  error: Remove NULL checks on error_propagate() calls
  error: Strip trailing '\n' from error string arguments (again again)
  tests: Silence false positive warning on generated test name
  qmp-test: fix response leak

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-03-27 19:20:57 +01:00
commit f55e88f2ab
24 changed files with 233 additions and 84 deletions

View File

@ -695,12 +695,11 @@ static void nvme_parse_filename(const char *filename, QDict *options,
unsigned long ns;
const char *slash = strchr(tmp, '/');
if (!slash) {
qdict_put(options, NVME_BLOCK_OPT_DEVICE,
qstring_from_str(tmp));
qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, tmp);
return;
}
device = g_strndup(tmp, slash - tmp);
qdict_put(options, NVME_BLOCK_OPT_DEVICE, qstring_from_str(device));
qdict_put_str(options, NVME_BLOCK_OPT_DEVICE, device);
g_free(device);
namespace = slash + 1;
if (*namespace && qemu_strtoul(namespace, NULL, 10, &ns)) {
@ -708,8 +707,8 @@ static void nvme_parse_filename(const char *filename, QDict *options,
namespace);
return;
}
qdict_put(options, NVME_BLOCK_OPT_NAMESPACE,
qstring_from_str(*namespace ? namespace : "1"));
qdict_put_str(options, NVME_BLOCK_OPT_NAMESPACE,
*namespace ? namespace : "1");
}
}
@ -1082,7 +1081,7 @@ static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts)
bs->drv->format_name);
}
qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name));
qdict_put_str(opts, "driver", bs->drv->format_name);
bs->full_open_options = opts;
}

2
hmp.c
View File

@ -381,7 +381,7 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "%c CPU #%" PRId64 ":", active,
cpu->value->cpu_index);
monitor_printf(mon, " thread-id=%" PRId64 "\n", cpu->value->thread_id);
monitor_printf(mon, " thread_id=%" PRId64 "\n", cpu->value->thread_id);
}
qapi_free_CpuInfoFastList(cpu_list);

View File

@ -13,6 +13,7 @@ extern Monitor *cur_mon;
#define MONITOR_USE_READLINE 0x02
#define MONITOR_USE_CONTROL 0x04
#define MONITOR_USE_PRETTY 0x08
#define MONITOR_USE_OOB 0x10
bool monitor_cur_is_qmp(void);

View File

@ -586,9 +586,7 @@ static gboolean qio_channel_websock_handshake_io(QIOChannel *ioc,
return TRUE;
}
if (err) {
error_propagate(&wioc->io_err, err);
}
error_propagate(&wioc->io_err, err);
trace_qio_channel_websock_handshake_reply(ioc);
qio_channel_add_watch(

118
monitor.c
View File

@ -36,6 +36,7 @@
#include "net/slirp.h"
#include "chardev/char-fe.h"
#include "chardev/char-io.h"
#include "chardev/char-mux.h"
#include "ui/qemu-spice.h"
#include "sysemu/numa.h"
#include "monitor/monitor.h"
@ -234,6 +235,22 @@ static struct {
QEMUBH *qmp_respond_bh;
} mon_global;
struct QMPRequest {
/* Owner of the request */
Monitor *mon;
/* "id" field of the request */
QObject *id;
/* Request object to be handled */
QObject *req;
/*
* Whether we need to resume the monitor afterward. This flag is
* used to emulate the old QMP server behavior that the current
* command must be completed before execution of the next one.
*/
bool need_resume;
};
typedef struct QMPRequest QMPRequest;
/* QMP checker flags */
#define QMP_ACCEPT_UNKNOWNS 1
@ -310,6 +327,38 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,
}
}
static void qmp_request_free(QMPRequest *req)
{
qobject_decref(req->id);
qobject_decref(req->req);
g_free(req);
}
/* Must with the mon->qmp.qmp_queue_lock held */
static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon)
{
while (!g_queue_is_empty(mon->qmp.qmp_requests)) {
qmp_request_free(g_queue_pop_head(mon->qmp.qmp_requests));
}
}
/* Must with the mon->qmp.qmp_queue_lock held */
static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon)
{
while (!g_queue_is_empty(mon->qmp.qmp_responses)) {
qobject_decref(g_queue_pop_head(mon->qmp.qmp_responses));
}
}
static void monitor_qmp_cleanup_queues(Monitor *mon)
{
qemu_mutex_lock(&mon->qmp.qmp_queue_lock);
monitor_qmp_cleanup_req_queue_locked(mon);
monitor_qmp_cleanup_resp_queue_locked(mon);
qemu_mutex_unlock(&mon->qmp.qmp_queue_lock);
}
static void monitor_flush_locked(Monitor *mon);
static gboolean monitor_unblocked(GIOChannel *chan, GIOCondition cond,
@ -701,6 +750,8 @@ static void monitor_data_destroy(Monitor *mon)
QDECREF(mon->outbuf);
qemu_mutex_destroy(&mon->out_lock);
qemu_mutex_destroy(&mon->qmp.qmp_queue_lock);
monitor_qmp_cleanup_req_queue_locked(mon);
monitor_qmp_cleanup_resp_queue_locked(mon);
g_queue_free(mon->qmp.qmp_requests);
g_queue_free(mon->qmp.qmp_responses);
}
@ -1203,8 +1254,14 @@ static bool qmp_cmd_oob_check(Monitor *mon, QDict *req, Error **errp)
cmd = qmp_find_command(mon->qmp.commands, command);
if (!cmd) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"The command %s has not been found", command);
if (mon->qmp.commands == &qmp_cap_negotiation_commands) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"Expecting capabilities negotiation "
"with 'qmp_capabilities'");
} else {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
"The command %s has not been found", command);
}
return false;
}
@ -4003,22 +4060,6 @@ static void monitor_qmp_respond(Monitor *mon, QObject *rsp,
qobject_decref(rsp);
}
struct QMPRequest {
/* Owner of the request */
Monitor *mon;
/* "id" field of the request */
QObject *id;
/* Request object to be handled */
QObject *req;
/*
* Whether we need to resume the monitor afterward. This flag is
* used to emulate the old QMP server behavior that the current
* command must be completed before execution of the next one.
*/
bool need_resume;
};
typedef struct QMPRequest QMPRequest;
/*
* Dispatch one single QMP request. The function will free the req_obj
* and objects inside it before return.
@ -4027,7 +4068,6 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
{
Monitor *mon, *old_mon;
QObject *req, *rsp = NULL, *id;
QDict *qdict = NULL;
bool need_resume;
req = req_obj->req;
@ -4050,18 +4090,6 @@ static void monitor_qmp_dispatch_one(QMPRequest *req_obj)
cur_mon = old_mon;
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_str(ERROR_CLASS_COMMAND_NOT_FOUND))) {
/* Provide a more useful error message */
qdict_del(qdict, "desc");
qdict_put_str(qdict, "desc", "Expecting capabilities negotiation"
" with 'qmp_capabilities'");
}
}
/* Respond if necessary */
monitor_qmp_respond(mon, rsp, NULL, id);
@ -4198,9 +4226,7 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens)
qapi_event_send_command_dropped(id,
COMMAND_DROP_REASON_QUEUE_FULL,
&error_abort);
qobject_decref(id);
qobject_decref(req);
g_free(req_obj);
qmp_request_free(req_obj);
return;
}
}
@ -4315,7 +4341,7 @@ static QObject *get_qmp_greeting(Monitor *mon)
/* Monitors that are not using IOThread won't support OOB */
continue;
}
qlist_append(cap_list, qstring_from_str(QMPCapability_str(cap)));
qlist_append_str(cap_list, QMPCapability_str(cap));
}
return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': %p}}",
@ -4342,6 +4368,7 @@ static void monitor_qmp_event(void *opaque, int event)
mon_refcount++;
break;
case CHR_EVENT_CLOSED:
monitor_qmp_cleanup_queues(mon);
json_message_parser_destroy(&mon->qmp.parser);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
mon_refcount--;
@ -4536,12 +4563,26 @@ static void monitor_qmp_setup_handlers_bh(void *opaque)
void monitor_init(Chardev *chr, int flags)
{
Monitor *mon = g_malloc(sizeof(*mon));
bool use_readline = flags & MONITOR_USE_READLINE;
bool use_oob = flags & MONITOR_USE_OOB;
monitor_data_init(mon, false, false);
if (use_oob) {
if (CHARDEV_IS_MUX(chr)) {
error_report("Monitor Out-Of-Band is not supported with "
"MUX typed chardev backend");
exit(1);
}
if (use_readline) {
error_report("Monitor Out-Of-band is only supported by QMP");
exit(1);
}
}
monitor_data_init(mon, false, use_oob);
qemu_chr_fe_init(&mon->chr, chr, &error_abort);
mon->flags = flags;
if (flags & MONITOR_USE_READLINE) {
if (use_readline) {
mon->rs = readline_init(monitor_readline_printf,
monitor_readline_flush,
mon,
@ -4637,6 +4678,9 @@ QemuOptsList qemu_mon_opts = {
},{
.name = "pretty",
.type = QEMU_OPT_BOOL,
},{
.name = "x-oob",
.type = QEMU_OPT_BOOL,
},
{ /* end of list */ }
},

View File

@ -872,7 +872,7 @@ def check_keys(expr_elem, meta, required, optional=[]):
raise QAPISemError(info,
"'%s' of %s '%s' should only use false value"
% (key, meta, name))
if key == 'boxed' and value is not True:
if (key == 'boxed' or key == 'allow-oob') and value is not True:
raise QAPISemError(info,
"'%s' of %s '%s' should only use true value"
% (key, meta, name))

View File

@ -86,25 +86,25 @@ static void assert_hvf_ok(hv_return_t ret)
switch (ret) {
case HV_ERROR:
error_report("Error: HV_ERROR\n");
error_report("Error: HV_ERROR");
break;
case HV_BUSY:
error_report("Error: HV_BUSY\n");
error_report("Error: HV_BUSY");
break;
case HV_BAD_ARGUMENT:
error_report("Error: HV_BAD_ARGUMENT\n");
error_report("Error: HV_BAD_ARGUMENT");
break;
case HV_NO_RESOURCES:
error_report("Error: HV_NO_RESOURCES\n");
error_report("Error: HV_NO_RESOURCES");
break;
case HV_NO_DEVICE:
error_report("Error: HV_NO_DEVICE\n");
error_report("Error: HV_NO_DEVICE");
break;
case HV_UNSUPPORTED:
error_report("Error: HV_UNSUPPORTED\n");
error_report("Error: HV_UNSUPPORTED");
break;
default:
error_report("Unknown Error\n");
error_report("Unknown Error");
}
abort();
@ -191,7 +191,7 @@ void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
if (mem) {
mem->size = 0;
if (do_hvf_set_memory(mem)) {
error_report("Failed to reset overlapping slot\n");
error_report("Failed to reset overlapping slot");
abort();
}
}
@ -211,7 +211,7 @@ void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
}
if (x == hvf_state->num_slots) {
error_report("No free slots\n");
error_report("No free slots");
abort();
}
@ -221,7 +221,7 @@ void hvf_set_phys_mem(MemoryRegionSection *section, bool add)
mem->region = area;
if (do_hvf_set_memory(mem)) {
error_report("Error registering new memory slot\n");
error_report("Error registering new memory slot");
abort();
}
}
@ -884,7 +884,7 @@ int hvf_vcpu_exec(CPUState *cpu)
break;
}
default:
error_report("Unrecognized CR %d\n", cr);
error_report("Unrecognized CR %d", cr);
abort();
}
RIP(env) += ins_len;
@ -930,7 +930,7 @@ int hvf_vcpu_exec(CPUState *cpu)
env->error_code = 0;
break;
default:
error_report("%llx: unhandled exit %llx\n", rip, exit_reason);
error_report("%llx: unhandled exit %llx", rip, exit_reason);
}
} while (ret == 0);

View File

@ -523,6 +523,7 @@ qapi-schema += missing-comma-object.json
qapi-schema += missing-type.json
qapi-schema += nested-struct-data.json
qapi-schema += non-objects.json
qapi-schema += oob-test.json
qapi-schema += pragma-doc-required-crap.json
qapi-schema += pragma-extra-junk.json
qapi-schema += pragma-name-case-whitelist-crap.json

View File

@ -166,7 +166,8 @@ static const char *qtest_qemu_binary(void)
return qemu_bin;
}
QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
QTestState *qtest_init_without_qmp_handshake(bool use_oob,
const char *extra_args)
{
QTestState *s;
int sock, qmpsock, i;
@ -199,12 +200,13 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
command = g_strdup_printf("exec %s "
"-qtest unix:%s,nowait "
"-qtest-log %s "
"-qmp unix:%s,nowait "
"-chardev socket,path=%s,nowait,id=char0 "
"-mon chardev=char0,mode=control%s "
"-machine accel=qtest "
"-display none "
"%s", qemu_binary, socket_path,
getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null",
qmp_socket_path,
qmp_socket_path, use_oob ? ",x-oob=on" : "",
extra_args ?: "");
execlp("/bin/sh", "sh", "-c", command, NULL);
exit(1);
@ -239,7 +241,7 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args)
QTestState *qtest_init(const char *extra_args)
{
QTestState *s = qtest_init_without_qmp_handshake(extra_args);
QTestState *s = qtest_init_without_qmp_handshake(false, extra_args);
/* Read the QMP greeting and then do the handshake */
qtest_qmp_discard_response(s, "");

View File

@ -56,11 +56,14 @@ QTestState *qtest_init(const char *extra_args);
/**
* qtest_init_without_qmp_handshake:
* @extra_args: other arguments to pass to QEMU.
* @use_oob: true to have the server advertise OOB support
* @extra_args: other arguments to pass to QEMU. CAUTION: these
* arguments are subject to word splitting and shell evaluation.
*
* Returns: #QTestState instance.
*/
QTestState *qtest_init_without_qmp_handshake(const char *extra_args);
QTestState *qtest_init_without_qmp_handshake(bool use_oob,
const char *extra_args);
/**
* qtest_quit:

View File

@ -28,9 +28,9 @@ object q_obj_cmd-arg
member arg2: str optional=True
member arg3: bool optional=False
command cmd q_obj_cmd-arg -> Object
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False
command cmd-boxed Object -> None
gen=True success_response=True boxed=True
gen=True success_response=True boxed=True oob=False
doc freeform
body=
= Section

View File

@ -5,4 +5,4 @@ module ident-with-escape.json
object q_obj_fooA-arg
member bar1: str optional=False
command fooA q_obj_fooA-arg -> None
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False

View File

@ -3,6 +3,6 @@ enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
prefix QTYPE
module indented-expr.json
command eins None -> None
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False
command zwei None -> None
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False

View File

@ -0,0 +1 @@
tests/qapi-schema/oob-test.json:2: 'allow-oob' of command 'oob-command-1' should only use true value

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,2 @@
# Check against oob illegal value
{ 'command': 'oob-command-1', 'allow-oob': 'some-string' }

View File

View File

@ -139,6 +139,9 @@
{ 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true }
# Smoke test on Out-Of-Band
{ 'command': 'an-oob-command', 'allow-oob': true }
# For testing integer range flattening in opts-visitor. The following schema
# corresponds to the option format:
#

View File

@ -16,7 +16,7 @@ object Empty1
object Empty2
base Empty1
command user_def_cmd0 Empty2 -> Empty2
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False
enum QEnumTwo ['value1', 'value2']
prefix QENUM_TWO
object UserDefOne
@ -143,29 +143,31 @@ object UserDefNativeListUnion
case sizes: q_obj_sizeList-wrapper
case any: q_obj_anyList-wrapper
command user_def_cmd None -> None
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False
object q_obj_user_def_cmd1-arg
member ud1a: UserDefOne optional=False
command user_def_cmd1 q_obj_user_def_cmd1-arg -> None
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False
object q_obj_user_def_cmd2-arg
member ud1a: UserDefOne optional=False
member ud1b: UserDefOne optional=True
command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False
object q_obj_guest-get-time-arg
member a: int optional=False
member b: int optional=True
command guest-get-time q_obj_guest-get-time-arg -> int
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False
object q_obj_guest-sync-arg
member arg: any optional=False
command guest-sync q_obj_guest-sync-arg -> any
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False
command boxed-struct UserDefZero -> None
gen=True success_response=True boxed=True
gen=True success_response=True boxed=True oob=False
command boxed-union UserDefNativeListUnion -> None
gen=True success_response=True boxed=True
gen=True success_response=True boxed=True oob=False
command an-oob-command None -> None
gen=True success_response=True boxed=False oob=True
object UserDefOptions
member i64: intList optional=True
member u64: uint64List optional=True
@ -229,4 +231,4 @@ object q_obj___org.qemu_x-command-arg
member c: __org.qemu_x-Union2 optional=False
member d: __org.qemu_x-Alt optional=False
command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Union1
gen=True success_response=True boxed=False
gen=True success_response=True boxed=False oob=False

View File

@ -45,8 +45,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor):
gen, success_response, boxed, allow_oob):
print('command %s %s -> %s' % \
(name, arg_type and arg_type.name, ret_type and ret_type.name))
print(' gen=%s success_response=%s boxed=%s' % \
(gen, success_response, boxed))
print(' gen=%s success_response=%s boxed=%s oob=%s' % \
(gen, success_response, boxed, allow_oob))
def visit_event(self, name, info, arg_type, boxed):
print('event %s %s' % (name, arg_type and arg_type.name))

View File

@ -81,7 +81,7 @@ static void test_qmp_protocol(void)
QList *capabilities;
QTestState *qts;
qts = qtest_init_without_qmp_handshake(common_args);
qts = qtest_init_without_qmp_handshake(false, common_args);
/* Test greeting */
resp = qtest_qmp_receive(qts);
@ -90,6 +90,7 @@ static void test_qmp_protocol(void)
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 = qtest_qmp(qts, "{ 'execute': 'query-version' }");
@ -134,6 +135,87 @@ static void test_qmp_protocol(void)
qtest_quit(qts);
}
/* Tests for Out-Of-Band support. */
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);
/* Check the greeting message. */
resp = qtest_qmp_receive(qts);
q = qdict_get_qdict(resp, "QMP");
g_assert(q);
capabilities = qdict_get_qlist(q, "capabilities");
g_assert(capabilities && !qlist_empty(capabilities));
entry = qlist_first(capabilities);
g_assert(entry);
qstr = qobject_to(QString, entry->value);
g_assert(qstr);
g_assert_cmpstr(qstring_get_str(qstr), ==, "oob");
QDECREF(resp);
/* Try a fake capability, it should fail. */
resp = qtest_qmp(qts,
"{ 'execute': 'qmp_capabilities', "
" 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }");
g_assert(qdict_haskey(resp, "error"));
QDECREF(resp);
/* Now, enable OOB in current QMP session, it should succeed. */
resp = qtest_qmp(qts,
"{ 'execute': 'qmp_capabilities', "
" 'arguments': { 'enable': [ 'oob' ] } }");
g_assert(qdict_haskey(resp, "return"));
QDECREF(resp);
/*
* 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 } }");
g_assert(qdict_haskey(resp, "error"));
QDECREF(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' }");
/* 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++;
}
QDECREF(resp);
}
qtest_quit(qts);
}
static int query_error_class(const char *cmd)
{
static struct {
@ -318,6 +400,7 @@ int main(int argc, char *argv[])
g_test_init(&argc, &argv, NULL);
qtest_add_func("qmp/protocol", test_qmp_protocol);
qtest_add_func("qmp/oob", test_qmp_oob);
qmp_schema_init(&schema);
add_query_tests(&schema);

View File

@ -16,6 +16,10 @@ void qmp_user_def_cmd(Error **errp)
{
}
void qmp_an_oob_command(Error **errp)
{
}
Empty2 *qmp_user_def_cmd0(Error **errp)
{
return g_new0(Empty2, 1);

View File

@ -1115,7 +1115,7 @@ static const SerializeOps visitors[] = {
static void add_visitor_type(const SerializeOps *ops)
{
char testname_prefix[128];
char testname_prefix[32];
char testname[128];
TestArgs *args;
int i = 0;

5
vl.c
View File

@ -2404,6 +2404,11 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp)
if (qemu_opt_get_bool(opts, "pretty", 0))
flags |= MONITOR_USE_PRETTY;
/* OOB is off by default */
if (qemu_opt_get_bool(opts, "x-oob", 0)) {
flags |= MONITOR_USE_OOB;
}
chardev = qemu_opt_get(opts, "chardev");
chr = qemu_chr_find(chardev);
if (chr == NULL) {