[libvirt] [PATCH 2/3] qemu: Allow serving VNC over a unix domain socket

Cole Robinson crobinso at redhat.com
Wed Jan 12 17:32:43 UTC 2011


QEMU supports serving VNC over a unix domain socket rather than traditional
TCP host/port. This is specified with:

<graphics type='vnc' socket='/foo/bar/baz'/>

This provides better security access control than VNC listening on
127.0.0.1, but will cause issues with tools that rely on the lax security
(virt-manager in fedora runs as regular user by default, and wouldn't be
able to access a socket owned by 'qemu' or 'root').

Also not currently supported by any clients, though I have patches for
virt-manager, and virt-viewer should be simple to update.

v2:
    schema: Make listen vs. socket a <choice>

Signed-off-by: Cole Robinson <crobinso at redhat.com>
---
 docs/formatdomain.html.in                          |    6 ++-
 docs/schemas/domain.rng                            |   47 +++++++++++-------
 src/conf/domain_conf.c                             |   30 +++++++----
 src/conf/domain_conf.h                             |    1 +
 src/qemu/qemu_command.c                            |   52 +++++++++++++-------
 src/qemu/qemu_driver.c                             |    1 +
 tests/qemuargv2xmltest.c                           |    1 +
 .../qemuxml2argv-graphics-vnc-socket.args          |    1 +
 .../qemuxml2argv-graphics-vnc-socket.xml           |   30 +++++++++++
 tests/qemuxml2argvtest.c                           |    1 +
 10 files changed, 122 insertions(+), 48 deletions(-)
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-socket.args
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-socket.xml

diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index e9fcea1..fbcff0b 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1153,7 +1153,11 @@ qemu-kvm -net nic,model=? /dev/null
   in clear text. The <code>keymap</code> attribute specifies the keymap
   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.
+  assumed to be in UTC. NB, this may not be supported by all hypervisors.<br>
+  <br>
+  Rather than using listen/port, QEMU supports a <code>socket</code>
+  attribute for listening on a unix domain socket path.
+  <span class="since">Since 0.8.8</span>
           </dd>
           <dt><code>"spice"</code></dt>
           <dd>
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng
index 9c1e9bb..1907e49 100644
--- a/docs/schemas/domain.rng
+++ b/docs/schemas/domain.rng
@@ -1104,24 +1104,35 @@
           <attribute name="type">
             <value>vnc</value>
           </attribute>
-          <optional>
-            <attribute name="port">
-              <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>
+          <choice>
+            <group>
+              <optional>
+                <attribute name="port">
+                  <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>
+            </group>
+            <group>
+              <optional>
+                <attribute name="socket">
+                  <ref name="absFilePath"/>
+                </attribute>
+              </optional>
+            </group>
+          </choice>
           <optional>
             <attribute name="passwd">
               <text/>
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 5b1c907..4621ae2 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -477,6 +477,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def)
     switch (def->type) {
     case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
         VIR_FREE(def->data.vnc.listenAddr);
+        VIR_FREE(def->data.vnc.socket);
         VIR_FREE(def->data.vnc.keymap);
         virDomainGraphicsAuthDefClear(&def->data.vnc.auth);
         break;
@@ -3365,6 +3366,7 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) {
         }
 
         def->data.vnc.listenAddr = virXMLPropString(node, "listen");
+        def->data.vnc.socket = virXMLPropString(node, "socket");
         def->data.vnc.keymap = virXMLPropString(node, "keymap");
 
         if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0)
@@ -6868,19 +6870,25 @@ virDomainGraphicsDefFormat(virBufferPtr buf,
 
     switch (def->type) {
     case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
-        if (def->data.vnc.port &&
-            (!def->data.vnc.autoport || !(flags & VIR_DOMAIN_XML_INACTIVE)))
-            virBufferVSprintf(buf, " port='%d'",
-                              def->data.vnc.port);
-        else if (def->data.vnc.autoport)
-            virBufferAddLit(buf, " port='-1'");
+        if (def->data.vnc.socket) {
+            if (def->data.vnc.socket)
+                virBufferVSprintf(buf, " socket='%s'",
+                                  def->data.vnc.socket);
+        } else {
+            if (def->data.vnc.port &&
+                (!def->data.vnc.autoport || !(flags & VIR_DOMAIN_XML_INACTIVE)))
+                virBufferVSprintf(buf, " port='%d'",
+                                  def->data.vnc.port);
+            else if (def->data.vnc.autoport)
+                virBufferAddLit(buf, " port='-1'");
 
-        virBufferVSprintf(buf, " autoport='%s'",
-                          def->data.vnc.autoport ? "yes" : "no");
+            virBufferVSprintf(buf, " autoport='%s'",
+                              def->data.vnc.autoport ? "yes" : "no");
 
-        if (def->data.vnc.listenAddr)
-            virBufferVSprintf(buf, " listen='%s'",
-                              def->data.vnc.listenAddr);
+            if (def->data.vnc.listenAddr)
+                virBufferVSprintf(buf, " listen='%s'",
+                                  def->data.vnc.listenAddr);
+        }
 
         if (def->data.vnc.keymap)
             virBufferEscapeString(buf, " keymap='%s'",
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 3da26e9..8c637b9 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -574,6 +574,7 @@ struct _virDomainGraphicsDef {
             unsigned int autoport :1;
             char *listenAddr;
             char *keymap;
+            char *socket;
             virDomainGraphicsAuthDef auth;
         } vnc;
         struct {
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index a0075a4..8e86f43 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -3512,7 +3512,11 @@ qemuBuildCommandLine(virConnectPtr conn,
         def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
         virBuffer opt = VIR_BUFFER_INITIALIZER;
 
-        if (qemuCmdFlags & QEMUD_CMD_FLAG_VNC_COLON) {
+        if (def->graphics[0]->data.vnc.socket) {
+            virBufferVSprintf(&opt, "unix:%s",
+                              def->graphics[0]->data.vnc.socket);
+
+        } else if (qemuCmdFlags & QEMUD_CMD_FLAG_VNC_COLON) {
             if (def->graphics[0]->data.vnc.listenAddr)
                 virBufferAdd(&opt, def->graphics[0]->data.vnc.listenAddr, -1);
             else if (driver->vncListen)
@@ -3521,6 +3525,12 @@ qemuBuildCommandLine(virConnectPtr conn,
             virBufferVSprintf(&opt, ":%d",
                               def->graphics[0]->data.vnc.port - 5900);
 
+        } else {
+            virBufferVSprintf(&opt, "%d",
+                              def->graphics[0]->data.vnc.port - 5900);
+        }
+
+        if (qemuCmdFlags & QEMUD_CMD_FLAG_VNC_COLON) {
             if (def->graphics[0]->data.vnc.auth.passwd ||
                 driver->vncPassword)
                 virBufferAddLit(&opt, ",password");
@@ -3545,9 +3555,6 @@ qemuBuildCommandLine(virConnectPtr conn,
 
                 /* TODO: Support ACLs later */
             }
-        } else {
-            virBufferVSprintf(&opt, "%d",
-                              def->graphics[0]->data.vnc.port - 5900);
         }
 
         virCommandAddArg(cmd, "-vnc");
@@ -5175,24 +5182,33 @@ virDomainDefPtr qemuParseCommandLine(virCapsPtr caps,
                 goto no_memory;
             vnc->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC;
 
-            tmp = strchr(val, ':');
-            if (tmp) {
-                char *opts;
-                if (virStrToLong_i(tmp+1, &opts, 10, &vnc->data.vnc.port) < 0) {
-                    VIR_FREE(vnc);
-                    qemuReportError(VIR_ERR_INTERNAL_ERROR,             \
-                                    _("cannot parse VNC port '%s'"), tmp+1);
-                    goto error;
-                }
-                vnc->data.vnc.listenAddr = strndup(val, tmp-val);
-                if (!vnc->data.vnc.listenAddr) {
+            if (STRPREFIX(val, "unix:")) {
+                vnc->data.vnc.socket = strdup(val + 5);
+                if (!vnc->data.vnc.socket) {
                     VIR_FREE(vnc);
                     goto no_memory;
                 }
-                vnc->data.vnc.port += 5900;
-                vnc->data.vnc.autoport = 0;
             } else {
-                vnc->data.vnc.autoport = 1;
+                tmp = strchr(val, ':');
+                if (tmp) {
+                    char *opts;
+                    if (virStrToLong_i(tmp+1, &opts, 10,
+                                       &vnc->data.vnc.port) < 0) {
+                        VIR_FREE(vnc);
+                        qemuReportError(VIR_ERR_INTERNAL_ERROR,
+                                        _("cannot parse VNC port '%s'"), tmp+1);
+                        goto error;
+                    }
+                    vnc->data.vnc.listenAddr = strndup(val, tmp-val);
+                    if (!vnc->data.vnc.listenAddr) {
+                        VIR_FREE(vnc);
+                        goto no_memory;
+                    }
+                    vnc->data.vnc.port += 5900;
+                    vnc->data.vnc.autoport = 0;
+                } else {
+                    vnc->data.vnc.autoport = 1;
+                }
             }
 
             if (VIR_REALLOC_N(def->graphics, def->ngraphics+1) < 0) {
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index a56b2f4..8a9ed42 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -2672,6 +2672,7 @@ static int qemudStartVMDaemon(virConnectPtr conn,
 
     if (vm->def->ngraphics == 1) {
         if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
+            !vm->def->graphics[0]->data.vnc.socket &&
             vm->def->graphics[0]->data.vnc.autoport) {
             int port = qemudNextFreePort(driver, QEMU_VNC_PORT_MIN);
             if (port < 0) {
diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c
index 8338af3..7499ba0 100644
--- a/tests/qemuargv2xmltest.c
+++ b/tests/qemuargv2xmltest.c
@@ -178,6 +178,7 @@ mymain(int argc, char **argv)
     DO_TEST("disk-drive-network-sheepdog");
     DO_TEST("disk-usb");
     DO_TEST("graphics-vnc");
+    DO_TEST("graphics-vnc-socket");
 
     driver.vncSASL = 1;
     driver.vncSASLdir = strdup("/root/.sasl2");
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-socket.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-socket.args
new file mode 100644
index 0000000..055c562
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-socket.args
@@ -0,0 +1 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none /usr/bin/qemu -S -M pc -m 214 -smp 1 -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -usb -vnc unix:/tmp/foo.socket
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-socket.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-socket.xml
new file mode 100644
index 0000000..d6ad72b
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-vnc-socket.xml
@@ -0,0 +1,30 @@
+<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'/>
+    <input type='mouse' bus='ps2'/>
+    <graphics type='vnc' socket='/tmp/foo.socket'/>
+    <video>
+      <model type='cirrus' vram='9216' heads='1'/>
+    </video>
+    <memballoon model='virtio'/>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 6760f67..8a1d5f7 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -316,6 +316,7 @@ mymain(int argc, char **argv)
     DO_TEST("disk-scsi-device-auto", QEMUD_CMD_FLAG_DRIVE |
             QEMUD_CMD_FLAG_DEVICE | QEMUD_CMD_FLAG_NODEFCONFIG, false);
     DO_TEST("graphics-vnc", 0, false);
+    DO_TEST("graphics-vnc-socket", 0, false);
 
     driver.vncSASL = 1;
     driver.vncSASLdir = strdup("/root/.sasl2");
-- 
1.7.3.2




More information about the libvir-list mailing list