2010-12-16 09:07:07 -06:00
|
|
|
/*
|
|
|
|
* qemu_capabilities.c: QEMU capabilities generation
|
|
|
|
*
|
2012-01-17 06:44:18 -06:00
|
|
|
* Copyright (C) 2006-2012 Red Hat, Inc.
|
2010-12-16 09:07:07 -06:00
|
|
|
* Copyright (C) 2006 Daniel P. Berrange
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 17:30:55 -05:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 05:06:23 -05:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2010-12-16 09:07:07 -06:00
|
|
|
*
|
|
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include "qemu_capabilities.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "logging.h"
|
|
|
|
#include "virterror_internal.h"
|
|
|
|
#include "util.h"
|
2011-07-19 13:32:58 -05:00
|
|
|
#include "virfile.h"
|
2012-10-02 03:59:56 -05:00
|
|
|
#include "virpidfile.h"
|
|
|
|
#include "virprocess.h"
|
2010-12-16 09:07:07 -06:00
|
|
|
#include "nodeinfo.h"
|
|
|
|
#include "cpu/cpu.h"
|
|
|
|
#include "domain_conf.h"
|
2011-01-12 17:26:34 -06:00
|
|
|
#include "command.h"
|
2012-09-13 09:36:56 -05:00
|
|
|
#include "bitmap.h"
|
2011-11-29 08:42:58 -06:00
|
|
|
#include "virnodesuspend.h"
|
2012-08-22 12:05:08 -05:00
|
|
|
#include "qemu_monitor.h"
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/utsname.h>
|
2011-02-08 08:22:39 -06:00
|
|
|
#include <stdarg.h>
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
|
|
|
2011-05-04 06:55:38 -05:00
|
|
|
/* While not public, these strings must not change. They
|
|
|
|
* are used in domain status files which are read on
|
|
|
|
* daemon restarts
|
|
|
|
*/
|
|
|
|
VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
|
|
|
|
"kqemu", /* 0 */
|
|
|
|
"vnc-colon",
|
|
|
|
"no-reboot",
|
|
|
|
"drive",
|
|
|
|
"drive-boot",
|
|
|
|
|
|
|
|
"name", /* 5 */
|
|
|
|
"uuid",
|
|
|
|
"domid",
|
|
|
|
"vnet-hdr",
|
|
|
|
"migrate-kvm-stdio",
|
|
|
|
|
|
|
|
"migrate-qemu-tcp", /* 10 */
|
|
|
|
"migrate-qemu-exec",
|
|
|
|
"drive-cache-v2",
|
|
|
|
"kvm",
|
|
|
|
"drive-format",
|
|
|
|
|
|
|
|
"vga", /* 15 */
|
|
|
|
"0.10",
|
|
|
|
"pci-device",
|
|
|
|
"mem-path",
|
|
|
|
"drive-serial",
|
|
|
|
|
|
|
|
"xen-domid", /* 20 */
|
|
|
|
"migrate-qemu-unix",
|
|
|
|
"chardev",
|
|
|
|
"enable-kvm",
|
|
|
|
"monitor-json",
|
|
|
|
|
|
|
|
"balloon", /* 25 */
|
|
|
|
"device",
|
|
|
|
"sdl",
|
|
|
|
"smp-topology",
|
|
|
|
"netdev",
|
|
|
|
|
|
|
|
"rtc", /* 30 */
|
2011-05-23 12:38:32 -05:00
|
|
|
"vhost-net",
|
2011-05-04 06:55:38 -05:00
|
|
|
"rtc-td-hack",
|
|
|
|
"no-hpet",
|
|
|
|
"no-kvm-pit",
|
|
|
|
|
|
|
|
"tdf", /* 35 */
|
|
|
|
"pci-configfd",
|
|
|
|
"nodefconfig",
|
|
|
|
"boot-menu",
|
|
|
|
"enable-kqemu",
|
|
|
|
|
|
|
|
"fsdev", /* 40 */
|
|
|
|
"nesting",
|
|
|
|
"name-process",
|
|
|
|
"drive-readonly",
|
|
|
|
"smbios-type",
|
|
|
|
|
|
|
|
"vga-qxl", /* 45 */
|
|
|
|
"spice",
|
|
|
|
"vga-none",
|
|
|
|
"migrate-qemu-fd",
|
|
|
|
"boot-index",
|
|
|
|
|
|
|
|
"hda-duplex", /* 50 */
|
|
|
|
"drive-aio",
|
|
|
|
"pci-multibus",
|
|
|
|
"pci-bootindex",
|
|
|
|
"ccid-emulated",
|
|
|
|
|
|
|
|
"ccid-passthru", /* 55 */
|
|
|
|
"chardev-spicevmc",
|
|
|
|
"device-spicevmc",
|
|
|
|
"virtio-tx-alg",
|
|
|
|
"device-qxl-vga",
|
2011-05-09 01:59:16 -05:00
|
|
|
|
|
|
|
"pci-multifunction", /* 60 */
|
2011-06-20 03:26:47 -05:00
|
|
|
"virtio-blk-pci.ioeventfd",
|
2011-07-08 02:56:17 -05:00
|
|
|
"sga",
|
2011-08-13 01:32:45 -05:00
|
|
|
"virtio-blk-pci.event_idx",
|
|
|
|
"virtio-net-pci.event_idx",
|
2011-09-02 07:56:50 -05:00
|
|
|
|
|
|
|
"cache-directsync", /* 65 */
|
|
|
|
"piix3-usb-uhci",
|
|
|
|
"piix4-usb-uhci",
|
|
|
|
"usb-ehci",
|
|
|
|
"ich9-usb-ehci1",
|
|
|
|
|
|
|
|
"vt82c686b-usb-uhci", /* 70 */
|
|
|
|
"pci-ohci",
|
|
|
|
"usb-redir",
|
2011-09-02 09:20:40 -05:00
|
|
|
"usb-hub",
|
2011-09-21 03:25:29 -05:00
|
|
|
"no-shutdown",
|
2011-09-22 14:33:47 -05:00
|
|
|
|
|
|
|
"cache-unsafe", /* 75 */
|
2011-09-20 12:31:52 -05:00
|
|
|
"rombar",
|
2011-09-27 22:46:08 -05:00
|
|
|
"ich9-ahci",
|
2011-12-19 19:08:29 -06:00
|
|
|
"no-acpi",
|
2011-12-21 09:51:29 -06:00
|
|
|
"fsdev-readonly",
|
2011-11-29 12:37:27 -06:00
|
|
|
|
2011-12-21 06:47:17 -06:00
|
|
|
"virtio-blk-pci.scsi", /* 80 */
|
2011-11-29 12:37:27 -06:00
|
|
|
"blk-sg-io",
|
2012-01-12 03:31:14 -06:00
|
|
|
"drive-copy-on-read",
|
2011-12-21 06:47:17 -06:00
|
|
|
"cpu-host",
|
2012-01-17 06:44:18 -06:00
|
|
|
"fsdev-writeout",
|
2012-01-18 10:42:33 -06:00
|
|
|
|
|
|
|
"drive-iotune", /* 85 */
|
2012-02-13 05:19:24 -06:00
|
|
|
"system_wakeup",
|
2012-02-27 04:20:21 -06:00
|
|
|
"scsi-disk.channel",
|
2012-03-12 09:19:56 -05:00
|
|
|
"scsi-block",
|
2012-03-14 17:29:21 -05:00
|
|
|
"transaction",
|
2012-04-11 16:40:16 -05:00
|
|
|
|
|
|
|
"block-job-sync", /* 90 */
|
|
|
|
"block-job-async",
|
2012-04-17 04:08:05 -05:00
|
|
|
"scsi-cd",
|
2012-04-17 04:16:52 -05:00
|
|
|
"ide-cd",
|
2012-04-26 05:11:49 -05:00
|
|
|
"no-user-config",
|
2012-05-15 17:55:08 -05:00
|
|
|
|
|
|
|
"hda-micro", /* 95 */
|
2012-06-11 22:06:33 -05:00
|
|
|
"dump-guest-memory",
|
2012-06-21 08:45:25 -05:00
|
|
|
"nec-usb-xhci",
|
2012-06-29 10:02:05 -05:00
|
|
|
"virtio-s390",
|
2012-07-12 10:45:57 -05:00
|
|
|
"balloon-event",
|
2012-05-15 17:55:08 -05:00
|
|
|
|
2012-08-03 15:33:05 -05:00
|
|
|
"bridge", /* 100 */
|
2012-08-08 01:25:24 -05:00
|
|
|
"lsi",
|
|
|
|
"virtio-scsi-pci",
|
2012-09-04 09:30:55 -05:00
|
|
|
"blockio",
|
2012-08-02 05:14:39 -05:00
|
|
|
"disable-s3",
|
2012-08-03 15:33:05 -05:00
|
|
|
|
2012-08-02 05:14:39 -05:00
|
|
|
"disable-s4", /* 105 */
|
2012-08-19 10:42:44 -05:00
|
|
|
"usb-redir.filter",
|
2012-09-17 21:12:11 -05:00
|
|
|
"ide-drive.wwn",
|
|
|
|
"scsi-disk.wwn",
|
2012-09-18 02:24:51 -05:00
|
|
|
"seccomp-sandbox",
|
2012-09-18 05:31:30 -05:00
|
|
|
|
|
|
|
"reboot-timeout", /* 110 */
|
2012-08-15 02:59:24 -05:00
|
|
|
"dump-guest-core",
|
2012-09-20 04:15:31 -05:00
|
|
|
"seamless-migration",
|
2011-05-04 06:55:38 -05:00
|
|
|
);
|
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
struct _qemuCaps {
|
|
|
|
virObject object;
|
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
bool usedQMP;
|
|
|
|
|
2012-09-10 05:47:56 -05:00
|
|
|
char *binary;
|
|
|
|
time_t mtime;
|
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
virBitmapPtr flags;
|
2012-08-22 05:11:28 -05:00
|
|
|
|
|
|
|
unsigned int version;
|
|
|
|
unsigned int kvmVersion;
|
|
|
|
|
|
|
|
char *arch;
|
|
|
|
|
|
|
|
size_t ncpuDefinitions;
|
|
|
|
char **cpuDefinitions;
|
|
|
|
|
|
|
|
size_t nmachineTypes;
|
|
|
|
char **machineTypes;
|
|
|
|
char **machineAliases;
|
2012-08-20 11:44:14 -05:00
|
|
|
};
|
|
|
|
|
2012-08-22 07:54:13 -05:00
|
|
|
struct _qemuCapsCache {
|
|
|
|
virMutex lock;
|
|
|
|
virHashTablePtr binaries;
|
2012-08-22 12:05:08 -05:00
|
|
|
char *libDir;
|
2012-10-02 03:59:56 -05:00
|
|
|
char *runDir;
|
2012-08-22 07:54:13 -05:00
|
|
|
};
|
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
|
|
|
|
static virClassPtr qemuCapsClass;
|
|
|
|
static void qemuCapsDispose(void *obj);
|
|
|
|
|
|
|
|
static int qemuCapsOnceInit(void)
|
|
|
|
{
|
|
|
|
if (!(qemuCapsClass = virClassNew("qemuCaps",
|
|
|
|
sizeof(qemuCaps),
|
|
|
|
qemuCapsDispose)))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_ONCE_GLOBAL_INIT(qemuCaps)
|
|
|
|
|
2012-08-20 10:05:45 -05:00
|
|
|
static virCommandPtr
|
|
|
|
qemuCapsProbeCommand(const char *qemu,
|
|
|
|
qemuCapsPtr caps)
|
|
|
|
{
|
|
|
|
virCommandPtr cmd = virCommandNew(qemu);
|
|
|
|
|
|
|
|
if (caps) {
|
|
|
|
if (qemuCapsGet(caps, QEMU_CAPS_NO_USER_CONFIG))
|
|
|
|
virCommandAddArg(cmd, "-no-user-config");
|
|
|
|
else if (qemuCapsGet(caps, QEMU_CAPS_NODEFCONFIG))
|
|
|
|
virCommandAddArg(cmd, "-nodefconfig");
|
|
|
|
}
|
|
|
|
|
|
|
|
virCommandAddEnvPassCommon(cmd);
|
|
|
|
virCommandClearCaps(cmd);
|
|
|
|
|
|
|
|
return cmd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-16 09:07:07 -06:00
|
|
|
/* Format is:
|
|
|
|
* <machine> <desc> [(default)|(alias of <canonical>)]
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuCapsParseMachineTypesStr(const char *output,
|
2012-08-22 11:23:00 -05:00
|
|
|
qemuCapsPtr caps)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
const char *p = output;
|
|
|
|
const char *next;
|
2012-08-22 11:23:00 -05:00
|
|
|
size_t defIdx = 0;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
do {
|
|
|
|
const char *t;
|
2012-08-22 11:23:00 -05:00
|
|
|
char *name;
|
|
|
|
char *canonical = NULL;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if ((next = strchr(p, '\n')))
|
|
|
|
++next;
|
|
|
|
|
|
|
|
if (STRPREFIX(p, "Supported machines are:"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!(t = strchr(p, ' ')) || (next && t >= next))
|
|
|
|
continue;
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (!(name = strndup(p, t - p)))
|
2010-12-16 09:07:07 -06:00
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
p = t;
|
2012-08-22 11:23:00 -05:00
|
|
|
if (!(t = strstr(p, "(default)")) && (!next || t < next))
|
|
|
|
defIdx = caps->nmachineTypes;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if ((t = strstr(p, "(alias of ")) && (!next || t < next)) {
|
|
|
|
p = t + strlen("(alias of ");
|
|
|
|
if (!(t = strchr(p, ')')) || (next && t >= next))
|
|
|
|
continue;
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (!(canonical = strndup(p, t - p))) {
|
|
|
|
VIR_FREE(name);
|
2010-12-16 09:07:07 -06:00
|
|
|
goto no_memory;
|
2012-08-22 11:23:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(caps->machineTypes, caps->nmachineTypes + 1) < 0 ||
|
|
|
|
VIR_REALLOC_N(caps->machineAliases, caps->nmachineTypes + 1) < 0) {
|
|
|
|
VIR_FREE(name);
|
|
|
|
VIR_FREE(canonical);
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
caps->nmachineTypes++;
|
|
|
|
if (canonical) {
|
|
|
|
caps->machineTypes[caps->nmachineTypes-1] = canonical;
|
|
|
|
caps->machineAliases[caps->nmachineTypes-1] = name;
|
|
|
|
} else {
|
|
|
|
caps->machineTypes[caps->nmachineTypes-1] = name;
|
|
|
|
caps->machineAliases[caps->nmachineTypes-1] = NULL;
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
} while ((p = next));
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
|
|
|
|
if (defIdx != 0) {
|
|
|
|
char *name = caps->machineTypes[defIdx];
|
|
|
|
char *alias = caps->machineAliases[defIdx];
|
|
|
|
memmove(caps->machineTypes + 1,
|
|
|
|
caps->machineTypes,
|
|
|
|
sizeof(caps->machineTypes[0]) * defIdx);
|
|
|
|
memmove(caps->machineAliases + 1,
|
|
|
|
caps->machineAliases,
|
|
|
|
sizeof(caps->machineAliases[0]) * defIdx);
|
|
|
|
caps->machineTypes[0] = name;
|
|
|
|
caps->machineAliases[0] = alias;
|
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
no_memory:
|
2010-12-16 09:07:07 -06:00
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
static int
|
|
|
|
qemuCapsProbeMachineTypes(qemuCapsPtr caps)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
char *output;
|
2011-01-12 17:26:34 -06:00
|
|
|
int ret = -1;
|
|
|
|
virCommandPtr cmd;
|
2011-02-14 14:00:22 -06:00
|
|
|
int status;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2011-02-02 06:35:31 -06:00
|
|
|
/* 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.
|
|
|
|
*/
|
2012-08-22 11:23:00 -05:00
|
|
|
if (!virFileIsExecutable(caps->binary)) {
|
|
|
|
virReportSystemError(errno, _("Cannot find QEMU binary %s"),
|
|
|
|
caps->binary);
|
2011-02-02 06:35:31 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
cmd = qemuCapsProbeCommand(caps->binary, caps);
|
2012-04-26 08:10:22 -05:00
|
|
|
virCommandAddArgList(cmd, "-M", "?", NULL);
|
2011-01-12 17:26:34 -06:00
|
|
|
virCommandSetOutputBuffer(cmd, &output);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2011-02-14 14:00:22 -06:00
|
|
|
/* Ignore failure from older qemu that did not understand '-M ?'. */
|
|
|
|
if (virCommandRun(cmd, &status) < 0)
|
2010-12-16 09:07:07 -06:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (qemuCapsParseMachineTypesStr(output, caps) < 0)
|
2011-01-12 17:26:34 -06:00
|
|
|
goto cleanup;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2011-01-12 17:26:34 -06:00
|
|
|
VIR_FREE(output);
|
|
|
|
virCommandFree(cmd);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
typedef int
|
|
|
|
(*qemuCapsParseCPUModels)(const char *output,
|
2012-08-22 11:23:00 -05:00
|
|
|
qemuCapsPtr caps);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
/* Format:
|
|
|
|
* <arch> <model>
|
|
|
|
* qemu-0.13 encloses some model names in []:
|
|
|
|
* <arch> [<model>]
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuCapsParseX86Models(const char *output,
|
2012-08-22 11:23:00 -05:00
|
|
|
qemuCapsPtr caps)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
const char *p = output;
|
|
|
|
const char *next;
|
2012-08-22 11:23:00 -05:00
|
|
|
int ret = -1;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
do {
|
|
|
|
const char *t;
|
2012-08-22 11:23:00 -05:00
|
|
|
size_t len;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if ((next = strchr(p, '\n')))
|
|
|
|
next++;
|
|
|
|
|
|
|
|
if (!(t = strchr(p, ' ')) || (next && t >= next))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!STRPREFIX(p, "x86"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
p = t;
|
|
|
|
while (*p == ' ')
|
|
|
|
p++;
|
|
|
|
|
|
|
|
if (*p == '\0' || *p == '\n')
|
|
|
|
continue;
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (VIR_EXPAND_N(caps->cpuDefinitions, caps->ncpuDefinitions, 1) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (next)
|
|
|
|
len = next - p - 1;
|
|
|
|
else
|
|
|
|
len = strlen(p);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (len > 2 && *p == '[' && p[len - 1] == ']') {
|
|
|
|
p++;
|
|
|
|
len -= 2;
|
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (!(caps->cpuDefinitions[caps->ncpuDefinitions - 1] = strndup(p, len))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
} while ((p = next));
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
ret = 0;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
cleanup:
|
|
|
|
return ret;
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
|
2011-10-03 08:01:33 -05:00
|
|
|
/* ppc64 parser.
|
|
|
|
* Format : PowerPC <machine> <description>
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
qemuCapsParsePPCModels(const char *output,
|
2012-08-22 11:23:00 -05:00
|
|
|
qemuCapsPtr caps)
|
2011-10-03 08:01:33 -05:00
|
|
|
{
|
|
|
|
const char *p = output;
|
|
|
|
const char *next;
|
2012-08-22 11:23:00 -05:00
|
|
|
int ret = -1;
|
2011-10-03 08:01:33 -05:00
|
|
|
|
|
|
|
do {
|
|
|
|
const char *t;
|
2012-08-22 11:23:00 -05:00
|
|
|
size_t len;
|
2011-10-03 08:01:33 -05:00
|
|
|
|
|
|
|
if ((next = strchr(p, '\n')))
|
|
|
|
next++;
|
|
|
|
|
|
|
|
if (!STRPREFIX(p, "PowerPC "))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Skip the preceding sub-string "PowerPC " */
|
|
|
|
p += 8;
|
|
|
|
|
|
|
|
/*Malformed string, does not obey the format 'PowerPC <model> <desc>'*/
|
|
|
|
if (!(t = strchr(p, ' ')) || (next && t >= next))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (*p == '\0')
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (*p == '\n')
|
|
|
|
continue;
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (VIR_EXPAND_N(caps->cpuDefinitions, caps->ncpuDefinitions, 1) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2011-10-03 08:01:33 -05:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
len = t - p - 1;
|
2011-10-03 08:01:33 -05:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (!(caps->cpuDefinitions[caps->ncpuDefinitions - 1] = strndup(p, len))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
2011-10-03 08:01:33 -05:00
|
|
|
}
|
|
|
|
} while ((p = next));
|
|
|
|
|
2011-12-09 11:18:58 -06:00
|
|
|
ret = 0;
|
2011-10-03 08:01:33 -05:00
|
|
|
|
2011-12-09 11:18:58 -06:00
|
|
|
cleanup:
|
|
|
|
return ret;
|
2011-10-03 08:01:33 -05:00
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
static int
|
|
|
|
qemuCapsProbeCPUModels(qemuCapsPtr caps)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
char *output = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
qemuCapsParseCPUModels parse;
|
2011-01-12 17:26:34 -06:00
|
|
|
virCommandPtr cmd;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (STREQ(caps->arch, "i686") ||
|
|
|
|
STREQ(caps->arch, "x86_64"))
|
2010-12-16 09:07:07 -06:00
|
|
|
parse = qemuCapsParseX86Models;
|
2012-08-22 11:23:00 -05:00
|
|
|
else if (STREQ(caps->arch, "ppc64"))
|
2011-10-03 08:01:33 -05:00
|
|
|
parse = qemuCapsParsePPCModels;
|
2010-12-16 09:07:07 -06:00
|
|
|
else {
|
2012-08-22 11:23:00 -05:00
|
|
|
VIR_DEBUG("don't know how to parse %s CPU models",
|
|
|
|
caps->arch);
|
2010-12-16 09:07:07 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
cmd = qemuCapsProbeCommand(caps->binary, caps);
|
2012-04-26 08:10:22 -05:00
|
|
|
virCommandAddArgList(cmd, "-cpu", "?", NULL);
|
2011-01-12 17:26:34 -06:00
|
|
|
virCommandSetOutputBuffer(cmd, &output);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2011-01-12 17:26:34 -06:00
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
2010-12-16 09:07:07 -06:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (parse(output, caps) < 0)
|
2010-12-16 09:07:07 -06:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(output);
|
2011-01-12 17:26:34 -06:00
|
|
|
virCommandFree(cmd);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-22 11:59:39 -05:00
|
|
|
static char *
|
|
|
|
qemuCapsFindBinaryForArch(const char *hostarch,
|
|
|
|
const char *guestarch)
|
|
|
|
{
|
|
|
|
char *ret;
|
|
|
|
|
|
|
|
if (STREQ(guestarch, "i686")) {
|
|
|
|
ret = virFindFileInPath("qemu-system-i386");
|
|
|
|
if (ret && !virFileIsExecutable(ret))
|
|
|
|
VIR_FREE(ret);
|
|
|
|
|
|
|
|
if (!ret && STREQ(hostarch, "x86_64")) {
|
|
|
|
ret = virFindFileInPath("qemu-system-x86_64");
|
|
|
|
if (ret && !virFileIsExecutable(ret))
|
|
|
|
VIR_FREE(ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
ret = virFindFileInPath("qemu");
|
|
|
|
} else if (STREQ(guestarch, "itanium")) {
|
|
|
|
ret = virFindFileInPath("qemu-system-ia64");
|
|
|
|
} else {
|
|
|
|
char *bin;
|
|
|
|
if (virAsprintf(&bin, "qemu-system-%s", guestarch) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ret = virFindFileInPath(bin);
|
|
|
|
VIR_FREE(bin);
|
|
|
|
}
|
|
|
|
if (ret && !virFileIsExecutable(ret))
|
|
|
|
VIR_FREE(ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuCapsGetArchWordSize(const char *guestarch)
|
|
|
|
{
|
|
|
|
if (STREQ(guestarch, "i686") ||
|
|
|
|
STREQ(guestarch, "ppc") ||
|
|
|
|
STREQ(guestarch, "sparc") ||
|
|
|
|
STREQ(guestarch, "mips") ||
|
|
|
|
STREQ(guestarch, "mipsel"))
|
|
|
|
return 32;
|
|
|
|
return 64;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
qemuCapsIsValidForKVM(const char *hostarch,
|
|
|
|
const char *guestarch)
|
|
|
|
{
|
|
|
|
if (STREQ(hostarch, guestarch))
|
|
|
|
return true;
|
|
|
|
if (STREQ(hostarch, "x86_64") &&
|
|
|
|
STREQ(guestarch, "i686"))
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-12-16 09:07:07 -06:00
|
|
|
static int
|
|
|
|
qemuCapsInitGuest(virCapsPtr caps,
|
2012-08-22 08:37:05 -05:00
|
|
|
qemuCapsCachePtr cache,
|
2012-08-22 11:59:39 -05:00
|
|
|
const char *hostarch,
|
|
|
|
const char *guestarch)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
virCapsGuestPtr guest;
|
|
|
|
int i;
|
|
|
|
int haskvm = 0;
|
|
|
|
int haskqemu = 0;
|
|
|
|
char *kvmbin = NULL;
|
|
|
|
char *binary = NULL;
|
|
|
|
virCapsGuestMachinePtr *machines = NULL;
|
2012-08-22 05:56:11 -05:00
|
|
|
size_t nmachines = 0;
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsPtr qemubinCaps = NULL;
|
|
|
|
qemuCapsPtr kvmbinCaps = NULL;
|
2010-12-16 09:07:07 -06:00
|
|
|
int ret = -1;
|
|
|
|
|
2012-10-11 11:31:20 -05:00
|
|
|
/* Check for existence of base emulator, or alternate base
|
2010-12-16 09:07:07 -06:00
|
|
|
* which can be used with magic cpu choice
|
|
|
|
*/
|
2012-08-22 11:59:39 -05:00
|
|
|
binary = qemuCapsFindBinaryForArch(hostarch, guestarch);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-07-13 09:30:55 -05:00
|
|
|
/* Ignore binary if extracting version info fails */
|
2012-08-22 08:37:05 -05:00
|
|
|
if (binary) {
|
|
|
|
if (!(qemubinCaps = qemuCapsCacheLookup(cache, binary))) {
|
|
|
|
virResetLastError();
|
|
|
|
VIR_FREE(binary);
|
|
|
|
}
|
|
|
|
}
|
2012-07-13 09:30:55 -05:00
|
|
|
|
|
|
|
/* qemu-kvm/kvm binaries can only be used if
|
2010-12-16 09:07:07 -06:00
|
|
|
* - host & guest arches match
|
|
|
|
* Or
|
|
|
|
* - hostarch is x86_64 and guest arch is i686
|
|
|
|
* The latter simply needs "-cpu qemu32"
|
|
|
|
*/
|
2012-08-22 11:59:39 -05:00
|
|
|
if (qemuCapsIsValidForKVM(hostarch, guestarch)) {
|
2012-07-13 09:30:55 -05:00
|
|
|
const char *const kvmbins[] = { "/usr/libexec/qemu-kvm", /* RHEL */
|
|
|
|
"qemu-kvm", /* Fedora */
|
|
|
|
"kvm" }; /* Upstream .spec */
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-07-13 09:30:55 -05:00
|
|
|
for (i = 0; i < ARRAY_CARDINALITY(kvmbins); ++i) {
|
|
|
|
kvmbin = virFindFileInPath(kvmbins[i]);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-07-13 09:30:55 -05:00
|
|
|
if (!kvmbin)
|
|
|
|
continue;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 08:37:05 -05:00
|
|
|
if (!(kvmbinCaps = qemuCapsCacheLookup(cache, kvmbin))) {
|
|
|
|
virResetLastError();
|
2012-07-13 09:30:55 -05:00
|
|
|
VIR_FREE(kvmbin);
|
|
|
|
continue;
|
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-07-13 09:30:55 -05:00
|
|
|
if (!binary) {
|
|
|
|
binary = kvmbin;
|
2012-08-20 11:44:14 -05:00
|
|
|
qemubinCaps = kvmbinCaps;
|
2012-07-13 09:30:55 -05:00
|
|
|
kvmbin = NULL;
|
2012-08-20 11:44:14 -05:00
|
|
|
kvmbinCaps = NULL;
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
2012-07-13 09:30:55 -05:00
|
|
|
break;
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!binary)
|
|
|
|
return 0;
|
|
|
|
|
2012-07-13 09:30:55 -05:00
|
|
|
if (access("/dev/kvm", F_OK) == 0 &&
|
2012-08-20 11:44:14 -05:00
|
|
|
(qemuCapsGet(qemubinCaps, QEMU_CAPS_KVM) ||
|
|
|
|
qemuCapsGet(qemubinCaps, QEMU_CAPS_ENABLE_KVM) ||
|
2012-07-13 09:30:55 -05:00
|
|
|
kvmbin))
|
|
|
|
haskvm = 1;
|
|
|
|
|
|
|
|
if (access("/dev/kqemu", F_OK) == 0 &&
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsGet(qemubinCaps, QEMU_CAPS_KQEMU))
|
2012-07-13 09:30:55 -05:00
|
|
|
haskqemu = 1;
|
2011-04-04 08:01:22 -05:00
|
|
|
|
2012-08-22 11:29:01 -05:00
|
|
|
if (qemuCapsGetMachineTypesCaps(qemubinCaps, &nmachines, &machines) < 0)
|
|
|
|
goto error;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
/* We register kvm as the base emulator too, since we can
|
|
|
|
* just give -no-kvm to disable acceleration if required */
|
|
|
|
if ((guest = virCapabilitiesAddGuest(caps,
|
2012-08-22 11:29:01 -05:00
|
|
|
"hvm",
|
2012-08-22 11:59:39 -05:00
|
|
|
guestarch,
|
|
|
|
qemuCapsGetArchWordSize(guestarch),
|
2010-12-16 09:07:07 -06:00
|
|
|
binary,
|
|
|
|
NULL,
|
|
|
|
nmachines,
|
|
|
|
machines)) == NULL)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
machines = NULL;
|
|
|
|
nmachines = 0;
|
|
|
|
|
|
|
|
if (caps->host.cpu &&
|
2012-08-22 08:37:05 -05:00
|
|
|
qemuCapsGetCPUDefinitions(qemubinCaps, NULL) > 0 &&
|
2010-12-16 09:07:07 -06:00
|
|
|
!virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0))
|
|
|
|
goto error;
|
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
if (qemuCapsGet(qemubinCaps, QEMU_CAPS_BOOTINDEX) &&
|
2011-04-04 08:01:22 -05:00
|
|
|
!virCapabilitiesAddGuestFeature(guest, "deviceboot", 1, 0))
|
2011-01-12 04:33:34 -06:00
|
|
|
goto error;
|
|
|
|
|
2012-08-22 11:29:01 -05:00
|
|
|
if (virCapabilitiesAddGuestDomain(guest,
|
|
|
|
"qemu",
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
NULL) == NULL)
|
|
|
|
goto error;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:29:01 -05:00
|
|
|
if (haskqemu &&
|
|
|
|
virCapabilitiesAddGuestDomain(guest,
|
|
|
|
"kqemu",
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
0,
|
|
|
|
NULL) == NULL)
|
|
|
|
goto error;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:29:01 -05:00
|
|
|
if (haskvm) {
|
|
|
|
virCapsGuestDomainPtr dom;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:29:01 -05:00
|
|
|
if (kvmbin &&
|
|
|
|
qemuCapsGetMachineTypesCaps(kvmbinCaps, &nmachines, &machines) < 0)
|
|
|
|
goto error;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:29:01 -05:00
|
|
|
if ((dom = virCapabilitiesAddGuestDomain(guest,
|
|
|
|
"kvm",
|
|
|
|
kvmbin ? kvmbin : binary,
|
|
|
|
NULL,
|
|
|
|
nmachines,
|
|
|
|
machines)) == NULL) {
|
|
|
|
goto error;
|
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-22 11:29:01 -05:00
|
|
|
machines = NULL;
|
|
|
|
nmachines = 0;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-08-22 11:59:39 -05:00
|
|
|
if ((STREQ(guestarch, "i686") ||
|
|
|
|
STREQ(guestarch, "x86_64")) &&
|
|
|
|
(virCapabilitiesAddGuestFeature(guest, "acpi", 1, 1) == NULL ||
|
|
|
|
virCapabilitiesAddGuestFeature(guest, "apic", 1, 0) == NULL))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (STREQ(guestarch, "i686") &&
|
|
|
|
(virCapabilitiesAddGuestFeature(guest, "pae", 1, 0) == NULL ||
|
|
|
|
virCapabilitiesAddGuestFeature(guest, "nonpae", 1, 0) == NULL))
|
|
|
|
goto error;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
2012-07-13 09:30:55 -05:00
|
|
|
VIR_FREE(binary);
|
|
|
|
VIR_FREE(kvmbin);
|
2012-08-20 11:44:14 -05:00
|
|
|
virObjectUnref(qemubinCaps);
|
|
|
|
virObjectUnref(kvmbinCaps);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
error:
|
|
|
|
virCapabilitiesFreeMachines(machines, nmachines);
|
|
|
|
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuCapsInitCPU(virCapsPtr caps,
|
2012-08-22 08:37:05 -05:00
|
|
|
const char *arch)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
virCPUDefPtr cpu = NULL;
|
|
|
|
union cpuData *data = NULL;
|
|
|
|
virNodeInfo nodeinfo;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(cpu) < 0
|
|
|
|
|| !(cpu->arch = strdup(arch))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nodeGetInfo(NULL, &nodeinfo))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
cpu->type = VIR_CPU_TYPE_HOST;
|
|
|
|
cpu->sockets = nodeinfo.sockets;
|
|
|
|
cpu->cores = nodeinfo.cores;
|
|
|
|
cpu->threads = nodeinfo.threads;
|
|
|
|
|
|
|
|
if (!(data = cpuNodeData(arch))
|
|
|
|
|| cpuDecode(cpu, data, NULL, 0, NULL) < 0)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
caps->host.cpu = cpu;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
cpuDataFree(arch, data);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
error:
|
|
|
|
virCPUDefFree(cpu);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
Fix default console type setting
The default console type may vary based on the OS type. ie a Xen
paravirt guests wants a 'xen' console, while a fullvirt guests
wants a 'serial' console.
A plain integer default console type in the capabilities does
not suffice. Instead introduce a callback that is passed the
OS type.
* src/conf/capabilities.h: Use a callback for default console
type
* src/conf/domain_conf.c, src/conf/domain_conf.h: Use callback
for default console type. Add missing LXC/OpenVZ console types.
* src/esx/esx_driver.c, src/libxl/libxl_conf.c,
src/lxc/lxc_conf.c, src/openvz/openvz_conf.c,
src/phyp/phyp_driver.c, src/qemu/qemu_capabilities.c,
src/uml/uml_conf.c, src/vbox/vbox_tmpl.c,
src/vmware/vmware_conf.c, src/xen/xen_hypervisor.c,
src/xenapi/xenapi_driver.c: Set default console type callback
2011-10-20 08:56:20 -05:00
|
|
|
static int qemuDefaultConsoleType(const char *ostype ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-22 08:37:05 -05:00
|
|
|
virCapsPtr qemuCapsInit(qemuCapsCachePtr cache)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
struct utsname utsname;
|
|
|
|
virCapsPtr caps;
|
|
|
|
int i;
|
2012-08-22 11:59:39 -05:00
|
|
|
const char *const arches[] = {
|
|
|
|
"i686", "x86_64", "arm",
|
|
|
|
"microblaze", "microblazeel",
|
|
|
|
"mips", "mipsel", "sparc",
|
|
|
|
"ppc", "ppc64", "itanium",
|
|
|
|
"s390x"
|
|
|
|
};
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
/* Really, this never fails - look at the man-page. */
|
|
|
|
uname (&utsname);
|
|
|
|
|
|
|
|
if ((caps = virCapabilitiesNew(utsname.machine,
|
|
|
|
1, 1)) == NULL)
|
2012-08-22 11:59:39 -05:00
|
|
|
goto error;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
/* Using KVM's mac prefix for QEMU too */
|
|
|
|
virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x52, 0x54, 0x00 });
|
|
|
|
|
|
|
|
/* Some machines have problematic NUMA toplogy causing
|
|
|
|
* unexpected failures. We don't want to break the QEMU
|
|
|
|
* driver in this scenario, so log errors & carry on
|
|
|
|
*/
|
|
|
|
if (nodeCapsInitNUMA(caps) < 0) {
|
|
|
|
virCapabilitiesFreeNUMAInfo(caps);
|
2011-05-09 04:24:09 -05:00
|
|
|
VIR_WARN("Failed to query host NUMA topology, disabling NUMA capabilities");
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
|
2012-08-22 08:37:05 -05:00
|
|
|
if (qemuCapsInitCPU(caps, utsname.machine) < 0)
|
|
|
|
VIR_WARN("Failed to get host CPU");
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2011-11-21 21:31:22 -06:00
|
|
|
/* Add the power management features of the host */
|
|
|
|
|
2011-11-29 08:50:04 -06:00
|
|
|
if (virNodeSuspendGetTargetMask(&caps->host.powerMgmt) < 0)
|
2011-11-21 21:31:22 -06:00
|
|
|
VIR_WARN("Failed to get host power management capabilities");
|
|
|
|
|
2010-12-16 09:07:07 -06:00
|
|
|
virCapabilitiesAddHostMigrateTransport(caps,
|
|
|
|
"tcp");
|
|
|
|
|
|
|
|
/* First the pure HVM guests */
|
2012-08-22 11:59:39 -05:00
|
|
|
for (i = 0 ; i < ARRAY_CARDINALITY(arches) ; i++)
|
2012-08-22 08:37:05 -05:00
|
|
|
if (qemuCapsInitGuest(caps, cache,
|
2010-12-16 09:07:07 -06:00
|
|
|
utsname.machine,
|
2012-08-22 11:59:39 -05:00
|
|
|
arches[i]) < 0)
|
|
|
|
goto error;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
/* QEMU Requires an emulator in the XML */
|
|
|
|
virCapabilitiesSetEmulatorRequired(caps);
|
|
|
|
|
Fix default console type setting
The default console type may vary based on the OS type. ie a Xen
paravirt guests wants a 'xen' console, while a fullvirt guests
wants a 'serial' console.
A plain integer default console type in the capabilities does
not suffice. Instead introduce a callback that is passed the
OS type.
* src/conf/capabilities.h: Use a callback for default console
type
* src/conf/domain_conf.c, src/conf/domain_conf.h: Use callback
for default console type. Add missing LXC/OpenVZ console types.
* src/esx/esx_driver.c, src/libxl/libxl_conf.c,
src/lxc/lxc_conf.c, src/openvz/openvz_conf.c,
src/phyp/phyp_driver.c, src/qemu/qemu_capabilities.c,
src/uml/uml_conf.c, src/vbox/vbox_tmpl.c,
src/vmware/vmware_conf.c, src/xen/xen_hypervisor.c,
src/xenapi/xenapi_driver.c: Set default console type callback
2011-10-20 08:56:20 -05:00
|
|
|
caps->defaultConsoleTargetType = qemuDefaultConsoleType;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
return caps;
|
|
|
|
|
2012-08-22 11:59:39 -05:00
|
|
|
error:
|
2010-12-16 09:07:07 -06:00
|
|
|
virCapabilitiesFree(caps);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-25 22:57:38 -06:00
|
|
|
static int
|
2010-12-16 09:07:07 -06:00
|
|
|
qemuCapsComputeCmdFlags(const char *help,
|
|
|
|
unsigned int version,
|
|
|
|
unsigned int is_kvm,
|
2011-02-08 08:22:39 -06:00
|
|
|
unsigned int kvm_version,
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsPtr caps,
|
2012-01-25 22:57:38 -06:00
|
|
|
bool check_yajl ATTRIBUTE_UNUSED)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
const char *p;
|
2012-08-03 15:33:05 -05:00
|
|
|
const char *fsdev, *netdev;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if (strstr(help, "-no-kqemu"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_KQEMU);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-enable-kqemu"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_ENABLE_KQEMU);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-no-kvm"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_KVM);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-enable-kvm"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_ENABLE_KVM);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-no-reboot"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_REBOOT);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-name")) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NAME);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, ",process="))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NAME_PROCESS);
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
if (strstr(help, "-uuid"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_UUID);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-xen-domid"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_XEN_DOMID);
|
2010-12-16 09:07:07 -06:00
|
|
|
else if (strstr(help, "-domid"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DOMID);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-drive")) {
|
2011-09-22 14:33:47 -05:00
|
|
|
const char *cache = strstr(help, "cache=");
|
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE);
|
2011-09-22 14:33:47 -05:00
|
|
|
if (cache && (p = strchr(cache, ']'))) {
|
|
|
|
if (memmem(cache, p - cache, "on|off", sizeof("on|off") - 1) == NULL)
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_CACHE_V2);
|
2011-09-22 14:33:47 -05:00
|
|
|
if (memmem(cache, p - cache, "directsync", sizeof("directsync") - 1))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_CACHE_DIRECTSYNC);
|
2011-09-22 14:33:47 -05:00
|
|
|
if (memmem(cache, p - cache, "unsafe", sizeof("unsafe") - 1))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_CACHE_UNSAFE);
|
2011-09-02 08:36:58 -05:00
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "format="))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_FORMAT);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "readonly="))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_READONLY);
|
2010-04-21 09:28:21 -05:00
|
|
|
if (strstr(help, "aio=threads|native"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_AIO);
|
2012-01-12 03:31:14 -06:00
|
|
|
if (strstr(help, "copy-on-read=on|off"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_COPY_ON_READ);
|
2012-01-18 10:42:33 -06:00
|
|
|
if (strstr(help, "bps="))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_IOTUNE);
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
if ((p = strstr(help, "-vga")) && !strstr(help, "-std-vga")) {
|
|
|
|
const char *nl = strstr(p, "\n");
|
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VGA);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if (strstr(p, "|qxl"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VGA_QXL);
|
2010-12-16 09:07:07 -06:00
|
|
|
if ((p = strstr(p, "|none")) && p < nl)
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VGA_NONE);
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
if (strstr(help, "-spice"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SPICE);
|
2012-09-20 04:15:31 -05:00
|
|
|
if (strstr(help, "seamless-migration="))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SEAMLESS_MIGRATION);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "boot=on"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_BOOT);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "serial=s"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_SERIAL);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-pcidevice"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_PCIDEVICE);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-mem-path"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MEM_PATH);
|
2011-01-13 09:54:33 -06:00
|
|
|
if (strstr(help, "-chardev")) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_CHARDEV);
|
2011-01-13 09:54:33 -06:00
|
|
|
if (strstr(help, "-chardev spicevmc"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_CHARDEV_SPICEVMC);
|
2011-01-13 09:54:33 -06:00
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-balloon"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_BALLOON);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-device")) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DEVICE);
|
2010-12-16 09:07:07 -06:00
|
|
|
/*
|
|
|
|
* When -device was introduced, qemu already supported drive's
|
|
|
|
* readonly option but didn't advertise that.
|
|
|
|
*/
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_READONLY);
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
if (strstr(help, "-nodefconfig"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NODEFCONFIG);
|
2012-04-26 05:11:49 -05:00
|
|
|
if (strstr(help, "-no-user-config"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_USER_CONFIG);
|
2010-12-16 09:07:07 -06:00
|
|
|
/* The trailing ' ' is important to avoid a bogus match */
|
|
|
|
if (strstr(help, "-rtc "))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_RTC);
|
2010-12-16 09:07:07 -06:00
|
|
|
/* to wit */
|
|
|
|
if (strstr(help, "-rtc-td-hack"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_RTC_TD_HACK);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-no-hpet"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_HPET);
|
2011-12-19 19:08:29 -06:00
|
|
|
if (strstr(help, "-no-acpi"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_ACPI);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-no-kvm-pit-reinjection"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_KVM_PIT);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-tdf"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_TDF);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-enable-nesting"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NESTING);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, ",menu=on"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_BOOT_MENU);
|
2012-09-18 05:31:30 -05:00
|
|
|
if (strstr(help, ",reboot-timeout=rb_time"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_REBOOT_TIMEOUT);
|
2011-12-21 09:51:29 -06:00
|
|
|
if ((fsdev = strstr(help, "-fsdev"))) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_FSDEV);
|
2011-12-21 09:51:29 -06:00
|
|
|
if (strstr(fsdev, "readonly"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_FSDEV_READONLY);
|
2012-01-17 06:44:18 -06:00
|
|
|
if (strstr(fsdev, "writeout"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_FSDEV_WRITEOUT);
|
2011-12-21 09:51:29 -06:00
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "-smbios type"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SMBIOS_TYPE);
|
2012-09-18 02:24:51 -05:00
|
|
|
if (strstr(help, "-sandbox"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SECCOMP_SANDBOX);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-08-03 15:33:05 -05:00
|
|
|
if ((netdev = strstr(help, "-netdev"))) {
|
2010-12-16 09:07:07 -06:00
|
|
|
/* Disable -netdev on 0.12 since although it exists,
|
|
|
|
* the corresponding netdev_add/remove monitor commands
|
2012-01-25 22:33:21 -06:00
|
|
|
* do not, and we need them to be able to do hotplug.
|
|
|
|
* But see below about RHEL build. */
|
2012-08-03 15:33:05 -05:00
|
|
|
if (version >= 13000) {
|
|
|
|
if (strstr(netdev, "bridge"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NETDEV_BRIDGE);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NETDEV);
|
2012-08-03 15:33:05 -05:00
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strstr(help, "-sdl"))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SDL);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (strstr(help, "cores=") &&
|
|
|
|
strstr(help, "threads=") &&
|
|
|
|
strstr(help, "sockets="))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SMP_TOPOLOGY);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if (version >= 9000)
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VNC_COLON);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if (is_kvm && (version >= 10000 || kvm_version >= 74))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VNET_HDR);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2011-05-23 11:59:41 -05:00
|
|
|
if (strstr(help, ",vhost=")) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VHOST_NET);
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
|
2011-09-21 03:25:29 -05:00
|
|
|
/* Do not use -no-shutdown if qemu doesn't support it or SIGTERM handling
|
|
|
|
* is most likely buggy when used with -no-shutdown (which applies for qemu
|
2011-10-17 05:15:20 -05:00
|
|
|
* 0.14.* and 0.15.0)
|
2011-09-21 03:25:29 -05:00
|
|
|
*/
|
2011-10-17 05:15:20 -05:00
|
|
|
if (strstr(help, "-no-shutdown") && (version < 14000 || version > 15000))
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_SHUTDOWN);
|
2011-09-21 03:25:29 -05:00
|
|
|
|
2012-08-15 02:59:24 -05:00
|
|
|
if (strstr(help, "dump-guest-core=on|off"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DUMP_GUEST_CORE);
|
|
|
|
|
2010-12-16 09:07:07 -06:00
|
|
|
/*
|
|
|
|
* Handling of -incoming arg with varying features
|
|
|
|
* -incoming tcp (kvm >= 79, qemu >= 0.10.0)
|
|
|
|
* -incoming exec (kvm >= 80, qemu >= 0.10.0)
|
2010-12-22 16:13:06 -06:00
|
|
|
* -incoming unix (qemu >= 0.12.0)
|
|
|
|
* -incoming fd (qemu >= 0.12.0)
|
2010-12-16 09:07:07 -06:00
|
|
|
* -incoming stdio (all earlier kvm)
|
|
|
|
*
|
|
|
|
* NB, there was a pre-kvm-79 'tcp' support, but it
|
|
|
|
* was broken, because it blocked the monitor console
|
|
|
|
* while waiting for data, so pretend it doesn't exist
|
|
|
|
*/
|
|
|
|
if (version >= 10000) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_TCP);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_EXEC);
|
2010-12-22 16:13:06 -06:00
|
|
|
if (version >= 12000) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_UNIX);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_FD);
|
2010-12-22 16:13:06 -06:00
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
} else if (kvm_version >= 79) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_TCP);
|
2010-12-16 09:07:07 -06:00
|
|
|
if (kvm_version >= 80)
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_EXEC);
|
2010-12-16 09:07:07 -06:00
|
|
|
} else if (kvm_version > 0) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_KVM_STDIO);
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
if (version >= 10000)
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_0_10);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2011-11-29 12:37:27 -06:00
|
|
|
if (version >= 11000)
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VIRTIO_BLK_SG_IO);
|
2011-11-29 12:37:27 -06:00
|
|
|
|
2010-12-16 09:07:07 -06:00
|
|
|
/* While JSON mode was available in 0.12.0, it was too
|
|
|
|
* incomplete to contemplate using. The 0.13.0 release
|
|
|
|
* is good enough to use, even though it lacks one or
|
2012-01-25 22:33:21 -06:00
|
|
|
* two features. This is also true of versions of qemu
|
|
|
|
* built for RHEL, labeled 0.12.1, but with extra text
|
|
|
|
* in the help output that mentions that features were
|
|
|
|
* backported for libvirt. The benefits of JSON mode now
|
|
|
|
* outweigh the downside.
|
2010-12-16 09:07:07 -06:00
|
|
|
*/
|
2011-10-20 15:36:32 -05:00
|
|
|
#if HAVE_YAJL
|
2012-01-25 22:33:21 -06:00
|
|
|
if (version >= 13000) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MONITOR_JSON);
|
2012-01-25 22:33:21 -06:00
|
|
|
} else if (version >= 12000 &&
|
|
|
|
strstr(help, "libvirt")) {
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MONITOR_JSON);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NETDEV);
|
2012-01-25 22:33:21 -06:00
|
|
|
}
|
2012-01-25 22:57:38 -06:00
|
|
|
#else
|
|
|
|
/* Starting with qemu 0.15 and newer, upstream qemu no longer
|
|
|
|
* promises to keep the human interface stable, but requests that
|
|
|
|
* we use QMP (the JSON interface) for everything. If the user
|
|
|
|
* forgot to include YAJL libraries when building their own
|
|
|
|
* libvirt but is targetting a newer qemu, we are better off
|
|
|
|
* telling them to recompile (the spec file includes the
|
2012-06-13 11:09:39 -05:00
|
|
|
* dependency, so distros won't hit this). This check is
|
|
|
|
* also in configure.ac (see $with_yajl). */
|
2012-01-25 22:57:38 -06:00
|
|
|
if (version >= 15000 ||
|
|
|
|
(version >= 12000 && strstr(help, "libvirt"))) {
|
|
|
|
if (check_yajl) {
|
2012-07-18 10:22:03 -05:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
|
|
_("this qemu binary requires libvirt to be "
|
|
|
|
"compiled with yajl"));
|
2012-01-25 22:57:38 -06:00
|
|
|
return -1;
|
|
|
|
}
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NETDEV);
|
2012-01-25 22:57:38 -06:00
|
|
|
}
|
2011-10-20 15:36:32 -05:00
|
|
|
#endif
|
2011-05-09 01:59:16 -05:00
|
|
|
|
|
|
|
if (version >= 13000)
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_PCI_MULTIFUNCTION);
|
2011-09-20 12:31:52 -05:00
|
|
|
|
|
|
|
/* Although very new versions of qemu advertise the presence of
|
|
|
|
* the rombar option in the output of "qemu -device pci-assign,?",
|
|
|
|
* this advertisement was added to the code long after the option
|
|
|
|
* itself. According to qemu developers, though, rombar is
|
|
|
|
* available in all qemu binaries from release 0.12 onward.
|
|
|
|
* Setting the capability this way makes it available in more
|
|
|
|
* cases where it might be needed, and shouldn't cause any false
|
|
|
|
* positives (in the case that it did, qemu would produce an error
|
|
|
|
* log and refuse to start, so it would be immediately obvious).
|
|
|
|
*/
|
|
|
|
if (version >= 12000)
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_PCI_ROMBAR);
|
2011-12-21 06:47:17 -06:00
|
|
|
|
|
|
|
if (version >= 11000)
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(caps, QEMU_CAPS_CPU_HOST);
|
2012-01-25 22:57:38 -06:00
|
|
|
return 0;
|
2010-12-16 09:07:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We parse the output of 'qemu -help' to get the QEMU
|
|
|
|
* version number. The first bit is easy, just parse
|
|
|
|
* 'QEMU PC emulator version x.y.z'
|
|
|
|
* or
|
|
|
|
* 'QEMU emulator version x.y.z'.
|
|
|
|
*
|
|
|
|
* With qemu-kvm, however, that is followed by a string
|
|
|
|
* in parenthesis as follows:
|
|
|
|
* - qemu-kvm-x.y.z in stable releases
|
|
|
|
* - kvm-XX for kvm versions up to kvm-85
|
|
|
|
* - qemu-kvm-devel-XX for kvm version kvm-86 and later
|
|
|
|
*
|
|
|
|
* For qemu-kvm versions before 0.10.z, we need to detect
|
|
|
|
* the KVM version number for some features. With 0.10.z
|
|
|
|
* and later, we just need the QEMU version number and
|
|
|
|
* whether it is KVM QEMU or mainline QEMU.
|
|
|
|
*/
|
|
|
|
#define QEMU_VERSION_STR_1 "QEMU emulator version"
|
|
|
|
#define QEMU_VERSION_STR_2 "QEMU PC emulator version"
|
|
|
|
#define QEMU_KVM_VER_PREFIX "(qemu-kvm-"
|
|
|
|
#define KVM_VER_PREFIX "(kvm-"
|
|
|
|
|
|
|
|
#define SKIP_BLANKS(p) do { while ((*(p) == ' ') || (*(p) == '\t')) (p)++; } while (0)
|
|
|
|
|
|
|
|
int qemuCapsParseHelpStr(const char *qemu,
|
|
|
|
const char *help,
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsPtr caps,
|
2010-12-16 09:07:07 -06:00
|
|
|
unsigned int *version,
|
|
|
|
unsigned int *is_kvm,
|
2012-01-25 22:57:38 -06:00
|
|
|
unsigned int *kvm_version,
|
|
|
|
bool check_yajl)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
unsigned major, minor, micro;
|
|
|
|
const char *p = help;
|
2011-02-08 08:22:39 -06:00
|
|
|
char *strflags;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2011-02-08 08:22:39 -06:00
|
|
|
*version = *is_kvm = *kvm_version = 0;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if (STRPREFIX(p, QEMU_VERSION_STR_1))
|
|
|
|
p += strlen(QEMU_VERSION_STR_1);
|
|
|
|
else if (STRPREFIX(p, QEMU_VERSION_STR_2))
|
|
|
|
p += strlen(QEMU_VERSION_STR_2);
|
|
|
|
else
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
SKIP_BLANKS(p);
|
|
|
|
|
|
|
|
major = virParseNumber(&p);
|
|
|
|
if (major == -1 || *p != '.')
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
++p;
|
|
|
|
|
|
|
|
minor = virParseNumber(&p);
|
2011-12-02 14:20:15 -06:00
|
|
|
if (minor == -1)
|
2010-12-16 09:07:07 -06:00
|
|
|
goto fail;
|
|
|
|
|
2011-12-02 14:20:15 -06:00
|
|
|
if (*p != '.') {
|
|
|
|
micro = 0;
|
|
|
|
} else {
|
|
|
|
++p;
|
|
|
|
micro = virParseNumber(&p);
|
|
|
|
if (micro == -1)
|
|
|
|
goto fail;
|
|
|
|
}
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
SKIP_BLANKS(p);
|
|
|
|
|
|
|
|
if (STRPREFIX(p, QEMU_KVM_VER_PREFIX)) {
|
|
|
|
*is_kvm = 1;
|
|
|
|
p += strlen(QEMU_KVM_VER_PREFIX);
|
|
|
|
} else if (STRPREFIX(p, KVM_VER_PREFIX)) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
*is_kvm = 1;
|
|
|
|
p += strlen(KVM_VER_PREFIX);
|
|
|
|
|
|
|
|
ret = virParseNumber(&p);
|
|
|
|
if (ret == -1)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
*kvm_version = ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
*version = (major * 1000 * 1000) + (minor * 1000) + micro;
|
|
|
|
|
2012-01-25 22:57:38 -06:00
|
|
|
if (qemuCapsComputeCmdFlags(help, *version, *is_kvm, *kvm_version,
|
2012-08-20 11:44:14 -05:00
|
|
|
caps, check_yajl) < 0)
|
2012-01-25 22:57:38 -06:00
|
|
|
goto cleanup;
|
2011-02-08 08:22:39 -06:00
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
strflags = virBitmapString(caps->flags);
|
2011-02-08 08:22:39 -06:00
|
|
|
VIR_DEBUG("Version %u.%u.%u, cooked version %u, flags %s",
|
|
|
|
major, minor, micro, *version, NULLSTR(strflags));
|
|
|
|
VIR_FREE(strflags);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if (*kvm_version)
|
|
|
|
VIR_DEBUG("KVM version %d detected", *kvm_version);
|
|
|
|
else if (*is_kvm)
|
|
|
|
VIR_DEBUG("qemu-kvm version %u.%u.%u detected", major, minor, micro);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
p = strchr(help, '\n');
|
2012-01-27 14:53:11 -06:00
|
|
|
if (!p)
|
|
|
|
p = strchr(help, '\0');
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-07-18 10:22:03 -05:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("cannot parse %s version number in '%.*s'"),
|
|
|
|
qemu, (int) (p - help), help);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2012-01-25 22:57:38 -06:00
|
|
|
cleanup:
|
2010-12-16 09:07:07 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-13 12:04:24 -05:00
|
|
|
|
|
|
|
struct qemuCapsStringFlags {
|
|
|
|
const char *value;
|
|
|
|
int flag;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
struct qemuCapsStringFlags qemuCapsObjectTypes[] = {
|
|
|
|
{ "hda-duplex", QEMU_CAPS_HDA_DUPLEX },
|
|
|
|
{ "hda-micro", QEMU_CAPS_HDA_MICRO },
|
|
|
|
{ "ccid-card-emulated", QEMU_CAPS_CCID_EMULATED },
|
|
|
|
{ "ccid-card-passthru", QEMU_CAPS_CCID_PASSTHRU },
|
|
|
|
{ "piix3-usb-uhci", QEMU_CAPS_PIIX3_USB_UHCI },
|
|
|
|
{ "piix4-usb-uhci", QEMU_CAPS_PIIX4_USB_UHCI },
|
|
|
|
{ "usb-ehci", QEMU_CAPS_USB_EHCI },
|
|
|
|
{ "ich9-usb-ehci1", QEMU_CAPS_ICH9_USB_EHCI1 },
|
|
|
|
{ "vt82c686b-usb-uhci", QEMU_CAPS_VT82C686B_USB_UHCI },
|
|
|
|
{ "pci-ohci", QEMU_CAPS_PCI_OHCI },
|
|
|
|
{ "nec-usb-xhci", QEMU_CAPS_NEC_USB_XHCI },
|
|
|
|
{ "usb-redir", QEMU_CAPS_USB_REDIR },
|
|
|
|
{ "usb-hub", QEMU_CAPS_USB_HUB },
|
|
|
|
{ "ich9-ahci", QEMU_CAPS_ICH9_AHCI },
|
|
|
|
{ "virtio-blk-s390", QEMU_CAPS_VIRTIO_S390 },
|
|
|
|
{ "lsi53c895a", QEMU_CAPS_SCSI_LSI },
|
|
|
|
{ "virtio-scsi-pci", QEMU_CAPS_VIRTIO_SCSI_PCI },
|
|
|
|
{ "spicevmc", QEMU_CAPS_DEVICE_SPICEVMC },
|
|
|
|
{ "qxl-vga", QEMU_CAPS_DEVICE_QXL_VGA },
|
2012-10-09 01:25:02 -05:00
|
|
|
{ "qxl", QEMU_CAPS_VGA_QXL },
|
2012-09-13 12:04:24 -05:00
|
|
|
{ "sga", QEMU_CAPS_SGA },
|
|
|
|
{ "scsi-block", QEMU_CAPS_SCSI_BLOCK },
|
|
|
|
{ "scsi-cd", QEMU_CAPS_SCSI_CD },
|
|
|
|
{ "ide-cd", QEMU_CAPS_IDE_CD },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static struct qemuCapsStringFlags qemuCapsObjectPropsVirtioBlk[] = {
|
|
|
|
{ "multifunction", QEMU_CAPS_PCI_MULTIFUNCTION },
|
|
|
|
{ "bootindex", QEMU_CAPS_BOOTINDEX },
|
|
|
|
{ "ioeventfd", QEMU_CAPS_VIRTIO_IOEVENTFD },
|
|
|
|
{ "event_idx", QEMU_CAPS_VIRTIO_BLK_EVENT_IDX },
|
|
|
|
{ "scsi", QEMU_CAPS_VIRTIO_BLK_SCSI },
|
|
|
|
{ "logical_block_size", QEMU_CAPS_BLOCKIO },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct qemuCapsStringFlags qemuCapsObjectPropsVirtioNet[] = {
|
|
|
|
{ "tx", QEMU_CAPS_VIRTIO_TX_ALG },
|
|
|
|
{ "event_idx", QEMU_CAPS_VIRTIO_NET_EVENT_IDX },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct qemuCapsStringFlags qemuCapsObjectPropsPciAssign[] = {
|
2012-08-22 12:05:08 -05:00
|
|
|
{ "rombar", QEMU_CAPS_PCI_ROMBAR },
|
2012-09-13 12:04:24 -05:00
|
|
|
{ "configfd", QEMU_CAPS_PCI_CONFIGFD },
|
|
|
|
{ "bootindex", QEMU_CAPS_PCI_BOOTINDEX },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct qemuCapsStringFlags qemuCapsObjectPropsScsiDisk[] = {
|
|
|
|
{ "channel", QEMU_CAPS_SCSI_DISK_CHANNEL },
|
|
|
|
{ "wwn", QEMU_CAPS_SCSI_DISK_WWN },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct qemuCapsStringFlags qemuCapsObjectPropsIDEDrive[] = {
|
|
|
|
{ "wwn", QEMU_CAPS_IDE_DRIVE_WWN },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct qemuCapsStringFlags qemuCapsObjectPropsPixx4PM[] = {
|
|
|
|
{ "disable_s3", QEMU_CAPS_DISABLE_S3 },
|
|
|
|
{ "disable_s4", QEMU_CAPS_DISABLE_S4 },
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct qemuCapsStringFlags qemuCapsObjectPropsUsbRedir[] = {
|
|
|
|
{ "filter", QEMU_CAPS_USB_REDIR_FILTER },
|
|
|
|
};
|
|
|
|
|
|
|
|
struct qemuCapsObjectTypeProps {
|
|
|
|
const char *type;
|
|
|
|
struct qemuCapsStringFlags *props;
|
|
|
|
size_t nprops;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct qemuCapsObjectTypeProps qemuCapsObjectProps[] = {
|
|
|
|
{ "virtio-blk-pci", qemuCapsObjectPropsVirtioBlk,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectPropsVirtioBlk) },
|
|
|
|
{ "virtio-net-pci", qemuCapsObjectPropsVirtioNet,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectPropsVirtioNet) },
|
|
|
|
{ "pci-assign", qemuCapsObjectPropsPciAssign,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectPropsPciAssign) },
|
2012-09-28 10:48:58 -05:00
|
|
|
{ "kvm-pci-assign", qemuCapsObjectPropsPciAssign,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectPropsPciAssign) },
|
2012-09-13 12:04:24 -05:00
|
|
|
{ "scsi-disk", qemuCapsObjectPropsScsiDisk,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectPropsScsiDisk) },
|
|
|
|
{ "ide-drive", qemuCapsObjectPropsIDEDrive,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectPropsIDEDrive) },
|
|
|
|
{ "PIIX4_PM", qemuCapsObjectPropsPixx4PM,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectPropsPixx4PM) },
|
|
|
|
{ "usb-redir", qemuCapsObjectPropsUsbRedir,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectPropsUsbRedir) },
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
qemuCapsProcessStringFlags(qemuCapsPtr caps,
|
|
|
|
size_t nflags,
|
|
|
|
struct qemuCapsStringFlags *flags,
|
|
|
|
size_t nvalues,
|
|
|
|
char *const*values)
|
|
|
|
{
|
|
|
|
size_t i, j;
|
|
|
|
for (i = 0 ; i < nflags ; i++) {
|
|
|
|
for (j = 0 ; j < nvalues ; j++) {
|
|
|
|
if (STREQ(values[j], flags[i].value)) {
|
|
|
|
qemuCapsSet(caps, flags[i].flag);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
qemuCapsFreeStringList(size_t len,
|
|
|
|
char **values)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
for (i = 0 ; i < len ; i++)
|
|
|
|
VIR_FREE(values[i]);
|
|
|
|
VIR_FREE(values);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define OBJECT_TYPE_PREFIX "name \""
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuCapsParseDeviceStrObjectTypes(const char *str,
|
|
|
|
char ***types)
|
|
|
|
{
|
|
|
|
const char *tmp = str;
|
|
|
|
int ret = -1;
|
|
|
|
size_t ntypelist = 0;
|
|
|
|
char **typelist = NULL;
|
|
|
|
|
|
|
|
*types = NULL;
|
|
|
|
|
|
|
|
while ((tmp = strstr(tmp, OBJECT_TYPE_PREFIX))) {
|
|
|
|
char *end;
|
|
|
|
tmp += strlen(OBJECT_TYPE_PREFIX);
|
|
|
|
end = strstr(tmp, "\"");
|
|
|
|
if (!end) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Malformed QEMU device list string, missing quote"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_EXPAND_N(typelist, ntypelist, 1) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!(typelist[ntypelist-1] = strndup(tmp, end-tmp))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*types = typelist;
|
|
|
|
ret = ntypelist;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (ret < 0)
|
|
|
|
qemuCapsFreeStringList(ntypelist, typelist);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuCapsParseDeviceStrObjectProps(const char *str,
|
|
|
|
const char *type,
|
|
|
|
char ***props)
|
|
|
|
{
|
|
|
|
const char *tmp = str;
|
|
|
|
int ret = -1;
|
|
|
|
size_t nproplist = 0;
|
|
|
|
char **proplist = NULL;
|
|
|
|
|
|
|
|
VIR_DEBUG("Extract type %s", type);
|
|
|
|
*props = NULL;
|
|
|
|
|
|
|
|
while ((tmp = strchr(tmp, '\n'))) {
|
|
|
|
char *end;
|
|
|
|
tmp += 1;
|
|
|
|
|
|
|
|
if (*tmp == '\0')
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (STRPREFIX(tmp, OBJECT_TYPE_PREFIX))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!STRPREFIX(tmp, type))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
tmp += strlen(type);
|
|
|
|
if (*tmp != '.')
|
|
|
|
continue;
|
|
|
|
tmp++;
|
|
|
|
|
|
|
|
end = strstr(tmp, "=");
|
|
|
|
if (!end) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Malformed QEMU device list string, missing '='"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (VIR_EXPAND_N(proplist, nproplist, 1) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!(proplist[nproplist-1] = strndup(tmp, end-tmp))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*props = proplist;
|
|
|
|
ret = nproplist;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (ret < 0)
|
|
|
|
qemuCapsFreeStringList(nproplist, proplist);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
qemuCapsParseDeviceStr(qemuCapsPtr caps, const char *str)
|
|
|
|
{
|
|
|
|
int nvalues;
|
|
|
|
char **values;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if ((nvalues = qemuCapsParseDeviceStrObjectTypes(str, &values)) < 0)
|
|
|
|
return -1;
|
|
|
|
qemuCapsProcessStringFlags(caps,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectTypes),
|
|
|
|
qemuCapsObjectTypes,
|
|
|
|
nvalues, values);
|
|
|
|
qemuCapsFreeStringList(nvalues, values);
|
|
|
|
|
|
|
|
for (i = 0 ; i < ARRAY_CARDINALITY(qemuCapsObjectProps); i++) {
|
|
|
|
const char *type = qemuCapsObjectProps[i].type;
|
|
|
|
if ((nvalues = qemuCapsParseDeviceStrObjectProps(str,
|
|
|
|
type,
|
|
|
|
&values)) < 0)
|
|
|
|
return -1;
|
|
|
|
qemuCapsProcessStringFlags(caps,
|
|
|
|
qemuCapsObjectProps[i].nprops,
|
|
|
|
qemuCapsObjectProps[i].props,
|
|
|
|
nvalues, values);
|
|
|
|
qemuCapsFreeStringList(nvalues, values);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prefer -chardev spicevmc (detected earlier) over -device spicevmc */
|
|
|
|
if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV_SPICEVMC))
|
|
|
|
qemuCapsClear(caps, QEMU_CAPS_DEVICE_SPICEVMC);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-01-13 10:09:15 -06:00
|
|
|
static int
|
|
|
|
qemuCapsExtractDeviceStr(const char *qemu,
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsPtr caps)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
2011-01-13 10:09:15 -06:00
|
|
|
char *output = NULL;
|
2011-01-12 17:26:34 -06:00
|
|
|
virCommandPtr cmd;
|
2011-01-13 10:09:15 -06:00
|
|
|
int ret = -1;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2011-01-13 10:09:15 -06:00
|
|
|
/* Cram together all device-related queries into one invocation;
|
|
|
|
* the output format makes it possible to distinguish what we
|
2011-01-18 10:47:30 -06:00
|
|
|
* need. With qemu 0.13.0 and later, unrecognized '-device
|
|
|
|
* bogus,?' cause an error in isolation, but are silently ignored
|
2011-01-13 09:54:33 -06:00
|
|
|
* in combination with '-device ?'. Upstream qemu 0.12.x doesn't
|
2011-01-18 10:47:30 -06:00
|
|
|
* understand '-device name,?', and always exits with status 1 for
|
|
|
|
* the simpler '-device ?', so this function is really only useful
|
2011-01-13 09:54:33 -06:00
|
|
|
* if -help includes "device driver,?". */
|
2012-08-20 11:44:14 -05:00
|
|
|
cmd = qemuCapsProbeCommand(qemu, caps);
|
2012-04-26 08:10:22 -05:00
|
|
|
virCommandAddArgList(cmd,
|
|
|
|
"-device", "?",
|
|
|
|
"-device", "pci-assign,?",
|
|
|
|
"-device", "virtio-blk-pci,?",
|
|
|
|
"-device", "virtio-net-pci,?",
|
|
|
|
"-device", "scsi-disk,?",
|
2012-08-02 05:14:39 -05:00
|
|
|
"-device", "PIIX4_PM,?",
|
2012-08-19 10:42:44 -05:00
|
|
|
"-device", "usb-redir,?",
|
2012-09-17 21:12:11 -05:00
|
|
|
"-device", "ide-drive,?",
|
2012-04-26 08:10:22 -05:00
|
|
|
NULL);
|
2011-01-12 17:26:34 -06:00
|
|
|
/* qemu -help goes to stdout, but qemu -device ? goes to stderr. */
|
2011-01-13 10:09:15 -06:00
|
|
|
virCommandSetErrorBuffer(cmd, &output);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
2011-01-12 17:26:34 -06:00
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
2010-12-16 09:07:07 -06:00
|
|
|
goto cleanup;
|
|
|
|
|
2012-09-13 12:04:24 -05:00
|
|
|
ret = qemuCapsParseDeviceStr(caps, output);
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
cleanup:
|
2011-01-13 10:09:15 -06:00
|
|
|
VIR_FREE(output);
|
2011-01-12 17:26:34 -06:00
|
|
|
virCommandFree(cmd);
|
2011-01-13 10:09:15 -06:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-16 09:07:07 -06:00
|
|
|
static void
|
|
|
|
uname_normalize (struct utsname *ut)
|
|
|
|
{
|
|
|
|
uname(ut);
|
|
|
|
|
|
|
|
/* Map i386, i486, i586 to i686. */
|
|
|
|
if (ut->machine[0] == 'i' &&
|
|
|
|
ut->machine[1] != '\0' &&
|
|
|
|
ut->machine[2] == '8' &&
|
|
|
|
ut->machine[3] == '6' &&
|
|
|
|
ut->machine[4] == '\0')
|
|
|
|
ut->machine[1] = '6';
|
|
|
|
}
|
|
|
|
|
2012-08-22 09:39:54 -05:00
|
|
|
int qemuCapsGetDefaultVersion(virCapsPtr caps,
|
|
|
|
qemuCapsCachePtr capsCache,
|
|
|
|
unsigned int *version)
|
2010-12-16 09:07:07 -06:00
|
|
|
{
|
|
|
|
const char *binary;
|
|
|
|
struct utsname ut;
|
2012-08-22 09:39:54 -05:00
|
|
|
qemuCapsPtr qemucaps;
|
2010-12-16 09:07:07 -06:00
|
|
|
|
|
|
|
if (*version > 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
uname_normalize(&ut);
|
|
|
|
if ((binary = virCapabilitiesDefaultGuestEmulator(caps,
|
|
|
|
"hvm",
|
|
|
|
ut.machine,
|
|
|
|
"qemu")) == NULL) {
|
2012-07-18 10:22:03 -05:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Cannot find suitable emulator for %s"), ut.machine);
|
2010-12-16 09:07:07 -06:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-08-22 09:39:54 -05:00
|
|
|
if (!(qemucaps = qemuCapsCacheLookup(capsCache, binary)))
|
2010-12-16 09:07:07 -06:00
|
|
|
return -1;
|
|
|
|
|
2012-08-22 09:39:54 -05:00
|
|
|
*version = qemuCapsGetVersion(qemucaps);
|
|
|
|
virObjectUnref(qemucaps);
|
2010-12-16 09:07:07 -06:00
|
|
|
return 0;
|
|
|
|
}
|
2011-02-08 08:08:12 -06:00
|
|
|
|
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
|
|
|
|
|
|
|
|
qemuCapsPtr
|
2011-02-08 08:22:39 -06:00
|
|
|
qemuCapsNew(void)
|
|
|
|
{
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsPtr caps;
|
2011-02-08 08:22:39 -06:00
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
if (qemuCapsInitialize() < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!(caps = virObjectNew(qemuCapsClass)))
|
|
|
|
return NULL;
|
|
|
|
|
2012-09-14 02:46:56 -05:00
|
|
|
if (!(caps->flags = virBitmapNew(QEMU_CAPS_LAST)))
|
2012-08-20 11:44:14 -05:00
|
|
|
goto no_memory;
|
2011-02-08 08:22:39 -06:00
|
|
|
|
|
|
|
return caps;
|
2012-08-20 11:44:14 -05:00
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
virObjectUnref(caps);
|
|
|
|
return NULL;
|
2011-02-08 08:22:39 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-11 07:42:20 -05:00
|
|
|
qemuCapsPtr qemuCapsNewCopy(qemuCapsPtr caps)
|
|
|
|
{
|
|
|
|
qemuCapsPtr ret = qemuCapsNew();
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
virBitmapCopy(ret->flags, caps->flags);
|
|
|
|
|
|
|
|
ret->version = caps->version;
|
|
|
|
ret->kvmVersion = caps->kvmVersion;
|
|
|
|
|
|
|
|
if (caps->arch &&
|
|
|
|
!(ret->arch = strdup(caps->arch)))
|
|
|
|
goto no_memory;
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(ret->cpuDefinitions, caps->ncpuDefinitions) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
ret->ncpuDefinitions = caps->ncpuDefinitions;
|
|
|
|
for (i = 0 ; i < caps->ncpuDefinitions ; i++) {
|
|
|
|
if (!(ret->cpuDefinitions[i] = strdup(caps->cpuDefinitions[i])))
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(ret->machineTypes, caps->nmachineTypes) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (VIR_ALLOC_N(ret->machineAliases, caps->nmachineTypes) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
ret->nmachineTypes = caps->nmachineTypes;
|
|
|
|
for (i = 0 ; i < caps->nmachineTypes ; i++) {
|
|
|
|
if (!(ret->machineTypes[i] = strdup(caps->machineTypes[i])))
|
|
|
|
goto no_memory;
|
|
|
|
if (caps->machineAliases[i] &&
|
|
|
|
!(ret->machineAliases[i] = strdup(caps->machineAliases[i])))
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
virObjectUnref(ret);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
void qemuCapsDispose(void *obj)
|
|
|
|
{
|
|
|
|
qemuCapsPtr caps = obj;
|
2012-08-22 05:11:28 -05:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
VIR_FREE(caps->arch);
|
|
|
|
|
|
|
|
for (i = 0 ; i < caps->nmachineTypes ; i++) {
|
|
|
|
VIR_FREE(caps->machineTypes[i]);
|
|
|
|
VIR_FREE(caps->machineAliases[i]);
|
|
|
|
}
|
|
|
|
VIR_FREE(caps->machineTypes);
|
|
|
|
VIR_FREE(caps->machineAliases);
|
|
|
|
|
|
|
|
for (i = 0 ; i < caps->ncpuDefinitions ; i++) {
|
|
|
|
VIR_FREE(caps->cpuDefinitions[i]);
|
|
|
|
}
|
|
|
|
VIR_FREE(caps->cpuDefinitions);
|
2012-08-20 11:44:14 -05:00
|
|
|
|
|
|
|
virBitmapFree(caps->flags);
|
2012-09-10 05:47:56 -05:00
|
|
|
|
|
|
|
VIR_FREE(caps->binary);
|
2012-08-20 11:44:14 -05:00
|
|
|
}
|
|
|
|
|
2011-02-08 08:08:12 -06:00
|
|
|
void
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSet(qemuCapsPtr caps,
|
2011-02-08 08:08:12 -06:00
|
|
|
enum qemuCapsFlags flag)
|
|
|
|
{
|
2012-08-20 11:44:14 -05:00
|
|
|
ignore_value(virBitmapSetBit(caps->flags, flag));
|
2011-02-08 08:22:39 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsSetList(qemuCapsPtr caps, ...)
|
2011-02-08 08:22:39 -06:00
|
|
|
{
|
|
|
|
va_list list;
|
|
|
|
int flag;
|
|
|
|
|
|
|
|
va_start(list, caps);
|
|
|
|
while ((flag = va_arg(list, int)) < QEMU_CAPS_LAST)
|
2012-08-20 11:44:14 -05:00
|
|
|
ignore_value(virBitmapSetBit(caps->flags, flag));
|
2011-02-08 08:22:39 -06:00
|
|
|
va_end(list);
|
2011-02-08 08:08:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsClear(qemuCapsPtr caps,
|
2011-02-08 08:08:12 -06:00
|
|
|
enum qemuCapsFlags flag)
|
|
|
|
{
|
2012-08-20 11:44:14 -05:00
|
|
|
ignore_value(virBitmapClearBit(caps->flags, flag));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *qemuCapsFlagsString(qemuCapsPtr caps)
|
|
|
|
{
|
|
|
|
return virBitmapString(caps->flags);
|
2011-02-08 08:08:12 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
2012-08-20 11:44:14 -05:00
|
|
|
qemuCapsGet(qemuCapsPtr caps,
|
2011-02-08 08:08:12 -06:00
|
|
|
enum qemuCapsFlags flag)
|
|
|
|
{
|
2011-02-08 08:22:39 -06:00
|
|
|
bool b;
|
|
|
|
|
2012-08-20 11:44:14 -05:00
|
|
|
if (!caps || virBitmapGetBit(caps->flags, flag, &b) < 0)
|
2011-02-08 08:22:39 -06:00
|
|
|
return false;
|
|
|
|
else
|
|
|
|
return b;
|
2011-02-08 08:08:12 -06:00
|
|
|
}
|
2012-08-22 05:11:28 -05:00
|
|
|
|
|
|
|
|
2012-08-22 08:37:05 -05:00
|
|
|
const char *qemuCapsGetBinary(qemuCapsPtr caps)
|
|
|
|
{
|
|
|
|
return caps->binary;
|
|
|
|
}
|
|
|
|
|
2012-08-22 05:11:28 -05:00
|
|
|
const char *qemuCapsGetArch(qemuCapsPtr caps)
|
|
|
|
{
|
|
|
|
return caps->arch;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int qemuCapsGetVersion(qemuCapsPtr caps)
|
|
|
|
{
|
|
|
|
return caps->version;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
unsigned int qemuCapsGetKVMVersion(qemuCapsPtr caps)
|
|
|
|
{
|
|
|
|
return caps->kvmVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-22 10:28:55 -05:00
|
|
|
int qemuCapsAddCPUDefinition(qemuCapsPtr caps,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
char *tmp = strdup(name);
|
|
|
|
if (!tmp) {
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (VIR_EXPAND_N(caps->cpuDefinitions, caps->ncpuDefinitions, 1) < 0) {
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
virReportOOMError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
caps->cpuDefinitions[caps->ncpuDefinitions-1] = tmp;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-22 05:11:28 -05:00
|
|
|
size_t qemuCapsGetCPUDefinitions(qemuCapsPtr caps,
|
|
|
|
char ***names)
|
|
|
|
{
|
2012-08-22 08:37:05 -05:00
|
|
|
if (names)
|
|
|
|
*names = caps->cpuDefinitions;
|
2012-08-22 05:11:28 -05:00
|
|
|
return caps->ncpuDefinitions;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t qemuCapsGetMachineTypes(qemuCapsPtr caps,
|
|
|
|
char ***names)
|
|
|
|
{
|
2012-08-22 08:37:05 -05:00
|
|
|
if (names)
|
|
|
|
*names = caps->machineTypes;
|
2012-08-22 05:11:28 -05:00
|
|
|
return caps->nmachineTypes;
|
|
|
|
}
|
|
|
|
|
2012-08-22 08:37:05 -05:00
|
|
|
int qemuCapsGetMachineTypesCaps(qemuCapsPtr caps,
|
|
|
|
size_t *nmachines,
|
|
|
|
virCapsGuestMachinePtr **machines)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
*nmachines = 0;
|
|
|
|
*machines = NULL;
|
|
|
|
if (VIR_ALLOC_N(*machines, caps->nmachineTypes) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
*nmachines = caps->nmachineTypes;
|
|
|
|
|
|
|
|
for (i = 0 ; i < caps->nmachineTypes ; i++) {
|
|
|
|
virCapsGuestMachinePtr mach;
|
|
|
|
if (VIR_ALLOC(mach) < 0)
|
|
|
|
goto no_memory;
|
|
|
|
if (caps->machineAliases[i]) {
|
|
|
|
if (!(mach->name = strdup(caps->machineAliases[i])))
|
|
|
|
goto no_memory;
|
|
|
|
if (!(mach->canonical = strdup(caps->machineTypes[i])))
|
|
|
|
goto no_memory;
|
|
|
|
} else {
|
|
|
|
if (!(mach->name = strdup(caps->machineTypes[i])))
|
|
|
|
goto no_memory;
|
|
|
|
}
|
|
|
|
(*machines)[i] = mach;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virCapabilitiesFreeMachines(*machines, *nmachines);
|
|
|
|
*nmachines = 0;
|
|
|
|
*machines = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-08-22 05:11:28 -05:00
|
|
|
|
|
|
|
const char *qemuCapsGetCanonicalMachine(qemuCapsPtr caps,
|
|
|
|
const char *name)
|
|
|
|
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0 ; i < caps->nmachineTypes ; i++) {
|
|
|
|
if (!caps->machineAliases[i])
|
|
|
|
continue;
|
|
|
|
if (STREQ(caps->machineAliases[i], name))
|
|
|
|
return caps->machineTypes[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return name;
|
|
|
|
}
|
2012-09-10 05:47:56 -05:00
|
|
|
|
|
|
|
|
2012-09-13 09:54:02 -05:00
|
|
|
static int
|
|
|
|
qemuCapsProbeQMPCommands(qemuCapsPtr caps,
|
|
|
|
qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
char **commands = NULL;
|
|
|
|
int ncommands;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if ((ncommands = qemuMonitorGetCommands(mon, &commands)) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = 0 ; i < ncommands ; i++) {
|
|
|
|
char *name = commands[i];
|
|
|
|
if (STREQ(name, "system_wakeup"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_WAKEUP);
|
|
|
|
else if (STREQ(name, "transaction"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_TRANSACTION);
|
|
|
|
else if (STREQ(name, "block_job_cancel"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_BLOCKJOB_SYNC);
|
|
|
|
else if (STREQ(name, "block-job-cancel"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_BLOCKJOB_ASYNC);
|
|
|
|
else if (STREQ(name, "dump-guest-memory"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DUMP_GUEST_MEMORY);
|
2012-08-22 12:05:08 -05:00
|
|
|
else if (STREQ(name, "query-spice"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SPICE);
|
|
|
|
else if (STREQ(name, "query-kvm"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_KVM);
|
2012-09-13 09:54:02 -05:00
|
|
|
VIR_FREE(name);
|
|
|
|
}
|
|
|
|
VIR_FREE(commands);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuCapsProbeQMPEvents(qemuCapsPtr caps,
|
|
|
|
qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
char **events = NULL;
|
|
|
|
int nevents;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if ((nevents = qemuMonitorGetEvents(mon, &events)) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
for (i = 0 ; i < nevents ; i++) {
|
|
|
|
char *name = events[i];
|
|
|
|
|
|
|
|
if (STREQ(name, "BALLOON_CHANGE"))
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_BALLOON_EVENT);
|
|
|
|
VIR_FREE(name);
|
|
|
|
}
|
|
|
|
VIR_FREE(events);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
static int
|
|
|
|
qemuCapsProbeQMPObjects(qemuCapsPtr caps,
|
|
|
|
qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int nvalues;
|
|
|
|
char **values;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if ((nvalues = qemuMonitorGetObjectTypes(mon, &values)) < 0)
|
|
|
|
return -1;
|
|
|
|
qemuCapsProcessStringFlags(caps,
|
|
|
|
ARRAY_CARDINALITY(qemuCapsObjectTypes),
|
|
|
|
qemuCapsObjectTypes,
|
|
|
|
nvalues, values);
|
|
|
|
qemuCapsFreeStringList(nvalues, values);
|
|
|
|
|
|
|
|
for (i = 0 ; i < ARRAY_CARDINALITY(qemuCapsObjectProps); i++) {
|
|
|
|
const char *type = qemuCapsObjectProps[i].type;
|
|
|
|
if ((nvalues = qemuMonitorGetObjectProps(mon,
|
|
|
|
type,
|
|
|
|
&values)) < 0)
|
|
|
|
return -1;
|
|
|
|
qemuCapsProcessStringFlags(caps,
|
|
|
|
qemuCapsObjectProps[i].nprops,
|
|
|
|
qemuCapsObjectProps[i].props,
|
|
|
|
nvalues, values);
|
|
|
|
qemuCapsFreeStringList(nvalues, values);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prefer -chardev spicevmc (detected earlier) over -device spicevmc */
|
|
|
|
if (qemuCapsGet(caps, QEMU_CAPS_CHARDEV_SPICEVMC))
|
|
|
|
qemuCapsClear(caps, QEMU_CAPS_DEVICE_SPICEVMC);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuCapsProbeQMPMachineTypes(qemuCapsPtr caps,
|
|
|
|
qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
qemuMonitorMachineInfoPtr *machines = NULL;
|
|
|
|
int nmachines = 0;
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if ((nmachines = qemuMonitorGetMachines(mon, &machines)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (VIR_ALLOC_N(caps->machineTypes, nmachines) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (VIR_ALLOC_N(caps->machineAliases, nmachines) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < nmachines ; i++) {
|
|
|
|
if (machines[i]->alias) {
|
|
|
|
if (!(caps->machineAliases[i] = strdup(machines[i]->name))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!(caps->machineTypes[i] = strdup(machines[i]->alias))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!(caps->machineTypes[i] = strdup(machines[i]->name))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
for (i = 0 ; i < nmachines ; i++)
|
|
|
|
qemuMonitorMachineInfoFree(machines[i]);
|
|
|
|
VIR_FREE(machines);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuCapsProbeQMPCPUDefinitions(qemuCapsPtr caps,
|
|
|
|
qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
int ncpuDefinitions;
|
|
|
|
char **cpuDefinitions;
|
|
|
|
|
|
|
|
if ((ncpuDefinitions = qemuMonitorGetCPUDefinitions(mon, &cpuDefinitions)) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
caps->ncpuDefinitions = ncpuDefinitions;
|
|
|
|
caps->cpuDefinitions = cpuDefinitions;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-13 09:54:02 -05:00
|
|
|
int qemuCapsProbeQMP(qemuCapsPtr caps,
|
|
|
|
qemuMonitorPtr mon)
|
|
|
|
{
|
|
|
|
VIR_DEBUG("caps=%p mon=%p", caps, mon);
|
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
if (caps->usedQMP)
|
|
|
|
return 0;
|
|
|
|
|
2012-09-13 09:54:02 -05:00
|
|
|
if (qemuCapsProbeQMPCommands(caps, mon) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (qemuCapsProbeQMPEvents(caps, mon) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-10 05:47:56 -05:00
|
|
|
#define QEMU_SYSTEM_PREFIX "qemu-system-"
|
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
static int
|
|
|
|
qemuCapsInitHelp(qemuCapsPtr caps)
|
2012-09-10 05:47:56 -05:00
|
|
|
{
|
2012-08-22 12:05:08 -05:00
|
|
|
virCommandPtr cmd = NULL;
|
2012-09-10 05:47:56 -05:00
|
|
|
unsigned int is_kvm;
|
|
|
|
char *help = NULL;
|
2012-08-22 12:05:08 -05:00
|
|
|
int ret = -1;
|
|
|
|
const char *tmp;
|
|
|
|
struct utsname ut;
|
2012-09-10 05:47:56 -05:00
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
VIR_DEBUG("caps=%p", caps);
|
2012-09-10 05:47:56 -05:00
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
tmp = strstr(caps->binary, QEMU_SYSTEM_PREFIX);
|
2012-09-10 05:47:56 -05:00
|
|
|
if (tmp) {
|
|
|
|
tmp += strlen(QEMU_SYSTEM_PREFIX);
|
2012-09-26 07:48:31 -05:00
|
|
|
|
|
|
|
/* For historical compat we use 'itanium' as arch name */
|
|
|
|
if (STREQ(tmp, "ia64"))
|
|
|
|
tmp = "itanium";
|
2012-09-10 05:47:56 -05:00
|
|
|
} else {
|
|
|
|
uname_normalize(&ut);
|
|
|
|
tmp = ut.machine;
|
|
|
|
}
|
2012-08-22 12:05:08 -05:00
|
|
|
if (!(caps->arch = strdup(tmp))) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
2012-09-10 05:47:56 -05:00
|
|
|
}
|
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
cmd = qemuCapsProbeCommand(caps->binary, NULL);
|
2012-09-10 05:47:56 -05:00
|
|
|
virCommandAddArgList(cmd, "-help", NULL);
|
|
|
|
virCommandSetOutputBuffer(cmd, &help);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, NULL) < 0)
|
2012-08-22 12:05:08 -05:00
|
|
|
goto cleanup;
|
2012-09-10 05:47:56 -05:00
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
if (qemuCapsParseHelpStr(caps->binary,
|
|
|
|
help, caps,
|
2012-09-10 05:47:56 -05:00
|
|
|
&caps->version,
|
|
|
|
&is_kvm,
|
|
|
|
&caps->kvmVersion,
|
|
|
|
false) < 0)
|
2012-08-22 12:05:08 -05:00
|
|
|
goto cleanup;
|
2012-09-10 05:47:56 -05:00
|
|
|
|
|
|
|
/* 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,?") &&
|
2012-08-22 12:05:08 -05:00
|
|
|
qemuCapsExtractDeviceStr(caps->binary, caps) < 0)
|
|
|
|
goto cleanup;
|
2012-09-10 05:47:56 -05:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (qemuCapsProbeCPUModels(caps) < 0)
|
2012-08-22 12:05:08 -05:00
|
|
|
goto cleanup;
|
2012-09-10 05:47:56 -05:00
|
|
|
|
2012-08-22 11:23:00 -05:00
|
|
|
if (qemuCapsProbeMachineTypes(caps) < 0)
|
2012-08-22 12:05:08 -05:00
|
|
|
goto cleanup;
|
2012-09-10 05:47:56 -05:00
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
ret = 0;
|
2012-09-10 05:47:56 -05:00
|
|
|
cleanup:
|
2012-08-22 12:05:08 -05:00
|
|
|
virCommandFree(cmd);
|
2012-09-10 05:47:56 -05:00
|
|
|
VIR_FREE(help);
|
2012-08-22 12:05:08 -05:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void qemuCapsMonitorNotify(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
|
|
virDomainObjPtr vm ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
static qemuMonitorCallbacks callbacks = {
|
|
|
|
.eofNotify = qemuCapsMonitorNotify,
|
|
|
|
.errorNotify = qemuCapsMonitorNotify,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Capabilities that we assume are always enabled
|
|
|
|
* for QEMU >= 1.2.0
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
qemuCapsInitQMPBasic(qemuCapsPtr caps)
|
|
|
|
{
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VNC_COLON);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_REBOOT);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NAME);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_UUID);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VNET_HDR);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_TCP);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_EXEC);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_CACHE_V2);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_FORMAT);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VGA);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_0_10);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MEM_PATH);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_SERIAL);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_UNIX);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_CHARDEV);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_ENABLE_KVM);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MONITOR_JSON);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_BALLOON);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DEVICE);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SDL);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SMP_TOPOLOGY);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NETDEV);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_RTC);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VHOST_NET);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_HPET);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NODEFCONFIG);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_BOOT_MENU);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_FSDEV);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NESTING);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NAME_PROCESS);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_READONLY);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_SMBIOS_TYPE);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VGA_NONE);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_MIGRATE_QEMU_FD);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_AIO);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_CHARDEV_SPICEVMC);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DEVICE_QXL_VGA);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_CACHE_DIRECTSYNC);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_SHUTDOWN);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_CACHE_UNSAFE);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_ACPI);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_FSDEV_READONLY);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_VIRTIO_BLK_SG_IO);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_COPY_ON_READ);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_CPU_HOST);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_FSDEV_WRITEOUT);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_DRIVE_IOTUNE);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_WAKEUP);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NO_USER_CONFIG);
|
|
|
|
qemuCapsSet(caps, QEMU_CAPS_NETDEV_BRIDGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
qemuCapsInitQMP(qemuCapsPtr caps,
|
2012-10-02 03:59:56 -05:00
|
|
|
const char *libDir,
|
|
|
|
const char *runDir)
|
2012-08-22 12:05:08 -05:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
virCommandPtr cmd = NULL;
|
|
|
|
qemuMonitorPtr mon = NULL;
|
|
|
|
int major, minor, micro;
|
|
|
|
char *package;
|
|
|
|
int status = 0;
|
|
|
|
virDomainChrSourceDef config;
|
|
|
|
char *monarg = NULL;
|
|
|
|
char *monpath = NULL;
|
2012-10-02 03:59:56 -05:00
|
|
|
char *pidfile = NULL;
|
2012-08-22 12:05:08 -05:00
|
|
|
|
2012-10-02 03:59:56 -05:00
|
|
|
/* the ".sock" sufix is important to avoid a possible clash with a qemu
|
|
|
|
* domain called "capabilities"
|
|
|
|
*/
|
2012-08-22 12:05:08 -05:00
|
|
|
if (virAsprintf(&monpath, "%s/%s", libDir, "capabilities.monitor.sock") < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (virAsprintf(&monarg, "unix:%s,server,nowait", monpath) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-10-02 03:59:56 -05:00
|
|
|
/* ".pidfile" suffix is used rather than ".pid" to avoid a possible clash
|
|
|
|
* with a qemu domain called "capabilities"
|
|
|
|
*/
|
|
|
|
if (virAsprintf(&pidfile, "%s/%s", runDir, "capabilities.pidfile") < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
memset(&config, 0, sizeof(config));
|
|
|
|
config.type = VIR_DOMAIN_CHR_TYPE_UNIX;
|
|
|
|
config.data.nix.path = monpath;
|
|
|
|
config.data.nix.listen = false;
|
|
|
|
|
|
|
|
VIR_DEBUG("Try to get caps via QMP caps=%p", caps);
|
|
|
|
|
|
|
|
cmd = virCommandNewArgList(caps->binary,
|
|
|
|
"-S",
|
|
|
|
"-no-user-config",
|
|
|
|
"-nodefaults",
|
|
|
|
"-nographic",
|
|
|
|
"-M", "none",
|
|
|
|
"-qmp", monarg,
|
2012-10-02 03:59:56 -05:00
|
|
|
"-pidfile", pidfile,
|
2012-08-22 12:05:08 -05:00
|
|
|
"-daemonize",
|
|
|
|
NULL);
|
|
|
|
virCommandAddEnvPassCommon(cmd);
|
|
|
|
virCommandClearCaps(cmd);
|
|
|
|
|
|
|
|
if (virCommandRun(cmd, &status) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (status != 0) {
|
|
|
|
ret = 0;
|
|
|
|
VIR_DEBUG("QEMU %s exited with status %d", caps->binary, status);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(mon = qemuMonitorOpen(NULL, &config, true, &callbacks)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
qemuMonitorLock(mon);
|
|
|
|
|
|
|
|
if (qemuMonitorSetCapabilities(mon) < 0) {
|
|
|
|
virErrorPtr err = virGetLastError();
|
|
|
|
VIR_DEBUG("Failed to set monitor capabilities %s",
|
|
|
|
err ? err->message : "<unknown problem>");
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qemuMonitorGetVersion(mon,
|
|
|
|
&major, &minor, µ,
|
|
|
|
&package) < 0) {
|
|
|
|
virErrorPtr err = virGetLastError();
|
|
|
|
VIR_DEBUG("Failed to query monitor version %s",
|
|
|
|
err ? err->message : "<unknown problem>");
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("Got version %d.%d.%d (%s)",
|
|
|
|
major, minor, micro, NULLSTR(package));
|
|
|
|
|
|
|
|
if (!(major >= 1 || (major == 1 && minor >= 1))) {
|
|
|
|
VIR_DEBUG("Not new enough for QMP capabilities detection");
|
|
|
|
ret = 0;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
caps->usedQMP = true;
|
|
|
|
|
|
|
|
qemuCapsInitQMPBasic(caps);
|
|
|
|
|
|
|
|
if (!(caps->arch = qemuMonitorGetTargetArch(mon)))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
if (qemuCapsProbeQMPCommands(caps, mon) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (qemuCapsProbeQMPEvents(caps, mon) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (qemuCapsProbeQMPObjects(caps, mon) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (qemuCapsProbeQMPMachineTypes(caps, mon) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
if (qemuCapsProbeQMPCPUDefinitions(caps, mon) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (mon)
|
|
|
|
qemuMonitorUnlock(mon);
|
|
|
|
qemuMonitorClose(mon);
|
|
|
|
virCommandAbort(cmd);
|
2012-09-10 05:47:56 -05:00
|
|
|
virCommandFree(cmd);
|
2012-08-22 12:05:08 -05:00
|
|
|
VIR_FREE(monarg);
|
|
|
|
VIR_FREE(monpath);
|
2012-10-02 03:59:56 -05:00
|
|
|
|
|
|
|
if (pidfile) {
|
|
|
|
char ebuf[1024];
|
|
|
|
pid_t pid;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if ((rc = virPidFileReadPath(pidfile, &pid)) < 0) {
|
2012-10-02 07:36:50 -05:00
|
|
|
VIR_DEBUG("Failed to read pidfile %s: %s",
|
2012-10-02 03:59:56 -05:00
|
|
|
pidfile, virStrerror(-rc, ebuf, sizeof(ebuf)));
|
|
|
|
} else {
|
|
|
|
VIR_DEBUG("Killing QMP caps process %lld", (long long) pid);
|
|
|
|
if (virProcessKill(pid, SIGKILL) < 0 && errno != ESRCH)
|
|
|
|
VIR_ERROR(_("Failed to kill process %lld: %s"),
|
|
|
|
(long long) pid,
|
|
|
|
virStrerror(errno, ebuf, sizeof(ebuf)));
|
|
|
|
}
|
|
|
|
unlink(pidfile);
|
|
|
|
VIR_FREE(pidfile);
|
|
|
|
}
|
2012-08-22 12:05:08 -05:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qemuCapsPtr qemuCapsNewForBinary(const char *binary,
|
2012-10-02 03:59:56 -05:00
|
|
|
const char *libDir,
|
|
|
|
const char *runDir)
|
2012-08-22 12:05:08 -05:00
|
|
|
{
|
|
|
|
qemuCapsPtr caps = qemuCapsNew();
|
|
|
|
struct stat sb;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
if (!(caps->binary = strdup(binary)))
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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)) {
|
|
|
|
virReportSystemError(errno, _("QEMU binary %s is not executable"),
|
|
|
|
binary);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2012-10-02 03:59:56 -05:00
|
|
|
if ((rv = qemuCapsInitQMP(caps, libDir, runDir)) < 0)
|
2012-08-22 12:05:08 -05:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
if (!caps->usedQMP &&
|
|
|
|
qemuCapsInitHelp(caps) < 0)
|
|
|
|
goto error;
|
|
|
|
|
2012-09-10 05:47:56 -05:00
|
|
|
return caps;
|
|
|
|
|
|
|
|
no_memory:
|
|
|
|
virReportOOMError();
|
|
|
|
error:
|
|
|
|
virObjectUnref(caps);
|
|
|
|
caps = NULL;
|
2012-08-22 12:05:08 -05:00
|
|
|
return NULL;
|
2012-09-10 05:47:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2012-08-22 07:54:13 -05:00
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
qemuCapsHashDataFree(void *payload, const void *key ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
virObjectUnref(payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qemuCapsCachePtr
|
2012-10-02 03:59:56 -05:00
|
|
|
qemuCapsCacheNew(const char *libDir, const char *runDir)
|
2012-08-22 07:54:13 -05:00
|
|
|
{
|
|
|
|
qemuCapsCachePtr cache;
|
|
|
|
|
|
|
|
if (VIR_ALLOC(cache) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virMutexInit(&cache->lock) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to initialize mutex"));
|
|
|
|
VIR_FREE(cache);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(cache->binaries = virHashCreate(10, qemuCapsHashDataFree)))
|
|
|
|
goto error;
|
2012-10-02 03:59:56 -05:00
|
|
|
if (!(cache->libDir = strdup(libDir)) ||
|
|
|
|
!(cache->runDir = strdup(runDir))) {
|
2012-08-22 12:05:08 -05:00
|
|
|
virReportOOMError();
|
|
|
|
goto error;
|
|
|
|
}
|
2012-08-22 07:54:13 -05:00
|
|
|
|
|
|
|
return cache;
|
|
|
|
|
|
|
|
error:
|
|
|
|
qemuCapsCacheFree(cache);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qemuCapsPtr
|
|
|
|
qemuCapsCacheLookup(qemuCapsCachePtr cache, const char *binary)
|
|
|
|
{
|
|
|
|
qemuCapsPtr ret = NULL;
|
|
|
|
virMutexLock(&cache->lock);
|
|
|
|
ret = virHashLookup(cache->binaries, binary);
|
|
|
|
if (ret &&
|
|
|
|
!qemuCapsIsValid(ret)) {
|
|
|
|
VIR_DEBUG("Cached capabilities %p no longer valid for %s",
|
|
|
|
ret, binary);
|
|
|
|
virHashRemoveEntry(cache->binaries, binary);
|
|
|
|
ret = NULL;
|
|
|
|
}
|
|
|
|
if (!ret) {
|
|
|
|
VIR_DEBUG("Creating capabilities for %s",
|
|
|
|
binary);
|
2012-10-02 03:59:56 -05:00
|
|
|
ret = qemuCapsNewForBinary(binary, cache->libDir, cache->runDir);
|
2012-08-22 07:54:13 -05:00
|
|
|
if (ret) {
|
|
|
|
VIR_DEBUG("Caching capabilities %p for %s",
|
|
|
|
ret, binary);
|
|
|
|
if (virHashAddEntry(cache->binaries, binary, ret) < 0) {
|
|
|
|
virObjectUnref(ret);
|
|
|
|
ret = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
VIR_DEBUG("Returning caps %p for %s", ret, binary);
|
|
|
|
virObjectRef(ret);
|
|
|
|
virMutexUnlock(&cache->lock);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qemuCapsPtr
|
|
|
|
qemuCapsCacheLookupCopy(qemuCapsCachePtr cache, const char *binary)
|
|
|
|
{
|
|
|
|
qemuCapsPtr caps = qemuCapsCacheLookup(cache, binary);
|
|
|
|
qemuCapsPtr ret;
|
|
|
|
|
|
|
|
if (!caps)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
ret = qemuCapsNewCopy(caps);
|
|
|
|
virObjectUnref(caps);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
qemuCapsCacheFree(qemuCapsCachePtr cache)
|
|
|
|
{
|
|
|
|
if (!cache)
|
|
|
|
return;
|
|
|
|
|
2012-08-22 12:05:08 -05:00
|
|
|
VIR_FREE(cache->libDir);
|
2012-10-02 03:59:56 -05:00
|
|
|
VIR_FREE(cache->runDir);
|
2012-08-22 07:54:13 -05:00
|
|
|
virHashFree(cache->binaries);
|
|
|
|
virMutexDestroy(&cache->lock);
|
|
|
|
VIR_FREE(cache);
|
|
|
|
}
|