]> xenbits.xensource.com Git - libvirt.git/commitdiff
Fix race in finding available vnc port
authorJim Fehlig <jfehlig@novell.com>
Fri, 21 May 2010 13:52:09 +0000 (07:52 -0600)
committerJim Fehlig <jfehlig@novell.com>
Fri, 21 May 2010 17:29:03 +0000 (11:29 -0600)
The qemu driver contains a subtle race in the logic to find next
available vnc port.  Currently it iterates through all available ports
and returns the first for which bind(2) succeeds.  However it is possible
that a previously issued port has not yet been bound by qemu, resulting
in the same port used for a subsequent domain.

This patch addresses the race by using a simple bitmap to "reserve" the
ports allocated by libvirt.

V2:
  - Put port bitmap in struct qemud_driver
  - Initialize bitmap in qemudStartup

V3:
  - Check for failure of virBitmapGetBit
  - Additional check for port != -1 before calling virbitmapClearBit

V4:
  - Check for failure of virBitmap{Set,Clear}Bit

src/qemu/qemu_conf.h
src/qemu/qemu_driver.c

index 11f8dcd22bbe1aca99b87c4e0acbb270f9a359fa..8fd8d798f5706f75bd1b4940115cd98de3f1d0a6 100644 (file)
@@ -39,6 +39,7 @@
 # include "pci.h"
 # include "cpu_conf.h"
 # include "driver.h"
+# include "bitmap.h"
 
 # define qemudDebug(fmt, ...) do {} while(0)
 
@@ -153,6 +154,8 @@ struct qemud_driver {
     char *saveImageFormat;
 
     pciDeviceList *activePciHostdevs;
+
+    virBitmapPtr reservedVNCPorts;
 };
 
 typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet;
index 9837fe253fc43610c841ea0af7a33f0dd74c9a5c..fcf0c6dc9336d2c4876501fc403775d83895d252 100644 (file)
@@ -1485,6 +1485,11 @@ qemudStartup(int privileged) {
          virEventAddTimeout(-1, qemuDomainEventFlush, qemu_driver, NULL)) < 0)
         goto error;
 
+    /* Allocate bitmap for vnc port reservation */
+    if ((qemu_driver->reservedVNCPorts =
+         virBitmapAlloc(QEMU_VNC_PORT_MAX - QEMU_VNC_PORT_MIN)) == NULL)
+        goto out_of_memory;
+
     if (privileged) {
         if (virAsprintf(&qemu_driver->logDir,
                         "%s/log/libvirt/qemu", LOCAL_STATE_DIR) == -1)
@@ -1781,6 +1786,7 @@ qemudShutdown(void) {
     virCapabilitiesFree(qemu_driver->caps);
 
     virDomainObjListDeinit(&qemu_driver->domains);
+    virBitmapFree(qemu_driver->reservedVNCPorts);
 
     VIR_FREE(qemu_driver->securityDriverName);
     VIR_FREE(qemu_driver->logDir);
@@ -2638,13 +2644,22 @@ qemuInitPCIAddresses(struct qemud_driver *driver,
     return ret;
 }
 
-static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
+static int qemudNextFreeVNCPort(struct qemud_driver *driver) {
     int i;
 
     for (i = QEMU_VNC_PORT_MIN; i < QEMU_VNC_PORT_MAX; i++) {
         int fd;
         int reuse = 1;
         struct sockaddr_in addr;
+        bool used = false;
+
+        if (virBitmapGetBit(driver->reservedVNCPorts,
+                            i - QEMU_VNC_PORT_MIN, &used) < 0)
+            VIR_DEBUG("virBitmapGetBit failed on bit %d", i - QEMU_VNC_PORT_MIN);
+
+        if (used)
+            continue;
+
         addr.sin_family = AF_INET;
         addr.sin_port = htons(i);
         addr.sin_addr.s_addr = htonl(INADDR_ANY);
@@ -2660,6 +2675,12 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
         if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
             /* Not in use, lets grab it */
             close(fd);
+            /* Add port to bitmap of reserved ports */
+            if (virBitmapSetBit(driver->reservedVNCPorts,
+                                i - QEMU_VNC_PORT_MIN) < 0) {
+                VIR_DEBUG("virBitmapSetBit failed on bit %d",
+                          i - QEMU_VNC_PORT_MIN);
+            }
             return i;
         }
         close(fd);
@@ -3700,6 +3721,21 @@ retry:
 
     qemudRemoveDomainStatus(driver, vm);
 
+    /* Remove VNC port from port reservation bitmap, but only if it was
+       reserved by the driver (autoport=yes)
+    */
+    if ((vm->def->ngraphics == 1) &&
+        vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
+        vm->def->graphics[0]->data.vnc.autoport &&
+        vm->def->graphics[0]->data.vnc.port != -1) {
+        if (virBitmapClearBit(driver->reservedVNCPorts,
+                              vm->def->graphics[0]->data.vnc.port - \
+                              QEMU_VNC_PORT_MIN) < 0) {
+            VIR_DEBUG("virBitmapClearBit failed on bit %d",
+                      vm->def->graphics[0]->data.vnc.port - QEMU_VNC_PORT_MIN);
+       }
+    }
+
     vm->pid = -1;
     vm->def->id = -1;
     vm->state = VIR_DOMAIN_SHUTOFF;