mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-04 16:23:55 -06:00
QAPI patches for 2018-12-13
-----BEGIN PGP SIGNATURE----- iQIcBAABAgAGBQJcE0VvAAoJEDhwtADrkYZTLCkP/RvRR9iTcoM98kcFNjqBZRQa rUbSNBavxwzutPiT40WcNhg7hc0Uaptve8oMkGcfyyTh9UyhdOe8WNPxTos96vYt GtUhNhknGlvP4A7Zjs6KIIhl084MtPkpuPERkXZL4lgNrIw8BrFoj5hkZ3UIvItf 14oA1o6Zf9UxN1Yt12lZnG9N8t4ld5IKhkXh/FQ6OJNHz9GrhPq4A7vd4ipBRBjt PjvXVOYCEkiHRfJ3Qv5Thk2C1xzLRFusA5ff1rju324KGPoM8oZ+xGSUVqD0hhMe Kpzv4a6HV7SuM1fqJoZrF87VOhAO9bpxzIHUp83FhpKGDH4xqppDWYno/+9imPDA DAHUaOeaKpX6O4ttB96jRwTEOAbq3TzPqtYiyRaXhbtCc0dKi0HxHmIpwS4KNkHK Y3VuoTavarMfuLl2gDO+9PJhHxol8g0oYiaxXddW0svgnSM3xBTz/hGE2duStHTb DSWDVB/oVIOyR8eWSglUnc+OOJrxSkiaJelSU730Uc6kIk7hiY8PFQiwqebsI6uq IOABDG1/W0FkSRNl5QwXnGlD0eUzl1ySm2zvsgvJrC8ooAhzjjWdkcwtEdEYxlUj KqH+8ZFP+mOckrW9boqYPVqOL4GzNMnK23vEoidurhyShsmiCTyk+jckiJrl/IMy OlwA850MKVJ3W3+knR0I =bymN -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2018-12-13-v2' into staging QAPI patches for 2018-12-13 # gpg: Signature made Fri 14 Dec 2018 05:53:51 GMT # gpg: using RSA key 3870B400EB918653 # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" # Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653 * remotes/armbru/tags/pull-qapi-2018-12-13-v2: (32 commits) qapi: add conditions to REPLICATION type/commands on the schema qapi: add more conditions to SPICE qapi: add condition to variants documentation qapi: add 'If:' condition to struct members documentation qapi: add 'If:' condition to enum values documentation qapi: Add #if conditions to generated code members qapi: add 'if' to alternate members qapi: add 'if' to union members qapi: Add 'if' to implicit struct members qapi: add a dictionary form for TYPE qapi-events: add 'if' condition to implicit event enum qapi: add 'if' to enum members qapi: add a dictionary form with 'name' key for enum members qapi: improve reporting of unknown or missing keys qapi: factor out checking for keys tests: print enum type members more like object type members qapi: change enum visitor and gen_enum* to take QAPISchemaMember qapi: Do not define enumeration value explicitly qapi: break long lines at 'data' member qapi: rename QAPISchemaEnumType.values to .members ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
81781be3c9
91 changed files with 1166 additions and 510 deletions
|
@ -1143,8 +1143,10 @@
|
|||
# This command is now obsolete and will always return an error since 2.10
|
||||
#
|
||||
##
|
||||
{ 'command': 'block_passwd', 'data': {'*device': 'str',
|
||||
'*node-name': 'str', 'password': 'str'} }
|
||||
{ 'command': 'block_passwd',
|
||||
'data': { '*device': 'str',
|
||||
'*node-name': 'str',
|
||||
'password': 'str' } }
|
||||
|
||||
##
|
||||
# @block_resize:
|
||||
|
@ -1171,9 +1173,10 @@
|
|||
# <- { "return": {} }
|
||||
#
|
||||
##
|
||||
{ 'command': 'block_resize', 'data': { '*device': 'str',
|
||||
'*node-name': 'str',
|
||||
'size': 'int' }}
|
||||
{ 'command': 'block_resize',
|
||||
'data': { '*device': 'str',
|
||||
'*node-name': 'str',
|
||||
'size': 'int' } }
|
||||
|
||||
##
|
||||
# @NewImageMode:
|
||||
|
@ -2620,7 +2623,9 @@
|
|||
'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster',
|
||||
'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks',
|
||||
'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow',
|
||||
'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog',
|
||||
'qcow2', 'qed', 'quorum', 'raw', 'rbd',
|
||||
{ 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
|
||||
'sheepdog',
|
||||
'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] }
|
||||
|
||||
##
|
||||
|
@ -3377,7 +3382,8 @@
|
|||
#
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ] }
|
||||
{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ],
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @BlockdevOptionsReplication:
|
||||
|
@ -3395,7 +3401,8 @@
|
|||
{ 'struct': 'BlockdevOptionsReplication',
|
||||
'base': 'BlockdevOptionsGenericFormat',
|
||||
'data': { 'mode': 'ReplicationMode',
|
||||
'*top-id': 'str' } }
|
||||
'*top-id': 'str' },
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @NFSTransport:
|
||||
|
@ -3711,7 +3718,8 @@
|
|||
'quorum': 'BlockdevOptionsQuorum',
|
||||
'raw': 'BlockdevOptionsRaw',
|
||||
'rbd': 'BlockdevOptionsRbd',
|
||||
'replication':'BlockdevOptionsReplication',
|
||||
'replication': { 'type': 'BlockdevOptionsReplication',
|
||||
'if': 'defined(CONFIG_REPLICATION)' },
|
||||
'sheepdog': 'BlockdevOptionsSheepdog',
|
||||
'ssh': 'BlockdevOptionsSsh',
|
||||
'throttle': 'BlockdevOptionsThrottle',
|
||||
|
|
150
qapi/char.json
150
qapi/char.json
|
@ -25,9 +25,10 @@
|
|||
#
|
||||
# Since: 0.14.0
|
||||
##
|
||||
{ 'struct': 'ChardevInfo', 'data': {'label': 'str',
|
||||
'filename': 'str',
|
||||
'frontend-open': 'bool'} }
|
||||
{ 'struct': 'ChardevInfo',
|
||||
'data': { 'label': 'str',
|
||||
'filename': 'str',
|
||||
'frontend-open': 'bool' } }
|
||||
|
||||
##
|
||||
# @query-chardev:
|
||||
|
@ -152,7 +153,8 @@
|
|||
#
|
||||
##
|
||||
{ 'command': 'ringbuf-write',
|
||||
'data': {'device': 'str', 'data': 'str',
|
||||
'data': { 'device': 'str',
|
||||
'data': 'str',
|
||||
'*format': 'DataFormat'} }
|
||||
|
||||
##
|
||||
|
@ -202,8 +204,9 @@
|
|||
#
|
||||
# Since: 2.6
|
||||
##
|
||||
{ 'struct': 'ChardevCommon', 'data': { '*logfile': 'str',
|
||||
'*logappend': 'bool' } }
|
||||
{ 'struct': 'ChardevCommon',
|
||||
'data': { '*logfile': 'str',
|
||||
'*logappend': 'bool' } }
|
||||
|
||||
##
|
||||
# @ChardevFile:
|
||||
|
@ -217,9 +220,10 @@
|
|||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'struct': 'ChardevFile', 'data': { '*in' : 'str',
|
||||
'out' : 'str',
|
||||
'*append': 'bool' },
|
||||
{ 'struct': 'ChardevFile',
|
||||
'data': { '*in': 'str',
|
||||
'out': 'str',
|
||||
'*append': 'bool' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
|
@ -232,7 +236,8 @@
|
|||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' },
|
||||
{ 'struct': 'ChardevHostdev',
|
||||
'data': { 'device': 'str' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
|
@ -260,15 +265,16 @@
|
|||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'struct': 'ChardevSocket', 'data': { 'addr' : 'SocketAddressLegacy',
|
||||
'*tls-creds' : 'str',
|
||||
'*server' : 'bool',
|
||||
'*wait' : 'bool',
|
||||
'*nodelay' : 'bool',
|
||||
'*telnet' : 'bool',
|
||||
'*tn3270' : 'bool',
|
||||
'*websocket' : 'bool',
|
||||
'*reconnect' : 'int' },
|
||||
{ 'struct': 'ChardevSocket',
|
||||
'data': { 'addr': 'SocketAddressLegacy',
|
||||
'*tls-creds': 'str',
|
||||
'*server': 'bool',
|
||||
'*wait': 'bool',
|
||||
'*nodelay': 'bool',
|
||||
'*telnet': 'bool',
|
||||
'*tn3270': 'bool',
|
||||
'*websocket': 'bool',
|
||||
'*reconnect': 'int' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
|
@ -281,8 +287,9 @@
|
|||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevUdp', 'data': { 'remote' : 'SocketAddressLegacy',
|
||||
'*local' : 'SocketAddressLegacy' },
|
||||
{ 'struct': 'ChardevUdp',
|
||||
'data': { 'remote': 'SocketAddressLegacy',
|
||||
'*local': 'SocketAddressLegacy' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
|
@ -294,7 +301,8 @@
|
|||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' },
|
||||
{ 'struct': 'ChardevMux',
|
||||
'data': { 'chardev': 'str' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
|
@ -308,7 +316,8 @@
|
|||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' },
|
||||
{ 'struct': 'ChardevStdio',
|
||||
'data': { '*signal': 'bool' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
|
||||
|
@ -321,9 +330,10 @@
|
|||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' },
|
||||
'base': 'ChardevCommon' }
|
||||
# TODO: 'if': 'defined(CONFIG_SPICE)'
|
||||
{ 'struct': 'ChardevSpiceChannel',
|
||||
'data': { 'type': 'str' },
|
||||
'base': 'ChardevCommon',
|
||||
'if': 'defined(CONFIG_SPICE)' }
|
||||
|
||||
##
|
||||
# @ChardevSpicePort:
|
||||
|
@ -334,9 +344,10 @@
|
|||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' },
|
||||
'base': 'ChardevCommon' }
|
||||
# TODO: 'if': 'defined(CONFIG_SPICE)'
|
||||
{ 'struct': 'ChardevSpicePort',
|
||||
'data': { 'fqdn': 'str' },
|
||||
'base': 'ChardevCommon',
|
||||
'if': 'defined(CONFIG_SPICE)' }
|
||||
|
||||
##
|
||||
# @ChardevVC:
|
||||
|
@ -350,10 +361,11 @@
|
|||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevVC', 'data': { '*width' : 'int',
|
||||
'*height' : 'int',
|
||||
'*cols' : 'int',
|
||||
'*rows' : 'int' },
|
||||
{ 'struct': 'ChardevVC',
|
||||
'data': { '*width': 'int',
|
||||
'*height': 'int',
|
||||
'*cols': 'int',
|
||||
'*rows': 'int' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
|
@ -365,7 +377,8 @@
|
|||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' },
|
||||
{ 'struct': 'ChardevRingbuf',
|
||||
'data': { '*size': 'int' },
|
||||
'base': 'ChardevCommon' }
|
||||
|
||||
##
|
||||
|
@ -375,29 +388,30 @@
|
|||
#
|
||||
# Since: 1.4 (testdev since 2.2, wctablet since 2.9)
|
||||
##
|
||||
{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile',
|
||||
'serial' : 'ChardevHostdev',
|
||||
'parallel': 'ChardevHostdev',
|
||||
'pipe' : 'ChardevHostdev',
|
||||
'socket' : 'ChardevSocket',
|
||||
'udp' : 'ChardevUdp',
|
||||
'pty' : 'ChardevCommon',
|
||||
'null' : 'ChardevCommon',
|
||||
'mux' : 'ChardevMux',
|
||||
'msmouse': 'ChardevCommon',
|
||||
'wctablet' : 'ChardevCommon',
|
||||
'braille': 'ChardevCommon',
|
||||
'testdev': 'ChardevCommon',
|
||||
'stdio' : 'ChardevStdio',
|
||||
'console': 'ChardevCommon',
|
||||
'spicevmc': 'ChardevSpiceChannel',
|
||||
# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' },
|
||||
'spiceport': 'ChardevSpicePort',
|
||||
# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' },
|
||||
'vc' : 'ChardevVC',
|
||||
'ringbuf': 'ChardevRingbuf',
|
||||
# next one is just for compatibility
|
||||
'memory' : 'ChardevRingbuf' } }
|
||||
{ 'union': 'ChardevBackend',
|
||||
'data': { 'file': 'ChardevFile',
|
||||
'serial': 'ChardevHostdev',
|
||||
'parallel': 'ChardevHostdev',
|
||||
'pipe': 'ChardevHostdev',
|
||||
'socket': 'ChardevSocket',
|
||||
'udp': 'ChardevUdp',
|
||||
'pty': 'ChardevCommon',
|
||||
'null': 'ChardevCommon',
|
||||
'mux': 'ChardevMux',
|
||||
'msmouse': 'ChardevCommon',
|
||||
'wctablet': 'ChardevCommon',
|
||||
'braille': 'ChardevCommon',
|
||||
'testdev': 'ChardevCommon',
|
||||
'stdio': 'ChardevStdio',
|
||||
'console': 'ChardevCommon',
|
||||
'spicevmc': { 'type': 'ChardevSpiceChannel',
|
||||
'if': 'defined(CONFIG_SPICE)' },
|
||||
'spiceport': { 'type': 'ChardevSpicePort',
|
||||
'if': 'defined(CONFIG_SPICE)' },
|
||||
'vc': 'ChardevVC',
|
||||
'ringbuf': 'ChardevRingbuf',
|
||||
# next one is just for compatibility
|
||||
'memory': 'ChardevRingbuf' } }
|
||||
|
||||
##
|
||||
# @ChardevReturn:
|
||||
|
@ -409,7 +423,8 @@
|
|||
#
|
||||
# Since: 1.4
|
||||
##
|
||||
{ 'struct' : 'ChardevReturn', 'data': { '*pty' : 'str' } }
|
||||
{ 'struct' : 'ChardevReturn',
|
||||
'data': { '*pty': 'str' } }
|
||||
|
||||
##
|
||||
# @chardev-add:
|
||||
|
@ -442,8 +457,9 @@
|
|||
# <- { "return": { "pty" : "/dev/pty/42" } }
|
||||
#
|
||||
##
|
||||
{ 'command': 'chardev-add', 'data': {'id' : 'str',
|
||||
'backend' : 'ChardevBackend' },
|
||||
{ 'command': 'chardev-add',
|
||||
'data': { 'id': 'str',
|
||||
'backend': 'ChardevBackend' },
|
||||
'returns': 'ChardevReturn' }
|
||||
|
||||
##
|
||||
|
@ -482,8 +498,9 @@
|
|||
# <- {"return": {}}
|
||||
#
|
||||
##
|
||||
{ 'command': 'chardev-change', 'data': {'id' : 'str',
|
||||
'backend' : 'ChardevBackend' },
|
||||
{ 'command': 'chardev-change',
|
||||
'data': { 'id': 'str',
|
||||
'backend': 'ChardevBackend' },
|
||||
'returns': 'ChardevReturn' }
|
||||
|
||||
##
|
||||
|
@ -503,7 +520,8 @@
|
|||
# <- { "return": {} }
|
||||
#
|
||||
##
|
||||
{ 'command': 'chardev-remove', 'data': {'id': 'str'} }
|
||||
{ 'command': 'chardev-remove',
|
||||
'data': { 'id': 'str' } }
|
||||
|
||||
##
|
||||
# @chardev-send-break:
|
||||
|
@ -522,7 +540,8 @@
|
|||
# <- { "return": {} }
|
||||
#
|
||||
##
|
||||
{ 'command': 'chardev-send-break', 'data': {'id': 'str'} }
|
||||
{ 'command': 'chardev-send-break',
|
||||
'data': { 'id': 'str' } }
|
||||
|
||||
##
|
||||
# @VSERPORT_CHANGE:
|
||||
|
@ -543,4 +562,5 @@
|
|||
#
|
||||
##
|
||||
{ 'event': 'VSERPORT_CHANGE',
|
||||
'data': { 'id': 'str', 'open': 'bool' } }
|
||||
'data': { 'id': 'str',
|
||||
'open': 'bool' } }
|
||||
|
|
|
@ -1257,7 +1257,8 @@
|
|||
# Since: 2.9
|
||||
##
|
||||
{ 'command': 'xen-set-replication',
|
||||
'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } }
|
||||
'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' },
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @ReplicationStatus:
|
||||
|
@ -1272,7 +1273,8 @@
|
|||
# Since: 2.9
|
||||
##
|
||||
{ 'struct': 'ReplicationStatus',
|
||||
'data': { 'error': 'bool', '*desc': 'str' } }
|
||||
'data': { 'error': 'bool', '*desc': 'str' },
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @query-xen-replication-status:
|
||||
|
@ -1289,7 +1291,8 @@
|
|||
# Since: 2.9
|
||||
##
|
||||
{ 'command': 'query-xen-replication-status',
|
||||
'returns': 'ReplicationStatus' }
|
||||
'returns': 'ReplicationStatus',
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @xen-colo-do-checkpoint:
|
||||
|
@ -1305,7 +1308,8 @@
|
|||
#
|
||||
# Since: 2.9
|
||||
##
|
||||
{ 'command': 'xen-colo-do-checkpoint' }
|
||||
{ 'command': 'xen-colo-do-checkpoint',
|
||||
'if': 'defined(CONFIG_REPLICATION)' }
|
||||
|
||||
##
|
||||
# @COLOStatus:
|
||||
|
@ -1356,7 +1360,8 @@
|
|||
#
|
||||
# Since: 3.0
|
||||
##
|
||||
{ 'command': 'migrate-recover', 'data': { 'uri': 'str' },
|
||||
{ 'command': 'migrate-recover',
|
||||
'data': { 'uri': 'str' },
|
||||
'allow-oob': true }
|
||||
|
||||
##
|
||||
|
|
|
@ -2385,7 +2385,9 @@
|
|||
# <- { "return": { "fdset-id": 1, "fd": 3 } }
|
||||
#
|
||||
##
|
||||
{ 'command': 'add-fd', 'data': {'*fdset-id': 'int', '*opaque': 'str'},
|
||||
{ 'command': 'add-fd',
|
||||
'data': { '*fdset-id': 'int',
|
||||
'*opaque': 'str' },
|
||||
'returns': 'AddfdInfo' }
|
||||
|
||||
##
|
||||
|
@ -2657,7 +2659,8 @@
|
|||
# }
|
||||
#
|
||||
##
|
||||
{'command': 'query-command-line-options', 'data': { '*option': 'str' },
|
||||
{'command': 'query-command-line-options',
|
||||
'data': { '*option': 'str' },
|
||||
'returns': ['CommandLineOptionInfo'],
|
||||
'allow-preconfig': true }
|
||||
|
||||
|
|
|
@ -657,7 +657,8 @@
|
|||
# }
|
||||
#
|
||||
##
|
||||
{ 'command': 'query-rx-filter', 'data': { '*name': 'str' },
|
||||
{ 'command': 'query-rx-filter',
|
||||
'data': { '*name': 'str' },
|
||||
'returns': ['RxFilterInfo'] }
|
||||
|
||||
##
|
||||
|
|
|
@ -562,19 +562,20 @@ static void qobject_input_type_number_keyval(Visitor *v, const char *name,
|
|||
{
|
||||
QObjectInputVisitor *qiv = to_qiv(v);
|
||||
const char *str = qobject_input_get_keyval(qiv, name, errp);
|
||||
char *endp;
|
||||
double val;
|
||||
|
||||
if (!str) {
|
||||
return;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
*obj = strtod(str, &endp);
|
||||
if (errno || endp == str || *endp || !isfinite(*obj)) {
|
||||
if (qemu_strtod_finite(str, NULL, &val)) {
|
||||
/* TODO report -ERANGE more nicely */
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE,
|
||||
full_name(qiv, name), "number");
|
||||
return;
|
||||
}
|
||||
|
||||
*obj = val;
|
||||
}
|
||||
|
||||
static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj,
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* Copyright Red Hat, Inc. 2012-2016
|
||||
*
|
||||
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
||||
* David Hildenbrand <david@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/osdep.h"
|
||||
|
@ -18,20 +18,42 @@
|
|||
#include "qapi/qmp/qerror.h"
|
||||
#include "qapi/qmp/qnull.h"
|
||||
#include "qemu/option.h"
|
||||
#include "qemu/queue.h"
|
||||
#include "qemu/range.h"
|
||||
#include "qemu/cutils.h"
|
||||
|
||||
typedef enum ListMode {
|
||||
/* no list parsing active / no list expected */
|
||||
LM_NONE,
|
||||
/* we have an unparsed string remaining */
|
||||
LM_UNPARSED,
|
||||
/* we have an unfinished int64 range */
|
||||
LM_INT64_RANGE,
|
||||
/* we have an unfinished uint64 range */
|
||||
LM_UINT64_RANGE,
|
||||
/* we have parsed the string completely and no range is remaining */
|
||||
LM_END,
|
||||
} ListMode;
|
||||
|
||||
/* protect against DOS attacks, limit the amount of elements per range */
|
||||
#define RANGE_MAX_ELEMENTS 65536
|
||||
|
||||
typedef union RangeElement {
|
||||
int64_t i64;
|
||||
uint64_t u64;
|
||||
} RangeElement;
|
||||
|
||||
struct StringInputVisitor
|
||||
{
|
||||
Visitor visitor;
|
||||
|
||||
GList *ranges;
|
||||
GList *cur_range;
|
||||
int64_t cur;
|
||||
/* List parsing state */
|
||||
ListMode lm;
|
||||
RangeElement rangeNext;
|
||||
RangeElement rangeEnd;
|
||||
const char *unparsed_string;
|
||||
void *list;
|
||||
|
||||
/* The original string to parse */
|
||||
const char *string;
|
||||
void *list; /* Only needed for sanity checking the caller */
|
||||
};
|
||||
|
||||
static StringInputVisitor *to_siv(Visitor *v)
|
||||
|
@ -39,136 +61,42 @@ static StringInputVisitor *to_siv(Visitor *v)
|
|||
return container_of(v, StringInputVisitor, visitor);
|
||||
}
|
||||
|
||||
static void free_range(void *range, void *dummy)
|
||||
{
|
||||
g_free(range);
|
||||
}
|
||||
|
||||
static int parse_str(StringInputVisitor *siv, const char *name, Error **errp)
|
||||
{
|
||||
char *str = (char *) siv->string;
|
||||
long long start, end;
|
||||
Range *cur;
|
||||
char *endptr;
|
||||
|
||||
if (siv->ranges) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!*str) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
errno = 0;
|
||||
start = strtoll(str, &endptr, 0);
|
||||
if (errno == 0 && endptr > str) {
|
||||
if (*endptr == '\0') {
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
range_set_bounds(cur, start, start);
|
||||
siv->ranges = range_list_insert(siv->ranges, cur);
|
||||
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));
|
||||
range_set_bounds(cur, start, end);
|
||||
siv->ranges = range_list_insert(siv->ranges, cur);
|
||||
cur = NULL;
|
||||
str = NULL;
|
||||
} else if (*endptr == ',') {
|
||||
str = endptr + 1;
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
range_set_bounds(cur, start, end);
|
||||
siv->ranges = range_list_insert(siv->ranges, cur);
|
||||
cur = NULL;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else if (*endptr == ',') {
|
||||
str = endptr + 1;
|
||||
cur = g_malloc0(sizeof(*cur));
|
||||
range_set_bounds(cur, start, start);
|
||||
siv->ranges = range_list_insert(siv->ranges, cur);
|
||||
cur = NULL;
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
goto error;
|
||||
}
|
||||
} while (str);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
g_list_foreach(siv->ranges, free_range, NULL);
|
||||
g_list_free(siv->ranges);
|
||||
siv->ranges = NULL;
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"an int64 value or range");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
start_list(Visitor *v, const char *name, GenericList **list, size_t size,
|
||||
Error **errp)
|
||||
static void start_list(Visitor *v, const char *name, GenericList **list,
|
||||
size_t size, Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
/* We don't support visits without a list */
|
||||
assert(list);
|
||||
assert(siv->lm == LM_NONE);
|
||||
siv->list = list;
|
||||
siv->unparsed_string = siv->string;
|
||||
|
||||
if (parse_str(siv, name, errp) < 0) {
|
||||
*list = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
siv->cur_range = g_list_first(siv->ranges);
|
||||
if (siv->cur_range) {
|
||||
Range *r = siv->cur_range->data;
|
||||
if (r) {
|
||||
siv->cur = range_lob(r);
|
||||
if (!siv->string[0]) {
|
||||
if (list) {
|
||||
*list = NULL;
|
||||
}
|
||||
*list = g_malloc0(size);
|
||||
siv->lm = LM_END;
|
||||
} else {
|
||||
*list = NULL;
|
||||
if (list) {
|
||||
*list = g_malloc0(size);
|
||||
}
|
||||
siv->lm = LM_UNPARSED;
|
||||
}
|
||||
}
|
||||
|
||||
static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
Range *r;
|
||||
|
||||
if (!siv->ranges || !siv->cur_range) {
|
||||
switch (siv->lm) {
|
||||
case LM_END:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r = siv->cur_range->data;
|
||||
if (!r) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!range_contains(r, siv->cur)) {
|
||||
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 = range_lob(r);
|
||||
case LM_INT64_RANGE:
|
||||
case LM_UINT64_RANGE:
|
||||
case LM_UNPARSED:
|
||||
/* we have an unparsed string or something left in a range */
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
tail->next = g_malloc0(size);
|
||||
|
@ -178,88 +106,208 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
|
|||
static void check_list(Visitor *v, Error **errp)
|
||||
{
|
||||
const StringInputVisitor *siv = to_siv(v);
|
||||
Range *r;
|
||||
GList *cur_range;
|
||||
|
||||
if (!siv->ranges || !siv->cur_range) {
|
||||
switch (siv->lm) {
|
||||
case LM_INT64_RANGE:
|
||||
case LM_UINT64_RANGE:
|
||||
case LM_UNPARSED:
|
||||
error_setg(errp, "Fewer list elements expected");
|
||||
return;
|
||||
}
|
||||
|
||||
r = siv->cur_range->data;
|
||||
if (!r) {
|
||||
case LM_END:
|
||||
return;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!range_contains(r, siv->cur)) {
|
||||
cur_range = g_list_next(siv->cur_range);
|
||||
if (!cur_range) {
|
||||
return;
|
||||
}
|
||||
r = cur_range->data;
|
||||
if (!r) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
error_setg(errp, "Range contains too many values");
|
||||
}
|
||||
|
||||
static void end_list(Visitor *v, void **obj)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
assert(siv->lm != LM_NONE);
|
||||
assert(siv->list == obj);
|
||||
siv->list = NULL;
|
||||
siv->unparsed_string = NULL;
|
||||
siv->lm = LM_NONE;
|
||||
}
|
||||
|
||||
static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
|
||||
{
|
||||
const char *endptr;
|
||||
int64_t start, end;
|
||||
|
||||
/* parse a simple int64 or range */
|
||||
if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
end = start;
|
||||
|
||||
switch (endptr[0]) {
|
||||
case '\0':
|
||||
siv->unparsed_string = endptr;
|
||||
break;
|
||||
case ',':
|
||||
siv->unparsed_string = endptr + 1;
|
||||
break;
|
||||
case '-':
|
||||
/* parse the end of the range */
|
||||
if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (endptr[0]) {
|
||||
case '\0':
|
||||
siv->unparsed_string = endptr;
|
||||
break;
|
||||
case ',':
|
||||
siv->unparsed_string = endptr + 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* we have a proper range (with maybe only one element) */
|
||||
siv->lm = LM_INT64_RANGE;
|
||||
siv->rangeNext.i64 = start;
|
||||
siv->rangeEnd.i64 = end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
|
||||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
int64_t val;
|
||||
|
||||
if (parse_str(siv, name, errp) < 0) {
|
||||
switch (siv->lm) {
|
||||
case LM_NONE:
|
||||
/* just parse a simple int64, bail out if not completely consumed */
|
||||
if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
|
||||
name ? name : "null", "int64");
|
||||
return;
|
||||
}
|
||||
*obj = val;
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
case LM_UNPARSED:
|
||||
if (try_parse_int64_list_entry(siv, obj)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"list of int64 values or ranges");
|
||||
return;
|
||||
}
|
||||
assert(siv->lm == LM_INT64_RANGE);
|
||||
/* fall through */
|
||||
case LM_INT64_RANGE:
|
||||
/* return the next element in the range */
|
||||
assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
|
||||
*obj = siv->rangeNext.i64++;
|
||||
|
||||
r = siv->cur_range->data;
|
||||
if (!r) {
|
||||
goto error;
|
||||
if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
|
||||
/* end of range, check if there is more to parse */
|
||||
siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
|
||||
}
|
||||
return;
|
||||
case LM_END:
|
||||
error_setg(errp, "Fewer list elements expected");
|
||||
return;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
siv->cur = range_lob(r);
|
||||
static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
|
||||
{
|
||||
const char *endptr;
|
||||
uint64_t start, end;
|
||||
|
||||
/* parse a simple uint64 or range */
|
||||
if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
end = start;
|
||||
|
||||
switch (endptr[0]) {
|
||||
case '\0':
|
||||
siv->unparsed_string = endptr;
|
||||
break;
|
||||
case ',':
|
||||
siv->unparsed_string = endptr + 1;
|
||||
break;
|
||||
case '-':
|
||||
/* parse the end of the range */
|
||||
if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
|
||||
return -EINVAL;
|
||||
}
|
||||
switch (endptr[0]) {
|
||||
case '\0':
|
||||
siv->unparsed_string = endptr;
|
||||
break;
|
||||
case ',':
|
||||
siv->unparsed_string = endptr + 1;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*obj = siv->cur;
|
||||
siv->cur++;
|
||||
return;
|
||||
|
||||
error:
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"an int64 value or range");
|
||||
/* we have a proper range (with maybe only one element) */
|
||||
siv->lm = LM_UINT64_RANGE;
|
||||
siv->rangeNext.u64 = start;
|
||||
siv->rangeEnd.u64 = end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
|
||||
Error **errp)
|
||||
{
|
||||
/* FIXME: parse_type_int64 mishandles values over INT64_MAX */
|
||||
int64_t i;
|
||||
Error *err = NULL;
|
||||
parse_type_int64(v, name, &i, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
} else {
|
||||
*obj = i;
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
uint64_t val;
|
||||
|
||||
switch (siv->lm) {
|
||||
case LM_NONE:
|
||||
/* just parse a simple uint64, bail out if not completely consumed */
|
||||
if (qemu_strtou64(siv->string, NULL, 0, &val)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"uint64");
|
||||
return;
|
||||
}
|
||||
*obj = val;
|
||||
return;
|
||||
case LM_UNPARSED:
|
||||
if (try_parse_uint64_list_entry(siv, obj)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
|
||||
"list of uint64 values or ranges");
|
||||
return;
|
||||
}
|
||||
assert(siv->lm == LM_UINT64_RANGE);
|
||||
/* fall through */
|
||||
case LM_UINT64_RANGE:
|
||||
/* return the next element in the range */
|
||||
assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
|
||||
*obj = siv->rangeNext.u64++;
|
||||
|
||||
if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
|
||||
/* end of range, check if there is more to parse */
|
||||
siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
|
||||
}
|
||||
return;
|
||||
case LM_END:
|
||||
error_setg(errp, "Fewer list elements expected");
|
||||
return;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -270,6 +318,7 @@ static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
|
|||
Error *err = NULL;
|
||||
uint64_t val;
|
||||
|
||||
assert(siv->lm == LM_NONE);
|
||||
parse_option_size(name, siv->string, &val, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
|
@ -284,6 +333,7 @@ static void parse_type_bool(Visitor *v, const char *name, bool *obj,
|
|||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
assert(siv->lm == LM_NONE);
|
||||
if (!strcasecmp(siv->string, "on") ||
|
||||
!strcasecmp(siv->string, "yes") ||
|
||||
!strcasecmp(siv->string, "true")) {
|
||||
|
@ -306,6 +356,7 @@ static void parse_type_str(Visitor *v, const char *name, char **obj,
|
|||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
assert(siv->lm == LM_NONE);
|
||||
*obj = g_strdup(siv->string);
|
||||
}
|
||||
|
||||
|
@ -313,12 +364,10 @@ static void parse_type_number(Visitor *v, const char *name, double *obj,
|
|||
Error **errp)
|
||||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
char *endp = (char *) siv->string;
|
||||
double val;
|
||||
|
||||
errno = 0;
|
||||
val = strtod(siv->string, &endp);
|
||||
if (errno || endp == siv->string || *endp) {
|
||||
assert(siv->lm == LM_NONE);
|
||||
if (qemu_strtod_finite(siv->string, NULL, &val)) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"number");
|
||||
return;
|
||||
|
@ -332,9 +381,10 @@ static void parse_type_null(Visitor *v, const char *name, QNull **obj,
|
|||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
assert(siv->lm == LM_NONE);
|
||||
*obj = NULL;
|
||||
|
||||
if (!siv->string || siv->string[0]) {
|
||||
if (siv->string[0]) {
|
||||
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
|
||||
"null");
|
||||
return;
|
||||
|
@ -347,8 +397,6 @@ static void string_input_free(Visitor *v)
|
|||
{
|
||||
StringInputVisitor *siv = to_siv(v);
|
||||
|
||||
g_list_foreach(siv->ranges, free_range, NULL);
|
||||
g_list_free(siv->ranges);
|
||||
g_free(siv);
|
||||
}
|
||||
|
||||
|
@ -374,5 +422,6 @@ Visitor *string_input_visitor_new(const char *str)
|
|||
v->visitor.free = string_input_free;
|
||||
|
||||
v->string = str;
|
||||
v->lm = LM_NONE;
|
||||
return &v->visitor;
|
||||
}
|
||||
|
|
|
@ -76,8 +76,9 @@
|
|||
#
|
||||
# Since: 1.5
|
||||
##
|
||||
{ 'struct': 'TPMPassthroughOptions', 'data': { '*path' : 'str',
|
||||
'*cancel-path' : 'str'} }
|
||||
{ 'struct': 'TPMPassthroughOptions',
|
||||
'data': { '*path': 'str',
|
||||
'*cancel-path': 'str' } }
|
||||
|
||||
##
|
||||
# @TPMEmulatorOptions:
|
||||
|
|
|
@ -598,7 +598,8 @@
|
|||
# Notes: An empty password in this command will set the password to the empty
|
||||
# string. Existing clients are unaffected by executing this command.
|
||||
##
|
||||
{ 'command': 'change-vnc-password', 'data': {'password': 'str'},
|
||||
{ 'command': 'change-vnc-password',
|
||||
'data': { 'password': 'str' },
|
||||
'if': 'defined(CONFIG_VNC)' }
|
||||
|
||||
##
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue