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:
Anthony Liguori 2012-08-13 16:12:35 -05:00
commit 633decd711
35 changed files with 691 additions and 1121 deletions

View File

@ -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

View File

@ -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 } }

View File

@ -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
-------------------

View File

@ -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);

View 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

View File

@ -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
View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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
View File

@ -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)

View File

@ -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'] }

View File

@ -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

View File

@ -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);

View File

@ -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) {

View File

@ -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));

View File

@ -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);
}

View File

@ -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
View File

@ -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
View File

@ -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 */

View File

@ -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
View File

@ -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;
}

View File

@ -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"
''',

View File

@ -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)))

View 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;

View File

@ -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)
{

View File

@ -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
View File

@ -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);