Fixed since v1: - avoid setup/restore security contexts - some hotplug code improvements, although the lack of dynamic chardev limits the utility of this code for now Fixed since v2: - create a new "redirdev" element instead of extending "hostdev" --- docs/formatdomain.html.in | 39 ++++++ docs/schemas/domain.rng | 44 +++++-- src/conf/domain_audit.c | 65 ++++++++++ src/conf/domain_audit.h | 5 + src/conf/domain_conf.c | 127 +++++++++++++++++++- src/conf/domain_conf.h | 24 ++++ src/libvirt_private.syms | 3 + src/qemu/qemu_command.c | 96 +++++++++++++++ src/qemu/qemu_command.h | 4 +- src/qemu/qemu_driver.c | 7 + src/qemu/qemu_hotplug.c | 43 +++++++ src/qemu/qemu_hotplug.h | 3 + tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args | 8 ++ tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml | 38 ++++++ tests/qemuxml2argvtest.c | 4 + tests/qemuxml2xmltest.c | 2 + 16 files changed, 494 insertions(+), 18 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index e35b76b..e1fd6ed 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -1385,6 +1385,45 @@ not used by qemu.</dd> </dl> + <h4><a name="elementsRedir">Redirected devices</a></h4> + + <p> + USB device redirection through a character device is + supported <span class="since">since after 0.9.5 (KVM + only)</span>: + </p> + +<pre> + ... + <devices> + <redirdev bus='usb' type='tcp'> + <source mode='connect' host='localhost' service='4000'/> + </redirdev> + </devices> + ...</pre> + + <dl> + <dt><code>redirdev</code></dt> + <dd>The <code>redirdev</code> element is the main container for + describing redirected devices. <code>bus</code> must be "usb" + for a USB device. + + An additional attribute <code>type</code> is required, + matching one of the + supported <a href="#elementsConsole">serial device</a> types, + to describe the host side of the + tunnel; <code>type='tcp'</code> + or <code>type='spicevmc'</code> (which uses the usbredir + channel of a <a href="#elementsGraphics">SPICE graphics + device</a>) are typical. Further sub-elements, such + as <code><source></code>, may be required according to + the given type, although a <code><target></code> + sub-element is not required (since the consumer of the + character device is the hypervisor itself, rather than a + device visible in the guest).</dd> + + </dl> + <h4><a name="elementsSmartcard">Smartcard devices</a></h4> <p> diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index b496a32..b89108c 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1723,21 +1723,25 @@ </element> </define> + <define name="qemucdevSrcTypeChoice"> + <choice> + <value>dev</value> + <value>file</value> + <value>pipe</value> + <value>unix</value> + <value>tcp</value> + <value>udp</value> + <value>null</value> + <value>stdio</value> + <value>vc</value> + <value>pty</value> + <value>spicevmc</value> + </choice> + </define> + <define name="qemucdevSrcType"> <attribute name="type"> - <choice> - <value>dev</value> - <value>file</value> - <value>pipe</value> - <value>unix</value> - <value>tcp</value> - <value>udp</value> - <value>null</value> - <value>stdio</value> - <value>vc</value> - <value>pty</value> - <value>spicevmc</value> - </choice> + <ref name="qemucdevSrcTypeChoice"/> </attribute> </define> <define name="qemucdevSrcDef"> @@ -1974,6 +1978,19 @@ </optional> </element> </define> + <define name="redirdev"> + <element name="redirdev"> + <attribute name="bus"> + <choice> + <value>usb</value> + </choice> + </attribute> + <attribute name="type"> + <ref name="qemucdevSrcTypeChoice"/> + </attribute> + <ref name="qemucdevSrcDef"/> + </element> + </define> <define name="hostdev"> <element name="hostdev"> <optional> @@ -2138,6 +2155,7 @@ <ref name="channel"/> <ref name="smartcard"/> <ref name="hub"/> + <ref name="redirdev"/> </choice> </zeroOrMore> <optional> diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c index 9d89c94..ef0f47a 100644 --- a/src/conf/domain_audit.c +++ b/src/conf/domain_audit.c @@ -309,6 +309,66 @@ cleanup: /** + * virDomainAuditRedirdev: + * @vm: domain making a change in pass-through host device + * @redirdev: device being attached or removed + * @reason: one of "start", "attach", or "detach" + * @success: true if the device passthrough operation succeeded + * + * Log an audit message about an attempted device passthrough change. + */ +void +virDomainAuditRedirdev(virDomainObjPtr vm, virDomainRedirdevDefPtr redirdev, + const char *reason, bool success) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *vmname; + char *address; + char *device; + const char *virt; + + virUUIDFormat(vm->def->uuid, uuidstr); + if (!(vmname = virAuditEncode("vm", vm->def->name))) { + VIR_WARN("OOM while encoding audit message"); + return; + } + + if (!(virt = virDomainVirtTypeToString(vm->def->virtType))) { + VIR_WARN("Unexpected virt type %d while encoding audit message", vm->def->virtType); + virt = "?"; + } + + switch (redirdev->bus) { + case VIR_DOMAIN_REDIRDEV_BUS_USB: + if (virAsprintf(&address, "USB redirdev") < 0) { + VIR_WARN("OOM while encoding audit message"); + goto cleanup; + } + default: + VIR_WARN("Unexpected redirdev bus while encoding audit message: %d", + redirdev->bus); + goto cleanup; + } + + if (!(device = virAuditEncode("device", VIR_AUDIT_STR(address)))) { + VIR_WARN("OOM while encoding audit message"); + goto cleanup; + } + + VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, + "virt=%s resrc=dev reason=%s %s uuid=%s bus=%s %s", + virt, reason, vmname, uuidstr, + virDomainRedirdevBusTypeToString(redirdev->bus), + device); + +cleanup: + VIR_FREE(vmname); + VIR_FREE(device); + VIR_FREE(address); +} + + +/** * virDomainAuditCgroup: * @vm: domain making the cgroups ACL change * @cgroup: cgroup that manages the devices @@ -538,6 +598,11 @@ virDomainAuditStart(virDomainObjPtr vm, const char *reason, bool success) virDomainAuditHostdev(vm, hostdev, "start", true); } + for (i = 0 ; i < vm->def->nredirdevs ; i++) { + virDomainRedirdevDefPtr redirdev = vm->def->redirdevs[i]; + virDomainAuditRedirdev(vm, redirdev, "start", true); + } + virDomainAuditMemory(vm, 0, vm->def->mem.cur_balloon, "start", true); virDomainAuditVcpu(vm, 0, vm->def->vcpus, "start", true); diff --git a/src/conf/domain_audit.h b/src/conf/domain_audit.h index 0e88fd3..9ea9d6d 100644 --- a/src/conf/domain_audit.h +++ b/src/conf/domain_audit.h @@ -101,5 +101,10 @@ void virDomainAuditVcpu(virDomainObjPtr vm, void virDomainAuditSecurityLabel(virDomainObjPtr vm, bool success) ATTRIBUTE_NONNULL(1); +void virDomainAuditRedirdev(virDomainObjPtr vm, + virDomainRedirdevDefPtr def, + const char *reason, + bool success) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); #endif /* __VIR_DOMAIN_AUDIT_H__ */ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 4fe92d7..08d957a 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -127,7 +127,8 @@ VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST, "watchdog", "controller", "graphics", - "hub") + "hub", + "redirdev") VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST, "none", @@ -440,6 +441,9 @@ VIR_ENUM_IMPL(virDomainState, VIR_DOMAIN_CRASHED+1, VIR_ENUM_IMPL(virDomainHub, VIR_DOMAIN_HUB_TYPE_LAST, "usb") +VIR_ENUM_IMPL(virDomainRedirdevBus, VIR_DOMAIN_REDIRDEV_BUS_LAST, + "usb") + #define VIR_DOMAIN_NOSTATE_LAST (VIR_DOMAIN_NOSTATE_UNKNOWN + 1) VIR_ENUM_IMPL(virDomainNostateReason, VIR_DOMAIN_NOSTATE_LAST, "unknown") @@ -1012,6 +1016,17 @@ void virDomainHubDefFree(virDomainHubDefPtr def) VIR_FREE(def); } +void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def) +{ + if (!def) + return; + + virDomainChrSourceDefClear(&def->source.chr); + virDomainDeviceInfoClear(&def->info); + + VIR_FREE(def); +} + void virDomainDeviceDefFree(virDomainDeviceDefPtr def) { if (!def) @@ -1051,6 +1066,9 @@ void virDomainDeviceDefFree(virDomainDeviceDefPtr def) case VIR_DOMAIN_DEVICE_HUB: virDomainHubDefFree(def->data.hub); break; + case VIR_DOMAIN_DEVICE_REDIRDEV: + virDomainRedirdevDefFree(def->data.redirdev); + break; } VIR_FREE(def); @@ -5343,7 +5361,6 @@ virDomainHostdevDefParseXML(const xmlNodePtr node, virBitmapPtr bootMap, unsigned int flags) { - xmlNodePtr cur; virDomainHostdevDefPtr def; char *mode, *type = NULL, *managed = NULL; @@ -5390,8 +5407,8 @@ virDomainHostdevDefParseXML(const xmlNodePtr node, if (xmlStrEqual(cur->name, BAD_CAST "source")) { if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { - if (virDomainHostdevSubsysUsbDefParseXML(cur, def) < 0) - goto error; + if (virDomainHostdevSubsysUsbDefParseXML(cur, def) < 0) + goto error; } if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { @@ -5444,6 +5461,68 @@ error: } +static virDomainRedirdevDefPtr +virDomainRedirdevDefParseXML(const xmlNodePtr node, + unsigned int flags) +{ + xmlNodePtr cur; + virDomainRedirdevDefPtr def; + char *bus, *type = NULL; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + return NULL; + } + + bus = virXMLPropString(node, "bus"); + if (bus) { + if ((def->bus = virDomainRedirdevBusTypeFromString(bus)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown redirdev bus '%s'"), bus); + goto error; + } + } else { + def->bus = VIR_DOMAIN_REDIRDEV_BUS_USB; + } + + type = virXMLPropString(node, "type"); + if (type) { + if ((def->source.chr.type = virDomainChrTypeFromString(type)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown redirdev character device type '%s'"), type); + goto error; + } + } else { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing type in redirdev")); + goto error; + } + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "source")) { + int remaining; + + remaining = virDomainChrSourceDefParseXML(&def->source.chr, cur, flags); + if (remaining != 0) + goto error; + } + } + cur = cur->next; + } + +cleanup: + VIR_FREE(bus); + VIR_FREE(type); + return def; + +error: + virDomainRedirdevDefFree(def); + def = NULL; + goto cleanup; +} + static int virDomainLifecycleParseXML(xmlXPathContextPtr ctxt, const char *xpath, int *val, @@ -5654,6 +5733,10 @@ virDomainDeviceDefPtr virDomainDeviceDefParse(virCapsPtr caps, dev->type = VIR_DOMAIN_DEVICE_HUB; if (!(dev->data.hub = virDomainHubDefParseXML(node, flags))) goto error; + } else if (xmlStrEqual(node->name, BAD_CAST "redirdev")) { + dev->type = VIR_DOMAIN_DEVICE_REDIRDEV; + if (!(dev->data.redirdev = virDomainRedirdevDefParseXML(node, flags))) + goto error; } else { virDomainReportError(VIR_ERR_XML_ERROR, "%s", _("unknown device type")); @@ -7073,6 +7156,22 @@ static virDomainDefPtr virDomainDefParseXML(virCapsPtr caps, } VIR_FREE(nodes); + /* analysis of the redirected devices */ + if ((n = virXPathNodeSet("./devices/redirdev", ctxt, &nodes)) < 0) { + goto error; + } + if (n && VIR_ALLOC_N(def->redirdevs, n) < 0) + goto no_memory; + for (i = 0 ; i < n ; i++) { + virDomainRedirdevDefPtr redirdev = virDomainRedirdevDefParseXML(nodes[i], + flags); + if (!redirdev) + goto error; + + def->redirdevs[def->nredirdevs++] = redirdev; + } + VIR_FREE(nodes); + /* analysis of security label */ if (virSecurityLabelDefParseXML(def, ctxt, flags) == -1) goto error; @@ -10162,6 +10261,22 @@ virDomainHostdevDefFormat(virBufferPtr buf, return 0; } +static int +virDomainRedirdevDefFormat(virBufferPtr buf, + virDomainRedirdevDefPtr def, + unsigned int flags) +{ + const char *bus; + + bus = virDomainRedirdevBusTypeToString(def->bus); + + virBufferAsprintf(buf, " <redirdev bus='%s'", bus); + if (virDomainChrSourceDefFormat(buf, &def->source.chr, false, flags) < 0) + return -1; + virBufferAddLit(buf, " </redirdev>\n"); + + return 0; +} static int virDomainHubDefFormat(virBufferPtr buf, @@ -10596,6 +10711,10 @@ virDomainDefFormatInternal(virDomainDefPtr def, if (virDomainHostdevDefFormat(&buf, def->hostdevs[n], flags) < 0) goto cleanup; + for (n = 0 ; n < def->nredirdevs ; n++) + if (virDomainRedirdevDefFormat(&buf, def->redirdevs[n], flags) < 0) + goto cleanup; + for (n = 0 ; n < def->nhubs ; n++) if (virDomainHubDefFormat(&buf, def->hubs[n], flags) < 0) goto cleanup; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 48bfd0f..c79610c 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -938,6 +938,23 @@ struct _virDomainHostdevDef { virDomainDeviceInfo info; /* Guest address */ }; +enum virDomainRedirdevBus { + VIR_DOMAIN_REDIRDEV_BUS_USB, + + VIR_DOMAIN_REDIRDEV_BUS_LAST +}; + +typedef struct _virDomainRedirdevDef virDomainRedirdevDef; +typedef virDomainRedirdevDef *virDomainRedirdevDefPtr; +struct _virDomainRedirdevDef { + int bus; /* enum virDomainRedirdevBus */ + + union { + virDomainChrSourceDef chr; + } source; + + virDomainDeviceInfo info; /* Guest address */ +}; enum { VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO, @@ -978,6 +995,7 @@ enum virDomainDeviceType { VIR_DOMAIN_DEVICE_CONTROLLER, VIR_DOMAIN_DEVICE_GRAPHICS, VIR_DOMAIN_DEVICE_HUB, + VIR_DOMAIN_DEVICE_REDIRDEV, VIR_DOMAIN_DEVICE_LAST, }; @@ -999,6 +1017,7 @@ struct _virDomainDeviceDef { virDomainWatchdogDefPtr watchdog; virDomainGraphicsDefPtr graphics; virDomainHubDefPtr hub; + virDomainRedirdevDefPtr redirdev; } data; }; @@ -1311,6 +1330,9 @@ struct _virDomainDef { int nhostdevs; virDomainHostdevDefPtr *hostdevs; + int nredirdevs; + virDomainRedirdevDefPtr *redirdevs; + int nsmartcards; virDomainSmartcardDefPtr *smartcards; @@ -1476,6 +1498,7 @@ void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def); void virDomainVideoDefFree(virDomainVideoDefPtr def); void virDomainHostdevDefFree(virDomainHostdevDefPtr def); void virDomainHubDefFree(virDomainHubDefPtr def); +void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def); void virDomainDeviceDefFree(virDomainDeviceDefPtr def); int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info, int type); @@ -1757,6 +1780,7 @@ VIR_ENUM_DECL(virDomainVideo) VIR_ENUM_DECL(virDomainHostdevMode) VIR_ENUM_DECL(virDomainHostdevSubsys) VIR_ENUM_DECL(virDomainHub) +VIR_ENUM_DECL(virDomainRedirdevBus) VIR_ENUM_DECL(virDomainInput) VIR_ENUM_DECL(virDomainInputBus) VIR_ENUM_DECL(virDomainGraphics) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6642ba9..101f99e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -219,6 +219,7 @@ virDomainAuditHostdev; virDomainAuditMemory; virDomainAuditNet; virDomainAuditNetDevice; +virDomainAuditRedirdev; virDomainAuditSecurityLabel; virDomainAuditStart; virDomainAuditStop; @@ -375,6 +376,8 @@ virDomainObjSetState; virDomainObjTaint; virDomainObjUnlock; virDomainObjUnref; +virDomainRedirdevBusTypeFromString; +virDomainRedirdevBusTypeToString; virDomainRemoveInactive; virDomainSaveConfig; virDomainSaveStatus; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index eaab981..1ea9f20 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -605,6 +605,33 @@ qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev int +qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx) +{ + if (idx == -1) { + int i; + idx = 0; + for (i = 0 ; i < def->nredirdevs ; i++) { + int thisidx; + if ((thisidx = qemuDomainDeviceAliasIndex(&def->redirdevs[i]->info, "redir")) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to determine device index for redirected device")); + return -1; + } + if (thisidx >= idx) + idx = thisidx + 1; + } + } + + if (virAsprintf(&redirdev->info.alias, "redir%d", idx) < 0) { + virReportOOMError(); + return -1; + } + + return 0; +} + + +int qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller) { const char *prefix = virDomainControllerTypeToString(controller->type); @@ -651,6 +678,10 @@ qemuAssignDeviceAliases(virDomainDefPtr def, virBitmapPtr qemuCaps) if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0) return -1; } + for (i = 0; i < def->nredirdevs ; i++) { + if (qemuAssignDeviceRedirdevAlias(def, def->redirdevs[i], i) < 0) + return -1; + } for (i = 0; i < def->nvideos ; i++) { if (virAsprintf(&def->videos[i]->info.alias, "video%d", i) < 0) goto no_memory; @@ -2330,6 +2361,45 @@ qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev) char * +qemuBuildRedirdevDevStr(virDomainRedirdevDefPtr dev, + virBitmapPtr qemuCaps) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (dev->bus != VIR_DOMAIN_REDIRDEV_BUS_USB) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Redirection bus %s is not supported by QEMU"), + virDomainRedirdevBusTypeToString(dev->bus)); + goto error; + } + + if (!qemuCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("USB redirection is not supported " + "by this version of QEMU")); + goto error; + } + + virBufferAsprintf(&buf, "usb-redir,chardev=char%s,id=%s", + dev->info.alias, + dev->info.alias); + + if (qemuBuildDeviceAddressStr(&buf, &dev->info, qemuCaps) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + +char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev, virBitmapPtr qemuCaps) { @@ -4820,6 +4890,32 @@ qemuBuildCommandLine(virConnectPtr conn, virCommandAddArgList(cmd, "-watchdog-action", action, NULL); } + /* Add redirected devices */ + for (i = 0 ; i < def->nredirdevs ; i++) { + virDomainRedirdevDefPtr redirdev = def->redirdevs[i]; + char *devstr; + + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(&redirdev->source.chr, + redirdev->info.alias, + qemuCaps))) { + goto error; + } + + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + if (!qemuCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) + goto error; + + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildRedirdevDevStr(redirdev, qemuCaps))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } + + /* Add host passthrough hardware */ for (i = 0 ; i < def->nhostdevs ; i++) { virDomainHostdevDefPtr hostdev = def->hostdevs[i]; diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 22bc15d..00e58a2 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -120,6 +120,7 @@ char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev, virBitmapPtr qemuCaps); char * qemuBuildHubDevStr(virDomainHubDefPtr dev, virBitmapPtr qemuCaps); +char * qemuBuildRedirdevDevStr(virDomainRedirdevDefPtr dev, virBitmapPtr qemuCaps); int qemuNetworkIfaceConnect(virDomainDefPtr def, @@ -189,8 +190,9 @@ int qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr ad int qemuDomainNetVLAN(virDomainNetDefPtr def); int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx); int qemuAssignDeviceDiskAlias(virDomainDiskDefPtr def, virBitmapPtr qemuCaps); -int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr net, int idx); +int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int idx); int qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller); +int qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx); int qemuParseKeywords(const char *str, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 421a98e..cbc31e6 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4908,6 +4908,13 @@ qemuDomainAttachDeviceLive(virDomainObjPtr vm, dev->data.hostdev = NULL; break; + case VIR_DOMAIN_DEVICE_REDIRDEV: + ret = qemuDomainAttachRedirdevDevice(driver, vm, + dev->data.redirdev); + if (!ret) + dev->data.redirdev = NULL; + break; + default: qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("device type '%s' cannot be attached"), diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 60cd241..6ae834c 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -911,6 +911,49 @@ error: } +int qemuDomainAttachRedirdevDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainRedirdevDefPtr redirdev) +{ + int ret; + qemuDomainObjPrivatePtr priv = vm->privateData; + char *devstr = NULL; + + if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) { + if (qemuAssignDeviceRedirdevAlias(vm->def, redirdev, -1) < 0) + goto error; + if (!(devstr = qemuBuildRedirdevDevStr(redirdev, priv->qemuCaps))) + goto error; + } + + if (VIR_REALLOC_N(vm->def->redirdevs, vm->def->nredirdevs+1) < 0) { + virReportOOMError(); + goto error; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE)) + ret = qemuMonitorAddDevice(priv->mon, devstr); + else + goto error; + + qemuDomainObjExitMonitorWithDriver(driver, vm); + virDomainAuditRedirdev(vm, redirdev, "attach", ret == 0); + if (ret < 0) + goto error; + + vm->def->redirdevs[vm->def->nredirdevs++] = redirdev; + + VIR_FREE(devstr); + + return 0; + +error: + VIR_FREE(devstr); + return -1; + +} + int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h index 009f1f6..ea1cca0 100644 --- a/src/qemu/qemu_hotplug.h +++ b/src/qemu/qemu_hotplug.h @@ -53,6 +53,9 @@ int qemuDomainAttachHostPciDevice(struct qemud_driver *driver, int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev); +int qemuDomainAttachRedirdevDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainRedirdevDefPtr hostdev); int qemuDomainAttachHostDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev); diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args b/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args new file mode 100644 index 0000000..445aa5f --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.args @@ -0,0 +1,8 @@ +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefconfig -nodefaults -chardev socket,id=charmonitor,path=/tmp/test-monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=readline -no-acpi -boot c \ +-device ich9-usb-ehci1,id=usb,bus=pci.0,multifunction=on,addr=0x4.0x7 \ +-device ich9-usb-uhci1,masterbus=usb.0,firstport=0,bus=pci.0,multifunction=on,addr=0x4.0x0 \ +-device ich9-usb-uhci2,masterbus=usb.0,firstport=2,bus=pci.0,multifunction=on,addr=0x4.0x1 \ +-device ich9-usb-uhci3,masterbus=usb.0,firstport=4,bus=pci.0,multifunction=on,addr=0x4.0x2 \ +-chardev socket,id=charredir0,host=localhost,port=4000 \ +-device usb-redir,chardev=charredir0,id=redir0 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,multifunction=on,addr=0x3.0x0 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml b/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml new file mode 100644 index 0000000..d644216 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-usb-redir.xml @@ -0,0 +1,38 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219136</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> + <controller type='usb' index='0' model='ich9-ehci1'> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x7'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci1'> + <master startport='0'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci2'> + <master startport='2'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x1'/> + </controller> + <controller type='usb' index='0' model='ich9-uhci3'> + <master startport='4'/> + <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x2'/> + </controller> + <redirdev bus='usb' type='tcp'> + <source mode='connect' host='localhost' service='4000'/> + <protocol type='raw'/> + </redirdev> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 9a8ecca..35e6d27 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -508,6 +508,10 @@ mymain(void) QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG, QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_PIIX3_USB_UHCI, QEMU_CAPS_USB_HUB, QEMU_CAPS_ICH9_USB_EHCI1); + DO_TEST("usb-redir", false, + QEMU_CAPS_CHARDEV, QEMU_CAPS_DEVICE, QEMU_CAPS_NODEFCONFIG, + QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_USB_HUB, + QEMU_CAPS_ICH9_USB_EHCI1, QEMU_CAPS_USB_REDIR); DO_TEST("smbios", false, QEMU_CAPS_SMBIOS_TYPE); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 4e109d5..af635d9 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -189,6 +189,8 @@ mymain(void) DO_TEST("lease"); DO_TEST("event_idx"); + DO_TEST("usb-redir"); + /* These tests generate different XML */ DO_TEST_DIFFERENT("balloon-device-auto"); DO_TEST_DIFFERENT("channel-virtio-auto"); -- 1.7.6 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list