Monitor patches

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABAgAGBQJVbWZHAAoJEDhwtADrkYZTQ6oQAIv3FRNj0buUSwh8Gwx8NrxJ
 DHz/NgdtoRV/olk2ZPYSmKUooTdKFcH1XsR7oYBoznh2m/CgiQXGeJQ7tTTcrWAj
 hw4vt4Uq0bMGPNFYNOBgsVC5cejiZo53YfXybRBLAcUJvOlbnPVJ+g7ru8mg3qaC
 rX0ZI8Cu0QmQWsGXHuKArVRGSsc+CN4SbXWyI4WmMH3g9OzTVjKVDkZekcU3NbXj
 GZq1mJBeVUsDT6wzRLGXe3qxkDAhK9THLoRd999/aQDWSpvUabNtotyZew5JG5Ey
 WKkmhSZsi0RgJTuPUcf5i83QKrWVN8EaADn67J5GkrnYYVPLdWuk/PhRgFiiWlVk
 mHFxEVyfQL7neTbb8dcwcREHIaSSb4BBF4+dyFk5921Idxs3kzw/5l20SwNi6bQy
 y23Tq/tBvbsie89O1gdXipZm3pz9xM9/9FtODjGnnVO9zb9Fi9TAyMMi8RmiS15k
 YL649rCgk9cwKOlwsMLSUajXGq0G1cOzou7M5DeDOLw5db5YEMMZkcpmETKNLaXk
 DWiX1E1K6PsDHDQkAipi9lmP9izVmpfXS4EWNVLaVCP3ht27VHgsZx4TaAAr8AVD
 NdA4wq/GEcUMcJI+erJe3YxZgPofO+Ja8whwyqwhvLM4g6g8gQgy7PNKM+FodXY1
 Z5+Ou90aprDpWjPIUHOv
 =ySet
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2015-06-02' into staging

Monitor patches

# gpg: Signature made Tue Jun  2 09:16:07 2015 BST using RSA key ID EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"

* remotes/armbru/tags/pull-monitor-2015-06-02: (21 commits)
  monitor: Change return type of monitor_cur_is_qmp() to bool
  monitor: Rename monitor_ctrl_mode() to monitor_is_qmp()
  monitor: Turn int command_mode into bool in_command_mode
  monitor: Drop do_qmp_capabilities()'s superfluous QMP check
  monitor: Unbox Monitor member mc and rename to qmp
  monitor: Rename monitor_control_read(), monitor_control_event()
  monitor: Rename handle_user_command() to handle_hmp_command()
  monitor: Limit QError use to command handlers
  monitor: Inline monitor_has_error() into its only caller
  monitor: Wean monitor_protocol_emitter() off mon->error
  monitor: Propagate errors through invalid_qmp_mode()
  monitor: Propagate errors through qmp_check_input_obj()
  monitor: Propagate errors through qmp_check_client_args()
  monitor: Drop unused "new" HMP command interface
  monitor: Use trad. command interface for HMP pcie_aer_inject_error
  monitor: Use traditional command interface for HMP device_add
  monitor: Use traditional command interface for HMP drive_del
  monitor: Convert client_migrate_info to QAPI
  monitor: Improve and document client_migrate_info protocol error
  monitor: Clean up after previous commit
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2015-06-02 18:23:28 +01:00
commit a67bfbb9e4
13 changed files with 231 additions and 325 deletions

View File

@ -2113,7 +2113,7 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name,
aio_context_release(aio_context);
}
int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
void hmp_drive_del(Monitor *mon, const QDict *qdict)
{
const char *id = qdict_get_str(qdict, "id");
BlockBackend *blk;
@ -2124,14 +2124,14 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
blk = blk_by_name(id);
if (!blk) {
error_report("Device '%s' not found", id);
return -1;
return;
}
bs = blk_bs(blk);
if (!blk_legacy_dinfo(blk)) {
error_report("Deleting device added with blockdev-add"
" is not supported");
return -1;
return;
}
aio_context = bdrv_get_aio_context(bs);
@ -2140,7 +2140,7 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) {
error_report_err(local_err);
aio_context_release(aio_context);
return -1;
return;
}
/* quiesce block driver; prevent further io */
@ -2163,7 +2163,6 @@ int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data)
}
aio_context_release(aio_context);
return 0;
}
void qmp_block_resize(bool has_device, const char *device,

View File

@ -178,8 +178,7 @@ ETEXI
.args_type = "id:B",
.params = "device",
.help = "remove host block device",
.user_print = monitor_user_noop,
.mhandler.cmd_new = hmp_drive_del,
.mhandler.cmd = hmp_drive_del,
},
STEXI
@ -654,8 +653,7 @@ ETEXI
.args_type = "device:O",
.params = "driver[,prop=value][,...]",
.help = "add device, like -device on the command line",
.user_print = monitor_user_noop,
.mhandler.cmd_new = do_device_add,
.mhandler.cmd = hmp_device_add,
.command_completion = device_add_completion,
},
@ -1011,17 +1009,16 @@ ETEXI
.name = "client_migrate_info",
.args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
.params = "protocol hostname port tls-port cert-subject",
.help = "send migration info to spice/vnc client",
.user_print = monitor_user_noop,
.mhandler.cmd_new = client_migrate_info,
.help = "set migration information for remote display",
.mhandler.cmd = hmp_client_migrate_info,
},
STEXI
@item client_migrate_info @var{protocol} @var{hostname} @var{port} @var{tls-port} @var{cert-subject}
@findex client_migrate_info
Set the spice/vnc connection info for the migration target. The spice/vnc
server will ask the spice/vnc client to automatically reconnect using the
new parameters (if specified) once the vm migration finished successfully.
Set migration information for remote display. This makes the server
ask the client to automatically reconnect using the new parameters
once migration finished successfully. Only implemented for SPICE.
ETEXI
{
@ -1186,8 +1183,7 @@ ETEXI
"<error_status> = error string or 32bit\n\t\t\t"
"<tlb header> = 32bit x 4\n\t\t\t"
"<tlb header prefix> = 32bit x 4",
.user_print = pcie_aer_inject_error_print,
.mhandler.cmd_new = hmp_pcie_aer_inject_error,
.mhandler.cmd = hmp_pcie_aer_inject_error,
},
STEXI

23
hmp.c
View File

@ -22,6 +22,7 @@
#include "qmp-commands.h"
#include "qemu/sockets.h"
#include "monitor/monitor.h"
#include "monitor/qdev.h"
#include "qapi/opts-visitor.h"
#include "qapi/string-output-visitor.h"
#include "qapi-visit.h"
@ -1250,6 +1251,23 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict)
}
}
void hmp_client_migrate_info(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
const char *protocol = qdict_get_str(qdict, "protocol");
const char *hostname = qdict_get_str(qdict, "hostname");
bool has_port = qdict_haskey(qdict, "port");
int port = qdict_get_try_int(qdict, "port", -1);
bool has_tls_port = qdict_haskey(qdict, "tls-port");
int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
const char *cert_subject = qdict_get_try_str(qdict, "cert-subject");
qmp_client_migrate_info(protocol, hostname,
has_port, port, has_tls_port, tls_port,
!!cert_subject, cert_subject, &err);
hmp_handle_error(mon, &err);
}
void hmp_set_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
@ -1482,6 +1500,11 @@ void hmp_migrate(Monitor *mon, const QDict *qdict)
}
}
void hmp_device_add(Monitor *mon, const QDict *qdict)
{
do_device_add(mon, qdict, NULL);
}
void hmp_device_del(Monitor *mon, const QDict *qdict)
{
const char *id = qdict_get_str(qdict, "id");

2
hmp.h
View File

@ -67,6 +67,7 @@ void hmp_migrate_set_speed(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_capability(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict);
void hmp_migrate_set_cache_size(Monitor *mon, const QDict *qdict);
void hmp_client_migrate_info(Monitor *mon, const QDict *qdict);
void hmp_set_password(Monitor *mon, const QDict *qdict);
void hmp_expire_password(Monitor *mon, const QDict *qdict);
void hmp_eject(Monitor *mon, const QDict *qdict);
@ -79,6 +80,7 @@ void hmp_block_job_pause(Monitor *mon, const QDict *qdict);
void hmp_block_job_resume(Monitor *mon, const QDict *qdict);
void hmp_block_job_complete(Monitor *mon, const QDict *qdict);
void hmp_migrate(Monitor *mon, const QDict *qdict);
void hmp_device_add(Monitor *mon, const QDict *qdict);
void hmp_device_del(Monitor *mon, const QDict *qdict);
void hmp_dump_guest_memory(Monitor *mon, const QDict *qdict);
void hmp_netdev_add(Monitor *mon, const QDict *qdict);

View File

@ -29,19 +29,7 @@ PciInfoList *qmp_query_pci(Error **errp)
return NULL;
}
static void pci_error_message(Monitor *mon)
void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict)
{
monitor_printf(mon, "PCI devices not supported\n");
}
int hmp_pcie_aer_inject_error(Monitor *mon,
const QDict *qdict, QObject **ret_data)
{
pci_error_message(mon);
return -ENOSYS;
}
void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
{
pci_error_message(mon);
}

View File

@ -815,21 +815,6 @@ const VMStateDescription vmstate_pcie_aer_log = {
}
};
void pcie_aer_inject_error_print(Monitor *mon, const QObject *data)
{
QDict *qdict;
int devfn;
assert(qobject_type(data) == QTYPE_QDICT);
qdict = qobject_to_qdict(data);
devfn = (int)qdict_get_int(qdict, "devfn");
monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n",
qdict_get_str(qdict, "id"),
qdict_get_str(qdict, "root_bus"),
(int) qdict_get_int(qdict, "bus"),
PCI_SLOT(devfn), PCI_FUNC(devfn));
}
typedef struct PCIEAERErrorName {
const char *name;
uint32_t val;
@ -962,8 +947,8 @@ static int pcie_aer_parse_error_string(const char *error_name,
return -EINVAL;
}
int hmp_pcie_aer_inject_error(Monitor *mon,
const QDict *qdict, QObject **ret_data)
static int do_pcie_aer_inject_error(Monitor *mon,
const QDict *qdict, QObject **ret_data)
{
const char *id = qdict_get_str(qdict, "id");
const char *error_name;
@ -1035,3 +1020,23 @@ int hmp_pcie_aer_inject_error(Monitor *mon,
return 0;
}
void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict)
{
QObject *data;
int devfn;
if (do_pcie_aer_inject_error(mon, qdict, &data) < 0) {
return;
}
assert(qobject_type(data) == QTYPE_QDICT);
qdict = qobject_to_qdict(data);
devfn = (int)qdict_get_int(qdict, "devfn");
monitor_printf(mon, "OK id: %s root bus: %s, bus: %x devfn: %x.%x\n",
qdict_get_str(qdict, "id"),
qdict_get_str(qdict, "root_bus"),
(int) qdict_get_int(qdict, "bus"),
PCI_SLOT(devfn), PCI_FUNC(devfn));
}

View File

@ -16,10 +16,7 @@ extern Monitor *default_mon;
#define MONITOR_USE_CONTROL 0x04
#define MONITOR_USE_PRETTY 0x08
/* flags for monitor commands */
#define MONITOR_CMD_ASYNC 0x0001
int monitor_cur_is_qmp(void);
bool monitor_cur_is_qmp(void);
void monitor_init(CharDriverState *chr, int flags);
@ -43,8 +40,6 @@ void monitor_flush(Monitor *mon);
int monitor_set_cpu(int cpu_index);
int monitor_get_cpu_index(void);
typedef void (MonitorCompletion)(void *opaque, QObject *ret_data);
void monitor_set_error(Monitor *mon, QError *qerror);
void monitor_read_command(Monitor *mon, int show_prompt);
int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func,

View File

@ -66,5 +66,5 @@ DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type);
void qmp_change_blockdev(const char *device, const char *filename,
const char *format, Error **errp);
void hmp_commit(Monitor *mon, const QDict *qdict);
int hmp_drive_del(Monitor *mon, const QDict *qdict, QObject **ret_data);
void hmp_drive_del(Monitor *mon, const QDict *qdict);
#endif

View File

@ -161,9 +161,7 @@ extern unsigned int nb_prom_envs;
void hmp_drive_add(Monitor *mon, const QDict *qdict);
/* pcie aer error injection */
void pcie_aer_inject_error_print(Monitor *mon, const QObject *data);
int hmp_pcie_aer_inject_error(Monitor *mon,
const QDict *qdict, QObject **ret_data);
void hmp_pcie_aer_inject_error(Monitor *mon, const QDict *qdict);
/* serial ports */

397
monitor.c
View File

@ -118,25 +118,15 @@
*
*/
typedef struct MonitorCompletionData MonitorCompletionData;
struct MonitorCompletionData {
Monitor *mon;
void (*user_print)(Monitor *mon, const QObject *data);
};
typedef struct mon_cmd_t {
const char *name;
const char *args_type;
const char *params;
const char *help;
void (*user_print)(Monitor *mon, const QObject *data);
union {
void (*cmd)(Monitor *mon, const QDict *qdict);
int (*cmd_new)(Monitor *mon, const QDict *params, QObject **ret_data);
int (*cmd_async)(Monitor *mon, const QDict *params,
MonitorCompletion *cb, void *opaque);
} mhandler;
int flags;
/* @sub_table is a list of 2nd level of commands. If it do not exist,
* mhandler should be used. If it exist, sub_table[?].mhandler should be
* used, and mhandler of 1st level plays the role of help function.
@ -171,11 +161,16 @@ struct MonFdset {
QLIST_ENTRY(MonFdset) next;
};
typedef struct MonitorControl {
typedef struct {
QObject *id;
JSONMessageParser parser;
int command_mode;
} MonitorControl;
/*
* When a client connects, we're in capabilities negotiation mode.
* When command qmp_capabilities succeeds, we go into command
* mode.
*/
bool in_command_mode; /* are we in command mode? */
} MonitorQMP;
/*
* To prevent flooding clients, events can be throttled. The
@ -205,7 +200,7 @@ struct Monitor {
int mux_out;
ReadLineState *rs;
MonitorControl *mc;
MonitorQMP qmp;
CPUState *mon_cpu;
BlockCompletionFunc *password_completion_cb;
void *password_opaque;
@ -236,21 +231,20 @@ Monitor *default_mon;
static void monitor_command_cb(void *opaque, const char *cmdline,
void *readline_opaque);
static inline int qmp_cmd_mode(const Monitor *mon)
{
return (mon->mc ? mon->mc->command_mode : 0);
}
/* Return true if in control mode, false otherwise */
static inline int monitor_ctrl_mode(const Monitor *mon)
/**
* Is @mon a QMP monitor?
*/
static inline bool monitor_is_qmp(const Monitor *mon)
{
return (mon->flags & MONITOR_USE_CONTROL);
}
/* Return non-zero iff we have a current monitor, and it is in QMP mode. */
int monitor_cur_is_qmp(void)
/**
* Is the current monitor, if any, a QMP monitor?
*/
bool monitor_cur_is_qmp(void)
{
return cur_mon && monitor_ctrl_mode(cur_mon);
return cur_mon && monitor_is_qmp(cur_mon);
}
void monitor_read_command(Monitor *mon, int show_prompt)
@ -360,7 +354,7 @@ void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap)
if (!mon)
return;
if (monitor_ctrl_mode(mon)) {
if (monitor_is_qmp(mon)) {
return;
}
@ -387,23 +381,6 @@ static int GCC_FMT_ATTR(2, 3) monitor_fprintf(FILE *stream,
return 0;
}
static void monitor_user_noop(Monitor *mon, const QObject *data) { }
static inline int handler_is_qobject(const mon_cmd_t *cmd)
{
return cmd->user_print != NULL;
}
static inline bool handler_is_async(const mon_cmd_t *cmd)
{
return cmd->flags & MONITOR_CMD_ASYNC;
}
static inline int monitor_has_error(const Monitor *mon)
{
return mon->error != NULL;
}
static void monitor_json_emitter(Monitor *mon, const QObject *data)
{
QString *json;
@ -418,24 +395,25 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data)
QDECREF(json);
}
static QDict *build_qmp_error_dict(const QError *err)
static QDict *build_qmp_error_dict(Error *err)
{
QObject *obj;
obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %p } }",
ErrorClass_lookup[err->err_class],
qerror_human(err));
obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %s } }",
ErrorClass_lookup[error_get_class(err)],
error_get_pretty(err));
return qobject_to_qdict(obj);
}
static void monitor_protocol_emitter(Monitor *mon, QObject *data)
static void monitor_protocol_emitter(Monitor *mon, QObject *data,
Error *err)
{
QDict *qmp;
trace_monitor_protocol_emitter(mon);
if (!monitor_has_error(mon)) {
if (!err) {
/* success response */
qmp = qdict_new();
if (data) {
@ -447,14 +425,12 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data)
}
} else {
/* error response */
qmp = build_qmp_error_dict(mon->error);
QDECREF(mon->error);
mon->error = NULL;
qmp = build_qmp_error_dict(err);
}
if (mon->mc->id) {
qdict_put_obj(qmp, "id", mon->mc->id);
mon->mc->id = NULL;
if (mon->qmp.id) {
qdict_put_obj(qmp, "id", mon->qmp.id);
mon->qmp.id = NULL;
}
monitor_json_emitter(mon, QOBJECT(qmp));
@ -474,7 +450,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QObject *data)
trace_monitor_protocol_event_emit(event, data);
QLIST_FOREACH(mon, &mon_list, entry) {
if (monitor_ctrl_mode(mon) && qmp_cmd_mode(mon)) {
if (monitor_is_qmp(mon) && mon->qmp.in_command_mode) {
monitor_json_emitter(mon, data);
}
}
@ -594,15 +570,11 @@ static void monitor_qapi_event_init(void)
static int do_qmp_capabilities(Monitor *mon, const QDict *params,
QObject **ret_data)
{
/* Will setup QMP capabilities in the future */
if (monitor_ctrl_mode(mon)) {
mon->mc->command_mode = 1;
}
mon->qmp.in_command_mode = true;
return 0;
}
static void handle_user_command(Monitor *mon, const char *cmdline);
static void handle_hmp_command(Monitor *mon, const char *cmdline);
static void monitor_data_init(Monitor *mon)
{
@ -641,7 +613,7 @@ char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index,
}
}
handle_user_command(&hmp, command_line);
handle_hmp_command(&hmp, command_line);
cur_mon = old_mon;
qemu_mutex_lock(&hmp.out_lock);
@ -917,45 +889,6 @@ static void hmp_trace_file(Monitor *mon, const QDict *qdict)
}
#endif
static void user_monitor_complete(void *opaque, QObject *ret_data)
{
MonitorCompletionData *data = (MonitorCompletionData *)opaque;
if (ret_data) {
data->user_print(data->mon, ret_data);
}
monitor_resume(data->mon);
g_free(data);
}
static void qmp_monitor_complete(void *opaque, QObject *ret_data)
{
monitor_protocol_emitter(opaque, ret_data);
}
static int qmp_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
const QDict *params)
{
return cmd->mhandler.cmd_async(mon, params, qmp_monitor_complete, mon);
}
static void user_async_cmd_handler(Monitor *mon, const mon_cmd_t *cmd,
const QDict *params)
{
int ret;
MonitorCompletionData *cb_data = g_malloc(sizeof(*cb_data));
cb_data->mon = mon;
cb_data->user_print = cmd->user_print;
monitor_suspend(mon);
ret = cmd->mhandler.cmd_async(mon, params,
user_monitor_complete, cb_data);
if (ret < 0) {
monitor_resume(mon);
g_free(cb_data);
}
}
static void hmp_info_help(Monitor *mon, const QDict *qdict)
{
help_cmd(mon, "info");
@ -1085,39 +1018,33 @@ static void hmp_info_trace_events(Monitor *mon, const QDict *qdict)
qapi_free_TraceEventInfoList(events);
}
static int client_migrate_info(Monitor *mon, const QDict *qdict,
QObject **ret_data)
void qmp_client_migrate_info(const char *protocol, const char *hostname,
bool has_port, int64_t port,
bool has_tls_port, int64_t tls_port,
bool has_cert_subject, const char *cert_subject,
Error **errp)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *hostname = qdict_get_str(qdict, "hostname");
const char *subject = qdict_get_try_str(qdict, "cert-subject");
int port = qdict_get_try_int(qdict, "port", -1);
int tls_port = qdict_get_try_int(qdict, "tls-port", -1);
Error *err = NULL;
int ret;
if (strcmp(protocol, "spice") == 0) {
if (!qemu_using_spice(&err)) {
qerror_report_err(err);
error_free(err);
return -1;
if (!qemu_using_spice(errp)) {
return;
}
if (port == -1 && tls_port == -1) {
qerror_report(QERR_MISSING_PARAMETER, "port/tls-port");
return -1;
if (!has_port && !has_tls_port) {
error_set(errp, QERR_MISSING_PARAMETER, "port/tls-port");
return;
}
ret = qemu_spice_migrate_info(hostname, port, tls_port, subject);
if (ret != 0) {
qerror_report(QERR_UNDEFINED_ERROR);
return -1;
if (qemu_spice_migrate_info(hostname,
has_port ? port : -1,
has_tls_port ? tls_port : -1,
cert_subject)) {
error_set(errp, QERR_UNDEFINED_ERROR);
return;
}
return 0;
return;
}
qerror_report(QERR_INVALID_PARAMETER, "protocol");
return -1;
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "spice");
}
static void hmp_logfile(Monitor *mon, const QDict *qdict)
@ -4098,19 +4025,7 @@ void monitor_set_error(Monitor *mon, QError *qerror)
}
}
static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret)
{
if (ret && !monitor_has_error(mon)) {
/*
* If it returns failure, it must have passed on error.
*
* Action: Report an internal error to the client if in QMP.
*/
qerror_report(QERR_UNDEFINED_ERROR);
}
}
static void handle_user_command(Monitor *mon, const char *cmdline)
static void handle_hmp_command(Monitor *mon, const char *cmdline)
{
QDict *qdict;
const mon_cmd_t *cmd;
@ -4118,26 +4033,10 @@ static void handle_user_command(Monitor *mon, const char *cmdline)
qdict = qdict_new();
cmd = monitor_parse_command(mon, cmdline, 0, mon->cmd_table, qdict);
if (!cmd)
goto out;
if (handler_is_async(cmd)) {
user_async_cmd_handler(mon, cmd, qdict);
} else if (handler_is_qobject(cmd)) {
QObject *data = NULL;
/* XXX: ignores the error code */
cmd->mhandler.cmd_new(mon, qdict, &data);
assert(!monitor_has_error(mon));
if (data) {
cmd->user_print(mon, data);
qobject_decref(data);
}
} else {
if (cmd) {
cmd->mhandler.cmd(mon, qdict);
}
out:
QDECREF(qdict);
}
@ -4803,19 +4702,21 @@ static int monitor_can_read(void *opaque)
return (mon->suspend_cnt == 0) ? 1 : 0;
}
static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd)
static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd,
Error **errp)
{
bool is_cap = cmd->mhandler.cmd_new == do_qmp_capabilities;
if (is_cap && qmp_cmd_mode(mon)) {
qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND,
"Capabilities negotiation is already complete, command "
"'%s' ignored", cmd->name);
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->name);
return true;
}
if (!is_cap && !qmp_cmd_mode(mon)) {
qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND,
"Expecting capabilities negotiation with "
"'qmp_capabilities' before command '%s'", cmd->name);
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->name);
return true;
}
return false;
@ -4831,8 +4732,9 @@ static bool invalid_qmp_mode(const Monitor *mon, const mon_cmd_t *cmd)
* the QMP_ACCEPT_UNKNOWNS flag is set, then the
* checking is skipped for it.
*/
static int check_client_args_type(const QDict *client_args,
const QDict *cmd_args, int flags)
static void check_client_args_type(const QDict *client_args,
const QDict *cmd_args, int flags,
Error **errp)
{
const QDictEntry *ent;
@ -4849,8 +4751,8 @@ static int check_client_args_type(const QDict *client_args,
continue;
}
/* client arg doesn't exist */
qerror_report(QERR_INVALID_PARAMETER, client_arg_name);
return -1;
error_set(errp, QERR_INVALID_PARAMETER, client_arg_name);
return;
}
arg_type = qobject_to_qstring(obj);
@ -4862,9 +4764,9 @@ static int check_client_args_type(const QDict *client_args,
case 'B':
case 's':
if (qobject_type(client_arg) != QTYPE_QSTRING) {
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
"string");
return -1;
error_set(errp, QERR_INVALID_PARAMETER_TYPE,
client_arg_name, "string");
return;
}
break;
case 'i':
@ -4872,25 +4774,25 @@ static int check_client_args_type(const QDict *client_args,
case 'M':
case 'o':
if (qobject_type(client_arg) != QTYPE_QINT) {
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
"int");
return -1;
error_set(errp, QERR_INVALID_PARAMETER_TYPE,
client_arg_name, "int");
return;
}
break;
case 'T':
if (qobject_type(client_arg) != QTYPE_QINT &&
qobject_type(client_arg) != QTYPE_QFLOAT) {
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
"number");
return -1;
error_set(errp, QERR_INVALID_PARAMETER_TYPE,
client_arg_name, "number");
return;
}
break;
case 'b':
case '-':
if (qobject_type(client_arg) != QTYPE_QBOOL) {
qerror_report(QERR_INVALID_PARAMETER_TYPE, client_arg_name,
"bool");
return -1;
error_set(errp, QERR_INVALID_PARAMETER_TYPE,
client_arg_name, "bool");
return;
}
break;
case 'O':
@ -4909,16 +4811,15 @@ static int check_client_args_type(const QDict *client_args,
abort();
}
}
return 0;
}
/*
* - Check if the client has passed all mandatory args
* - Set special flags for argument validation
*/
static int check_mandatory_args(const QDict *cmd_args,
const QDict *client_args, int *flags)
static void check_mandatory_args(const QDict *cmd_args,
const QDict *client_args, int *flags,
Error **errp)
{
const QDictEntry *ent;
@ -4933,12 +4834,10 @@ static int check_mandatory_args(const QDict *cmd_args,
} else if (qstring_get_str(type)[0] != '-' &&
qstring_get_str(type)[1] != '?' &&
!qdict_haskey(client_args, cmd_arg_name)) {
qerror_report(QERR_MISSING_PARAMETER, cmd_arg_name);
return -1;
error_set(errp, QERR_MISSING_PARAMETER, cmd_arg_name);
return;
}
}
return 0;
}
static QDict *qdict_from_args_type(const char *args_type)
@ -4994,24 +4893,26 @@ out:
* 3. Each argument provided by the client must have the type expected
* by the command
*/
static int qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args)
static void qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args,
Error **errp)
{
int flags, err;
Error *err = NULL;
int flags;
QDict *cmd_args;
cmd_args = qdict_from_args_type(cmd->args_type);
flags = 0;
err = check_mandatory_args(cmd_args, client_args, &flags);
check_mandatory_args(cmd_args, client_args, &flags, &err);
if (err) {
goto out;
}
err = check_client_args_type(client_args, cmd_args, flags);
check_client_args_type(client_args, cmd_args, flags, &err);
out:
error_propagate(errp, err);
QDECREF(cmd_args);
return err;
}
/*
@ -5024,14 +4925,14 @@ out:
* 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)
static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp)
{
const QDictEntry *ent;
int has_exec_key = 0;
QDict *input_dict;
if (qobject_type(input_obj) != QTYPE_QDICT) {
qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "object");
error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "object");
return NULL;
}
@ -5043,81 +4944,68 @@ static QDict *qmp_check_input_obj(QObject *input_obj)
if (!strcmp(arg_name, "execute")) {
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute",
"string");
error_set(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) {
qerror_report(QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "arguments",
"object");
error_set(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER,
"arguments", "object");
return NULL;
}
} else if (!strcmp(arg_name, "id")) {
/* FIXME: check duplicated IDs for async commands */
} else {
qerror_report(QERR_QMP_EXTRA_MEMBER, arg_name);
error_set(errp, QERR_QMP_EXTRA_MEMBER, arg_name);
return NULL;
}
}
if (!has_exec_key) {
qerror_report(QERR_QMP_BAD_INPUT_OBJECT, "execute");
error_set(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute");
return NULL;
}
return input_dict;
}
static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd,
const QDict *params)
{
int ret;
QObject *data = NULL;
ret = cmd->mhandler.cmd_new(mon, params, &data);
handler_audit(mon, cmd, ret);
monitor_protocol_emitter(mon, data);
qobject_decref(data);
}
static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
{
int err;
QObject *obj;
Error *local_err = NULL;
QObject *obj, *data;
QDict *input, *args;
const mon_cmd_t *cmd;
const char *cmd_name;
Monitor *mon = cur_mon;
args = input = NULL;
data = NULL;
obj = json_parser_parse(tokens, NULL);
if (!obj) {
// FIXME: should be triggered in json_parser_parse()
qerror_report(QERR_JSON_PARSING);
error_set(&local_err, QERR_JSON_PARSING);
goto err_out;
}
input = qmp_check_input_obj(obj);
input = qmp_check_input_obj(obj, &local_err);
if (!input) {
qobject_decref(obj);
goto err_out;
}
mon->mc->id = qdict_get(input, "id");
qobject_incref(mon->mc->id);
mon->qmp.id = qdict_get(input, "id");
qobject_incref(mon->qmp.id);
cmd_name = qdict_get_str(input, "execute");
trace_handle_qmp_command(mon, cmd_name);
cmd = qmp_find_cmd(cmd_name);
if (!cmd) {
qerror_report(ERROR_CLASS_COMMAND_NOT_FOUND,
"The command %s has not been found", cmd_name);
error_set(&local_err, ERROR_CLASS_COMMAND_NOT_FOUND,
"The command %s has not been found", cmd_name);
goto err_out;
}
if (invalid_qmp_mode(mon, cmd)) {
if (invalid_qmp_mode(mon, cmd, &local_err)) {
goto err_out;
}
@ -5129,40 +5017,39 @@ static void handle_qmp_command(JSONMessageParser *parser, QList *tokens)
QINCREF(args);
}
err = qmp_check_client_args(cmd, args);
if (err < 0) {
qmp_check_client_args(cmd, args, &local_err);
if (local_err) {
goto err_out;
}
if (handler_is_async(cmd)) {
err = qmp_async_cmd_handler(mon, cmd, args);
if (err) {
/* emit the error response */
goto err_out;
if (cmd->mhandler.cmd_new(mon, args, &data)) {
/* Command failed... */
if (!mon->error) {
/* ... without setting an error, so make one up */
error_set(&local_err, QERR_UNDEFINED_ERROR);
}
} else {
qmp_call_cmd(mon, cmd, args);
}
if (mon->error) {
error_set(&local_err, mon->error->err_class, "%s",
mon->error->err_msg);
}
goto out;
err_out:
monitor_protocol_emitter(mon, NULL);
out:
monitor_protocol_emitter(mon, data, local_err);
qobject_decref(data);
QDECREF(mon->error);
mon->error = NULL;
QDECREF(input);
QDECREF(args);
}
/**
* monitor_control_read(): Read and handle QMP input
*/
static void monitor_control_read(void *opaque, const uint8_t *buf, int size)
static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size)
{
Monitor *old_mon = cur_mon;
cur_mon = opaque;
json_message_parser_feed(&cur_mon->mc->parser, (const char *) buf, size);
json_message_parser_feed(&cur_mon->qmp.parser, (const char *) buf, size);
cur_mon = old_mon;
}
@ -5181,7 +5068,7 @@ static void monitor_read(void *opaque, const uint8_t *buf, int size)
if (size == 0 || buf[size - 1] != 0)
monitor_printf(cur_mon, "corrupted command\n");
else
handle_user_command(cur_mon, (char *)buf);
handle_hmp_command(cur_mon, (char *)buf);
}
cur_mon = old_mon;
@ -5193,7 +5080,7 @@ static void monitor_command_cb(void *opaque, const char *cmdline,
Monitor *mon = opaque;
monitor_suspend(mon);
handle_user_command(mon, cmdline);
handle_hmp_command(mon, cmdline);
monitor_resume(mon);
}
@ -5221,25 +5108,22 @@ static QObject *get_qmp_greeting(void)
return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver);
}
/**
* monitor_control_event(): Print QMP gretting
*/
static void monitor_control_event(void *opaque, int event)
static void monitor_qmp_event(void *opaque, int event)
{
QObject *data;
Monitor *mon = opaque;
switch (event) {
case CHR_EVENT_OPENED:
mon->mc->command_mode = 0;
mon->qmp.in_command_mode = false;
data = get_qmp_greeting();
monitor_json_emitter(mon, data);
qobject_decref(data);
mon_refcount++;
break;
case CHR_EVENT_CLOSED:
json_message_parser_destroy(&mon->mc->parser);
json_message_parser_init(&mon->mc->parser, handle_qmp_command);
json_message_parser_destroy(&mon->qmp.parser);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
mon_refcount--;
monitor_fdsets_cleanup();
break;
@ -5371,14 +5255,11 @@ void monitor_init(CharDriverState *chr, int flags)
monitor_read_command(mon, 0);
}
if (monitor_ctrl_mode(mon)) {
mon->mc = g_malloc0(sizeof(MonitorControl));
/* Control mode requires special handlers */
qemu_chr_add_handlers(chr, monitor_can_read, monitor_control_read,
monitor_control_event, mon);
if (monitor_is_qmp(mon)) {
qemu_chr_add_handlers(chr, monitor_can_read, monitor_qmp_read,
monitor_qmp_event, mon);
qemu_chr_fe_set_echo(chr, true);
json_message_parser_init(&mon->mc->parser, handle_qmp_command);
json_message_parser_init(&mon->qmp.parser, handle_qmp_command);
} else {
qemu_chr_add_handlers(chr, monitor_can_read, monitor_read,
monitor_event, mon);

View File

@ -637,6 +637,25 @@
{ 'command': 'query-migrate-parameters',
'returns': 'MigrationParameters' }
##
# @client_migrate_info
#
# Set migration information for remote display. This makes the server
# ask the client to automatically reconnect using the new parameters
# once migration finished successfully. Only implemented for SPICE.
#
# @protocol: must be "spice"
# @hostname: migration target hostname
# @port: #optional spice tcp port for plaintext channels
# @tls-port: #optional spice tcp port for tls-secured channels
# @cert-subject: #optional server certificate subject
#
# Since: 0.14.0
##
{ 'command': 'client_migrate_info',
'data': { 'protocol': 'str', 'hostname': 'str', '*port': 'int',
'*tls-port': 'int', '*cert-subject': 'str' } }
##
# @MouseInfo:
#

View File

@ -784,23 +784,23 @@ EQMP
.name = "client_migrate_info",
.args_type = "protocol:s,hostname:s,port:i?,tls-port:i?,cert-subject:s?",
.params = "protocol hostname port tls-port cert-subject",
.help = "send migration info to spice/vnc client",
.mhandler.cmd_new = client_migrate_info,
.help = "set migration information for remote display",
.mhandler.cmd_new = qmp_marshal_input_client_migrate_info,
},
SQMP
client_migrate_info
------------------
-------------------
Set the spice/vnc connection info for the migration target. The spice/vnc
server will ask the spice/vnc client to automatically reconnect using the
new parameters (if specified) once the vm migration finished successfully.
Set migration information for remote display. This makes the server
ask the client to automatically reconnect using the new parameters
once migration finished successfully. Only implemented for SPICE.
Arguments:
- "protocol": protocol: "spice" or "vnc" (json-string)
- "protocol": must be "spice" (json-string)
- "hostname": migration target hostname (json-string)
- "port": spice/vnc tcp port for plaintext channels (json-int, optional)
- "port": spice tcp port for plaintext channels (json-int, optional)
- "tls-port": spice tcp port for tls-secured channels (json-int, optional)
- "cert-subject": server certificate subject (json-string, optional)

View File

@ -1,7 +1,7 @@
#include "qemu-common.h"
#include "monitor/monitor.h"
int monitor_cur_is_qmp(void)
bool monitor_cur_is_qmp(void)
{
return 0;
return false;
}