let remote_display_entry = int_entry "remote_display_port_min"
| int_entry "remote_display_port_max"
+ | int_entry "remote_websocket_port_min"
+ | int_entry "remote_websocket_port_max"
let security_entry = str_entry "security_driver"
| bool_entry "security_default_confined"
#remote_display_port_min = 5900
#remote_display_port_max = 65535
+# VNC WebSocket port policies, same rules apply as with remote display
+# ports. VNC WebSockets use similar display <-> port mappings, with
+# the exception being that ports starts from 5700 instead of 5900.
+#
+#remote_websocket_port_min = 5700
+#remote_websocket_port_max = 65535
# The default security driver is SELinux. If SELinux is disabled
# on the host, then the security driver will automatically disable
"scsi-generic.bootindex", /* 145 */
"mem-merge",
+ "vnc-websocket",
);
struct _virQEMUCaps {
if (qemuCaps->version >= 1003000)
virQEMUCapsSet(qemuCaps, QEMU_CAPS_MACHINE_USB_OPT);
+ /* WebSockets were introduced between 1.3.0 and 1.3.1 */
+ if (qemuCaps->version >= 1003001)
+ virQEMUCapsSet(qemuCaps, QEMU_CAPS_VNC_WEBSOCKET);
+
if (virQEMUCapsProbeQMPCommands(qemuCaps, mon) < 0)
goto cleanup;
if (virQEMUCapsProbeQMPEvents(qemuCaps, mon) < 0)
QEMU_CAPS_DEVICE_SCSI_GENERIC = 144, /* -device scsi-generic */
QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX = 145, /* -device scsi-generic.bootindex */
QEMU_CAPS_MEM_MERGE = 146, /* -machine mem-merge */
+ QEMU_CAPS_VNC_WEBSOCKET = 147, /* -vnc x:y,websocket */
QEMU_CAPS_LAST, /* this must always be the last item */
};
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_COLON)) {
+ if (!graphics->data.vnc.socket &&
+ graphics->data.vnc.websocket) {
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_WEBSOCKET)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("VNC WebSockets are not supported "
+ "with this QEMU binary"));
+ goto error;
+ }
+ virBufferAsprintf(&opt, ",websocket=%d", graphics->data.vnc.websocket);
+ }
+
if (graphics->data.vnc.auth.passwd || cfg->vncPassword)
virBufferAddLit(&opt, ",password");
* -vnc some.host.name:4
*/
char *opts;
+ char *port;
const char *sep = ":";
if (val[0] == '[')
sep = "]:";
_("missing VNC port number in '%s'"), val);
goto error;
}
- if (virStrToLong_i(tmp+strlen(sep), &opts, 10,
+ port = tmp + strlen(sep);
+ if (virStrToLong_i(port, &opts, 10,
&vnc->data.vnc.port) < 0) {
virDomainGraphicsDefFree(vnc);
virReportError(VIR_ERR_INTERNAL_ERROR,
- _("cannot parse VNC port '%s'"), tmp+1);
+ _("cannot parse VNC port '%s'"), port);
goto error;
}
if (val[0] == '[')
virDomainGraphicsDefFree(vnc);
goto no_memory;
}
+
+ if (*opts == ',') {
+ char *orig_opts = strdup(opts + 1);
+ if (!orig_opts) {
+ virDomainGraphicsDefFree(vnc);
+ goto no_memory;
+ }
+ opts = orig_opts;
+
+ while (opts && *opts) {
+ char *nextopt = strchr(opts, ',');
+ if (nextopt)
+ *(nextopt++) = '\0';
+
+ if (STRPREFIX(opts, "websocket")) {
+ char *websocket = opts + strlen("websocket");
+ if (*(websocket++) == '=' &&
+ *websocket) {
+ /* If the websocket continues with
+ * '=<something>', we'll parse it */
+ if (virStrToLong_i(websocket,
+ NULL, 0,
+ &vnc->data.vnc.websocket) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot parse VNC "
+ "WebSocket port '%s'"),
+ websocket);
+ virDomainGraphicsDefFree(vnc);
+ VIR_FREE(orig_opts);
+ goto error;
+ }
+ } else {
+ /* Otherwise, we'll compute the port the same
+ * way QEMU does, by adding a 5700 to the
+ * display value. */
+ vnc->data.vnc.websocket =
+ vnc->data.vnc.port + 5700;
+ }
+ }
+
+ opts = nextopt;
+ }
+ VIR_FREE(orig_opts);
+ }
vnc->data.vnc.port += 5900;
vnc->data.vnc.autoport = false;
}
# define QEMU_REMOTE_PORT_MIN 5900
# define QEMU_REMOTE_PORT_MAX 65535
+# define QEMU_WEBSOCKET_PORT_MIN 5700
+# define QEMU_WEBSOCKET_PORT_MAX 65535
+
virCommandPtr qemuBuildCommandLine(virConnectPtr conn,
virQEMUDriverPtr driver,
cfg->remotePortMin = QEMU_REMOTE_PORT_MIN;
cfg->remotePortMax = QEMU_REMOTE_PORT_MAX;
+ cfg->webSocketPortMin = QEMU_WEBSOCKET_PORT_MIN;
+ cfg->webSocketPortMax = QEMU_WEBSOCKET_PORT_MAX;
+
#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
GET_VALUE_STR("spice_password", cfg->spicePassword);
+ GET_VALUE_LONG("remote_websocket_port_min", cfg->webSocketPortMin);
+ if (cfg->webSocketPortMin < QEMU_WEBSOCKET_PORT_MIN) {
+ /* if the port is too low, we can't get the display name
+ * to tell to vnc (usually subtract 5700, e.g. localhost:1
+ * for port 5701) */
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("%s: remote_websocket_port_min: port must be greater "
+ "than or equal to %d"),
+ filename, QEMU_WEBSOCKET_PORT_MIN);
+ goto cleanup;
+ }
+
+ GET_VALUE_LONG("remote_websocket_port_max", cfg->webSocketPortMax);
+ if (cfg->webSocketPortMax > QEMU_WEBSOCKET_PORT_MAX ||
+ cfg->webSocketPortMax < cfg->webSocketPortMin) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("%s: remote_websocket_port_max: port must be between "
+ "the minimal port and %d"),
+ filename, QEMU_WEBSOCKET_PORT_MAX);
+ goto cleanup;
+ }
+
+ if (cfg->webSocketPortMin > cfg->webSocketPortMax) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("%s: remote_websocket_port_min: min port must not be "
+ "greater than max port"), filename);
+ goto cleanup;
+ }
+
GET_VALUE_LONG("remote_display_port_min", cfg->remotePortMin);
if (cfg->remotePortMin < QEMU_REMOTE_PORT_MIN) {
/* if the port is too low, we can't get the display name
int remotePortMin;
int remotePortMax;
+ int webSocketPortMin;
+ int webSocketPortMax;
+
char *hugetlbfsMount;
char *hugepagePath;
char *bridgeHelperName;
/* Immutable pointer, self-locking APIs */
virPortAllocatorPtr remotePorts;
+ /* Immutable pointer, self-locking APIs */
+ virPortAllocatorPtr webSocketPorts;
+
/* Immutable pointer, lockless APIs*/
virSysinfoDefPtr hostsysinfo;
cfg->remotePortMax)) == NULL)
goto error;
+ if ((qemu_driver->webSocketPorts =
+ virPortAllocatorNew(cfg->webSocketPortMin,
+ cfg->webSocketPortMax)) == NULL)
+ goto error;
+
if (qemuSecurityInit(qemu_driver) < 0)
goto error;
return ret;
}
+static int
+qemuProcessVNCAllocatePorts(virQEMUDriverPtr driver,
+ virDomainGraphicsDefPtr graphics)
+{
+ unsigned short port;
+
+ if (graphics->data.vnc.socket)
+ return 0;
+
+ if (graphics->data.vnc.autoport) {
+ if (virPortAllocatorAcquire(driver->remotePorts, &port) < 0)
+ return -1;
+ graphics->data.vnc.port = port;
+ }
+
+ if (graphics->data.vnc.websocket == -1) {
+ if (virPortAllocatorAcquire(driver->webSocketPorts, &port) < 0)
+ return -1;
+ graphics->data.vnc.websocket = port;
+ }
+
+ return 0;
+}
static int
qemuProcessSPICEAllocatePorts(virQEMUDriverPtr driver,
for (i = 0 ; i < vm->def->ngraphics; ++i) {
virDomainGraphicsDefPtr graphics = vm->def->graphics[i];
- if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
- !graphics->data.vnc.socket &&
- graphics->data.vnc.autoport) {
- unsigned short port;
- if (virPortAllocatorAcquire(driver->remotePorts, &port) < 0)
+ if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
+ if (qemuProcessVNCAllocatePorts(driver, graphics) < 0)
goto cleanup;
- graphics->data.vnc.port = port;
} else if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
if (qemuProcessSPICEAllocatePorts(driver, cfg, graphics) < 0)
goto cleanup;
*/
for (i = 0 ; i < vm->def->ngraphics; ++i) {
virDomainGraphicsDefPtr graphics = vm->def->graphics[i];
- if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
- graphics->data.vnc.autoport) {
- ignore_value(virPortAllocatorRelease(driver->remotePorts,
- graphics->data.vnc.port));
+ if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
+ if (graphics->data.vnc.autoport) {
+ ignore_value(virPortAllocatorRelease(driver->remotePorts,
+ graphics->data.vnc.port));
+ }
+ if (graphics->data.vnc.websocket) {
+ ignore_value(virPortAllocatorRelease(driver->webSocketPorts,
+ graphics->data.vnc.port));
+ }
}
if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE &&
graphics->data.spice.autoport) {
{ "spice_password" = "XYZ12345" }
{ "remote_display_port_min" = "5900" }
{ "remote_display_port_max" = "65535" }
+{ "remote_websocket_port_min" = "5700" }
+{ "remote_websocket_port_max" = "65535" }
{ "security_driver" = "selinux" }
{ "security_default_confined" = "1" }
{ "security_require_confined" = "1" }
DO_TEST("disk-usb");
DO_TEST("graphics-vnc");
DO_TEST("graphics-vnc-socket");
+ DO_TEST("graphics-vnc-websocket");
DO_TEST("graphics-vnc-sasl");
DO_TEST("graphics-vnc-tls");
--- /dev/null
+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 \
+-usb -net none -serial none -parallel none -vnc 127.0.0.1:0,websocket=5700
--- /dev/null
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219100</memory>
+ <currentMemory unit='KiB'>219100</currentMemory>
+ <vcpu placement='static'>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>
+ <controller type='usb' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <input type='mouse' bus='ps2'/>
+ <graphics type='vnc' port='5900' autoport='no' websocket='5700' listen='127.0.0.1'>
+ <listen type='address' address='127.0.0.1'/>
+ </graphics>
+ <video>
+ <model type='cirrus' vram='9216' heads='1'/>
+ </video>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
DO_TEST("graphics-vnc", QEMU_CAPS_VNC);
DO_TEST("graphics-vnc-socket", QEMU_CAPS_VNC);
+ DO_TEST("graphics-vnc-websocket", QEMU_CAPS_VNC, QEMU_CAPS_VNC_WEBSOCKET);
driver.config->vncSASL = 1;
VIR_FREE(driver.config->vncSASLdir);
DO_TEST_FULL("disk-mirror", true, WHEN_INACTIVE);
DO_TEST("graphics-listen-network");
DO_TEST("graphics-vnc");
+ DO_TEST("graphics-vnc-websocket");
DO_TEST("graphics-vnc-sasl");
DO_TEST("graphics-vnc-tls");
DO_TEST("graphics-sdl");