QAPI patches patches for 2021-08-05
-----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmEL8FQSHGFybWJydUBy ZWRoYXQuY29tAAoJEDhwtADrkYZTA/MP/ixcTS2eQXXzbckLNULfkODtLMq2S/o9 lDYLDh3y39/4B/i70pfy6opQGUyiuMdrzqXK51Mh9WSxyn4CfxHEjfJVD/ex0hVQ DevTIpIY+W9pH5Tx/EXsm0bV0PDtRo08ov8oRg+6IXKZvVNI1tMPAn0UtSmLxDJJ saux+AA75DMTxJ4jUx+a6gz7yPJIL9ZIO7m5dp4hV8sT8PwGk6SIC1LGUPPquFvY fn0wIHEOTKKlrC27YkaVF8CQ3xmCqBiR5HznFkSBEkpupeK10Yeb4ZccBLhArwhj D4iF8VUYh/xR2No8W3lZ5SaW0aAWeKhsJrorBceiz9Qi/N/6fcT7NGf3H6D/D0Ho m3xoETnNYVvCPRkfiYykyYh7nyuhLK2uAOsHy0KpkOlVcdfdVXePesj9xiw6kGte Fv/UjXiNmiqIyZfVEt0tYk4ooRdgbBGTJ+ZnC73aKQpWZXC24SdB9xIyPPZ77W3s NuuGWar8iPzSB2ZeIvIzXFewDAz/Sg01xQBsjjS6X/QI2TdtMIuFD842MLlW1rzM ia0BGxmo6uyiNPUyDYZI6bSOk0huM09niXX2JFI0pGVIYoFVjoaLyr20hNLfqRfQ SFeUqj+6lKw/K2dEMhTtJe7U2KyJf6Hs3LWiL8TzrxshDPK/a7woWhCfH43GsWS0 oE6kLCRX8v67 =ge3b -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-08-05' into staging QAPI patches patches for 2021-08-05 # gpg: Signature made Thu 05 Aug 2021 15:06:12 BST # gpg: using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653 # gpg: issuer "armbru@redhat.com" # gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full] # gpg: aka "Markus Armbruster <armbru@pond.sub.org>" [full] # Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653 * remotes/armbru/tags/pull-qapi-2021-08-05: docs: convert writing-qmp-commands.txt to writing-qmp-commands.rst docs/qapi-code-gen: add cross-references docs/qapi-code-gen: Beautify formatting docs: convert qapi-code-gen.txt to ReST docs/devel/qapi-code-gen: Update examples to match current code Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
ca92f16276
@ -42,3 +42,5 @@ modifying QEMU's source code.
|
|||||||
multi-process
|
multi-process
|
||||||
ebpf_rss
|
ebpf_rss
|
||||||
vfio-migration
|
vfio-migration
|
||||||
|
qapi-code-gen
|
||||||
|
writing-qmp-commands
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,5 @@
|
|||||||
= How to write QMP commands using the QAPI framework =
|
How to write QMP commands using the QAPI framework
|
||||||
|
==================================================
|
||||||
|
|
||||||
This document is a step-by-step guide on how to write new QMP commands using
|
This document is a step-by-step guide on how to write new QMP commands using
|
||||||
the QAPI framework. It also shows how to implement new style HMP commands.
|
the QAPI framework. It also shows how to implement new style HMP commands.
|
||||||
@ -10,7 +11,9 @@ For an in-depth introduction to the QAPI framework, please refer to
|
|||||||
docs/devel/qapi-code-gen.txt. For documentation about the QMP protocol,
|
docs/devel/qapi-code-gen.txt. For documentation about the QMP protocol,
|
||||||
start with docs/interop/qmp-intro.txt.
|
start with docs/interop/qmp-intro.txt.
|
||||||
|
|
||||||
== Overview ==
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
Generally speaking, the following steps should be taken in order to write a
|
Generally speaking, the following steps should be taken in order to write a
|
||||||
new QMP command.
|
new QMP command.
|
||||||
@ -31,55 +34,59 @@ new QMP command.
|
|||||||
The following sections will demonstrate each of the steps above. We will start
|
The following sections will demonstrate each of the steps above. We will start
|
||||||
very simple and get more complex as we progress.
|
very simple and get more complex as we progress.
|
||||||
|
|
||||||
=== Testing ===
|
|
||||||
|
Testing
|
||||||
|
-------
|
||||||
|
|
||||||
For all the examples in the next sections, the test setup is the same and is
|
For all the examples in the next sections, the test setup is the same and is
|
||||||
shown here.
|
shown here.
|
||||||
|
|
||||||
First, QEMU should be started like this:
|
First, QEMU should be started like this::
|
||||||
|
|
||||||
# qemu-system-TARGET [...] \
|
# qemu-system-TARGET [...] \
|
||||||
-chardev socket,id=qmp,port=4444,host=localhost,server=on \
|
-chardev socket,id=qmp,port=4444,host=localhost,server=on \
|
||||||
-mon chardev=qmp,mode=control,pretty=on
|
-mon chardev=qmp,mode=control,pretty=on
|
||||||
|
|
||||||
Then, in a different terminal:
|
Then, in a different terminal::
|
||||||
|
|
||||||
$ telnet localhost 4444
|
$ telnet localhost 4444
|
||||||
Trying 127.0.0.1...
|
Trying 127.0.0.1...
|
||||||
Connected to localhost.
|
Connected to localhost.
|
||||||
Escape character is '^]'.
|
Escape character is '^]'.
|
||||||
{
|
{
|
||||||
"QMP": {
|
"QMP": {
|
||||||
"version": {
|
"version": {
|
||||||
"qemu": {
|
"qemu": {
|
||||||
"micro": 50,
|
"micro": 50,
|
||||||
"minor": 15,
|
"minor": 15,
|
||||||
"major": 0
|
"major": 0
|
||||||
},
|
},
|
||||||
"package": ""
|
"package": ""
|
||||||
},
|
},
|
||||||
"capabilities": [
|
"capabilities": [
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
The above output is the QMP server saying you're connected. The server is
|
The above output is the QMP server saying you're connected. The server is
|
||||||
actually in capabilities negotiation mode. To enter in command mode type:
|
actually in capabilities negotiation mode. To enter in command mode type::
|
||||||
|
|
||||||
{ "execute": "qmp_capabilities" }
|
{ "execute": "qmp_capabilities" }
|
||||||
|
|
||||||
Then the server should respond:
|
Then the server should respond::
|
||||||
|
|
||||||
{
|
{
|
||||||
"return": {
|
"return": {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Which is QMP's way of saying "the latest command executed OK and didn't return
|
Which is QMP's way of saying "the latest command executed OK and didn't return
|
||||||
any data". Now you're ready to enter the QMP example commands as explained in
|
any data". Now you're ready to enter the QMP example commands as explained in
|
||||||
the following sections.
|
the following sections.
|
||||||
|
|
||||||
== Writing a command that doesn't return data ==
|
|
||||||
|
Writing a command that doesn't return data
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
That's the most simple QMP command that can be written. Usually, this kind of
|
That's the most simple QMP command that can be written. Usually, this kind of
|
||||||
command carries some meaningful action in QEMU but here it will just print
|
command carries some meaningful action in QEMU but here it will just print
|
||||||
@ -90,9 +97,9 @@ return any data.
|
|||||||
|
|
||||||
The first step is defining the command in the appropriate QAPI schema
|
The first step is defining the command in the appropriate QAPI schema
|
||||||
module. We pick module qapi/misc.json, and add the following line at
|
module. We pick module qapi/misc.json, and add the following line at
|
||||||
the bottom:
|
the bottom::
|
||||||
|
|
||||||
{ 'command': 'hello-world' }
|
{ 'command': 'hello-world' }
|
||||||
|
|
||||||
The "command" keyword defines a new QMP command. It's an JSON object. All
|
The "command" keyword defines a new QMP command. It's an JSON object. All
|
||||||
schema entries are JSON objects. The line above will instruct the QAPI to
|
schema entries are JSON objects. The line above will instruct the QAPI to
|
||||||
@ -102,19 +109,19 @@ protocol data.
|
|||||||
The next step is to write the "hello-world" implementation. As explained
|
The next step is to write the "hello-world" implementation. As explained
|
||||||
earlier, it's preferable for commands to live in QEMU subsystems. But
|
earlier, it's preferable for commands to live in QEMU subsystems. But
|
||||||
"hello-world" doesn't pertain to any, so we put its implementation in
|
"hello-world" doesn't pertain to any, so we put its implementation in
|
||||||
monitor/qmp-cmds.c:
|
monitor/qmp-cmds.c::
|
||||||
|
|
||||||
void qmp_hello_world(Error **errp)
|
void qmp_hello_world(Error **errp)
|
||||||
{
|
{
|
||||||
printf("Hello, world!\n");
|
printf("Hello, world!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
There are a few things to be noticed:
|
There are a few things to be noticed:
|
||||||
|
|
||||||
1. QMP command implementation functions must be prefixed with "qmp_"
|
1. QMP command implementation functions must be prefixed with "qmp\_"
|
||||||
2. qmp_hello_world() returns void, this is in accordance with the fact that the
|
2. qmp_hello_world() returns void, this is in accordance with the fact that the
|
||||||
command doesn't return any data
|
command doesn't return any data
|
||||||
3. It takes an "Error **" argument. This is required. Later we will see how to
|
3. It takes an "Error \*\*" argument. This is required. Later we will see how to
|
||||||
return errors and take additional arguments. The Error argument should not
|
return errors and take additional arguments. The Error argument should not
|
||||||
be touched if the command doesn't return errors
|
be touched if the command doesn't return errors
|
||||||
4. We won't add the function's prototype. That's automatically done by the QAPI
|
4. We won't add the function's prototype. That's automatically done by the QAPI
|
||||||
@ -122,23 +129,25 @@ There are a few things to be noticed:
|
|||||||
because it's the easiest way to demonstrate a QMP command
|
because it's the easiest way to demonstrate a QMP command
|
||||||
|
|
||||||
You're done. Now build qemu, run it as suggested in the "Testing" section,
|
You're done. Now build qemu, run it as suggested in the "Testing" section,
|
||||||
and then type the following QMP command:
|
and then type the following QMP command::
|
||||||
|
|
||||||
{ "execute": "hello-world" }
|
{ "execute": "hello-world" }
|
||||||
|
|
||||||
Then check the terminal running qemu and look for the "Hello, world" string. If
|
Then check the terminal running qemu and look for the "Hello, world" string. If
|
||||||
you don't see it then something went wrong.
|
you don't see it then something went wrong.
|
||||||
|
|
||||||
=== Arguments ===
|
|
||||||
|
Arguments
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
Let's add an argument called "message" to our "hello-world" command. The new
|
Let's add an argument called "message" to our "hello-world" command. The new
|
||||||
argument will contain the string to be printed to stdout. It's an optional
|
argument will contain the string to be printed to stdout. It's an optional
|
||||||
argument, if it's not present we print our default "Hello, World" string.
|
argument, if it's not present we print our default "Hello, World" string.
|
||||||
|
|
||||||
The first change we have to do is to modify the command specification in the
|
The first change we have to do is to modify the command specification in the
|
||||||
schema file to the following:
|
schema file to the following::
|
||||||
|
|
||||||
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
|
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
|
||||||
|
|
||||||
Notice the new 'data' member in the schema. It's an JSON object whose each
|
Notice the new 'data' member in the schema. It's an JSON object whose each
|
||||||
element is an argument to the command in question. Also notice the asterisk,
|
element is an argument to the command in question. Also notice the asterisk,
|
||||||
@ -147,80 +156,82 @@ for mandatory arguments). Finally, 'str' is the argument's type, which
|
|||||||
stands for "string". The QAPI also supports integers, booleans, enumerations
|
stands for "string". The QAPI also supports integers, booleans, enumerations
|
||||||
and user defined types.
|
and user defined types.
|
||||||
|
|
||||||
Now, let's update our C implementation in monitor/qmp-cmds.c:
|
Now, let's update our C implementation in monitor/qmp-cmds.c::
|
||||||
|
|
||||||
void qmp_hello_world(bool has_message, const char *message, Error **errp)
|
void qmp_hello_world(bool has_message, const char *message, Error **errp)
|
||||||
{
|
{
|
||||||
if (has_message) {
|
if (has_message) {
|
||||||
printf("%s\n", message);
|
printf("%s\n", message);
|
||||||
} else {
|
} else {
|
||||||
printf("Hello, world\n");
|
printf("Hello, world\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
There are two important details to be noticed:
|
There are two important details to be noticed:
|
||||||
|
|
||||||
1. All optional arguments are accompanied by a 'has_' boolean, which is set
|
1. All optional arguments are accompanied by a 'has\_' boolean, which is set
|
||||||
if the optional argument is present or false otherwise
|
if the optional argument is present or false otherwise
|
||||||
2. The C implementation signature must follow the schema's argument ordering,
|
2. The C implementation signature must follow the schema's argument ordering,
|
||||||
which is defined by the "data" member
|
which is defined by the "data" member
|
||||||
|
|
||||||
Time to test our new version of the "hello-world" command. Build qemu, run it as
|
Time to test our new version of the "hello-world" command. Build qemu, run it as
|
||||||
described in the "Testing" section and then send two commands:
|
described in the "Testing" section and then send two commands::
|
||||||
|
|
||||||
{ "execute": "hello-world" }
|
{ "execute": "hello-world" }
|
||||||
{
|
{
|
||||||
"return": {
|
"return": {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }
|
{ "execute": "hello-world", "arguments": { "message": "We love qemu" } }
|
||||||
{
|
{
|
||||||
"return": {
|
"return": {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
You should see "Hello, world" and "We love qemu" in the terminal running qemu,
|
You should see "Hello, world" and "We love qemu" in the terminal running qemu,
|
||||||
if you don't see these strings, then something went wrong.
|
if you don't see these strings, then something went wrong.
|
||||||
|
|
||||||
=== Errors ===
|
|
||||||
|
Errors
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
QMP commands should use the error interface exported by the error.h header
|
QMP commands should use the error interface exported by the error.h header
|
||||||
file. Basically, most errors are set by calling the error_setg() function.
|
file. Basically, most errors are set by calling the error_setg() function.
|
||||||
|
|
||||||
Let's say we don't accept the string "message" to contain the word "love". If
|
Let's say we don't accept the string "message" to contain the word "love". If
|
||||||
it does contain it, we want the "hello-world" command to return an error:
|
it does contain it, we want the "hello-world" command to return an error::
|
||||||
|
|
||||||
void qmp_hello_world(bool has_message, const char *message, Error **errp)
|
void qmp_hello_world(bool has_message, const char *message, Error **errp)
|
||||||
{
|
{
|
||||||
if (has_message) {
|
if (has_message) {
|
||||||
if (strstr(message, "love")) {
|
if (strstr(message, "love")) {
|
||||||
error_setg(errp, "the word 'love' is not allowed");
|
error_setg(errp, "the word 'love' is not allowed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printf("%s\n", message);
|
printf("%s\n", message);
|
||||||
} else {
|
} else {
|
||||||
printf("Hello, world\n");
|
printf("Hello, world\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
The first argument to the error_setg() function is the Error pointer
|
The first argument to the error_setg() function is the Error pointer
|
||||||
to pointer, which is passed to all QMP functions. The next argument is a human
|
to pointer, which is passed to all QMP functions. The next argument is a human
|
||||||
description of the error, this is a free-form printf-like string.
|
description of the error, this is a free-form printf-like string.
|
||||||
|
|
||||||
Let's test the example above. Build qemu, run it as defined in the "Testing"
|
Let's test the example above. Build qemu, run it as defined in the "Testing"
|
||||||
section, and then issue the following command:
|
section, and then issue the following command::
|
||||||
|
|
||||||
{ "execute": "hello-world", "arguments": { "message": "all you need is love" } }
|
{ "execute": "hello-world", "arguments": { "message": "all you need is love" } }
|
||||||
|
|
||||||
The QMP server's response should be:
|
The QMP server's response should be::
|
||||||
|
|
||||||
{
|
{
|
||||||
"error": {
|
"error": {
|
||||||
"class": "GenericError",
|
"class": "GenericError",
|
||||||
"desc": "the word 'love' is not allowed"
|
"desc": "the word 'love' is not allowed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Note that error_setg() produces a "GenericError" class. In general,
|
Note that error_setg() produces a "GenericError" class. In general,
|
||||||
all QMP errors should have that error class. There are two exceptions
|
all QMP errors should have that error class. There are two exceptions
|
||||||
@ -234,34 +245,38 @@ to this rule:
|
|||||||
If the failure you want to report falls into one of the two cases above,
|
If the failure you want to report falls into one of the two cases above,
|
||||||
use error_set() with a second argument of an ErrorClass value.
|
use error_set() with a second argument of an ErrorClass value.
|
||||||
|
|
||||||
=== Command Documentation ===
|
|
||||||
|
Command Documentation
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
There's only one step missing to make "hello-world"'s implementation complete,
|
There's only one step missing to make "hello-world"'s implementation complete,
|
||||||
and that's its documentation in the schema file.
|
and that's its documentation in the schema file.
|
||||||
|
|
||||||
There are many examples of such documentation in the schema file already, but
|
There are many examples of such documentation in the schema file already, but
|
||||||
here goes "hello-world"'s new entry for qapi/misc.json:
|
here goes "hello-world"'s new entry for qapi/misc.json::
|
||||||
|
|
||||||
##
|
##
|
||||||
# @hello-world:
|
# @hello-world:
|
||||||
#
|
#
|
||||||
# Print a client provided string to the standard output stream.
|
# Print a client provided string to the standard output stream.
|
||||||
#
|
#
|
||||||
# @message: string to be printed
|
# @message: string to be printed
|
||||||
#
|
#
|
||||||
# Returns: Nothing on success.
|
# Returns: Nothing on success.
|
||||||
#
|
#
|
||||||
# Notes: if @message is not provided, the "Hello, world" string will
|
# Notes: if @message is not provided, the "Hello, world" string will
|
||||||
# be printed instead
|
# be printed instead
|
||||||
#
|
#
|
||||||
# Since: <next qemu stable release, eg. 1.0>
|
# Since: <next qemu stable release, eg. 1.0>
|
||||||
##
|
##
|
||||||
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
|
{ 'command': 'hello-world', 'data': { '*message': 'str' } }
|
||||||
|
|
||||||
Please, note that the "Returns" clause is optional if a command doesn't return
|
Please, note that the "Returns" clause is optional if a command doesn't return
|
||||||
any data nor any errors.
|
any data nor any errors.
|
||||||
|
|
||||||
=== Implementing the HMP command ===
|
|
||||||
|
Implementing the HMP command
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Now that the QMP command is in place, we can also make it available in the human
|
Now that the QMP command is in place, we can also make it available in the human
|
||||||
monitor (HMP).
|
monitor (HMP).
|
||||||
@ -270,20 +285,20 @@ With the introduction of the QAPI, HMP commands make QMP calls. Most of the
|
|||||||
time HMP commands are simple wrappers. All HMP commands implementation exist in
|
time HMP commands are simple wrappers. All HMP commands implementation exist in
|
||||||
the monitor/hmp-cmds.c file.
|
the monitor/hmp-cmds.c file.
|
||||||
|
|
||||||
Here's the implementation of the "hello-world" HMP command:
|
Here's the implementation of the "hello-world" HMP command::
|
||||||
|
|
||||||
void hmp_hello_world(Monitor *mon, const QDict *qdict)
|
void hmp_hello_world(Monitor *mon, const QDict *qdict)
|
||||||
{
|
{
|
||||||
const char *message = qdict_get_try_str(qdict, "message");
|
const char *message = qdict_get_try_str(qdict, "message");
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
qmp_hello_world(!!message, message, &err);
|
qmp_hello_world(!!message, message, &err);
|
||||||
if (err) {
|
if (err) {
|
||||||
monitor_printf(mon, "%s\n", error_get_pretty(err));
|
monitor_printf(mon, "%s\n", error_get_pretty(err));
|
||||||
error_free(err);
|
error_free(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Also, you have to add the function's prototype to the hmp.h file.
|
Also, you have to add the function's prototype to the hmp.h file.
|
||||||
|
|
||||||
@ -299,7 +314,7 @@ There are three important points to be noticed:
|
|||||||
QMP call
|
QMP call
|
||||||
|
|
||||||
There's one last step to actually make the command available to monitor users,
|
There's one last step to actually make the command available to monitor users,
|
||||||
we should add it to the hmp-commands.hx file:
|
we should add it to the hmp-commands.hx file::
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "hello-world",
|
.name = "hello-world",
|
||||||
@ -309,11 +324,13 @@ we should add it to the hmp-commands.hx file:
|
|||||||
.cmd = hmp_hello_world,
|
.cmd = hmp_hello_world,
|
||||||
},
|
},
|
||||||
|
|
||||||
STEXI
|
::
|
||||||
@item hello_world @var{message}
|
|
||||||
@findex hello_world
|
STEXI
|
||||||
Print message to the standard output
|
@item hello_world @var{message}
|
||||||
ETEXI
|
@findex hello_world
|
||||||
|
Print message to the standard output
|
||||||
|
ETEXI
|
||||||
|
|
||||||
To test this you have to open a user monitor and issue the "hello-world"
|
To test this you have to open a user monitor and issue the "hello-world"
|
||||||
command. It might be instructive to check the command's documentation with
|
command. It might be instructive to check the command's documentation with
|
||||||
@ -322,7 +339,9 @@ HMP's "help" command.
|
|||||||
Please, check the "-monitor" command-line option to know how to open a user
|
Please, check the "-monitor" command-line option to know how to open a user
|
||||||
monitor.
|
monitor.
|
||||||
|
|
||||||
== Writing a command that returns data ==
|
|
||||||
|
Writing a command that returns data
|
||||||
|
-----------------------------------
|
||||||
|
|
||||||
A QMP command is capable of returning any data the QAPI supports like integers,
|
A QMP command is capable of returning any data the QAPI supports like integers,
|
||||||
strings, booleans, enumerations and user defined types.
|
strings, booleans, enumerations and user defined types.
|
||||||
@ -330,7 +349,9 @@ strings, booleans, enumerations and user defined types.
|
|||||||
In this section we will focus on user defined types. Please, check the QAPI
|
In this section we will focus on user defined types. Please, check the QAPI
|
||||||
documentation for information about the other types.
|
documentation for information about the other types.
|
||||||
|
|
||||||
=== User Defined Types ===
|
|
||||||
|
User Defined Types
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
FIXME This example needs to be redone after commit 6d32717
|
FIXME This example needs to be redone after commit 6d32717
|
||||||
|
|
||||||
@ -344,63 +365,63 @@ returned as a string, the latter is an integer in nanoseconds (which is not
|
|||||||
very useful in practice, as the timer has probably already fired when the
|
very useful in practice, as the timer has probably already fired when the
|
||||||
information reaches the client).
|
information reaches the client).
|
||||||
|
|
||||||
The best way to return that data is to create a new QAPI type, as shown below:
|
The best way to return that data is to create a new QAPI type, as shown below::
|
||||||
|
|
||||||
##
|
##
|
||||||
# @QemuAlarmClock
|
# @QemuAlarmClock
|
||||||
#
|
#
|
||||||
# QEMU alarm clock information.
|
# QEMU alarm clock information.
|
||||||
#
|
#
|
||||||
# @clock-name: The alarm clock method's name.
|
# @clock-name: The alarm clock method's name.
|
||||||
#
|
#
|
||||||
# @next-deadline: The time (in nanoseconds) the next alarm will fire.
|
# @next-deadline: The time (in nanoseconds) the next alarm will fire.
|
||||||
#
|
#
|
||||||
# Since: 1.0
|
# Since: 1.0
|
||||||
##
|
##
|
||||||
{ 'type': 'QemuAlarmClock',
|
{ 'type': 'QemuAlarmClock',
|
||||||
'data': { 'clock-name': 'str', '*next-deadline': 'int' } }
|
'data': { 'clock-name': 'str', '*next-deadline': 'int' } }
|
||||||
|
|
||||||
The "type" keyword defines a new QAPI type. Its "data" member contains the
|
The "type" keyword defines a new QAPI type. Its "data" member contains the
|
||||||
type's members. In this example our members are the "clock-name" and the
|
type's members. In this example our members are the "clock-name" and the
|
||||||
"next-deadline" one, which is optional.
|
"next-deadline" one, which is optional.
|
||||||
|
|
||||||
Now let's define the query-alarm-clock command:
|
Now let's define the query-alarm-clock command::
|
||||||
|
|
||||||
##
|
##
|
||||||
# @query-alarm-clock
|
# @query-alarm-clock
|
||||||
#
|
#
|
||||||
# Return information about QEMU's alarm clock.
|
# Return information about QEMU's alarm clock.
|
||||||
#
|
#
|
||||||
# Returns a @QemuAlarmClock instance describing the alarm clock method
|
# Returns a @QemuAlarmClock instance describing the alarm clock method
|
||||||
# being currently used by QEMU (this is usually set by the '-clock'
|
# being currently used by QEMU (this is usually set by the '-clock'
|
||||||
# command-line option).
|
# command-line option).
|
||||||
#
|
#
|
||||||
# Since: 1.0
|
# Since: 1.0
|
||||||
##
|
##
|
||||||
{ 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' }
|
{ 'command': 'query-alarm-clock', 'returns': 'QemuAlarmClock' }
|
||||||
|
|
||||||
Notice the "returns" keyword. As its name suggests, it's used to define the
|
Notice the "returns" keyword. As its name suggests, it's used to define the
|
||||||
data returned by a command.
|
data returned by a command.
|
||||||
|
|
||||||
It's time to implement the qmp_query_alarm_clock() function, you can put it
|
It's time to implement the qmp_query_alarm_clock() function, you can put it
|
||||||
in the qemu-timer.c file:
|
in the qemu-timer.c file::
|
||||||
|
|
||||||
QemuAlarmClock *qmp_query_alarm_clock(Error **errp)
|
QemuAlarmClock *qmp_query_alarm_clock(Error **errp)
|
||||||
{
|
{
|
||||||
QemuAlarmClock *clock;
|
QemuAlarmClock *clock;
|
||||||
int64_t deadline;
|
int64_t deadline;
|
||||||
|
|
||||||
clock = g_malloc0(sizeof(*clock));
|
clock = g_malloc0(sizeof(*clock));
|
||||||
|
|
||||||
deadline = qemu_next_alarm_deadline();
|
deadline = qemu_next_alarm_deadline();
|
||||||
if (deadline > 0) {
|
if (deadline > 0) {
|
||||||
clock->has_next_deadline = true;
|
clock->has_next_deadline = true;
|
||||||
clock->next_deadline = deadline;
|
clock->next_deadline = deadline;
|
||||||
}
|
}
|
||||||
clock->clock_name = g_strdup(alarm_timer->name);
|
clock->clock_name = g_strdup(alarm_timer->name);
|
||||||
|
|
||||||
return clock;
|
return clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
There are a number of things to be noticed:
|
There are a number of things to be noticed:
|
||||||
|
|
||||||
@ -423,40 +444,42 @@ There are a number of things to be noticed:
|
|||||||
6. You have to include "qapi/qapi-commands-misc.h" in qemu-timer.c
|
6. You have to include "qapi/qapi-commands-misc.h" in qemu-timer.c
|
||||||
|
|
||||||
Time to test the new command. Build qemu, run it as described in the "Testing"
|
Time to test the new command. Build qemu, run it as described in the "Testing"
|
||||||
section and try this:
|
section and try this::
|
||||||
|
|
||||||
{ "execute": "query-alarm-clock" }
|
{ "execute": "query-alarm-clock" }
|
||||||
{
|
{
|
||||||
"return": {
|
"return": {
|
||||||
"next-deadline": 2368219,
|
"next-deadline": 2368219,
|
||||||
"clock-name": "dynticks"
|
"clock-name": "dynticks"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
==== The HMP command ====
|
|
||||||
|
|
||||||
Here's the HMP counterpart of the query-alarm-clock command:
|
The HMP command
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
void hmp_info_alarm_clock(Monitor *mon)
|
Here's the HMP counterpart of the query-alarm-clock command::
|
||||||
{
|
|
||||||
QemuAlarmClock *clock;
|
|
||||||
Error *err = NULL;
|
|
||||||
|
|
||||||
clock = qmp_query_alarm_clock(&err);
|
void hmp_info_alarm_clock(Monitor *mon)
|
||||||
if (err) {
|
{
|
||||||
monitor_printf(mon, "Could not query alarm clock information\n");
|
QemuAlarmClock *clock;
|
||||||
error_free(err);
|
Error *err = NULL;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
monitor_printf(mon, "Alarm clock method in use: '%s'\n", clock->clock_name);
|
clock = qmp_query_alarm_clock(&err);
|
||||||
if (clock->has_next_deadline) {
|
if (err) {
|
||||||
monitor_printf(mon, "Next alarm will fire in %" PRId64 " nanoseconds\n",
|
monitor_printf(mon, "Could not query alarm clock information\n");
|
||||||
clock->next_deadline);
|
error_free(err);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qapi_free_QemuAlarmClock(clock);
|
monitor_printf(mon, "Alarm clock method in use: '%s'\n", clock->clock_name);
|
||||||
}
|
if (clock->has_next_deadline) {
|
||||||
|
monitor_printf(mon, "Next alarm will fire in %" PRId64 " nanoseconds\n",
|
||||||
|
clock->next_deadline);
|
||||||
|
}
|
||||||
|
|
||||||
|
qapi_free_QemuAlarmClock(clock);
|
||||||
|
}
|
||||||
|
|
||||||
It's important to notice that hmp_info_alarm_clock() calls
|
It's important to notice that hmp_info_alarm_clock() calls
|
||||||
qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_clock().
|
qapi_free_QemuAlarmClock() to free the data returned by qmp_query_alarm_clock().
|
||||||
@ -471,7 +494,7 @@ it's good practice to always check for errors.
|
|||||||
|
|
||||||
Another important detail is that HMP's "info" commands don't go into the
|
Another important detail is that HMP's "info" commands don't go into the
|
||||||
hmp-commands.hx. Instead, they go into the info_cmds[] table, which is defined
|
hmp-commands.hx. Instead, they go into the info_cmds[] table, which is defined
|
||||||
in the monitor/misc.c file. The entry for the "info alarmclock" follows:
|
in the monitor/misc.c file. The entry for the "info alarmclock" follows::
|
||||||
|
|
||||||
{
|
{
|
||||||
.name = "alarmclock",
|
.name = "alarmclock",
|
||||||
@ -483,63 +506,65 @@ in the monitor/misc.c file. The entry for the "info alarmclock" follows:
|
|||||||
|
|
||||||
To test this, run qemu and type "info alarmclock" in the user monitor.
|
To test this, run qemu and type "info alarmclock" in the user monitor.
|
||||||
|
|
||||||
=== Returning Lists ===
|
|
||||||
|
Returning Lists
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
For this example, we're going to return all available methods for the timer
|
For this example, we're going to return all available methods for the timer
|
||||||
alarm, which is pretty much what the command-line option "-clock ?" does,
|
alarm, which is pretty much what the command-line option "-clock ?" does,
|
||||||
except that we're also going to inform which method is in use.
|
except that we're also going to inform which method is in use.
|
||||||
|
|
||||||
This first step is to define a new type:
|
This first step is to define a new type::
|
||||||
|
|
||||||
##
|
##
|
||||||
# @TimerAlarmMethod
|
# @TimerAlarmMethod
|
||||||
#
|
#
|
||||||
# Timer alarm method information.
|
# Timer alarm method information.
|
||||||
#
|
#
|
||||||
# @method-name: The method's name.
|
# @method-name: The method's name.
|
||||||
#
|
#
|
||||||
# @current: true if this alarm method is currently in use, false otherwise
|
# @current: true if this alarm method is currently in use, false otherwise
|
||||||
#
|
#
|
||||||
# Since: 1.0
|
# Since: 1.0
|
||||||
##
|
##
|
||||||
{ 'type': 'TimerAlarmMethod',
|
{ 'type': 'TimerAlarmMethod',
|
||||||
'data': { 'method-name': 'str', 'current': 'bool' } }
|
'data': { 'method-name': 'str', 'current': 'bool' } }
|
||||||
|
|
||||||
The command will be called "query-alarm-methods", here is its schema
|
The command will be called "query-alarm-methods", here is its schema
|
||||||
specification:
|
specification::
|
||||||
|
|
||||||
##
|
##
|
||||||
# @query-alarm-methods
|
# @query-alarm-methods
|
||||||
#
|
#
|
||||||
# Returns information about available alarm methods.
|
# Returns information about available alarm methods.
|
||||||
#
|
#
|
||||||
# Returns: a list of @TimerAlarmMethod for each method
|
# Returns: a list of @TimerAlarmMethod for each method
|
||||||
#
|
#
|
||||||
# Since: 1.0
|
# Since: 1.0
|
||||||
##
|
##
|
||||||
{ 'command': 'query-alarm-methods', 'returns': ['TimerAlarmMethod'] }
|
{ 'command': 'query-alarm-methods', 'returns': ['TimerAlarmMethod'] }
|
||||||
|
|
||||||
Notice the syntax for returning lists "'returns': ['TimerAlarmMethod']", this
|
Notice the syntax for returning lists "'returns': ['TimerAlarmMethod']", this
|
||||||
should be read as "returns a list of TimerAlarmMethod instances".
|
should be read as "returns a list of TimerAlarmMethod instances".
|
||||||
|
|
||||||
The C implementation follows:
|
The C implementation follows::
|
||||||
|
|
||||||
TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp)
|
TimerAlarmMethodList *qmp_query_alarm_methods(Error **errp)
|
||||||
{
|
{
|
||||||
TimerAlarmMethodList *method_list = NULL;
|
TimerAlarmMethodList *method_list = NULL;
|
||||||
const struct qemu_alarm_timer *p;
|
const struct qemu_alarm_timer *p;
|
||||||
bool current = true;
|
bool current = true;
|
||||||
|
|
||||||
for (p = alarm_timers; p->name; p++) {
|
for (p = alarm_timers; p->name; p++) {
|
||||||
TimerAlarmMethod *value = g_malloc0(*value);
|
TimerAlarmMethod *value = g_malloc0(*value);
|
||||||
value->method_name = g_strdup(p->name);
|
value->method_name = g_strdup(p->name);
|
||||||
value->current = current;
|
value->current = current;
|
||||||
QAPI_LIST_PREPEND(method_list, value);
|
QAPI_LIST_PREPEND(method_list, value);
|
||||||
current = false;
|
current = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return method_list;
|
return method_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
The most important difference from the previous examples is the
|
The most important difference from the previous examples is the
|
||||||
TimerAlarmMethodList type, which is automatically generated by the QAPI from
|
TimerAlarmMethodList type, which is automatically generated by the QAPI from
|
||||||
@ -557,41 +582,41 @@ first element of the alarm_timers array. Also notice that QAPI lists are handled
|
|||||||
by hand and we return the head of the list.
|
by hand and we return the head of the list.
|
||||||
|
|
||||||
Now Build qemu, run it as explained in the "Testing" section and try our new
|
Now Build qemu, run it as explained in the "Testing" section and try our new
|
||||||
command:
|
command::
|
||||||
|
|
||||||
{ "execute": "query-alarm-methods" }
|
{ "execute": "query-alarm-methods" }
|
||||||
{
|
{
|
||||||
"return": [
|
"return": [
|
||||||
{
|
{
|
||||||
"current": false,
|
"current": false,
|
||||||
"method-name": "unix"
|
"method-name": "unix"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"current": true,
|
"current": true,
|
||||||
"method-name": "dynticks"
|
"method-name": "dynticks"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
The HMP counterpart is a bit more complex than previous examples because it
|
The HMP counterpart is a bit more complex than previous examples because it
|
||||||
has to traverse the list, it's shown below for reference:
|
has to traverse the list, it's shown below for reference::
|
||||||
|
|
||||||
void hmp_info_alarm_methods(Monitor *mon)
|
void hmp_info_alarm_methods(Monitor *mon)
|
||||||
{
|
{
|
||||||
TimerAlarmMethodList *method_list, *method;
|
TimerAlarmMethodList *method_list, *method;
|
||||||
Error *err = NULL;
|
Error *err = NULL;
|
||||||
|
|
||||||
method_list = qmp_query_alarm_methods(&err);
|
method_list = qmp_query_alarm_methods(&err);
|
||||||
if (err) {
|
if (err) {
|
||||||
monitor_printf(mon, "Could not query alarm methods\n");
|
monitor_printf(mon, "Could not query alarm methods\n");
|
||||||
error_free(err);
|
error_free(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (method = method_list; method; method = method->next) {
|
for (method = method_list; method; method = method->next) {
|
||||||
monitor_printf(mon, "%c %s\n", method->value->current ? '*' : ' ',
|
monitor_printf(mon, "%c %s\n", method->value->current ? '*' : ' ',
|
||||||
method->value->method_name);
|
method->value->method_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
qapi_free_TimerAlarmMethodList(method_list);
|
qapi_free_TimerAlarmMethodList(method_list);
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user