From: "Daniel P. Berrange" <berrange(a)redhat.com>
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(a)redhat.com>
---
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