diff options
Diffstat (limited to 'qapi')
| -rw-r--r-- | qapi/Makefile.objs | 6 | ||||
| -rw-r--r-- | qapi/block-core.json | 2111 | ||||
| -rw-r--r-- | qapi/block.json | 180 | ||||
| -rw-r--r-- | qapi/common.json | 116 | ||||
| -rw-r--r-- | qapi/event.json | 358 | ||||
| -rw-r--r-- | qapi/opts-visitor.c | 557 | ||||
| -rw-r--r-- | qapi/qapi-dealloc-visitor.c | 225 | ||||
| -rw-r--r-- | qapi/qapi-util.c | 34 | ||||
| -rw-r--r-- | qapi/qapi-visit-core.c | 313 | ||||
| -rw-r--r-- | qapi/qmp-dispatch.c | 141 | ||||
| -rw-r--r-- | qapi/qmp-event.c | 74 | ||||
| -rw-r--r-- | qapi/qmp-input-visitor.c | 349 | ||||
| -rw-r--r-- | qapi/qmp-output-visitor.c | 240 | ||||
| -rw-r--r-- | qapi/qmp-registry.c | 91 | ||||
| -rw-r--r-- | qapi/rocker.json | 286 | ||||
| -rw-r--r-- | qapi/string-input-visitor.c | 347 | ||||
| -rw-r--r-- | qapi/string-output-visitor.c | 353 | ||||
| -rw-r--r-- | qapi/trace.json | 65 | 
18 files changed, 5846 insertions, 0 deletions
diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs new file mode 100644 index 00000000..22789706 --- /dev/null +++ b/qapi/Makefile.objs @@ -0,0 +1,6 @@ +util-obj-y = qapi-visit-core.o qapi-dealloc-visitor.o qmp-input-visitor.o +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 +util-obj-y += qapi-util.o diff --git a/qapi/block-core.json b/qapi/block-core.json new file mode 100644 index 00000000..7b2efb86 --- /dev/null +++ b/qapi/block-core.json @@ -0,0 +1,2111 @@ +# -*- 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 +# +## + +{ 'struct': '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 +# +# @corrupt: #optional true if the image has been marked corrupt; only valid for +#           compat >= 1.1 (since 2.2) +# +# @refcount-bits: width of a refcount entry in bits (since 2.3) +# +# Since: 1.7 +## +{ 'struct': 'ImageInfoSpecificQCow2', +  'data': { +      'compat': 'str', +      '*lazy-refcounts': 'bool', +      '*corrupt': 'bool', +      'refcount-bits': 'int' +  } } + +## +# @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 +## +{ 'struct': '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 +# +## + +{ 'struct': '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 +# +## + +{ 'struct': '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' } } + +## +# @BlockdevCacheInfo +# +# Cache mode information for a block device +# +# @writeback:   true if writeback mode is enabled +# @direct:      true if the host page cache is bypassed (O_DIRECT) +# @no-flush:    true if flush requests are ignored for the device +# +# Since: 2.3 +## +{ 'struct': 'BlockdevCacheInfo', +  'data': { 'writeback': 'bool', +            'direct': 'bool', +            'no-flush': 'bool' } } + +## +# @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' +#       2.2: 'archipelago' added, 'cow' dropped +#       2.3: 'host_floppy' deprecated +# +# @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) +# +# @group: #optional throttle group name (Since 2.4) +# +# @cache: the cache mode used for the block device (since: 2.3) +# +# @write_threshold: configured write threshold for the device. +#                   0 if disabled. (Since 2.3) +# +# Since: 0.14.0 +# +## +{ 'struct': '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', '*group': 'str', 'cache': 'BlockdevCacheInfo', +            'write_threshold': '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 +## +{ 'struct': 'BlockDeviceMapEntry', +  'data': { 'start': 'int', 'length': 'int', 'depth': 'int', 'zero': 'bool', +            'data': 'bool', '*offset': 'int' } } + +## +# @DirtyBitmapStatus: +# +# An enumeration of possible states that a dirty bitmap can report to the user. +# +# @frozen: The bitmap is currently in-use by a backup operation or block job, +#          and is immutable. +# +# @disabled: The bitmap is currently in-use by an internal operation and is +#            read-only. It can still be deleted. +# +# @active: The bitmap is actively monitoring for new writes, and can be cleared, +#          deleted, or used for backup operations. +# +# Since: 2.4 +## +{ 'enum': 'DirtyBitmapStatus', +  'data': ['active', 'disabled', 'frozen'] } + +## +# @BlockDirtyInfo: +# +# Block dirty bitmap information. +# +# @name: #optional the name of the dirty bitmap (Since 2.4) +# +# @count: number of dirty bytes according to the dirty bitmap +# +# @granularity: granularity of the dirty bitmap in bytes (since 1.4) +# +# @status: current status of the dirty bitmap (since 2.4) +# +# Since: 1.3 +## +{ 'struct': 'BlockDirtyInfo', +  'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32', +           'status': 'DirtyBitmapStatus'} } + +## +# @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 +#             (supported device models: virtio-blk, ide, scsi-disk) +# +# @inserted: #optional @BlockDeviceInfo describing the device if media is +#            present +# +# Since:  0.14.0 +## +{ 'struct': '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. +# +# @rd_merged: Number of read requests that have been merged into another +#             request (Since 2.3). +# +# @wr_merged: Number of write requests that have been merged into another +#             request (Since 2.3). +# +# Since: 0.14.0 +## +{ 'struct': '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', +           'rd_merged': 'int', 'wr_merged': '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. +# +# @node-name: #optional The node name of the device. (Since 2.3) +# +# @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 +## +{ 'struct': 'BlockStats', +  'data': {'*device': 'str', '*node-name': 'str', +           'stats': 'BlockDeviceStats', +           '*parent': 'BlockStats', +           '*backing': 'BlockStats'} } + +## +# @query-blockstats: +# +# Query the @BlockStats for all virtual block devices. +# +# @query-nodes: #optional If true, the command will query all the block nodes +#               that have a node name, in a list which will include "parent" +#               information, but not "backing". +#               If false or omitted, the behavior is as before - query all the +#               device backends, recursively including their "parent" and +#               "backing". (Since 2.3) +# +# Returns: A list of @BlockStats for each virtual block devices. +# +# Since: 0.14.0 +## +{ 'command': 'query-blockstats', +  'data': { '*query-nodes': 'bool' }, +  '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 +# +# @incremental: only copy data described by the dirty bitmap. Since: 2.4 +# +# Since: 1.3 +## +{ 'enum': 'MirrorSyncMode', +  'data': ['top', 'full', 'none', 'incremental'] } + +## +# @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) +# +# @ready: true if the job may be completed (since 2.2) +# +# Since: 1.1 +## +{ 'struct': 'BlockJobInfo', +  'data': {'type': 'str', 'device': 'str', 'len': 'int', +           'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int', +           'io-status': 'BlockDeviceIoStatus', 'ready': 'bool'} } + +## +# @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'. +## +{ 'struct': '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, from a +#        dirty bitmap, 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 +# +# @bitmap: #optional the name of dirty bitmap if sync is "incremental". +#          Must be present if sync is "incremental", must NOT be present +#          otherwise. (Since 2.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). +# +# 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 +## +{ 'struct': 'DriveBackup', +  'data': { 'device': 'str', 'target': 'str', '*format': 'str', +            'sync': 'MirrorSyncMode', '*mode': 'NewImageMode', +            '*speed': 'int', '*bitmap': 'str', +            '*on-source-error': 'BlockdevOnError', +            '*on-target-error': 'BlockdevOnError' } } + +## +# @BlockdevBackup +# +# @device: the name of the device which should be copied. +# +# @target: the name of the backup target device. +# +# @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). +# +# @speed: #optional the maximum speed, in bytes per second. The default is 0, +#         for unlimited. +# +# @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: 2.3 +## +{ 'struct': 'BlockdevBackup', +  'data': { 'device': 'str', 'target': 'str', +            'sync': 'MirrorSyncMode', +            '*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' } + +## +# @blockdev-backup +# +# Start a point-in-time copy of a block device to a new destination.  The +# status of ongoing blockdev-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 BlockdevBackup. +# +# Since 2.3 +## +{ 'command': 'blockdev-backup', 'data': 'BlockdevBackup' } + + +## +# @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). +# @unmap: #optional Whether to try to unmap target sectors where source has +#         only zero. If true, and target unallocated sectors will read as zero, +#         target image sectors will be unmapped; otherwise, zeroes will be +#         written. Both will result in identical contents. +#         Default is true. (Since 2.4) +# +# 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', +            '*unmap': 'bool' } } + +## +# @BlockDirtyBitmap +# +# @node: name of device/node which the bitmap is tracking +# +# @name: name of the dirty bitmap +# +# Since 2.4 +## +{ 'struct': 'BlockDirtyBitmap', +  'data': { 'node': 'str', 'name': 'str' } } + +## +# @BlockDirtyBitmapAdd +# +# @node: name of device/node which the bitmap is tracking +# +# @name: name of the dirty bitmap +# +# @granularity: #optional the bitmap granularity, default is 64k for +#               block-dirty-bitmap-add +# +# Since 2.4 +## +{ 'struct': 'BlockDirtyBitmapAdd', +  'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32' } } + +## +# @block-dirty-bitmap-add +# +# Create a dirty bitmap with a name on the node +# +# Returns: nothing on success +#          If @node is not a valid block device or node, DeviceNotFound +#          If @name is already taken, GenericError with an explanation +# +# Since 2.4 +## +{ 'command': 'block-dirty-bitmap-add', +  'data': 'BlockDirtyBitmapAdd' } + +## +# @block-dirty-bitmap-remove +# +# Remove a dirty bitmap on the node +# +# Returns: nothing on success +#          If @node is not a valid block device or node, DeviceNotFound +#          If @name is not found, GenericError with an explanation +#          if @name is frozen by an operation, GenericError +# +# Since 2.4 +## +{ 'command': 'block-dirty-bitmap-remove', +  'data': 'BlockDirtyBitmap' } + +## +# @block-dirty-bitmap-clear +# +# Clear (reset) a dirty bitmap on the device +# +# Returns: nothing on success +#          If @node is not a valid block device, DeviceNotFound +#          If @name is not found, GenericError with an explanation +# +# Since 2.4 +## +{ 'command': 'block-dirty-bitmap-clear', +  'data': 'BlockDirtyBitmap' } + +## +# @block_set_io_throttle: +# +# Change I/O throttle limits for a block drive. +# +# Since QEMU 2.4, each device with I/O limits is member of a throttle +# group. +# +# If two or more devices are members of the same group, the limits +# will apply to the combined I/O of the whole group in a round-robin +# fashion. Therefore, setting new I/O limits to a device will affect +# the whole group. +# +# The name of the group can be specified using the 'group' parameter. +# If the parameter is unset, it is assumed to be the current group of +# that device. If it's not in any group yet, the name of the device +# will be used as the name for its group. +# +# The 'group' parameter can also be used to move a device to a +# different group. In this case the limits specified in the parameters +# will be applied to the new group only. +# +# I/O limits can be disabled by setting all of them to 0. In this case +# the device will be removed from its group and the rest of its +# members will not be affected. The 'group' parameter is ignored. +# +# @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) +# +# @group: #optional throttle group name (Since 2.4) +# +# 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', '*group': 'str' } } + +## +# @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 +## +{ 'struct': '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 +# @host_floppy: deprecated since 2.3 +# +# Since: 2.0 +## +{ 'enum': 'BlockdevDriver', +  'data': [ 'archipelago', 'blkdebug', 'blkverify', 'bochs', 'cloop', +            'dmg', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device', +            'host_floppy', 'http', 'https', 'null-aio', 'null-co', 'parallels', +            'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'tftp', 'vdi', 'vhdx', +            'vmdk', 'vpc', 'vvfat' ] } + +## +# @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 +## +{ 'struct': '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 +## +{ 'struct': 'BlockdevOptionsFile', +  'data': { 'filename': 'str' } } + +## +# @BlockdevOptionsNull +# +# Driver specific block device options for the null backend. +# +# @size:    #optional size of the device in bytes. +# @latency-ns: #optional emulated latency (in nanoseconds) in processing +#              requests. Default to zero which completes requests immediately. +#              (Since 2.4) +# +# Since: 2.2 +## +{ 'struct': 'BlockdevOptionsNull', +  'data': { '*size': 'int', '*latency-ns': 'uint64' } } + +## +# @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) +# @label:       #optional set the volume label, limited to 11 bytes. FAT16 and +#               FAT32 traditionally have some restrictions on labels, which are +#               ignored by most operating systems. Defaults to "QEMU VVFAT". +#               (since 2.4) +# @rw:          #optional whether to allow write operations (default: false) +# +# Since: 1.7 +## +{ 'struct': 'BlockdevOptionsVVFAT', +  'data': { 'dir': 'str', '*fat-type': 'int', '*floppy': 'bool', +            '*label': 'str', '*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 +## +{ 'struct': '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 +## +{ 'struct': 'BlockdevOptionsGenericCOWFormat', +  'base': 'BlockdevOptionsGenericFormat', +  'data': { '*backing': 'BlockdevRef' } } + +## +# @Qcow2OverlapCheckMode +# +# General overlap check modes. +# +# @none:        Do not perform any checks +# +# @constant:    Perform only checks which can be done in constant time and +#               without reading anything from disk +# +# @cached:      Perform only checks which can be done without reading anything +#               from disk +# +# @all:         Perform all available overlap checks +# +# Since: 2.2 +## +{ 'enum': 'Qcow2OverlapCheckMode', +  'data': [ 'none', 'constant', 'cached', 'all' ] } + +## +# @Qcow2OverlapCheckFlags +# +# Structure of flags for each metadata structure. Setting a field to 'true' +# makes qemu guard that structure against unintended overwriting. The default +# value is chosen according to the template given. +# +# @template: Specifies a template mode which can be adjusted using the other +#            flags, defaults to 'cached' +# +# Since: 2.2 +## +{ 'struct': 'Qcow2OverlapCheckFlags', +  'data': { '*template':       'Qcow2OverlapCheckMode', +            '*main-header':    'bool', +            '*active-l1':      'bool', +            '*active-l2':      'bool', +            '*refcount-table': 'bool', +            '*refcount-block': 'bool', +            '*snapshot-table': 'bool', +            '*inactive-l1':    'bool', +            '*inactive-l2':    'bool' } } + +## +# @Qcow2OverlapChecks +# +# Specifies which metadata structures should be guarded against unintended +# overwriting. +# +# @flags:   set of flags for separate specification of each metadata structure +#           type +# +# @mode:    named mode which chooses a specific set of flags +# +# Since: 2.2 +## +{ 'alternate': 'Qcow2OverlapChecks', +  'data': { 'flags': 'Qcow2OverlapCheckFlags', +            'mode':  'Qcow2OverlapCheckMode' } } + +## +# @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 +# +# @overlap-check:         #optional which overlap checks to perform for writes +#                         to the image, defaults to 'cached' (since 2.2) +# +# @cache-size:            #optional the maximum total size of the L2 table and +#                         refcount block caches in bytes (since 2.2) +# +# @l2-cache-size:         #optional the maximum size of the L2 table cache in +#                         bytes (since 2.2) +# +# @refcount-cache-size:   #optional the maximum size of the refcount block cache +#                         in bytes (since 2.2) +# +# Since: 1.7 +## +{ 'struct': 'BlockdevOptionsQcow2', +  'base': 'BlockdevOptionsGenericCOWFormat', +  'data': { '*lazy-refcounts': 'bool', +            '*pass-discard-request': 'bool', +            '*pass-discard-snapshot': 'bool', +            '*pass-discard-other': 'bool', +            '*overlap-check': 'Qcow2OverlapChecks', +            '*cache-size': 'int', +            '*l2-cache-size': 'int', +            '*refcount-cache-size': 'int' } } + + +## +# @BlockdevOptionsArchipelago +# +# Driver specific block device options for Archipelago. +# +# @volume:              Name of the Archipelago volume image +# +# @mport:               #optional The port number on which mapperd is +#                       listening. This is optional +#                       and if not specified, QEMU will make Archipelago +#                       use the default port (1001). +# +# @vport:               #optional The port number on which vlmcd is +#                       listening. This is optional +#                       and if not specified, QEMU will make Archipelago +#                       use the default port (501). +# +# @segment:             #optional The name of the shared memory segment +#                       Archipelago stack is using. This is optional +#                       and if not specified, QEMU will make Archipelago +#                       use the default value, 'archipelago'. +# Since: 2.2 +## +{ 'struct': 'BlockdevOptionsArchipelago', +  'data': { 'volume': 'str', +            '*mport': 'int', +            '*vport': 'int', +            '*segment': 'str' } } + + +## +# @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', 'pwritev_rmw.head', 'pwritev_rmw.after_head', +            'pwritev_rmw.tail', 'pwritev_rmw.after_tail', 'pwritev', +            'pwritev_zero', 'pwritev_done', 'empty_image_prepare' ] } + +## +# @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 +## +{ 'struct': '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 +## +{ 'struct': '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 +## +{ 'struct': '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 +## +{ 'struct': 'BlockdevOptionsBlkverify', +  'data': { 'test': 'BlockdevRef', +            'raw': 'BlockdevRef' } } + +## +# @QuorumReadPattern +# +# An enumeration of quorum read patterns. +# +# @quorum: read all the children and do a quorum vote on reads +# +# @fifo: read only from the first child that has not failed +# +# Since: 2.2 +## +{ 'enum': 'QuorumReadPattern', 'data': [ 'quorum', 'fifo' ] } + +## +# @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) +# +# @read-pattern: #optional choose read pattern and set to quorum by default +#                (Since 2.2) +# +# Since: 2.0 +## +{ 'struct': 'BlockdevOptionsQuorum', +  'data': { '*blkverify': 'bool', +            'children': [ 'BlockdevRef' ], +            'vote-threshold': 'int', +            '*rewrite-corrupted': 'bool', +            '*read-pattern': 'QuorumReadPattern' } } + +## +# @BlockdevOptions +# +# Options for creating a block device. +# +# Since: 1.7 +## +{ 'union': 'BlockdevOptions', +  'base': 'BlockdevOptionsBase', +  'discriminator': 'driver', +  'data': { +      'archipelago':'BlockdevOptionsArchipelago', +      'blkdebug':   'BlockdevOptionsBlkdebug', +      'blkverify':  'BlockdevOptionsBlkverify', +      'bochs':      'BlockdevOptionsGenericFormat', +      'cloop':      'BlockdevOptionsGenericFormat', +      'dmg':        'BlockdevOptionsGenericFormat', +      'file':       'BlockdevOptionsFile', +      'ftp':        'BlockdevOptionsFile', +      'ftps':       'BlockdevOptionsFile', +# TODO gluster: Wait for structured options +      'host_cdrom': 'BlockdevOptionsFile', +      'host_device':'BlockdevOptionsFile', +      'host_floppy':'BlockdevOptionsFile', +      'http':       'BlockdevOptionsFile', +      'https':      'BlockdevOptionsFile', +# TODO iscsi: Wait for structured options +# TODO nbd: Should take InetSocketAddress for 'host'? +# TODO nfs: Wait for structured options +      'null-aio':   'BlockdevOptionsNull', +      'null-co':    'BlockdevOptionsNull', +      'parallels':  'BlockdevOptionsGenericFormat', +      'qcow2':      'BlockdevOptionsQcow2', +      'qcow':       'BlockdevOptionsGenericCOWFormat', +      'qed':        'BlockdevOptionsGenericCOWFormat', +      'quorum':     'BlockdevOptionsQuorum', +      'raw':        'BlockdevOptionsGenericFormat', +# TODO rbd: Wait for structured options +# TODO sheepdog: Wait for structured options +# TODO ssh: Should take InetSocketAddress for 'host'? +      'tftp':       'BlockdevOptionsFile', +      'vdi':        'BlockdevOptionsGenericFormat', +      'vhdx':       'BlockdevOptionsGenericFormat', +      'vmdk':       'BlockdevOptionsGenericCOWFormat', +      'vpc':        'BlockdevOptionsGenericFormat', +      'vvfat':      'BlockdevOptionsVVFAT' +  } } + +## +# @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 +## +{ 'alternate': 'BlockdevRef', +  'data': { 'definition': 'BlockdevOptions', +            'reference': 'str' } } + +## +# @blockdev-add: +# +# Creates a new block device. +# +# This command is still a work in progress.  It doesn't support all +# block drivers, it lacks a matching blockdev-del, and more.  Stay +# away from it unless you want to help with its development. +# +# @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 corruption has been detected in a disk image +# +# @device: device name. This is always present for compatibility +#          reasons, but it can be empty ("") if the image does not +#          have a device name associated. +# +# @node-name: #optional node name (Since: 2.4) +# +# @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 host's access offset into the image +# +# @size: #optional, if the corruption resulted from an image access, this is +#        the access size +# +# fatal: if set, the image is marked corrupt and therefore unusable after this +#        event and must be repaired (Since 2.2; before, every +#        BLOCK_IMAGE_CORRUPTED event was fatal) +# +# Since: 1.7 +## +{ 'event': 'BLOCK_IMAGE_CORRUPTED', +  'data': { 'device'     : 'str', +            '*node-name' : 'str', +            'msg'        : 'str', +            '*offset'    : 'int', +            '*size'      : 'int', +            'fatal'      : 'bool' } } + +## +# @BLOCK_IO_ERROR +# +# Emitted when a disk I/O error occurs +# +# @device: device name +# +# @operation: I/O operation +# +# @action: action that has been taken +# +# @nospace: #optional true if I/O error was caused due to a no-space +#           condition. This key is only present if query-block's +#           io-status is present, please see query-block documentation +#           for more information (since: 2.2) +# +# @reason: human readable string describing the error cause. +#          (This field is a debugging aid for humans, it should not +#           be parsed by applications) (since: 2.2) +# +# 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', '*nospace': 'bool', +            'reason': 'str' } } + +## +# @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' } } + +# @PreallocMode +# +# Preallocation mode of QEMU image file +# +# @off: no preallocation +# @metadata: preallocate only for metadata +# @falloc: like @full preallocation but allocate disk space by +#          posix_fallocate() rather than writing zeros. +# @full: preallocate all data by writing zeros to device to ensure disk +#        space is really available. @full preallocation also sets up +#        metadata correctly. +# +# Since 2.2 +## +{ 'enum': 'PreallocMode', +  'data': [ 'off', 'metadata', 'falloc', 'full' ] } + +## +# @BLOCK_WRITE_THRESHOLD +# +# Emitted when writes on block device reaches or exceeds the +# configured write threshold. For thin-provisioned devices, this +# means the device should be extended to avoid pausing for +# disk exhaustion. +# The event is one shot. Once triggered, it needs to be +# re-registered with another block-set-threshold command. +# +# @node-name: graph node name on which the threshold was exceeded. +# +# @amount-exceeded: amount of data which exceeded the threshold, in bytes. +# +# @write-threshold: last configured threshold, in bytes. +# +# Since: 2.3 +## +{ 'event': 'BLOCK_WRITE_THRESHOLD', +  'data': { 'node-name': 'str', +            'amount-exceeded': 'uint64', +            'write-threshold': 'uint64' } } + +## +# @block-set-write-threshold +# +# Change the write threshold for a block drive. An event will be delivered +# if a write to this block drive crosses the configured threshold. +# This is useful to transparently resize thin-provisioned drives without +# the guest OS noticing. +# +# @node-name: graph node name on which the threshold must be set. +# +# @write-threshold: configured threshold for the block device, bytes. +#                   Use 0 to disable the threshold. +# +# Since: 2.3 +## +{ 'command': 'block-set-write-threshold', +  'data': { 'node-name': 'str', 'write-threshold': 'uint64' } } diff --git a/qapi/block.json b/qapi/block.json new file mode 100644 index 00000000..aad645c4 --- /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 +## +{ 'struct': '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 00000000..bad56bf6 --- /dev/null +++ b/qapi/common.json @@ -0,0 +1,116 @@ +# -*- 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' ] } + +## +# @VersionTriple +# +# A three-part version number. +# +# @qemu.major:  The major version number. +# +# @qemu.minor:  The minor version number. +# +# @qemu.micro:  The micro version number. +# +# Since: 2.4 +## +{ 'struct': 'VersionTriple', +  'data': {'major': 'int', 'minor': 'int', 'micro': 'int'} } + + +## +# @VersionInfo: +# +# A description of QEMU's version. +# +# @qemu:        The 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 +## +{ 'struct': 'VersionInfo', +  'data': {'qemu': 'VersionTriple', '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 +## +{ 'struct': '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'] } + +## +# @OnOffAuto +# +# An enumeration of three options: on, off, and auto +# +# @auto: QEMU selects the value between on and off +# +# @on: Enabled +# +# @off: Disabled +# +# Since: 2.2 +## +{ 'enum': 'OnOffAuto', +  'data': [ 'auto', 'on', 'off' ] } diff --git a/qapi/event.json b/qapi/event.json new file mode 100644 index 00000000..f0cef010 --- /dev/null +++ b/qapi/event.json @@ -0,0 +1,358 @@ +## +# @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' } + +## +# @MIGRATION +# +# Emitted when a migration event happens +# +# @status: @MigrationStatus describing the current migration status. +# +# Since: 2.4 +## +{ 'event': 'MIGRATION', +  'data': {'status': 'MigrationStatus'}} + +## +# @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' } } + +## +# @MEM_UNPLUG_ERROR +# +# Emitted when memory hot unplug error occurs. +# +# @device: device name +# +# @msg: Informative message +# +# Since: 2.4 +## +{ 'event': 'MEM_UNPLUG_ERROR', +  'data': { 'device': 'str', 'msg': 'str' } } diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c new file mode 100644 index 00000000..7ae33b31 --- /dev/null +++ b/qapi/opts-visitor.c @@ -0,0 +1,557 @@ +/* + * Options Visitor + * + * Copyright Red Hat, Inc. 2012, 2013 + * + * Author: Laszlo Ersek <lersek@redhat.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 "qemu-common.h" +#include "qapi/qmp/qerror.h" +#include "qapi/opts-visitor.h" +#include "qemu/queue.h" +#include "qemu/option_int.h" +#include "qapi/visitor-impl.h" + + +enum ListMode +{ +    LM_NONE,             /* not traversing a list of repeated options */ +    LM_STARTED,          /* opts_start_list() succeeded */ + +    LM_IN_PROGRESS,      /* opts_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,  /* opts_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. */ +}; + +typedef enum ListMode ListMode; + +struct OptsVisitor +{ +    Visitor visitor; + +    /* Ownership remains with opts_visitor_new()'s caller. */ +    const QemuOpts *opts_root; + +    unsigned depth; + +    /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value +     * is a non-empty GQueue, enumerating all QemuOpt occurrences with that +     * name. */ +    GHashTable *unprocessed_opts; + +    /* The list currently being traversed with opts_start_list() / +     * opts_next_list(). The list must have a struct element type in the +     * schema, with a single mandatory scalar member. */ +    ListMode list_mode; +    GQueue *repeated_opts; + +    /* When parsing a list of repeating options as integers, values of the form +     * "a-b", representing a closed interval, are allowed. Elements in the +     * range are generated individually. +     */ +    union { +        int64_t s; +        uint64_t u; +    } range_next, range_limit; + +    /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for +     * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does +     * not survive or escape the OptsVisitor object. +     */ +    QemuOpt *fake_id_opt; +}; + + +static void +destroy_list(gpointer list) +{ +  g_queue_free(list); +} + + +static void +opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt) +{ +    GQueue *list; + +    list = g_hash_table_lookup(unprocessed_opts, opt->name); +    if (list == NULL) { +        list = g_queue_new(); + +        /* GHashTable will never try to free the keys -- we supply NULL as +         * "key_destroy_func" in opts_start_struct(). Thus cast away key +         * const-ness in order to suppress gcc's warning. +         */ +        g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list); +    } + +    /* Similarly, destroy_list() doesn't call g_queue_free_full(). */ +    g_queue_push_tail(list, (gpointer)opt); +} + + +static void +opts_start_struct(Visitor *v, void **obj, const char *kind, +                  const char *name, size_t size, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); +    const QemuOpt *opt; + +    if (obj) { +        *obj = g_malloc0(size > 0 ? size : 1); +    } +    if (ov->depth++ > 0) { +        return; +    } + +    ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal, +                                                 NULL, &destroy_list); +    QTAILQ_FOREACH(opt, &ov->opts_root->head, next) { +        /* ensured by qemu-option.c::opts_do_parse() */ +        assert(strcmp(opt->name, "id") != 0); + +        opts_visitor_insert(ov->unprocessed_opts, opt); +    } + +    if (ov->opts_root->id != NULL) { +        ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt); + +        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); +    } +} + + +static gboolean +ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data) +{ +    return TRUE; +} + + +static void +opts_end_struct(Visitor *v, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); +    GQueue *any; + +    if (--ov->depth > 0) { +        return; +    } + +    /* we should have processed all (distinct) QemuOpt instances */ +    any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL); +    if (any) { +        const QemuOpt *first; + +        first = g_queue_peek_head(any); +        error_setg(errp, QERR_INVALID_PARAMETER, first->name); +    } +    g_hash_table_destroy(ov->unprocessed_opts); +    ov->unprocessed_opts = NULL; +    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; +} + + +static GQueue * +lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp) +{ +    GQueue *list; + +    list = g_hash_table_lookup(ov->unprocessed_opts, name); +    if (!list) { +        error_setg(errp, QERR_MISSING_PARAMETER, name); +    } +    return list; +} + + +static void +opts_start_list(Visitor *v, const char *name, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + +    /* we can't traverse a list in a list */ +    assert(ov->list_mode == LM_NONE); +    ov->repeated_opts = lookup_distinct(ov, name, errp); +    if (ov->repeated_opts != NULL) { +        ov->list_mode = LM_STARTED; +    } +} + + +static GenericList * +opts_next_list(Visitor *v, GenericList **list, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); +    GenericList **link; + +    switch (ov->list_mode) { +    case LM_STARTED: +        ov->list_mode = LM_IN_PROGRESS; +        link = list; +        break; + +    case LM_SIGNED_INTERVAL: +    case LM_UNSIGNED_INTERVAL: +        link = &(*list)->next; + +        if (ov->list_mode == LM_SIGNED_INTERVAL) { +            if (ov->range_next.s < ov->range_limit.s) { +                ++ov->range_next.s; +                break; +            } +        } else if (ov->range_next.u < ov->range_limit.u) { +            ++ov->range_next.u; +            break; +        } +        ov->list_mode = LM_IN_PROGRESS; +        /* range has been completed, fall through in order to pop option */ + +    case LM_IN_PROGRESS: { +        const QemuOpt *opt; + +        opt = g_queue_pop_head(ov->repeated_opts); +        if (g_queue_is_empty(ov->repeated_opts)) { +            g_hash_table_remove(ov->unprocessed_opts, opt->name); +            return NULL; +        } +        link = &(*list)->next; +        break; +    } + +    default: +        abort(); +    } + +    *link = g_malloc0(sizeof **link); +    return *link; +} + + +static void +opts_end_list(Visitor *v, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + +    assert(ov->list_mode == LM_STARTED || +           ov->list_mode == LM_IN_PROGRESS || +           ov->list_mode == LM_SIGNED_INTERVAL || +           ov->list_mode == LM_UNSIGNED_INTERVAL); +    ov->repeated_opts = NULL; +    ov->list_mode = LM_NONE; +} + + +static const QemuOpt * +lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp) +{ +    if (ov->list_mode == LM_NONE) { +        GQueue *list; + +        /* the last occurrence of any QemuOpt takes effect when queried by name +         */ +        list = lookup_distinct(ov, name, errp); +        return list ? g_queue_peek_tail(list) : NULL; +    } +    assert(ov->list_mode == LM_IN_PROGRESS); +    return g_queue_peek_head(ov->repeated_opts); +} + + +static void +processed(OptsVisitor *ov, const char *name) +{ +    if (ov->list_mode == LM_NONE) { +        g_hash_table_remove(ov->unprocessed_opts, name); +        return; +    } +    assert(ov->list_mode == LM_IN_PROGRESS); +    /* do nothing */ +} + + +static void +opts_type_str(Visitor *v, char **obj, const char *name, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); +    const QemuOpt *opt; + +    opt = lookup_scalar(ov, name, errp); +    if (!opt) { +        return; +    } +    *obj = g_strdup(opt->str ? opt->str : ""); +    processed(ov, name); +} + + +/* mimics qemu-option.c::parse_option_bool() */ +static void +opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); +    const QemuOpt *opt; + +    opt = lookup_scalar(ov, name, errp); +    if (!opt) { +        return; +    } + +    if (opt->str) { +        if (strcmp(opt->str, "on") == 0 || +            strcmp(opt->str, "yes") == 0 || +            strcmp(opt->str, "y") == 0) { +            *obj = true; +        } else if (strcmp(opt->str, "off") == 0 || +            strcmp(opt->str, "no") == 0 || +            strcmp(opt->str, "n") == 0) { +            *obj = false; +        } else { +            error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, +                       "on|yes|y|off|no|n"); +            return; +        } +    } else { +        *obj = true; +    } + +    processed(ov, name); +} + + +static void +opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); +    const QemuOpt *opt; +    const char *str; +    long long val; +    char *endptr; + +    if (ov->list_mode == LM_SIGNED_INTERVAL) { +        *obj = ov->range_next.s; +        return; +    } + +    opt = lookup_scalar(ov, name, errp); +    if (!opt) { +        return; +    } +    str = opt->str ? opt->str : ""; + +    /* we've gotten past lookup_scalar() */ +    assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); + +    errno = 0; +    val = strtoll(str, &endptr, 0); +    if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) { +        if (*endptr == '\0') { +            *obj = val; +            processed(ov, name); +            return; +        } +        if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { +            long long val2; + +            str = endptr + 1; +            val2 = strtoll(str, &endptr, 0); +            if (errno == 0 && endptr > str && *endptr == '\0' && +                INT64_MIN <= val2 && val2 <= INT64_MAX && val <= val2 && +                (val > INT64_MAX - OPTS_VISITOR_RANGE_MAX || +                 val2 < val + OPTS_VISITOR_RANGE_MAX)) { +                ov->range_next.s = val; +                ov->range_limit.s = val2; +                ov->list_mode = LM_SIGNED_INTERVAL; + +                /* as if entering on the top */ +                *obj = ov->range_next.s; +                return; +            } +        } +    } +    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, +               (ov->list_mode == LM_NONE) ? "an int64 value" : +                                            "an int64 value or range"); +} + + +static void +opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); +    const QemuOpt *opt; +    const char *str; +    unsigned long long val; +    char *endptr; + +    if (ov->list_mode == LM_UNSIGNED_INTERVAL) { +        *obj = ov->range_next.u; +        return; +    } + +    opt = lookup_scalar(ov, name, errp); +    if (!opt) { +        return; +    } +    str = opt->str; + +    /* we've gotten past lookup_scalar() */ +    assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); + +    if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) { +        if (*endptr == '\0') { +            *obj = val; +            processed(ov, name); +            return; +        } +        if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { +            unsigned long long val2; + +            str = endptr + 1; +            if (parse_uint_full(str, &val2, 0) == 0 && +                val2 <= UINT64_MAX && val <= val2 && +                val2 - val < OPTS_VISITOR_RANGE_MAX) { +                ov->range_next.u = val; +                ov->range_limit.u = val2; +                ov->list_mode = LM_UNSIGNED_INTERVAL; + +                /* as if entering on the top */ +                *obj = ov->range_next.u; +                return; +            } +        } +    } +    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, +               (ov->list_mode == LM_NONE) ? "a uint64 value" : +                                            "a uint64 value or range"); +} + + +static void +opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); +    const QemuOpt *opt; +    int64_t val; +    char *endptr; + +    opt = lookup_scalar(ov, name, errp); +    if (!opt) { +        return; +    } + +    val = strtosz_suffix(opt->str ? opt->str : "", &endptr, +                         STRTOSZ_DEFSUFFIX_B); +    if (val < 0 || *endptr) { +        error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, +                   "a size value representible as a non-negative int64"); +        return; +    } + +    *obj = val; +    processed(ov, name); +} + + +static void +opts_optional(Visitor *v, bool *present, const char *name, Error **errp) +{ +    OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); + +    /* we only support a single mandatory scalar field in a list node */ +    assert(ov->list_mode == LM_NONE); +    *present = (lookup_distinct(ov, name, NULL) != NULL); +} + + +OptsVisitor * +opts_visitor_new(const QemuOpts *opts) +{ +    OptsVisitor *ov; + +    ov = g_malloc0(sizeof *ov); + +    ov->visitor.start_struct = &opts_start_struct; +    ov->visitor.end_struct   = &opts_end_struct; + +    ov->visitor.start_list = &opts_start_list; +    ov->visitor.next_list  = &opts_next_list; +    ov->visitor.end_list   = &opts_end_list; + +    /* input_type_enum() covers both "normal" enums and union discriminators. +     * The union discriminator field is always generated as "type"; it should +     * match the "type" QemuOpt child of any QemuOpts. +     * +     * input_type_enum() will remove the looked-up key from the +     * "unprocessed_opts" hash even if the lookup fails, because the removal is +     * done earlier in opts_type_str(). This should be harmless. +     */ +    ov->visitor.type_enum = &input_type_enum; + +    ov->visitor.type_int    = &opts_type_int; +    ov->visitor.type_uint64 = &opts_type_uint64; +    ov->visitor.type_size   = &opts_type_size; +    ov->visitor.type_bool   = &opts_type_bool; +    ov->visitor.type_str    = &opts_type_str; + +    /* type_number() is not filled in, but this is not the first visitor to +     * skip some mandatory methods... */ + +    ov->visitor.optional = &opts_optional; + +    ov->opts_root = opts; + +    return ov; +} + + +void +opts_visitor_cleanup(OptsVisitor *ov) +{ +    if (ov->unprocessed_opts != NULL) { +        g_hash_table_destroy(ov->unprocessed_opts); +    } +    g_free(ov->fake_id_opt); +    g_free(ov); +} + + +Visitor * +opts_get_visitor(OptsVisitor *ov) +{ +    return &ov->visitor; +} diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c new file mode 100644 index 00000000..d7f92c5d --- /dev/null +++ b/qapi/qapi-dealloc-visitor.c @@ -0,0 +1,225 @@ +/* + * Dealloc Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + *  Michael Roth   <mdroth@linux.vnet.ibm.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 "qapi/dealloc-visitor.h" +#include "qemu/queue.h" +#include "qemu-common.h" +#include "qapi/qmp/types.h" +#include "qapi/visitor-impl.h" + +typedef struct StackEntry +{ +    void *value; +    bool is_list_head; +    QTAILQ_ENTRY(StackEntry) node; +} StackEntry; + +struct QapiDeallocVisitor +{ +    Visitor visitor; +    QTAILQ_HEAD(, StackEntry) stack; +    bool is_list_head; +}; + +static QapiDeallocVisitor *to_qov(Visitor *v) +{ +    return container_of(v, QapiDeallocVisitor, visitor); +} + +static void qapi_dealloc_push(QapiDeallocVisitor *qov, void *value) +{ +    StackEntry *e = g_malloc0(sizeof(*e)); + +    e->value = value; + +    /* see if we're just pushing a list head tracker */ +    if (value == NULL) { +        e->is_list_head = true; +    } +    QTAILQ_INSERT_HEAD(&qov->stack, e, node); +} + +static void *qapi_dealloc_pop(QapiDeallocVisitor *qov) +{ +    StackEntry *e = QTAILQ_FIRST(&qov->stack); +    QObject *value; +    QTAILQ_REMOVE(&qov->stack, e, node); +    value = e->value; +    g_free(e); +    return value; +} + +static void qapi_dealloc_start_struct(Visitor *v, void **obj, const char *kind, +                                      const char *name, size_t unused, +                                      Error **errp) +{ +    QapiDeallocVisitor *qov = to_qov(v); +    qapi_dealloc_push(qov, obj); +} + +static void qapi_dealloc_end_struct(Visitor *v, Error **errp) +{ +    QapiDeallocVisitor *qov = to_qov(v); +    void **obj = qapi_dealloc_pop(qov); +    if (obj) { +        g_free(*obj); +    } +} + +static void qapi_dealloc_start_implicit_struct(Visitor *v, +                                               void **obj, +                                               size_t size, +                                               Error **errp) +{ +    QapiDeallocVisitor *qov = to_qov(v); +    qapi_dealloc_push(qov, obj); +} + +static void qapi_dealloc_end_implicit_struct(Visitor *v, Error **errp) +{ +    QapiDeallocVisitor *qov = to_qov(v); +    void **obj = qapi_dealloc_pop(qov); +    if (obj) { +        g_free(*obj); +    } +} + +static void qapi_dealloc_start_list(Visitor *v, const char *name, Error **errp) +{ +    QapiDeallocVisitor *qov = to_qov(v); +    qapi_dealloc_push(qov, NULL); +} + +static GenericList *qapi_dealloc_next_list(Visitor *v, GenericList **listp, +                                           Error **errp) +{ +    GenericList *list = *listp; +    QapiDeallocVisitor *qov = to_qov(v); +    StackEntry *e = QTAILQ_FIRST(&qov->stack); + +    if (e && e->is_list_head) { +        e->is_list_head = false; +        return list; +    } + +    if (list) { +        list = list->next; +        g_free(*listp); +        return list; +    } + +    return NULL; +} + +static void qapi_dealloc_end_list(Visitor *v, Error **errp) +{ +    QapiDeallocVisitor *qov = to_qov(v); +    void *obj = qapi_dealloc_pop(qov); +    assert(obj == NULL); /* should've been list head tracker with no payload */ +} + +static void qapi_dealloc_type_str(Visitor *v, char **obj, const char *name, +                                  Error **errp) +{ +    if (obj) { +        g_free(*obj); +    } +} + +static void qapi_dealloc_type_int(Visitor *v, int64_t *obj, const char *name, +                                  Error **errp) +{ +} + +static void qapi_dealloc_type_bool(Visitor *v, bool *obj, const char *name, +                                   Error **errp) +{ +} + +static void qapi_dealloc_type_number(Visitor *v, double *obj, const char *name, +                                     Error **errp) +{ +} + +static void qapi_dealloc_type_size(Visitor *v, uint64_t *obj, const char *name, +                                   Error **errp) +{ +} + +static void qapi_dealloc_type_enum(Visitor *v, int *obj, +                                   const char * const strings[], +                                   const char *kind, const char *name, +                                   Error **errp) +{ +} + +/* If there's no data present, the dealloc visitor has nothing to free. + * Thus, indicate to visitor code that the subsequent union fields can + * be skipped. This is not an error condition, since the cleanup of the + * rest of an object can continue unhindered, so leave errp unset in + * these cases. + * + * NOTE: In cases where we're attempting to deallocate an object that + * may have missing fields, the field indicating the union type may + * be missing. In such a case, it's possible we don't have enough + * information to differentiate data_present == false from a case where + * data *is* present but happens to be a scalar with a value of 0. + * This is okay, since in the case of the dealloc visitor there's no + * work that needs to done in either situation. + * + * The current inability in QAPI code to more thoroughly verify a union + * type in such cases will likely need to be addressed if we wish to + * implement this interface for other types of visitors in the future, + * however. + */ +static bool qapi_dealloc_start_union(Visitor *v, bool data_present, +                                     Error **errp) +{ +    return data_present; +} + +Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v) +{ +    return &v->visitor; +} + +void qapi_dealloc_visitor_cleanup(QapiDeallocVisitor *v) +{ +    g_free(v); +} + +QapiDeallocVisitor *qapi_dealloc_visitor_new(void) +{ +    QapiDeallocVisitor *v; + +    v = g_malloc0(sizeof(*v)); + +    v->visitor.start_struct = qapi_dealloc_start_struct; +    v->visitor.end_struct = qapi_dealloc_end_struct; +    v->visitor.start_implicit_struct = qapi_dealloc_start_implicit_struct; +    v->visitor.end_implicit_struct = qapi_dealloc_end_implicit_struct; +    v->visitor.start_list = qapi_dealloc_start_list; +    v->visitor.next_list = qapi_dealloc_next_list; +    v->visitor.end_list = qapi_dealloc_end_list; +    v->visitor.type_enum = qapi_dealloc_type_enum; +    v->visitor.type_int = qapi_dealloc_type_int; +    v->visitor.type_bool = qapi_dealloc_type_bool; +    v->visitor.type_str = qapi_dealloc_type_str; +    v->visitor.type_number = qapi_dealloc_type_number; +    v->visitor.type_size = qapi_dealloc_type_size; +    v->visitor.start_union = qapi_dealloc_start_union; + +    QTAILQ_INIT(&v->stack); + +    return v; +} diff --git a/qapi/qapi-util.c b/qapi/qapi-util.c new file mode 100644 index 00000000..bcdc94d5 --- /dev/null +++ b/qapi/qapi-util.c @@ -0,0 +1,34 @@ +/* + * QAPI util functions + * + * Authors: + *  Hu Tao       <hutao@cn.fujitsu.com> + *  Peter Lieven <pl@kamp.de> + *  + * 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 "qemu-common.h" +#include "qapi/error.h" +#include "qapi/util.h" + +int qapi_enum_parse(const char * const lookup[], const char *buf, +                    int max, int def, Error **errp) +{ +    int i; + +    if (!buf) { +        return def; +    } + +    for (i = 0; i < max; i++) { +        if (!strcmp(buf, lookup[i])) { +            return i; +        } +    } + +    error_setg(errp, "invalid parameter value: %s", buf); +    return def; +} diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c new file mode 100644 index 00000000..5a7c9005 --- /dev/null +++ b/qapi/qapi-visit-core.c @@ -0,0 +1,313 @@ +/* + * Core Definitions for QAPI Visitor Classes + * + * Copyright IBM, Corp. 2011 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.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 "qemu-common.h" +#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qerror.h" +#include "qapi/visitor.h" +#include "qapi/visitor-impl.h" + +void visit_start_struct(Visitor *v, void **obj, const char *kind, +                        const char *name, size_t size, Error **errp) +{ +    v->start_struct(v, obj, kind, name, size, errp); +} + +void visit_end_struct(Visitor *v, Error **errp) +{ +    v->end_struct(v, errp); +} + +void visit_start_implicit_struct(Visitor *v, void **obj, size_t size, +                                 Error **errp) +{ +    if (v->start_implicit_struct) { +        v->start_implicit_struct(v, obj, size, errp); +    } +} + +void visit_end_implicit_struct(Visitor *v, Error **errp) +{ +    if (v->end_implicit_struct) { +        v->end_implicit_struct(v, errp); +    } +} + +void visit_start_list(Visitor *v, const char *name, Error **errp) +{ +    v->start_list(v, name, errp); +} + +GenericList *visit_next_list(Visitor *v, GenericList **list, Error **errp) +{ +    return v->next_list(v, list, errp); +} + +void visit_end_list(Visitor *v, Error **errp) +{ +    v->end_list(v, errp); +} + +bool visit_start_union(Visitor *v, bool data_present, Error **errp) +{ +    if (v->start_union) { +        return v->start_union(v, data_present, errp); +    } +    return true; +} + +void visit_end_union(Visitor *v, bool data_present, Error **errp) +{ +    if (v->end_union) { +        v->end_union(v, data_present, errp); +    } +} + +void visit_optional(Visitor *v, bool *present, const char *name, +                    Error **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 (v->get_next_type) { +        v->get_next_type(v, obj, qtypes, name, errp); +    } +} + +void visit_type_enum(Visitor *v, int *obj, const char * const strings[], +                     const char *kind, const char *name, Error **errp) +{ +    v->type_enum(v, obj, strings, kind, name, errp); +} + +void visit_type_int(Visitor *v, int64_t *obj, const char *name, Error **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 (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_setg(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 (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_setg(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 (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_setg(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 (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 (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_setg(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 (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_setg(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 (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_setg(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 (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 (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) +{ +    v->type_bool(v, obj, name, errp); +} + +void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp) +{ +    v->type_str(v, obj, name, errp); +} + +void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp) +{ +    v->type_number(v, obj, name, errp); +} + +void output_type_enum(Visitor *v, int *obj, const char * const strings[], +                      const char *kind, const char *name, +                      Error **errp) +{ +    int i = 0; +    int value = *obj; +    char *enum_str; + +    assert(strings); +    while (strings[i++] != NULL); +    if (value < 0 || value >= i - 1) { +        error_setg(errp, QERR_INVALID_PARAMETER, name ? name : "null"); +        return; +    } + +    enum_str = (char *)strings[value]; +    visit_type_str(v, &enum_str, name, errp); +} + +void input_type_enum(Visitor *v, int *obj, const char * const 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, &local_err); +    if (local_err) { +        error_propagate(errp, local_err); +        return; +    } + +    while (strings[value] != NULL) { +        if (strcmp(strings[value], enum_str) == 0) { +            break; +        } +        value++; +    } + +    if (strings[value] == NULL) { +        error_setg(errp, QERR_INVALID_PARAMETER, enum_str); +        g_free(enum_str); +        return; +    } + +    g_free(enum_str); +    *obj = value; +} diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c new file mode 100644 index 00000000..7bcc8608 --- /dev/null +++ b/qapi/qmp-dispatch.c @@ -0,0 +1,141 @@ +/* + * Core Definitions for QAPI/QMP Dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.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 "qapi/qmp/types.h" +#include "qapi/qmp/dispatch.h" +#include "qapi/qmp/json-parser.h" +#include "qapi-types.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" + +static QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp) +{ +    const QDictEntry *ent; +    const char *arg_name; +    const QObject *arg_obj; +    bool has_exec_key = false; +    QDict *dict = NULL; + +    if (qobject_type(request) != QTYPE_QDICT) { +        error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT, +                   "request is not a dictionary"); +        return NULL; +    } + +    dict = qobject_to_qdict(request); + +    for (ent = qdict_first(dict); ent; +         ent = qdict_next(dict, ent)) { +        arg_name = qdict_entry_key(ent); +        arg_obj = qdict_entry_value(ent); + +        if (!strcmp(arg_name, "execute")) { +            if (qobject_type(arg_obj) != QTYPE_QSTRING) { +                error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT_MEMBER, "execute", +                           "string"); +                return NULL; +            } +            has_exec_key = true; +        } else if (strcmp(arg_name, "arguments")) { +            error_setg(errp, QERR_QMP_EXTRA_MEMBER, arg_name); +            return NULL; +        } +    } + +    if (!has_exec_key) { +        error_setg(errp, QERR_QMP_BAD_INPUT_OBJECT, "execute"); +        return NULL; +    } + +    return dict; +} + +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) { +        return NULL; +    } + +    command = qdict_get_str(dict, "execute"); +    cmd = qmp_find_command(command); +    if (cmd == NULL) { +        error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND, +                  "The command %s has not been found", command); +        return NULL; +    } +    if (!cmd->enabled) { +        error_setg(errp, "The command %s has been disabled for this instance", +                   command); +        return NULL; +    } + +    if (!qdict_haskey(dict, "arguments")) { +        args = qdict_new(); +    } else { +        args = qdict_get_qdict(dict, "arguments"); +        QINCREF(args); +    } + +    switch (cmd->type) { +    case QCT_NORMAL: +        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; +    } + +    QDECREF(args); + +    return ret; +} + +QObject *qmp_build_error_object(Error *err) +{ +    return qobject_from_jsonf("{ 'class': %s, 'desc': %s }", +                              ErrorClass_lookup[error_get_class(err)], +                              error_get_pretty(err)); +} + +QObject *qmp_dispatch(QObject *request) +{ +    Error *err = NULL; +    QObject *ret; +    QDict *rsp; + +    ret = do_qmp_dispatch(request, &err); + +    rsp = qdict_new(); +    if (err) { +        qdict_put_obj(rsp, "error", qmp_build_error_object(err)); +        error_free(err); +    } else if (ret) { +        qdict_put_obj(rsp, "return", ret); +    } else { +        QDECREF(rsp); +        return NULL; +    } + +    return QOBJECT(rsp); +} diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c new file mode 100644 index 00000000..0d1ce0bd --- /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 new file mode 100644 index 00000000..e97b8a42 --- /dev/null +++ b/qapi/qmp-input-visitor.c @@ -0,0 +1,349 @@ +/* + * Input Visitor + * + * Copyright IBM, Corp. 2011 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.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 "qapi/qmp-input-visitor.h" +#include "qapi/visitor-impl.h" +#include "qemu/queue.h" +#include "qemu-common.h" +#include "qapi/qmp/types.h" +#include "qapi/qmp/qerror.h" + +#define QIV_STACK_SIZE 1024 + +typedef struct StackObject +{ +    QObject *obj; +    const QListEntry *entry; +    GHashTable *h; +} StackObject; + +struct QmpInputVisitor +{ +    Visitor visitor; +    StackObject stack[QIV_STACK_SIZE]; +    int nb_stack; +    bool strict; +}; + +static QmpInputVisitor *to_qiv(Visitor *v) +{ +    return container_of(v, QmpInputVisitor, visitor); +} + +static QObject *qmp_input_get_object(QmpInputVisitor *qiv, +                                     const char *name, +                                     bool consume) +{ +    QObject *qobj = qiv->stack[qiv->nb_stack - 1].obj; + +    if (qobj) { +        if (name && qobject_type(qobj) == QTYPE_QDICT) { +            if (qiv->stack[qiv->nb_stack - 1].h && consume) { +                g_hash_table_remove(qiv->stack[qiv->nb_stack - 1].h, name); +            } +            return qdict_get(qobject_to_qdict(qobj), name); +        } else if (qiv->stack[qiv->nb_stack - 1].entry) { +            return qlist_entry_obj(qiv->stack[qiv->nb_stack - 1].entry); +        } +    } + +    return qobj; +} + +static void qdict_add_key(const char *key, QObject *obj, void *opaque) +{ +    GHashTable *h = opaque; +    g_hash_table_insert(h, (gpointer) key, NULL); +} + +static void qmp_input_push(QmpInputVisitor *qiv, QObject *obj, Error **errp) +{ +    GHashTable *h; + +    if (qiv->nb_stack >= QIV_STACK_SIZE) { +        error_setg(errp, "An internal buffer overran"); +        return; +    } + +    qiv->stack[qiv->nb_stack].obj = obj; +    qiv->stack[qiv->nb_stack].entry = NULL; +    qiv->stack[qiv->nb_stack].h = NULL; + +    if (qiv->strict && qobject_type(obj) == QTYPE_QDICT) { +        h = g_hash_table_new(g_str_hash, g_str_equal); +        qdict_iter(qobject_to_qdict(obj), qdict_add_key, h); +        qiv->stack[qiv->nb_stack].h = h; +    } + +    qiv->nb_stack++; +} + +/** Only for qmp_input_pop. */ +static gboolean always_true(gpointer key, gpointer val, gpointer user_pkey) +{ +    *(const char **)user_pkey = (const char *)key; +    return TRUE; +} + +static void qmp_input_pop(QmpInputVisitor *qiv, Error **errp) +{ +    assert(qiv->nb_stack > 0); + +    if (qiv->strict) { +        GHashTable * const top_ht = qiv->stack[qiv->nb_stack - 1].h; +        if (top_ht) { +            if (g_hash_table_size(top_ht)) { +                const char *key; +                g_hash_table_find(top_ht, always_true, &key); +                error_setg(errp, QERR_QMP_EXTRA_MEMBER, key); +            } +            g_hash_table_unref(top_ht); +        } +    } + +    qiv->nb_stack--; +} + +static void qmp_input_start_struct(Visitor *v, void **obj, const char *kind, +                                   const char *name, size_t size, Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); +    QObject *qobj = qmp_input_get_object(qiv, name, true); +    Error *err = NULL; + +    if (!qobj || qobject_type(qobj) != QTYPE_QDICT) { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "QDict"); +        return; +    } + +    qmp_input_push(qiv, qobj, &err); +    if (err) { +        error_propagate(errp, err); +        return; +    } + +    if (obj) { +        *obj = g_malloc0(size); +    } +} + +static void qmp_input_end_struct(Visitor *v, Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); + +    qmp_input_pop(qiv, errp); +} + +static void qmp_input_start_implicit_struct(Visitor *v, void **obj, +                                            size_t size, Error **errp) +{ +    if (obj) { +        *obj = g_malloc0(size); +    } +} + +static void qmp_input_end_implicit_struct(Visitor *v, Error **errp) +{ +} + +static void qmp_input_start_list(Visitor *v, const char *name, Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); +    QObject *qobj = qmp_input_get_object(qiv, name, true); + +    if (!qobj || qobject_type(qobj) != QTYPE_QLIST) { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "list"); +        return; +    } + +    qmp_input_push(qiv, qobj, errp); +} + +static GenericList *qmp_input_next_list(Visitor *v, GenericList **list, +                                        Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); +    GenericList *entry; +    StackObject *so = &qiv->stack[qiv->nb_stack - 1]; +    bool first; + +    if (so->entry == NULL) { +        so->entry = qlist_first(qobject_to_qlist(so->obj)); +        first = true; +    } else { +        so->entry = qlist_next(so->entry); +        first = false; +    } + +    if (so->entry == NULL) { +        return NULL; +    } + +    entry = g_malloc0(sizeof(*entry)); +    if (first) { +        *list = entry; +    } else { +        (*list)->next = entry; +    } + +    return entry; +} + +static void qmp_input_end_list(Visitor *v, Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); + +    qmp_input_pop(qiv, errp); +} + +static void qmp_input_get_next_type(Visitor *v, int *kind, const int *qobjects, +                                    const char *name, Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); +    QObject *qobj = qmp_input_get_object(qiv, name, false); + +    if (!qobj) { +        error_setg(errp, QERR_MISSING_PARAMETER, name ? name : "null"); +        return; +    } +    *kind = qobjects[qobject_type(qobj)]; +} + +static void qmp_input_type_int(Visitor *v, int64_t *obj, const char *name, +                               Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); +    QObject *qobj = qmp_input_get_object(qiv, name, true); + +    if (!qobj || qobject_type(qobj) != QTYPE_QINT) { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "integer"); +        return; +    } + +    *obj = qint_get_int(qobject_to_qint(qobj)); +} + +static void qmp_input_type_bool(Visitor *v, bool *obj, const char *name, +                                Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); +    QObject *qobj = qmp_input_get_object(qiv, name, true); + +    if (!qobj || qobject_type(qobj) != QTYPE_QBOOL) { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "boolean"); +        return; +    } + +    *obj = qbool_get_bool(qobject_to_qbool(qobj)); +} + +static void qmp_input_type_str(Visitor *v, char **obj, const char *name, +                               Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); +    QObject *qobj = qmp_input_get_object(qiv, name, true); + +    if (!qobj || qobject_type(qobj) != QTYPE_QSTRING) { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "string"); +        return; +    } + +    *obj = g_strdup(qstring_get_str(qobject_to_qstring(qobj))); +} + +static void qmp_input_type_number(Visitor *v, double *obj, const char *name, +                                  Error **errp) +{ +    QmpInputVisitor *qiv = to_qiv(v); +    QObject *qobj = qmp_input_get_object(qiv, name, true); + +    if (!qobj || (qobject_type(qobj) != QTYPE_QFLOAT && +        qobject_type(qobj) != QTYPE_QINT)) { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "number"); +        return; +    } + +    if (qobject_type(qobj) == QTYPE_QINT) { +        *obj = qint_get_int(qobject_to_qint(qobj)); +    } else { +        *obj = qfloat_get_double(qobject_to_qfloat(qobj)); +    } +} + +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); + +    if (!qobj) { +        *present = false; +        return; +    } + +    *present = true; +} + +Visitor *qmp_input_get_visitor(QmpInputVisitor *v) +{ +    return &v->visitor; +} + +void qmp_input_visitor_cleanup(QmpInputVisitor *v) +{ +    qobject_decref(v->stack[0].obj); +    g_free(v); +} + +QmpInputVisitor *qmp_input_visitor_new(QObject *obj) +{ +    QmpInputVisitor *v; + +    v = g_malloc0(sizeof(*v)); + +    v->visitor.start_struct = qmp_input_start_struct; +    v->visitor.end_struct = qmp_input_end_struct; +    v->visitor.start_implicit_struct = qmp_input_start_implicit_struct; +    v->visitor.end_implicit_struct = qmp_input_end_implicit_struct; +    v->visitor.start_list = qmp_input_start_list; +    v->visitor.next_list = qmp_input_next_list; +    v->visitor.end_list = qmp_input_end_list; +    v->visitor.type_enum = input_type_enum; +    v->visitor.type_int = qmp_input_type_int; +    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.optional = qmp_input_optional; +    v->visitor.get_next_type = qmp_input_get_next_type; + +    qmp_input_push(v, obj, NULL); +    qobject_incref(obj); + +    return v; +} + +QmpInputVisitor *qmp_input_visitor_new_strict(QObject *obj) +{ +    QmpInputVisitor *v; + +    v = qmp_input_visitor_new(obj); +    v->strict = true; + +    return v; +} diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c new file mode 100644 index 00000000..efc19d58 --- /dev/null +++ b/qapi/qmp-output-visitor.c @@ -0,0 +1,240 @@ +/* + * Core Definitions for QAPI/QMP Command Registry + * + * Copyright IBM, Corp. 2011 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.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 "qapi/qmp-output-visitor.h" +#include "qapi/visitor-impl.h" +#include "qemu/queue.h" +#include "qemu-common.h" +#include "qapi/qmp/types.h" + +typedef struct QStackEntry +{ +    QObject *value; +    bool is_list_head; +    QTAILQ_ENTRY(QStackEntry) node; +} QStackEntry; + +typedef QTAILQ_HEAD(QStack, QStackEntry) QStack; + +struct QmpOutputVisitor +{ +    Visitor visitor; +    QStack stack; +}; + +#define qmp_output_add(qov, name, value) \ +    qmp_output_add_obj(qov, name, QOBJECT(value)) +#define qmp_output_push(qov, value) qmp_output_push_obj(qov, QOBJECT(value)) + +static QmpOutputVisitor *to_qov(Visitor *v) +{ +    return container_of(v, QmpOutputVisitor, visitor); +} + +static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value) +{ +    QStackEntry *e = g_malloc0(sizeof(*e)); + +    e->value = value; +    if (qobject_type(e->value) == QTYPE_QLIST) { +        e->is_list_head = true; +    } +    QTAILQ_INSERT_HEAD(&qov->stack, e, node); +} + +static QObject *qmp_output_pop(QmpOutputVisitor *qov) +{ +    QStackEntry *e = QTAILQ_FIRST(&qov->stack); +    QObject *value; +    QTAILQ_REMOVE(&qov->stack, e, node); +    value = e->value; +    g_free(e); +    return value; +} + +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; +} + +static QObject *qmp_output_last(QmpOutputVisitor *qov) +{ +    QStackEntry *e = QTAILQ_FIRST(&qov->stack); +    return e->value; +} + +static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name, +                               QObject *value) +{ +    QObject *cur; + +    if (QTAILQ_EMPTY(&qov->stack)) { +        qmp_output_push_obj(qov, value); +        return; +    } + +    cur = qmp_output_last(qov); + +    switch (qobject_type(cur)) { +    case QTYPE_QDICT: +        qdict_put_obj(qobject_to_qdict(cur), name, value); +        break; +    case QTYPE_QLIST: +        qlist_append_obj(qobject_to_qlist(cur), value); +        break; +    default: +        qobject_decref(qmp_output_pop(qov)); +        qmp_output_push_obj(qov, value); +        break; +    } +} + +static void qmp_output_start_struct(Visitor *v, void **obj, const char *kind, +                                    const char *name, size_t unused, +                                    Error **errp) +{ +    QmpOutputVisitor *qov = to_qov(v); +    QDict *dict = qdict_new(); + +    qmp_output_add(qov, name, dict); +    qmp_output_push(qov, dict); +} + +static void qmp_output_end_struct(Visitor *v, Error **errp) +{ +    QmpOutputVisitor *qov = to_qov(v); +    qmp_output_pop(qov); +} + +static void qmp_output_start_list(Visitor *v, const char *name, Error **errp) +{ +    QmpOutputVisitor *qov = to_qov(v); +    QList *list = qlist_new(); + +    qmp_output_add(qov, name, list); +    qmp_output_push(qov, list); +} + +static GenericList *qmp_output_next_list(Visitor *v, GenericList **listp, +                                         Error **errp) +{ +    GenericList *list = *listp; +    QmpOutputVisitor *qov = to_qov(v); +    QStackEntry *e = QTAILQ_FIRST(&qov->stack); + +    assert(e); +    if (e->is_list_head) { +        e->is_list_head = false; +        return list; +    } + +    return list ? list->next : NULL; +} + +static void qmp_output_end_list(Visitor *v, Error **errp) +{ +    QmpOutputVisitor *qov = to_qov(v); +    qmp_output_pop(qov); +} + +static void qmp_output_type_int(Visitor *v, int64_t *obj, const char *name, +                                Error **errp) +{ +    QmpOutputVisitor *qov = to_qov(v); +    qmp_output_add(qov, name, qint_from_int(*obj)); +} + +static void qmp_output_type_bool(Visitor *v, bool *obj, const char *name, +                                 Error **errp) +{ +    QmpOutputVisitor *qov = to_qov(v); +    qmp_output_add(qov, name, qbool_from_bool(*obj)); +} + +static void qmp_output_type_str(Visitor *v, char **obj, const char *name, +                                Error **errp) +{ +    QmpOutputVisitor *qov = to_qov(v); +    if (*obj) { +        qmp_output_add(qov, name, qstring_from_str(*obj)); +    } else { +        qmp_output_add(qov, name, qstring_from_str("")); +    } +} + +static void qmp_output_type_number(Visitor *v, double *obj, const char *name, +                                   Error **errp) +{ +    QmpOutputVisitor *qov = to_qov(v); +    qmp_output_add(qov, name, qfloat_from_double(*obj)); +} + +QObject *qmp_output_get_qobject(QmpOutputVisitor *qov) +{ +    QObject *obj = qmp_output_first(qov); +    if (obj) { +        qobject_incref(obj); +    } +    return obj; +} + +Visitor *qmp_output_get_visitor(QmpOutputVisitor *v) +{ +    return &v->visitor; +} + +void qmp_output_visitor_cleanup(QmpOutputVisitor *v) +{ +    QStackEntry *e, *tmp; + +    /* The bottom QStackEntry, if any, owns the root QObject. See the +     * qmp_output_push_obj() invocations in qmp_output_add_obj(). */ +    QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v); + +    QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) { +        QTAILQ_REMOVE(&v->stack, e, node); +        g_free(e); +    } + +    qobject_decref(root); +    g_free(v); +} + +QmpOutputVisitor *qmp_output_visitor_new(void) +{ +    QmpOutputVisitor *v; + +    v = g_malloc0(sizeof(*v)); + +    v->visitor.start_struct = qmp_output_start_struct; +    v->visitor.end_struct = qmp_output_end_struct; +    v->visitor.start_list = qmp_output_start_list; +    v->visitor.next_list = qmp_output_next_list; +    v->visitor.end_list = qmp_output_end_list; +    v->visitor.type_enum = output_type_enum; +    v->visitor.type_int = qmp_output_type_int; +    v->visitor.type_bool = qmp_output_type_bool; +    v->visitor.type_str = qmp_output_type_str; +    v->visitor.type_number = qmp_output_type_number; + +    QTAILQ_INIT(&v->stack); + +    return v; +} diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c new file mode 100644 index 00000000..3e4498a3 --- /dev/null +++ b/qapi/qmp-registry.c @@ -0,0 +1,91 @@ +/* + * Core Definitions for QAPI/QMP Dispatch + * + * Copyright IBM, Corp. 2011 + * + * Authors: + *  Anthony Liguori   <aliguori@us.ibm.com> + *  Michael Roth      <mdroth@us.ibm.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 <glib.h> +#include <string.h> +#include "qapi/qmp/dispatch.h" + +static QTAILQ_HEAD(QmpCommandList, QmpCommand) qmp_commands = +    QTAILQ_HEAD_INITIALIZER(qmp_commands); + +void qmp_register_command(const char *name, QmpCommandFunc *fn, +                          QmpCommandOptions options) +{ +    QmpCommand *cmd = g_malloc0(sizeof(*cmd)); + +    cmd->name = name; +    cmd->type = QCT_NORMAL; +    cmd->fn = fn; +    cmd->enabled = true; +    cmd->options = options; +    QTAILQ_INSERT_TAIL(&qmp_commands, cmd, node); +} + +QmpCommand *qmp_find_command(const char *name) +{ +    QmpCommand *cmd; + +    QTAILQ_FOREACH(cmd, &qmp_commands, node) { +        if (strcmp(cmd->name, name) == 0) { +            return cmd; +        } +    } +    return NULL; +} + +static void qmp_toggle_command(const char *name, bool enabled) +{ +    QmpCommand *cmd; + +    QTAILQ_FOREACH(cmd, &qmp_commands, node) { +        if (strcmp(cmd->name, name) == 0) { +            cmd->enabled = enabled; +            return; +        } +    } +} + +void qmp_disable_command(const char *name) +{ +    qmp_toggle_command(name, false); +} + +void qmp_enable_command(const char *name) +{ +    qmp_toggle_command(name, true); +} + +bool qmp_command_is_enabled(const QmpCommand *cmd) +{ +    return cmd->enabled; +} + +const char *qmp_command_name(const QmpCommand *cmd) +{ +    return cmd->name; +} + +bool qmp_has_success_response(const QmpCommand *cmd) +{ +    return !(cmd->options & QCO_NO_SUCCESS_RESP); +} + +void qmp_for_each_command(qmp_cmd_callback_fn fn, void *opaque) +{ +    QmpCommand *cmd; + +    QTAILQ_FOREACH(cmd, &qmp_commands, node) { +        fn(cmd, opaque); +    } +} diff --git a/qapi/rocker.json b/qapi/rocker.json new file mode 100644 index 00000000..2fe7fdfa --- /dev/null +++ b/qapi/rocker.json @@ -0,0 +1,286 @@ +## +# @Rocker: +# +# Rocker switch information. +# +# @name: switch name +# +# @id: switch ID +# +# @ports: number of front-panel ports +# +# Since: 2.4 +## +{ 'struct': 'RockerSwitch', +  'data': { 'name': 'str', 'id': 'uint64', 'ports': 'uint32' } } + +## +# @query-rocker: +# +# Return rocker switch information. +# +# Returns: @Rocker information +# +# Since: 2.4 +## +{ 'command': 'query-rocker', +  'data': { 'name': 'str' }, +  'returns': 'RockerSwitch' } + +## +# @RockerPortDuplex: +# +# An eumeration of port duplex states. +# +# @half: half duplex +# +# @full: full duplex +# +# Since: 2.4 +## +{ 'enum': 'RockerPortDuplex', 'data': [ 'half', 'full' ] } + +## +# @RockerPortAutoneg: +# +# An eumeration of port autoneg states. +# +# @off: autoneg is off +# +# @on: autoneg is on +# +# Since: 2.4 +## +{ 'enum': 'RockerPortAutoneg', 'data': [ 'off', 'on' ] } + +## +# @RockerPort: +# +# Rocker switch port information. +# +# @name: port name +# +# @enabled: port is enabled for I/O +# +# @link-up: physical link is UP on port +# +# @speed: port link speed in Mbps +# +# @duplex: port link duplex +# +# @autoneg: port link autoneg +# +# Since: 2.4 +## +{ 'struct': 'RockerPort', +  'data': { 'name': 'str', 'enabled': 'bool', 'link-up': 'bool', +            'speed': 'uint32', 'duplex': 'RockerPortDuplex', +            'autoneg': 'RockerPortAutoneg' } } + +## +# @query-rocker-ports: +# +# Return rocker switch information. +# +# Returns: @Rocker information +# +# Since: 2.4 +## +{ 'command': 'query-rocker-ports', +  'data': { 'name': 'str' }, +  'returns': ['RockerPort'] } + +## +# @RockerOfDpaFlowKey: +# +# Rocker switch OF-DPA flow key +# +# @priority: key priority, 0 being lowest priority +# +# @tbl-id: flow table ID +# +# @in-pport: #optional physical input port +# +# @tunnel-id: #optional tunnel ID +# +# @vlan-id: #optional VLAN ID +# +# @eth-type: #optional Ethernet header type +# +# @eth-src: #optional Ethernet header source MAC address +# +# @eth-dst: #optional Ethernet header destination MAC address +# +# @ip-proto: #optional IP Header protocol field +# +# @ip-tos: #optional IP header TOS field +# +# @ip-dst: #optional IP header destination address +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the flow key depending if they're relevant to the flow key. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlowKey', +  'data' : { 'priority': 'uint32', 'tbl-id': 'uint32', '*in-pport': 'uint32', +             '*tunnel-id': 'uint32', '*vlan-id': 'uint16', +             '*eth-type': 'uint16', '*eth-src': 'str', '*eth-dst': 'str', +             '*ip-proto': 'uint8', '*ip-tos': 'uint8', '*ip-dst': 'str' } } + +## +# @RockerOfDpaFlowMask: +# +# Rocker switch OF-DPA flow mask +# +# @in-pport: #optional physical input port +# +# @tunnel-id: #optional tunnel ID +# +# @vlan-id: #optional VLAN ID +# +# @eth-src: #optional Ethernet header source MAC address +# +# @eth-dst: #optional Ethernet header destination MAC address +# +# @ip-proto: #optional IP Header protocol field +# +# @ip-tos: #optional IP header TOS field +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the flow mask depending if they're relevant to the flow mask. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlowMask', +  'data' : { '*in-pport': 'uint32', '*tunnel-id': 'uint32', +             '*vlan-id': 'uint16', '*eth-src': 'str', '*eth-dst': 'str', +             '*ip-proto': 'uint8', '*ip-tos': 'uint8' } } + +## +# @RockerOfDpaFlowAction: +# +# Rocker switch OF-DPA flow action +# +# @goto-tbl: #optional next table ID +# +# @group-id: #optional group ID +# +# @tunnel-lport: #optional tunnel logical port ID +# +# @vlan-id: #optional VLAN ID +# +# @new-vlan-id: #optional new VLAN ID +# +# @out-pport: #optional physical output port +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the flow action depending if they're relevant to the flow action. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlowAction', +  'data' : { '*goto-tbl': 'uint32', '*group-id': 'uint32', +             '*tunnel-lport': 'uint32', '*vlan-id': 'uint16', +             '*new-vlan-id': 'uint16', '*out-pport': 'uint32' } } + +## +# @RockerOfDpaFlow: +# +# Rocker switch OF-DPA flow +# +# @cookie: flow unique cookie ID +# +# @hits: count of matches (hits) on flow +# +# @key: flow key +# +# @mask: flow mask +# +# @action: flow action +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlow', +  'data': { 'cookie': 'uint64', 'hits': 'uint64', 'key': 'RockerOfDpaFlowKey', +            'mask': 'RockerOfDpaFlowMask', 'action': 'RockerOfDpaFlowAction' } } + +## +# @query-rocker-of-dpa-flows: +# +# Return rocker OF-DPA flow information. +# +# @name: switch name +# +# @tbl-id: #optional flow table ID.  If tbl-id is not specified, returns +# flow information for all tables. +# +# Returns: @Rocker OF-DPA flow information +# +# Since: 2.4 +## +{ 'command': 'query-rocker-of-dpa-flows', +  'data': { 'name': 'str', '*tbl-id': 'uint32' }, +  'returns': ['RockerOfDpaFlow'] } + +## +# @RockerOfDpaGroup: +# +# Rocker switch OF-DPA group +# +# @id: group unique ID +# +# @type: group type +# +# @vlan-id: #optional VLAN ID +# +# @pport: #optional physical port number +# +# @index: #optional group index, unique with group type +# +# @out-pport: #optional output physical port number +# +# @group-id: #optional next group ID +# +# @set-vlan-id: #optional VLAN ID to set +# +# @pop-vlan: #optional pop VLAN headr from packet +# +# @group-ids: #optional list of next group IDs +# +# @set-eth-src: #optional set source MAC address in Ethernet header +# +# @set-eth-dst: #optional set destination MAC address in Ethernet header +# +# @ttl-check: #optional perform TTL check +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the group depending if they're relevant to the group type. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaGroup', +  'data': { 'id': 'uint32',  'type': 'uint8', '*vlan-id': 'uint16', +            '*pport': 'uint32', '*index': 'uint32', '*out-pport': 'uint32', +            '*group-id': 'uint32', '*set-vlan-id': 'uint16', +            '*pop-vlan': 'uint8', '*group-ids': ['uint32'], +            '*set-eth-src': 'str', '*set-eth-dst': 'str', +            '*ttl-check': 'uint8' } } + +## +# @query-rocker-of-dpa-groups: +# +# Return rocker OF-DPA group information. +# +# @name: switch name +# +# @type: #optional group type.  If type is not specified, returns +# group information for all group types. +# +# Returns: @Rocker OF-DPA group information +# +# Since: 2.4 +## +{ 'command': 'query-rocker-of-dpa-groups', +  'data': { 'name': 'str', '*type': 'uint8' }, +  'returns': ['RockerOfDpaGroup'] } diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c new file mode 100644 index 00000000..bbd6a545 --- /dev/null +++ b/qapi/string-input-visitor.c @@ -0,0 +1,347 @@ +/* + * String parsing visitor + * + * Copyright Red Hat, Inc. 2012 + * + * Author: Paolo Bonzini <pbonzini@redhat.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 "qemu-common.h" +#include "qapi/string-input-visitor.h" +#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); + +    if (!siv->string) { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "integer"); +        return; +    } + +    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_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, +               "an int64 value or range"); +} + +static void parse_type_size(Visitor *v, uint64_t *obj, const char *name, +                            Error **errp) +{ +    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); +    Error *err = NULL; +    uint64_t val; + +    if (siv->string) { +        parse_option_size(name, siv->string, &val, &err); +    } else { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "size"); +        return; +    } +    if (err) { +        error_propagate(errp, err); +        return; +    } + +    *obj = val; +} + +static void parse_type_bool(Visitor *v, bool *obj, const char *name, +                            Error **errp) +{ +    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + +    if (siv->string) { +        if (!strcasecmp(siv->string, "on") || +            !strcasecmp(siv->string, "yes") || +            !strcasecmp(siv->string, "true")) { +            *obj = true; +            return; +        } +        if (!strcasecmp(siv->string, "off") || +            !strcasecmp(siv->string, "no") || +            !strcasecmp(siv->string, "false")) { +            *obj = false; +            return; +        } +    } + +    error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +               "boolean"); +} + +static void parse_type_str(Visitor *v, char **obj, const char *name, +                           Error **errp) +{ +    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); +    if (siv->string) { +        *obj = g_strdup(siv->string); +    } else { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "string"); +    } +} + +static void parse_type_number(Visitor *v, double *obj, const char *name, +                              Error **errp) +{ +    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); +    char *endp = (char *) siv->string; +    double val; + +    errno = 0; +    if (siv->string) { +        val = strtod(siv->string, &endp); +    } +    if (!siv->string || errno || endp == siv->string || *endp) { +        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", +                   "number"); +        return; +    } + +    *obj = val; +} + +static void parse_optional(Visitor *v, bool *present, const char *name, +                           Error **errp) +{ +    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); + +    if (!siv->string) { +        *present = false; +        return; +    } + +    *present = true; +} + +Visitor *string_input_get_visitor(StringInputVisitor *v) +{ +    return &v->visitor; +} + +void string_input_visitor_cleanup(StringInputVisitor *v) +{ +    g_list_foreach(v->ranges, free_range, NULL); +    g_list_free(v->ranges); +    g_free(v); +} + +StringInputVisitor *string_input_visitor_new(const char *str) +{ +    StringInputVisitor *v; + +    v = g_malloc0(sizeof(*v)); + +    v->visitor.type_enum = input_type_enum; +    v->visitor.type_int = parse_type_int; +    v->visitor.type_size = parse_type_size; +    v->visitor.type_bool = parse_type_bool; +    v->visitor.type_str = parse_type_str; +    v->visitor.type_number = parse_type_number; +    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 new file mode 100644 index 00000000..b86ce2cd --- /dev/null +++ b/qapi/string-output-visitor.c @@ -0,0 +1,353 @@ +/* + * String printing Visitor + * + * Copyright Red Hat, Inc. 2012 + * + * Author: Paolo Bonzini <pbonzini@redhat.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 "qemu-common.h" +#include "qapi/string-output-visitor.h" +#include "qapi/visitor-impl.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; +    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) +{ +    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); +    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) { +        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, ")"); +    } +} + +static void print_type_size(Visitor *v, uint64_t *obj, const char *name, +                           Error **errp) +{ +    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); +    static const char suffixes[] = { 'B', 'K', 'M', 'G', 'T', 'P', 'E' }; +    uint64_t div, val; +    char *out; +    int i; + +    if (!sov->human) { +        out = g_strdup_printf("%"PRIu64, *obj); +        string_output_set(sov, out); +        return; +    } + +    val = *obj; + +    /* The exponent (returned in i) minus one gives us +     * floor(log2(val * 1024 / 1000).  The correction makes us +     * switch to the higher power when the integer part is >= 1000. +     */ +    frexp(val / (1000.0 / 1024.0), &i); +    i = (i - 1) / 10; +    assert(i < ARRAY_SIZE(suffixes)); +    div = 1ULL << (i * 10); + +    out = g_strdup_printf("%"PRIu64" (%0.3g %c%s)", val, +                          (double)val/div, suffixes[i], i ? "iB" : ""); +    string_output_set(sov, out); +} + +static void print_type_bool(Visitor *v, bool *obj, const char *name, +                            Error **errp) +{ +    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); +    string_output_set(sov, g_strdup(*obj ? "true" : "false")); +} + +static void print_type_str(Visitor *v, char **obj, const char *name, +                           Error **errp) +{ +    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); +    char *out; + +    if (sov->human) { +        out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>"); +    } else { +        out = g_strdup(*obj ? *obj : ""); +    } +    string_output_set(sov, out); +} + +static void print_type_number(Visitor *v, double *obj, const char *name, +                              Error **errp) +{ +    StringOutputVisitor *sov = DO_UPCAST(StringOutputVisitor, visitor, v); +    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 = g_string_free(sov->string, false); +    sov->string = NULL; +    return string; +} + +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) +{ +    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); +} + +StringOutputVisitor *string_output_visitor_new(bool human) +{ +    StringOutputVisitor *v; + +    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; +    v->visitor.type_size = print_type_size; +    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; +} diff --git a/qapi/trace.json b/qapi/trace.json new file mode 100644 index 00000000..01b0a52a --- /dev/null +++ b/qapi/trace.json @@ -0,0 +1,65 @@ +# -*- mode: python -*- +# +# Copyright (C) 2011-2014 LluĂs Vilanova <vilanova@ac.upc.edu> +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + + +## +# @TraceEventState: +# +# State of a tracing event. +# +# @unavailable: The event is statically disabled. +# +# @disabled: The event is dynamically disabled. +# +# @enabled: The event is dynamically enabled. +# +# Since 2.2 +## +{ 'enum': 'TraceEventState', +  'data': ['unavailable', 'disabled', 'enabled'] } + +## +# @TraceEventInfo: +# +# Information of a tracing event. +# +# @name: Event name. +# @state: Tracing state. +# +# Since 2.2 +## +{ 'struct': 'TraceEventInfo', +  'data': {'name': 'str', 'state': 'TraceEventState'} } + +## +# @trace-event-get-state: +# +# Query the state of events. +# +# @name: Event name pattern (case-sensitive glob). +# +# Returns: a list of @TraceEventInfo for the matching events +# +# Since 2.2 +## +{ 'command': 'trace-event-get-state', +  'data': {'name': 'str'}, +  'returns': ['TraceEventInfo'] } + +## +# @trace-event-set-state: +# +# Set the dynamic tracing state of events. +# +# @name: Event name pattern (case-sensitive glob). +# @enable: Whether to enable tracing. +# @ignore-unavailable: #optional Do not match unavailable events with @name. +# +# Since 2.2 +## +{ 'command': 'trace-event-set-state', +  'data': {'name': 'str', 'enable': 'bool', '*ignore-unavailable': 'bool'} }  | 
