Merge remote-tracking branch 'qmp/queue/qmp' into staging
* qmp/queue/qmp: (48 commits) target-ppc: add implementation of query-cpu-definitions (v2) target-i386: add implementation of query-cpu-definitions (v2) qapi: add query-cpu-definitions command (v2) compiler: add macro for GCC weak symbols qapi: add query-machines command qapi: mark QOM commands stable qmp: introduce device-list-properties command qmp: add SUSPEND_DISK event qmp: qmp-events.txt: add missing doc for the SUSPEND event qmp: qmp-events.txt: put events in alphabetical order qmp: emit the WAKEUP event when the guest is put to run qmp: don't emit the RESET event on wakeup from S3 scripts: qapi-commands.py: qmp-commands.h: include qdict.h docs: writing-qmp-commands.txt: update error section error, qerror: drop QDict member qerror: drop qerror_table and qerror_format() error, qerror: pass desc string to error calls error: drop error_get_qobject()/error_set_qobject() qemu-ga: switch to the new error format on the wire qmp: switch to the new error format on the wire ...
This commit is contained in:
commit
633decd711
@ -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
|
||||
|
@ -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 } }
|
||||
|
@ -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
|
||||
-------------------
|
||||
|
1
block.c
1
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);
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
10
configure
vendored
10
configure
vendored
@ -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
|
||||
|
@ -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
|
||||
|
95
error.c
95
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;
|
||||
}
|
||||
|
34
error.h
34
error.h
@ -13,20 +13,21 @@
|
||||
#define ERROR_H
|
||||
|
||||
#include "compiler.h"
|
||||
#include "qapi-types.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
29
error_int.h
29
error_int.h
@ -1,29 +0,0 @@
|
||||
/*
|
||||
* QEMU Error Objects
|
||||
*
|
||||
* Copyright IBM, Corp. 2011
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
*
|
||||
* 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
|
69
hmp.c
69
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);
|
||||
|
1
hmp.h
1
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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
84
monitor.c
84
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);
|
||||
|
@ -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,
|
||||
|
||||
|
2
nbd.c
2
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)
|
||||
|
192
qapi-schema.json
192
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<subtype>' 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'] }
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
516
qerror.c
516
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);
|
||||
}
|
||||
|
168
qerror.h
168
qerror.h
@ -16,36 +16,20 @@
|
||||
#include "qstring.h"
|
||||
#include "qemu-error.h"
|
||||
#include "error.h"
|
||||
#include "qapi-types.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
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 */
|
||||
|
@ -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,
|
||||
},
|
||||
|
||||
|
56
qmp.c
56
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;
|
||||
}
|
||||
|
@ -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"
|
||||
|
||||
''',
|
||||
|
@ -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)))
|
||||
|
||||
|
@ -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;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "gdbstub.h"
|
||||
#include <kvm.h>
|
||||
#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)
|
||||
{
|
||||
|
2
ui/vnc.c
2
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;
|
||||
|
49
vl.c
49
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);
|
||||
|
Loading…
Reference in New Issue
Block a user