Monitor patches for 2023-01-19

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmPJO+8SHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZTbBwP/RsZLLDCz6moSet4Hly+vPTWibyYYDkS
 uk6a70Ja7fsAcONumBaXdpoinPtbED662eYxidbLFr//tAjnKu57jnwAIFTTOKJc
 sCVtdgOlbNSZFrqyIr8ctY6yKJLLBaa02in/BczFIQphPatpUwvxrvrdgxc58NNK
 qaeKDnWKXvZ6EUaYPpruxqE5J/NV0ykyab9Rc8rNJqdUMnqbd662zHcA0l31misH
 gAfKBhReo53XUbfvoFS9kaoBQaTJPk4CcKHaT7NJXR8ezlIEQVLtFzdfAf04kSdG
 8VRwwx5NDpmafATEaMXJhJ74Fyc8biDWXoQ6aA1jdUCu39B2EPl1H1qlCG9ZYp7w
 CkbJU6UwtOYsG11NU8Mr1u9rMlAgAVkkLsMYiiI4cpLBkI0vkoV66ms3oZgYUtbq
 /TgCpfjKpE87ZBLIwJjGsg+TMN2AH5lpt/49HV8QEFA8mI7h29oo2HvheXE7PPzH
 b9iIe9ADrKwB5DpJW2vigPj+fSHqoSGd3R/hUcMVOubKnJme97mys2hD+sfxga/H
 qfKLzgTqQI2dGhfow/8wzfYdfYmkii+ggDyzcxLAxx2ITO/fLWJygmTgXJmA3dl6
 pnpZvhOu4dEgK4VstugXcvgCVHUVXd9wqI9wxJAc+mKanTmZIJBnxCm/QVG2/w/d
 5IgmP9Se7vb0
 =7OEC
 -----END PGP SIGNATURE-----

Merge tag 'pull-monitor-2023-01-19' of https://repo.or.cz/qemu/armbru into staging

Monitor patches for 2023-01-19

# -----BEGIN PGP SIGNATURE-----
#
# iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmPJO+8SHGFybWJydUBy
# ZWRoYXQuY29tAAoJEDhwtADrkYZTbBwP/RsZLLDCz6moSet4Hly+vPTWibyYYDkS
# uk6a70Ja7fsAcONumBaXdpoinPtbED662eYxidbLFr//tAjnKu57jnwAIFTTOKJc
# sCVtdgOlbNSZFrqyIr8ctY6yKJLLBaa02in/BczFIQphPatpUwvxrvrdgxc58NNK
# qaeKDnWKXvZ6EUaYPpruxqE5J/NV0ykyab9Rc8rNJqdUMnqbd662zHcA0l31misH
# gAfKBhReo53XUbfvoFS9kaoBQaTJPk4CcKHaT7NJXR8ezlIEQVLtFzdfAf04kSdG
# 8VRwwx5NDpmafATEaMXJhJ74Fyc8biDWXoQ6aA1jdUCu39B2EPl1H1qlCG9ZYp7w
# CkbJU6UwtOYsG11NU8Mr1u9rMlAgAVkkLsMYiiI4cpLBkI0vkoV66ms3oZgYUtbq
# /TgCpfjKpE87ZBLIwJjGsg+TMN2AH5lpt/49HV8QEFA8mI7h29oo2HvheXE7PPzH
# b9iIe9ADrKwB5DpJW2vigPj+fSHqoSGd3R/hUcMVOubKnJme97mys2hD+sfxga/H
# qfKLzgTqQI2dGhfow/8wzfYdfYmkii+ggDyzcxLAxx2ITO/fLWJygmTgXJmA3dl6
# pnpZvhOu4dEgK4VstugXcvgCVHUVXd9wqI9wxJAc+mKanTmZIJBnxCm/QVG2/w/d
# 5IgmP9Se7vb0
# =7OEC
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 19 Jan 2023 12:47:43 GMT
# 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

* tag 'pull-monitor-2023-01-19' of https://repo.or.cz/qemu/armbru:
  ui: Simplify control flow in qemu_mouse_set()
  ui: Split hmp_mouse_set() and move the HMP part to ui/
  ui: Don't check for mode change after mouse_set error
  ui: Reduce nesting in hmp_change_vnc() slightly
  ui: Factor out hmp_change_vnc(), and move to ui/ui-hmp-cmds.c
  ui: Improve "change vnc" error reporting
  ui: Move HMP commands from monitor to new ui/ui-hmp-cmds.c
  ui: Factor out qmp_add_client() parts and move to ui/ui-qmp-cmds.c
  ui: Move QMP commands from monitor to new ui/ui-qmp-cmds.c
  ui: Clean up a few things checkpatch.pl would flag later on
  ui/spice: Give hmp_info_spice()'s channel_names[] static linkage
  ui/spice: QXLInterface method set_mm_time() is now dead, drop
  ui/spice: Require spice-server >= 0.14.0
  Revert "hmp: info spice: take out webdav"
  ui/spice: Require spice-protocol >= 0.14.0
  ui: Fix silent truncation of numeric keys in HMP sendkey
  ui: Check numeric part of expire_password argument @time properly

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2023-01-19 18:58:03 +00:00
commit 70d17c3eed
19 changed files with 727 additions and 650 deletions

View File

@ -98,9 +98,7 @@ static SpiceCharDeviceInterface vmc_interface = {
.write = vmc_write,
.read = vmc_read,
.event = vmc_event,
#if SPICE_SERVER_VERSION >= 0x000c06
.flags = SPICE_CHAR_DEVICE_NOTIFY_WRITABLE,
#endif
};

View File

@ -260,8 +260,7 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay)
QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG,
0));
} else {
/* >= release 0.12.6, < release 0.14.2 */
#if SPICE_SERVER_VERSION >= 0x000c06 && SPICE_SERVER_VERSION < 0x000e02
#if SPICE_SERVER_VERSION < 0x000e02 /* release 0.14.2 */
if (qxl->max_outputs) {
spice_qxl_set_max_monitors(&qxl->ssd.qxl, qxl->max_outputs);
}
@ -544,22 +543,6 @@ static void interface_set_compression_level(QXLInstance *sin, int level)
qxl_rom_set_dirty(qxl);
}
#if SPICE_NEEDS_SET_MM_TIME
static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
{
PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
if (!qemu_spice_display_is_running(&qxl->ssd)) {
return;
}
trace_qxl_interface_set_mm_time(qxl->id, mm_time);
qxl->shadow_rom.mm_clock = cpu_to_le32(mm_time);
qxl->rom->mm_clock = cpu_to_le32(mm_time);
qxl_rom_set_dirty(qxl);
}
#endif
static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
{
PCIQXLDevice *qxl = container_of(sin, PCIQXLDevice, ssd.qxl);
@ -1089,12 +1072,10 @@ static int interface_client_monitors_config(QXLInstance *sin,
return 1;
}
#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
/* limit number of outputs based on setting limit */
if (qxl->max_outputs && qxl->max_outputs <= max_outputs) {
max_outputs = qxl->max_outputs;
}
#endif
config_changed = qxl_rom_monitors_config_changed(rom,
monitors_config,
@ -1148,9 +1129,6 @@ static const QXLInterface qxl_interface = {
#endif
.set_compression_level = interface_set_compression_level,
#if SPICE_NEEDS_SET_MM_TIME
.set_mm_time = interface_set_mm_time,
#endif
.get_init_info = interface_get_init_info,
/* the callbacks below are called from spice server thread context */
@ -2487,9 +2465,7 @@ static Property qxl_properties[] = {
DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024),
#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
DEFINE_PROP_UINT16("max_outputs", PCIQXLDevice, max_outputs, 0),
#endif
DEFINE_PROP_UINT32("xres", PCIQXLDevice, xres, 0),
DEFINE_PROP_UINT32("yres", PCIQXLDevice, yres, 0),
DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false),

View File

@ -99,9 +99,7 @@ struct PCIQXLDevice {
QXLModes *modes;
uint32_t rom_size;
MemoryRegion rom_bar;
#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */
uint16_t max_outputs;
#endif
/* vram pci bar */
uint64_t vram_size;

View File

@ -55,7 +55,6 @@ virtio_gpu_fence_ctrl(uint64_t fence, uint32_t type) "fence 0x%" PRIx64 ", type
virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64
# qxl.c
disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d"
disable qxl_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u"
qxl_create_guest_primary(int qid, uint32_t width, uint32_t height, uint64_t mem, uint32_t format, uint32_t position) "%d %ux%u mem=0x%" PRIx64 " %u,%u"
qxl_create_guest_primary_rest(int qid, int32_t stride, uint32_t type, uint32_t flags) "%d %d,%d,%d"

View File

@ -73,6 +73,11 @@ void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict);
void hmp_set_password(Monitor *mon, const QDict *qdict);
void hmp_expire_password(Monitor *mon, const QDict *qdict);
void hmp_change(Monitor *mon, const QDict *qdict);
#ifdef CONFIG_VNC
void hmp_change_vnc(Monitor *mon, const char *device, const char *target,
const char *arg, const char *read_only, bool force,
Error **errp);
#endif
void hmp_migrate(Monitor *mon, const QDict *qdict);
void hmp_device_add(Monitor *mon, const QDict *qdict);
void hmp_device_del(Monitor *mon, const QDict *qdict);
@ -81,6 +86,9 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict);
void hmp_netdev_del(Monitor *mon, const QDict *qdict);
void hmp_getfd(Monitor *mon, const QDict *qdict);
void hmp_closefd(Monitor *mon, const QDict *qdict);
void hmp_mouse_move(Monitor *mon, const QDict *qdict);
void hmp_mouse_button(Monitor *mon, const QDict *qdict);
void hmp_mouse_set(Monitor *mon, const QDict *qdict);
void hmp_sendkey(Monitor *mon, const QDict *qdict);
void coroutine_fn hmp_screendump(Monitor *mon, const QDict *qdict);
void hmp_chardev_add(Monitor *mon, const QDict *qdict);

View File

@ -0,0 +1,26 @@
/*
* QMP command helpers
*
* Copyright (c) 2022 Red Hat Inc.
*
* Authors:
* Markus Armbruster <armbru@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*/
#ifndef MONITOR_QMP_HELPERS_H
bool qmp_add_client_spice(int fd, bool has_skipauth, bool skipauth,
bool has_tls, bool tls, Error **errp);
#ifdef CONFIG_VNC
bool qmp_add_client_vnc(int fd, bool has_skipauth, bool skipauth,
bool has_tls, bool tls, Error **errp);
#endif
#ifdef CONFIG_DBUS_DISPLAY
bool qmp_add_client_dbus_display(int fd, bool has_skipauth, bool skipauth,
bool has_tls, bool tls, Error **errp);
#endif
#endif

View File

@ -65,7 +65,7 @@ void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry);
void kbd_put_ledstate(int ledstate);
void hmp_mouse_set(Monitor *mon, const QDict *qdict);
bool qemu_mouse_set(int index, Error **errp);
/* keysym is a unicode code except for special keys (see QEMU_KEY_xxx
constants) */

View File

@ -34,13 +34,7 @@ int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con);
int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
const char *subject);
#if !defined(SPICE_SERVER_VERSION) || (SPICE_SERVER_VERSION < 0xc06)
#define SPICE_NEEDS_SET_MM_TIME 1
#else
#define SPICE_NEEDS_SET_MM_TIME 0
#endif
#if defined(SPICE_SERVER_VERSION) && (SPICE_SERVER_VERSION >= 0x000f00)
#if SPICE_SERVER_VERSION >= 0x000f00 /* release 0.15.0 */
#define SPICE_HAS_ATTACHED_WORKER 1
#else
#define SPICE_HAS_ATTACHED_WORKER 0

View File

@ -28,11 +28,9 @@
#include "ui/console.h"
#if defined(CONFIG_OPENGL) && defined(CONFIG_GBM)
# if SPICE_SERVER_VERSION >= 0x000d01 /* release 0.13.1 */
# define HAVE_SPICE_GL 1
# include "ui/egl-helpers.h"
# include "ui/egl-context.h"
# endif
#endif
#define NUM_MEMSLOTS 8

View File

@ -742,13 +742,13 @@ endif
spice_protocol = not_found
if not get_option('spice_protocol').auto() or have_system
spice_protocol = dependency('spice-protocol', version: '>=0.12.3',
spice_protocol = dependency('spice-protocol', version: '>=0.14.0',
required: get_option('spice_protocol'),
method: 'pkg-config', kwargs: static_kwargs)
endif
spice = not_found
if not get_option('spice').auto() or have_system
spice = dependency('spice-server', version: '>=0.12.5',
spice = dependency('spice-server', version: '>=0.14.0',
required: get_option('spice'),
method: 'pkg-config', kwargs: static_kwargs)
endif

View File

@ -25,7 +25,7 @@
#include "qemu/timer.h"
#include "qemu/sockets.h"
#include "qemu/help_option.h"
#include "monitor/monitor-internal.h"
#include "monitor/monitor.h"
#include "qapi/error.h"
#include "qapi/clone-visitor.h"
#include "qapi/opts-visitor.h"
@ -41,7 +41,6 @@
#include "qapi/qapi-commands-run-state.h"
#include "qapi/qapi-commands-stats.h"
#include "qapi/qapi-commands-tpm.h"
#include "qapi/qapi-commands-ui.h"
#include "qapi/qapi-commands-virtio.h"
#include "qapi/qapi-visit-virtio.h"
#include "qapi/qapi-visit-net.h"
@ -51,7 +50,6 @@
#include "qapi/string-input-visitor.h"
#include "qapi/string-output-visitor.h"
#include "qom/object_interfaces.h"
#include "ui/console.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "hw/core/cpu.h"
@ -59,10 +57,6 @@
#include "migration/snapshot.h"
#include "migration/misc.h"
#ifdef CONFIG_SPICE
#include <spice/enums.h>
#endif
bool hmp_handle_error(Monitor *mon, Error *err)
{
if (err) {
@ -178,26 +172,6 @@ void hmp_info_chardev(Monitor *mon, const QDict *qdict)
qapi_free_ChardevInfoList(char_info);
}
void hmp_info_mice(Monitor *mon, const QDict *qdict)
{
MouseInfoList *mice_list, *mouse;
mice_list = qmp_query_mice(NULL);
if (!mice_list) {
monitor_printf(mon, "No mouse devices connected\n");
return;
}
for (mouse = mice_list; mouse; mouse = mouse->next) {
monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n",
mouse->value->current ? '*' : ' ',
mouse->value->index, mouse->value->name,
mouse->value->absolute ? " (absolute)" : "");
}
qapi_free_MouseInfoList(mice_list);
}
void hmp_info_migrate(Monitor *mon, const QDict *qdict)
{
MigrationInfo *info;
@ -516,172 +490,6 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict)
qapi_free_MigrationParameters(params);
}
#ifdef CONFIG_VNC
/* Helper for hmp_info_vnc_clients, _servers */
static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info,
const char *name)
{
monitor_printf(mon, " %s: %s:%s (%s%s)\n",
name,
info->host,
info->service,
NetworkAddressFamily_str(info->family),
info->websocket ? " (Websocket)" : "");
}
/* Helper displaying and auth and crypt info */
static void hmp_info_vnc_authcrypt(Monitor *mon, const char *indent,
VncPrimaryAuth auth,
VncVencryptSubAuth *vencrypt)
{
monitor_printf(mon, "%sAuth: %s (Sub: %s)\n", indent,
VncPrimaryAuth_str(auth),
vencrypt ? VncVencryptSubAuth_str(*vencrypt) : "none");
}
static void hmp_info_vnc_clients(Monitor *mon, VncClientInfoList *client)
{
while (client) {
VncClientInfo *cinfo = client->value;
hmp_info_VncBasicInfo(mon, qapi_VncClientInfo_base(cinfo), "Client");
monitor_printf(mon, " x509_dname: %s\n",
cinfo->x509_dname ?: "none");
monitor_printf(mon, " sasl_username: %s\n",
cinfo->sasl_username ?: "none");
client = client->next;
}
}
static void hmp_info_vnc_servers(Monitor *mon, VncServerInfo2List *server)
{
while (server) {
VncServerInfo2 *sinfo = server->value;
hmp_info_VncBasicInfo(mon, qapi_VncServerInfo2_base(sinfo), "Server");
hmp_info_vnc_authcrypt(mon, " ", sinfo->auth,
sinfo->has_vencrypt ? &sinfo->vencrypt : NULL);
server = server->next;
}
}
void hmp_info_vnc(Monitor *mon, const QDict *qdict)
{
VncInfo2List *info2l, *info2l_head;
Error *err = NULL;
info2l = qmp_query_vnc_servers(&err);
info2l_head = info2l;
if (hmp_handle_error(mon, err)) {
return;
}
if (!info2l) {
monitor_printf(mon, "None\n");
return;
}
while (info2l) {
VncInfo2 *info = info2l->value;
monitor_printf(mon, "%s:\n", info->id);
hmp_info_vnc_servers(mon, info->server);
hmp_info_vnc_clients(mon, info->clients);
if (!info->server) {
/* The server entry displays its auth, we only
* need to display in the case of 'reverse' connections
* where there's no server.
*/
hmp_info_vnc_authcrypt(mon, " ", info->auth,
info->has_vencrypt ? &info->vencrypt : NULL);
}
if (info->display) {
monitor_printf(mon, " Display: %s\n", info->display);
}
info2l = info2l->next;
}
qapi_free_VncInfo2List(info2l_head);
}
#endif
#ifdef CONFIG_SPICE
void hmp_info_spice(Monitor *mon, const QDict *qdict)
{
SpiceChannelList *chan;
SpiceInfo *info;
const char *channel_name;
const char * const channel_names[] = {
[SPICE_CHANNEL_MAIN] = "main",
[SPICE_CHANNEL_DISPLAY] = "display",
[SPICE_CHANNEL_INPUTS] = "inputs",
[SPICE_CHANNEL_CURSOR] = "cursor",
[SPICE_CHANNEL_PLAYBACK] = "playback",
[SPICE_CHANNEL_RECORD] = "record",
[SPICE_CHANNEL_TUNNEL] = "tunnel",
[SPICE_CHANNEL_SMARTCARD] = "smartcard",
[SPICE_CHANNEL_USBREDIR] = "usbredir",
[SPICE_CHANNEL_PORT] = "port",
#if 0
/* minimum spice-protocol is 0.12.3, webdav was added in 0.12.7,
* no easy way to #ifdef (SPICE_CHANNEL_* is a enum). Disable
* as quick fix for build failures with older versions. */
[SPICE_CHANNEL_WEBDAV] = "webdav",
#endif
};
info = qmp_query_spice(NULL);
if (!info->enabled) {
monitor_printf(mon, "Server: disabled\n");
goto out;
}
monitor_printf(mon, "Server:\n");
if (info->has_port) {
monitor_printf(mon, " address: %s:%" PRId64 "\n",
info->host, info->port);
}
if (info->has_tls_port) {
monitor_printf(mon, " address: %s:%" PRId64 " [tls]\n",
info->host, info->tls_port);
}
monitor_printf(mon, " migrated: %s\n",
info->migrated ? "true" : "false");
monitor_printf(mon, " auth: %s\n", info->auth);
monitor_printf(mon, " compiled: %s\n", info->compiled_version);
monitor_printf(mon, " mouse-mode: %s\n",
SpiceQueryMouseMode_str(info->mouse_mode));
if (!info->has_channels || info->channels == NULL) {
monitor_printf(mon, "Channels: none\n");
} else {
for (chan = info->channels; chan; chan = chan->next) {
monitor_printf(mon, "Channel:\n");
monitor_printf(mon, " address: %s:%s%s\n",
chan->value->host, chan->value->port,
chan->value->tls ? " [tls]" : "");
monitor_printf(mon, " session: %" PRId64 "\n",
chan->value->connection_id);
monitor_printf(mon, " channel: %" PRId64 ":%" PRId64 "\n",
chan->value->channel_type, chan->value->channel_id);
channel_name = "unknown";
if (chan->value->channel_type > 0 &&
chan->value->channel_type < ARRAY_SIZE(channel_names) &&
channel_names[chan->value->channel_type]) {
channel_name = channel_names[chan->value->channel_type];
}
monitor_printf(mon, " channel name: %s\n", channel_name);
}
}
out:
qapi_free_SpiceInfo(info);
}
#endif
void hmp_info_balloon(Monitor *mon, const QDict *qdict)
{
BalloonInfo *info;
@ -1266,78 +1074,6 @@ void hmp_x_colo_lost_heartbeat(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, err);
}
void hmp_set_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *password = qdict_get_str(qdict, "password");
const char *display = qdict_get_try_str(qdict, "display");
const char *connected = qdict_get_try_str(qdict, "connected");
Error *err = NULL;
SetPasswordOptions opts = {
.password = (char *)password,
.has_connected = !!connected,
};
opts.connected = qapi_enum_parse(&SetPasswordAction_lookup, connected,
SET_PASSWORD_ACTION_KEEP, &err);
if (err) {
goto out;
}
opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
DISPLAY_PROTOCOL_VNC, &err);
if (err) {
goto out;
}
if (opts.protocol == DISPLAY_PROTOCOL_VNC) {
opts.u.vnc.display = (char *)display;
}
qmp_set_password(&opts, &err);
out:
hmp_handle_error(mon, err);
}
void hmp_expire_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *whenstr = qdict_get_str(qdict, "time");
const char *display = qdict_get_try_str(qdict, "display");
Error *err = NULL;
ExpirePasswordOptions opts = {
.time = (char *)whenstr,
};
opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
DISPLAY_PROTOCOL_VNC, &err);
if (err) {
goto out;
}
if (opts.protocol == DISPLAY_PROTOCOL_VNC) {
opts.u.vnc.display = (char *)display;
}
qmp_expire_password(&opts, &err);
out:
hmp_handle_error(mon, err);
}
#ifdef CONFIG_VNC
static void hmp_change_read_arg(void *opaque, const char *password,
void *readline_opaque)
{
qmp_change_vnc_password(password, NULL);
monitor_read_command(opaque, 1);
}
#endif
void hmp_change(Monitor *mon, const QDict *qdict)
{
const char *device = qdict_get_str(qdict, "device");
@ -1350,23 +1086,7 @@ void hmp_change(Monitor *mon, const QDict *qdict)
#ifdef CONFIG_VNC
if (strcmp(device, "vnc") == 0) {
if (read_only) {
monitor_printf(mon,
"Parameter 'read-only-mode' is invalid for VNC\n");
return;
}
if (strcmp(target, "passwd") == 0 ||
strcmp(target, "password") == 0) {
if (!arg) {
MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
monitor_read_password(hmp_mon, hmp_change_read_arg, NULL);
return;
} else {
qmp_change_vnc_password(arg, &err);
}
} else {
monitor_printf(mon, "Expected 'password' after 'vnc'\n");
}
hmp_change_vnc(mon, device, target, arg, read_only, force, &err);
} else
#endif
{
@ -1525,90 +1245,6 @@ void hmp_closefd(Monitor *mon, const QDict *qdict)
hmp_handle_error(mon, err);
}
void hmp_sendkey(Monitor *mon, const QDict *qdict)
{
const char *keys = qdict_get_str(qdict, "keys");
KeyValue *v = NULL;
KeyValueList *head = NULL, **tail = &head;
int has_hold_time = qdict_haskey(qdict, "hold-time");
int hold_time = qdict_get_try_int(qdict, "hold-time", -1);
Error *err = NULL;
const char *separator;
int keyname_len;
while (1) {
separator = qemu_strchrnul(keys, '-');
keyname_len = separator - keys;
/* Be compatible with old interface, convert user inputted "<" */
if (keys[0] == '<' && keyname_len == 1) {
keys = "less";
keyname_len = 4;
}
v = g_malloc0(sizeof(*v));
if (strstart(keys, "0x", NULL)) {
char *endp;
int value = strtoul(keys, &endp, 0);
assert(endp <= keys + keyname_len);
if (endp != keys + keyname_len) {
goto err_out;
}
v->type = KEY_VALUE_KIND_NUMBER;
v->u.number.data = value;
} else {
int idx = index_from_key(keys, keyname_len);
if (idx == Q_KEY_CODE__MAX) {
goto err_out;
}
v->type = KEY_VALUE_KIND_QCODE;
v->u.qcode.data = idx;
}
QAPI_LIST_APPEND(tail, v);
v = NULL;
if (!*separator) {
break;
}
keys = separator + 1;
}
qmp_send_key(head, has_hold_time, hold_time, &err);
hmp_handle_error(mon, err);
out:
qapi_free_KeyValue(v);
qapi_free_KeyValueList(head);
return;
err_out:
monitor_printf(mon, "invalid parameter: %.*s\n", keyname_len, keys);
goto out;
}
void coroutine_fn
hmp_screendump(Monitor *mon, const QDict *qdict)
{
const char *filename = qdict_get_str(qdict, "filename");
const char *id = qdict_get_try_str(qdict, "device");
int64_t head = qdict_get_try_int(qdict, "head", 0);
const char *input_format = qdict_get_try_str(qdict, "format");
Error *err = NULL;
ImageFormat format;
format = qapi_enum_parse(&ImageFormat_lookup, input_format,
IMAGE_FORMAT_PPM, &err);
if (err) {
goto end;
}
qmp_screendump(filename, id, id != NULL, head,
input_format != NULL, format, &err);
end:
hmp_handle_error(mon, err);
}
void hmp_chardev_add(Monitor *mon, const QDict *qdict)
{
const char *args = qdict_get_str(qdict, "args");

View File

@ -33,8 +33,6 @@
#include "ui/qemu-spice.h"
#include "qemu/config-file.h"
#include "qemu/ctype.h"
#include "ui/console.h"
#include "ui/input.h"
#include "audio/audio.h"
#include "disas/disas.h"
#include "qemu/timer.h"
@ -825,49 +823,6 @@ static void hmp_sum(Monitor *mon, const QDict *qdict)
monitor_printf(mon, "%05d\n", sum);
}
static int mouse_button_state;
static void hmp_mouse_move(Monitor *mon, const QDict *qdict)
{
int dx, dy, dz, button;
const char *dx_str = qdict_get_str(qdict, "dx_str");
const char *dy_str = qdict_get_str(qdict, "dy_str");
const char *dz_str = qdict_get_try_str(qdict, "dz_str");
dx = strtol(dx_str, NULL, 0);
dy = strtol(dy_str, NULL, 0);
qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx);
qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy);
if (dz_str) {
dz = strtol(dz_str, NULL, 0);
if (dz != 0) {
button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN;
qemu_input_queue_btn(NULL, button, true);
qemu_input_event_sync();
qemu_input_queue_btn(NULL, button, false);
}
}
qemu_input_event_sync();
}
static void hmp_mouse_button(Monitor *mon, const QDict *qdict)
{
static uint32_t bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
[INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
[INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
};
int button_state = qdict_get_int(qdict, "button_state");
if (mouse_button_state == button_state) {
return;
}
qemu_input_update_buttons(NULL, bmap, mouse_button_state, button_state);
qemu_input_event_sync();
mouse_button_state = button_state;
}
static void hmp_ioport_read(Monitor *mon, const QDict *qdict)
{
int size = qdict_get_int(qdict, "size");
@ -1700,28 +1655,6 @@ void object_del_completion(ReadLineState *rs, int nb_args, const char *str)
qapi_free_ObjectPropertyInfoList(start);
}
void sendkey_completion(ReadLineState *rs, int nb_args, const char *str)
{
int i;
char *sep;
size_t len;
if (nb_args != 2) {
return;
}
sep = strrchr(str, '-');
if (sep) {
str = sep + 1;
}
len = strlen(str);
readline_set_completion_index(rs, len);
for (i = 0; i < Q_KEY_CODE__MAX; i++) {
if (!strncmp(str, QKeyCode_str(i), len)) {
readline_add_completion(rs, QKeyCode_str(i));
}
}
}
void set_link_completion(ReadLineState *rs, int nb_args, const char *str)
{
size_t len;

View File

@ -17,13 +17,11 @@
#include "qemu/cutils.h"
#include "qemu/option.h"
#include "monitor/monitor.h"
#include "monitor/qmp-helpers.h"
#include "sysemu/sysemu.h"
#include "qemu/config-file.h"
#include "qemu/uuid.h"
#include "chardev/char.h"
#include "ui/qemu-spice.h"
#include "ui/console.h"
#include "ui/dbus-display.h"
#include "sysemu/kvm.h"
#include "sysemu/runstate.h"
#include "sysemu/runstate-action.h"
@ -36,9 +34,7 @@
#include "qapi/qapi-commands-machine.h"
#include "qapi/qapi-commands-misc.h"
#include "qapi/qapi-commands-stats.h"
#include "qapi/qapi-commands-ui.h"
#include "qapi/type-helpers.h"
#include "qapi/qmp/qerror.h"
#include "exec/ramlist.h"
#include "hw/mem/memory-device.h"
#include "hw/acpi/acpi_dev_interface.h"
@ -168,130 +164,54 @@ void qmp_system_wakeup(Error **errp)
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp);
}
void qmp_set_password(SetPasswordOptions *opts, Error **errp)
{
int rc;
if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
if (!qemu_using_spice(errp)) {
return;
}
rc = qemu_spice.set_passwd(opts->password,
opts->connected == SET_PASSWORD_ACTION_FAIL,
opts->connected == SET_PASSWORD_ACTION_DISCONNECT);
} else {
assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
if (opts->connected != SET_PASSWORD_ACTION_KEEP) {
/* vnc supports "connected=keep" only */
error_setg(errp, QERR_INVALID_PARAMETER, "connected");
return;
}
/* Note that setting an empty password will not disable login through
* this interface. */
rc = vnc_display_password(opts->u.vnc.display, opts->password);
}
if (rc != 0) {
error_setg(errp, "Could not set password");
}
}
void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp)
{
time_t when;
int rc;
const char *whenstr = opts->time;
if (strcmp(whenstr, "now") == 0) {
when = 0;
} else if (strcmp(whenstr, "never") == 0) {
when = TIME_MAX;
} else if (whenstr[0] == '+') {
when = time(NULL) + strtoull(whenstr+1, NULL, 10);
} else {
when = strtoull(whenstr, NULL, 10);
}
if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
if (!qemu_using_spice(errp)) {
return;
}
rc = qemu_spice.set_pw_expire(when);
} else {
assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
rc = vnc_display_pw_expire(opts->u.vnc.display, when);
}
if (rc != 0) {
error_setg(errp, "Could not set password expire time");
}
}
#ifdef CONFIG_VNC
void qmp_change_vnc_password(const char *password, Error **errp)
{
if (vnc_display_password(NULL, password) < 0) {
error_setg(errp, "Could not set password");
}
}
#endif
void qmp_add_client(const char *protocol, const char *fdname,
bool has_skipauth, bool skipauth, bool has_tls, bool tls,
Error **errp)
{
static const struct {
const char *name;
bool (*add_client)(int fd, bool has_skipauth, bool skipauth,
bool has_tls, bool tls, Error **errp);
} protocol_table[] = {
{ "spice", qmp_add_client_spice },
#ifdef CONFIG_VNC
{ "vnc", qmp_add_client_vnc },
#endif
#ifdef CONFIG_DBUS_DISPLAY
{ "@dbus-display", qmp_add_client_dbus_display },
#endif
};
Chardev *s;
int fd;
int fd, i;
fd = monitor_get_fd(monitor_cur(), fdname, errp);
if (fd < 0) {
return;
}
if (strcmp(protocol, "spice") == 0) {
if (!qemu_using_spice(errp)) {
close(fd);
for (i = 0; i < ARRAY_SIZE(protocol_table); i++) {
if (!strcmp(protocol, protocol_table[i].name)) {
if (!protocol_table[i].add_client(fd, has_skipauth, skipauth,
has_tls, tls, errp)) {
close(fd);
}
return;
}
skipauth = has_skipauth ? skipauth : false;
tls = has_tls ? tls : false;
if (qemu_spice.display_add_client(fd, skipauth, tls) < 0) {
error_setg(errp, "spice failed to add client");
close(fd);
}
return;
#ifdef CONFIG_VNC
} else if (strcmp(protocol, "vnc") == 0) {
skipauth = has_skipauth ? skipauth : false;
vnc_display_add_client(NULL, fd, skipauth);
return;
#endif
#ifdef CONFIG_DBUS_DISPLAY
} else if (strcmp(protocol, "@dbus-display") == 0) {
if (!qemu_using_dbus_display(errp)) {
close(fd);
return;
}
if (!qemu_dbus_display.add_client(fd, errp)) {
close(fd);
return;
}
return;
#endif
} else if ((s = qemu_chr_find(protocol)) != NULL) {
if (qemu_chr_add_client(s, fd) < 0) {
error_setg(errp, "failed to add client");
close(fd);
return;
}
return;
}
error_setg(errp, "protocol '%s' is invalid", protocol);
close(fd);
s = qemu_chr_find(protocol);
if (!s) {
error_setg(errp, "protocol '%s' is invalid", protocol);
close(fd);
return;
}
if (qemu_chr_add_client(s, fd) < 0) {
error_setg(errp, "failed to add client");
close(fd);
return;
}
}
MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp)
{
return qmp_memory_device_list();
@ -330,38 +250,6 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp)
return mem_info;
}
void qmp_display_reload(DisplayReloadOptions *arg, Error **errp)
{
switch (arg->type) {
case DISPLAY_RELOAD_TYPE_VNC:
#ifdef CONFIG_VNC
if (arg->u.vnc.has_tls_certs && arg->u.vnc.tls_certs) {
vnc_display_reload_certs(NULL, errp);
}
#else
error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'");
#endif
break;
default:
abort();
}
}
void qmp_display_update(DisplayUpdateOptions *arg, Error **errp)
{
switch (arg->type) {
case DISPLAY_UPDATE_TYPE_VNC:
#ifdef CONFIG_VNC
vnc_display_update(&arg->u.vnc, errp);
#else
error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'");
#endif
break;
default:
abort();
}
}
static int qmp_x_query_rdma_foreach(Object *obj, void *opaque)
{
RdmaProvider *rdma;

View File

@ -2,8 +2,6 @@
#include "sysemu/sysemu.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-ui.h"
#include "qapi/qmp/qdict.h"
#include "qemu/error-report.h"
#include "trace.h"
#include "ui/input.h"
#include "ui/console.h"
@ -594,29 +592,29 @@ MouseInfoList *qmp_query_mice(Error **errp)
return mice_list;
}
void hmp_mouse_set(Monitor *mon, const QDict *qdict)
bool qemu_mouse_set(int index, Error **errp)
{
QemuInputHandlerState *s;
int index = qdict_get_int(qdict, "index");
int found = 0;
QTAILQ_FOREACH(s, &handlers, node) {
if (s->id != index) {
continue;
if (s->id == index) {
break;
}
if (!(s->handler->mask & (INPUT_EVENT_MASK_REL |
INPUT_EVENT_MASK_ABS))) {
error_report("Input device '%s' is not a mouse", s->handler->name);
return;
}
found = 1;
qemu_input_handler_activate(s);
break;
}
if (!found) {
error_report("Mouse at index '%d' not found", index);
if (!s) {
error_setg(errp, "Mouse at index '%d' not found", index);
return false;
}
if (!(s->handler->mask & (INPUT_EVENT_MASK_REL |
INPUT_EVENT_MASK_ABS))) {
error_setg(errp, "Input device '%s' is not a mouse",
s->handler->name);
return false;
}
qemu_input_handler_activate(s);
qemu_input_check_mode_change();
return true;
}

View File

@ -14,6 +14,8 @@ softmmu_ss.add(files(
'kbd-state.c',
'keymaps.c',
'qemu-pixman.c',
'ui-hmp-cmds.c',
'ui-qmp-cmds.c',
'util.c',
))
if dbus_display

View File

@ -517,13 +517,6 @@ static void interface_set_compression_level(QXLInstance *sin, int level)
/* nothing to do */
}
#if SPICE_NEEDS_SET_MM_TIME
static void interface_set_mm_time(QXLInstance *sin, uint32_t mm_time)
{
/* nothing to do */
}
#endif
static void interface_get_init_info(QXLInstance *sin, QXLDevInitInfo *info)
{
SimpleSpiceDisplay *ssd = container_of(sin, SimpleSpiceDisplay, qxl);
@ -715,9 +708,6 @@ static const QXLInterface dpy_interface = {
.attache_worker = interface_attach_worker,
#endif
.set_compression_level = interface_set_compression_level,
#if SPICE_NEEDS_SET_MM_TIME
.set_mm_time = interface_set_mm_time,
#endif
.get_init_info = interface_get_init_info,
/* the callbacks below are called from spice server thread context */

460
ui/ui-hmp-cmds.c Normal file
View File

@ -0,0 +1,460 @@
/*
* HMP commands related to UI
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu/osdep.h"
#ifdef CONFIG_SPICE
#include <spice/enums.h>
#endif
#include "monitor/hmp.h"
#include "monitor/monitor-internal.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-ui.h"
#include "qapi/qmp/qdict.h"
#include "qemu/cutils.h"
#include "ui/console.h"
#include "ui/input.h"
static int mouse_button_state;
void hmp_mouse_move(Monitor *mon, const QDict *qdict)
{
int dx, dy, dz, button;
const char *dx_str = qdict_get_str(qdict, "dx_str");
const char *dy_str = qdict_get_str(qdict, "dy_str");
const char *dz_str = qdict_get_try_str(qdict, "dz_str");
dx = strtol(dx_str, NULL, 0);
dy = strtol(dy_str, NULL, 0);
qemu_input_queue_rel(NULL, INPUT_AXIS_X, dx);
qemu_input_queue_rel(NULL, INPUT_AXIS_Y, dy);
if (dz_str) {
dz = strtol(dz_str, NULL, 0);
if (dz != 0) {
button = (dz > 0) ? INPUT_BUTTON_WHEEL_UP : INPUT_BUTTON_WHEEL_DOWN;
qemu_input_queue_btn(NULL, button, true);
qemu_input_event_sync();
qemu_input_queue_btn(NULL, button, false);
}
}
qemu_input_event_sync();
}
void hmp_mouse_button(Monitor *mon, const QDict *qdict)
{
static uint32_t bmap[INPUT_BUTTON__MAX] = {
[INPUT_BUTTON_LEFT] = MOUSE_EVENT_LBUTTON,
[INPUT_BUTTON_MIDDLE] = MOUSE_EVENT_MBUTTON,
[INPUT_BUTTON_RIGHT] = MOUSE_EVENT_RBUTTON,
};
int button_state = qdict_get_int(qdict, "button_state");
if (mouse_button_state == button_state) {
return;
}
qemu_input_update_buttons(NULL, bmap, mouse_button_state, button_state);
qemu_input_event_sync();
mouse_button_state = button_state;
}
void hmp_mouse_set(Monitor *mon, const QDict *qdict)
{
Error *err = NULL;
qemu_mouse_set(qdict_get_int(qdict, "index"), &err);
hmp_handle_error(mon, err);
}
void hmp_info_mice(Monitor *mon, const QDict *qdict)
{
MouseInfoList *mice_list, *mouse;
mice_list = qmp_query_mice(NULL);
if (!mice_list) {
monitor_printf(mon, "No mouse devices connected\n");
return;
}
for (mouse = mice_list; mouse; mouse = mouse->next) {
monitor_printf(mon, "%c Mouse #%" PRId64 ": %s%s\n",
mouse->value->current ? '*' : ' ',
mouse->value->index, mouse->value->name,
mouse->value->absolute ? " (absolute)" : "");
}
qapi_free_MouseInfoList(mice_list);
}
#ifdef CONFIG_VNC
/* Helper for hmp_info_vnc_clients, _servers */
static void hmp_info_VncBasicInfo(Monitor *mon, VncBasicInfo *info,
const char *name)
{
monitor_printf(mon, " %s: %s:%s (%s%s)\n",
name,
info->host,
info->service,
NetworkAddressFamily_str(info->family),
info->websocket ? " (Websocket)" : "");
}
/* Helper displaying and auth and crypt info */
static void hmp_info_vnc_authcrypt(Monitor *mon, const char *indent,
VncPrimaryAuth auth,
VncVencryptSubAuth *vencrypt)
{
monitor_printf(mon, "%sAuth: %s (Sub: %s)\n", indent,
VncPrimaryAuth_str(auth),
vencrypt ? VncVencryptSubAuth_str(*vencrypt) : "none");
}
static void hmp_info_vnc_clients(Monitor *mon, VncClientInfoList *client)
{
while (client) {
VncClientInfo *cinfo = client->value;
hmp_info_VncBasicInfo(mon, qapi_VncClientInfo_base(cinfo), "Client");
monitor_printf(mon, " x509_dname: %s\n",
cinfo->x509_dname ?: "none");
monitor_printf(mon, " sasl_username: %s\n",
cinfo->sasl_username ?: "none");
client = client->next;
}
}
static void hmp_info_vnc_servers(Monitor *mon, VncServerInfo2List *server)
{
while (server) {
VncServerInfo2 *sinfo = server->value;
hmp_info_VncBasicInfo(mon, qapi_VncServerInfo2_base(sinfo), "Server");
hmp_info_vnc_authcrypt(mon, " ", sinfo->auth,
sinfo->has_vencrypt ? &sinfo->vencrypt : NULL);
server = server->next;
}
}
void hmp_info_vnc(Monitor *mon, const QDict *qdict)
{
VncInfo2List *info2l, *info2l_head;
Error *err = NULL;
info2l = qmp_query_vnc_servers(&err);
info2l_head = info2l;
if (hmp_handle_error(mon, err)) {
return;
}
if (!info2l) {
monitor_printf(mon, "None\n");
return;
}
while (info2l) {
VncInfo2 *info = info2l->value;
monitor_printf(mon, "%s:\n", info->id);
hmp_info_vnc_servers(mon, info->server);
hmp_info_vnc_clients(mon, info->clients);
if (!info->server) {
/*
* The server entry displays its auth, we only need to
* display in the case of 'reverse' connections where
* there's no server.
*/
hmp_info_vnc_authcrypt(mon, " ", info->auth,
info->has_vencrypt ? &info->vencrypt : NULL);
}
if (info->display) {
monitor_printf(mon, " Display: %s\n", info->display);
}
info2l = info2l->next;
}
qapi_free_VncInfo2List(info2l_head);
}
#endif
#ifdef CONFIG_SPICE
void hmp_info_spice(Monitor *mon, const QDict *qdict)
{
SpiceChannelList *chan;
SpiceInfo *info;
const char *channel_name;
static const char *const channel_names[] = {
[SPICE_CHANNEL_MAIN] = "main",
[SPICE_CHANNEL_DISPLAY] = "display",
[SPICE_CHANNEL_INPUTS] = "inputs",
[SPICE_CHANNEL_CURSOR] = "cursor",
[SPICE_CHANNEL_PLAYBACK] = "playback",
[SPICE_CHANNEL_RECORD] = "record",
[SPICE_CHANNEL_TUNNEL] = "tunnel",
[SPICE_CHANNEL_SMARTCARD] = "smartcard",
[SPICE_CHANNEL_USBREDIR] = "usbredir",
[SPICE_CHANNEL_PORT] = "port",
[SPICE_CHANNEL_WEBDAV] = "webdav",
};
info = qmp_query_spice(NULL);
if (!info->enabled) {
monitor_printf(mon, "Server: disabled\n");
goto out;
}
monitor_printf(mon, "Server:\n");
if (info->has_port) {
monitor_printf(mon, " address: %s:%" PRId64 "\n",
info->host, info->port);
}
if (info->has_tls_port) {
monitor_printf(mon, " address: %s:%" PRId64 " [tls]\n",
info->host, info->tls_port);
}
monitor_printf(mon, " migrated: %s\n",
info->migrated ? "true" : "false");
monitor_printf(mon, " auth: %s\n", info->auth);
monitor_printf(mon, " compiled: %s\n", info->compiled_version);
monitor_printf(mon, " mouse-mode: %s\n",
SpiceQueryMouseMode_str(info->mouse_mode));
if (!info->has_channels || info->channels == NULL) {
monitor_printf(mon, "Channels: none\n");
} else {
for (chan = info->channels; chan; chan = chan->next) {
monitor_printf(mon, "Channel:\n");
monitor_printf(mon, " address: %s:%s%s\n",
chan->value->host, chan->value->port,
chan->value->tls ? " [tls]" : "");
monitor_printf(mon, " session: %" PRId64 "\n",
chan->value->connection_id);
monitor_printf(mon, " channel: %" PRId64 ":%" PRId64 "\n",
chan->value->channel_type, chan->value->channel_id);
channel_name = "unknown";
if (chan->value->channel_type > 0 &&
chan->value->channel_type < ARRAY_SIZE(channel_names) &&
channel_names[chan->value->channel_type]) {
channel_name = channel_names[chan->value->channel_type];
}
monitor_printf(mon, " channel name: %s\n", channel_name);
}
}
out:
qapi_free_SpiceInfo(info);
}
#endif
void hmp_set_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *password = qdict_get_str(qdict, "password");
const char *display = qdict_get_try_str(qdict, "display");
const char *connected = qdict_get_try_str(qdict, "connected");
Error *err = NULL;
SetPasswordOptions opts = {
.password = (char *)password,
.has_connected = !!connected,
};
opts.connected = qapi_enum_parse(&SetPasswordAction_lookup, connected,
SET_PASSWORD_ACTION_KEEP, &err);
if (err) {
goto out;
}
opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
DISPLAY_PROTOCOL_VNC, &err);
if (err) {
goto out;
}
if (opts.protocol == DISPLAY_PROTOCOL_VNC) {
opts.u.vnc.display = (char *)display;
}
qmp_set_password(&opts, &err);
out:
hmp_handle_error(mon, err);
}
void hmp_expire_password(Monitor *mon, const QDict *qdict)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *whenstr = qdict_get_str(qdict, "time");
const char *display = qdict_get_try_str(qdict, "display");
Error *err = NULL;
ExpirePasswordOptions opts = {
.time = (char *)whenstr,
};
opts.protocol = qapi_enum_parse(&DisplayProtocol_lookup, protocol,
DISPLAY_PROTOCOL_VNC, &err);
if (err) {
goto out;
}
if (opts.protocol == DISPLAY_PROTOCOL_VNC) {
opts.u.vnc.display = (char *)display;
}
qmp_expire_password(&opts, &err);
out:
hmp_handle_error(mon, err);
}
#ifdef CONFIG_VNC
static void hmp_change_read_arg(void *opaque, const char *password,
void *readline_opaque)
{
qmp_change_vnc_password(password, NULL);
monitor_read_command(opaque, 1);
}
void hmp_change_vnc(Monitor *mon, const char *device, const char *target,
const char *arg, const char *read_only, bool force,
Error **errp)
{
if (read_only) {
error_setg(errp, "Parameter 'read-only-mode' is invalid for VNC");
return;
}
if (strcmp(target, "passwd") && strcmp(target, "password")) {
error_setg(errp, "Expected 'password' after 'vnc'");
return;
}
if (!arg) {
MonitorHMP *hmp_mon = container_of(mon, MonitorHMP, common);
monitor_read_password(hmp_mon, hmp_change_read_arg, NULL);
} else {
qmp_change_vnc_password(arg, errp);
}
}
#endif
void hmp_sendkey(Monitor *mon, const QDict *qdict)
{
const char *keys = qdict_get_str(qdict, "keys");
KeyValue *v = NULL;
KeyValueList *head = NULL, **tail = &head;
int has_hold_time = qdict_haskey(qdict, "hold-time");
int hold_time = qdict_get_try_int(qdict, "hold-time", -1);
Error *err = NULL;
const char *separator;
int keyname_len;
while (1) {
separator = qemu_strchrnul(keys, '-');
keyname_len = separator - keys;
/* Be compatible with old interface, convert user inputted "<" */
if (keys[0] == '<' && keyname_len == 1) {
keys = "less";
keyname_len = 4;
}
v = g_malloc0(sizeof(*v));
if (strstart(keys, "0x", NULL)) {
const char *endp;
int value;
if (qemu_strtoi(keys, &endp, 0, &value) < 0) {
goto err_out;
}
assert(endp <= keys + keyname_len);
if (endp != keys + keyname_len) {
goto err_out;
}
v->type = KEY_VALUE_KIND_NUMBER;
v->u.number.data = value;
} else {
int idx = index_from_key(keys, keyname_len);
if (idx == Q_KEY_CODE__MAX) {
goto err_out;
}
v->type = KEY_VALUE_KIND_QCODE;
v->u.qcode.data = idx;
}
QAPI_LIST_APPEND(tail, v);
v = NULL;
if (!*separator) {
break;
}
keys = separator + 1;
}
qmp_send_key(head, has_hold_time, hold_time, &err);
hmp_handle_error(mon, err);
out:
qapi_free_KeyValue(v);
qapi_free_KeyValueList(head);
return;
err_out:
monitor_printf(mon, "invalid parameter: %.*s\n", keyname_len, keys);
goto out;
}
void sendkey_completion(ReadLineState *rs, int nb_args, const char *str)
{
int i;
char *sep;
size_t len;
if (nb_args != 2) {
return;
}
sep = strrchr(str, '-');
if (sep) {
str = sep + 1;
}
len = strlen(str);
readline_set_completion_index(rs, len);
for (i = 0; i < Q_KEY_CODE__MAX; i++) {
if (!strncmp(str, QKeyCode_str(i), len)) {
readline_add_completion(rs, QKeyCode_str(i));
}
}
}
void coroutine_fn
hmp_screendump(Monitor *mon, const QDict *qdict)
{
const char *filename = qdict_get_str(qdict, "filename");
const char *id = qdict_get_try_str(qdict, "device");
int64_t head = qdict_get_try_int(qdict, "head", 0);
const char *input_format = qdict_get_try_str(qdict, "format");
Error *err = NULL;
ImageFormat format;
format = qapi_enum_parse(&ImageFormat_lookup, input_format,
IMAGE_FORMAT_PPM, &err);
if (err) {
goto end;
}
qmp_screendump(filename, id, id != NULL, head,
input_format != NULL, format, &err);
end:
hmp_handle_error(mon, err);
}

177
ui/ui-qmp-cmds.c Normal file
View File

@ -0,0 +1,177 @@
/*
* QMP commands related to UI
*
* Copyright IBM, Corp. 2011
*
* Authors:
* Anthony Liguori <aliguori@us.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "monitor/qmp-helpers.h"
#include "qapi/qapi-commands-ui.h"
#include "qapi/qmp/qerror.h"
#include "qemu/cutils.h"
#include "ui/console.h"
#include "ui/dbus-display.h"
#include "ui/qemu-spice.h"
void qmp_set_password(SetPasswordOptions *opts, Error **errp)
{
int rc;
if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
if (!qemu_using_spice(errp)) {
return;
}
rc = qemu_spice.set_passwd(opts->password,
opts->connected == SET_PASSWORD_ACTION_FAIL,
opts->connected == SET_PASSWORD_ACTION_DISCONNECT);
} else {
assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
if (opts->connected != SET_PASSWORD_ACTION_KEEP) {
/* vnc supports "connected=keep" only */
error_setg(errp, QERR_INVALID_PARAMETER, "connected");
return;
}
/*
* Note that setting an empty password will not disable login
* through this interface.
*/
rc = vnc_display_password(opts->u.vnc.display, opts->password);
}
if (rc != 0) {
error_setg(errp, "Could not set password");
}
}
void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp)
{
time_t when;
int rc;
const char *whenstr = opts->time;
const char *numstr = NULL;
uint64_t num;
if (strcmp(whenstr, "now") == 0) {
when = 0;
} else if (strcmp(whenstr, "never") == 0) {
when = TIME_MAX;
} else if (whenstr[0] == '+') {
when = time(NULL);
numstr = whenstr + 1;
} else {
when = 0;
numstr = whenstr;
}
if (numstr) {
if (qemu_strtou64(numstr, NULL, 10, &num) < 0) {
error_setg(errp, "Parameter 'time' doesn't take value '%s'",
whenstr);
return;
}
when += num;
}
if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
if (!qemu_using_spice(errp)) {
return;
}
rc = qemu_spice.set_pw_expire(when);
} else {
assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
rc = vnc_display_pw_expire(opts->u.vnc.display, when);
}
if (rc != 0) {
error_setg(errp, "Could not set password expire time");
}
}
#ifdef CONFIG_VNC
void qmp_change_vnc_password(const char *password, Error **errp)
{
if (vnc_display_password(NULL, password) < 0) {
error_setg(errp, "Could not set password");
}
}
#endif
bool qmp_add_client_spice(int fd, bool has_skipauth, bool skipauth,
bool has_tls, bool tls, Error **errp)
{
if (!qemu_using_spice(errp)) {
return false;
}
skipauth = has_skipauth ? skipauth : false;
tls = has_tls ? tls : false;
if (qemu_spice.display_add_client(fd, skipauth, tls) < 0) {
error_setg(errp, "spice failed to add client");
return false;
}
return true;
}
#ifdef CONFIG_VNC
bool qmp_add_client_vnc(int fd, bool has_skipauth, bool skipauth,
bool has_tls, bool tls, Error **errp)
{
skipauth = has_skipauth ? skipauth : false;
vnc_display_add_client(NULL, fd, skipauth);
return true;
}
#endif
#ifdef CONFIG_DBUS_DISPLAY
bool qmp_add_client_dbus_display(int fd, bool has_skipauth, bool skipauth,
bool has_tls, bool tls, Error **errp)
{
if (!qemu_using_dbus_display(errp)) {
return false;
}
if (!qemu_dbus_display.add_client(fd, errp)) {
return false;
}
return true;
}
#endif
void qmp_display_reload(DisplayReloadOptions *arg, Error **errp)
{
switch (arg->type) {
case DISPLAY_RELOAD_TYPE_VNC:
#ifdef CONFIG_VNC
if (arg->u.vnc.has_tls_certs && arg->u.vnc.tls_certs) {
vnc_display_reload_certs(NULL, errp);
}
#else
error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'");
#endif
break;
default:
abort();
}
}
void qmp_display_update(DisplayUpdateOptions *arg, Error **errp)
{
switch (arg->type) {
case DISPLAY_UPDATE_TYPE_VNC:
#ifdef CONFIG_VNC
vnc_display_update(&arg->u.vnc, errp);
#else
error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'");
#endif
break;
default:
abort();
}
}

View File

@ -87,9 +87,7 @@ static const char *cap_name[] = {
[VD_AGENT_CAP_MONITORS_CONFIG_POSITION] = "monitors-config-position",
[VD_AGENT_CAP_FILE_XFER_DISABLED] = "file-xfer-disabled",
[VD_AGENT_CAP_FILE_XFER_DETAILED_ERRORS] = "file-xfer-detailed-errors",
#if CHECK_SPICE_PROTOCOL_VERSION(0, 14, 0)
[VD_AGENT_CAP_GRAPHICS_DEVICE_INFO] = "graphics-device-info",
#endif
#if CHECK_SPICE_PROTOCOL_VERSION(0, 14, 1)
[VD_AGENT_CAP_CLIPBOARD_NO_RELEASE_ON_REGRAB] = "clipboard-no-release-on-regrab",
[VD_AGENT_CAP_CLIPBOARD_GRAB_SERIAL] = "clipboard-grab-serial",
@ -112,9 +110,7 @@ static const char *msg_name[] = {
[VD_AGENT_CLIENT_DISCONNECTED] = "client-disconnected",
[VD_AGENT_MAX_CLIPBOARD] = "max-clipboard",
[VD_AGENT_AUDIO_VOLUME_SYNC] = "audio-volume-sync",
#if CHECK_SPICE_PROTOCOL_VERSION(0, 14, 0)
[VD_AGENT_GRAPHICS_DEVICE_INFO] = "graphics-device-info",
#endif
};
static const char *sel_name[] = {