[libvirt] [PATCH 0/9] Core support for SPICE configuration (v2)

A version 2 of http://www.redhat.com/archives/libvir-list/2010-November/msg00049.html This wires up the core support for SPICE configuration and QXL graphics devices. It also introduces a syntax for setting password expiry times for VNC/SPICE servers, although this isn't yet supported in upstream QEMU. Also not yet included here is seemless migration of SPICE clients, which is also pending QEMU work. The main changes in v2 are adding docs and fixing the minor issues that Eric found. 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/formatdomain.html.in | 3 ++- docs/schemas/domain.rng | 1 + src/conf/domain_conf.c | 3 ++- src/conf/domain_conf.h | 1 + src/qemu/qemu_conf.c | 3 ++- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 96de121..dcb7af8 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1154,7 +1154,8 @@ qemu-kvm -net nic,model=? /dev/null <dt><code>model</code></dt> <dd> The <code>model</code> element has a mandatory <code>type</code> - attribute which takes the value "vga", "cirrus", "vmvga", "xen" or "vbox". + attribute which takes the value "vga", "cirrus", "vmvga", "qxl", + "xen" or "vbox", depending on the hypervisor features available. You can also provide the amount of video memory in kilobytes using <code>vram</code> and the number of screen with <code>heads</code>. </dd> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 520bdd4..16fc444 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1169,6 +1169,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 9f3c08e..7ad6901 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -252,7 +252,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 8e32c3b..028b33a 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -482,6 +482,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 7d2a1d6..d96ff82 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.7.2.3

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/formatdomain.html.in | 12 +++++++ docs/schemas/domain.rng | 38 +++++++++++++++++++++ src/conf/domain_conf.c | 80 ++++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 9 +++++ src/qemu/qemu_conf.c | 2 +- 5 files changed, 139 insertions(+), 2 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index dcb7af8..e6a8162 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1104,6 +1104,18 @@ qemu-kvm -net nic,model=? /dev/null in clear text. The <code>keymap</code> attribute specifies the keymap to use. </dd> + <dt><code>"spice"</code></dt> + <dd> + Starts a SPICE server. The <code>port</code> attribute specifies the TCP + port number (with -1 as legacy syntax indicating that it should be + auto-allocated), while <code>tlsPort</code> gives an alternative + secure port number. The <code>autoport</code> attribute is the new + preferred syntax for indicating autoallocation of both port numbers. + The <code>listen</code> attribute is an IP address for the server to + listen on. The <code>passwd</code> attribute provides a SPICE password + in clear text. The <code>keymap</code> attribute specifies the keymap + to use. + </dd> <dt><code>"rdp"</code></dt> <dd> Starts a RDP server. The <code>port</code> attribute diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 16fc444..7903000 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1096,6 +1096,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 7ad6901..04a4185 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -268,7 +268,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", @@ -455,6 +456,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); @@ -3214,6 +3221,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: @@ -6531,6 +6582,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 028b33a..d519cb0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -512,6 +512,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, }; @@ -544,6 +545,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 d96ff82..104e874 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -5072,7 +5072,7 @@ int qemudBuildCommandLine(virConnectPtr conn, 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.7.2.3

On 11/08/2010 12:27 PM, Daniel P. Berrange wrote:
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
@@ -3214,6 +3221,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; + }
Still missing validation that data.spice.port got assigned something less than 0x10000. But I'm not sure it should hold up the series.
@@ -544,6 +545,14 @@ struct _virDomainGraphicsDef { char *display; unsigned int fullscreen :1; } desktop; + struct { + int port; + int tlsPort; + char *listenAddr;
No response to my question in 3/10 about whether we should use virSocketAddr instead of a raw string? https://www.redhat.com/archives/libvir-list/2010-November/msg00154.html But still something that could be added as a later patch. What you have is not inherently wrong, so: ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Nov 08, 2010 at 04:20:51PM -0700, Eric Blake wrote:
On 11/08/2010 12:27 PM, Daniel P. Berrange wrote:
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
@@ -3214,6 +3221,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; + }
Still missing validation that data.spice.port got assigned something less than 0x10000. But I'm not sure it should hold up the series.
@@ -544,6 +545,14 @@ struct _virDomainGraphicsDef { char *display; unsigned int fullscreen :1; } desktop; + struct { + int port; + int tlsPort; + char *listenAddr;
No response to my question in 3/10 about whether we should use virSocketAddr instead of a raw string? https://www.redhat.com/archives/libvir-list/2010-November/msg00154.html
Sorry, I meant to reply to that patch. Basically my goal was to make the SPICE stuff look the same as the VNC stuff. All the issues you raise apply to the VNC stuff, and even to other parts of the domain_conf.h like chardevs. So we mass port to virSocketAddr would be nicer. 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 :|

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/qemuxml2argvtest.c, tests/qemuxml2xmltest.c, tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args, tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml: Test case for generating spice args with RHEL6 kvm --- src/qemu/qemu_conf.c | 15 +++++++++- src/qemu/qemu_conf.h | 1 + .../qemuxml2argv-graphics-spice.args | 1 + .../qemuxml2argv-graphics-spice.xml | 27 ++++++++++++++++++++ tests/qemuxml2argvtest.c | 4 +++ tests/qemuxml2xmltest.c | 1 + 6 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 104e874..45af71f 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) { @@ -1202,8 +1202,12 @@ static unsigned long long qemudComputeCmdFlags(const char *help, if (strstr(help, "readonly=")) flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; } - 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")) @@ -5081,6 +5085,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 2b143e6..60c4c23 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -98,6 +98,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_NAME_PROCESS = (1LL << 42), /* Is -name process= available */ QEMUD_CMD_FLAG_DRIVE_READONLY= (1LL << 43), /* -drive readonly=on|off */ QEMUD_CMD_FLAG_SMBIOS_TYPE = (1LL << 44), /* Is -smbios type= available */ + QEMUD_CMD_FLAG_VGA_QXL = (1LL << 45), /* The 'qxl' arg for '-vga' */ }; /* Main driver state */ diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args new file mode 100644 index 0000000..94cd90e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml new file mode 100644 index 0000000..031a622 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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 7040aee..18f9dfe 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -323,6 +323,10 @@ mymain(int argc, char **argv) DO_TEST("graphics-sdl", 0, false); DO_TEST("graphics-sdl-fullscreen", 0, false); DO_TEST("nographics-vga", QEMUD_CMD_FLAG_VGA, false); + DO_TEST("graphics-spice", + QEMUD_CMD_FLAG_VGA | QEMUD_CMD_FLAG_VGA_QXL | + QEMUD_CMD_FLAG_DEVICE, false); + DO_TEST("input-usbmouse", 0, false); DO_TEST("input-usbtablet", 0, false); DO_TEST("input-xen", QEMUD_CMD_FLAG_DOMID, true); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index cdc4390..326a1f1 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -149,6 +149,7 @@ mymain(int argc, char **argv) DO_TEST("graphics-vnc-tls"); DO_TEST("graphics-sdl"); DO_TEST("graphics-sdl-fullscreen"); + DO_TEST("graphics-spice"); DO_TEST("input-usbmouse"); DO_TEST("input-usbtablet"); DO_TEST("input-xen"); -- 1.7.2.3

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 | 37 ++++++++++++++++++++ src/qemu/qemu_conf.h | 1 + .../qemuxml2argv-graphics-spice.args | 2 +- .../qemuxml2argv-graphics-spice.xml | 2 + tests/qemuxml2argvtest.c | 2 +- 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 45af71f..cd09db2 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1208,6 +1208,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")) @@ -5067,6 +5069,41 @@ int qemudBuildCommandLine(virConnectPtr conn, 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 == 1)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported graphics type '%s'"), diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 60c4c23..e00c83d 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -99,6 +99,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_DRIVE_READONLY= (1LL << 43), /* -drive readonly=on|off */ QEMUD_CMD_FLAG_SMBIOS_TYPE = (1LL << 44), /* Is -smbios type= available */ QEMUD_CMD_FLAG_VGA_QXL = (1LL << 45), /* The 'qxl' arg for '-vga' */ + QEMUD_CMD_FLAG_SPICE = (1LL << 46), /* Is -spice avail */ }; /* Main driver state */ diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args index 94cd90e..8d195e5 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml index 031a622..08dfb26 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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 18f9dfe..c314b2c 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -325,7 +325,7 @@ mymain(int argc, char **argv) DO_TEST("nographics-vga", QEMUD_CMD_FLAG_VGA, false); DO_TEST("graphics-spice", QEMUD_CMD_FLAG_VGA | QEMUD_CMD_FLAG_VGA_QXL | - QEMUD_CMD_FLAG_DEVICE, false); + QEMUD_CMD_FLAG_DEVICE | QEMUD_CMD_FLAG_SPICE, false); DO_TEST("input-usbmouse", 0, false); DO_TEST("input-usbtablet", 0, false); -- 1.7.2.3

* src/qemu/qemu_driver.c: Allocate the TCP ports for SPICE before starting guest --- src/qemu/qemu_driver.c | 38 ++++++++++++++++++++++++++------------ 1 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e7b37e1..c9f5e47 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2956,10 +2956,12 @@ qemuInitPCIAddresses(struct qemud_driver *driver, return ret; } -static int qemudNextFreeVNCPort(struct qemud_driver *driver) { + +static int qemudNextFreePort(struct qemud_driver *driver, + int startPort) { int i; - for (i = QEMU_VNC_PORT_MIN; i < QEMU_VNC_PORT_MAX; i++) { + for (i = startPort ; i < 65535 ; i++) { int fd; int reuse = 1; struct sockaddr_in addr; @@ -3927,17 +3929,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(driver, 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(driver, 5900); + int tlsPort = port == -1 ? -1 : qemudNextFreePort(driver, port + 1); + if (port < 0 || tlsPort < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to find 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.7.2.3

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.args | 2 +- tests/qemuxml2argvtest.c | 5 ++ 6 files changed, 123 insertions(+), 5 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index e2c581e..f4f965e 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 cd09db2..e4a4db3 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -116,6 +116,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; + } + #if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R /* For privileged driver, try and find hugepage mount automatically. * Non-privileged driver requires admin to create a dir for the @@ -219,6 +228,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))) { @@ -5082,11 +5128,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 e00c83d..790ce98 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -141,6 +141,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 c9f5e47..766de66 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3942,13 +3942,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(driver, 5900); - int tlsPort = port == -1 ? -1 : qemudNextFreePort(driver, port + 1); - if (port < 0 || tlsPort < 0) { + int tlsPort = -1; + if (port < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Unable to find unused SPICE ports")); + "%s", _("Unable to find an unused SPICE port")); goto cleanup; } + if (driver->spiceTLS) { + tlsPort = qemudNextFreePort(driver, 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.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args index 8d195e5..e412fdb 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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 c314b2c..b149ef4 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -214,6 +214,11 @@ 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, expectError) \ do { \ -- 1.7.2.3

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 --- docs/formatdomain.html.in | 8 +++- src/conf/domain_conf.c | 104 +++++++++++++++++++++++++++++++++++++++------ 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 +++--- 9 files changed, 136 insertions(+), 47 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index e6a8162..a4ff500 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1102,7 +1102,9 @@ qemu-kvm -net nic,model=? /dev/null The <code>listen</code> attribute is an IP address for the server to listen on. The <code>passwd</code> attribute provides a VNC password in clear text. The <code>keymap</code> attribute specifies the keymap - to use. + to use. It is possible to set a limit on the validity of the password + be giving an timestamp <code>passwdValidTo='2010-04-09T15:51:00'</code> + assumed to be in UTC. NB, this may not be supported by all hypervisors. </dd> <dt><code>"spice"</code></dt> <dd> @@ -1114,7 +1116,9 @@ qemu-kvm -net nic,model=? /dev/null The <code>listen</code> attribute is an IP address for the server to listen on. The <code>passwd</code> attribute provides a SPICE password in clear text. The <code>keymap</code> attribute specifies the keymap - to use. + to use. It is possible to set a limit on the validity of the password + be giving an timestamp <code>passwdValidTo='2010-04-09T15:51:00'</code> + assumed to be in UTC. NB, this may not be supported by all hypervisors. </dd> <dt><code>"rdp"</code></dt> <dd> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 04a4185..d4f8069 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -432,6 +432,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) @@ -441,7 +452,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: @@ -460,7 +471,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; } @@ -3080,6 +3091,55 @@ 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 */ + + def->validTo = timegm(&tm); + def->expires = 1; + } + + return 0; +} + + /* Parse the XML definition for a graphics device */ static virDomainGraphicsDefPtr virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { @@ -3138,8 +3198,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"); @@ -3263,8 +3325,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: @@ -6496,6 +6559,27 @@ virDomainTimerDefFormat(virBufferPtr buf, return 0; } +static void +virDomainGraphicsAuthDefFormatAttr(virBufferPtr buf, + virDomainGraphicsAuthDefPtr def, + unsigned int flags) +{ + if (!def->passwd) + return; + + if (flags & VIR_DOMAIN_XML_SECURE) + 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, @@ -6531,11 +6615,7 @@ 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); - + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.vnc.auth, flags); break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: @@ -6602,11 +6682,7 @@ 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); - + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.spice.auth, flags); break; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index d519cb0..fababca 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -517,6 +517,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 { @@ -527,7 +536,7 @@ struct _virDomainGraphicsDef { unsigned int autoport :1; char *listenAddr; char *keymap; - char *passwd; + virDomainGraphicsAuthDef auth; } vnc; struct { char *display; @@ -550,7 +559,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 36818df..b6b3954 100644 --- a/src/esx/esx_vmx.c +++ b/src/esx/esx_vmx.c @@ -1431,7 +1431,7 @@ esxVMX_ParseVNC(virConfPtr conf, virDomainGraphicsDefPtr *def) esxUtil_GetConfigString(conf, "RemoteDisplay.vnc.keymap", &(*def)->data.vnc.keymap, true) < 0 || esxUtil_GetConfigString(conf, "RemoteDisplay.vnc.password", - &(*def)->data.vnc.passwd, true) < 0) { + &(*def)->data.vnc.auth.passwd, true) < 0) { goto failure; } @@ -2831,9 +2831,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 2079c51..0b0a08a 100644 --- a/src/opennebula/one_conf.c +++ b/src/opennebula/one_conf.c @@ -262,9 +262,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 e4a4db3..4816e20 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -5029,7 +5029,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"); @@ -5139,7 +5139,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 766de66..052f95d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2522,12 +2522,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); } @@ -8890,19 +8890,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 5c3a4bd..8c25fd2 100644 --- a/src/xen/xend_internal.c +++ b/src/xen/xend_internal.c @@ -1808,7 +1808,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 && @@ -1930,7 +1930,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 && @@ -5251,8 +5251,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); } @@ -5294,8 +5294,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 a4d1a30..4d6b41b 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -1304,7 +1304,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; @@ -1376,7 +1376,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))) @@ -2541,9 +2541,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", @@ -2572,9 +2572,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.7.2.3

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.args | 2 +- .../qemuxml2argv-graphics-spice.xml | 3 + 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 4816e20..94ecec0 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -3251,6 +3251,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; +} + + int qemudOpenPCIConfig(virDomainHostdevDefPtr dev) { @@ -5171,13 +5201,7 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - 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 */ @@ -5223,6 +5247,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.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args index e412fdb..44809b0 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml index 08dfb26..6fe9a60 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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.7.2.3

This extends the SPICE XML to allow channel security options <graphics type='spice' port='-1' 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/formatdomain.html.in | 15 ++++ 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.args | 2 +- .../qemuxml2argv-graphics-spice.xml | 5 +- 8 files changed, 153 insertions(+), 3 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index a4ff500..8db8b52 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1108,6 +1108,7 @@ qemu-kvm -net nic,model=? /dev/null </dd> <dt><code>"spice"</code></dt> <dd> + <p> Starts a SPICE server. The <code>port</code> attribute specifies the TCP port number (with -1 as legacy syntax indicating that it should be auto-allocated), while <code>tlsPort</code> gives an alternative @@ -1119,6 +1120,20 @@ qemu-kvm -net nic,model=? /dev/null to use. It is possible to set a limit on the validity of the password be giving an timestamp <code>passwdValidTo='2010-04-09T15:51:00'</code> assumed to be in UTC. NB, this may not be supported by all hypervisors. + </p> + <p> + When SPICE has both a normal and TLS secured TCP port configured, it + can be desirable to restrict what channels can be run on each port. + This is achieved by adding one or more <channel> elements inside + the main <graphics> element. Valid channel names include + <code>main</code>,<code>display</code>,<code>inputs</code>,<code>cursor</code>, + <code>playback</code>,<code>record</code>. + </p> + <pre> + <graphics type='spice' port='-1' tlsPort='-1' autoport='yes'> + <channel name='main' mode='secure'/> + <channel name='record' mode='insecure'/> + </graphics></pre> </dd> <dt><code>"rdp"</code></dt> <dd> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 7903000..5b7ecc2 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1093,6 +1093,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 d4f8069..c1fbd65 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -271,6 +271,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") @@ -3284,6 +3299,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; @@ -3328,6 +3344,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: @@ -6586,6 +6636,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, int flags) { const char *type = virDomainGraphicsTypeToString(def->type); + int children = 0; + int i; if (!type) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -6687,7 +6739,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 fababca..aca3b10 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -525,6 +525,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; @@ -561,6 +579,7 @@ struct _virDomainGraphicsDef { char *keymap; virDomainGraphicsAuthDef auth; unsigned int autoport :1; + int channels[VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST]; } spice; } data; }; @@ -1236,6 +1255,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 8f267f6..39eb648 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -204,6 +204,10 @@ virDomainFindByName; virDomainFindByUUID; virDomainGetRootFilesystem; virDomainGraphicsDefFree; +virDomainGraphicsSpiceChannelModeTypeFromString; +virDomainGraphicsSpiceChannelModeTypeToString; +virDomainGraphicsSpiceChannelNameTypeFromString; +virDomainGraphicsSpiceChannelNameTypeToString; virDomainGraphicsTypeFromString; virDomainGraphicsTypeToString; virDomainHostdevDefFree; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 94ecec0..5e83655 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -5177,6 +5177,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.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args index 44809b0..87b8c06 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml index 6fe9a60..bdce04b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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.7.2.3

On 11/08/2010 12:27 PM, Daniel P. Berrange wrote:
This extends the SPICE XML to allow channel security options
<graphics type='spice' port='-1' 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
ACK series; except for my comments to patch 2/9, I think you covered all my findings from v1 of your series. And my comments to 2/9 don't invalidate the series, so much as provide suggestions for future patches to improve the series. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (2)
-
Daniel P. Berrange
-
Eric Blake