summaryrefslogtreecommitdiff
path: root/qapi
diff options
context:
space:
mode:
authorChanho Park <chanho61.park@samsung.com>2014-09-05 20:35:53 +0900
committerChanho Park <chanho61.park@samsung.com>2014-09-05 20:35:53 +0900
commit16b1353a36171ae06d63fd309f4772dbfb1da113 (patch)
treecf6c297ee81aba0d9b47f23d78a889667e7bce48 /qapi
parenta15119db2ff5c2fdfdeb913b297bf8aa3399132e (diff)
downloadqemu-16b1353a36171ae06d63fd309f4772dbfb1da113.tar.gz
qemu-16b1353a36171ae06d63fd309f4772dbfb1da113.tar.bz2
qemu-16b1353a36171ae06d63fd309f4772dbfb1da113.zip
Imported Upstream version 2.1.0upstream/2.1.0
Diffstat (limited to 'qapi')
-rw-r--r--qapi/Makefile.objs1
-rw-r--r--qapi/block-core.json1645
-rw-r--r--qapi/block.json180
-rw-r--r--qapi/common.json89
-rw-r--r--qapi/event.json332
-rw-r--r--qapi/opts-visitor.c26
-rw-r--r--qapi/qapi-dealloc-visitor.c4
-rw-r--r--qapi/qapi-visit-core.c259
-rw-r--r--qapi/qmp-dispatch.c27
-rw-r--r--qapi/qmp-event.c74
-rw-r--r--qapi/qmp-input-visitor.c8
-rw-r--r--qapi/qmp-output-visitor.c6
-rw-r--r--qapi/string-input-visitor.c207
-rw-r--r--qapi/string-output-visitor.c235
14 files changed, 2888 insertions, 205 deletions
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs
index 1f9c97342..d14b769cf 100644
--- a/qapi/Makefile.objs
+++ b/qapi/Makefile.objs
@@ -3,3 +3,4 @@ util-obj-y += qmp-output-visitor.o qmp-registry.o qmp-dispatch.o
util-obj-y += string-input-visitor.o string-output-visitor.o
util-obj-y += opts-visitor.o
+util-obj-y += qmp-event.o
diff --git a/qapi/block-core.json b/qapi/block-core.json
new file mode 100644
index 000000000..e378653a7
--- /dev/null
+++ b/qapi/block-core.json
@@ -0,0 +1,1645 @@
+# -*- Mode: Python -*-
+#
+# QAPI block core definitions (vm unrelated)
+
+# QAPI common definitions
+{ 'include': 'common.json' }
+
+##
+# @SnapshotInfo
+#
+# @id: unique snapshot id
+#
+# @name: user chosen name
+#
+# @vm-state-size: size of the VM state
+#
+# @date-sec: UTC date of the snapshot in seconds
+#
+# @date-nsec: fractional part in nano seconds to be used with date-sec
+#
+# @vm-clock-sec: VM clock relative to boot in seconds
+#
+# @vm-clock-nsec: fractional part in nano seconds to be used with vm-clock-sec
+#
+# Since: 1.3
+#
+##
+
+{ 'type': 'SnapshotInfo',
+ 'data': { 'id': 'str', 'name': 'str', 'vm-state-size': 'int',
+ 'date-sec': 'int', 'date-nsec': 'int',
+ 'vm-clock-sec': 'int', 'vm-clock-nsec': 'int' } }
+
+##
+# @ImageInfoSpecificQCow2:
+#
+# @compat: compatibility level
+#
+# @lazy-refcounts: #optional on or off; only valid for compat >= 1.1
+#
+# Since: 1.7
+##
+{ 'type': 'ImageInfoSpecificQCow2',
+ 'data': {
+ 'compat': 'str',
+ '*lazy-refcounts': 'bool'
+ } }
+
+##
+# @ImageInfoSpecificVmdk:
+#
+# @create-type: The create type of VMDK image
+#
+# @cid: Content id of image
+#
+# @parent-cid: Parent VMDK image's cid
+#
+# @extents: List of extent files
+#
+# Since: 1.7
+##
+{ 'type': 'ImageInfoSpecificVmdk',
+ 'data': {
+ 'create-type': 'str',
+ 'cid': 'int',
+ 'parent-cid': 'int',
+ 'extents': ['ImageInfo']
+ } }
+
+##
+# @ImageInfoSpecific:
+#
+# A discriminated record of image format specific information structures.
+#
+# Since: 1.7
+##
+
+{ 'union': 'ImageInfoSpecific',
+ 'data': {
+ 'qcow2': 'ImageInfoSpecificQCow2',
+ 'vmdk': 'ImageInfoSpecificVmdk'
+ } }
+
+##
+# @ImageInfo:
+#
+# Information about a QEMU image file
+#
+# @filename: name of the image file
+#
+# @format: format of the image file
+#
+# @virtual-size: maximum capacity in bytes of the image
+#
+# @actual-size: #optional actual size on disk in bytes of the image
+#
+# @dirty-flag: #optional true if image is not cleanly closed
+#
+# @cluster-size: #optional size of a cluster in bytes
+#
+# @encrypted: #optional true if the image is encrypted
+#
+# @compressed: #optional true if the image is compressed (Since 1.7)
+#
+# @backing-filename: #optional name of the backing file
+#
+# @full-backing-filename: #optional full path of the backing file
+#
+# @backing-filename-format: #optional the format of the backing file
+#
+# @snapshots: #optional list of VM snapshots
+#
+# @backing-image: #optional info of the backing image (since 1.6)
+#
+# @format-specific: #optional structure supplying additional format-specific
+# information (since 1.7)
+#
+# Since: 1.3
+#
+##
+
+{ 'type': 'ImageInfo',
+ 'data': {'filename': 'str', 'format': 'str', '*dirty-flag': 'bool',
+ '*actual-size': 'int', 'virtual-size': 'int',
+ '*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool',
+ '*backing-filename': 'str', '*full-backing-filename': 'str',
+ '*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'],
+ '*backing-image': 'ImageInfo',
+ '*format-specific': 'ImageInfoSpecific' } }
+
+##
+# @ImageCheck:
+#
+# Information about a QEMU image file check
+#
+# @filename: name of the image file checked
+#
+# @format: format of the image file checked
+#
+# @check-errors: number of unexpected errors occurred during check
+#
+# @image-end-offset: #optional offset (in bytes) where the image ends, this
+# field is present if the driver for the image format
+# supports it
+#
+# @corruptions: #optional number of corruptions found during the check if any
+#
+# @leaks: #optional number of leaks found during the check if any
+#
+# @corruptions-fixed: #optional number of corruptions fixed during the check
+# if any
+#
+# @leaks-fixed: #optional number of leaks fixed during the check if any
+#
+# @total-clusters: #optional total number of clusters, this field is present
+# if the driver for the image format supports it
+#
+# @allocated-clusters: #optional total number of allocated clusters, this
+# field is present if the driver for the image format
+# supports it
+#
+# @fragmented-clusters: #optional total number of fragmented clusters, this
+# field is present if the driver for the image format
+# supports it
+#
+# @compressed-clusters: #optional total number of compressed clusters, this
+# field is present if the driver for the image format
+# supports it
+#
+# Since: 1.4
+#
+##
+
+{ 'type': 'ImageCheck',
+ 'data': {'filename': 'str', 'format': 'str', 'check-errors': 'int',
+ '*image-end-offset': 'int', '*corruptions': 'int', '*leaks': 'int',
+ '*corruptions-fixed': 'int', '*leaks-fixed': 'int',
+ '*total-clusters': 'int', '*allocated-clusters': 'int',
+ '*fragmented-clusters': 'int', '*compressed-clusters': 'int' } }
+
+##
+# @BlockDeviceInfo:
+#
+# Information about the backing device for a block device.
+#
+# @file: the filename of the backing device
+#
+# @node-name: #optional the name of the block driver node (Since 2.0)
+#
+# @ro: true if the backing device was open read-only
+#
+# @drv: the name of the block format used to open the backing device. As of
+# 0.14.0 this can be: 'blkdebug', 'bochs', 'cloop', 'cow', 'dmg',
+# 'file', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device',
+# 'host_floppy', 'http', 'https', 'nbd', 'parallels', 'qcow',
+# 'qcow2', 'raw', 'tftp', 'vdi', 'vmdk', 'vpc', 'vvfat'
+#
+# @backing_file: #optional the name of the backing file (for copy-on-write)
+#
+# @backing_file_depth: number of files in the backing file chain (since: 1.2)
+#
+# @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
+#
+# @detect_zeroes: detect and optimize zero writes (Since 2.1)
+#
+# @bps: total throughput limit in bytes per second is specified
+#
+# @bps_rd: read throughput limit in bytes per second is specified
+#
+# @bps_wr: write throughput limit in bytes per second is specified
+#
+# @iops: total I/O operations per second is specified
+#
+# @iops_rd: read I/O operations per second is specified
+#
+# @iops_wr: write I/O operations per second is specified
+#
+# @image: the info of image used (since: 1.6)
+#
+# @bps_max: #optional total max in bytes (Since 1.7)
+#
+# @bps_rd_max: #optional read max in bytes (Since 1.7)
+#
+# @bps_wr_max: #optional write max in bytes (Since 1.7)
+#
+# @iops_max: #optional total I/O operations max (Since 1.7)
+#
+# @iops_rd_max: #optional read I/O operations max (Since 1.7)
+#
+# @iops_wr_max: #optional write I/O operations max (Since 1.7)
+#
+# @iops_size: #optional an I/O size in bytes (Since 1.7)
+#
+# Since: 0.14.0
+#
+##
+{ 'type': 'BlockDeviceInfo',
+ 'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str',
+ '*backing_file': 'str', 'backing_file_depth': 'int',
+ 'encrypted': 'bool', 'encryption_key_missing': 'bool',
+ 'detect_zeroes': 'BlockdevDetectZeroesOptions',
+ 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
+ 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
+ 'image': 'ImageInfo',
+ '*bps_max': 'int', '*bps_rd_max': 'int',
+ '*bps_wr_max': 'int', '*iops_max': 'int',
+ '*iops_rd_max': 'int', '*iops_wr_max': 'int',
+ '*iops_size': 'int' } }
+
+##
+# @BlockDeviceIoStatus:
+#
+# An enumeration of block device I/O status.
+#
+# @ok: The last I/O operation has succeeded
+#
+# @failed: The last I/O operation has failed
+#
+# @nospace: The last I/O operation has failed due to a no-space condition
+#
+# Since: 1.0
+##
+{ 'enum': 'BlockDeviceIoStatus', 'data': [ 'ok', 'failed', 'nospace' ] }
+
+##
+# @BlockDeviceMapEntry:
+#
+# Entry in the metadata map of the device (returned by "qemu-img map")
+#
+# @start: Offset in the image of the first byte described by this entry
+# (in bytes)
+#
+# @length: Length of the range described by this entry (in bytes)
+#
+# @depth: Number of layers (0 = top image, 1 = top image's backing file, etc.)
+# before reaching one for which the range is allocated. The value is
+# in the range 0 to the depth of the image chain - 1.
+#
+# @zero: the sectors in this range read as zeros
+#
+# @data: reading the image will actually read data from a file (in particular,
+# if @offset is present this means that the sectors are not simply
+# preallocated, but contain actual data in raw format)
+#
+# @offset: if present, the image file stores the data for this range in
+# raw format at the given offset.
+#
+# Since 1.7
+##
+{ 'type': 'BlockDeviceMapEntry',
+ 'data': { 'start': 'int', 'length': 'int', 'depth': 'int', 'zero': 'bool',
+ 'data': 'bool', '*offset': 'int' } }
+
+##
+# @BlockDirtyInfo:
+#
+# Block dirty bitmap information.
+#
+# @count: number of dirty bytes according to the dirty bitmap
+#
+# @granularity: granularity of the dirty bitmap in bytes (since 1.4)
+#
+# Since: 1.3
+##
+{ 'type': 'BlockDirtyInfo',
+ 'data': {'count': 'int', 'granularity': 'int'} }
+
+##
+# @BlockInfo:
+#
+# Block device information. This structure describes a virtual device and
+# the backing device associated with it.
+#
+# @device: The device name associated with the virtual device.
+#
+# @type: This field is returned only for compatibility reasons, it should
+# not be used (always returns 'unknown')
+#
+# @removable: True if the device supports removable media.
+#
+# @locked: True if the guest has locked this device from having its media
+# removed
+#
+# @tray_open: #optional True if the device has a tray and it is open
+# (only present if removable is true)
+#
+# @dirty-bitmaps: #optional dirty bitmaps information (only present if the
+# driver has one or more dirty bitmaps) (Since 2.0)
+#
+# @io-status: #optional @BlockDeviceIoStatus. Only present if the device
+# supports it and the VM is configured to stop on errors
+#
+# @inserted: #optional @BlockDeviceInfo describing the device if media is
+# present
+#
+# Since: 0.14.0
+##
+{ 'type': 'BlockInfo',
+ 'data': {'device': 'str', 'type': 'str', 'removable': 'bool',
+ 'locked': 'bool', '*inserted': 'BlockDeviceInfo',
+ '*tray_open': 'bool', '*io-status': 'BlockDeviceIoStatus',
+ '*dirty-bitmaps': ['BlockDirtyInfo'] } }
+
+##
+# @query-block:
+#
+# Get a list of BlockInfo for all virtual block devices.
+#
+# Returns: a list of @BlockInfo describing each virtual block device
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-block', 'returns': ['BlockInfo'] }
+
+##
+# @BlockDeviceStats:
+#
+# Statistics of a virtual block device or a block backing device.
+#
+# @rd_bytes: The number of bytes read by the device.
+#
+# @wr_bytes: The number of bytes written by the device.
+#
+# @rd_operations: The number of read operations performed by the device.
+#
+# @wr_operations: The number of write operations performed by the device.
+#
+# @flush_operations: The number of cache flush operations performed by the
+# device (since 0.15.0)
+#
+# @flush_total_time_ns: Total time spend on cache flushes in nano-seconds
+# (since 0.15.0).
+#
+# @wr_total_time_ns: Total time spend on writes in nano-seconds (since 0.15.0).
+#
+# @rd_total_time_ns: Total_time_spend on reads in nano-seconds (since 0.15.0).
+#
+# @wr_highest_offset: The offset after the greatest byte written to the
+# device. The intended use of this information is for
+# growable sparse files (like qcow2) that are used on top
+# of a physical device.
+#
+# Since: 0.14.0
+##
+{ 'type': 'BlockDeviceStats',
+ 'data': {'rd_bytes': 'int', 'wr_bytes': 'int', 'rd_operations': 'int',
+ 'wr_operations': 'int', 'flush_operations': 'int',
+ 'flush_total_time_ns': 'int', 'wr_total_time_ns': 'int',
+ 'rd_total_time_ns': 'int', 'wr_highest_offset': 'int' } }
+
+##
+# @BlockStats:
+#
+# Statistics of a virtual block device or a block backing device.
+#
+# @device: #optional If the stats are for a virtual block device, the name
+# corresponding to the virtual block device.
+#
+# @stats: A @BlockDeviceStats for the device.
+#
+# @parent: #optional This describes the file block device if it has one.
+#
+# @backing: #optional This describes the backing block device if it has one.
+# (Since 2.0)
+#
+# Since: 0.14.0
+##
+{ 'type': 'BlockStats',
+ 'data': {'*device': 'str', 'stats': 'BlockDeviceStats',
+ '*parent': 'BlockStats',
+ '*backing': 'BlockStats'} }
+
+##
+# @query-blockstats:
+#
+# Query the @BlockStats for all virtual block devices.
+#
+# Returns: A list of @BlockStats for each virtual block devices.
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-blockstats', 'returns': ['BlockStats'] }
+
+##
+# @BlockdevOnError:
+#
+# An enumeration of possible behaviors for errors on I/O operations.
+# The exact meaning depends on whether the I/O was initiated by a guest
+# or by a block job
+#
+# @report: for guest operations, report the error to the guest;
+# for jobs, cancel the job
+#
+# @ignore: ignore the error, only report a QMP event (BLOCK_IO_ERROR
+# or BLOCK_JOB_ERROR)
+#
+# @enospc: same as @stop on ENOSPC, same as @report otherwise.
+#
+# @stop: for guest operations, stop the virtual machine;
+# for jobs, pause the job
+#
+# Since: 1.3
+##
+{ 'enum': 'BlockdevOnError',
+ 'data': ['report', 'ignore', 'enospc', 'stop'] }
+
+##
+# @MirrorSyncMode:
+#
+# An enumeration of possible behaviors for the initial synchronization
+# phase of storage mirroring.
+#
+# @top: copies data in the topmost image to the destination
+#
+# @full: copies data from all images to the destination
+#
+# @none: only copy data written from now on
+#
+# Since: 1.3
+##
+{ 'enum': 'MirrorSyncMode',
+ 'data': ['top', 'full', 'none'] }
+
+##
+# @BlockJobType:
+#
+# Type of a block job.
+#
+# @commit: block commit job type, see "block-commit"
+#
+# @stream: block stream job type, see "block-stream"
+#
+# @mirror: drive mirror job type, see "drive-mirror"
+#
+# @backup: drive backup job type, see "drive-backup"
+#
+# Since: 1.7
+##
+{ 'enum': 'BlockJobType',
+ 'data': ['commit', 'stream', 'mirror', 'backup'] }
+
+##
+# @BlockJobInfo:
+#
+# Information about a long-running block device operation.
+#
+# @type: the job type ('stream' for image streaming)
+#
+# @device: the block device name
+#
+# @len: the maximum progress value
+#
+# @busy: false if the job is known to be in a quiescent state, with
+# no pending I/O. Since 1.3.
+#
+# @paused: whether the job is paused or, if @busy is true, will
+# pause itself as soon as possible. Since 1.3.
+#
+# @offset: the current progress value
+#
+# @speed: the rate limit, bytes per second
+#
+# @io-status: the status of the job (since 1.3)
+#
+# Since: 1.1
+##
+{ 'type': 'BlockJobInfo',
+ 'data': {'type': 'str', 'device': 'str', 'len': 'int',
+ 'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
+ 'io-status': 'BlockDeviceIoStatus'} }
+
+##
+# @query-block-jobs:
+#
+# Return information about long-running block device operations.
+#
+# Returns: a list of @BlockJobInfo for each active block job
+#
+# Since: 1.1
+##
+{ 'command': 'query-block-jobs', 'returns': ['BlockJobInfo'] }
+
+##
+# @block_passwd:
+#
+# This command sets the password of a block device that has not been open
+# with a password and requires one.
+#
+# The two cases where this can happen are a block device is created through
+# QEMU's initial command line or a block device is changed through the legacy
+# @change interface.
+#
+# In the event that the block device is created through the initial command
+# line, the VM will start in the stopped state regardless of whether '-S' is
+# used. The intention is for a management tool to query the block devices to
+# determine which ones are encrypted, set the passwords with this command, and
+# then start the guest with the @cont command.
+#
+# Either @device or @node-name must be set but not both.
+#
+# @device: #optional the name of the block backend device to set the password on
+#
+# @node-name: #optional graph node name to set the password on (Since 2.0)
+#
+# @password: the password to use for the device
+#
+# Returns: nothing on success
+# If @device is not a valid block device, DeviceNotFound
+# If @device is not encrypted, DeviceNotEncrypted
+#
+# Notes: Not all block formats support encryption and some that do are not
+# able to validate that a password is correct. Disk corruption may
+# occur if an invalid password is specified.
+#
+# Since: 0.14.0
+##
+{ 'command': 'block_passwd', 'data': {'*device': 'str',
+ '*node-name': 'str', 'password': 'str'} }
+
+##
+# @block_resize
+#
+# Resize a block image while a guest is running.
+#
+# Either @device or @node-name must be set but not both.
+#
+# @device: #optional the name of the device to get the image resized
+#
+# @node-name: #optional graph node name to get the image resized (Since 2.0)
+#
+# @size: new image size in bytes
+#
+# Returns: nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
+# Since: 0.14.0
+##
+{ 'command': 'block_resize', 'data': { '*device': 'str',
+ '*node-name': 'str',
+ 'size': 'int' }}
+
+##
+# @NewImageMode
+#
+# An enumeration that tells QEMU how to set the backing file path in
+# a new image file.
+#
+# @existing: QEMU should look for an existing image file.
+#
+# @absolute-paths: QEMU should create a new image with absolute paths
+# for the backing file. If there is no backing file available, the new
+# image will not be backed either.
+#
+# Since: 1.1
+##
+{ 'enum': 'NewImageMode',
+ 'data': [ 'existing', 'absolute-paths' ] }
+
+##
+# @BlockdevSnapshot
+#
+# Either @device or @node-name must be set but not both.
+#
+# @device: #optional the name of the device to generate the snapshot from.
+#
+# @node-name: #optional graph node name to generate the snapshot from (Since 2.0)
+#
+# @snapshot-file: the target of the new image. A new file will be created.
+#
+# @snapshot-node-name: #optional the graph node name of the new image (Since 2.0)
+#
+# @format: #optional the format of the snapshot image, default is 'qcow2'.
+#
+# @mode: #optional whether and how QEMU should create a new image, default is
+# 'absolute-paths'.
+##
+{ 'type': 'BlockdevSnapshot',
+ 'data': { '*device': 'str', '*node-name': 'str',
+ 'snapshot-file': 'str', '*snapshot-node-name': 'str',
+ '*format': 'str', '*mode': 'NewImageMode' } }
+
+##
+# @DriveBackup
+#
+# @device: the name of the device which should be copied.
+#
+# @target: the target of the new image. If the file exists, or if it
+# is a device, the existing file/device will be used as the new
+# destination. If it does not exist, a new file will be created.
+#
+# @format: #optional the format of the new destination, default is to
+# probe if @mode is 'existing', else the format of the source
+#
+# @sync: what parts of the disk image should be copied to the destination
+# (all the disk, only the sectors allocated in the topmost image, or
+# only new I/O).
+#
+# @mode: #optional whether and how QEMU should create a new image, default is
+# 'absolute-paths'.
+#
+# @speed: #optional the maximum speed, in bytes per second
+#
+# @on-source-error: #optional the action to take on an error on the source,
+# default 'report'. 'stop' and 'enospc' can only be used
+# if the block device supports io-status (see BlockInfo).
+#
+# @on-target-error: #optional the action to take on an error on the target,
+# default 'report' (no limitations, since this applies to
+# a different block device than @device).
+#
+# Note that @on-source-error and @on-target-error only affect background I/O.
+# If an error occurs during a guest write request, the device's rerror/werror
+# actions will be used.
+#
+# Since: 1.6
+##
+{ 'type': 'DriveBackup',
+ 'data': { 'device': 'str', 'target': 'str', '*format': 'str',
+ 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
+ '*speed': 'int',
+ '*on-source-error': 'BlockdevOnError',
+ '*on-target-error': 'BlockdevOnError' } }
+
+##
+# @blockdev-snapshot-sync
+#
+# Generates a synchronous snapshot of a block device.
+#
+# For the arguments, see the documentation of BlockdevSnapshot.
+#
+# Returns: nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
+# Since 0.14.0
+##
+{ 'command': 'blockdev-snapshot-sync',
+ 'data': 'BlockdevSnapshot' }
+
+##
+# @change-backing-file
+#
+# Change the backing file in the image file metadata. This does not
+# cause QEMU to reopen the image file to reparse the backing filename
+# (it may, however, perform a reopen to change permissions from
+# r/o -> r/w -> r/o, if needed). The new backing file string is written
+# into the image file metadata, and the QEMU internal strings are
+# updated.
+#
+# @image-node-name: The name of the block driver state node of the
+# image to modify.
+#
+# @device: The name of the device that owns image-node-name.
+#
+# @backing-file: The string to write as the backing file. This
+# string is not validated, so care should be taken
+# when specifying the string or the image chain may
+# not be able to be reopened again.
+#
+# Since: 2.1
+##
+{ 'command': 'change-backing-file',
+ 'data': { 'device': 'str', 'image-node-name': 'str',
+ 'backing-file': 'str' } }
+
+##
+# @block-commit
+#
+# Live commit of data from overlay image nodes into backing nodes - i.e.,
+# writes data between 'top' and 'base' into 'base'.
+#
+# @device: the name of the device
+#
+# @base: #optional The file name of the backing image to write data into.
+# If not specified, this is the deepest backing image
+#
+# @top: #optional The file name of the backing image within the image chain,
+# which contains the topmost data to be committed down. If
+# not specified, this is the active layer.
+#
+# @backing-file: #optional The backing file string to write into the overlay
+# image of 'top'. If 'top' is the active layer,
+# specifying a backing file string is an error. This
+# filename is not validated.
+#
+# If a pathname string is such that it cannot be
+# resolved by QEMU, that means that subsequent QMP or
+# HMP commands must use node-names for the image in
+# question, as filename lookup methods will fail.
+#
+# If not specified, QEMU will automatically determine
+# the backing file string to use, or error out if
+# there is no obvious choice. Care should be taken
+# when specifying the string, to specify a valid
+# filename or protocol.
+# (Since 2.1)
+#
+# If top == base, that is an error.
+# If top == active, the job will not be completed by itself,
+# user needs to complete the job with the block-job-complete
+# command after getting the ready event. (Since 2.0)
+#
+# If the base image is smaller than top, then the base image
+# will be resized to be the same size as top. If top is
+# smaller than the base image, the base will not be
+# truncated. If you want the base image size to match the
+# size of the smaller top, you can safely truncate it
+# yourself once the commit operation successfully completes.
+#
+# @speed: #optional the maximum speed, in bytes per second
+#
+# Returns: Nothing on success
+# If commit or stream is already active on this device, DeviceInUse
+# If @device does not exist, DeviceNotFound
+# If image commit is not supported by this device, NotSupported
+# If @base or @top is invalid, a generic error is returned
+# If @speed is invalid, InvalidParameter
+#
+# Since: 1.3
+#
+##
+{ 'command': 'block-commit',
+ 'data': { 'device': 'str', '*base': 'str', '*top': 'str',
+ '*backing-file': 'str', '*speed': 'int' } }
+
+##
+# @drive-backup
+#
+# Start a point-in-time copy of a block device to a new destination. The
+# status of ongoing drive-backup operations can be checked with
+# query-block-jobs where the BlockJobInfo.type field has the value 'backup'.
+# The operation can be stopped before it has completed using the
+# block-job-cancel command.
+#
+# For the arguments, see the documentation of DriveBackup.
+#
+# Returns: nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
+# Since 1.6
+##
+{ 'command': 'drive-backup', 'data': 'DriveBackup' }
+
+##
+# @query-named-block-nodes
+#
+# Get the named block driver list
+#
+# Returns: the list of BlockDeviceInfo
+#
+# Since 2.0
+##
+{ 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] }
+
+##
+# @drive-mirror
+#
+# Start mirroring a block device's writes to a new destination.
+#
+# @device: the name of the device whose writes should be mirrored.
+#
+# @target: the target of the new image. If the file exists, or if it
+# is a device, the existing file/device will be used as the new
+# destination. If it does not exist, a new file will be created.
+#
+# @format: #optional the format of the new destination, default is to
+# probe if @mode is 'existing', else the format of the source
+#
+# @node-name: #optional the new block driver state node name in the graph
+# (Since 2.1)
+#
+# @replaces: #optional with sync=full graph node name to be replaced by the new
+# image when a whole image copy is done. This can be used to repair
+# broken Quorum files. (Since 2.1)
+#
+# @mode: #optional whether and how QEMU should create a new image, default is
+# 'absolute-paths'.
+#
+# @speed: #optional the maximum speed, in bytes per second
+#
+# @sync: what parts of the disk image should be copied to the destination
+# (all the disk, only the sectors allocated in the topmost image, or
+# only new I/O).
+#
+# @granularity: #optional granularity of the dirty bitmap, default is 64K
+# if the image format doesn't have clusters, 4K if the clusters
+# are smaller than that, else the cluster size. Must be a
+# power of 2 between 512 and 64M (since 1.4).
+#
+# @buf-size: #optional maximum amount of data in flight from source to
+# target (since 1.4).
+#
+# @on-source-error: #optional the action to take on an error on the source,
+# default 'report'. 'stop' and 'enospc' can only be used
+# if the block device supports io-status (see BlockInfo).
+#
+# @on-target-error: #optional the action to take on an error on the target,
+# default 'report' (no limitations, since this applies to
+# a different block device than @device).
+#
+# Returns: nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
+# Since 1.3
+##
+{ 'command': 'drive-mirror',
+ 'data': { 'device': 'str', 'target': 'str', '*format': 'str',
+ '*node-name': 'str', '*replaces': 'str',
+ 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
+ '*speed': 'int', '*granularity': 'uint32',
+ '*buf-size': 'int', '*on-source-error': 'BlockdevOnError',
+ '*on-target-error': 'BlockdevOnError' } }
+
+##
+# @block_set_io_throttle:
+#
+# Change I/O throttle limits for a block drive.
+#
+# @device: The name of the device
+#
+# @bps: total throughput limit in bytes per second
+#
+# @bps_rd: read throughput limit in bytes per second
+#
+# @bps_wr: write throughput limit in bytes per second
+#
+# @iops: total I/O operations per second
+#
+# @ops_rd: read I/O operations per second
+#
+# @iops_wr: write I/O operations per second
+#
+# @bps_max: #optional total max in bytes (Since 1.7)
+#
+# @bps_rd_max: #optional read max in bytes (Since 1.7)
+#
+# @bps_wr_max: #optional write max in bytes (Since 1.7)
+#
+# @iops_max: #optional total I/O operations max (Since 1.7)
+#
+# @iops_rd_max: #optional read I/O operations max (Since 1.7)
+#
+# @iops_wr_max: #optional write I/O operations max (Since 1.7)
+#
+# @iops_size: #optional an I/O size in bytes (Since 1.7)
+#
+# Returns: Nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
+# Since: 1.1
+##
+{ 'command': 'block_set_io_throttle',
+ 'data': { 'device': 'str', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int',
+ 'iops': 'int', 'iops_rd': 'int', 'iops_wr': 'int',
+ '*bps_max': 'int', '*bps_rd_max': 'int',
+ '*bps_wr_max': 'int', '*iops_max': 'int',
+ '*iops_rd_max': 'int', '*iops_wr_max': 'int',
+ '*iops_size': 'int' } }
+
+##
+# @block-stream:
+#
+# Copy data from a backing file into a block device.
+#
+# The block streaming operation is performed in the background until the entire
+# backing file has been copied. This command returns immediately once streaming
+# has started. The status of ongoing block streaming operations can be checked
+# with query-block-jobs. The operation can be stopped before it has completed
+# using the block-job-cancel command.
+#
+# If a base file is specified then sectors are not copied from that base file and
+# its backing chain. When streaming completes the image file will have the base
+# file as its backing file. This can be used to stream a subset of the backing
+# file chain instead of flattening the entire image.
+#
+# On successful completion the image file is updated to drop the backing file
+# and the BLOCK_JOB_COMPLETED event is emitted.
+#
+# @device: the device name
+#
+# @base: #optional the common backing file name
+#
+# @backing-file: #optional The backing file string to write into the active
+# layer. This filename is not validated.
+#
+# If a pathname string is such that it cannot be
+# resolved by QEMU, that means that subsequent QMP or
+# HMP commands must use node-names for the image in
+# question, as filename lookup methods will fail.
+#
+# If not specified, QEMU will automatically determine
+# the backing file string to use, or error out if there
+# is no obvious choice. Care should be taken when
+# specifying the string, to specify a valid filename or
+# protocol.
+# (Since 2.1)
+#
+# @speed: #optional the maximum speed, in bytes per second
+#
+# @on-error: #optional the action to take on an error (default report).
+# 'stop' and 'enospc' can only be used if the block device
+# supports io-status (see BlockInfo). Since 1.3.
+#
+# Returns: Nothing on success
+# If @device does not exist, DeviceNotFound
+#
+# Since: 1.1
+##
+{ 'command': 'block-stream',
+ 'data': { 'device': 'str', '*base': 'str', '*backing-file': 'str',
+ '*speed': 'int', '*on-error': 'BlockdevOnError' } }
+
+##
+# @block-job-set-speed:
+#
+# Set maximum speed for a background block operation.
+#
+# This command can only be issued when there is an active block job.
+#
+# Throttling can be disabled by setting the speed to 0.
+#
+# @device: the device name
+#
+# @speed: the maximum speed, in bytes per second, or 0 for unlimited.
+# Defaults to 0.
+#
+# Returns: Nothing on success
+# If no background operation is active on this device, DeviceNotActive
+#
+# Since: 1.1
+##
+{ 'command': 'block-job-set-speed',
+ 'data': { 'device': 'str', 'speed': 'int' } }
+
+##
+# @block-job-cancel:
+#
+# Stop an active background block operation.
+#
+# This command returns immediately after marking the active background block
+# operation for cancellation. It is an error to call this command if no
+# operation is in progress.
+#
+# The operation will cancel as soon as possible and then emit the
+# BLOCK_JOB_CANCELLED event. Before that happens the job is still visible when
+# enumerated using query-block-jobs.
+#
+# For streaming, the image file retains its backing file unless the streaming
+# operation happens to complete just as it is being cancelled. A new streaming
+# operation can be started at a later time to finish copying all data from the
+# backing file.
+#
+# @device: the device name
+#
+# @force: #optional whether to allow cancellation of a paused job (default
+# false). Since 1.3.
+#
+# Returns: Nothing on success
+# If no background operation is active on this device, DeviceNotActive
+#
+# Since: 1.1
+##
+{ 'command': 'block-job-cancel', 'data': { 'device': 'str', '*force': 'bool' } }
+
+##
+# @block-job-pause:
+#
+# Pause an active background block operation.
+#
+# This command returns immediately after marking the active background block
+# operation for pausing. It is an error to call this command if no
+# operation is in progress. Pausing an already paused job has no cumulative
+# effect; a single block-job-resume command will resume the job.
+#
+# The operation will pause as soon as possible. No event is emitted when
+# the operation is actually paused. Cancelling a paused job automatically
+# resumes it.
+#
+# @device: the device name
+#
+# Returns: Nothing on success
+# If no background operation is active on this device, DeviceNotActive
+#
+# Since: 1.3
+##
+{ 'command': 'block-job-pause', 'data': { 'device': 'str' } }
+
+##
+# @block-job-resume:
+#
+# Resume an active background block operation.
+#
+# This command returns immediately after resuming a paused background block
+# operation. It is an error to call this command if no operation is in
+# progress. Resuming an already running job is not an error.
+#
+# This command also clears the error status of the job.
+#
+# @device: the device name
+#
+# Returns: Nothing on success
+# If no background operation is active on this device, DeviceNotActive
+#
+# Since: 1.3
+##
+{ 'command': 'block-job-resume', 'data': { 'device': 'str' } }
+
+##
+# @block-job-complete:
+#
+# Manually trigger completion of an active background block operation. This
+# is supported for drive mirroring, where it also switches the device to
+# write to the target path only. The ability to complete is signaled with
+# a BLOCK_JOB_READY event.
+#
+# This command completes an active background block operation synchronously.
+# The ordering of this command's return with the BLOCK_JOB_COMPLETED event
+# is not defined. Note that if an I/O error occurs during the processing of
+# this command: 1) the command itself will fail; 2) the error will be processed
+# according to the rerror/werror arguments that were specified when starting
+# the operation.
+#
+# A cancelled or paused job cannot be completed.
+#
+# @device: the device name
+#
+# Returns: Nothing on success
+# If no background operation is active on this device, DeviceNotActive
+#
+# Since: 1.3
+##
+{ 'command': 'block-job-complete', 'data': { 'device': 'str' } }
+
+##
+# @BlockdevDiscardOptions
+#
+# Determines how to handle discard requests.
+#
+# @ignore: Ignore the request
+# @unmap: Forward as an unmap request
+#
+# Since: 1.7
+##
+{ 'enum': 'BlockdevDiscardOptions',
+ 'data': [ 'ignore', 'unmap' ] }
+
+##
+# @BlockdevDetectZeroesOptions
+#
+# Describes the operation mode for the automatic conversion of plain
+# zero writes by the OS to driver specific optimized zero write commands.
+#
+# @off: Disabled (default)
+# @on: Enabled
+# @unmap: Enabled and even try to unmap blocks if possible. This requires
+# also that @BlockdevDiscardOptions is set to unmap for this device.
+#
+# Since: 2.1
+##
+{ 'enum': 'BlockdevDetectZeroesOptions',
+ 'data': [ 'off', 'on', 'unmap' ] }
+
+##
+# @BlockdevAioOptions
+#
+# Selects the AIO backend to handle I/O requests
+#
+# @threads: Use qemu's thread pool
+# @native: Use native AIO backend (only Linux and Windows)
+#
+# Since: 1.7
+##
+{ 'enum': 'BlockdevAioOptions',
+ 'data': [ 'threads', 'native' ] }
+
+##
+# @BlockdevCacheOptions
+#
+# Includes cache-related options for block devices
+#
+# @writeback: #optional enables writeback mode for any caches (default: true)
+# @direct: #optional enables use of O_DIRECT (bypass the host page cache;
+# default: false)
+# @no-flush: #optional ignore any flush requests for the device (default:
+# false)
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevCacheOptions',
+ 'data': { '*writeback': 'bool',
+ '*direct': 'bool',
+ '*no-flush': 'bool' } }
+
+##
+# @BlockdevDriver
+#
+# Drivers that are supported in block device operations.
+#
+# @host_device, @host_cdrom, @host_floppy: Since 2.1
+#
+# Since: 2.0
+##
+{ 'enum': 'BlockdevDriver',
+ 'data': [ 'file', 'host_device', 'host_cdrom', 'host_floppy',
+ 'http', 'https', 'ftp', 'ftps', 'tftp', 'vvfat', 'blkdebug',
+ 'blkverify', 'bochs', 'cloop', 'cow', 'dmg', 'parallels', 'qcow',
+ 'qcow2', 'qed', 'raw', 'vdi', 'vhdx', 'vmdk', 'vpc', 'quorum' ] }
+
+##
+# @BlockdevOptionsBase
+#
+# Options that are available for all block devices, independent of the block
+# driver.
+#
+# @driver: block driver name
+# @id: #optional id by which the new block device can be referred to.
+# This is a required option on the top level of blockdev-add, and
+# currently not allowed on any other level.
+# @node-name: #optional the name of a block driver state node (Since 2.0)
+# @discard: #optional discard-related options (default: ignore)
+# @cache: #optional cache-related options
+# @aio: #optional AIO backend (default: threads)
+# @rerror: #optional how to handle read errors on the device
+# (default: report)
+# @werror: #optional how to handle write errors on the device
+# (default: enospc)
+# @read-only: #optional whether the block device should be read-only
+# (default: false)
+# @detect-zeroes: #optional detect and optimize zero writes (Since 2.1)
+# (default: off)
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsBase',
+ 'data': { 'driver': 'BlockdevDriver',
+ '*id': 'str',
+ '*node-name': 'str',
+ '*discard': 'BlockdevDiscardOptions',
+ '*cache': 'BlockdevCacheOptions',
+ '*aio': 'BlockdevAioOptions',
+ '*rerror': 'BlockdevOnError',
+ '*werror': 'BlockdevOnError',
+ '*read-only': 'bool',
+ '*detect-zeroes': 'BlockdevDetectZeroesOptions' } }
+
+##
+# @BlockdevOptionsFile
+#
+# Driver specific block device options for the file backend and similar
+# protocols.
+#
+# @filename: path to the image file
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsFile',
+ 'data': { 'filename': 'str' } }
+
+##
+# @BlockdevOptionsVVFAT
+#
+# Driver specific block device options for the vvfat protocol.
+#
+# @dir: directory to be exported as FAT image
+# @fat-type: #optional FAT type: 12, 16 or 32
+# @floppy: #optional whether to export a floppy image (true) or
+# partitioned hard disk (false; default)
+# @rw: #optional whether to allow write operations (default: false)
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsVVFAT',
+ 'data': { 'dir': 'str', '*fat-type': 'int', '*floppy': 'bool',
+ '*rw': 'bool' } }
+
+##
+# @BlockdevOptionsGenericFormat
+#
+# Driver specific block device options for image format that have no option
+# besides their data source.
+#
+# @file: reference to or definition of the data source block device
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsGenericFormat',
+ 'data': { 'file': 'BlockdevRef' } }
+
+##
+# @BlockdevOptionsGenericCOWFormat
+#
+# Driver specific block device options for image format that have no option
+# besides their data source and an optional backing file.
+#
+# @backing: #optional reference to or definition of the backing file block
+# device (if missing, taken from the image file content). It is
+# allowed to pass an empty string here in order to disable the
+# default backing file.
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsGenericCOWFormat',
+ 'base': 'BlockdevOptionsGenericFormat',
+ 'data': { '*backing': 'BlockdevRef' } }
+
+##
+# @BlockdevOptionsQcow2
+#
+# Driver specific block device options for qcow2.
+#
+# @lazy-refcounts: #optional whether to enable the lazy refcounts
+# feature (default is taken from the image file)
+#
+# @pass-discard-request: #optional whether discard requests to the qcow2
+# device should be forwarded to the data source
+#
+# @pass-discard-snapshot: #optional whether discard requests for the data source
+# should be issued when a snapshot operation (e.g.
+# deleting a snapshot) frees clusters in the qcow2 file
+#
+# @pass-discard-other: #optional whether discard requests for the data source
+# should be issued on other occasions where a cluster
+# gets freed
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevOptionsQcow2',
+ 'base': 'BlockdevOptionsGenericCOWFormat',
+ 'data': { '*lazy-refcounts': 'bool',
+ '*pass-discard-request': 'bool',
+ '*pass-discard-snapshot': 'bool',
+ '*pass-discard-other': 'bool' } }
+
+##
+# @BlkdebugEvent
+#
+# Trigger events supported by blkdebug.
+##
+{ 'enum': 'BlkdebugEvent',
+ 'data': [ 'l1_update', 'l1_grow.alloc_table', 'l1_grow.write_table',
+ 'l1_grow.activate_table', 'l2_load', 'l2_update',
+ 'l2_update_compressed', 'l2_alloc.cow_read', 'l2_alloc.write',
+ 'read_aio', 'read_backing_aio', 'read_compressed', 'write_aio',
+ 'write_compressed', 'vmstate_load', 'vmstate_save', 'cow_read',
+ 'cow_write', 'reftable_load', 'reftable_grow', 'reftable_update',
+ 'refblock_load', 'refblock_update', 'refblock_update_part',
+ 'refblock_alloc', 'refblock_alloc.hookup', 'refblock_alloc.write',
+ 'refblock_alloc.write_blocks', 'refblock_alloc.write_table',
+ 'refblock_alloc.switch_table', 'cluster_alloc',
+ 'cluster_alloc_bytes', 'cluster_free', 'flush_to_os',
+ 'flush_to_disk' ] }
+
+##
+# @BlkdebugInjectErrorOptions
+#
+# Describes a single error injection for blkdebug.
+#
+# @event: trigger event
+#
+# @state: #optional the state identifier blkdebug needs to be in to
+# actually trigger the event; defaults to "any"
+#
+# @errno: #optional error identifier (errno) to be returned; defaults to
+# EIO
+#
+# @sector: #optional specifies the sector index which has to be affected
+# in order to actually trigger the event; defaults to "any
+# sector"
+#
+# @once: #optional disables further events after this one has been
+# triggered; defaults to false
+#
+# @immediately: #optional fail immediately; defaults to false
+#
+# Since: 2.0
+##
+{ 'type': 'BlkdebugInjectErrorOptions',
+ 'data': { 'event': 'BlkdebugEvent',
+ '*state': 'int',
+ '*errno': 'int',
+ '*sector': 'int',
+ '*once': 'bool',
+ '*immediately': 'bool' } }
+
+##
+# @BlkdebugSetStateOptions
+#
+# Describes a single state-change event for blkdebug.
+#
+# @event: trigger event
+#
+# @state: #optional the current state identifier blkdebug needs to be in;
+# defaults to "any"
+#
+# @new_state: the state identifier blkdebug is supposed to assume if
+# this event is triggered
+#
+# Since: 2.0
+##
+{ 'type': 'BlkdebugSetStateOptions',
+ 'data': { 'event': 'BlkdebugEvent',
+ '*state': 'int',
+ 'new_state': 'int' } }
+
+##
+# @BlockdevOptionsBlkdebug
+#
+# Driver specific block device options for blkdebug.
+#
+# @image: underlying raw block device (or image file)
+#
+# @config: #optional filename of the configuration file
+#
+# @align: #optional required alignment for requests in bytes
+#
+# @inject-error: #optional array of error injection descriptions
+#
+# @set-state: #optional array of state-change descriptions
+#
+# Since: 2.0
+##
+{ 'type': 'BlockdevOptionsBlkdebug',
+ 'data': { 'image': 'BlockdevRef',
+ '*config': 'str',
+ '*align': 'int',
+ '*inject-error': ['BlkdebugInjectErrorOptions'],
+ '*set-state': ['BlkdebugSetStateOptions'] } }
+
+##
+# @BlockdevOptionsBlkverify
+#
+# Driver specific block device options for blkverify.
+#
+# @test: block device to be tested
+#
+# @raw: raw image used for verification
+#
+# Since: 2.0
+##
+{ 'type': 'BlockdevOptionsBlkverify',
+ 'data': { 'test': 'BlockdevRef',
+ 'raw': 'BlockdevRef' } }
+
+##
+# @BlockdevOptionsQuorum
+#
+# Driver specific block device options for Quorum
+#
+# @blkverify: #optional true if the driver must print content mismatch
+# set to false by default
+#
+# @children: the children block devices to use
+#
+# @vote-threshold: the vote limit under which a read will fail
+#
+# @rewrite-corrupted: #optional rewrite corrupted data when quorum is reached
+# (Since 2.1)
+#
+# Since: 2.0
+##
+{ 'type': 'BlockdevOptionsQuorum',
+ 'data': { '*blkverify': 'bool',
+ 'children': [ 'BlockdevRef' ],
+ 'vote-threshold': 'int', '*rewrite-corrupted': 'bool' } }
+
+##
+# @BlockdevOptions
+#
+# Options for creating a block device.
+#
+# Since: 1.7
+##
+{ 'union': 'BlockdevOptions',
+ 'base': 'BlockdevOptionsBase',
+ 'discriminator': 'driver',
+ 'data': {
+ 'file': 'BlockdevOptionsFile',
+ 'host_device':'BlockdevOptionsFile',
+ 'host_cdrom': 'BlockdevOptionsFile',
+ 'host_floppy':'BlockdevOptionsFile',
+ 'http': 'BlockdevOptionsFile',
+ 'https': 'BlockdevOptionsFile',
+ 'ftp': 'BlockdevOptionsFile',
+ 'ftps': 'BlockdevOptionsFile',
+ 'tftp': 'BlockdevOptionsFile',
+# TODO gluster: Wait for structured options
+# TODO iscsi: Wait for structured options
+# TODO nbd: Should take InetSocketAddress for 'host'?
+# TODO nfs: Wait for structured options
+# TODO rbd: Wait for structured options
+# TODO sheepdog: Wait for structured options
+# TODO ssh: Should take InetSocketAddress for 'host'?
+ 'vvfat': 'BlockdevOptionsVVFAT',
+ 'blkdebug': 'BlockdevOptionsBlkdebug',
+ 'blkverify': 'BlockdevOptionsBlkverify',
+ 'bochs': 'BlockdevOptionsGenericFormat',
+ 'cloop': 'BlockdevOptionsGenericFormat',
+ 'cow': 'BlockdevOptionsGenericCOWFormat',
+ 'dmg': 'BlockdevOptionsGenericFormat',
+ 'parallels': 'BlockdevOptionsGenericFormat',
+ 'qcow': 'BlockdevOptionsGenericCOWFormat',
+ 'qcow2': 'BlockdevOptionsQcow2',
+ 'qed': 'BlockdevOptionsGenericCOWFormat',
+ 'raw': 'BlockdevOptionsGenericFormat',
+ 'vdi': 'BlockdevOptionsGenericFormat',
+ 'vhdx': 'BlockdevOptionsGenericFormat',
+ 'vmdk': 'BlockdevOptionsGenericCOWFormat',
+ 'vpc': 'BlockdevOptionsGenericFormat',
+ 'quorum': 'BlockdevOptionsQuorum'
+ } }
+
+##
+# @BlockdevRef
+#
+# Reference to a block device.
+#
+# @definition: defines a new block device inline
+# @reference: references the ID of an existing block device. An
+# empty string means that no block device should be
+# referenced.
+#
+# Since: 1.7
+##
+{ 'union': 'BlockdevRef',
+ 'discriminator': {},
+ 'data': { 'definition': 'BlockdevOptions',
+ 'reference': 'str' } }
+
+##
+# @blockdev-add:
+#
+# Creates a new block device.
+#
+# @options: block device options for the new device
+#
+# Since: 1.7
+##
+{ 'command': 'blockdev-add', 'data': { 'options': 'BlockdevOptions' } }
+
+
+##
+# @BlockErrorAction
+#
+# An enumeration of action that has been taken when a DISK I/O occurs
+#
+# @ignore: error has been ignored
+#
+# @report: error has been reported to the device
+#
+# @stop: error caused VM to be stopped
+#
+# Since: 2.1
+##
+{ 'enum': 'BlockErrorAction',
+ 'data': [ 'ignore', 'report', 'stop' ] }
+
+
+##
+# @BLOCK_IMAGE_CORRUPTED
+#
+# Emitted when a disk image is being marked corrupt
+#
+# @device: device name
+#
+# @msg: informative message for human consumption, such as the kind of
+# corruption being detected. It should not be parsed by machine as it is
+# not guaranteed to be stable
+#
+# @offset: #optional, if the corruption resulted from an image access, this is
+# the access offset into the image
+#
+# @size: #optional, if the corruption resulted from an image access, this is
+# the access size
+#
+# Since: 1.7
+##
+{ 'event': 'BLOCK_IMAGE_CORRUPTED',
+ 'data': { 'device' : 'str',
+ 'msg' : 'str',
+ '*offset': 'int',
+ '*size' : 'int' } }
+
+##
+# @BLOCK_IO_ERROR
+#
+# Emitted when a disk I/O error occurs
+#
+# @device: device name
+#
+# @operation: I/O operation
+#
+# @action: action that has been taken
+#
+# Note: If action is "stop", a STOP event will eventually follow the
+# BLOCK_IO_ERROR event
+#
+# Since: 0.13.0
+##
+{ 'event': 'BLOCK_IO_ERROR',
+ 'data': { 'device': 'str', 'operation': 'IoOperationType',
+ 'action': 'BlockErrorAction' } }
+
+##
+# @BLOCK_JOB_COMPLETED
+#
+# Emitted when a block job has completed
+#
+# @type: job type
+#
+# @device: device name
+#
+# @len: maximum progress value
+#
+# @offset: current progress value. On success this is equal to len.
+# On failure this is less than len
+#
+# @speed: rate limit, bytes per second
+#
+# @error: #optional, error message. 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
+#
+# Since: 1.1
+##
+{ 'event': 'BLOCK_JOB_COMPLETED',
+ 'data': { 'type' : 'BlockJobType',
+ 'device': 'str',
+ 'len' : 'int',
+ 'offset': 'int',
+ 'speed' : 'int',
+ '*error': 'str' } }
+
+##
+# @BLOCK_JOB_CANCELLED
+#
+# Emitted when a block job has been cancelled
+#
+# @type: job type
+#
+# @device: device name
+#
+# @len: maximum progress value
+#
+# @offset: current progress value. On success this is equal to len.
+# On failure this is less than len
+#
+# @speed: rate limit, bytes per second
+#
+# Since: 1.1
+##
+{ 'event': 'BLOCK_JOB_CANCELLED',
+ 'data': { 'type' : 'BlockJobType',
+ 'device': 'str',
+ 'len' : 'int',
+ 'offset': 'int',
+ 'speed' : 'int' } }
+
+##
+# @BLOCK_JOB_ERROR
+#
+# Emitted when a block job encounters an error
+#
+# @device: device name
+#
+# @operation: I/O operation
+#
+# @action: action that has been taken
+#
+# Since: 1.3
+##
+{ 'event': 'BLOCK_JOB_ERROR',
+ 'data': { 'device' : 'str',
+ 'operation': 'IoOperationType',
+ 'action' : 'BlockErrorAction' } }
+
+##
+# @BLOCK_JOB_READY
+#
+# Emitted when a block job is ready to complete
+#
+# @type: job type
+#
+# @device: device name
+#
+# @len: maximum progress value
+#
+# @offset: current progress value. On success this is equal to len.
+# On failure this is less than len
+#
+# @speed: rate limit, bytes per second
+#
+# Note: The "ready to complete" status is always reset by a @BLOCK_JOB_ERROR
+# event
+#
+# Since: 1.3
+##
+{ 'event': 'BLOCK_JOB_READY',
+ 'data': { 'type' : 'BlockJobType',
+ 'device': 'str',
+ 'len' : 'int',
+ 'offset': 'int',
+ 'speed' : 'int' } }
diff --git a/qapi/block.json b/qapi/block.json
new file mode 100644
index 000000000..e3134657b
--- /dev/null
+++ b/qapi/block.json
@@ -0,0 +1,180 @@
+# -*- Mode: Python -*-
+#
+# QAPI block definitions (vm related)
+
+# QAPI block core definitions
+{ 'include': 'block-core.json' }
+
+##
+# BiosAtaTranslation:
+#
+# Policy that BIOS should use to interpret cylinder/head/sector
+# addresses. Note that Bochs BIOS and SeaBIOS will not actually
+# translate logical CHS to physical; instead, they will use logical
+# block addressing.
+#
+# @auto: If cylinder/heads/sizes are passed, choose between none and LBA
+# depending on the size of the disk. If they are not passed,
+# choose none if QEMU can guess that the disk had 16 or fewer
+# heads, large if QEMU can guess that the disk had 131072 or
+# fewer tracks across all heads (i.e. cylinders*heads<131072),
+# otherwise LBA.
+#
+# @none: The physical disk geometry is equal to the logical geometry.
+#
+# @lba: Assume 63 sectors per track and one of 16, 32, 64, 128 or 255
+# heads (if fewer than 255 are enough to cover the whole disk
+# with 1024 cylinders/head). The number of cylinders/head is
+# then computed based on the number of sectors and heads.
+#
+# @large: The number of cylinders per head is scaled down to 1024
+# by correspondingly scaling up the number of heads.
+#
+# @rechs: Same as @large, but first convert a 16-head geometry to
+# 15-head, by proportionally scaling up the number of
+# cylinders/head.
+#
+# Since: 2.0
+##
+{ 'enum': 'BiosAtaTranslation',
+ 'data': ['auto', 'none', 'lba', 'large', 'rechs']}
+
+##
+# @BlockdevSnapshotInternal
+#
+# @device: the name of the device to generate the snapshot from
+#
+# @name: the name of the internal snapshot to be created
+#
+# Notes: In transaction, if @name is empty, or any snapshot matching @name
+# exists, the operation will fail. Only some image formats support it,
+# for example, qcow2, rbd, and sheepdog.
+#
+# Since: 1.7
+##
+{ 'type': 'BlockdevSnapshotInternal',
+ 'data': { 'device': 'str', 'name': 'str' } }
+
+##
+# @blockdev-snapshot-internal-sync
+#
+# Synchronously take an internal snapshot of a block device, when the format
+# of the image used supports it.
+#
+# For the arguments, see the documentation of BlockdevSnapshotInternal.
+#
+# Returns: nothing on success
+# If @device is not a valid block device, DeviceNotFound
+# If any snapshot matching @name exists, or @name is empty,
+# GenericError
+# If the format of the image used does not support it,
+# BlockFormatFeatureNotSupported
+#
+# Since 1.7
+##
+{ 'command': 'blockdev-snapshot-internal-sync',
+ 'data': 'BlockdevSnapshotInternal' }
+
+##
+# @blockdev-snapshot-delete-internal-sync
+#
+# Synchronously delete an internal snapshot of a block device, when the format
+# of the image used support it. The snapshot is identified by name or id or
+# both. One of the name or id is required. Return SnapshotInfo for the
+# successfully deleted snapshot.
+#
+# @device: the name of the device to delete the snapshot from
+#
+# @id: optional the snapshot's ID to be deleted
+#
+# @name: optional the snapshot's name to be deleted
+#
+# Returns: SnapshotInfo on success
+# If @device is not a valid block device, DeviceNotFound
+# If snapshot not found, GenericError
+# If the format of the image used does not support it,
+# BlockFormatFeatureNotSupported
+# If @id and @name are both not specified, GenericError
+#
+# Since 1.7
+##
+{ 'command': 'blockdev-snapshot-delete-internal-sync',
+ 'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
+ 'returns': 'SnapshotInfo' }
+
+##
+# @eject:
+#
+# Ejects a device from a removable drive.
+#
+# @device: The name of the device
+#
+# @force: @optional If true, eject regardless of whether the drive is locked.
+# If not specified, the default value is false.
+#
+# Returns: Nothing on success
+# If @device is not a valid block device, DeviceNotFound
+#
+# Notes: Ejecting a device will no media results in success
+#
+# Since: 0.14.0
+##
+{ 'command': 'eject', 'data': {'device': 'str', '*force': 'bool'} }
+
+##
+# @nbd-server-start:
+#
+# Start an NBD server listening on the given host and port. Block
+# devices can then be exported using @nbd-server-add. The NBD
+# server will present them as named exports; for example, another
+# QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME".
+#
+# @addr: Address on which to listen.
+#
+# Returns: error if the server is already running.
+#
+# Since: 1.3.0
+##
+{ 'command': 'nbd-server-start',
+ 'data': { 'addr': 'SocketAddress' } }
+
+##
+# @nbd-server-add:
+#
+# Export a device to QEMU's embedded NBD server.
+#
+# @device: Block device to be exported
+#
+# @writable: Whether clients should be able to write to the device via the
+# NBD connection (default false). #optional
+#
+# Returns: error if the device is already marked for export.
+#
+# Since: 1.3.0
+##
+{ 'command': 'nbd-server-add', 'data': {'device': 'str', '*writable': 'bool'} }
+
+##
+# @nbd-server-stop:
+#
+# Stop QEMU's embedded NBD server, and unregister all devices previously
+# added via @nbd-server-add.
+#
+# Since: 1.3.0
+##
+{ 'command': 'nbd-server-stop' }
+
+##
+# @DEVICE_TRAY_MOVED
+#
+# Emitted whenever the tray of a removable device is moved by the guest or by
+# HMP/QMP commands
+#
+# @device: device name
+#
+# @tray-open: true if the tray has been opened or false if it has been closed
+#
+# Since: 1.1
+##
+{ 'event': 'DEVICE_TRAY_MOVED',
+ 'data': { 'device': 'str', 'tray-open': 'bool' } }
diff --git a/qapi/common.json b/qapi/common.json
new file mode 100644
index 000000000..4e9a21f2f
--- /dev/null
+++ b/qapi/common.json
@@ -0,0 +1,89 @@
+# -*- Mode: Python -*-
+#
+# QAPI common definitions
+
+##
+# @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
+#
+# Since: 1.2
+##
+{ 'enum': 'ErrorClass',
+ 'data': [ 'GenericError', 'CommandNotFound', 'DeviceEncrypted',
+ 'DeviceNotActive', 'DeviceNotFound', 'KVMMissingCap' ] }
+
+##
+# @VersionInfo:
+#
+# A description of QEMU's version.
+#
+# @qemu.major: The major version of QEMU
+#
+# @qemu.minor: The minor version of QEMU
+#
+# @qemu.micro: The micro version of QEMU. By current convention, a micro
+# version of 50 signifies a development branch. A micro version
+# greater than or equal to 90 signifies a release candidate for
+# the next minor version. A micro version of less than 50
+# signifies a stable release.
+#
+# @package: QEMU will always set this field to an empty string. Downstream
+# versions of QEMU should set this to a non-empty string. The
+# exact format depends on the downstream however it highly
+# recommended that a unique name is used.
+#
+# Since: 0.14.0
+##
+{ 'type': 'VersionInfo',
+ 'data': {'qemu': {'major': 'int', 'minor': 'int', 'micro': 'int'},
+ 'package': 'str'} }
+
+##
+# @query-version:
+#
+# Returns the current version of QEMU.
+#
+# Returns: A @VersionInfo object describing the current version of QEMU.
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-version', 'returns': 'VersionInfo' }
+
+##
+# @CommandInfo:
+#
+# Information about a QMP command
+#
+# @name: The command name
+#
+# Since: 0.14.0
+##
+{ 'type': 'CommandInfo', 'data': {'name': 'str'} }
+
+##
+# @query-commands:
+#
+# Return a list of supported QMP commands by this server
+#
+# Returns: A list of @CommandInfo for all supported commands
+#
+# Since: 0.14.0
+##
+{ 'command': 'query-commands', 'returns': ['CommandInfo'] }
+
diff --git a/qapi/event.json b/qapi/event.json
new file mode 100644
index 000000000..c51dc491e
--- /dev/null
+++ b/qapi/event.json
@@ -0,0 +1,332 @@
+##
+# @SHUTDOWN
+#
+# Emitted when the virtual machine has shut down, indicating that qemu is
+# about to exit.
+#
+# Note: If the command-line option "-no-shutdown" has been specified, qemu will
+# not exit, and a STOP event will eventually follow the SHUTDOWN event
+#
+# Since: 0.12.0
+##
+{ 'event': 'SHUTDOWN' }
+
+##
+# @POWERDOWN
+#
+# Emitted when the virtual machine is powered down through the power control
+# system, such as via ACPI.
+#
+# Since: 0.12.0
+##
+{ 'event': 'POWERDOWN' }
+
+##
+# @RESET
+#
+# Emitted when the virtual machine is reset
+#
+# Since: 0.12.0
+##
+{ 'event': 'RESET' }
+
+##
+# @STOP
+#
+# Emitted when the virtual machine is stopped
+#
+# Since: 0.12.0
+##
+{ 'event': 'STOP' }
+
+##
+# @RESUME
+#
+# Emitted when the virtual machine resumes execution
+#
+# Since: 0.12.0
+##
+{ 'event': 'RESUME' }
+
+##
+# @SUSPEND
+#
+# Emitted when guest enters a hardware suspension state, for example, S3 state,
+# which is sometimes called standby state
+#
+# Since: 1.1
+##
+{ 'event': 'SUSPEND' }
+
+##
+# @SUSPEND_DISK
+#
+# Emitted when guest enters a hardware suspension state with data saved on
+# disk, for example, S4 state, which is sometimes called hibernate state
+#
+# Note: QEMU shuts down (similar to event @SHUTDOWN) when entering this state
+#
+# Since: 1.2
+##
+{ 'event': 'SUSPEND_DISK' }
+
+##
+# @WAKEUP
+#
+# Emitted when the guest has woken up from suspend state and is running
+#
+# Since: 1.1
+##
+{ 'event': 'WAKEUP' }
+
+##
+# @RTC_CHANGE
+#
+# Emitted when the guest changes the RTC time.
+#
+# @offset: offset between base RTC clock (as specified by -rtc base), and
+# new RTC clock value
+#
+# Since: 0.13.0
+##
+{ 'event': 'RTC_CHANGE',
+ 'data': { 'offset': 'int' } }
+
+##
+# @WATCHDOG
+#
+# Emitted when the watchdog device's timer is expired
+#
+# @action: action that has been taken
+#
+# Note: If action is "reset", "shutdown", or "pause" the WATCHDOG event is
+# followed respectively by the RESET, SHUTDOWN, or STOP events
+#
+# Since: 0.13.0
+##
+{ 'event': 'WATCHDOG',
+ 'data': { 'action': 'WatchdogExpirationAction' } }
+
+##
+# @DEVICE_DELETED
+#
+# Emitted whenever the device removal completion is acknowledged by the guest.
+# At this point, it's safe to reuse the specified device ID. Device removal can
+# be initiated by the guest or by HMP/QMP commands.
+#
+# @device: #optional, device name
+#
+# @path: device path
+#
+# Since: 1.5
+##
+{ 'event': 'DEVICE_DELETED',
+ 'data': { '*device': 'str', 'path': 'str' } }
+
+##
+# @NIC_RX_FILTER_CHANGED
+#
+# Emitted once until the 'query-rx-filter' command is executed, the first event
+# will always be emitted
+#
+# @name: #optional, net client name
+#
+# @path: device path
+#
+# Since: 1.6
+##
+{ 'event': 'NIC_RX_FILTER_CHANGED',
+ 'data': { '*name': 'str', 'path': 'str' } }
+
+##
+# @VNC_CONNECTED
+#
+# Emitted when a VNC client establishes a connection
+#
+# @server: server information
+#
+# @client: client information
+#
+# Note: This event is emitted before any authentication takes place, thus
+# the authentication ID is not provided
+#
+# Since: 0.13.0
+##
+{ 'event': 'VNC_CONNECTED',
+ 'data': { 'server': 'VncServerInfo',
+ 'client': 'VncBasicInfo' } }
+
+##
+# @VNC_INITIALIZED
+#
+# Emitted after authentication takes place (if any) and the VNC session is
+# made active
+#
+# @server: server information
+#
+# @client: client information
+#
+# Since: 0.13.0
+##
+{ 'event': 'VNC_INITIALIZED',
+ 'data': { 'server': 'VncServerInfo',
+ 'client': 'VncClientInfo' } }
+
+##
+# @VNC_DISCONNECTED
+#
+# Emitted when the connection is closed
+#
+# @server: server information
+#
+# @client: client information
+#
+# Since: 0.13.0
+##
+{ 'event': 'VNC_DISCONNECTED',
+ 'data': { 'server': 'VncServerInfo',
+ 'client': 'VncClientInfo' } }
+
+##
+# @SPICE_CONNECTED
+#
+# Emitted when a SPICE client establishes a connection
+#
+# @server: server information
+#
+# @client: client information
+#
+# Since: 0.14.0
+##
+{ 'event': 'SPICE_CONNECTED',
+ 'data': { 'server': 'SpiceBasicInfo',
+ 'client': 'SpiceBasicInfo' } }
+
+##
+# @SPICE_INITIALIZED
+#
+# Emitted after initial handshake and authentication takes place (if any)
+# and the SPICE channel is up and running
+#
+# @server: server information
+#
+# @client: client information
+#
+# Since: 0.14.0
+##
+{ 'event': 'SPICE_INITIALIZED',
+ 'data': { 'server': 'SpiceServerInfo',
+ 'client': 'SpiceChannel' } }
+
+##
+# @SPICE_DISCONNECTED
+#
+# Emitted when the SPICE connection is closed
+#
+# @server: server information
+#
+# @client: client information
+#
+# Since: 0.14.0
+##
+{ 'event': 'SPICE_DISCONNECTED',
+ 'data': { 'server': 'SpiceBasicInfo',
+ 'client': 'SpiceBasicInfo' } }
+
+##
+# @SPICE_MIGRATE_COMPLETED
+#
+# Emitted when SPICE migration has completed
+#
+# Since: 1.3
+##
+{ 'event': 'SPICE_MIGRATE_COMPLETED' }
+
+##
+# @ACPI_DEVICE_OST
+#
+# Emitted when guest executes ACPI _OST method.
+#
+# Since: 2.1
+#
+# @info: ACPIOSTInfo type as described in qapi-schema.json
+##
+{ 'event': 'ACPI_DEVICE_OST',
+ 'data': { 'info': 'ACPIOSTInfo' } }
+
+##
+# @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
+#
+# @actual: actual level of the guest memory balloon in bytes
+#
+# Since: 1.2
+##
+{ 'event': 'BALLOON_CHANGE',
+ 'data': { 'actual': 'int' } }
+
+##
+# @GUEST_PANICKED
+#
+# Emitted when guest OS panic is detected
+#
+# @action: action that has been taken, currently always "pause"
+#
+# Since: 1.5
+##
+{ 'event': 'GUEST_PANICKED',
+ 'data': { 'action': 'GuestPanicAction' } }
+
+##
+# @QUORUM_FAILURE
+#
+# Emitted by the Quorum block driver if it fails to establish a quorum
+#
+# @reference: device name if defined else node name
+#
+# @sector-num: number of the first sector of the failed read operation
+#
+# @sectors-count: failed read operation sector count
+#
+# Since: 2.0
+##
+{ 'event': 'QUORUM_FAILURE',
+ 'data': { 'reference': 'str', 'sector-num': 'int', 'sectors-count': 'int' } }
+
+##
+# @QUORUM_REPORT_BAD
+#
+# Emitted to report a corruption of a Quorum file
+#
+# @error: #optional, error message. Only present on failure. This field
+# contains a human-readable error message. There are no semantics other
+# than that the block layer reported an error and clients should not
+# try to interpret the error string.
+#
+# @node-name: the graph node name of the block driver state
+#
+# @sector-num: number of the first sector of the failed read operation
+#
+# @sectors-count: failed read operation sector count
+#
+# Since: 2.0
+##
+{ 'event': 'QUORUM_REPORT_BAD',
+ 'data': { '*error': 'str', 'node-name': 'str',
+ 'sector-num': 'int', 'sectors-count': 'int' } }
+
+##
+# @VSERPORT_CHANGE
+#
+# Emitted when the guest opens or closes a virtio-serial port.
+#
+# @id: device identifier of the virtio-serial port
+#
+# @open: true if the guest has opened the virtio-serial port
+#
+# Since: 2.1
+##
+{ 'event': 'VSERPORT_CHANGE',
+ 'data': { 'id': 'str', 'open': 'bool' } }
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 5d830a2b5..f2ad6d729 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -143,8 +143,8 @@ opts_start_struct(Visitor *v, void **obj, const char *kind,
if (ov->opts_root->id != NULL) {
ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt);
- ov->fake_id_opt->name = "id";
- ov->fake_id_opt->str = ov->opts_root->id;
+ ov->fake_id_opt->name = g_strdup("id");
+ ov->fake_id_opt->str = g_strdup(ov->opts_root->id);
opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt);
}
}
@@ -177,7 +177,11 @@ opts_end_struct(Visitor *v, Error **errp)
}
g_hash_table_destroy(ov->unprocessed_opts);
ov->unprocessed_opts = NULL;
- g_free(ov->fake_id_opt);
+ if (ov->fake_id_opt) {
+ g_free(ov->fake_id_opt->name);
+ g_free(ov->fake_id_opt->str);
+ g_free(ov->fake_id_opt);
+ }
ov->fake_id_opt = NULL;
}
@@ -472,19 +476,19 @@ opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
val = strtosz_suffix(opt->str ? opt->str : "", &endptr,
STRTOSZ_DEFSUFFIX_B);
- if (val != -1 && *endptr == '\0') {
- *obj = val;
- processed(ov, name);
+ if (val < 0 || *endptr) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
+ "a size value representible as a non-negative int64");
return;
}
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name,
- "a size value representible as a non-negative int64");
+
+ *obj = val;
+ processed(ov, name);
}
static void
-opts_start_optional(Visitor *v, bool *present, const char *name,
- Error **errp)
+opts_optional(Visitor *v, bool *present, const char *name, Error **errp)
{
OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v);
@@ -527,7 +531,7 @@ opts_visitor_new(const QemuOpts *opts)
/* type_number() is not filled in, but this is not the first visitor to
* skip some mandatory methods... */
- ov->visitor.start_optional = &opts_start_optional;
+ ov->visitor.optional = &opts_optional;
ov->opts_root = opts;
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index d0ea118fe..dc53545fa 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -131,7 +131,9 @@ static void qapi_dealloc_end_list(Visitor *v, Error **errp)
static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name,
Error **errp)
{
- g_free(*obj);
+ if (obj) {
+ g_free(*obj);
+ }
}
static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name,
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 6451a21a2..55f8d4068 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -17,46 +17,27 @@
#include "qapi/visitor.h"
#include "qapi/visitor-impl.h"
-void visit_start_handle(Visitor *v, void **obj, const char *kind,
- const char *name, Error **errp)
-{
- if (!error_is_set(errp) && v->start_handle) {
- v->start_handle(v, obj, kind, name, errp);
- }
-}
-
-void visit_end_handle(Visitor *v, Error **errp)
-{
- if (!error_is_set(errp) && v->end_handle) {
- v->end_handle(v, errp);
- }
-}
-
void visit_start_struct(Visitor *v, void **obj, const char *kind,
const char *name, size_t size, Error **errp)
{
- if (!error_is_set(errp)) {
- v->start_struct(v, obj, kind, name, size, errp);
- }
+ v->start_struct(v, obj, kind, name, size, errp);
}
void visit_end_struct(Visitor *v, Error **errp)
{
- assert(!error_is_set(errp));
v->end_struct(v, errp);
}
void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
Error **errp)
{
- if (!error_is_set(errp) && v->start_implicit_struct) {
+ if (v->start_implicit_struct) {
v->start_implicit_struct(v, obj, size, errp);
}
}
void visit_end_implicit_struct(Visitor *v, Error **errp)
{
- assert(!error_is_set(errp));
if (v->end_implicit_struct) {
v->end_implicit_struct(v, errp);
}
@@ -64,45 +45,31 @@ void visit_end_implicit_struct(Visitor *v, Error **errp)
void visit_start_list(Visitor *v, const char *name, Error **errp)
{
- if (!error_is_set(errp)) {
- v->start_list(v, name, errp);
- }
+ v->start_list(v, name, errp);
}
GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp)
{
- if (!error_is_set(errp)) {
- return v->next_list(v, list, errp);
- }
-
- return 0;
+ return v->next_list(v, list, errp);
}
void visit_end_list(Visitor *v, Error **errp)
{
- assert(!error_is_set(errp));
v->end_list(v, errp);
}
-void visit_start_optional(Visitor *v, bool *present, const char *name,
- Error **errp)
+void visit_optional(Visitor *v, bool *present, const char *name,
+ Error **errp)
{
- if (!error_is_set(errp) && v->start_optional) {
- v->start_optional(v, present, name, errp);
- }
-}
-
-void visit_end_optional(Visitor *v, Error **errp)
-{
- if (!error_is_set(errp) && v->end_optional) {
- v->end_optional(v, errp);
+ if (v->optional) {
+ v->optional(v, present, name, errp);
}
}
void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
const char *name, Error **errp)
{
- if (!error_is_set(errp) && v->get_next_type) {
+ if (v->get_next_type) {
v->get_next_type(v, obj, qtypes, name, errp);
}
}
@@ -110,192 +77,172 @@ void visit_get_next_type(Visitor *v, int *obj, const int *qtypes,
void visit_type_enum(Visitor *v, int *obj, const char *strings[],
const char *kind, const char *name, Error **errp)
{
- if (!error_is_set(errp)) {
- v->type_enum(v, obj, strings, kind, name, errp);
- }
+ v->type_enum(v, obj, strings, kind, name, errp);
}
void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp)
{
- if (!error_is_set(errp)) {
- v->type_int(v, obj, name, errp);
- }
+ v->type_int(v, obj, name, errp);
}
void visit_type_uint8(Visitor *v, uint8_t *obj, const char *name, Error **errp)
{
int64_t value;
- if (!error_is_set(errp)) {
- if (v->type_uint8) {
- v->type_uint8(v, obj, name, errp);
- } else {
- value = *obj;
- v->type_int(v, &value, name, errp);
- if (value < 0 || value > UINT8_MAX) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
- "uint8_t");
- return;
- }
- *obj = value;
+
+ if (v->type_uint8) {
+ v->type_uint8(v, obj, name, errp);
+ } else {
+ value = *obj;
+ v->type_int(v, &value, name, errp);
+ if (value < 0 || value > UINT8_MAX) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+ "uint8_t");
+ return;
}
+ *obj = value;
}
}
void visit_type_uint16(Visitor *v, uint16_t *obj, const char *name, Error **errp)
{
int64_t value;
- if (!error_is_set(errp)) {
- if (v->type_uint16) {
- v->type_uint16(v, obj, name, errp);
- } else {
- value = *obj;
- v->type_int(v, &value, name, errp);
- if (value < 0 || value > UINT16_MAX) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
- "uint16_t");
- return;
- }
- *obj = value;
+
+ if (v->type_uint16) {
+ v->type_uint16(v, obj, name, errp);
+ } else {
+ value = *obj;
+ v->type_int(v, &value, name, errp);
+ if (value < 0 || value > UINT16_MAX) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+ "uint16_t");
+ return;
}
+ *obj = value;
}
}
void visit_type_uint32(Visitor *v, uint32_t *obj, const char *name, Error **errp)
{
int64_t value;
- if (!error_is_set(errp)) {
- if (v->type_uint32) {
- v->type_uint32(v, obj, name, errp);
- } else {
- value = *obj;
- v->type_int(v, &value, name, errp);
- if (value < 0 || value > UINT32_MAX) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
- "uint32_t");
- return;
- }
- *obj = value;
+
+ if (v->type_uint32) {
+ v->type_uint32(v, obj, name, errp);
+ } else {
+ value = *obj;
+ v->type_int(v, &value, name, errp);
+ if (value < 0 || value > UINT32_MAX) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+ "uint32_t");
+ return;
}
+ *obj = value;
}
}
void visit_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp)
{
int64_t value;
- if (!error_is_set(errp)) {
- if (v->type_uint64) {
- v->type_uint64(v, obj, name, errp);
- } else {
- value = *obj;
- v->type_int(v, &value, name, errp);
- *obj = value;
- }
+
+ if (v->type_uint64) {
+ v->type_uint64(v, obj, name, errp);
+ } else {
+ value = *obj;
+ v->type_int(v, &value, name, errp);
+ *obj = value;
}
}
void visit_type_int8(Visitor *v, int8_t *obj, const char *name, Error **errp)
{
int64_t value;
- if (!error_is_set(errp)) {
- if (v->type_int8) {
- v->type_int8(v, obj, name, errp);
- } else {
- value = *obj;
- v->type_int(v, &value, name, errp);
- if (value < INT8_MIN || value > INT8_MAX) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
- "int8_t");
- return;
- }
- *obj = value;
+
+ if (v->type_int8) {
+ v->type_int8(v, obj, name, errp);
+ } else {
+ value = *obj;
+ v->type_int(v, &value, name, errp);
+ if (value < INT8_MIN || value > INT8_MAX) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+ "int8_t");
+ return;
}
+ *obj = value;
}
}
void visit_type_int16(Visitor *v, int16_t *obj, const char *name, Error **errp)
{
int64_t value;
- if (!error_is_set(errp)) {
- if (v->type_int16) {
- v->type_int16(v, obj, name, errp);
- } else {
- value = *obj;
- v->type_int(v, &value, name, errp);
- if (value < INT16_MIN || value > INT16_MAX) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
- "int16_t");
- return;
- }
- *obj = value;
+
+ if (v->type_int16) {
+ v->type_int16(v, obj, name, errp);
+ } else {
+ value = *obj;
+ v->type_int(v, &value, name, errp);
+ if (value < INT16_MIN || value > INT16_MAX) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+ "int16_t");
+ return;
}
+ *obj = value;
}
}
void visit_type_int32(Visitor *v, int32_t *obj, const char *name, Error **errp)
{
int64_t value;
- if (!error_is_set(errp)) {
- if (v->type_int32) {
- v->type_int32(v, obj, name, errp);
- } else {
- value = *obj;
- v->type_int(v, &value, name, errp);
- if (value < INT32_MIN || value > INT32_MAX) {
- error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
- "int32_t");
- return;
- }
- *obj = value;
+
+ if (v->type_int32) {
+ v->type_int32(v, obj, name, errp);
+ } else {
+ value = *obj;
+ v->type_int(v, &value, name, errp);
+ if (value < INT32_MIN || value > INT32_MAX) {
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
+ "int32_t");
+ return;
}
+ *obj = value;
}
}
void visit_type_int64(Visitor *v, int64_t *obj, const char *name, Error **errp)
{
- if (!error_is_set(errp)) {
- if (v->type_int64) {
- v->type_int64(v, obj, name, errp);
- } else {
- v->type_int(v, obj, name, errp);
- }
+ if (v->type_int64) {
+ v->type_int64(v, obj, name, errp);
+ } else {
+ v->type_int(v, obj, name, errp);
}
}
void visit_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp)
{
int64_t value;
- if (!error_is_set(errp)) {
- if (v->type_size) {
- v->type_size(v, obj, name, errp);
- } else if (v->type_uint64) {
- v->type_uint64(v, obj, name, errp);
- } else {
- value = *obj;
- v->type_int(v, &value, name, errp);
- *obj = value;
- }
+
+ if (v->type_size) {
+ v->type_size(v, obj, name, errp);
+ } else if (v->type_uint64) {
+ v->type_uint64(v, obj, name, errp);
+ } else {
+ value = *obj;
+ v->type_int(v, &value, name, errp);
+ *obj = value;
}
}
void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp)
{
- if (!error_is_set(errp)) {
- v->type_bool(v, obj, name, errp);
- }
+ v->type_bool(v, obj, name, errp);
}
void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp)
{
- if (!error_is_set(errp)) {
- v->type_str(v, obj, name, errp);
- }
+ v->type_str(v, obj, name, errp);
}
void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp)
{
- if (!error_is_set(errp)) {
- v->type_number(v, obj, name, errp);
- }
+ v->type_number(v, obj, name, errp);
}
void output_type_enum(Visitor *v, int *obj, const char *strings[],
@@ -321,13 +268,15 @@ void input_type_enum(Visitor *v, int *obj, const char *strings[],
const char *kind, const char *name,
Error **errp)
{
+ Error *local_err = NULL;
int64_t value = 0;
char *enum_str;
assert(strings);
- visit_type_str(v, &enum_str, name, errp);
- if (error_is_set(errp)) {
+ visit_type_str(v, &enum_str, name, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
return;
}
diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c
index 921de33bc..168b083c8 100644
--- a/qapi/qmp-dispatch.c
+++ b/qapi/qmp-dispatch.c
@@ -62,14 +62,14 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
static QObject *do_qmp_dispatch(QObject *request, Error **errp)
{
+ Error *local_err = NULL;
const char *command;
QDict *args, *dict;
QmpCommand *cmd;
QObject *ret = NULL;
-
dict = qmp_dispatch_check_obj(request, errp);
- if (!dict || error_is_set(errp)) {
+ if (!dict) {
return NULL;
}
@@ -80,7 +80,8 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp)
return NULL;
}
if (!cmd->enabled) {
- error_set(errp, QERR_COMMAND_DISABLED, command);
+ error_setg(errp, "The command %s has been disabled for this instance",
+ command);
return NULL;
}
@@ -93,13 +94,13 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp)
switch (cmd->type) {
case QCT_NORMAL:
- cmd->fn(args, &ret, errp);
- if (!error_is_set(errp)) {
- if (cmd->options & QCO_NO_SUCCESS_RESP) {
- g_assert(!ret);
- } else if (!ret) {
- ret = QOBJECT(qdict_new());
- }
+ cmd->fn(args, &ret, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ } else if (cmd->options & QCO_NO_SUCCESS_RESP) {
+ g_assert(!ret);
+ } else if (!ret) {
+ ret = QOBJECT(qdict_new());
}
break;
}
@@ -109,11 +110,11 @@ static QObject *do_qmp_dispatch(QObject *request, Error **errp)
return ret;
}
-QObject *qmp_build_error_object(Error *errp)
+QObject *qmp_build_error_object(Error *err)
{
return qobject_from_jsonf("{ 'class': %s, 'desc': %s }",
- ErrorClass_lookup[error_get_class(errp)],
- error_get_pretty(errp));
+ ErrorClass_lookup[error_get_class(err)],
+ error_get_pretty(err));
}
QObject *qmp_dispatch(QObject *request)
diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c
new file mode 100644
index 000000000..0d1ce0bd1
--- /dev/null
+++ b/qapi/qmp-event.c
@@ -0,0 +1,74 @@
+/*
+ * QMP Event related
+ *
+ * Copyright (c) 2014 Wenchao Xia
+ *
+ * Authors:
+ * Wenchao Xia <wenchaoqemu@gmail.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
+ * See the COPYING.LIB file in the top-level directory.
+ *
+ */
+
+#include <inttypes.h>
+
+#include "qemu-common.h"
+#include "qapi/qmp-event.h"
+#include "qapi/qmp/qstring.h"
+#include "qapi/qmp/qjson.h"
+
+#ifdef _WIN32
+#include "sysemu/os-win32.h"
+#endif
+
+#ifdef CONFIG_POSIX
+#include "sysemu/os-posix.h"
+#endif
+
+static QMPEventFuncEmit qmp_emit;
+
+void qmp_event_set_func_emit(QMPEventFuncEmit emit)
+{
+ qmp_emit = emit;
+}
+
+QMPEventFuncEmit qmp_event_get_func_emit(void)
+{
+ return qmp_emit;
+}
+
+static void timestamp_put(QDict *qdict)
+{
+ int err;
+ QObject *obj;
+ qemu_timeval tv;
+ int64_t sec, usec;
+
+ err = qemu_gettimeofday(&tv);
+ if (err < 0) {
+ /* Put -1 to indicate failure of getting host time */
+ sec = -1;
+ usec = -1;
+ } else {
+ sec = tv.tv_sec;
+ usec = tv.tv_usec;
+ }
+
+ obj = qobject_from_jsonf("{ 'seconds': %" PRId64 ", "
+ "'microseconds': %" PRId64 " }",
+ sec, usec);
+ qdict_put_obj(qdict, "timestamp", obj);
+}
+
+/*
+ * Build a QDict, then fill event name and time stamp, caller should free the
+ * QDict after usage.
+ */
+QDict *qmp_event_build_dict(const char *event_name)
+{
+ QDict *dict = qdict_new();
+ qdict_put(dict, "event", qstring_from_str(event_name));
+ timestamp_put(dict);
+ return dict;
+}
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index bf42c04ea..d8612062f 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -71,7 +71,7 @@ static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp)
GHashTable *h;
if (qiv->nb_stack >= QIV_STACK_SIZE) {
- error_set(errp, QERR_BUFFER_OVERRUN);
+ error_setg(errp, "An internal buffer overran");
return;
}
@@ -286,8 +286,8 @@ static void qmp_input_type_number(Visitor *v, double *obj, const char *name,
}
}
-static void qmp_input_start_optional(Visitor *v, bool *present,
- const char *name, Error **errp)
+static void qmp_input_optional(Visitor *v, bool *present, const char *name,
+ Error **errp)
{
QmpInputVisitor *qiv = to_qiv(v);
QObject *qobj = qmp_input_get_object(qiv, name, true);
@@ -329,7 +329,7 @@ QmpInputVisitor *qmp_input_visitor_new(QObject *obj)
v->visitor.type_bool = qmp_input_type_bool;
v->visitor.type_str = qmp_input_type_str;
v->visitor.type_number = qmp_input_type_number;
- v->visitor.start_optional = qmp_input_start_optional;
+ v->visitor.optional = qmp_input_optional;
v->visitor.get_next_type = qmp_input_get_next_type;
qmp_input_push(v, obj, NULL);
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 74a5684ed..96b338463 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -66,6 +66,12 @@ static QObject *qmp_output_pop(QmpOutputVisitor *qov)
static QObject *qmp_output_first(QmpOutputVisitor *qov)
{
QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
+
+ /* FIXME - find a better way to deal with NULL values */
+ if (!e) {
+ return NULL;
+ }
+
return e->value;
}
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index 793548ae3..d8a8db02e 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -15,31 +15,210 @@
#include "qapi/visitor-impl.h"
#include "qapi/qmp/qerror.h"
#include "qemu/option.h"
+#include "qemu/queue.h"
+#include "qemu/range.h"
+
struct StringInputVisitor
{
Visitor visitor;
+
+ bool head;
+
+ GList *ranges;
+ GList *cur_range;
+ int64_t cur;
+
const char *string;
};
+static void free_range(void *range, void *dummy)
+{
+ g_free(range);
+}
+
+static void parse_str(StringInputVisitor *siv, Error **errp)
+{
+ char *str = (char *) siv->string;
+ long long start, end;
+ Range *cur;
+ char *endptr;
+
+ if (siv->ranges) {
+ return;
+ }
+
+ do {
+ errno = 0;
+ start = strtoll(str, &endptr, 0);
+ if (errno == 0 && endptr > str) {
+ if (*endptr == '\0') {
+ cur = g_malloc0(sizeof(*cur));
+ cur->begin = start;
+ cur->end = start + 1;
+ siv->ranges = g_list_insert_sorted_merged(siv->ranges, cur,
+ range_compare);
+ cur = NULL;
+ str = NULL;
+ } else if (*endptr == '-') {
+ str = endptr + 1;
+ errno = 0;
+ end = strtoll(str, &endptr, 0);
+ if (errno == 0 && endptr > str && start <= end &&
+ (start > INT64_MAX - 65536 ||
+ end < start + 65536)) {
+ if (*endptr == '\0') {
+ cur = g_malloc0(sizeof(*cur));
+ cur->begin = start;
+ cur->end = end + 1;
+ siv->ranges =
+ g_list_insert_sorted_merged(siv->ranges,
+ cur,
+ range_compare);
+ cur = NULL;
+ str = NULL;
+ } else if (*endptr == ',') {
+ str = endptr + 1;
+ cur = g_malloc0(sizeof(*cur));
+ cur->begin = start;
+ cur->end = end + 1;
+ siv->ranges =
+ g_list_insert_sorted_merged(siv->ranges,
+ cur,
+ range_compare);
+ cur = NULL;
+ } else {
+ goto error;
+ }
+ } else {
+ goto error;
+ }
+ } else if (*endptr == ',') {
+ str = endptr + 1;
+ cur = g_malloc0(sizeof(*cur));
+ cur->begin = start;
+ cur->end = start + 1;
+ siv->ranges = g_list_insert_sorted_merged(siv->ranges,
+ cur,
+ range_compare);
+ cur = NULL;
+ } else {
+ goto error;
+ }
+ } else {
+ goto error;
+ }
+ } while (str);
+
+ return;
+error:
+ g_list_foreach(siv->ranges, free_range, NULL);
+ g_list_free(siv->ranges);
+ siv->ranges = NULL;
+}
+
+static void
+start_list(Visitor *v, const char *name, Error **errp)
+{
+ StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+
+ parse_str(siv, errp);
+
+ siv->cur_range = g_list_first(siv->ranges);
+ if (siv->cur_range) {
+ Range *r = siv->cur_range->data;
+ if (r) {
+ siv->cur = r->begin;
+ }
+ }
+}
+
+static GenericList *
+next_list(Visitor *v, GenericList **list, Error **errp)
+{
+ StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+ GenericList **link;
+ Range *r;
+
+ if (!siv->ranges || !siv->cur_range) {
+ return NULL;
+ }
+
+ r = siv->cur_range->data;
+ if (!r) {
+ return NULL;
+ }
+
+ if (siv->cur < r->begin || siv->cur >= r->end) {
+ siv->cur_range = g_list_next(siv->cur_range);
+ if (!siv->cur_range) {
+ return NULL;
+ }
+ r = siv->cur_range->data;
+ if (!r) {
+ return NULL;
+ }
+ siv->cur = r->begin;
+ }
+
+ if (siv->head) {
+ link = list;
+ siv->head = false;
+ } else {
+ link = &(*list)->next;
+ }
+
+ *link = g_malloc0(sizeof **link);
+ return *link;
+}
+
+static void
+end_list(Visitor *v, Error **errp)
+{
+ StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
+ siv->head = true;
+}
+
static void parse_type_int(Visitor *v, int64_t *obj, const char *name,
Error **errp)
{
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
- char *endp = (char *) siv->string;
- long long val;
- errno = 0;
- if (siv->string) {
- val = strtoll(siv->string, &endp, 0);
- }
- if (!siv->string || errno || endp == siv->string || *endp) {
+ if (!siv->string) {
error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"integer");
return;
}
- *obj = val;
+ parse_str(siv, errp);
+
+ if (!siv->ranges) {
+ goto error;
+ }
+
+ if (!siv->cur_range) {
+ Range *r;
+
+ siv->cur_range = g_list_first(siv->ranges);
+ if (!siv->cur_range) {
+ goto error;
+ }
+
+ r = siv->cur_range->data;
+ if (!r) {
+ goto error;
+ }
+
+ siv->cur = r->begin;
+ }
+
+ *obj = siv->cur;
+ siv->cur++;
+ return;
+
+error:
+ error_set(errp, QERR_INVALID_PARAMETER_VALUE, name,
+ "an int64 value or range");
}
static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
@@ -120,8 +299,8 @@ static void parse_type_number(Visitor *v, double *obj, const char *name,
*obj = val;
}
-static void parse_start_optional(Visitor *v, bool *present,
- const char *name, Error **errp)
+static void parse_optional(Visitor *v, bool *present, const char *name,
+ Error **errp)
{
StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
@@ -140,6 +319,8 @@ Visitor *string_input_get_visitor(StringInputVisitor *v)
void string_input_visitor_cleanup(StringInputVisitor *v)
{
+ g_list_foreach(v->ranges, free_range, NULL);
+ g_list_free(v->ranges);
g_free(v);
}
@@ -155,8 +336,12 @@ StringInputVisitor *string_input_visitor_new(const char *str)
v->visitor.type_bool = parse_type_bool;
v->visitor.type_str = parse_type_str;
v->visitor.type_number = parse_type_number;
- v->visitor.start_optional = parse_start_optional;
+ v->visitor.start_list = start_list;
+ v->visitor.next_list = next_list;
+ v->visitor.end_list = end_list;
+ v->visitor.optional = parse_optional;
v->string = str;
+ v->head = true;
return v;
}
diff --git a/qapi/string-output-visitor.c b/qapi/string-output-visitor.c
index fb1d2e806..1ab857458 100644
--- a/qapi/string-output-visitor.c
+++ b/qapi/string-output-visitor.c
@@ -16,32 +16,181 @@
#include "qapi/qmp/qerror.h"
#include "qemu/host-utils.h"
#include <math.h>
+#include "qemu/range.h"
+
+enum ListMode {
+ LM_NONE, /* not traversing a list of repeated options */
+ LM_STARTED, /* start_list() succeeded */
+
+ LM_IN_PROGRESS, /* next_list() has been called.
+ *
+ * Generating the next list link will consume the most
+ * recently parsed QemuOpt instance of the repeated
+ * option.
+ *
+ * Parsing a value into the list link will examine the
+ * next QemuOpt instance of the repeated option, and
+ * possibly enter LM_SIGNED_INTERVAL or
+ * LM_UNSIGNED_INTERVAL.
+ */
+
+ LM_SIGNED_INTERVAL, /* next_list() has been called.
+ *
+ * Generating the next list link will consume the most
+ * recently stored element from the signed interval,
+ * parsed from the most recent QemuOpt instance of the
+ * repeated option. This may consume QemuOpt itself
+ * and return to LM_IN_PROGRESS.
+ *
+ * Parsing a value into the list link will store the
+ * next element of the signed interval.
+ */
+
+ LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */
+
+ LM_END
+};
+
+typedef enum ListMode ListMode;
struct StringOutputVisitor
{
Visitor visitor;
bool human;
- char *string;
+ GString *string;
+ bool head;
+ ListMode list_mode;
+ union {
+ int64_t s;
+ uint64_t u;
+ } range_start, range_end;
+ GList *ranges;
};
static void string_output_set(StringOutputVisitor *sov, char *string)
{
- g_free(sov->string);
- sov->string = string;
+ if (sov->string) {
+ g_string_free(sov->string, true);
+ }
+ sov->string = g_string_new(string);
+ g_free(string);
+}
+
+static void string_output_append(StringOutputVisitor *sov, int64_t a)
+{
+ Range *r = g_malloc0(sizeof(*r));
+ r->begin = a;
+ r->end = a + 1;
+ sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
+}
+
+static void string_output_append_range(StringOutputVisitor *sov,
+ int64_t s, int64_t e)
+{
+ Range *r = g_malloc0(sizeof(*r));
+ r->begin = s;
+ r->end = e + 1;
+ sov->ranges = g_list_insert_sorted_merged(sov->ranges, r, range_compare);
+}
+
+static void format_string(StringOutputVisitor *sov, Range *r, bool next,
+ bool human)
+{
+ if (r->end - r->begin > 1) {
+ if (human) {
+ g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64,
+ r->begin, r->end - 1);
+
+ } else {
+ g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
+ r->begin, r->end - 1);
+ }
+ } else {
+ if (human) {
+ g_string_append_printf(sov->string, "0x%" PRIx64, r->begin);
+ } else {
+ g_string_append_printf(sov->string, "%" PRId64, r->begin);
+ }
+ }
+ if (next) {
+ g_string_append(sov->string, ",");
+ }
}
static void print_type_int(Visitor *v, int64_t *obj, const char *name,
Error **errp)
{
StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
- char *out;
+ GList *l;
+
+ switch (sov->list_mode) {
+ case LM_NONE:
+ string_output_append(sov, *obj);
+ break;
+
+ case LM_STARTED:
+ sov->range_start.s = *obj;
+ sov->range_end.s = *obj;
+ sov->list_mode = LM_IN_PROGRESS;
+ return;
+
+ case LM_IN_PROGRESS:
+ if (sov->range_end.s + 1 == *obj) {
+ sov->range_end.s++;
+ } else {
+ if (sov->range_start.s == sov->range_end.s) {
+ string_output_append(sov, sov->range_end.s);
+ } else {
+ assert(sov->range_start.s < sov->range_end.s);
+ string_output_append_range(sov, sov->range_start.s,
+ sov->range_end.s);
+ }
+
+ sov->range_start.s = *obj;
+ sov->range_end.s = *obj;
+ }
+ return;
+
+ case LM_END:
+ if (sov->range_end.s + 1 == *obj) {
+ sov->range_end.s++;
+ assert(sov->range_start.s < sov->range_end.s);
+ string_output_append_range(sov, sov->range_start.s,
+ sov->range_end.s);
+ } else {
+ if (sov->range_start.s == sov->range_end.s) {
+ string_output_append(sov, sov->range_end.s);
+ } else {
+ assert(sov->range_start.s < sov->range_end.s);
+
+ string_output_append_range(sov, sov->range_start.s,
+ sov->range_end.s);
+ }
+ string_output_append(sov, *obj);
+ }
+ break;
+
+ default:
+ abort();
+ }
+
+ l = sov->ranges;
+ while (l) {
+ Range *r = l->data;
+ format_string(sov, r, l->next != NULL, false);
+ l = l->next;
+ }
if (sov->human) {
- out = g_strdup_printf("%lld (%#llx)", (long long) *obj, (long long) *obj);
- } else {
- out = g_strdup_printf("%lld", (long long) *obj);
+ l = sov->ranges;
+ g_string_append(sov->string, " (");
+ while (l) {
+ Range *r = l->data;
+ format_string(sov, r, l->next != NULL, true);
+ l = l->next;
+ }
+ g_string_append(sov->string, ")");
}
- string_output_set(sov, out);
}
static void print_type_size(Visitor *v, uint64_t *obj, const char *name,
@@ -103,9 +252,61 @@ static void print_type_number(Visitor *v, double *obj, const char *name,
string_output_set(sov, g_strdup_printf("%f", *obj));
}
+static void
+start_list(Visitor *v, const char *name, Error **errp)
+{
+ StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+
+ /* we can't traverse a list in a list */
+ assert(sov->list_mode == LM_NONE);
+ sov->list_mode = LM_STARTED;
+ sov->head = true;
+}
+
+static GenericList *
+next_list(Visitor *v, GenericList **list, Error **errp)
+{
+ StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+ GenericList *ret = NULL;
+ if (*list) {
+ if (sov->head) {
+ ret = *list;
+ } else {
+ ret = (*list)->next;
+ }
+
+ if (sov->head) {
+ if (ret && ret->next == NULL) {
+ sov->list_mode = LM_NONE;
+ }
+ sov->head = false;
+ } else {
+ if (ret && ret->next == NULL) {
+ sov->list_mode = LM_END;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void
+end_list(Visitor *v, Error **errp)
+{
+ StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v);
+
+ assert(sov->list_mode == LM_STARTED ||
+ sov->list_mode == LM_END ||
+ sov->list_mode == LM_NONE ||
+ sov->list_mode == LM_IN_PROGRESS);
+ sov->list_mode = LM_NONE;
+ sov->head = true;
+
+}
+
char *string_output_get_string(StringOutputVisitor *sov)
{
- char *string = sov->string;
+ char *string = g_string_free(sov->string, false);
sov->string = NULL;
return string;
}
@@ -115,9 +316,19 @@ Visitor *string_output_get_visitor(StringOutputVisitor *sov)
return &sov->visitor;
}
+static void free_range(void *range, void *dummy)
+{
+ g_free(range);
+}
+
void string_output_visitor_cleanup(StringOutputVisitor *sov)
{
- g_free(sov->string);
+ if (sov->string) {
+ g_string_free(sov->string, true);
+ }
+
+ g_list_foreach(sov->ranges, free_range, NULL);
+ g_list_free(sov->ranges);
g_free(sov);
}
@@ -127,6 +338,7 @@ StringOutputVisitor *string_output_visitor_new(bool human)
v = g_malloc0(sizeof(*v));
+ v->string = g_string_new(NULL);
v->human = human;
v->visitor.type_enum = output_type_enum;
v->visitor.type_int = print_type_int;
@@ -134,6 +346,9 @@ StringOutputVisitor *string_output_visitor_new(bool human)
v->visitor.type_bool = print_type_bool;
v->visitor.type_str = print_type_str;
v->visitor.type_number = print_type_number;
+ v->visitor.start_list = start_list;
+ v->visitor.next_list = next_list;
+ v->visitor.end_list = end_list;
return v;
}