[libvirt] [PATCH 10/10] Move QEMU hotplug helper code out of the QEMU driver

Daniel P. Berrange berrange at redhat.com
Thu Dec 16 16:50:10 UTC 2010


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 at 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 at 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




More information about the libvir-list mailing list