From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> Introduce a qemuCapsNewForBinary() API which creates a new QEMU capabilities object, populated with data relating to a specific QEMU binary. The qemuCaps object is also given a timestamp, which makes it possible to detect when the cached capabilities for a binary are out of date Signed-off-by: Daniel P. Berrange <berrange@xxxxxxxxxx> --- src/qemu/qemu_capabilities.c | 142 +++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 3 + 2 files changed, 145 insertions(+) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 97aeac7..e8b5797 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -181,6 +181,9 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, struct _qemuCaps { virObject object; + char *binary; + time_t mtime; + virBitmapPtr flags; unsigned int version; @@ -1790,6 +1793,8 @@ void qemuCapsDispose(void *obj) VIR_FREE(caps->cpuDefinitions); virBitmapFree(caps->flags); + + VIR_FREE(caps->binary); } void @@ -1889,3 +1894,140 @@ const char *qemuCapsGetCanonicalMachine(qemuCapsPtr caps, return name; } + + +#define QEMU_SYSTEM_PREFIX "qemu-system-" + +qemuCapsPtr qemuCapsNewForBinary(const char *binary) +{ + qemuCapsPtr caps = qemuCapsNew(); + const char *tmp; + struct utsname ut; + unsigned int is_kvm; + char *help = NULL; + virCommandPtr cmd = NULL; + virCapsGuestMachinePtr *machines = NULL; + size_t nmachines; + size_t i; + struct stat sb; + + if (!(caps->binary = strdup(binary))) + goto no_memory; + + tmp = strstr(binary, QEMU_SYSTEM_PREFIX); + if (tmp) { + tmp += strlen(QEMU_SYSTEM_PREFIX); + } else { + uname_normalize(&ut); + tmp = ut.machine; + } + if (!(caps->arch = strdup(tmp))) + goto no_memory; + + /* We would also want to check faccessat if we cared about ACLs, + * but we don't. */ + if (stat(binary, &sb) < 0) { + virReportSystemError(errno, _("Cannot check QEMU binary %s"), + binary); + goto error; + } + if (!(S_ISREG(sb.st_mode) && (sb.st_mode & 0111) != 0)) { + errno = S_ISDIR(sb.st_mode) ? EISDIR : EACCES; + virReportSystemError(errno, _("QEMU binary %s is not executable"), + binary); + goto error; + } + caps->mtime = sb.st_mtime; + + /* 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 it's hard to feed back a useful error. + */ + if (!virFileIsExecutable(binary)) { + goto error; + } + + cmd = qemuCapsProbeCommand(binary, NULL); + virCommandAddArgList(cmd, "-help", NULL); + virCommandSetOutputBuffer(cmd, &help); + + if (virCommandRun(cmd, NULL) < 0) + goto error; + + if (qemuCapsParseHelpStr(binary, help, caps, + &caps->version, + &is_kvm, + &caps->kvmVersion, + false) < 0) + goto error; + + /* Currently only x86_64 and i686 support PCI-multibus. */ + if (STREQLEN(caps->arch, "x86_64", 6) || + STREQLEN(caps->arch, "i686", 4)) { + qemuCapsSet(caps, QEMU_CAPS_PCI_MULTIBUS); + } + + /* S390 and probably other archs do not support no-acpi - + maybe the qemu option parsing should be re-thought. */ + if (STRPREFIX(caps->arch, "s390")) + qemuCapsClear(caps, QEMU_CAPS_NO_ACPI); + + /* qemuCapsExtractDeviceStr will only set additional caps if qemu + * understands the 0.13.0+ notion of "-device driver,". */ + if (qemuCapsGet(caps, QEMU_CAPS_DEVICE) && + strstr(help, "-device driver,?") && + qemuCapsExtractDeviceStr(binary, caps) < 0) + goto error; + + if (qemuCapsProbeCPUModels(binary, caps, caps->arch, + &caps->ncpuDefinitions, + (const char ***)&caps->cpuDefinitions) < 0) + goto error; + + if (qemuCapsProbeMachineTypes(binary, caps, + &machines, &nmachines) < 0) + goto error; + + if (VIR_ALLOC_N(caps->machineTypes, nmachines) < 0) + goto no_memory; + if (VIR_ALLOC_N(caps->machineAliases, nmachines) < 0) + goto no_memory; + caps->nmachineTypes = nmachines; + + for (i = 0 ; i < caps->nmachineTypes ; i++) { + if (machines[i]->canonical) { + caps->machineTypes[i] = machines[i]->canonical; + caps->machineAliases[i] = machines[i]->name; + } else { + caps->machineTypes[i] = machines[i]->name; + } + } + VIR_FREE(machines); + +cleanup: + VIR_FREE(help); + virCommandFree(cmd); + return caps; + +no_memory: + virReportOOMError(); +error: + virCapabilitiesFreeMachines(machines, nmachines); + virObjectUnref(caps); + caps = NULL; + goto cleanup; +} + + +bool qemuCapsIsValid(qemuCapsPtr caps) +{ + struct stat sb; + + if (!caps->binary) + return true; + + if (stat(caps->binary, &sb) < 0) + return false; + + return sb.st_mtime == caps->mtime; +} diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 9d31094..68d71a9 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -150,6 +150,7 @@ typedef qemuCaps *qemuCapsPtr; qemuCapsPtr qemuCapsNew(void); qemuCapsPtr qemuCapsNewCopy(qemuCapsPtr caps); +qemuCapsPtr qemuCapsNewForBinary(const char *binary); void qemuCapsSet(qemuCapsPtr caps, enum qemuCapsFlags flag) ATTRIBUTE_NONNULL(1); @@ -174,6 +175,8 @@ size_t qemuCapsGetMachineTypes(qemuCapsPtr caps, const char *qemuCapsGetCanonicalMachine(qemuCapsPtr caps, const char *name); +bool qemuCapsIsValid(qemuCapsPtr caps); + virCapsPtr qemuCapsInit(virCapsPtr old_caps); int qemuCapsProbeMachineTypes(const char *binary, -- 1.7.11.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list