diff --git a/Makefile.objs b/Makefile.objs index e0fb69b202..309d066286 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -212,6 +212,7 @@ common-obj-$(CONFIG_SMARTCARD_NSS) += $(libcacard-y) # qapi qapi-obj-y = qapi/ +qapi-obj-y += qapi-types.o qapi-visit.o common-obj-y += qmp-marshal.o qapi-visit.o qapi-types.o common-obj-y += qmp.o hmp.o diff --git a/QMP/qmp-events.txt b/QMP/qmp-events.txt index 9ba7079589..287805825f 100644 --- a/QMP/qmp-events.txt +++ b/QMP/qmp-events.txt @@ -1,6 +1,23 @@ QEMU Monitor Protocol Events ============================ +BALLOON_CHANGE +-------------- + +Emitted when the guest changes the actual BALLOON level. This +value is equivalent to the 'actual' field return by the +'query-balloon' command + +Data: + +- "actual": actual level of the guest memory balloon in bytes (json-number) + +Example: + +{ "event": "BALLOON_CHANGE", + "data": { "actual": 944766976 }, + "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } + BLOCK_IO_ERROR -------------- @@ -26,6 +43,57 @@ Example: Note: If action is "stop", a STOP event will eventually follow the BLOCK_IO_ERROR event. +BLOCK_JOB_CANCELLED +------------------- + +Emitted when a block job has been cancelled. + +Data: + +- "type": Job type ("stream" for image streaming, json-string) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) + +Example: + +{ "event": "BLOCK_JOB_CANCELLED", + "data": { "type": "stream", "device": "virtio-disk0", + "len": 10737418240, "offset": 134217728, + "speed": 0 }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + +BLOCK_JOB_COMPLETED +------------------- + +Emitted when a block job has completed. + +Data: + +- "type": Job type ("stream" for image streaming, json-string) +- "device": Device name (json-string) +- "len": Maximum progress value (json-int) +- "offset": Current progress value (json-int) + On success this is equal to len. + On failure this is less than len. +- "speed": Rate limit, bytes per second (json-int) +- "error": Error message (json-string, optional) + Only present on failure. This field contains a human-readable + error message. There are no semantics other than that streaming + has failed and clients should not try to interpret the error + string. + +Example: + +{ "event": "BLOCK_JOB_COMPLETED", + "data": { "type": "stream", "device": "virtio-disk0", + "len": 10737418240, "offset": 10737418240, + "speed": 0 }, + "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } + DEVICE_TRAY_MOVED ----------------- @@ -98,6 +166,68 @@ Example: Note: If the command-line option "-no-shutdown" has been specified, a STOP event will eventually follow the SHUTDOWN event. +SPICE_CONNECTED, SPICE_DISCONNECTED +----------------------------------- + +Emitted when a SPICE client connects or disconnects. + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, + "event": "SPICE_CONNECTED", + "data": { + "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} +}} + +SPICE_INITIALIZED +----------------- + +Emitted after initial handshake and authentication takes place (if any) +and the SPICE channel is up'n'running + +Data: + +- "server": Server information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "auth": authentication method (json-string, optional) +- "client": Client information (json-object) + - "host": IP address (json-string) + - "port": port number (json-string) + - "family": address family (json-string, "ipv4" or "ipv6") + - "connection-id": spice connection id. All channels with the same id + belong to the same spice session (json-int) + - "channel-type": channel type. "1" is the main control channel, filter for + this one if you want track spice sessions only (json-int) + - "channel-id": channel id. Usually "0", might be different needed when + multiple channels of the same type exist, such as multiple + display channels in a multihead setup (json-int) + - "tls": whevener the channel is encrypted (json-bool) + +Example: + +{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, + "event": "SPICE_INITIALIZED", + "data": {"server": {"auth": "spice", "port": "5921", + "family": "ipv4", "host": "127.0.0.1"}, + "client": {"port": "49004", "family": "ipv4", "channel-type": 3, + "connection-id": 1804289383, "host": "127.0.0.1", + "channel-id": 0, "tls": true} +}} + STOP ---- @@ -110,6 +240,32 @@ Example: { "event": "STOP", "timestamp": { "seconds": 1267041730, "microseconds": 281295 } } +SUSPEND +------- + +Emitted when guest enters S3 state. + +Data: None. + +Example: + +{ "event": "SUSPEND", + "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } + +SUSPEND_DISK +------------ + +Emitted when the guest makes a request to enter S4 state. + +Data: None. + +Example: + +{ "event": "SUSPEND_DISK", + "timestamp": { "seconds": 1344456160, "microseconds": 309119 } } + +Note: QEMU shuts down when entering S4 state. + VNC_CONNECTED ------------- @@ -200,69 +356,17 @@ Example: "host": "127.0.0.1", "sasl_username": "luiz" } }, "timestamp": { "seconds": 1263475302, "microseconds": 150772 } } -SPICE_CONNECTED, SPICE_DISCONNECTED ------------------------------------ +WAKEUP +------ -Emitted when a SPICE client connects or disconnects. +Emitted when the guest has woken up from S3 and is running. -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") -- "client": Client information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") +Data: None. Example: -{ "timestamp": {"seconds": 1290688046, "microseconds": 388707}, - "event": "SPICE_CONNECTED", - "data": { - "server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"}, - "client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"} -}} - - -SPICE_INITIALIZED ------------------ - -Emitted after initial handshake and authentication takes place (if any) -and the SPICE channel is up'n'running - -Data: - -- "server": Server information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "auth": authentication method (json-string, optional) -- "client": Client information (json-object) - - "host": IP address (json-string) - - "port": port number (json-string) - - "family": address family (json-string, "ipv4" or "ipv6") - - "connection-id": spice connection id. All channels with the same id - belong to the same spice session (json-int) - - "channel-type": channel type. "1" is the main control channel, filter for - this one if you want track spice sessions only (json-int) - - "channel-id": channel id. Usually "0", might be different needed when - multiple channels of the same type exist, such as multiple - display channels in a multihead setup (json-int) - - "tls": whevener the channel is encrypted (json-bool) - -Example: - -{ "timestamp": {"seconds": 1290688046, "microseconds": 417172}, - "event": "SPICE_INITIALIZED", - "data": {"server": {"auth": "spice", "port": "5921", - "family": "ipv4", "host": "127.0.0.1"}, - "client": {"port": "49004", "family": "ipv4", "channel-type": 3, - "connection-id": 1804289383, "host": "127.0.0.1", - "channel-id": 0, "tls": true} -}} - +{ "event": "WATCHDOG", + "timestamp": { "seconds": 1344522075, "microseconds": 745528 } } WATCHDOG -------- @@ -282,74 +386,3 @@ Example: Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is followed respectively by the RESET, SHUTDOWN, or STOP events. - - -BLOCK_JOB_COMPLETED -------------------- - -Emitted when a block job has completed. - -Data: - -- "type": Job type ("stream" for image streaming, json-string) -- "device": Device name (json-string) -- "len": Maximum progress value (json-int) -- "offset": Current progress value (json-int) - On success this is equal to len. - On failure this is less than len. -- "speed": Rate limit, bytes per second (json-int) -- "error": Error message (json-string, optional) - Only present on failure. This field contains a human-readable - error message. There are no semantics other than that streaming - has failed and clients should not try to interpret the error - string. - -Example: - -{ "event": "BLOCK_JOB_COMPLETED", - "data": { "type": "stream", "device": "virtio-disk0", - "len": 10737418240, "offset": 10737418240, - "speed": 0 }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - - -BLOCK_JOB_CANCELLED -------------------- - -Emitted when a block job has been cancelled. - -Data: - -- "type": Job type ("stream" for image streaming, json-string) -- "device": Device name (json-string) -- "len": Maximum progress value (json-int) -- "offset": Current progress value (json-int) - On success this is equal to len. - On failure this is less than len. -- "speed": Rate limit, bytes per second (json-int) - -Example: - -{ "event": "BLOCK_JOB_CANCELLED", - "data": { "type": "stream", "device": "virtio-disk0", - "len": 10737418240, "offset": 134217728, - "speed": 0 }, - "timestamp": { "seconds": 1267061043, "microseconds": 959568 } } - - -BALLOON_CHANGE ----------- - -Emitted when the guest changes the actual BALLOON level. This -value is equivalent to the 'actual' field return by the -'query-balloon' command - -Data: - -- "actual": actual level of the guest memory balloon in bytes (json-number) - -Example: - -{ "event": "BALLOON_CHANGE", - "data": { "actual": 944766976 }, - "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } diff --git a/QMP/qmp-spec.txt b/QMP/qmp-spec.txt index 1ba916c9f2..a27789692b 100644 --- a/QMP/qmp-spec.txt +++ b/QMP/qmp-spec.txt @@ -106,14 +106,11 @@ completed because of an error condition. The format is: -{ "error": { "class": json-string, "data": json-object, "desc": json-string }, - "id": json-value } +{ "error": { "class": json-string, "desc": json-string }, "id": json-value } Where, -- The "class" member contains the error class name (eg. "ServiceUnavailable") -- The "data" member contains specific error data and is defined in a - per-command basis, it will be an empty json-object if the error has no data +- The "class" member contains the error class name (eg. "GenericError") - The "desc" member is a human-readable error message. Clients should not attempt to parse this message. - The "id" member contains the transaction identification associated with @@ -173,8 +170,7 @@ S: {"return": {"enabled": true, "present": true}, "id": "example"} ------------------ C: { "execute": } -S: {"error": {"class": "JSONParsing", "desc": "Invalid JSON syntax", "data": -{}}} +S: {"error": {"class": "GenericError", "desc": "Invalid JSON syntax" } } 3.5 Powerdown event ------------------- diff --git a/block.c b/block.c index 24323c11d0..016858bf8c 100644 --- a/block.c +++ b/block.c @@ -2445,6 +2445,7 @@ BlockInfoList *qmp_query_block(Error **errp) info->value->inserted->ro = bs->read_only; info->value->inserted->drv = g_strdup(bs->drv->format_name); info->value->inserted->encrypted = bs->encrypted; + info->value->inserted->encryption_key_missing = bdrv_key_required(bs); if (bs->backing_file[0]) { info->value->inserted->has_backing_file = true; info->value->inserted->backing_file = g_strdup(bs->backing_file); diff --git a/block_int.h b/block_int.h index 6c1d9cafb1..4452f6f398 100644 --- a/block_int.h +++ b/block_int.h @@ -30,6 +30,7 @@ #include "qemu-coroutine.h" #include "qemu-timer.h" #include "qapi-types.h" +#include "qerror.h" #define BLOCK_FLAG_ENCRYPT 1 #define BLOCK_FLAG_COMPAT6 4 diff --git a/compiler.h b/compiler.h index 736e77075a..f76921e5b0 100644 --- a/compiler.h +++ b/compiler.h @@ -45,6 +45,7 @@ # define GCC_ATTR __attribute__((__unused__, format(gnu_printf, 1, 2))) # define GCC_FMT_ATTR(n, m) __attribute__((format(gnu_printf, n, m))) # endif +#define GCC_WEAK __attribute__((weak)) #else #define GCC_ATTR /**/ #define GCC_FMT_ATTR(n, m) diff --git a/configure b/configure index 97b69a0d73..fea62f1d29 100755 --- a/configure +++ b/configure @@ -171,7 +171,6 @@ vhost_net="no" kvm="no" gprof="no" debug_tcg="no" -debug_mon="no" debug="no" strip_opt="yes" tcg_interpreter="no" @@ -657,14 +656,9 @@ for opt do ;; --disable-debug-tcg) debug_tcg="no" ;; - --enable-debug-mon) debug_mon="yes" - ;; - --disable-debug-mon) debug_mon="no" - ;; --enable-debug) # Enable debugging options that aren't excessively noisy debug_tcg="yes" - debug_mon="yes" debug="yes" strip_opt="no" ;; @@ -3064,7 +3058,6 @@ echo "host CPU $cpu" echo "host big endian $bigendian" echo "target list $target_list" echo "tcg debug enabled $debug_tcg" -echo "Mon debug enabled $debug_mon" echo "gprof enabled $gprof" echo "sparse enabled $sparse" echo "strip binaries $strip_opt" @@ -3157,9 +3150,6 @@ echo "ARCH=$ARCH" >> $config_host_mak if test "$debug_tcg" = "yes" ; then echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak fi -if test "$debug_mon" = "yes" ; then - echo "CONFIG_DEBUG_MONITOR=y" >> $config_host_mak -fi if test "$debug" = "yes" ; then echo "CONFIG_DEBUG_EXEC=y" >> $config_host_mak fi diff --git a/docs/writing-qmp-commands.txt b/docs/writing-qmp-commands.txt index 0ad51aa22a..8349dec8af 100644 --- a/docs/writing-qmp-commands.txt +++ b/docs/writing-qmp-commands.txt @@ -210,19 +210,17 @@ if you don't see these strings, then something went wrong. === Errors === QMP commands should use the error interface exported by the error.h header -file. The basic function used to set an error is the error_set() one. +file. Basically, errors are set by calling the error_set() function. Let's say we don't accept the string "message" to contain the word "love". If -it does contain it, we want the "hello-world" command to the return the -InvalidParameter error. - -Only one change is required, and it's in the C implementation: +it does contain it, we want the "hello-world" command to return an error: void qmp_hello_world(bool has_message, const char *message, Error **errp) { if (has_message) { if (strstr(message, "love")) { - error_set(errp, QERR_INVALID_PARAMETER, "message"); + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "the word 'love' is not allowed"); return; } printf("%s\n", message); @@ -231,30 +229,40 @@ void qmp_hello_world(bool has_message, const char *message, Error **errp) } } -Let's test it. Build qemu, run it as defined in the "Testing" section, and -then issue the following command: +The first argument to the error_set() function is the Error pointer to pointer, +which is passed to all QMP functions. The second argument is a ErrorClass +value, which should be ERROR_CLASS_GENERIC_ERROR most of the time (more +details about error classes are given below). The third argument is a human +description of the error, this is a free-form printf-like string. -{ "execute": "hello-world", "arguments": { "message": "we love qemu" } } +Let's test the example above. Build qemu, run it as defined in the "Testing" +section, and then issue the following command: + +{ "execute": "hello-world", "arguments": { "message": "all you need is love" } } The QMP server's response should be: { "error": { - "class": "InvalidParameter", - "desc": "Invalid parameter 'message'", - "data": { - "name": "message" - } + "class": "GenericError", + "desc": "the word 'love' is not allowed" } } -Which is the InvalidParameter error. +As a general rule, all QMP errors should use ERROR_CLASS_GENERIC_ERROR. There +are two exceptions to this rule: -When you have to return an error but you're unsure what error to return or -which arguments an error takes, you should look at the qerror.h file. Note -that you might be required to add new errors if needed. + 1. A non-generic ErrorClass value exists* for the failure you want to report + (eg. DeviceNotFound) -FIXME: describe better the error API and how to add new errors. + 2. Management applications have to take special action on the failure you + want to report, hence you have to add a new ErrorClass value so that they + can check for it + +If the failure you want to report doesn't fall in one of the two cases above, +just report ERROR_CLASS_GENERIC_ERROR. + + * All existing ErrorClass values are defined in the qapi-schema.json file === Command Documentation === @@ -275,7 +283,6 @@ here goes "hello-world"'s new entry for the qapi-schema.json file: # @message: #optional string to be printed # # Returns: Nothing on success. -# If @message contains "love", InvalidParameter # # Notes: if @message is not provided, the "Hello, world" string will # be printed instead diff --git a/error.c b/error.c index 58f55a012e..1f05fc466e 100644 --- a/error.c +++ b/error.c @@ -14,17 +14,16 @@ #include "error.h" #include "qjson.h" #include "qdict.h" -#include "error_int.h" +#include "qapi-types.h" #include "qerror.h" struct Error { - QDict *obj; - const char *fmt; char *msg; + ErrorClass err_class; }; -void error_set(Error **errp, const char *fmt, ...) +void error_set(Error **errp, ErrorClass err_class, const char *fmt, ...) { Error *err; va_list ap; @@ -37,9 +36,9 @@ void error_set(Error **errp, const char *fmt, ...) err = g_malloc0(sizeof(*err)); va_start(ap, fmt); - err->obj = qobject_to_qdict(qobject_from_jsonv(fmt, &ap)); + err->msg = g_strdup_vprintf(fmt, ap); va_end(ap); - err->fmt = fmt; + err->err_class = err_class; *errp = err; } @@ -50,9 +49,7 @@ Error *error_copy(const Error *err) err_new = g_malloc0(sizeof(*err)); err_new->msg = g_strdup(err->msg); - err_new->fmt = err->fmt; - err_new->obj = err->obj; - QINCREF(err_new->obj); + err_new->err_class = err->err_class; return err_new; } @@ -62,75 +59,24 @@ bool error_is_set(Error **errp) return (errp && *errp); } +ErrorClass error_get_class(const Error *err) +{ + return err->err_class; +} + const char *error_get_pretty(Error *err) { - if (err->msg == NULL) { - QString *str; - str = qerror_format(err->fmt, err->obj); - err->msg = g_strdup(qstring_get_str(str)); - QDECREF(str); - } - return err->msg; } -const char *error_get_field(Error *err, const char *field) -{ - if (strcmp(field, "class") == 0) { - return qdict_get_str(err->obj, field); - } else { - QDict *dict = qdict_get_qdict(err->obj, "data"); - return qdict_get_str(dict, field); - } -} - -QDict *error_get_data(Error *err) -{ - QDict *data = qdict_get_qdict(err->obj, "data"); - QINCREF(data); - return data; -} - -void error_set_field(Error *err, const char *field, const char *value) -{ - QDict *dict = qdict_get_qdict(err->obj, "data"); - qdict_put(dict, field, qstring_from_str(value)); -} - void error_free(Error *err) { if (err) { - QDECREF(err->obj); g_free(err->msg); g_free(err); } } -bool error_is_type(Error *err, const char *fmt) -{ - const char *error_class; - char *ptr; - char *end; - - if (!err) { - return false; - } - - ptr = strstr(fmt, "'class': '"); - assert(ptr != NULL); - ptr += strlen("'class': '"); - - end = strchr(ptr, '\''); - assert(end != NULL); - - error_class = error_get_field(err, "class"); - if (strlen(error_class) != end - ptr) { - return false; - } - - return strncmp(ptr, error_class, end - ptr) == 0; -} - void error_propagate(Error **dst_err, Error *local_err) { if (dst_err && !*dst_err) { @@ -139,22 +85,3 @@ void error_propagate(Error **dst_err, Error *local_err) error_free(local_err); } } - -QObject *error_get_qobject(Error *err) -{ - QINCREF(err->obj); - return QOBJECT(err->obj); -} - -void error_set_qobject(Error **errp, QObject *obj) -{ - Error *err; - if (errp == NULL) { - return; - } - err = g_malloc0(sizeof(*err)); - err->obj = qobject_to_qdict(obj); - qobject_incref(obj); - - *errp = err; -} diff --git a/error.h b/error.h index 3d9d96def0..96fc20328f 100644 --- a/error.h +++ b/error.h @@ -13,20 +13,21 @@ #define ERROR_H #include "compiler.h" +#include "qapi-types.h" #include /** - * A class representing internal errors within QEMU. An error has a string - * typename and optionally a set of named string parameters. + * A class representing internal errors within QEMU. An error has a ErrorClass + * code and a human message. */ typedef struct Error Error; /** - * Set an indirect pointer to an error given a printf-style format parameter. - * Currently, qerror.h defines these error formats. This function is not - * meant to be used outside of QEMU. + * Set an indirect pointer to an error given a ErrorClass value and a + * printf-style human message. This function is not meant to be used outside + * of QEMU. */ -void error_set(Error **err, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void error_set(Error **err, ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(3, 4); /** * Returns true if an indirect pointer to an error is pointing to a valid @@ -34,6 +35,11 @@ void error_set(Error **err, const char *fmt, ...) GCC_FMT_ATTR(2, 3); */ bool error_is_set(Error **err); +/* + * Get the error class of an error object. + */ +ErrorClass error_get_class(const Error *err); + /** * Returns an exact copy of the error passed as an argument. */ @@ -44,16 +50,6 @@ Error *error_copy(const Error *err); */ const char *error_get_pretty(Error *err); -/** - * Get an individual named error field. - */ -const char *error_get_field(Error *err, const char *field); - -/** - * Get an individual named error field. - */ -void error_set_field(Error *err, const char *field, const char *value); - /** * Propagate an error to an indirect pointer to an error. This function will * always transfer ownership of the error reference and handles the case where @@ -66,10 +62,4 @@ void error_propagate(Error **dst_err, Error *local_err); */ void error_free(Error *err); -/** - * Determine if an error is of a speific type (based on the qerror format). - * Non-QEMU users should get the `class' field to identify the error type. - */ -bool error_is_type(Error *err, const char *fmt); - #endif diff --git a/error_int.h b/error_int.h deleted file mode 100644 index 5e3942405a..0000000000 --- a/error_int.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * QEMU Error Objects - * - * Copyright IBM, Corp. 2011 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU LGPL, version 2. See - * the COPYING.LIB file in the top-level directory. - */ -#ifndef QEMU_ERROR_INT_H -#define QEMU_ERROR_INT_H - -#include "qemu-common.h" -#include "qobject.h" -#include "qdict.h" -#include "error.h" - -/** - * Internal QEMU functions for working with Error. - * - * These are used to convert QErrors to Errors - */ -QDict *error_get_data(Error *err); -QObject *error_get_qobject(Error *err); -void error_set_qobject(Error **errp, QObject *obj); - -#endif diff --git a/hmp.c b/hmp.c index c13386b114..a9d5675fc8 100644 --- a/hmp.c +++ b/hmp.c @@ -670,34 +670,35 @@ void hmp_pmemsave(Monitor *mon, const QDict *qdict) static void hmp_cont_cb(void *opaque, int err) { - Monitor *mon = opaque; - if (!err) { - hmp_cont(mon, NULL); + qmp_cont(NULL); } } +static bool key_is_missing(const BlockInfo *bdev) +{ + return (bdev->inserted && bdev->inserted->encryption_key_missing); +} + void hmp_cont(Monitor *mon, const QDict *qdict) { + BlockInfoList *bdev_list, *bdev; Error *errp = NULL; - qmp_cont(&errp); - if (error_is_set(&errp)) { - if (error_is_type(errp, QERR_DEVICE_ENCRYPTED)) { - const char *device; - - /* The device is encrypted. Ask the user for the password - and retry */ - - device = error_get_field(errp, "device"); - assert(device != NULL); - - monitor_read_block_device_key(mon, device, hmp_cont_cb, mon); - error_free(errp); - return; + bdev_list = qmp_query_block(NULL); + for (bdev = bdev_list; bdev; bdev = bdev->next) { + if (key_is_missing(bdev->value)) { + monitor_read_block_device_key(mon, bdev->value->device, + hmp_cont_cb, NULL); + goto out; } - hmp_handle_error(mon, &errp); } + + qmp_cont(&errp); + hmp_handle_error(mon, &errp); + +out: + qapi_free_BlockInfoList(bdev_list); } void hmp_system_wakeup(Monitor *mon, const QDict *qdict) @@ -878,22 +879,6 @@ static void hmp_change_read_arg(Monitor *mon, const char *password, monitor_read_command(mon, 1); } -static void cb_hmp_change_bdrv_pwd(Monitor *mon, const char *password, - void *opaque) -{ - Error *encryption_err = opaque; - Error *err = NULL; - const char *device; - - device = error_get_field(encryption_err, "device"); - - qmp_block_passwd(device, password, &err); - hmp_handle_error(mon, &err); - error_free(encryption_err); - - monitor_read_command(mon, 1); -} - void hmp_change(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); @@ -911,18 +896,10 @@ void hmp_change(Monitor *mon, const QDict *qdict) } qmp_change(device, target, !!arg, arg, &err); - if (error_is_type(err, QERR_DEVICE_ENCRYPTED)) { - monitor_printf(mon, "%s (%s) is encrypted.\n", - error_get_field(err, "device"), - error_get_field(err, "filename")); - if (!monitor_get_rs(mon)) { - monitor_printf(mon, - "terminal does not support password prompting\n"); - error_free(err); - return; - } - readline_start(monitor_get_rs(mon), "Password: ", 1, - cb_hmp_change_bdrv_pwd, err); + if (error_is_set(&err) && + error_get_class(err) == ERROR_CLASS_DEVICE_ENCRYPTED) { + error_free(err); + monitor_read_block_device_key(mon, device, NULL, NULL); return; } hmp_handle_error(mon, &err); diff --git a/hmp.h b/hmp.h index 6d6e53bd6e..7dd93bf0f4 100644 --- a/hmp.h +++ b/hmp.h @@ -16,6 +16,7 @@ #include "qemu-common.h" #include "qapi-types.h" +#include "qdict.h" void hmp_info_name(Monitor *mon); void hmp_info_version(Monitor *mon); diff --git a/hw/acpi.c b/hw/acpi.c index effc7ec23e..f7950be267 100644 --- a/hw/acpi.c +++ b/hw/acpi.c @@ -22,6 +22,7 @@ #include "hw.h" #include "pc.h" #include "acpi.h" +#include "monitor.h" struct acpi_table_header { uint16_t _length; /* our length, not actual part of the hdr */ @@ -386,6 +387,7 @@ void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val, char s4) break; default: if (sus_typ == s4) { /* S4 request */ + monitor_protocol_event(QEVENT_SUSPEND_DISK, NULL); qemu_system_shutdown_request(); } break; diff --git a/migration-tcp.c b/migration-tcp.c index 440804db75..ac891c38a3 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -82,27 +82,23 @@ static void tcp_wait_for_connect(void *opaque) int tcp_start_outgoing_migration(MigrationState *s, const char *host_port, Error **errp) { + bool in_progress; + s->get_error = socket_errno; s->write = socket_write; s->close = tcp_close; - s->fd = inet_connect(host_port, false, errp); - - if (!error_is_set(errp)) { - migrate_fd_connect(s); - } else if (error_is_type(*errp, QERR_SOCKET_CONNECT_IN_PROGRESS)) { - DPRINTF("connect in progress\n"); - qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); - } else if (error_is_type(*errp, QERR_SOCKET_CREATE_FAILED)) { - DPRINTF("connect failed\n"); - return -1; - } else if (error_is_type(*errp, QERR_SOCKET_CONNECT_FAILED)) { - DPRINTF("connect failed\n"); + s->fd = inet_connect(host_port, false, &in_progress, errp); + if (error_is_set(errp)) { migrate_fd_error(s); return -1; + } + + if (in_progress) { + DPRINTF("connect in progress\n"); + qemu_set_fd_handler2(s->fd, NULL, NULL, tcp_wait_for_connect, s); } else { - DPRINTF("unknown error\n"); - return -1; + migrate_fd_connect(s); } return 0; diff --git a/monitor.c b/monitor.c index 5227cf15e3..dd63f1d640 100644 --- a/monitor.c +++ b/monitor.c @@ -172,41 +172,11 @@ struct Monitor { CPUArchState *mon_cpu; BlockDriverCompletionFunc *password_completion_cb; void *password_opaque; -#ifdef CONFIG_DEBUG_MONITOR - int print_calls_nr; -#endif QError *error; QLIST_HEAD(,mon_fd_t) fds; QLIST_ENTRY(Monitor) entry; }; -#ifdef CONFIG_DEBUG_MONITOR -#define MON_DEBUG(fmt, ...) do { \ - fprintf(stderr, "Monitor: "); \ - fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) - -static inline void mon_print_count_inc(Monitor *mon) -{ - mon->print_calls_nr++; -} - -static inline void mon_print_count_init(Monitor *mon) -{ - mon->print_calls_nr = 0; -} - -static inline int mon_print_count_get(const Monitor *mon) -{ - return mon->print_calls_nr; -} - -#else /* !CONFIG_DEBUG_MONITOR */ -#define MON_DEBUG(fmt, ...) do { } while (0) -static inline void mon_print_count_inc(Monitor *mon) { } -static inline void mon_print_count_init(Monitor *mon) { } -static inline int mon_print_count_get(const Monitor *mon) { return 0; } -#endif /* CONFIG_DEBUG_MONITOR */ - /* QMP checker flags */ #define QMP_ACCEPT_UNKNOWNS 1 @@ -299,8 +269,6 @@ void monitor_vprintf(Monitor *mon, const char *fmt, va_list ap) if (!mon) return; - mon_print_count_inc(mon); - if (monitor_ctrl_mode(mon)) { return; } @@ -385,16 +353,26 @@ static void monitor_json_emitter(Monitor *mon, const QObject *data) QDECREF(json); } +static QDict *build_qmp_error_dict(const QError *err) +{ + QObject *obj; + + obj = qobject_from_jsonf("{ 'error': { 'class': %s, 'desc': %p } }", + ErrorClass_lookup[err->err_class], + qerror_human(err)); + + return qobject_to_qdict(obj); +} + static void monitor_protocol_emitter(Monitor *mon, QObject *data) { QDict *qmp; trace_monitor_protocol_emitter(mon); - qmp = qdict_new(); - if (!monitor_has_error(mon)) { /* success response */ + qmp = qdict_new(); if (data) { qobject_incref(data); qdict_put_obj(qmp, "return", data); @@ -404,9 +382,7 @@ static void monitor_protocol_emitter(Monitor *mon, QObject *data) } } else { /* error response */ - qdict_put(mon->error->error, "desc", qerror_human(mon->error)); - qdict_put(qmp, "error", mon->error->error); - QINCREF(mon->error->error); + qmp = build_qmp_error_dict(mon->error); QDECREF(mon->error); mon->error = NULL; } @@ -456,6 +432,7 @@ static const char *monitor_event_names[] = { [QEVENT_BLOCK_JOB_CANCELLED] = "BLOCK_JOB_CANCELLED", [QEVENT_DEVICE_TRAY_MOVED] = "DEVICE_TRAY_MOVED", [QEVENT_SUSPEND] = "SUSPEND", + [QEVENT_SUSPEND_DISK] = "SUSPEND_DISK", [QEVENT_WAKEUP] = "WAKEUP", [QEVENT_BALLOON_CHANGE] = "BALLOON_CHANGE", }; @@ -3874,8 +3851,6 @@ void monitor_set_error(Monitor *mon, QError *qerror) if (!mon->error) { mon->error = qerror; } else { - MON_DEBUG("Additional error report at %s:%d\n", - qerror->file, qerror->linenr); QDECREF(qerror); } } @@ -3889,36 +3864,7 @@ static void handler_audit(Monitor *mon, const mon_cmd_t *cmd, int ret) * Action: Report an internal error to the client if in QMP. */ qerror_report(QERR_UNDEFINED_ERROR); - MON_DEBUG("command '%s' returned failure but did not pass an error\n", - cmd->name); } - -#ifdef CONFIG_DEBUG_MONITOR - if (!ret && monitor_has_error(mon)) { - /* - * If it returns success, it must not have passed an error. - * - * Action: Report the passed error to the client. - */ - MON_DEBUG("command '%s' returned success but passed an error\n", - cmd->name); - } - - if (mon_print_count_get(mon) > 0 && strcmp(cmd->name, "info") != 0) { - /* - * Handlers should not call Monitor print functions. - * - * Action: Ignore them in QMP. - * - * (XXX: we don't check any 'info' or 'query' command here - * because the user print function _is_ called by do_info(), hence - * we will trigger this check. This problem will go away when we - * make 'query' commands real and kill do_info()) - */ - MON_DEBUG("command '%s' called print functions %d time(s)\n", - cmd->name, mon_print_count_get(mon)); - } -#endif } static void handle_user_command(Monitor *mon, const char *cmdline) @@ -4447,8 +4393,6 @@ static void qmp_call_cmd(Monitor *mon, const mon_cmd_t *cmd, int ret; QObject *data = NULL; - mon_print_count_init(mon); - ret = cmd->mhandler.cmd_new(mon, params, &data); handler_audit(mon, cmd, ret); monitor_protocol_emitter(mon, data); diff --git a/monitor.h b/monitor.h index 5f4de1b3da..4ef9a046f8 100644 --- a/monitor.h +++ b/monitor.h @@ -40,6 +40,7 @@ typedef enum MonitorEvent { QEVENT_BLOCK_JOB_CANCELLED, QEVENT_DEVICE_TRAY_MOVED, QEVENT_SUSPEND, + QEVENT_SUSPEND_DISK, QEVENT_WAKEUP, QEVENT_BALLOON_CHANGE, diff --git a/nbd.c b/nbd.c index dc0adf90ed..0dd60c5f4c 100644 --- a/nbd.c +++ b/nbd.c @@ -162,7 +162,7 @@ int tcp_socket_outgoing(const char *address, uint16_t port) int tcp_socket_outgoing_spec(const char *address_and_port) { - return inet_connect(address_and_port, true, NULL); + return inet_connect(address_and_port, true, NULL, NULL); } int tcp_socket_incoming(const char *address, uint16_t port) diff --git a/qapi-schema.json b/qapi-schema.json index 56d9d7b0e2..53bbe46e4d 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2,6 +2,36 @@ # # QAPI Schema +## +# @ErrorClass +# +# QEMU error classes +# +# @GenericError: this is used for errors that don't require a specific error +# class. This should be the default case for most errors +# +# @CommandNotFound: the requested command has not been found +# +# @DeviceEncrypted: the requested operation can't be fulfilled because the +# selected device is encrypted +# +# @DeviceNotActive: a device has failed to be become active +# +# @DeviceNotFound: the requested device has not been found +# +# @KVMMissingCap: the requested operation can't be fulfilled because a +# required KVM capability is missing +# +# @MigrationExpected: the requested operation can't be fulfilled because a +# migration process is expected +# +# Since: 1.2 +## +{ 'enum': 'ErrorClass', + 'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted', + 'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap', + 'MigrationExpected' ] } + ## # @NameInfo: # @@ -486,6 +516,9 @@ # # @encrypted: true if the backing device is encrypted # +# @encryption_key_missing: true if the backing device is encrypted but an +# valid encryption key is missing +# # @bps: total throughput limit in bytes per second is specified # # @bps_rd: read throughput limit in bytes per second is specified @@ -505,9 +538,9 @@ { 'type': 'BlockDeviceInfo', 'data': { 'file': 'str', 'ro': 'bool', 'drv': 'str', '*backing_file': 'str', 'backing_file_depth': 'int', - 'encrypted': 'bool', 'bps': 'int', 'bps_rd': 'int', - 'bps_wr': 'int', 'iops': 'int', 'iops_rd': 'int', - 'iops_wr': 'int'} } + 'encrypted': 'bool', 'encryption_key_missing': 'bool', + 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', + 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int'} } ## # @BlockDeviceIoStatus: @@ -709,7 +742,6 @@ # Returns information about the current VNC server # # Returns: @VncInfo -# If VNC support is not compiled in, FeatureDisabled # # Since: 0.14.0 ## @@ -1093,9 +1125,6 @@ # virtual address (defaults to CPU 0) # # Returns: Nothing on success -# If @cpu is not a valid VCPU, InvalidParameterValue -# If @filename cannot be opened, OpenFileFailed -# If an I/O error occurs while writing the file, IOError # # Since: 0.14.0 # @@ -1116,8 +1145,6 @@ # @filename: the file to save the memory to as binary data # # Returns: Nothing on success -# If @filename cannot be opened, OpenFileFailed -# If an I/O error occurs while writing the file, IOError # # Since: 0.14.0 # @@ -1159,7 +1186,6 @@ # Injects an Non-Maskable Interrupt into all guest's VCPUs. # # Returns: If successful, nothing -# If the Virtual Machine doesn't support NMI injection, Unsupported # # Since: 0.14.0 # @@ -1210,7 +1236,6 @@ # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound # If @device is not encrypted, DeviceNotEncrypted -# If @password is not valid for this device, InvalidPassword # # Notes: Not all block formats support encryption and some that do are not # able to validate that a password is correct. Disk corruption may @@ -1251,11 +1276,6 @@ # # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound -# If @size is negative, InvalidParameterValue -# If the block device has no medium inserted, DeviceHasNoMedium -# If the block device does not support resize, Unsupported -# If the block device is read-only, DeviceIsReadOnly -# If a long-running operation is using the device, DeviceInUse # # Since: 0.14.0 ## @@ -1317,10 +1337,6 @@ # # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound -# If @device is busy, DeviceInUse will be returned -# If @snapshot-file can't be created, OpenFileFailed -# If @snapshot-file can't be opened, OpenFileFailed -# If @format is invalid, InvalidBlockFormat # # Note: The transaction aborts on the first failure. Therefore, there will # be only one device or snapshot file returned in an error condition, and @@ -1349,8 +1365,6 @@ # # Returns: nothing on success # If @device is not a valid block device, DeviceNotFound -# If @snapshot-file can't be opened, OpenFileFailed -# If @format is invalid, InvalidBlockFormat # # Since 0.14.0 ## @@ -1474,9 +1488,7 @@ # 4) A link type in the form 'link' where subtype is a qdev # device type name. Link properties form the device model graph. # -# Since: 1.1 -# -# Notes: This type is experimental. Its syntax may change in future releases. +# Since: 1.2 ## { 'type': 'ObjectPropertyInfo', 'data': { 'name': 'str', 'type': 'str' } } @@ -1493,10 +1505,7 @@ # Returns: a list of @ObjectPropertyInfo that describe the properties of the # object. # -# Since: 1.1 -# -# Notes: This command is experimental. It's syntax may change in future -# releases. +# Since: 1.2 ## { 'command': 'qom-list', 'data': { 'path': 'str' }, @@ -1532,9 +1541,7 @@ # returns as #str pathnames. All integer property types (u8, u16, etc) # are returned as #int. # -# Since: 1.1 -# -# Notes: This command is experimental and may change syntax in future releases. +# Since: 1.2 ## { 'command': 'qom-get', 'data': { 'path': 'str', 'property': 'str' }, @@ -1553,9 +1560,7 @@ # @value: a value who's type is appropriate for the property type. See @qom-get # for a description of type mapping. # -# Since: 1.1 -# -# Notes: This command is experimental and may change syntax in future releases. +# Since: 1.2 ## { 'command': 'qom-set', 'data': { 'path': 'str', 'property': 'str', 'value': 'visitor' }, @@ -1579,11 +1584,6 @@ # # Returns: Nothing on success # If Spice is not enabled, DeviceNotFound -# If @protocol does not support connected, InvalidParameter -# If @protocol is invalid, InvalidParameter -# If any other error occurs, SetPasswdFailed -# -# Notes: If VNC is not enabled, SetPasswdFailed is returned. # # Since: 0.14.0 ## @@ -1605,8 +1605,6 @@ # # Returns: Nothing on success # If @protocol is `spice' and Spice is not active, DeviceNotFound -# If an error occurs setting password expiration, SetPasswdFailed -# If @protocol is not `spice' or 'vnc', InvalidParameter # # Since: 0.14.0 # @@ -1629,8 +1627,6 @@ # # Returns: Nothing on success # If @device is not a valid block device, DeviceNotFound -# If @device is not removable and @force is false, DeviceNotRemovable -# If @force is false and @device is locked, DeviceLocked # # Notes: Ejecting a device will no media results in success # @@ -1673,7 +1669,6 @@ # # Returns: Nothing on success. # If @device is not a valid block device, DeviceNotFound -# If @format is not a valid block format, InvalidBlockFormat # If the new block device is encrypted, DeviceEncrypted. Note that # if this error is returned, the device has been opened successfully # and an additional call to @block_passwd is required to set the @@ -1709,7 +1704,6 @@ # # Returns: Nothing on success # If @device is not a valid block device, DeviceNotFound -# If the argument combination is invalid, InvalidParameterCombination # # Since: 1.1 ## @@ -1743,11 +1737,7 @@ # @speed: #optional the maximum speed, in bytes per second # # Returns: Nothing on success -# If streaming is already active on this device, DeviceInUse # If @device does not exist, DeviceNotFound -# If image streaming is not supported by this device, NotSupported -# If @base does not exist, BaseNotFound -# If @speed is invalid, InvalidParameter # # Since: 1.1 ## @@ -1769,8 +1759,6 @@ # Defaults to 0. # # Returns: Nothing on success -# If the job type does not support throttling, NotSupported -# If the speed value is invalid, InvalidParameter # If no background operation is active on this device, DeviceNotActive # # Since: 1.1 @@ -1800,7 +1788,6 @@ # # Returns: Nothing on success # If no background operation is active on this device, DeviceNotActive -# If cancellation already in progress, DeviceInUse # # Since: 1.1 ## @@ -1832,13 +1819,39 @@ # Returns: a list of @ObjectTypeInfo or an empty list if no results are found # # Since: 1.1 -# -# Notes: This command is experimental and may change syntax in future releases. ## { 'command': 'qom-list-types', 'data': { '*implements': 'str', '*abstract': 'bool' }, 'returns': [ 'ObjectTypeInfo' ] } +## +# @DevicePropertyInfo: +# +# Information about device properties. +# +# @name: the name of the property +# @type: the typename of the property +# +# Since: 1.2 +## +{ 'type': 'DevicePropertyInfo', + 'data': { 'name': 'str', 'type': 'str' } } + +## +# @device-list-properties: +# +# List properties associated with a device. +# +# @typename: the type name of a device +# +# Returns: a list of DevicePropertyInfo describing a devices properties +# +# Since: 1.2 +## +{ 'command': 'device-list-properties', + 'data': { 'typename': 'str'}, + 'returns': [ 'DevicePropertyInfo' ] } + ## # @migrate # @@ -1870,8 +1883,6 @@ # format. # # Returns: Nothing on success -# If @filename cannot be opened, OpenFileFailed -# If an I/O error occurs while writing the file, IOError # # Since: 1.1 ## @@ -1886,7 +1897,6 @@ # # Returns: Nothing on success # If @id is not a valid device, DeviceNotFound -# If the device does not support unplug, BusNoHotplug # # Notes: When this command completes, the device may not be removed from the # guest. Hot removal is an operation that requires guest cooperation. @@ -1927,14 +1937,6 @@ # want to dump all guest's memory, please specify the start @begin and @length # # Returns: nothing on success -# If @begin contains an invalid address, InvalidParameter -# If only one of @begin and @length is specified, MissingParameter -# If @protocol stats with "fd:", and the fd cannot be found, FdNotFound -# If @protocol starts with "file:", and the file cannot be -# opened, OpenFileFailed -# If @protocol does not start with "fd:" or "file:", InvalidParameter -# If an I/O error occurs while writing the file, IOError -# If the target does not support this command, Unsupported # # Since: 1.2 ## @@ -1961,10 +1963,6 @@ # # Returns: Nothing on success # If @type is not a valid network backend, DeviceNotFound -# If @id is not a valid identifier, InvalidParameterValue -# if @id already exists, DuplicateId -# If @props contains an invalid parameter for this backend, -# InvalidParameter ## { 'command': 'netdev_add', 'data': {'type': 'str', 'id': 'str', '*props': '**'}, @@ -2284,8 +2282,6 @@ # @fdname: file descriptor name # # Returns: Nothing on success -# If file descriptor was not received, FdNotSupplied -# If @fdname is not valid, InvalidParameterType # # Since: 0.14.0 # @@ -2305,8 +2301,58 @@ # @fdname: file descriptor name # # Returns: Nothing on success -# If @fdname is not found, FdNotFound # # Since: 0.14.0 ## { 'command': 'closefd', 'data': {'fdname': 'str'} } + +## +# @MachineInfo: +# +# Information describing a machine. +# +# @name: the name of the machine +# +# @alias: #optional an alias for the machine name +# +# @default: #optional whether the machine is default +# +# Since: 1.2.0 +## +{ 'type': 'MachineInfo', + 'data': { 'name': 'str', '*alias': 'str', + '*is-default': 'bool' } } + +## +# @query-machines: +# +# Return a list of supported machines +# +# Returns: a list of MachineInfo +# +# Since: 1.2.0 +## +{ 'command': 'query-machines', 'returns': ['MachineInfo'] } + +## +# @CpuDefinitionInfo: +# +# Virtual CPU definition. +# +# @name: the name of the CPU definition +# +# Since: 1.2.0 +## +{ 'type': 'CpuDefinitionInfo', + 'data': { 'name': 'str' } } + +## +# @query-cpu-definitions: +# +# Return a list of supported virtual CPU definitions +# +# Returns: a list of CpuDefInfo +# +# Since: 1.2.0 +## +{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] } diff --git a/qapi/qmp-core.h b/qapi/qmp-core.h index b0f64ba1ee..00446cff9b 100644 --- a/qapi/qmp-core.h +++ b/qapi/qmp-core.h @@ -49,6 +49,7 @@ void qmp_disable_command(const char *name); void qmp_enable_command(const char *name); bool qmp_command_is_enabled(const char *name); char **qmp_get_command_list(void); +QObject *qmp_build_error_object(Error *errp); #endif diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 122c1a29ba..4085994686 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -14,8 +14,8 @@ #include "qemu-objects.h" #include "qapi/qmp-core.h" #include "json-parser.h" +#include "qapi-types.h" #include "error.h" -#include "error_int.h" #include "qerror.h" static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) @@ -109,6 +109,13 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp) return ret; } +QObject *qmp_build_error_object(Error *errp) +{ + return qobject_from_jsonf("{ 'class': %s, 'desc': %s }", + ErrorClass_lookup[error_get_class(errp)], + error_get_pretty(errp)); +} + QObject *qmp_dispatch(QObject *request) { Error *err = NULL; @@ -119,7 +126,7 @@ QObject *qmp_dispatch(QObject *request) rsp = qdict_new(); if (err) { - qdict_put_obj(rsp, "error", error_get_qobject(err)); + qdict_put_obj(rsp, "error", qmp_build_error_object(err)); error_free(err); } else if (ret) { qdict_put_obj(rsp, "return", ret); diff --git a/qemu-char.c b/qemu-char.c index c2aaaeeb8f..382c71ebcd 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2446,7 +2446,7 @@ static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) if (is_listen) { fd = inet_listen_opts(opts, 0, NULL); } else { - fd = inet_connect_opts(opts, NULL); + fd = inet_connect_opts(opts, NULL, NULL); } } if (fd < 0) { diff --git a/qemu-ga.c b/qemu-ga.c index f1a39ec3a6..8f87621ae4 100644 --- a/qemu-ga.c +++ b/qemu-ga.c @@ -28,7 +28,6 @@ #include "module.h" #include "signal.h" #include "qerror.h" -#include "error_int.h" #include "qapi/qmp-core.h" #include "qga/channel.h" #ifdef _WIN32 @@ -515,7 +514,7 @@ static void process_event(JSONMessageParser *parser, QList *tokens) } else { g_warning("failed to parse event: %s", error_get_pretty(err)); } - qdict_put_obj(qdict, "error", error_get_qobject(err)); + qdict_put_obj(qdict, "error", qmp_build_error_object(err)); error_free(err); } else { qdict = qobject_to_qdict(obj); @@ -532,7 +531,7 @@ static void process_event(JSONMessageParser *parser, QList *tokens) qdict = qdict_new(); g_warning("unrecognized payload format"); error_set(&err, QERR_UNSUPPORTED); - qdict_put_obj(qdict, "error", error_get_qobject(err)); + qdict_put_obj(qdict, "error", qmp_build_error_object(err)); error_free(err); } ret = send_response(s, QOBJECT(qdict)); diff --git a/qemu-sockets.c b/qemu-sockets.c index beb2bb6f4a..361d890da3 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -209,7 +209,7 @@ listen: return slisten; } -int inet_connect_opts(QemuOpts *opts, Error **errp) +int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp) { struct addrinfo ai,*res,*e; const char *addr; @@ -224,6 +224,10 @@ int inet_connect_opts(QemuOpts *opts, Error **errp) ai.ai_family = PF_UNSPEC; ai.ai_socktype = SOCK_STREAM; + if (in_progress) { + *in_progress = false; + } + addr = qemu_opt_get(opts, "host"); port = qemu_opt_get(opts, "port"); block = qemu_opt_get_bool(opts, "block", 0); @@ -277,7 +281,9 @@ int inet_connect_opts(QemuOpts *opts, Error **errp) #else if (!block && (rc == -EINPROGRESS)) { #endif - error_set(errp, QERR_SOCKET_CONNECT_IN_PROGRESS); + if (in_progress) { + *in_progress = true; + } } else if (rc < 0) { if (NULL == e->ai_next) fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__, @@ -487,7 +493,7 @@ int inet_listen(const char *str, char *ostr, int olen, return sock; } -int inet_connect(const char *str, bool block, Error **errp) +int inet_connect(const char *str, bool block, bool *in_progress, Error **errp) { QemuOpts *opts; int sock = -1; @@ -497,7 +503,7 @@ int inet_connect(const char *str, bool block, Error **errp) if (block) { qemu_opt_set(opts, "block", "on"); } - sock = inet_connect_opts(opts, errp); + sock = inet_connect_opts(opts, in_progress, errp); } else { error_set(errp, QERR_SOCKET_CREATE_FAILED); } diff --git a/qemu_socket.h b/qemu_socket.h index 4689ff340d..30ae6af8b8 100644 --- a/qemu_socket.h +++ b/qemu_socket.h @@ -42,8 +42,8 @@ int send_all(int fd, const void *buf, int len1); int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp); int inet_listen(const char *str, char *ostr, int olen, int socktype, int port_offset, Error **errp); -int inet_connect_opts(QemuOpts *opts, Error **errp); -int inet_connect(const char *str, bool block, Error **errp); +int inet_connect_opts(QemuOpts *opts, bool *in_progress, Error **errp); +int inet_connect(const char *str, bool block, bool *in_progress, Error **errp); int inet_dgram_opts(QemuOpts *opts); const char *inet_strfamily(int family); diff --git a/qerror.c b/qerror.c index 92c4eff179..08185047b4 100644 --- a/qerror.c +++ b/qerror.c @@ -22,321 +22,12 @@ static const QType qerror_type = { .destroy = qerror_destroy_obj, }; -/** - * The 'desc' parameter is a printf-like string, the format of the format - * string is: - * - * %(KEY) - * - * Where KEY is a QDict key, which has to be passed to qerror_from_info(). - * - * Example: - * - * "foo error on device: %(device) slot: %(slot_nr)" - * - * A single percent sign can be printed if followed by a second one, - * for example: - * - * "running out of foo: %(foo)%%" - * - * Please keep the entries in alphabetical order. - * Use scripts/check-qerror.sh to check. - */ -static const QErrorStringTable qerror_table[] = { - { - .error_fmt = QERR_ADD_CLIENT_FAILED, - .desc = "Could not add client", - }, - { - .error_fmt = QERR_AMBIGUOUS_PATH, - .desc = "Path '%(path)' does not uniquely identify a %(object)" - }, - { - .error_fmt = QERR_BAD_BUS_FOR_DEVICE, - .desc = "Device '%(device)' can't go on a %(bad_bus_type) bus", - }, - { - .error_fmt = QERR_BASE_NOT_FOUND, - .desc = "Base '%(base)' not found", - }, - { - .error_fmt = QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, - .desc = "Block format '%(format)' used by device '%(name)' does not support feature '%(feature)'", - }, - { - .error_fmt = QERR_BUS_NO_HOTPLUG, - .desc = "Bus '%(bus)' does not support hotplugging", - }, - { - .error_fmt = QERR_BUS_NOT_FOUND, - .desc = "Bus '%(bus)' not found", - }, - { - .error_fmt = QERR_COMMAND_DISABLED, - .desc = "The command %(name) has been disabled for this instance", - }, - { - .error_fmt = QERR_COMMAND_NOT_FOUND, - .desc = "The command %(name) has not been found", - }, - { - .error_fmt = QERR_DEVICE_ENCRYPTED, - .desc = "Device '%(device)' is encrypted", - }, - { - .error_fmt = QERR_DEVICE_FEATURE_BLOCKS_MIGRATION, - .desc = "Migration is disabled when using feature '%(feature)' in device '%(device)'", - }, - { - .error_fmt = QERR_DEVICE_HAS_NO_MEDIUM, - .desc = "Device '%(device)' has no medium", - }, - { - .error_fmt = QERR_DEVICE_INIT_FAILED, - .desc = "Device '%(device)' could not be initialized", - }, - { - .error_fmt = QERR_DEVICE_IN_USE, - .desc = "Device '%(device)' is in use", - }, - { - .error_fmt = QERR_DEVICE_IS_READ_ONLY, - .desc = "Device '%(device)' is read only", - }, - { - .error_fmt = QERR_DEVICE_LOCKED, - .desc = "Device '%(device)' is locked", - }, - { - .error_fmt = QERR_DEVICE_MULTIPLE_BUSSES, - .desc = "Device '%(device)' has multiple child busses", - }, - { - .error_fmt = QERR_DEVICE_NO_BUS, - .desc = "Device '%(device)' has no child bus", - }, - { - .error_fmt = QERR_DEVICE_NO_HOTPLUG, - .desc = "Device '%(device)' does not support hotplugging", - }, - { - .error_fmt = QERR_DEVICE_NOT_ACTIVE, - .desc = "Device '%(device)' has not been activated", - }, - { - .error_fmt = QERR_DEVICE_NOT_ENCRYPTED, - .desc = "Device '%(device)' is not encrypted", - }, - { - .error_fmt = QERR_DEVICE_NOT_FOUND, - .desc = "Device '%(device)' not found", - }, - { - .error_fmt = QERR_DEVICE_NOT_REMOVABLE, - .desc = "Device '%(device)' is not removable", - }, - { - .error_fmt = QERR_DUPLICATE_ID, - .desc = "Duplicate ID '%(id)' for %(object)", - }, - { - .error_fmt = QERR_FD_NOT_FOUND, - .desc = "File descriptor named '%(name)' not found", - }, - { - .error_fmt = QERR_FD_NOT_SUPPLIED, - .desc = "No file descriptor supplied via SCM_RIGHTS", - }, - { - .error_fmt = QERR_FEATURE_DISABLED, - .desc = "The feature '%(name)' is not enabled", - }, - { - .error_fmt = QERR_INVALID_BLOCK_FORMAT, - .desc = "Invalid block format '%(name)'", - }, - { - .error_fmt = QERR_INVALID_OPTION_GROUP, - .desc = "There is no option group '%(group)'", - }, - { - .error_fmt = QERR_INVALID_PARAMETER, - .desc = "Invalid parameter '%(name)'", - }, - { - .error_fmt = QERR_INVALID_PARAMETER_COMBINATION, - .desc = "Invalid parameter combination", - }, - { - .error_fmt = QERR_INVALID_PARAMETER_TYPE, - .desc = "Invalid parameter type for '%(name)', expected: %(expected)", - }, - { - .error_fmt = QERR_INVALID_PARAMETER_VALUE, - .desc = "Parameter '%(name)' expects %(expected)", - }, - { - .error_fmt = QERR_INVALID_PASSWORD, - .desc = "Password incorrect", - }, - { - .error_fmt = QERR_IO_ERROR, - .desc = "An IO error has occurred", - }, - { - .error_fmt = QERR_JSON_PARSE_ERROR, - .desc = "JSON parse error, %(message)", - - }, - { - .error_fmt = QERR_JSON_PARSING, - .desc = "Invalid JSON syntax", - }, - { - .error_fmt = QERR_KVM_MISSING_CAP, - .desc = "Using KVM without %(capability), %(feature) unavailable", - }, - { - .error_fmt = QERR_MIGRATION_ACTIVE, - .desc = "There's a migration process in progress", - }, - { - .error_fmt = QERR_MIGRATION_NOT_SUPPORTED, - .desc = "State blocked by non-migratable device '%(device)'", - }, - { - .error_fmt = QERR_MIGRATION_EXPECTED, - .desc = "An incoming migration is expected before this command can be executed", - }, - { - .error_fmt = QERR_MISSING_PARAMETER, - .desc = "Parameter '%(name)' is missing", - }, - { - .error_fmt = QERR_NO_BUS_FOR_DEVICE, - .desc = "No '%(bus)' bus found for device '%(device)'", - }, - { - .error_fmt = QERR_NOT_SUPPORTED, - .desc = "Not supported", - }, - { - .error_fmt = QERR_OPEN_FILE_FAILED, - .desc = "Could not open '%(filename)'", - }, - { - .error_fmt = QERR_PERMISSION_DENIED, - .desc = "Insufficient permission to perform this operation", - }, - { - .error_fmt = QERR_PROPERTY_NOT_FOUND, - .desc = "Property '%(device).%(property)' not found", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_BAD, - .desc = "Property '%(device).%(property)' doesn't take value '%(value)'", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_IN_USE, - .desc = "Property '%(device).%(property)' can't take value '%(value)', it's in use", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_NOT_FOUND, - .desc = "Property '%(device).%(property)' can't find value '%(value)'", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_NOT_POWER_OF_2, - .desc = "Property '%(device).%(property)' doesn't take " - "value '%(value)', it's not a power of 2", - }, - { - .error_fmt = QERR_PROPERTY_VALUE_OUT_OF_RANGE, - .desc = "Property '%(device).%(property)' doesn't take " - "value %(value) (minimum: %(min), maximum: %(max))", - }, - { - .error_fmt = QERR_QGA_COMMAND_FAILED, - .desc = "Guest agent command failed, error was '%(message)'", - }, - { - .error_fmt = QERR_QGA_LOGGING_FAILED, - .desc = "Guest agent failed to log non-optional log statement", - }, - { - .error_fmt = QERR_QMP_BAD_INPUT_OBJECT, - .desc = "Expected '%(expected)' in QMP input", - }, - { - .error_fmt = QERR_QMP_BAD_INPUT_OBJECT_MEMBER, - .desc = "QMP input object member '%(member)' expects '%(expected)'", - }, - { - .error_fmt = QERR_QMP_EXTRA_MEMBER, - .desc = "QMP input object member '%(member)' is unexpected", - }, - { - .error_fmt = QERR_RESET_REQUIRED, - .desc = "Resetting the Virtual Machine is required", - }, - { - .error_fmt = QERR_SET_PASSWD_FAILED, - .desc = "Could not set password", - }, - { - .error_fmt = QERR_TOO_MANY_FILES, - .desc = "Too many open files", - }, - { - .error_fmt = QERR_UNDEFINED_ERROR, - .desc = "An undefined error has occurred", - }, - { - .error_fmt = QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, - .desc = "'%(device)' uses a %(format) feature which is not " - "supported by this qemu version: %(feature)", - }, - { - .error_fmt = QERR_UNSUPPORTED, - .desc = "this feature or command is not currently supported", - }, - { - .error_fmt = QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION, - .desc = "Migration is disabled when VirtFS export path '%(path)' " - "is mounted in the guest using mount_tag '%(tag)'", - }, - { - .error_fmt = QERR_VNC_SERVER_FAILED, - .desc = "Could not start VNC server on %(target)", - }, - { - .error_fmt = QERR_SOCKET_CONNECT_IN_PROGRESS, - .desc = "Connection can not be completed immediately", - }, - { - .error_fmt = QERR_SOCKET_CONNECT_FAILED, - .desc = "Failed to connect to socket", - }, - { - .error_fmt = QERR_SOCKET_LISTEN_FAILED, - .desc = "Failed to set socket to listening mode", - }, - { - .error_fmt = QERR_SOCKET_BIND_FAILED, - .desc = "Failed to bind socket", - }, - { - .error_fmt = QERR_SOCKET_CREATE_FAILED, - .desc = "Failed to create socket", - }, - {} -}; - /** * qerror_new(): Create a new QError * * Return strong reference. */ -QError *qerror_new(void) +static QError *qerror_new(void) { QError *qerr; @@ -346,200 +37,31 @@ QError *qerror_new(void) return qerr; } -static void GCC_FMT_ATTR(2, 3) qerror_abort(const QError *qerr, - const char *fmt, ...) -{ - va_list ap; - - fprintf(stderr, "qerror: bad call in function '%s':\n", qerr->func); - fprintf(stderr, "qerror: -> "); - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); - - fprintf(stderr, "\nqerror: call at %s:%d\n", qerr->file, qerr->linenr); - abort(); -} - -static void GCC_FMT_ATTR(2, 0) qerror_set_data(QError *qerr, - const char *fmt, va_list *va) -{ - QObject *obj; - - obj = qobject_from_jsonv(fmt, va); - if (!obj) { - qerror_abort(qerr, "invalid format '%s'", fmt); - } - if (qobject_type(obj) != QTYPE_QDICT) { - qerror_abort(qerr, "error format is not a QDict '%s'", fmt); - } - - qerr->error = qobject_to_qdict(obj); - - obj = qdict_get(qerr->error, "class"); - if (!obj) { - qerror_abort(qerr, "missing 'class' key in '%s'", fmt); - } - if (qobject_type(obj) != QTYPE_QSTRING) { - qerror_abort(qerr, "'class' key value should be a QString"); - } - - obj = qdict_get(qerr->error, "data"); - if (!obj) { - qerror_abort(qerr, "missing 'data' key in '%s'", fmt); - } - if (qobject_type(obj) != QTYPE_QDICT) { - qerror_abort(qerr, "'data' key value should be a QDICT"); - } -} - -static void qerror_set_desc(QError *qerr, const char *fmt) -{ - int i; - - // FIXME: inefficient loop - - for (i = 0; qerror_table[i].error_fmt; i++) { - if (strcmp(qerror_table[i].error_fmt, fmt) == 0) { - qerr->entry = &qerror_table[i]; - return; - } - } - - qerror_abort(qerr, "error format '%s' not found", fmt); -} - /** * qerror_from_info(): Create a new QError from error information * - * The information consists of: - * - * - file the file name of where the error occurred - * - linenr the line number of where the error occurred - * - func the function name of where the error occurred - * - fmt JSON printf-like dictionary, there must exist keys 'class' and - * 'data' - * - va va_list of all arguments specified by fmt - * * Return strong reference. */ -QError *qerror_from_info(const char *file, int linenr, const char *func, - const char *fmt, va_list *va) +static QError *qerror_from_info(ErrorClass err_class, const char *fmt, + va_list *va) { QError *qerr; qerr = qerror_new(); loc_save(&qerr->loc); - qerr->linenr = linenr; - qerr->file = file; - qerr->func = func; - if (!fmt) { - qerror_abort(qerr, "QDict not specified"); - } - - qerror_set_data(qerr, fmt, va); - qerror_set_desc(qerr, fmt); + qerr->err_msg = g_strdup_vprintf(fmt, *va); + qerr->err_class = err_class; return qerr; } -static void parse_error(const QErrorStringTable *entry, int c) -{ - fprintf(stderr, "expected '%c' in '%s'", c, entry->desc); - abort(); -} - -static const char *append_field(QDict *error, QString *outstr, - const QErrorStringTable *entry, - const char *start) -{ - QObject *obj; - QDict *qdict; - QString *key_qs; - const char *end, *key; - - if (*start != '%') - parse_error(entry, '%'); - start++; - if (*start != '(') - parse_error(entry, '('); - start++; - - end = strchr(start, ')'); - if (!end) - parse_error(entry, ')'); - - key_qs = qstring_from_substr(start, 0, end - start - 1); - key = qstring_get_str(key_qs); - - qdict = qobject_to_qdict(qdict_get(error, "data")); - obj = qdict_get(qdict, key); - if (!obj) { - abort(); - } - - switch (qobject_type(obj)) { - case QTYPE_QSTRING: - qstring_append(outstr, qdict_get_str(qdict, key)); - break; - case QTYPE_QINT: - qstring_append_int(outstr, qdict_get_int(qdict, key)); - break; - default: - abort(); - } - - QDECREF(key_qs); - return ++end; -} - -static QString *qerror_format_desc(QDict *error, - const QErrorStringTable *entry) -{ - QString *qstring; - const char *p; - - assert(entry != NULL); - - qstring = qstring_new(); - - for (p = entry->desc; *p != '\0';) { - if (*p != '%') { - qstring_append_chr(qstring, *p++); - } else if (*(p + 1) == '%') { - qstring_append_chr(qstring, '%'); - p += 2; - } else { - p = append_field(error, qstring, entry, p); - } - } - - return qstring; -} - -QString *qerror_format(const char *fmt, QDict *error) -{ - const QErrorStringTable *entry = NULL; - int i; - - for (i = 0; qerror_table[i].error_fmt; i++) { - if (strcmp(qerror_table[i].error_fmt, fmt) == 0) { - entry = &qerror_table[i]; - break; - } - } - - return qerror_format_desc(error, entry); -} - /** * qerror_human(): Format QError data into human-readable string. */ QString *qerror_human(const QError *qerror) { - return qerror_format_desc(qerror->error, qerror->entry); + return qstring_from_str(qerror->err_msg); } /** @@ -549,7 +71,7 @@ QString *qerror_human(const QError *qerror) * it uses error_report() for this, so that the output is routed to the right * place (ie. stderr or Monitor's device). */ -void qerror_print(QError *qerror) +static void qerror_print(QError *qerror) { QString *qstring = qerror_human(qerror); loc_push_restore(&qerror->loc); @@ -558,14 +80,13 @@ void qerror_print(QError *qerror) QDECREF(qstring); } -void qerror_report_internal(const char *file, int linenr, const char *func, - const char *fmt, ...) +void qerror_report(ErrorClass eclass, const char *fmt, ...) { va_list va; QError *qerror; va_start(va, fmt); - qerror = qerror_from_info(file, linenr, func, fmt, &va); + qerror = qerror_from_info(eclass, fmt, &va); va_end(va); if (monitor_cur_is_qmp()) { @@ -579,27 +100,18 @@ void qerror_report_internal(const char *file, int linenr, const char *func, /* Evil... */ struct Error { - QDict *obj; - const char *fmt; char *msg; + ErrorClass err_class; }; void qerror_report_err(Error *err) { QError *qerr; - int i; qerr = qerror_new(); loc_save(&qerr->loc); - QINCREF(err->obj); - qerr->error = err->obj; - - for (i = 0; qerror_table[i].error_fmt; i++) { - if (strcmp(qerror_table[i].error_fmt, err->fmt) == 0) { - qerr->entry = &qerror_table[i]; - break; - } - } + qerr->err_msg = g_strdup(err->msg); + qerr->err_class = err->err_class; if (monitor_cur_is_qmp()) { monitor_set_error(cur_mon, qerr); @@ -620,7 +132,7 @@ void assert_no_error(Error *err) /** * qobject_to_qerror(): Convert a QObject into a QError */ -QError *qobject_to_qerror(const QObject *obj) +static QError *qobject_to_qerror(const QObject *obj) { if (qobject_type(obj) != QTYPE_QERROR) { return NULL; @@ -639,6 +151,6 @@ static void qerror_destroy_obj(QObject *obj) assert(obj != NULL); qerr = qobject_to_qerror(obj); - QDECREF(qerr->error); + g_free(qerr->err_msg); g_free(qerr); } diff --git a/qerror.h b/qerror.h index b4c8758f40..d0a76a4f71 100644 --- a/qerror.h +++ b/qerror.h @@ -16,36 +16,20 @@ #include "qstring.h" #include "qemu-error.h" #include "error.h" +#include "qapi-types.h" #include -typedef struct QErrorStringTable { - const char *desc; - const char *error_fmt; -} QErrorStringTable; - typedef struct QError { QObject_HEAD; - QDict *error; Location loc; - int linenr; - const char *file; - const char *func; - const QErrorStringTable *entry; + char *err_msg; + ErrorClass err_class; } QError; -QError *qerror_new(void); -QError *qerror_from_info(const char *file, int linenr, const char *func, - const char *fmt, va_list *va) GCC_FMT_ATTR(4, 0); QString *qerror_human(const QError *qerror); -void qerror_print(QError *qerror); -void qerror_report_internal(const char *file, int linenr, const char *func, - const char *fmt, ...) GCC_FMT_ATTR(4, 5); +void qerror_report(ErrorClass err_class, const char *fmt, ...) GCC_FMT_ATTR(2, 3); void qerror_report_err(Error *err); void assert_no_error(Error *err); -QString *qerror_format(const char *fmt, QDict *error); -#define qerror_report(fmt, ...) \ - qerror_report_internal(__FILE__, __LINE__, __func__, fmt, ## __VA_ARGS__) -QError *qobject_to_qerror(const QObject *obj); /* * QError class list @@ -53,217 +37,213 @@ QError *qobject_to_qerror(const QObject *obj); * Use scripts/check-qerror.sh to check. */ #define QERR_ADD_CLIENT_FAILED \ - "{ 'class': 'AddClientFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Could not add client" #define QERR_AMBIGUOUS_PATH \ - "{ 'class': 'AmbiguousPath', 'data': { 'path': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Path '%s' does not uniquely identify an object" #define QERR_BAD_BUS_FOR_DEVICE \ - "{ 'class': 'BadBusForDevice', 'data': { 'device': %s, 'bad_bus_type': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' can't go on a %s bus" #define QERR_BASE_NOT_FOUND \ - "{ 'class': 'BaseNotFound', 'data': { 'base': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Base '%s' not found" #define QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED \ - "{ 'class': 'BlockFormatFeatureNotSupported', 'data': { 'format': %s, 'name': %s, 'feature': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Block format '%s' used by device '%s' does not support feature '%s'" #define QERR_BUFFER_OVERRUN \ - "{ 'class': 'BufferOverrun', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "An internal buffer overran" #define QERR_BUS_NO_HOTPLUG \ - "{ 'class': 'BusNoHotplug', 'data': { 'bus': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Bus '%s' does not support hotplugging" #define QERR_BUS_NOT_FOUND \ - "{ 'class': 'BusNotFound', 'data': { 'bus': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Bus '%s' not found" #define QERR_COMMAND_DISABLED \ - "{ 'class': 'CommandDisabled', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "The command %s has been disabled for this instance" #define QERR_COMMAND_NOT_FOUND \ - "{ 'class': 'CommandNotFound', 'data': { 'name': %s } }" + ERROR_CLASS_COMMAND_NOT_FOUND, "The command %s has not been found" #define QERR_DEVICE_ENCRYPTED \ - "{ 'class': 'DeviceEncrypted', 'data': { 'device': %s, 'filename': %s } }" + ERROR_CLASS_DEVICE_ENCRYPTED, "'%s' (%s) is encrypted" #define QERR_DEVICE_FEATURE_BLOCKS_MIGRATION \ - "{ 'class': 'DeviceFeatureBlocksMigration', 'data': { 'device': %s, 'feature': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when using feature '%s' in device '%s'" #define QERR_DEVICE_HAS_NO_MEDIUM \ - "{ 'class': 'DeviceHasNoMedium', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' has no medium" #define QERR_DEVICE_INIT_FAILED \ - "{ 'class': 'DeviceInitFailed', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' could not be initialized" #define QERR_DEVICE_IN_USE \ - "{ 'class': 'DeviceInUse', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is in use" #define QERR_DEVICE_IS_READ_ONLY \ - "{ 'class': 'DeviceIsReadOnly', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is read only" #define QERR_DEVICE_LOCKED \ - "{ 'class': 'DeviceLocked', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is locked" #define QERR_DEVICE_MULTIPLE_BUSSES \ - "{ 'class': 'DeviceMultipleBusses', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' has multiple child busses" #define QERR_DEVICE_NO_BUS \ - "{ 'class': 'DeviceNoBus', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' has no child bus" #define QERR_DEVICE_NO_HOTPLUG \ - "{ 'class': 'DeviceNoHotplug', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' does not support hotplugging" #define QERR_DEVICE_NOT_ACTIVE \ - "{ 'class': 'DeviceNotActive', 'data': { 'device': %s } }" + ERROR_CLASS_DEVICE_NOT_ACTIVE, "Device '%s' has not been activated" #define QERR_DEVICE_NOT_ENCRYPTED \ - "{ 'class': 'DeviceNotEncrypted', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is not encrypted" #define QERR_DEVICE_NOT_FOUND \ - "{ 'class': 'DeviceNotFound', 'data': { 'device': %s } }" + ERROR_CLASS_DEVICE_NOT_FOUND, "Device '%s' not found" #define QERR_DEVICE_NOT_REMOVABLE \ - "{ 'class': 'DeviceNotRemovable', 'data': { 'device': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Device '%s' is not removable" #define QERR_DUPLICATE_ID \ - "{ 'class': 'DuplicateId', 'data': { 'id': %s, 'object': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Duplicate ID '%s' for %s" #define QERR_FD_NOT_FOUND \ - "{ 'class': 'FdNotFound', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "File descriptor named '%s' not found" #define QERR_FD_NOT_SUPPLIED \ - "{ 'class': 'FdNotSupplied', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "No file descriptor supplied via SCM_RIGHTS" #define QERR_FEATURE_DISABLED \ - "{ 'class': 'FeatureDisabled', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "The feature '%s' is not enabled" #define QERR_INVALID_BLOCK_FORMAT \ - "{ 'class': 'InvalidBlockFormat', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Invalid block format '%s'" #define QERR_INVALID_OPTION_GROUP \ - "{ 'class': 'InvalidOptionGroup', 'data': { 'group': %s } }" + ERROR_CLASS_GENERIC_ERROR, "There is no option group '%s'" #define QERR_INVALID_PARAMETER \ - "{ 'class': 'InvalidParameter', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Invalid parameter '%s'" #define QERR_INVALID_PARAMETER_COMBINATION \ - "{ 'class': 'InvalidParameterCombination', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Invalid parameter combination" #define QERR_INVALID_PARAMETER_TYPE \ - "{ 'class': 'InvalidParameterType', 'data': { 'name': %s,'expected': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Invalid parameter type for '%s', expected: %s" #define QERR_INVALID_PARAMETER_VALUE \ - "{ 'class': 'InvalidParameterValue', 'data': { 'name': %s, 'expected': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Parameter '%s' expects %s" #define QERR_INVALID_PASSWORD \ - "{ 'class': 'InvalidPassword', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Password incorrect" #define QERR_IO_ERROR \ - "{ 'class': 'IOError', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "An IO error has occurred" #define QERR_JSON_PARSE_ERROR \ - "{ 'class': 'JSONParseError', 'data': { 'message': %s } }" + ERROR_CLASS_GENERIC_ERROR, "JSON parse error, %s" #define QERR_JSON_PARSING \ - "{ 'class': 'JSONParsing', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Invalid JSON syntax" #define QERR_KVM_MISSING_CAP \ - "{ 'class': 'KVMMissingCap', 'data': { 'capability': %s, 'feature': %s } }" + ERROR_CLASS_K_V_M_MISSING_CAP, "Using KVM without %s, %s unavailable" #define QERR_MIGRATION_ACTIVE \ - "{ 'class': 'MigrationActive', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "There's a migration process in progress" #define QERR_MIGRATION_NOT_SUPPORTED \ - "{ 'class': 'MigrationNotSupported', 'data': {'device': %s} }" + ERROR_CLASS_GENERIC_ERROR, "State blocked by non-migratable device '%s'" #define QERR_MIGRATION_EXPECTED \ - "{ 'class': 'MigrationExpected', 'data': {} }" + ERROR_CLASS_MIGRATION_EXPECTED, "An incoming migration is expected before this command can be executed" #define QERR_MISSING_PARAMETER \ - "{ 'class': 'MissingParameter', 'data': { 'name': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Parameter '%s' is missing" #define QERR_NO_BUS_FOR_DEVICE \ - "{ 'class': 'NoBusForDevice', 'data': { 'device': %s, 'bus': %s } }" + ERROR_CLASS_GENERIC_ERROR, "No '%s' bus found for device '%s'" #define QERR_NOT_SUPPORTED \ - "{ 'class': 'NotSupported', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Not supported" #define QERR_OPEN_FILE_FAILED \ - "{ 'class': 'OpenFileFailed', 'data': { 'filename': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Could not open '%s'" #define QERR_PERMISSION_DENIED \ - "{ 'class': 'PermissionDenied', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Insufficient permission to perform this operation" #define QERR_PROPERTY_NOT_FOUND \ - "{ 'class': 'PropertyNotFound', 'data': { 'device': %s, 'property': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Property '%s.%s' not found" #define QERR_PROPERTY_VALUE_BAD \ - "{ 'class': 'PropertyValueBad', 'data': { 'device': %s, 'property': %s, 'value': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Property '%s.%s' doesn't take value '%s'" #define QERR_PROPERTY_VALUE_IN_USE \ - "{ 'class': 'PropertyValueInUse', 'data': { 'device': %s, 'property': %s, 'value': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Property '%s.%s' can't take value '%s', it's in use" #define QERR_PROPERTY_VALUE_NOT_FOUND \ - "{ 'class': 'PropertyValueNotFound', 'data': { 'device': %s, 'property': %s, 'value': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Property '%s.%s' can't find value '%s'" #define QERR_PROPERTY_VALUE_NOT_POWER_OF_2 \ - "{ 'class': 'PropertyValueNotPowerOf2', 'data': { " \ - "'device': %s, 'property': %s, 'value': %"PRId64" } }" + ERROR_CLASS_GENERIC_ERROR, "Property %s.%s doesn't take value '%" PRId64 "', it's not a power of 2" #define QERR_PROPERTY_VALUE_OUT_OF_RANGE \ - "{ 'class': 'PropertyValueOutOfRange', 'data': { 'device': %s, 'property': %s, 'value': %"PRId64", 'min': %"PRId64", 'max': %"PRId64" } }" + ERROR_CLASS_GENERIC_ERROR, "Property %s.%s doesn't take value %" PRId64 " (minimum: %" PRId64 ", maximum: %" PRId64 ")" #define QERR_QGA_COMMAND_FAILED \ - "{ 'class': 'QgaCommandFailed', 'data': { 'message': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Guest agent command failed, error was '%s'" #define QERR_QGA_LOGGING_FAILED \ - "{ 'class': 'QgaLoggingFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Guest agent failed to log non-optional log statement" #define QERR_QMP_BAD_INPUT_OBJECT \ - "{ 'class': 'QMPBadInputObject', 'data': { 'expected': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Expected '%s' in QMP input" #define QERR_QMP_BAD_INPUT_OBJECT_MEMBER \ - "{ 'class': 'QMPBadInputObjectMember', 'data': { 'member': %s, 'expected': %s } }" + ERROR_CLASS_GENERIC_ERROR, "QMP input object member '%s' expects '%s'" #define QERR_QMP_EXTRA_MEMBER \ - "{ 'class': 'QMPExtraInputObjectMember', 'data': { 'member': %s } }" + ERROR_CLASS_GENERIC_ERROR, "QMP input object member '%s' is unexpected" #define QERR_RESET_REQUIRED \ - "{ 'class': 'ResetRequired', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Resetting the Virtual Machine is required" #define QERR_SET_PASSWD_FAILED \ - "{ 'class': 'SetPasswdFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Could not set password" #define QERR_TOO_MANY_FILES \ - "{ 'class': 'TooManyFiles', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Too many open files" #define QERR_UNDEFINED_ERROR \ - "{ 'class': 'UndefinedError', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "An undefined error has occurred" #define QERR_UNKNOWN_BLOCK_FORMAT_FEATURE \ - "{ 'class': 'UnknownBlockFormatFeature', 'data': { 'device': %s, 'format': %s, 'feature': %s } }" + ERROR_CLASS_GENERIC_ERROR, "'%s' uses a %s feature which is not supported by this qemu version: %s" #define QERR_UNSUPPORTED \ - "{ 'class': 'Unsupported', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "this feature or command is not currently supported" #define QERR_VIRTFS_FEATURE_BLOCKS_MIGRATION \ - "{ 'class': 'VirtFSFeatureBlocksMigration', 'data': { 'path': %s, 'tag': %s } }" + ERROR_CLASS_GENERIC_ERROR, "Migration is disabled when VirtFS export path '%s' is mounted in the guest using mount_tag '%s'" #define QERR_VNC_SERVER_FAILED \ - "{ 'class': 'VNCServerFailed', 'data': { 'target': %s } }" - -#define QERR_SOCKET_CONNECT_IN_PROGRESS \ - "{ 'class': 'SockConnectInprogress', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Could not start VNC server on %s" #define QERR_SOCKET_CONNECT_FAILED \ - "{ 'class': 'SockConnectFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Failed to connect to socket" #define QERR_SOCKET_LISTEN_FAILED \ - "{ 'class': 'SockListenFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Failed to set socket to listening mode" #define QERR_SOCKET_BIND_FAILED \ - "{ 'class': 'SockBindFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Failed to bind socket" #define QERR_SOCKET_CREATE_FAILED \ - "{ 'class': 'SockCreateFailed', 'data': {} }" + ERROR_CLASS_GENERIC_ERROR, "Failed to create socket" #endif /* QERROR_H */ diff --git a/qmp-commands.hx b/qmp-commands.hx index 0363d7cca9..527b9f7c24 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -435,8 +435,8 @@ Example: -> { "execute": "inject-nmi" } <- { "return": {} } -Note: inject-nmi is only supported for x86 guest currently, it will - returns "Unsupported" error for non-x86 guest. +Note: inject-nmi fails when the guest doesn't support injecting. + Currently, only x86 guests do. EQMP @@ -2368,3 +2368,22 @@ EQMP .args_type = "implements:s?,abstract:b?", .mhandler.cmd_new = qmp_marshal_input_qom_list_types, }, + + { + .name = "device-list-properties", + .args_type = "typename:s", + .mhandler.cmd_new = qmp_marshal_input_device_list_properties, + }, + + { + .name = "query-machines", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_machines, + }, + + { + .name = "query-cpu-definitions", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_query_cpu_definitions, + }, + diff --git a/qmp.c b/qmp.c index fee9fb2a9d..6c1e4e8978 100644 --- a/qmp.c +++ b/qmp.c @@ -417,3 +417,59 @@ ObjectTypeInfoList *qmp_qom_list_types(bool has_implements, return ret; } + +DevicePropertyInfoList *qmp_device_list_properties(const char *typename, + Error **errp) +{ + ObjectClass *klass; + Property *prop; + DevicePropertyInfoList *prop_list = NULL; + + klass = object_class_by_name(typename); + if (klass == NULL) { + error_set(errp, QERR_DEVICE_NOT_FOUND, typename); + return NULL; + } + + klass = object_class_dynamic_cast(klass, TYPE_DEVICE); + if (klass == NULL) { + error_set(errp, QERR_INVALID_PARAMETER_VALUE, + "name", TYPE_DEVICE); + return NULL; + } + + do { + for (prop = DEVICE_CLASS(klass)->props; prop && prop->name; prop++) { + DevicePropertyInfoList *entry; + DevicePropertyInfo *info; + + /* + * TODO Properties without a parser are just for dirty hacks. + * qdev_prop_ptr is the only such PropertyInfo. It's marked + * for removal. This conditional should be removed along with + * it. + */ + if (!prop->info->set) { + continue; /* no way to set it, don't show */ + } + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(prop->name); + info->type = g_strdup(prop->info->legacy_name ?: prop->info->name); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = prop_list; + prop_list = entry; + } + klass = object_class_get_parent(klass); + } while (klass != object_class_by_name(TYPE_DEVICE)); + + return prop_list; +} + +CpuDefinitionInfoList GCC_WEAK *qmp_query_cpu_definitions(Error **errp) +{ + error_set(errp, QERR_NOT_SUPPORTED); + return NULL; +} diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 9eed40e18a..3c4678dbf1 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -342,6 +342,7 @@ def gen_command_decl_prologue(header, guard, prefix=""): #define %(guard)s #include "%(prefix)sqapi-types.h" +#include "qdict.h" #include "error.h" ''', diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index 4a734f58d5..cf601ae2d2 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -70,7 +70,7 @@ const char *%(name)s_lookup[] = { ret += mcgen(''' "%(value)s", ''', - value=value.lower()) + value=value) ret += mcgen(''' NULL, @@ -79,6 +79,16 @@ const char *%(name)s_lookup[] = { ''') return ret +def generate_enum_name(name): + if name.isupper(): + return c_fun(name) + new_name = '' + for c in c_fun(name): + if c.isupper(): + new_name += '_' + new_name += c + return new_name.lstrip('_').upper() + def generate_enum(name, values): lookup_decl = mcgen(''' extern const char *%(name)s_lookup[]; @@ -100,7 +110,7 @@ typedef enum %(name)s %(abbrev)s_%(value)s = %(i)d, ''', abbrev=de_camel_case(name).upper(), - value=c_fun(value).upper(), + value=generate_enum_name(value), i=i) i += 1 @@ -253,7 +263,8 @@ fdecl.write(mcgen(''' #ifndef %(guard)s #define %(guard)s -#include "qapi/qapi-types-core.h" +#include "qemu-common.h" + ''', guard=guardname(h_file))) diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 880cfea3f8..6d5d0d6e10 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -28,6 +28,7 @@ #include "qemu-config.h" #include "qapi/qapi-visit-core.h" +#include "qmp-commands.h" #include "hyperv.h" @@ -1125,6 +1126,27 @@ void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf, const char *optarg) } } +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + x86_def_t *def; + + for (def = x86_defs; def; def = def->next) { + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(def->name); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = cpu_list; + cpu_list = entry; + } + + return cpu_list; +} + int cpu_x86_register(X86CPU *cpu, const char *cpu_model) { CPUX86State *env = &cpu->env; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 5742229197..6fe4168dc0 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -27,6 +27,7 @@ #include "gdbstub.h" #include #include "kvm_ppc.h" +#include "qmp-commands.h" //#define PPC_DUMP_CPU //#define PPC_DEBUG_SPR @@ -10345,6 +10346,31 @@ void ppc_cpu_list (FILE *f, fprintf_function cpu_fprintf) } } +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(ppc_defs); i++) { + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; + + if (!ppc_cpu_usable(&ppc_defs[i])) { + continue; + } + + info = g_malloc0(sizeof(*info)); + info->name = g_strdup(ppc_defs[i].name); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = cpu_list; + cpu_list = entry; + } + + return cpu_list; +} + /* CPUClass::reset() */ static void ppc_cpu_reset(CPUState *s) { diff --git a/ui/vnc.c b/ui/vnc.c index 312ad7fe36..385e345c31 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3061,7 +3061,7 @@ int vnc_display_open(DisplayState *ds, const char *display) if (strncmp(display, "unix:", 5) == 0) vs->lsock = unix_connect(display+5); else - vs->lsock = inet_connect(display, true, NULL); + vs->lsock = inet_connect(display, true, NULL, NULL); if (-1 == vs->lsock) { g_free(vs->display); vs->display = NULL; diff --git a/vl.c b/vl.c index 91076f0e7c..d01256a6a3 100644 --- a/vl.c +++ b/vl.c @@ -1213,6 +1213,37 @@ QEMUMachine *find_default_machine(void) return NULL; } +MachineInfoList *qmp_query_machines(Error **errp) +{ + MachineInfoList *mach_list = NULL; + QEMUMachine *m; + + for (m = first_machine; m; m = m->next) { + MachineInfoList *entry; + MachineInfo *info; + + info = g_malloc0(sizeof(*info)); + if (m->is_default) { + info->has_is_default = true; + info->is_default = true; + } + + if (m->alias) { + info->has_alias = true; + info->alias = g_strdup(m->alias); + } + + info->name = g_strdup(m->name); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = mach_list; + mach_list = entry; + } + + return mach_list; +} + /***********************************************************/ /* main execution loop */ @@ -1298,6 +1329,7 @@ static pid_t shutdown_pid; static int powerdown_requested; static int debug_requested; static int suspend_requested; +static int wakeup_requested; static NotifierList suspend_notifiers = NOTIFIER_LIST_INITIALIZER(suspend_notifiers); static NotifierList wakeup_notifiers = @@ -1352,6 +1384,13 @@ static int qemu_suspend_requested(void) return r; } +static int qemu_wakeup_requested(void) +{ + int r = wakeup_requested; + wakeup_requested = 0; + return r; +} + int qemu_powerdown_requested(void) { int r = powerdown_requested; @@ -1457,9 +1496,8 @@ void qemu_system_wakeup_request(WakeupReason reason) return; } runstate_set(RUN_STATE_RUNNING); - monitor_protocol_event(QEVENT_WAKEUP, NULL); notifier_list_notify(&wakeup_notifiers, &reason); - reset_requested = 1; + wakeup_requested = 1; qemu_notify_event(); } @@ -1539,6 +1577,13 @@ static bool main_loop_should_exit(void) runstate_set(RUN_STATE_PAUSED); } } + if (qemu_wakeup_requested()) { + pause_all_vcpus(); + cpu_synchronize_all_states(); + qemu_system_reset(VMRESET_SILENT); + resume_all_vcpus(); + monitor_protocol_event(QEVENT_WAKEUP, NULL); + } if (qemu_powerdown_requested()) { monitor_protocol_event(QEVENT_POWERDOWN, NULL); qemu_irq_raise(qemu_system_powerdown);