Merge branch 'spice.v23.pull' of git://anongit.freedesktop.org/spice/qemu

* 'spice.v23.pull' of git://anongit.freedesktop.org/spice/qemu:
  vnc/spice: add set_passwd monitor command.
  vnc: support password expire
  vnc: auth reject cleanup
  spice: add qmp 'query-spice' and hmp 'info spice' commands.
  spice: connection events.
  spice: add qxl device
  spice: add qxl vgabios binary.
This commit is contained in:
Aurelien Jarno 2010-12-27 22:59:48 +01:00
commit 818c2e1b97
24 changed files with 2887 additions and 22 deletions

View File

@ -205,7 +205,7 @@ common de-ch es fo fr-ca hu ja mk nl-be pt sl tr
ifdef INSTALL_BLOBS
BLOBS=bios.bin vgabios.bin vgabios-cirrus.bin \
vgabios-stdvga.bin vgabios-vmware.bin \
vgabios-stdvga.bin vgabios-vmware.bin vgabios-qxl.bin \
ppc_rom.bin openbios-sparc32 openbios-sparc64 openbios-ppc \
gpxe-eepro100-80861209.rom \
pxe-e1000.bin \

View File

@ -217,6 +217,7 @@ obj-i386-y += vmmouse.o vmport.o hpet.o applesmc.o
obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
obj-i386-y += debugcon.o multiboot.o
obj-i386-y += pc_piix.o
obj-i386-$(CONFIG_SPICE) += qxl.o qxl-logger.o qxl-render.o
# shared objects
obj-ppc-y = ppc.o

View File

@ -182,6 +182,70 @@ Example:
"host": "127.0.0.1", "sasl_username": "luiz" } },
"timestamp": { "seconds": 1263475302, "microseconds": 150772 } }
SPICE_CONNECTED, SPICE_DISCONNECTED
-----------------------------------
Emitted when a SPICE client connects or disconnects.
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "client": Client information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
Example:
{ "timestamp": {"seconds": 1290688046, "microseconds": 388707},
"event": "SPICE_CONNECTED",
"data": {
"server": { "port": "5920", "family": "ipv4", "host": "127.0.0.1"},
"client": {"port": "52873", "family": "ipv4", "host": "127.0.0.1"}
}}
SPICE_INITIALIZED
-----------------
Emitted after initial handshake and authentication takes place (if any)
and the SPICE channel is up'n'running
Data:
- "server": Server information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "auth": authentication method (json-string, optional)
- "client": Client information (json-object)
- "host": IP address (json-string)
- "port": port number (json-string)
- "family": address family (json-string, "ipv4" or "ipv6")
- "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:
{ "timestamp": {"seconds": 1290688046, "microseconds": 417172},
"event": "SPICE_INITIALIZED",
"data": {"server": {"auth": "spice", "port": "5921",
"family": "ipv4", "host": "127.0.0.1"},
"client": {"port": "49004", "family": "ipv4", "channel-type": 3,
"connection-id": 1804289383, "host": "127.0.0.1",
"channel-id": 0, "tls": true}
}}
WATCHDOG
--------

View File

@ -369,6 +369,7 @@ void vnc_display_init(DisplayState *ds);
void vnc_display_close(DisplayState *ds);
int vnc_display_open(DisplayState *ds, const char *display);
int vnc_display_password(DisplayState *ds, const char *password);
int vnc_display_pw_expire(DisplayState *ds, time_t expires);
void do_info_vnc_print(Monitor *mon, const QObject *data);
void do_info_vnc(Monitor *mon, QObject **ret_data);
char *vnc_display_local_addr(DisplayState *ds);

View File

@ -1151,6 +1151,60 @@ STEXI
@item block_passwd @var{device} @var{password}
@findex block_passwd
Set the encrypted device @var{device} password to @var{password}
ETEXI
{
.name = "set_password",
.args_type = "protocol:s,password:s,connected:s?",
.params = "protocol password action-if-connected",
.help = "set spice/vnc password",
.user_print = monitor_user_noop,
.mhandler.cmd_new = set_password,
},
STEXI
@item set_password [ vnc | spice ] password [ action-if-connected ]
@findex set_password
Change spice/vnc password. Use zero to make the password stay valid
forever. @var{action-if-connected} specifies what should happen in
case a connection is established: @var{fail} makes the password change
fail. @var{disconnect} changes the password and disconnects the
client. @var{keep} changes the password and keeps the connection up.
@var{keep} is the default.
ETEXI
{
.name = "expire_password",
.args_type = "protocol:s,time:s",
.params = "protocol time",
.help = "set spice/vnc password expire-time",
.user_print = monitor_user_noop,
.mhandler.cmd_new = expire_password,
},
STEXI
@item expire_password [ vnc | spice ] expire-time
@findex expire_password
Specify when a password for spice/vnc becomes
invalid. @var{expire-time} accepts:
@table @var
@item now
Invalidate password instantly.
@item never
Password stays valid forever.
@item +nsec
Password stays valid for @var{nsec} seconds starting now.
@item nsec
Password is invalidated at the given time. @var{nsec} are the seconds
passed since 1970, i.e. unix epoch.
@end table
ETEXI
{

14
hw/hw.h
View File

@ -528,6 +528,17 @@ extern const VMStateInfo vmstate_info_unused_buffer;
.start = (_start), \
}
#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _start, _field_size) { \
.name = (stringify(_field)), \
.version_id = (_version), \
.field_exists = (_test), \
.size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\
.info = &vmstate_info_buffer, \
.flags = VMS_VBUFFER|VMS_POINTER, \
.offset = offsetof(_state, _field), \
.start = (_start), \
}
#define VMSTATE_BUFFER_UNSAFE_INFO(_field, _state, _version, _info, _size) { \
.name = (stringify(_field)), \
.version_id = (_version), \
@ -745,6 +756,9 @@ extern const VMStateDescription vmstate_i2c_slave;
#define VMSTATE_PARTIAL_VBUFFER(_f, _s, _size) \
VMSTATE_VBUFFER(_f, _s, 0, NULL, 0, _size)
#define VMSTATE_PARTIAL_VBUFFER_UINT32(_f, _s, _size) \
VMSTATE_VBUFFER_UINT32(_f, _s, 0, NULL, 0, _size)
#define VMSTATE_SUB_VBUFFER(_f, _s, _start, _size) \
VMSTATE_VBUFFER(_f, _s, 0, NULL, _start, _size)

View File

@ -40,6 +40,7 @@
#include "sysbus.h"
#include "sysemu.h"
#include "blockdev.h"
#include "ui/qemu-spice.h"
/* output Bochs bios info messages */
//#define DEBUG_BIOS
@ -992,6 +993,13 @@ void pc_vga_init(PCIBus *pci_bus)
pci_vmsvga_init(pci_bus);
else
fprintf(stderr, "%s: vmware_vga: no PCI bus\n", __FUNCTION__);
#ifdef CONFIG_SPICE
} else if (qxl_enabled) {
if (pci_bus)
pci_create_simple(pci_bus, -1, "qxl-vga");
else
fprintf(stderr, "%s: qxl: no PCI bus\n", __FUNCTION__);
#endif
} else if (std_vga_enabled) {
if (pci_bus) {
pci_vga_init(pci_bus);

248
hw/qxl-logger.c Normal file
View File

@ -0,0 +1,248 @@
/*
* qxl command logging -- for debug purposes
*
* Copyright (C) 2010 Red Hat, Inc.
*
* maintained by Gerd Hoffmann <kraxel@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qxl.h"
static const char *qxl_type[] = {
[ QXL_CMD_NOP ] = "nop",
[ QXL_CMD_DRAW ] = "draw",
[ QXL_CMD_UPDATE ] = "update",
[ QXL_CMD_CURSOR ] = "cursor",
[ QXL_CMD_MESSAGE ] = "message",
[ QXL_CMD_SURFACE ] = "surface",
};
static const char *qxl_draw_type[] = {
[ QXL_DRAW_NOP ] = "nop",
[ QXL_DRAW_FILL ] = "fill",
[ QXL_DRAW_OPAQUE ] = "opaque",
[ QXL_DRAW_COPY ] = "copy",
[ QXL_COPY_BITS ] = "copy-bits",
[ QXL_DRAW_BLEND ] = "blend",
[ QXL_DRAW_BLACKNESS ] = "blackness",
[ QXL_DRAW_WHITENESS ] = "whitemess",
[ QXL_DRAW_INVERS ] = "invers",
[ QXL_DRAW_ROP3 ] = "rop3",
[ QXL_DRAW_STROKE ] = "stroke",
[ QXL_DRAW_TEXT ] = "text",
[ QXL_DRAW_TRANSPARENT ] = "transparent",
[ QXL_DRAW_ALPHA_BLEND ] = "alpha-blend",
};
static const char *qxl_draw_effect[] = {
[ QXL_EFFECT_BLEND ] = "blend",
[ QXL_EFFECT_OPAQUE ] = "opaque",
[ QXL_EFFECT_REVERT_ON_DUP ] = "revert-on-dup",
[ QXL_EFFECT_BLACKNESS_ON_DUP ] = "blackness-on-dup",
[ QXL_EFFECT_WHITENESS_ON_DUP ] = "whiteness-on-dup",
[ QXL_EFFECT_NOP_ON_DUP ] = "nop-on-dup",
[ QXL_EFFECT_NOP ] = "nop",
[ QXL_EFFECT_OPAQUE_BRUSH ] = "opaque-brush",
};
static const char *qxl_surface_cmd[] = {
[ QXL_SURFACE_CMD_CREATE ] = "create",
[ QXL_SURFACE_CMD_DESTROY ] = "destroy",
};
static const char *spice_surface_fmt[] = {
[ SPICE_SURFACE_FMT_INVALID ] = "invalid",
[ SPICE_SURFACE_FMT_1_A ] = "alpha/1",
[ SPICE_SURFACE_FMT_8_A ] = "alpha/8",
[ SPICE_SURFACE_FMT_16_555 ] = "555/16",
[ SPICE_SURFACE_FMT_16_565 ] = "565/16",
[ SPICE_SURFACE_FMT_32_xRGB ] = "xRGB/32",
[ SPICE_SURFACE_FMT_32_ARGB ] = "ARGB/32",
};
static const char *qxl_cursor_cmd[] = {
[ QXL_CURSOR_SET ] = "set",
[ QXL_CURSOR_MOVE ] = "move",
[ QXL_CURSOR_HIDE ] = "hide",
[ QXL_CURSOR_TRAIL ] = "trail",
};
static const char *spice_cursor_type[] = {
[ SPICE_CURSOR_TYPE_ALPHA ] = "alpha",
[ SPICE_CURSOR_TYPE_MONO ] = "mono",
[ SPICE_CURSOR_TYPE_COLOR4 ] = "color4",
[ SPICE_CURSOR_TYPE_COLOR8 ] = "color8",
[ SPICE_CURSOR_TYPE_COLOR16 ] = "color16",
[ SPICE_CURSOR_TYPE_COLOR24 ] = "color24",
[ SPICE_CURSOR_TYPE_COLOR32 ] = "color32",
};
static const char *qxl_v2n(const char *n[], size_t l, int v)
{
if (v >= l || !n[v]) {
return "???";
}
return n[v];
}
#define qxl_name(_list, _value) qxl_v2n(_list, ARRAY_SIZE(_list), _value)
static void qxl_log_image(PCIQXLDevice *qxl, QXLPHYSICAL addr, int group_id)
{
QXLImage *image;
QXLImageDescriptor *desc;
image = qxl_phys2virt(qxl, addr, group_id);
desc = &image->descriptor;
fprintf(stderr, " (id %" PRIx64 " type %d flags %d width %d height %d",
desc->id, desc->type, desc->flags, desc->width, desc->height);
switch (desc->type) {
case SPICE_IMAGE_TYPE_BITMAP:
fprintf(stderr, ", fmt %d flags %d x %d y %d stride %d"
" palette %" PRIx64 " data %" PRIx64,
image->bitmap.format, image->bitmap.flags,
image->bitmap.x, image->bitmap.y,
image->bitmap.stride,
image->bitmap.palette, image->bitmap.data);
break;
}
fprintf(stderr, ")");
}
static void qxl_log_rect(QXLRect *rect)
{
fprintf(stderr, " %dx%d+%d+%d",
rect->right - rect->left,
rect->bottom - rect->top,
rect->left, rect->top);
}
static void qxl_log_cmd_draw_copy(PCIQXLDevice *qxl, QXLCopy *copy, int group_id)
{
fprintf(stderr, " src %" PRIx64,
copy->src_bitmap);
qxl_log_image(qxl, copy->src_bitmap, group_id);
fprintf(stderr, " area");
qxl_log_rect(&copy->src_area);
fprintf(stderr, " rop %d", copy->rop_descriptor);
}
static void qxl_log_cmd_draw(PCIQXLDevice *qxl, QXLDrawable *draw, int group_id)
{
fprintf(stderr, ": surface_id %d type %s effect %s",
draw->surface_id,
qxl_name(qxl_draw_type, draw->type),
qxl_name(qxl_draw_effect, draw->effect));
switch (draw->type) {
case QXL_DRAW_COPY:
qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id);
break;
}
}
static void qxl_log_cmd_draw_compat(PCIQXLDevice *qxl, QXLCompatDrawable *draw,
int group_id)
{
fprintf(stderr, ": type %s effect %s",
qxl_name(qxl_draw_type, draw->type),
qxl_name(qxl_draw_effect, draw->effect));
if (draw->bitmap_offset) {
fprintf(stderr, ": bitmap %d",
draw->bitmap_offset);
qxl_log_rect(&draw->bitmap_area);
}
switch (draw->type) {
case QXL_DRAW_COPY:
qxl_log_cmd_draw_copy(qxl, &draw->u.copy, group_id);
break;
}
}
static void qxl_log_cmd_surface(PCIQXLDevice *qxl, QXLSurfaceCmd *cmd)
{
fprintf(stderr, ": %s id %d",
qxl_name(qxl_surface_cmd, cmd->type),
cmd->surface_id);
if (cmd->type == QXL_SURFACE_CMD_CREATE) {
fprintf(stderr, " size %dx%d stride %d format %s (count %d, max %d)",
cmd->u.surface_create.width,
cmd->u.surface_create.height,
cmd->u.surface_create.stride,
qxl_name(spice_surface_fmt, cmd->u.surface_create.format),
qxl->guest_surfaces.count, qxl->guest_surfaces.max);
}
if (cmd->type == QXL_SURFACE_CMD_DESTROY) {
fprintf(stderr, " (count %d)", qxl->guest_surfaces.count);
}
}
void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id)
{
QXLCursor *cursor;
fprintf(stderr, ": %s",
qxl_name(qxl_cursor_cmd, cmd->type));
switch (cmd->type) {
case QXL_CURSOR_SET:
fprintf(stderr, " +%d+%d visible %s, shape @ 0x%" PRIx64,
cmd->u.set.position.x,
cmd->u.set.position.y,
cmd->u.set.visible ? "yes" : "no",
cmd->u.set.shape);
cursor = qxl_phys2virt(qxl, cmd->u.set.shape, group_id);
fprintf(stderr, " type %s size %dx%d hot-spot +%d+%d"
" unique 0x%" PRIx64 " data-size %d",
qxl_name(spice_cursor_type, cursor->header.type),
cursor->header.width, cursor->header.height,
cursor->header.hot_spot_x, cursor->header.hot_spot_y,
cursor->header.unique, cursor->data_size);
break;
case QXL_CURSOR_MOVE:
fprintf(stderr, " +%d+%d", cmd->u.position.x, cmd->u.position.y);
break;
}
}
void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext)
{
bool compat = ext->flags & QXL_COMMAND_FLAG_COMPAT;
void *data;
if (!qxl->cmdlog) {
return;
}
fprintf(stderr, "qxl-%d/%s:", qxl->id, ring);
fprintf(stderr, " cmd @ 0x%" PRIx64 " %s%s", ext->cmd.data,
qxl_name(qxl_type, ext->cmd.type),
compat ? "(compat)" : "");
data = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
switch (ext->cmd.type) {
case QXL_CMD_DRAW:
if (!compat) {
qxl_log_cmd_draw(qxl, data, ext->group_id);
} else {
qxl_log_cmd_draw_compat(qxl, data, ext->group_id);
}
break;
case QXL_CMD_SURFACE:
qxl_log_cmd_surface(qxl, data);
break;
case QXL_CMD_CURSOR:
qxl_log_cmd_cursor(qxl, data, ext->group_id);
break;
}
fprintf(stderr, "\n");
}

226
hw/qxl-render.c Normal file
View File

@ -0,0 +1,226 @@
/*
* qxl local rendering (aka display on sdl/vnc)
*
* Copyright (C) 2010 Red Hat, Inc.
*
* maintained by Gerd Hoffmann <kraxel@redhat.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 or
* (at your option) version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qxl.h"
static void qxl_flip(PCIQXLDevice *qxl, QXLRect *rect)
{
uint8_t *src = qxl->guest_primary.data;
uint8_t *dst = qxl->guest_primary.flipped;
int len, i;
src += (qxl->guest_primary.surface.height - rect->top - 1) *
qxl->guest_primary.stride;
dst += rect->top * qxl->guest_primary.stride;
src += rect->left * qxl->guest_primary.bytes_pp;
dst += rect->left * qxl->guest_primary.bytes_pp;
len = (rect->right - rect->left) * qxl->guest_primary.bytes_pp;
for (i = rect->top; i < rect->bottom; i++) {
memcpy(dst, src, len);
dst += qxl->guest_primary.stride;
src -= qxl->guest_primary.stride;
}
}
void qxl_render_resize(PCIQXLDevice *qxl)
{
QXLSurfaceCreate *sc = &qxl->guest_primary.surface;
qxl->guest_primary.stride = sc->stride;
qxl->guest_primary.resized++;
switch (sc->format) {
case SPICE_SURFACE_FMT_16_555:
qxl->guest_primary.bytes_pp = 2;
qxl->guest_primary.bits_pp = 15;
break;
case SPICE_SURFACE_FMT_16_565:
qxl->guest_primary.bytes_pp = 2;
qxl->guest_primary.bits_pp = 16;
break;
case SPICE_SURFACE_FMT_32_xRGB:
case SPICE_SURFACE_FMT_32_ARGB:
qxl->guest_primary.bytes_pp = 4;
qxl->guest_primary.bits_pp = 32;
break;
default:
fprintf(stderr, "%s: unhandled format: %x\n", __FUNCTION__,
qxl->guest_primary.surface.format);
qxl->guest_primary.bytes_pp = 4;
qxl->guest_primary.bits_pp = 32;
break;
}
}
void qxl_render_update(PCIQXLDevice *qxl)
{
VGACommonState *vga = &qxl->vga;
QXLRect dirty[32], update;
void *ptr;
int i;
if (qxl->guest_primary.resized) {
qxl->guest_primary.resized = 0;
if (qxl->guest_primary.flipped) {
qemu_free(qxl->guest_primary.flipped);
qxl->guest_primary.flipped = NULL;
}
qemu_free_displaysurface(vga->ds);
qxl->guest_primary.data = qemu_get_ram_ptr(qxl->vga.vram_offset);
if (qxl->guest_primary.stride < 0) {
/* spice surface is upside down -> need extra buffer to flip */
qxl->guest_primary.stride = -qxl->guest_primary.stride;
qxl->guest_primary.flipped = qemu_malloc(qxl->guest_primary.surface.width *
qxl->guest_primary.stride);
ptr = qxl->guest_primary.flipped;
} else {
ptr = qxl->guest_primary.data;
}
dprint(qxl, 1, "%s: %dx%d, stride %d, bpp %d, depth %d, flip %s\n",
__FUNCTION__,
qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height,
qxl->guest_primary.stride,
qxl->guest_primary.bytes_pp,
qxl->guest_primary.bits_pp,
qxl->guest_primary.flipped ? "yes" : "no");
vga->ds->surface =
qemu_create_displaysurface_from(qxl->guest_primary.surface.width,
qxl->guest_primary.surface.height,
qxl->guest_primary.bits_pp,
qxl->guest_primary.stride,
ptr);
dpy_resize(vga->ds);
}
if (!qxl->guest_primary.commands) {
return;
}
qxl->guest_primary.commands = 0;
update.left = 0;
update.right = qxl->guest_primary.surface.width;
update.top = 0;
update.bottom = qxl->guest_primary.surface.height;
memset(dirty, 0, sizeof(dirty));
qxl->ssd.worker->update_area(qxl->ssd.worker, 0, &update,
dirty, ARRAY_SIZE(dirty), 1);
for (i = 0; i < ARRAY_SIZE(dirty); i++) {
if (qemu_spice_rect_is_empty(dirty+i)) {
break;
}
if (qxl->guest_primary.flipped) {
qxl_flip(qxl, dirty+i);
}
dpy_update(vga->ds,
dirty[i].left, dirty[i].top,
dirty[i].right - dirty[i].left,
dirty[i].bottom - dirty[i].top);
}
}
static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor)
{
QEMUCursor *c;
uint8_t *image, *mask;
int size;
c = cursor_alloc(cursor->header.width, cursor->header.height);
c->hot_x = cursor->header.hot_spot_x;
c->hot_y = cursor->header.hot_spot_y;
switch (cursor->header.type) {
case SPICE_CURSOR_TYPE_ALPHA:
size = cursor->header.width * cursor->header.height * sizeof(uint32_t);
memcpy(c->data, cursor->chunk.data, size);
if (qxl->debug > 2) {
cursor_print_ascii_art(c, "qxl/alpha");
}
break;
case SPICE_CURSOR_TYPE_MONO:
mask = cursor->chunk.data;
image = mask + cursor_get_mono_bpl(c) * c->width;
cursor_set_mono(c, 0xffffff, 0x000000, image, 1, mask);
if (qxl->debug > 2) {
cursor_print_ascii_art(c, "qxl/mono");
}
break;
default:
fprintf(stderr, "%s: not implemented: type %d\n",
__FUNCTION__, cursor->header.type);
goto fail;
}
return c;
fail:
cursor_put(c);
return NULL;
}
/* called from spice server thread context only */
void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext)
{
QXLCursorCmd *cmd = qxl_phys2virt(qxl, ext->cmd.data, ext->group_id);
QXLCursor *cursor;
QEMUCursor *c;
int x = -1, y = -1;
if (!qxl->ssd.ds->mouse_set || !qxl->ssd.ds->cursor_define) {
return;
}
if (qxl->debug > 1 && cmd->type != QXL_CURSOR_MOVE) {
fprintf(stderr, "%s", __FUNCTION__);
qxl_log_cmd_cursor(qxl, cmd, ext->group_id);
fprintf(stderr, "\n");
}
switch (cmd->type) {
case QXL_CURSOR_SET:
x = cmd->u.set.position.x;
y = cmd->u.set.position.y;
cursor = qxl_phys2virt(qxl, cmd->u.set.shape, ext->group_id);
if (cursor->chunk.data_size != cursor->data_size) {
fprintf(stderr, "%s: multiple chunks\n", __FUNCTION__);
return;
}
c = qxl_cursor(qxl, cursor);
if (c == NULL) {
c = cursor_builtin_left_ptr();
}
qemu_mutex_lock_iothread();
qxl->ssd.ds->cursor_define(c);
qxl->ssd.ds->mouse_set(x, y, 1);
qemu_mutex_unlock_iothread();
cursor_put(c);
break;
case QXL_CURSOR_MOVE:
x = cmd->u.position.x;
y = cmd->u.position.y;
qemu_mutex_lock_iothread();
qxl->ssd.ds->mouse_set(x, y, 1);
qemu_mutex_unlock_iothread();
break;
}
}

1587
hw/qxl.c Normal file

File diff suppressed because it is too large Load Diff

112
hw/qxl.h Normal file
View File

@ -0,0 +1,112 @@
#include "qemu-common.h"
#include "console.h"
#include "hw.h"
#include "pci.h"
#include "vga_int.h"
#include "ui/qemu-spice.h"
#include "ui/spice-display.h"
enum qxl_mode {
QXL_MODE_UNDEFINED,
QXL_MODE_VGA,
QXL_MODE_COMPAT, /* spice 0.4.x */
QXL_MODE_NATIVE,
};
typedef struct PCIQXLDevice {
PCIDevice pci;
SimpleSpiceDisplay ssd;
int id;
uint32_t debug;
uint32_t guestdebug;
uint32_t cmdlog;
enum qxl_mode mode;
uint32_t cmdflags;
int generation;
uint32_t revision;
int32_t num_memslots;
int32_t num_surfaces;
struct guest_slots {
QXLMemSlot slot;
void *ptr;
uint64_t size;
uint64_t delta;
uint32_t active;
} guest_slots[NUM_MEMSLOTS];
struct guest_primary {
QXLSurfaceCreate surface;
uint32_t commands;
uint32_t resized;
int32_t stride;
uint32_t bits_pp;
uint32_t bytes_pp;
uint8_t *data, *flipped;
} guest_primary;
struct surfaces {
QXLPHYSICAL cmds[NUM_SURFACES];
uint32_t count;
uint32_t max;
} guest_surfaces;
QXLPHYSICAL guest_cursor;
/* thread signaling */
pthread_t main;
int pipe[2];
/* ram pci bar */
QXLRam *ram;
VGACommonState vga;
uint32_t num_free_res;
QXLReleaseInfo *last_release;
uint32_t last_release_offset;
uint32_t oom_running;
/* rom pci bar */
QXLRom shadow_rom;
QXLRom *rom;
QXLModes *modes;
uint32_t rom_size;
uint64_t rom_offset;
/* vram pci bar */
uint32_t vram_size;
uint64_t vram_offset;
/* io bar */
uint32_t io_base;
/* spice 0.4 loadvm compatibility */
void *worker_data;
uint32_t worker_data_size;
} PCIQXLDevice;
#define PANIC_ON(x) if ((x)) { \
printf("%s: PANIC %s failed\n", __FUNCTION__, #x); \
exit(-1); \
}
#define dprint(_qxl, _level, _fmt, ...) \
do { \
if (_qxl->debug >= _level) { \
fprintf(stderr, "qxl-%d: ", _qxl->id); \
fprintf(stderr, _fmt, ## __VA_ARGS__); \
} \
} while (0)
/* qxl.c */
void *qxl_phys2virt(PCIQXLDevice *qxl, QXLPHYSICAL phys, int group_id);
/* qxl-logger.c */
void qxl_log_cmd_cursor(PCIQXLDevice *qxl, QXLCursorCmd *cmd, int group_id);
void qxl_log_command(PCIQXLDevice *qxl, const char *ring, QXLCommandExt *ext);
/* qxl-render.c */
void qxl_render_resize(PCIQXLDevice *qxl);
void qxl_render_update(PCIQXLDevice *qxl);
void qxl_render_cursor(PCIQXLDevice *qxl, QXLCommandExt *ext);

View File

@ -106,7 +106,7 @@ typedef void (* vga_update_retrace_info_fn)(struct VGACommonState *s);
typedef struct VGACommonState {
uint8_t *vram_ptr;
ram_addr_t vram_offset;
unsigned int vram_size;
uint32_t vram_size;
uint32_t lfb_addr;
uint32_t lfb_end;
uint32_t map_addr;

130
monitor.c
View File

@ -34,6 +34,7 @@
#include "net.h"
#include "net/slirp.h"
#include "qemu-char.h"
#include "ui/qemu-spice.h"
#include "sysemu.h"
#include "monitor.h"
#include "readline.h"
@ -59,6 +60,7 @@
#ifdef CONFIG_SIMPLE_TRACE
#include "trace.h"
#endif
#include "ui/qemu-spice.h"
//#define DEBUG
//#define DEBUG_COMPLETION
@ -457,6 +459,15 @@ void monitor_protocol_event(MonitorEvent event, QObject *data)
case QEVENT_WATCHDOG:
event_name = "WATCHDOG";
break;
case QEVENT_SPICE_CONNECTED:
event_name = "SPICE_CONNECTED";
break;
case QEVENT_SPICE_INITIALIZED:
event_name = "SPICE_INITIALIZED";
break;
case QEVENT_SPICE_DISCONNECTED:
event_name = "SPICE_DISCONNECTED";
break;
default:
abort();
break;
@ -1063,6 +1074,105 @@ static int do_change(Monitor *mon, const QDict *qdict, QObject **ret_data)
return ret;
}
static int set_password(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *password = qdict_get_str(qdict, "password");
const char *connected = qdict_get_try_str(qdict, "connected");
int disconnect_if_connected = 0;
int fail_if_connected = 0;
int rc;
if (connected) {
if (strcmp(connected, "fail") == 0) {
fail_if_connected = 1;
} else if (strcmp(connected, "disconnect") == 0) {
disconnect_if_connected = 1;
} else if (strcmp(connected, "keep") == 0) {
/* nothing */
} else {
qerror_report(QERR_INVALID_PARAMETER, "connected");
return -1;
}
}
if (strcmp(protocol, "spice") == 0) {
if (!using_spice) {
/* correct one? spice isn't a device ,,, */
qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
return -1;
}
rc = qemu_spice_set_passwd(password, fail_if_connected,
disconnect_if_connected);
if (rc != 0) {
qerror_report(QERR_SET_PASSWD_FAILED);
return -1;
}
return 0;
}
if (strcmp(protocol, "vnc") == 0) {
if (fail_if_connected || disconnect_if_connected) {
/* vnc supports "connected=keep" only */
qerror_report(QERR_INVALID_PARAMETER, "connected");
return -1;
}
rc = vnc_display_password(NULL, password);
if (rc != 0) {
qerror_report(QERR_SET_PASSWD_FAILED);
return -1;
}
return 0;
}
qerror_report(QERR_INVALID_PARAMETER, "protocol");
return -1;
}
static int expire_password(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
const char *protocol = qdict_get_str(qdict, "protocol");
const char *whenstr = qdict_get_str(qdict, "time");
time_t when;
int rc;
if (strcmp(whenstr, "now")) {
when = 0;
} else if (strcmp(whenstr, "never")) {
when = TIME_MAX;
} else if (whenstr[0] == '+') {
when = time(NULL) + strtoull(whenstr+1, NULL, 10);
} else {
when = strtoull(whenstr, NULL, 10);
}
if (strcmp(protocol, "spice") == 0) {
if (!using_spice) {
/* correct one? spice isn't a device ,,, */
qerror_report(QERR_DEVICE_NOT_ACTIVE, "spice");
return -1;
}
rc = qemu_spice_set_pw_expire(when);
if (rc != 0) {
qerror_report(QERR_SET_PASSWD_FAILED);
return -1;
}
return 0;
}
if (strcmp(protocol, "vnc") == 0) {
rc = vnc_display_pw_expire(NULL, when);
if (rc != 0) {
qerror_report(QERR_SET_PASSWD_FAILED);
return -1;
}
return 0;
}
qerror_report(QERR_INVALID_PARAMETER, "protocol");
return -1;
}
static int do_screen_dump(Monitor *mon, const QDict *qdict, QObject **ret_data)
{
vga_hw_screen_dump(qdict_get_str(qdict, "filename"));
@ -2859,6 +2969,16 @@ static const mon_cmd_t info_cmds[] = {
.user_print = do_info_vnc_print,
.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",
.args_type = "",
@ -3046,6 +3166,16 @@ static const mon_cmd_t qmp_query_cmds[] = {
.user_print = do_info_vnc_print,
.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",
.args_type = "",

View File

@ -32,6 +32,9 @@ typedef enum MonitorEvent {
QEVENT_BLOCK_IO_ERROR,
QEVENT_RTC_CHANGE,
QEVENT_WATCHDOG,
QEVENT_SPICE_CONNECTED,
QEVENT_SPICE_INITIALIZED,
QEVENT_SPICE_DISCONNECTED,
QEVENT_MAX,
} MonitorEvent;

BIN
pc-bios/vgabios-qxl.bin Normal file

Binary file not shown.

View File

@ -50,6 +50,9 @@ typedef struct DeviceState DeviceState;
#if !defined(ENOTSUP)
#define ENOTSUP 4096
#endif
#ifndef TIME_MAX
#define TIME_MAX LONG_MAX
#endif
#ifndef CONFIG_IOVEC
#define CONFIG_IOVEC

View File

@ -751,7 +751,7 @@ Rotate graphical output 90 deg left (only PXA LCD).
ETEXI
DEF("vga", HAS_ARG, QEMU_OPTION_vga,
"-vga [std|cirrus|vmware|xenfb|none]\n"
"-vga [std|cirrus|vmware|qxl|xenfb|none]\n"
" select video card type\n", QEMU_ARCH_ALL)
STEXI
@item -vga @var{type}
@ -772,6 +772,10 @@ this option.
VMWare SVGA-II compatible adapter. Use it if you have sufficiently
recent XFree86/XOrg server or Windows guest with a driver for this
card.
@item qxl
QXL paravirtual graphic card. It is VGA compatible (including VESA
2.0 VBE support). Works best with qxl guest drivers installed though.
Recommended choice when using the spice protocol.
@item none
Disable VGA card.
@end table

View File

@ -735,6 +735,63 @@ Example:
"password": "12345" } }
<- { "return": {} }
EQMP
{
.name = "set_password",
.args_type = "protocol:s,password:s,connected:s?",
.params = "protocol password action-if-connected",
.help = "set spice/vnc password",
.user_print = monitor_user_noop,
.mhandler.cmd_new = set_password,
},
SQMP
set_password
------------
Set the password for vnc/spice protocols.
Arguments:
- "protocol": protocol name (json-string)
- "password": password (json-string)
- "connected": [ keep | disconnect | fail ] (josn-string, optional)
Example:
-> { "execute": "set_password", "arguments": { "protocol": "vnc",
"password": "secret" } }
<- { "return": {} }
EQMP
{
.name = "expire_password",
.args_type = "protocol:s,time:s",
.params = "protocol time",
.help = "set spice/vnc password expire-time",
.user_print = monitor_user_noop,
.mhandler.cmd_new = expire_password,
},
SQMP
expire_password
---------------
Set the password expire time for vnc/spice protocols.
Arguments:
- "protocol": protocol name (json-string)
- "time": [ now | never | +secs | secs ] (json-string)
Example:
-> { "execute": "expire_password", "arguments": { "protocol": "vnc",
"time": "+60" } }
<- { "return": {} }
EQMP
{
@ -1438,6 +1495,76 @@ Example:
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
query-name
----------

View File

@ -104,7 +104,7 @@ extern int incoming_expected;
extern int bios_size;
typedef enum {
VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB
VGA_NONE, VGA_STD, VGA_CIRRUS, VGA_VMWARE, VGA_XENFB, VGA_QXL,
} VGAInterfaceType;
extern int vga_interface_type;
@ -112,6 +112,7 @@ extern int vga_interface_type;
#define std_vga_enabled (vga_interface_type == VGA_STD)
#define xenfb_enabled (vga_interface_type == VGA_XENFB)
#define vmsvga_enabled (vga_interface_type == VGA_VMWARE)
#define qxl_enabled (vga_interface_type == VGA_QXL)
extern int graphic_width;
extern int graphic_height;

View File

@ -32,10 +32,18 @@ void qemu_spice_input_init(void);
void qemu_spice_audio_init(void);
void qemu_spice_display_init(DisplayState *ds);
int qemu_spice_add_interface(SpiceBaseInstance *sin);
int qemu_spice_set_passwd(const char *passwd,
bool fail_if_connected, bool disconnect_if_connected);
int qemu_spice_set_pw_expire(time_t expires);
void do_info_spice_print(Monitor *mon, const QObject *data);
void do_info_spice(Monitor *mon, QObject **ret_data);
#else /* CONFIG_SPICE */
#define using_spice 0
#define qemu_spice_set_passwd(_p, _f1, _f2) (-1)
#define qemu_spice_set_pw_expire(_e) (-1)
#endif /* CONFIG_SPICE */

View File

@ -18,16 +18,26 @@
#include <spice.h>
#include <spice-experimental.h>
#include <netdb.h>
#include "qemu-common.h"
#include "qemu-spice.h"
#include "qemu-timer.h"
#include "qemu-queue.h"
#include "qemu-x509.h"
#include "qemu_socket.h"
#include "qint.h"
#include "qbool.h"
#include "qstring.h"
#include "qjson.h"
#include "monitor.h"
/* core bits */
static SpiceServer *spice_server;
static const char *auth = "spice";
static char *auth_passwd;
static time_t auth_expires = TIME_MAX;
int using_spice = 0;
struct SpiceTimer {
@ -121,6 +131,118 @@ static void watch_remove(SpiceWatch *watch)
qemu_free(watch);
}
#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)
{
char host[NI_MAXHOST], port[NI_MAXSERV];
const char *family;
getnameinfo(addr, len, host, sizeof(host), port, sizeof(port),
NI_NUMERICHOST | NI_NUMERICSERV);
family = inet_strfamily(addr->sa_family);
qdict_put(dict, "host", qstring_from_str(host));
qdict_put(dict, "port", qstring_from_str(port));
qdict_put(dict, "family", qstring_from_str(family));
}
static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info)
{
int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
qdict_put(dict, "connection-id", qint_from_int(info->connection_id));
qdict_put(dict, "channel-type", qint_from_int(info->type));
qdict_put(dict, "channel-id", qint_from_int(info->id));
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 const int qevent[] = {
[ SPICE_CHANNEL_EVENT_CONNECTED ] = QEVENT_SPICE_CONNECTED,
[ SPICE_CHANNEL_EVENT_INITIALIZED ] = QEVENT_SPICE_INITIALIZED,
[ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED,
};
QDict *server, *client;
QObject *data;
client = qdict_new();
add_addr_info(client, &info->paddr, info->plen);
server = qdict_new();
add_addr_info(server, &info->laddr, info->llen);
if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
qdict_put(server, "auth", qstring_from_str(auth));
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 }",
QOBJECT(client), QOBJECT(server));
monitor_protocol_event(qevent[event], data);
qobject_decref(data);
}
#else /* SPICE_INTERFACE_CORE_MINOR >= 3 */
static QList *channel_list_get(void)
{
return NULL;
}
#endif /* SPICE_INTERFACE_CORE_MINOR >= 3 */
static SpiceCoreInterface core_interface = {
.base.type = SPICE_INTERFACE_CORE,
.base.description = "qemu core services",
@ -135,6 +257,10 @@ static SpiceCoreInterface core_interface = {
.watch_add = watch_add,
.watch_update_mask = watch_update_mask,
.watch_remove = watch_remove,
#if SPICE_INTERFACE_CORE_MINOR >= 3
.channel_event = channel_event,
#endif
};
/* config string parsing */
@ -204,6 +330,92 @@ static const char *wan_compression_names[] = {
/* 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)
{
int security = 0;
@ -316,6 +528,7 @@ void qemu_spice_init(void)
spice_server_set_ticket(spice_server, password, 0, 0, 0);
}
if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
auth = "none";
spice_server_set_noauth(spice_server);
}
@ -370,9 +583,57 @@ void qemu_spice_init(void)
int qemu_spice_add_interface(SpiceBaseInstance *sin)
{
if (!spice_server) {
if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) {
fprintf(stderr, "Oops: spice configured but not active\n");
exit(1);
}
/*
* Create a spice server instance.
* It does *not* listen on the network.
* It handles QXL local rendering only.
*
* With a command line like '-vnc :0 -vga qxl' you'll end up here.
*/
spice_server = spice_server_new();
spice_server_init(spice_server, &core_interface);
}
return spice_server_add_interface(spice_server, sin);
}
static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn)
{
time_t lifetime, now = time(NULL);
char *passwd;
if (now < auth_expires) {
passwd = auth_passwd;
lifetime = (auth_expires - now);
if (lifetime > INT_MAX) {
lifetime = INT_MAX;
}
} else {
passwd = NULL;
lifetime = 1;
}
return spice_server_set_ticket(spice_server, passwd, lifetime,
fail_if_conn, disconnect_if_conn);
}
int qemu_spice_set_passwd(const char *passwd,
bool fail_if_conn, bool disconnect_if_conn)
{
free(auth_passwd);
auth_passwd = strdup(passwd);
return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn);
}
int qemu_spice_set_pw_expire(time_t expires)
{
auth_expires = expires;
return qemu_spice_set_ticket(false, false);
}
static void spice_register_config(void)
{
qemu_add_opts(&qemu_spice_opts);

View File

@ -2082,18 +2082,15 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
int i, j, pwlen;
unsigned char key[8];
time_t now = time(NULL);
if (!vs->vd->password || !vs->vd->password[0]) {
VNC_DEBUG("No password configured on server");
vnc_write_u32(vs, 1); /* Reject auth */
if (vs->minor >= 8) {
static const char err[] = "Authentication failed";
vnc_write_u32(vs, sizeof(err));
vnc_write(vs, err, sizeof(err));
}
vnc_flush(vs);
vnc_client_error(vs);
return 0;
goto reject;
}
if (vs->vd->expires < now) {
VNC_DEBUG("Password is expired");
goto reject;
}
memcpy(response, vs->challenge, VNC_AUTH_CHALLENGE_SIZE);
@ -2109,14 +2106,7 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
/* Compare expected vs actual challenge response */
if (memcmp(response, data, VNC_AUTH_CHALLENGE_SIZE) != 0) {
VNC_DEBUG("Client challenge reponse did not match\n");
vnc_write_u32(vs, 1); /* Reject auth */
if (vs->minor >= 8) {
static const char err[] = "Authentication failed";
vnc_write_u32(vs, sizeof(err));
vnc_write(vs, err, sizeof(err));
}
vnc_flush(vs);
vnc_client_error(vs);
goto reject;
} else {
VNC_DEBUG("Accepting VNC challenge response\n");
vnc_write_u32(vs, 0); /* Accept auth */
@ -2125,6 +2115,17 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
start_client_init(vs);
}
return 0;
reject:
vnc_write_u32(vs, 1); /* Reject auth */
if (vs->minor >= 8) {
static const char err[] = "Authentication failed";
vnc_write_u32(vs, sizeof(err));
vnc_write(vs, err, sizeof(err));
}
vnc_flush(vs);
vnc_client_error(vs);
return 0;
}
void start_auth_vnc(VncState *vs)
@ -2436,6 +2437,7 @@ void vnc_display_init(DisplayState *ds)
vs->ds = ds;
QTAILQ_INIT(&vs->clients);
vs->expires = TIME_MAX;
if (keyboard_layout)
vs->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout);
@ -2507,6 +2509,14 @@ int vnc_display_password(DisplayState *ds, const char *password)
return 0;
}
int vnc_display_pw_expire(DisplayState *ds, time_t expires)
{
VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;
vs->expires = expires;
return 0;
}
char *vnc_display_local_addr(DisplayState *ds)
{
VncDisplay *vs = ds ? (VncDisplay *)ds->opaque : vnc_display;

View File

@ -120,6 +120,7 @@ struct VncDisplay
char *display;
char *password;
time_t expires;
int auth;
bool lossy;
#ifdef CONFIG_VNC_TLS

4
vl.c
View File

@ -1504,6 +1504,8 @@ static void select_vgahw (const char *p)
vga_interface_type = VGA_VMWARE;
} else if (strstart(p, "xenfb", &opts)) {
vga_interface_type = VGA_XENFB;
} else if (strstart(p, "qxl", &opts)) {
vga_interface_type = VGA_QXL;
} else if (!strstart(p, "none", &opts)) {
invalid_vga:
fprintf(stderr, "Unknown vga type: %s\n", p);
@ -3055,7 +3057,7 @@ int main(int argc, char **argv, char **envp)
}
}
#ifdef CONFIG_SPICE
if (using_spice) {
if (using_spice && !qxl_enabled) {
qemu_spice_display_init(ds);
}
#endif