diff options
-rw-r--r-- | blockdev.c | 9 | ||||
-rw-r--r-- | hmp-commands.hx | 20 | ||||
-rw-r--r-- | hmp.c | 23 | ||||
-rw-r--r-- | hmp.h | 2 | ||||
-rw-r--r-- | hw/pci/pci-stub.c | 14 | ||||
-rw-r--r-- | hw/pci/pcie_aer.c | 39 | ||||
-rw-r--r-- | include/monitor/monitor.h | 7 | ||||
-rw-r--r-- | include/sysemu/blockdev.h | 2 | ||||
-rw-r--r-- | include/sysemu/sysemu.h | 4 | ||||
-rw-r--r-- | monitor.c | 397 | ||||
-rw-r--r-- | qapi-schema.json | 19 | ||||
-rw-r--r-- | qmp-commands.hx | 16 | ||||
-rw-r--r-- | stubs/mon-is-qmp.c | 4 |
13 files changed, 231 insertions, 325 deletions
diff --git a/blockdev.c b/blockdev.c index 5eaf77e599..de94a8bcb3 100644 --- a/blockdev.c +++ b/blockdev.c @@ -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, diff --git a/hmp-commands.hx b/hmp-commands.hx index e864a6ca81..3d7dfccf7c 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -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 @@ -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"); @@ -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); diff --git a/hw/pci/pci-stub.c b/hw/pci/pci-stub.c index 5e564c3a87..f8f237e823 100644 --- a/hw/pci/pci-stub.c +++ b/hw/pci/pci-stub.c @@ -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); -} diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index b48c09cd11..c8dea8ed9c 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -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)); +} diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index df67d56ec0..57f8394a94 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -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, diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 7ca59b5070..310415025c 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -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 diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 8a52934728..e10c2c5217 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -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 */ @@ -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); } - - goto out; + if (mon->error) { + error_set(&local_err, mon->error->err_class, "%s", + mon->error->err_msg); + } 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); diff --git a/qapi-schema.json b/qapi-schema.json index 0662a9b445..6e17a5c36c 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -638,6 +638,25 @@ '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: # # Information about a mouse device. diff --git a/qmp-commands.hx b/qmp-commands.hx index 14e109eb5c..867a21fab6 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -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) diff --git a/stubs/mon-is-qmp.c b/stubs/mon-is-qmp.c index 1f0a8fd98a..1ef136ab1d 100644 --- a/stubs/mon-is-qmp.c +++ b/stubs/mon-is-qmp.c @@ -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; } |