[libvirt] [PATCH 0/8] Initial integration of lock managers

This patch series is the bare minimum framework required to integrate with external lock managers. This is a fleshing out of the original proposal in http://www.redhat.com/archives/libvir-list/2010-September/msg00167.html What's in this series * Major refactor of the security drivers to simplify code and clarify interaction with lock maanger * Low level internal lock manager plugin API for locking objects and their resources * High level internal lock manager API for locking virtual machines and their disks * Simple 'no op' plugin impl to stub out all the code * Re-ordering of QEMU startup to add a handshake between libvirtd & the child process, immediately prior to exec(). This allows acquiring locks in the child, before the parent libvirtd process then re-labels disks * The public API header for 3rd parties to implement new lock manager plugins against * Some basic documentation What's not in this series * A standard 'fcntl' based plugin * Enhanced migration process to transfer lock state * Hooks to re-validate the lock at time of VM resume

The current security driver usage requires horrible code like if (driver->securityDriver && driver->securityDriver->domainSetSecurityHostdevLabel && driver->securityDriver->domainSetSecurityHostdevLabel(driver->securityDriver, vm, hostdev) < 0) This pair of checks for NULL clutters up the code, making the driver calls 2 lines longer than they really need to be. The goal of the patchset is to change the calling convention to simply if (virSecurityManagerSetHostdevLabel(driver->securityDriver, vm, hostdev) < 0) The first check for 'driver->securityDriver' being NULL is removed by introducing a 'no op' security driver that will always be present if no real driver is enabled. This guarentees driver->securityDriver != NULL. The second check for 'driver->securityDriver->domainSetSecurityHostdevLabel' being non-NULL is hidden in a new abstraction called virSecurityManager. This separates the driver callbacks, from main internal API. The addition of a virSecurityManager object, that is separate from the virSecurityDriver struct also allows for security drivers to carry state / configuration information directly. Thus the DAC/Stack drivers from src/qemu which used to pull config from 'struct qemud_driver' can now be moved into the 'src/security' directory and store their config directly. * src/qemu/qemu_conf.h, src/qemu/qemu_driver.c: Update to use new virSecurityManager APIs * src/qemu/qemu_security_dac.c, src/qemu/qemu_security_dac.h src/qemu/qemu_security_stacked.c, src/qemu/qemu_security_stacked.h: Move into src/security directory * src/security/security_stack.c, src/security/security_stack.h, src/security/security_dac.c, src/security/security_dac.h: Generic versions of previous QEMU specific drivers * src/security/security_apparmor.c, src/security/security_apparmor.h, src/security/security_driver.c, src/security/security_driver.h, src/security/security_selinux.c, src/security/security_selinux.h: Update to take virSecurityManagerPtr object as the first param in all callbacks * src/security/security_nop.c, src/security/security_nop.h: Stub implementation of all security driver APIs. * src/security/security_manager.h, src/security/security_manager.c: New internal API for invoking security drivers --- src/Makefile.am | 12 +- src/libvirt_private.syms | 33 ++- src/qemu/qemu_conf.h | 6 +- src/qemu/qemu_driver.c | 250 +++++--------- src/qemu/qemu_security_dac.c | 590 ------------------------------- src/qemu/qemu_security_dac.h | 22 -- src/qemu/qemu_security_stacked.c | 418 ---------------------- src/qemu/qemu_security_stacked.h | 22 -- src/security/security_apparmor.c | 152 ++++++--- src/security/security_apparmor.h | 2 + src/security/security_dac.c | 712 ++++++++++++++++++++++++++++++++++++++ src/security/security_dac.h | 27 ++ src/security/security_driver.c | 116 ++----- src/security/security_driver.h | 95 +++--- src/security/security_manager.c | 291 ++++++++++++++++ src/security/security_manager.h | 74 ++++ src/security/security_nop.c | 168 +++++++++ src/security/security_nop.h | 17 + src/security/security_selinux.c | 145 +++++--- src/security/security_selinux.h | 2 +- src/security/security_stack.c | 383 ++++++++++++++++++++ src/security/security_stack.h | 24 ++ 22 files changed, 2079 insertions(+), 1482 deletions(-) delete mode 100644 src/qemu/qemu_security_dac.c delete mode 100644 src/qemu/qemu_security_dac.h delete mode 100644 src/qemu/qemu_security_stacked.c delete mode 100644 src/qemu/qemu_security_stacked.h create mode 100644 src/security/security_dac.c create mode 100644 src/security/security_dac.h create mode 100644 src/security/security_manager.c create mode 100644 src/security/security_manager.h create mode 100644 src/security/security_nop.c create mode 100644 src/security/security_nop.h create mode 100644 src/security/security_stack.c create mode 100644 src/security/security_stack.h diff --git a/src/Makefile.am b/src/Makefile.am index a9a1986..66af9cf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -273,11 +273,7 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_monitor_json.h \ qemu/qemu_driver.c qemu/qemu_driver.h \ qemu/qemu_bridge_filter.c \ - qemu/qemu_bridge_filter.h \ - qemu/qemu_security_stacked.h \ - qemu/qemu_security_stacked.c \ - qemu/qemu_security_dac.h \ - qemu/qemu_security_dac.c + qemu/qemu_bridge_filter.h XENAPI_DRIVER_SOURCES = \ xenapi/xenapi_driver.c xenapi/xenapi_driver.h \ @@ -376,7 +372,11 @@ NWFILTER_DRIVER_SOURCES = \ # Security framework and drivers for various models SECURITY_DRIVER_SOURCES = \ - security/security_driver.h security/security_driver.c + security/security_driver.h security/security_driver.c \ + security/security_nop.h security/security_nop.c \ + security/security_stack.h security/security_stack.c \ + security/security_dac.h security/security_dac.c \ + security/security_manager.h security/security_manager.c SECURITY_DRIVER_SELINUX_SOURCES = \ security/security_selinux.h security/security_selinux.c diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 8bf1028..3af0210 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -646,13 +646,32 @@ virSecretDefParseFile; virSecretDefParseString; -# security.h -virSecurityDriverGetDOI; -virSecurityDriverGetModel; -virSecurityDriverInit; -virSecurityDriverSetDOI; -virSecurityDriverStartup; -virSecurityDriverVerify; +# security_driver.h +virSecurityDriverLookup; + + +# security_manager.h +virSecurityManagerClearSocketLabel; +virSecurityManagerGenLabel; +virSecurityManagerGetDOI; +virSecurityManagerGetModel; +virSecurityManagerGetProcessLabel; +virSecurityManagerNew; +virSecurityManagerNewStack; +virSecurityManagerNewDAC; +virSecurityManagerReleaseLabel; +virSecurityManagerReserveLabel; +virSecurityManagerRestoreImageLabel; +virSecurityManagerRestoreAllLabel; +virSecurityManagerRestoreHostdevLabel; +virSecurityManagerRestoreSavedStateLabel; +virSecurityManagerSetAllLabel; +virSecurityManagerSetImageLabel; +virSecurityManagerSetHostdevLabel; +virSecurityManagerSetProcessLabel; +virSecurityManagerSetSavedStateLabel; +virSecurityManagerSetSocketLabel; +virSecurityManagerVerify; # storage_conf.h diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 790ce98..ba3c7b1 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -34,7 +34,7 @@ # include "domain_conf.h" # include "domain_event.h" # include "threads.h" -# include "security/security_driver.h" +# include "security/security_manager.h" # include "cgroup.h" # include "pci.h" # include "cpu_conf.h" @@ -166,9 +166,7 @@ struct qemud_driver { int domainEventDispatching; char *securityDriverName; - virSecurityDriverPtr securityDriver; - virSecurityDriverPtr securityPrimaryDriver; - virSecurityDriverPtr securitySecondaryDriver; + virSecurityManagerPtr securityManager; char *saveImageFormat; char *dumpImageFormat; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ed1ea6b..2b901b8 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -70,8 +70,6 @@ #include "pci.h" #include "hostusb.h" #include "processinfo.h" -#include "qemu_security_stacked.h" -#include "qemu_security_dac.h" #include "cgroup.h" #include "libvirt_internal.h" #include "xml.h" @@ -1395,10 +1393,7 @@ qemuConnectMonitor(struct qemud_driver *driver, virDomainObjPtr vm) qemuDomainObjPrivatePtr priv = vm->privateData; int ret = -1; - if (driver->securityDriver && - driver->securityDriver->domainSetSecuritySocketLabel && - driver->securityDriver->domainSetSecuritySocketLabel - (driver->securityDriver,vm) < 0) { + if (virSecurityManagerSetSocketLabel(driver->securityManager, vm) < 0) { VIR_ERROR(_("Failed to set security context for monitor for %s"), vm->def->name); goto error; @@ -1416,10 +1411,7 @@ qemuConnectMonitor(struct qemud_driver *driver, virDomainObjPtr vm) if (priv->mon == NULL) virDomainObjUnref(vm); - if (driver->securityDriver && - driver->securityDriver->domainClearSecuritySocketLabel && - driver->securityDriver->domainClearSecuritySocketLabel - (driver->securityDriver,vm) < 0) { + if (virSecurityManagerClearSocketLabel(driver->securityManager, vm) < 0) { VIR_ERROR(_("Failed to clear security context for monitor for %s"), vm->def->name); goto error; @@ -1488,10 +1480,7 @@ qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaq goto error; } - if (driver->securityDriver && - driver->securityDriver->domainReserveSecurityLabel && - driver->securityDriver->domainReserveSecurityLabel(driver->securityDriver, - obj) < 0) + if (virSecurityManagerReserveLabel(driver->securityManager, obj) < 0) goto error; if (qemudVMFiltersInstantiate(conn, obj->def)) @@ -1531,30 +1520,24 @@ qemuReconnectDomains(virConnectPtr conn, struct qemud_driver *driver) static int qemudSecurityInit(struct qemud_driver *qemud_drv) { - int ret; - virSecurityDriverPtr security_drv; - - qemuSecurityStackedSetDriver(qemud_drv); - qemuSecurityDACSetDriver(qemud_drv); - - ret = virSecurityDriverStartup(&security_drv, - qemud_drv->securityDriverName, - qemud_drv->allowDiskFormatProbing); - if (ret == -1) { - VIR_ERROR0(_("Failed to start security driver")); + virSecurityManagerPtr mgr = virSecurityManagerNew(qemud_drv->securityDriverName, + qemud_drv->allowDiskFormatProbing); + if (!mgr) return -1; - } - /* No primary security driver wanted to be enabled: just setup - * the DAC driver on its own */ - if (ret == -2) { - qemud_drv->securityDriver = &qemuDACSecurityDriver; - VIR_INFO0(_("No security driver available")); + if (qemud_drv->privileged) { + virSecurityManagerPtr dac = virSecurityManagerNewDAC(qemud_drv->user, + qemud_drv->group, + qemud_drv->allowDiskFormatProbing, + qemud_drv->dynamicOwnership); + if (!dac) + return -1; + + if (!(qemud_drv->securityManager = virSecurityManagerNewStack(mgr, + dac))) + return -1; } else { - qemud_drv->securityPrimaryDriver = security_drv; - qemud_drv->securitySecondaryDriver = &qemuDACSecurityDriver; - qemud_drv->securityDriver = &qemuStackedSecurityDriver; - VIR_INFO("Initialized security driver %s", security_drv->name); + qemud_drv->securityManager = mgr; } return 0; @@ -1600,20 +1583,22 @@ qemuCreateCapabilities(virCapsPtr oldcaps, caps->ns.href = qemuDomainDefNamespaceHref; /* Security driver data */ - if (driver->securityPrimaryDriver) { - const char *doi, *model; + const char *doi, *model; - doi = virSecurityDriverGetDOI(driver->securityPrimaryDriver); - model = virSecurityDriverGetModel(driver->securityPrimaryDriver); + doi = virSecurityManagerGetDOI(driver->securityManager); + model = virSecurityManagerGetModel(driver->securityManager); + if (STREQ(model, "none")) { + model = ""; + doi = ""; + } - if (!(caps->host.secModel.model = strdup(model))) - goto no_memory; - if (!(caps->host.secModel.doi = strdup(doi))) - goto no_memory; + if (!(caps->host.secModel.model = strdup(model))) + goto no_memory; + if (!(caps->host.secModel.doi = strdup(doi))) + goto no_memory; - VIR_DEBUG("Initialized caps for security driver \"%s\" with " - "DOI \"%s\"", model, doi); - } + VIR_DEBUG("Initialized caps for security driver \"%s\" with " + "DOI \"%s\"", model, doi); return caps; @@ -3681,9 +3666,7 @@ static int qemudSecurityHook(void *data) { if (qemudInitCpuAffinity(h->vm) < 0) return -1; - if (h->driver->securityDriver && - h->driver->securityDriver->domainSetSecurityProcessLabel && - h->driver->securityDriver->domainSetSecurityProcessLabel(h->driver->securityDriver, h->vm) < 0) + if (virSecurityManagerSetProcessLabel(h->driver->securityManager, h->vm) < 0) return -1; return 0; @@ -3912,22 +3895,17 @@ static int qemudStartVMDaemon(virConnectPtr conn, /* If you are using a SecurityDriver with dynamic labelling, then generate a security label for isolation */ DEBUG0("Generating domain security label (if required)"); - if (driver->securityDriver && - driver->securityDriver->domainGenSecurityLabel) { - ret = driver->securityDriver->domainGenSecurityLabel(driver->securityDriver, - vm); - qemuDomainSecurityLabelAudit(vm, ret >= 0); - if (ret < 0) - goto cleanup; + if (virSecurityManagerGenLabel(driver->securityManager, + vm) < 0) { + qemuDomainSecurityLabelAudit(vm, false); + goto cleanup; } + qemuDomainSecurityLabelAudit(vm, true); DEBUG0("Generating setting domain security labels (if required)"); - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityAllLabel && - driver->securityDriver->domainSetSecurityAllLabel(driver->securityDriver, - vm, stdin_path) < 0) { + if (virSecurityManagerSetAllLabel(driver->securityManager, + vm, stdin_path) < 0) goto cleanup; - } /* Ensure no historical cgroup for this VM is lying around bogus * settings */ @@ -4342,14 +4320,10 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver, } /* Reset Security Labels */ - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityAllLabel) - driver->securityDriver->domainRestoreSecurityAllLabel(driver->securityDriver, - vm, migrated); - if (driver->securityDriver && - driver->securityDriver->domainReleaseSecurityLabel) - driver->securityDriver->domainReleaseSecurityLabel(driver->securityDriver, - vm); + virSecurityManagerRestoreAllLabel(driver->securityManager, + vm, migrated); + virSecurityManagerReleaseLabel(driver->securityManager, + vm); /* Clear out dynamically assigned labels */ if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_DYNAMIC) { @@ -4830,7 +4804,7 @@ static virDomainPtr qemudDomainCreate(virConnectPtr conn, const char *xml, VIR_DOMAIN_XML_INACTIVE))) goto cleanup; - if (virSecurityDriverVerify(def) < 0) + if (virSecurityManagerVerify(driver->securityManager, def) < 0) goto cleanup; if (virDomainObjIsDuplicate(&driver->domains, def, 1) < 0) @@ -5743,10 +5717,8 @@ static int qemudDomainSaveFlag(struct qemud_driver *driver, virDomainPtr dom, } if ((!bypassSecurityDriver) && - driver->securityDriver && - driver->securityDriver->domainSetSavedStateLabel && - driver->securityDriver->domainSetSavedStateLabel(driver->securityDriver, - vm, path) == -1) + virSecurityManagerSetSavedStateLabel(driver->securityManager, + vm, path) < 0) goto endjob; if (header.compressed == QEMUD_SAVE_FORMAT_RAW) { @@ -5779,10 +5751,8 @@ static int qemudDomainSaveFlag(struct qemud_driver *driver, virDomainPtr dom, goto endjob; if ((!bypassSecurityDriver) && - driver->securityDriver && - driver->securityDriver->domainRestoreSavedStateLabel && - driver->securityDriver->domainRestoreSavedStateLabel(driver->securityDriver, - vm, path) == -1) + virSecurityManagerRestoreSavedStateLabel(driver->securityManager, + vm, path) < 0) VIR_WARN("failed to restore save state label on %s", path); if (cgroup != NULL) { @@ -5828,10 +5798,8 @@ endjob: } if ((!bypassSecurityDriver) && - driver->securityDriver && - driver->securityDriver->domainRestoreSavedStateLabel && - driver->securityDriver->domainRestoreSavedStateLabel(driver->securityDriver, - vm, path) == -1) + virSecurityManagerRestoreSavedStateLabel(driver->securityManager, + vm, path) < 0) VIR_WARN("failed to restore save state label on %s", path); } @@ -6096,10 +6064,8 @@ static int qemudDomainCoreDump(virDomainPtr dom, goto endjob; } - if (driver->securityDriver && - driver->securityDriver->domainSetSavedStateLabel && - driver->securityDriver->domainSetSavedStateLabel(driver->securityDriver, - vm, path) == -1) + if (virSecurityManagerSetSavedStateLabel(driver->securityManager, + vm, path) < 0) goto endjob; /* Migrate will always stop the VM, so the resume condition is @@ -6156,11 +6122,9 @@ static int qemudDomainCoreDump(virDomainPtr dom, paused = 1; - if (driver->securityDriver && - driver->securityDriver->domainRestoreSavedStateLabel && - driver->securityDriver->domainRestoreSavedStateLabel(driver->securityDriver, - vm, path) == -1) - goto endjob; + if (virSecurityManagerRestoreSavedStateLabel(driver->securityManager, + vm, path) < 0) + VIR_WARN("Failed to restore security label on %s", path); endjob: if ((ret == 0) && (flags & VIR_DUMP_CRASH)) { @@ -6643,10 +6607,8 @@ static int qemudDomainGetSecurityLabel(virDomainPtr dom, virSecurityLabelPtr sec * QEMU monitor hasn't seen SIGHUP/ERR on poll(). */ if (virDomainObjIsActive(vm)) { - if (driver->securityDriver && - driver->securityDriver->domainGetSecurityProcessLabel && - driver->securityDriver->domainGetSecurityProcessLabel(driver->securityDriver, - vm, seclabel) < 0) { + if (virSecurityManagerGetProcessLabel(driver->securityManager, + vm, seclabel) < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to get security label")); goto cleanup; @@ -6670,11 +6632,6 @@ static int qemudNodeGetSecurityModel(virConnectPtr conn, int ret = 0; qemuDriverLock(driver); - if (!driver->securityPrimaryDriver) { - memset(secmodel, 0, sizeof (*secmodel)); - goto cleanup; - } - p = driver->caps->host.secModel.model; if (strlen(p) >= VIR_SECURITY_MODEL_BUFLEN-1) { qemuReportError(VIR_ERR_INTERNAL_ERROR, @@ -7067,10 +7024,8 @@ qemudDomainSaveImageStartVM(virConnectPtr conn, ret = 0; out: - if (driver->securityDriver && - driver->securityDriver->domainRestoreSavedStateLabel && - driver->securityDriver->domainRestoreSavedStateLabel(driver->securityDriver, - vm, path) == -1) + if (virSecurityManagerRestoreSavedStateLabel(driver->securityManager, + vm, path) < 0) VIR_WARN("failed to restore save state label on %s", path); return ret; @@ -7646,7 +7601,7 @@ static virDomainPtr qemudDomainDefine(virConnectPtr conn, const char *xml) { VIR_DOMAIN_XML_INACTIVE))) goto cleanup; - if (virSecurityDriverVerify(def) < 0) + if (virSecurityManagerVerify(driver->securityManager, def) < 0) goto cleanup; if ((dupVM = virDomainObjIsDuplicate(&driver->domains, def, 0)) < 0) @@ -7786,10 +7741,8 @@ static int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, return -1; } - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityImageLabel && - driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) + if (virSecurityManagerSetImageLabel(driver->securityManager, + vm, disk) < 0) return -1; if (!(driveAlias = qemuDeviceDriveHostAlias(origdisk, qemuCmdFlags))) @@ -7818,10 +7771,8 @@ static int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, if (ret < 0) goto error; - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, origdisk) < 0) + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, origdisk) < 0) VIR_WARN("Unable to restore security label on ejected image %s", origdisk->src); VIR_FREE(origdisk->src); @@ -7837,11 +7788,10 @@ static int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, error: VIR_FREE(driveAlias); - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, disk) < 0) VIR_WARN("Unable to restore security label on new media %s", disk->src); + return -1; } @@ -7865,10 +7815,8 @@ static int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver, } } - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityImageLabel && - driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) + if (virSecurityManagerSetImageLabel(driver->securityManager, + vm, disk) < 0) return -1; if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { @@ -7935,10 +7883,8 @@ error: 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) + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, disk) < 0) VIR_WARN("Unable to restore security label on %s", disk->src); return -1; @@ -8078,10 +8024,8 @@ static int qemudDomainAttachSCSIDisk(struct qemud_driver *driver, } - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityImageLabel && - driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) + if (virSecurityManagerSetImageLabel(driver->securityManager, + vm, disk) < 0) return -1; /* We should have an address already, so make sure */ @@ -8167,10 +8111,8 @@ error: VIR_FREE(devstr); VIR_FREE(drivestr); - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, disk) < 0) VIR_WARN("Unable to restore security label on %s", disk->src); return -1; @@ -8195,10 +8137,8 @@ static int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver, } } - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityImageLabel && - driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) + if (virSecurityManagerSetImageLabel(driver->securityManager, + vm, disk) < 0) return -1; if (!disk->src) { @@ -8254,10 +8194,8 @@ error: VIR_FREE(devstr); VIR_FREE(drivestr); - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, disk) < 0) VIR_WARN("Unable to restore security label on %s", disk->src); return -1; @@ -8681,10 +8619,8 @@ static int qemudDomainAttachHostDevice(struct qemud_driver *driver, } - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityHostdevLabel && - driver->securityDriver->domainSetSecurityHostdevLabel(driver->securityDriver, - vm, hostdev) < 0) + if (virSecurityManagerSetHostdevLabel(driver->securityManager, + vm, hostdev) < 0) return -1; switch (hostdev->source.subsys.type) { @@ -8710,10 +8646,8 @@ static int qemudDomainAttachHostDevice(struct qemud_driver *driver, return 0; error: - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityHostdevLabel && - driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver, - vm, hostdev) < 0) + if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, + vm, hostdev) < 0) VIR_WARN0("Unable to restore host device labelling on hotplug fail"); return -1; @@ -9150,10 +9084,8 @@ static int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver, virDomainDiskDefFree(detach); - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, dev->data.disk) < 0) + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, dev->data.disk) < 0) VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); if (cgroup != NULL) { @@ -9216,10 +9148,8 @@ static int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver, virDomainDiskDefFree(detach); - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, dev->data.disk) < 0) + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, dev->data.disk) < 0) VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); if (cgroup != NULL) { @@ -9652,10 +9582,8 @@ static int qemudDomainDetachHostDevice(struct qemud_driver *driver, return -1; } - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityHostdevLabel && - driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver, - vm, dev->data.hostdev) < 0) + if (virSecurityManagerRestoreHostdevLabel(driver->securityManager, + vm, dev->data.hostdev) < 0) VIR_WARN0("Failed to restore host device labelling"); return ret; diff --git a/src/qemu/qemu_security_dac.c b/src/qemu/qemu_security_dac.c deleted file mode 100644 index 55dc0c6..0000000 --- a/src/qemu/qemu_security_dac.c +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright (C) 2010 Red Hat, Inc. - * - * 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. - * - * QEMU POSIX DAC security driver - */ -#include <config.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -#include "qemu_security_dac.h" -#include "qemu_conf.h" -#include "datatypes.h" -#include "virterror_internal.h" -#include "util.h" -#include "memory.h" -#include "logging.h" -#include "pci.h" -#include "hostusb.h" -#include "storage_file.h" - -#define VIR_FROM_THIS VIR_FROM_QEMU - -static struct qemud_driver *driver; - -void qemuSecurityDACSetDriver(struct qemud_driver *newdriver) -{ - driver = newdriver; -} - - -static int -qemuSecurityDACSetOwnership(const char *path, int uid, int gid) -{ - VIR_INFO("Setting DAC user and group on '%s' to '%d:%d'", path, uid, gid); - - if (chown(path, uid, gid) < 0) { - struct stat sb; - int chown_errno = errno; - - if (stat(path, &sb) >= 0) { - if (sb.st_uid == uid && - sb.st_gid == gid) { - /* It's alright, there's nothing to change anyway. */ - return 0; - } - } - - if (chown_errno == EOPNOTSUPP) { - VIR_INFO("Setting user and group to '%d:%d' on '%s' not supported by filesystem", - uid, gid, path); - } else if (chown_errno == EPERM) { - VIR_INFO("Setting user and group to '%d:%d' on '%s' not permitted", - uid, gid, path); - } else if (chown_errno == EROFS) { - VIR_INFO("Setting user and group to '%d:%d' on '%s' not possible on readonly filesystem", - uid, gid, path); - } else { - virReportSystemError(chown_errno, - _("unable to set user and group to '%d:%d' on '%s'"), - uid, gid, path); - return -1; - } - } - return 0; -} - -static int -qemuSecurityDACRestoreSecurityFileLabel(const char *path) -{ - struct stat buf; - int rc = -1; - char *newpath = NULL; - - VIR_INFO("Restoring DAC user and group on '%s'", path); - - if (virFileResolveLink(path, &newpath) < 0) { - virReportSystemError(errno, - _("cannot resolve symlink %s"), path); - goto err; - } - - if (stat(newpath, &buf) != 0) - goto err; - - /* XXX record previous ownership */ - rc = qemuSecurityDACSetOwnership(newpath, 0, 0); - -err: - VIR_FREE(newpath); - return rc; -} - - -static int -qemuSecurityDACSetSecurityFileLabel(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, - const char *path, - size_t depth ATTRIBUTE_UNUSED, - void *opaque ATTRIBUTE_UNUSED) -{ - return qemuSecurityDACSetOwnership(path, driver->user, driver->group); -} - - -static int -qemuSecurityDACSetSecurityImageLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED, - virDomainDiskDefPtr disk) - -{ - if (!driver->privileged || !driver->dynamicOwnership) - return 0; - - return virDomainDiskDefForeachPath(disk, - driver->allowDiskFormatProbing, - false, - qemuSecurityDACSetSecurityFileLabel, - NULL); -} - - -static int -qemuSecurityDACRestoreSecurityImageLabelInt(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED, - virDomainDiskDefPtr disk, - int migrated) -{ - if (!driver->privileged || !driver->dynamicOwnership) - return 0; - - /* Don't restore labels on readoly/shared disks, because - * other VMs may still be accessing these - * Alternatively we could iterate over all running - * domains and try to figure out if it is in use, but - * this would not work for clustered filesystems, since - * we can't see running VMs using the file on other nodes - * Safest bet is thus to skip the restore step. - */ - if (disk->readonly || disk->shared) - return 0; - - if (!disk->src) - return 0; - - /* If we have a shared FS & doing migrated, we must not - * change ownership, because that kills access on the - * destination host which is sub-optimal for the guest - * VM's I/O attempts :-) - */ - if (migrated) { - int rc = virStorageFileIsSharedFS(disk->src); - if (rc < 0) - return -1; - if (rc == 1) { - VIR_DEBUG("Skipping image label restore on %s because FS is shared", - disk->src); - return 0; - } - } - - return qemuSecurityDACRestoreSecurityFileLabel(disk->src); -} - - -static int -qemuSecurityDACRestoreSecurityImageLabel(virSecurityDriverPtr drv, - virDomainObjPtr vm, - virDomainDiskDefPtr disk) -{ - return qemuSecurityDACRestoreSecurityImageLabelInt(drv, vm, disk, 0); -} - - -static int -qemuSecurityDACSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, - const char *file, - void *opaque ATTRIBUTE_UNUSED) -{ - return qemuSecurityDACSetOwnership(file, driver->user, driver->group); -} - - -static int -qemuSecurityDACSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, - const char *file, - void *opaque ATTRIBUTE_UNUSED) -{ - return qemuSecurityDACSetOwnership(file, driver->user, driver->group); -} - - -static int -qemuSecurityDACSetSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - virDomainHostdevDefPtr dev) - -{ - int ret = -1; - - if (!driver->privileged || !driver->dynamicOwnership) - return 0; - - if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - return 0; - - switch (dev->source.subsys.type) { - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { - usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device); - - if (!usb) - goto done; - - ret = usbDeviceFileIterate(usb, qemuSecurityDACSetSecurityUSBLabel, vm); - usbFreeDevice(usb); - break; - } - - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: { - pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain, - dev->source.subsys.u.pci.bus, - dev->source.subsys.u.pci.slot, - dev->source.subsys.u.pci.function); - - if (!pci) - goto done; - - ret = pciDeviceFileIterate(pci, qemuSecurityDACSetSecurityPCILabel, vm); - pciFreeDevice(pci); - - break; - } - - default: - ret = 0; - break; - } - -done: - return ret; -} - - -static int -qemuSecurityDACRestoreSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, - const char *file, - void *opaque ATTRIBUTE_UNUSED) -{ - return qemuSecurityDACRestoreSecurityFileLabel(file); -} - - -static int -qemuSecurityDACRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, - const char *file, - void *opaque ATTRIBUTE_UNUSED) -{ - return qemuSecurityDACRestoreSecurityFileLabel(file); -} - - -static int -qemuSecurityDACRestoreSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED, - virDomainHostdevDefPtr dev) - -{ - int ret = -1; - - if (!driver->privileged || !driver->dynamicOwnership) - return 0; - - if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - return 0; - - switch (dev->source.subsys.type) { - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { - usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device); - - if (!usb) - goto done; - - ret = usbDeviceFileIterate(usb, qemuSecurityDACRestoreSecurityUSBLabel, NULL); - usbFreeDevice(usb); - - break; - } - - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: { - pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain, - dev->source.subsys.u.pci.bus, - dev->source.subsys.u.pci.slot, - dev->source.subsys.u.pci.function); - - if (!pci) - goto done; - - ret = pciDeviceFileIterate(pci, qemuSecurityDACRestoreSecurityPCILabel, NULL); - pciFreeDevice(pci); - - break; - } - - default: - ret = 0; - break; - } - -done: - return ret; -} - - -static int -qemuSecurityDACSetChardevLabel(virDomainObjPtr vm, - virDomainChrDefPtr dev) - -{ - const virSecurityLabelDefPtr secdef = &vm->def->seclabel; - char *in = NULL, *out = NULL; - int ret = -1; - - if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) - return 0; - - switch (dev->type) { - case VIR_DOMAIN_CHR_TYPE_DEV: - case VIR_DOMAIN_CHR_TYPE_FILE: - ret = qemuSecurityDACSetOwnership(dev->data.file.path, driver->user, driver->group); - break; - - case VIR_DOMAIN_CHR_TYPE_PIPE: - if ((virAsprintf(&in, "%s.in", dev->data.file.path) < 0) || - (virAsprintf(&out, "%s.out", dev->data.file.path) < 0)) { - virReportOOMError(); - goto done; - } - if ((qemuSecurityDACSetOwnership(in, driver->user, driver->group) < 0) || - (qemuSecurityDACSetOwnership(out, driver->user, driver->group) < 0)) - goto done; - ret = 0; - break; - - default: - ret = 0; - break; - } - -done: - VIR_FREE(in); - VIR_FREE(out); - return ret; -} - -static int -qemuSecurityDACRestoreChardevLabel(virDomainObjPtr vm, - virDomainChrDefPtr dev) - -{ - const virSecurityLabelDefPtr secdef = &vm->def->seclabel; - char *in = NULL, *out = NULL; - int ret = -1; - - if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) - return 0; - - switch (dev->type) { - case VIR_DOMAIN_CHR_TYPE_DEV: - case VIR_DOMAIN_CHR_TYPE_FILE: - ret = qemuSecurityDACRestoreSecurityFileLabel(dev->data.file.path); - break; - - case VIR_DOMAIN_CHR_TYPE_PIPE: - if ((virAsprintf(&out, "%s.out", dev->data.file.path) < 0) || - (virAsprintf(&in, "%s.in", dev->data.file.path) < 0)) { - virReportOOMError(); - goto done; - } - if ((qemuSecurityDACRestoreSecurityFileLabel(out) < 0) || - (qemuSecurityDACRestoreSecurityFileLabel(in) < 0)) - goto done; - ret = 0; - break; - - default: - ret = 0; - break; - } - -done: - VIR_FREE(in); - VIR_FREE(out); - return ret; -} - - -static int -qemuSecurityDACRestoreChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, - virDomainChrDefPtr dev, - void *opaque) -{ - virDomainObjPtr vm = opaque; - - return qemuSecurityDACRestoreChardevLabel(vm, dev); -} - - -static int -qemuSecurityDACRestoreSecurityAllLabel(virSecurityDriverPtr drv, - virDomainObjPtr vm, - int migrated) -{ - int i; - int rc = 0; - - if (!driver->privileged || !driver->dynamicOwnership) - return 0; - - VIR_DEBUG("Restoring security label on %s migrated=%d", - vm->def->name, migrated); - - for (i = 0 ; i < vm->def->nhostdevs ; i++) { - if (qemuSecurityDACRestoreSecurityHostdevLabel(drv, - vm, - vm->def->hostdevs[i]) < 0) - rc = -1; - } - for (i = 0 ; i < vm->def->ndisks ; i++) { - if (qemuSecurityDACRestoreSecurityImageLabelInt(drv, - vm, - vm->def->disks[i], - migrated) < 0) - rc = -1; - } - - if (virDomainChrDefForeach(vm->def, - false, - qemuSecurityDACRestoreChardevCallback, - vm) < 0) - rc = -1; - - if (vm->def->os.kernel && - qemuSecurityDACRestoreSecurityFileLabel(vm->def->os.kernel) < 0) - rc = -1; - - if (vm->def->os.initrd && - qemuSecurityDACRestoreSecurityFileLabel(vm->def->os.initrd) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityDACSetChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, - virDomainChrDefPtr dev, - void *opaque) -{ - virDomainObjPtr vm = opaque; - - return qemuSecurityDACSetChardevLabel(vm, dev); -} - - -static int -qemuSecurityDACSetSecurityAllLabel(virSecurityDriverPtr drv, - virDomainObjPtr vm, - const char *stdin_path ATTRIBUTE_UNUSED) -{ - int i; - - if (!driver->privileged || !driver->dynamicOwnership) - return 0; - - for (i = 0 ; i < vm->def->ndisks ; i++) { - /* XXX fixme - we need to recursively label the entriy tree :-( */ - if (vm->def->disks[i]->type == VIR_DOMAIN_DISK_TYPE_DIR) - continue; - if (qemuSecurityDACSetSecurityImageLabel(drv, - vm, - vm->def->disks[i]) < 0) - return -1; - } - for (i = 0 ; i < vm->def->nhostdevs ; i++) { - if (qemuSecurityDACSetSecurityHostdevLabel(drv, - vm, - vm->def->hostdevs[i]) < 0) - return -1; - } - - if (virDomainChrDefForeach(vm->def, - true, - qemuSecurityDACSetChardevCallback, - vm) < 0) - return -1; - - if (vm->def->os.kernel && - qemuSecurityDACSetOwnership(vm->def->os.kernel, - driver->user, - driver->group) < 0) - return -1; - - if (vm->def->os.initrd && - qemuSecurityDACSetOwnership(vm->def->os.initrd, - driver->user, - driver->group) < 0) - return -1; - - return 0; -} - - -static int -qemuSecurityDACSetSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED, - const char *savefile) -{ - if (!driver->privileged) - return 0; - - return qemuSecurityDACSetOwnership(savefile, driver->user, driver->group); -} - - -static int -qemuSecurityDACRestoreSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED, - const char *savefile) -{ - if (!driver->privileged) - return 0; - - return qemuSecurityDACRestoreSecurityFileLabel(savefile); -} - - -static int -qemuSecurityDACSetProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm ATTRIBUTE_UNUSED) -{ - DEBUG("Dropping privileges of VM to %d:%d", driver->user, driver->group); - - if (!driver->privileged) - return 0; - - if (driver->group) { - if (setregid(driver->group, driver->group) < 0) { - virReportSystemError(errno, - _("cannot change to '%d' group"), - driver->group); - return -1; - } - } - if (driver->user) { - if (setreuid(driver->user, driver->user) < 0) { - virReportSystemError(errno, - _("cannot change to '%d' user"), - driver->user); - return -1; - } - } - - return 0; -} - - - -virSecurityDriver qemuDACSecurityDriver = { - .name = "qemuDAC", - - .domainSetSecurityProcessLabel = qemuSecurityDACSetProcessLabel, - - .domainSetSecurityImageLabel = qemuSecurityDACSetSecurityImageLabel, - .domainRestoreSecurityImageLabel = qemuSecurityDACRestoreSecurityImageLabel, - - .domainSetSecurityAllLabel = qemuSecurityDACSetSecurityAllLabel, - .domainRestoreSecurityAllLabel = qemuSecurityDACRestoreSecurityAllLabel, - - .domainSetSecurityHostdevLabel = qemuSecurityDACSetSecurityHostdevLabel, - .domainRestoreSecurityHostdevLabel = qemuSecurityDACRestoreSecurityHostdevLabel, - - .domainSetSavedStateLabel = qemuSecurityDACSetSavedStateLabel, - .domainRestoreSavedStateLabel = qemuSecurityDACRestoreSavedStateLabel, -}; diff --git a/src/qemu/qemu_security_dac.h b/src/qemu/qemu_security_dac.h deleted file mode 100644 index a742f7a..0000000 --- a/src/qemu/qemu_security_dac.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2010 Red Hat, Inc. - * - * 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. - * - * QEMU POSIX DAC security driver - */ - -#include "security/security_driver.h" -#include "qemu_conf.h" - -#ifndef __QEMU_SECURITY_DAC -# define __QEMU_SECURITY_DAC - -extern virSecurityDriver qemuDACSecurityDriver; - -void qemuSecurityDACSetDriver(struct qemud_driver *driver); - -#endif /* __QEMU_SECURITY_DAC */ diff --git a/src/qemu/qemu_security_stacked.c b/src/qemu/qemu_security_stacked.c deleted file mode 100644 index 432d095..0000000 --- a/src/qemu/qemu_security_stacked.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (C) 2010 Red Hat, Inc. - * - * 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. - * - * QEMU stacked security driver - */ - -#include <config.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -#include "qemu_security_stacked.h" - -#include "qemu_conf.h" -#include "datatypes.h" -#include "virterror_internal.h" -#include "util.h" -#include "memory.h" -#include "logging.h" -#include "pci.h" -#include "hostusb.h" -#include "storage_file.h" - -#define VIR_FROM_THIS VIR_FROM_QEMU - - -static struct qemud_driver *driver; - -void qemuSecurityStackedSetDriver(struct qemud_driver *newdriver) -{ - driver = newdriver; -} - - -static int -qemuSecurityStackedVerify(virDomainDefPtr def) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainSecurityVerify && - driver->securitySecondaryDriver->domainSecurityVerify(def) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainSecurityVerify && - driver->securityPrimaryDriver->domainSecurityVerify(def) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedGenLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainGenSecurityLabel && - driver->securitySecondaryDriver->domainGenSecurityLabel(driver->securitySecondaryDriver, - vm) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainGenSecurityLabel && - driver->securityPrimaryDriver->domainGenSecurityLabel(driver->securityPrimaryDriver, - vm) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedReleaseLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainReleaseSecurityLabel && - driver->securitySecondaryDriver->domainReleaseSecurityLabel(driver->securitySecondaryDriver, - vm) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainReleaseSecurityLabel && - driver->securityPrimaryDriver->domainReleaseSecurityLabel(driver->securityPrimaryDriver, - vm) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedReserveLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainReserveSecurityLabel && - driver->securitySecondaryDriver->domainReserveSecurityLabel(driver->securitySecondaryDriver, - vm) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainReserveSecurityLabel && - driver->securityPrimaryDriver->domainReserveSecurityLabel(driver->securityPrimaryDriver, - vm) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedSetSecurityImageLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - virDomainDiskDefPtr disk) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainSetSecurityImageLabel && - driver->securitySecondaryDriver->domainSetSecurityImageLabel(driver->securitySecondaryDriver, - vm, disk) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainSetSecurityImageLabel && - driver->securityPrimaryDriver->domainSetSecurityImageLabel(driver->securityPrimaryDriver, - vm, disk) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedRestoreSecurityImageLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - virDomainDiskDefPtr disk) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainRestoreSecurityImageLabel && - driver->securitySecondaryDriver->domainRestoreSecurityImageLabel(driver->securitySecondaryDriver, - vm, disk) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainRestoreSecurityImageLabel && - driver->securityPrimaryDriver->domainRestoreSecurityImageLabel(driver->securityPrimaryDriver, - vm, disk) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedSetSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - virDomainHostdevDefPtr dev) - -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainSetSecurityHostdevLabel && - driver->securitySecondaryDriver->domainSetSecurityHostdevLabel(driver->securitySecondaryDriver, - vm, dev) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainSetSecurityHostdevLabel && - driver->securityPrimaryDriver->domainSetSecurityHostdevLabel(driver->securityPrimaryDriver, - vm, dev) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedRestoreSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - virDomainHostdevDefPtr dev) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainRestoreSecurityHostdevLabel && - driver->securitySecondaryDriver->domainRestoreSecurityHostdevLabel(driver->securitySecondaryDriver, - vm, dev) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainRestoreSecurityHostdevLabel && - driver->securityPrimaryDriver->domainRestoreSecurityHostdevLabel(driver->securityPrimaryDriver, - vm, dev) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedSetSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - const char *stdin_path) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainSetSecurityAllLabel && - driver->securitySecondaryDriver->domainSetSecurityAllLabel(driver->securitySecondaryDriver, - vm, stdin_path) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainSetSecurityAllLabel && - driver->securityPrimaryDriver->domainSetSecurityAllLabel(driver->securityPrimaryDriver, - vm, stdin_path) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - int migrated) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainRestoreSecurityAllLabel && - driver->securitySecondaryDriver->domainRestoreSecurityAllLabel(driver->securitySecondaryDriver, - vm, migrated) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainRestoreSecurityAllLabel && - driver->securityPrimaryDriver->domainRestoreSecurityAllLabel(driver->securityPrimaryDriver, - vm, migrated) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedSetSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - const char *savefile) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainSetSavedStateLabel && - driver->securitySecondaryDriver->domainSetSavedStateLabel(driver->securitySecondaryDriver, - vm, savefile) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainSetSavedStateLabel && - driver->securityPrimaryDriver->domainSetSavedStateLabel(driver->securityPrimaryDriver, - vm, savefile) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedRestoreSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - const char *savefile) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainRestoreSavedStateLabel && - driver->securitySecondaryDriver->domainRestoreSavedStateLabel(driver->securitySecondaryDriver, - vm, savefile) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainRestoreSavedStateLabel && - driver->securityPrimaryDriver->domainRestoreSavedStateLabel(driver->securityPrimaryDriver, - vm, savefile) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedSetProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainSetSecurityProcessLabel && - driver->securitySecondaryDriver->domainSetSecurityProcessLabel(driver->securitySecondaryDriver, - vm) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainSetSecurityProcessLabel && - driver->securityPrimaryDriver->domainSetSecurityProcessLabel(driver->securityPrimaryDriver, - vm) < 0) - rc = -1; - - return rc; -} - -static int -qemuSecurityStackedGetProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm, - virSecurityLabelPtr seclabel) -{ - int rc = 0; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainGetSecurityProcessLabel && - driver->securityPrimaryDriver->domainGetSecurityProcessLabel(driver->securityPrimaryDriver, - vm, - seclabel) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedSetSocketLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm) -{ - int rc = 0; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainSetSecuritySocketLabel && - driver->securityPrimaryDriver->domainSetSecuritySocketLabel(driver->securityPrimaryDriver, - vm) < 0) - rc = -1; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainSetSecuritySocketLabel && - driver->securitySecondaryDriver->domainSetSecuritySocketLabel(driver->securitySecondaryDriver, - vm) < 0) - rc = -1; - - return rc; -} - - -static int -qemuSecurityStackedClearSocketLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, - virDomainObjPtr vm) -{ - int rc = 0; - - if (driver->securitySecondaryDriver && - driver->securitySecondaryDriver->domainClearSecuritySocketLabel && - driver->securitySecondaryDriver->domainClearSecuritySocketLabel(driver->securitySecondaryDriver, - vm) < 0) - rc = -1; - - if (driver->securityPrimaryDriver && - driver->securityPrimaryDriver->domainClearSecuritySocketLabel && - driver->securityPrimaryDriver->domainClearSecuritySocketLabel(driver->securityPrimaryDriver, - vm) < 0) - rc = -1; - - return rc; -} - - -virSecurityDriver qemuStackedSecurityDriver = { - .name = "qemuStacked", - .domainSecurityVerify = qemuSecurityStackedVerify, - - .domainGenSecurityLabel = qemuSecurityStackedGenLabel, - .domainReleaseSecurityLabel = qemuSecurityStackedReleaseLabel, - .domainReserveSecurityLabel = qemuSecurityStackedReserveLabel, - - .domainGetSecurityProcessLabel = qemuSecurityStackedGetProcessLabel, - .domainSetSecurityProcessLabel = qemuSecurityStackedSetProcessLabel, - - .domainSetSecurityImageLabel = qemuSecurityStackedSetSecurityImageLabel, - .domainRestoreSecurityImageLabel = qemuSecurityStackedRestoreSecurityImageLabel, - - .domainSetSecurityAllLabel = qemuSecurityStackedSetSecurityAllLabel, - .domainRestoreSecurityAllLabel = qemuSecurityStackedRestoreSecurityAllLabel, - - .domainSetSecurityHostdevLabel = qemuSecurityStackedSetSecurityHostdevLabel, - .domainRestoreSecurityHostdevLabel = qemuSecurityStackedRestoreSecurityHostdevLabel, - - .domainSetSavedStateLabel = qemuSecurityStackedSetSavedStateLabel, - .domainRestoreSavedStateLabel = qemuSecurityStackedRestoreSavedStateLabel, - - .domainClearSecuritySocketLabel = qemuSecurityStackedClearSocketLabel, - .domainSetSecuritySocketLabel = qemuSecurityStackedSetSocketLabel, -}; diff --git a/src/qemu/qemu_security_stacked.h b/src/qemu/qemu_security_stacked.h deleted file mode 100644 index 07f76d5..0000000 --- a/src/qemu/qemu_security_stacked.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2010 Red Hat, Inc. - * - * 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. - * - * QEMU stacked security driver - */ - -#include "security/security_driver.h" -#include "qemu_conf.h" - -#ifndef __QEMU_SECURITY_STACKED -# define __QEMU_SECURITY_STACKED - -extern virSecurityDriver qemuStackedSecurityDriver; - -void qemuSecurityStackedSetDriver(struct qemud_driver *driver); - -#endif /* __QEMU_SECURITY_DAC */ diff --git a/src/security/security_apparmor.c b/src/security/security_apparmor.c index b43c4ac..37cd928 100644 --- a/src/security/security_apparmor.c +++ b/src/security/security_apparmor.c @@ -1,4 +1,3 @@ - /* * AppArmor security driver for libvirt * Copyright (C) 2009-2010 Canonical Ltd. @@ -28,7 +27,6 @@ #include "internal.h" -#include "security_driver.h" #include "security_apparmor.h" #include "util.h" #include "memory.h" @@ -47,7 +45,7 @@ /* Data structure to pass to *FileIterate so we have everything we need */ struct SDPDOP { - virSecurityDriverPtr drv; + virSecurityManagerPtr mgr; virDomainObjPtr vm; }; @@ -158,7 +156,7 @@ profile_status_file(const char *str) * load (add) a profile. Will create one if necessary */ static int -load_profile(virSecurityDriverPtr drv, +load_profile(virSecurityManagerPtr mgr, const char *profile, virDomainObjPtr vm, const char *fn, @@ -169,7 +167,7 @@ load_profile(virSecurityDriverPtr drv, char *xml = NULL; int pipefd[2]; pid_t child; - const char *probe = virSecurityDriverGetAllowDiskFormatProbing(drv) + const char *probe = virSecurityManagerGetAllowDiskFormatProbing(mgr) ? "1" : "0"; if (pipe(pipefd) < -1) { @@ -300,7 +298,7 @@ cleanup: * NULL. */ static int -reload_profile(virSecurityDriverPtr drv, +reload_profile(virSecurityManagerPtr mgr, virDomainObjPtr vm, const char *fn, bool append) @@ -317,7 +315,7 @@ reload_profile(virSecurityDriverPtr drv, /* Update the profile only if it is loaded */ if (profile_loaded(secdef->imagelabel) >= 0) { - if (load_profile(drv, secdef->imagelabel, vm, fn, append) < 0) { + if (load_profile(mgr, secdef->imagelabel, vm, fn, append) < 0) { virSecurityReportError(VIR_ERR_INTERNAL_ERROR, _("cannot update AppArmor profile " "\'%s\'"), @@ -340,7 +338,7 @@ AppArmorSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, struct SDPDOP *ptr = opaque; virDomainObjPtr vm = ptr->vm; - if (reload_profile(ptr->drv, vm, file, true) < 0) { + if (reload_profile(ptr->mgr, vm, file, true) < 0) { const virSecurityLabelDefPtr secdef = &vm->def->seclabel; virSecurityReportError(VIR_ERR_INTERNAL_ERROR, _("cannot update AppArmor profile " @@ -358,7 +356,7 @@ AppArmorSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, struct SDPDOP *ptr = opaque; virDomainObjPtr vm = ptr->vm; - if (reload_profile(ptr->drv, vm, file, true) < 0) { + if (reload_profile(ptr->mgr, vm, file, true) < 0) { const virSecurityLabelDefPtr secdef = &vm->def->seclabel; virSecurityReportError(VIR_ERR_INTERNAL_ERROR, _("cannot update AppArmor profile " @@ -371,7 +369,7 @@ AppArmorSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, /* Called on libvirtd startup to see if AppArmor is available */ static int -AppArmorSecurityDriverProbe(void) +AppArmorSecurityManagerProbe(void) { char *template = NULL; int rc = SECURITY_DRIVER_DISABLE; @@ -403,21 +401,37 @@ AppArmorSecurityDriverProbe(void) * currently not used. */ static int -AppArmorSecurityDriverOpen(virSecurityDriverPtr drv, - bool allowDiskFormatProbing) +AppArmorSecurityManagerOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +AppArmorSecurityManagerClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) { - virSecurityDriverSetDOI(drv, SECURITY_APPARMOR_VOID_DOI); - virSecurityDriverSetAllowDiskFormatProbing(drv, allowDiskFormatProbing); return 0; } +static const char * +AppArmorSecurityManagerGetModel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return SECURITY_APPARMOR_NAME; +} + +static const char * +AppArmorSecurityManagerGetDOI(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return SECURITY_APPARMOR_VOID_DOI; +} + + /* Currently called in qemudStartVMDaemon to setup a 'label'. We look for and * use a profile based on the UUID, otherwise create one based on a template. * Keep in mind that this is called on 'start' with RestoreSecurityLabel being * called on shutdown. */ static int -AppArmorGenSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +AppArmorGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm) { int rc = -1; @@ -472,7 +486,7 @@ AppArmorGenSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, } static int -AppArmorSetSecurityAllLabel(virSecurityDriverPtr drv, +AppArmorSetSecurityAllLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, const char *stdin_path) { if (vm->def->seclabel.type == VIR_DOMAIN_SECLABEL_STATIC) @@ -480,7 +494,7 @@ AppArmorSetSecurityAllLabel(virSecurityDriverPtr drv, /* if the profile is not already loaded, then load one */ if (profile_loaded(vm->def->seclabel.label) < 0) { - if (load_profile(drv, vm->def->seclabel.label, vm, stdin_path, + if (load_profile(mgr, vm->def->seclabel.label, vm, stdin_path, false) < 0) { virSecurityReportError(VIR_ERR_INTERNAL_ERROR, _("cannot generate AppArmor profile " @@ -496,7 +510,7 @@ AppArmorSetSecurityAllLabel(virSecurityDriverPtr drv, * running. */ static int -AppArmorGetSecurityProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +AppArmorGetSecurityProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, virSecurityLabelPtr sec) { @@ -530,7 +544,7 @@ AppArmorGetSecurityProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, * more details. Currently called via qemudShutdownVMDaemon. */ static int -AppArmorReleaseSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +AppArmorReleaseSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm) { const virSecurityLabelDefPtr secdef = &vm->def->seclabel; @@ -544,7 +558,7 @@ AppArmorReleaseSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, static int -AppArmorRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +AppArmorRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, int migrated ATTRIBUTE_UNUSED) { @@ -565,7 +579,7 @@ AppArmorRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, * LOCALSTATEDIR/log/libvirt/qemu/<vm name>.log */ static int -AppArmorSetSecurityProcessLabel(virSecurityDriverPtr drv, virDomainObjPtr vm) +AppArmorSetSecurityProcessLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm) { const virSecurityLabelDefPtr secdef = &vm->def->seclabel; int rc = -1; @@ -574,12 +588,12 @@ AppArmorSetSecurityProcessLabel(virSecurityDriverPtr drv, virDomainObjPtr vm) if ((profile_name = get_profile_name(vm)) == NULL) return rc; - if (STRNEQ(drv->name, secdef->model)) { + if (STRNEQ(virSecurityManagerGetModel(mgr), secdef->model)) { virSecurityReportError(VIR_ERR_INTERNAL_ERROR, _("security label driver mismatch: " "\'%s\' model configured for domain, but " "hypervisor driver is \'%s\'."), - secdef->model, drv->name); + secdef->model, virSecurityManagerGetModel(mgr)); if (use_apparmor() > 0) goto clean; } @@ -597,19 +611,33 @@ AppArmorSetSecurityProcessLabel(virSecurityDriverPtr drv, virDomainObjPtr vm) return rc; } +static int +AppArmorSetSecuritySocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +AppArmorClearSecuritySocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + /* Called when hotplugging */ static int -AppArmorRestoreSecurityImageLabel(virSecurityDriverPtr drv, +AppArmorRestoreSecurityImageLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainDiskDefPtr disk ATTRIBUTE_UNUSED) { - return reload_profile(drv, vm, NULL, false); + return reload_profile(mgr, vm, NULL, false); } /* Called when hotplugging */ static int -AppArmorSetSecurityImageLabel(virSecurityDriverPtr drv, +AppArmorSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainDiskDefPtr disk) { const virSecurityLabelDefPtr secdef = &vm->def->seclabel; @@ -635,7 +663,7 @@ AppArmorSetSecurityImageLabel(virSecurityDriverPtr drv, /* update the profile only if it is loaded */ if (profile_loaded(secdef->imagelabel) >= 0) { - if (load_profile(drv, secdef->imagelabel, vm, disk->src, + if (load_profile(mgr, secdef->imagelabel, vm, disk->src, false) < 0) { virSecurityReportError(VIR_ERR_INTERNAL_ERROR, _("cannot update AppArmor profile " @@ -654,7 +682,8 @@ AppArmorSetSecurityImageLabel(virSecurityDriverPtr drv, } static int -AppArmorSecurityVerify(virDomainDefPtr def) +AppArmorSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def) { const virSecurityLabelDefPtr secdef = &def->seclabel; @@ -670,7 +699,7 @@ AppArmorSecurityVerify(virDomainDefPtr def) } static int -AppArmorReserveSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +AppArmorReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm ATTRIBUTE_UNUSED) { /* NOOP. Nothing to reserve with AppArmor */ @@ -678,7 +707,7 @@ AppArmorReserveSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, } static int -AppArmorSetSecurityHostdevLabel(virSecurityDriverPtr drv, +AppArmorSetSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainHostdevDefPtr dev) @@ -698,7 +727,7 @@ AppArmorSetSecurityHostdevLabel(virSecurityDriverPtr drv, if (VIR_ALLOC(ptr) < 0) return -1; - ptr->drv = drv; + ptr->mgr = mgr; ptr->vm = vm; switch (dev->source.subsys.type) { @@ -740,7 +769,7 @@ done: static int -AppArmorRestoreSecurityHostdevLabel(virSecurityDriverPtr drv, +AppArmorRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED) @@ -749,42 +778,57 @@ AppArmorRestoreSecurityHostdevLabel(virSecurityDriverPtr drv, if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) return 0; - return reload_profile(drv, vm, NULL, false); + return reload_profile(mgr, vm, NULL, false); } static int -AppArmorSetSavedStateLabel(virSecurityDriverPtr drv, +AppArmorSetSavedStateLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, const char *savefile) { - return reload_profile(drv, vm, savefile, true); + return reload_profile(mgr, vm, savefile, true); } static int -AppArmorRestoreSavedStateLabel(virSecurityDriverPtr drv, +AppArmorRestoreSavedStateLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, const char *savefile ATTRIBUTE_UNUSED) { - return reload_profile(drv, vm, NULL, false); + return reload_profile(mgr, vm, NULL, false); } virSecurityDriver virAppArmorSecurityDriver = { - .name = SECURITY_APPARMOR_NAME, - .probe = AppArmorSecurityDriverProbe, - .open = AppArmorSecurityDriverOpen, - .domainSecurityVerify = AppArmorSecurityVerify, - .domainSetSecurityImageLabel = AppArmorSetSecurityImageLabel, - .domainRestoreSecurityImageLabel = AppArmorRestoreSecurityImageLabel, - .domainGenSecurityLabel = AppArmorGenSecurityLabel, - .domainReserveSecurityLabel = AppArmorReserveSecurityLabel, - .domainReleaseSecurityLabel = AppArmorReleaseSecurityLabel, - .domainGetSecurityProcessLabel = AppArmorGetSecurityProcessLabel, - .domainSetSecurityProcessLabel = AppArmorSetSecurityProcessLabel, - .domainRestoreSecurityAllLabel = AppArmorRestoreSecurityAllLabel, - .domainSetSecurityAllLabel = AppArmorSetSecurityAllLabel, - .domainSetSecurityHostdevLabel = AppArmorSetSecurityHostdevLabel, - .domainRestoreSecurityHostdevLabel = AppArmorRestoreSecurityHostdevLabel, - .domainSetSavedStateLabel = AppArmorSetSavedStateLabel, - .domainRestoreSavedStateLabel = AppArmorRestoreSavedStateLabel, + 0, + SECURITY_APPARMOR_NAME, + AppArmorSecurityManagerProbe, + AppArmorSecurityManagerOpen, + AppArmorSecurityManagerClose, + + AppArmorSecurityManagerGetModel, + AppArmorSecurityManagerGetDOI, + + AppArmorSecurityVerify, + + AppArmorSetSecurityImageLabel, + AppArmorRestoreSecurityImageLabel, + + AppArmorSetSecuritySocketLabel, + AppArmorClearSecuritySocketLabel, + + AppArmorGenSecurityLabel, + AppArmorReserveSecurityLabel, + AppArmorReleaseSecurityLabel, + + AppArmorGetSecurityProcessLabel, + AppArmorSetSecurityProcessLabel, + + AppArmorSetSecurityAllLabel, + AppArmorRestoreSecurityAllLabel, + + AppArmorSetSecurityHostdevLabel, + AppArmorRestoreSecurityHostdevLabel, + + AppArmorSetSavedStateLabel, + AppArmorRestoreSavedStateLabel, }; diff --git a/src/security/security_apparmor.h b/src/security/security_apparmor.h index eb7e140..90d9ddb 100644 --- a/src/security/security_apparmor.h +++ b/src/security/security_apparmor.h @@ -14,6 +14,8 @@ #ifndef __VIR_SECURITY_APPARMOR_H__ # define __VIR_SECURITY_APPARMOR_H__ +#include "security_driver.h" + extern virSecurityDriver virAppArmorSecurityDriver; # define AA_PREFIX "libvirt-" diff --git a/src/security/security_dac.c b/src/security/security_dac.c new file mode 100644 index 0000000..b4b9c6e --- /dev/null +++ b/src/security/security_dac.c @@ -0,0 +1,712 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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. + * + * QEMU POSIX DAC security driver + */ +#include <config.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "security_dac.h" +#include "virterror_internal.h" +#include "util.h" +#include "memory.h" +#include "logging.h" +#include "pci.h" +#include "hostusb.h" +#include "storage_file.h" + +#define VIR_FROM_THIS VIR_FROM_SECURITY + +typedef struct _virSecurityDACData virSecurityDACData; +typedef virSecurityDACData *virSecurityDACDataPtr; + +struct _virSecurityDACData { + uid_t user; + gid_t group; + bool dynamicOwnership; +}; + +void virSecurityDACSetUser(virSecurityManagerPtr mgr, + uid_t user) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + priv->user = user; +} + +void virSecurityDACSetGroup(virSecurityManagerPtr mgr, + gid_t group) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + priv->group = group; +} + +void virSecurityDACSetDynamicOwnership(virSecurityManagerPtr mgr, + bool dynamicOwnership) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + priv->dynamicOwnership = dynamicOwnership; +} + +static virSecurityDriverStatus +virSecurityDACProbe(void) +{ + return SECURITY_DRIVER_ENABLE; +} + +static int +virSecurityDACOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +virSecurityDACClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + + +static const char * virSecurityDACGetModel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return "dac"; +} + +static const char * virSecurityDACGetDOI(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return "0"; +} + +static int +virSecurityDACSetOwnership(const char *path, int uid, int gid) +{ + VIR_INFO("Setting DAC user and group on '%s' to '%d:%d'", path, uid, gid); + + if (chown(path, uid, gid) < 0) { + struct stat sb; + int chown_errno = errno; + + if (stat(path, &sb) >= 0) { + if (sb.st_uid == uid && + sb.st_gid == gid) { + /* It's alright, there's nothing to change anyway. */ + return 0; + } + } + + if (chown_errno == EOPNOTSUPP) { + VIR_INFO("Setting user and group to '%d:%d' on '%s' not supported by filesystem", + uid, gid, path); + } else if (chown_errno == EPERM) { + VIR_INFO("Setting user and group to '%d:%d' on '%s' not permitted", + uid, gid, path); + } else if (chown_errno == EROFS) { + VIR_INFO("Setting user and group to '%d:%d' on '%s' not possible on readonly filesystem", + uid, gid, path); + } else { + virReportSystemError(chown_errno, + _("unable to set user and group to '%d:%d' on '%s'"), + uid, gid, path); + return -1; + } + } + return 0; +} + +static int +virSecurityDACRestoreSecurityFileLabel(const char *path) +{ + struct stat buf; + int rc = -1; + char *newpath = NULL; + + VIR_INFO("Restoring DAC user and group on '%s'", path); + + if (virFileResolveLink(path, &newpath) < 0) { + virReportSystemError(errno, + _("cannot resolve symlink %s"), path); + goto err; + } + + if (stat(newpath, &buf) != 0) + goto err; + + /* XXX record previous ownership */ + rc = virSecurityDACSetOwnership(newpath, 0, 0); + +err: + VIR_FREE(newpath); + return rc; +} + + +static int +virSecurityDACSetSecurityFileLabel(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, + const char *path, + size_t depth ATTRIBUTE_UNUSED, + void *opaque) +{ + virSecurityManagerPtr mgr = opaque; + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + return virSecurityDACSetOwnership(path, priv->user, priv->group); +} + + +static int +virSecurityDACSetSecurityImageLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainDiskDefPtr disk) + +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + if (!priv->dynamicOwnership) + return 0; + + return virDomainDiskDefForeachPath(disk, + virSecurityManagerGetAllowDiskFormatProbing(mgr), + false, + virSecurityDACSetSecurityFileLabel, + mgr); +} + + +static int +virSecurityDACRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainDiskDefPtr disk, + int migrated) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + if (!priv->dynamicOwnership) + return 0; + + /* Don't restore labels on readoly/shared disks, because + * other VMs may still be accessing these + * Alternatively we could iterate over all running + * domains and try to figure out if it is in use, but + * this would not work for clustered filesystems, since + * we can't see running VMs using the file on other nodes + * Safest bet is thus to skip the restore step. + */ + if (disk->readonly || disk->shared) + return 0; + + if (!disk->src) + return 0; + + /* If we have a shared FS & doing migrated, we must not + * change ownership, because that kills access on the + * destination host which is sub-optimal for the guest + * VM's I/O attempts :-) + */ + if (migrated) { + int rc = virStorageFileIsSharedFS(disk->src); + if (rc < 0) + return -1; + if (rc == 1) { + VIR_DEBUG("Skipping image label restore on %s because FS is shared", + disk->src); + return 0; + } + } + + return virSecurityDACRestoreSecurityFileLabel(disk->src); +} + + +static int +virSecurityDACRestoreSecurityImageLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + return virSecurityDACRestoreSecurityImageLabelInt(mgr, vm, disk, 0); +} + + +static int +virSecurityDACSetSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque) +{ + virSecurityManagerPtr mgr = opaque; + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + return virSecurityDACSetOwnership(file, priv->user, priv->group); +} + + +static int +virSecurityDACSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque) +{ + virSecurityManagerPtr mgr = opaque; + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + return virSecurityDACSetOwnership(file, priv->user, priv->group); +} + + +static int +virSecurityDACSetSecurityHostdevLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr dev) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int ret = -1; + + if (!priv->dynamicOwnership) + return 0; + + if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + return 0; + + switch (dev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { + usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, + dev->source.subsys.u.usb.device); + + if (!usb) + goto done; + + ret = usbDeviceFileIterate(usb, virSecurityDACSetSecurityUSBLabel, mgr); + usbFreeDevice(usb); + break; + } + + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: { + pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain, + dev->source.subsys.u.pci.bus, + dev->source.subsys.u.pci.slot, + dev->source.subsys.u.pci.function); + + if (!pci) + goto done; + + ret = pciDeviceFileIterate(pci, virSecurityDACSetSecurityPCILabel, mgr); + pciFreeDevice(pci); + + break; + } + + default: + ret = 0; + break; + } + +done: + return ret; +} + + +static int +virSecurityDACRestoreSecurityPCILabel(pciDevice *dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque ATTRIBUTE_UNUSED) +{ + return virSecurityDACRestoreSecurityFileLabel(file); +} + + +static int +virSecurityDACRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, + const char *file, + void *opaque ATTRIBUTE_UNUSED) +{ + return virSecurityDACRestoreSecurityFileLabel(file); +} + + +static int +virSecurityDACRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr dev) + +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int ret = -1; + + if (!priv->dynamicOwnership) + return 0; + + if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + return 0; + + switch (dev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: { + usbDevice *usb = usbGetDevice(dev->source.subsys.u.usb.bus, + dev->source.subsys.u.usb.device); + + if (!usb) + goto done; + + ret = usbDeviceFileIterate(usb, virSecurityDACRestoreSecurityUSBLabel, mgr); + usbFreeDevice(usb); + + break; + } + + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: { + pciDevice *pci = pciGetDevice(dev->source.subsys.u.pci.domain, + dev->source.subsys.u.pci.bus, + dev->source.subsys.u.pci.slot, + dev->source.subsys.u.pci.function); + + if (!pci) + goto done; + + ret = pciDeviceFileIterate(pci, virSecurityDACRestoreSecurityPCILabel, mgr); + pciFreeDevice(pci); + + break; + } + + default: + ret = 0; + break; + } + +done: + return ret; +} + + +static int +virSecurityDACSetChardevLabel(virSecurityManagerPtr mgr, + virDomainChrDefPtr dev) + +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + char *in = NULL, *out = NULL; + int ret = -1; + + switch (dev->type) { + case VIR_DOMAIN_CHR_TYPE_DEV: + case VIR_DOMAIN_CHR_TYPE_FILE: + ret = virSecurityDACSetOwnership(dev->data.file.path, priv->user, priv->group); + break; + + case VIR_DOMAIN_CHR_TYPE_PIPE: + if ((virAsprintf(&in, "%s.in", dev->data.file.path) < 0) || + (virAsprintf(&out, "%s.out", dev->data.file.path) < 0)) { + virReportOOMError(); + goto done; + } + if ((virSecurityDACSetOwnership(in, priv->user, priv->group) < 0) || + (virSecurityDACSetOwnership(out, priv->user, priv->group) < 0)) + goto done; + ret = 0; + break; + + default: + ret = 0; + break; + } + +done: + VIR_FREE(in); + VIR_FREE(out); + return ret; +} + +static int +virSecurityDACRestoreChardevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainChrDefPtr dev) +{ + char *in = NULL, *out = NULL; + int ret = -1; + + switch (dev->type) { + case VIR_DOMAIN_CHR_TYPE_DEV: + case VIR_DOMAIN_CHR_TYPE_FILE: + ret = virSecurityDACRestoreSecurityFileLabel(dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_PIPE: + if ((virAsprintf(&out, "%s.out", dev->data.file.path) < 0) || + (virAsprintf(&in, "%s.in", dev->data.file.path) < 0)) { + virReportOOMError(); + goto done; + } + if ((virSecurityDACRestoreSecurityFileLabel(out) < 0) || + (virSecurityDACRestoreSecurityFileLabel(in) < 0)) + goto done; + ret = 0; + break; + + default: + ret = 0; + break; + } + +done: + VIR_FREE(in); + VIR_FREE(out); + return ret; +} + + +static int +virSecurityDACRestoreChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainChrDefPtr dev, + void *opaque) +{ + virSecurityManagerPtr mgr = opaque; + + return virSecurityDACRestoreChardevLabel(mgr, dev); +} + + +static int +virSecurityDACRestoreSecurityAllLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + int migrated) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int i; + int rc = 0; + + if (!priv->dynamicOwnership) + return 0; + + + VIR_DEBUG("Restoring security label on %s migrated=%d", + vm->def->name, migrated); + + for (i = 0 ; i < vm->def->nhostdevs ; i++) { + if (virSecurityDACRestoreSecurityHostdevLabel(mgr, + vm, + vm->def->hostdevs[i]) < 0) + rc = -1; + } + for (i = 0 ; i < vm->def->ndisks ; i++) { + if (virSecurityDACRestoreSecurityImageLabelInt(mgr, + vm, + vm->def->disks[i], + migrated) < 0) + rc = -1; + } + + if (virDomainChrDefForeach(vm->def, + false, + virSecurityDACRestoreChardevCallback, + vm) < 0) + rc = -1; + + if (vm->def->os.kernel && + virSecurityDACRestoreSecurityFileLabel(vm->def->os.kernel) < 0) + rc = -1; + + if (vm->def->os.initrd && + virSecurityDACRestoreSecurityFileLabel(vm->def->os.initrd) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityDACSetChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainChrDefPtr dev, + void *opaque) +{ + virSecurityManagerPtr mgr = opaque; + + return virSecurityDACSetChardevLabel(mgr, dev); +} + + +static int +virSecurityDACSetSecurityAllLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + const char *stdin_path ATTRIBUTE_UNUSED) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int i; + + if (!priv->dynamicOwnership) + return 0; + + for (i = 0 ; i < vm->def->ndisks ; i++) { + /* XXX fixme - we need to recursively label the entriy tree :-( */ + if (vm->def->disks[i]->type == VIR_DOMAIN_DISK_TYPE_DIR) + continue; + if (virSecurityDACSetSecurityImageLabel(mgr, + vm, + vm->def->disks[i]) < 0) + return -1; + } + for (i = 0 ; i < vm->def->nhostdevs ; i++) { + if (virSecurityDACSetSecurityHostdevLabel(mgr, + vm, + vm->def->hostdevs[i]) < 0) + return -1; + } + + if (virDomainChrDefForeach(vm->def, + true, + virSecurityDACSetChardevCallback, + vm) < 0) + return -1; + + if (vm->def->os.kernel && + virSecurityDACSetOwnership(vm->def->os.kernel, + priv->user, + priv->group) < 0) + return -1; + + if (vm->def->os.initrd && + virSecurityDACSetOwnership(vm->def->os.initrd, + priv->user, + priv->group) < 0) + return -1; + + return 0; +} + + +static int +virSecurityDACSetSavedStateLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + const char *savefile) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + return virSecurityDACSetOwnership(savefile, priv->user, priv->group); +} + + +static int +virSecurityDACRestoreSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + const char *savefile) +{ + return virSecurityDACRestoreSecurityFileLabel(savefile); +} + + +static int +virSecurityDACSetProcessLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + virSecurityDACDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + DEBUG("Dropping privileges of VM to %d:%d", priv->user, priv->group); + + if (priv->group) { + if (setregid(priv->group, priv->group) < 0) { + virReportSystemError(errno, + _("cannot change to '%d' group"), + priv->group); + return -1; + } + } + if (priv->user) { + if (setreuid(priv->user, priv->user) < 0) { + virReportSystemError(errno, + _("cannot change to '%d' user"), + priv->user); + return -1; + } + } + + return 0; +} + + +static int +virSecurityDACVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +virSecurityDACGenLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +virSecurityDACReleaseLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +virSecurityDACReserveLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +virSecurityDACGetProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virSecurityLabelPtr seclabel ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +virSecurityDACSetSocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + + +static int +virSecurityDACClearSocketLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + + +virSecurityDriver virSecurityDriverDAC = { + sizeof(virSecurityDACData), + "virDAC", + + virSecurityDACProbe, + virSecurityDACOpen, + virSecurityDACClose, + + virSecurityDACGetModel, + virSecurityDACGetDOI, + + virSecurityDACVerify, + + virSecurityDACSetSecurityImageLabel, + virSecurityDACRestoreSecurityImageLabel, + + virSecurityDACSetSocketLabel, + virSecurityDACClearSocketLabel, + + virSecurityDACGenLabel, + virSecurityDACReserveLabel, + virSecurityDACReleaseLabel, + + virSecurityDACGetProcessLabel, + virSecurityDACSetProcessLabel, + + virSecurityDACSetSecurityAllLabel, + virSecurityDACRestoreSecurityAllLabel, + + virSecurityDACSetSecurityHostdevLabel, + virSecurityDACRestoreSecurityHostdevLabel, + + virSecurityDACSetSavedStateLabel, + virSecurityDACRestoreSavedStateLabel, +}; diff --git a/src/security/security_dac.h b/src/security/security_dac.h new file mode 100644 index 0000000..b690236 --- /dev/null +++ b/src/security/security_dac.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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. + * + * POSIX DAC security driver + */ + +#include "security_driver.h" + +#ifndef __VIR_SECURITY_DAC +# define __VIR_SECURITY_DAC + +extern virSecurityDriver virSecurityDriverDAC; + +void virSecurityDACSetUser(virSecurityManagerPtr mgr, + uid_t user); +void virSecurityDACSetGroup(virSecurityManagerPtr mgr, + gid_t group); + +void virSecurityDACSetDynamicOwnership(virSecurityManagerPtr mgr, + bool dynamic); + +#endif /* __VIR_SECURITY_DAC */ diff --git a/src/security/security_driver.c b/src/security/security_driver.c index 9e32fa4..6d75dcc 100644 --- a/src/security/security_driver.c +++ b/src/security/security_driver.c @@ -24,116 +24,52 @@ # include "security_apparmor.h" #endif +#include "security_nop.h" + static virSecurityDriverPtr security_drivers[] = { #ifdef WITH_SECDRIVER_SELINUX - &virSELinuxSecurityDriver, + &virSecurityDriverSELinux, #endif #ifdef WITH_SECDRIVER_APPARMOR &virAppArmorSecurityDriver, #endif - NULL + &virSecurityDriverNop, /* Must always be last, since it will always probe */ }; -int -virSecurityDriverVerify(virDomainDefPtr def) -{ - unsigned int i; - const virSecurityLabelDefPtr secdef = &def->seclabel; - - if (!secdef->model || - STREQ(secdef->model, "none")) - return 0; - - for (i = 0; security_drivers[i] != NULL ; i++) { - if (STREQ(security_drivers[i]->name, secdef->model)) { - return security_drivers[i]->domainSecurityVerify(def); - } - } - virSecurityReportError(VIR_ERR_XML_ERROR, - _("invalid security model '%s'"), secdef->model); - return -1; -} - -int -virSecurityDriverStartup(virSecurityDriverPtr *drv, - const char *name, - bool allowDiskFormatProbing) +virSecurityDriverPtr virSecurityDriverLookup(const char *name) { - unsigned int i; - - if (name && STREQ(name, "none")) - return -2; + virSecurityDriverPtr drv = NULL; + int i; - for (i = 0; security_drivers[i] != NULL ; i++) { + for (i = 0; i < ARRAY_CARDINALITY(security_drivers) ; i++) { virSecurityDriverPtr tmp = security_drivers[i]; - if (name && STRNEQ(tmp->name, name)) - continue; - - switch (tmp->probe()) { - case SECURITY_DRIVER_ENABLE: - virSecurityDriverInit(tmp); - if (tmp->open(tmp, allowDiskFormatProbing) == -1) { - return -1; - } else { - *drv = tmp; - return 0; + if (name) { + if (STREQ(tmp->name, name)) { + drv = tmp; + break; } - break; + } else { + switch (tmp->probe()) { + case SECURITY_DRIVER_ENABLE: + drv = tmp; + break; - case SECURITY_DRIVER_DISABLE: - break; + case SECURITY_DRIVER_DISABLE: + break; - default: - return -1; + default: + return NULL; + } } } - return -2; -} - -/* - * Helpers - */ -void -virSecurityDriverInit(virSecurityDriverPtr drv) -{ - memset(&drv->_private, 0, sizeof drv->_private); -} -int -virSecurityDriverSetDOI(virSecurityDriverPtr drv, - const char *doi) -{ - if (strlen(doi) >= VIR_SECURITY_DOI_BUFLEN) { + if (!drv) { virSecurityReportError(VIR_ERR_INTERNAL_ERROR, - _("%s: DOI \'%s\' is " - "longer than the maximum allowed length of %d"), - __func__, doi, VIR_SECURITY_DOI_BUFLEN - 1); - return -1; + "Security driver %s not found", NULLSTR(name)); + return NULL; } - strcpy(drv->_private.doi, doi); - return 0; -} - -const char * -virSecurityDriverGetDOI(virSecurityDriverPtr drv) -{ - return drv->_private.doi; -} -const char * -virSecurityDriverGetModel(virSecurityDriverPtr drv) -{ - return drv->name; + return drv; } -void virSecurityDriverSetAllowDiskFormatProbing(virSecurityDriverPtr drv, - bool allowDiskFormatProbing) -{ - drv->_private.allowDiskFormatProbing = allowDiskFormatProbing; -} - -bool virSecurityDriverGetAllowDiskFormatProbing(virSecurityDriverPtr drv) -{ - return drv->_private.allowDiskFormatProbing; -} diff --git a/src/security/security_driver.h b/src/security/security_driver.h index d768f32..e5a8d41 100644 --- a/src/security/security_driver.h +++ b/src/security/security_driver.h @@ -16,6 +16,8 @@ # include "internal.h" # include "domain_conf.h" +# include "security_manager.h" + /* * Return values for security driver probing: the driver will determine * whether it should be enabled or disabled. @@ -29,104 +31,91 @@ typedef enum { typedef struct _virSecurityDriver virSecurityDriver; typedef virSecurityDriver *virSecurityDriverPtr; -typedef struct _virSecurityDriverState virSecurityDriverState; -typedef virSecurityDriverState *virSecurityDriverStatePtr; - typedef virSecurityDriverStatus (*virSecurityDriverProbe) (void); -typedef int (*virSecurityDriverOpen) (virSecurityDriverPtr drv, - bool allowDiskFormatProbing); -typedef int (*virSecurityDomainRestoreImageLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDriverOpen) (virSecurityManagerPtr mgr); +typedef int (*virSecurityDriverClose) (virSecurityManagerPtr mgr); + +typedef const char *(*virSecurityDriverGetModel) (virSecurityManagerPtr mgr); +typedef const char *(*virSecurityDriverGetDOI) (virSecurityManagerPtr mgr); + +typedef int (*virSecurityDomainRestoreImageLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainDiskDefPtr disk); -typedef int (*virSecurityDomainSetSocketLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainSetSocketLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm); -typedef int (*virSecurityDomainClearSocketLabel)(virSecurityDriverPtr drv, +typedef int (*virSecurityDomainClearSocketLabel)(virSecurityManagerPtr mgr, virDomainObjPtr vm); -typedef int (*virSecurityDomainSetImageLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainSetImageLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainDiskDefPtr disk); -typedef int (*virSecurityDomainRestoreHostdevLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainRestoreHostdevLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainHostdevDefPtr dev); -typedef int (*virSecurityDomainSetHostdevLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainSetHostdevLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainHostdevDefPtr dev); -typedef int (*virSecurityDomainSetSavedStateLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainSetSavedStateLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm, const char *savefile); -typedef int (*virSecurityDomainRestoreSavedStateLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainRestoreSavedStateLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm, const char *savefile); -typedef int (*virSecurityDomainGenLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainGenLabel) (virSecurityManagerPtr mgr, virDomainObjPtr sec); -typedef int (*virSecurityDomainReserveLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainReserveLabel) (virSecurityManagerPtr mgr, virDomainObjPtr sec); -typedef int (*virSecurityDomainReleaseLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainReleaseLabel) (virSecurityManagerPtr mgr, virDomainObjPtr sec); -typedef int (*virSecurityDomainSetAllLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainSetAllLabel) (virSecurityManagerPtr mgr, virDomainObjPtr sec, const char *stdin_path); -typedef int (*virSecurityDomainRestoreAllLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainRestoreAllLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm, int migrated); -typedef int (*virSecurityDomainGetProcessLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainGetProcessLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm, virSecurityLabelPtr sec); -typedef int (*virSecurityDomainSetProcessLabel) (virSecurityDriverPtr drv, +typedef int (*virSecurityDomainSetProcessLabel) (virSecurityManagerPtr mgr, virDomainObjPtr vm); -typedef int (*virSecurityDomainSecurityVerify) (virDomainDefPtr def); +typedef int (*virSecurityDomainSecurityVerify) (virSecurityManagerPtr mgr, + virDomainDefPtr def); + struct _virSecurityDriver { + size_t privateDataLen; const char *name; virSecurityDriverProbe probe; virSecurityDriverOpen open; + virSecurityDriverClose close; + + virSecurityDriverGetModel getModel; + virSecurityDriverGetDOI getDOI; + virSecurityDomainSecurityVerify domainSecurityVerify; + + virSecurityDomainSetImageLabel domainSetSecurityImageLabel; virSecurityDomainRestoreImageLabel domainRestoreSecurityImageLabel; + virSecurityDomainSetSocketLabel domainSetSecuritySocketLabel; virSecurityDomainClearSocketLabel domainClearSecuritySocketLabel; - virSecurityDomainSetImageLabel domainSetSecurityImageLabel; + virSecurityDomainGenLabel domainGenSecurityLabel; virSecurityDomainReserveLabel domainReserveSecurityLabel; virSecurityDomainReleaseLabel domainReleaseSecurityLabel; + virSecurityDomainGetProcessLabel domainGetSecurityProcessLabel; virSecurityDomainSetProcessLabel domainSetSecurityProcessLabel; + virSecurityDomainSetAllLabel domainSetSecurityAllLabel; virSecurityDomainRestoreAllLabel domainRestoreSecurityAllLabel; - virSecurityDomainRestoreHostdevLabel domainRestoreSecurityHostdevLabel; + virSecurityDomainSetHostdevLabel domainSetSecurityHostdevLabel; + virSecurityDomainRestoreHostdevLabel domainRestoreSecurityHostdevLabel; + virSecurityDomainSetSavedStateLabel domainSetSavedStateLabel; virSecurityDomainRestoreSavedStateLabel domainRestoreSavedStateLabel; - - /* - * This is internally managed driver state and should only be accessed - * via helpers below. - */ - struct { - char doi[VIR_SECURITY_DOI_BUFLEN]; - bool allowDiskFormatProbing; - } _private; }; -/* Global methods */ -int virSecurityDriverStartup(virSecurityDriverPtr *drv, - const char *name, - bool allowDiskFormatProbing); - -int -virSecurityDriverVerify(virDomainDefPtr def); - -# define virSecurityReportError(code, ...) \ - virReportErrorHelper(NULL, VIR_FROM_SECURITY, code, __FILE__, \ - __FUNCTION__, __LINE__, __VA_ARGS__) - -/* Helpers */ -void virSecurityDriverInit(virSecurityDriverPtr drv); -int virSecurityDriverSetDOI(virSecurityDriverPtr drv, - const char *doi); -void virSecurityDriverSetAllowDiskFormatProbing(virSecurityDriverPtr drv, - bool allowDiskFormatProbing); -const char *virSecurityDriverGetDOI(virSecurityDriverPtr drv); -const char *virSecurityDriverGetModel(virSecurityDriverPtr drv); -bool virSecurityDriverGetAllowDiskFormatProbing(virSecurityDriverPtr drv); +virSecurityDriverPtr virSecurityDriverLookup(const char *name); #endif /* __VIR_SECURITY_H__ */ diff --git a/src/security/security_manager.c b/src/security/security_manager.c new file mode 100644 index 0000000..7ab6e37 --- /dev/null +++ b/src/security/security_manager.c @@ -0,0 +1,291 @@ + +#include <config.h> + + +#include "security_driver.h" +#include "security_stack.h" +#include "security_dac.h" +#include "virterror_internal.h" +#include "memory.h" +#include "logging.h" + +#define VIR_FROM_THIS VIR_FROM_SECURITY + + +struct _virSecurityManager { + virSecurityDriverPtr drv; + bool allowDiskFormatProbing; +}; + +static virSecurityManagerPtr virSecurityManagerNewDriver(virSecurityDriverPtr drv, + bool allowDiskFormatProbing) +{ + virSecurityManagerPtr mgr; + + if (VIR_ALLOC_VAR(mgr, char, drv->privateDataLen) < 0) { + virReportOOMError(); + return NULL; + } + + mgr->drv = drv; + mgr->allowDiskFormatProbing = allowDiskFormatProbing; + + if (drv->open(mgr) < 0) { + virSecurityManagerFree(mgr); + return NULL; + } + + return mgr; +} + +virSecurityManagerPtr virSecurityManagerNewStack(virSecurityManagerPtr primary, + virSecurityManagerPtr secondary) +{ + virSecurityManagerPtr mgr = + virSecurityManagerNewDriver(&virSecurityDriverStack, + virSecurityManagerGetAllowDiskFormatProbing(primary)); + + virSecurityStackSetPrimary(mgr, primary); + virSecurityStackSetSecondary(mgr, secondary); + + return mgr; +} + +virSecurityManagerPtr virSecurityManagerNewDAC(uid_t user, + gid_t group, + bool allowDiskFormatProbing, + bool dynamicOwnership) +{ + virSecurityManagerPtr mgr = + virSecurityManagerNewDriver(&virSecurityDriverDAC, + allowDiskFormatProbing); + + virSecurityDACSetUser(mgr, user); + virSecurityDACSetGroup(mgr, group); + virSecurityDACSetDynamicOwnership(mgr, dynamicOwnership); + + return mgr; +} + +virSecurityManagerPtr virSecurityManagerNew(const char *name, + bool allowDiskFormatProbing) +{ + virSecurityDriverPtr drv = virSecurityDriverLookup(name); + if (!drv) + return NULL; + + return virSecurityManagerNewDriver(drv, allowDiskFormatProbing); +} + + +void *virSecurityManagerGetPrivateData(virSecurityManagerPtr mgr) +{ + return mgr + sizeof(mgr); +} + + +void virSecurityManagerFree(virSecurityManagerPtr mgr) +{ + if (!mgr) + return; + + if (mgr->drv->close) + mgr->drv->close(mgr); + + VIR_FREE(mgr); +} + +const char * +virSecurityManagerGetDOI(virSecurityManagerPtr mgr) +{ + if (mgr->drv->getDOI) + return mgr->drv->getDOI(mgr); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + +const char * +virSecurityManagerGetModel(virSecurityManagerPtr mgr) +{ + if (mgr->drv->getModel) + return mgr->drv->getModel(mgr); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return NULL; +} + +bool virSecurityManagerGetAllowDiskFormatProbing(virSecurityManagerPtr mgr) +{ + return mgr->allowDiskFormatProbing; +} + +int virSecurityManagerRestoreImageLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + if (mgr->drv->domainRestoreSecurityImageLabel) + return mgr->drv->domainRestoreSecurityImageLabel(mgr, vm, disk); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerSetSocketLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + if (mgr->drv->domainSetSecuritySocketLabel) + return mgr->drv->domainSetSecuritySocketLabel(mgr, vm); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerClearSocketLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + if (mgr->drv->domainClearSecuritySocketLabel) + return mgr->drv->domainClearSecuritySocketLabel(mgr, vm); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerSetImageLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + if (mgr->drv->domainSetSecurityImageLabel) + return mgr->drv->domainSetSecurityImageLabel(mgr, vm, disk); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerRestoreHostdevLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainHostdevDefPtr dev) +{ + if (mgr->drv->domainRestoreSecurityHostdevLabel) + return mgr->drv->domainRestoreSecurityHostdevLabel(mgr, vm, dev); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerSetHostdevLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainHostdevDefPtr dev) +{ + if (mgr->drv->domainSetSecurityHostdevLabel) + return mgr->drv->domainSetSecurityHostdevLabel(mgr, vm, dev); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerSetSavedStateLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + const char *savefile) +{ + if (mgr->drv->domainSetSavedStateLabel) + return mgr->drv->domainSetSavedStateLabel(mgr, vm, savefile); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerRestoreSavedStateLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + const char *savefile) +{ + if (mgr->drv->domainRestoreSavedStateLabel) + return mgr->drv->domainRestoreSavedStateLabel(mgr, vm, savefile); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerGenLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + if (mgr->drv->domainGenSecurityLabel) + return mgr->drv->domainGenSecurityLabel(mgr, vm); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerReserveLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + if (mgr->drv->domainReserveSecurityLabel) + return mgr->drv->domainReserveSecurityLabel(mgr, vm); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerReleaseLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + if (mgr->drv->domainReleaseSecurityLabel) + return mgr->drv->domainReleaseSecurityLabel(mgr, vm); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerSetAllLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + const char *stdin_path) +{ + if (mgr->drv->domainSetSecurityAllLabel) + return mgr->drv->domainSetSecurityAllLabel(mgr, vm, stdin_path); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerRestoreAllLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + int migrated) +{ + if (mgr->drv->domainRestoreSecurityAllLabel) + return mgr->drv->domainRestoreSecurityAllLabel(mgr, vm, migrated); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerGetProcessLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virSecurityLabelPtr sec) +{ + if (mgr->drv->domainGetSecurityProcessLabel) + return mgr->drv->domainGetSecurityProcessLabel(mgr, vm, sec); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerSetProcessLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + if (mgr->drv->domainSetSecurityProcessLabel) + return mgr->drv->domainSetSecurityProcessLabel(mgr, vm); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + +int virSecurityManagerVerify(virSecurityManagerPtr mgr, + virDomainDefPtr def) +{ + if (mgr->drv->domainSecurityVerify) + return mgr->drv->domainSecurityVerify(mgr, def); + + virSecurityReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + return -1; +} + diff --git a/src/security/security_manager.h b/src/security/security_manager.h new file mode 100644 index 0000000..c0ef84f --- /dev/null +++ b/src/security/security_manager.h @@ -0,0 +1,74 @@ + +#ifndef VIR_SECURITY_MANAGER_H__ +#define VIR_SECURITY_MANAGER_H__ + +# define virSecurityReportError(code, ...) \ + virReportErrorHelper(NULL, VIR_FROM_SECURITY, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + + +typedef struct _virSecurityManager virSecurityManager; +typedef virSecurityManager *virSecurityManagerPtr; + +virSecurityManagerPtr virSecurityManagerNew(const char *name, + bool allowDiskFormatProbing); + +virSecurityManagerPtr virSecurityManagerNewStack(virSecurityManagerPtr primary, + virSecurityManagerPtr secondary); + +virSecurityManagerPtr virSecurityManagerNewDAC(uid_t user, + gid_t group, + bool allowDiskFormatProbing, + bool dynamicOwnership); + +void *virSecurityManagerGetPrivateData(virSecurityManagerPtr mgr); + +void virSecurityManagerFree(virSecurityManagerPtr mgr); + +const char *virSecurityManagerGetDOI(virSecurityManagerPtr mgr); +const char *virSecurityManagerGetModel(virSecurityManagerPtr mgr); +bool virSecurityManagerGetAllowDiskFormatProbing(virSecurityManagerPtr mgr); + +int virSecurityManagerRestoreImageLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainDiskDefPtr disk); +int virSecurityManagerSetSocketLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm); +int virSecurityManagerClearSocketLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm); +int virSecurityManagerSetImageLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainDiskDefPtr disk); +int virSecurityManagerRestoreHostdevLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainHostdevDefPtr dev); +int virSecurityManagerSetHostdevLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainHostdevDefPtr dev); +int virSecurityManagerSetSavedStateLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + const char *savefile); +int virSecurityManagerRestoreSavedStateLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + const char *savefile); +int virSecurityManagerGenLabel(virSecurityManagerPtr mgr, + virDomainObjPtr sec); +int virSecurityManagerReserveLabel(virSecurityManagerPtr mgr, + virDomainObjPtr sec); +int virSecurityManagerReleaseLabel(virSecurityManagerPtr mgr, + virDomainObjPtr sec); +int virSecurityManagerSetAllLabel(virSecurityManagerPtr mgr, + virDomainObjPtr sec, + const char *stdin_path); +int virSecurityManagerRestoreAllLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + int migrated); +int virSecurityManagerGetProcessLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virSecurityLabelPtr sec); +int virSecurityManagerSetProcessLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm); +int virSecurityManagerVerify(virSecurityManagerPtr mgr, + virDomainDefPtr def); + +#endif /* VIR_SECURITY_MANAGER_H__ */ diff --git a/src/security/security_nop.c b/src/security/security_nop.c new file mode 100644 index 0000000..947cf55 --- /dev/null +++ b/src/security/security_nop.c @@ -0,0 +1,168 @@ + + +#include <config.h> + +#include "security_nop.h" + +static virSecurityDriverStatus virSecurityDriverProbeNop(void) +{ + return SECURITY_DRIVER_ENABLE; +} + +static int virSecurityDriverOpenNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDriverCloseNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + +static const char * virSecurityDriverGetModelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return "none"; +} + +static const char * virSecurityDriverGetDOINop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return "0"; +} + +static int virSecurityDomainRestoreImageLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainDiskDefPtr disk ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainSetSocketLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainClearSocketLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainSetImageLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainDiskDefPtr disk ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainRestoreHostdevLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainSetHostdevLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virDomainHostdevDefPtr dev ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainSetSavedStateLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + const char *savefile ATTRIBUTE_UNUSED) +{ + return 0; +} +static int virSecurityDomainRestoreSavedStateLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + const char *savefile ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainGenLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr sec ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainReserveLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr sec ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainReleaseLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr sec ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainSetAllLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr sec ATTRIBUTE_UNUSED, + const char *stdin_path ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainRestoreAllLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + int migrated ATTRIBUTE_UNUSED) +{ + return 0; +} +static int virSecurityDomainGetProcessLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED, + virSecurityLabelPtr sec ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainSetProcessLabelNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainObjPtr vm ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int virSecurityDomainVerifyNop(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def ATTRIBUTE_UNUSED) +{ + return 0; +} + +virSecurityDriver virSecurityDriverNop = { + 0, + "nop", + virSecurityDriverProbeNop, + virSecurityDriverOpenNop, + virSecurityDriverCloseNop, + + virSecurityDriverGetModelNop, + virSecurityDriverGetDOINop, + + virSecurityDomainVerifyNop, + + virSecurityDomainSetImageLabelNop, + virSecurityDomainRestoreImageLabelNop, + + virSecurityDomainSetSocketLabelNop, + virSecurityDomainClearSocketLabelNop, + + virSecurityDomainGenLabelNop, + virSecurityDomainReserveLabelNop, + virSecurityDomainReleaseLabelNop, + + virSecurityDomainGetProcessLabelNop, + virSecurityDomainSetProcessLabelNop, + + virSecurityDomainSetAllLabelNop, + virSecurityDomainRestoreAllLabelNop, + + virSecurityDomainSetHostdevLabelNop, + virSecurityDomainRestoreHostdevLabelNop, + + virSecurityDomainSetSavedStateLabelNop, + virSecurityDomainRestoreSavedStateLabelNop, +}; diff --git a/src/security/security_nop.h b/src/security/security_nop.h new file mode 100644 index 0000000..714e646 --- /dev/null +++ b/src/security/security_nop.h @@ -0,0 +1,17 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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. + * + */ +#ifndef __VIR_SECURITY_NOP_H__ +# define __VIR_SECURITY_NOP_H__ + +#include "security_driver.h" + +extern virSecurityDriver virSecurityDriverNop; + +#endif /* __VIR_SECURITY_NOP_H__ */ diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c index 996177a..3f3fe90 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -157,7 +157,7 @@ SELinuxInitialize(void) } static int -SELinuxGenSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxGenSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm) { int rc = -1; @@ -222,7 +222,7 @@ done: } static int -SELinuxReserveSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxReserveSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm) { security_context_t pctx; @@ -267,20 +267,34 @@ SELinuxSecurityDriverProbe(void) } static int -SELinuxSecurityDriverOpen(virSecurityDriverPtr drv, - bool allowDiskFormatProbing) +SELinuxSecurityDriverOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return SELinuxInitialize(); +} + +static int +SELinuxSecurityDriverClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + + +static const char *SELinuxSecurityGetModel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return SECURITY_SELINUX_NAME; +} + +static const char *SELinuxSecurityGetDOI(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) { /* * Where will the DOI come from? SELinux configuration, or qemu * configuration? For the moment, we'll just set it to "0". */ - virSecurityDriverSetDOI(drv, SECURITY_SELINUX_VOID_DOI); - virSecurityDriverSetAllowDiskFormatProbing(drv, allowDiskFormatProbing); - return SELinuxInitialize(); + return SECURITY_SELINUX_VOID_DOI; } static int -SELinuxGetSecurityProcessLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxGetSecurityProcessLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, virSecurityLabelPtr sec) { @@ -393,7 +407,7 @@ err: } static int -SELinuxRestoreSecurityImageLabelInt(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxRestoreSecurityImageLabelInt(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, virDomainDiskDefPtr disk, int migrated) @@ -438,11 +452,11 @@ SELinuxRestoreSecurityImageLabelInt(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, static int -SELinuxRestoreSecurityImageLabel(virSecurityDriverPtr drv, +SELinuxRestoreSecurityImageLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainDiskDefPtr disk) { - return SELinuxRestoreSecurityImageLabelInt(drv, vm, disk, 0); + return SELinuxRestoreSecurityImageLabelInt(mgr, vm, disk, 0); } @@ -476,13 +490,13 @@ SELinuxSetSecurityFileLabel(virDomainDiskDefPtr disk, } static int -SELinuxSetSecurityImageLabel(virSecurityDriverPtr drv, +SELinuxSetSecurityImageLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, virDomainDiskDefPtr disk) { const virSecurityLabelDefPtr secdef = &vm->def->seclabel; - bool allowDiskFormatProbing = virSecurityDriverGetAllowDiskFormatProbing(drv); + bool allowDiskFormatProbing = virSecurityManagerGetAllowDiskFormatProbing(mgr); if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) return 0; @@ -516,7 +530,7 @@ SELinuxSetSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, } static int -SELinuxSetSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxSetSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, virDomainHostdevDefPtr dev) @@ -585,7 +599,7 @@ SELinuxRestoreSecurityUSBLabel(usbDevice *dev ATTRIBUTE_UNUSED, } static int -SELinuxRestoreSecurityHostdevLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, virDomainHostdevDefPtr dev) @@ -734,7 +748,7 @@ SELinuxRestoreSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, static int -SELinuxRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxRestoreSecurityAllLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, int migrated ATTRIBUTE_UNUSED) { @@ -748,13 +762,13 @@ SELinuxRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, return 0; for (i = 0 ; i < vm->def->nhostdevs ; i++) { - if (SELinuxRestoreSecurityHostdevLabel(drv, + if (SELinuxRestoreSecurityHostdevLabel(mgr, vm, vm->def->hostdevs[i]) < 0) rc = -1; } for (i = 0 ; i < vm->def->ndisks ; i++) { - if (SELinuxRestoreSecurityImageLabelInt(drv, + if (SELinuxRestoreSecurityImageLabelInt(mgr, vm, vm->def->disks[i], migrated) < 0) @@ -779,7 +793,7 @@ SELinuxRestoreSecurityAllLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, } static int -SELinuxReleaseSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxReleaseSecurityLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm) { const virSecurityLabelDefPtr secdef = &vm->def->seclabel; @@ -803,7 +817,7 @@ SELinuxReleaseSecurityLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, static int -SELinuxSetSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxSetSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, const char *savefile) { @@ -817,7 +831,7 @@ SELinuxSetSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, static int -SELinuxRestoreSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, +SELinuxRestoreSavedStateLabel(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, virDomainObjPtr vm, const char *savefile) { @@ -831,9 +845,19 @@ SELinuxRestoreSavedStateLabel(virSecurityDriverPtr drv ATTRIBUTE_UNUSED, static int -SELinuxSecurityVerify(virDomainDefPtr def) +SELinuxSecurityVerify(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED, + virDomainDefPtr def) { const virSecurityLabelDefPtr secdef = &def->seclabel; + if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { + virSecurityReportError(VIR_ERR_INTERNAL_ERROR, + _("security label driver mismatch: " + "'%s' model configured for domain, but " + "hypervisor driver is '%s'."), + secdef->model, virSecurityManagerGetModel(mgr)); + return -1; + } + if (secdef->type == VIR_DOMAIN_SECLABEL_STATIC) { if (security_check_context(secdef->label) != 0) { virSecurityReportError(VIR_ERR_XML_ERROR, @@ -845,7 +869,7 @@ SELinuxSecurityVerify(virDomainDefPtr def) } static int -SELinuxSetSecurityProcessLabel(virSecurityDriverPtr drv, +SELinuxSetSecurityProcessLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm) { /* TODO: verify DOI */ @@ -854,12 +878,12 @@ SELinuxSetSecurityProcessLabel(virSecurityDriverPtr drv, if (vm->def->seclabel.label == NULL) return 0; - if (!STREQ(drv->name, secdef->model)) { + if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { virSecurityReportError(VIR_ERR_INTERNAL_ERROR, _("security label driver mismatch: " "'%s' model configured for domain, but " "hypervisor driver is '%s'."), - secdef->model, drv->name); + secdef->model, virSecurityManagerGetModel(mgr)); if (security_getenforce() == 1) return -1; } @@ -876,7 +900,7 @@ SELinuxSetSecurityProcessLabel(virSecurityDriverPtr drv, } static int -SELinuxSetSecuritySocketLabel(virSecurityDriverPtr drv, +SELinuxSetSecuritySocketLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm) { /* TODO: verify DOI */ @@ -889,12 +913,12 @@ SELinuxSetSecuritySocketLabel(virSecurityDriverPtr drv, if (vm->def->seclabel.label == NULL) return 0; - if (!STREQ(drv->name, secdef->model)) { + if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { virSecurityReportError(VIR_ERR_INTERNAL_ERROR, _("security label driver mismatch: " "'%s' model configured for domain, but " "hypervisor driver is '%s'."), - secdef->model, drv->name); + secdef->model, virSecurityManagerGetModel(mgr)); goto done; } @@ -947,7 +971,7 @@ done: } static int -SELinuxClearSecuritySocketLabel(virSecurityDriverPtr drv, +SELinuxClearSecuritySocketLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm) { /* TODO: verify DOI */ @@ -956,12 +980,12 @@ SELinuxClearSecuritySocketLabel(virSecurityDriverPtr drv, if (vm->def->seclabel.label == NULL) return 0; - if (!STREQ(drv->name, secdef->model)) { + if (!STREQ(virSecurityManagerGetModel(mgr), secdef->model)) { virSecurityReportError(VIR_ERR_INTERNAL_ERROR, _("security label driver mismatch: " "'%s' model configured for domain, but " "hypervisor driver is '%s'."), - secdef->model, drv->name); + secdef->model, virSecurityManagerGetModel(mgr)); if (security_getenforce() == 1) return -1; } @@ -989,7 +1013,7 @@ SELinuxSetSecurityChardevCallback(virDomainDefPtr def ATTRIBUTE_UNUSED, static int -SELinuxSetSecurityAllLabel(virSecurityDriverPtr drv, +SELinuxSetSecurityAllLabel(virSecurityManagerPtr mgr, virDomainObjPtr vm, const char *stdin_path) { @@ -1006,12 +1030,12 @@ SELinuxSetSecurityAllLabel(virSecurityDriverPtr drv, vm->def->disks[i]->src, vm->def->disks[i]->dst); continue; } - if (SELinuxSetSecurityImageLabel(drv, + if (SELinuxSetSecurityImageLabel(mgr, vm, vm->def->disks[i]) < 0) return -1; } for (i = 0 ; i < vm->def->nhostdevs ; i++) { - if (SELinuxSetSecurityHostdevLabel(drv, + if (SELinuxSetSecurityHostdevLabel(mgr, vm, vm->def->hostdevs[i]) < 0) return -1; @@ -1041,24 +1065,37 @@ SELinuxSetSecurityAllLabel(virSecurityDriverPtr drv, return 0; } -virSecurityDriver virSELinuxSecurityDriver = { - .name = SECURITY_SELINUX_NAME, - .probe = SELinuxSecurityDriverProbe, - .open = SELinuxSecurityDriverOpen, - .domainSecurityVerify = SELinuxSecurityVerify, - .domainSetSecurityImageLabel = SELinuxSetSecurityImageLabel, - .domainSetSecuritySocketLabel = SELinuxSetSecuritySocketLabel, - .domainClearSecuritySocketLabel = SELinuxClearSecuritySocketLabel, - .domainRestoreSecurityImageLabel = SELinuxRestoreSecurityImageLabel, - .domainGenSecurityLabel = SELinuxGenSecurityLabel, - .domainReserveSecurityLabel = SELinuxReserveSecurityLabel, - .domainReleaseSecurityLabel = SELinuxReleaseSecurityLabel, - .domainGetSecurityProcessLabel = SELinuxGetSecurityProcessLabel, - .domainSetSecurityProcessLabel = SELinuxSetSecurityProcessLabel, - .domainRestoreSecurityAllLabel = SELinuxRestoreSecurityAllLabel, - .domainSetSecurityAllLabel = SELinuxSetSecurityAllLabel, - .domainSetSecurityHostdevLabel = SELinuxSetSecurityHostdevLabel, - .domainRestoreSecurityHostdevLabel = SELinuxRestoreSecurityHostdevLabel, - .domainSetSavedStateLabel = SELinuxSetSavedStateLabel, - .domainRestoreSavedStateLabel = SELinuxRestoreSavedStateLabel, +virSecurityDriver virSecurityDriverSELinux = { + 0, + SECURITY_SELINUX_NAME, + SELinuxSecurityDriverProbe, + SELinuxSecurityDriverOpen, + SELinuxSecurityDriverClose, + + SELinuxSecurityGetModel, + SELinuxSecurityGetDOI, + + SELinuxSecurityVerify, + + SELinuxSetSecurityImageLabel, + SELinuxRestoreSecurityImageLabel, + + SELinuxSetSecuritySocketLabel, + SELinuxClearSecuritySocketLabel, + + SELinuxGenSecurityLabel, + SELinuxReserveSecurityLabel, + SELinuxReleaseSecurityLabel, + + SELinuxGetSecurityProcessLabel, + SELinuxSetSecurityProcessLabel, + + SELinuxSetSecurityAllLabel, + SELinuxRestoreSecurityAllLabel, + + SELinuxSetSecurityHostdevLabel, + SELinuxRestoreSecurityHostdevLabel, + + SELinuxSetSavedStateLabel, + SELinuxRestoreSavedStateLabel, }; diff --git a/src/security/security_selinux.h b/src/security/security_selinux.h index 056ba75..aa67421 100644 --- a/src/security/security_selinux.h +++ b/src/security/security_selinux.h @@ -13,6 +13,6 @@ #ifndef __VIR_SECURITY_SELINUX_H__ # define __VIR_SECURITY_SELINUX_H__ -extern virSecurityDriver virSELinuxSecurityDriver; +extern virSecurityDriver virSecurityDriverSELinux; #endif /* __VIR_SECURITY_SELINUX_H__ */ diff --git a/src/security/security_stack.c b/src/security/security_stack.c new file mode 100644 index 0000000..9b3753a --- /dev/null +++ b/src/security/security_stack.c @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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. + * + * QEMU stacked security driver + */ + +#include <config.h> + +#include "security_stack.h" + +#include "virterror_internal.h" + +#define VIR_FROM_THIS VIR_FROM_SECURITY + +typedef struct _virSecurityStackData virSecurityStackData; +typedef virSecurityStackData *virSecurityStackDataPtr; + +struct _virSecurityStackData { + virSecurityManagerPtr primary; + virSecurityManagerPtr secondary; +}; + +void virSecurityStackSetPrimary(virSecurityManagerPtr mgr, + virSecurityManagerPtr primary) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + priv->primary = primary; +} + +void virSecurityStackSetSecondary(virSecurityManagerPtr mgr, + virSecurityManagerPtr secondary) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + priv->secondary = secondary; +} + +static virSecurityDriverStatus +virSecurityStackProbe(void) +{ + return SECURITY_DRIVER_ENABLE; +} + +static int +virSecurityStackOpen(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +virSecurityStackClose(virSecurityManagerPtr mgr ATTRIBUTE_UNUSED) +{ + return 0; +} + +static const char * virSecurityStackGetModel(virSecurityManagerPtr mgr) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + return virSecurityManagerGetModel(priv->primary); +} + +static const char * virSecurityStackGetDOI(virSecurityManagerPtr mgr) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + + return virSecurityManagerGetDOI(priv->primary); +} + +static int +virSecurityStackVerify(virSecurityManagerPtr mgr, + virDomainDefPtr def) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerVerify(priv->primary, def) < 0) + rc = -1; + +#if 0 + if (virSecurityManagerVerify(priv->secondary, def) < 0) + rc = -1; +#endif + + return rc; +} + + +static int +virSecurityStackGenLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerGenLabel(priv->primary, vm) < 0) + rc = -1; +#if 0 + if (virSecurityManagerGenLabel(priv->secondary, vm) < 0) + rc = -1; +#endif + + return rc; +} + + +static int +virSecurityStackReleaseLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerReleaseLabel(priv->primary, vm) < 0) + rc = -1; +#if 0 + if (virSecurityManagerReleaseLabel(priv->secondary, vm) < 0) + rc = -1; +#endif + + return rc; +} + + +static int +virSecurityStackReserveLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerReserveLabel(priv->primary, vm) < 0) + rc = -1; +#if 0 + if (virSecurityManagerReserveLabel(priv->secondary, vm) < 0) + rc = -1; +#endif + + return rc; +} + + +static int +virSecurityStackSetSecurityImageLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerSetImageLabel(priv->secondary, vm, disk) < 0) + rc = -1; + if (virSecurityManagerSetImageLabel(priv->primary, vm, disk) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackRestoreSecurityImageLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainDiskDefPtr disk) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerRestoreImageLabel(priv->secondary, vm, disk) < 0) + rc = -1; + if (virSecurityManagerRestoreImageLabel(priv->primary, vm, disk) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackSetSecurityHostdevLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainHostdevDefPtr dev) + +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerSetHostdevLabel(priv->secondary, vm, dev) < 0) + rc = -1; + if (virSecurityManagerSetHostdevLabel(priv->primary, vm, dev) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackRestoreSecurityHostdevLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virDomainHostdevDefPtr dev) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerRestoreHostdevLabel(priv->secondary, vm, dev) < 0) + rc = -1; + if (virSecurityManagerRestoreHostdevLabel(priv->primary, vm, dev) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackSetSecurityAllLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + const char *stdin_path) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerSetAllLabel(priv->secondary, vm, stdin_path) < 0) + rc = -1; + if (virSecurityManagerSetAllLabel(priv->primary, vm, stdin_path) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackRestoreSecurityAllLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + int migrated) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerRestoreAllLabel(priv->secondary, vm, migrated) < 0) + rc = -1; + if (virSecurityManagerRestoreAllLabel(priv->primary, vm, migrated) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackSetSavedStateLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + const char *savefile) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerSetSavedStateLabel(priv->secondary, vm, savefile) < 0) + rc = -1; + if (virSecurityManagerSetSavedStateLabel(priv->primary, vm, savefile) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackRestoreSavedStateLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + const char *savefile) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerRestoreSavedStateLabel(priv->secondary, vm, savefile) < 0) + rc = -1; + if (virSecurityManagerRestoreSavedStateLabel(priv->primary, vm, savefile) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackSetProcessLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerSetProcessLabel(priv->secondary, vm) < 0) + rc = -1; + if (virSecurityManagerSetProcessLabel(priv->primary, vm) < 0) + rc = -1; + + return rc; +} + +static int +virSecurityStackGetProcessLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm, + virSecurityLabelPtr seclabel) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + +#if 0 + if (virSecurityManagerGetProcessLabel(priv->secondary, vm, seclabel) < 0) + rc = -1; +#endif + if (virSecurityManagerGetProcessLabel(priv->primary, vm, seclabel) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackSetSocketLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerSetSocketLabel(priv->secondary, vm) < 0) + rc = -1; + if (virSecurityManagerSetSocketLabel(priv->primary, vm) < 0) + rc = -1; + + return rc; +} + + +static int +virSecurityStackClearSocketLabel(virSecurityManagerPtr mgr, + virDomainObjPtr vm) +{ + virSecurityStackDataPtr priv = virSecurityManagerGetPrivateData(mgr); + int rc = 0; + + if (virSecurityManagerClearSocketLabel(priv->secondary, vm) < 0) + rc = -1; + if (virSecurityManagerClearSocketLabel(priv->primary, vm) < 0) + rc = -1; + + return rc; +} + + +virSecurityDriver virSecurityDriverStack = { + sizeof(virSecurityStackData), + "stack", + virSecurityStackProbe, + virSecurityStackOpen, + virSecurityStackClose, + + virSecurityStackGetModel, + virSecurityStackGetDOI, + + virSecurityStackVerify, + + virSecurityStackSetSecurityImageLabel, + virSecurityStackRestoreSecurityImageLabel, + + virSecurityStackSetSocketLabel, + virSecurityStackClearSocketLabel, + + virSecurityStackGenLabel, + virSecurityStackReserveLabel, + virSecurityStackReleaseLabel, + + virSecurityStackGetProcessLabel, + virSecurityStackSetProcessLabel, + + virSecurityStackSetSecurityAllLabel, + virSecurityStackRestoreSecurityAllLabel, + + virSecurityStackSetSecurityHostdevLabel, + virSecurityStackRestoreSecurityHostdevLabel, + + virSecurityStackSetSavedStateLabel, + virSecurityStackRestoreSavedStateLabel, +}; diff --git a/src/security/security_stack.h b/src/security/security_stack.h new file mode 100644 index 0000000..c924842 --- /dev/null +++ b/src/security/security_stack.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2010 Red Hat, Inc. + * + * 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. + * + * QEMU stacked security driver + */ + +#include "security_driver.h" + +#ifndef __VIR_SECURITY_STACK +# define __VIR_SECURITY_STACK + +extern virSecurityDriver virSecurityDriverStack; + +void virSecurityStackSetPrimary(virSecurityManagerPtr mgr, + virSecurityManagerPtr primary); +void virSecurityStackSetSecondary(virSecurityManagerPtr mgr, + virSecurityManagerPtr secondary); + +#endif /* __VIR_SECURITY_DAC */ -- 1.7.2.3

On 11/22/2010 11:09 AM, Daniel P. Berrange wrote:
The current security driver usage requires horrible code like
if (driver->securityDriver && driver->securityDriver->domainSetSecurityHostdevLabel && driver->securityDriver->domainSetSecurityHostdevLabel(driver->securityDriver, vm, hostdev) < 0)
This pair of checks for NULL clutters up the code, making the driver calls 2 lines longer than they really need to be. The goal of the patchset is to change the calling convention to simply
if (virSecurityManagerSetHostdevLabel(driver->securityDriver, vm, hostdev) < 0)
The first check for 'driver->securityDriver' being NULL is removed by introducing a 'no op' security driver that will always be present if no real driver is enabled. This guarentees driver->securityDriver
s/guarentees/guarantees/
!= NULL.
The second check for 'driver->securityDriver->domainSetSecurityHostdevLabel' being non-NULL is hidden in a new abstraction called virSecurityManager. This separates the driver callbacks, from main internal API. The addition of a virSecurityManager object, that is separate from the virSecurityDriver struct also allows for security drivers to carry state / configuration information directly. Thus the DAC/Stack drivers from src/qemu which used to pull config from 'struct qemud_driver' can now be moved into the 'src/security' directory and store their config directly.
* src/qemu/qemu_conf.h, src/qemu/qemu_driver.c: Update to use new virSecurityManager APIs * src/qemu/qemu_security_dac.c, src/qemu/qemu_security_dac.h src/qemu/qemu_security_stacked.c, src/qemu/qemu_security_stacked.h: Move into src/security directory * src/security/security_stack.c, src/security/security_stack.h, src/security/security_dac.c, src/security/security_dac.h: Generic versions of previous QEMU specific drivers * src/security/security_apparmor.c, src/security/security_apparmor.h, src/security/security_driver.c, src/security/security_driver.h, src/security/security_selinux.c, src/security/security_selinux.h: Update to take virSecurityManagerPtr object as the first param in all callbacks * src/security/security_nop.c, src/security/security_nop.h: Stub implementation of all security driver APIs. * src/security/security_manager.h, src/security/security_manager.c: New internal API for invoking security drivers
Sounds okay, I don't know whether this would have been possible to break up into smaller incremental patches.
src/security/security_stack.h | 24 ++ 22 files changed, 2079 insertions(+), 1482 deletions(-)
One huge patch. And using 'git diff -C' didn't make it much smaller (only security_nop.h was detected as a 51% rename; I was hoping that it would have recognized more of the files as a rename to see just the diff caused by the rename). Oh well, I'll start my review anyway. At least it passes the smoke test of applying it followed by 'make'. It fails four tests of 'make syntax-check': libvirt_unmarked_diagnostics src/security/security_driver.c-69- "Security driver %s not found", NULLSTR(name)); maint.mk: found unmarked diagnostic(s) +++ po/POTFILES.in @@ -56,11 +56,10 @@ src/qemu/qemu_monitor.c src/qemu/qemu_monitor_json.c src/qemu/qemu_monitor_text.c -src/qemu/qemu_security_dac.c src/remote/remote_driver.c src/secret/secret_driver.c src/security/security_apparmor.c -src/security/security_driver.c +src/security/security_dac.c preprocessor_indentation cppi: src/security/security_apparmor.h: line 17: not properly indented cppi: src/security/security_manager.h: line 3: not properly indented cppi: src/security/security_nop.h: line 13: not properly indented maint.mk: incorrect preprocessor indentation prohibit_empty_lines_at_EOF src/security/security_driver.c src/security/security_manager.c maint.mk: the above files end with empty line(s) It also fails 'make check' (the testsuite needs updating to use the new headers): seclabeltest.c: In function 'main': seclabeltest.c:18:5: error: implicit declaration of function 'virSecurityDriverStartup' [-Wimplicit-function-declaration] seclabeltest.c:18:5: error: nested extern declaration of 'virSecurityDriverStartup' [-Wnested-externs] seclabeltest.c:28:13: error: expected expression before 'virSecurityDriverGetModel' seclabeltest.c:36:11: error: expected expression before 'virSecurityDriverGetDOI'
+++ b/src/qemu/qemu_driver.c deleted file mode 100644 index 55dc0c6..0000000 --- a/src/qemu/qemu_security_dac.c
I checked this file and its new counterpart security/security_dac.c. Looks like a faithful rename...
-qemuSecurityDACSetSecurityAllLabel(virSecurityDriverPtr drv, - virDomainObjPtr vm, - const char *stdin_path ATTRIBUTE_UNUSED) -{ - int i; - - if (!driver->privileged || !driver->dynamicOwnership) - return 0; - - for (i = 0 ; i < vm->def->ndisks ; i++) { - /* XXX fixme - we need to recursively label the entriy tree :-( */
...including the typo (s/entriy/entire/ if you'd like).
diff --git a/src/qemu/qemu_security_stacked.c b/src/qemu/qemu_security_stacked.c
And my review stops here for now (I'm out of time today). -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Define the basic framework lock manager plugins. The basic plugin API for 3rd parties to implemented is defined in include/libvirt/plugins/lock_manager.h The libvirt code for loading & calling into plugins is in src/locking/lock_manager.{c,h} To allow QEMU driver code to assume that there is always a lock manager present, a simple 'nop' implementation is provided and linked in so that it is guarenteed to be available. This impl returns success for everything, so it should be no functional change vs previous libvirt releases. * configure.ac: Add include/libvirt/plugins/Makefile * include/libvirt/Makefile.am: Add plugins sub directory * include/libvirt/plugins/Makefile.am: Install plugin headers * include/libvirt/plugins/lock_manager.h: Public API for implementing plugins * include/libvirt/virterror.h, src/util/virterror.c: Add VIR_FROM_LOCKING * src/locking/lock_manager.c, src/locking/lock_manager.h: Internal API for managing locking * src/locking/lock_manager_nop.c, src/locking/lock_manager_nop.h: A lock manager that always succeeds * src/locking/lock_manager_nop.h, src/locking/domain_lock.c: Higher level API targetted at locking virDomainObj instances. * src/Makefile.am: Add locking code --- configure.ac | 1 + include/libvirt/Makefile.am | 2 + include/libvirt/plugins/Makefile.am | 3 + include/libvirt/plugins/lock_manager.h | 356 ++++++++++++++++++++++++++++++++ include/libvirt/virterror.h | 1 + po/POTFILES.in | 1 + src/Makefile.am | 5 +- src/libvirt_private.syms | 31 +++ src/locking/README | 158 ++++++++++++++ src/locking/domain_lock.c | 343 ++++++++++++++++++++++++++++++ src/locking/domain_lock.h | 50 +++++ src/locking/lock_manager.c | 348 +++++++++++++++++++++++++++++++ src/locking/lock_manager.h | 77 +++++++ src/locking/lock_manager_nop.c | 159 ++++++++++++++ src/locking/lock_manager_nop.h | 4 + src/util/virterror.c | 3 + 16 files changed, 1541 insertions(+), 1 deletions(-) create mode 100644 include/libvirt/plugins/Makefile.am create mode 100644 include/libvirt/plugins/lock_manager.h create mode 100644 src/locking/README create mode 100644 src/locking/domain_lock.c create mode 100644 src/locking/domain_lock.h create mode 100644 src/locking/lock_manager.c create mode 100644 src/locking/lock_manager.h create mode 100644 src/locking/lock_manager_nop.c create mode 100644 src/locking/lock_manager_nop.h diff --git a/configure.ac b/configure.ac index d21d558..b912a7a 100644 --- a/configure.ac +++ b/configure.ac @@ -2245,6 +2245,7 @@ AC_OUTPUT(Makefile src/Makefile include/Makefile docs/Makefile \ libvirt.pc libvirt.spec mingw32-libvirt.spec \ po/Makefile.in \ include/libvirt/Makefile include/libvirt/libvirt.h \ + include/libvirt/plugins/Makefile \ python/Makefile python/tests/Makefile \ daemon/Makefile \ tools/Makefile \ diff --git a/include/libvirt/Makefile.am b/include/libvirt/Makefile.am index b2c2b76..1795254 100644 --- a/include/libvirt/Makefile.am +++ b/include/libvirt/Makefile.am @@ -6,6 +6,8 @@ virinc_HEADERS = libvirt.h \ libvirt-qemu.h \ virterror.h +SUBDIRS = plugins + install-exec-hook: $(mkinstalldirs) $(DESTDIR)$(virincdir) diff --git a/include/libvirt/plugins/Makefile.am b/include/libvirt/plugins/Makefile.am new file mode 100644 index 0000000..0626d74 --- /dev/null +++ b/include/libvirt/plugins/Makefile.am @@ -0,0 +1,3 @@ + +pluginincdir = $(includedir)/libvirt/plugins +plugininc_HEADERS = lock_manager.h diff --git a/include/libvirt/plugins/lock_manager.h b/include/libvirt/plugins/lock_manager.h new file mode 100644 index 0000000..c22621b --- /dev/null +++ b/include/libvirt/plugins/lock_manager.h @@ -0,0 +1,356 @@ +/* + * lock_manager.h: Defines the lock manager plugin API + * + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 + * + */ + +#ifndef __VIR_PLUGINS_LOCK_MANAGER_H__ +# define __VIR_PLUGINS_LOCK_MANAGER_H__ + +#include <stdarg.h> + +typedef struct _virLockManager virLockManager; +typedef virLockManager *virLockManagerPtr; + +typedef struct _virLockDriver virLockDriver; +typedef virLockDriver *virLockDriverPtr; + +enum { + /* The managed object is a virtual guest domain */ + VIR_LOCK_MANAGER_OBJECT_TYPE_DOMAIN = 0, +} virLockManagerObjectType; + +/* + * Flags to pass to 'load_drv' and also 'new_drv' method + * Plugins must support at least one of the modes. If a + * mode is unsupported, it must return an error + */ +enum { + VIR_LOCK_MANAGER_MODE_CONTENT = (1 << 0), + VIR_LOCK_MANAGER_MODE_METADATA = (1 << 1), +} virLockManagerFlags; + +enum { + /* The resource to be locked is a virtual disk */ + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK = 0, +} virLockManagerResourceType; + +typedef enum { + /* The resource is assigned in readonly mode */ + VIR_LOCK_MANAGER_RESOURCE_READONLY = (1 << 0), + /* The resource is assigned in shared, writable mode */ + VIR_LOCK_MANAGER_RESOURCE_SHARED = (1 << 1), +} virLockManagerResourceFlags; + +/* + * Changes in major version denote incompatible ABI changes + * Changes in minor version denote new compatible API entry points + * Changes in micro version denote new compatible flags + */ +# define VIR_LOCK_MANAGER_VERSION_MAJOR 1 +# define VIR_LOCK_MANAGER_VERSION_MINOR 0 +# define VIR_LOCK_MANAGER_VERSION_MICRO 0 + +# define VIR_LOCK_MANAGER_VERSION \ + ((VIR_LOCK_MANAGER_VERSION_MAJOR * 1000 * 1000) + \ + (VIR_LOCK_MANAGER_VERSION_MINOR * 1000) + \ + (VIR_LOCK_MANAGER_VERSION_MICRO)) + + +typedef void (*virLockDriverError)(const char *fmt, ...); +typedef void (*virLockDriverErrorV)(const char *fmt, va_list args); + +/** + * virLockDriverInit: + * @version: the libvirt requested plugin ABI version + * @flags: the libvirt requested plugin optional extras + * + * Allow the plugin to validate the libvirt requested + * plugin version / flags. This allows the plugin impl + * to block its use in versions of libvirtd which are + * too old to support key features. + * + * NB: A plugin may be loaded multiple times, for different + * libvirt drivers (eg QEMU, LXC, UML) + * + * Returns -1 if the requested version/flags were inadequate + */ +typedef int (*virLockDriverInit)(unsigned int version, + unsigned int flags); + +/** + * virLockDriverDeinit: + * + * Called to release any resources prior to the plugin + * being unloaded from memory. Returns -1 to prevent + * plugin from being unloaded from memory. + */ +typedef int (*virLockDriverDeinit)(void); + +/** + * virLockManagerNew: + * @man: the lock manager context + * @type: the type of process to be supervised + * @flags: optional flags, currently unused + * + * Initialize a new context to supervise a process, usually + * a virtual machine. If the lock driver requested a + * private data, the <code>privateData</code> field of + * <code>man</code> will be initialized with a suitable + * pointer. + * + * Returns 0 if successful initialized a new context, -1 on error + */ +typedef int (*virLockDriverNew)(virLockManagerPtr man, + unsigned int type, + unsigned int flags); + +/** + * virLockDriverFree: + * @manager: the lock manager context + * + * Release any resources associated with the lock manager + * context private data + */ +typedef void (*virLockDriverFree)(virLockManagerPtr man); + +/** + * virLockDriverAddResource: + * @manager: the lock manager context + * @type: the resource type virLockManagerResourceType + * @name: the resource name + * @flags: the resource access flags + * + * Assign a resource to a managed object. This will + * only be called prior to the object is being locked + * when it is inactive. eg, to set the initial boot + * time disk assignments on a VM + * The format of @name varies according to + * the resource @type. A VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK + * will have a fully qualified file path. + * + * If no flags are given, the resource is assumed to be + * used in exclusive, read-write mode. Access can be + * relaxed to readonly, or shared read-write. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAddResource)(virLockManagerPtr man, + unsigned int type, + const char *name, + unsigned int flags); + +/** + * virLockDriverSetParameter: + * @manager: the lock manager context + * @key: the parameter name + * @value: the parameter value + * + * Set a configuration parameter for the managed process. + * A process of VIR_LOCK_MANAGER_START_DOMAIN will be + * given at least 3 parameters: + * + * - id: the domain unique id + * - uuid: the domain uuid + * - name: the domain name + * + * There may be other parameters specific to the lock manager + * plugin that are provided for the managed process + * + * This should only be called prior to the supervised process + * being started. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverSetParameter)(virLockManagerPtr man, + const char *key, + const char *value); + +/** + * virLockDriverAcquireObject: + * @manager: the lock manager context + * @state: the current lock state + * @flags: optional flags, currently unused + * + * Start managing resources for the object. If the + * object is being transferred from another location + * the current lock state may be passed in. This + * must be called from the PID that represents the + * object to be managed. If the lock is lost at any + * time, the PID will be killed off by the lock manager. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAcquireObject)(virLockManagerPtr man, + const char *state, + unsigned int flags); + +/** + * virLockDriverAttachObject: + * @manager: the lock manager context + * @pid: the managed object PID + * @flags: optional flags, currently unused + * + * Re-attach to an existing lock manager instance managing + * PID @pid. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAttachObject)(virLockManagerPtr man, + pid_t pid, + unsigned int flags); + +/** + * virLockDriverDetachObject: + * @manager: the lock manager context + * @pid: the managed object PID + * @flags: optional flags, currently unused + * + * Deattach from an existing lock manager instance managing + * PID @pid. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverDetachObject)(virLockManagerPtr man, + pid_t pid, + unsigned int flags); + +/** + * virLockDriverReleaseObject: + * @manager: the lock manager context + * @flags: optional flags + * + * Inform the lock manager that the supervised process has + * been, or can be stopped. This can must be called from + * the same context as the previous virLockDriverAttachObject + * or virLockDriverAcquireObject call. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverReleaseObject)(virLockManagerPtr man, + unsigned int flags); + +/** + * virLockDriverGetState: + * @manager: the lock manager context + * @state: pointer to be filled with lock state + * @flags: optional flags, currently unused + * + * Retrieve the current lock state. The returned + * lock state may be NULL if none is required. The + * caller is responsible for freeing the lock + * state string when it is no longer required + * + * Returns 0 on success, or -1 on failure. + */ +typedef int (*virLockDriverGetState)(virLockManagerPtr man, + char **state, + unsigned int flags); + +/** + * virLockDriverAcquireResource: + * @manager: the lock manager context + * @type: the resource type virLockDriverResourceType + * @name: the resource name + * @flags: the resource access flags + * + * Assign a resource to a managed object. This will + * only be called when the object is already locked + * and active. eg, to hotplug a disk into a VM. + * The format of @name varies according to + * the resource @type. A VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK + * will have a fully qualified file path. + * + * If no flags are given, the resource is assumed to be + * used in exclusive, read-write mode. Access can be + * relaxed to readonly, or shared read-write. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAcquireResource)(virLockManagerPtr man, + unsigned int type, + const char *name, + unsigned int flags); + +/** + * virLockDriverReleaseResource: + * @manager: the lock manager context + * @type: the resource type virLockDriverResourceType + * @name: the resource name + * @flags: the resource access flags + * + * Dynamically release a resource for a running process. + * This may only be called after the process has been + * started. The format of @name varies according to + * the resource @type. A VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK + * will have a fully qualified file path. + * + * If no flags are given, the resource is assumed to be + * used in exclusive, read-write mode. Access can be + * relaxed to readonly, or shared read-write. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverReleaseResource)(virLockManagerPtr man, + unsigned int type, + const char *name, + unsigned int flags); + +struct _virLockManager { + virLockDriverPtr driver; + virLockDriverError error; + virLockDriverErrorV errorV; + void *privateData; +}; + +/** + * The plugin must export a static instance of this + * driver table, with the name 'virLockDriverImpl' + */ +struct _virLockDriver { + /** + * @version: the newest implemented plugin ABI version + * @flags: optional flags, currently unused + */ + unsigned int version; + unsigned int flags; + + size_t privateDataLen; + + virLockDriverInit drvInit; + virLockDriverDeinit drvDeinit; + + virLockDriverNew drvNew; + virLockDriverFree drvFree; + + virLockDriverAddResource drvAddResource; + virLockDriverSetParameter drvSetParameter; + + virLockDriverAcquireObject drvAcquireObject; + virLockDriverAttachObject drvAttachObject; + virLockDriverDetachObject drvDetachObject; + virLockDriverReleaseObject drvReleaseObject; + + virLockDriverGetState drvGetState; + + virLockDriverAcquireResource drvAcquireResource; + virLockDriverAcquireResource drvReleaseResource; +}; + + +#endif /* __VIR_PLUGINS_LOCK_MANAGER_H__ */ diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index eaeb477..c60f7bf 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -76,6 +76,7 @@ typedef enum { VIR_FROM_AUDIT, /* Error from auditing subsystem */ VIR_FROM_SYSINFO, /* Error from sysinfo/SMBIOS */ VIR_FROM_STREAMS, /* Error from I/O streams */ + VIR_FROM_LOCKING, /* Error from lock manager */ } virErrorDomain; diff --git a/po/POTFILES.in b/po/POTFILES.in index 2820ac1..75c259c 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -30,6 +30,7 @@ src/fdstream.c src/interface/netcf_driver.c src/internal.h src/libvirt.c +src/locking/lock_manager.c src/lxc/lxc_container.c src/lxc/lxc_conf.c src/lxc/lxc_controller.c diff --git a/src/Makefile.am b/src/Makefile.am index 66af9cf..750612f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -89,7 +89,10 @@ DRIVER_SOURCES = \ datatypes.c datatypes.h \ fdstream.c fdstream.h \ $(NODE_INFO_SOURCES) \ - libvirt.c libvirt_internal.h + libvirt.c libvirt_internal.h \ + locking/lock_manager.c locking/lock_manager.h \ + locking/lock_manager_nop.c locking/lock_manager_nop.h \ + locking/domain_lock.c locking/domain_lock.h # XML configuration format handling sources diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3af0210..e808375 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -312,6 +312,19 @@ virDomainEventWatchdogNewFromDom; virDomainEventWatchdogNewFromObj; +# domain_lock.h +virDomainLockFree; +virDomainLockForExec; +virDomainLockForStartup; +virDomainLockForShutdown; +virDomainLockForModify; +virDomainLockBeginDiskAttach; +virDomainLockBeginDiskDetach; +virDomainLockEndDiskAttach; +virDomainLockEndDiskDetach; +virDomainLockReleaseAndFree; + + # domain_nwfilter.h virDomainConfNWFilterInstantiate; virDomainConfNWFilterRegister; @@ -479,6 +492,24 @@ virRegisterSecretDriver; virRegisterStorageDriver; +# locking.h +virLockManagerAcquireObject; +virLockManagerAcquireResource; +virLockManagerAddResource; +virLockManagerAttachObject; +virLockManagerDetachObject; +virLockManagerFree; +virLockManagerGetState; +virLockManagerNew; +virLockManagerPluginNew; +virLockManagerPluginRef; +virLockManagerPluginUnref; +virLockManagerReleaseObject; +virLockManagerReleaseResource; +virLockManagerSetParameter; +virLockManagerStartup; + + # logging.h virLogDefineFilter; virLogDefineOutput; diff --git a/src/locking/README b/src/locking/README new file mode 100644 index 0000000..4fa4f89 --- /dev/null +++ b/src/locking/README @@ -0,0 +1,158 @@ + +At libvirtd startup: + + plugin = virLockManagerPluginLoad("sync-manager"); + + +At libvirtd shtudown: + + virLockManagerPluginUnload(plugin) + + +At guest startup: + + manager = virLockManagerNew(plugin, + VIR_LOCK_MANAGER_OBJECT_DOMAIN, + 0); + + virLockManagerSetParameter(manager, "id", id); + virLockManagerSetParameter(manager, "uuid", uuid); + virLockManagerSetParameter(manager, "name", name); + + foreach disk + virLockManagerRegisterResource(manager, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk.path, + ..flags...); + + if (!virLockManagerAcquireObject(manager)) + abort.. + + run QEMU + + +At guest shutdown: + + ...send QEMU 'quit' monitor command, and/or kill(qemupid)... + + if (!virLockManagerShutdown(manager)) + kill(supervisorpid); /* XXX or leave it running ??? */ + + virLockManagerFree(manager); + + + +At libvirtd restart with running guests: + + foreach still running guest + manager = virLockManagerNew(driver, + VIR_LOCK_MANAGER_START_DOMAIN, + VIR_LOCK_MANAGER_NEW_ATTACH); + virLockManagerSetParameter(manager, "id", id); + virLockManagerSetParameter(manager, "uuid", uuid); + virLockManagerSetParameter(manager, "name", name); + + if (!virLockManagerGetChild(manager, &qemupid)) + kill(supervisorpid); /* XXX or leave it running ??? */ + + + +With disk hotplug: + + if (virLockManagerAcquireResource(manager, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk.path + ..flags..)) + ...abort hotplug attempt ... + + ...hotplug the device... + + + +With disk unhotplug: + + ...hotunplug the device... + + if (virLockManagerReleaseResource(manager, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk.path + ..flags..)) + ...log warning ... + + + +During migration: + + 1. On source host + + if (!virLockManagerPrepareMigrate(manager, hosturi)) + ..don't start migration.. + + 2. On dest host + + manager = virLockManagerNew(driver, + VIR_LOCK_MANAGER_START_DOMAIN, + VIR_LOCK_MANAGER_NEW_MIGRATE); + virLockManagerSetParameter(manager, "id", id); + virLockManagerSetParameter(manager, "uuid", uuid); + virLockManagerSetParameter(manager, "name", name); + + foreach disk + virLockManagerRegisterResource(manager, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk.path, + ..flags...); + + char **supervisorargv; + int supervisorargc; + + supervisor = virLockManagerGetSupervisorPath(manager); + virLockManagerGetSupervisorArgs(&argv, &argc); + + cmd = qemuBuildCommandLine(supervisor, supervisorargv, supervisorargv); + + supervisorpid = virCommandExec(cmd); + + if (!virLockManagerGetChild(manager, &qemupid)) + kill(supervisorpid); /* XXX or leave it running ??? */ + + 3. Initiate migration in QEMU on source and wait for completion + + 4a. On failure + + 4a1 On target + + virLockManagerCompleteMigrateIn(manager, + VIR_LOCK_MANAGER_MIGRATE_CANCEL); + virLockManagerShutdown(manager); + virLockManagerFree(manager); + + 4a2 On source + + virLockManagerCompleteMigrateIn(manager, + VIR_LOCK_MANAGER_MIGRATE_CANCEL); + + 4b. On succcess + + + 4b1 On target + + virLockManagerCompleteMigrateIn(manager, 0); + + 42 On source + + virLockManagerCompleteMigrateIn(manager, 0); + virLockManagerShutdown(manager); + virLockManagerFree(manager); + + +Notes: + + - If a lock manager impl does just VM level leases, it can + ignore all the resource paths at startup. + + - If a lock manager impl does not support migrate + it can return an error from all migrate calls + + - If a lock manger impl does not support hotplug + it can return an error from all resource acquire/release calls diff --git a/src/locking/domain_lock.c b/src/locking/domain_lock.c new file mode 100644 index 0000000..086ab4d --- /dev/null +++ b/src/locking/domain_lock.c @@ -0,0 +1,343 @@ + +#include <config.h> + +#include "domain_lock.h" +#include "memory.h" +#include "uuid.h" +#include "virterror_internal.h" + +#define VIR_FROM_THIS VIR_FROM_LOCKING + +struct _virDomainLock { + virLockManagerPtr contentLock; + virLockManagerPtr metadataLock; + + pid_t pid; + + bool releaseContentLock; + bool releaseMetadataLock; +}; + +static virLockManagerPtr virDomainLockNewManager(virLockManagerPluginPtr plugin, + virDomainObjPtr vm, + const char *state, + int mode, + bool acquireLock) +{ + virLockManagerPtr lock; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + int i; + + if (!(lock = virLockManagerNew(plugin, + mode, + VIR_LOCK_MANAGER_OBJECT_TYPE_DOMAIN))) + return NULL; + + virUUIDFormat(vm->def->uuid, uuidstr); + if (virLockManagerSetParameter(lock, + "uuid", uuidstr) < 0) + goto error; + if (virLockManagerSetParameter(lock, + "name", vm->def->name) < 0) + goto error; + + if (acquireLock) { + for (i = 0 ; i < vm->def->ndisks ; i++) { + virDomainDiskDefPtr disk = vm->def->disks[i]; + unsigned int diskFlags = 0; + if (!disk->src) + continue; + + if (!(disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK || + disk->type == VIR_DOMAIN_DISK_TYPE_FILE || + disk->type == VIR_DOMAIN_DISK_TYPE_DIR)) + continue; + + if (disk->readonly) + diskFlags |= VIR_LOCK_MANAGER_RESOURCE_READONLY; + if (disk->shared) + diskFlags |= VIR_LOCK_MANAGER_RESOURCE_SHARED; + + if (virLockManagerAddResource(lock, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk->src, + diskFlags) < 0) { + virLockManagerFree(lock); + return NULL; + } + } + + if (virLockManagerAcquireObject(lock, state, 0) < 0) + goto error; + } else { + if (virLockManagerAttachObject(lock, vm->pid, 0) < 0) + goto error; + } + + return lock; + +error: + virLockManagerFree(lock); + return NULL; +} + + +static virDomainLockPtr virDomainLockNew(virLockManagerPluginPtr contentLockPlugin, + virLockManagerPluginPtr metadataLockPlugin, + virDomainObjPtr dom, + const char *contentState, + const char *metadataState, + bool acquireContentLock, + bool acquireMetadataLock) +{ + virDomainLockPtr lock; + + if (VIR_ALLOC(lock) < 0) { + virReportOOMError(); + goto error; + } + + if (contentLockPlugin && + !(lock->contentLock = virDomainLockNewManager(contentLockPlugin, + dom, + contentState, + VIR_LOCK_MANAGER_MODE_CONTENT, + acquireContentLock))) + goto error; + + lock->releaseContentLock = acquireContentLock; + + if (metadataLockPlugin && + !(lock->metadataLock = virDomainLockNewManager(metadataLockPlugin, + dom, + metadataState, + VIR_LOCK_MANAGER_MODE_METADATA, + acquireMetadataLock))) + goto error; + + lock->releaseMetadataLock = acquireMetadataLock; + + lock->pid = dom->pid; + + return lock; + +error: + virDomainLockFree(lock); + return NULL; +} + + +virDomainLockPtr virDomainLockForExec(virLockManagerPluginPtr contentLockPlugin, + const char *contentState, + virDomainObjPtr dom) +{ + return virDomainLockNew(contentLockPlugin, + NULL, + dom, + contentState, + NULL, + true, + false); +} + +virDomainLockPtr virDomainLockForStartup(virLockManagerPluginPtr contentLockPlugin, + virLockManagerPluginPtr metadataLockPlugin, + const char *metadataState, + virDomainObjPtr dom) +{ + return virDomainLockNew(contentLockPlugin, + metadataLockPlugin, + dom, + NULL, + metadataState, + false, + true); +} + +virDomainLockPtr virDomainLockForShutdown(virLockManagerPluginPtr contentLockPlugin, + virLockManagerPluginPtr metadataLockPlugin, + virDomainObjPtr dom) +{ + return virDomainLockNew(contentLockPlugin, + metadataLockPlugin, + dom, + NULL, + NULL, + true, + true); +} + +virDomainLockPtr virDomainLockForModify(virLockManagerPluginPtr contentLockPlugin, + virLockManagerPluginPtr metadataLockPlugin, + virDomainObjPtr dom) +{ + return virDomainLockNew(contentLockPlugin, + metadataLockPlugin, + dom, + NULL, + NULL, + false, + false); +} + + +static int virDomainLockDiskOperation(virDomainLockPtr lock, + virDomainDiskDefPtr disk, + bool isBegin, + bool isAttach) +{ + unsigned int diskFlags = 0; + if (!disk->src) + return 0; + + if (!(disk->type == VIR_DOMAIN_DISK_TYPE_BLOCK || + disk->type == VIR_DOMAIN_DISK_TYPE_FILE || + disk->type == VIR_DOMAIN_DISK_TYPE_DIR)) + return 0; + + if (disk->readonly) + diskFlags |= VIR_LOCK_MANAGER_RESOURCE_READONLY; + if (disk->shared) + diskFlags |= VIR_LOCK_MANAGER_RESOURCE_SHARED; + + if (isAttach) { + if (isBegin) { + if (lock->contentLock && + virLockManagerAcquireResource(lock->contentLock, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk->src, + diskFlags) < 0) + return -1; + + if (lock->metadataLock && + virLockManagerAcquireResource(lock->metadataLock, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk->src, + diskFlags) < 0) { + virLockManagerReleaseResource(lock->contentLock, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk->src, + diskFlags); + return -1; + } + } else { + if (lock->metadataLock && + virLockManagerReleaseResource(lock->metadataLock, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk->src, + diskFlags) < 0) + return -1; + } + } else { + if (isBegin) { + if (lock->metadataLock && + virLockManagerAcquireResource(lock->metadataLock, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk->src, + diskFlags) < 0) { + return -1; + } + } else { + if (lock->metadataLock && + virLockManagerReleaseResource(lock->metadataLock, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk->src, + diskFlags) < 0) + return -1; + + if (lock->contentLock && + virLockManagerReleaseResource(lock->contentLock, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + disk->src, + diskFlags) < 0) + return -1; + + } + } + + return 0; +} + +int virDomainLockBeginDiskAttach(virDomainLockPtr lock, + virDomainDiskDefPtr disk) +{ + return virDomainLockDiskOperation(lock, disk, true, true); +} + +int virDomainLockEndDiskAttach(virDomainLockPtr lock, + virDomainDiskDefPtr disk) +{ + return virDomainLockDiskOperation(lock, disk, false, true); +} + + +int virDomainLockBeginDiskDetach(virDomainLockPtr lock, + virDomainDiskDefPtr disk) +{ + return virDomainLockDiskOperation(lock, disk, true, false); +} + + +int virDomainLockEndDiskDetach(virDomainLockPtr lock, + virDomainDiskDefPtr disk) +{ + return virDomainLockDiskOperation(lock, disk, false, false); +} + + +void virDomainLockFree(virDomainLockPtr lock) +{ + if (!lock) + return; + + virLockManagerFree(lock->metadataLock); + virLockManagerFree(lock->contentLock); + + VIR_FREE(lock); +} + +void virDomainLockReleaseAndFree(virDomainLockPtr lock) +{ + if (!lock) + return; + + if (lock->metadataLock) { + if (lock->releaseMetadataLock) + virLockManagerReleaseObject(lock->metadataLock, 0); + else + virLockManagerDetachObject(lock->metadataLock, lock->pid, 0); + } + + if (lock->contentLock) { + if (lock->releaseContentLock) + virLockManagerReleaseObject(lock->contentLock, 0); + else + virLockManagerDetachObject(lock->contentLock, lock->pid, 0); + } + + virDomainLockFree(lock); +} + +int virDomainLockGetState(virDomainLockPtr lock, + char **contentState, + char **metadataState, + unsigned int flags) +{ + *contentState = NULL; + *metadataState = NULL; + + if (lock->contentLock) { + if (virLockManagerGetState(lock->contentLock, contentState, flags) < 0) + return -1; + } + + if (lock->metadataLock) { + if (virLockManagerGetState(lock->metadataLock, metadataState, flags) < 0) { + VIR_FREE(*contentState); + return -1; + } + } + + return 0; +} + diff --git a/src/locking/domain_lock.h b/src/locking/domain_lock.h new file mode 100644 index 0000000..923410e --- /dev/null +++ b/src/locking/domain_lock.h @@ -0,0 +1,50 @@ + +#include "internal.h" +#include "domain_conf.h" +#include "lock_manager.h" + +#ifndef __VIR_DOMAIN_LOCK_H__ +# define __VIR_DOMAIN_LOCK_H__ + +typedef struct _virDomainLock virDomainLock; +typedef virDomainLock *virDomainLockPtr; + +virDomainLockPtr virDomainLockForExec(virLockManagerPluginPtr contentLockPlugin, + const char *state, + virDomainObjPtr dom); + +virDomainLockPtr virDomainLockForStartup(virLockManagerPluginPtr contentLockPlugin, + virLockManagerPluginPtr metadataLockPlugin, + const char *state, + virDomainObjPtr dom); + +virDomainLockPtr virDomainLockForShutdown(virLockManagerPluginPtr contentLockPlugin, + virLockManagerPluginPtr metadataLockPlugin, + virDomainObjPtr dom); + +virDomainLockPtr virDomainLockForModify(virLockManagerPluginPtr contentLockPlugin, + virLockManagerPluginPtr metadataLockPlugin, + virDomainObjPtr dom); + +int virDomainLockBeginDiskAttach(virDomainLockPtr dom, + virDomainDiskDefPtr disk); + +int virDomainLockEndDiskAttach(virDomainLockPtr dom, + virDomainDiskDefPtr disk); + +int virDomainLockBeginDiskDetach(virDomainLockPtr dom, + virDomainDiskDefPtr disk); + +int virDomainLockEndDiskDetach(virDomainLockPtr dom, + virDomainDiskDefPtr disk); + +void virDomainLockFree(virDomainLockPtr lock); +void virDomainLockReleaseAndFree(virDomainLockPtr lock); + +int virDomainLockGetState(virDomainLockPtr manager, + char **contentState, + char **metadataState, + unsigned int flags); + + +#endif /* __VIR_DOMAIN_LOCK_H__ */ diff --git a/src/locking/lock_manager.c b/src/locking/lock_manager.c new file mode 100644 index 0000000..dc5e7d8 --- /dev/null +++ b/src/locking/lock_manager.c @@ -0,0 +1,348 @@ +/* + * lock_manager.c: Implements the internal lock manager API + * + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 + * + */ + +#include <config.h> + +#include "lock_manager.h" +#include "lock_manager_nop.h" +#include "virterror_internal.h" +#include "logging.h" +#include "util.h" +#include "memory.h" + +#include <dlfcn.h> +#include <stdlib.h> +#include <unistd.h> + +#define VIR_FROM_THIS VIR_FROM_LOCKING + +#define virLockError(code, ...) \ + virReportErrorHelper(NULL, VIR_FROM_THIS, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +#define CHECK_PLUGIN(field, errret) \ + if (!plugin->driver->field) { \ + virLockError(VIR_ERR_INTERNAL_ERROR, \ + _("Missing '%s' field in lock manager driver"), \ + #field); \ + return errret; \ + } + +#define CHECK_MANAGER(field, errret) \ + if (!manager->driver->field) { \ + virLockError(VIR_ERR_INTERNAL_ERROR, \ + _("Missing '%s' field in lock manager driver"), \ + #field); \ + return errret; \ + } + +struct _virLockManagerPlugin { + virLockDriverPtr driver; + void *handle; + int refs; +}; + +#define DEFAULT_LOCK_MANAGER_PLUGIN_DIR LIBDIR "/libvirt/lock_manager" + +static void virLockManagerErrorV(const char *fmt, va_list args ATTRIBUTE_UNUSED) +{ + /* XXX how to pass va-list to virReportErrorFull ??? */ + virLockError(VIR_ERR_INTERNAL_ERROR, "%s", fmt); +} + + +static void virLockManagerError(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + virLockManagerErrorV(fmt, args); + va_end(args); +} + +/** + * virLockManagerPluginNew: + * @name: the name of the plugin + * @flag: optional plugin flags + * + * Attempt to load the plugin $(libdir)/libvirt/lock_manager/@name.so + * The plugin driver entry point will be resolved & invoked to obtain + * the lock manager driver. + * + * Even if the loading of the plugin succeeded, this may still + * return NULL if the plugin impl decided that we (libvirtd) + * are too old to support a feature it requires + * + * Returns a plugin object, or NULL if loading failed. + */ +virLockManagerPluginPtr virLockManagerPluginNew(const char *name, + unsigned int flags) +{ + void *handle = NULL; + virLockDriverPtr driver; + virLockManagerPluginPtr plugin; + const char *moddir = getenv("LIBVIRT_LOCK_MANAGER_PLUGIN_DIR"); + char *modfile = NULL; + + if (STREQ(name, "nop")) { + driver = &virLockDriverNop; + } else { + if (moddir == NULL) + moddir = DEFAULT_LOCK_MANAGER_PLUGIN_DIR; + + VIR_DEBUG("Module load %s from %s", name, moddir); + + if (virAsprintf(&modfile, "%s/%s.so", moddir, name) < 0) { + virReportOOMError(); + return NULL; + } + + if (access(modfile, R_OK) < 0) { + virReportSystemError(errno, + _("Plugin %s not accessible"), + modfile); + goto cleanup; + } + + handle = dlopen(modfile, RTLD_NOW | RTLD_LOCAL); + if (!handle) { + virLockError(VIR_ERR_SYSTEM_ERROR, + _("Failed to load plugin %s: %s"), + modfile, dlerror()); + goto cleanup; + } + + if (!(driver = dlsym(handle, "virLockDriverImpl"))) { + virLockError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing plugin initialization symbol 'virLockDriverImpl'")); + goto cleanup; + } + } + + if (driver->drvInit(VIR_LOCK_MANAGER_VERSION, flags) < 0) { + virLockError(VIR_ERR_INTERNAL_ERROR, "%s", + _("plugin ABI is not compatible")); + goto cleanup; + } + + if (VIR_ALLOC(plugin) < 0) { + virReportOOMError(); + goto cleanup; + } + + plugin->driver = driver; + plugin->handle = handle; + plugin->refs = 1; + + VIR_FREE(modfile); + return plugin; + +cleanup: + VIR_FREE(modfile); + if (handle) + dlclose(handle); + return NULL; +} + + +/** + * virLockManagerPluginRef: + * @plugin: the plugin implementation to ref + * + * Acquires an additional reference on the plugin. + */ +void virLockManagerPluginRef(virLockManagerPluginPtr plugin) +{ + plugin->refs++; +} + + +/** + * virLockManagerPluginUnref: + * @plugin: the plugin implementation to unref + * + * Releases a reference on the plugin. When the last reference + * is released, it will attempt to unload the plugin from memory. + * The plugin may refuse to allow unloading if this would + * result in an unsafe scenario. + * + */ +void virLockManagerPluginUnref(virLockManagerPluginPtr plugin) +{ + if (!plugin) + return; + + plugin->refs--; + + if (plugin->refs > 0) + return; + + if (plugin->driver->drvDeinit() >= 0) { + if (plugin->handle) + dlclose(plugin->handle); + } else { + VIR_WARN0("Unable to unload lock maanger plugin from memory"); + return; + } + + VIR_FREE(plugin); +} + + +/** + * virLockManagerNew: + * @plugin: the plugin implementation to use + * @type: the type of process to be supervised + * @flags: optional flags, currently unused + * + * Create a new context to supervise a process, usually + * a virtual machine. + * + * Returns a new lock manager context + */ +virLockManagerPtr virLockManagerNew(virLockManagerPluginPtr plugin, + unsigned int type, + unsigned int flags) +{ + virLockManagerPtr lock; + CHECK_PLUGIN(drvNew, NULL); + + if (VIR_ALLOC_VAR(lock, char, plugin->driver->privateDataLen) < 0) { + virReportOOMError(); + return NULL; + } + + lock->driver = plugin->driver; + lock->error = virLockManagerError; + lock->errorV = virLockManagerErrorV; + lock->privateData = plugin->driver->privateDataLen ? lock + sizeof(*lock) : NULL; + + if (plugin->driver->drvNew(lock, type, flags) < 0) { + VIR_FREE(lock); + return NULL; + } + + return lock; +} + + +int virLockManagerAddResource(virLockManagerPtr manager, + unsigned int type, + const char *name, + unsigned int flags) +{ + CHECK_MANAGER(drvAddResource, -1); + + return manager->driver->drvAddResource(manager, type, name, flags); +} + +int virLockManagerAcquireObject(virLockManagerPtr manager, + const char *state, + unsigned int flags) +{ + CHECK_MANAGER(drvAcquireObject, -1); + + return manager->driver->drvAcquireObject(manager, state, flags); +} + + +int virLockManagerAttachObject(virLockManagerPtr manager, + pid_t pid, + unsigned int flags) +{ + CHECK_MANAGER(drvAttachObject, -1); + + return manager->driver->drvAttachObject(manager, pid, flags); +} + + +int virLockManagerDetachObject(virLockManagerPtr manager, + pid_t pid, + unsigned int flags) +{ + CHECK_MANAGER(drvDetachObject, -1); + + return manager->driver->drvDetachObject(manager, pid, flags); +} + + +int virLockManagerReleaseObject(virLockManagerPtr manager, + unsigned int flags) +{ + CHECK_MANAGER(drvReleaseObject, -1); + + return manager->driver->drvReleaseObject(manager, flags); +} + + +int virLockManagerGetState(virLockManagerPtr manager, + char **state, + unsigned int flags) +{ + CHECK_MANAGER(drvGetState, -1); + + return manager->driver->drvGetState(manager, state, flags); +} + + +int virLockManagerSetParameter(virLockManagerPtr manager, + const char *key, + const char *value) +{ + CHECK_MANAGER(drvSetParameter, -1); + + return manager->driver->drvSetParameter(manager, key, value); +} + + +int virLockManagerAcquireResource(virLockManagerPtr manager, + unsigned int type, + const char *name, + unsigned int flags) +{ + CHECK_MANAGER(drvAcquireResource, -1); + + return manager->driver->drvAcquireResource(manager, type, name, flags); +} + + +int virLockManagerReleaseResource(virLockManagerPtr manager, + unsigned int type, + const char *name, + unsigned int flags) +{ + CHECK_MANAGER(drvReleaseResource, -1); + + return manager->driver->drvReleaseResource(manager, type, name, flags); +} + + +int virLockManagerFree(virLockManagerPtr manager) +{ + if (!manager) + return 0; + + CHECK_MANAGER(drvFree, -1); + + manager->driver->drvFree(manager); + + return 0; +} diff --git a/src/locking/lock_manager.h b/src/locking/lock_manager.h new file mode 100644 index 0000000..35d4433 --- /dev/null +++ b/src/locking/lock_manager.h @@ -0,0 +1,77 @@ +/* + * lock_manager.h: Defines the internal lock manager API + * + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 + * + */ + +#ifndef __VIR_LOCK_MANAGER_H__ +# define __VIR_LOCK_MANAGER_H__ + +# include "internal.h" +# include <libvirt/plugins/lock_manager.h> + +typedef struct _virLockManagerPlugin virLockManagerPlugin; +typedef virLockManagerPlugin *virLockManagerPluginPtr; + +virLockManagerPluginPtr virLockManagerPluginNew(const char *name, + unsigned int flags); +void virLockManagerPluginRef(virLockManagerPluginPtr plugin); +void virLockManagerPluginUnref(virLockManagerPluginPtr plugin); + +virLockManagerPtr virLockManagerNew(virLockManagerPluginPtr plugin, + unsigned int type, + unsigned int flags); + +int virLockManagerSetParameter(virLockManagerPtr manager, + const char *key, + const char *value); + +int virLockManagerAddResource(virLockManagerPtr manager, + unsigned int type, + const char *name, + unsigned int flags); + +int virLockManagerAcquireObject(virLockManagerPtr manager, + const char *state, + unsigned int flags); +int virLockManagerAttachObject(virLockManagerPtr manager, + pid_t pid, + unsigned int flags); +int virLockManagerDetachObject(virLockManagerPtr manager, + pid_t pid, + unsigned int flags); +int virLockManagerReleaseObject(virLockManagerPtr manager, + unsigned int flags); + +int virLockManagerGetState(virLockManagerPtr manager, + char **state, + unsigned int flags); + +int virLockManagerAcquireResource(virLockManagerPtr manager, + unsigned int type, + const char *name, + unsigned int flags); + +int virLockManagerReleaseResource(virLockManagerPtr manager, + unsigned int type, + const char *name, + unsigned int flags); + +int virLockManagerFree(virLockManagerPtr manager); + +#endif /* __VIR_LOCK_MANAGER_H__ */ diff --git a/src/locking/lock_manager_nop.c b/src/locking/lock_manager_nop.c new file mode 100644 index 0000000..e03d6ec --- /dev/null +++ b/src/locking/lock_manager_nop.c @@ -0,0 +1,159 @@ + +#include <config.h> + +#include "lock_manager_nop.h" +#include "memory.h" +#include "logging.h" + +/** + * + * NB: This file must *not* use regular virReportError functions + * but instead use the lock manager plugin error reporting hooks + * + */ + +static int virLockManagerNopInit(unsigned int version, + unsigned int flags) +{ + VIR_DEBUG("version=%u flags=%u", version, flags); + + return 0; +} + +static int virLockManagerNopDeinit(void) +{ + VIR_DEBUG0(""); + + return 0; +} + +static int virLockManagerNopNew(virLockManagerPtr lock, + unsigned int type, + unsigned int flags) +{ + VIR_DEBUG("lock=%p, type=%u flags=%u", lock, type, flags); + + return 0; +} + +static int virLockManagerNopAddResource(virLockManagerPtr lock, + unsigned int type, + const char *name, + unsigned int flags) +{ + VIR_DEBUG("lock=%p type=%u name=%s flags=%u", lock, type, name, flags); + + return 0; +} + +static int virLockManagerNopSetParameter(virLockManagerPtr lock, + const char *key, + const char *value) +{ + VIR_DEBUG("lock=%p key=%s value=%s", lock, key, value); + + return 0; +} + +static int virLockManagerNopAcquireObject(virLockManagerPtr lock, + const char *state, + unsigned int flags) +{ + VIR_DEBUG("lock=%p state=%s flags=%u", lock, state, flags); + + return 0; +} + +static int virLockManagerNopAttachObject(virLockManagerPtr lock, + pid_t pid, + unsigned int flags) +{ + VIR_DEBUG("lock=%p pid=%u flags=%u", lock, (unsigned int)pid, flags); + + return 0; +} + +static int virLockManagerNopDetachObject(virLockManagerPtr lock, + pid_t pid, + unsigned int flags) +{ + VIR_DEBUG("lock=%p pid=%u flags=%u", lock, (unsigned int)pid, flags); + + return 0; +} + + +static int virLockManagerNopReleaseObject(virLockManagerPtr lock, + unsigned int flags) +{ + VIR_DEBUG("lock=%p flags=%u", lock, flags); + + return 0; +} + +static int virLockManagerNopGetState(virLockManagerPtr lock, + char **state, + unsigned int flags) +{ + VIR_DEBUG("lock=%p state=%p flags=%u", lock, state, flags); + + *state = NULL; + + return 0; +} + + +static int virLockManagerNopAcquireResource(virLockManagerPtr lock, + unsigned int type, + const char *name, + unsigned int flags) +{ + VIR_DEBUG("lock=%p type=%u name=%s flags=%u", lock, type, name, flags); + + return 0; +} + +static int virLockManagerNopReleaseResource(virLockManagerPtr lock, + unsigned int type, + const char *name, + unsigned int flags) +{ + VIR_DEBUG("lock=%p type=%u name=%s flags=%u", lock, type, name, flags); + + return 0; +} + +static void virLockManagerNopFree(virLockManagerPtr man) +{ + VIR_DEBUG("lock=%p", man); + + VIR_FREE(man); +} + +virLockDriver virLockDriverNop = +{ + .version = VIR_LOCK_MANAGER_VERSION, + .flags = VIR_LOCK_MANAGER_MODE_CONTENT | VIR_LOCK_MANAGER_MODE_METADATA, + + .privateDataLen = 0, + + .drvInit = virLockManagerNopInit, + .drvDeinit = virLockManagerNopDeinit, + + .drvNew = virLockManagerNopNew, + .drvFree = virLockManagerNopFree, + + .drvAddResource = virLockManagerNopAddResource, + .drvSetParameter = virLockManagerNopSetParameter, + + .drvAcquireObject = virLockManagerNopAcquireObject, + .drvAttachObject = virLockManagerNopAttachObject, + .drvDetachObject = virLockManagerNopDetachObject, + .drvReleaseObject = virLockManagerNopReleaseObject, + + .drvGetState = virLockManagerNopGetState, + + .drvAcquireResource = virLockManagerNopAcquireResource, + .drvReleaseResource = virLockManagerNopReleaseResource, +}; + diff --git a/src/locking/lock_manager_nop.h b/src/locking/lock_manager_nop.h new file mode 100644 index 0000000..61d6d2c --- /dev/null +++ b/src/locking/lock_manager_nop.h @@ -0,0 +1,4 @@ + +#include "lock_manager.h" + +extern virLockDriver virLockDriverNop; diff --git a/src/util/virterror.c b/src/util/virterror.c index 9757fd4..d524d04 100644 --- a/src/util/virterror.c +++ b/src/util/virterror.c @@ -196,6 +196,9 @@ static const char *virErrorDomainName(virErrorDomain domain) { case VIR_FROM_STREAMS: dom = "Streams "; break; + case VIR_FROM_LOCKING: + dom = "Locking "; + break; } return(dom); } -- 1.7.2.3

On Mon, Nov 22, 2010 at 06:09:21PM +0000, Daniel P. Berrange wrote:
+/* + * Flags to pass to 'load_drv' and also 'new_drv' method + * Plugins must support at least one of the modes. If a + * mode is unsupported, it must return an error + */ +enum { + VIR_LOCK_MANAGER_MODE_CONTENT = (1 << 0), + VIR_LOCK_MANAGER_MODE_METADATA = (1 << 1), +} virLockManagerFlags;
If I want one plugin to provide both modes, but want to deal with each mode differently, then wouldn't I need a separate _virLockDriver structure for each, or a mode arg in each function? Dave

On Mon, Nov 22, 2010 at 05:47:19PM -0500, David Teigland wrote:
On Mon, Nov 22, 2010 at 06:09:21PM +0000, Daniel P. Berrange wrote:
+/* + * Flags to pass to 'load_drv' and also 'new_drv' method + * Plugins must support at least one of the modes. If a + * mode is unsupported, it must return an error + */ +enum { + VIR_LOCK_MANAGER_MODE_CONTENT = (1 << 0), + VIR_LOCK_MANAGER_MODE_METADATA = (1 << 1), +} virLockManagerFlags;
If I want one plugin to provide both modes, but want to deal with each mode differently, then wouldn't I need a separate _virLockDriver structure for each, or a mode arg in each function?
The mode will get passed to the virLockDriverNew callback in the flags field, whenever setting up any lock, so you wouldn't need a separate virLockDriver table. Regards, Daniel

+/** + * virLockManagerNew: + * @man: the lock manager context + * @type: the type of process to be supervised + * @flags: optional flags, currently unused + * + * Initialize a new context to supervise a process, usually + * a virtual machine. If the lock driver requested a + * private data, the <code>privateData</code> field of + * <code>man</code> will be initialized with a suitable + * pointer. + * + * Returns 0 if successful initialized a new context, -1 on error + */ +typedef int (*virLockDriverNew)(virLockManagerPtr man, + unsigned int type, + unsigned int flags);
So I don't malloc man->privateData...
+/** + * virLockDriverFree: + * @manager: the lock manager context + * + * Release any resources associated with the lock manager + * context private data + */ +typedef void (*virLockDriverFree)(virLockManagerPtr man);
but I do free it?
+/** + * virLockDriverSetParameter: + * @manager: the lock manager context + * @key: the parameter name + * @value: the parameter value + * + * Set a configuration parameter for the managed process. + * A process of VIR_LOCK_MANAGER_START_DOMAIN will be + * given at least 3 parameters: + * + * - id: the domain unique id + * - uuid: the domain uuid + * - name: the domain name + * + * There may be other parameters specific to the lock manager + * plugin that are provided for the managed process + * + * This should only be called prior to the supervised process + * being started. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverSetParameter)(virLockManagerPtr man, + const char *key, + const char *value);
I think we may want to set parameters on resources as well.
+/** + * virLockDriverAcquireObject: + * @manager: the lock manager context + * @state: the current lock state + * @flags: optional flags, currently unused + * + * Start managing resources for the object. If the + * object is being transferred from another location + * the current lock state may be passed in. This + * must be called from the PID that represents the + * object to be managed. If the lock is lost at any + * time, the PID will be killed off by the lock manager. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAcquireObject)(virLockManagerPtr man, + const char *state, + unsigned int flags);
I know that the migration part is still pending, but I assume one of the flags here would indicate "incoming"?
+/** + * virLockDriverGetState: + * @manager: the lock manager context + * @state: pointer to be filled with lock state + * @flags: optional flags, currently unused + * + * Retrieve the current lock state. The returned + * lock state may be NULL if none is required. The + * caller is responsible for freeing the lock + * state string when it is no longer required + * + * Returns 0 on success, or -1 on failure. + */ +typedef int (*virLockDriverGetState)(virLockManagerPtr man, + char **state, + unsigned int flags);
Is there a reason that "state" should be a string instead of binary data with size? This may have more uses than migration, but for migration it may be nice to return the state from the first migrate function on the source.
+/** + * virLockDriverAcquireResource: + * @manager: the lock manager context + * @type: the resource type virLockDriverResourceType + * @name: the resource name + * @flags: the resource access flags + * + * Assign a resource to a managed object. This will + * only be called when the object is already locked + * and active. eg, to hotplug a disk into a VM. + * The format of @name varies according to + * the resource @type. A VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK + * will have a fully qualified file path. + * + * If no flags are given, the resource is assumed to be + * used in exclusive, read-write mode. Access can be + * relaxed to readonly, or shared read-write. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAcquireResource)(virLockManagerPtr man, + unsigned int type, + const char *name, + unsigned int flags);
This doesn't work with my suggestion above about setting parameters on resources between adding them and acquiring them, if that's something we want to do. It also won't allow locking multiple resources together like the original acquire does, i.e. acquire all these locks or none. Dave

On Tue, Nov 23, 2010 at 05:00:05PM -0500, David Teigland wrote:
+/** + * virLockManagerNew: + * @man: the lock manager context + * @type: the type of process to be supervised + * @flags: optional flags, currently unused + * + * Initialize a new context to supervise a process, usually + * a virtual machine. If the lock driver requested a + * private data, the <code>privateData</code> field of + * <code>man</code> will be initialized with a suitable + * pointer. + * + * Returns 0 if successful initialized a new context, -1 on error + */ +typedef int (*virLockDriverNew)(virLockManagerPtr man, + unsigned int type, + unsigned int flags);
So I don't malloc man->privateData...
Nope, no need to malloc it. Just set the 'privateDataLen' in the driver table to however many bytes you need, eg sizeof(your private struct), and this will be malloced at the same time as the main virLockManagerPtr in one contigous block.
+/** + * virLockDriverFree: + * @manager: the lock manager context + * + * Release any resources associated with the lock manager + * context private data + */ +typedef void (*virLockDriverFree)(virLockManagerPtr man);
but I do free it?
Nope, only need to free stuff stored in your private data struct, not the private data struct itself.
+/** + * virLockDriverSetParameter: + * @manager: the lock manager context + * @key: the parameter name + * @value: the parameter value + * + * Set a configuration parameter for the managed process. + * A process of VIR_LOCK_MANAGER_START_DOMAIN will be + * given at least 3 parameters: + * + * - id: the domain unique id + * - uuid: the domain uuid + * - name: the domain name + * + * There may be other parameters specific to the lock manager + * plugin that are provided for the managed process + * + * This should only be called prior to the supervised process + * being started. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverSetParameter)(virLockManagerPtr man, + const char *key, + const char *value);
I think we may want to set parameters on resources as well.
What for ? I'd prefer to avoid that becuase I don't want to create an intermediate object to fully represent resources
+/** + * virLockDriverAcquireObject: + * @manager: the lock manager context + * @state: the current lock state + * @flags: optional flags, currently unused + * + * Start managing resources for the object. If the + * object is being transferred from another location + * the current lock state may be passed in. This + * must be called from the PID that represents the + * object to be managed. If the lock is lost at any + * time, the PID will be killed off by the lock manager. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAcquireObject)(virLockManagerPtr man, + const char *state, + unsigned int flags);
I know that the migration part is still pending, but I assume one of the flags here would indicate "incoming"?
In the case of incoming migration, 'state' will be non-NULL ie a representation of the current lock state from the migration source.
+/** + * virLockDriverGetState: + * @manager: the lock manager context + * @state: pointer to be filled with lock state + * @flags: optional flags, currently unused + * + * Retrieve the current lock state. The returned + * lock state may be NULL if none is required. The + * caller is responsible for freeing the lock + * state string when it is no longer required + * + * Returns 0 on success, or -1 on failure. + */ +typedef int (*virLockDriverGetState)(virLockManagerPtr man, + char **state, + unsigned int flags);
Is there a reason that "state" should be a string instead of binary data with size?
This may have more uses than migration, but for migration it may be nice to return the state from the first migrate function on the source.
The 'state' will ultimately be placed in an XML document to be transferred during the migration process. This API only rewquires that it be a NULL terminated string. If you need to transfer binary data as the state, then I'd recommend just base64 encoding your blob of data. libvirt will take care of any XML special character escaping if it is needed.
+/** + * virLockDriverAcquireResource: + * @manager: the lock manager context + * @type: the resource type virLockDriverResourceType + * @name: the resource name + * @flags: the resource access flags + * + * Assign a resource to a managed object. This will + * only be called when the object is already locked + * and active. eg, to hotplug a disk into a VM. + * The format of @name varies according to + * the resource @type. A VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK + * will have a fully qualified file path. + * + * If no flags are given, the resource is assumed to be + * used in exclusive, read-write mode. Access can be + * relaxed to readonly, or shared read-write. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAcquireResource)(virLockManagerPtr man, + unsigned int type, + const char *name, + unsigned int flags);
This doesn't work with my suggestion above about setting parameters on resources between adding them and acquiring them, if that's something we want to do. It also won't allow locking multiple resources together like the original acquire does, i.e. acquire all these locks or none.
Daniel

On Mon, Nov 22, 2010 at 06:09:21PM +0000, Daniel P. Berrange wrote:
+/* + * Flags to pass to 'load_drv' and also 'new_drv' method + * Plugins must support at least one of the modes. If a + * mode is unsupported, it must return an error + */ +enum { + VIR_LOCK_MANAGER_MODE_CONTENT = (1 << 0), + VIR_LOCK_MANAGER_MODE_METADATA = (1 << 1), +} virLockManagerFlags;
These terms indicate what libvirt is using the locks for, but from a lock manager perspective, they don't have any meaning. It's not clear what the lock manager should do differently for one type vs the other. Is it the scope of the locks that differs? i.e. locking between local processes on a single host, vs distributed locking between hosts? If that's the case, then maybe name them LOCAL and DISTRIBUTED?
+enum { + /* The resource to be locked is a virtual disk */ + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK = 0, +} virLockManagerResourceType;
Generic is good, and there are parts where it could still be more like a generic, traditional lock manager API...
+typedef enum { + /* The resource is assigned in readonly mode */ + VIR_LOCK_MANAGER_RESOURCE_READONLY = (1 << 0), + /* The resource is assigned in shared, writable mode */ + VIR_LOCK_MANAGER_RESOURCE_SHARED = (1 << 1), +} virLockManagerResourceFlags;
I really think you should adopt traditional lock modes instead: LOCK_MODE_EXCLUSIVE (aka WRITE) - incompatible with other SH and EX locks. LOCK_MODE_SHARED (aka READ) - compatible with SH locks, not with EX locks. LOCK_MODE_NONE - the lock manager is unused, and it's up to the application to do its own locking or coordination when accessing the resource, e.g. if there's a clustered db or fs on the device that does its own coordination. You seem to be calling this SHARED, but there's actually no lock or exclusion that a lock manager would implement for this kind of usage. Each resource should have its own lock mode, and a mode should always be specified (rather than defaulting to EX). libvirt would use the lock mode based on how the resource will be used, it would take a SH lock on a readonly resource. non-blocking and force options would probably be quite useful to have right from the beginning for testing.
+/** + * virLockManagerNew: + * @man: the lock manager context + * @type: the type of process to be supervised + * @flags: optional flags, currently unused + * + * Initialize a new context to supervise a process, usually + * a virtual machine. If the lock driver requested a + * private data, the <code>privateData</code> field of + * <code>man</code> will be initialized with a suitable + * pointer. + * + * Returns 0 if successful initialized a new context, -1 on error + */ +typedef int (*virLockDriverNew)(virLockManagerPtr man, + unsigned int type, + unsigned int flags);
This is fundamentally about creating/defining a new process or new lock owner. A name like NewOwner or NewPid or NewVM would make that more clear. You refer to it sometimes as a "managed object".
+/** + * virLockDriverAddResource: + * @manager: the lock manager context + * @type: the resource type virLockManagerResourceType + * @name: the resource name + * @flags: the resource access flags + * + * Assign a resource to a managed object. This will + * only be called prior to the object is being locked + * when it is inactive. eg, to set the initial boot + * time disk assignments on a VM + * The format of @name varies according to + * the resource @type. A VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK + * will have a fully qualified file path. + * + * If no flags are given, the resource is assumed to be + * used in exclusive, read-write mode. Access can be + * relaxed to readonly, or shared read-write. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAddResource)(virLockManagerPtr man, + unsigned int type, + const char *name, + unsigned int flags);
As mentioned above, I think we want a lock mode arg.
+/** + * virLockDriverAcquireObject: + * @manager: the lock manager context + * @state: the current lock state + * @flags: optional flags, currently unused + * + * Start managing resources for the object. If the + * object is being transferred from another location + * the current lock state may be passed in. This + * must be called from the PID that represents the + * object to be managed. If the lock is lost at any + * time, the PID will be killed off by the lock manager. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAcquireObject)(virLockManagerPtr man, + const char *state, + unsigned int flags);
AcquireResources?
+/** + * virLockDriverAttachObject: + * @manager: the lock manager context + * @pid: the managed object PID + * @flags: optional flags, currently unused + * + * Re-attach to an existing lock manager instance managing + * PID @pid. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAttachObject)(virLockManagerPtr man, + pid_t pid, + unsigned int flags); + +/** + * virLockDriverDetachObject: + * @manager: the lock manager context + * @pid: the managed object PID + * @flags: optional flags, currently unused + * + * Deattach from an existing lock manager instance managing + * PID @pid. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverDetachObject)(virLockManagerPtr man, + pid_t pid, + unsigned int flags);
I guess these two are to allow libvirtd to be restarted while the lock manager remains running and holding locks? Again, Pid/Owner/VM would be less obtuse than Object.
+/** + * virLockDriverAcquireResource: + * @manager: the lock manager context + * @type: the resource type virLockDriverResourceType + * @name: the resource name + * @flags: the resource access flags + * + * Assign a resource to a managed object. This will + * only be called when the object is already locked + * and active. eg, to hotplug a disk into a VM. + * The format of @name varies according to + * the resource @type. A VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK + * will have a fully qualified file path. + * + * If no flags are given, the resource is assumed to be + * used in exclusive, read-write mode. Access can be + * relaxed to readonly, or shared read-write. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAcquireResource)(virLockManagerPtr man, + unsigned int type, + const char *name, + unsigned int flags);
Since I suggested AddResource+AcquireResources above, Acquire would a bit inconsistent here; perhaps AttachResource or AssignResource? Dave

On Wed, Nov 24, 2010 at 03:08:41PM -0500, David Teigland wrote:
LOCK_MODE_NONE - the lock manager is unused, and it's up to the application to do its own locking or coordination when accessing the resource, e.g. if there's a clustered db or fs on the device that does its own coordination. You seem to be calling this SHARED, but there's actually no lock or exclusion that a lock manager would implement for this kind of usage.
I failed to mention that with a more elaborate set of lock modes like a dlm uses, the CW "concurrent write" mode fits the use case of leaving the coordination to the application. Simpler lock managers only support Shared or Exclusive, of course, in which case "None" would apply. Dave

On Mon, Nov 29, 2010 at 02:49:26PM -0500, David Teigland wrote:
On Wed, Nov 24, 2010 at 03:08:41PM -0500, David Teigland wrote:
LOCK_MODE_NONE - the lock manager is unused, and it's up to the application to do its own locking or coordination when accessing the resource, e.g. if there's a clustered db or fs on the device that does its own coordination. You seem to be calling this SHARED, but there's actually no lock or exclusion that a lock manager would implement for this kind of usage.
I failed to mention that with a more elaborate set of lock modes like a dlm uses, the CW "concurrent write" mode fits the use case of leaving the coordination to the application. Simpler lock managers only support Shared or Exclusive, of course, in which case "None" would apply.
This kind of difference in capabilities between lock managers is a good example of why I only provide the disk sharing mode to the plugin API, and leave choice of locking mode to the plugin. Daniel

On Wed, Nov 24, 2010 at 03:08:41PM -0500, David Teigland wrote:
On Mon, Nov 22, 2010 at 06:09:21PM +0000, Daniel P. Berrange wrote:
+/* + * Flags to pass to 'load_drv' and also 'new_drv' method + * Plugins must support at least one of the modes. If a + * mode is unsupported, it must return an error + */ +enum { + VIR_LOCK_MANAGER_MODE_CONTENT = (1 << 0), + VIR_LOCK_MANAGER_MODE_METADATA = (1 << 1), +} virLockManagerFlags;
These terms indicate what libvirt is using the locks for, but from a lock manager perspective, they don't have any meaning. It's not clear what the lock manager should do differently for one type vs the other.
Is it the scope of the locks that differs? i.e. locking between local processes on a single host, vs distributed locking between hosts? If that's the case, then maybe name them LOCAL and DISTRIBUTED?
The 'CONTENT' lock is protecting the content of the disk, so the scope must apply to any host able to access the disk. The 'METADATA' lock is protecting the file attributes of the disk, so the scope must apply to an host able to access the same inode. Thus for block devices, or local filesystems (ext3, xfs, etc) the scope is only 1 host. For network or cluster filesystems (nfs, gfs etc) the scope would obviously be multi-host. An 'fcntl' based lock gives a scope that matches this. The more important difference between the CONTENT & METADATA locks though, is the effect of the disk sharing modes. The CONTENT locks will always take into account the sharing mode, while a METADATA lock will always be exclusive.
+enum { + /* The resource to be locked is a virtual disk */ + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK = 0, +} virLockManagerResourceType;
Generic is good, and there are parts where it could still be more like a generic, traditional lock manager API...
+typedef enum { + /* The resource is assigned in readonly mode */ + VIR_LOCK_MANAGER_RESOURCE_READONLY = (1 << 0), + /* The resource is assigned in shared, writable mode */ + VIR_LOCK_MANAGER_RESOURCE_SHARED = (1 << 1), +} virLockManagerResourceFlags;
I really think you should adopt traditional lock modes instead:
LOCK_MODE_EXCLUSIVE (aka WRITE) - incompatible with other SH and EX locks.
LOCK_MODE_SHARED (aka READ) - compatible with SH locks, not with EX locks.
LOCK_MODE_NONE - the lock manager is unused, and it's up to the application to do its own locking or coordination when accessing the resource, e.g. if there's a clustered db or fs on the device that does its own coordination. You seem to be calling this SHARED, but there's actually no lock or exclusion that a lock manager would implement for this kind of usage.
Each resource should have its own lock mode, and a mode should always be specified (rather than defaulting to EX). libvirt would use the lock mode based on how the resource will be used, it would take a SH lock on a readonly resource.
NB the libvirt flags above are *not* expressing lock modes, rather they are expressing the VM's disk access mode. I wanted to leave it upto the plugin to decide how to translate this into a lock mode. The rules for VM disk access mode can be more granular that what a EXCLUSIVE/SHARED lock mode would allow - No flags - exclusive read/write for the VM - SHARED - shared read/write across any VMs - READONLY - shared read across any VMs In the future, it is likely that we'll allow grouping of VMs, so the SHARED disk access mode would not neccessarily equate to a simple SHARED lock mode. eg, one group of VMs could share disk, and this disk would not be accessible to any other group of VMs. In other words, instead of applying a SHARED lock directly on the resource, there would be scope for a EXCLUSIVE lock on a (resource, vm group) pair. Now, whether we'll ever implement this in any lock managers is open to debate, but I want to at least leave open the possibility in the lock manager API. Thus disk access modes are preferrable to directly passing lock modes, so that the lock manager plugin has maximum flexibility in how it applies the locking.
non-blocking and force options would probably be quite useful to have right from the beginning for testing.
NB all locks are non-blocking. If the lock for a resource is already held, the plugin would return an error to the caller, and libvirt would abort startup of the guest. We don't want to block startup indefinitely waiting for a lock. I've thought about whether we might want a way to break a lock, or launch even if locks can't be obtained, but figure this can be added later without any design impact.
+/** + * virLockManagerNew: + * @man: the lock manager context + * @type: the type of process to be supervised + * @flags: optional flags, currently unused + * + * Initialize a new context to supervise a process, usually + * a virtual machine. If the lock driver requested a + * private data, the <code>privateData</code> field of + * <code>man</code> will be initialized with a suitable + * pointer. + * + * Returns 0 if successful initialized a new context, -1 on error + */ +typedef int (*virLockDriverNew)(virLockManagerPtr man, + unsigned int type, + unsigned int flags);
This is fundamentally about creating/defining a new process or new lock owner. A name like NewOwner or NewPid or NewVM would make that more clear. You refer to it sometimes as a "managed object".
Yep, NewObject would be suitable.
+/** + * virLockDriverAddResource: + * @manager: the lock manager context + * @type: the resource type virLockManagerResourceType + * @name: the resource name + * @flags: the resource access flags + * + * Assign a resource to a managed object. This will + * only be called prior to the object is being locked + * when it is inactive. eg, to set the initial boot + * time disk assignments on a VM + * The format of @name varies according to + * the resource @type. A VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK + * will have a fully qualified file path. + * + * If no flags are given, the resource is assumed to be + * used in exclusive, read-write mode. Access can be + * relaxed to readonly, or shared read-write. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAddResource)(virLockManagerPtr man, + unsigned int type, + const char *name, + unsigned int flags);
As mentioned above, I think we want a lock mode arg.
+/** + * virLockDriverAcquireObject: + * @manager: the lock manager context + * @state: the current lock state + * @flags: optional flags, currently unused + * + * Start managing resources for the object. If the + * object is being transferred from another location + * the current lock state may be passed in. This + * must be called from the PID that represents the + * object to be managed. If the lock is lost at any + * time, the PID will be killed off by the lock manager. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAcquireObject)(virLockManagerPtr man, + const char *state, + unsigned int flags);
AcquireResources?
It depends which way you're looking at it. I tend to think 'Object' implies all associated resources.
+/** + * virLockDriverAttachObject: + * @manager: the lock manager context + * @pid: the managed object PID + * @flags: optional flags, currently unused + * + * Re-attach to an existing lock manager instance managing + * PID @pid. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverAttachObject)(virLockManagerPtr man, + pid_t pid, + unsigned int flags); + +/** + * virLockDriverDetachObject: + * @manager: the lock manager context + * @pid: the managed object PID + * @flags: optional flags, currently unused + * + * Deattach from an existing lock manager instance managing + * PID @pid. + * + * Returns 0 on success, or -1 on failure + */ +typedef int (*virLockDriverDetachObject)(virLockManagerPtr man, + pid_t pid, + unsigned int flags);
I guess these two are to allow libvirtd to be restarted while the lock manager remains running and holding locks? Again, Pid/Owner/VM would be less obtuse than Object.
The primary AcquireObject call is done in the context of the QEMU child process, so libvirtd never directly owns the lock. The AttachObject/DetachObject calls are made by libvirtd, whenever it is about todo something on behalf of the managed object holding the lock. eg when libvirtd does disk hotplug it will do $man = NewObject() AttachObject($man, $QEMUPID); AcquireResource($man, $newdiskpath); DetachObject($man); FreeObject($man) So basically the AttachObject call is telling the lock manager plugin what $PID the following AcquireResource operation needs to be applied to. Chances are your DetachObject call will only need to be a no-op but I put the hook in just in case there's a need for it Regards, Daniel

On Tue, Nov 30, 2010 at 10:20:38AM +0000, Daniel P. Berrange wrote:
The AttachObject/DetachObject calls are made by libvirtd, whenever it is about todo something on behalf of the managed object holding the lock. eg when libvirtd does disk hotplug it will do
$man = NewObject() AttachObject($man, $QEMUPID); AcquireResource($man, $newdiskpath); DetachObject($man); FreeObject($man)
So basically the AttachObject call is telling the lock manager plugin what $PID the following AcquireResource operation needs to be applied to. Chances are your DetachObject call will only need to be a no-op but I put the hook in just in case there's a need for it
OK, it seems a roundabout way of SetParameter($man, "pid", $QEMUPID), but "just in case ..." seems to be the consistent approach here. Dave

The QEMU integrates with the lock manager instructure in a number of key places * During startup, a lock is acquired in between the fork & exec * During startup, the libvirtd process acquires a lock before setting file labelling * During shutdown, the libvirtd process acquires a lock before restoring file labelling * During hotplug, unplug & media change the libvirtd process holds a lock while setting/restoring labels The main content lock is only ever held by the QEMU child process, or libvirtd during VM shutdown. The rest of the operations only require libvirtd to hold the metadata locks, relying on the active QEMU still holding the content lock. * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h, src/qemu/libvirtd_qemu.aug, src/qemu/test_libvirtd_qemu.aug: Add config parameter for configuring lock managers * src/qemu/qemu_driver.c: Add calls to the lock manager --- src/qemu/libvirtd_qemu.aug | 1 + src/qemu/qemu_conf.c | 30 +++++ src/qemu/qemu_conf.h | 5 + src/qemu/qemu_driver.c | 253 +++++++++++++++++++++++++++++++++------ src/qemu/test_libvirtd_qemu.aug | 4 + 5 files changed, 256 insertions(+), 37 deletions(-) diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index 78852f3..5adc447 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -43,6 +43,7 @@ module Libvirtd_qemu = | bool_entry "clear_emulator_capabilities" | bool_entry "allow_disk_format_probing" | bool_entry "set_process_name" + | str_entry "lock_manager" (* Each enty in the config is one of the following three ... *) let entry = vnc_entry diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 35caccc..dce9a63 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -148,6 +148,14 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, */ if (access (filename, R_OK) == -1) { VIR_INFO("Could not read qemu config file %s", filename); + if (!(driver->contentLockManager = + virLockManagerPluginNew("nop", + VIR_LOCK_MANAGER_MODE_CONTENT))) + VIR_ERROR("Failed to load lock manager %s", "nop"); + if (!(driver->metadataLockManager = + virLockManagerPluginNew("nop", + VIR_LOCK_MANAGER_MODE_METADATA))) + VIR_ERROR("Failed to load lock manager %s", "nop"); return 0; } @@ -435,6 +443,28 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, CHECK_TYPE ("set_process_name", VIR_CONF_LONG); if (p) driver->setProcessName = p->l; + p = virConfGetValue (conf, "lock_manager"); + CHECK_TYPE ("lock_manager", VIR_CONF_STRING); + if (p && p->str) { + if (!(driver->contentLockManager = + virLockManagerPluginNew(p->str, + VIR_LOCK_MANAGER_MODE_CONTENT))) + VIR_ERROR("Failed to load lock manager %s", p->str); + if (!(driver->metadataLockManager = + virLockManagerPluginNew(p->str, + VIR_LOCK_MANAGER_MODE_METADATA))) + VIR_ERROR("Failed to load lock manager %s", p->str); + } else { + if (!(driver->contentLockManager = + virLockManagerPluginNew("nop", + VIR_LOCK_MANAGER_MODE_CONTENT))) + VIR_ERROR("Failed to load lock manager %s", "nop"); + if (!(driver->metadataLockManager = + virLockManagerPluginNew("nop", + VIR_LOCK_MANAGER_MODE_METADATA))) + VIR_ERROR("Failed to load lock manager %s", "nop"); + } + virConfFree (conf); return 0; } diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index ba3c7b1..3789ed2 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -40,6 +40,7 @@ # include "cpu_conf.h" # include "driver.h" # include "bitmap.h" +# include "locking/lock_manager.h" # define qemudDebug(fmt, ...) do {} while(0) @@ -176,6 +177,10 @@ struct qemud_driver { virBitmapPtr reservedVNCPorts; virSysinfoDefPtr hostsysinfo; + + /* These two might point to the same instance */ + virLockManagerPluginPtr contentLockManager; + virLockManagerPluginPtr metadataLockManager; }; typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 2b901b8..9dcf02d 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -83,6 +83,8 @@ #include "files.h" #include "fdstream.h" #include "configmake.h" +#include "locking/domain_lock.h" + #define VIR_FROM_THIS VIR_FROM_QEMU @@ -1857,6 +1859,15 @@ qemudStartup(int privileged) { goto error; } + /* We should always at least have the 'nop' manager, so + * NULLs here are a fatal error + */ + if (!qemu_driver->contentLockManager || + !qemu_driver->metadataLockManager) { + VIR_ERROR0("Missing content/metadata lock managers"); + goto error; + } + if (qemudSecurityInit(qemu_driver) < 0) goto error; @@ -2088,6 +2099,9 @@ qemudShutdown(void) { virCgroupFree(&qemu_driver->cgroup); + virLockManagerPluginUnref(qemu_driver->contentLockManager); + virLockManagerPluginUnref(qemu_driver->metadataLockManager); + qemuDriverUnlock(qemu_driver); virMutexDestroy(&qemu_driver->lock); VIR_FREE(qemu_driver); @@ -3650,26 +3664,45 @@ struct qemudHookData { virConnectPtr conn; virDomainObjPtr vm; struct qemud_driver *driver; + char *lockState; }; static int qemudSecurityHook(void *data) { struct qemudHookData *h = data; + virDomainLockPtr lock; + int ret = -1; + + if (!(lock = virDomainLockForExec(h->driver->contentLockManager, + h->lockState, + h->vm))) + goto cleanup; /* This must take place before exec(), so that all QEMU * memory allocation is on the correct NUMA node */ if (qemuAddToCgroup(h->driver, h->vm->def) < 0) - return -1; + goto cleanup; /* This must be done after cgroup placement to avoid resetting CPU * affinity */ if (qemudInitCpuAffinity(h->vm) < 0) - return -1; + goto cleanup; if (virSecurityManagerSetProcessLabel(h->driver->securityManager, h->vm) < 0) - return -1; + goto cleanup; - return 0; + /* XXX temp hack to let disk labelling complete */ + sleep(10); + + ret = 0; + +cleanup: + /* Free, but don't release the lock. The object lock + * must remain for lifetime of QEMU process. + */ + virDomainLockFree(lock); + + return ret; } static int @@ -3864,11 +3897,13 @@ static int qemudStartVMDaemon(virConnectPtr conn, int logfile = -1; char *timestamp; qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainLockPtr lock = NULL; struct qemudHookData hookData; hookData.conn = conn; hookData.vm = vm; hookData.driver = driver; + hookData.lockState = NULL; /* XXX add lock state from migration source ! */ FD_ZERO(&keepfd); @@ -3902,11 +3937,6 @@ static int qemudStartVMDaemon(virConnectPtr conn, } qemuDomainSecurityLabelAudit(vm, true); - DEBUG0("Generating setting domain security labels (if required)"); - if (virSecurityManagerSetAllLabel(driver->securityManager, - vm, stdin_path) < 0) - goto cleanup; - /* Ensure no historical cgroup for this VM is lying around bogus * settings */ DEBUG0("Ensuring no historical cgroup is lying around"); @@ -4117,13 +4147,33 @@ static int qemudStartVMDaemon(virConnectPtr conn, runflags |= VIR_EXEC_CLEAR_CAPS; } + VIR_WARN("Executing %s", argv[0]); ret = virExecDaemonize(argv, progenv, &keepfd, &child, stdin_fd, &logfile, &logfile, runflags, qemudSecurityHook, &hookData, pidfile); + VIR_WARN("Executing done %s", argv[0]); VIR_FREE(pidfile); + /* XXX this is bollocks. Need a sync point */ + sleep(5); + + VIR_WARN("Locking %s", argv[0]); + if (!(lock = virDomainLockForStartup(driver->contentLockManager, + driver->metadataLockManager, + NULL, /* XXX lock state */ + vm))) + goto cleanup; + + VIR_WARN("Labelling %s", argv[0]); + DEBUG0("Setting domain security labels"); + if (virSecurityManagerSetAllLabel(driver->securityManager, + vm, stdin_path) < 0) + goto cleanup; + VIR_WARN("All done %s", argv[0]); + + /* wait for qemu process to to show up */ if (ret == 0) { if (virFileReadPid(driver->stateDir, vm->def->name, &vm->pid)) { @@ -4213,11 +4263,18 @@ static int qemudStartVMDaemon(virConnectPtr conn, if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) goto cleanup; + virDomainLockReleaseAndFree(lock); + VIR_FORCE_CLOSE(logfile); return 0; cleanup: + /* Must release before we call into ShutdownVMDaemon() because + * that will re-aquire the lock in order to perform relabelling + */ + virDomainLockReleaseAndFree(lock); + /* We jump here if we failed to start the VM for any reason, or * if we failed to initialize the now running VM. kill it off and * pretend we never started it */ @@ -4320,8 +4377,14 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver, } /* Reset Security Labels */ - virSecurityManagerRestoreAllLabel(driver->securityManager, - vm, migrated); + virDomainLockPtr lock = NULL; + if ((lock = virDomainLockForShutdown(driver->contentLockManager, + driver->metadataLockManager, + vm)) != NULL) { + virSecurityManagerRestoreAllLabel(driver->securityManager, + vm, migrated); + virDomainLockReleaseAndFree(lock); + } virSecurityManagerReleaseLabel(driver->securityManager, vm); @@ -7709,6 +7772,8 @@ static int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, int i; int ret; char *driveAlias = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainLockPtr lock; origdisk = NULL; for (i = 0 ; i < vm->def->ndisks ; i++) { @@ -7741,14 +7806,28 @@ static int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, return -1; } + if (!(lock = virDomainLockForModify(driver->contentLockManager, + driver->metadataLockManager, + vm))) + return -1; + + if (virDomainLockBeginDiskAttach(lock, disk) < 0) { + virDomainLockReleaseAndFree(lock); + return -1; + } + if (virSecurityManagerSetImageLabel(driver->securityManager, - vm, disk) < 0) + vm, disk) < 0) { + virDomainLockReleaseAndFree(lock); return -1; + } + + if (virDomainLockEndDiskAttach(lock, disk) < 0) + VIR_WARN("Unable to release lock on disk %s", disk->src); if (!(driveAlias = qemuDeviceDriveHostAlias(origdisk, qemuCmdFlags))) goto error; - qemuDomainObjPrivatePtr priv = vm->privateData; qemuDomainObjEnterMonitorWithDriver(driver, vm); if (disk->src) { const char *format = NULL; @@ -7771,9 +7850,13 @@ static int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, if (ret < 0) goto error; - if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm, origdisk) < 0) - VIR_WARN("Unable to restore security label on ejected image %s", origdisk->src); + if (virDomainLockBeginDiskDetach(lock, origdisk) >= 0) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, origdisk) < 0) + VIR_WARN("Unable to restore security label on ejected image %s", origdisk->src); + if (virDomainLockEndDiskDetach(lock, origdisk) < 0) + VIR_WARN("Unable to release lock on disk %s", origdisk->src); + } VIR_FREE(origdisk->src); origdisk->src = disk->src; @@ -7783,14 +7866,21 @@ static int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, VIR_FREE(driveAlias); virDomainDiskDefFree(disk); + virDomainLockReleaseAndFree(lock); return ret; error: VIR_FREE(driveAlias); - if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm, disk) < 0) - VIR_WARN("Unable to restore security label on new media %s", disk->src); + + if (virDomainLockBeginDiskDetach(lock, disk) >= 0) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, disk) < 0) + VIR_WARN("Unable to restore security label on new media %s", disk->src); + if (virDomainLockEndDiskDetach(lock, disk) < 0) + VIR_WARN("Unable to release lock on disk %s", disk->src); + } + virDomainLockReleaseAndFree(lock); return -1; } @@ -7806,6 +7896,7 @@ static int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver, qemuDomainObjPrivatePtr priv = vm->privateData; char *devstr = NULL; char *drivestr = NULL; + virDomainLockPtr lock = NULL; for (i = 0 ; i < vm->def->ndisks ; i++) { if (STREQ(vm->def->disks[i]->dst, disk->dst)) { @@ -7815,9 +7906,24 @@ static int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver, } } + if (!(lock = virDomainLockForModify(driver->contentLockManager, + driver->metadataLockManager, + vm))) + return -1; + + if (virDomainLockBeginDiskAttach(lock, disk) < 0) { + virDomainLockReleaseAndFree(lock); + return -1; + } + if (virSecurityManagerSetImageLabel(driver->securityManager, - vm, disk) < 0) + vm, disk) < 0) { + virDomainLockReleaseAndFree(lock); return -1; + } + + if (virDomainLockEndDiskAttach(lock, disk) < 0) + VIR_WARN("Unable to release lock on disk %s", disk->src); if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &disk->info) < 0) @@ -7871,6 +7977,7 @@ static int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver, VIR_FREE(devstr); VIR_FREE(drivestr); + virDomainLockReleaseAndFree(lock); return 0; @@ -7883,9 +7990,14 @@ error: qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &disk->info) < 0) VIR_WARN("Unable to release PCI address on %s", disk->src); - if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); + if (virDomainLockBeginDiskDetach(lock, disk) >= 0) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + if (virDomainLockEndDiskDetach(lock, disk) < 0) + VIR_WARN("Unable to release lock on disk %s", disk->src); + } + virDomainLockReleaseAndFree(lock); return -1; } @@ -8014,6 +8126,7 @@ static int qemudDomainAttachSCSIDisk(struct qemud_driver *driver, char *drivestr = NULL; char *devstr = NULL; int ret = -1; + virDomainLockPtr lock = NULL; for (i = 0 ; i < vm->def->ndisks ; i++) { if (STREQ(vm->def->disks[i]->dst, disk->dst)) { @@ -8023,10 +8136,24 @@ static int qemudDomainAttachSCSIDisk(struct qemud_driver *driver, } } + if (!(lock = virDomainLockForModify(driver->contentLockManager, + driver->metadataLockManager, + vm))) + return -1; + + if (virDomainLockBeginDiskAttach(lock, disk) < 0) { + virDomainLockReleaseAndFree(lock); + return -1; + } if (virSecurityManagerSetImageLabel(driver->securityManager, - vm, disk) < 0) + vm, disk) < 0) { + virDomainLockReleaseAndFree(lock); return -1; + } + + if (virDomainLockEndDiskAttach(lock, disk) < 0) + VIR_WARN("Unable to release lock on disk %s", disk->src); /* We should have an address already, so make sure */ if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { @@ -8104,6 +8231,7 @@ static int qemudDomainAttachSCSIDisk(struct qemud_driver *driver, VIR_FREE(devstr); VIR_FREE(drivestr); + virDomainLockReleaseAndFree(lock); return 0; @@ -8111,9 +8239,14 @@ error: VIR_FREE(devstr); VIR_FREE(drivestr); - if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); + if (virDomainLockBeginDiskDetach(lock, disk) >= 0) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + if (virDomainLockEndDiskDetach(lock, disk) < 0) + VIR_WARN("Unable to release lock on disk %s", disk->src); + } + virDomainLockReleaseAndFree(lock); return -1; } @@ -8128,6 +8261,7 @@ static int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver, int i, ret; char *drivestr = NULL; char *devstr = NULL; + virDomainLockPtr lock = NULL; for (i = 0 ; i < vm->def->ndisks ; i++) { if (STREQ(vm->def->disks[i]->dst, disk->dst)) { @@ -8137,10 +8271,26 @@ static int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver, } } + if (!(lock = virDomainLockForModify(driver->contentLockManager, + driver->metadataLockManager, + vm))) + return -1; + + if (virDomainLockBeginDiskAttach(lock, disk) < 0) { + virDomainLockReleaseAndFree(lock); + return -1; + } + if (virSecurityManagerSetImageLabel(driver->securityManager, - vm, disk) < 0) + vm, disk) < 0) { + virDomainLockReleaseAndFree(lock); return -1; + } + + if (virDomainLockEndDiskAttach(lock, disk) < 0) + VIR_WARN("Unable to release lock on disk %s", disk->src); + /* XXX not correct once we allow attaching a USB CDROM */ if (!disk->src) { qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("disk source path is missing")); @@ -8187,6 +8337,7 @@ static int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver, VIR_FREE(devstr); VIR_FREE(drivestr); + virDomainLockReleaseAndFree(lock); return 0; @@ -8194,9 +8345,15 @@ error: VIR_FREE(devstr); VIR_FREE(drivestr); - if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); + + if (virDomainLockBeginDiskDetach(lock, disk) >= 0) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + if (virDomainLockEndDiskDetach(lock, disk) < 0) + VIR_WARN("Unable to release lock on disk %s", disk->src); + } + virDomainLockReleaseAndFree(lock); return -1; } @@ -9032,6 +9189,12 @@ static int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver, virDomainDiskDefPtr detach = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; virCgroupPtr cgroup = NULL; + virDomainLockPtr lock = NULL; + + if (!(lock = virDomainLockForModify(driver->contentLockManager, + driver->metadataLockManager, + vm))) + return -1; i = qemudFindDisk(vm->def, dev->data.disk->dst); @@ -9084,9 +9247,13 @@ static int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver, virDomainDiskDefFree(detach); - if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm, dev->data.disk) < 0) - VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); + if (virDomainLockBeginDiskDetach(lock, dev->data.disk)) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, dev->data.disk) < 0) + VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); + if (virDomainLockEndDiskDetach(lock, dev->data.disk) < 0) + VIR_WARN("Unable to release lock on disk %s", dev->data.disk->src); + } if (cgroup != NULL) { if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) @@ -9097,6 +9264,7 @@ static int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver, ret = 0; cleanup: + virDomainLockReleaseAndFree(lock); return ret; } @@ -9109,6 +9277,12 @@ static int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver, virDomainDiskDefPtr detach = NULL; qemuDomainObjPrivatePtr priv = vm->privateData; virCgroupPtr cgroup = NULL; + virDomainLockPtr lock = NULL; + + if (!(lock = virDomainLockForModify(driver->contentLockManager, + driver->metadataLockManager, + vm))) + return -1; i = qemudFindDisk(vm->def, dev->data.disk->dst); @@ -9148,9 +9322,13 @@ static int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver, virDomainDiskDefFree(detach); - if (virSecurityManagerRestoreImageLabel(driver->securityManager, - vm, dev->data.disk) < 0) - VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); + if (virDomainLockBeginDiskDetach(lock, dev->data.disk)) { + if (virSecurityManagerRestoreImageLabel(driver->securityManager, + vm, dev->data.disk) < 0) + VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); + if (virDomainLockEndDiskDetach(lock, dev->data.disk) < 0) + VIR_WARN("Unable to release lock on disk %s", dev->data.disk->src); + } if (cgroup != NULL) { if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) @@ -9162,6 +9340,7 @@ static int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver, cleanup: virCgroupFree(&cgroup); + virDomainLockReleaseAndFree(lock); return ret; } diff --git a/src/qemu/test_libvirtd_qemu.aug b/src/qemu/test_libvirtd_qemu.aug index d3ae58d..fd56eb9 100644 --- a/src/qemu/test_libvirtd_qemu.aug +++ b/src/qemu/test_libvirtd_qemu.aug @@ -107,6 +107,8 @@ vnc_allow_host_audio = 1 clear_emulator_capabilities = 0 allow_disk_format_probing = 1 + +lock_manager = \"fcntl\" " test Libvirtd_qemu.lns get conf = @@ -224,3 +226,5 @@ allow_disk_format_probing = 1 { "clear_emulator_capabilities" = "0" } { "#empty" } { "allow_disk_format_probing" = "1" } +{ "#empty" } +{ "lock_manager" = "fcntl" } -- 1.7.2.3

This introduces a new set of APIs in src/util/command.h to use for invoking commands. This is intended to replace all current usage of virRun and virExec variants, with a more flexible and less error prone API. --- src/Makefile.am | 1 + src/libvirt_private.syms | 25 ++ src/util/command.c | 838 ++++++++++++++++++++++++++++++++++++++++++ src/util/command.h | 202 ++++++++++ tests/.gitignore | 4 + tests/Makefile.am | 14 +- tests/commanddata/test1.log | 12 + tests/commanddata/test10.log | 14 + tests/commanddata/test11.log | 12 + tests/commanddata/test12.log | 12 + tests/commanddata/test13.log | 12 + tests/commanddata/test2.log | 14 + tests/commanddata/test3.log | 12 + tests/commanddata/test4.log | 10 + tests/commanddata/test5.log | 6 + tests/commanddata/test6.log | 11 + tests/commanddata/test7.log | 7 + tests/commanddata/test8.log | 14 + tests/commanddata/test9.log | 14 + tests/commandhelper.c | 112 ++++++ tests/commandtest.c | 494 +++++++++++++++++++++++++ 21 files changed, 1839 insertions(+), 1 deletions(-) create mode 100644 src/util/command.c create mode 100644 src/util/command.h create mode 100644 tests/commanddata/test1.log create mode 100644 tests/commanddata/test10.log create mode 100644 tests/commanddata/test11.log create mode 100644 tests/commanddata/test12.log create mode 100644 tests/commanddata/test13.log create mode 100644 tests/commanddata/test2.log create mode 100644 tests/commanddata/test3.log create mode 100644 tests/commanddata/test4.log create mode 100644 tests/commanddata/test5.log create mode 100644 tests/commanddata/test6.log create mode 100644 tests/commanddata/test7.log create mode 100644 tests/commanddata/test8.log create mode 100644 tests/commanddata/test9.log create mode 100644 tests/commandhelper.c create mode 100644 tests/commandtest.c diff --git a/src/Makefile.am b/src/Makefile.am index 750612f..2f10b6d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,6 +48,7 @@ UTIL_SOURCES = \ util/bitmap.c util/bitmap.h \ util/bridge.c util/bridge.h \ util/buf.c util/buf.h \ + util/command.c util/command.h \ util/conf.c util/conf.h \ util/cgroup.c util/cgroup.h \ util/event.c util/event.h \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index e808375..fdd23f9 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -82,6 +82,31 @@ virCgroupSetMemorySoftLimit; virCgroupSetSwapHardLimit; +# command.h +virCommandAddArg; +virCommandAddArgPair; +virCommandAddEnvPass; +virCommandAddEnvPair; +virCommandAddEnvString; +virCommandAddEnvPassCommon; +virCommandClearCaps; +virCommandDaemonize; +virCommandFree; +virCommandNew; +virCommandPreserveFD; +virCommandRun; +virCommandSetErrorBuffer; +virCommandSetErrorFD; +virCommandSetInputBuffer; +virCommandSetInputFD; +virCommandSetOutputBuffer; +virCommandSetOutputFD; +virCommandSetPidFile; +virCommandSetPreExecHook; +virCommandToString; +virCommandWriteArgLog; + + # conf.h virConfFree; virConfFreeValue; diff --git a/src/util/command.c b/src/util/command.c new file mode 100644 index 0000000..3f6c6f5 --- /dev/null +++ b/src/util/command.c @@ -0,0 +1,838 @@ +/* + * command.c: Child command execution + * + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 + * + */ + +#include <config.h> + +#include "command.h" +#include "memory.h" +#include "virterror_internal.h" +#include "util.h" +#include "logging.h" + +#include <stdlib.h> +#include <poll.h> +#include <sys/wait.h> + +#define VIR_FROM_THIS VIR_FROM_NONE + +#define virCommandError(code, ...) \ + virReportErrorHelper(NULL, VIR_FROM_NONE, code, __FILE__, \ + __FUNCTION__, __LINE__, __VA_ARGS__) + +struct _virCommand { + int has_error; + + int nargs; + char **args; + + int nenv; + char **env; + + fd_set preserve; + unsigned int flags; + + const char *inbuf; + char **outbuf; + char **errbuf; + + int infd; + int inpipe; + int outfd; + int errfd; + int *outfdptr; + int *errfdptr; + + virExecHook hook; + void *opaque; + + pid_t pid; + char *pidfile; +}; + +/* + * Create a new command for named binary + */ +virCommandPtr virCommandNew(const char *binary) +{ + const char *const args[] = { binary, NULL }; + + return virCommandNewArgs(args); +} + +/* + * Create a new command with a NULL terminated + * set of args, taking binary from argv[0] + */ +virCommandPtr virCommandNewArgs(const char *const*args) +{ + virCommandPtr cmd; + + if (VIR_ALLOC(cmd) < 0) + return NULL; + + virCommandAddArgSet(cmd, args); + + if (cmd->has_error) { + virCommandFree(cmd); + return NULL; + } + + FD_ZERO(&cmd->preserve); + cmd->infd = cmd->outfd = cmd->errfd = -1; + cmd->inpipe = -1; + cmd->pid = -1; + + return cmd; +} + + +/* + * Preserve the specified file descriptor + * in the child, instead of closing it + */ +void virCommandPreserveFD(virCommandPtr cmd, + int fd) +{ + if (!cmd || cmd->has_error) + return; + + FD_SET(fd, &cmd->preserve); +} + +/* + * Save the child PID in a pidfile + */ +void virCommandSetPidFile(virCommandPtr cmd, + const char *pidfile) +{ + if (!cmd || cmd->has_error) + return; + + VIR_FREE(cmd->pidfile); + if (!(cmd->pidfile = strdup(pidfile))) + cmd->has_error = 1; +} + +/* + * Remove all capabilities from the child + */ +void virCommandClearCaps(virCommandPtr cmd) +{ + if (!cmd || cmd->has_error) + return; + + cmd->flags |= VIR_EXEC_CLEAR_CAPS; +} + + +/* + * Re-allow a specific capability + */ +void virCommandAllowCap(virCommandPtr cmd, + int capability ATTRIBUTE_UNUSED) +{ + if (!cmd || cmd->has_error) + return; + + /* XXX ? */ +} + + +/* + * Daemonize the child process + */ +void virCommandDaemonize(virCommandPtr cmd) +{ + if (!cmd || cmd->has_error) + return; + + cmd->flags |= VIR_EXEC_DAEMON; +} + + +/* + * Add an environment variable to the child + * using separate name & value strings + */ +void virCommandAddEnvPair(virCommandPtr cmd, + const char *name, + const char *value) +{ + char *env; + + if (!cmd || cmd->has_error) + return; + + if (virAsprintf(&env, "%s=%s", name, value ? value : "") < 0) { + cmd->has_error = 1; + return; + } + + if (VIR_REALLOC_N(cmd->env, cmd->nenv + 2) < 0) { + VIR_FREE(env); + cmd->has_error = 1; + return; + } + + cmd->env[cmd->nenv] = env; + cmd->env[cmd->nenv+1] = NULL; + cmd->nenv++; +} + + +/* + * Add an environemnt variable to the child + * using a preformated env string FOO=BAR + */ +void virCommandAddEnvString(virCommandPtr cmd, + const char *str) +{ + if (!cmd || cmd->has_error) + return; + + char *env; + + if (!cmd || cmd->has_error) + return; + + if (!(env = strdup(str))) { + cmd->has_error = 1; + return; + } + + if (VIR_REALLOC_N(cmd->env, cmd->nenv + 2) < 0) { + VIR_FREE(env); + cmd->has_error = 1; + return; + } + + cmd->env[cmd->nenv] = env; + cmd->env[cmd->nenv+1] = NULL; + cmd->nenv++; +} + + +/* + * Pass an environment variable to the child + * using current process' value + */ +void virCommandAddEnvPass(virCommandPtr cmd, + const char *name) +{ + char *value; + if (!cmd || cmd->has_error) + return; + + value = getenv(name); + if (value) + virCommandAddEnvPair(cmd, name, value); +} + + +void virCommandAddEnvPassCommon(virCommandPtr cmd) +{ + virCommandAddEnvPair(cmd, "LC_ALL", "C"); + + virCommandAddEnvPass(cmd, "LD_PRELOAD"); + virCommandAddEnvPass(cmd, "LD_LIBRARY_PATH"); + virCommandAddEnvPass(cmd, "PATH"); + virCommandAddEnvPass(cmd, "HOME"); + virCommandAddEnvPass(cmd, "USER"); + virCommandAddEnvPass(cmd, "LOGNAME"); + virCommandAddEnvPass(cmd, "TMPDIR"); +} + +/* + * Add a command line argument to the child + */ +void virCommandAddArg(virCommandPtr cmd, + const char *val) +{ + char *arg; + + if (!cmd || cmd->has_error) + return; + + if (!(arg = strdup(val))) { + cmd->has_error = 1; + return; + } + + if (VIR_REALLOC_N(cmd->args, cmd->nargs + 2) < 0) { + VIR_FREE(arg); + cmd->has_error = 1; + return; + } + + cmd->args[cmd->nargs] = arg; + cmd->args[cmd->nargs+1] = NULL; + cmd->nargs++; +} + + +void virCommandAddArgPair(virCommandPtr cmd, + const char *name, + const char *val) +{ + char *arg; + + if (!cmd || cmd->has_error) + return; + + if (virAsprintf(&arg, "%s=%s", name, val) < 0) { + cmd->has_error = 1; + return; + } + + if (VIR_REALLOC_N(cmd->args, cmd->nargs + 2) < 0) { + VIR_FREE(arg); + cmd->has_error = 1; + return; + } + + cmd->args[cmd->nargs] = arg; + cmd->args[cmd->nargs+1] = NULL; + cmd->nargs++; +} + +/* + * Add a NULL terminated list of args + */ +void virCommandAddArgSet(virCommandPtr cmd, + const char *const*vals) +{ + int narg = 0; + + if (!cmd || cmd->has_error) + return; + + while (vals[narg] != NULL) + narg++; + + if (VIR_REALLOC_N(cmd->args, cmd->nargs + narg + 1) < 0) { + cmd->has_error = 1; + return; + } + + narg = 0; + while (vals[narg] != NULL) { + char *arg = strdup(vals[narg]); + if (!arg) { + cmd->has_error = 1; + return; + } + cmd->args[cmd->nargs + narg] = arg; + narg++; + } + cmd->args[cmd->nargs + narg] = NULL; + cmd->nargs += narg; +} + + +/* + * Feed the child's stdin from a string buffer + */ +void virCommandSetInputBuffer(virCommandPtr cmd, + const char *inbuf) +{ + if (!cmd || cmd->has_error) + return; + + cmd->inbuf = inbuf; + cmd->infd = -1; + cmd->inpipe = -1; +} + + +/* + * Capture the child's stdout to a string buffer + */ +void virCommandSetOutputBuffer(virCommandPtr cmd, + char **outbuf) +{ + if (!cmd || cmd->has_error) + return; + + cmd->outbuf = outbuf; + cmd->outfdptr = &cmd->outfd; +} + + +/* + * Capture the child's stderr to a string buffer + */ +void virCommandSetErrorBuffer(virCommandPtr cmd, + char **errbuf) +{ + if (!cmd || cmd->has_error) + return; + + cmd->errbuf = errbuf; + cmd->errfdptr = &cmd->errfd; +} + + +/* + * Attach a file descriptor to the child's stdin + */ +void virCommandSetInputFD(virCommandPtr cmd, + int infd) +{ + if (!cmd || cmd->has_error) + return; + + cmd->inbuf = NULL; + cmd->inpipe = -1; + cmd->infd = infd; +} + + +/* + * Attach a file descriptor to the child's stdout + */ +void virCommandSetOutputFD(virCommandPtr cmd, + int *outfd) +{ + if (!cmd || cmd->has_error) + return; + + cmd->outbuf = NULL; + cmd->outfdptr = outfd; +} + + +/* + * Attach a file descriptor to the child's stderr + */ +void virCommandSetErrorFD(virCommandPtr cmd, + int *errfd) +{ + if (!cmd || cmd->has_error) + return; + + cmd->errbuf = NULL; + cmd->errfdptr = errfd; +} + + +void virCommandSetPreExecHook(virCommandPtr cmd, + virExecHook hook, + void *opaque) +{ + if (!cmd || cmd->has_error) + return; + + cmd->hook = hook; + cmd->opaque = opaque; +} + + +static int +virCommandProcessIO(virCommandPtr cmd) +{ + int infd = -1, outfd = -1, errfd = -1; + size_t inlen = 0, outlen = 0, errlen = 0; + size_t inoff = 0; + + /* With an input buffer, feed data to child + * via pipe */ + if (cmd->inbuf) { + inlen = strlen(cmd->inbuf); + infd = cmd->inpipe; + } + + /* With out/err buffer, we're the outfd/errfd + * have been filled with an FD for us */ + if (cmd->outbuf) + outfd = cmd->outfd; + if (cmd->errbuf) + errfd = cmd->errfd; + + for (;;) { + int i; + struct pollfd fds[3]; + int nfds = 0; + + if (infd != -1) { + fds[nfds].fd = infd; + fds[nfds].events = POLLOUT; + nfds++; + } + if (outfd != -1) { + fds[nfds].fd = outfd; + fds[nfds].events = POLLIN; + nfds++; + } + if (errfd != -1) { + fds[nfds].fd = errfd; + fds[nfds].events = POLLIN; + nfds++; + } + + if (nfds == 0) + break; + + if (poll(fds,nfds, -1) < 0) { + if ((errno == EAGAIN) || (errno == EINTR)) + continue; + virReportSystemError(errno, "%s", + _("unable to poll on child")); + return -1; + } + + for (i = 0; i < nfds ; i++) { + if (fds[i].fd == errfd || + fds[i].fd == outfd) { + char data[1024]; + char **buf; + size_t *len; + int done; + if (fds[i].fd == outfd) { + buf = cmd->outbuf; + len = &outlen; + } else { + buf = cmd->errbuf; + len = &errlen; + } + + done = read(fds[i].fd, data, sizeof(data)); + if (done < 0) { + if (errno != EINTR && + errno != EAGAIN) { + virReportSystemError(errno, "%s", + _("unable to write to child input")); + return -1; + } + } else if (done == 0) { + if (fds[i].fd == outfd) + outfd = -1; + else + errfd = -1; + } else { + if (VIR_REALLOC_N(*buf, *len + done + 1) < 0) { + virReportOOMError(); + return -1; + } + memmove(*buf + *len, data, done); + *len += done; + (*buf)[*len] = '\0'; + } + } else { + int done; + + done = write(infd, cmd->inbuf + inoff, + inlen - inoff); + if (done < 0) { + if (errno != EINTR && + errno != EAGAIN) { + virReportSystemError(errno, "%s", + _("unable to write to child input")); + return -1; + } + } else { + inoff += done; + if (inoff == inlen) { + close(infd); + infd = -1; + } + } + } + + } + } + + return 0; + +} + + +/* + * Run the command and wait for completion. + * Returns -1 on any error executing the + * command. Returns 0 if the command executed, + * with the exit status set + */ +int virCommandRun(virCommandPtr cmd, + int *exitstatus) +{ + int ret = 0; + char *outbuf = NULL; + char *errbuf = NULL; + int infd[2]; + if (!cmd || cmd->has_error) { + virReportOOMError(); + return -1; + } + + /* If we have an input buffer, we need + * a pipe to feed the data to the child */ + if (cmd->inbuf) { + if (pipe(infd) < 0) { + virReportSystemError(errno, "%s", + _("unable to open pipe")); + return -1; + } + cmd->infd = infd[0]; + cmd->inpipe = infd[1]; + } + + if (virCommandRunAsync(cmd, NULL) < 0) { + if (cmd->inbuf) { + close(infd[0]); + close(infd[1]); + } + return -1; + } + + /* If caller hasn't requested capture of + * stdout/err, then capture it ourselves + * so we can log it + */ + if (!cmd->outbuf && + !cmd->outfdptr) { + cmd->outfd = -1; + cmd->outfdptr = &cmd->outfd; + cmd->outbuf = &outbuf; + } + if (!cmd->errbuf && + !cmd->errfdptr) { + cmd->errfd = -1; + cmd->errfdptr = &cmd->errfd; + cmd->errbuf = &errbuf; + } + + if (cmd->inbuf || cmd->outbuf || cmd->errbuf) + ret = virCommandProcessIO(cmd); + + if (virCommandWait(cmd, exitstatus) < 0) + ret = -1; + + VIR_DEBUG("Result stdout: '%s' stderr: '%s'", + NULLSTR(*cmd->outbuf), + NULLSTR(*cmd->errbuf)); + + /* Reset any capturing, in case caller runs + * this identical command again */ + if (cmd->inbuf) { + close(infd[0]); + close(infd[1]); + } + if (cmd->outbuf == &outbuf) { + if (cmd->outfd != -1) + close(cmd->outfd); + cmd->outfd = -1; + cmd->outfdptr = NULL; + cmd->outbuf = NULL; + } + if (cmd->errbuf == &errbuf) { + if (cmd->errfd != -1) + close(cmd->errfd); + cmd->errfd = -1; + cmd->errfdptr = NULL; + cmd->errbuf = NULL; + } + + return ret; +} + + +/* + * Run the command asynchronously + * Returns -1 on any error executing the + * command. Returns 0 if the command executed. + */ +int virCommandRunAsync(virCommandPtr cmd, + pid_t *pid) +{ + int ret; + char *str; + + if (!cmd || cmd->has_error) { + virReportOOMError(); + return -1; + } + + if (cmd->pid != -1) { + virCommandError(VIR_ERR_INTERNAL_ERROR, + _("command is already running as pid %d"), + cmd->pid); + return -1; + } + + str = virArgvToString((const char *const *)cmd->args); + VIR_DEBUG("About to run %s", str ? str : cmd->args[0]); + VIR_FREE(str); + + ret = virExecWithHook((const char *const *)cmd->args, + (const char *const *)cmd->env, + &cmd->preserve, + &cmd->pid, + cmd->infd, + cmd->outfdptr, + cmd->errfdptr, + cmd->flags, + cmd->hook, + cmd->opaque, + cmd->pidfile); + + VIR_DEBUG("Command result %d, with PID is %d", + ret, (int)cmd->pid); + + if (ret == 0 && pid) + *pid = cmd->pid; + + return ret; +} + + +/* + * Wait for the async command to complete. + * Return -1 on any error waiting for + * completion. Returns 0 if the command + * finished with the exit status set + */ +int virCommandWait(virCommandPtr cmd, + int *exitstatus) +{ + int ret; + int status; + + if (!cmd || cmd->has_error) { + virReportOOMError(); + return -1; + } + + if (cmd->pid == -1) { + virCommandError(VIR_ERR_INTERNAL_ERROR, "%s", + _("command is not yet running")); + return -1; + } + + + /* Wait for intermediate process to exit */ + while ((ret = waitpid(cmd->pid, &status, 0)) == -1 && + errno == EINTR); + + if (ret == -1) { + virReportSystemError(errno, _("unable to wait for process %d"), + cmd->pid); + return -1; + } + + cmd->pid = -1; + + if (exitstatus == NULL) { + if (status != 0) { + virCommandError(VIR_ERR_INTERNAL_ERROR, + _("Intermediate daemon process exited with status %d."), + WEXITSTATUS(status)); + return -1; + } + } else { + *exitstatus = status; + } + + return 0; +} + + +void virCommandWriteArgLog(virCommandPtr cmd, int logfd) +{ + int ioError = 0; + int i; + + for (i = 0 ; i < cmd->nenv ; i++) { + if (safewrite(logfd, cmd->env[i], strlen(cmd->env[i])) < 0) + ioError = errno; + if (safewrite(logfd, " ", 1) < 0) + ioError = errno; + } + for (i = 0 ; i < cmd->nargs ; i++) { + if (safewrite(logfd, cmd->args[i], strlen(cmd->args[i])) < 0) + ioError = errno; + if (safewrite(logfd, " ", 1) < 0) + ioError = errno; + } + + if (ioError) { + char ebuf[1024]; + VIR_WARN("Unable to write command %s args to logfile: %s", + cmd->args[0], virStrerror(ioError, ebuf, sizeof ebuf)); + } +} + + +char *virCommandToString(virCommandPtr cmd) +{ + int i; + virBuffer buf = VIR_BUFFER_INITIALIZER; + if (!cmd || cmd->has_error) { + virReportOOMError(); + return NULL; + } + + for (i = 0 ; i < cmd->nenv ; i++) { + virBufferAdd(&buf, cmd->env[i], strlen(cmd->env[i])); + virBufferAddLit(&buf, " "); + } + for (i = 0 ; i < cmd->nargs ; i++) { + virBufferAdd(&buf, cmd->args[i], strlen(cmd->args[i])); + virBufferAddLit(&buf, " "); + } + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + + +/* + * Release all resources + * + * XXX close all FDs in cmd->preserve + */ +void virCommandFree(virCommandPtr cmd) +{ + int i; + if (!cmd) + return; + + if (cmd->outfd != -1) + close(cmd->outfd); + if (cmd->errfd != -1) + close(cmd->errfd); + + for (i = 0 ; i < cmd->nargs ; i++) + VIR_FREE(cmd->args[i]); + VIR_FREE(cmd->args); + + for (i = 0 ; i < cmd->nenv ; i++) + VIR_FREE(cmd->env[i]); + VIR_FREE(cmd->env); + + VIR_FREE(cmd->pidfile); + + VIR_FREE(cmd); +} diff --git a/src/util/command.h b/src/util/command.h new file mode 100644 index 0000000..8b99361 --- /dev/null +++ b/src/util/command.h @@ -0,0 +1,202 @@ +/* + * command.h: Child command execution + * + * Copyright (C) 2010 Red Hat, Inc. + * + * 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 + * + */ + +#ifndef __VIR_COMMAND_H__ +# define __VIR_COMMAND_H__ + +# include "internal.h" +# include "util.h" + +typedef struct _virCommand virCommand; +typedef virCommand *virCommandPtr; + +/* + * Create a new command for named binary + */ +virCommandPtr virCommandNew(const char *binary); + +/* + * Create a new command with a NULL terminated + * set of args, taking binary from argv[0] + */ +virCommandPtr virCommandNewArgs(const char *const*args); + +/* All error report from these setup APIs is + * delayed until the Run/Exec/Wait methods + */ + +/* + * Preserve the specified file descriptor + * in the child, instead of closing it + */ +void virCommandPreserveFD(virCommandPtr cmd, + int fd); + +/* + * Save the child PID in a pidfile + */ +void virCommandSetPidFile(virCommandPtr cmd, + const char *pidfile); + +/* + * Remove all capabilities from the child + */ +void virCommandClearCaps(virCommandPtr cmd); + +/* + * Re-allow a specific capability + */ +void virCommandAllowCap(virCommandPtr cmd, + int capability); + +/* + * Daemonize the child process + */ +void virCommandDaemonize(virCommandPtr cmd); + + +/* + * Add an environment variable to the child + * using separate name & value strings + */ +void virCommandAddEnvPair(virCommandPtr cmd, + const char *name, + const char *value); + +/* + * Add an environemnt variable to the child + * using a preformated env string FOO=BAR + */ +void virCommandAddEnvString(virCommandPtr cmd, + const char *str); +/* + * Pass an environment variable to the child + * using current process' value + */ +void virCommandAddEnvPass(virCommandPtr cmd, + const char *name); +/* + * Pass a common set of environment variables + * to the child using current process' values + */ +void virCommandAddEnvPassCommon(virCommandPtr cmd); + +/* + * Add a command line argument to the child + */ +void virCommandAddArg(virCommandPtr cmd, + const char *val); +/* + * Add a command line argument to the child + */ +void virCommandAddArgPair(virCommandPtr cmd, + const char *name, + const char *val); +/* + * Add a NULL terminated list of args + */ +void virCommandAddArgSet(virCommandPtr cmd, + const char *const*vals); + + +/* + * Feed the child's stdin from a string buffer. + * + * NB: Only works with virCommandRun() + */ +void virCommandSetInputBuffer(virCommandPtr cmd, + const char *inbuf); +/* + * Capture the child's stdout to a string buffer + * + * NB: Only works with virCommandRun() + */ +void virCommandSetOutputBuffer(virCommandPtr cmd, + char **outbuf); +/* + * Capture the child's stderr to a string buffer + * + * NB: Only works with virCommandRun() + */ +void virCommandSetErrorBuffer(virCommandPtr cmd, + char **errbuf); + +/* + * Set a file descriptor as the child's stdin + */ +void virCommandSetInputFD(virCommandPtr cmd, + int infd); +/* + * Set a file descriptor as the child's stdout + */ +void virCommandSetOutputFD(virCommandPtr cmd, + int *outfd); +/* + * Set a file descriptor as the child's stderr + */ +void virCommandSetErrorFD(virCommandPtr cmd, + int *errfd); + +/* + * A hook function to run between fork + exec + */ +void virCommandSetPreExecHook(virCommandPtr cmd, + virExecHook hook, + void *opaque); + +/* + * Run the command and wait for completion. + * Returns -1 on any error executing the + * command. Returns 0 if the command executed, + * with the exit status set + */ +int virCommandRun(virCommandPtr cmd, + int *exitstatus) ATTRIBUTE_RETURN_CHECK; + +/* + * Run the command asynchronously + * Returns -1 on any error executing the + * command. Returns 0 if the command executed. + */ +int virCommandRunAsync(virCommandPtr cmd, + pid_t *pid) ATTRIBUTE_RETURN_CHECK; + +char *virCommandToString(virCommandPtr cmd); + +/* + * Wait for the async command to complete. + * Return -1 on any error waiting for + * completion. Returns 0 if the command + * finished with the exit status set + */ +int virCommandWait(virCommandPtr cmd, + int *exitstatus) ATTRIBUTE_RETURN_CHECK; + +void virCommandWriteArgLog(virCommandPtr cmd, + int fd); + +/* + * Release all resources + */ +void virCommandFree(virCommandPtr cmd); + + +#endif /* __VIR_COMMAND_H__ */ diff --git a/tests/.gitignore b/tests/.gitignore index 8ad3e98..393a131 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -32,3 +32,7 @@ xencapstest xmconfigtest xml2sexprtest xml2vmxtest +commandtest +commandhelper +commandhelper.pid +commandhelper.log diff --git a/tests/Makefile.am b/tests/Makefile.am index 77b6fb9..2b98835 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -72,7 +72,8 @@ EXTRA_DIST = \ qemuhelpdata check_PROGRAMS = virshtest conftest sockettest \ - nodeinfotest qparamtest virbuftest + nodeinfotest qparamtest virbuftest \ + commandtest commandhelper if WITH_XEN check_PROGRAMS += xml2sexprtest sexpr2xmltest \ @@ -154,6 +155,7 @@ TESTS = virshtest \ qparamtest \ virbuftest \ sockettest \ + commandtest \ $(test_scripts) if WITH_XEN @@ -339,6 +341,16 @@ nodeinfotest_SOURCES = \ nodeinfotest.c testutils.h testutils.c nodeinfotest_LDADD = $(LDADDS) +commandtest_SOURCES = \ + commandtest.c testutils.h testutils.c +commandtest_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" +commandtest_LDADD = $(LDADDS) + +commandhelper_SOURCES = \ + commandhelper.c +commandhelper_CFLAGS = -Dabs_builddir="\"$(abs_builddir)\"" +commandhelper_LDADD = $(LDADDS) + if WITH_SECDRIVER_SELINUX seclabeltest_SOURCES = \ seclabeltest.c diff --git a/tests/commanddata/test1.log b/tests/commanddata/test1.log new file mode 100644 index 0000000..1b59206 --- /dev/null +++ b/tests/commanddata/test1.log @@ -0,0 +1,12 @@ +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:HOSTNAME=test +ENV:LANG=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test10.log b/tests/commanddata/test10.log new file mode 100644 index 0000000..e1d6092 --- /dev/null +++ b/tests/commanddata/test10.log @@ -0,0 +1,14 @@ +ARG:-version +ARG:-log=bar.log +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:HOSTNAME=test +ENV:LANG=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test11.log b/tests/commanddata/test11.log new file mode 100644 index 0000000..1b59206 --- /dev/null +++ b/tests/commanddata/test11.log @@ -0,0 +1,12 @@ +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:HOSTNAME=test +ENV:LANG=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test12.log b/tests/commanddata/test12.log new file mode 100644 index 0000000..1b59206 --- /dev/null +++ b/tests/commanddata/test12.log @@ -0,0 +1,12 @@ +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:HOSTNAME=test +ENV:LANG=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test13.log b/tests/commanddata/test13.log new file mode 100644 index 0000000..1b59206 --- /dev/null +++ b/tests/commanddata/test13.log @@ -0,0 +1,12 @@ +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:HOSTNAME=test +ENV:LANG=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test2.log b/tests/commanddata/test2.log new file mode 100644 index 0000000..6bd7974 --- /dev/null +++ b/tests/commanddata/test2.log @@ -0,0 +1,14 @@ +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:HOSTNAME=test +ENV:LANG=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +FD:3 +FD:5 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test3.log b/tests/commanddata/test3.log new file mode 100644 index 0000000..1876685 --- /dev/null +++ b/tests/commanddata/test3.log @@ -0,0 +1,12 @@ +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:HOSTNAME=test +ENV:LANG=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:yes +CWD:/ diff --git a/tests/commanddata/test4.log b/tests/commanddata/test4.log new file mode 100644 index 0000000..f745c3f --- /dev/null +++ b/tests/commanddata/test4.log @@ -0,0 +1,10 @@ +ENV:HOME=/home/test +ENV:LC_ALL=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test5.log b/tests/commanddata/test5.log new file mode 100644 index 0000000..5394428 --- /dev/null +++ b/tests/commanddata/test5.log @@ -0,0 +1,6 @@ +ENV:DISPLAY=:0.0 +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test6.log b/tests/commanddata/test6.log new file mode 100644 index 0000000..cdfe445 --- /dev/null +++ b/tests/commanddata/test6.log @@ -0,0 +1,11 @@ +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:LC_ALL=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test7.log b/tests/commanddata/test7.log new file mode 100644 index 0000000..87874fd --- /dev/null +++ b/tests/commanddata/test7.log @@ -0,0 +1,7 @@ +ENV:LANG=C +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test8.log b/tests/commanddata/test8.log new file mode 100644 index 0000000..e1d6092 --- /dev/null +++ b/tests/commanddata/test8.log @@ -0,0 +1,14 @@ +ARG:-version +ARG:-log=bar.log +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:HOSTNAME=test +ENV:LANG=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commanddata/test9.log b/tests/commanddata/test9.log new file mode 100644 index 0000000..e1d6092 --- /dev/null +++ b/tests/commanddata/test9.log @@ -0,0 +1,14 @@ +ARG:-version +ARG:-log=bar.log +ENV:DISPLAY=:0.0 +ENV:HOME=/home/test +ENV:HOSTNAME=test +ENV:LANG=C +ENV:LOGNAME=testTMPDIR=/tmp +ENV:PATH=/usr/bin:/bin +ENV:USER=test +FD:0 +FD:1 +FD:2 +DAEMON:no +CWD:/tmp diff --git a/tests/commandhelper.c b/tests/commandhelper.c new file mode 100644 index 0000000..f22a4d3 --- /dev/null +++ b/tests/commandhelper.c @@ -0,0 +1,112 @@ + +#include <config.h> + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> + +#include "internal.h" +#include "util.h" +#include "memory.h" + + +static int envsort(const void *a, const void *b) { + const char *const*astrptr = a; + const char *const*bstrptr = b; + const char *astr = *astrptr; + const char *bstr = *bstrptr; + char *aeq = strchr(astr, '='); + char *beq = strchr(bstr, '='); + char *akey = strndup(astr, aeq - astr); + char *bkey = strndup(bstr, beq - bstr); + int ret = strcmp(akey, bkey); + free(akey); + free(bkey); + return ret; +} + +int main(int argc, char **argv) { + int i, n; + char **origenv; + char **newenv; + FILE *log = fopen(abs_builddir "/commandhelper.log", "w"); + + if (!log) + goto error; + + for (i = 1 ; i < argc ; i++) { + fprintf(log, "ARG:%s\n", argv[i]); + } + + origenv = environ; + n = 0; + while (*origenv != NULL) { + n++; + origenv++; + } + + if (VIR_ALLOC_N(newenv, n) < 0) { + exit(EXIT_FAILURE); + } + + origenv = environ; + n = i = 0; + while (*origenv != NULL) { + newenv[i++] = *origenv; + n++; + origenv++; + } + qsort(newenv, n, sizeof(newenv[0]), envsort); + + for (i = 0 ; i < n ; i++) { + fprintf(log, "ENV:%s\n", newenv[i]); + } + + for (i = 0 ; i < sysconf(_SC_OPEN_MAX) ; i++) { + int f; + int closed; + if (i == fileno(log)) + continue; + closed = fcntl(i, F_GETFD, &f) == -1 && + errno == EBADF; + if (!closed) + fprintf(log, "FD:%d\n", i); + } + + fprintf(log, "DAEMON:%s\n", getppid() == 1 ? "yes" : "no"); + char cwd[1024]; + fprintf(log, "CWD:%s\n", getcwd(cwd, sizeof(cwd))); + + fclose(log); + + char buf[1024]; + ssize_t got; + + fprintf(stdout, "BEGIN STDOUT\n"); + fflush(stdout); + fprintf(stderr, "BEGIN STDERR\n"); + fflush(stderr); + + for (;;) { + got = read(STDIN_FILENO, buf, sizeof(buf)); + if (got < 0) + goto error; + if (got == 0) + break; + if (safewrite(STDOUT_FILENO, buf, got) != got) + goto error; + if (safewrite(STDERR_FILENO, buf, got) != got) + goto error; + } + + fprintf(stdout, "END STDOUT\n"); + fflush(stdout); + fprintf(stderr, "END STDERR\n"); + fflush(stderr); + + return EXIT_SUCCESS; + +error: + return EXIT_FAILURE; +} diff --git a/tests/commandtest.c b/tests/commandtest.c new file mode 100644 index 0000000..c66d345 --- /dev/null +++ b/tests/commandtest.c @@ -0,0 +1,494 @@ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> + +#include "testutils.h" +#include "internal.h" +#include "nodeinfo.h" +#include "util.h" +#include "memory.h" +#include "command.h" + +#ifndef __linux__ + +static int +mymain(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) +{ + exit (EXIT_AM_SKIP); +} + +#else + +static char *progname; +static char *abs_srcdir; + + +static int checkoutput(const char *testname) { + int ret = -1; + char cwd[1024]; + char *expectname = NULL; + char *expectlog = NULL; + char *actualname = NULL; + char *actuallog = NULL; + + if (!getcwd(cwd, sizeof(cwd))) + return -1; + + if (virAsprintf(&expectname, "%s/commanddata/%s.log", abs_srcdir, testname) < 0) + goto cleanup; + if (virAsprintf(&actualname, "%s/commandhelper.log", abs_builddir) < 0) + goto cleanup; + + if (virFileReadAll(expectname, 1024*64, &expectlog) < 0) { + fprintf(stderr, "cannot read %s\n", expectname); + goto cleanup; + } + + if (virFileReadAll(actualname, 1024*64, &actuallog) < 0) { + fprintf(stderr, "cannot read %s\n", actualname); + goto cleanup; + } + + if (STRNEQ(expectlog, actuallog)) { + virtTestDifference(stderr, expectlog, actuallog); + goto cleanup; + } + + + ret = 0; + +cleanup: + unlink(actuallog); + VIR_FREE(actuallog); + VIR_FREE(actualname); + VIR_FREE(expectlog); + VIR_FREE(expectname); + return ret; +} + +/* + * Run program, no args, inherit all ENV, keep CWD. + * Only stdin/out/err open + */ +static int test0(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper-doesnotexist"); + + if (virCommandRun(cmd, NULL) < 0) { + return 0; + } + + virCommandFree(cmd); + + return -1; +} + +/* + * Run program, no args, inherit all ENV, keep CWD. + * Only stdin/out/err open + */ +static int test1(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test1"); +} + +/* + * Run program, no args, inherit all ENV, keep CWD. + * stdin/out/err + two extra FD open + */ +static int test2(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + int newfd1 = dup(STDERR_FILENO); + int newfd2 = dup(STDERR_FILENO); + int newfd3 = dup(STDERR_FILENO); + close(newfd2); + + virCommandPreserveFD(cmd, newfd1); + virCommandPreserveFD(cmd, newfd3); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test2"); +} + + +/* + * Run program, no args, inherit all ENV, CWD is / + * Only stdin/out/err open. + * Daemonized + */ +static int test3(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + pid_t pid; + char *pidfile = virFilePid(abs_builddir, "commandhelper"); + + virCommandSetPidFile(cmd, pidfile); + virCommandDaemonize(cmd); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + if (virFileReadPid(abs_builddir, "commandhelper", &pid) != 0) { + printf("cannot read pidfile\n"); + return -1; + } + while (kill(pid, 0) != -1) + usleep(100*1000); + + virCommandFree(cmd); + + VIR_FREE(pidfile); + + return checkoutput("test3"); +} + + +/* + * Run program, no args, inherit filtered ENV, keep CWD. + * Only stdin/out/err open + */ +static int test4(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + + virCommandAddEnvPassCommon(cmd); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test4"); +} + + +/* + * Run program, no args, inherit filtered ENV, keep CWD. + * Only stdin/out/err open + */ +static int test5(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + + virCommandAddEnvPass(cmd, "DISPLAY"); + virCommandAddEnvPass(cmd, "DOESNOTEXIST"); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test5"); +} + + +/* + * Run program, no args, inherit filtered ENV, keep CWD. + * Only stdin/out/err open + */ +static int test6(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + + virCommandAddEnvPassCommon(cmd); + virCommandAddEnvPass(cmd, "DISPLAY"); + virCommandAddEnvPass(cmd, "DOESNOTEXIST"); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test6"); +} + +/* + * Run program, no args, inherit filtered ENV, keep CWD. + * Only stdin/out/err open + */ +static int test7(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + + virCommandAddEnvString(cmd, "LANG=C"); + virCommandAddEnvPair(cmd, "USER", "test"); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test7"); +} + + +/* + * Run program, some args, inherit all ENV, keep CWD. + * Only stdin/out/err open + */ +static int test8(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + + virCommandAddArg(cmd, "-version"); + virCommandAddArgPair(cmd, "-log", "bar.log"); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test8"); +} + + +/* + * Run program, some args, inherit all ENV, keep CWD. + * Only stdin/out/err open + */ +static int test9(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + const char *const args[] = { + "-version", "-log=bar.log", NULL, + }; + + virCommandAddArgSet(cmd, args); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test9"); +} + +/* + * Run program, some args, inherit all ENV, keep CWD. + * Only stdin/out/err open + */ +static int test10(const void *unused ATTRIBUTE_UNUSED) { + const char *args[] = { + abs_builddir "/commandhelper", + "-version", "-log=bar.log", NULL, + }; + virCommandPtr cmd = virCommandNewArgs(args); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test10"); +} + +/* + * Run program, no args, inherit all ENV, keep CWD. + * Only stdin/out/err open. Set stdin data + */ +static int test11(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + + virCommandSetInputBuffer(cmd, "Hello World\n"); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + return checkoutput("test11"); +} + +/* + * Run program, no args, inherit all ENV, keep CWD. + * Only stdin/out/err open. Set stdin data + */ +static int test12(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + char *outactual = NULL; + const char *outexpect = "BEGIN STDOUT\n" + "Hello World\n" + "END STDOUT\n"; + int ret = -1; + + virCommandSetInputBuffer(cmd, "Hello World\n"); + virCommandSetOutputBuffer(cmd, &outactual); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + if (!STREQ(outactual, outexpect)) { + virtTestDifference(stderr, outactual, outexpect); + goto cleanup; + } + + if (checkoutput("test12") < 0) + goto cleanup; + + ret = 0; + +cleanup: + VIR_FREE(outactual); + return ret; +} + +/* + * Run program, no args, inherit all ENV, keep CWD. + * Only stdin/out/err open. Set stdin data + */ +static int test13(const void *unused ATTRIBUTE_UNUSED) { + virCommandPtr cmd = virCommandNew(abs_builddir "/commandhelper"); + char *outactual = NULL; + const char *outexpect = "BEGIN STDOUT\n" + "Hello World\n" + "END STDOUT\n"; + char *erractual = NULL; + const char *errexpect = "BEGIN STDERR\n" + "Hello World\n" + "END STDERR\n"; + int ret = -1; + + virCommandSetInputBuffer(cmd, "Hello World\n"); + virCommandSetOutputBuffer(cmd, &outactual); + virCommandSetErrorBuffer(cmd, &erractual); + + if (virCommandRun(cmd, NULL) < 0) { + virErrorPtr err = virGetLastError(); + printf("Cannot run child %s\n", err->message); + return -1; + } + + virCommandFree(cmd); + + if (!STREQ(outactual, outexpect)) { + virtTestDifference(stderr, outactual, outexpect); + goto cleanup; + } + if (!STREQ(erractual, errexpect)) { + virtTestDifference(stderr, erractual, errexpect); + goto cleanup; + } + + if (checkoutput("test13") < 0) + goto cleanup; + + ret = 0; + +cleanup: + VIR_FREE(outactual); + VIR_FREE(erractual); + return ret; +} + + +static int +mymain(int argc, char **argv) +{ + int ret = 0; + char cwd[PATH_MAX]; + + abs_srcdir = getenv("abs_srcdir"); + if (!abs_srcdir) + abs_srcdir = getcwd(cwd, sizeof(cwd)); + + progname = argv[0]; + + if (argc > 1) { + fprintf(stderr, "Usage: %s\n", progname); + return(EXIT_FAILURE); + } + + if (chdir("/tmp") < 0) + return(EXIT_FAILURE); + + virInitialize(); + + const char *const newenv[] = { + "PATH=/usr/bin:/bin", + "HOSTNAME=test", + "LANG=C", + "HOME=/home/test", + "USER=test", + "LOGNAME=test" + "TMPDIR=/tmp", + "DISPLAY=:0.0", + NULL + }; + environ = (char **)newenv; + +#define DO_TEST(NAME) \ + if (virtTestRun("Command Exec " #NAME " test", \ + 1, NAME, NULL) < 0) \ + ret = -1 + + char *actualname; + if (virAsprintf(&actualname, "%s/commandhelper.log", abs_builddir) < 0) + return EXIT_FAILURE; + unlink(actualname); + VIR_FREE(actualname); + + DO_TEST(test0); + DO_TEST(test1); + DO_TEST(test2); + DO_TEST(test3); + DO_TEST(test4); + DO_TEST(test5); + DO_TEST(test6); + DO_TEST(test7); + DO_TEST(test8); + DO_TEST(test9); + DO_TEST(test10); + DO_TEST(test11); + DO_TEST(test12); + DO_TEST(test13); + + return(ret==0 ? EXIT_SUCCESS : EXIT_FAILURE); +} + +#endif /* __linux__ */ + +VIRT_TEST_MAIN(mymain) -- 1.7.2.3

On 11/22/2010 11:09 AM, Daniel P. Berrange wrote:
This introduces a new set of APIs in src/util/command.h to use for invoking commands. This is intended to replace all current usage of virRun and virExec variants, with a more flexible and less error prone API.
This conflicts with my patch series from last week to add this (although I still haven't pushed because a v2 is needed to resolve comments from last week); I'll try and fold in your improvements for the parent/child handshake in my v2, and get that posted soon. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Mon, Nov 22, 2010 at 12:12:28PM -0700, Eric Blake wrote:
On 11/22/2010 11:09 AM, Daniel P. Berrange wrote:
This introduces a new set of APIs in src/util/command.h to use for invoking commands. This is intended to replace all current usage of virRun and virExec variants, with a more flexible and less error prone API.
This conflicts with my patch series from last week to add this (although I still haven't pushed because a v2 is needed to resolve comments from last week); I'll try and fold in your improvements for the parent/child handshake in my v2, and get that posted soon.
Yep, this is just my minimal quick hack to let me work on this patch series. I'll kill / resolve it once your patches are merged. Daniel

Switch the QEMU driver over to using the virCommand APIs instead of passing around char **argv, **env. * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h, src/qemu/qemu_driver.c: Convert to virCommand --- src/locking/lock_manager.c | 2 + src/qemu/qemu_conf.c | 632 +++++++++++++++++++------------------------- src/qemu/qemu_conf.h | 21 +- src/qemu/qemu_driver.c | 195 +++----------- 4 files changed, 324 insertions(+), 526 deletions(-) diff --git a/src/locking/lock_manager.c b/src/locking/lock_manager.c index dc5e7d8..6bd2844 100644 --- a/src/locking/lock_manager.c +++ b/src/locking/lock_manager.c @@ -32,6 +32,8 @@ #include <stdlib.h> #include <unistd.h> +#include "configmake.h" + #define VIR_FROM_THIS VIR_FROM_LOCKING #define virLockError(code, ...) \ diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index dce9a63..ec255c9 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1554,12 +1554,24 @@ int qemudExtractVersionInfo(const char *qemu, int ret = -1, status; unsigned int version, is_kvm, kvm_version; unsigned long long flags = 0; + struct stat sb; if (retflags) *retflags = 0; if (retversion) *retversion = 0; + /* Make sure the binary we are about to try exec'ing exists. + * Technically we could catch the exec() failure, but that's + * in a sub-process so its hard to feed back a useful error + */ + if (stat(qemu, &sb) < 0) { + virReportSystemError(errno, + _("Cannot find QEMU binary %s"), + qemu); + return -1; + } + if (virExec(qemuarg, qemuenv, NULL, &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) return -1; @@ -3972,18 +3984,14 @@ qemuBuildSmpArgStr(const virDomainDefPtr def, * XXX 'conn' is only required to resolve network -> bridge name * figure out how to remove this requirement some day */ -int qemudBuildCommandLine(virConnectPtr conn, - struct qemud_driver *driver, - virDomainDefPtr def, - virDomainChrDefPtr monitor_chr, - int monitor_json, - unsigned long long qemuCmdFlags, - const char ***retargv, - const char ***retenv, - int **vmfds, - int *nvmfds, - const char *migrateFrom, - virDomainSnapshotObjPtr current_snapshot) +virCommandPtr qemudBuildCommandLine(virConnectPtr conn, + struct qemud_driver *driver, + virDomainDefPtr def, + virDomainChrDefPtr monitor_chr, + int monitor_json, + unsigned long long qemuCmdFlags, + const char *migrateFrom, + virDomainSnapshotObjPtr current_snapshot) { int i; char memory[50]; @@ -3993,10 +4001,6 @@ int qemudBuildCommandLine(virConnectPtr conn, int enableKQEMU = 0; int disableKVM = 0; int enableKVM = 0; - int qargc = 0, qarga = 0; - const char **qargv = NULL; - int qenvc = 0, qenva = 0; - const char **qenv = NULL; const char *emulator; char uuid[VIR_UUID_STRING_BUFLEN]; char domid[50]; @@ -4004,11 +4008,12 @@ int qemudBuildCommandLine(virConnectPtr conn, char *smp; int last_good_net = -1; bool hasHwVirt = false; + virCommandPtr cmd; uname_normalize(&ut); if (qemuAssignDeviceAliases(def, qemuCmdFlags) < 0) - return -1; + return NULL; virUUIDFormat(def->uuid, uuid); @@ -4020,7 +4025,7 @@ int qemudBuildCommandLine(virConnectPtr conn, if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("TCP migration is not supported with this QEMU binary")); - return -1; + return NULL; } } else if (STREQ(migrateFrom, "stdio")) { if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) { @@ -4028,13 +4033,13 @@ int qemudBuildCommandLine(virConnectPtr conn, } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("STDIO migration is not supported with this QEMU binary")); - return -1; + return NULL; } } else if (STRPREFIX(migrateFrom, "exec")) { if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC)) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("STDIO migration is not supported with this QEMU binary")); - return -1; + return NULL; } } } @@ -4095,78 +4100,11 @@ int qemudBuildCommandLine(virConnectPtr conn, break; } -#define ADD_ARG_SPACE \ - do { \ - if (qargc == qarga) { \ - qarga += 10; \ - if (VIR_REALLOC_N(qargv, qarga) < 0) \ - goto no_memory; \ - } \ - } while (0) - -#define ADD_ARG(thisarg) \ - do { \ - ADD_ARG_SPACE; \ - qargv[qargc++] = thisarg; \ - } while (0) - -#define ADD_ARG_LIT(thisarg) \ - do { \ - ADD_ARG_SPACE; \ - if ((qargv[qargc++] = strdup(thisarg)) == NULL) \ - goto no_memory; \ - } while (0) - -#define ADD_USBDISK(thisarg) \ - do { \ - ADD_ARG_LIT("-usbdevice"); \ - ADD_ARG_SPACE; \ - if ((virAsprintf((char **)&(qargv[qargc++]), \ - "disk:%s", thisarg)) == -1) { \ - goto no_memory; \ - } \ - } while (0) - -#define ADD_ENV_SPACE \ - do { \ - if (qenvc == qenva) { \ - qenva += 10; \ - if (VIR_REALLOC_N(qenv, qenva) < 0) \ - goto no_memory; \ - } \ - } while (0) - -#define ADD_ENV(thisarg) \ - do { \ - ADD_ENV_SPACE; \ - qenv[qenvc++] = thisarg; \ - } while (0) - -#define ADD_ENV_LIT(thisarg) \ - do { \ - ADD_ENV_SPACE; \ - if ((qenv[qenvc++] = strdup(thisarg)) == NULL) \ - goto no_memory; \ - } while (0) - -#define ADD_ENV_PAIR(envname, val) \ - do { \ - char *envval; \ - ADD_ENV_SPACE; \ - if (virAsprintf(&envval, "%s=%s", envname, val) < 0) \ - goto no_memory; \ - qenv[qenvc++] = envval; \ - } while (0) - - /* Make sure to unset or set all envvars in qemuxml2argvtest.c that - * are copied here using this macro, otherwise the test may fail */ -#define ADD_ENV_COPY(envname) \ - do { \ - char *val = getenv(envname); \ - if (val != NULL) { \ - ADD_ENV_PAIR(envname, val); \ - } \ - } while (0) + cmd = virCommandNew(emulator); + if (!cmd) { + virReportOOMError(); + return NULL; + } /* Set '-m MB' based on maxmem, because the lower 'memory' limit * is set post-startup using the balloon driver. If balloon driver @@ -4175,26 +4113,17 @@ int qemudBuildCommandLine(virConnectPtr conn, snprintf(memory, sizeof(memory), "%lu", def->mem.max_balloon/1024); snprintf(domid, sizeof(domid), "%d", def->id); - ADD_ENV_LIT("LC_ALL=C"); + virCommandAddEnvPassCommon(cmd); - ADD_ENV_COPY("LD_PRELOAD"); - ADD_ENV_COPY("LD_LIBRARY_PATH"); - ADD_ENV_COPY("PATH"); - ADD_ENV_COPY("HOME"); - ADD_ENV_COPY("USER"); - ADD_ENV_COPY("LOGNAME"); - ADD_ENV_COPY("TMPDIR"); - - ADD_ARG_LIT(emulator); - ADD_ARG_LIT("-S"); + virCommandAddArg(cmd, "-S"); /* This should *never* be NULL, since we always provide * a machine in the capabilities data for QEMU. So this * check is just here as a safety in case the unexpected * happens */ if (def->os.machine) { - ADD_ARG_LIT("-M"); - ADD_ARG_LIT(def->os.machine); + virCommandAddArg(cmd, "-M"); + virCommandAddArg(cmd, def->os.machine); } if (qemuBuildCpuArgStr(driver, def, emulator, qemuCmdFlags, @@ -4202,27 +4131,27 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; if (cpu) { - ADD_ARG_LIT("-cpu"); - ADD_ARG_LIT(cpu); + virCommandAddArg(cmd, "-cpu"); + virCommandAddArg(cmd, cpu); VIR_FREE(cpu); if ((qemuCmdFlags & QEMUD_CMD_FLAG_NESTING) && hasHwVirt) - ADD_ARG_LIT("-enable-nesting"); + virCommandAddArg(cmd, "-enable-nesting"); } if (disableKQEMU) - ADD_ARG_LIT("-no-kqemu"); + virCommandAddArg(cmd, "-no-kqemu"); else if (enableKQEMU) { - ADD_ARG_LIT("-enable-kqemu"); - ADD_ARG_LIT("-kernel-kqemu"); + virCommandAddArg(cmd, "-enable-kqemu"); + virCommandAddArg(cmd, "-kernel-kqemu"); } if (disableKVM) - ADD_ARG_LIT("-no-kvm"); + virCommandAddArg(cmd, "-no-kvm"); if (enableKVM) - ADD_ARG_LIT("-enable-kvm"); - ADD_ARG_LIT("-m"); - ADD_ARG_LIT(memory); + virCommandAddArg(cmd, "-enable-kvm"); + virCommandAddArg(cmd, "-m"); + virCommandAddArg(cmd, memory); if (def->mem.hugepage_backed) { if (!driver->hugetlbfs_mount) { qemuReportError(VIR_ERR_INTERNAL_ERROR, @@ -4240,43 +4169,44 @@ int qemudBuildCommandLine(virConnectPtr conn, def->emulator); goto error; } - ADD_ARG_LIT("-mem-prealloc"); - ADD_ARG_LIT("-mem-path"); - ADD_ARG_LIT(driver->hugepage_path); + virCommandAddArg(cmd, "-mem-prealloc"); + virCommandAddArg(cmd, "-mem-path"); + virCommandAddArg(cmd, driver->hugepage_path); } - ADD_ARG_LIT("-smp"); + virCommandAddArg(cmd, "-smp"); if (!(smp = qemuBuildSmpArgStr(def, qemuCmdFlags))) goto error; - ADD_ARG(smp); + virCommandAddArg(cmd, smp); + VIR_FREE(smp); if (qemuCmdFlags & QEMUD_CMD_FLAG_NAME) { - ADD_ARG_LIT("-name"); + virCommandAddArg(cmd, "-name"); if (driver->setProcessName && (qemuCmdFlags & QEMUD_CMD_FLAG_NAME_PROCESS)) { char *name; if (virAsprintf(&name, "%s,process=qemu:%s", def->name, def->name) < 0) goto no_memory; - ADD_ARG_LIT(name); + virCommandAddArg(cmd, name); } else { - ADD_ARG_LIT(def->name); + virCommandAddArg(cmd, def->name); } } if (qemuCmdFlags & QEMUD_CMD_FLAG_UUID) { - ADD_ARG_LIT("-uuid"); - ADD_ARG_LIT(uuid); + virCommandAddArg(cmd, "-uuid"); + virCommandAddArg(cmd, uuid); } if (def->virtType == VIR_DOMAIN_VIRT_XEN || STREQ(def->os.type, "xen") || STREQ(def->os.type, "linux")) { if (qemuCmdFlags & QEMUD_CMD_FLAG_DOMID) { - ADD_ARG_LIT("-domid"); - ADD_ARG_LIT(domid); + virCommandAddArg(cmd, "-domid"); + virCommandAddArg(cmd, domid); } else if (qemuCmdFlags & QEMUD_CMD_FLAG_XEN_DOMID) { - ADD_ARG_LIT("-xen-attach"); - ADD_ARG_LIT("-xen-domid"); - ADD_ARG_LIT(domid); + virCommandAddArg(cmd, "-xen-attach"); + virCommandAddArg(cmd, "-xen-domid"); + virCommandAddArg(cmd, domid); } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("qemu emulator '%s' does not support xen"), @@ -4318,13 +4248,15 @@ int qemudBuildCommandLine(virConnectPtr conn, smbioscmd = qemuBuildSmbiosBiosStr(source); if (smbioscmd != NULL) { - ADD_ARG_LIT("-smbios"); - ADD_ARG(smbioscmd); + virCommandAddArg(cmd, "-smbios"); + virCommandAddArg(cmd, smbioscmd); + VIR_FREE(smbioscmd); } smbioscmd = qemuBuildSmbiosSystemStr(source); if (smbioscmd != NULL) { - ADD_ARG_LIT("-smbios"); - ADD_ARG(smbioscmd); + virCommandAddArg(cmd, "-smbios"); + virCommandAddArg(cmd, smbioscmd); + VIR_FREE(smbioscmd); } } } @@ -4337,12 +4269,12 @@ int qemudBuildCommandLine(virConnectPtr conn, * these defaults ourselves... */ if (!def->graphics) - ADD_ARG_LIT("-nographic"); + virCommandAddArg(cmd, "-nographic"); if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { if (qemuCmdFlags & QEMUD_CMD_FLAG_NODEFCONFIG) - ADD_ARG_LIT("-nodefconfig"); /* Disabling global config files */ - ADD_ARG_LIT("-nodefaults"); /* Disabling default guest devices */ + virCommandAddArg(cmd, "-nodefconfig"); /* Disabling global config files */ + virCommandAddArg(cmd, "-nodefaults"); /* Disabling default guest devices */ } if (monitor_chr) { @@ -4350,39 +4282,42 @@ int qemudBuildCommandLine(virConnectPtr conn, /* Use -chardev if it's available */ if (qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) { - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(chrdev = qemuBuildChrChardevStr(monitor_chr))) goto error; - ADD_ARG(chrdev); + virCommandAddArg(cmd, chrdev); + VIR_FREE(chrdev); - ADD_ARG_LIT("-mon"); + virCommandAddArg(cmd, "-mon"); if (monitor_json) - ADD_ARG_LIT("chardev=monitor,mode=control"); + virCommandAddArg(cmd, "chardev=monitor,mode=control"); else - ADD_ARG_LIT("chardev=monitor,mode=readline"); + virCommandAddArg(cmd, "chardev=monitor,mode=readline"); } else { const char *prefix = NULL; if (monitor_json) prefix = "control,"; - ADD_ARG_LIT("-monitor"); + virCommandAddArg(cmd, "-monitor"); if (!(chrdev = qemuBuildChrArgStr(monitor_chr, prefix))) goto error; - ADD_ARG(chrdev); + virCommandAddArg(cmd, chrdev); + VIR_FREE(chrdev); } } if (qemuCmdFlags & QEMUD_CMD_FLAG_RTC) { const char *rtcopt; - ADD_ARG_LIT("-rtc"); + virCommandAddArg(cmd, "-rtc"); if (!(rtcopt = qemuBuildClockArgStr(&def->clock))) goto error; - ADD_ARG(rtcopt); + virCommandAddArg(cmd, rtcopt); + VIR_FREE(rtcopt); } else { switch (def->clock.offset) { case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: - ADD_ARG_LIT("-localtime"); + virCommandAddArg(cmd, "-localtime"); break; case VIR_DOMAIN_CLOCK_OFFSET_UTC: @@ -4397,9 +4332,8 @@ int qemudBuildCommandLine(virConnectPtr conn, } } if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE && - def->clock.data.timezone) { - ADD_ENV_PAIR("TZ", def->clock.data.timezone); - } + def->clock.data.timezone) + virCommandAddEnvPair(cmd, "TZ", def->clock.data.timezone); for (i = 0; i < def->clock.ntimers; i++) { switch (def->clock.timers[i]->name) { @@ -4422,7 +4356,7 @@ int qemudBuildCommandLine(virConnectPtr conn, /* the default - do nothing */ break; case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: - ADD_ARG_LIT("-rtc-td-hack"); + virCommandAddArg(cmd, "-rtc-td-hack"); break; case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: @@ -4451,14 +4385,14 @@ int qemudBuildCommandLine(virConnectPtr conn, /* delay is the default if we don't have kernel (-no-kvm-pit), otherwise, the default is catchup. */ if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) - ADD_ARG_LIT("-no-kvm-pit-reinjection"); + virCommandAddArg(cmd, "-no-kvm-pit-reinjection"); break; case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) { /* do nothing - this is default for kvm-pit */ } else if (qemuCmdFlags & QEMUD_CMD_FLAG_TDF) { /* -tdf switches to 'catchup' with userspace pit. */ - ADD_ARG_LIT("-tdf"); + virCommandAddArg(cmd, "-tdf"); } else { /* can't catchup if we have neither pit mode */ qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -4487,7 +4421,7 @@ int qemudBuildCommandLine(virConnectPtr conn, if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_HPET) { if (def->clock.timers[i]->present == 0) - ADD_ARG_LIT("-no-hpet"); + virCommandAddArg(cmd, "-no-hpet"); } else { /* no hpet timer available. The only possible action is to raise an error if present="yes" */ @@ -4502,10 +4436,10 @@ int qemudBuildCommandLine(virConnectPtr conn, if ((qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT) && def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART) - ADD_ARG_LIT("-no-reboot"); + virCommandAddArg(cmd, "-no-reboot"); if (!(def->features & (1 << VIR_DOMAIN_FEATURE_ACPI))) - ADD_ARG_LIT("-no-acpi"); + virCommandAddArg(cmd, "-no-acpi"); if (!def->os.bootloader) { for (i = 0 ; i < def->os.nBootDevs ; i++) { @@ -4529,7 +4463,8 @@ int qemudBuildCommandLine(virConnectPtr conn, } if (def->os.nBootDevs) { virBuffer boot_buf = VIR_BUFFER_INITIALIZER; - ADD_ARG_LIT("-boot"); + char *bootstr; + virCommandAddArg(cmd, "-boot"); boot[def->os.nBootDevs] = '\0'; @@ -4548,24 +4483,26 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG(virBufferContentAndReset(&boot_buf)); + bootstr = virBufferContentAndReset(&boot_buf); + virCommandAddArg(cmd, bootstr); + VIR_FREE(bootstr); } if (def->os.kernel) { - ADD_ARG_LIT("-kernel"); - ADD_ARG_LIT(def->os.kernel); + virCommandAddArg(cmd, "-kernel"); + virCommandAddArg(cmd, def->os.kernel); } if (def->os.initrd) { - ADD_ARG_LIT("-initrd"); - ADD_ARG_LIT(def->os.initrd); + virCommandAddArg(cmd, "-initrd"); + virCommandAddArg(cmd, def->os.initrd); } if (def->os.cmdline) { - ADD_ARG_LIT("-append"); - ADD_ARG_LIT(def->os.cmdline); + virCommandAddArg(cmd, "-append"); + virCommandAddArg(cmd, def->os.cmdline); } } else { - ADD_ARG_LIT("-bootloader"); - ADD_ARG_LIT(def->os.bootloader); + virCommandAddArg(cmd, "-bootloader"); + virCommandAddArg(cmd, def->os.bootloader); } for (i = 0 ; i < def->ndisks ; i++) { @@ -4598,13 +4535,14 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); char *devstr; if (!(devstr = qemuBuildControllerDevStr(def->controllers[i]))) goto no_memory; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } } @@ -4640,7 +4578,8 @@ int qemudBuildCommandLine(virConnectPtr conn, if ((disk->bus == VIR_DOMAIN_DISK_BUS_USB) && !(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - ADD_USBDISK(disk->src); + virCommandAddArg(cmd, "-usbdevice"); + virCommandAddArg(cmd, disk->src); } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported usb disk type for '%s'"), disk->src); @@ -4664,7 +4603,7 @@ int qemudBuildCommandLine(virConnectPtr conn, break; } - ADD_ARG_LIT("-drive"); + virCommandAddArg(cmd, "-drive"); /* Unfortunately it is nt possible to use -device for floppys, or Xen paravirt @@ -4678,24 +4617,27 @@ int qemudBuildCommandLine(virConnectPtr conn, (withDeviceArg ? qemuCmdFlags : (qemuCmdFlags & ~QEMUD_CMD_FLAG_DEVICE))))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); if (withDeviceArg) { if (disk->bus == VIR_DOMAIN_DISK_BUS_FDC) { char *fdc; - ADD_ARG_LIT("-global"); + virCommandAddArg(cmd, "-global"); if (virAsprintf(&fdc, "isa-fdc.drive%c=drive-%s", disk->info.addr.drive.unit ? 'B' : 'A', disk->info.alias) < 0) goto no_memory; - ADD_ARG(fdc); + virCommandAddArg(cmd, fdc); + VIR_FREE(fdc); } else { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(optstr = qemuBuildDriveDevStr(disk))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); } } } @@ -4707,7 +4649,8 @@ int qemudBuildCommandLine(virConnectPtr conn, if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - ADD_USBDISK(disk->src); + virCommandAddArg(cmd, "-usbdevice"); + virCommandAddArg(cmd, disk->src); } else { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported usb disk type for '%s'"), disk->src); @@ -4756,8 +4699,8 @@ int qemudBuildCommandLine(virConnectPtr conn, snprintf(file, PATH_MAX, "%s", disk->src); } - ADD_ARG_LIT(dev); - ADD_ARG_LIT(file); + virCommandAddArg(cmd, dev); + virCommandAddArg(cmd, file); } } @@ -4766,15 +4709,17 @@ int qemudBuildCommandLine(virConnectPtr conn, char *optstr; virDomainFSDefPtr fs = def->fss[i]; - ADD_ARG_LIT("-fsdev"); + virCommandAddArg(cmd, "-fsdev"); if (!(optstr = qemuBuildFSStr(fs, qemuCmdFlags))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(optstr = qemuBuildFSDevStr(fs))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); } } else { if (def->nfss) { @@ -4787,8 +4732,8 @@ int qemudBuildCommandLine(virConnectPtr conn, if (!def->nnets) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-net"); - ADD_ARG_LIT("none"); + virCommandAddArg(cmd, "-net"); + virCommandAddArg(cmd, "none"); } } else { for (i = 0 ; i < def->nnets ; i++) { @@ -4811,15 +4756,9 @@ int qemudBuildCommandLine(virConnectPtr conn, if (tapfd < 0) goto error; - if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) { - virDomainConfNWFilterTeardown(net); - VIR_FORCE_CLOSE(tapfd); - goto no_memory; - } - last_good_net = i; - (*vmfds)[(*nvmfds)++] = tapfd; + virCommandPreserveFD(cmd, tapfd); if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", tapfd) >= sizeof(tapfd_name)) goto no_memory; @@ -4830,15 +4769,9 @@ int qemudBuildCommandLine(virConnectPtr conn, if (tapfd < 0) goto error; - if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) { - virDomainConfNWFilterTeardown(net); - VIR_FORCE_CLOSE(tapfd); - goto no_memory; - } - last_good_net = i; - (*vmfds)[(*nvmfds)++] = tapfd; + virCommandPreserveFD(cmd, tapfd); if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", tapfd) >= sizeof(tapfd_name)) goto no_memory; @@ -4851,12 +4784,8 @@ int qemudBuildCommandLine(virConnectPtr conn, network device */ int vhostfd = qemudOpenVhostNet(net, qemuCmdFlags); if (vhostfd >= 0) { - if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) { - VIR_FORCE_CLOSE(vhostfd); - goto no_memory; - } + virCommandPreserveFD(cmd, vhostfd); - (*vmfds)[(*nvmfds)++] = vhostfd; if (snprintf(vhostfd_name, sizeof(vhostfd_name), "%d", vhostfd) >= sizeof(vhostfd_name)) goto no_memory; @@ -4872,30 +4801,34 @@ int qemudBuildCommandLine(virConnectPtr conn, */ if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-netdev"); + virCommandAddArg(cmd, "-netdev"); if (!(host = qemuBuildHostNetStr(net, ',', vlan, tapfd_name, vhostfd_name))) goto error; - ADD_ARG(host); + virCommandAddArg(cmd, host); + VIR_FREE(host); } if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(nic = qemuBuildNicDevStr(net, vlan))) goto error; - ADD_ARG(nic); + virCommandAddArg(cmd, nic); + VIR_FREE(nic); } else { - ADD_ARG_LIT("-net"); + virCommandAddArg(cmd, "-net"); if (!(nic = qemuBuildNicStr(net, "nic,", vlan))) goto error; - ADD_ARG(nic); + virCommandAddArg(cmd, nic); + VIR_FREE(nic); } if (!((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))) { - ADD_ARG_LIT("-net"); + virCommandAddArg(cmd, "-net"); if (!(host = qemuBuildHostNetStr(net, ',', vlan, tapfd_name, vhostfd_name))) goto error; - ADD_ARG(host); + virCommandAddArg(cmd, host); + VIR_FREE(host); } } } @@ -4903,8 +4836,8 @@ int qemudBuildCommandLine(virConnectPtr conn, if (!def->nserials) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-serial"); - ADD_ARG_LIT("none"); + virCommandAddArg(cmd, "-serial"); + virCommandAddArg(cmd, "none"); } } else { for (i = 0 ; i < def->nserials ; i++) { @@ -4914,20 +4847,23 @@ int qemudBuildCommandLine(virConnectPtr conn, /* Use -chardev with -device if they are available */ if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(serial))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (virAsprintf(&devstr, "isa-serial,chardev=%s", serial->info.alias) < 0) goto no_memory; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } else { - ADD_ARG_LIT("-serial"); + virCommandAddArg(cmd, "-serial"); if (!(devstr = qemuBuildChrArgStr(serial, NULL))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } } } @@ -4935,8 +4871,8 @@ int qemudBuildCommandLine(virConnectPtr conn, if (!def->nparallels) { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-parallel"); - ADD_ARG_LIT("none"); + virCommandAddArg(cmd, "-parallel"); + virCommandAddArg(cmd, "none"); } } else { for (i = 0 ; i < def->nparallels ; i++) { @@ -4946,20 +4882,23 @@ int qemudBuildCommandLine(virConnectPtr conn, /* Use -chardev with -device if they are available */ if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(parallel))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (virAsprintf(&devstr, "isa-parallel,chardev=%s", parallel->info.alias) < 0) goto no_memory; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } else { - ADD_ARG_LIT("-parallel"); + virCommandAddArg(cmd, "-parallel"); if (!(devstr = qemuBuildChrArgStr(parallel, NULL))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } } } @@ -4977,24 +4916,26 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(channel))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); char *addr = virSocketFormatAddr(channel->target.addr); if (!addr) goto error; int port = virSocketGetPort(channel->target.addr); - ADD_ARG_LIT("-netdev"); + virCommandAddArg(cmd, "-netdev"); if (virAsprintf(&devstr, "user,guestfwd=tcp:%s:%i,chardev=%s,id=user-%s", addr, port, channel->info.alias, channel->info.alias) < 0) { VIR_FREE(addr); goto no_memory; } VIR_FREE(addr); - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); break; case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: @@ -5004,15 +4945,17 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(channel))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(devstr = qemuBuildVirtioSerialPortDevStr(channel))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); break; } } @@ -5030,15 +4973,17 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-chardev"); + virCommandAddArg(cmd, "-chardev"); if (!(devstr = qemuBuildChrChardevStr(console))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(devstr = qemuBuildVirtioSerialPortDevStr(console))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); break; case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL: @@ -5052,20 +4997,21 @@ int qemudBuildCommandLine(virConnectPtr conn, } } - ADD_ARG_LIT("-usb"); + virCommandAddArg(cmd, "-usb"); for (i = 0 ; i < def->ninputs ; i++) { virDomainInputDefPtr input = def->inputs[i]; if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) { if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { char *optstr; - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(optstr = qemuBuildUSBInputDevStr(input))) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); } else { - ADD_ARG_LIT("-usbdevice"); - ADD_ARG_LIT(input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? "mouse" : "tablet"); + virCommandAddArg(cmd, "-usbdevice"); + virCommandAddArg(cmd, input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? "mouse" : "tablet"); } } } @@ -5109,7 +5055,7 @@ int qemudBuildCommandLine(virConnectPtr conn, virBufferAddLit(&opt, ",sasl"); if (driver->vncSASLdir) - ADD_ENV_PAIR("SASL_CONF_DIR", driver->vncSASLdir); + virCommandAddEnvPair(cmd, "SASL_CONF_DIR", driver->vncSASLdir); /* TODO: Support ACLs later */ } @@ -5124,11 +5070,12 @@ int qemudBuildCommandLine(virConnectPtr conn, optstr = virBufferContentAndReset(&opt); - ADD_ARG_LIT("-vnc"); - ADD_ARG(optstr); + virCommandAddArg(cmd, "-vnc"); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); if (def->graphics[0]->data.vnc.keymap) { - ADD_ARG_LIT("-k"); - ADD_ARG_LIT(def->graphics[0]->data.vnc.keymap); + virCommandAddArg(cmd, "-k"); + virCommandAddArg(cmd, def->graphics[0]->data.vnc.keymap); } /* Unless user requested it, set the audio backend to none, to @@ -5136,45 +5083,34 @@ int qemudBuildCommandLine(virConnectPtr conn, * security issues and might not work when using VNC. */ if (driver->vncAllowHostAudio) { - ADD_ENV_COPY("QEMU_AUDIO_DRV"); + virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); } else { - ADD_ENV_LIT("QEMU_AUDIO_DRV=none"); + virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none"); } } else if ((def->ngraphics == 1) && def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { - char *xauth = NULL; - char *display = NULL; + if (def->graphics[0]->data.sdl.xauth) + virCommandAddEnvPair(cmd, "XAUTHORITY", + def->graphics[0]->data.sdl.xauth); + if (def->graphics[0]->data.sdl.display) + virCommandAddEnvPair(cmd, "DISPLAY", + def->graphics[0]->data.sdl.display); - if (def->graphics[0]->data.sdl.xauth && - virAsprintf(&xauth, "XAUTHORITY=%s", - def->graphics[0]->data.sdl.xauth) < 0) - goto no_memory; - if (def->graphics[0]->data.sdl.display && - virAsprintf(&display, "DISPLAY=%s", - def->graphics[0]->data.sdl.display) < 0) { - VIR_FREE(xauth); - goto no_memory; - } - - if (xauth) - ADD_ENV(xauth); - if (display) - ADD_ENV(display); if (def->graphics[0]->data.sdl.fullscreen) - ADD_ARG_LIT("-full-screen"); + virCommandAddArg(cmd, "-full-screen"); /* If using SDL for video, then we should just let it * use QEMU's host audio drivers, possibly SDL too * User can set these two before starting libvirtd */ - ADD_ENV_COPY("QEMU_AUDIO_DRV"); - ADD_ENV_COPY("SDL_AUDIODRIVER"); + virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); + virCommandAddEnvPass(cmd, "SDL_AUDIODRIVER"); /* New QEMU has this flag to let us explicitly ask for * SDL graphics. This is better than relying on the * default, since the default changes :-( */ if (qemuCmdFlags & QEMUD_CMD_FLAG_SDL) - ADD_ARG_LIT("-sdl"); + virCommandAddArg(cmd, "-sdl"); } else if ((def->ngraphics == 1) && def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { @@ -5227,16 +5163,17 @@ int qemudBuildCommandLine(virConnectPtr conn, optstr = virBufferContentAndReset(&opt); - ADD_ARG_LIT("-spice"); - ADD_ARG(optstr); + virCommandAddArg(cmd, "-spice"); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); if (def->graphics[0]->data.spice.keymap) { - ADD_ARG_LIT("-k"); - ADD_ARG_LIT(def->graphics[0]->data.spice.keymap); + virCommandAddArg(cmd, "-k"); + virCommandAddArg(cmd, def->graphics[0]->data.spice.keymap); } /* SPICE includes native support for tunnelling audio, so we * set the audio backend to point at SPICE's own driver */ - ADD_ENV_LIT("QEMU_AUDIO_DRV=spice"); + virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=spice"); } else if ((def->ngraphics == 1)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, @@ -5265,18 +5202,18 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-vga"); - ADD_ARG_LIT(vgastr); + virCommandAddArg(cmd, "-vga"); + virCommandAddArg(cmd, vgastr); } } else { switch (def->videos[0]->type) { case VIR_DOMAIN_VIDEO_TYPE_VGA: - ADD_ARG_LIT("-std-vga"); + virCommandAddArg(cmd, "-std-vga"); break; case VIR_DOMAIN_VIDEO_TYPE_VMVGA: - ADD_ARG_LIT("-vmwarevga"); + virCommandAddArg(cmd, "-vmwarevga"); break; case VIR_DOMAIN_VIDEO_TYPE_XEN: @@ -5303,12 +5240,13 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(str = qemuBuildVideoDevStr(def->videos[i]))) goto error; - ADD_ARG(str); + virCommandAddArg(cmd, str); + VIR_FREE(str); } } else { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, @@ -5321,8 +5259,8 @@ int qemudBuildCommandLine(virConnectPtr conn, /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && (qemuCmdFlags & QEMUD_CMD_FLAG_VGA)) { - ADD_ARG_LIT("-vga"); - ADD_ARG_LIT("none"); + virCommandAddArg(cmd, "-vga"); + virCommandAddArg(cmd, "none"); } } @@ -5337,15 +5275,16 @@ int qemudBuildCommandLine(virConnectPtr conn, * we don't need to set any PCI address on it, so we don't * mind too much */ if (sound->model == VIR_DOMAIN_SOUND_MODEL_PCSPK) { - ADD_ARG_LIT("-soundhw"); - ADD_ARG_LIT("pcspk"); + virCommandAddArg(cmd, "-soundhw"); + virCommandAddArg(cmd, "pcspk"); } else { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(str = qemuBuildSoundDevStr(sound))) goto error; - ADD_ARG(str); + virCommandAddArg(cmd, str); + VIR_FREE(str); } } } else { @@ -5368,8 +5307,9 @@ int qemudBuildCommandLine(virConnectPtr conn, if (i < (def->nsounds - 1)) strncat(modstr, ",", size--); } - ADD_ARG_LIT("-soundhw"); - ADD_ARG(modstr); + virCommandAddArg(cmd, "-soundhw"); + virCommandAddArg(cmd, modstr); + VIR_FREE(modstr); } } @@ -5379,13 +5319,13 @@ int qemudBuildCommandLine(virConnectPtr conn, char *optstr; if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); optstr = qemuBuildWatchdogDevStr(watchdog); if (!optstr) goto error; } else { - ADD_ARG_LIT("-watchdog"); + virCommandAddArg(cmd, "-watchdog"); const char *model = virDomainWatchdogModelTypeToString(watchdog->model); if (!model) { @@ -5397,7 +5337,8 @@ int qemudBuildCommandLine(virConnectPtr conn, if (!(optstr = strdup(model))) goto no_memory; } - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); const char *action = virDomainWatchdogActionTypeToString(watchdog->action); if (!action) { @@ -5405,8 +5346,8 @@ int qemudBuildCommandLine(virConnectPtr conn, "%s", _("invalid watchdog action")); goto error; } - ADD_ARG_LIT("-watchdog-action"); - ADD_ARG_LIT(action); + virCommandAddArg(cmd, "-watchdog-action"); + virCommandAddArg(cmd, action); } /* Add host passthrough hardware */ @@ -5419,15 +5360,17 @@ int qemudBuildCommandLine(virConnectPtr conn, hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } else { - ADD_ARG_LIT("-usbdevice"); + virCommandAddArg(cmd, "-usbdevice"); if (!(devstr = qemuBuildUSBHostdevUsbDevStr(hostdev))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } } @@ -5446,26 +5389,22 @@ int qemudBuildCommandLine(virConnectPtr conn, goto no_memory; } - if (VIR_REALLOC_N(*vmfds, (*nvmfds)+1) < 0) { - VIR_FREE(configfd_name); - VIR_FORCE_CLOSE(configfd); - goto no_memory; - } - - (*vmfds)[(*nvmfds)++] = configfd; + virCommandPreserveFD(cmd, configfd); } } - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name); VIR_FREE(configfd_name); if (!devstr) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) { - ADD_ARG_LIT("-pcidevice"); + virCommandAddArg(cmd, "-pcidevice"); if (!(devstr = qemuBuildPCIHostdevPCIDevStr(hostdev))) goto error; - ADD_ARG(devstr); + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); } else { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("PCI device assignment is not supported by this version of qemu")); @@ -5475,8 +5414,8 @@ int qemudBuildCommandLine(virConnectPtr conn, } if (migrateFrom) { - ADD_ARG_LIT("-incoming"); - ADD_ARG_LIT(migrateFrom); + virCommandAddArg(cmd, "-incoming"); + virCommandAddArg(cmd, migrateFrom); } /* QEMU changed its default behavior to not include the virtio balloon @@ -5495,76 +5434,47 @@ int qemudBuildCommandLine(virConnectPtr conn, } if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { char *optstr; - ADD_ARG_LIT("-device"); + virCommandAddArg(cmd, "-device"); optstr = qemuBuildMemballoonDevStr(def->memballoon); if (!optstr) goto error; - ADD_ARG(optstr); + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); } else if (qemuCmdFlags & QEMUD_CMD_FLAG_BALLOON) { - ADD_ARG_LIT("-balloon"); - ADD_ARG_LIT("virtio"); + virCommandAddArg(cmd, "-balloon"); + virCommandAddArg(cmd, "virtio"); } } if (current_snapshot && current_snapshot->def->active) { - ADD_ARG_LIT("-loadvm"); - ADD_ARG_LIT(current_snapshot->def->name); + virCommandAddArg(cmd, "-loadvm"); + virCommandAddArg(cmd, current_snapshot->def->name); } if (def->namespaceData) { - qemuDomainCmdlineDefPtr cmd; - - cmd = def->namespaceData; - for (i = 0; i < cmd->num_args; i++) - ADD_ARG_LIT(cmd->args[i]); - for (i = 0; i < cmd->num_env; i++) { - if (cmd->env_value[i]) - ADD_ENV_PAIR(cmd->env_name[i], cmd->env_value[i]); + qemuDomainCmdlineDefPtr qemucmd; + + qemucmd = def->namespaceData; + for (i = 0; i < qemucmd->num_args; i++) + virCommandAddArg(cmd, qemucmd->args[i]); + for (i = 0; i < qemucmd->num_env; i++) { + if (qemucmd->env_value[i]) + virCommandAddEnvPair(cmd, qemucmd->env_name[i], qemucmd->env_value[i]); else - ADD_ENV_PAIR(cmd->env_name[i], ""); + virCommandAddEnvPair(cmd, qemucmd->env_name[i], ""); } } - ADD_ARG(NULL); - ADD_ENV(NULL); - - *retargv = qargv; - *retenv = qenv; - return 0; + return cmd; no_memory: virReportOOMError(); error: for (i = 0; i <= last_good_net; i++) virDomainConfNWFilterTeardown(def->nets[i]); - if (vmfds && - *vmfds) { - for (i = 0; i < *nvmfds; i++) - VIR_FORCE_CLOSE((*vmfds)[i]); - VIR_FREE(*vmfds); - *nvmfds = 0; - } - if (qargv) { - for (i = 0 ; i < qargc ; i++) - VIR_FREE((qargv)[i]); - VIR_FREE(qargv); - } - if (qenv) { - for (i = 0 ; i < qenvc ; i++) - VIR_FREE((qenv)[i]); - VIR_FREE(qenv); - } - return -1; - -#undef ADD_ARG -#undef ADD_ARG_LIT -#undef ADD_ARG_SPACE -#undef ADD_USBDISK -#undef ADD_ENV -#undef ADD_ENV_COPY -#undef ADD_ENV_LIT -#undef ADD_ENV_SPACE + virCommandFree(cmd); + return NULL; } diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 3789ed2..b006e75 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -41,6 +41,7 @@ # include "driver.h" # include "bitmap.h" # include "locking/lock_manager.h" +# include "command.h" # define qemudDebug(fmt, ...) do {} while(0) @@ -230,18 +231,14 @@ int qemudParseHelpStr (const char *qemu, unsigned int *is_kvm, unsigned int *kvm_version); -int qemudBuildCommandLine (virConnectPtr conn, - struct qemud_driver *driver, - virDomainDefPtr def, - virDomainChrDefPtr monitor_chr, - int monitor_json, - unsigned long long qemuCmdFlags, - const char ***retargv, - const char ***retenv, - int **vmfds, - int *nvmfds, - const char *migrateFrom, - virDomainSnapshotObjPtr current_snapshot) +virCommandPtr qemudBuildCommandLine(virConnectPtr conn, + struct qemud_driver *driver, + virDomainDefPtr def, + virDomainChrDefPtr monitor_chr, + int monitor_json, + unsigned long long qemuCmdFlags, + const char *migrateFrom, + virDomainSnapshotObjPtr current_snapshot) ATTRIBUTE_NONNULL(1); /* With vlan == -1, use netdev syntax, else old hostnet */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9dcf02d..0b0b6fe 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3017,14 +3017,6 @@ qemuAssignPCIAddresses(virDomainDefPtr def) int ret = -1; unsigned long long qemuCmdFlags = 0; qemuDomainPCIAddressSetPtr addrs = NULL; - struct stat sb; - - if (stat(def->emulator, &sb) < 0) { - virReportSystemError(errno, - _("Cannot find QEMU binary %s"), - def->emulator); - goto cleanup; - } if (qemudExtractVersionInfo(def->emulator, NULL, @@ -3881,16 +3873,8 @@ static int qemudStartVMDaemon(virConnectPtr conn, bool start_paused, int stdin_fd, const char *stdin_path) { - const char **argv = NULL, **tmp; - const char **progenv = NULL; - int i, ret, runflags; - struct stat sb; - int *vmfds = NULL; - int nvmfds = 0; + int ret; unsigned long long qemuCmdFlags; - fd_set keepfd; - const char *emulator; - pid_t child; int pos = -1; char ebuf[1024]; char *pidfile = NULL; @@ -3898,6 +3882,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, char *timestamp; qemuDomainObjPrivatePtr priv = vm->privateData; virDomainLockPtr lock = NULL; + virCommandPtr cmd = NULL; struct qemudHookData hookData; hookData.conn = conn; @@ -3905,8 +3890,6 @@ static int qemudStartVMDaemon(virConnectPtr conn, hookData.driver = driver; hookData.lockState = NULL; /* XXX add lock state from migration source ! */ - FD_ZERO(&keepfd); - DEBUG0("Beginning VM startup process"); if (virDomainObjIsActive(vm)) { @@ -3987,21 +3970,8 @@ static int qemudStartVMDaemon(virConnectPtr conn, if ((logfile = qemudLogFD(driver, vm->def->name)) < 0) goto cleanup; - emulator = vm->def->emulator; - - /* Make sure the binary we are about to try exec'ing exists. - * Technically we could catch the exec() failure, but that's - * in a sub-process so its hard to feed back a useful error - */ - if (stat(emulator, &sb) < 0) { - virReportSystemError(errno, - _("Cannot find QEMU binary %s"), - emulator); - goto cleanup; - } - DEBUG0("Determing emulator version"); - if (qemudExtractVersionInfo(emulator, + if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) goto cleanup; @@ -4070,10 +4040,10 @@ static int qemudStartVMDaemon(virConnectPtr conn, DEBUG0("Building emulator command line"); vm->def->id = driver->nextvmid++; - if (qemudBuildCommandLine(conn, driver, vm->def, priv->monConfig, - priv->monJSON, qemuCmdFlags, &argv, &progenv, - &vmfds, &nvmfds, migrateFrom, - vm->current_snapshot) < 0) + if (!(cmd = qemudBuildCommandLine(conn, driver, vm->def, priv->monConfig, + priv->monJSON, qemuCmdFlags, + migrateFrom, + vm->current_snapshot))) goto cleanup; if (qemuDomainSnapshotSetInactive(vm, driver->snapshotDir) < 0) @@ -4108,70 +4078,46 @@ static int qemudStartVMDaemon(virConnectPtr conn, VIR_FREE(timestamp); } - tmp = progenv; - - while (*tmp) { - if (safewrite(logfile, *tmp, strlen(*tmp)) < 0) - VIR_WARN("Unable to write envv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - if (safewrite(logfile, " ", 1) < 0) - VIR_WARN("Unable to write envv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - tmp++; - } - tmp = argv; - while (*tmp) { - if (safewrite(logfile, *tmp, strlen(*tmp)) < 0) - VIR_WARN("Unable to write argv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - if (safewrite(logfile, " ", 1) < 0) - VIR_WARN("Unable to write argv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); - tmp++; - } - if (safewrite(logfile, "\n", 1) < 0) - VIR_WARN("Unable to write argv to logfile: %s", - virStrerror(errno, ebuf, sizeof ebuf)); + virCommandWriteArgLog(cmd, logfile); if ((pos = lseek(logfile, 0, SEEK_END)) < 0) VIR_WARN("Unable to seek to end of logfile: %s", virStrerror(errno, ebuf, sizeof ebuf)); - for (i = 0 ; i < nvmfds ; i++) - FD_SET(vmfds[i], &keepfd); - VIR_DEBUG("Clear emulator capabilities: %d", driver->clearEmulatorCapabilities); - runflags = VIR_EXEC_NONBLOCK; - if (driver->clearEmulatorCapabilities) { - runflags |= VIR_EXEC_CLEAR_CAPS; - } - - VIR_WARN("Executing %s", argv[0]); - ret = virExecDaemonize(argv, progenv, &keepfd, &child, - stdin_fd, &logfile, &logfile, - runflags, - qemudSecurityHook, &hookData, - pidfile); - VIR_WARN("Executing done %s", argv[0]); + /** XXXX runflags = VIR_EXEC_NONBLOCK; */ + if (driver->clearEmulatorCapabilities) + virCommandClearCaps(cmd); + + VIR_WARN("Executing %s", vm->def->emulator); + virCommandSetPreExecHook(cmd, qemudSecurityHook, &hookData); + virCommandSetInputFD(cmd, stdin_fd); + virCommandSetOutputFD(cmd, &logfile); + virCommandSetErrorFD(cmd, &logfile); + virCommandSetPidFile(cmd, pidfile); + virCommandDaemonize(cmd); + + ret = virCommandRun(cmd, NULL); + VIR_WARN("Executing done %s", vm->def->emulator); VIR_FREE(pidfile); /* XXX this is bollocks. Need a sync point */ sleep(5); - VIR_WARN("Locking %s", argv[0]); + VIR_WARN("Locking %s", vm->def->emulator); if (!(lock = virDomainLockForStartup(driver->contentLockManager, driver->metadataLockManager, NULL, /* XXX lock state */ vm))) goto cleanup; - VIR_WARN("Labelling %s", argv[0]); + VIR_WARN("Labelling %s", vm->def->emulator); DEBUG0("Setting domain security labels"); if (virSecurityManagerSetAllLabel(driver->securityManager, vm, stdin_path) < 0) goto cleanup; - VIR_WARN("All done %s", argv[0]); + VIR_WARN("All done %s", vm->def->emulator); /* wait for qemu process to to show up */ @@ -4181,6 +4127,11 @@ static int qemudStartVMDaemon(virConnectPtr conn, _("Domain %s didn't show up\n"), vm->def->name); ret = -1; } +#if 0 + /* + * XXX this is bogus. It isn't safe to set vm->pid = chidl + * because child no longer exists. + */ } else if (ret == -2) { /* The virExec process that launches the daemon failed. Pending on * when it failed (we can't determine for sure), there may be @@ -4191,30 +4142,16 @@ static int qemudStartVMDaemon(virConnectPtr conn, */ vm->pid = child; ret = 0; +#endif } if (migrateFrom) start_paused = true; vm->state = start_paused ? VIR_DOMAIN_PAUSED : VIR_DOMAIN_RUNNING; - for (i = 0 ; argv[i] ; i++) - VIR_FREE(argv[i]); - VIR_FREE(argv); - - for (i = 0 ; progenv[i] ; i++) - VIR_FREE(progenv[i]); - VIR_FREE(progenv); - if (ret == -1) /* The VM failed to start; tear filters before taps */ virDomainConfVMNWFilterTeardown(vm); - if (vmfds) { - for (i = 0 ; i < nvmfds ; i++) { - VIR_FORCE_CLOSE(vmfds[i]); - } - VIR_FREE(vmfds); - } - if (ret == -1) /* The VM failed to start */ goto cleanup; @@ -4264,6 +4201,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, goto cleanup; virDomainLockReleaseAndFree(lock); + virCommandFree(cmd); VIR_FORCE_CLOSE(logfile); @@ -4274,14 +4212,15 @@ cleanup: * that will re-aquire the lock in order to perform relabelling */ virDomainLockReleaseAndFree(lock); + virCommandFree(cmd); + + VIR_FORCE_CLOSE(logfile); /* We jump here if we failed to start the VM for any reason, or * if we failed to initialize the now running VM. kill it off and * pretend we never started it */ qemudShutdownVMDaemon(driver, vm, 0); - VIR_FORCE_CLOSE(logfile); - return -1; } @@ -7312,13 +7251,8 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, struct qemud_driver *driver = conn->privateData; virDomainDefPtr def = NULL; virDomainChrDef monConfig; - const char *emulator; unsigned long long qemuCmdFlags; - struct stat sb; - const char **retargv = NULL; - const char **retenv = NULL; - const char **tmp; - virBuffer buf = VIR_BUFFER_INITIALIZER; + virCommandPtr cmd = NULL; char *ret = NULL; int i; @@ -7368,69 +7302,24 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, def->graphics[i]->data.vnc.autoport) def->graphics[i]->data.vnc.port = 5900; } - emulator = def->emulator; - - /* Make sure the binary we are about to try exec'ing exists. - * Technically we could catch the exec() failure, but that's - * in a sub-process so its hard to feed back a useful error - */ - if (stat(emulator, &sb) < 0) { - virReportSystemError(errno, - _("Cannot find QEMU binary %s"), - emulator); - goto cleanup; - } - if (qemudExtractVersionInfo(emulator, + if (qemudExtractVersionInfo(def->emulator, NULL, - &qemuCmdFlags) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Cannot determine QEMU argv syntax %s"), - emulator); + &qemuCmdFlags) < 0) goto cleanup; - } if (qemuPrepareMonitorChr(driver, &monConfig, def->name) < 0) goto cleanup; - if (qemudBuildCommandLine(conn, driver, def, - &monConfig, 0, qemuCmdFlags, - &retargv, &retenv, - NULL, NULL, /* Don't want it to create TAP devices */ - NULL, NULL) < 0) { - goto cleanup; - } - - tmp = retenv; - while (*tmp) { - virBufferAdd(&buf, *tmp, strlen(*tmp)); - virBufferAddLit(&buf, " "); - tmp++; - } - tmp = retargv; - while (*tmp) { - virBufferAdd(&buf, *tmp, strlen(*tmp)); - virBufferAddLit(&buf, " "); - tmp++; - } - - if (virBufferError(&buf)) { - virBufferFreeAndReset(&buf); - virReportOOMError(); + if (!(cmd = qemudBuildCommandLine(conn, driver, def, + &monConfig, 0, qemuCmdFlags, + NULL, NULL))) goto cleanup; - } - ret = virBufferContentAndReset(&buf); + ret = virCommandToString(cmd); cleanup: qemuDriverUnlock(driver); - for (tmp = retargv ; tmp && *tmp ; tmp++) - VIR_FREE(*tmp); - VIR_FREE(retargv); - - for (tmp = retenv ; tmp && *tmp ; tmp++) - VIR_FREE(*tmp); - VIR_FREE(retenv); virDomainDefFree(def); return ret; -- 1.7.2.3

Allow the parent process to perform a bi-directional handshake with the child process during fork/exec. The child process will fork and do its initial setup. Immediately prior to the exec(), it will stop & wait for a handshake from the parent process. The parent process will spawn the child and wait until the child reaches the handshake point. It will do whatever extra setup work is required, before signalling the child to continue. The implementation of this is done using two pairs of blocking pipes. The first pair is used to block the parent, until the child writes a single byte. Then the second pair pair is used to block the child, until the parent confirms with another single byte. * src/util/command.c, src/util/command.h, src/libvirt_private.syms: Add APIs to perform a handshake --- src/libvirt_private.syms | 3 + src/util/command.c | 92 ++++++++++++++++++++++++++++++++++++++++++++-- src/util/command.h | 5 ++ 3 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fdd23f9..1b4de1d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -92,8 +92,11 @@ virCommandAddEnvPassCommon; virCommandClearCaps; virCommandDaemonize; virCommandFree; +virCommandHandshakeNotify; +virCommandHandshakeWait; virCommandNew; virCommandPreserveFD; +virCommandRequireHandshake; virCommandRun; virCommandSetErrorBuffer; virCommandSetErrorFD; diff --git a/src/util/command.c b/src/util/command.c index 3f6c6f5..c711691 100644 --- a/src/util/command.c +++ b/src/util/command.c @@ -26,8 +26,10 @@ #include "virterror_internal.h" #include "util.h" #include "logging.h" +#include "files.h" #include <stdlib.h> +#include <stdbool.h> #include <poll.h> #include <sys/wait.h> @@ -60,6 +62,10 @@ struct _virCommand { int *outfdptr; int *errfdptr; + bool handshake; + int handshakeWait[2]; + int handshakeNotify[2]; + virExecHook hook; void *opaque; @@ -626,8 +632,8 @@ int virCommandRun(virCommandPtr cmd, ret = -1; VIR_DEBUG("Result stdout: '%s' stderr: '%s'", - NULLSTR(*cmd->outbuf), - NULLSTR(*cmd->errbuf)); + cmd->outbuf ? NULLSTR(*cmd->outbuf) : "(null)", + cmd->errbuf ? NULLSTR(*cmd->errbuf) : "(null)"); /* Reset any capturing, in case caller runs * this identical command again */ @@ -654,6 +660,38 @@ int virCommandRun(virCommandPtr cmd, } +static int virCommandHookImpl(void *data) +{ + virCommandPtr cmd = data; + + if (cmd->hook && + cmd->hook(cmd->opaque) < 0) + return -1; + + if (cmd->handshake) { + char c = 'w'; + VIR_WARN0("Notifying parent for handshake start"); + if (safewrite(cmd->handshakeWait[1], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to notify parent process")); + return -1; + } + VIR_WARN0("Waiting on parent for handshake complete "); + if (saferead(cmd->handshakeNotify[0], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to wait on parent process")); + return -1; + } + if (c != 'n') { + virReportSystemError(EINVAL, _("Unexpected data '%d' from parent process"), (int)c); + return -1; + } + VIR_WARN0("Handshake is done, child is running"); + VIR_FORCE_CLOSE(cmd->handshakeWait[1]); + VIR_FORCE_CLOSE(cmd->handshakeNotify[0]); + } + + return 0; +} + /* * Run the command asynchronously * Returns -1 on any error executing the @@ -689,8 +727,8 @@ int virCommandRunAsync(virCommandPtr cmd, cmd->outfdptr, cmd->errfdptr, cmd->flags, - cmd->hook, - cmd->opaque, + virCommandHookImpl, + cmd, cmd->pidfile); VIR_DEBUG("Command result %d, with PID is %d", @@ -808,6 +846,45 @@ char *virCommandToString(virCommandPtr cmd) } +void virCommandRequireHandshake(virCommandPtr cmd) +{ + if (pipe(cmd->handshakeWait) < 0) + cmd->has_error = errno; + if (pipe(cmd->handshakeNotify) < 0) { + VIR_FORCE_CLOSE(cmd->handshakeWait[0]); + VIR_FORCE_CLOSE(cmd->handshakeWait[1]); + } + + virCommandPreserveFD(cmd, cmd->handshakeWait[1]); + virCommandPreserveFD(cmd, cmd->handshakeNotify[0]); + cmd->handshake = true; +} + +int virCommandHandshakeWait(virCommandPtr cmd) +{ + char c; + if (saferead(cmd->handshakeWait[0], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to wait for child process")); + return -1; + } + if (c != 'w') { + virReportSystemError(EINVAL, _("Unexpected data '%d' from child process"), (int)c); + return -1; + } + return 0; +} + +int virCommandHandshakeNotify(virCommandPtr cmd) +{ + char c = 'n'; + if (safewrite(cmd->handshakeNotify[1], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to notify child process")); + return -1; + } + return 0; +} + + /* * Release all resources * @@ -832,6 +909,13 @@ void virCommandFree(virCommandPtr cmd) VIR_FREE(cmd->env[i]); VIR_FREE(cmd->env); + if (cmd->handshake) { + VIR_FORCE_CLOSE(cmd->handshakeWait[0]); + VIR_FORCE_CLOSE(cmd->handshakeWait[1]); + VIR_FORCE_CLOSE(cmd->handshakeNotify[0]); + VIR_FORCE_CLOSE(cmd->handshakeNotify[1]); + } + VIR_FREE(cmd->pidfile); VIR_FREE(cmd); diff --git a/src/util/command.h b/src/util/command.h index 8b99361..f1a45b1 100644 --- a/src/util/command.h +++ b/src/util/command.h @@ -193,6 +193,11 @@ int virCommandWait(virCommandPtr cmd, void virCommandWriteArgLog(virCommandPtr cmd, int fd); +void virCommandRequireHandshake(virCommandPtr cmd); + +int virCommandHandshakeWait(virCommandPtr cmd); +int virCommandHandshakeNotify(virCommandPtr cmd); + /* * Release all resources */ -- 1.7.2.3

On 11/22/2010 11:09 AM, Daniel P. Berrange wrote: [reviving this thread a bit]
Allow the parent process to perform a bi-directional handshake with the child process during fork/exec. The child process will fork and do its initial setup. Immediately prior to the exec(), it will stop & wait for a handshake from the parent process. The parent process will spawn the child and wait until the child reaches the handshake point. It will do whatever extra setup work is required, before signalling the child to continue.
The implementation of this is done using two pairs of blocking pipes. The first pair is used to block the parent, until the child writes a single byte. Then the second pair pair is used to block the child, until the parent confirms with another single byte.
* src/util/command.c, src/util/command.h, src/libvirt_private.syms: Add APIs to perform a handshake --- src/libvirt_private.syms | 3 + src/util/command.c | 92 ++++++++++++++++++++++++++++++++++++++++++++-- src/util/command.h | 5 ++ 3 files changed, 96 insertions(+), 4 deletions(-)
@@ -626,8 +632,8 @@ int virCommandRun(virCommandPtr cmd, ret = -1;
VIR_DEBUG("Result stdout: '%s' stderr: '%s'", - NULLSTR(*cmd->outbuf), - NULLSTR(*cmd->errbuf)); + cmd->outbuf ? NULLSTR(*cmd->outbuf) : "(null)", + cmd->errbuf ? NULLSTR(*cmd->errbuf) : "(null)");
Ah - I see you stumbled on the same problem that has already been fixed. You won't need this hunk :)
+static int virCommandHookImpl(void *data) +{
Hmm, I already implemented a static function virCommandHook() for managing cwd swapping; this should be merged into that function.
+ virCommandPtr cmd = data; + + if (cmd->hook && + cmd->hook(cmd->opaque) < 0) + return -1; + + if (cmd->handshake) { + char c = 'w'; + VIR_WARN0("Notifying parent for handshake start"); + if (safewrite(cmd->handshakeWait[1], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to notify parent process")); + return -1; + } + VIR_WARN0("Waiting on parent for handshake complete ");
trailing space
+ if (saferead(cmd->handshakeNotify[0], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to wait on parent process")); + return -1; + } + if (c != 'n') { + virReportSystemError(EINVAL, _("Unexpected data '%d' from parent process"), (int)c); + return -1; + } + VIR_WARN0("Handshake is done, child is running"); + VIR_FORCE_CLOSE(cmd->handshakeWait[1]); + VIR_FORCE_CLOSE(cmd->handshakeNotify[0]); + } + + return 0; +}
Looks correct for the hook.
+void virCommandRequireHandshake(virCommandPtr cmd) +{
if (!cmd || cmd->has_error) return;
+ if (pipe(cmd->handshakeWait) < 0) + cmd->has_error = errno; + if (pipe(cmd->handshakeNotify) < 0) { + VIR_FORCE_CLOSE(cmd->handshakeWait[0]); + VIR_FORCE_CLOSE(cmd->handshakeWait[1]); + }
Oops; forgot to set cmd->has_error in this case. And this is the first instance of setting cmd->has_error to an errno other than ENOMEM; we'll have to make sure virCommandRun() does the right thing in this case. Also, we should have a sanity check that use of RequireHandshake entails that virCommandRunAsync (and not virCommandRun) is called, so that we don't deadlock. Can RequireHandshake and Daemonize be mixed, or should our sanity checks declare them as incompatible?
+ + virCommandPreserveFD(cmd, cmd->handshakeWait[1]); + virCommandPreserveFD(cmd, cmd->handshakeNotify[0]);
Would these be better as virCommandTransferFD()?
+ cmd->handshake = true; +} + +int virCommandHandshakeWait(virCommandPtr cmd) +{ + char c;
Needs error checking that the command has been started but not yet waited on (that is, cmd->pid should be non-negative).
+ if (saferead(cmd->handshakeWait[0], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to wait for child process")); + return -1; + } + if (c != 'w') { + virReportSystemError(EINVAL, _("Unexpected data '%d' from child process"), (int)c); + return -1; + } + return 0; +} + +int virCommandHandshakeNotify(virCommandPtr cmd) +{ + char c = 'n';
Likewise. Should this also check that handshakewait has been called first?
+ if (safewrite(cmd->handshakeNotify[1], &c, sizeof(c)) != sizeof(c)) { + virReportSystemError(errno, "%s", _("Unable to notify child process")); + return -1; + } + return 0; +} + +
+void virCommandRequireHandshake(virCommandPtr cmd); + +int virCommandHandshakeWait(virCommandPtr cmd); +int virCommandHandshakeNotify(virCommandPtr cmd);
Should these last two be marked ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK? Do you want me to take over modernizing this patch, and updating tests/commandtest.c to exercise it, since I've been doing other virCommand code? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Perform a handshake during QEMU startup to allow file relabelling to be performed after QEMU has been forked, but before it has been exec'd. This is to allow the lock manager to acquire locks against the QEMU pid, before relabelling takes place * src/qemu/qemu_driver.c: Handshake during QEMU startup --- src/qemu/qemu_driver.c | 23 ++++++++++++++--------- 1 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0b0b6fe..bf6cd0e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3683,9 +3683,6 @@ static int qemudSecurityHook(void *data) { if (virSecurityManagerSetProcessLabel(h->driver->securityManager, h->vm) < 0) goto cleanup; - /* XXX temp hack to let disk labelling complete */ - sleep(10); - ret = 0; cleanup: @@ -4097,28 +4094,36 @@ static int qemudStartVMDaemon(virConnectPtr conn, virCommandSetErrorFD(cmd, &logfile); virCommandSetPidFile(cmd, pidfile); virCommandDaemonize(cmd); + virCommandRequireHandshake(cmd); ret = virCommandRun(cmd, NULL); - VIR_WARN("Executing done %s", vm->def->emulator); VIR_FREE(pidfile); - /* XXX this is bollocks. Need a sync point */ - sleep(5); + VIR_WARN0("Waiting for handshake from child"); + if (virCommandHandshakeWait(cmd) < 0) { + ret = -1; + goto cleanup; + } + VIR_WARN0("Got handshake start, doing locks"); - VIR_WARN("Locking %s", vm->def->emulator); if (!(lock = virDomainLockForStartup(driver->contentLockManager, driver->metadataLockManager, NULL, /* XXX lock state */ vm))) goto cleanup; - VIR_WARN("Labelling %s", vm->def->emulator); + VIR_WARN0("Got Locks, doing labelling"); DEBUG0("Setting domain security labels"); if (virSecurityManagerSetAllLabel(driver->securityManager, vm, stdin_path) < 0) goto cleanup; - VIR_WARN("All done %s", vm->def->emulator); + VIR_WARN0("Labelling done, completing hanshake to child"); + if (virCommandHandshakeNotify(cmd) < 0) { + ret = -1; + goto cleanup; + } + VIR_WARN0("Handshake complete, child running"); /* wait for qemu process to to show up */ if (ret == 0) { -- 1.7.2.3

On 11/22/2010 11:09 AM, Daniel P. Berrange wrote:
Perform a handshake during QEMU startup to allow file relabelling to be performed after QEMU has been forked, but before it has been exec'd. This is to allow the lock manager to acquire locks against the QEMU pid, before relabelling takes place
* src/qemu/qemu_driver.c: Handshake during QEMU startup --- src/qemu/qemu_driver.c | 23 ++++++++++++++--------- 1 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0b0b6fe..bf6cd0e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3683,9 +3683,6 @@ static int qemudSecurityHook(void *data) { if (virSecurityManagerSetProcessLabel(h->driver->securityManager, h->vm) < 0) goto cleanup;
- /* XXX temp hack to let disk labelling complete */ - sleep(10); - ret = 0;
cleanup: @@ -4097,28 +4094,36 @@ static int qemudStartVMDaemon(virConnectPtr conn, virCommandSetErrorFD(cmd, &logfile); virCommandSetPidFile(cmd, pidfile); virCommandDaemonize(cmd); + virCommandRequireHandshake(cmd);
ret = virCommandRun(cmd, NULL);
Oh, I see - you want to daemonize, require handshake, AND do a blocking virCommandRun. I see now - __virExec does two forks when daemonize is requested, and only runs the callback hook in the second (daemon) child, rather than in the (short-lived) intermediary. That answers some of my questions in the previous patch.
- VIR_WARN("Executing done %s", vm->def->emulator); VIR_FREE(pidfile);
- /* XXX this is bollocks. Need a sync point */ - sleep(5); + VIR_WARN0("Waiting for handshake from child"); + if (virCommandHandshakeWait(cmd) < 0) {
But it still probably requires virCommand to do some sanity checking that HandshakeWait is called after the child has been spawned.
+ VIR_WARN0("Labelling done, completing hanshake to child");
s/hanshake/handshake/ Definitely an improvement. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

--- docs/internals-locking.html.in | 301 ++++++++++++++++++++++++++++++++++++++++ 1 files changed, 301 insertions(+), 0 deletions(-) create mode 100644 docs/internals-locking.html.in diff --git a/docs/internals-locking.html.in b/docs/internals-locking.html.in new file mode 100644 index 0000000..90054f0 --- /dev/null +++ b/docs/internals-locking.html.in @@ -0,0 +1,301 @@ +<html> + <body> + <h1>Resource Lock Manager</h1> + + <ul id="toc"></ul> + + <p> + This page describes the design of the resource lock manager + that is used for locking disk images with the QEMU driver. + </p> + + <h2><a name="goals">Goals</a></h2> + + <p> + The high level goal is to prevent the same disk image being + used by more than one QEMU instance at a time (unless the + disk is marked as sharable, or readonly). The scenarios + to be prevented are thus: + </p> + + <ol> + <li> + Two different guests running configured to point at the + same disk image. + </li> + <li> + One guest being started more than once on two different + machines due to admin mistake + </li> + <li> + One guest being started more than once on a single machine + due to libvirt driver bug on aa single machine. + </li> + </ol> + + <h2><a name="requirement">Requirements</a></h2> + + <p> + The high level goal leads to a set of requirements + for the lock manager design + </p> + + <ol> + <li> + A lock must be held on a disk whenever a QEMU process + has the disk open + </li> + <li> + The lock scheme must allow QEMU to be configured with + readonly, shared write, or exclusive writable disks + </li> + <li> + A lock must be held on a disk whenever libvirtd makes + changes to user/group ownership and SELinux labelling. + </li> + <li> + At least one locking impl must allow use of libvirtd on + a single host without any admin config tasks + </li> + <li> + A lock handover must be performed during the migration + process where 2 QEMU processes will have the same disk + open concurrently. + </li> + <li> + The lock manager must be able to identify and kill the + process accessing the resource if the lock is revoked. + </li> + </ol> + + <h2><a name="design">Design</a></h2> + + <p> + The requirements call for a design with two distinct lockspaces: + </p> + + <ol> + <li> + The <strong>primary lockspace</strong> is used to protect the content of + disk images. This will honour the disk sharing modes to + allow readonly/shared disk to be assigned to multiple + guests concurrently. + </li> + <li> + The <strong>secondary lockspace</strong> is used to protect the metadata + of disk images. This lock will be held whenever file + permissions / ownership / attributes are changed, and + is always exclusive, regardless of sharing mode. The + primary lock will be held prior to obtaining the secondary + lock. + </li> + </ol> + + <p> + Within each lockspace the following operations will need to be + supported + </p> + + <ul> + <li> + <strong>Acquire object lock</strong> + Acquire locks on all resources initially + registered against an object + </li> + <li> + <strong>Release object lock</strong> + Release locks on all resources currently + registered against an object + </li> + <li> + <strong>Associate object lock</strong> + Associate the current process with an existing + set of locks for an object + </li> + <li> + <strong>Deassociate object lock</strong> + Deassociate the current process with an + existing set of locks for an object. + </li> + <li> + <strong>Register resource</strong> + Register an initial resource against an object + </li> + <li> + <strong>Get object lock state</strong> + Obtain an representation of the current object + lock state. + </li> + <li> + <strong>Acquire a resource lock</strong> + Register and acquire a lock for a resource + to be added to a locked object. + </li> + <li> + <strong>Release a resource lock</strong> + Dereigster and release a lock for a resource + to be removed from a lock object + </li> + </ul> + + <h2><a name="impl">Plugin Implementations</a></h2> + + <p> + Lock manager implementations are provided as LGPLv2+ + licensed, dlopen()able library modules. A different + lock manager implementation may be used + for the primary and secondary lockspaces. With the + QEMU driver, these can be configured via the + <code>/etc/libvirt/qemu.conf</code> configuration + file by specifying the lock manager name. + </p> + + <pre> + contentLockManager="fcntl" + metadataLockManager="fcntl" + </pre> + + <p> + Lock manager implmentations are free to support + both content and metadata locks, however, if the + plugin author is only able to handle one lockspace, + the other can be delegated to the standard fcntl + lock manager. The QEMU driver will load the lock + manager plugin binaries from the following location + </p> + + <pre> +/usr/{lib,lib64}/libvirt/lock_manager/$NAME.so +</pre> + + <p> + The lock manager plugin must export a single ELF + symbol named <code>virLockDriverImpl</code>, which is + a static instance of the <code>virLockDriver</code> + struct. The struct is defined in the header file + </p> + + <pre> + #include <libvirt/plugins/lock_manager.h> + </pre> + + <p> + All callbacks in the struct must be initialized + to non-NULL pointers. The semantics of each + callback are defined in the API docs embedded + in the previously mentioned header file + </p> + + <h2><a name="usagePatterns">Lock usage patterns</a></h2> + + <p> + The following psuedo code illustrates the common + patterns of operations invoked on the lock + manager plugin callbacks. + </p> + + <h3><a name="usageLockAcquire">Lock acquisition</a></h3> + + <p> + Lock acquisition will always be performed from the + process that is to own the lock. This is typically + the QEMU child process, in between the fork+exec + pairing, but it may occassionally be held directly + by libvirtd. + </p> + + <pre> + mgr = virLockManagerNew(lockPlugin, + VIR_LOCK_MANAGER_MODE_CONTENT, + VIR_LOCK_MANAGER_TYPE_DOMAIN); + virLockManagerSetParameter(mgr, "uuid", $uuid); + virLockManagerSetParameter(mgr, "name", $name); + + foreach (initial disks) + virLockManagerAddResource(mgr, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + $path, $flags); + + if (virLockManagerAcquireObject(mgr) < 0) + ...abort... + </pre> + + <p> + The lock is implicitly released when the process + that acquired it exits, however, a process may + voluntarily give up the lock by running + </p> + + <pre> + virLockManagerReleaseObject(mgr); + </pre> + + <h3><a name="usageLockAttach">Lock attachment</a></h3> + + <p> + Any time a process needs todo work on behalf of + another process that holds a lock, it will associate + itself with the existing lock. This sequence is + identical to the previous one, except for the + last step. + </p> + + + <pre> + mgr = virLockManagerNew(contentLock, + VIR_LOCK_MANAGER_MODE_CONTENT, + VIR_LOCK_MANAGER_TYPE_DOMAIN); + virLockManagerSetParameter(mgr, "uuid", $uuid); + virLockManagerSetParameter(mgr, "name", $name); + + foreach (current disks) + virLockManagerAddResource(mgr, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + $path, $flags); + + if (virLockManagerAttachObject(mgr, $pid) < 0) + ...abort... + </pre> + + <p> + A lock association will always be explicitly broken + by running + </p> + + <pre> + virLockManagerDetachObject(mgr, $pid); + </pre> + + + <h3><a name="usageLiveResourceChange">Live resource changes</a></h3> + + <p> + When adding a resource to an existing locked object (eg to + hotplug a disk into a VM), the lock manager will first + attach to the locked object, acquire a lock on the + new resource, then detach from the locked object. + </p> + + <pre> + ... initial glue ... + if (virLockManagerAttachObject(mgr, $pid) < 0) + ...abort... + + if (virLockManagerAcquireResource(mgr, + VIR_LOCK_MANAGER_RESOURCE_TYPE_DISK, + $path, $flags) < 0) + ...abort... + + ...assign resource to object + + virLockManagerDetachObject(mgr, $pid) + </pre> + + <p> + Removing a resource from an existing object is an identical + process, but with <code>virLockManagerReleaseResource</code> + invoked instead + </p> + + </body> +</html> -- 1.7.2.3
participants (3)
-
Daniel P. Berrange
-
David Teigland
-
Eric Blake