mirror of https://github.com/xemu-project/xemu.git
spice: add qmp 'query-spice' and hmp 'info spice' commands.
The patch adds a 'query-spice' monitor command which returns informations about the spice server configuration and also a list of channel connections. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
6f8c63fbd7
commit
cb42a870c3
20
monitor.c
20
monitor.c
|
@ -2581,6 +2581,16 @@ static const mon_cmd_t info_cmds[] = {
|
||||||
.user_print = do_info_vnc_print,
|
.user_print = do_info_vnc_print,
|
||||||
.mhandler.info_new = do_info_vnc,
|
.mhandler.info_new = do_info_vnc,
|
||||||
},
|
},
|
||||||
|
#if defined(CONFIG_SPICE)
|
||||||
|
{
|
||||||
|
.name = "spice",
|
||||||
|
.args_type = "",
|
||||||
|
.params = "",
|
||||||
|
.help = "show the spice server status",
|
||||||
|
.user_print = do_info_spice_print,
|
||||||
|
.mhandler.info_new = do_info_spice,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
.name = "name",
|
.name = "name",
|
||||||
.args_type = "",
|
.args_type = "",
|
||||||
|
@ -2768,6 +2778,16 @@ static const mon_cmd_t qmp_query_cmds[] = {
|
||||||
.user_print = do_info_vnc_print,
|
.user_print = do_info_vnc_print,
|
||||||
.mhandler.info_new = do_info_vnc,
|
.mhandler.info_new = do_info_vnc,
|
||||||
},
|
},
|
||||||
|
#if defined(CONFIG_SPICE)
|
||||||
|
{
|
||||||
|
.name = "spice",
|
||||||
|
.args_type = "",
|
||||||
|
.params = "",
|
||||||
|
.help = "show the spice server status",
|
||||||
|
.user_print = do_info_spice_print,
|
||||||
|
.mhandler.info_new = do_info_spice,
|
||||||
|
},
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
.name = "name",
|
.name = "name",
|
||||||
.args_type = "",
|
.args_type = "",
|
||||||
|
|
|
@ -1438,6 +1438,76 @@ Example:
|
||||||
|
|
||||||
EQMP
|
EQMP
|
||||||
|
|
||||||
|
SQMP
|
||||||
|
query-spice
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Show SPICE server information.
|
||||||
|
|
||||||
|
Return a json-object with server information. Connected clients are returned
|
||||||
|
as a json-array of json-objects.
|
||||||
|
|
||||||
|
The main json-object contains the following:
|
||||||
|
|
||||||
|
- "enabled": true or false (json-bool)
|
||||||
|
- "host": server's IP address (json-string)
|
||||||
|
- "port": server's port number (json-int, optional)
|
||||||
|
- "tls-port": server's port number (json-int, optional)
|
||||||
|
- "auth": authentication method (json-string)
|
||||||
|
- Possible values: "none", "spice"
|
||||||
|
- "channels": a json-array of all active channels clients
|
||||||
|
|
||||||
|
Channels are described by a json-object, each one contain the following:
|
||||||
|
|
||||||
|
- "host": client's IP address (json-string)
|
||||||
|
- "family": address family (json-string)
|
||||||
|
- Possible values: "ipv4", "ipv6", "unix", "unknown"
|
||||||
|
- "port": client's port number (json-string)
|
||||||
|
- "connection-id": spice connection id. All channels with the same id
|
||||||
|
belong to the same spice session (json-int)
|
||||||
|
- "channel-type": channel type. "1" is the main control channel, filter for
|
||||||
|
this one if you want track spice sessions only (json-int)
|
||||||
|
- "channel-id": channel id. Usually "0", might be different needed when
|
||||||
|
multiple channels of the same type exist, such as multiple
|
||||||
|
display channels in a multihead setup (json-int)
|
||||||
|
- "tls": whevener the channel is encrypted (json-bool)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
-> { "execute": "query-spice" }
|
||||||
|
<- {
|
||||||
|
"return": {
|
||||||
|
"enabled": true,
|
||||||
|
"auth": "spice",
|
||||||
|
"port": 5920,
|
||||||
|
"tls-port": 5921,
|
||||||
|
"host": "0.0.0.0",
|
||||||
|
"channels": [
|
||||||
|
{
|
||||||
|
"port": "54924",
|
||||||
|
"family": "ipv4",
|
||||||
|
"channel-type": 1,
|
||||||
|
"connection-id": 1804289383,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"channel-id": 0,
|
||||||
|
"tls": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"port": "36710",
|
||||||
|
"family": "ipv4",
|
||||||
|
"channel-type": 4,
|
||||||
|
"connection-id": 1804289383,
|
||||||
|
"host": "127.0.0.1",
|
||||||
|
"channel-id": 0,
|
||||||
|
"tls": false
|
||||||
|
},
|
||||||
|
[ ... more channels follow ... ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EQMP
|
||||||
|
|
||||||
SQMP
|
SQMP
|
||||||
query-name
|
query-name
|
||||||
----------
|
----------
|
||||||
|
|
|
@ -33,6 +33,9 @@ void qemu_spice_audio_init(void);
|
||||||
void qemu_spice_display_init(DisplayState *ds);
|
void qemu_spice_display_init(DisplayState *ds);
|
||||||
int qemu_spice_add_interface(SpiceBaseInstance *sin);
|
int qemu_spice_add_interface(SpiceBaseInstance *sin);
|
||||||
|
|
||||||
|
void do_info_spice_print(Monitor *mon, const QObject *data);
|
||||||
|
void do_info_spice(Monitor *mon, QObject **ret_data);
|
||||||
|
|
||||||
#else /* CONFIG_SPICE */
|
#else /* CONFIG_SPICE */
|
||||||
|
|
||||||
#define using_spice 0
|
#define using_spice 0
|
||||||
|
|
136
ui/spice-core.c
136
ui/spice-core.c
|
@ -131,6 +131,36 @@ static void watch_remove(SpiceWatch *watch)
|
||||||
|
|
||||||
#if SPICE_INTERFACE_CORE_MINOR >= 3
|
#if SPICE_INTERFACE_CORE_MINOR >= 3
|
||||||
|
|
||||||
|
typedef struct ChannelList ChannelList;
|
||||||
|
struct ChannelList {
|
||||||
|
SpiceChannelEventInfo *info;
|
||||||
|
QTAILQ_ENTRY(ChannelList) link;
|
||||||
|
};
|
||||||
|
static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list);
|
||||||
|
|
||||||
|
static void channel_list_add(SpiceChannelEventInfo *info)
|
||||||
|
{
|
||||||
|
ChannelList *item;
|
||||||
|
|
||||||
|
item = qemu_mallocz(sizeof(*item));
|
||||||
|
item->info = info;
|
||||||
|
QTAILQ_INSERT_TAIL(&channel_list, item, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void channel_list_del(SpiceChannelEventInfo *info)
|
||||||
|
{
|
||||||
|
ChannelList *item;
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(item, &channel_list, link) {
|
||||||
|
if (item->info != info) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
QTAILQ_REMOVE(&channel_list, item, link);
|
||||||
|
qemu_free(item);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void add_addr_info(QDict *dict, struct sockaddr *addr, int len)
|
static void add_addr_info(QDict *dict, struct sockaddr *addr, int len)
|
||||||
{
|
{
|
||||||
char host[NI_MAXHOST], port[NI_MAXSERV];
|
char host[NI_MAXHOST], port[NI_MAXSERV];
|
||||||
|
@ -155,6 +185,22 @@ static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info)
|
||||||
qdict_put(dict, "tls", qbool_from_int(tls));
|
qdict_put(dict, "tls", qbool_from_int(tls));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QList *channel_list_get(void)
|
||||||
|
{
|
||||||
|
ChannelList *item;
|
||||||
|
QList *list;
|
||||||
|
QDict *dict;
|
||||||
|
|
||||||
|
list = qlist_new();
|
||||||
|
QTAILQ_FOREACH(item, &channel_list, link) {
|
||||||
|
dict = qdict_new();
|
||||||
|
add_addr_info(dict, &item->info->paddr, item->info->plen);
|
||||||
|
add_channel_info(dict, item->info);
|
||||||
|
qlist_append(list, dict);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
static void channel_event(int event, SpiceChannelEventInfo *info)
|
static void channel_event(int event, SpiceChannelEventInfo *info)
|
||||||
{
|
{
|
||||||
static const int qevent[] = {
|
static const int qevent[] = {
|
||||||
|
@ -174,6 +220,10 @@ static void channel_event(int event, SpiceChannelEventInfo *info)
|
||||||
if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
|
if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
|
||||||
qdict_put(server, "auth", qstring_from_str(auth));
|
qdict_put(server, "auth", qstring_from_str(auth));
|
||||||
add_channel_info(client, info);
|
add_channel_info(client, info);
|
||||||
|
channel_list_add(info);
|
||||||
|
}
|
||||||
|
if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) {
|
||||||
|
channel_list_del(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
|
data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
|
||||||
|
@ -278,6 +328,92 @@ static const char *wan_compression_names[] = {
|
||||||
|
|
||||||
/* functions for the rest of qemu */
|
/* functions for the rest of qemu */
|
||||||
|
|
||||||
|
static void info_spice_iter(QObject *obj, void *opaque)
|
||||||
|
{
|
||||||
|
QDict *client;
|
||||||
|
Monitor *mon = opaque;
|
||||||
|
|
||||||
|
client = qobject_to_qdict(obj);
|
||||||
|
monitor_printf(mon, "Channel:\n");
|
||||||
|
monitor_printf(mon, " address: %s:%s%s\n",
|
||||||
|
qdict_get_str(client, "host"),
|
||||||
|
qdict_get_str(client, "port"),
|
||||||
|
qdict_get_bool(client, "tls") ? " [tls]" : "");
|
||||||
|
monitor_printf(mon, " session: %" PRId64 "\n",
|
||||||
|
qdict_get_int(client, "connection-id"));
|
||||||
|
monitor_printf(mon, " channel: %d:%d\n",
|
||||||
|
(int)qdict_get_int(client, "channel-type"),
|
||||||
|
(int)qdict_get_int(client, "channel-id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_info_spice_print(Monitor *mon, const QObject *data)
|
||||||
|
{
|
||||||
|
QDict *server;
|
||||||
|
QList *channels;
|
||||||
|
const char *host;
|
||||||
|
int port;
|
||||||
|
|
||||||
|
server = qobject_to_qdict(data);
|
||||||
|
if (qdict_get_bool(server, "enabled") == 0) {
|
||||||
|
monitor_printf(mon, "Server: disabled\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor_printf(mon, "Server:\n");
|
||||||
|
host = qdict_get_str(server, "host");
|
||||||
|
port = qdict_get_try_int(server, "port", -1);
|
||||||
|
if (port != -1) {
|
||||||
|
monitor_printf(mon, " address: %s:%d\n", host, port);
|
||||||
|
}
|
||||||
|
port = qdict_get_try_int(server, "tls-port", -1);
|
||||||
|
if (port != -1) {
|
||||||
|
monitor_printf(mon, " address: %s:%d [tls]\n", host, port);
|
||||||
|
}
|
||||||
|
monitor_printf(mon, " auth: %s\n", qdict_get_str(server, "auth"));
|
||||||
|
|
||||||
|
channels = qdict_get_qlist(server, "channels");
|
||||||
|
if (qlist_empty(channels)) {
|
||||||
|
monitor_printf(mon, "Channels: none\n");
|
||||||
|
} else {
|
||||||
|
qlist_iter(channels, info_spice_iter, mon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_info_spice(Monitor *mon, QObject **ret_data)
|
||||||
|
{
|
||||||
|
QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
|
||||||
|
QDict *server;
|
||||||
|
QList *clist;
|
||||||
|
const char *addr;
|
||||||
|
int port, tls_port;
|
||||||
|
|
||||||
|
if (!spice_server) {
|
||||||
|
*ret_data = qobject_from_jsonf("{ 'enabled': false }");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = qemu_opt_get(opts, "addr");
|
||||||
|
port = qemu_opt_get_number(opts, "port", 0);
|
||||||
|
tls_port = qemu_opt_get_number(opts, "tls-port", 0);
|
||||||
|
clist = channel_list_get();
|
||||||
|
|
||||||
|
server = qdict_new();
|
||||||
|
qdict_put(server, "enabled", qbool_from_int(true));
|
||||||
|
qdict_put(server, "auth", qstring_from_str(auth));
|
||||||
|
qdict_put(server, "host", qstring_from_str(addr ? addr : "0.0.0.0"));
|
||||||
|
if (port) {
|
||||||
|
qdict_put(server, "port", qint_from_int(port));
|
||||||
|
}
|
||||||
|
if (tls_port) {
|
||||||
|
qdict_put(server, "tls-port", qint_from_int(tls_port));
|
||||||
|
}
|
||||||
|
if (clist) {
|
||||||
|
qdict_put(server, "channels", clist);
|
||||||
|
}
|
||||||
|
|
||||||
|
*ret_data = QOBJECT(server);
|
||||||
|
}
|
||||||
|
|
||||||
static int add_channel(const char *name, const char *value, void *opaque)
|
static int add_channel(const char *name, const char *value, void *opaque)
|
||||||
{
|
{
|
||||||
int security = 0;
|
int security = 0;
|
||||||
|
|
Loading…
Reference in New Issue