The QEMU driver file is far too large. Move all the hotplug
helper code out into a separate file. No functional change.
* src/qemu/qemu_hotplug.c, src/qemu/qemu_hotplug.h,
src/Makefile.am: Add hotplug helper file
* src/qemu/qemu_driver.c: Delete hotplug code
---
src/Makefile.am | 1 +
src/qemu/qemu_driver.c | 2019 ++++------------------------------------------
src/qemu/qemu_hotplug.c | 1711 +++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_hotplug.h | 103 +++
4 files changed, 1991 insertions(+), 1843 deletions(-)
create mode 100644 src/qemu/qemu_hotplug.c
create mode 100644 src/qemu/qemu_hotplug.h
diff --git a/src/Makefile.am b/src/Makefile.am
index d30774b..64e890f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -273,6 +273,7 @@ QEMU_DRIVER_SOURCES = \
qemu/qemu_audit.c qemu/qemu_audit.h \
qemu/qemu_cgroup.c qemu/qemu_cgroup.c \
qemu/qemu_hostdev.c qemu/qemu_hostdev.h \
+ qemu/qemu_hotplug.c qemu/qemu_hotplug.h \
qemu/qemu_conf.c qemu/qemu_conf.h \
qemu/qemu_monitor.c qemu/qemu_monitor.h \
qemu/qemu_monitor_text.c \
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 1c4cced..b95c405 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -57,6 +57,7 @@
#include "qemu_command.h"
#include "qemu_cgroup.h"
#include "qemu_hostdev.h"
+#include "qemu_hotplug.h"
#include "qemu_monitor.h"
#include "qemu_bridge_filter.h"
#include "qemu_audit.h"
@@ -6470,1952 +6471,284 @@ cleanup:
}
-static int qemudDomainChangeEjectableMedia(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDiskDefPtr disk,
- unsigned long long qemuCmdFlags,
- bool force)
-{
- virDomainDiskDefPtr origdisk = NULL;
- int i;
- int ret;
- char *driveAlias = NULL;
-
- origdisk = NULL;
- for (i = 0 ; i < vm->def->ndisks ; i++) {
- if (vm->def->disks[i]->bus == disk->bus &&
- STREQ(vm->def->disks[i]->dst, disk->dst)) {
- origdisk = vm->def->disks[i];
- break;
- }
- }
-
- if (!origdisk) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("No device with bus '%s' and target
'%s'"),
- virDomainDiskBusTypeToString(disk->bus),
- disk->dst);
- return -1;
- }
-
- if (!origdisk->info.alias) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("missing disk device alias name for %s"),
origdisk->dst);
- return -1;
- }
-
- if (origdisk->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
- origdisk->device != VIR_DOMAIN_DISK_DEVICE_CDROM) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Removable media not supported for %s device"),
- virDomainDiskDeviceTypeToString(disk->device));
- return -1;
- }
-
- if (driver->securityDriver &&
- driver->securityDriver->domainSetSecurityImageLabel &&
-
driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver,
- vm, disk) < 0)
- return -1;
-
- if (!(driveAlias = qemuDeviceDriveHostAlias(origdisk, qemuCmdFlags)))
- goto error;
-
- qemuDomainObjPrivatePtr priv = vm->privateData;
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (disk->src) {
- const char *format = NULL;
- if (disk->type != VIR_DOMAIN_DISK_TYPE_DIR) {
- if (disk->driverType)
- format = disk->driverType;
- else if (origdisk->driverType)
- format = origdisk->driverType;
- }
- ret = qemuMonitorChangeMedia(priv->mon,
- driveAlias,
- disk->src, format);
- } else {
- ret = qemuMonitorEjectMedia(priv->mon, driveAlias, force);
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- qemuDomainDiskAudit(vm, origdisk, disk, "update", ret >= 0);
-
- if (ret < 0)
- goto error;
-
- if (driver->securityDriver &&
- driver->securityDriver->domainRestoreSecurityImageLabel &&
-
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
- vm, origdisk) < 0)
- VIR_WARN("Unable to restore security label on ejected image %s",
origdisk->src);
-
- VIR_FREE(origdisk->src);
- origdisk->src = disk->src;
- disk->src = NULL;
- origdisk->type = disk->type;
-
- VIR_FREE(driveAlias);
-
- virDomainDiskDefFree(disk);
-
- return ret;
-
-error:
- VIR_FREE(driveAlias);
- if (driver->securityDriver &&
- driver->securityDriver->domainRestoreSecurityImageLabel &&
-
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
- vm, disk) < 0)
- VIR_WARN("Unable to restore security label on new media %s",
disk->src);
- return -1;
-}
-
-
-static int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDiskDefPtr disk,
- unsigned long long qemuCmdFlags)
-{
- int i, ret;
- const char* type = virDomainDiskBusTypeToString(disk->bus);
- qemuDomainObjPrivatePtr priv = vm->privateData;
- char *devstr = NULL;
- char *drivestr = NULL;
-
- for (i = 0 ; i < vm->def->ndisks ; i++) {
- if (STREQ(vm->def->disks[i]->dst, disk->dst)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("target %s already exists"), disk->dst);
- return -1;
- }
- }
-
- if (driver->securityDriver &&
- driver->securityDriver->domainSetSecurityImageLabel &&
-
driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver,
- vm, disk) < 0)
- return -1;
-
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &disk->info) <
0)
- goto error;
- if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0)
- goto error;
-
- if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags)))
- goto error;
-
- if (!(devstr = qemuBuildDriveDevStr(disk)))
- goto error;
- }
-
- if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) {
- virReportOOMError();
- goto error;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- ret = qemuMonitorAddDrive(priv->mon, drivestr);
- if (ret == 0) {
- ret = qemuMonitorAddDevice(priv->mon, devstr);
- if (ret < 0) {
- VIR_WARN("qemuMonitorAddDevice failed on %s (%s)",
- drivestr, devstr);
- /* XXX should call 'drive_del' on error but this does not
- exist yet */
- }
- }
- } else {
- virDomainDevicePCIAddress guestAddr;
- ret = qemuMonitorAddPCIDisk(priv->mon,
- disk->src,
- type,
- &guestAddr);
- if (ret == 0) {
- disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
- memcpy(&disk->info.addr.pci, &guestAddr, sizeof(guestAddr));
- }
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0);
-
- if (ret < 0)
- goto error;
-
- virDomainDiskInsertPreAlloced(vm->def, disk);
-
- VIR_FREE(devstr);
- VIR_FREE(drivestr);
-
- return 0;
-
-error:
- VIR_FREE(devstr);
- VIR_FREE(drivestr);
-
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
- (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
- qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &disk->info) < 0)
- VIR_WARN("Unable to release PCI address on %s", disk->src);
-
- if (driver->securityDriver &&
- driver->securityDriver->domainRestoreSecurityImageLabel &&
-
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
- vm, disk) < 0)
- VIR_WARN("Unable to restore security label on %s", disk->src);
-
- return -1;
-}
-
-
-static int qemudDomainAttachPciControllerDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainControllerDefPtr controller,
- unsigned long long qemuCmdFlags)
-{
- int i;
- int ret = -1;
- const char* type = virDomainControllerTypeToString(controller->type);
- char *devstr = NULL;
- qemuDomainObjPrivatePtr priv = vm->privateData;
-
- for (i = 0 ; i < vm->def->ncontrollers ; i++) {
- if ((vm->def->controllers[i]->type == controller->type) &&
- (vm->def->controllers[i]->idx == controller->idx)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("target %s:%d already exists"),
- type, controller->idx);
- return -1;
- }
- }
-
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &controller->info)
< 0)
- goto cleanup;
- if (qemuAssignDeviceControllerAlias(controller) < 0)
- goto cleanup;
-
- if (!(devstr = qemuBuildControllerDevStr(controller))) {
- virReportOOMError();
- goto cleanup;
- }
- }
-
- if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers+1) < 0)
{
- virReportOOMError();
- goto cleanup;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- ret = qemuMonitorAddDevice(priv->mon, devstr);
- } else {
- ret = qemuMonitorAttachPCIDiskController(priv->mon,
- type,
- &controller->info.addr.pci);
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- if (ret == 0) {
- controller->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
- virDomainControllerInsertPreAlloced(vm->def, controller);
- }
-
-cleanup:
- if ((ret != 0) &&
- (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
- (controller->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
- qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &controller->info) <
0)
- VIR_WARN0("Unable to release PCI address on controller");
-
- VIR_FREE(devstr);
- return ret;
-}
-
-
-static virDomainControllerDefPtr
-qemuDomainFindOrCreateSCSIDiskController(struct qemud_driver *driver,
- virDomainObjPtr vm,
- int controller,
- unsigned long long qemuCmdFlags)
-{
- int i;
- virDomainControllerDefPtr cont;
- for (i = 0 ; i < vm->def->ncontrollers ; i++) {
- cont = vm->def->controllers[i];
-
- if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI)
- continue;
-
- if (cont->idx == controller)
- return cont;
- }
-
- /* No SCSI controller present, for backward compatibility we
- * now hotplug a controller */
- if (VIR_ALLOC(cont) < 0) {
- virReportOOMError();
- return NULL;
- }
- cont->type = VIR_DOMAIN_CONTROLLER_TYPE_SCSI;
- cont->idx = 0;
- cont->model = -1;
-
- VIR_INFO0("No SCSI controller present, hotplugging one");
- if (qemudDomainAttachPciControllerDevice(driver,
- vm, cont, qemuCmdFlags) < 0) {
- VIR_FREE(cont);
- return NULL;
- }
-
- if (!virDomainObjIsActive(vm)) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("guest unexpectedly quit"));
- /* cont doesn't need freeing here, since the reference
- * now held in def->controllers */
- return NULL;
- }
-
- return cont;
-}
-
-
-static int qemudDomainAttachSCSIDisk(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDiskDefPtr disk,
- unsigned long long qemuCmdFlags)
-{
- int i;
- qemuDomainObjPrivatePtr priv = vm->privateData;
- virDomainControllerDefPtr cont = NULL;
- char *drivestr = NULL;
- char *devstr = NULL;
- int ret = -1;
-
- for (i = 0 ; i < vm->def->ndisks ; i++) {
- if (STREQ(vm->def->disks[i]->dst, disk->dst)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("target %s already exists"), disk->dst);
- return -1;
- }
- }
-
-
- if (driver->securityDriver &&
- driver->securityDriver->domainSetSecurityImageLabel &&
-
driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver,
- vm, disk) < 0)
- return -1;
-
- /* We should have an address already, so make sure */
- if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("unexpected disk address type %s"),
- virDomainDeviceAddressTypeToString(disk->info.type));
- goto error;
- }
-
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0)
- goto error;
- if (!(devstr = qemuBuildDriveDevStr(disk)))
- goto error;
- }
-
- if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags)))
- goto error;
-
- for (i = 0 ; i <= disk->info.addr.drive.controller ; i++) {
- cont = qemuDomainFindOrCreateSCSIDiskController(driver, vm, i, qemuCmdFlags);
- if (!cont)
- goto error;
- }
-
- /* Tell clang that "cont" is non-NULL.
- This is because disk->info.addr.driver.controller is unsigned,
- and hence the above loop must iterate at least once. */
- sa_assert (cont);
-
- if (cont->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("SCSI controller %d was missing its PCI address"),
cont->idx);
- goto error;
- }
-
- if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) {
- virReportOOMError();
- goto error;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- ret = qemuMonitorAddDrive(priv->mon, drivestr);
- if (ret == 0) {
- ret = qemuMonitorAddDevice(priv->mon, devstr);
- if (ret < 0) {
- VIR_WARN("qemuMonitorAddDevice failed on %s (%s)",
- drivestr, devstr);
- /* XXX should call 'drive_del' on error but this does not
- exist yet */
- }
- }
- } else {
- virDomainDeviceDriveAddress driveAddr;
- ret = qemuMonitorAttachDrive(priv->mon,
- drivestr,
- &cont->info.addr.pci,
- &driveAddr);
- if (ret == 0) {
- /* XXX we should probably validate that the addr matches
- * our existing defined addr instead of overwriting */
- disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
- memcpy(&disk->info.addr.drive, &driveAddr, sizeof(driveAddr));
- }
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0);
-
- if (ret < 0)
- goto error;
-
- virDomainDiskInsertPreAlloced(vm->def, disk);
-
- VIR_FREE(devstr);
- VIR_FREE(drivestr);
-
- return 0;
-
-error:
- VIR_FREE(devstr);
- VIR_FREE(drivestr);
-
- if (driver->securityDriver &&
- driver->securityDriver->domainRestoreSecurityImageLabel &&
-
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
- vm, disk) < 0)
- VIR_WARN("Unable to restore security label on %s", disk->src);
-
- return -1;
-}
-
-
-static int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDiskDefPtr disk,
- unsigned long long qemuCmdFlags)
-{
- qemuDomainObjPrivatePtr priv = vm->privateData;
- int i, ret;
- char *drivestr = NULL;
- char *devstr = NULL;
-
- for (i = 0 ; i < vm->def->ndisks ; i++) {
- if (STREQ(vm->def->disks[i]->dst, disk->dst)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("target %s already exists"), disk->dst);
- return -1;
- }
- }
-
- if (driver->securityDriver &&
- driver->securityDriver->domainSetSecurityImageLabel &&
-
driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver,
- vm, disk) < 0)
- return -1;
-
- if (!disk->src) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("disk source path is missing"));
- goto error;
- }
-
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0)
- goto error;
- if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags)))
- goto error;
- if (!(devstr = qemuBuildDriveDevStr(disk)))
- goto error;
- }
-
- if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) {
- virReportOOMError();
- goto error;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- ret = qemuMonitorAddDrive(priv->mon, drivestr);
- if (ret == 0) {
- ret = qemuMonitorAddDevice(priv->mon, devstr);
- if (ret < 0) {
- VIR_WARN("qemuMonitorAddDevice failed on %s (%s)",
- drivestr, devstr);
- /* XXX should call 'drive_del' on error but this does not
- exist yet */
- }
- }
- } else {
- ret = qemuMonitorAddUSBDisk(priv->mon, disk->src);
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0);
-
- if (ret < 0)
- goto error;
-
- virDomainDiskInsertPreAlloced(vm->def, disk);
-
- VIR_FREE(devstr);
- VIR_FREE(drivestr);
-
- return 0;
-
-error:
- VIR_FREE(devstr);
- VIR_FREE(drivestr);
-
- if (driver->securityDriver &&
- driver->securityDriver->domainRestoreSecurityImageLabel &&
-
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
- vm, disk) < 0)
- VIR_WARN("Unable to restore security label on %s", disk->src);
-
- return -1;
-}
-
-
-/* XXX conn required for network -> bridge resolution */
-static int qemudDomainAttachNetDevice(virConnectPtr conn,
- struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainNetDefPtr net,
- unsigned long long qemuCmdFlags)
-{
- qemuDomainObjPrivatePtr priv = vm->privateData;
- char *tapfd_name = NULL;
- int tapfd = -1;
- char *nicstr = NULL;
- char *netstr = NULL;
- int ret = -1;
- virDomainDevicePCIAddress guestAddr;
- int vlan;
-
- if (!(qemuCmdFlags & QEMUD_CMD_FLAG_HOST_NET_ADD)) {
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("installed qemu version does not support
host_net_add"));
- return -1;
- }
-
- if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE ||
- net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
- if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("network device type '%s' cannot be attached:
"
- "qemu is not using a unix socket monitor"),
- virDomainNetTypeToString(net->type));
- return -1;
- }
-
- if ((tapfd = qemuNetworkIfaceConnect(conn, driver, net, qemuCmdFlags)) < 0)
- return -1;
- } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("network device type '%s' cannot be attached:
"
- "qemu is not using a unix socket monitor"),
- virDomainNetTypeToString(net->type));
- return -1;
- }
-
- if ((tapfd = qemuPhysIfaceConnect(conn, driver, net,
- qemuCmdFlags,
- vm->def->uuid,
- VIR_VM_OP_CREATE)) < 0)
- return -1;
- }
-
- if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0)
- goto no_memory;
-
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_NET_NAME) ||
- (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0)
- goto cleanup;
- }
-
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
- qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &net->info) < 0)
- goto cleanup;
-
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
- (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- vlan = -1;
- } else {
- vlan = qemuDomainNetVLAN(net);
-
- if (vlan < 0) {
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("Unable to attach network devices without
vlan"));
- goto cleanup;
- }
- }
-
- if (tapfd != -1) {
- if (virAsprintf(&tapfd_name, "fd-%s", net->info.alias) < 0)
- goto no_memory;
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuMonitorSendFileHandle(priv->mon, tapfd_name, tapfd) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- goto cleanup;
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- if (!virDomainObjIsActive(vm)) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("guest unexpectedly quit"));
- goto cleanup;
- }
- }
-
- /* FIXME - need to support vhost-net here (5th arg) */
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
- (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- if (!(netstr = qemuBuildHostNetStr(net, ',',
- -1, tapfd_name, 0)))
- goto try_tapfd_close;
- } else {
- if (!(netstr = qemuBuildHostNetStr(net, ' ',
- vlan, tapfd_name, 0)))
- goto try_tapfd_close;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
- (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- if (qemuMonitorAddNetdev(priv->mon, netstr) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- qemuDomainNetAudit(vm, NULL, net, "attach", false);
- goto try_tapfd_close;
- }
- } else {
- if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- qemuDomainNetAudit(vm, NULL, net, "attach", false);
- goto try_tapfd_close;
- }
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- VIR_FORCE_CLOSE(tapfd);
-
- if (!virDomainObjIsActive(vm)) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("guest unexpectedly quit"));
- goto cleanup;
- }
-
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (!(nicstr = qemuBuildNicDevStr(net, vlan)))
- goto try_remove;
- } else {
- if (!(nicstr = qemuBuildNicStr(net, NULL, vlan)))
- goto try_remove;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuMonitorAddDevice(priv->mon, nicstr) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- qemuDomainNetAudit(vm, NULL, net, "attach", false);
- goto try_remove;
- }
- } else {
- if (qemuMonitorAddPCINetwork(priv->mon, nicstr,
- &guestAddr) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- qemuDomainNetAudit(vm, NULL, net, "attach", false);
- goto try_remove;
- }
- net->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
- memcpy(&net->info.addr.pci, &guestAddr, sizeof(guestAddr));
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- qemuDomainNetAudit(vm, NULL, net, "attach", true);
-
- ret = 0;
-
- vm->def->nets[vm->def->nnets++] = net;
-
-cleanup:
- if ((ret != 0) &&
- (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
- (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
- qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &net->info) < 0)
- VIR_WARN0("Unable to release PCI address on NIC");
-
- if (ret != 0)
- virDomainConfNWFilterTeardown(net);
-
- VIR_FREE(nicstr);
- VIR_FREE(netstr);
- VIR_FREE(tapfd_name);
- VIR_FORCE_CLOSE(tapfd);
-
- return ret;
-
-try_remove:
- if (!virDomainObjIsActive(vm))
- goto cleanup;
-
- if (vlan < 0) {
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
- (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- char *netdev_name;
- if (virAsprintf(&netdev_name, "host%s", net->info.alias)
< 0)
- goto no_memory;
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuMonitorRemoveNetdev(priv->mon, netdev_name) < 0)
- VIR_WARN("Failed to remove network backend for netdev %s",
- netdev_name);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- VIR_FREE(netdev_name);
- } else {
- VIR_WARN0("Unable to remove network backend");
- }
- } else {
- char *hostnet_name;
- if (virAsprintf(&hostnet_name, "host%s", net->info.alias) <
0)
- goto no_memory;
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0)
- VIR_WARN("Failed to remove network backend for vlan %d, net %s",
- vlan, hostnet_name);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- VIR_FREE(hostnet_name);
- }
- goto cleanup;
-
-try_tapfd_close:
- if (!virDomainObjIsActive(vm))
- goto cleanup;
-
- if (tapfd_name) {
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuMonitorCloseFileHandle(priv->mon, tapfd_name) < 0)
- VIR_WARN("Failed to close tapfd with '%s'", tapfd_name);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- }
-
- goto cleanup;
-
-no_memory:
- virReportOOMError();
- goto cleanup;
-}
-
-
-static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainHostdevDefPtr hostdev,
- unsigned long long qemuCmdFlags)
-{
- qemuDomainObjPrivatePtr priv = vm->privateData;
- int ret;
- char *devstr = NULL;
- int configfd = -1;
- char *configfd_name = NULL;
-
- if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
- virReportOOMError();
- return -1;
- }
-
- if (qemuPrepareHostdevPCIDevices(driver, &hostdev, 1) < 0)
- return -1;
-
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0)
- goto error;
- if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) <
0)
- goto error;
- if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
- configfd = qemuOpenPCIConfig(hostdev);
- if (configfd >= 0) {
- if (virAsprintf(&configfd_name, "fd-%s",
- hostdev->info.alias) < 0) {
- virReportOOMError();
- goto error;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuMonitorSendFileHandle(priv->mon, configfd_name,
- configfd) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- goto error;
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- }
- }
-
- if (!virDomainObjIsActive(vm)) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("guest unexpectedly quit during hotplug"));
- goto error;
- }
-
- if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name)))
- goto error;
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- ret = qemuMonitorAddDevice(priv->mon, devstr);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- } else {
- virDomainDevicePCIAddress guestAddr;
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- ret = qemuMonitorAddPCIHostDevice(priv->mon,
- &hostdev->source.subsys.u.pci,
- &guestAddr);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- hostdev->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
- memcpy(&hostdev->info.addr.pci, &guestAddr, sizeof(guestAddr));
- }
- if (ret < 0)
- goto error;
-
- vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
-
- VIR_FREE(devstr);
- VIR_FREE(configfd_name);
- VIR_FORCE_CLOSE(configfd);
-
- return 0;
-
-error:
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
- (hostdev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
- qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &hostdev->info) <
0)
- VIR_WARN0("Unable to release PCI address on host device");
-
- qemuDomainReAttachHostdevDevices(driver, &hostdev, 1);
-
- VIR_FREE(devstr);
- VIR_FREE(configfd_name);
- VIR_FORCE_CLOSE(configfd);
-
- return -1;
-}
-
-
-static int qemudDomainAttachHostUsbDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainHostdevDefPtr hostdev,
- unsigned long long qemuCmdFlags)
-{
- int ret;
- qemuDomainObjPrivatePtr priv = vm->privateData;
- char *devstr = NULL;
-
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0)
- goto error;
- if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev)))
- goto error;
- }
-
- if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
- virReportOOMError();
- goto error;
- }
-
- if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
- virCgroupPtr cgroup = NULL;
- usbDevice *usb;
-
- if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0)
!=0 ) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Unable to find cgroup for %s\n"),
- vm->def->name);
- goto error;
- }
-
- if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus,
- hostdev->source.subsys.u.usb.device)) == NULL)
- goto error;
-
- if (usbDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, cgroup) < 0 )
- goto error;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)
- ret = qemuMonitorAddDevice(priv->mon, devstr);
- else
- ret = qemuMonitorAddUSBDeviceExact(priv->mon,
- hostdev->source.subsys.u.usb.bus,
- hostdev->source.subsys.u.usb.device);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- if (ret < 0)
- goto error;
-
- vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
-
- VIR_FREE(devstr);
-
- return 0;
-
-error:
- VIR_FREE(devstr);
- return -1;
-}
-
-
-static int qemudDomainAttachHostDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainHostdevDefPtr hostdev,
- unsigned long long qemuCmdFlags)
-{
- if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("hostdev mode '%s' not supported"),
- virDomainHostdevModeTypeToString(hostdev->mode));
- return -1;
- }
-
- /* Resolve USB product/vendor to bus/device */
- if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
- hostdev->source.subsys.u.usb.vendor) {
- usbDevice *usb
- = usbFindDevice(hostdev->source.subsys.u.usb.vendor,
- hostdev->source.subsys.u.usb.product);
-
- if (!usb)
- return -1;
-
- hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
- hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
-
- usbFreeDevice(usb);
- }
-
-
- if (driver->securityDriver &&
- driver->securityDriver->domainSetSecurityHostdevLabel &&
-
driver->securityDriver->domainSetSecurityHostdevLabel(driver->securityDriver,
- vm, hostdev) < 0)
- return -1;
-
- switch (hostdev->source.subsys.type) {
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
- if (qemudDomainAttachHostPciDevice(driver, vm,
- hostdev, qemuCmdFlags) < 0)
- goto error;
- break;
-
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
- if (qemudDomainAttachHostUsbDevice(driver, vm,
- hostdev, qemuCmdFlags) < 0)
- goto error;
- break;
-
- default:
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("hostdev subsys type '%s' not supported"),
-
virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
- goto error;
- }
-
- return 0;
-
-error:
- if (driver->securityDriver &&
- driver->securityDriver->domainRestoreSecurityHostdevLabel &&
-
driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver,
- vm, hostdev) < 0)
- VIR_WARN0("Unable to restore host device labelling on hotplug fail");
-
- return -1;
-}
-
-
-static int qemudDomainAttachDevice(virDomainPtr dom,
- const char *xml)
-{
- struct qemud_driver *driver = dom->conn->privateData;
- virDomainObjPtr vm;
- virDomainDeviceDefPtr dev = NULL;
- unsigned long long qemuCmdFlags;
- virCgroupPtr cgroup = NULL;
- int ret = -1;
-
- qemuDriverLock(driver);
- vm = virDomainFindByUUID(&driver->domains, dom->uuid);
- if (!vm) {
- char uuidstr[VIR_UUID_STRING_BUFLEN];
- virUUIDFormat(dom->uuid, uuidstr);
- qemuReportError(VIR_ERR_NO_DOMAIN,
- _("no domain with matching uuid '%s'"),
uuidstr);
- goto cleanup;
- }
-
- if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
- goto cleanup;
-
- if (!virDomainObjIsActive(vm)) {
- qemuReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("cannot attach device on inactive
domain"));
- goto endjob;
- }
-
- dev = virDomainDeviceDefParse(driver->caps, vm->def, xml,
- VIR_DOMAIN_XML_INACTIVE);
- if (dev == NULL)
- goto endjob;
-
- if (qemuCapsExtractVersionInfo(vm->def->emulator,
- NULL,
- &qemuCmdFlags) < 0)
- goto endjob;
-
- if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
- if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
- if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup,
0) !=0 ) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Unable to find cgroup for %s\n"),
- vm->def->name);
- goto endjob;
- }
- if (qemuSetupDiskCgroup(driver, cgroup, dev->data.disk) < 0)
- goto endjob;
- }
-
- switch (dev->data.disk->device) {
- case VIR_DOMAIN_DISK_DEVICE_CDROM:
- case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
- ret = qemudDomainChangeEjectableMedia(driver, vm,
- dev->data.disk,
- qemuCmdFlags,
- false);
- if (ret == 0)
- dev->data.disk = NULL;
- break;
-
- case VIR_DOMAIN_DISK_DEVICE_DISK:
- if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
- ret = qemudDomainAttachUsbMassstorageDevice(driver, vm,
- dev->data.disk,
qemuCmdFlags);
- if (ret == 0)
- dev->data.disk = NULL;
- } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) {
- ret = qemudDomainAttachPciDiskDevice(driver, vm,
- dev->data.disk, qemuCmdFlags);
- if (ret == 0)
- dev->data.disk = NULL;
- } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) {
- ret = qemudDomainAttachSCSIDisk(driver, vm,
- dev->data.disk, qemuCmdFlags);
- if (ret == 0)
- dev->data.disk = NULL;
- } else {
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("disk bus '%s' cannot be
hotplugged."),
-
virDomainDiskBusTypeToString(dev->data.disk->bus));
- /* fallthrough */
- }
- break;
-
- default:
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("disk device type '%s' cannot be
hotplugged"),
-
virDomainDiskDeviceTypeToString(dev->data.disk->device));
- /* Fallthrough */
- }
- if (ret != 0 && cgroup) {
- if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0)
- VIR_WARN("Failed to teardown cgroup for disk path %s",
- NULLSTR(dev->data.disk->src));
- }
- } else if (dev->type == VIR_DOMAIN_DEVICE_CONTROLLER) {
- if (dev->data.controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
- ret = qemudDomainAttachPciControllerDevice(driver, vm,
- dev->data.controller,
qemuCmdFlags);
- if (ret == 0)
- dev->data.controller = NULL;
- } else {
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("disk controller bus '%s' cannot be
hotplugged."),
-
virDomainControllerTypeToString(dev->data.controller->type));
- /* fallthrough */
- }
- } else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
- ret = qemudDomainAttachNetDevice(dom->conn, driver, vm,
-
dev->data.net, qemuCmdFlags);
- if (ret == 0)
-
dev->data.net = NULL;
- } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
- ret = qemudDomainAttachHostDevice(driver, vm,
- dev->data.hostdev, qemuCmdFlags);
- if (ret == 0)
- dev->data.hostdev = NULL;
- } else {
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("device type '%s' cannot be attached"),
- virDomainDeviceTypeToString(dev->type));
- goto endjob;
- }
-
- if (!ret && virDomainSaveStatus(driver->caps, driver->stateDir, vm)
< 0)
- ret = -1;
-
-endjob:
- if (qemuDomainObjEndJob(vm) == 0)
- vm = NULL;
-
-cleanup:
- if (cgroup)
- virCgroupFree(&cgroup);
-
- virDomainDeviceDefFree(dev);
- if (vm)
- virDomainObjUnlock(vm);
- qemuDriverUnlock(driver);
- return ret;
-}
-
-static int qemudDomainAttachDeviceFlags(virDomainPtr dom,
- const char *xml,
- unsigned int flags) {
- if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
- qemuReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("cannot modify the persistent
configuration of a domain"));
- return -1;
- }
-
- return qemudDomainAttachDevice(dom, xml);
-}
-
-
-static virDomainGraphicsDefPtr qemuDomainFindGraphics(virDomainObjPtr vm,
- virDomainGraphicsDefPtr dev)
-{
- int i;
-
- for (i = 0 ; i < vm->def->ngraphics ; i++) {
- if (vm->def->graphics[i]->type == dev->type)
- return vm->def->graphics[i];
- }
-
- return NULL;
-}
-
-
-static int
-qemuDomainChangeGraphics(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainGraphicsDefPtr dev)
-{
- virDomainGraphicsDefPtr olddev = qemuDomainFindGraphics(vm, dev);
- qemuDomainObjPrivatePtr priv = vm->privateData;
- int ret = -1;
-
- if (!olddev) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("cannot find existing graphics device to modify"));
- return -1;
- }
-
- switch (dev->type) {
- case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
- if ((olddev->data.vnc.autoport != dev->data.vnc.autoport) ||
- (!dev->data.vnc.autoport && (olddev->data.vnc.port !=
dev->data.vnc.port))) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("cannot change port settings on vnc graphics"));
- return -1;
- }
- if (STRNEQ_NULLABLE(olddev->data.vnc.listenAddr, dev->data.vnc.listenAddr))
{
- qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("cannot change listen address setting on vnc
graphics"));
- return -1;
- }
- if (STRNEQ_NULLABLE(olddev->data.vnc.keymap, dev->data.vnc.keymap)) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("cannot change keymap setting on vnc
graphics"));
- return -1;
- }
-
- 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.auth.passwd ?
- dev->data.vnc.auth.passwd :
- driver->vncPassword);
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- /* Steal the new dev's char * reference */
- 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;
- }
- break;
-
- default:
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("unable to change config on '%s' graphics
type"),
- virDomainGraphicsTypeToString(dev->type));
- break;
- }
-
- return ret;
-}
-
-
-static int qemuDomainUpdateDeviceFlags(virDomainPtr dom,
- const char *xml,
- unsigned int flags)
-{
- struct qemud_driver *driver = dom->conn->privateData;
- virDomainObjPtr vm;
- virDomainDeviceDefPtr dev = NULL;
- unsigned long long qemuCmdFlags;
- virCgroupPtr cgroup = NULL;
- int ret = -1;
- bool force = (flags & VIR_DOMAIN_DEVICE_MODIFY_FORCE) != 0;
-
- virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
- VIR_DOMAIN_DEVICE_MODIFY_LIVE |
- VIR_DOMAIN_DEVICE_MODIFY_CONFIG |
- VIR_DOMAIN_DEVICE_MODIFY_FORCE, -1);
-
- if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
- qemuReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("cannot modify the persistent
configuration of a domain"));
- return -1;
- }
-
- qemuDriverLock(driver);
- vm = virDomainFindByUUID(&driver->domains, dom->uuid);
- if (!vm) {
- char uuidstr[VIR_UUID_STRING_BUFLEN];
- virUUIDFormat(dom->uuid, uuidstr);
- qemuReportError(VIR_ERR_NO_DOMAIN,
- _("no domain with matching uuid '%s'"),
uuidstr);
- goto cleanup;
- }
-
- if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
- goto cleanup;
-
- if (!virDomainObjIsActive(vm)) {
- qemuReportError(VIR_ERR_OPERATION_INVALID,
- "%s", _("cannot attach device on inactive
domain"));
- goto endjob;
- }
-
- dev = virDomainDeviceDefParse(driver->caps, vm->def, xml,
- VIR_DOMAIN_XML_INACTIVE);
- if (dev == NULL)
- goto endjob;
-
- if (qemuCapsExtractVersionInfo(vm->def->emulator,
- NULL,
- &qemuCmdFlags) < 0)
- goto endjob;
-
- switch (dev->type) {
- case VIR_DOMAIN_DEVICE_DISK:
- if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
- if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup,
0) !=0 ) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Unable to find cgroup for %s\n"),
- vm->def->name);
- goto endjob;
- }
- if (qemuSetupDiskCgroup(driver, cgroup, dev->data.disk) < 0)
- goto endjob;
- }
-
- switch (dev->data.disk->device) {
- case VIR_DOMAIN_DISK_DEVICE_CDROM:
- case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
- ret = qemudDomainChangeEjectableMedia(driver, vm,
- dev->data.disk,
- qemuCmdFlags,
- force);
- if (ret == 0)
- dev->data.disk = NULL;
- break;
-
-
- default:
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("disk bus '%s' cannot be updated."),
- virDomainDiskBusTypeToString(dev->data.disk->bus));
- break;
- }
-
- if (ret != 0 && cgroup) {
- if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0)
- VIR_WARN("Failed to teardown cgroup for disk path %s",
- NULLSTR(dev->data.disk->src));
- }
- break;
-
- case VIR_DOMAIN_DEVICE_GRAPHICS:
- ret = qemuDomainChangeGraphics(driver, vm, dev->data.graphics);
- break;
-
- default:
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("disk device type '%s' cannot be updated"),
- virDomainDiskDeviceTypeToString(dev->data.disk->device));
- break;
- }
-
- if (!ret && virDomainSaveStatus(driver->caps, driver->stateDir, vm)
< 0)
- ret = -1;
-
-endjob:
- if (qemuDomainObjEndJob(vm) == 0)
- vm = NULL;
-
-cleanup:
- if (cgroup)
- virCgroupFree(&cgroup);
-
- virDomainDeviceDefFree(dev);
- if (vm)
- virDomainObjUnlock(vm);
- qemuDriverUnlock(driver);
- return ret;
-}
-
-
-static inline int qemudFindDisk(virDomainDefPtr def, const char *dst)
-{
- int i;
-
- for (i = 0 ; i < def->ndisks ; i++) {
- if (STREQ(def->disks[i]->dst, dst)) {
- return i;
- }
- }
-
- return -1;
-}
-
-
-static int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDeviceDefPtr dev,
- unsigned long long qemuCmdFlags)
-{
- int i, ret = -1;
- virDomainDiskDefPtr detach = NULL;
- qemuDomainObjPrivatePtr priv = vm->privateData;
- virCgroupPtr cgroup = NULL;
- char *drivestr = NULL;
-
- i = qemudFindDisk(vm->def, dev->data.disk->dst);
-
- if (i < 0) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("disk %s not found"), dev->data.disk->dst);
- goto cleanup;
- }
-
- detach = vm->def->disks[i];
-
- if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
- if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=
0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Unable to find cgroup for %s\n"),
- vm->def->name);
- goto cleanup;
- }
- }
-
- if (!virDomainDeviceAddressIsValid(&detach->info,
- VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
- _("device cannot be detached without a PCI address"));
- goto cleanup;
- }
-
- /* build the actual drive id string as the disk->info.alias doesn't
- * contain the QEMU_DRIVE_HOST_PREFIX that is passed to qemu */
- if (virAsprintf(&drivestr, "%s%s",
- QEMU_DRIVE_HOST_PREFIX, detach->info.alias) < 0) {
- virReportOOMError();
- goto cleanup;
- }
-
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
- qemuDomainObjExitMonitor(vm);
- goto cleanup;
- }
- } else {
- if (qemuMonitorRemovePCIDevice(priv->mon,
- &detach->info.addr.pci) < 0) {
- qemuDomainObjExitMonitor(vm);
- goto cleanup;
- }
- }
-
- /* disconnect guest from host device */
- qemuMonitorDriveDel(priv->mon, drivestr);
-
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- qemuDomainDiskAudit(vm, detach, NULL, "detach", ret >= 0);
-
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
- qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
- VIR_WARN("Unable to release PCI address on %s",
dev->data.disk->src);
-
- virDomainDiskRemove(vm->def, i);
-
- virDomainDiskDefFree(detach);
-
- if (driver->securityDriver &&
- driver->securityDriver->domainRestoreSecurityImageLabel &&
-
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
- vm, dev->data.disk)
< 0)
- VIR_WARN("Unable to restore security label on %s",
dev->data.disk->src);
-
- if (cgroup != NULL) {
- if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0)
- VIR_WARN("Failed to teardown cgroup for disk path %s",
- NULLSTR(dev->data.disk->src));
- }
-
- ret = 0;
-
-cleanup:
- VIR_FREE(drivestr);
- return ret;
-}
-
-static int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDeviceDefPtr dev,
- unsigned long long qemuCmdFlags)
+static int qemudDomainAttachDevice(virDomainPtr dom,
+ const char *xml)
{
- int i, ret = -1;
- virDomainDiskDefPtr detach = NULL;
- qemuDomainObjPrivatePtr priv = vm->privateData;
+ struct qemud_driver *driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ virDomainDeviceDefPtr dev = NULL;
+ unsigned long long qemuCmdFlags;
virCgroupPtr cgroup = NULL;
- char *drivestr = NULL;
-
- i = qemudFindDisk(vm->def, dev->data.disk->dst);
-
- if (i < 0) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("disk %s not found"), dev->data.disk->dst);
- goto cleanup;
- }
-
- if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
- _("Underlying qemu does not support SCSI disk
removal"));
- goto cleanup;
- }
-
- detach = vm->def->disks[i];
-
- if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
- if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=
0) {
- qemuReportError(VIR_ERR_INTERNAL_ERROR,
- _("Unable to find cgroup for %s\n"),
- vm->def->name);
- goto cleanup;
- }
- }
-
- /* build the actual drive id string as the disk->info.alias doesn't
- * contain the QEMU_DRIVE_HOST_PREFIX that is passed to qemu */
- if (virAsprintf(&drivestr, "%s%s",
- QEMU_DRIVE_HOST_PREFIX, detach->info.alias) < 0) {
- virReportOOMError();
- goto cleanup;
- }
+ int ret = -1;
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
- qemuDomainObjExitMonitor(vm);
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
goto cleanup;
}
- /* disconnect guest from host device */
- qemuMonitorDriveDel(priv->mon, drivestr);
-
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- qemuDomainDiskAudit(vm, detach, NULL, "detach", ret >= 0);
-
- virDomainDiskRemove(vm->def, i);
-
- virDomainDiskDefFree(detach);
-
- if (driver->securityDriver &&
- driver->securityDriver->domainRestoreSecurityImageLabel &&
-
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
- vm, dev->data.disk)
< 0)
- VIR_WARN("Unable to restore security label on %s",
dev->data.disk->src);
-
- if (cgroup != NULL) {
- if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0)
- VIR_WARN("Failed to teardown cgroup for disk path %s",
- NULLSTR(dev->data.disk->src));
- }
-
- ret = 0;
-
-cleanup:
- VIR_FREE(drivestr);
- virCgroupFree(&cgroup);
- return ret;
-}
-
-static int qemudDomainDetachPciControllerDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDeviceDefPtr dev,
- unsigned long long qemuCmdFlags)
-{
- int i, ret = -1;
- virDomainControllerDefPtr detach = NULL;
- qemuDomainObjPrivatePtr priv = vm->privateData;
-
- for (i = 0 ; i < vm->def->ncontrollers ; i++) {
- if ((vm->def->controllers[i]->type == dev->data.controller->type)
&&
- (vm->def->controllers[i]->idx == dev->data.controller->idx))
{
- detach = vm->def->controllers[i];
- break;
- }
- }
-
- if (!detach) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("disk controller %s:%d not found"),
-
virDomainControllerTypeToString(dev->data.controller->type),
- dev->data.controller->idx);
+ if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup;
- }
- if (!virDomainDeviceAddressIsValid(&detach->info,
- VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
- _("device cannot be detached without a PCI address"));
- goto cleanup;
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("cannot attach device on inactive
domain"));
+ goto endjob;
}
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuAssignDeviceControllerAlias(detach) < 0)
- goto cleanup;
- }
+ dev = virDomainDeviceDefParse(driver->caps, vm->def, xml,
+ VIR_DOMAIN_XML_INACTIVE);
+ if (dev == NULL)
+ goto endjob;
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuMonitorDelDevice(priv->mon, detach->info.alias)) {
- qemuDomainObjExitMonitor(vm);
- goto cleanup;
- }
- } else {
- if (qemuMonitorRemovePCIDevice(priv->mon,
- &detach->info.addr.pci) < 0) {
- qemuDomainObjExitMonitor(vm);
- goto cleanup;
- }
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
+ if (qemuCapsExtractVersionInfo(vm->def->emulator,
+ NULL,
+ &qemuCmdFlags) < 0)
+ goto endjob;
- if (vm->def->ncontrollers > 1) {
- memmove(vm->def->controllers + i,
- vm->def->controllers + i + 1,
- sizeof(*vm->def->controllers) *
- (vm->def->ncontrollers - (i + 1)));
- vm->def->ncontrollers--;
- if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers) <
0) {
- /* ignore, harmless */
+ if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
+ if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
+ if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup,
0) !=0 ) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find cgroup for %s\n"),
+ vm->def->name);
+ goto endjob;
+ }
+ if (qemuSetupDiskCgroup(driver, cgroup, dev->data.disk) < 0)
+ goto endjob;
}
- } else {
- VIR_FREE(vm->def->controllers);
- vm->def->ncontrollers = 0;
- }
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
- qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
- VIR_WARN0("Unable to release PCI address on controller");
-
- virDomainControllerDefFree(detach);
-
- ret = 0;
-
-cleanup:
- return ret;
-}
-
-static int
-qemudDomainDetachNetDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDeviceDefPtr dev,
- unsigned long long qemuCmdFlags)
-{
- int i, ret = -1;
- virDomainNetDefPtr detach = NULL;
- qemuDomainObjPrivatePtr priv = vm->privateData;
- int vlan;
- char *hostnet_name = NULL;
-
- for (i = 0 ; i < vm->def->nnets ; i++) {
- virDomainNetDefPtr net = vm->def->nets[i];
-
- if (!memcmp(net->mac, dev->data.net->mac, sizeof(net->mac))) {
- detach = net;
+ switch (dev->data.disk->device) {
+ case VIR_DOMAIN_DISK_DEVICE_CDROM:
+ case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
+ ret = qemudDomainChangeEjectableMedia(driver, vm,
+ dev->data.disk,
+ qemuCmdFlags,
+ false);
+ if (ret == 0)
+ dev->data.disk = NULL;
break;
- }
- }
-
- if (!detach) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("network device %02x:%02x:%02x:%02x:%02x:%02x not
found"),
- dev->data.net->mac[0], dev->data.net->mac[1],
- dev->data.net->mac[2], dev->data.net->mac[3],
- dev->data.net->mac[4], dev->data.net->mac[5]);
- goto cleanup;
- }
-
- if (!virDomainDeviceAddressIsValid(&detach->info,
- VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- "%s", _("device cannot be detached without a PCI
address"));
- goto cleanup;
- }
-
- if ((vlan = qemuDomainNetVLAN(detach)) < 0) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- "%s", _("unable to determine original
VLAN"));
- goto cleanup;
- }
- if (virAsprintf(&hostnet_name, "host%s", detach->info.alias) < 0)
{
- virReportOOMError();
- goto cleanup;
- }
+ case VIR_DOMAIN_DISK_DEVICE_DISK:
+ if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
+ ret = qemudDomainAttachUsbMassstorageDevice(driver, vm,
+ dev->data.disk,
qemuCmdFlags);
+ if (ret == 0)
+ dev->data.disk = NULL;
+ } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) {
+ ret = qemudDomainAttachPciDiskDevice(driver, vm,
+ dev->data.disk, qemuCmdFlags);
+ if (ret == 0)
+ dev->data.disk = NULL;
+ } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) {
+ ret = qemudDomainAttachSCSIDisk(driver, vm,
+ dev->data.disk, qemuCmdFlags);
+ if (ret == 0)
+ dev->data.disk = NULL;
+ } else {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("disk bus '%s' cannot be
hotplugged."),
+
virDomainDiskBusTypeToString(dev->data.disk->bus));
+ /* fallthrough */
+ }
+ break;
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
- qemuDomainObjExitMonitor(vm);
- qemuDomainNetAudit(vm, detach, NULL, "detach", false);
- goto cleanup;
+ default:
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("disk device type '%s' cannot be
hotplugged"),
+
virDomainDiskDeviceTypeToString(dev->data.disk->device));
+ /* Fallthrough */
}
- } else {
- if (qemuMonitorRemovePCIDevice(priv->mon,
- &detach->info.addr.pci) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- qemuDomainNetAudit(vm, detach, NULL, "detach", false);
- goto cleanup;
+ if (ret != 0 && cgroup) {
+ if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0)
+ VIR_WARN("Failed to teardown cgroup for disk path %s",
+ NULLSTR(dev->data.disk->src));
}
- }
-
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
- (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- qemuDomainNetAudit(vm, detach, NULL, "detach", false);
- goto cleanup;
+ } else if (dev->type == VIR_DOMAIN_DEVICE_CONTROLLER) {
+ if (dev->data.controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
+ ret = qemudDomainAttachPciControllerDevice(driver, vm,
+ dev->data.controller,
qemuCmdFlags);
+ if (ret == 0)
+ dev->data.controller = NULL;
+ } else {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("disk controller bus '%s' cannot be
hotplugged."),
+
virDomainControllerTypeToString(dev->data.controller->type));
+ /* fallthrough */
}
+ } else if (dev->type == VIR_DOMAIN_DEVICE_NET) {
+ ret = qemudDomainAttachNetDevice(dom->conn, driver, vm,
+
dev->data.net, qemuCmdFlags);
+ if (ret == 0)
+
dev->data.net = NULL;
+ } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
+ ret = qemudDomainAttachHostDevice(driver, vm,
+ dev->data.hostdev, qemuCmdFlags);
+ if (ret == 0)
+ dev->data.hostdev = NULL;
} else {
- if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- qemuDomainNetAudit(vm, detach, NULL, "detach", false);
- goto cleanup;
- }
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- qemuDomainNetAudit(vm, detach, NULL, "detach", true);
-
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
- qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
- VIR_WARN0("Unable to release PCI address on NIC");
-
- virDomainConfNWFilterTeardown(detach);
-
-#if WITH_MACVTAP
- if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
- delMacvtap(detach->ifname, detach->mac, detach->data.direct.linkdev,
- &detach->data.direct.virtPortProfile);
- VIR_FREE(detach->ifname);
- }
-#endif
-
- if ((driver->macFilter) && (detach->ifname != NULL)) {
- if ((errno = networkDisallowMacOnPort(driver,
- detach->ifname,
- detach->mac))) {
- virReportSystemError(errno,
- _("failed to remove ebtables rule on '%s'"),
- detach->ifname);
- }
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("device type '%s' cannot be attached"),
+ virDomainDeviceTypeToString(dev->type));
+ goto endjob;
}
- if (vm->def->nnets > 1) {
- memmove(vm->def->nets + i,
- vm->def->nets + i + 1,
- sizeof(*vm->def->nets) *
- (vm->def->nnets - (i + 1)));
- vm->def->nnets--;
- if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets) < 0) {
- /* ignore, harmless */
- }
- } else {
- VIR_FREE(vm->def->nets);
- vm->def->nnets = 0;
- }
- virDomainNetDefFree(detach);
+ if (!ret && virDomainSaveStatus(driver->caps, driver->stateDir, vm)
< 0)
+ ret = -1;
- ret = 0;
+endjob:
+ if (qemuDomainObjEndJob(vm) == 0)
+ vm = NULL;
cleanup:
- VIR_FREE(hostnet_name);
+ if (cgroup)
+ virCgroupFree(&cgroup);
+
+ virDomainDeviceDefFree(dev);
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
return ret;
}
-static int qemudDomainDetachHostPciDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDeviceDefPtr dev,
- unsigned long long qemuCmdFlags)
-{
- virDomainHostdevDefPtr detach = NULL;
- qemuDomainObjPrivatePtr priv = vm->privateData;
- int i, ret;
- pciDevice *pci;
-
- for (i = 0 ; i < vm->def->nhostdevs ; i++) {
- if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
- vm->def->hostdevs[i]->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
- continue;
-
- unsigned domain = vm->def->hostdevs[i]->source.subsys.u.pci.domain;
- unsigned bus = vm->def->hostdevs[i]->source.subsys.u.pci.bus;
- unsigned slot = vm->def->hostdevs[i]->source.subsys.u.pci.slot;
- unsigned function = vm->def->hostdevs[i]->source.subsys.u.pci.function;
-
- if (dev->data.hostdev->source.subsys.u.pci.domain == domain &&
- dev->data.hostdev->source.subsys.u.pci.bus == bus &&
- dev->data.hostdev->source.subsys.u.pci.slot == slot &&
- dev->data.hostdev->source.subsys.u.pci.function == function) {
- detach = vm->def->hostdevs[i];
- break;
- }
- }
-
- if (!detach) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("host pci device %.4x:%.2x:%.2x.%.1x not found"),
- dev->data.hostdev->source.subsys.u.pci.domain,
- dev->data.hostdev->source.subsys.u.pci.bus,
- dev->data.hostdev->source.subsys.u.pci.slot,
- dev->data.hostdev->source.subsys.u.pci.function);
- return -1;
- }
-
- if (!virDomainDeviceAddressIsValid(&detach->info,
- VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- "%s", _("device cannot be detached without a PCI
address"));
+static int qemudDomainAttachDeviceFlags(virDomainPtr dom,
+ const char *xml,
+ unsigned int flags) {
+ if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("cannot modify the persistent
configuration of a domain"));
return -1;
}
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
- if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
- qemuDomainObjExitMonitor(vm);
- return -1;
- }
- } else {
- if (qemuMonitorRemovePCIDevice(priv->mon,
- &detach->info.addr.pci) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- return -1;
- }
- }
- qemuDomainObjExitMonitorWithDriver(driver, vm);
-
- ret = 0;
-
- pci = pciGetDevice(detach->source.subsys.u.pci.domain,
- detach->source.subsys.u.pci.bus,
- detach->source.subsys.u.pci.slot,
- detach->source.subsys.u.pci.function);
- if (!pci)
- ret = -1;
- else {
- pciDeviceSetManaged(pci, detach->managed);
- pciDeviceListDel(driver->activePciHostdevs, pci);
- if (pciResetDevice(pci, driver->activePciHostdevs, NULL) < 0)
- ret = -1;
- qemuReattachPciDevice(pci, driver);
- pciFreeDevice(pci);
- }
-
- if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
- qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
- VIR_WARN0("Unable to release PCI address on host device");
-
- if (vm->def->nhostdevs > 1) {
- memmove(vm->def->hostdevs + i,
- vm->def->hostdevs + i + 1,
- sizeof(*vm->def->hostdevs) *
- (vm->def->nhostdevs - (i + 1)));
- vm->def->nhostdevs--;
- if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) {
- /* ignore, harmless */
- }
- } else {
- VIR_FREE(vm->def->hostdevs);
- vm->def->nhostdevs = 0;
- }
- virDomainHostdevDefFree(detach);
-
- return ret;
+ return qemudDomainAttachDevice(dom, xml);
}
-static int qemudDomainDetachHostUsbDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDeviceDefPtr dev,
- unsigned long long qemuCmdFlags)
-{
- virDomainHostdevDefPtr detach = NULL;
- qemuDomainObjPrivatePtr priv = vm->privateData;
- int i, ret;
-
- for (i = 0 ; i < vm->def->nhostdevs ; i++) {
- if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
- vm->def->hostdevs[i]->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
- continue;
- unsigned bus = vm->def->hostdevs[i]->source.subsys.u.usb.bus;
- unsigned device = vm->def->hostdevs[i]->source.subsys.u.usb.device;
- unsigned product = vm->def->hostdevs[i]->source.subsys.u.usb.product;
- unsigned vendor = vm->def->hostdevs[i]->source.subsys.u.usb.vendor;
+static int qemuDomainUpdateDeviceFlags(virDomainPtr dom,
+ const char *xml,
+ unsigned int flags)
+{
+ struct qemud_driver *driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ virDomainDeviceDefPtr dev = NULL;
+ unsigned long long qemuCmdFlags;
+ virCgroupPtr cgroup = NULL;
+ int ret = -1;
+ bool force = (flags & VIR_DOMAIN_DEVICE_MODIFY_FORCE) != 0;
- if (dev->data.hostdev->source.subsys.u.usb.bus &&
- dev->data.hostdev->source.subsys.u.usb.device) {
- if (dev->data.hostdev->source.subsys.u.usb.bus == bus &&
- dev->data.hostdev->source.subsys.u.usb.device == device) {
- detach = vm->def->hostdevs[i];
- break;
- }
- } else {
- if (dev->data.hostdev->source.subsys.u.usb.product == product
&&
- dev->data.hostdev->source.subsys.u.usb.vendor == vendor) {
- detach = vm->def->hostdevs[i];
- break;
- }
- }
- }
+ virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT |
+ VIR_DOMAIN_DEVICE_MODIFY_LIVE |
+ VIR_DOMAIN_DEVICE_MODIFY_CONFIG |
+ VIR_DOMAIN_DEVICE_MODIFY_FORCE, -1);
- if (!detach) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- _("host usb device %03d.%03d not found"),
- dev->data.hostdev->source.subsys.u.usb.bus,
- dev->data.hostdev->source.subsys.u.usb.device);
+ if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("cannot modify the persistent
configuration of a domain"));
return -1;
}
- if (!detach->info.alias) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- "%s", _("device cannot be detached without a
device alias"));
- return -1;
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ goto cleanup;
}
- if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
- qemuReportError(VIR_ERR_OPERATION_FAILED,
- "%s", _("device cannot be detached with this QEMU
version"));
- return -1;
- }
+ if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
+ goto cleanup;
- qemuDomainObjEnterMonitorWithDriver(driver, vm);
- if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- return -1;
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("cannot attach device on inactive
domain"));
+ goto endjob;
}
- qemuDomainObjExitMonitorWithDriver(driver, vm);
- ret = 0;
+ dev = virDomainDeviceDefParse(driver->caps, vm->def, xml,
+ VIR_DOMAIN_XML_INACTIVE);
+ if (dev == NULL)
+ goto endjob;
+
+ if (qemuCapsExtractVersionInfo(vm->def->emulator,
+ NULL,
+ &qemuCmdFlags) < 0)
+ goto endjob;
- if (vm->def->nhostdevs > 1) {
- memmove(vm->def->hostdevs + i,
- vm->def->hostdevs + i + 1,
- sizeof(*vm->def->hostdevs) *
- (vm->def->nhostdevs - (i + 1)));
- vm->def->nhostdevs--;
- if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) {
- /* ignore, harmless */
+ switch (dev->type) {
+ case VIR_DOMAIN_DEVICE_DISK:
+ if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
+ if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup,
0) !=0 ) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find cgroup for %s\n"),
+ vm->def->name);
+ goto endjob;
+ }
+ if (qemuSetupDiskCgroup(driver, cgroup, dev->data.disk) < 0)
+ goto endjob;
}
- } else {
- VIR_FREE(vm->def->hostdevs);
- vm->def->nhostdevs = 0;
- }
- virDomainHostdevDefFree(detach);
- return ret;
-}
+ switch (dev->data.disk->device) {
+ case VIR_DOMAIN_DISK_DEVICE_CDROM:
+ case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
+ ret = qemudDomainChangeEjectableMedia(driver, vm,
+ dev->data.disk,
+ qemuCmdFlags,
+ force);
+ if (ret == 0)
+ dev->data.disk = NULL;
+ break;
-static int qemudDomainDetachHostDevice(struct qemud_driver *driver,
- virDomainObjPtr vm,
- virDomainDeviceDefPtr dev,
- unsigned long long qemuCmdFlags)
-{
- virDomainHostdevDefPtr hostdev = dev->data.hostdev;
- int ret;
- if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
- qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("hostdev mode '%s' not supported"),
- virDomainHostdevModeTypeToString(hostdev->mode));
- return -1;
- }
+ default:
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("disk bus '%s' cannot be updated."),
+ virDomainDiskBusTypeToString(dev->data.disk->bus));
+ break;
+ }
- switch (hostdev->source.subsys.type) {
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
- ret = qemudDomainDetachHostPciDevice(driver, vm, dev, qemuCmdFlags);
+ if (ret != 0 && cgroup) {
+ if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0)
+ VIR_WARN("Failed to teardown cgroup for disk path %s",
+ NULLSTR(dev->data.disk->src));
+ }
break;
- case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
- ret = qemudDomainDetachHostUsbDevice(driver, vm, dev, qemuCmdFlags);
+
+ case VIR_DOMAIN_DEVICE_GRAPHICS:
+ ret = qemuDomainChangeGraphics(driver, vm, dev->data.graphics);
break;
+
default:
qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("hostdev subsys type '%s' not supported"),
-
virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
- return -1;
+ _("disk device type '%s' cannot be updated"),
+ virDomainDiskDeviceTypeToString(dev->data.disk->device));
+ break;
}
- if (driver->securityDriver &&
- driver->securityDriver->domainRestoreSecurityHostdevLabel &&
-
driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver,
- vm,
dev->data.hostdev) < 0)
- VIR_WARN0("Failed to restore host device labelling");
+ if (!ret && virDomainSaveStatus(driver->caps, driver->stateDir, vm)
< 0)
+ ret = -1;
+
+endjob:
+ if (qemuDomainObjEndJob(vm) == 0)
+ vm = NULL;
+
+cleanup:
+ if (cgroup)
+ virCgroupFree(&cgroup);
+ virDomainDeviceDefFree(dev);
+ if (vm)
+ virDomainObjUnlock(vm);
+ qemuDriverUnlock(driver);
return ret;
}
+
static int qemudDomainDetachDevice(virDomainPtr dom,
const char *xml) {
struct qemud_driver *driver = dom->conn->privateData;
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
new file mode 100644
index 0000000..0b60d27
--- /dev/null
+++ b/src/qemu/qemu_hotplug.c
@@ -0,0 +1,1711 @@
+/*
+ * qemu_hotplug.h: QEMU device hotplug management
+ *
+ * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Daniel P. Berrange <berrange(a)redhat.com>
+ */
+
+
+#include <config.h>
+
+#include "qemu_hotplug.h"
+#include "qemu_capabilities.h"
+#include "qemu_domain.h"
+#include "qemu_command.h"
+#include "qemu_bridge_filter.h"
+#include "qemu_audit.h"
+#include "qemu_hostdev.h"
+#include "domain_nwfilter.h"
+#include "logging.h"
+#include "virterror_internal.h"
+#include "memory.h"
+#include "pci.h"
+#include "files.h"
+#include "qemu_cgroup.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+int qemudDomainChangeEjectableMedia(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long qemuCmdFlags,
+ bool force)
+{
+ virDomainDiskDefPtr origdisk = NULL;
+ int i;
+ int ret;
+ char *driveAlias = NULL;
+
+ origdisk = NULL;
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ if (vm->def->disks[i]->bus == disk->bus &&
+ STREQ(vm->def->disks[i]->dst, disk->dst)) {
+ origdisk = vm->def->disks[i];
+ break;
+ }
+ }
+
+ if (!origdisk) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("No device with bus '%s' and target
'%s'"),
+ virDomainDiskBusTypeToString(disk->bus),
+ disk->dst);
+ return -1;
+ }
+
+ if (!origdisk->info.alias) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("missing disk device alias name for %s"),
origdisk->dst);
+ return -1;
+ }
+
+ if (origdisk->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
+ origdisk->device != VIR_DOMAIN_DISK_DEVICE_CDROM) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Removable media not supported for %s device"),
+ virDomainDiskDeviceTypeToString(disk->device));
+ return -1;
+ }
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainSetSecurityImageLabel &&
+
driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver,
+ vm, disk) < 0)
+ return -1;
+
+ if (!(driveAlias = qemuDeviceDriveHostAlias(origdisk, qemuCmdFlags)))
+ goto error;
+
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (disk->src) {
+ const char *format = NULL;
+ if (disk->type != VIR_DOMAIN_DISK_TYPE_DIR) {
+ if (disk->driverType)
+ format = disk->driverType;
+ else if (origdisk->driverType)
+ format = origdisk->driverType;
+ }
+ ret = qemuMonitorChangeMedia(priv->mon,
+ driveAlias,
+ disk->src, format);
+ } else {
+ ret = qemuMonitorEjectMedia(priv->mon, driveAlias, force);
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ qemuDomainDiskAudit(vm, origdisk, disk, "update", ret >= 0);
+
+ if (ret < 0)
+ goto error;
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainRestoreSecurityImageLabel &&
+
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
+ vm, origdisk) < 0)
+ VIR_WARN("Unable to restore security label on ejected image %s",
origdisk->src);
+
+ VIR_FREE(origdisk->src);
+ origdisk->src = disk->src;
+ disk->src = NULL;
+ origdisk->type = disk->type;
+
+ VIR_FREE(driveAlias);
+
+ virDomainDiskDefFree(disk);
+
+ return ret;
+
+error:
+ VIR_FREE(driveAlias);
+ if (driver->securityDriver &&
+ driver->securityDriver->domainRestoreSecurityImageLabel &&
+
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
+ vm, disk) < 0)
+ VIR_WARN("Unable to restore security label on new media %s",
disk->src);
+ return -1;
+}
+
+
+int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long qemuCmdFlags)
+{
+ int i, ret;
+ const char* type = virDomainDiskBusTypeToString(disk->bus);
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ char *devstr = NULL;
+ char *drivestr = NULL;
+
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ if (STREQ(vm->def->disks[i]->dst, disk->dst)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("target %s already exists"), disk->dst);
+ return -1;
+ }
+ }
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainSetSecurityImageLabel &&
+
driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver,
+ vm, disk) < 0)
+ return -1;
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &disk->info) <
0)
+ goto error;
+ if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0)
+ goto error;
+
+ if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags)))
+ goto error;
+
+ if (!(devstr = qemuBuildDriveDevStr(disk)))
+ goto error;
+ }
+
+ if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ ret = qemuMonitorAddDrive(priv->mon, drivestr);
+ if (ret == 0) {
+ ret = qemuMonitorAddDevice(priv->mon, devstr);
+ if (ret < 0) {
+ VIR_WARN("qemuMonitorAddDevice failed on %s (%s)",
+ drivestr, devstr);
+ /* XXX should call 'drive_del' on error but this does not
+ exist yet */
+ }
+ }
+ } else {
+ virDomainDevicePCIAddress guestAddr;
+ ret = qemuMonitorAddPCIDisk(priv->mon,
+ disk->src,
+ type,
+ &guestAddr);
+ if (ret == 0) {
+ disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+ memcpy(&disk->info.addr.pci, &guestAddr, sizeof(guestAddr));
+ }
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0);
+
+ if (ret < 0)
+ goto error;
+
+ virDomainDiskInsertPreAlloced(vm->def, disk);
+
+ VIR_FREE(devstr);
+ VIR_FREE(drivestr);
+
+ return 0;
+
+error:
+ VIR_FREE(devstr);
+ VIR_FREE(drivestr);
+
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
+ (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
+ qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &disk->info) < 0)
+ VIR_WARN("Unable to release PCI address on %s", disk->src);
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainRestoreSecurityImageLabel &&
+
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
+ vm, disk) < 0)
+ VIR_WARN("Unable to restore security label on %s", disk->src);
+
+ return -1;
+}
+
+
+int qemudDomainAttachPciControllerDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainControllerDefPtr controller,
+ unsigned long long qemuCmdFlags)
+{
+ int i;
+ int ret = -1;
+ const char* type = virDomainControllerTypeToString(controller->type);
+ char *devstr = NULL;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+
+ for (i = 0 ; i < vm->def->ncontrollers ; i++) {
+ if ((vm->def->controllers[i]->type == controller->type) &&
+ (vm->def->controllers[i]->idx == controller->idx)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("target %s:%d already exists"),
+ type, controller->idx);
+ return -1;
+ }
+ }
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &controller->info)
< 0)
+ goto cleanup;
+ if (qemuAssignDeviceControllerAlias(controller) < 0)
+ goto cleanup;
+
+ if (!(devstr = qemuBuildControllerDevStr(controller))) {
+ virReportOOMError();
+ goto cleanup;
+ }
+ }
+
+ if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers+1) < 0)
{
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ ret = qemuMonitorAddDevice(priv->mon, devstr);
+ } else {
+ ret = qemuMonitorAttachPCIDiskController(priv->mon,
+ type,
+ &controller->info.addr.pci);
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ if (ret == 0) {
+ controller->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+ virDomainControllerInsertPreAlloced(vm->def, controller);
+ }
+
+cleanup:
+ if ((ret != 0) &&
+ (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
+ (controller->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
+ qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &controller->info) <
0)
+ VIR_WARN0("Unable to release PCI address on controller");
+
+ VIR_FREE(devstr);
+ return ret;
+}
+
+
+static virDomainControllerDefPtr
+qemuDomainFindOrCreateSCSIDiskController(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ int controller,
+ unsigned long long qemuCmdFlags)
+{
+ int i;
+ virDomainControllerDefPtr cont;
+ for (i = 0 ; i < vm->def->ncontrollers ; i++) {
+ cont = vm->def->controllers[i];
+
+ if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI)
+ continue;
+
+ if (cont->idx == controller)
+ return cont;
+ }
+
+ /* No SCSI controller present, for backward compatibility we
+ * now hotplug a controller */
+ if (VIR_ALLOC(cont) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+ cont->type = VIR_DOMAIN_CONTROLLER_TYPE_SCSI;
+ cont->idx = 0;
+ cont->model = -1;
+
+ VIR_INFO0("No SCSI controller present, hotplugging one");
+ if (qemudDomainAttachPciControllerDevice(driver,
+ vm, cont, qemuCmdFlags) < 0) {
+ VIR_FREE(cont);
+ return NULL;
+ }
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest unexpectedly quit"));
+ /* cont doesn't need freeing here, since the reference
+ * now held in def->controllers */
+ return NULL;
+ }
+
+ return cont;
+}
+
+
+int qemudDomainAttachSCSIDisk(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long qemuCmdFlags)
+{
+ int i;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virDomainControllerDefPtr cont = NULL;
+ char *drivestr = NULL;
+ char *devstr = NULL;
+ int ret = -1;
+
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ if (STREQ(vm->def->disks[i]->dst, disk->dst)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("target %s already exists"), disk->dst);
+ return -1;
+ }
+ }
+
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainSetSecurityImageLabel &&
+
driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver,
+ vm, disk) < 0)
+ return -1;
+
+ /* We should have an address already, so make sure */
+ if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unexpected disk address type %s"),
+ virDomainDeviceAddressTypeToString(disk->info.type));
+ goto error;
+ }
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0)
+ goto error;
+ if (!(devstr = qemuBuildDriveDevStr(disk)))
+ goto error;
+ }
+
+ if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags)))
+ goto error;
+
+ for (i = 0 ; i <= disk->info.addr.drive.controller ; i++) {
+ cont = qemuDomainFindOrCreateSCSIDiskController(driver, vm, i, qemuCmdFlags);
+ if (!cont)
+ goto error;
+ }
+
+ /* Tell clang that "cont" is non-NULL.
+ This is because disk->info.addr.driver.controller is unsigned,
+ and hence the above loop must iterate at least once. */
+ sa_assert (cont);
+
+ if (cont->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("SCSI controller %d was missing its PCI address"),
cont->idx);
+ goto error;
+ }
+
+ if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ ret = qemuMonitorAddDrive(priv->mon, drivestr);
+ if (ret == 0) {
+ ret = qemuMonitorAddDevice(priv->mon, devstr);
+ if (ret < 0) {
+ VIR_WARN("qemuMonitorAddDevice failed on %s (%s)",
+ drivestr, devstr);
+ /* XXX should call 'drive_del' on error but this does not
+ exist yet */
+ }
+ }
+ } else {
+ virDomainDeviceDriveAddress driveAddr;
+ ret = qemuMonitorAttachDrive(priv->mon,
+ drivestr,
+ &cont->info.addr.pci,
+ &driveAddr);
+ if (ret == 0) {
+ /* XXX we should probably validate that the addr matches
+ * our existing defined addr instead of overwriting */
+ disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
+ memcpy(&disk->info.addr.drive, &driveAddr, sizeof(driveAddr));
+ }
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0);
+
+ if (ret < 0)
+ goto error;
+
+ virDomainDiskInsertPreAlloced(vm->def, disk);
+
+ VIR_FREE(devstr);
+ VIR_FREE(drivestr);
+
+ return 0;
+
+error:
+ VIR_FREE(devstr);
+ VIR_FREE(drivestr);
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainRestoreSecurityImageLabel &&
+
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
+ vm, disk) < 0)
+ VIR_WARN("Unable to restore security label on %s", disk->src);
+
+ return -1;
+}
+
+
+int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long qemuCmdFlags)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int i, ret;
+ char *drivestr = NULL;
+ char *devstr = NULL;
+
+ for (i = 0 ; i < vm->def->ndisks ; i++) {
+ if (STREQ(vm->def->disks[i]->dst, disk->dst)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("target %s already exists"), disk->dst);
+ return -1;
+ }
+ }
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainSetSecurityImageLabel &&
+
driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver,
+ vm, disk) < 0)
+ return -1;
+
+ if (!disk->src) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("disk source path is missing"));
+ goto error;
+ }
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0)
+ goto error;
+ if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags)))
+ goto error;
+ if (!(devstr = qemuBuildDriveDevStr(disk)))
+ goto error;
+ }
+
+ if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ ret = qemuMonitorAddDrive(priv->mon, drivestr);
+ if (ret == 0) {
+ ret = qemuMonitorAddDevice(priv->mon, devstr);
+ if (ret < 0) {
+ VIR_WARN("qemuMonitorAddDevice failed on %s (%s)",
+ drivestr, devstr);
+ /* XXX should call 'drive_del' on error but this does not
+ exist yet */
+ }
+ }
+ } else {
+ ret = qemuMonitorAddUSBDisk(priv->mon, disk->src);
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0);
+
+ if (ret < 0)
+ goto error;
+
+ virDomainDiskInsertPreAlloced(vm->def, disk);
+
+ VIR_FREE(devstr);
+ VIR_FREE(drivestr);
+
+ return 0;
+
+error:
+ VIR_FREE(devstr);
+ VIR_FREE(drivestr);
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainRestoreSecurityImageLabel &&
+
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
+ vm, disk) < 0)
+ VIR_WARN("Unable to restore security label on %s", disk->src);
+
+ return -1;
+}
+
+
+/* XXX conn required for network -> bridge resolution */
+int qemudDomainAttachNetDevice(virConnectPtr conn,
+ struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainNetDefPtr net,
+ unsigned long long qemuCmdFlags)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ char *tapfd_name = NULL;
+ int tapfd = -1;
+ char *nicstr = NULL;
+ char *netstr = NULL;
+ int ret = -1;
+ virDomainDevicePCIAddress guestAddr;
+ int vlan;
+
+ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_HOST_NET_ADD)) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("installed qemu version does not support
host_net_add"));
+ return -1;
+ }
+
+ if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE ||
+ net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
+ if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("network device type '%s' cannot be attached:
"
+ "qemu is not using a unix socket monitor"),
+ virDomainNetTypeToString(net->type));
+ return -1;
+ }
+
+ if ((tapfd = qemuNetworkIfaceConnect(conn, driver, net, qemuCmdFlags)) < 0)
+ return -1;
+ } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
+ if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("network device type '%s' cannot be attached:
"
+ "qemu is not using a unix socket monitor"),
+ virDomainNetTypeToString(net->type));
+ return -1;
+ }
+
+ if ((tapfd = qemuPhysIfaceConnect(conn, driver, net,
+ qemuCmdFlags,
+ vm->def->uuid,
+ VIR_VM_OP_CREATE)) < 0)
+ return -1;
+ }
+
+ if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0)
+ goto no_memory;
+
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_NET_NAME) ||
+ (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0)
+ goto cleanup;
+ }
+
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
+ qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &net->info) < 0)
+ goto cleanup;
+
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+ (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ vlan = -1;
+ } else {
+ vlan = qemuDomainNetVLAN(net);
+
+ if (vlan < 0) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Unable to attach network devices without
vlan"));
+ goto cleanup;
+ }
+ }
+
+ if (tapfd != -1) {
+ if (virAsprintf(&tapfd_name, "fd-%s", net->info.alias) < 0)
+ goto no_memory;
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorSendFileHandle(priv->mon, tapfd_name, tapfd) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ goto cleanup;
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest unexpectedly quit"));
+ goto cleanup;
+ }
+ }
+
+ /* FIXME - need to support vhost-net here (5th arg) */
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+ (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ if (!(netstr = qemuBuildHostNetStr(net, ',',
+ -1, tapfd_name, 0)))
+ goto try_tapfd_close;
+ } else {
+ if (!(netstr = qemuBuildHostNetStr(net, ' ',
+ vlan, tapfd_name, 0)))
+ goto try_tapfd_close;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+ (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ if (qemuMonitorAddNetdev(priv->mon, netstr) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ qemuDomainNetAudit(vm, NULL, net, "attach", false);
+ goto try_tapfd_close;
+ }
+ } else {
+ if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ qemuDomainNetAudit(vm, NULL, net, "attach", false);
+ goto try_tapfd_close;
+ }
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ VIR_FORCE_CLOSE(tapfd);
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest unexpectedly quit"));
+ goto cleanup;
+ }
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (!(nicstr = qemuBuildNicDevStr(net, vlan)))
+ goto try_remove;
+ } else {
+ if (!(nicstr = qemuBuildNicStr(net, NULL, vlan)))
+ goto try_remove;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuMonitorAddDevice(priv->mon, nicstr) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ qemuDomainNetAudit(vm, NULL, net, "attach", false);
+ goto try_remove;
+ }
+ } else {
+ if (qemuMonitorAddPCINetwork(priv->mon, nicstr,
+ &guestAddr) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ qemuDomainNetAudit(vm, NULL, net, "attach", false);
+ goto try_remove;
+ }
+ net->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+ memcpy(&net->info.addr.pci, &guestAddr, sizeof(guestAddr));
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ qemuDomainNetAudit(vm, NULL, net, "attach", true);
+
+ ret = 0;
+
+ vm->def->nets[vm->def->nnets++] = net;
+
+cleanup:
+ if ((ret != 0) &&
+ (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
+ (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
+ qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &net->info) < 0)
+ VIR_WARN0("Unable to release PCI address on NIC");
+
+ if (ret != 0)
+ virDomainConfNWFilterTeardown(net);
+
+ VIR_FREE(nicstr);
+ VIR_FREE(netstr);
+ VIR_FREE(tapfd_name);
+ VIR_FORCE_CLOSE(tapfd);
+
+ return ret;
+
+try_remove:
+ if (!virDomainObjIsActive(vm))
+ goto cleanup;
+
+ if (vlan < 0) {
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+ (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ char *netdev_name;
+ if (virAsprintf(&netdev_name, "host%s", net->info.alias)
< 0)
+ goto no_memory;
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorRemoveNetdev(priv->mon, netdev_name) < 0)
+ VIR_WARN("Failed to remove network backend for netdev %s",
+ netdev_name);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ VIR_FREE(netdev_name);
+ } else {
+ VIR_WARN0("Unable to remove network backend");
+ }
+ } else {
+ char *hostnet_name;
+ if (virAsprintf(&hostnet_name, "host%s", net->info.alias) <
0)
+ goto no_memory;
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0)
+ VIR_WARN("Failed to remove network backend for vlan %d, net %s",
+ vlan, hostnet_name);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ VIR_FREE(hostnet_name);
+ }
+ goto cleanup;
+
+try_tapfd_close:
+ if (!virDomainObjIsActive(vm))
+ goto cleanup;
+
+ if (tapfd_name) {
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorCloseFileHandle(priv->mon, tapfd_name) < 0)
+ VIR_WARN("Failed to close tapfd with '%s'", tapfd_name);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ }
+
+ goto cleanup;
+
+no_memory:
+ virReportOOMError();
+ goto cleanup;
+}
+
+
+int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainHostdevDefPtr hostdev,
+ unsigned long long qemuCmdFlags)
+{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int ret;
+ char *devstr = NULL;
+ int configfd = -1;
+ char *configfd_name = NULL;
+
+ if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
+ virReportOOMError();
+ return -1;
+ }
+
+ if (qemuPrepareHostdevPCIDevices(driver, &hostdev, 1) < 0)
+ return -1;
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0)
+ goto error;
+ if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) <
0)
+ goto error;
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) {
+ configfd = qemuOpenPCIConfig(hostdev);
+ if (configfd >= 0) {
+ if (virAsprintf(&configfd_name, "fd-%s",
+ hostdev->info.alias) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorSendFileHandle(priv->mon, configfd_name,
+ configfd) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ goto error;
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ }
+ }
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest unexpectedly quit during hotplug"));
+ goto error;
+ }
+
+ if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name)))
+ goto error;
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorAddDevice(priv->mon, devstr);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ } else {
+ virDomainDevicePCIAddress guestAddr;
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ ret = qemuMonitorAddPCIHostDevice(priv->mon,
+ &hostdev->source.subsys.u.pci,
+ &guestAddr);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ hostdev->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
+ memcpy(&hostdev->info.addr.pci, &guestAddr, sizeof(guestAddr));
+ }
+ if (ret < 0)
+ goto error;
+
+ vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
+
+ VIR_FREE(devstr);
+ VIR_FREE(configfd_name);
+ VIR_FORCE_CLOSE(configfd);
+
+ return 0;
+
+error:
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
+ (hostdev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) &&
+ qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &hostdev->info) <
0)
+ VIR_WARN0("Unable to release PCI address on host device");
+
+ qemuDomainReAttachHostdevDevices(driver, &hostdev, 1);
+
+ VIR_FREE(devstr);
+ VIR_FREE(configfd_name);
+ VIR_FORCE_CLOSE(configfd);
+
+ return -1;
+}
+
+
+int qemudDomainAttachHostUsbDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainHostdevDefPtr hostdev,
+ unsigned long long qemuCmdFlags)
+{
+ int ret;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ char *devstr = NULL;
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0)
+ goto error;
+ if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev)))
+ goto error;
+ }
+
+ if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+
+ if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
+ virCgroupPtr cgroup = NULL;
+ usbDevice *usb;
+
+ if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0)
!=0 ) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find cgroup for %s\n"),
+ vm->def->name);
+ goto error;
+ }
+
+ if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device)) == NULL)
+ goto error;
+
+ if (usbDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, cgroup) < 0 )
+ goto error;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)
+ ret = qemuMonitorAddDevice(priv->mon, devstr);
+ else
+ ret = qemuMonitorAddUSBDeviceExact(priv->mon,
+ hostdev->source.subsys.u.usb.bus,
+ hostdev->source.subsys.u.usb.device);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ if (ret < 0)
+ goto error;
+
+ vm->def->hostdevs[vm->def->nhostdevs++] = hostdev;
+
+ VIR_FREE(devstr);
+
+ return 0;
+
+error:
+ VIR_FREE(devstr);
+ return -1;
+}
+
+
+int qemudDomainAttachHostDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainHostdevDefPtr hostdev,
+ unsigned long long qemuCmdFlags)
+{
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("hostdev mode '%s' not supported"),
+ virDomainHostdevModeTypeToString(hostdev->mode));
+ return -1;
+ }
+
+ /* Resolve USB product/vendor to bus/device */
+ if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
+ hostdev->source.subsys.u.usb.vendor) {
+ usbDevice *usb
+ = usbFindDevice(hostdev->source.subsys.u.usb.vendor,
+ hostdev->source.subsys.u.usb.product);
+
+ if (!usb)
+ return -1;
+
+ hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb);
+ hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb);
+
+ usbFreeDevice(usb);
+ }
+
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainSetSecurityHostdevLabel &&
+
driver->securityDriver->domainSetSecurityHostdevLabel(driver->securityDriver,
+ vm, hostdev) < 0)
+ return -1;
+
+ switch (hostdev->source.subsys.type) {
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+ if (qemudDomainAttachHostPciDevice(driver, vm,
+ hostdev, qemuCmdFlags) < 0)
+ goto error;
+ break;
+
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
+ if (qemudDomainAttachHostUsbDevice(driver, vm,
+ hostdev, qemuCmdFlags) < 0)
+ goto error;
+ break;
+
+ default:
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("hostdev subsys type '%s' not supported"),
+
virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
+ goto error;
+ }
+
+ return 0;
+
+error:
+ if (driver->securityDriver &&
+ driver->securityDriver->domainRestoreSecurityHostdevLabel &&
+
driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver,
+ vm, hostdev) < 0)
+ VIR_WARN0("Unable to restore host device labelling on hotplug fail");
+
+ return -1;
+}
+
+
+static virDomainGraphicsDefPtr qemuDomainFindGraphics(virDomainObjPtr vm,
+ virDomainGraphicsDefPtr dev)
+{
+ int i;
+
+ for (i = 0 ; i < vm->def->ngraphics ; i++) {
+ if (vm->def->graphics[i]->type == dev->type)
+ return vm->def->graphics[i];
+ }
+
+ return NULL;
+}
+
+
+int
+qemuDomainChangeGraphics(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainGraphicsDefPtr dev)
+{
+ virDomainGraphicsDefPtr olddev = qemuDomainFindGraphics(vm, dev);
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int ret = -1;
+
+ if (!olddev) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot find existing graphics device to modify"));
+ return -1;
+ }
+
+ switch (dev->type) {
+ case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
+ if ((olddev->data.vnc.autoport != dev->data.vnc.autoport) ||
+ (!dev->data.vnc.autoport && (olddev->data.vnc.port !=
dev->data.vnc.port))) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot change port settings on vnc graphics"));
+ return -1;
+ }
+ if (STRNEQ_NULLABLE(olddev->data.vnc.listenAddr, dev->data.vnc.listenAddr))
{
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot change listen address setting on vnc
graphics"));
+ return -1;
+ }
+ if (STRNEQ_NULLABLE(olddev->data.vnc.keymap, dev->data.vnc.keymap)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("cannot change keymap setting on vnc
graphics"));
+ return -1;
+ }
+
+ 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.auth.passwd ?
+ dev->data.vnc.auth.passwd :
+ driver->vncPassword);
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ /* Steal the new dev's char * reference */
+ 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;
+ }
+ break;
+
+ default:
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to change config on '%s' graphics
type"),
+ virDomainGraphicsTypeToString(dev->type));
+ break;
+ }
+
+ return ret;
+}
+
+
+static inline int qemudFindDisk(virDomainDefPtr def, const char *dst)
+{
+ int i;
+
+ for (i = 0 ; i < def->ndisks ; i++) {
+ if (STREQ(def->disks[i]->dst, dst)) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags)
+{
+ int i, ret = -1;
+ virDomainDiskDefPtr detach = NULL;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virCgroupPtr cgroup = NULL;
+ char *drivestr = NULL;
+
+ i = qemudFindDisk(vm->def, dev->data.disk->dst);
+
+ if (i < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("disk %s not found"), dev->data.disk->dst);
+ goto cleanup;
+ }
+
+ detach = vm->def->disks[i];
+
+ if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
+ if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=
0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find cgroup for %s\n"),
+ vm->def->name);
+ goto cleanup;
+ }
+ }
+
+ if (!virDomainDeviceAddressIsValid(&detach->info,
+ VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("device cannot be detached without a PCI address"));
+ goto cleanup;
+ }
+
+ /* build the actual drive id string as the disk->info.alias doesn't
+ * contain the QEMU_DRIVE_HOST_PREFIX that is passed to qemu */
+ if (virAsprintf(&drivestr, "%s%s",
+ QEMU_DRIVE_HOST_PREFIX, detach->info.alias) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
+ qemuDomainObjExitMonitor(vm);
+ goto cleanup;
+ }
+ } else {
+ if (qemuMonitorRemovePCIDevice(priv->mon,
+ &detach->info.addr.pci) < 0) {
+ qemuDomainObjExitMonitor(vm);
+ goto cleanup;
+ }
+ }
+
+ /* disconnect guest from host device */
+ qemuMonitorDriveDel(priv->mon, drivestr);
+
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ qemuDomainDiskAudit(vm, detach, NULL, "detach", ret >= 0);
+
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
+ qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
+ VIR_WARN("Unable to release PCI address on %s",
dev->data.disk->src);
+
+ virDomainDiskRemove(vm->def, i);
+
+ virDomainDiskDefFree(detach);
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainRestoreSecurityImageLabel &&
+
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
+ vm, dev->data.disk)
< 0)
+ VIR_WARN("Unable to restore security label on %s",
dev->data.disk->src);
+
+ if (cgroup != NULL) {
+ if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0)
+ VIR_WARN("Failed to teardown cgroup for disk path %s",
+ NULLSTR(dev->data.disk->src));
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(drivestr);
+ return ret;
+}
+
+int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags)
+{
+ int i, ret = -1;
+ virDomainDiskDefPtr detach = NULL;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virCgroupPtr cgroup = NULL;
+ char *drivestr = NULL;
+
+ i = qemudFindDisk(vm->def, dev->data.disk->dst);
+
+ if (i < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("disk %s not found"), dev->data.disk->dst);
+ goto cleanup;
+ }
+
+ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("Underlying qemu does not support SCSI disk
removal"));
+ goto cleanup;
+ }
+
+ detach = vm->def->disks[i];
+
+ if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) {
+ if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=
0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to find cgroup for %s\n"),
+ vm->def->name);
+ goto cleanup;
+ }
+ }
+
+ /* build the actual drive id string as the disk->info.alias doesn't
+ * contain the QEMU_DRIVE_HOST_PREFIX that is passed to qemu */
+ if (virAsprintf(&drivestr, "%s%s",
+ QEMU_DRIVE_HOST_PREFIX, detach->info.alias) < 0) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
+ qemuDomainObjExitMonitor(vm);
+ goto cleanup;
+ }
+
+ /* disconnect guest from host device */
+ qemuMonitorDriveDel(priv->mon, drivestr);
+
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ qemuDomainDiskAudit(vm, detach, NULL, "detach", ret >= 0);
+
+ virDomainDiskRemove(vm->def, i);
+
+ virDomainDiskDefFree(detach);
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainRestoreSecurityImageLabel &&
+
driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver,
+ vm, dev->data.disk)
< 0)
+ VIR_WARN("Unable to restore security label on %s",
dev->data.disk->src);
+
+ if (cgroup != NULL) {
+ if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0)
+ VIR_WARN("Failed to teardown cgroup for disk path %s",
+ NULLSTR(dev->data.disk->src));
+ }
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(drivestr);
+ virCgroupFree(&cgroup);
+ return ret;
+}
+
+int qemudDomainDetachPciControllerDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags)
+{
+ int i, ret = -1;
+ virDomainControllerDefPtr detach = NULL;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+
+ for (i = 0 ; i < vm->def->ncontrollers ; i++) {
+ if ((vm->def->controllers[i]->type == dev->data.controller->type)
&&
+ (vm->def->controllers[i]->idx == dev->data.controller->idx))
{
+ detach = vm->def->controllers[i];
+ break;
+ }
+ }
+
+ if (!detach) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("disk controller %s:%d not found"),
+
virDomainControllerTypeToString(dev->data.controller->type),
+ dev->data.controller->idx);
+ goto cleanup;
+ }
+
+ if (!virDomainDeviceAddressIsValid(&detach->info,
+ VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED, "%s",
+ _("device cannot be detached without a PCI address"));
+ goto cleanup;
+ }
+
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuAssignDeviceControllerAlias(detach) < 0)
+ goto cleanup;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuMonitorDelDevice(priv->mon, detach->info.alias)) {
+ qemuDomainObjExitMonitor(vm);
+ goto cleanup;
+ }
+ } else {
+ if (qemuMonitorRemovePCIDevice(priv->mon,
+ &detach->info.addr.pci) < 0) {
+ qemuDomainObjExitMonitor(vm);
+ goto cleanup;
+ }
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ if (vm->def->ncontrollers > 1) {
+ memmove(vm->def->controllers + i,
+ vm->def->controllers + i + 1,
+ sizeof(*vm->def->controllers) *
+ (vm->def->ncontrollers - (i + 1)));
+ vm->def->ncontrollers--;
+ if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers) <
0) {
+ /* ignore, harmless */
+ }
+ } else {
+ VIR_FREE(vm->def->controllers);
+ vm->def->ncontrollers = 0;
+ }
+
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
+ qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
+ VIR_WARN0("Unable to release PCI address on controller");
+
+ virDomainControllerDefFree(detach);
+
+ ret = 0;
+
+cleanup:
+ return ret;
+}
+
+int
+qemudDomainDetachNetDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags)
+{
+ int i, ret = -1;
+ virDomainNetDefPtr detach = NULL;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int vlan;
+ char *hostnet_name = NULL;
+
+ for (i = 0 ; i < vm->def->nnets ; i++) {
+ virDomainNetDefPtr net = vm->def->nets[i];
+
+ if (!memcmp(net->mac, dev->data.net->mac, sizeof(net->mac))) {
+ detach = net;
+ break;
+ }
+ }
+
+ if (!detach) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("network device %02x:%02x:%02x:%02x:%02x:%02x not
found"),
+ dev->data.net->mac[0], dev->data.net->mac[1],
+ dev->data.net->mac[2], dev->data.net->mac[3],
+ dev->data.net->mac[4], dev->data.net->mac[5]);
+ goto cleanup;
+ }
+
+ if (!virDomainDeviceAddressIsValid(&detach->info,
+ VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("device cannot be detached without a PCI
address"));
+ goto cleanup;
+ }
+
+ if ((vlan = qemuDomainNetVLAN(detach)) < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("unable to determine original
VLAN"));
+ goto cleanup;
+ }
+
+ if (virAsprintf(&hostnet_name, "host%s", detach->info.alias) < 0)
{
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
+ qemuDomainObjExitMonitor(vm);
+ qemuDomainNetAudit(vm, detach, NULL, "detach", false);
+ goto cleanup;
+ }
+ } else {
+ if (qemuMonitorRemovePCIDevice(priv->mon,
+ &detach->info.addr.pci) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ qemuDomainNetAudit(vm, detach, NULL, "detach", false);
+ goto cleanup;
+ }
+ }
+
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) &&
+ (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ qemuDomainNetAudit(vm, detach, NULL, "detach", false);
+ goto cleanup;
+ }
+ } else {
+ if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ qemuDomainNetAudit(vm, detach, NULL, "detach", false);
+ goto cleanup;
+ }
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ qemuDomainNetAudit(vm, detach, NULL, "detach", true);
+
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
+ qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
+ VIR_WARN0("Unable to release PCI address on NIC");
+
+ virDomainConfNWFilterTeardown(detach);
+
+#if WITH_MACVTAP
+ if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
+ delMacvtap(detach->ifname, detach->mac, detach->data.direct.linkdev,
+ &detach->data.direct.virtPortProfile);
+ VIR_FREE(detach->ifname);
+ }
+#endif
+
+ if ((driver->macFilter) && (detach->ifname != NULL)) {
+ if ((errno = networkDisallowMacOnPort(driver,
+ detach->ifname,
+ detach->mac))) {
+ virReportSystemError(errno,
+ _("failed to remove ebtables rule on '%s'"),
+ detach->ifname);
+ }
+ }
+
+ if (vm->def->nnets > 1) {
+ memmove(vm->def->nets + i,
+ vm->def->nets + i + 1,
+ sizeof(*vm->def->nets) *
+ (vm->def->nnets - (i + 1)));
+ vm->def->nnets--;
+ if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets) < 0) {
+ /* ignore, harmless */
+ }
+ } else {
+ VIR_FREE(vm->def->nets);
+ vm->def->nnets = 0;
+ }
+ virDomainNetDefFree(detach);
+
+ ret = 0;
+
+cleanup:
+ VIR_FREE(hostnet_name);
+ return ret;
+}
+
+int qemudDomainDetachHostPciDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags)
+{
+ virDomainHostdevDefPtr detach = NULL;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int i, ret;
+ pciDevice *pci;
+
+ for (i = 0 ; i < vm->def->nhostdevs ; i++) {
+ if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ vm->def->hostdevs[i]->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
+ continue;
+
+ unsigned domain = vm->def->hostdevs[i]->source.subsys.u.pci.domain;
+ unsigned bus = vm->def->hostdevs[i]->source.subsys.u.pci.bus;
+ unsigned slot = vm->def->hostdevs[i]->source.subsys.u.pci.slot;
+ unsigned function = vm->def->hostdevs[i]->source.subsys.u.pci.function;
+
+ if (dev->data.hostdev->source.subsys.u.pci.domain == domain &&
+ dev->data.hostdev->source.subsys.u.pci.bus == bus &&
+ dev->data.hostdev->source.subsys.u.pci.slot == slot &&
+ dev->data.hostdev->source.subsys.u.pci.function == function) {
+ detach = vm->def->hostdevs[i];
+ break;
+ }
+ }
+
+ if (!detach) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("host pci device %.4x:%.2x:%.2x.%.1x not found"),
+ dev->data.hostdev->source.subsys.u.pci.domain,
+ dev->data.hostdev->source.subsys.u.pci.bus,
+ dev->data.hostdev->source.subsys.u.pci.slot,
+ dev->data.hostdev->source.subsys.u.pci.function);
+ return -1;
+ }
+
+ if (!virDomainDeviceAddressIsValid(&detach->info,
+ VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("device cannot be detached without a PCI
address"));
+ return -1;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
+ if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
+ qemuDomainObjExitMonitor(vm);
+ return -1;
+ }
+ } else {
+ if (qemuMonitorRemovePCIDevice(priv->mon,
+ &detach->info.addr.pci) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ return -1;
+ }
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ ret = 0;
+
+ pci = pciGetDevice(detach->source.subsys.u.pci.domain,
+ detach->source.subsys.u.pci.bus,
+ detach->source.subsys.u.pci.slot,
+ detach->source.subsys.u.pci.function);
+ if (!pci)
+ ret = -1;
+ else {
+ pciDeviceSetManaged(pci, detach->managed);
+ pciDeviceListDel(driver->activePciHostdevs, pci);
+ if (pciResetDevice(pci, driver->activePciHostdevs, NULL) < 0)
+ ret = -1;
+ qemuReattachPciDevice(pci, driver);
+ pciFreeDevice(pci);
+ }
+
+ if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) &&
+ qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0)
+ VIR_WARN0("Unable to release PCI address on host device");
+
+ if (vm->def->nhostdevs > 1) {
+ memmove(vm->def->hostdevs + i,
+ vm->def->hostdevs + i + 1,
+ sizeof(*vm->def->hostdevs) *
+ (vm->def->nhostdevs - (i + 1)));
+ vm->def->nhostdevs--;
+ if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) {
+ /* ignore, harmless */
+ }
+ } else {
+ VIR_FREE(vm->def->hostdevs);
+ vm->def->nhostdevs = 0;
+ }
+ virDomainHostdevDefFree(detach);
+
+ return ret;
+}
+
+int qemudDomainDetachHostUsbDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags)
+{
+ virDomainHostdevDefPtr detach = NULL;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ int i, ret;
+
+ for (i = 0 ; i < vm->def->nhostdevs ; i++) {
+ if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ vm->def->hostdevs[i]->source.subsys.type !=
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB)
+ continue;
+
+ unsigned bus = vm->def->hostdevs[i]->source.subsys.u.usb.bus;
+ unsigned device = vm->def->hostdevs[i]->source.subsys.u.usb.device;
+ unsigned product = vm->def->hostdevs[i]->source.subsys.u.usb.product;
+ unsigned vendor = vm->def->hostdevs[i]->source.subsys.u.usb.vendor;
+
+ if (dev->data.hostdev->source.subsys.u.usb.bus &&
+ dev->data.hostdev->source.subsys.u.usb.device) {
+ if (dev->data.hostdev->source.subsys.u.usb.bus == bus &&
+ dev->data.hostdev->source.subsys.u.usb.device == device) {
+ detach = vm->def->hostdevs[i];
+ break;
+ }
+ } else {
+ if (dev->data.hostdev->source.subsys.u.usb.product == product
&&
+ dev->data.hostdev->source.subsys.u.usb.vendor == vendor) {
+ detach = vm->def->hostdevs[i];
+ break;
+ }
+ }
+ }
+
+ if (!detach) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ _("host usb device %03d.%03d not found"),
+ dev->data.hostdev->source.subsys.u.usb.bus,
+ dev->data.hostdev->source.subsys.u.usb.device);
+ return -1;
+ }
+
+ if (!detach->info.alias) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("device cannot be detached without a
device alias"));
+ return -1;
+ }
+
+ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("device cannot be detached with this QEMU
version"));
+ return -1;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ return -1;
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ ret = 0;
+
+ if (vm->def->nhostdevs > 1) {
+ memmove(vm->def->hostdevs + i,
+ vm->def->hostdevs + i + 1,
+ sizeof(*vm->def->hostdevs) *
+ (vm->def->nhostdevs - (i + 1)));
+ vm->def->nhostdevs--;
+ if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) {
+ /* ignore, harmless */
+ }
+ } else {
+ VIR_FREE(vm->def->hostdevs);
+ vm->def->nhostdevs = 0;
+ }
+ virDomainHostdevDefFree(detach);
+
+ return ret;
+}
+
+int qemudDomainDetachHostDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags)
+{
+ virDomainHostdevDefPtr hostdev = dev->data.hostdev;
+ int ret;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("hostdev mode '%s' not supported"),
+ virDomainHostdevModeTypeToString(hostdev->mode));
+ return -1;
+ }
+
+ switch (hostdev->source.subsys.type) {
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+ ret = qemudDomainDetachHostPciDevice(driver, vm, dev, qemuCmdFlags);
+ break;
+ case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
+ ret = qemudDomainDetachHostUsbDevice(driver, vm, dev, qemuCmdFlags);
+ break;
+ default:
+ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("hostdev subsys type '%s' not supported"),
+
virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type));
+ return -1;
+ }
+
+ if (driver->securityDriver &&
+ driver->securityDriver->domainRestoreSecurityHostdevLabel &&
+
driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver,
+ vm,
dev->data.hostdev) < 0)
+ VIR_WARN0("Failed to restore host device labelling");
+
+ return ret;
+}
+
diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h
new file mode 100644
index 0000000..552f509
--- /dev/null
+++ b/src/qemu/qemu_hotplug.h
@@ -0,0 +1,103 @@
+/*
+ * qemu_hotplug.h: QEMU device hotplug management
+ *
+ * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Daniel P. Berrange <berrange(a)redhat.com>
+ */
+
+#ifndef __QEMU_HOTPLUG_H__
+# define __QEMU_HOTPLUG_H__
+
+# include <stdbool.h>
+
+# include "qemu_conf.h"
+# include "domain_conf.h"
+
+int qemudDomainChangeEjectableMedia(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long qemuCmdFlags,
+ bool force);
+int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long qemuCmdFlags);
+int qemudDomainAttachPciControllerDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainControllerDefPtr controller,
+ unsigned long long qemuCmdFlags);
+int qemudDomainAttachSCSIDisk(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long qemuCmdFlags);
+int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDiskDefPtr disk,
+ unsigned long long qemuCmdFlags);
+int qemudDomainAttachNetDevice(virConnectPtr conn,
+ struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainNetDefPtr net,
+ unsigned long long qemuCmdFlags);
+int qemudDomainAttachHostPciDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainHostdevDefPtr hostdev,
+ unsigned long long qemuCmdFlags);
+int qemudDomainAttachHostUsbDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainHostdevDefPtr hostdev,
+ unsigned long long qemuCmdFlags);
+int qemudDomainAttachHostDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainHostdevDefPtr hostdev,
+ unsigned long long qemuCmdFlags);
+int qemuDomainChangeGraphics(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainGraphicsDefPtr dev);
+int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags);
+int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags);
+int qemudDomainDetachPciControllerDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags);
+int qemudDomainDetachNetDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags);
+int qemudDomainDetachHostPciDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags);
+int qemudDomainDetachHostUsbDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags);
+int qemudDomainDetachHostDevice(struct qemud_driver *driver,
+ virDomainObjPtr vm,
+ virDomainDeviceDefPtr dev,
+ unsigned long long qemuCmdFlags);
+
+
+#endif /* __QEMU_HOTPLUG_H__ */
--
1.7.2.3