/* * qemu_hotplug.h: QEMU device hotplug management * * Copyright (C) 2006-2012 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, see * . * * Author: Daniel P. Berrange */ #include #include "qemu_hotplug.h" #include "qemu_capabilities.h" #include "qemu_domain.h" #include "qemu_command.h" #include "qemu_bridge_filter.h" #include "qemu_hostdev.h" #include "domain_audit.h" #include "domain_nwfilter.h" #include "logging.h" #include "virterror_internal.h" #include "memory.h" #include "pci.h" #include "virfile.h" #include "qemu_cgroup.h" #include "locking/domain_lock.h" #include "network/bridge_driver.h" #include "virnetdev.h" #include "virnetdevbridge.h" #include "virnetdevtap.h" #include "device_conf.h" #define VIR_FROM_THIS VIR_FROM_QEMU #define CHANGE_MEDIA_RETRIES 10 int qemuDomainChangeEjectableMedia(struct qemud_driver *driver, virDomainObjPtr vm, virDomainDiskDefPtr disk, bool force) { virDomainDiskDefPtr origdisk = NULL; int i; int ret; char *driveAlias = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; int retries = CHANGE_MEDIA_RETRIES; 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) { virReportError(VIR_ERR_INTERNAL_ERROR, _("No device with bus '%s' and target '%s'"), virDomainDiskBusTypeToString(disk->bus), disk->dst); return -1; } if (!origdisk->info.alias) { virReportError(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) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Removable media not supported for %s device"), virDomainDiskDeviceTypeToString(disk->device)); return -1; } if (virDomainLockDiskAttach(driver->lockManager, vm, disk) < 0) return -1; if (virSecurityManagerSetImageLabel(driver->securityManager, vm->def, disk) < 0) { if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) VIR_WARN("Unable to release lock on %s", disk->src); return -1; } if (!(driveAlias = qemuDeviceDriveHostAlias(origdisk, priv->caps))) goto error; qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorEjectMedia(priv->mon, driveAlias, force); qemuDomainObjExitMonitor(driver, vm); virObjectRef(vm); /* we don't want to report errors from media tray_open polling */ while (retries) { if (origdisk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN) break; retries--; virDomainObjUnlock(vm); VIR_DEBUG("Waiting 500ms for tray to open. Retries left %d", retries); usleep(500 * 1000); /* sleep 500ms */ virDomainObjLock(vm); } virObjectUnref(vm); if (retries <= 0) { if (ret == 0) { /* If ret == -1, EjectMedia already set an error message */ virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("Unable to eject media")); } goto audit; } ret = 0; if (disk->src) { /* deliberately don't depend on 'ret' as 'eject' may have failed the * first time and we are going to check the drive state anyway */ 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; } qemuDomainObjEnterMonitor(driver, vm); ret = qemuMonitorChangeMedia(priv->mon, driveAlias, disk->src, format); qemuDomainObjExitMonitor(driver, vm); } audit: virDomainAuditDisk(vm, origdisk->src, disk->src, "update", ret >= 0); if (ret < 0) goto error; if (virSecurityManagerRestoreImageLabel(driver->securityManager, vm->def, origdisk) < 0) VIR_WARN("Unable to restore security label on ejected image %s", origdisk->src); if (virDomainLockDiskDetach(driver->lockManager, vm, origdisk) < 0) VIR_WARN("Unable to release lock on disk %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 (virSecurityManagerRestoreImageLabel(driver->securityManager, vm->def, disk) < 0) VIR_WARN("Unable to restore security label on new media %s", disk->src); if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) VIR_WARN("Unable to release lock on %s", disk->src); return -1; } int qemuDomainCheckEjectableMedia(struct qemud_driver *driver, virDomainObjPtr vm, enum qemuDomainAsyncJob asyncJob) { qemuDomainObjPrivatePtr priv = vm->privateData; virHashTablePtr table = NULL; int ret = -1; int i; if (qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob) == 0) { table = qemuMonitorGetBlockInfo(priv->mon); qemuDomainObjExitMonitorWithDriver(driver, vm); } if (!table) goto cleanup; for (i = 0; i < vm->def->ndisks; i++) { virDomainDiskDefPtr disk = vm->def->disks[i]; struct qemuDomainDiskInfo *info; if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK || disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { continue; } info = qemuMonitorBlockInfoLookup(table, disk->info.alias); if (!info) goto cleanup; if (info->tray_open && disk->src) VIR_FREE(disk->src); } ret = 0; cleanup: virHashFree(table); return ret; } int qemuDomainAttachPciDiskDevice(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm, virDomainDiskDefPtr disk) { int i, ret; const char* type = virDomainDiskBusTypeToString(disk->bus); qemuDomainObjPrivatePtr priv = vm->privateData; char *devstr = NULL; char *drivestr = NULL; bool releaseaddr = false; for (i = 0 ; i < vm->def->ndisks ; i++) { if (STREQ(vm->def->disks[i]->dst, disk->dst)) { virReportError(VIR_ERR_OPERATION_FAILED, _("target %s already exists"), disk->dst); return -1; } } if (virDomainLockDiskAttach(driver->lockManager, vm, disk) < 0) return -1; if (virSecurityManagerSetImageLabel(driver->securityManager, vm->def, disk) < 0) { if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) VIR_WARN("Unable to release lock on %s", disk->src); return -1; } if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &disk->info) < 0) goto error; releaseaddr = true; if (qemuAssignDeviceDiskAlias(vm->def, disk, priv->caps) < 0) goto error; if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->caps))) goto error; if (!(devstr = qemuBuildDriveDevStr(NULL, disk, 0, priv->caps))) goto error; } if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { virReportOOMError(); goto error; } qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { ret = qemuMonitorAddDrive(priv->mon, drivestr); if (ret == 0) { ret = qemuMonitorAddDevice(priv->mon, devstr); if (ret < 0) { virErrorPtr orig_err = virSaveLastError(); if (qemuMonitorDriveDel(priv->mon, drivestr) < 0) { VIR_WARN("Unable to remove drive %s (%s) after failed " "qemuMonitorAddDevice", drivestr, devstr); } if (orig_err) { virSetError(orig_err); virFreeError(orig_err); } } } } else { virDevicePCIAddress guestAddr = disk->info.addr.pci; 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); virDomainAuditDisk(vm, NULL, disk->src, "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 (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE) && (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && releaseaddr && qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, disk->info.addr.pci.slot) < 0) VIR_WARN("Unable to release PCI address on %s", disk->src); if (virSecurityManagerRestoreImageLabel(driver->securityManager, vm->def, disk) < 0) VIR_WARN("Unable to restore security label on %s", disk->src); if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) VIR_WARN("Unable to release lock on %s", disk->src); return -1; } int qemuDomainAttachPciControllerDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainControllerDefPtr controller) { int ret = -1; const char* type = virDomainControllerTypeToString(controller->type); char *devstr = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; bool releaseaddr = false; if (virDomainControllerFind(vm->def, controller->type, controller->idx) > 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("target %s:%d already exists"), type, controller->idx); return -1; } if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &controller->info) < 0) goto cleanup; releaseaddr = true; if (qemuAssignDeviceControllerAlias(controller) < 0) goto cleanup; if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && controller->model == -1 && !qemuCapsGet(priv->caps, QEMU_CAPS_PIIX3_USB_UHCI)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("USB controller hotplug unsupported in this QEMU binary")); goto cleanup; } if (!(devstr = qemuBuildControllerDevStr(vm->def, controller, priv->caps, NULL))) { goto cleanup; } } if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers+1) < 0) { virReportOOMError(); goto cleanup; } qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_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) && qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE) && (controller->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && releaseaddr && qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, controller->info.addr.pci.slot) < 0) VIR_WARN("Unable to release PCI address on controller"); VIR_FREE(devstr); return ret; } static virDomainControllerDefPtr qemuDomainFindOrCreateSCSIDiskController(struct qemud_driver *driver, virDomainObjPtr vm, int controller) { 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 = controller; cont->model = -1; VIR_INFO("No SCSI controller present, hotplugging one"); if (qemuDomainAttachPciControllerDevice(driver, vm, cont) < 0) { VIR_FREE(cont); return NULL; } if (!virDomainObjIsActive(vm)) { virReportError(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 qemuDomainAttachSCSIDisk(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm, virDomainDiskDefPtr disk) { 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)) { virReportError(VIR_ERR_OPERATION_FAILED, _("target %s already exists"), disk->dst); return -1; } } if (virDomainLockDiskAttach(driver->lockManager, vm, disk) < 0) return -1; if (virSecurityManagerSetImageLabel(driver->securityManager, vm->def, disk) < 0) { if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) VIR_WARN("Unable to release lock on %s", disk->src); return -1; } /* We should have an address already, so make sure */ if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected disk address type %s"), virDomainDeviceAddressTypeToString(disk->info.type)); goto error; } if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceDiskAlias(vm->def, disk, priv->caps) < 0) goto error; if (!(devstr = qemuBuildDriveDevStr(vm->def, disk, 0, priv->caps))) goto error; } if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->caps))) goto error; for (i = 0 ; i <= disk->info.addr.drive.controller ; i++) { cont = qemuDomainFindOrCreateSCSIDiskController(driver, vm, i); 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) { virReportError(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 (qemuCapsGet(priv->caps, QEMU_CAPS_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; disk->info.addr.drive.bus = driveAddr.bus; disk->info.addr.drive.unit = driveAddr.unit; } } qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditDisk(vm, NULL, disk->src, "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 (virSecurityManagerRestoreImageLabel(driver->securityManager, vm->def, disk) < 0) VIR_WARN("Unable to restore security label on %s", disk->src); if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) VIR_WARN("Unable to release lock on %s", disk->src); return -1; } int qemuDomainAttachUsbMassstorageDevice(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm, virDomainDiskDefPtr disk) { 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)) { virReportError(VIR_ERR_OPERATION_FAILED, _("target %s already exists"), disk->dst); return -1; } } if (virDomainLockDiskAttach(driver->lockManager, vm, disk) < 0) return -1; if (virSecurityManagerSetImageLabel(driver->securityManager, vm->def, disk) < 0) { if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) VIR_WARN("Unable to release lock on %s", disk->src); return -1; } /* XXX not correct once we allow attaching a USB CDROM */ if (!disk->src) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("disk source path is missing")); goto error; } if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceDiskAlias(vm->def, disk, priv->caps) < 0) goto error; if (!(drivestr = qemuBuildDriveStr(conn, disk, false, priv->caps))) goto error; if (!(devstr = qemuBuildDriveDevStr(NULL, disk, 0, priv->caps))) goto error; } if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { virReportOOMError(); goto error; } qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_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); virDomainAuditDisk(vm, NULL, disk->src, "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 (virSecurityManagerRestoreImageLabel(driver->securityManager, vm->def, disk) < 0) VIR_WARN("Unable to restore security label on %s", disk->src); if (virDomainLockDiskDetach(driver->lockManager, vm, disk) < 0) VIR_WARN("Unable to release lock on %s", disk->src); return -1; } /* XXX conn required for network -> bridge resolution */ int qemuDomainAttachNetDevice(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm, virDomainNetDefPtr net) { qemuDomainObjPrivatePtr priv = vm->privateData; char *tapfd_name = NULL; int tapfd = -1; char *vhostfd_name = NULL; int vhostfd = -1; char *nicstr = NULL; char *netstr = NULL; virNetDevVPortProfilePtr vport = NULL; int ret = -1; virDevicePCIAddress guestAddr; int vlan; bool releaseaddr = false; bool iface_connected = false; int actualType; /* preallocate new slot for device */ if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0) { virReportOOMError(); return -1; } /* If appropriate, grab a physical device from the configured * network's pool of devices, or resolve bridge device name * to the one defined in the network definition. */ if (networkAllocateActualDevice(net) < 0) return -1; actualType = virDomainNetGetActualType(net); if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) { /* This is really a "smart hostdev", so it should be attached * as a hostdev (the hostdev code will reach over into the * netdev-specific code as appropriate), then also added to * the nets list (see cleanup:) if successful. */ ret = qemuDomainAttachHostDevice(driver, vm, virDomainNetGetActualHostdev(net)); goto cleanup; } if (!qemuCapsGet(priv->caps, QEMU_CAPS_HOST_NET_ADD)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("installed qemu version does not support host_net_add")); goto cleanup; } if (actualType == VIR_DOMAIN_NET_TYPE_BRIDGE || actualType == VIR_DOMAIN_NET_TYPE_NETWORK) { /* * If type=bridge then we attempt to allocate the tap fd here only if * running under a privilged user or -netdev bridge option is not * supported. */ if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK || driver->privileged || (!qemuCapsGet (priv->caps, QEMU_CAPS_NETDEV_BRIDGE))) { if ((tapfd = qemuNetworkIfaceConnect(vm->def, conn, driver, net, priv->caps)) < 0) goto cleanup; iface_connected = true; if (qemuOpenVhostNet(vm->def, net, priv->caps, &vhostfd) < 0) goto cleanup; } } else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) { if ((tapfd = qemuPhysIfaceConnect(vm->def, driver, net, priv->caps, VIR_NETDEV_VPORT_PROFILE_OP_CREATE)) < 0) goto cleanup; iface_connected = true; if (qemuOpenVhostNet(vm->def, net, priv->caps, &vhostfd) < 0) goto cleanup; } if (qemuCapsGet(priv->caps, QEMU_CAPS_NET_NAME) || qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0) goto cleanup; } if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE) && qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &net->info) < 0) goto cleanup; releaseaddr = true; if (qemuCapsGet(priv->caps, QEMU_CAPS_NETDEV) && qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { vlan = -1; } else { vlan = qemuDomainNetVLAN(net); if (vlan < 0) { virReportError(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; } if (vhostfd != -1) { if (virAsprintf(&vhostfd_name, "vhostfd-%s", net->info.alias) < 0) goto no_memory; } if (qemuCapsGet(priv->caps, QEMU_CAPS_NETDEV) && qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (!(netstr = qemuBuildHostNetStr(net, driver, priv->caps, ',', -1, tapfd_name, vhostfd_name))) goto cleanup; } else { if (!(netstr = qemuBuildHostNetStr(net, driver, priv->caps, ' ', vlan, tapfd_name, vhostfd_name))) goto cleanup; } qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_NETDEV) && qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuMonitorAddNetdev(priv->mon, netstr, tapfd, tapfd_name, vhostfd, vhostfd_name) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(vm, NULL, net, "attach", false); goto cleanup; } } else { if (qemuMonitorAddHostNetwork(priv->mon, netstr, tapfd, tapfd_name, vhostfd, vhostfd_name) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(vm, NULL, net, "attach", false); goto cleanup; } } qemuDomainObjExitMonitorWithDriver(driver, vm); VIR_FORCE_CLOSE(tapfd); VIR_FORCE_CLOSE(vhostfd); if (!virDomainObjIsActive(vm)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("guest unexpectedly quit")); goto cleanup; } if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (!(nicstr = qemuBuildNicDevStr(net, vlan, 0, priv->caps))) goto try_remove; } else { if (!(nicstr = qemuBuildNicStr(net, NULL, vlan))) goto try_remove; } qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuMonitorAddDevice(priv->mon, nicstr) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(vm, NULL, net, "attach", false); goto try_remove; } } else { guestAddr = net->info.addr.pci; if (qemuMonitorAddPCINetwork(priv->mon, nicstr, &guestAddr) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(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); /* set link state */ if (net->linkstate == VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN) { if (!net->info.alias) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device alias not found: cannot set link state to down")); } else { qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_NETDEV)) { if (qemuMonitorSetLink(priv->mon, net->info.alias, VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(vm, NULL, net, "attach", false); goto try_remove; } } else { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("setting of link state not supported: Link is up")); } qemuDomainObjExitMonitorWithDriver(driver, vm); } /* link set to down */ } virDomainAuditNet(vm, NULL, net, "attach", true); ret = 0; cleanup: if (!ret) { vm->def->nets[vm->def->nnets++] = net; } else { if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE) && (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && releaseaddr && qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, net->info.addr.pci.slot) < 0) VIR_WARN("Unable to release PCI address on NIC"); if (iface_connected) { virDomainConfNWFilterTeardown(net); vport = virDomainNetGetActualVirtPortProfile(net); if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) ignore_value(virNetDevOpenvswitchRemovePort( virDomainNetGetActualBridgeName(net), net->ifname)); } networkReleaseActualDevice(net); } VIR_FREE(nicstr); VIR_FREE(netstr); VIR_FREE(tapfd_name); VIR_FORCE_CLOSE(tapfd); VIR_FREE(vhostfd_name); VIR_FORCE_CLOSE(vhostfd); return ret; try_remove: if (!virDomainObjIsActive(vm)) goto cleanup; if (vlan < 0) { if (qemuCapsGet(priv->caps, QEMU_CAPS_NETDEV) && qemuCapsGet(priv->caps, QEMU_CAPS_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_WARN("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; no_memory: virReportOOMError(); goto cleanup; } int qemuDomainAttachHostPciDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { qemuDomainObjPrivatePtr priv = vm->privateData; int ret; char *devstr = NULL; int configfd = -1; char *configfd_name = NULL; bool releaseaddr = false; if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { virReportOOMError(); return -1; } if (qemuPrepareHostdevPCIDevices(driver, vm->def->name, vm->def->uuid, &hostdev, 1) < 0) return -1; if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0) goto error; if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, hostdev->info) < 0) goto error; releaseaddr = true; if (qemuCapsGet(priv->caps, QEMU_CAPS_PCI_CONFIGFD)) { configfd = qemuOpenPCIConfig(hostdev); if (configfd >= 0) { if (virAsprintf(&configfd_name, "fd-%s", hostdev->info->alias) < 0) { virReportOOMError(); goto error; } } } if (!virDomainObjIsActive(vm)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("guest unexpectedly quit during hotplug")); goto error; } if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name, priv->caps))) goto error; qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorAddDeviceWithFd(priv->mon, devstr, configfd, configfd_name); qemuDomainObjExitMonitorWithDriver(driver, vm); } else { virDevicePCIAddress guestAddr = hostdev->info->addr.pci; 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)); } virDomainAuditHostdev(vm, hostdev, "attach", ret == 0); 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 (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE) && (hostdev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && releaseaddr && qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, hostdev->info->addr.pci.slot) < 0) VIR_WARN("Unable to release PCI address on host device"); qemuDomainReAttachHostdevDevices(driver, vm->def->name, &hostdev, 1); VIR_FREE(devstr); VIR_FREE(configfd_name); VIR_FORCE_CLOSE(configfd); return -1; } int qemuDomainAttachRedirdevDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainRedirdevDefPtr redirdev) { int ret; qemuDomainObjPrivatePtr priv = vm->privateData; virDomainDefPtr def = vm->def; char *devstr = NULL; if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceRedirdevAlias(vm->def, redirdev, -1) < 0) goto error; if (!(devstr = qemuBuildRedirdevDevStr(def, redirdev, priv->caps))) goto error; } if (VIR_REALLOC_N(vm->def->redirdevs, vm->def->nredirdevs+1) < 0) { virReportOOMError(); goto error; } qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) ret = qemuMonitorAddDevice(priv->mon, devstr); else goto error; qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditRedirdev(vm, redirdev, "attach", ret == 0); if (ret < 0) goto error; vm->def->redirdevs[vm->def->nredirdevs++] = redirdev; VIR_FREE(devstr); return 0; error: VIR_FREE(devstr); return -1; } int qemuDomainAttachHostUsbDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { int ret; qemuDomainObjPrivatePtr priv = vm->privateData; char *devstr = NULL; if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0) goto error; if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev, priv->caps))) 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; qemuCgroupData data; if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=0 ) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to find cgroup for %s"), vm->def->name); goto error; } if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, hostdev->source.subsys.u.usb.device)) == NULL) goto error; data.vm = vm; data.cgroup = cgroup; if (usbDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, &data) < 0) goto error; } qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_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); virDomainAuditHostdev(vm, hostdev, "attach", ret == 0); if (ret < 0) goto error; vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; VIR_FREE(devstr); return 0; error: VIR_FREE(devstr); return -1; } int qemuDomainAttachHostDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainHostdevDefPtr hostdev) { usbDeviceList *list; usbDevice *usb = NULL; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hostdev mode '%s' not supported"), virDomainHostdevModeTypeToString(hostdev->mode)); return -1; } if (!(list = usbDeviceListNew())) goto cleanup; if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { unsigned vendor = hostdev->source.subsys.u.usb.vendor; unsigned product = hostdev->source.subsys.u.usb.product; unsigned bus = hostdev->source.subsys.u.usb.bus; unsigned device = hostdev->source.subsys.u.usb.device; if (vendor && bus) { usb = usbFindDevice(vendor, product, bus, device); } else if (vendor && !bus) { usbDeviceList *devs = usbFindDeviceByVendor(vendor, product); if (!devs) goto cleanup; if (usbDeviceListCount(devs) > 1) { virReportError(VIR_ERR_OPERATION_FAILED, _("multiple USB devices for %x:%x, " "use
to specify one"), vendor, product); usbDeviceListFree(devs); goto cleanup; } usb = usbDeviceListGet(devs, 0); usbDeviceListSteal(devs, usb); usbDeviceListFree(devs); hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); } else if (!vendor && bus) { usb = usbFindDeviceByBus(bus, device); } if (!usb) goto cleanup; if (usbDeviceListAdd(list, usb) < 0) { usbFreeDevice(usb); usb = NULL; goto cleanup; } if (qemuPrepareHostdevUSBDevices(driver, vm->def->name, list) < 0) { usb = NULL; goto cleanup; } usbDeviceListSteal(list, usb); } if (virSecurityManagerSetHostdevLabel(driver->securityManager, vm->def, hostdev) < 0) goto cleanup; switch (hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: if (qemuDomainAttachHostPciDevice(driver, vm, hostdev) < 0) goto error; break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: if (qemuDomainAttachHostUsbDevice(driver, vm, hostdev) < 0) goto error; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hostdev subsys type '%s' not supported"), virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type)); goto error; } usbDeviceListFree(list); return 0; error: if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, vm->def, hostdev) < 0) VIR_WARN("Unable to restore host device labelling on hotplug fail"); cleanup: usbDeviceListFree(list); if (usb) usbDeviceListSteal(driver->activeUsbHostdevs, usb); return -1; } static virDomainNetDefPtr qemuDomainFindNet(virDomainObjPtr vm, virDomainNetDefPtr dev) { int i; for (i = 0; i < vm->def->nnets; i++) { if (virMacAddrCmp(&vm->def->nets[i]->mac, &dev->mac) == 0) return vm->def->nets[i]; } return NULL; } static int qemuDomainChangeNetBridge(virDomainObjPtr vm, virDomainNetDefPtr olddev, virDomainNetDefPtr newdev) { int ret = -1; char *oldbridge = olddev->data.bridge.brname; char *newbridge = newdev->data.bridge.brname; VIR_DEBUG("Change bridge for interface %s: %s -> %s", olddev->ifname, oldbridge, newbridge); if (virNetDevExists(newbridge) != 1) { virReportError(VIR_ERR_OPERATION_FAILED, _("bridge %s doesn't exist"), newbridge); return -1; } if (oldbridge) { ret = virNetDevBridgeRemovePort(oldbridge, olddev->ifname); virDomainAuditNet(vm, olddev, NULL, "detach", ret == 0); if (ret < 0) return -1; } /* move newbridge into olddev now so Audit log is correct */ olddev->data.bridge.brname = newbridge; ret = virNetDevBridgeAddPort(newbridge, olddev->ifname); virDomainAuditNet(vm, NULL, olddev, "attach", ret == 0); if (ret < 0) { /* restore oldbridge to olddev */ olddev->data.bridge.brname = oldbridge; ret = virNetDevBridgeAddPort(oldbridge, olddev->ifname); virDomainAuditNet(vm, NULL, olddev, "attach", ret == 0); if (ret < 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("unable to recover former state by adding port " "to bridge %s"), oldbridge); } return -1; } /* oldbridge no longer needed, and newbridge moved to olddev */ VIR_FREE(oldbridge); newdev->data.bridge.brname = NULL; return 0; } int qemuDomainChangeNetLinkState(struct qemud_driver *driver, virDomainObjPtr vm, virDomainNetDefPtr dev, int linkstate) { int ret = -1; qemuDomainObjPrivatePtr priv = vm->privateData; VIR_DEBUG("dev: %s, state: %d", dev->info.alias, linkstate); if (!dev->info.alias) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("can't change link state: device alias not found")); return -1; } qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorSetLink(priv->mon, dev->info.alias, linkstate); if (ret < 0) goto cleanup; /* modify the device configuration */ dev->linkstate = linkstate; cleanup: qemuDomainObjExitMonitorWithDriver(driver, vm); return ret; } int qemuDomainChangeNet(struct qemud_driver *driver, virDomainObjPtr vm, virDomainPtr dom ATTRIBUTE_UNUSED, virDomainNetDefPtr dev) { virDomainNetDefPtr olddev = qemuDomainFindNet(vm, dev); int ret = 0; if (!olddev) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot find existing network device to modify")); return -1; } if (olddev->type != dev->type) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot change network interface type")); return -1; } if (!virNetDevVPortProfileEqual(olddev->virtPortProfile, dev->virtPortProfile)) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot change settings")); } switch (olddev->type) { case VIR_DOMAIN_NET_TYPE_USER: break; case VIR_DOMAIN_NET_TYPE_ETHERNET: if (STRNEQ_NULLABLE(olddev->data.ethernet.dev, dev->data.ethernet.dev) || STRNEQ_NULLABLE(olddev->script, dev->script) || STRNEQ_NULLABLE(olddev->data.ethernet.ipaddr, dev->data.ethernet.ipaddr)) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot modify ethernet network device configuration")); return -1; } break; case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_MCAST: if (STRNEQ_NULLABLE(olddev->data.socket.address, dev->data.socket.address) || olddev->data.socket.port != dev->data.socket.port) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot modify network socket device configuration")); return -1; } break; case VIR_DOMAIN_NET_TYPE_NETWORK: if (STRNEQ_NULLABLE(olddev->data.network.name, dev->data.network.name) || STRNEQ_NULLABLE(olddev->data.network.portgroup, dev->data.network.portgroup)) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot modify network device configuration")); return -1; } break; case VIR_DOMAIN_NET_TYPE_BRIDGE: /* allow changing brname */ break; case VIR_DOMAIN_NET_TYPE_INTERNAL: if (STRNEQ_NULLABLE(olddev->data.internal.name, dev->data.internal.name)) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot modify internal network device configuration")); return -1; } break; case VIR_DOMAIN_NET_TYPE_DIRECT: if (STRNEQ_NULLABLE(olddev->data.direct.linkdev, dev->data.direct.linkdev) || olddev->data.direct.mode != dev->data.direct.mode) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot modify direct network device configuration")); return -1; } break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to change config on '%s' network type"), virDomainNetTypeToString(dev->type)); break; } /* all other unmodifiable parameters */ if (STRNEQ_NULLABLE(olddev->model, dev->model) || STRNEQ_NULLABLE(olddev->filter, dev->filter)) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot modify network device configuration")); return -1; } /* check if device name has been set, if no, retain the autogenerated one */ if (dev->ifname && STRNEQ_NULLABLE(olddev->ifname, dev->ifname)) { virReportError(VIR_ERR_NO_SUPPORT, "%s", _("cannot modify network device configuration")); return -1; } if (olddev->type == VIR_DOMAIN_NET_TYPE_BRIDGE && STRNEQ_NULLABLE(olddev->data.bridge.brname, dev->data.bridge.brname)) { if ((ret = qemuDomainChangeNetBridge(vm, olddev, dev)) < 0) return ret; } if (olddev->linkstate != dev->linkstate) { if ((ret = qemuDomainChangeNetLinkState(driver, vm, olddev, dev->linkstate)) < 0) return ret; } return ret; } 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); const char *oldListenAddr, *newListenAddr; const char *oldListenNetwork, *newListenNetwork; int ret = -1; if (!olddev) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot find existing graphics device to modify")); return -1; } oldListenAddr = virDomainGraphicsListenGetAddress(olddev, 0); newListenAddr = virDomainGraphicsListenGetAddress(dev, 0); oldListenNetwork = virDomainGraphicsListenGetNetwork(olddev, 0); newListenNetwork = virDomainGraphicsListenGetNetwork(dev, 0); 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))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot change port settings on vnc graphics")); return -1; } if (STRNEQ_NULLABLE(oldListenAddr,newListenAddr)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot change listen address setting on vnc graphics")); return -1; } if (STRNEQ_NULLABLE(oldListenNetwork,newListenNetwork)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot change listen network setting on vnc graphics")); return -1; } if (STRNEQ_NULLABLE(olddev->data.vnc.keymap, dev->data.vnc.keymap)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot change keymap setting on vnc graphics")); return -1; } /* If a password lifetime was, or is set, or action if connected has * changed, then we must always run, even if new password matches * old password */ if (olddev->data.vnc.auth.expires || dev->data.vnc.auth.expires || olddev->data.vnc.auth.connected != dev->data.vnc.auth.connected || 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); ret = qemuDomainChangeGraphicsPasswords(driver, vm, VIR_DOMAIN_GRAPHICS_TYPE_VNC, &dev->data.vnc.auth, driver->vncPassword); if (ret < 0) return ret; /* 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; olddev->data.vnc.auth.validTo = dev->data.vnc.auth.validTo; olddev->data.vnc.auth.expires = dev->data.vnc.auth.expires; olddev->data.vnc.auth.connected = dev->data.vnc.auth.connected; } else { ret = 0; } break; case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: if ((olddev->data.spice.autoport != dev->data.spice.autoport) || (!dev->data.spice.autoport && (olddev->data.spice.port != dev->data.spice.port)) || (!dev->data.spice.autoport && (olddev->data.spice.tlsPort != dev->data.spice.tlsPort))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot change port settings on spice graphics")); return -1; } if (STRNEQ_NULLABLE(oldListenAddr, newListenAddr)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot change listen address setting on spice graphics")); return -1; } if (STRNEQ_NULLABLE(oldListenNetwork, newListenNetwork)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot change listen network setting on spice graphics")); return -1; } if (STRNEQ_NULLABLE(olddev->data.spice.keymap, dev->data.spice.keymap)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot change keymap setting on spice graphics")); return -1; } /* We must reset the password if it has changed but also if: * - password lifetime is or was set * - the requested action has changed * - the action is "disconnect" */ if (olddev->data.spice.auth.expires || dev->data.spice.auth.expires || olddev->data.spice.auth.connected != dev->data.spice.auth.connected || dev->data.spice.auth.connected == VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_DISCONNECT || STRNEQ_NULLABLE(olddev->data.spice.auth.passwd, dev->data.spice.auth.passwd)) { VIR_DEBUG("Updating password on SPICE server %p %p", dev->data.spice.auth.passwd, driver->spicePassword); ret = qemuDomainChangeGraphicsPasswords(driver, vm, VIR_DOMAIN_GRAPHICS_TYPE_SPICE, &dev->data.spice.auth, driver->spicePassword); if (ret < 0) return ret; /* Steal the new dev's char * reference */ VIR_FREE(olddev->data.spice.auth.passwd); olddev->data.spice.auth.passwd = dev->data.spice.auth.passwd; dev->data.spice.auth.passwd = NULL; olddev->data.spice.auth.validTo = dev->data.spice.auth.validTo; olddev->data.spice.auth.expires = dev->data.spice.auth.expires; olddev->data.spice.auth.connected = dev->data.spice.auth.connected; } else { VIR_DEBUG("Not updating since password didn't change"); ret = 0; } break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to change config on '%s' graphics type"), virDomainGraphicsTypeToString(dev->type)); break; } return ret; } static inline int qemuFindDisk(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 qemuComparePCIDevice(virDomainDefPtr def ATTRIBUTE_UNUSED, virDomainDeviceDefPtr device ATTRIBUTE_UNUSED, virDomainDeviceInfoPtr info1, void *opaque) { virDomainDeviceInfoPtr info2 = opaque; if (info1->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI || info2->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) return 0; if (info1->addr.pci.slot == info2->addr.pci.slot && info1->addr.pci.function != info2->addr.pci.function) return -1; return 0; } static bool qemuIsMultiFunctionDevice(virDomainDefPtr def, virDomainDeviceInfoPtr dev) { if (virDomainDeviceInfoIterate(def, qemuComparePCIDevice, dev) < 0) return true; return false; } int qemuDomainDetachPciDiskDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int i, ret = -1; virDomainDiskDefPtr detach = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; virCgroupPtr cgroup = NULL; char *drivestr = NULL; i = qemuFindDisk(vm->def, dev->data.disk->dst); if (i < 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("disk %s not found"), dev->data.disk->dst); goto cleanup; } detach = vm->def->disks[i]; if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %s"), dev->data.disk->dst); goto cleanup; } if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to find cgroup for %s"), vm->def->name); goto cleanup; } } if (!virDomainDeviceAddressIsValid(&detach->info, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { virReportError(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 (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditDisk(vm, detach->src, NULL, "detach", false); goto cleanup; } } else { if (qemuMonitorRemovePCIDevice(priv->mon, &detach->info.addr.pci) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditDisk(vm, detach->src, NULL, "detach", false); goto cleanup; } } /* disconnect guest from host device */ qemuMonitorDriveDel(priv->mon, drivestr); qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditDisk(vm, detach->src, NULL, "detach", true); if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE) && qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, detach->info.addr.pci.slot) < 0) VIR_WARN("Unable to release PCI address on %s", dev->data.disk->src); virDomainDiskRemove(vm->def, i); virDomainDiskDefFree(detach); if (virSecurityManagerRestoreImageLabel(driver->securityManager, vm->def, dev->data.disk) < 0) VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); if (cgroup != NULL) { if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } if (virDomainLockDiskDetach(driver->lockManager, vm, dev->data.disk) < 0) VIR_WARN("Unable to release lock on %s", dev->data.disk->src); ret = 0; cleanup: virCgroupFree(&cgroup); VIR_FREE(drivestr); return ret; } int qemuDomainDetachDiskDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int i, ret = -1; virDomainDiskDefPtr detach = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; virCgroupPtr cgroup = NULL; char *drivestr = NULL; i = qemuFindDisk(vm->def, dev->data.disk->dst); if (i < 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("disk %s not found"), dev->data.disk->dst); goto cleanup; } if (!qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { virReportError(VIR_ERR_OPERATION_FAILED, _("Underlying qemu does not support %s disk removal"), virDomainDiskBusTypeToString(dev->data.disk->bus)); goto cleanup; } detach = vm->def->disks[i]; if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to find cgroup for %s"), 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) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditDisk(vm, detach->src, NULL, "detach", false); goto cleanup; } /* disconnect guest from host device */ qemuMonitorDriveDel(priv->mon, drivestr); qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditDisk(vm, detach->src, NULL, "detach", true); virDomainDiskRemove(vm->def, i); virDomainDiskDefFree(detach); if (virSecurityManagerRestoreImageLabel(driver->securityManager, vm->def, dev->data.disk) < 0) VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); if (cgroup != NULL) { if (qemuTeardownDiskCgroup(driver, vm, cgroup, dev->data.disk) < 0) VIR_WARN("Failed to teardown cgroup for disk path %s", NULLSTR(dev->data.disk->src)); } if (virDomainLockDiskDetach(driver->lockManager, vm, dev->data.disk) < 0) VIR_WARN("Unable to release lock on disk %s", dev->data.disk->src); ret = 0; cleanup: VIR_FREE(drivestr); virCgroupFree(&cgroup); return ret; } static bool qemuDomainDiskControllerIsBusy(virDomainObjPtr vm, virDomainControllerDefPtr detach) { int i; virDomainDiskDefPtr disk; for (i = 0; i < vm->def->ndisks; i++) { disk = vm->def->disks[i]; if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) /* the disk does not use disk controller */ continue; /* check whether the disk uses this type controller */ if (disk->bus == VIR_DOMAIN_DISK_BUS_IDE && detach->type != VIR_DOMAIN_CONTROLLER_TYPE_IDE) continue; if (disk->bus == VIR_DOMAIN_DISK_BUS_FDC && detach->type != VIR_DOMAIN_CONTROLLER_TYPE_FDC) continue; if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI && detach->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) continue; if (disk->info.addr.drive.controller == detach->idx) return true; } return false; } static bool qemuDomainControllerIsBusy(virDomainObjPtr vm, virDomainControllerDefPtr detach) { switch (detach->type) { case VIR_DOMAIN_CONTROLLER_TYPE_IDE: case VIR_DOMAIN_CONTROLLER_TYPE_FDC: case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: return qemuDomainDiskControllerIsBusy(vm, detach); case VIR_DOMAIN_CONTROLLER_TYPE_SATA: case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: case VIR_DOMAIN_CONTROLLER_TYPE_CCID: default: /* libvirt does not support sata controller, and does not support to * detach virtio and smart card controller. */ return true; } } int qemuDomainDetachPciControllerDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int idx, ret = -1; virDomainControllerDefPtr detach = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; if ((idx = virDomainControllerFind(vm->def, dev->data.controller->type, dev->data.controller->idx)) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("disk controller %s:%d not found"), virDomainControllerTypeToString(dev->data.controller->type), dev->data.controller->idx); goto cleanup; } detach = vm->def->controllers[idx]; if (!virDomainDeviceAddressIsValid(&detach->info, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device cannot be detached without a PCI address")); goto cleanup; } if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %s"), dev->data.disk->dst); goto cleanup; } if (qemuDomainControllerIsBusy(vm, detach)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device cannot be detached: device is busy")); goto cleanup; } if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuAssignDeviceControllerAlias(detach) < 0) goto cleanup; } qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuMonitorDelDevice(priv->mon, detach->info.alias)) { qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } } else { if (qemuMonitorRemovePCIDevice(priv->mon, &detach->info.addr.pci) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); goto cleanup; } } qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainControllerRemove(vm->def, idx); virDomainControllerDefFree(detach); if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE) && qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, detach->info.addr.pci.slot) < 0) VIR_WARN("Unable to release PCI address on controller"); ret = 0; cleanup: return ret; } static int qemuDomainDetachHostPciDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainHostdevDefPtr detach) { qemuDomainObjPrivatePtr priv = vm->privateData; virDomainHostdevSubsysPtr subsys = &detach->source.subsys; int ret; pciDevice *pci; pciDevice *activePci; if (qemuIsMultiFunctionDevice(vm->def, detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device: %.4x:%.2x:%.2x.%.1x"), subsys->u.pci.domain, subsys->u.pci.bus, subsys->u.pci.slot, subsys->u.pci.function); return -1; } if (!virDomainDeviceAddressIsValid(detach->info, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device cannot be detached without a PCI address")); return -1; } qemuDomainObjEnterMonitorWithDriver(driver, vm); if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { ret = qemuMonitorDelDevice(priv->mon, detach->info->alias); } else { ret = qemuMonitorRemovePCIDevice(priv->mon, &detach->info->addr.pci); } qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditHostdev(vm, detach, "detach", ret == 0); if (ret < 0) return -1; /* * For SRIOV net host devices, unset mac and port profile before * reset and reattach device */ if (detach->parent.data.net) qemuDomainHostdevNetConfigRestore(detach, driver->stateDir); pci = pciGetDevice(subsys->u.pci.domain, subsys->u.pci.bus, subsys->u.pci.slot, subsys->u.pci.function); if (pci) { activePci = pciDeviceListSteal(driver->activePciHostdevs, pci); if (activePci && pciResetDevice(activePci, driver->activePciHostdevs, driver->inactivePciHostdevs) == 0) { qemuReattachPciDevice(activePci, driver); } else { /* reset of the device failed, treat it as if it was returned */ pciFreeDevice(activePci); ret = -1; } pciFreeDevice(pci); } else { ret = -1; } if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE) && qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, detach->info->addr.pci.slot) < 0) VIR_WARN("Unable to release PCI address on host device"); return ret; } static int qemuDomainDetachHostUsbDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainHostdevDefPtr detach) { qemuDomainObjPrivatePtr priv = vm->privateData; virDomainHostdevSubsysPtr subsys = &detach->source.subsys; usbDevice *usb; int ret; if (!detach->info->alias) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device cannot be detached without a device alias")); return -1; } if (!qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device cannot be detached with this QEMU version")); return -1; } qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorDelDevice(priv->mon, detach->info->alias); qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditHostdev(vm, detach, "detach", ret == 0); if (ret < 0) return -1; usb = usbGetDevice(subsys->u.usb.bus, subsys->u.usb.device); if (usb) { usbDeviceListDel(driver->activeUsbHostdevs, usb); usbFreeDevice(usb); } else { VIR_WARN("Unable to find device %03d.%03d in list of used USB devices", subsys->u.usb.bus, subsys->u.usb.device); } return ret; } static int qemuDomainDetachThisHostDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainHostdevDefPtr detach, int idx) { int ret = -1; if (idx < 0) { /* caller didn't know index of hostdev in hostdevs list, so we * need to find it. */ for (idx = 0; idx < vm->def->nhostdevs; idx++) { if (vm->def->hostdevs[idx] == detach) break; } if (idx >= vm->def->nhostdevs) { virReportError(VIR_ERR_INTERNAL_ERROR, _("device not found in hostdevs list (%d entries)"), vm->def->nhostdevs); return ret; } } switch (detach->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: ret = qemuDomainDetachHostPciDevice(driver, vm, detach); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: ret = qemuDomainDetachHostUsbDevice(driver, vm, detach); break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hostdev subsys type '%s' not supported"), virDomainHostdevSubsysTypeToString(detach->source.subsys.type)); return -1; } if (!ret) { if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, vm->def, detach) < 0) { VIR_WARN("Failed to restore host device labelling"); } virDomainHostdevRemove(vm->def, idx); virDomainHostdevDefFree(detach); } return ret; } /* search for a hostdev matching dev and detach it */ int qemuDomainDetachHostDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { virDomainHostdevDefPtr hostdev = dev->data.hostdev; virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys; virDomainHostdevDefPtr detach = NULL; int idx; if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("hostdev mode '%s' not supported"), virDomainHostdevModeTypeToString(hostdev->mode)); return -1; } idx = virDomainHostdevFind(vm->def, hostdev, &detach); if (idx < 0) { switch(subsys->type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: virReportError(VIR_ERR_OPERATION_FAILED, _("host pci device %.4x:%.2x:%.2x.%.1x not found"), subsys->u.pci.domain, subsys->u.pci.bus, subsys->u.pci.slot, subsys->u.pci.function); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: if (subsys->u.usb.bus && subsys->u.usb.device) { virReportError(VIR_ERR_OPERATION_FAILED, _("host usb device %03d.%03d not found"), subsys->u.usb.bus, subsys->u.usb.device); } else { virReportError(VIR_ERR_OPERATION_FAILED, _("host usb device vendor=0x%.4x product=0x%.4x not found"), subsys->u.usb.vendor, subsys->u.usb.product); } break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected hostdev type %d"), subsys->type); break; } return -1; } /* If this is a network hostdev, we need to use the higher-level detach * function so that mac address / virtualport are reset */ if (detach->parent.type == VIR_DOMAIN_DEVICE_NET) return qemuDomainDetachNetDevice(driver, vm, &detach->parent); else return qemuDomainDetachThisHostDevice(driver, vm, detach, idx); } int qemuDomainDetachNetDevice(struct qemud_driver *driver, virDomainObjPtr vm, virDomainDeviceDefPtr dev) { int i, ret = -1; virDomainNetDefPtr detach = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; int vlan; char *hostnet_name = NULL; virNetDevVPortProfilePtr vport = NULL; for (i = 0 ; i < vm->def->nnets ; i++) { virDomainNetDefPtr net = vm->def->nets[i]; if (!virMacAddrCmp(&net->mac, &dev->data.net->mac)) { detach = net; break; } } if (!detach) { virReportError(VIR_ERR_OPERATION_FAILED, _("network device %02x:%02x:%02x:%02x:%02x:%02x not found"), dev->data.net->mac.addr[0], dev->data.net->mac.addr[1], dev->data.net->mac.addr[2], dev->data.net->mac.addr[3], dev->data.net->mac.addr[4], dev->data.net->mac.addr[5]); goto cleanup; } if (virDomainNetGetActualType(detach) == VIR_DOMAIN_NET_TYPE_HOSTDEV) { ret = qemuDomainDetachThisHostDevice(driver, vm, virDomainNetGetActualHostdev(detach), -1); goto cleanup; } if (!virDomainDeviceAddressIsValid(&detach->info, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("device cannot be detached without a PCI address")); goto cleanup; } if (qemuIsMultiFunctionDevice(vm->def, &detach->info)) { virReportError(VIR_ERR_OPERATION_FAILED, _("cannot hot unplug multifunction PCI device :%s"), dev->data.disk->dst); goto cleanup; } if ((vlan = qemuDomainNetVLAN(detach)) < 0) { virReportError(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 (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(vm, detach, NULL, "detach", false); goto cleanup; } } else { if (qemuMonitorRemovePCIDevice(priv->mon, &detach->info.addr.pci) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(vm, detach, NULL, "detach", false); goto cleanup; } } if (qemuCapsGet(priv->caps, QEMU_CAPS_NETDEV) && qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE)) { if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(vm, detach, NULL, "detach", false); goto cleanup; } } else { if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) { qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(vm, detach, NULL, "detach", false); goto cleanup; } } qemuDomainObjExitMonitorWithDriver(driver, vm); virDomainAuditNet(vm, detach, NULL, "detach", true); if (qemuCapsGet(priv->caps, QEMU_CAPS_DEVICE) && qemuDomainPCIAddressReleaseSlot(priv->pciaddrs, detach->info.addr.pci.slot) < 0) VIR_WARN("Unable to release PCI address on NIC"); virDomainConfNWFilterTeardown(detach); if (virDomainNetGetActualType(detach) == VIR_DOMAIN_NET_TYPE_DIRECT) { ignore_value(virNetDevMacVLanDeleteWithVPortProfile( detach->ifname, &detach->mac, virDomainNetGetActualDirectDev(detach), virDomainNetGetActualDirectMode(detach), virDomainNetGetActualVirtPortProfile(detach), driver->stateDir)); VIR_FREE(detach->ifname); } 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); } } vport = virDomainNetGetActualVirtPortProfile(detach); if (vport && vport->virtPortType == VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH) ignore_value(virNetDevOpenvswitchRemovePort( virDomainNetGetActualBridgeName(detach), detach->ifname)); ret = 0; cleanup: if (!ret) { networkReleaseActualDevice(detach); virDomainNetRemove(vm->def, i); virDomainNetDefFree(detach); } VIR_FREE(hostnet_name); return ret; } int qemuDomainChangeGraphicsPasswords(struct qemud_driver *driver, virDomainObjPtr vm, int type, virDomainGraphicsAuthDefPtr auth, const char *defaultPasswd) { qemuDomainObjPrivatePtr priv = vm->privateData; time_t now = time(NULL); char expire_time [64]; const char *connected = NULL; const char *password; int ret; if (!auth->passwd && !driver->vncPassword) return 0; password = auth->passwd ? auth->passwd : defaultPasswd; if (auth->connected) connected = virDomainGraphicsAuthConnectedTypeToString(auth->connected); qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorSetPassword(priv->mon, type, password, connected); if (ret == -2) { if (type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Graphics password only supported for VNC")); ret = -1; } else { ret = qemuMonitorSetVNCPassword(priv->mon, password); } } if (ret != 0) goto cleanup; if (password[0] == '\0') { snprintf(expire_time, sizeof(expire_time), "now"); } else if (auth->expires) { time_t lifetime = auth->validTo - now; if (lifetime <= 0) snprintf(expire_time, sizeof(expire_time), "now"); else snprintf(expire_time, sizeof(expire_time), "%lu", (long unsigned)auth->validTo); } else { snprintf(expire_time, sizeof(expire_time), "never"); } ret = qemuMonitorExpirePassword(priv->mon, type, expire_time); if (ret == -2) { /* XXX we could fake this with a timer */ if (auth->expires) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Expiry of passwords is not supported")); ret = -1; } else { ret = 0; } } cleanup: qemuDomainObjExitMonitorWithDriver(driver, vm); return ret; } int qemuDomainAttachLease(struct qemud_driver *driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease) { if (virDomainLeaseInsertPreAlloc(vm->def) < 0) return -1; if (virDomainLockLeaseAttach(driver->lockManager, vm, lease) < 0) { virDomainLeaseInsertPreAlloced(vm->def, NULL); return -1; } virDomainLeaseInsertPreAlloced(vm->def, lease); return 0; } int qemuDomainDetachLease(struct qemud_driver *driver, virDomainObjPtr vm, virDomainLeaseDefPtr lease) { virDomainLeaseDefPtr det_lease; int i; if ((i = virDomainLeaseIndex(vm->def, lease)) < 0) { virReportError(VIR_ERR_INVALID_ARG, _("Lease %s in lockspace %s does not exist"), lease->key, NULLSTR(lease->lockspace)); return -1; } if (virDomainLockLeaseDetach(driver->lockManager, vm, lease) < 0) return -1; det_lease = virDomainLeaseRemoveAt(vm->def, i); virDomainLeaseDefFree(det_lease); return 0; }