X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=monitor.c;h=b07f254698454a8bee263d35c3bfa5feac124ae9;hb=6b39b06339ee59559b31f860d4af635b046322df;hp=a5d054b03960aac235e930b35aeaf3d6b0858212;hpb=d4633541ee0ec266ef4e55e1d71a98a18762d80c;p=mirror_qemu.git diff --git a/monitor.c b/monitor.c index a5d054b039..b07f254698 100644 --- a/monitor.c +++ b/monitor.c @@ -54,15 +54,11 @@ #include "qemu/acl.h" #include "sysemu/tpm.h" #include "qapi/qmp/qerror.h" -#include "qapi/qmp/qint.h" -#include "qapi/qmp/qfloat.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qstring.h" +#include "qapi/qmp/types.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" -#include +#include "qom/object_interfaces.h" #include "cpu.h" #include "trace.h" #include "trace/control.h" @@ -83,12 +79,7 @@ #include "sysemu/block-backend.h" #include "sysemu/qtest.h" #include "qemu/cutils.h" - -/* for hmp_info_irq/pic */ -#if defined(TARGET_SPARC) -#include "hw/sparc/sun4m.h" -#endif -#include "hw/lm32/lm32_pic.h" +#include "qapi/qmp/dispatch.h" #if defined(TARGET_S390X) #include "hw/s390x/storage-keys.h" @@ -133,13 +124,10 @@ typedef struct mon_cmd_t { const char *args_type; const char *params; const char *help; - union { - void (*cmd)(Monitor *mon, const QDict *qdict); - void (*cmd_new)(QDict *params, QObject **ret_data, Error **errp); - } mhandler; - /* @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. + void (*cmd)(Monitor *mon, const QDict *qdict); + /* @sub_table is a list of 2nd level of commands. If it does not exist, + * cmd should be used. If it exists, sub_table[?].cmd should be + * used, and cmd of 1st level plays the role of help function. */ struct mon_cmd_t *sub_table; void (*command_completion)(ReadLineState *rs, int nb_args, const char *str); @@ -172,7 +160,6 @@ struct MonFdset { }; typedef struct { - QObject *id; JSONMessageParser parser; /* * When a client connects, we're in capabilities negotiation mode. @@ -235,8 +222,6 @@ static int mon_refcount; static mon_cmd_t mon_cmds[]; static mon_cmd_t info_cmds[]; -static const mon_cmd_t qmp_cmds[]; - Monitor *cur_mon; static QEMUClockType event_clock_type = QEMU_CLOCK_REALTIME; @@ -407,49 +392,6 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data) QDECREF(json); } -static QDict *build_qmp_error_dict(Error *err) -{ - QObject *obj; - - obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %s } }", - QapiErrorClass_lookup[error_get_class(err)], - error_get_pretty(err)); - - return qobject_to_qdict(obj); -} - -static void monitor_protocol_emitter(Monitor *mon, QObject *data, - Error *err) -{ - QDict *qmp; - - trace_monitor_protocol_emitter(mon); - - if (!err) { - /* success response */ - qmp = qdict_new(); - if (data) { - qobject_incref(data); - qdict_put_obj(qmp, "return", data); - } else { - /* return an empty QDict by default */ - qdict_put(qmp, "return", qdict_new()); - } - } else { - /* error response */ - qmp = build_qmp_error_dict(err); - } - - if (mon->qmp.id) { - qdict_put_obj(qmp, "id", mon->qmp.id); - mon->qmp.id = NULL; - } - - monitor_json_emitter(mon, QOBJECT(qmp)); - QDECREF(qmp); -} - - static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { /* Limit guest-triggerable events to 1 per second */ [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS }, @@ -621,7 +563,7 @@ static void monitor_qapi_event_init(void) qmp_event_set_func_emit(monitor_qapi_event_queue); } -static void qmp_capabilities(QDict *params, QObject **ret_data, Error **errp) +void qmp_qmp_capabilities(Error **errp) { cur_mon->qmp.in_command_mode = true; } @@ -639,6 +581,13 @@ static void monitor_data_init(Monitor *mon) static void monitor_data_destroy(Monitor *mon) { + if (mon->chr) { + qemu_chr_add_handlers(mon->chr, NULL, NULL, NULL, NULL); + } + if (monitor_is_qmp(mon)) { + json_message_parser_destroy(&mon->qmp.parser); + } + g_free(mon->rs); QDECREF(mon->outbuf); qemu_mutex_destroy(&mon->out_lock); } @@ -908,9 +857,16 @@ static void hmp_trace_event(Monitor *mon, const QDict *qdict) { const char *tp_name = qdict_get_str(qdict, "name"); bool new_state = qdict_get_bool(qdict, "option"); + bool has_vcpu = qdict_haskey(qdict, "vcpu"); + int vcpu = qdict_get_try_int(qdict, "vcpu", 0); Error *local_err = NULL; - qmp_trace_event_set_state(tp_name, new_state, true, true, &local_err); + if (vcpu < 0) { + monitor_printf(mon, "argument vcpu must be positive"); + return; + } + + qmp_trace_event_set_state(tp_name, new_state, true, true, has_vcpu, vcpu, &local_err); if (local_err) { error_report_err(local_err); } @@ -946,21 +902,28 @@ static void hmp_info_help(Monitor *mon, const QDict *qdict) help_cmd(mon, "info"); } -CommandInfoList *qmp_query_commands(Error **errp) +static void query_commands_cb(QmpCommand *cmd, void *opaque) { - CommandInfoList *info, *cmd_list = NULL; - const mon_cmd_t *cmd; + CommandInfoList *info, **list = opaque; - for (cmd = qmp_cmds; cmd->name != NULL; cmd++) { - info = g_malloc0(sizeof(*info)); - info->value = g_malloc0(sizeof(*info->value)); - info->value->name = g_strdup(cmd->name); - - info->next = cmd_list; - cmd_list = info; + if (!cmd->enabled) { + return; } - return cmd_list; + info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->name = g_strdup(cmd->name); + info->next = *list; + *list = info; +} + +CommandInfoList *qmp_query_commands(Error **errp) +{ + CommandInfoList *list = NULL; + + qmp_for_each_command(query_commands_cb, &list); + + return list; } EventInfoList *qmp_query_events(Error **errp) @@ -997,6 +960,58 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, *ret_data = qobject_from_json(qmp_schema_json); } +/* + * We used to define commands in qmp-commands.hx in addition to the + * QAPI schema. This permitted defining some of them only in certain + * configurations. query-commands has always reflected that (good, + * because it lets QMP clients figure out what's actually available), + * while query-qmp-schema never did (not so good). This function is a + * hack to keep the configuration-specific commands defined exactly as + * before, even though qmp-commands.hx is gone. + * + * FIXME Educate the QAPI schema on configuration-specific commands, + * and drop this hack. + */ +static void qmp_unregister_commands_hack(void) +{ +#ifndef CONFIG_SPICE + qmp_unregister_command("query-spice"); +#endif +#ifndef TARGET_I386 + qmp_unregister_command("rtc-reset-reinjection"); +#endif +#ifndef TARGET_S390X + qmp_unregister_command("dump-skeys"); +#endif +#ifndef TARGET_ARM + qmp_unregister_command("query-gic-capabilities"); +#endif +#if !defined(TARGET_S390X) + qmp_unregister_command("query-cpu-model-expansion"); + qmp_unregister_command("query-cpu-model-baseline"); + qmp_unregister_command("query-cpu-model-comparison"); +#endif +#if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \ + && !defined(TARGET_S390X) + qmp_unregister_command("query-cpu-definitions"); +#endif +} + +static void qmp_init_marshal(void) +{ + qmp_register_command("query-qmp-schema", qmp_query_qmp_schema, + QCO_NO_OPTIONS); + qmp_register_command("device_add", qmp_device_add, + QCO_NO_OPTIONS); + qmp_register_command("netdev_add", qmp_netdev_add, + QCO_NO_OPTIONS); + + /* call it after the rest of qapi_init() */ + register_module_init(qmp_unregister_commands_hack, MODULE_INIT_QAPI); +} + +qapi_init(qmp_init_marshal); + /* set the current CPU defined by the user */ int monitor_set_cpu(int cpu_index) { @@ -1013,7 +1028,7 @@ int monitor_set_cpu(int cpu_index) CPUState *mon_get_cpu(void) { if (!cur_mon->mon_cpu) { - monitor_set_cpu(0); + monitor_set_cpu(first_cpu->cpu_index); } cpu_synchronize_state(cur_mon->mon_cpu); return cur_mon->mon_cpu; @@ -1069,8 +1084,26 @@ static void hmp_info_cpustats(Monitor *mon, const QDict *qdict) static void hmp_info_trace_events(Monitor *mon, const QDict *qdict) { - TraceEventInfoList *events = qmp_trace_event_get_state("*", NULL); + const char *name = qdict_get_try_str(qdict, "name"); + bool has_vcpu = qdict_haskey(qdict, "vcpu"); + int vcpu = qdict_get_try_int(qdict, "vcpu", 0); + TraceEventInfoList *events; TraceEventInfoList *elem; + Error *local_err = NULL; + + if (name == NULL) { + name = "*"; + } + if (vcpu < 0) { + monitor_printf(mon, "argument vcpu must be positive"); + return; + } + + events = qmp_trace_event_get_state(name, has_vcpu, vcpu, &local_err); + if (local_err) { + error_report_err(local_err); + return; + } for (elem = events; elem != NULL; elem = elem->next) { monitor_printf(mon, "%s : state %u\n", @@ -1111,7 +1144,12 @@ void qmp_client_migrate_info(const char *protocol, const char *hostname, static void hmp_logfile(Monitor *mon, const QDict *qdict) { - qemu_set_log_filename(qdict_get_str(qdict, "filename")); + Error *err = NULL; + + qemu_set_log_filename(qdict_get_str(qdict, "filename"), &err); + if (err) { + error_report_err(err); + } } static void hmp_log(Monitor *mon, const QDict *qdict) @@ -2130,11 +2168,6 @@ static mon_cmd_t mon_cmds[] = { { NULL, NULL, }, }; -static const mon_cmd_t qmp_cmds[] = { -#include "qmp-commands-old.h" - { /* NULL */ }, -}; - /*******************************************************************/ static const char *pch; @@ -2485,11 +2518,6 @@ static const mon_cmd_t *search_dispatch_table(const mon_cmd_t *disp_table, return NULL; } -static const mon_cmd_t *qmp_find_cmd(const char *cmdname) -{ - return search_dispatch_table(qmp_cmds, cmdname); -} - /* * Parse command name from @cmdp according to command table @table. * If blank, return NULL. @@ -2921,7 +2949,7 @@ static void handle_hmp_command(Monitor *mon, const char *cmdline) return; } - cmd->mhandler.cmd(mon, qdict); + cmd->cmd(mon, qdict); QDECREF(qdict); } @@ -3054,8 +3082,8 @@ void netdev_add_completion(ReadLineState *rs, int nb_args, const char *str) } len = strlen(str); readline_set_completion_index(rs, len); - for (i = 0; NetClientOptionsKind_lookup[i]; i++) { - add_completion_option(rs, str, NetClientOptionsKind_lookup[i]); + for (i = 0; NetClientDriver_lookup[i]; i++) { + add_completion_option(rs, str, NetClientDriver_lookup[i]); } } @@ -3255,7 +3283,7 @@ void set_link_completion(ReadLineState *rs, int nb_args, const char *str) NetClientState *ncs[MAX_QUEUE_NUM]; int count, i; count = qemu_find_net_clients_except(NULL, ncs, - NET_CLIENT_OPTIONS_KIND_NONE, + NET_CLIENT_DRIVER_NONE, MAX_QUEUE_NUM); for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { const char *name = ncs[i]->name; @@ -3280,7 +3308,7 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) len = strlen(str); readline_set_completion_index(rs, len); - count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_OPTIONS_KIND_NIC, + count = qemu_find_net_clients_except(NULL, ncs, NET_CLIENT_DRIVER_NIC, MAX_QUEUE_NUM); for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { QemuOpts *opts; @@ -3295,6 +3323,23 @@ void netdev_del_completion(ReadLineState *rs, int nb_args, const char *str) } } +void info_trace_events_completion(ReadLineState *rs, int nb_args, const char *str) +{ + size_t len; + + len = strlen(str); + readline_set_completion_index(rs, len); + if (nb_args == 2) { + TraceEventID id; + for (id = 0; id < trace_event_count(); id++) { + const char *event_name = trace_event_get_name(trace_event_id(id)); + if (!strncmp(str, event_name, len)) { + readline_add_completion(rs, event_name); + } + } + } +} + void trace_event_completion(ReadLineState *rs, int nb_args, const char *str) { size_t len; @@ -3392,7 +3437,7 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str) readline_set_completion_index(rs, len); if (nb_args == 2) { count = qemu_find_net_clients_except(NULL, ncs, - NET_CLIENT_OPTIONS_KIND_NONE, + NET_CLIENT_DRIVER_NONE, MAX_QUEUE_NUM); for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { int id; @@ -3409,13 +3454,13 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str) return; } else if (nb_args == 3) { count = qemu_find_net_clients_except(NULL, ncs, - NET_CLIENT_OPTIONS_KIND_NIC, + NET_CLIENT_DRIVER_NIC, MAX_QUEUE_NUM); for (i = 0; i < MIN(count, MAX_QUEUE_NUM); i++) { int id; const char *name; - if (ncs[i]->info->type == NET_CLIENT_OPTIONS_KIND_HUBPORT || + if (ncs[i]->info->type == NET_CLIENT_DRIVER_HUBPORT || net_hub_id_for_client(ncs[i], &id)) { continue; } @@ -3603,219 +3648,26 @@ 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 char *cmd, Error **errp) { - bool is_cap = cmd->mhandler.cmd_new == qmp_capabilities; + bool is_cap = g_str_equal(cmd, "qmp_capabilities"); if (is_cap && mon->qmp.in_command_mode) { error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, "Capabilities negotiation is already complete, command " - "'%s' ignored", cmd->name); + "'%s' ignored", cmd); return true; } if (!is_cap && !mon->qmp.in_command_mode) { error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, "Expecting capabilities negotiation with " - "'qmp_capabilities' before command '%s'", cmd->name); + "'qmp_capabilities' before command '%s'", cmd); return true; } return false; } -/* - * Argument validation rules: - * - * 1. The argument must exist in cmd_args qdict - * 2. The argument type must be the expected one - * - * Special case: If the argument doesn't exist in cmd_args and - * the QMP_ACCEPT_UNKNOWNS flag is set, then the - * checking is skipped for it. - */ -static void check_client_args_type(const QDict *client_args, - const QDict *cmd_args, int flags, - Error **errp) -{ - const QDictEntry *ent; - - for (ent = qdict_first(client_args); ent;ent = qdict_next(client_args,ent)){ - QObject *obj; - QString *arg_type; - const QObject *client_arg = qdict_entry_value(ent); - const char *client_arg_name = qdict_entry_key(ent); - - obj = qdict_get(cmd_args, client_arg_name); - if (!obj) { - if (flags & QMP_ACCEPT_UNKNOWNS) { - /* handler accepts unknowns */ - continue; - } - /* client arg doesn't exist */ - error_setg(errp, QERR_INVALID_PARAMETER, client_arg_name); - return; - } - - arg_type = qobject_to_qstring(obj); - assert(arg_type != NULL); - - /* check if argument's type is correct */ - switch (qstring_get_str(arg_type)[0]) { - case 'F': - case 'B': - case 's': - if (qobject_type(client_arg) != QTYPE_QSTRING) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - client_arg_name, "string"); - return; - } - break; - case 'i': - case 'l': - case 'M': - case 'o': - if (qobject_type(client_arg) != QTYPE_QINT) { - error_setg(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) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - client_arg_name, "number"); - return; - } - break; - case 'b': - case '-': - if (qobject_type(client_arg) != QTYPE_QBOOL) { - error_setg(errp, QERR_INVALID_PARAMETER_TYPE, - client_arg_name, "bool"); - return; - } - break; - case 'O': - assert(flags & QMP_ACCEPT_UNKNOWNS); - break; - case 'q': - /* Any QObject can be passed. */ - break; - case '/': - case '.': - /* - * These types are not supported by QMP and thus are not - * handled here. Fall through. - */ - default: - abort(); - } - } -} - -/* - * - Check if the client has passed all mandatory args - * - Set special flags for argument validation - */ -static void check_mandatory_args(const QDict *cmd_args, - const QDict *client_args, int *flags, - Error **errp) -{ - const QDictEntry *ent; - - for (ent = qdict_first(cmd_args); ent; ent = qdict_next(cmd_args, ent)) { - const char *cmd_arg_name = qdict_entry_key(ent); - QString *type = qobject_to_qstring(qdict_entry_value(ent)); - assert(type != NULL); - - if (qstring_get_str(type)[0] == 'O') { - assert((*flags & QMP_ACCEPT_UNKNOWNS) == 0); - *flags |= QMP_ACCEPT_UNKNOWNS; - } else if (qstring_get_str(type)[0] != '-' && - qstring_get_str(type)[1] != '?' && - !qdict_haskey(client_args, cmd_arg_name)) { - error_setg(errp, QERR_MISSING_PARAMETER, cmd_arg_name); - return; - } - } -} - -static QDict *qdict_from_args_type(const char *args_type) -{ - int i; - QDict *qdict; - QString *key, *type, *cur_qs; - - assert(args_type != NULL); - - qdict = qdict_new(); - - if (args_type == NULL || args_type[0] == '\0') { - /* no args, empty qdict */ - goto out; - } - - key = qstring_new(); - type = qstring_new(); - - cur_qs = key; - - for (i = 0;; i++) { - switch (args_type[i]) { - case ',': - case '\0': - qdict_put(qdict, qstring_get_str(key), type); - QDECREF(key); - if (args_type[i] == '\0') { - goto out; - } - type = qstring_new(); /* qdict has ref */ - cur_qs = key = qstring_new(); - break; - case ':': - cur_qs = type; - break; - default: - qstring_append_chr(cur_qs, args_type[i]); - break; - } - } - -out: - return qdict; -} - -/* - * Client argument checking rules: - * - * 1. Client must provide all mandatory arguments - * 2. Each argument provided by the client must be expected - * 3. Each argument provided by the client must have the type expected - * by the command - */ -static void qmp_check_client_args(const mon_cmd_t *cmd, QDict *client_args, - Error **errp) -{ - Error *err = NULL; - int flags; - QDict *cmd_args; - - cmd_args = qdict_from_args_type(cmd->args_type); - - flags = 0; - check_mandatory_args(cmd_args, client_args, &flags, &err); - if (err) { - goto out; - } - - check_client_args_type(client_args, cmd_args, flags, &err); - -out: - error_propagate(errp, err); - QDECREF(cmd_args); -} - /* * Input object checking rules * @@ -3874,65 +3726,58 @@ static QDict *qmp_check_input_obj(QObject *input_obj, Error **errp) static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) { - Error *local_err = NULL; - QObject *obj, *data; - QDict *input, *args; - const mon_cmd_t *cmd; + QObject *req, *rsp = NULL, *id = NULL; + QDict *qdict = NULL; const char *cmd_name; Monitor *mon = cur_mon; + Error *err = NULL; - args = input = NULL; - data = NULL; - - obj = json_parser_parse(tokens, NULL); - if (!obj) { - // FIXME: should be triggered in json_parser_parse() - error_setg(&local_err, QERR_JSON_PARSING); + req = json_parser_parse_err(tokens, NULL, &err); + if (err || !req || qobject_type(req) != QTYPE_QDICT) { + if (!err) { + error_setg(&err, QERR_JSON_PARSING); + } goto err_out; } - input = qmp_check_input_obj(obj, &local_err); - if (!input) { - qobject_decref(obj); + qdict = qmp_check_input_obj(req, &err); + if (!qdict) { goto err_out; } - mon->qmp.id = qdict_get(input, "id"); - qobject_incref(mon->qmp.id); + id = qdict_get(qdict, "id"); + qobject_incref(id); + qdict_del(qdict, "id"); - cmd_name = qdict_get_str(input, "execute"); + cmd_name = qdict_get_str(qdict, "execute"); trace_handle_qmp_command(mon, cmd_name); - cmd = qmp_find_cmd(cmd_name); - if (!cmd) { - 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, &local_err)) { + + if (invalid_qmp_mode(mon, cmd_name, &err)) { goto err_out; } - obj = qdict_get(input, "arguments"); - if (!obj) { - args = qdict_new(); - } else { - args = qobject_to_qdict(obj); - QINCREF(args); - } + rsp = qmp_dispatch(req); - qmp_check_client_args(cmd, args, &local_err); - if (local_err) { - goto err_out; +err_out: + if (err) { + qdict = qdict_new(); + qdict_put_obj(qdict, "error", qmp_build_error_object(err)); + error_free(err); + rsp = QOBJECT(qdict); } - cmd->mhandler.cmd_new(args, &data, &local_err); + if (rsp) { + if (id) { + qdict_put_obj(qobject_to_qdict(rsp), "id", id); + id = NULL; + } -err_out: - monitor_protocol_emitter(mon, data, local_err); - qobject_decref(data); - error_free(local_err); - QDECREF(input); - QDECREF(args); + monitor_json_emitter(mon, rsp); + } + + qobject_decref(id); + qobject_decref(rsp); + qobject_decref(req); } static void monitor_qmp_read(void *opaque, const uint8_t *buf, int size) @@ -3997,7 +3842,9 @@ static QObject *get_qmp_greeting(void) QObject *ver = NULL; qmp_marshal_query_version(NULL, &ver, NULL); - return qobject_from_jsonf("{'QMP':{'version': %p,'capabilities': []}}",ver); + + return qobject_from_jsonf("{'QMP': {'version': %p, 'capabilities': []}}", + ver); } static void monitor_qmp_event(void *opaque, int event) @@ -4092,15 +3939,6 @@ static void sortcmdlist(void) qsort((void *)info_cmds, array_num, elem_size, compare_mon_cmd); } - -/* - * Local variables: - * c-indent-level: 4 - * c-basic-offset: 4 - * tab-width: 8 - * End: - */ - /* These functions just adapt the readline interface in a typesafe way. We * could cast function pointers but that discards compiler checks. */ @@ -4162,6 +4000,19 @@ void monitor_init(CharDriverState *chr, int flags) qemu_mutex_unlock(&monitor_lock); } +void monitor_cleanup(void) +{ + Monitor *mon, *next; + + qemu_mutex_lock(&monitor_lock); + QLIST_FOREACH_SAFE(mon, &mon_list, entry, next) { + QLIST_REMOVE(mon, entry); + monitor_data_destroy(mon); + g_free(mon); + } + qemu_mutex_unlock(&monitor_lock); +} + static void bdrv_password_cb(void *opaque, const char *password, void *readline_opaque) {