[libvirt] [PATCH 00/10] Add support for SPICE graphics in QEMU

Gerd today posted patches to upstream QEMU which support SPICE graphics. They have not been accepted / merged yet, but I'm optimistic that the command line syntax will not change significantly. http://lists.gnu.org/archive/html/qemu-devel/2010-04/msg00967.html This patch series adds corresponding support in libvirt's QEMU driver. Although this patch series adds support in the XML RNG for a password expiry time, this functionality isn't wired up in this patch series since I need more time to write fallback code for cases where QEMU doesn't do expiry itself. Daniel

* src/qemu_conf.c: Add dummy entry in enumeration * docs/schemas/domain.rng: Add 'qxl' as a type for the <video> tag * src/domain_conf.c, src/domain_conf.h: Add QXL to video type enumerations --- docs/schemas/domain.rng | 1 + src/conf/domain_conf.c | 3 ++- src/conf/domain_conf.h | 1 + src/qemu/qemu_conf.c | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index c877a35..bf3759e 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1050,6 +1050,7 @@ <value>vmvga</value> <value>xen</value> <value>vbox</value> + <value>qxl</value> </choice> </attribute> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 2de838b..a10ef66 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -199,7 +199,8 @@ VIR_ENUM_IMPL(virDomainVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "cirrus", "vmvga", "xen", - "vbox") + "vbox", + "qxl") VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST, "mouse", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 82f2d15..afd143d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -442,6 +442,7 @@ enum virDomainVideoType { VIR_DOMAIN_VIDEO_TYPE_VMVGA, VIR_DOMAIN_VIDEO_TYPE_XEN, VIR_DOMAIN_VIDEO_TYPE_VBOX, + VIR_DOMAIN_VIDEO_TYPE_QXL, VIR_DOMAIN_VIDEO_TYPE_LAST }; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 6557b2f..767e168 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -92,7 +92,8 @@ VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "cirrus", "vmware", "", /* no arg needed for xen */ - "" /* don't support vbox */); + "", /* don't support vbox */ + "", /* Not implemented QXL yet */); int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename) { -- 1.6.6.1

This adds an element <graphics type='spice' port='5903' tlsPort='5904' autoport='yes' listen='127.0.0.1'/> This is the bare minimum that should be exposed in the guest config for SPICE. Other parameters are better handled as per host level configuration tunables * docs/schemas/domain.rng: Define the SPICE <graphics> schema * src/domain_conf.h, src/domain_conf.c: Add parsing and formatting for SPICE graphics config * src/qemu_conf.c: Complain about unsupported graphics types --- docs/schemas/domain.rng | 38 ++++++++++++++++++++++ src/conf/domain_conf.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 9 +++++ src/qemu/qemu_conf.c | 11 ++++++- 4 files changed, 136 insertions(+), 2 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index bf3759e..edd0b7c 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -977,6 +977,44 @@ </group> <group> <attribute name="type"> + <value>spice</value> + </attribute> + <optional> + <attribute name="port"> + <ref name="PortNumber"/> + </attribute> + </optional> + <optional> + <attribute name="tlsPort"> + <ref name="PortNumber"/> + </attribute> + </optional> + <optional> + <attribute name="autoport"> + <choice> + <value>yes</value> + <value>no</value> + </choice> + </attribute> + </optional> + <optional> + <attribute name="listen"> + <ref name="addrIP"/> + </attribute> + </optional> + <optional> + <attribute name="passwd"> + <text/> + </attribute> + </optional> + <optional> + <attribute name="keymap"> + <text/> + </attribute> + </optional> + </group> + <group> + <attribute name="type"> <value>rdp</value> </attribute> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a10ef66..b25c622 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -215,7 +215,8 @@ VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST, "sdl", "vnc", "rdp", - "desktop") + "desktop", + "spice") VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST, "subsystem", @@ -397,6 +398,12 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: VIR_FREE(def->data.desktop.display); break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + VIR_FREE(def->data.spice.listenAddr); + VIR_FREE(def->data.spice.keymap); + VIR_FREE(def->data.spice.passwd); + break; } VIR_FREE(def); @@ -2849,6 +2856,50 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { def->data.desktop.fullscreen = 0; def->data.desktop.display = virXMLPropString(node, "display"); + } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + char *port = virXMLPropString(node, "port"); + char *tlsPort; + char *autoport; + + if (port) { + if (virStrToLong_i(port, NULL, 10, &def->data.spice.port) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse spice port %s"), port); + VIR_FREE(port); + goto error; + } + VIR_FREE(port); + } else { + def->data.spice.port = 5900; + } + + tlsPort = virXMLPropString(node, "tlsPort"); + if (tlsPort) { + if (virStrToLong_i(tlsPort, NULL, 10, &def->data.spice.tlsPort) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse spice tlsPort %s"), tlsPort); + VIR_FREE(tlsPort); + goto error; + } + VIR_FREE(tlsPort); + } else { + def->data.spice.tlsPort = 0; + } + + if ((autoport = virXMLPropString(node, "autoport")) != NULL) { + if (STREQ(autoport, "yes")) { + if (flags & VIR_DOMAIN_XML_INACTIVE) { + def->data.spice.port = 0; + def->data.spice.tlsPort = 0; + } + def->data.spice.autoport = 1; + } + VIR_FREE(autoport); + } + + def->data.spice.listenAddr = virXMLPropString(node, "listen"); + def->data.spice.passwd = virXMLPropString(node, "passwd"); + def->data.spice.keymap = virXMLPropString(node, "keymap"); } cleanup: @@ -5653,6 +5704,33 @@ virDomainGraphicsDefFormat(virBufferPtr buf, break; + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + if (def->data.spice.port) + virBufferVSprintf(buf, " port='%d'", + def->data.spice.port); + + if (def->data.spice.tlsPort) + virBufferVSprintf(buf, " tlsPort='%d'", + def->data.spice.tlsPort); + + virBufferVSprintf(buf, " autoport='%s'", + def->data.spice.autoport ? "yes" : "no"); + + if (def->data.spice.listenAddr) + virBufferVSprintf(buf, " listen='%s'", + def->data.spice.listenAddr); + + if (def->data.spice.keymap) + virBufferEscapeString(buf, " keymap='%s'", + def->data.spice.keymap); + + if (def->data.spice.passwd && + (flags & VIR_DOMAIN_XML_SECURE)) + virBufferEscapeString(buf, " passwd='%s'", + def->data.spice.passwd); + + break; + } virBufferAddLit(buf, "/>\n"); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index afd143d..cda1d98 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -472,6 +472,7 @@ enum virDomainGraphicsType { VIR_DOMAIN_GRAPHICS_TYPE_VNC, VIR_DOMAIN_GRAPHICS_TYPE_RDP, VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP, + VIR_DOMAIN_GRAPHICS_TYPE_SPICE, VIR_DOMAIN_GRAPHICS_TYPE_LAST, }; @@ -504,6 +505,14 @@ struct _virDomainGraphicsDef { char *display; unsigned int fullscreen :1; } desktop; + struct { + int port; + int tlsPort; + char *listenAddr; + char *keymap; + char *passwd; + unsigned int autoport :1; + } spice; } data; }; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 767e168..84b7067 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4303,6 +4303,12 @@ int qemudBuildCommandLine(virConnectPtr conn, } } + if (def->ngraphics > 1) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("only one graphics output is currently supported")); + goto error; + } + if ((def->ngraphics == 1) && def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { virBuffer opt = VIR_BUFFER_INITIALIZER; @@ -4399,11 +4405,14 @@ int qemudBuildCommandLine(virConnectPtr conn, * default, since the default changes :-( */ if (qemuCmdFlags & QEMUD_CMD_FLAG_SDL) ADD_ARG_LIT("-sdl"); + } else if (def->ngraphics) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("unsupported graphics output requested")); } if (def->nvideos) { if (def->nvideos > 1) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("only one video card is currently supported")); goto error; } -- 1.6.6.1

Instead of reporting VIR_ERR_INTERNAL_ERROR use the more specific VIR_ERR_CONFIG_UNSUPPORTED * src/qemu/qemu_conf.c: Report VIR_ERR_CONFIG_UNSUPPORTED for unsupported video adapters --- src/qemu/qemu_conf.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 84b7067..b922907 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4423,7 +4423,7 @@ int qemudBuildCommandLine(virConnectPtr conn, } else { const char *vgastr = qemuVideoTypeToString(def->videos[0]->type); if (!vgastr || STREQ(vgastr, "")) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("video type %s is not supported with QEMU"), virDomainVideoTypeToString(def->videos[0]->type)); goto error; @@ -4449,8 +4449,8 @@ int qemudBuildCommandLine(virConnectPtr conn, break; default: - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("video type %s is not supported with QEMU"), + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("video type %s is not supported with this QEMU"), virDomainVideoTypeToString(def->videos[0]->type)); goto error; } -- 1.6.6.1

On Wed, Apr 14, 2010 at 11:33:21AM +0100, Daniel P. Berrange wrote:
Instead of reporting VIR_ERR_INTERNAL_ERROR use the more specific VIR_ERR_CONFIG_UNSUPPORTED
* src/qemu/qemu_conf.c: Report VIR_ERR_CONFIG_UNSUPPORTED for unsupported video adapters
ACK on that one, not dependant on spice support Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This supports the '-vga qxl' parameter in upstream QEMU/KVM which has SPICE support added. This isn't particularly useful until you get the next patch for -spice support. Also note that while the libvirt XML supports multiple video devices, this patch only supports a single one. A later patch can add support for 2nd, 3rd, etc PCI devices for QXL * src/qemu/qemu_conf.h: Flag for QXL support * src/qemu/qemu_conf.c: Probe for '-vga qxl' support and implement it * tests/qemuhelptest.c, tests/qemuhelpdata/qemu-kvm-0.12.1-rhel6: Test case for '-vga qxl' probing. Currently broken. * tests/qemuxml2argvtest.c, tests/qemuxml2xmltest.c, tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args, tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml: Test case for generating spice args with RHEL6 kvm --- src/qemu/qemu_conf.c | 15 ++- src/qemu/qemu_conf.h | 1 + tests/qemuhelpdata/qemu-kvm-0.12.1-rhel6 | 212 ++++++++++++++++++++ tests/qemuhelptest.c | 28 +++ .../qemuxml2argv-graphics-spice-rhel6.args | 1 + .../qemuxml2argv-graphics-spice-rhel6.xml | 27 +++ tests/qemuxml2argvtest.c | 10 + tests/qemuxml2xmltest.c | 1 + 8 files changed, 293 insertions(+), 2 deletions(-) create mode 100644 tests/qemuhelpdata/qemu-kvm-0.12.1-rhel6 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b922907..d59b0e9 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -93,7 +93,7 @@ VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "vmware", "", /* no arg needed for xen */ "", /* don't support vbox */ - "", /* Not implemented QXL yet */); + "qxl"); int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename) { @@ -1143,8 +1143,12 @@ static unsigned long long qemudComputeCmdFlags(const char *help, if (strstr(help, "format=")) flags |= QEMUD_CMD_FLAG_DRIVE_FORMAT; } - if (strstr(help, "-vga") && !strstr(help, "-std-vga")) + if (strstr(help, "-vga") && !strstr(help, "-std-vga")) { flags |= QEMUD_CMD_FLAG_VGA; + + if (strstr(help, "|qxl")) + flags |= QEMUD_CMD_FLAG_VGA_QXL; + } if (strstr(help, "boot=on")) flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; if (strstr(help, "serial=s")) @@ -4421,6 +4425,13 @@ int qemudBuildCommandLine(virConnectPtr conn, if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_XEN) { /* nothing - vga has no effect on Xen pvfb */ } else { + if ((def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_QXL) && + !(qemuCmdFlags & QEMUD_CMD_FLAG_VGA_QXL)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU does not support QXL graphics adapters")); + goto error; + } + const char *vgastr = qemuVideoTypeToString(def->videos[0]->type); if (!vgastr || STREQ(vgastr, "")) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e0666cb..5321955 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -88,6 +88,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_NO_HPET = (1LL << 33), /* -no-hpet flag is supported */ QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection supported */ QEMUD_CMD_FLAG_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */ + QEMUD_CMD_FLAG_VGA_QXL = (1LL << 36), /* The 'qxl' arg for '-vga' */ }; /* Main driver state */ diff --git a/tests/qemuhelpdata/qemu-kvm-0.12.1-rhel6 b/tests/qemuhelpdata/qemu-kvm-0.12.1-rhel6 new file mode 100644 index 0000000..db54f6b --- /dev/null +++ b/tests/qemuhelpdata/qemu-kvm-0.12.1-rhel6 @@ -0,0 +1,212 @@ +QEMU PC emulator version 0.12.1 (qemu-kvm-0.12.1.2), Copyright (c) 2003-2008 Fabrice Bellard +usage: qemu [options] [disk_image] + +'disk_image' is a raw hard image image for IDE hard disk 0 + +Standard options: +-h or -help display this help and exit +-version display version information and exit +-M machine select emulated machine (-M ? for list) +-cpu cpu select CPU (-cpu ? for list) +-smp n[,maxcpus=cpus][,cores=cores][,threads=threads][,sockets=sockets] + set the number of CPUs to 'n' [default=1] + maxcpus= maximum number of total cpus, including + offline CPUs for hotplug etc. + cores= number of CPU cores on one socket + threads= number of threads on one CPU core + sockets= number of discrete sockets in the system +-numa node[,mem=size][,cpus=cpu[-cpu]][,nodeid=node] +-fda/-fdb file use 'file' as floppy disk 0/1 image +-hda/-hdb file use 'file' as IDE hard disk 0/1 image +-hdc/-hdd file use 'file' as IDE hard disk 2/3 image +-cdrom file use 'file' as IDE cdrom image (cdrom is ide1 master) +-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i] + [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off] + [,cache=writethrough|writeback|none][,format=f][,serial=s] + [,addr=A][,id=name][,aio=threads|native] + [,boot=on|off] + use 'file' as a drive image +-set group.id.arg=value + set <arg> parameter for item <id> of type <group> + i.e. -set drive.$id.file=/path/to/image +-global driver.property=value + set a global default for a driver property +-mtdblock file use 'file' as on-board Flash memory image +-sd file use 'file' as SecureDigital card image +-pflash file use 'file' as a parallel flash image +-boot [order=drives][,once=drives][,menu=on|off] + 'drives': floppy (a), hard disk (c), CD-ROM (d), network (n) +-snapshot write to temporary files instead of disk image files +-m megs set virtual RAM size to megs MB [default=128] +-k language use keyboard layout (for example 'fr' for French) +-audio-help print list of audio drivers and their options +-soundhw c1,... enable audio support + and only specified sound cards (comma separated list) + use -soundhw ? to get the list of supported cards + use -soundhw all to enable all of them +-usb enable the USB driver (will be the default soon) +-usbdevice name add the host or guest USB device 'name' +-device driver[,options] add device +-name string1[,process=string2] set the name of the guest + string1 sets the window title and string2 the process name (on Linux) +-uuid %08x-%04x-%04x-%04x-%012x + specify machine UUID + +Display options: +-nographic disable graphical output and redirect serial I/Os to console +-spice <args> use spice +-portrait rotate graphical output 90 deg left (only PXA LCD) +-vga [std|cirrus|vmware|xenfb|qxl|none] + select video card type +-full-screen start in full screen +-vnc display start a VNC server on display + +i386 target only: +-win2k-hack use it when installing Windows 2000 to avoid a disk full bug +-no-fd-bootchk disable boot signature checking for floppy disks +-no-acpi disable ACPI +-no-hpet disable HPET +-balloon none disable balloon device +-balloon virtio[,addr=str] + enable virtio balloon device (default) +-acpitable [sig=str][,rev=n][,oem_id=str][,oem_table_id=str][,oem_rev=n][,asl_compiler_id=str][,asl_compiler_rev=n][,data=file1[:file2]...] + ACPI table description +-smbios file=binary + Load SMBIOS entry from binary file +-smbios type=0[,vendor=str][,version=str][,date=str][,release=%d.%d] + Specify SMBIOS type 0 fields +-smbios type=1[,manufacturer=str][,product=str][,version=str][,serial=str] + [,uuid=uuid][,sku=str][,family=str] + Specify SMBIOS type 1 fields + +Network options: +-net nic[,vlan=n][,macaddr=mac][,model=type][,name=str][,addr=str][,vectors=v] + create a new Network Interface Card and connect it to VLAN 'n' +-net user[,vlan=n][,name=str][,net=addr[/mask]][,host=addr][,restrict=y|n] + [,hostname=host][,dhcpstart=addr][,dns=addr][,tftp=dir][,bootfile=f] + [,hostfwd=rule][,guestfwd=rule][,smb=dir[,smbserver=addr]] + connect the user mode network stack to VLAN 'n', configure its + DHCP server and enabled optional services +-net tap[,vlan=n][,name=str][,fd=h][,ifname=name][,script=file][,downscript=dfile][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h] + connect the host TAP network interface to VLAN 'n' and use the + network scripts 'file' (default=/etc/qemu-ifup) + and 'dfile' (default=/etc/qemu-ifdown); + use '[down]script=no' to disable script execution; + use 'fd=h' to connect to an already opened TAP interface + use 'sndbuf=nbytes' to limit the size of the send buffer; the + default of 'sndbuf=1048576' can be disabled using 'sndbuf=0' + use vnet_hdr=off to avoid enabling the IFF_VNET_HDR tap flag; use + vnet_hdr=on to make the lack of IFF_VNET_HDR support an error condition + use vhost=on to enable experimental in kernel accelerator + use 'vhostfd=h' to connect to an already opened vhost net device +-net socket[,vlan=n][,name=str][,fd=h][,listen=[host]:port][,connect=host:port] + connect the vlan 'n' to another VLAN using a socket connection +-net socket[,vlan=n][,name=str][,fd=h][,mcast=maddr:port] + connect the vlan 'n' to multicast maddr and port +-net dump[,vlan=n][,file=f][,len=n] + dump traffic on vlan 'n' to file 'f' (max n bytes per packet) +-net none use it alone to have zero network devices; if no -net option + is provided, the default is '-net nic -net user' +-netdev [user|tap|socket],id=str[,option][,option][,...] + +Character device options: +-chardev null,id=id +-chardev socket,id=id[,host=host],port=host[,to=to][,ipv4][,ipv6][,nodelay] + [,server][,nowait][,telnet] (tcp) +-chardev socket,id=id,path=path[,server][,nowait][,telnet] (unix) +-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr] + [,localport=localport][,ipv4][,ipv6] +-chardev msmouse,id=id +-chardev vc,id=id[[,width=width][,height=height]][[,cols=cols][,rows=rows]] +-chardev file,id=id,path=path +-chardev pipe,id=id,path=path +-chardev pty,id=id +-chardev stdio,id=id +-chardev tty,id=id,path=path +-chardev parport,id=id,path=path + +Bluetooth(R) options: +-bt hci,null dumb bluetooth HCI - doesn't respond to commands +-bt hci,host[:id] + use host's HCI with the given name +-bt hci[,vlan=n] + emulate a standard HCI in virtual scatternet 'n' +-bt vhci[,vlan=n] + add host computer to virtual scatternet 'n' using VHCI +-bt device:dev[,vlan=n] + emulate a bluetooth device 'dev' in scatternet 'n' + +Linux/Multiboot boot specific: +-kernel bzImage use 'bzImage' as kernel image +-append cmdline use 'cmdline' as kernel command line +-initrd file use 'file' as initial ram disk + +Debug/Expert options: +-serial dev redirect the serial port to char device 'dev' +-parallel dev redirect the parallel port to char device 'dev' +-monitor dev redirect the monitor to char device 'dev' +-qmp dev like -monitor but opens in 'control' mode. +-mon chardev=[name][,mode=readline|control][,default] +-pidfile file write PID to 'file' +-singlestep always run in singlestep mode +-S freeze CPU at startup (use 'c' to start execution) +-gdb dev wait for gdb connection on 'dev' +-s shorthand for -gdb tcp::1234 +-d item1,... output log to /tmp/qemu.log (use -d ? for a list of log items) +-hdachs c,h,s[,t] + force hard disk 0 physical geometry and the optional BIOS + translation (t=none or lba) (usually qemu can guess them) +-L path set the directory for the BIOS, VGA BIOS and keymaps +-bios file set the filename for the BIOS +-enable-kvm enable KVM full virtualization support +-no-reboot exit instead of rebooting +-no-shutdown stop before shutdown +-loadvm [tag|id] + start right away with a saved state (loadvm in monitor) +-daemonize daemonize QEMU after initializing +-option-rom rom load a file, rom, into the option ROM space +-clock force the use of the given methods for timer alarm. + To see what timers are available use -clock ? +-rtc [base=utc|localtime|date][,clock=host|vm][,driftfix=none|slew] + set the RTC base and clock, enable drift fix for clock ticks +-icount [N|auto] + enable virtual instruction counter with 2^N clock ticks per + instruction +-watchdog i6300esb|ib700 + enable virtual hardware watchdog [default=none] +-watchdog-action reset|shutdown|poweroff|pause|debug|none + action when watchdog fires [default=reset] +-echr chr set terminal escape character instead of ctrl-a +-virtioconsole c + set virtio console +-show-cursor show cursor +-tb-size n set TB size +-incoming p prepare for incoming migration, listen on port p +-nodefaults don't create default devices. +-chroot dir Chroot to dir just before starting the VM. +-runas user Change to user id user just before starting the VM. +-readconfig <file> +-writeconfig <file> + read/write config file +-no-kvm disable KVM hardware virtualization +-no-kvm-irqchip disable KVM kernel mode PIC/IOAPIC/LAPIC +-no-kvm-pit disable KVM kernel mode PIT +-no-kvm-pit-reinjection disable KVM kernel mode PIT interrupt reinjection +-pcidevice host=bus:dev.func[,dma=none][,name=string] + expose a PCI device to the guest OS. + dma=none: don't perform any dma translations (default is to use an iommu) + 'string' is used in log output. +-enable-nesting enable support for running a VM inside the VM (AMD only) +-nvram FILE provide ia64 nvram contents +-tdf enable guest time drift compensation +-kvm-shadow-memory MEGABYTES + allocate MEGABYTES for kvm mmu shadowing +-mem-path FILE provide backing storage for guest RAM +-mem-prealloc preallocate guest memory (use with -mempath) + +During emulation, the following keys are useful: +ctrl-alt-f toggle full screen +ctrl-alt-n switch to virtual console 'n' +ctrl-alt toggle mouse and keyboard grab + +When using -nographic, press 'ctrl-a h' to get some help. diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index b8c4b91..8dc49f0 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -245,6 +245,34 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_RTC | QEMUD_CMD_FLAG_NO_HPET, 12001, 0, 0); + DO_TEST("qemu-kvm-0.12.1-rhel6", + QEMUD_CMD_FLAG_VNC_COLON | + QEMUD_CMD_FLAG_NO_REBOOT | + QEMUD_CMD_FLAG_DRIVE | + QEMUD_CMD_FLAG_DRIVE_BOOT | + QEMUD_CMD_FLAG_NAME | + QEMUD_CMD_FLAG_UUID | + QEMUD_CMD_FLAG_VNET_HDR | + QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP | + QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC | + QEMUD_CMD_FLAG_DRIVE_CACHE_V2 | + QEMUD_CMD_FLAG_KVM | + QEMUD_CMD_FLAG_DRIVE_FORMAT | + QEMUD_CMD_FLAG_DRIVE_SERIAL | + QEMUD_CMD_FLAG_VGA | + QEMUD_CMD_FLAG_0_10 | + QEMUD_CMD_FLAG_PCIDEVICE | + QEMUD_CMD_FLAG_MEM_PATH | + QEMUD_CMD_FLAG_ENABLE_KVM | + QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX | + QEMUD_CMD_FLAG_CHARDEV | + QEMUD_CMD_FLAG_BALLOON | + QEMUD_CMD_FLAG_DEVICE | + QEMUD_CMD_FLAG_SMP_TOPOLOGY | + QEMUD_CMD_FLAG_RTC | + QEMUD_CMD_FLAG_VNET_HOST | + QEMUD_CMD_FLAG_VGA_QXL, + 12001, 1, 0); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args new file mode 100644 index 0000000..94cd90e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args @@ -0,0 +1 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml new file mode 100644 index 0000000..031a622 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml @@ -0,0 +1,27 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>1</vcpu> + <os> + <type arch='i686' machine='pc'>hvm</type> + <boot dev='hd'/> + </os> + <clock offset='utc'/> + <on_poweroff>destroy</on_poweroff> + <on_reboot>restart</on_reboot> + <on_crash>destroy</on_crash> + <devices> + <emulator>/usr/bin/qemu</emulator> + <disk type='block' device='disk'> + <source dev='/dev/HostVG/QEMUGuest1'/> + <target dev='hda' bus='ide'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <controller type='ide' index='0'/> + <video> + <model type='qxl' vram='65536' heads='1'/> + </video> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 9e4d5bf..2e960d1 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -14,6 +14,7 @@ # include "testutils.h" # include "qemu/qemu_conf.h" # include "datatypes.h" +# include "virterror_internal.h" # include "testutilsqemu.h" @@ -39,6 +40,8 @@ static int testCompareXMLToArgvFiles(const char *xml, virDomainChrDef monitor_chr; virConnectPtr conn; + virResetLastError(); + if (!(conn = virGetConnect())) goto fail; @@ -125,6 +128,9 @@ static int testCompareXMLToArgvFiles(const char *xml, ret = 0; fail: + if (ret != 0 && virGetLastError()) + virDispatchError(conn); + free(actualargv); if (argv) { tmp = argv; @@ -289,6 +295,10 @@ mymain(int argc, char **argv) DO_TEST("graphics-sdl", 0); DO_TEST("graphics-sdl-fullscreen", 0); DO_TEST("nographics-vga", QEMUD_CMD_FLAG_VGA); + DO_TEST("graphics-spice-rhel6", + QEMUD_CMD_FLAG_VGA | QEMUD_CMD_FLAG_VGA_QXL | + QEMUD_CMD_FLAG_DEVICE); + DO_TEST("input-usbmouse", 0); DO_TEST("input-usbtablet", 0); DO_TEST("input-xen", QEMUD_CMD_FLAG_DOMID); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 1ac6edc..60f60c7 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -110,6 +110,7 @@ mymain(int argc, char **argv) DO_TEST("graphics-vnc-tls"); DO_TEST("graphics-sdl"); DO_TEST("graphics-sdl-fullscreen"); + DO_TEST("graphics-spice-rhel6"); DO_TEST("input-usbmouse"); DO_TEST("input-usbtablet"); DO_TEST("input-xen"); -- 1.6.6.1

This supports the -spice argument posted for review against the latest upstream QEMU/KVM. This supports the bare minimum config with port, TLS port & listen address. The x509 bits are added in a later patch. * src/qemu_conf.c, src/qemu_conf.h: Add SPICE flag. Check for -spice availability. Format -spice arg for command line * qemuhelptest.c: Add SPICE flag * qemuxml2argvdata/qemuxml2argv-graphics-spice.args: Add <graphics> for spice * qemuxml2argvdata/qemuxml2argv-graphics-spice.xml: Add -spice arg * qemuxml2argvtest.c: Add SPICE flag --- src/qemu/qemu_conf.c | 36 ++++++++++++++++++++ src/qemu/qemu_conf.h | 1 + tests/qemuhelptest.c | 3 +- .../qemuxml2argv-graphics-spice-rhel6.args | 2 +- .../qemuxml2argv-graphics-spice-rhel6.xml | 2 + tests/qemuxml2argvtest.c | 2 +- 6 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index d59b0e9..e4a5985 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1149,6 +1149,8 @@ static unsigned long long qemudComputeCmdFlags(const char *help, if (strstr(help, "|qxl")) flags |= QEMUD_CMD_FLAG_VGA_QXL; } + if (strstr(help, "-spice")) + flags |= QEMUD_CMD_FLAG_SPICE; if (strstr(help, "boot=on")) flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; if (strstr(help, "serial=s")) @@ -4409,6 +4411,40 @@ int qemudBuildCommandLine(virConnectPtr conn, * default, since the default changes :-( */ if (qemuCmdFlags & QEMUD_CMD_FLAG_SDL) ADD_ARG_LIT("-sdl"); + } else if ((def->ngraphics == 1) && + def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + virBuffer opt = VIR_BUFFER_INITIALIZER; + char *optstr; + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_SPICE)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("spice graphics are not supported with this QEMU")); + goto error; + } + + virBufferVSprintf(&opt, "port=%u", def->graphics[0]->data.spice.port); + + if (def->graphics[0]->data.spice.tlsPort) + virBufferVSprintf(&opt, ",tls-port=%u", def->graphics[0]->data.spice.tlsPort); + + if (def->graphics[0]->data.spice.listenAddr) + virBufferVSprintf(&opt, ",addr=%s", def->graphics[0]->data.spice.listenAddr); + + if (virBufferError(&opt)) + goto no_memory; + + optstr = virBufferContentAndReset(&opt); + + ADD_ARG_LIT("-spice"); + ADD_ARG(optstr); + if (def->graphics[0]->data.spice.keymap) { + ADD_ARG_LIT("-k"); + ADD_ARG_LIT(def->graphics[0]->data.spice.keymap); + } + /* SPICE includes native support for tunnelling audio, so we + * set the audio backend to point at SPICE's own driver + */ + ADD_ENV_LIT("QEMU_AUDIO_DRV=spice"); } else if (def->ngraphics) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unsupported graphics output requested")); diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 5321955..2d169bd 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -89,6 +89,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection supported */ QEMUD_CMD_FLAG_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */ QEMUD_CMD_FLAG_VGA_QXL = (1LL << 36), /* The 'qxl' arg for '-vga' */ + QEMUD_CMD_FLAG_SPICE = (1LL << 37), /* Is -spice avail (RHEL-6 custom) */ }; /* Main driver state */ diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c index 8dc49f0..f57fc51 100644 --- a/tests/qemuhelptest.c +++ b/tests/qemuhelptest.c @@ -271,7 +271,8 @@ mymain(int argc, char **argv) QEMUD_CMD_FLAG_SMP_TOPOLOGY | QEMUD_CMD_FLAG_RTC | QEMUD_CMD_FLAG_VNET_HOST | - QEMUD_CMD_FLAG_VGA_QXL, + QEMUD_CMD_FLAG_VGA_QXL | + QEMUD_CMD_FLAG_SPICE, 12001, 1, 0); return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args index 94cd90e..8d195e5 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args @@ -1 +1 @@ -LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1 -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml index 031a622..08dfb26 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml @@ -20,6 +20,8 @@ <address type='drive' controller='0' bus='0' unit='0'/> </disk> <controller type='ide' index='0'/> + <input type='mouse' bus='ps2'/> + <graphics type='spice' port='5903' tlsPort='5904' autoport='no' listen='127.0.0.1'/> <video> <model type='qxl' vram='65536' heads='1'/> </video> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 2e960d1..5e46dca 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -297,7 +297,7 @@ mymain(int argc, char **argv) DO_TEST("nographics-vga", QEMUD_CMD_FLAG_VGA); DO_TEST("graphics-spice-rhel6", QEMUD_CMD_FLAG_VGA | QEMUD_CMD_FLAG_VGA_QXL | - QEMUD_CMD_FLAG_DEVICE); + QEMUD_CMD_FLAG_DEVICE | QEMUD_CMD_FLAG_SPICE); DO_TEST("input-usbmouse", 0); DO_TEST("input-usbtablet", 0); -- 1.6.6.1

* src/qemu/qemu_driver.c: Allocate the TCP ports for SPICE before starting guest --- src/qemu/qemu_driver.c | 36 ++++++++++++++++++++++++------------ 1 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index df1d435..2aa39c1 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2594,10 +2594,10 @@ qemuInitPCIAddresses(struct qemud_driver *driver, return ret; } -static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) { +static int qemudNextFreePort(int startPort) { int i; - for (i = 5900 ; i < 65535 ; i++) { + for (i = startPort ; i < 65535 ; i++) { int fd; int reuse = 1; struct sockaddr_in addr; @@ -3200,17 +3200,29 @@ static int qemudStartVMDaemon(virConnectPtr conn, DEBUG0("Ensuring no historical cgroup is lying around"); qemuRemoveCgroup(driver, vm, 1); - if ((vm->def->ngraphics == 1) && - vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - vm->def->graphics[0]->data.vnc.autoport) { - DEBUG0("Determining VNC port"); - int port = qemudNextFreeVNCPort(driver); - if (port < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Unable to find an unused VNC port")); - goto cleanup; + if (vm->def->ngraphics == 1) { + if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && + vm->def->graphics[0]->data.vnc.autoport) { + int port = qemudNextFreePort(5900); + if (port < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to find an unused VNC port")); + goto cleanup; + } + vm->def->graphics[0]->data.vnc.port = port; + } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && + vm->def->graphics[0]->data.spice.autoport) { + int port = qemudNextFreePort(5900); + int tlsPort = port == -1 ? -1 : qemudNextFreePort(port + 1); + if (port < 0 || tlsPort < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to find an unused SPICE ports")); + goto cleanup; + } + + vm->def->graphics[0]->data.spice.port = port; + vm->def->graphics[0]->data.spice.tlsPort = tlsPort; } - vm->def->graphics[0]->data.vnc.port = port; } if (virFileMakePath(driver->logDir) != 0) { -- 1.6.6.1

In common with VNC, the QEMU driver configuration file is used specify the host level TLS certificate location and a default password / listen address * src/qemu/qemu.conf: Add spice_listen, spice_tls, spice_tls_x509_cert_dir & spice_password config params * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Parsing of spice config parameters and updating -spice arg generation to use them * tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args, tests/qemuxml2argvtest.c: Expand test case to cover driver level configuration --- src/qemu/qemu.conf | 40 +++++++++++++ src/qemu/qemu_conf.c | 62 +++++++++++++++++++- src/qemu/qemu_conf.h | 4 + src/qemu/qemu_driver.c | 15 ++++- .../qemuxml2argv-graphics-spice-rhel6.args | 2 +- tests/qemuxml2argvtest.c | 7 ++- 6 files changed, 124 insertions(+), 6 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 3da332f..980dc8b 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -80,6 +80,46 @@ +# SPICE is configured to listen on 127.0.0.1 by default. +# To make it listen on all public interfaces, uncomment +# this next option. +# +# NB, strong recommendation to enable TLS + x509 certificate +# verification when allowing public access +# +# spice_listen = "0.0.0.0" + + +# Enable use of TLS encryption on the SPICE server. +# +# It is necessary to setup CA and issue a server certificate +# before enabling this. +# +# spice_tls = 1 + + +# Use of TLS requires that x509 certificates be issued. The +# default it to keep them in /etc/pki/libvirt-spice. This directory +# must contain +# +# ca-cert.pem - the CA master certificate +# server-cert.pem - the server certificate signed with ca-cert.pem +# server-key.pem - the server private key +# +# This option allows the certificate directory to be changed +# +# spice_tls_x509_cert_dir = "/etc/pki/libvirt-spice" + + +# The default SPICE password. This parameter is only used if the +# per-domain XML config does not already provide a password. To +# allow access without passwords, leave this commented out. An +# empty string will still enable passwords, but be rejected by +# QEMU effectively preventing any use of SPICE. Obviously change +# this example here before you set this +# +# spice_password = "XYZ12345" + # The default security driver is SELinux. If SELinux is disabled # on the host, then the security driver will automatically disable diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index e4a5985..ca251a3 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -115,6 +115,15 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, return -1; } + if (!(driver->spiceListen = strdup("127.0.0.1"))) { + virReportOOMError(); + return -1; + } + if (!(driver->spiceTLSx509certdir = strdup(SYSCONF_DIR "/pki/libvirt-spice"))) { + virReportOOMError(); + return -1; + } + #ifdef HAVE_MNTENT_H /* For privileged driver, try and find hugepage mount automatically. * Non-privileged driver requires admin to create a dir for the @@ -213,6 +222,43 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, } } + p = virConfGetValue (conf, "spice_tls"); + CHECK_TYPE ("spice_tls", VIR_CONF_LONG); + if (p) driver->spiceTLS = p->l; + + p = virConfGetValue (conf, "spice_tls_x509_cert_dir"); + CHECK_TYPE ("spice_tls_x509_cert_dir", VIR_CONF_STRING); + if (p && p->str) { + VIR_FREE(driver->spiceTLSx509certdir); + if (!(driver->spiceTLSx509certdir = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + + p = virConfGetValue (conf, "spice_listen"); + CHECK_TYPE ("spice_listen", VIR_CONF_STRING); + if (p && p->str) { + VIR_FREE(driver->spiceListen); + if (!(driver->spiceListen = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + + p = virConfGetValue (conf, "spice_password"); + CHECK_TYPE ("spice_password", VIR_CONF_STRING); + if (p && p->str) { + VIR_FREE(driver->spicePassword); + if (!(driver->spicePassword = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + p = virConfGetValue (conf, "user"); CHECK_TYPE ("user", VIR_CONF_STRING); if (!(user = strdup(p && p->str ? p->str : QEMU_USER))) { @@ -4424,11 +4470,25 @@ int qemudBuildCommandLine(virConnectPtr conn, virBufferVSprintf(&opt, "port=%u", def->graphics[0]->data.spice.port); - if (def->graphics[0]->data.spice.tlsPort) + if (driver->spiceTLS && def->graphics[0]->data.spice.tlsPort != -1) virBufferVSprintf(&opt, ",tls-port=%u", def->graphics[0]->data.spice.tlsPort); if (def->graphics[0]->data.spice.listenAddr) virBufferVSprintf(&opt, ",addr=%s", def->graphics[0]->data.spice.listenAddr); + else if (driver->spiceListen) + virBufferVSprintf(&opt, ",addr=%s", driver->spiceListen); + + /* In the password case we set it via monitor command, to avoid + * making it visible on CLI, so there's no use of password=XXX + * in this bit of the code */ + if (!def->graphics[0]->data.spice.passwd && + !driver->spicePassword) + virBufferAddLit(&opt, ",disable-ticketing"); + + if (driver->spiceTLS) + virBufferVSprintf(&opt, ",x509-dir=%s", + driver->spiceTLSx509certdir); + if (virBufferError(&opt)) goto no_memory; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 2d169bd..bf18102 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -131,6 +131,10 @@ struct qemud_driver { char *vncListen; char *vncPassword; char *vncSASLdir; + unsigned int spiceTLS : 1; + char *spiceTLSx509certdir; + char *spiceListen; + char *spicePassword; char *hugetlbfs_mount; char *hugepage_path; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2aa39c1..f290172 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3213,13 +3213,22 @@ static int qemudStartVMDaemon(virConnectPtr conn, } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && vm->def->graphics[0]->data.spice.autoport) { int port = qemudNextFreePort(5900); - int tlsPort = port == -1 ? -1 : qemudNextFreePort(port + 1); - if (port < 0 || tlsPort < 0) { + int tlsPort = -1; + if (port < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Unable to find an unused SPICE ports")); + "%s", _("Unable to find an unused SPICE port")); goto cleanup; } + if (driver->spiceTLS) { + tlsPort = qemudNextFreePort(port + 1); + if (tlsPort < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to find an unused SPICE TLS port")); + goto cleanup; + } + } + vm->def->graphics[0]->data.spice.port = port; vm->def->graphics[0]->data.spice.tlsPort = tlsPort; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args index 8d195e5..e412fdb 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args @@ -1 +1 @@ -LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1 -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 5e46dca..10bf4b1 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -198,8 +198,13 @@ mymain(int argc, char **argv) return EXIT_FAILURE; if ((driver.hugepage_path = strdup("/dev/hugepages/libvirt/qemu")) == NULL) return EXIT_FAILURE; + driver.spiceTLS = 1; + if (!(driver.spiceTLSx509certdir = strdup("/etc/pki/libvirt-spice"))) + return EXIT_FAILURE; + if (!(driver.spicePassword = strdup("123456"))) + return EXIT_FAILURE; -# define DO_TEST_FULL(name, extraFlags, migrateFrom) \ +# define DO_TEST_FULL(name, extraFlags, migrateFrom) \ do { \ const struct testInfo info = { name, extraFlags, migrateFrom }; \ if (virtTestRun("QEMU XML-2-ARGV " name, \ -- 1.6.6.1

This extends the XML syntax for <graphics> to allow a password expiry time to be set eg <graphics type='vnc' port='5900' autoport='yes' keymap='en-us' passwd='12345' passwdValidTo='2010-04-09T15:51:00'/> The timestamp is in UTC. * src/conf/domain_conf.h: Pull passwd out into separate struct virDomainGraphicsAuthDef to allow sharing between VNC & SPICE * src/conf/domain_conf.c: Add parsing/formatting of new passwdValidTo argument * src/opennebula/one_conf.c, src/qemu/qemu_conf.c, src/qemu/qemu_driver.c, src/xen/xend_internal.c, src/xen/xm_internal.c: Update for changed struct containing VNC password --- src/conf/domain_conf.c | 102 +++++++++++++++++++++++++++++++++++++++----- src/conf/domain_conf.h | 13 +++++- src/esx/esx_vmx.c | 6 +- src/opennebula/one_conf.c | 4 +- src/qemu/qemu_conf.c | 4 +- src/qemu/qemu_driver.c | 20 ++++---- src/xen/xend_internal.c | 12 +++--- src/xen/xm_internal.c | 12 +++--- 8 files changed, 130 insertions(+), 43 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index b25c622..eee78b1 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -374,6 +374,17 @@ virDomainObjPtr virDomainFindByName(const virDomainObjListPtr doms, #endif /* !PROXY */ +static void +virDomainGraphicsAuthDefClear(virDomainGraphicsAuthDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->passwd); + + /* Don't free def */ +} + void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) { if (!def) @@ -383,7 +394,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) case VIR_DOMAIN_GRAPHICS_TYPE_VNC: VIR_FREE(def->data.vnc.listenAddr); VIR_FREE(def->data.vnc.keymap); - VIR_FREE(def->data.vnc.passwd); + virDomainGraphicsAuthDefClear(&def->data.vnc.auth); break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: @@ -402,7 +413,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: VIR_FREE(def->data.spice.listenAddr); VIR_FREE(def->data.spice.keymap); - VIR_FREE(def->data.spice.passwd); + virDomainGraphicsAuthDefClear(&def->data.spice.auth); break; } @@ -2715,6 +2726,56 @@ error: goto cleanup; } + +static int +virDomainGraphicsAuthDefParseXML(xmlNodePtr node, virDomainGraphicsAuthDefPtr def) +{ + char *validTo = NULL; + + def->passwd = virXMLPropString(node, "passwd"); + + if (!def->passwd) + return 0; + + validTo = virXMLPropString(node, "passwdValidTo"); + if (validTo) { + char *tmp; + struct tm tm; + memset(&tm, 0, sizeof(tm)); + /* Expect: YYYY-MM-DDTHH:MM:SS (%d-%d-%dT%d:%d:%d) eg 2010-11-28T14:29:01 */ + if (/* year */ + virStrToLong_i(validTo, &tmp, 10, &tm.tm_year) < 0 || *tmp != '-' || + /* month */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_mon) < 0 || *tmp != '-' || + /* day */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_mday) < 0 || *tmp != 'T' || + /* hour */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_hour) < 0 || *tmp != ':' || + /* minute */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_min) < 0 || *tmp != ':' || + /* second */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_sec) < 0 || *tmp != '\0') { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse password validity time '%s', expect YYYY-MM-DDTHH:MM:SS"), + validTo); + VIR_FREE(validTo); + VIR_FREE(def->passwd); + return -1; + } + VIR_FREE(validTo); + + tm.tm_year -= 1900; /* Human epoch starts at 0 BC, not 1900BC */ + tm.tm_mon--; /* Humans start months at 1, computers at 0 */ + + /* XXX this is broken it needs to be UTC not localtime */ + def->validTo = timegm(&tm); + def->expires = 1; + } + + return 0; +} + + /* Parse the XML definition for a graphics device */ static virDomainGraphicsDefPtr virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { @@ -2773,8 +2834,10 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { } def->data.vnc.listenAddr = virXMLPropString(node, "listen"); - def->data.vnc.passwd = virXMLPropString(node, "passwd"); def->data.vnc.keymap = virXMLPropString(node, "keymap"); + + if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0) + goto error; } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { char *fullscreen = virXMLPropString(node, "fullscreen"); @@ -2898,8 +2961,9 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { } def->data.spice.listenAddr = virXMLPropString(node, "listen"); - def->data.spice.passwd = virXMLPropString(node, "passwd"); def->data.spice.keymap = virXMLPropString(node, "keymap"); + if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0) + goto error; } cleanup: @@ -5618,6 +5682,24 @@ virDomainTimerDefFormat(virBufferPtr buf, return 0; } +static void +virDomainGraphicsAuthDefFormatAttr(virBufferPtr buf, + virDomainGraphicsAuthDefPtr def) +{ + if (!def->passwd) + return; + + virBufferEscapeString(buf, " passwd='%s'", + def->passwd); + if (def->expires) { + char strbuf[100]; + struct tm tmbuf, *tm; + tm = gmtime_r(&def->validTo, &tmbuf); + strftime(strbuf, sizeof(strbuf), "%Y-%m-%dT%H:%M:%S", tm); + virBufferVSprintf(buf, " passwdValidTo='%s'", strbuf); + } +} + static int virDomainGraphicsDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def, @@ -5653,10 +5735,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virBufferEscapeString(buf, " keymap='%s'", def->data.vnc.keymap); - if (def->data.vnc.passwd && - (flags & VIR_DOMAIN_XML_SECURE)) - virBufferEscapeString(buf, " passwd='%s'", - def->data.vnc.passwd); + if (flags & VIR_DOMAIN_XML_SECURE) + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.vnc.auth); break; @@ -5724,10 +5804,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virBufferEscapeString(buf, " keymap='%s'", def->data.spice.keymap); - if (def->data.spice.passwd && - (flags & VIR_DOMAIN_XML_SECURE)) - virBufferEscapeString(buf, " passwd='%s'", - def->data.spice.passwd); + if (flags & VIR_DOMAIN_XML_SECURE) + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.spice.auth); break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index cda1d98..bbd7e84 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -477,6 +477,15 @@ enum virDomainGraphicsType { VIR_DOMAIN_GRAPHICS_TYPE_LAST, }; +typedef struct _virDomainGraphicsAuthDef virDomainGraphicsAuthDef; +typedef virDomainGraphicsAuthDef *virDomainGraphicsAuthDefPtr; +struct _virDomainGraphicsAuthDef { + char *passwd; + unsigned int expires: 1; /* Whether there is an expiry time set */ + time_t validTo; /* seconds since epoch */ +}; + + typedef struct _virDomainGraphicsDef virDomainGraphicsDef; typedef virDomainGraphicsDef *virDomainGraphicsDefPtr; struct _virDomainGraphicsDef { @@ -487,7 +496,7 @@ struct _virDomainGraphicsDef { unsigned int autoport :1; char *listenAddr; char *keymap; - char *passwd; + virDomainGraphicsAuthDef auth; } vnc; struct { char *display; @@ -510,7 +519,7 @@ struct _virDomainGraphicsDef { int tlsPort; char *listenAddr; char *keymap; - char *passwd; + virDomainGraphicsAuthDef auth; unsigned int autoport :1; } spice; } data; diff --git a/src/esx/esx_vmx.c b/src/esx/esx_vmx.c index c965e00..f7de901 100644 --- a/src/esx/esx_vmx.c +++ b/src/esx/esx_vmx.c @@ -1201,7 +1201,7 @@ esxVMX_ParseVNC(virConfPtr conf, virDomainGraphicsDefPtr *def) esxUtil_GetConfigString(conf, "RemoteDisplay.vnc.keymap", &(*def)->data.vnc.keymap, 1) < 0 || esxUtil_GetConfigString(conf, "RemoteDisplay.vnc.password", - &(*def)->data.vnc.passwd, 1) < 0) { + &(*def)->data.vnc.auth.passwd, 1) < 0) { goto failure; } @@ -2469,9 +2469,9 @@ esxVMX_FormatVNC(virDomainGraphicsDefPtr def, virBufferPtr buffer) def->data.vnc.keymap); } - if (def->data.vnc.passwd != NULL) { + if (def->data.vnc.auth.passwd != NULL) { virBufferVSprintf(buffer, "RemoteDisplay.vnc.password = \"%s\"\n", - def->data.vnc.passwd); + def->data.vnc.auth.passwd); } return 0; diff --git a/src/opennebula/one_conf.c b/src/opennebula/one_conf.c index 029d475..b76a1ac 100644 --- a/src/opennebula/one_conf.c +++ b/src/opennebula/one_conf.c @@ -259,9 +259,9 @@ char* xmlOneTemplate(virDomainDefPtr def) virBufferVSprintf(&buf,",\n port = \"%d\"", def->graphics[i]->data.vnc.port); - if (def->graphics[i]->data.vnc.passwd != NULL) + if (def->graphics[i]->data.vnc.auth.passwd != NULL) virBufferVSprintf(&buf,",\n passwd = \"%s\"", - def->graphics[i]->data.vnc.passwd); + def->graphics[i]->data.vnc.auth.passwd); virBufferAddLit(&buf," ]\n"); diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index ca251a3..f4a6c99 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4375,7 +4375,7 @@ int qemudBuildCommandLine(virConnectPtr conn, virBufferVSprintf(&opt, ":%d", def->graphics[0]->data.vnc.port - 5900); - if (def->graphics[0]->data.vnc.passwd || + if (def->graphics[0]->data.vnc.auth.passwd || driver->vncPassword) virBufferAddLit(&opt, ",password"); @@ -4481,7 +4481,7 @@ int qemudBuildCommandLine(virConnectPtr conn, /* In the password case we set it via monitor command, to avoid * making it visible on CLI, so there's no use of password=XXX * in this bit of the code */ - if (!def->graphics[0]->data.spice.passwd && + if (!def->graphics[0]->data.spice.auth.passwd && !driver->spicePassword) virBufferAddLit(&opt, ",disable-ticketing"); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f290172..013d85d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2191,12 +2191,12 @@ qemuInitPasswords(virConnectPtr conn, if ((vm->def->ngraphics == 1) && vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - (vm->def->graphics[0]->data.vnc.passwd || driver->vncPassword)) { + (vm->def->graphics[0]->data.vnc.auth.passwd || driver->vncPassword)) { qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorSetVNCPassword(priv->mon, - vm->def->graphics[0]->data.vnc.passwd ? - vm->def->graphics[0]->data.vnc.passwd : + vm->def->graphics[0]->data.vnc.auth.passwd ? + vm->def->graphics[0]->data.vnc.auth.passwd : driver->vncPassword); qemuDomainObjExitMonitorWithDriver(driver, vm); } @@ -7611,19 +7611,19 @@ qemuDomainChangeGraphics(struct qemud_driver *driver, return -1; } - if (STRNEQ_NULLABLE(olddev->data.vnc.passwd, dev->data.vnc.passwd)) { - VIR_DEBUG("Updating password on VNC server %p %p", dev->data.vnc.passwd, driver->vncPassword); + if (STRNEQ_NULLABLE(olddev->data.vnc.auth.passwd, dev->data.vnc.auth.passwd)) { + VIR_DEBUG("Updating password on VNC server %p %p", dev->data.vnc.auth.passwd, driver->vncPassword); qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorSetVNCPassword(priv->mon, - dev->data.vnc.passwd ? - dev->data.vnc.passwd : + dev->data.vnc.auth.passwd ? + dev->data.vnc.auth.passwd : driver->vncPassword); qemuDomainObjExitMonitorWithDriver(driver, vm); /* Steal the new dev's char * reference */ - VIR_FREE(olddev->data.vnc.passwd); - olddev->data.vnc.passwd = dev->data.vnc.passwd; - dev->data.vnc.passwd = NULL; + VIR_FREE(olddev->data.vnc.auth.passwd); + olddev->data.vnc.auth.passwd = dev->data.vnc.auth.passwd; + dev->data.vnc.auth.passwd = NULL; } else { ret = 0; } diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c index c4e73b7..b7138fe 100644 --- a/src/xen/xend_internal.c +++ b/src/xen/xend_internal.c @@ -2007,7 +2007,7 @@ xenDaemonParseSxprGraphicsOld(virConnectPtr conn, goto no_memory; if (vncPasswd && - !(graphics->data.vnc.passwd = strdup(vncPasswd))) + !(graphics->data.vnc.auth.passwd = strdup(vncPasswd))) goto no_memory; if (keymap && @@ -2129,7 +2129,7 @@ xenDaemonParseSxprGraphicsNew(virConnectPtr conn, goto no_memory; if (vncPasswd && - !(graphics->data.vnc.passwd = strdup(vncPasswd))) + !(graphics->data.vnc.auth.passwd = strdup(vncPasswd))) goto no_memory; if (keymap && @@ -5336,8 +5336,8 @@ xenDaemonFormatSxprGraphicsNew(virDomainGraphicsDefPtr def, if (def->data.vnc.listenAddr) virBufferVSprintf(buf, "(vnclisten '%s')", def->data.vnc.listenAddr); - if (def->data.vnc.passwd) - virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.passwd); + if (def->data.vnc.auth.passwd) + virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.auth.passwd); if (def->data.vnc.keymap) virBufferVSprintf(buf, "(keymap '%s')", def->data.vnc.keymap); } @@ -5379,8 +5379,8 @@ xenDaemonFormatSxprGraphicsOld(virDomainGraphicsDefPtr def, if (def->data.vnc.listenAddr) virBufferVSprintf(buf, "(vnclisten '%s')", def->data.vnc.listenAddr); - if (def->data.vnc.passwd) - virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.passwd); + if (def->data.vnc.auth.passwd) + virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.auth.passwd); if (def->data.vnc.keymap) virBufferVSprintf(buf, "(keymap '%s')", def->data.vnc.keymap); diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c index a7a09a0..554fe40 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -1298,7 +1298,7 @@ xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { } if (xenXMConfigCopyStringOpt(conf, "vnclisten", &graphics->data.vnc.listenAddr) < 0) goto cleanup; - if (xenXMConfigCopyStringOpt(conf, "vncpasswd", &graphics->data.vnc.passwd) < 0) + if (xenXMConfigCopyStringOpt(conf, "vncpasswd", &graphics->data.vnc.auth.passwd) < 0) goto cleanup; if (xenXMConfigCopyStringOpt(conf, "keymap", &graphics->data.vnc.keymap) < 0) goto cleanup; @@ -1370,7 +1370,7 @@ xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { if (!(graphics->data.vnc.listenAddr = strdup(key + 10))) goto no_memory; } else if (STRPREFIX(key, "vncpasswd=")) { - if (!(graphics->data.vnc.passwd = strdup(key + 10))) + if (!(graphics->data.vnc.auth.passwd = strdup(key + 10))) goto no_memory; } else if (STRPREFIX(key, "keymap=")) { if (!(graphics->data.vnc.keymap = strdup(key + 7))) @@ -2436,9 +2436,9 @@ virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, xenXMConfigSetString(conf, "vnclisten", def->graphics[0]->data.vnc.listenAddr) < 0) goto no_memory; - if (def->graphics[0]->data.vnc.passwd && + if (def->graphics[0]->data.vnc.auth.passwd && xenXMConfigSetString(conf, "vncpasswd", - def->graphics[0]->data.vnc.passwd) < 0) + def->graphics[0]->data.vnc.auth.passwd) < 0) goto no_memory; if (def->graphics[0]->data.vnc.keymap && xenXMConfigSetString(conf, "keymap", @@ -2467,9 +2467,9 @@ virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, if (def->graphics[0]->data.vnc.listenAddr) virBufferVSprintf(&buf, ",vnclisten=%s", def->graphics[0]->data.vnc.listenAddr); - if (def->graphics[0]->data.vnc.passwd) + if (def->graphics[0]->data.vnc.auth.passwd) virBufferVSprintf(&buf, ",vncpasswd=%s", - def->graphics[0]->data.vnc.passwd); + def->graphics[0]->data.vnc.auth.passwd); if (def->graphics[0]->data.vnc.keymap) virBufferVSprintf(&buf, ",keymap=%s", def->graphics[0]->data.vnc.keymap); -- 1.6.6.1

QEMU crashes & burns if you try multiple Cirrus video cards, but QXL copes fine. Adapt QEMU config code to allow multiple QXL video cards * src/qemu/qemu_conf.c: Support multiple QXL video cards --- src/qemu/qemu_conf.c | 64 +++++++++++++++++-- .../qemuxml2argv-graphics-spice-rhel6.args | 2 +- .../qemuxml2argv-graphics-spice-rhel6.xml | 3 + 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index f4a6c99..78778ef 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -2922,6 +2922,36 @@ error: } +static char * +qemuBuildVideoDevStr(virDomainVideoDefPtr video) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *model = qemuVideoTypeToString(video->type); + + if (!model) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("invalid video model")); + goto error; + } + + virBufferVSprintf(&buf, "%s", model); + virBufferVSprintf(&buf, ",id=%s", video->info.alias); + if (qemuBuildDeviceAddressStr(&buf, &video->info) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev) { @@ -4510,13 +4540,7 @@ int qemudBuildCommandLine(virConnectPtr conn, "%s", _("unsupported graphics output requested")); } - if (def->nvideos) { - if (def->nvideos > 1) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("only one video card is currently supported")); - goto error; - } - + if (def->nvideos > 0) { if (qemuCmdFlags & QEMUD_CMD_FLAG_VGA) { if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_XEN) { /* nothing - vga has no effect on Xen pvfb */ @@ -4562,6 +4586,32 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } } + + if (def->nvideos > 1) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + for (i = 1 ; i < def->nvideos ; i++) { + char *str; + if (def->videos[i]->type != VIR_DOMAIN_VIDEO_TYPE_QXL) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("video type %s is only valid as primary video card"), + virDomainVideoTypeToString(def->videos[0]->type)); + goto error; + } + + ADD_ARG_LIT("-device"); + + if (!(str = qemuBuildVideoDevStr(def->videos[i]))) + goto error; + + ADD_ARG(str); + } + } else { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("only one video card is currently supported")); + goto error; + } + } + } else { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args index e412fdb..44809b0 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args @@ -1 +1 @@ -LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice -vga qxl -device qxl,id=video1,bus=pci.0,addr=0x4 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml index 08dfb26..6fe9a60 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml @@ -25,5 +25,8 @@ <video> <model type='qxl' vram='65536' heads='1'/> </video> + <video> + <model type='qxl' vram='65536' heads='1'/> + </video> </devices> </domain> -- 1.6.6.1

This extends the SPICE XML to allow channel security options <graphics type='spice' port='5901' tlsPort='-1' autoport='yes'> <channel name='main' mode='secure'/> <channel name='record' mode='insecure'/> </graphics> Any non-specified channel uses the default, which allows both secure & insecure usage * src/conf/domain_conf.c, src/conf/domain_conf.h, src/libvirt_private.syms: Add XML syntax for specifying per channel security options for spice;. * src/qemu/qemu_conf.c: Configure channel security with spice --- docs/schemas/domain.rng | 21 ++++++ src/conf/domain_conf.c | 75 +++++++++++++++++++- src/conf/domain_conf.h | 21 ++++++ src/libvirt_private.syms | 4 + src/qemu/qemu_conf.c | 13 ++++ .../qemuxml2argv-graphics-spice-rhel6.args | 2 +- .../qemuxml2argv-graphics-spice-rhel6.xml | 5 +- 7 files changed, 138 insertions(+), 3 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index edd0b7c..17d7b88 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1012,6 +1012,27 @@ <text/> </attribute> </optional> + <zeroOrMore> + <element name="channel"> + <attribute name="name"> + <choice> + <value>main</value> + <value>display</value> + <value>inputs</value> + <value>cursor</value> + <value>playback</value> + <value>record</value> + </choice> + </attribute> + <attribute name="mode"> + <choice> + <value>any</value> + <value>secure</value> + <value>insecure</value> + </choice> + </attribute> + </element> + </zeroOrMore> </group> <group> <attribute name="type"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index eee78b1..ffc446f 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -218,6 +218,21 @@ VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST, "desktop", "spice") +VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST, + "main", + "display", + "inputs", + "cursor", + "playback", + "record"); + +VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelMode, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_LAST, + "any", + "secure", + "insecure"); + VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST, "subsystem", "capabilities") @@ -2920,6 +2935,7 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { def->data.desktop.display = virXMLPropString(node, "display"); } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + xmlNodePtr cur; char *port = virXMLPropString(node, "port"); char *tlsPort; char *autoport; @@ -2964,6 +2980,40 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { def->data.spice.keymap = virXMLPropString(node, "keymap"); if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0) goto error; + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "channel")) { + const char *name, *mode; + int nameval, modeval; + name = virXMLPropString(cur, "name"); + mode = virXMLPropString(cur, "mode"); + + if (!name || !mode) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("spice channel missing name/mode")); + goto error; + } + + if ((nameval = virDomainGraphicsSpiceChannelNameTypeFromString(name)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown spice channel name %s"), + name); + goto error; + } + if ((modeval = virDomainGraphicsSpiceChannelModeTypeFromString(mode)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown spice channel mode %s"), + mode); + goto error; + } + + def->data.spice.channels[nameval] = modeval; + } + } + cur = cur->next; + } } cleanup: @@ -5706,6 +5756,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, int flags) { const char *type = virDomainGraphicsTypeToString(def->type); + int children = 0; + int i; if (!type) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -5811,7 +5863,28 @@ virDomainGraphicsDefFormat(virBufferPtr buf, } - virBufferAddLit(buf, "/>\n"); + if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) { + int mode = def->data.spice.channels[i]; + if (mode == VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY) + continue; + + if (!children) { + virBufferAddLit(buf, ">\n"); + children = 1; + } + + virBufferVSprintf(buf, " <channel name='%s' mode='%s'/>\n", + virDomainGraphicsSpiceChannelNameTypeToString(i), + virDomainGraphicsSpiceChannelModeTypeToString(mode)); + } + } + + if (children) { + virBufferAddLit(buf, " </graphics>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } return 0; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index bbd7e84..d7ea1e0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -485,6 +485,24 @@ struct _virDomainGraphicsAuthDef { time_t validTo; /* seconds since epoch */ }; +enum virDomainGraphicsSpiceChannelName { + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MAIN, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_DISPLAY, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_INPUT, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_CURSOR, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_PLAYBACK, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_RECORD, + + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST +}; + +enum virDomainGraphicsSpiceChannelMode { + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE, + + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_LAST +}; typedef struct _virDomainGraphicsDef virDomainGraphicsDef; typedef virDomainGraphicsDef *virDomainGraphicsDefPtr; @@ -521,6 +539,7 @@ struct _virDomainGraphicsDef { char *keymap; virDomainGraphicsAuthDef auth; unsigned int autoport :1; + int channels[VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST]; } spice; } data; }; @@ -1103,6 +1122,8 @@ VIR_ENUM_DECL(virDomainHostdevSubsys) VIR_ENUM_DECL(virDomainInput) VIR_ENUM_DECL(virDomainInputBus) VIR_ENUM_DECL(virDomainGraphics) +VIR_ENUM_DECL(virDomainGraphicsSpiceChannelName) +VIR_ENUM_DECL(virDomainGraphicsSpiceChannelMode) /* from libvirt.h */ VIR_ENUM_DECL(virDomainState) VIR_ENUM_DECL(virDomainSeclabel) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 7950bcd..f1a91f7 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -146,6 +146,10 @@ virDomainGetRootFilesystem; virDomainGraphicsTypeFromString; virDomainGraphicsTypeToString; virDomainGraphicsDefFree; +virDomainGraphicsSpiceChannelNameTypeFromString; +virDomainGraphicsSpiceChannelNameTypeToString; +virDomainGraphicsSpiceChannelModeTypeFromString; +virDomainGraphicsSpiceChannelModeTypeToString; virDomainHostdevDefFree; virDomainHostdevModeTypeToString; virDomainHostdevSubsysTypeToString; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 78778ef..31214b1 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4519,6 +4519,19 @@ int qemudBuildCommandLine(virConnectPtr conn, virBufferVSprintf(&opt, ",x509-dir=%s", driver->spiceTLSx509certdir); + for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) { + int mode = def->graphics[0]->data.spice.channels[i]; + switch (mode) { + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE: + virBufferVSprintf(&opt, ",tls-channel=%s", + virDomainGraphicsSpiceChannelNameTypeToString(i)); + break; + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE: + virBufferVSprintf(&opt, ",plaintext-channel=%s", + virDomainGraphicsSpiceChannelNameTypeToString(i)); + break; + } + } if (virBufferError(&opt)) goto no_memory; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args index 44809b0..87b8c06 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args @@ -1 +1 @@ -LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice -vga qxl -device qxl,id=video1,bus=pci.0,addr=0x4 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice,tls-channel=main,plaintext-channel=inputs -vga qxl -device qxl,id=video1,bus=pci.0,addr=0x4 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml index 6fe9a60..bdce04b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.xml @@ -21,7 +21,10 @@ </disk> <controller type='ide' index='0'/> <input type='mouse' bus='ps2'/> - <graphics type='spice' port='5903' tlsPort='5904' autoport='no' listen='127.0.0.1'/> + <graphics type='spice' port='5903' tlsPort='5904' autoport='no' listen='127.0.0.1'> + <channel name='main' mode='secure'/> + <channel name='inputs' mode='insecure'/> + </graphics> <video> <model type='qxl' vram='65536' heads='1'/> </video> -- 1.6.6.1

On Wed, Apr 14, 2010 at 11:33:18AM +0100, Daniel P. Berrange wrote:
Gerd today posted patches to upstream QEMU which support SPICE graphics. They have not been accepted / merged yet, but I'm optimistic that the command line syntax will not change significantly.
http://lists.gnu.org/archive/html/qemu-devel/2010-04/msg00967.html
yes that's a good progress sign, hopefully it will get through without too much redesign of the commands.
This patch series adds corresponding support in libvirt's QEMU driver. Although this patch series adds support in the XML RNG for a password expiry time, this functionality isn't wired up in this patch series since I need more time to write fallback code for cases where QEMU doesn't do expiry itself.
The patches look good, I think we should apply 3/10 now, but wait for the commit in QEmu upstream (or the release to be 100% sure) before pushing the rest, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Hi, I want to test your SPICE patch. Where can I get it? Thanks a lot. BR Bitman Zhou 在 2010-04-14三的 11:33 +0100,Daniel P. Berrange写道:
Gerd today posted patches to upstream QEMU which support SPICE graphics. They have not been accepted / merged yet, but I'm optimistic that the command line syntax will not change significantly.
http://lists.gnu.org/archive/html/qemu-devel/2010-04/msg00967.html
This patch series adds corresponding support in libvirt's QEMU driver. Although this patch series adds support in the XML RNG for a password expiry time, this functionality isn't wired up in this patch series since I need more time to write fallback code for cases where QEMU doesn't do expiry itself.
Daniel
-- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list

On Mon, May 24, 2010 at 12:18:57PM +0800, Bitman Zhou wrote:
Hi,
I want to test your SPICE patch. Where can I get it? Thanks a lot.
Best bet if you want to test SPICE bits, is to pull from my personal staging tree which hsa the SPICE bits from RHEL6 http://gitorious.org/~berrange/libvirt/staging/commits/rhel6-addons
??? 2010-04-14?????? 11:33 +0100???Daniel P. Berrange?????????
Gerd today posted patches to upstream QEMU which support SPICE graphics. They have not been accepted / merged yet, but I'm optimistic that the command line syntax will not change significantly.
http://lists.gnu.org/archive/html/qemu-devel/2010-04/msg00967.html
This patch series adds corresponding support in libvirt's QEMU driver. Although this patch series adds support in the XML RNG for a password expiry time, this functionality isn't wired up in this patch series since I need more time to write fallback code for cases where QEMU doesn't do expiry itself.
Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|
participants (3)
-
Bitman Zhou
-
Daniel P. Berrange
-
Daniel Veillard