[libvirt] [PATCH 00/10] Re-arrange the QEMU driver code

The QEMU driver is getting horribly large & I constantly find myself getting lost in it. This series re-arranges the code, pulling out the following areas of functionality into new files - capabilities construction - command line arg generation/parsing - domain private data / namespace handling - audit reporting - cgroup management - host device management - device hotplug There is no functional change in any of these patches. A handful of methods have been renamed if they started with the obsolete 'qemud' prefix. Since these patches will massively conflict with any further patches to the QEMU driver, the sooner they're applied the better... There is more to be pulled out later which is slightly more entangled / difficult - Events - Process startup/shutdown - Migration Daniel

The qemu_conf.c code is doing three jobs, driver config file loading, QEMU capabilities management and QEMU command line management. Move the capabilities code into its own file * src/qemu/qemu_capabilities.c, src/qemu/qemu_capabilities.h: New capabilities management code * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Delete capabilities code * src/qemu/qemu_conf.h: Adapt for API renames * src/Makefile.am: add src/qemu/qemu_capabilities.c --- src/Makefile.am | 1 + src/qemu/qemu_capabilities.c | 1253 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 113 ++++ src/qemu/qemu_conf.c | 1191 +--------------------------------------- src/qemu/qemu_conf.h | 81 --- src/qemu/qemu_driver.c | 55 +- 6 files changed, 1398 insertions(+), 1296 deletions(-) create mode 100644 src/qemu/qemu_capabilities.c create mode 100644 src/qemu/qemu_capabilities.h diff --git a/src/Makefile.am b/src/Makefile.am index 196d8af..4ce0b35 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -267,6 +267,7 @@ VBOX_DRIVER_SOURCES = \ VBOX_DRIVER_EXTRA_DIST = vbox/vbox_tmpl.c vbox/README QEMU_DRIVER_SOURCES = \ + qemu/qemu_capabilities.c qemu/qemu_capabilities.h\ qemu/qemu_conf.c qemu/qemu_conf.h \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c new file mode 100644 index 0000000..913fbf7 --- /dev/null +++ b/src/qemu/qemu_capabilities.c @@ -0,0 +1,1253 @@ +/* + * qemu_capabilities.c: QEMU capabilities generation + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * 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" +#include "files.h" +#include "nodeinfo.h" +#include "cpu/cpu.h" +#include "domain_conf.h" +#include "qemu_conf.h" + +#include <sys/stat.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/utsname.h> + +#define VIR_FROM_THIS VIR_FROM_QEMU + +struct qemu_feature_flags { + const char *name; + const int default_on; + const int toggle; +}; + +struct qemu_arch_info { + const char *arch; + int wordsize; + const char *machine; + const char *binary; + const char *altbinary; + const struct qemu_feature_flags *flags; + int nflags; +}; + +/* Feature flags for the architecture info */ +static const struct qemu_feature_flags const arch_info_i686_flags [] = { + { "pae", 1, 0 }, + { "nonpae", 1, 0 }, + { "acpi", 1, 1 }, + { "apic", 1, 0 }, +}; + +static const struct qemu_feature_flags const arch_info_x86_64_flags [] = { + { "acpi", 1, 1 }, + { "apic", 1, 0 }, +}; + +/* The archicture tables for supported QEMU archs */ +static const struct qemu_arch_info const arch_info_hvm[] = { + { "i686", 32, NULL, "qemu", + "qemu-system-x86_64", arch_info_i686_flags, 4 }, + { "x86_64", 64, NULL, "qemu-system-x86_64", + NULL, arch_info_x86_64_flags, 2 }, + { "arm", 32, NULL, "qemu-system-arm", NULL, NULL, 0 }, + { "mips", 32, NULL, "qemu-system-mips", NULL, NULL, 0 }, + { "mipsel", 32, NULL, "qemu-system-mipsel", NULL, NULL, 0 }, + { "sparc", 32, NULL, "qemu-system-sparc", NULL, NULL, 0 }, + { "ppc", 32, NULL, "qemu-system-ppc", NULL, NULL, 0 }, + { "itanium", 64, NULL, "qemu-system-ia64", NULL, NULL, 0 }, + { "s390x", 64, NULL, "qemu-system-s390x", NULL, NULL, 0 }, +}; + +static const struct qemu_arch_info const arch_info_xen[] = { + { "i686", 32, "xenner", "xenner", NULL, arch_info_i686_flags, 4 }, + { "x86_64", 64, "xenner", "xenner", NULL, arch_info_x86_64_flags, 2 }, +}; + + +/* Format is: + * <machine> <desc> [(default)|(alias of <canonical>)] + */ +static int +qemuCapsParseMachineTypesStr(const char *output, + virCapsGuestMachinePtr **machines, + int *nmachines) +{ + const char *p = output; + const char *next; + virCapsGuestMachinePtr *list = NULL; + int nitems = 0; + + do { + const char *t; + virCapsGuestMachinePtr machine; + + if ((next = strchr(p, '\n'))) + ++next; + + if (STRPREFIX(p, "Supported machines are:")) + continue; + + if (!(t = strchr(p, ' ')) || (next && t >= next)) + continue; + + if (VIR_ALLOC(machine) < 0) + goto no_memory; + + if (!(machine->name = strndup(p, t - p))) { + VIR_FREE(machine); + goto no_memory; + } + + if (VIR_REALLOC_N(list, nitems + 1) < 0) { + VIR_FREE(machine->name); + VIR_FREE(machine); + goto no_memory; + } + + p = t; + if (!(t = strstr(p, "(default)")) || (next && t >= next)) { + list[nitems++] = machine; + } else { + /* put the default first in the list */ + memmove(list + 1, list, sizeof(*list) * nitems); + list[0] = machine; + nitems++; + } + + if ((t = strstr(p, "(alias of ")) && (!next || t < next)) { + p = t + strlen("(alias of "); + if (!(t = strchr(p, ')')) || (next && t >= next)) + continue; + + if (!(machine->canonical = strndup(p, t - p))) + goto no_memory; + } + } while ((p = next)); + + *machines = list; + *nmachines = nitems; + + return 0; + + no_memory: + virReportOOMError(); + virCapabilitiesFreeMachines(list, nitems); + return -1; +} + +int +qemuCapsProbeMachineTypes(const char *binary, + virCapsGuestMachinePtr **machines, + int *nmachines) +{ + const char *const qemuarg[] = { binary, "-M", "?", NULL }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + char *output; + enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; + pid_t child; + int newstdout = -1, len; + int ret = -1, status; + + if (virExec(qemuarg, qemuenv, NULL, + &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) + return -1; + + len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); + if (len < 0) { + virReportSystemError(errno, "%s", + _("Unable to read 'qemu -M ?' output")); + goto cleanup; + } + + if (qemuCapsParseMachineTypesStr(output, machines, nmachines) < 0) + goto cleanup2; + + ret = 0; + +cleanup2: + VIR_FREE(output); +cleanup: + if (VIR_CLOSE(newstdout) < 0) + ret = -1; + +rewait: + if (waitpid(child, &status, 0) != child) { + if (errno == EINTR) + goto rewait; + + VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), + WEXITSTATUS(status), (unsigned long)child); + ret = -1; + } + /* Check & log unexpected exit status, but don't fail, + * as there's really no need to throw an error if we did + * actually read a valid version number above */ + if (WEXITSTATUS(status) != 0) { + VIR_WARN("Unexpected exit status '%d', qemu probably failed", + WEXITSTATUS(status)); + } + + return ret; +} + +static int +qemuCapsGetOldMachinesFromInfo(virCapsGuestDomainInfoPtr info, + const char *emulator, + time_t emulator_mtime, + virCapsGuestMachinePtr **machines, + int *nmachines) +{ + virCapsGuestMachinePtr *list; + int i; + + if (!info->nmachines) + return 0; + + if (!info->emulator || !STREQ(emulator, info->emulator)) + return 0; + + if (emulator_mtime != info->emulator_mtime) { + VIR_DEBUG("mtime on %s has changed, refreshing machine types", + info->emulator); + return 0; + } + + if (VIR_ALLOC_N(list, info->nmachines) < 0) { + virReportOOMError(); + return 0; + } + + for (i = 0; i < info->nmachines; i++) { + if (VIR_ALLOC(list[i]) < 0) { + goto no_memory; + } + if (info->machines[i]->name && + !(list[i]->name = strdup(info->machines[i]->name))) { + goto no_memory; + } + if (info->machines[i]->canonical && + !(list[i]->canonical = strdup(info->machines[i]->canonical))) { + goto no_memory; + } + } + + *machines = list; + *nmachines = info->nmachines; + + return 1; + + no_memory: + virReportOOMError(); + virCapabilitiesFreeMachines(list, info->nmachines); + return 0; +} + +static int +qemuCapsGetOldMachines(const char *ostype, + const char *arch, + int wordsize, + const char *emulator, + time_t emulator_mtime, + virCapsPtr old_caps, + virCapsGuestMachinePtr **machines, + int *nmachines) +{ + int i; + + for (i = 0; i < old_caps->nguests; i++) { + virCapsGuestPtr guest = old_caps->guests[i]; + int j; + + if (!STREQ(ostype, guest->ostype) || + !STREQ(arch, guest->arch.name) || + wordsize != guest->arch.wordsize) + continue; + + for (j = 0; j < guest->arch.ndomains; j++) { + virCapsGuestDomainPtr dom = guest->arch.domains[j]; + + if (qemuCapsGetOldMachinesFromInfo(&dom->info, + emulator, emulator_mtime, + machines, nmachines)) + return 1; + } + + if (qemuCapsGetOldMachinesFromInfo(&guest->arch.defaultInfo, + emulator, emulator_mtime, + machines, nmachines)) + return 1; + } + + return 0; +} + + +typedef int +(*qemuCapsParseCPUModels)(const char *output, + unsigned int *retcount, + const char ***retcpus); + +/* Format: + * <arch> <model> + * qemu-0.13 encloses some model names in []: + * <arch> [<model>] + */ +static int +qemuCapsParseX86Models(const char *output, + unsigned int *retcount, + const char ***retcpus) +{ + const char *p = output; + const char *next; + unsigned int count = 0; + const char **cpus = NULL; + int i; + + do { + const char *t; + + 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; + + if (retcpus) { + unsigned int len; + + if (VIR_REALLOC_N(cpus, count + 1) < 0) + goto error; + + if (next) + len = next - p - 1; + else + len = strlen(p); + + if (len > 2 && *p == '[' && p[len - 1] == ']') { + p++; + len -= 2; + } + + if (!(cpus[count] = strndup(p, len))) + goto error; + } + count++; + } while ((p = next)); + + if (retcount) + *retcount = count; + if (retcpus) + *retcpus = cpus; + + return 0; + +error: + if (cpus) { + for (i = 0; i < count; i++) + VIR_FREE(cpus[i]); + } + VIR_FREE(cpus); + + return -1; +} + + +int +qemuCapsProbeCPUModels(const char *qemu, + unsigned long long qemuCmdFlags, + const char *arch, + unsigned int *count, + const char ***cpus) +{ + const char *const qemuarg[] = { + qemu, + "-cpu", "?", + (qemuCmdFlags & QEMUD_CMD_FLAG_NODEFCONFIG) ? "-nodefconfig" : NULL, + NULL + }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; + char *output = NULL; + int newstdout = -1; + int ret = -1; + pid_t child; + int status; + int len; + qemuCapsParseCPUModels parse; + + if (count) + *count = 0; + if (cpus) + *cpus = NULL; + + if (STREQ(arch, "i686") || STREQ(arch, "x86_64")) + parse = qemuCapsParseX86Models; + else { + VIR_DEBUG("don't know how to parse %s CPU models", arch); + return 0; + } + + if (virExec(qemuarg, qemuenv, NULL, + &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) + return -1; + + len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); + if (len < 0) { + virReportSystemError(errno, "%s", + _("Unable to read QEMU supported CPU models")); + goto cleanup; + } + + if (parse(output, count, cpus) < 0) { + virReportOOMError(); + goto cleanup; + } + + ret = 0; + +cleanup: + VIR_FREE(output); + if (VIR_CLOSE(newstdout) < 0) + ret = -1; + +rewait: + if (waitpid(child, &status, 0) != child) { + if (errno == EINTR) + goto rewait; + + VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), + WEXITSTATUS(status), (unsigned long)child); + ret = -1; + } + /* Check & log unexpected exit status, but don't fail, + * as there's really no need to throw an error if we did + * actually read a valid version number above */ + if (WEXITSTATUS(status) != 0) { + VIR_WARN("Unexpected exit status '%d', qemu probably failed", + WEXITSTATUS(status)); + } + + return ret; +} + + +static int +qemuCapsInitGuest(virCapsPtr caps, + virCapsPtr old_caps, + const char *hostmachine, + const struct qemu_arch_info *info, + int hvm) +{ + virCapsGuestPtr guest; + int i; + int haskvm = 0; + int haskqemu = 0; + char *kvmbin = NULL; + char *binary = NULL; + time_t binary_mtime; + virCapsGuestMachinePtr *machines = NULL; + int nmachines = 0; + struct stat st; + unsigned int ncpus; + int ret = -1; + + /* Check for existance of base emulator, or alternate base + * which can be used with magic cpu choice + */ + binary = virFindFileInPath(info->binary); + + if (binary == NULL || access(binary, X_OK) != 0) { + VIR_FREE(binary); + binary = virFindFileInPath(info->altbinary); + + if (binary != NULL && access(binary, X_OK) != 0) + VIR_FREE(binary); + } + + /* Can use acceleration for KVM/KQEMU if + * - host & guest arches match + * Or + * - hostarch is x86_64 and guest arch is i686 + * The latter simply needs "-cpu qemu32" + */ + if (STREQ(info->arch, hostmachine) || + (STREQ(hostmachine, "x86_64") && STREQ(info->arch, "i686"))) { + if (access("/dev/kvm", F_OK) == 0) { + const char *const kvmbins[] = { "/usr/libexec/qemu-kvm", /* RHEL */ + "qemu-kvm", /* Fedora */ + "kvm" }; /* Upstream .spec */ + + for (i = 0; i < ARRAY_CARDINALITY(kvmbins); ++i) { + kvmbin = virFindFileInPath(kvmbins[i]); + + if (kvmbin == NULL || access(kvmbin, X_OK) != 0) { + VIR_FREE(kvmbin); + continue; + } + + haskvm = 1; + if (!binary) + binary = kvmbin; + + break; + } + } + + if (access("/dev/kqemu", F_OK) == 0) + haskqemu = 1; + } + + if (!binary) + return 0; + + if (stat(binary, &st) == 0) { + binary_mtime = st.st_mtime; + } else { + char ebuf[1024]; + VIR_WARN("Failed to stat %s, most peculiar : %s", + binary, virStrerror(errno, ebuf, sizeof(ebuf))); + binary_mtime = 0; + } + + if (info->machine) { + virCapsGuestMachinePtr machine; + + if (VIR_ALLOC(machine) < 0) { + goto no_memory; + } + + if (!(machine->name = strdup(info->machine))) { + VIR_FREE(machine); + goto no_memory; + } + + nmachines = 1; + + if (VIR_ALLOC_N(machines, nmachines) < 0) { + VIR_FREE(machine->name); + VIR_FREE(machine); + goto no_memory; + } + + machines[0] = machine; + } else { + int probe = 1; + if (old_caps && binary_mtime) + probe = !qemuCapsGetOldMachines(hvm ? "hvm" : "xen", info->arch, + info->wordsize, binary, binary_mtime, + old_caps, &machines, &nmachines); + if (probe && + qemuCapsProbeMachineTypes(binary, &machines, &nmachines) < 0) + goto error; + } + + /* We register kvm as the base emulator too, since we can + * just give -no-kvm to disable acceleration if required */ + if ((guest = virCapabilitiesAddGuest(caps, + hvm ? "hvm" : "xen", + info->arch, + info->wordsize, + binary, + NULL, + nmachines, + machines)) == NULL) + goto error; + + machines = NULL; + nmachines = 0; + + guest->arch.defaultInfo.emulator_mtime = binary_mtime; + + if (caps->host.cpu && + qemuCapsProbeCPUModels(binary, 0, info->arch, &ncpus, NULL) == 0 && + ncpus > 0 && + !virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0)) + goto error; + + if (hvm) { + if (virCapabilitiesAddGuestDomain(guest, + "qemu", + NULL, + NULL, + 0, + NULL) == NULL) + goto error; + + if (haskqemu && + virCapabilitiesAddGuestDomain(guest, + "kqemu", + NULL, + NULL, + 0, + NULL) == NULL) + goto error; + + if (haskvm) { + virCapsGuestDomainPtr dom; + + if (stat(kvmbin, &st) == 0) { + binary_mtime = st.st_mtime; + } else { + char ebuf[1024]; + VIR_WARN("Failed to stat %s, most peculiar : %s", + binary, virStrerror(errno, ebuf, sizeof(ebuf))); + binary_mtime = 0; + } + + if (!STREQ(binary, kvmbin)) { + int probe = 1; + if (old_caps && binary_mtime) + probe = !qemuCapsGetOldMachines("hvm", info->arch, info->wordsize, + kvmbin, binary_mtime, + old_caps, &machines, &nmachines); + if (probe && + qemuCapsProbeMachineTypes(kvmbin, &machines, &nmachines) < 0) + goto error; + } + + if ((dom = virCapabilitiesAddGuestDomain(guest, + "kvm", + kvmbin, + NULL, + nmachines, + machines)) == NULL) { + goto error; + } + + machines = NULL; + nmachines = 0; + + dom->info.emulator_mtime = binary_mtime; + } + } else { + if (virCapabilitiesAddGuestDomain(guest, + "kvm", + NULL, + NULL, + 0, + NULL) == NULL) + goto error; + } + + if (info->nflags) { + for (i = 0 ; i < info->nflags ; i++) { + if (virCapabilitiesAddGuestFeature(guest, + info->flags[i].name, + info->flags[i].default_on, + info->flags[i].toggle) == NULL) + goto error; + } + } + + ret = 0; + +cleanup: + if (binary == kvmbin) { + /* don't double free */ + VIR_FREE(binary); + } else { + VIR_FREE(binary); + VIR_FREE(kvmbin); + } + + return ret; + +no_memory: + virReportOOMError(); + +error: + virCapabilitiesFreeMachines(machines, nmachines); + + goto cleanup; +} + + +static int +qemuCapsInitCPU(virCapsPtr caps, + const char *arch) +{ + 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; +} + + +virCapsPtr qemuCapsInit(virCapsPtr old_caps) +{ + struct utsname utsname; + virCapsPtr caps; + int i; + char *xenner = NULL; + + /* Really, this never fails - look at the man-page. */ + uname (&utsname); + + if ((caps = virCapabilitiesNew(utsname.machine, + 1, 1)) == NULL) + goto no_memory; + + /* 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); + VIR_WARN0("Failed to query host NUMA topology, disabling NUMA capabilities"); + } + + if (old_caps == NULL || old_caps->host.cpu == NULL) { + if (qemuCapsInitCPU(caps, utsname.machine) < 0) + VIR_WARN0("Failed to get host CPU"); + } + else { + caps->host.cpu = old_caps->host.cpu; + old_caps->host.cpu = NULL; + } + + virCapabilitiesAddHostMigrateTransport(caps, + "tcp"); + + /* First the pure HVM guests */ + for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_hvm) ; i++) + if (qemuCapsInitGuest(caps, old_caps, + utsname.machine, + &arch_info_hvm[i], 1) < 0) + goto no_memory; + + /* Then possibly the Xen paravirt guests (ie Xenner */ + xenner = virFindFileInPath("xenner"); + + if (xenner != NULL && access(xenner, X_OK) == 0 && + access("/dev/kvm", F_OK) == 0) { + for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_xen) ; i++) + /* Allow Xen 32-on-32, 32-on-64 and 64-on-64 */ + if (STREQ(arch_info_xen[i].arch, utsname.machine) || + (STREQ(utsname.machine, "x86_64") && + STREQ(arch_info_xen[i].arch, "i686"))) { + if (qemuCapsInitGuest(caps, old_caps, + utsname.machine, + &arch_info_xen[i], 0) < 0) + goto no_memory; + } + } + + VIR_FREE(xenner); + + /* QEMU Requires an emulator in the XML */ + virCapabilitiesSetEmulatorRequired(caps); + + caps->defaultConsoleTargetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; + + return caps; + + no_memory: + VIR_FREE(xenner); + virCapabilitiesFree(caps); + return NULL; +} + + +static unsigned long long +qemuCapsComputeCmdFlags(const char *help, + unsigned int version, + unsigned int is_kvm, + unsigned int kvm_version) +{ + unsigned long long flags = 0; + const char *p; + + if (strstr(help, "-no-kqemu")) + flags |= QEMUD_CMD_FLAG_KQEMU; + if (strstr(help, "-enable-kqemu")) + flags |= QEMUD_CMD_FLAG_ENABLE_KQEMU; + if (strstr(help, "-no-kvm")) + flags |= QEMUD_CMD_FLAG_KVM; + if (strstr(help, "-enable-kvm")) + flags |= QEMUD_CMD_FLAG_ENABLE_KVM; + if (strstr(help, "-no-reboot")) + flags |= QEMUD_CMD_FLAG_NO_REBOOT; + if (strstr(help, "-name")) { + flags |= QEMUD_CMD_FLAG_NAME; + if (strstr(help, ",process=")) + flags |= QEMUD_CMD_FLAG_NAME_PROCESS; + } + if (strstr(help, "-uuid")) + flags |= QEMUD_CMD_FLAG_UUID; + if (strstr(help, "-xen-domid")) + flags |= QEMUD_CMD_FLAG_XEN_DOMID; + else if (strstr(help, "-domid")) + flags |= QEMUD_CMD_FLAG_DOMID; + if (strstr(help, "-drive")) { + flags |= QEMUD_CMD_FLAG_DRIVE; + if (strstr(help, "cache=") && + !strstr(help, "cache=on|off")) + flags |= QEMUD_CMD_FLAG_DRIVE_CACHE_V2; + if (strstr(help, "format=")) + flags |= QEMUD_CMD_FLAG_DRIVE_FORMAT; + if (strstr(help, "readonly=")) + flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; + } + if ((p = strstr(help, "-vga")) && !strstr(help, "-std-vga")) { + const char *nl = strstr(p, "\n"); + + flags |= QEMUD_CMD_FLAG_VGA; + + if (strstr(p, "|qxl")) + flags |= QEMUD_CMD_FLAG_VGA_QXL; + if ((p = strstr(p, "|none")) && p < nl) + flags |= QEMUD_CMD_FLAG_VGA_NONE; + } + if (strstr(help, "-spice")) + flags |= QEMUD_CMD_FLAG_SPICE; + if (strstr(help, "boot=on")) + flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; + if (strstr(help, "serial=s")) + flags |= QEMUD_CMD_FLAG_DRIVE_SERIAL; + if (strstr(help, "-pcidevice")) + flags |= QEMUD_CMD_FLAG_PCIDEVICE; + if (strstr(help, "-mem-path")) + flags |= QEMUD_CMD_FLAG_MEM_PATH; + if (strstr(help, "-chardev")) + flags |= QEMUD_CMD_FLAG_CHARDEV; + if (strstr(help, "-balloon")) + flags |= QEMUD_CMD_FLAG_BALLOON; + if (strstr(help, "-device")) { + flags |= QEMUD_CMD_FLAG_DEVICE; + /* + * When -device was introduced, qemu already supported drive's + * readonly option but didn't advertise that. + */ + flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; + } + if (strstr(help, "-nodefconfig")) + flags |= QEMUD_CMD_FLAG_NODEFCONFIG; + /* The trailing ' ' is important to avoid a bogus match */ + if (strstr(help, "-rtc ")) + flags |= QEMUD_CMD_FLAG_RTC; + /* to wit */ + if (strstr(help, "-rtc-td-hack")) + flags |= QEMUD_CMD_FLAG_RTC_TD_HACK; + if (strstr(help, "-no-hpet")) + flags |= QEMUD_CMD_FLAG_NO_HPET; + if (strstr(help, "-no-kvm-pit-reinjection")) + flags |= QEMUD_CMD_FLAG_NO_KVM_PIT; + if (strstr(help, "-tdf")) + flags |= QEMUD_CMD_FLAG_TDF; + if (strstr(help, "-enable-nesting")) + flags |= QEMUD_CMD_FLAG_NESTING; + if (strstr(help, ",menu=on")) + flags |= QEMUD_CMD_FLAG_BOOT_MENU; + if (strstr(help, "-fsdev")) + flags |= QEMUD_CMD_FLAG_FSDEV; + if (strstr(help, "-smbios type")) + flags |= QEMUD_CMD_FLAG_SMBIOS_TYPE; + + if (strstr(help, "-netdev")) { + /* Disable -netdev on 0.12 since although it exists, + * the corresponding netdev_add/remove monitor commands + * do not, and we need them to be able todo hotplug */ + if (version >= 13000) + flags |= QEMUD_CMD_FLAG_NETDEV; + } + + if (strstr(help, "-sdl")) + flags |= QEMUD_CMD_FLAG_SDL; + if (strstr(help, "cores=") && + strstr(help, "threads=") && + strstr(help, "sockets=")) + flags |= QEMUD_CMD_FLAG_SMP_TOPOLOGY; + + if (version >= 9000) + flags |= QEMUD_CMD_FLAG_VNC_COLON; + + if (is_kvm && (version >= 10000 || kvm_version >= 74)) + flags |= QEMUD_CMD_FLAG_VNET_HDR; + + if (is_kvm && strstr(help, ",vhost=")) { + flags |= QEMUD_CMD_FLAG_VNET_HOST; + } + + /* + * Handling of -incoming arg with varying features + * -incoming tcp (kvm >= 79, qemu >= 0.10.0) + * -incoming exec (kvm >= 80, qemu >= 0.10.0) + * -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) { + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP; + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC; + if (version >= 12000) + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX; + } else if (kvm_version >= 79) { + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP; + if (kvm_version >= 80) + flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC; + } else if (kvm_version > 0) { + flags |= QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO; + } + + if (version >= 10000) + flags |= QEMUD_CMD_FLAG_0_10; + + /* 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 + * two features. The benefits of JSON mode now outweigh + * the downside. + */ + if (version >= 13000) + flags |= QEMUD_CMD_FLAG_MONITOR_JSON; + + return flags; +} + +/* 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, + unsigned long long *flags, + unsigned int *version, + unsigned int *is_kvm, + unsigned int *kvm_version) +{ + unsigned major, minor, micro; + const char *p = help; + + *flags = *version = *is_kvm = *kvm_version = 0; + + 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); + if (minor == -1 || *p != '.') + goto fail; + + ++p; + + micro = virParseNumber(&p); + if (micro == -1) + goto fail; + + 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; + + *flags = qemuCapsComputeCmdFlags(help, *version, *is_kvm, *kvm_version); + + VIR_DEBUG("Version %u.%u.%u, cooked version %u, flags 0x%llx", + major, minor, micro, *version, *flags); + 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'); + if (p) + p = strndup(help, p - help); + + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse %s version number in '%s'"), + qemu, p ? p : help); + + VIR_FREE(p); + + return -1; +} + +static void +qemuCapsParsePCIDeviceStrs(const char *qemu, + unsigned long long *flags) +{ + const char *const qemuarg[] = { qemu, "-device", "pci-assign,?", NULL }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + pid_t child; + int status; + int newstderr = -1; + + if (virExec(qemuarg, qemuenv, NULL, + &child, -1, NULL, &newstderr, VIR_EXEC_CLEAR_CAPS) < 0) + return; + + char *pciassign = NULL; + enum { MAX_PCI_OUTPUT_SIZE = 1024*4 }; + int len = virFileReadLimFD(newstderr, MAX_PCI_OUTPUT_SIZE, &pciassign); + if (len < 0) { + virReportSystemError(errno, + _("Unable to read %s pci-assign device output"), + qemu); + goto cleanup; + } + + if (strstr(pciassign, "pci-assign.configfd")) + *flags |= QEMUD_CMD_FLAG_PCI_CONFIGFD; + +cleanup: + VIR_FREE(pciassign); + VIR_FORCE_CLOSE(newstderr); +rewait: + if (waitpid(child, &status, 0) != child) { + if (errno == EINTR) + goto rewait; + + VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), + WEXITSTATUS(status), (unsigned long)child); + } + if (WEXITSTATUS(status) != 0) { + VIR_WARN("Unexpected exit status '%d', qemu probably failed", + WEXITSTATUS(status)); + } +} + +int qemuCapsExtractVersionInfo(const char *qemu, + unsigned int *retversion, + unsigned long long *retflags) +{ + const char *const qemuarg[] = { qemu, "-help", NULL }; + const char *const qemuenv[] = { "LC_ALL=C", NULL }; + pid_t child; + int newstdout = -1; + int ret = -1, status; + unsigned int version, is_kvm, kvm_version; + unsigned long long flags = 0; + + if (retflags) + *retflags = 0; + if (retversion) + *retversion = 0; + + /* Make sure the binary we are about to try exec'ing exists. + * Technically we could catch the exec() failure, but that's + * in a sub-process so it's hard to feed back a useful error. + */ + if (access(qemu, X_OK) < 0) { + virReportSystemError(errno, _("Cannot find QEMU binary %s"), qemu); + return -1; + } + + if (virExec(qemuarg, qemuenv, NULL, + &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) + return -1; + + char *help = NULL; + enum { MAX_HELP_OUTPUT_SIZE = 1024*64 }; + int len = virFileReadLimFD(newstdout, MAX_HELP_OUTPUT_SIZE, &help); + if (len < 0) { + virReportSystemError(errno, + _("Unable to read %s help output"), qemu); + goto cleanup2; + } + + if (qemuCapsParseHelpStr(qemu, help, &flags, + &version, &is_kvm, &kvm_version) == -1) + goto cleanup2; + + if (flags & QEMUD_CMD_FLAG_DEVICE) + qemuCapsParsePCIDeviceStrs(qemu, &flags); + + if (retversion) + *retversion = version; + if (retflags) + *retflags = flags; + + ret = 0; + +cleanup2: + VIR_FREE(help); + if (VIR_CLOSE(newstdout) < 0) + ret = -1; + +rewait: + if (waitpid(child, &status, 0) != child) { + if (errno == EINTR) + goto rewait; + + VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), + WEXITSTATUS(status), (unsigned long)child); + ret = -1; + } + /* Check & log unexpected exit status, but don't fail, + * as there's really no need to throw an error if we did + * actually read a valid version number above */ + if (WEXITSTATUS(status) != 0) { + VIR_WARN("Unexpected exit status '%d', qemu probably failed", + WEXITSTATUS(status)); + } + + return ret; +} + +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'; +} + +int qemuCapsExtractVersion(virCapsPtr caps, + unsigned int *version) +{ + const char *binary; + struct stat sb; + struct utsname ut; + + if (*version > 0) + return 0; + + uname_normalize(&ut); + if ((binary = virCapabilitiesDefaultGuestEmulator(caps, + "hvm", + ut.machine, + "qemu")) == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Cannot find suitable emulator for %s"), ut.machine); + return -1; + } + + if (stat(binary, &sb) < 0) { + virReportSystemError(errno, + _("Cannot find QEMU binary %s"), binary); + return -1; + } + + if (qemuCapsExtractVersionInfo(binary, version, NULL) < 0) { + return -1; + } + + return 0; +} diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h new file mode 100644 index 0000000..83afd9b --- /dev/null +++ b/src/qemu/qemu_capabilities.h @@ -0,0 +1,113 @@ +/* + * qemu_capabilities.h: QEMU capabilities generation + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __QEMU_CAPABILITIES_H__ +# define __QEMU_CAPABILITIES_H__ + +# include "capabilities.h" + +/* Internal flags to keep track of qemu command line capabilities */ +enum qemuCapsFlags { + QEMUD_CMD_FLAG_KQEMU = (1 << 0), /* Whether KQEMU is compiled in */ + QEMUD_CMD_FLAG_VNC_COLON = (1 << 1), /* Does the VNC take just port, or address + display */ + QEMUD_CMD_FLAG_NO_REBOOT = (1 << 2), /* Is the -no-reboot flag available */ + QEMUD_CMD_FLAG_DRIVE = (1 << 3), /* Is the new -drive arg available */ + QEMUD_CMD_FLAG_DRIVE_BOOT = (1 << 4), /* Does -drive support boot=on */ + QEMUD_CMD_FLAG_NAME = (1 << 5), /* Is the -name flag available */ + QEMUD_CMD_FLAG_UUID = (1 << 6), /* Is the -uuid flag available */ + QEMUD_CMD_FLAG_DOMID = (1 << 7), /* Xenner only, special -domid flag available */ + QEMUD_CMD_FLAG_VNET_HDR = (1 << 8), + QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO = (1 << 9), /* Original migration code from KVM. Also had tcp, but we can't use that + * since it had a design bug blocking the entire monitor console */ + QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP = (1 << 10), /* New migration syntax after merge to QEMU with TCP transport */ + QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC = (1 << 11), /* New migration syntax after merge to QEMU with EXEC transport */ + QEMUD_CMD_FLAG_DRIVE_CACHE_V2 = (1 << 12), /* Is the cache= flag wanting new v2 values */ + QEMUD_CMD_FLAG_KVM = (1 << 13), /* Whether KVM is compiled in */ + QEMUD_CMD_FLAG_DRIVE_FORMAT = (1 << 14), /* Is -drive format= avail */ + QEMUD_CMD_FLAG_VGA = (1 << 15), /* Is -vga avail */ + + /* features added in qemu-0.10.0 or later */ + QEMUD_CMD_FLAG_0_10 = (1 << 16), + QEMUD_CMD_FLAG_NET_NAME = QEMUD_CMD_FLAG_0_10, /* -net ...,name=str */ + QEMUD_CMD_FLAG_HOST_NET_ADD = QEMUD_CMD_FLAG_0_10, /* host_net_add monitor command */ + + QEMUD_CMD_FLAG_PCIDEVICE = (1 << 17), /* PCI device assignment only supported by qemu-kvm */ + QEMUD_CMD_FLAG_MEM_PATH = (1 << 18), /* mmap'ped guest backing supported */ + QEMUD_CMD_FLAG_DRIVE_SERIAL = (1 << 19), /* -driver serial= available */ + QEMUD_CMD_FLAG_XEN_DOMID = (1 << 20), /* -xen-domid (new style xen integration) */ + QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX = (1 << 21), /* Does qemu support unix domain sockets for migration? */ + QEMUD_CMD_FLAG_CHARDEV = (1 << 22), /* Is the new -chardev arg available */ + QEMUD_CMD_FLAG_ENABLE_KVM = (1 << 23), /* Is the -enable-kvm flag available to "enable KVM full virtualization support" */ + QEMUD_CMD_FLAG_MONITOR_JSON = (1 << 24), /* JSON mode for monitor */ + QEMUD_CMD_FLAG_BALLOON = (1 << 25), /* -balloon available */ + QEMUD_CMD_FLAG_DEVICE = (1 << 26), /* Is the new -device arg available */ + QEMUD_CMD_FLAG_SDL = (1 << 27), /* Is the new -sdl arg available */ + QEMUD_CMD_FLAG_SMP_TOPOLOGY = (1 << 28), /* Is sockets=s,cores=c,threads=t available for -smp? */ + QEMUD_CMD_FLAG_NETDEV = (1 << 29), /* The -netdev flag & netdev_add/remove monitor commands */ + QEMUD_CMD_FLAG_RTC = (1 << 30), /* The -rtc flag for clock options */ + QEMUD_CMD_FLAG_VNET_HOST = (1LL << 31), /* vnet-host support is available in qemu */ + QEMUD_CMD_FLAG_RTC_TD_HACK = (1LL << 32), /* -rtc-td-hack available */ + QEMUD_CMD_FLAG_NO_HPET = (1LL << 33), /* -no-hpet flag is supported */ + QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection supported */ + QEMUD_CMD_FLAG_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */ + QEMUD_CMD_FLAG_PCI_CONFIGFD = (1LL << 36), /* pci-assign.configfd */ + QEMUD_CMD_FLAG_NODEFCONFIG = (1LL << 37), /* -nodefconfig */ + QEMUD_CMD_FLAG_BOOT_MENU = (1LL << 38), /* -boot menu=on support */ + QEMUD_CMD_FLAG_ENABLE_KQEMU = (1LL << 39), /* -enable-kqemu flag */ + QEMUD_CMD_FLAG_FSDEV = (1LL << 40), /* -fstype filesystem passthrough */ + QEMUD_CMD_FLAG_NESTING = (1LL << 41), /* -enable-nesting (SVM/VMX) */ + QEMUD_CMD_FLAG_NAME_PROCESS = (1LL << 42), /* Is -name process= available */ + QEMUD_CMD_FLAG_DRIVE_READONLY= (1LL << 43), /* -drive readonly=on|off */ + QEMUD_CMD_FLAG_SMBIOS_TYPE = (1LL << 44), /* Is -smbios type= available */ + QEMUD_CMD_FLAG_VGA_QXL = (1LL << 45), /* The 'qxl' arg for '-vga' */ + QEMUD_CMD_FLAG_SPICE = (1LL << 46), /* Is -spice avail */ + QEMUD_CMD_FLAG_VGA_NONE = (1LL << 47), /* The 'none' arg for '-vga' */ +}; + +virCapsPtr qemuCapsInit(virCapsPtr old_caps); + +int qemuCapsProbeMachineTypes(const char *binary, + virCapsGuestMachinePtr **machines, + int *nmachines); + +int qemuCapsProbeCPUModels(const char *qemu, + unsigned long long qemuCmdFlags, + const char *arch, + unsigned int *count, + const char ***cpus); + +int qemuCapsExtractVersion(virCapsPtr caps, + unsigned int *version); +int qemuCapsExtractVersionInfo(const char *qemu, + unsigned int *version, + unsigned long long *qemuCmdFlags); + +int qemuCapsParseHelpStr(const char *qemu, + const char *str, + unsigned long long *qemuCmdFlags, + unsigned int *version, + unsigned int *is_kvm, + unsigned int *kvm_version); + + +#endif /* __QEMU_CAPABILITIES_H__*/ diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index f4b524e..0c9c676 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -40,6 +40,7 @@ #include "c-ctype.h" #include "virterror_internal.h" #include "qemu_conf.h" +#include "qemu_capabilities.h" #include "qemu_bridge_filter.h" #include "uuid.h" #include "buf.h" @@ -450,1162 +451,6 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, return 0; } -struct qemu_feature_flags { - const char *name; - const int default_on; - const int toggle; -}; - -struct qemu_arch_info { - const char *arch; - int wordsize; - const char *machine; - const char *binary; - const char *altbinary; - const struct qemu_feature_flags *flags; - int nflags; -}; - -/* Feature flags for the architecture info */ -static const struct qemu_feature_flags const arch_info_i686_flags [] = { - { "pae", 1, 0 }, - { "nonpae", 1, 0 }, - { "acpi", 1, 1 }, - { "apic", 1, 0 }, -}; - -static const struct qemu_feature_flags const arch_info_x86_64_flags [] = { - { "acpi", 1, 1 }, - { "apic", 1, 0 }, -}; - -/* The archicture tables for supported QEMU archs */ -static const struct qemu_arch_info const arch_info_hvm[] = { - { "i686", 32, NULL, "qemu", - "qemu-system-x86_64", arch_info_i686_flags, 4 }, - { "x86_64", 64, NULL, "qemu-system-x86_64", - NULL, arch_info_x86_64_flags, 2 }, - { "arm", 32, NULL, "qemu-system-arm", NULL, NULL, 0 }, - { "mips", 32, NULL, "qemu-system-mips", NULL, NULL, 0 }, - { "mipsel", 32, NULL, "qemu-system-mipsel", NULL, NULL, 0 }, - { "sparc", 32, NULL, "qemu-system-sparc", NULL, NULL, 0 }, - { "ppc", 32, NULL, "qemu-system-ppc", NULL, NULL, 0 }, - { "itanium", 64, NULL, "qemu-system-ia64", NULL, NULL, 0 }, - { "s390x", 64, NULL, "qemu-system-s390x", NULL, NULL, 0 }, -}; - -static const struct qemu_arch_info const arch_info_xen[] = { - { "i686", 32, "xenner", "xenner", NULL, arch_info_i686_flags, 4 }, - { "x86_64", 64, "xenner", "xenner", NULL, arch_info_x86_64_flags, 2 }, -}; - - -/* Format is: - * <machine> <desc> [(default)|(alias of <canonical>)] - */ -static int -qemudParseMachineTypesStr(const char *output, - virCapsGuestMachinePtr **machines, - int *nmachines) -{ - const char *p = output; - const char *next; - virCapsGuestMachinePtr *list = NULL; - int nitems = 0; - - do { - const char *t; - virCapsGuestMachinePtr machine; - - if ((next = strchr(p, '\n'))) - ++next; - - if (STRPREFIX(p, "Supported machines are:")) - continue; - - if (!(t = strchr(p, ' ')) || (next && t >= next)) - continue; - - if (VIR_ALLOC(machine) < 0) - goto no_memory; - - if (!(machine->name = strndup(p, t - p))) { - VIR_FREE(machine); - goto no_memory; - } - - if (VIR_REALLOC_N(list, nitems + 1) < 0) { - VIR_FREE(machine->name); - VIR_FREE(machine); - goto no_memory; - } - - p = t; - if (!(t = strstr(p, "(default)")) || (next && t >= next)) { - list[nitems++] = machine; - } else { - /* put the default first in the list */ - memmove(list + 1, list, sizeof(*list) * nitems); - list[0] = machine; - nitems++; - } - - if ((t = strstr(p, "(alias of ")) && (!next || t < next)) { - p = t + strlen("(alias of "); - if (!(t = strchr(p, ')')) || (next && t >= next)) - continue; - - if (!(machine->canonical = strndup(p, t - p))) - goto no_memory; - } - } while ((p = next)); - - *machines = list; - *nmachines = nitems; - - return 0; - - no_memory: - virReportOOMError(); - virCapabilitiesFreeMachines(list, nitems); - return -1; -} - -int -qemudProbeMachineTypes(const char *binary, - virCapsGuestMachinePtr **machines, - int *nmachines) -{ - const char *const qemuarg[] = { binary, "-M", "?", NULL }; - const char *const qemuenv[] = { "LC_ALL=C", NULL }; - char *output; - enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; - pid_t child; - int newstdout = -1, len; - int ret = -1, status; - - if (virExec(qemuarg, qemuenv, NULL, - &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) - return -1; - - len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); - if (len < 0) { - virReportSystemError(errno, "%s", - _("Unable to read 'qemu -M ?' output")); - goto cleanup; - } - - if (qemudParseMachineTypesStr(output, machines, nmachines) < 0) - goto cleanup2; - - ret = 0; - -cleanup2: - VIR_FREE(output); -cleanup: - if (VIR_CLOSE(newstdout) < 0) - ret = -1; - -rewait: - if (waitpid(child, &status, 0) != child) { - if (errno == EINTR) - goto rewait; - - VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), - WEXITSTATUS(status), (unsigned long)child); - ret = -1; - } - /* Check & log unexpected exit status, but don't fail, - * as there's really no need to throw an error if we did - * actually read a valid version number above */ - if (WEXITSTATUS(status) != 0) { - VIR_WARN("Unexpected exit status '%d', qemu probably failed", - WEXITSTATUS(status)); - } - - return ret; -} - -static int -qemudGetOldMachinesFromInfo(virCapsGuestDomainInfoPtr info, - const char *emulator, - time_t emulator_mtime, - virCapsGuestMachinePtr **machines, - int *nmachines) -{ - virCapsGuestMachinePtr *list; - int i; - - if (!info->nmachines) - return 0; - - if (!info->emulator || !STREQ(emulator, info->emulator)) - return 0; - - if (emulator_mtime != info->emulator_mtime) { - VIR_DEBUG("mtime on %s has changed, refreshing machine types", - info->emulator); - return 0; - } - - if (VIR_ALLOC_N(list, info->nmachines) < 0) { - virReportOOMError(); - return 0; - } - - for (i = 0; i < info->nmachines; i++) { - if (VIR_ALLOC(list[i]) < 0) { - goto no_memory; - } - if (info->machines[i]->name && - !(list[i]->name = strdup(info->machines[i]->name))) { - goto no_memory; - } - if (info->machines[i]->canonical && - !(list[i]->canonical = strdup(info->machines[i]->canonical))) { - goto no_memory; - } - } - - *machines = list; - *nmachines = info->nmachines; - - return 1; - - no_memory: - virReportOOMError(); - virCapabilitiesFreeMachines(list, info->nmachines); - return 0; -} - -static int -qemudGetOldMachines(const char *ostype, - const char *arch, - int wordsize, - const char *emulator, - time_t emulator_mtime, - virCapsPtr old_caps, - virCapsGuestMachinePtr **machines, - int *nmachines) -{ - int i; - - for (i = 0; i < old_caps->nguests; i++) { - virCapsGuestPtr guest = old_caps->guests[i]; - int j; - - if (!STREQ(ostype, guest->ostype) || - !STREQ(arch, guest->arch.name) || - wordsize != guest->arch.wordsize) - continue; - - for (j = 0; j < guest->arch.ndomains; j++) { - virCapsGuestDomainPtr dom = guest->arch.domains[j]; - - if (qemudGetOldMachinesFromInfo(&dom->info, - emulator, emulator_mtime, - machines, nmachines)) - return 1; - } - - if (qemudGetOldMachinesFromInfo(&guest->arch.defaultInfo, - emulator, emulator_mtime, - machines, nmachines)) - return 1; - } - - return 0; -} - - -typedef int -(*qemudParseCPUModels)(const char *output, - unsigned int *retcount, - const char ***retcpus); - -/* Format: - * <arch> <model> - * qemu-0.13 encloses some model names in []: - * <arch> [<model>] - */ -static int -qemudParseX86Models(const char *output, - unsigned int *retcount, - const char ***retcpus) -{ - const char *p = output; - const char *next; - unsigned int count = 0; - const char **cpus = NULL; - int i; - - do { - const char *t; - - 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; - - if (retcpus) { - unsigned int len; - - if (VIR_REALLOC_N(cpus, count + 1) < 0) - goto error; - - if (next) - len = next - p - 1; - else - len = strlen(p); - - if (len > 2 && *p == '[' && p[len - 1] == ']') { - p++; - len -= 2; - } - - if (!(cpus[count] = strndup(p, len))) - goto error; - } - count++; - } while ((p = next)); - - if (retcount) - *retcount = count; - if (retcpus) - *retcpus = cpus; - - return 0; - -error: - if (cpus) { - for (i = 0; i < count; i++) - VIR_FREE(cpus[i]); - } - VIR_FREE(cpus); - - return -1; -} - - -int -qemudProbeCPUModels(const char *qemu, - unsigned long long qemuCmdFlags, - const char *arch, - unsigned int *count, - const char ***cpus) -{ - const char *const qemuarg[] = { - qemu, - "-cpu", "?", - (qemuCmdFlags & QEMUD_CMD_FLAG_NODEFCONFIG) ? "-nodefconfig" : NULL, - NULL - }; - const char *const qemuenv[] = { "LC_ALL=C", NULL }; - enum { MAX_MACHINES_OUTPUT_SIZE = 1024*4 }; - char *output = NULL; - int newstdout = -1; - int ret = -1; - pid_t child; - int status; - int len; - qemudParseCPUModels parse; - - if (count) - *count = 0; - if (cpus) - *cpus = NULL; - - if (STREQ(arch, "i686") || STREQ(arch, "x86_64")) - parse = qemudParseX86Models; - else { - VIR_DEBUG("don't know how to parse %s CPU models", arch); - return 0; - } - - if (virExec(qemuarg, qemuenv, NULL, - &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) - return -1; - - len = virFileReadLimFD(newstdout, MAX_MACHINES_OUTPUT_SIZE, &output); - if (len < 0) { - virReportSystemError(errno, "%s", - _("Unable to read QEMU supported CPU models")); - goto cleanup; - } - - if (parse(output, count, cpus) < 0) { - virReportOOMError(); - goto cleanup; - } - - ret = 0; - -cleanup: - VIR_FREE(output); - if (VIR_CLOSE(newstdout) < 0) - ret = -1; - -rewait: - if (waitpid(child, &status, 0) != child) { - if (errno == EINTR) - goto rewait; - - VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), - WEXITSTATUS(status), (unsigned long)child); - ret = -1; - } - /* Check & log unexpected exit status, but don't fail, - * as there's really no need to throw an error if we did - * actually read a valid version number above */ - if (WEXITSTATUS(status) != 0) { - VIR_WARN("Unexpected exit status '%d', qemu probably failed", - WEXITSTATUS(status)); - } - - return ret; -} - - -static int -qemudCapsInitGuest(virCapsPtr caps, - virCapsPtr old_caps, - const char *hostmachine, - const struct qemu_arch_info *info, - int hvm) { - virCapsGuestPtr guest; - int i; - int haskvm = 0; - int haskqemu = 0; - char *kvmbin = NULL; - char *binary = NULL; - time_t binary_mtime; - virCapsGuestMachinePtr *machines = NULL; - int nmachines = 0; - struct stat st; - unsigned int ncpus; - int ret = -1; - - /* Check for existance of base emulator, or alternate base - * which can be used with magic cpu choice - */ - binary = virFindFileInPath(info->binary); - - if (binary == NULL || access(binary, X_OK) != 0) { - VIR_FREE(binary); - binary = virFindFileInPath(info->altbinary); - - if (binary != NULL && access(binary, X_OK) != 0) - VIR_FREE(binary); - } - - /* Can use acceleration for KVM/KQEMU if - * - host & guest arches match - * Or - * - hostarch is x86_64 and guest arch is i686 - * The latter simply needs "-cpu qemu32" - */ - if (STREQ(info->arch, hostmachine) || - (STREQ(hostmachine, "x86_64") && STREQ(info->arch, "i686"))) { - if (access("/dev/kvm", F_OK) == 0) { - const char *const kvmbins[] = { "/usr/libexec/qemu-kvm", /* RHEL */ - "qemu-kvm", /* Fedora */ - "kvm" }; /* Upstream .spec */ - - for (i = 0; i < ARRAY_CARDINALITY(kvmbins); ++i) { - kvmbin = virFindFileInPath(kvmbins[i]); - - if (kvmbin == NULL || access(kvmbin, X_OK) != 0) { - VIR_FREE(kvmbin); - continue; - } - - haskvm = 1; - if (!binary) - binary = kvmbin; - - break; - } - } - - if (access("/dev/kqemu", F_OK) == 0) - haskqemu = 1; - } - - if (!binary) - return 0; - - if (stat(binary, &st) == 0) { - binary_mtime = st.st_mtime; - } else { - char ebuf[1024]; - VIR_WARN("Failed to stat %s, most peculiar : %s", - binary, virStrerror(errno, ebuf, sizeof(ebuf))); - binary_mtime = 0; - } - - if (info->machine) { - virCapsGuestMachinePtr machine; - - if (VIR_ALLOC(machine) < 0) { - goto no_memory; - } - - if (!(machine->name = strdup(info->machine))) { - VIR_FREE(machine); - goto no_memory; - } - - nmachines = 1; - - if (VIR_ALLOC_N(machines, nmachines) < 0) { - VIR_FREE(machine->name); - VIR_FREE(machine); - goto no_memory; - } - - machines[0] = machine; - } else { - int probe = 1; - if (old_caps && binary_mtime) - probe = !qemudGetOldMachines(hvm ? "hvm" : "xen", info->arch, - info->wordsize, binary, binary_mtime, - old_caps, &machines, &nmachines); - if (probe && - qemudProbeMachineTypes(binary, &machines, &nmachines) < 0) - goto error; - } - - /* We register kvm as the base emulator too, since we can - * just give -no-kvm to disable acceleration if required */ - if ((guest = virCapabilitiesAddGuest(caps, - hvm ? "hvm" : "xen", - info->arch, - info->wordsize, - binary, - NULL, - nmachines, - machines)) == NULL) - goto error; - - machines = NULL; - nmachines = 0; - - guest->arch.defaultInfo.emulator_mtime = binary_mtime; - - if (caps->host.cpu && - qemudProbeCPUModels(binary, 0, info->arch, &ncpus, NULL) == 0 && - ncpus > 0 && - !virCapabilitiesAddGuestFeature(guest, "cpuselection", 1, 0)) - goto error; - - if (hvm) { - if (virCapabilitiesAddGuestDomain(guest, - "qemu", - NULL, - NULL, - 0, - NULL) == NULL) - goto error; - - if (haskqemu && - virCapabilitiesAddGuestDomain(guest, - "kqemu", - NULL, - NULL, - 0, - NULL) == NULL) - goto error; - - if (haskvm) { - virCapsGuestDomainPtr dom; - - if (stat(kvmbin, &st) == 0) { - binary_mtime = st.st_mtime; - } else { - char ebuf[1024]; - VIR_WARN("Failed to stat %s, most peculiar : %s", - binary, virStrerror(errno, ebuf, sizeof(ebuf))); - binary_mtime = 0; - } - - if (!STREQ(binary, kvmbin)) { - int probe = 1; - if (old_caps && binary_mtime) - probe = !qemudGetOldMachines("hvm", info->arch, info->wordsize, - kvmbin, binary_mtime, - old_caps, &machines, &nmachines); - if (probe && - qemudProbeMachineTypes(kvmbin, &machines, &nmachines) < 0) - goto error; - } - - if ((dom = virCapabilitiesAddGuestDomain(guest, - "kvm", - kvmbin, - NULL, - nmachines, - machines)) == NULL) { - goto error; - } - - machines = NULL; - nmachines = 0; - - dom->info.emulator_mtime = binary_mtime; - } - } else { - if (virCapabilitiesAddGuestDomain(guest, - "kvm", - NULL, - NULL, - 0, - NULL) == NULL) - goto error; - } - - if (info->nflags) { - for (i = 0 ; i < info->nflags ; i++) { - if (virCapabilitiesAddGuestFeature(guest, - info->flags[i].name, - info->flags[i].default_on, - info->flags[i].toggle) == NULL) - goto error; - } - } - - ret = 0; - -cleanup: - if (binary == kvmbin) { - /* don't double free */ - VIR_FREE(binary); - } else { - VIR_FREE(binary); - VIR_FREE(kvmbin); - } - - return ret; - -no_memory: - virReportOOMError(); - -error: - virCapabilitiesFreeMachines(machines, nmachines); - - goto cleanup; -} - - -static int -qemudCapsInitCPU(virCapsPtr caps, - const char *arch) -{ - 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; -} - - -virCapsPtr qemudCapsInit(virCapsPtr old_caps) { - struct utsname utsname; - virCapsPtr caps; - int i; - char *xenner = NULL; - - /* Really, this never fails - look at the man-page. */ - uname (&utsname); - - if ((caps = virCapabilitiesNew(utsname.machine, - 1, 1)) == NULL) - goto no_memory; - - /* 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); - VIR_WARN0("Failed to query host NUMA topology, disabling NUMA capabilities"); - } - - if (old_caps == NULL || old_caps->host.cpu == NULL) { - if (qemudCapsInitCPU(caps, utsname.machine) < 0) - VIR_WARN0("Failed to get host CPU"); - } - else { - caps->host.cpu = old_caps->host.cpu; - old_caps->host.cpu = NULL; - } - - virCapabilitiesAddHostMigrateTransport(caps, - "tcp"); - - /* First the pure HVM guests */ - for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_hvm) ; i++) - if (qemudCapsInitGuest(caps, old_caps, - utsname.machine, - &arch_info_hvm[i], 1) < 0) - goto no_memory; - - /* Then possibly the Xen paravirt guests (ie Xenner */ - xenner = virFindFileInPath("xenner"); - - if (xenner != NULL && access(xenner, X_OK) == 0 && - access("/dev/kvm", F_OK) == 0) { - for (i = 0 ; i < ARRAY_CARDINALITY(arch_info_xen) ; i++) - /* Allow Xen 32-on-32, 32-on-64 and 64-on-64 */ - if (STREQ(arch_info_xen[i].arch, utsname.machine) || - (STREQ(utsname.machine, "x86_64") && - STREQ(arch_info_xen[i].arch, "i686"))) { - if (qemudCapsInitGuest(caps, old_caps, - utsname.machine, - &arch_info_xen[i], 0) < 0) - goto no_memory; - } - } - - VIR_FREE(xenner); - - /* QEMU Requires an emulator in the XML */ - virCapabilitiesSetEmulatorRequired(caps); - - caps->defaultConsoleTargetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; - - return caps; - - no_memory: - VIR_FREE(xenner); - virCapabilitiesFree(caps); - return NULL; -} - -static unsigned long long qemudComputeCmdFlags(const char *help, - unsigned int version, - unsigned int is_kvm, - unsigned int kvm_version) -{ - unsigned long long flags = 0; - const char *p; - - if (strstr(help, "-no-kqemu")) - flags |= QEMUD_CMD_FLAG_KQEMU; - if (strstr(help, "-enable-kqemu")) - flags |= QEMUD_CMD_FLAG_ENABLE_KQEMU; - if (strstr(help, "-no-kvm")) - flags |= QEMUD_CMD_FLAG_KVM; - if (strstr(help, "-enable-kvm")) - flags |= QEMUD_CMD_FLAG_ENABLE_KVM; - if (strstr(help, "-no-reboot")) - flags |= QEMUD_CMD_FLAG_NO_REBOOT; - if (strstr(help, "-name")) { - flags |= QEMUD_CMD_FLAG_NAME; - if (strstr(help, ",process=")) - flags |= QEMUD_CMD_FLAG_NAME_PROCESS; - } - if (strstr(help, "-uuid")) - flags |= QEMUD_CMD_FLAG_UUID; - if (strstr(help, "-xen-domid")) - flags |= QEMUD_CMD_FLAG_XEN_DOMID; - else if (strstr(help, "-domid")) - flags |= QEMUD_CMD_FLAG_DOMID; - if (strstr(help, "-drive")) { - flags |= QEMUD_CMD_FLAG_DRIVE; - if (strstr(help, "cache=") && - !strstr(help, "cache=on|off")) - flags |= QEMUD_CMD_FLAG_DRIVE_CACHE_V2; - if (strstr(help, "format=")) - flags |= QEMUD_CMD_FLAG_DRIVE_FORMAT; - if (strstr(help, "readonly=")) - flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; - } - if ((p = strstr(help, "-vga")) && !strstr(help, "-std-vga")) { - const char *nl = strstr(p, "\n"); - - flags |= QEMUD_CMD_FLAG_VGA; - - if (strstr(p, "|qxl")) - flags |= QEMUD_CMD_FLAG_VGA_QXL; - if ((p = strstr(p, "|none")) && p < nl) - flags |= QEMUD_CMD_FLAG_VGA_NONE; - } - if (strstr(help, "-spice")) - flags |= QEMUD_CMD_FLAG_SPICE; - if (strstr(help, "boot=on")) - flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; - if (strstr(help, "serial=s")) - flags |= QEMUD_CMD_FLAG_DRIVE_SERIAL; - if (strstr(help, "-pcidevice")) - flags |= QEMUD_CMD_FLAG_PCIDEVICE; - if (strstr(help, "-mem-path")) - flags |= QEMUD_CMD_FLAG_MEM_PATH; - if (strstr(help, "-chardev")) - flags |= QEMUD_CMD_FLAG_CHARDEV; - if (strstr(help, "-balloon")) - flags |= QEMUD_CMD_FLAG_BALLOON; - if (strstr(help, "-device")) { - flags |= QEMUD_CMD_FLAG_DEVICE; - /* - * When -device was introduced, qemu already supported drive's - * readonly option but didn't advertise that. - */ - flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; - } - if (strstr(help, "-nodefconfig")) - flags |= QEMUD_CMD_FLAG_NODEFCONFIG; - /* The trailing ' ' is important to avoid a bogus match */ - if (strstr(help, "-rtc ")) - flags |= QEMUD_CMD_FLAG_RTC; - /* to wit */ - if (strstr(help, "-rtc-td-hack")) - flags |= QEMUD_CMD_FLAG_RTC_TD_HACK; - if (strstr(help, "-no-hpet")) - flags |= QEMUD_CMD_FLAG_NO_HPET; - if (strstr(help, "-no-kvm-pit-reinjection")) - flags |= QEMUD_CMD_FLAG_NO_KVM_PIT; - if (strstr(help, "-tdf")) - flags |= QEMUD_CMD_FLAG_TDF; - if (strstr(help, "-enable-nesting")) - flags |= QEMUD_CMD_FLAG_NESTING; - if (strstr(help, ",menu=on")) - flags |= QEMUD_CMD_FLAG_BOOT_MENU; - if (strstr(help, "-fsdev")) - flags |= QEMUD_CMD_FLAG_FSDEV; - if (strstr(help, "-smbios type")) - flags |= QEMUD_CMD_FLAG_SMBIOS_TYPE; - - if (strstr(help, "-netdev")) { - /* Disable -netdev on 0.12 since although it exists, - * the corresponding netdev_add/remove monitor commands - * do not, and we need them to be able todo hotplug */ - if (version >= 13000) - flags |= QEMUD_CMD_FLAG_NETDEV; - } - - if (strstr(help, "-sdl")) - flags |= QEMUD_CMD_FLAG_SDL; - if (strstr(help, "cores=") && - strstr(help, "threads=") && - strstr(help, "sockets=")) - flags |= QEMUD_CMD_FLAG_SMP_TOPOLOGY; - - if (version >= 9000) - flags |= QEMUD_CMD_FLAG_VNC_COLON; - - if (is_kvm && (version >= 10000 || kvm_version >= 74)) - flags |= QEMUD_CMD_FLAG_VNET_HDR; - - if (is_kvm && strstr(help, ",vhost=")) { - flags |= QEMUD_CMD_FLAG_VNET_HOST; - } - - /* - * Handling of -incoming arg with varying features - * -incoming tcp (kvm >= 79, qemu >= 0.10.0) - * -incoming exec (kvm >= 80, qemu >= 0.10.0) - * -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) { - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP; - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC; - if (version >= 12000) - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX; - } else if (kvm_version >= 79) { - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP; - if (kvm_version >= 80) - flags |= QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC; - } else if (kvm_version > 0) { - flags |= QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO; - } - - if (version >= 10000) - flags |= QEMUD_CMD_FLAG_0_10; - - /* 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 - * two features. The benefits of JSON mode now outweigh - * the downside. - */ - if (version >= 13000) - flags |= QEMUD_CMD_FLAG_MONITOR_JSON; - - return flags; -} - -/* 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 qemudParseHelpStr(const char *qemu, - const char *help, - unsigned long long *flags, - unsigned int *version, - unsigned int *is_kvm, - unsigned int *kvm_version) -{ - unsigned major, minor, micro; - const char *p = help; - - *flags = *version = *is_kvm = *kvm_version = 0; - - 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); - if (minor == -1 || *p != '.') - goto fail; - - ++p; - - micro = virParseNumber(&p); - if (micro == -1) - goto fail; - - 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; - - *flags = qemudComputeCmdFlags(help, *version, *is_kvm, *kvm_version); - - VIR_DEBUG("Version %u.%u.%u, cooked version %u, flags 0x%llx", - major, minor, micro, *version, *flags); - 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'); - if (p) - p = strndup(help, p - help); - - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse %s version number in '%s'"), - qemu, p ? p : help); - - VIR_FREE(p); - - return -1; -} - -static void qemudParsePCIDeviceStrs(const char *qemu, unsigned long long *flags) -{ - const char *const qemuarg[] = { qemu, "-device", "pci-assign,?", NULL }; - const char *const qemuenv[] = { "LC_ALL=C", NULL }; - pid_t child; - int status; - int newstderr = -1; - - if (virExec(qemuarg, qemuenv, NULL, - &child, -1, NULL, &newstderr, VIR_EXEC_CLEAR_CAPS) < 0) - return; - - char *pciassign = NULL; - enum { MAX_PCI_OUTPUT_SIZE = 1024*4 }; - int len = virFileReadLimFD(newstderr, MAX_PCI_OUTPUT_SIZE, &pciassign); - if (len < 0) { - virReportSystemError(errno, - _("Unable to read %s pci-assign device output"), - qemu); - goto cleanup; - } - - if (strstr(pciassign, "pci-assign.configfd")) - *flags |= QEMUD_CMD_FLAG_PCI_CONFIGFD; - -cleanup: - VIR_FREE(pciassign); - VIR_FORCE_CLOSE(newstderr); -rewait: - if (waitpid(child, &status, 0) != child) { - if (errno == EINTR) - goto rewait; - - VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), - WEXITSTATUS(status), (unsigned long)child); - } - if (WEXITSTATUS(status) != 0) { - VIR_WARN("Unexpected exit status '%d', qemu probably failed", - WEXITSTATUS(status)); - } -} - -int qemudExtractVersionInfo(const char *qemu, - unsigned int *retversion, - unsigned long long *retflags) { - const char *const qemuarg[] = { qemu, "-help", NULL }; - const char *const qemuenv[] = { "LC_ALL=C", NULL }; - pid_t child; - int newstdout = -1; - int ret = -1, status; - unsigned int version, is_kvm, kvm_version; - unsigned long long flags = 0; - - if (retflags) - *retflags = 0; - if (retversion) - *retversion = 0; - - /* Make sure the binary we are about to try exec'ing exists. - * Technically we could catch the exec() failure, but that's - * in a sub-process so it's hard to feed back a useful error. - */ - if (access(qemu, X_OK) < 0) { - virReportSystemError(errno, _("Cannot find QEMU binary %s"), qemu); - return -1; - } - - if (virExec(qemuarg, qemuenv, NULL, - &child, -1, &newstdout, NULL, VIR_EXEC_CLEAR_CAPS) < 0) - return -1; - - char *help = NULL; - enum { MAX_HELP_OUTPUT_SIZE = 1024*64 }; - int len = virFileReadLimFD(newstdout, MAX_HELP_OUTPUT_SIZE, &help); - if (len < 0) { - virReportSystemError(errno, - _("Unable to read %s help output"), qemu); - goto cleanup2; - } - - if (qemudParseHelpStr(qemu, help, &flags, - &version, &is_kvm, &kvm_version) == -1) - goto cleanup2; - - if (flags & QEMUD_CMD_FLAG_DEVICE) - qemudParsePCIDeviceStrs(qemu, &flags); - - if (retversion) - *retversion = version; - if (retflags) - *retflags = flags; - - ret = 0; - -cleanup2: - VIR_FREE(help); - if (VIR_CLOSE(newstdout) < 0) - ret = -1; - -rewait: - if (waitpid(child, &status, 0) != child) { - if (errno == EINTR) - goto rewait; - - VIR_ERROR(_("Unexpected exit status from qemu %d pid %lu"), - WEXITSTATUS(status), (unsigned long)child); - ret = -1; - } - /* Check & log unexpected exit status, but don't fail, - * as there's really no need to throw an error if we did - * actually read a valid version number above */ - if (WEXITSTATUS(status) != 0) { - VIR_WARN("Unexpected exit status '%d', qemu probably failed", - WEXITSTATUS(status)); - } - - return ret; -} static void uname_normalize (struct utsname *ut) @@ -1621,36 +466,6 @@ uname_normalize (struct utsname *ut) ut->machine[1] = '6'; } -int qemudExtractVersion(struct qemud_driver *driver) { - const char *binary; - struct stat sb; - struct utsname ut; - - if (driver->qemuVersion > 0) - return 0; - - uname_normalize(&ut); - if ((binary = virCapabilitiesDefaultGuestEmulator(driver->caps, - "hvm", - ut.machine, - "qemu")) == NULL) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Cannot find suitable emulator for %s"), ut.machine); - return -1; - } - - if (stat(binary, &sb) < 0) { - virReportSystemError(errno, - _("Cannot find QEMU binary %s"), binary); - return -1; - } - - if (qemudExtractVersionInfo(binary, &driver->qemuVersion, NULL) < 0) { - return -1; - } - - return 0; -} /** * qemudPhysIfaceConnect: @@ -3848,8 +2663,8 @@ qemuBuildCpuArgStr(const struct qemud_driver *driver, *hasHwVirt = false; if (def->cpu && def->cpu->model) { - if (qemudProbeCPUModels(emulator, qemuCmdFlags, ut->machine, - &ncpus, &cpus) < 0) + if (qemuCapsProbeCPUModels(emulator, qemuCmdFlags, ut->machine, + &ncpus, &cpus) < 0) goto cleanup; if (!ncpus || !host) { diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e6b413d..86c65a6 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -47,63 +47,6 @@ # define QEMUD_CPUMASK_LEN CPU_SETSIZE -/* Internal flags to keep track of qemu command line capabilities */ -enum qemud_cmd_flags { - QEMUD_CMD_FLAG_KQEMU = (1 << 0), /* Whether KQEMU is compiled in */ - QEMUD_CMD_FLAG_VNC_COLON = (1 << 1), /* Does the VNC take just port, or address + display */ - QEMUD_CMD_FLAG_NO_REBOOT = (1 << 2), /* Is the -no-reboot flag available */ - QEMUD_CMD_FLAG_DRIVE = (1 << 3), /* Is the new -drive arg available */ - QEMUD_CMD_FLAG_DRIVE_BOOT = (1 << 4), /* Does -drive support boot=on */ - QEMUD_CMD_FLAG_NAME = (1 << 5), /* Is the -name flag available */ - QEMUD_CMD_FLAG_UUID = (1 << 6), /* Is the -uuid flag available */ - QEMUD_CMD_FLAG_DOMID = (1 << 7), /* Xenner only, special -domid flag available */ - QEMUD_CMD_FLAG_VNET_HDR = (1 << 8), - QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO = (1 << 9), /* Original migration code from KVM. Also had tcp, but we can't use that - * since it had a design bug blocking the entire monitor console */ - QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP = (1 << 10), /* New migration syntax after merge to QEMU with TCP transport */ - QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC = (1 << 11), /* New migration syntax after merge to QEMU with EXEC transport */ - QEMUD_CMD_FLAG_DRIVE_CACHE_V2 = (1 << 12), /* Is the cache= flag wanting new v2 values */ - QEMUD_CMD_FLAG_KVM = (1 << 13), /* Whether KVM is compiled in */ - QEMUD_CMD_FLAG_DRIVE_FORMAT = (1 << 14), /* Is -drive format= avail */ - QEMUD_CMD_FLAG_VGA = (1 << 15), /* Is -vga avail */ - - /* features added in qemu-0.10.0 or later */ - QEMUD_CMD_FLAG_0_10 = (1 << 16), - QEMUD_CMD_FLAG_NET_NAME = QEMUD_CMD_FLAG_0_10, /* -net ...,name=str */ - QEMUD_CMD_FLAG_HOST_NET_ADD = QEMUD_CMD_FLAG_0_10, /* host_net_add monitor command */ - - QEMUD_CMD_FLAG_PCIDEVICE = (1 << 17), /* PCI device assignment only supported by qemu-kvm */ - QEMUD_CMD_FLAG_MEM_PATH = (1 << 18), /* mmap'ped guest backing supported */ - QEMUD_CMD_FLAG_DRIVE_SERIAL = (1 << 19), /* -driver serial= available */ - QEMUD_CMD_FLAG_XEN_DOMID = (1 << 20), /* -xen-domid (new style xen integration) */ - QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX = (1 << 21), /* Does qemu support unix domain sockets for migration? */ - QEMUD_CMD_FLAG_CHARDEV = (1 << 22), /* Is the new -chardev arg available */ - QEMUD_CMD_FLAG_ENABLE_KVM = (1 << 23), /* Is the -enable-kvm flag available to "enable KVM full virtualization support" */ - QEMUD_CMD_FLAG_MONITOR_JSON = (1 << 24), /* JSON mode for monitor */ - QEMUD_CMD_FLAG_BALLOON = (1 << 25), /* -balloon available */ - QEMUD_CMD_FLAG_DEVICE = (1 << 26), /* Is the new -device arg available */ - QEMUD_CMD_FLAG_SDL = (1 << 27), /* Is the new -sdl arg available */ - QEMUD_CMD_FLAG_SMP_TOPOLOGY = (1 << 28), /* Is sockets=s,cores=c,threads=t available for -smp? */ - QEMUD_CMD_FLAG_NETDEV = (1 << 29), /* The -netdev flag & netdev_add/remove monitor commands */ - QEMUD_CMD_FLAG_RTC = (1 << 30), /* The -rtc flag for clock options */ - QEMUD_CMD_FLAG_VNET_HOST = (1LL << 31), /* vnet-host support is available in qemu */ - QEMUD_CMD_FLAG_RTC_TD_HACK = (1LL << 32), /* -rtc-td-hack available */ - QEMUD_CMD_FLAG_NO_HPET = (1LL << 33), /* -no-hpet flag is supported */ - QEMUD_CMD_FLAG_NO_KVM_PIT = (1LL << 34), /* -no-kvm-pit-reinjection supported */ - QEMUD_CMD_FLAG_TDF = (1LL << 35), /* -tdf flag (user-mode pit catchup) */ - QEMUD_CMD_FLAG_PCI_CONFIGFD = (1LL << 36), /* pci-assign.configfd */ - QEMUD_CMD_FLAG_NODEFCONFIG = (1LL << 37), /* -nodefconfig */ - QEMUD_CMD_FLAG_BOOT_MENU = (1LL << 38), /* -boot menu=on support */ - QEMUD_CMD_FLAG_ENABLE_KQEMU = (1LL << 39), /* -enable-kqemu flag */ - QEMUD_CMD_FLAG_FSDEV = (1LL << 40), /* -fstype filesystem passthrough */ - QEMUD_CMD_FLAG_NESTING = (1LL << 41), /* -enable-nesting (SVM/VMX) */ - QEMUD_CMD_FLAG_NAME_PROCESS = (1LL << 42), /* Is -name process= available */ - QEMUD_CMD_FLAG_DRIVE_READONLY= (1LL << 43), /* -drive readonly=on|off */ - QEMUD_CMD_FLAG_SMBIOS_TYPE = (1LL << 44), /* Is -smbios type= available */ - QEMUD_CMD_FLAG_VGA_QXL = (1LL << 45), /* The 'qxl' arg for '-vga' */ - QEMUD_CMD_FLAG_SPICE = (1LL << 46), /* Is -spice avail */ - QEMUD_CMD_FLAG_VGA_NONE = (1LL << 47), /* The 'none' arg for '-vga' */ -}; /* Main driver state */ struct qemud_driver { @@ -220,20 +163,6 @@ struct _qemuDomainCmdlineDef { int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename); -virCapsPtr qemudCapsInit (virCapsPtr old_caps); - -int qemudExtractVersion (struct qemud_driver *driver); -int qemudExtractVersionInfo (const char *qemu, - unsigned int *version, - unsigned long long *qemuCmdFlags); - -int qemudParseHelpStr (const char *qemu, - const char *str, - unsigned long long *qemuCmdFlags, - unsigned int *version, - unsigned int *is_kvm, - unsigned int *kvm_version); - virCommandPtr qemudBuildCommandLine (virConnectPtr conn, struct qemud_driver *driver, virDomainDefPtr def, @@ -324,16 +253,6 @@ int qemudPhysIfaceConnect(virConnectPtr conn, const unsigned char *vmuuid, enum virVMOperationType vmop); -int qemudProbeMachineTypes (const char *binary, - virCapsGuestMachinePtr **machines, - int *nmachines); - -int qemudProbeCPUModels (const char *qemu, - unsigned long long qemuCmdFlags, - const char *arch, - unsigned int *count, - const char ***cpus); - int qemudCanonicalizeMachine (struct qemud_driver *driver, virDomainDefPtr def); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c096890..f303075 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -54,6 +54,7 @@ #include "datatypes.h" #include "qemu_driver.h" #include "qemu_conf.h" +#include "qemu_capabilities.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" #include "c-ctype.h" @@ -1553,9 +1554,9 @@ qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaq /* XXX we should be persisting the original flags in the XML * not re-detecting them, since the binary may have changed * since launch time */ - if (qemudExtractVersionInfo(obj->def->emulator, - NULL, - &qemuCmdFlags) >= 0 && + if (qemuCapsExtractVersionInfo(obj->def->emulator, + NULL, + &qemuCmdFlags) >= 0 && (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { priv->persistentAddrs = 1; @@ -1644,7 +1645,7 @@ qemuCreateCapabilities(virCapsPtr oldcaps, virCapsPtr caps; /* Basic host arch / guest machine capabilities */ - if (!(caps = qemudCapsInit(oldcaps))) { + if (!(caps = qemuCapsInit(oldcaps))) { virReportOOMError(); return NULL; } @@ -3118,9 +3119,9 @@ qemuAssignPCIAddresses(virDomainDefPtr def) unsigned long long qemuCmdFlags = 0; qemuDomainPCIAddressSetPtr addrs = NULL; - if (qemudExtractVersionInfo(def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(def->emulator, + NULL, + &qemuCmdFlags) < 0) goto cleanup; if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { @@ -4063,9 +4064,9 @@ static int qemudStartVMDaemon(virConnectPtr conn, goto cleanup; DEBUG0("Determining emulator version"); - if (qemudExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) goto cleanup; DEBUG0("Setting up domain cgroup (if required)"); @@ -4851,10 +4852,10 @@ static int qemudGetVersion(virConnectPtr conn, unsigned long *version) { int ret = -1; qemuDriverLock(driver); - if (qemudExtractVersion(driver) < 0) + if (qemuCapsExtractVersion(driver->caps, &driver->qemuVersion) < 0) goto cleanup; - *version = qemu_driver->qemuVersion; + *version = driver->qemuVersion; ret = 0; cleanup: @@ -7465,9 +7466,9 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, def->graphics[i]->data.vnc.port = QEMU_VNC_PORT_MIN; } - if (qemudExtractVersionInfo(def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(def->emulator, + NULL, + &qemuCmdFlags) < 0) goto cleanup; if (qemuPrepareMonitorChr(driver, &monConfig, def->name) < 0) @@ -7637,7 +7638,7 @@ qemudCanonicalizeMachineDirect(virDomainDefPtr def, char **canonical) virCapsGuestMachinePtr *machines = NULL; int i, nmachines = 0; - if (qemudProbeMachineTypes(def->emulator, &machines, &nmachines) < 0) { + if (qemuCapsProbeMachineTypes(def->emulator, &machines, &nmachines) < 0) { virReportOOMError(); return -1; } @@ -8826,9 +8827,9 @@ static int qemudDomainAttachDevice(virDomainPtr dom, if (dev == NULL) goto endjob; - if (qemudExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) goto endjob; if (dev->type == VIR_DOMAIN_DEVICE_DISK) { @@ -9073,9 +9074,9 @@ static int qemuDomainUpdateDeviceFlags(virDomainPtr dom, if (dev == NULL) goto endjob; - if (qemudExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) goto endjob; switch (dev->type) { @@ -9793,9 +9794,9 @@ static int qemudDomainDetachDevice(virDomainPtr dom, if (dev == NULL) goto endjob; - if (qemudExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) goto endjob; if (dev->type == VIR_DOMAIN_DEVICE_DISK && @@ -11060,7 +11061,7 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn, unlink(unixfile); /* check that this qemu version supports the interactive exec */ - if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { + if (qemuCapsExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot determine QEMU argv syntax %s"), vm->def->emulator); @@ -11571,7 +11572,7 @@ static int doTunnelMigrate(virDomainPtr dom, } /* check that this qemu version supports the unix migration */ - if (qemudExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { + if (qemuCapsExtractVersionInfo(vm->def->emulator, NULL, &qemuCmdFlags) < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot extract Qemu version from '%s'"), vm->def->emulator); -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
The qemu_conf.c code is doing three jobs, driver config file loading, QEMU capabilities management and QEMU command line management. Move the capabilities code into its own file
* src/qemu/qemu_capabilities.c, src/qemu/qemu_capabilities.h: New capabilities management code * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Delete capabilities code * src/qemu/qemu_conf.h: Adapt for API renames * src/Makefile.am: add src/qemu/qemu_capabilities.c --- src/Makefile.am | 1 + src/qemu/qemu_capabilities.c | 1253 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 113 ++++ src/qemu/qemu_conf.c | 1191 +--------------------------------------- src/qemu/qemu_conf.h | 81 --- src/qemu/qemu_driver.c | 55 +- 6 files changed, 1398 insertions(+), 1296 deletions(-) create mode 100644 src/qemu/qemu_capabilities.c create mode 100644 src/qemu/qemu_capabilities.h
+ +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'; +}
This one helper function wasn't removed from qemu_conf.c; since both files want to use it, shouldn't it be exposed in a common header and shared, rather than duplicating it? Other than that nit, ACK - this appeared to be straight code motion plus fallout from renaming to a consistent API (thanks for not trying to squash any cleanups into the code motion, although I certainly spotted some followup cleanups that would be nice such as converting virExec to virCommand). -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 12/16/2010 11:05 AM, Eric Blake wrote:
On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
The qemu_conf.c code is doing three jobs, driver config file loading, QEMU capabilities management and QEMU command line management. Move the capabilities code into its own file
* src/qemu/qemu_capabilities.c, src/qemu/qemu_capabilities.h: New capabilities management code * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Delete capabilities code * src/qemu/qemu_conf.h: Adapt for API renames * src/Makefile.am: add src/qemu/qemu_capabilities.c --- src/Makefile.am | 1 + src/qemu/qemu_capabilities.c | 1253 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 113 ++++ src/qemu/qemu_conf.c | 1191 +--------------------------------------- src/qemu/qemu_conf.h | 81 --- src/qemu/qemu_driver.c | 55 +-
Other than that nit, ACK - this appeared to be straight code motion plus fallout from renaming to a consistent API
Actually, you need to run 'make syntax-check' (missing a change to po/POTIFILES.in) and 'make check' (qemuxml2argvtest.c fails to compile, because it isn't including the new qemu_capabilities.h). -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 12/16/2010 05:09 PM, Eric Blake wrote:
On 12/16/2010 11:05 AM, Eric Blake wrote:
On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
The qemu_conf.c code is doing three jobs, driver config file loading, QEMU capabilities management and QEMU command line management. Move the capabilities code into its own file
* src/qemu/qemu_capabilities.c, src/qemu/qemu_capabilities.h: New capabilities management code * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Delete capabilities code * src/qemu/qemu_conf.h: Adapt for API renames * src/Makefile.am: add src/qemu/qemu_capabilities.c --- src/Makefile.am | 1 + src/qemu/qemu_capabilities.c | 1253 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_capabilities.h | 113 ++++ src/qemu/qemu_conf.c | 1191 +--------------------------------------- src/qemu/qemu_conf.h | 81 --- src/qemu/qemu_driver.c | 55 +-
Other than that nit, ACK - this appeared to be straight code motion plus fallout from renaming to a consistent API
Actually, you need to run 'make syntax-check' (missing a change to po/POTIFILES.in) and 'make check' (qemuxml2argvtest.c fails to compile, because it isn't including the new qemu_capabilities.h).
diff --git i/po/POTFILES.in w/po/POTFILES.in index e7be0d3..c2979bd 100644 --- i/po/POTFILES.in +++ w/po/POTFILES.in @@ -51,6 +51,7 @@ src/openvz/openvz_conf.c src/openvz/openvz_driver.c src/phyp/phyp_driver.c src/qemu/qemu_bridge_filter.c +src/qemu/qemu_capabilities.c src/qemu/qemu_conf.c src/qemu/qemu_driver.c src/qemu/qemu_monitor.c diff --git i/tests/qemuhelptest.c w/tests/qemuhelptest.c index 6ff1feb..3f82692 100644 --- i/tests/qemuhelptest.c +++ w/tests/qemuhelptest.c @@ -7,6 +7,7 @@ # include "testutils.h" # include "qemu/qemu_conf.h" +# include "qemu/qemu_capabilities.h" # define MAX_HELP_OUTPUT_SIZE 1024*64 @@ -50,8 +51,8 @@ static int testHelpStrParsing(const void *data) if (virtTestLoadFile(path, &help, MAX_HELP_OUTPUT_SIZE) < 0) return -1; - if (qemudParseHelpStr("QEMU", help, &flags, - &version, &is_kvm, &kvm_version) == -1) + if (qemuCapsParseHelpStr("QEMU", help, &flags, + &version, &is_kvm, &kvm_version) == -1) return -1; if (flags != info->flags) { diff --git i/tests/qemuxml2argvtest.c w/tests/qemuxml2argvtest.c index 5387432..f1cdc30 100644 --- i/tests/qemuxml2argvtest.c +++ w/tests/qemuxml2argvtest.c @@ -14,6 +14,7 @@ # include "internal.h" # include "testutils.h" # include "qemu/qemu_conf.h" +# include "qemu/qemu_capabilities.h" # include "datatypes.h" # include "cpu/cpu_map.h" -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

The qemu_conf.c code is doing three jobs, driver config file loading, QEMU capabilities management and QEMU command line management. Move the command line code into its own file * src/qemu/qemu_command.c, src/qemu/qemu_command.h: New command line management code * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Delete command line code * src/qemu/qemu_conf.h, src/qemu_conf.c: Adapt for API renames * src/Makefile.am: add src/qemu/qemu_command.c * src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_text.c: Add import of qemu_command.h --- src/Makefile.am | 1 + src/qemu/qemu_command.c | 5793 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_command.h | 165 ++ src/qemu/qemu_conf.c | 5748 ----------------------------------------- src/qemu/qemu_conf.h | 134 - src/qemu/qemu_driver.c | 27 +- src/qemu/qemu_monitor_json.c | 2 +- src/qemu/qemu_monitor_text.c | 2 +- 8 files changed, 5975 insertions(+), 5897 deletions(-) create mode 100644 src/qemu/qemu_command.c create mode 100644 src/qemu/qemu_command.h diff --git a/src/Makefile.am b/src/Makefile.am index 4ce0b35..d4626d9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -268,6 +268,7 @@ VBOX_DRIVER_EXTRA_DIST = vbox/vbox_tmpl.c vbox/README QEMU_DRIVER_SOURCES = \ qemu/qemu_capabilities.c qemu/qemu_capabilities.h\ + qemu/qemu_command.c qemu/qemu_command.h \ qemu/qemu_conf.c qemu/qemu_conf.h \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c new file mode 100644 index 0000000..bde3904 --- /dev/null +++ b/src/qemu/qemu_command.c @@ -0,0 +1,5793 @@ +/* + * qemu_command.c: QEMU command generation + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "qemu_command.h" +#include "qemu_capabilities.h" +#include "qemu_bridge_filter.h" +#include "cpu/cpu.h" +#include "memory.h" +#include "logging.h" +#include "virterror_internal.h" +#include "util.h" +#include "files.h" +#include "uuid.h" +#include "c-ctype.h" +#include "domain_nwfilter.h" + +#include <sys/utsname.h> +#include <sys/stat.h> +#include <fcntl.h> + +#define VIR_FROM_THIS VIR_FROM_QEMU + + +VIR_ENUM_DECL(virDomainDiskQEMUBus) +VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_DOMAIN_DISK_BUS_LAST, + "ide", + "floppy", + "scsi", + "virtio", + "xen", + "usb", + "uml", + "sata") + + +VIR_ENUM_DECL(qemuDiskCacheV1) +VIR_ENUM_DECL(qemuDiskCacheV2) + +VIR_ENUM_IMPL(qemuDiskCacheV1, VIR_DOMAIN_DISK_CACHE_LAST, + "default", + "off", + "off", /* writethrough not supported, so for safety, disable */ + "on"); /* Old 'on' was equivalent to 'writeback' */ + +VIR_ENUM_IMPL(qemuDiskCacheV2, VIR_DOMAIN_DISK_CACHE_LAST, + "default", + "none", + "writethrough", + "writeback"); + +VIR_ENUM_DECL(qemuVideo) + +VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, + "std", + "cirrus", + "vmware", + "", /* no arg needed for xen */ + "", /* don't support vbox */ + "qxl"); + +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'; +} + + +/** + * qemuPhysIfaceConnect: + * @conn: pointer to virConnect object + * @driver: pointer to the qemud_driver + * @net: pointer to he VM's interface description with direct device type + * @qemuCmdFlags: flags for qemu + * @vmuuid: The UUID of the VM (needed by 802.1Qbh) + * + * Returns a filedescriptor on success or -1 in case of error. + */ +int +qemuPhysIfaceConnect(virConnectPtr conn, + struct qemud_driver *driver, + virDomainNetDefPtr net, + unsigned long long qemuCmdFlags, + const unsigned char *vmuuid, + enum virVMOperationType vmop) +{ + int rc; +#if WITH_MACVTAP + char *res_ifname = NULL; + int vnet_hdr = 0; + int err; + + if (qemuCmdFlags & QEMUD_CMD_FLAG_VNET_HDR && + net->model && STREQ(net->model, "virtio")) + vnet_hdr = 1; + + rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev, + net->data.direct.mode, vnet_hdr, vmuuid, + &net->data.direct.virtPortProfile, &res_ifname, + vmop); + if (rc >= 0) { + VIR_FREE(net->ifname); + net->ifname = res_ifname; + } + + if (rc >=0 && driver->macFilter) { + if ((err = networkAllowMacOnPort(driver, net->ifname, net->mac))) { + virReportSystemError(err, + _("failed to add ebtables rule to allow MAC address on '%s'"), + net->ifname); + } + } + + if (rc >= 0) { + if ((net->filter) && (net->ifname)) { + err = virDomainConfNWFilterInstantiate(conn, net); + if (err) { + VIR_FORCE_CLOSE(rc); + delMacvtap(net->ifname, net->mac, net->data.direct.linkdev, + &net->data.direct.virtPortProfile); + VIR_FREE(net->ifname); + } + } + } +#else + (void)conn; + (void)net; + (void)qemuCmdFlags; + (void)driver; + (void)vmuuid; + (void)vmop; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("No support for macvtap device")); + rc = -1; +#endif + return rc; +} + + +int +qemuNetworkIfaceConnect(virConnectPtr conn, + struct qemud_driver *driver, + virDomainNetDefPtr net, + unsigned long long qemuCmdFlags) +{ + char *brname = NULL; + int err; + int tapfd = -1; + int vnet_hdr = 0; + int template_ifname = 0; + unsigned char tapmac[VIR_MAC_BUFLEN]; + + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + int active, fail = 0; + virErrorPtr errobj; + virNetworkPtr network = virNetworkLookupByName(conn, + net->data.network.name); + if (!network) + return -1; + + active = virNetworkIsActive(network); + if (active != 1) { + fail = 1; + + if (active == 0) + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Network '%s' is not active."), + net->data.network.name); + } + + if (!fail) { + brname = virNetworkGetBridgeName(network); + if (brname == NULL) + fail = 1; + } + + /* Make sure any above failure is preserved */ + errobj = virSaveLastError(); + virNetworkFree(network); + virSetError(errobj); + virFreeError(errobj); + + if (fail) + return -1; + + } else if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { + if (!(brname = strdup(net->data.bridge.brname))) { + virReportOOMError(); + return -1; + } + } else { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Network type %d is not supported"), net->type); + return -1; + } + + if (!driver->brctl && (err = brInit(&driver->brctl))) { + virReportSystemError(err, "%s", + _("cannot initialize bridge support")); + goto cleanup; + } + + if (!net->ifname || + STRPREFIX(net->ifname, "vnet") || + strchr(net->ifname, '%')) { + VIR_FREE(net->ifname); + if (!(net->ifname = strdup("vnet%d"))) { + virReportOOMError(); + goto cleanup; + } + /* avoid exposing vnet%d in dumpxml or error outputs */ + template_ifname = 1; + } + + if (qemuCmdFlags & QEMUD_CMD_FLAG_VNET_HDR && + net->model && STREQ(net->model, "virtio")) + vnet_hdr = 1; + + memcpy(tapmac, net->mac, VIR_MAC_BUFLEN); + tapmac[0] = 0xFE; /* Discourage bridge from using TAP dev MAC */ + if ((err = brAddTap(driver->brctl, + brname, + &net->ifname, + tapmac, + vnet_hdr, + &tapfd))) { + if (err == ENOTSUP) { + /* In this particular case, give a better diagnostic. */ + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to add tap interface to bridge. " + "%s is not a bridge device"), brname); + } else if (err == ENOENT) { + /* When the tun drive is missing, give a better message. */ + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Failed to add tap interface to bridge. " + "Your kernel is missing the 'tun' module or " + "CONFIG_TUN, or you need to add the " + "/dev/net/tun device node.")); + } else if (template_ifname) { + virReportSystemError(err, + _("Failed to add tap interface to bridge '%s'"), + brname); + } else { + virReportSystemError(err, + _("Failed to add tap interface '%s' to bridge '%s'"), + net->ifname, brname); + } + if (template_ifname) + VIR_FREE(net->ifname); + tapfd = -1; + } + + if (driver->macFilter) { + if ((err = networkAllowMacOnPort(driver, net->ifname, net->mac))) { + virReportSystemError(err, + _("failed to add ebtables rule to allow MAC address on '%s'"), + net->ifname); + } + } + + if (tapfd >= 0) { + if ((net->filter) && (net->ifname)) { + err = virDomainConfNWFilterInstantiate(conn, net); + if (err) + VIR_FORCE_CLOSE(tapfd); + } + } + +cleanup: + VIR_FREE(brname); + + return tapfd; +} + + +int +qemuOpenVhostNet(virDomainNetDefPtr net, + unsigned long long qemuCmdFlags) +{ + + /* If qemu supports vhost-net mode (including the -netdev command + * option), the nic model is virtio, and we can open + * /dev/vhost_net, assume that vhost-net mode is available and + * return the fd to /dev/vhost_net. Otherwise, return -1. + */ + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_VNET_HOST && + qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV && + qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE && + net->model && STREQ(net->model, "virtio"))) + return -1; + + return open("/dev/vhost-net", O_RDWR, 0); +} + + +static int qemuDomainDeviceAliasIndex(virDomainDeviceInfoPtr info, + const char *prefix) +{ + int idx; + + if (!info->alias) + return -1; + if (!STRPREFIX(info->alias, prefix)) + return -1; + + if (virStrToLong_i(info->alias + strlen(prefix), NULL, 10, &idx) < 0) + return -1; + + return idx; +} + + +int qemuDomainNetVLAN(virDomainNetDefPtr def) +{ + return qemuDomainDeviceAliasIndex(&def->info, "net"); +} + + +/* Names used before -drive existed */ +static int qemuAssignDeviceDiskAliasLegacy(virDomainDiskDefPtr disk) +{ + char *devname; + + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM && + STREQ(disk->dst, "hdc")) + devname = strdup("cdrom"); + else + devname = strdup(disk->dst); + + if (!devname) { + virReportOOMError(); + return -1; + } + + disk->info.alias = devname; + return 0; +} + + +char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, + unsigned long long qemudCmdFlags) +{ + char *ret; + + if (qemudCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (virAsprintf(&ret, "%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias) < 0) { + virReportOOMError(); + return NULL; + } + } else { + if (!(ret = strdup(disk->info.alias))) { + virReportOOMError(); + return NULL; + } + } + return ret; +} + + +/* Names used before -drive supported the id= option */ +static int qemuAssignDeviceDiskAliasFixed(virDomainDiskDefPtr disk) +{ + int busid, devid; + int ret; + char *devname; + + if (virDiskNameToBusDeviceIndex(disk, &busid, &devid) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot convert disk '%s' to bus/device index"), + disk->dst); + return -1; + } + + switch (disk->bus) { + case VIR_DOMAIN_DISK_BUS_IDE: + if (disk->device== VIR_DOMAIN_DISK_DEVICE_DISK) + ret = virAsprintf(&devname, "ide%d-hd%d", busid, devid); + else + ret = virAsprintf(&devname, "ide%d-cd%d", busid, devid); + break; + case VIR_DOMAIN_DISK_BUS_SCSI: + if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) + ret = virAsprintf(&devname, "scsi%d-hd%d", busid, devid); + else + ret = virAsprintf(&devname, "scsi%d-cd%d", busid, devid); + break; + case VIR_DOMAIN_DISK_BUS_FDC: + ret = virAsprintf(&devname, "floppy%d", devid); + break; + case VIR_DOMAIN_DISK_BUS_VIRTIO: + ret = virAsprintf(&devname, "virtio%d", devid); + break; + case VIR_DOMAIN_DISK_BUS_XEN: + ret = virAsprintf(&devname, "xenblk%d", devid); + break; + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Unsupported disk name mapping for bus '%s'"), + virDomainDiskBusTypeToString(disk->bus)); + return -1; + } + + if (ret == -1) { + virReportOOMError(); + return -1; + } + + disk->info.alias = devname; + + return 0; +} + + +/* Our custom -drive naming scheme used with id= */ +static int qemuAssignDeviceDiskAliasCustom(virDomainDiskDefPtr disk) +{ + const char *prefix = virDomainDiskBusTypeToString(disk->bus); + if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { + if (virAsprintf(&disk->info.alias, "%s%d-%d-%d", prefix, + disk->info.addr.drive.controller, + disk->info.addr.drive.bus, + disk->info.addr.drive.unit) < 0) + goto no_memory; + } else { + int idx = virDiskNameToIndex(disk->dst); + if (virAsprintf(&disk->info.alias, "%s-disk%d", prefix, idx) < 0) + goto no_memory; + } + + return 0; + +no_memory: + virReportOOMError(); + return -1; +} + + +int +qemuAssignDeviceDiskAlias(virDomainDiskDefPtr def, unsigned long long qemuCmdFlags) +{ + if (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) + return qemuAssignDeviceDiskAliasCustom(def); + else + return qemuAssignDeviceDiskAliasFixed(def); + } else { + return qemuAssignDeviceDiskAliasLegacy(def); + } +} + + +int +qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx) +{ + if (idx == -1) { + int i; + idx = 0; + for (i = 0 ; i < def->nnets ; i++) { + int thisidx; + if ((thisidx = qemuDomainDeviceAliasIndex(&def->nets[i]->info, "net")) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to determine device index for network device")); + return -1; + } + if (thisidx >= idx) + idx = thisidx + 1; + } + } + + if (virAsprintf(&net->info.alias, "net%d", idx) < 0) { + virReportOOMError(); + return -1; + } + + return 0; +} + + +int +qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int idx) +{ + if (idx == -1) { + int i; + idx = 0; + for (i = 0 ; i < def->nhostdevs ; i++) { + int thisidx; + if ((thisidx = qemuDomainDeviceAliasIndex(&def->hostdevs[i]->info, "hostdev")) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Unable to determine device index for hostdevwork device")); + return -1; + } + if (thisidx >= idx) + idx = thisidx + 1; + } + } + + if (virAsprintf(&hostdev->info.alias, "hostdev%d", idx) < 0) { + virReportOOMError(); + return -1; + } + + return 0; +} + + +int +qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller) +{ + const char *prefix = virDomainControllerTypeToString(controller->type); + + if (virAsprintf(&controller->info.alias, "%s%d", prefix, + controller->idx) < 0) { + virReportOOMError(); + return -1; + } + + return 0; +} + + +static int +qemuAssignDeviceAliases(virDomainDefPtr def, unsigned long long qemuCmdFlags) +{ + int i; + + for (i = 0; i < def->ndisks ; i++) { + if (qemuAssignDeviceDiskAlias(def->disks[i], qemuCmdFlags) < 0) + return -1; + } + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NET_NAME) || + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + for (i = 0; i < def->nnets ; i++) { + if (qemuAssignDeviceNetAlias(def, def->nets[i], i) < 0) + return -1; + } + } + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) + return 0; + + for (i = 0; i < def->nfss ; i++) { + if (virAsprintf(&def->fss[i]->info.alias, "fs%d", i) < 0) + goto no_memory; + } + for (i = 0; i < def->nsounds ; i++) { + if (virAsprintf(&def->sounds[i]->info.alias, "sound%d", i) < 0) + goto no_memory; + } + for (i = 0; i < def->nhostdevs ; i++) { + if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0) + return -1; + } + for (i = 0; i < def->nvideos ; i++) { + if (virAsprintf(&def->videos[i]->info.alias, "video%d", i) < 0) + goto no_memory; + } + for (i = 0; i < def->ncontrollers ; i++) { + if (qemuAssignDeviceControllerAlias(def->controllers[i]) < 0) + return -1; + } + for (i = 0; i < def->ninputs ; i++) { + if (virAsprintf(&def->inputs[i]->info.alias, "input%d", i) < 0) + goto no_memory; + } + for (i = 0; i < def->nparallels ; i++) { + if (virAsprintf(&def->parallels[i]->info.alias, "parallel%d", i) < 0) + goto no_memory; + } + for (i = 0; i < def->nserials ; i++) { + if (virAsprintf(&def->serials[i]->info.alias, "serial%d", i) < 0) + goto no_memory; + } + for (i = 0; i < def->nchannels ; i++) { + if (virAsprintf(&def->channels[i]->info.alias, "channel%d", i) < 0) + goto no_memory; + } + if (def->console) { + if (virAsprintf(&def->console->info.alias, "console%d", i) < 0) + goto no_memory; + } + if (def->watchdog) { + if (virAsprintf(&def->watchdog->info.alias, "watchdog%d", 0) < 0) + goto no_memory; + } + if (def->memballoon) { + if (virAsprintf(&def->memballoon->info.alias, "balloon%d", 0) < 0) + goto no_memory; + } + + return 0; + + no_memory: + virReportOOMError(); + return -1; +} + + +#define QEMU_PCI_ADDRESS_LAST_SLOT 31 +struct _qemuDomainPCIAddressSet { + virHashTablePtr used; + int nextslot; +}; + + +static char *qemuPCIAddressAsString(virDomainDeviceInfoPtr dev) +{ + char *addr; + + if (dev->addr.pci.domain != 0 || + dev->addr.pci.bus != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Only PCI domain 0 and bus 0 are available")); + return NULL; + } + + if (virAsprintf(&addr, "%d:%d:%d", + dev->addr.pci.domain, + dev->addr.pci.bus, + dev->addr.pci.slot) < 0) { + virReportOOMError(); + return NULL; + } + return addr; +} + + +static int qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED, + virDomainDeviceInfoPtr dev, + void *opaque) +{ + qemuDomainPCIAddressSetPtr addrs = opaque; + + if (dev->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + char *addr = qemuPCIAddressAsString(dev); + if (!addr) + return -1; + + VIR_DEBUG("Remembering PCI addr %s", addr); + + if (virHashAddEntry(addrs->used, addr, addr) < 0) { + VIR_FREE(addr); + return -1; + } + } + + return 0; +} + + +qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def) +{ + qemuDomainPCIAddressSetPtr addrs; + + if (VIR_ALLOC(addrs) < 0) + goto no_memory; + + if (!(addrs->used = virHashCreate(10))) + goto no_memory; + + if (virDomainDeviceInfoIterate(def, qemuCollectPCIAddress, addrs) < 0) + goto error; + + return addrs; + +no_memory: + virReportOOMError(); +error: + qemuDomainPCIAddressSetFree(addrs); + return NULL; +} + +int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs, + virDomainDeviceInfoPtr dev) +{ + char *addr; + + addr = qemuPCIAddressAsString(dev); + if (!addr) + return -1; + + VIR_DEBUG("Reserving PCI addr %s", addr); + + if (virHashLookup(addrs->used, addr)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to reserve PCI address %s"), addr); + VIR_FREE(addr); + return -1; + } + + if (virHashAddEntry(addrs->used, addr, addr)) { + VIR_FREE(addr); + return -1; + } + + if (dev->addr.pci.slot > addrs->nextslot) { + addrs->nextslot = dev->addr.pci.slot + 1; + if (QEMU_PCI_ADDRESS_LAST_SLOT < addrs->nextslot) + addrs->nextslot = 0; + } + + return 0; +} + +int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs, + int slot) +{ + virDomainDeviceInfo dev; + + dev.addr.pci.domain = 0; + dev.addr.pci.bus = 0; + dev.addr.pci.slot = slot; + + return qemuDomainPCIAddressReserveAddr(addrs, &dev); +} + + +int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs, + virDomainDeviceInfoPtr dev) +{ + int ret = 0; + if (dev->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) + ret = qemuDomainPCIAddressReserveAddr(addrs, dev); + else + ret = qemuDomainPCIAddressSetNextAddr(addrs, dev); + return ret; +} + +static void qemuDomainPCIAddressSetFreeEntry(void *payload, const char *name ATTRIBUTE_UNUSED) +{ + VIR_FREE(payload); +} + + +int qemuDomainPCIAddressReleaseAddr(qemuDomainPCIAddressSetPtr addrs, + virDomainDeviceInfoPtr dev) +{ + char *addr; + int ret; + + addr = qemuPCIAddressAsString(dev); + if (!addr) + return -1; + + ret = virHashRemoveEntry(addrs->used, addr, qemuDomainPCIAddressSetFreeEntry); + + VIR_FREE(addr); + + return ret; +} + + +void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs) +{ + if (!addrs) + return; + + virHashFree(addrs->used, qemuDomainPCIAddressSetFreeEntry); + VIR_FREE(addrs); +} + + +int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs, + virDomainDeviceInfoPtr dev) +{ + int i; + int iteration; + + for (i = addrs->nextslot, iteration = 0; + iteration <= QEMU_PCI_ADDRESS_LAST_SLOT; i++, iteration++) { + virDomainDeviceInfo maybe; + char *addr; + + if (QEMU_PCI_ADDRESS_LAST_SLOT < i) + i = 0; + memset(&maybe, 0, sizeof(maybe)); + maybe.addr.pci.domain = 0; + maybe.addr.pci.bus = 0; + maybe.addr.pci.slot = i; + + if (!(addr = qemuPCIAddressAsString(&maybe))) + return -1; + + if (virHashLookup(addrs->used, addr)) { + VIR_DEBUG("PCI addr %s already in use", addr); + VIR_FREE(addr); + continue; + } + + VIR_DEBUG("Allocating PCI addr %s", addr); + + if (virHashAddEntry(addrs->used, addr, addr) < 0) { + VIR_FREE(addr); + return -1; + } + + dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + dev->addr.pci = maybe.addr.pci; + + addrs->nextslot = i + 1; + if (QEMU_PCI_ADDRESS_LAST_SLOT < addrs->nextslot) + addrs->nextslot = 0; + + return 0; + } + + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("No more available PCI addresses")); + return -1; +} + +/* + * This assigns static PCI slots to all configured devices. + * The ordering here is chosen to match the ordering used + * with old QEMU < 0.12, so that if a user updates a QEMU + * host from old QEMU to QEMU >= 0.12, their guests should + * get PCI addresses in the same order as before. + * + * NB, if they previously hotplugged devices then all bets + * are off. Hotplug for old QEMU was unfixably broken wrt + * to stable PCI addressing. + * + * Order is: + * + * - Host bridge (slot 0) + * - PIIX3 ISA bridge, IDE controller, something else unknown, USB controller (slot 1) + * - Video (slot 2) + * + * Incrementally assign slots from 3 onwards: + * + * - Net + * - Sound + * - SCSI controllers + * - VirtIO block + * - VirtIO balloon + * - Host device passthrough + * - Watchdog + * + * Prior to this function being invoked, qemuCollectPCIAddress() will have + * added all existing PCI addresses from the 'def' to 'addrs'. Thus this + * function must only try to reserve addresses if info.type == NONE and + * skip over info.type == PCI + */ +int +qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) +{ + int i; + bool reservedIDE = false; + + /* Host bridge */ + if (qemuDomainPCIAddressReserveSlot(addrs, 0) < 0) + goto error; + + /* Verify that first IDE controller (if any) is on the PIIX3, fn 1 */ + for (i = 0; i < def->ncontrollers ; i++) { + /* First IDE controller lives on the PIIX3 at slot=1, function=1 */ + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && + def->controllers[i]->idx == 0) { + if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + if (def->controllers[i]->info.addr.pci.domain != 0 || + def->controllers[i]->info.addr.pci.bus != 0 || + def->controllers[i]->info.addr.pci.slot != 1 || + def->controllers[i]->info.addr.pci.function != 1) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Primary IDE controller must have PCI address 0:0:1.1")); + goto error; + } + /* If TYPE==PCI, then then qemuCollectPCIAddress() function + * has already reserved the address, so we must skip */ + reservedIDE = true; + } else { + def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + def->controllers[i]->info.addr.pci.domain = 0; + def->controllers[i]->info.addr.pci.bus = 0; + def->controllers[i]->info.addr.pci.slot = 1; + def->controllers[i]->info.addr.pci.function = 1; + } + } + } + + /* PIIX3 (ISA bridge, IDE controller, something else unknown, USB controller) + * hardcoded slot=1, multifunction device + */ + if (!reservedIDE && + qemuDomainPCIAddressReserveSlot(addrs, 1) < 0) + goto error; + + /* First VGA is hardcoded slot=2 */ + if (def->nvideos > 0) { + if (def->videos[0]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + if (def->videos[0]->info.addr.pci.domain != 0 || + def->videos[0]->info.addr.pci.bus != 0 || + def->videos[0]->info.addr.pci.slot != 2 || + def->videos[0]->info.addr.pci.function != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Primary video card must have PCI address 0:0:2.0")); + goto error; + } + } else { + def->videos[0]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + def->videos[0]->info.addr.pci.domain = 0; + def->videos[0]->info.addr.pci.bus = 0; + def->videos[0]->info.addr.pci.slot = 2; + def->videos[0]->info.addr.pci.function = 0; + if (qemuDomainPCIAddressReserveSlot(addrs, 2) < 0) + goto error; + } + } + for (i = 0; i < def->nfss ; i++) { + if (def->fss[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + + /* Only support VirtIO-9p-pci so far. If that changes, + * we might need to skip devices here */ + if (qemuDomainPCIAddressSetNextAddr(addrs, &def->fss[i]->info) < 0) + goto error; + } + + /* Network interfaces */ + for (i = 0; i < def->nnets ; i++) { + if (def->nets[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + if (qemuDomainPCIAddressSetNextAddr(addrs, &def->nets[i]->info) < 0) + goto error; + } + + /* Sound cards */ + for (i = 0; i < def->nsounds ; i++) { + if (def->sounds[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + /* Skip ISA sound card, and PCSPK */ + if (def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_SB16 || + def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_PCSPK) + continue; + + if (qemuDomainPCIAddressSetNextAddr(addrs, &def->sounds[i]->info) < 0) + goto error; + } + + /* Disk controllers (SCSI only for now) */ + for (i = 0; i < def->ncontrollers ; i++) { + /* FDC lives behind the ISA bridge */ + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC) + continue; + + /* First IDE controller lives on the PIIX3 at slot=1, function=1, + dealt with earlier on*/ + if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && + def->controllers[i]->idx == 0) + continue; + + if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + if (qemuDomainPCIAddressSetNextAddr(addrs, &def->controllers[i]->info) < 0) + goto error; + } + + /* Disks (VirtIO only for now */ + for (i = 0; i < def->ndisks ; i++) { + if (def->disks[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + + /* Only VirtIO disks use PCI addrs */ + if (def->disks[i]->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) + continue; + + if (qemuDomainPCIAddressSetNextAddr(addrs, &def->disks[i]->info) < 0) + goto error; + } + + /* Host PCI devices */ + for (i = 0; i < def->nhostdevs ; i++) { + if (def->hostdevs[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + if (def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + if (qemuDomainPCIAddressSetNextAddr(addrs, &def->hostdevs[i]->info) < 0) + goto error; + } + + /* VirtIO balloon */ + if (def->memballoon && + def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO && + def->memballoon->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + if (qemuDomainPCIAddressSetNextAddr(addrs, &def->memballoon->info) < 0) + goto error; + } + + /* A watchdog */ + if (def->watchdog && + def->watchdog->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + if (qemuDomainPCIAddressSetNextAddr(addrs, &def->watchdog->info) < 0) + goto error; + } + + /* Further non-primary video cards */ + for (i = 1; i < def->nvideos ; i++) { + if (def->videos[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) + continue; + if (qemuDomainPCIAddressSetNextAddr(addrs, &def->videos[i]->info) < 0) + goto error; + } + for (i = 0; i < def->ninputs ; i++) { + /* Nada - none are PCI based (yet) */ + } + for (i = 0; i < def->nparallels ; i++) { + /* Nada - none are PCI based (yet) */ + } + for (i = 0; i < def->nserials ; i++) { + /* Nada - none are PCI based (yet) */ + } + for (i = 0; i < def->nchannels ; i++) { + /* Nada - none are PCI based (yet) */ + } + + return 0; + +error: + return -1; +} + + +static int +qemuBuildDeviceAddressStr(virBufferPtr buf, + virDomainDeviceInfoPtr info) +{ + if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + if (info->addr.pci.domain != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Only PCI device addresses with domain=0 are supported")); + return -1; + } + if (info->addr.pci.bus != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Only PCI device addresses with bus=0 are supported")); + return -1; + } + if (info->addr.pci.function != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Only PCI device addresses with function=0 are supported")); + return -1; + } + + /* XXX + * When QEMU grows support for > 1 PCI bus, then pci.0 changes + * to pci.1, pci.2, etc + * When QEMU grows support for > 1 PCI domain, then pci.0 change + * to pciNN.0 where NN is the domain number + */ + virBufferVSprintf(buf, ",bus=pci.0,addr=0x%x", info->addr.pci.slot); + } + return 0; +} + + +#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" + +static int +qemuSafeSerialParamValue(const char *value) +{ + if (strspn(value, QEMU_SERIAL_PARAM_ACCEPTED_CHARS) != strlen (value)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("driver serial '%s' contains unsafe characters"), + value); + return -1; + } + + return 0; +} + + +char * +qemuBuildDriveStr(virDomainDiskDefPtr disk, + int bootable, + unsigned long long qemuCmdFlags) +{ + virBuffer opt = VIR_BUFFER_INITIALIZER; + const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); + int idx = virDiskNameToIndex(disk->dst); + int busid = -1, unitid = -1; + + if (idx < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk type '%s'"), disk->dst); + goto error; + } + + switch (disk->bus) { + case VIR_DOMAIN_DISK_BUS_SCSI: + if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected address type for scsi disk")); + goto error; + } + + /* Setting bus= attr for SCSI drives, causes a controller + * to be created. Yes this is slightly odd. It is not possible + * to have > 1 bus on a SCSI controller (yet). */ + if (disk->info.addr.drive.bus != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("SCSI controller only supports 1 bus")); + goto error; + } + busid = disk->info.addr.drive.controller; + unitid = disk->info.addr.drive.unit; + break; + + case VIR_DOMAIN_DISK_BUS_IDE: + if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected address type for ide disk")); + goto error; + } + /* We can only have 1 IDE controller (currently) */ + if (disk->info.addr.drive.controller != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Only 1 %s controller is supported"), bus); + goto error; + } + busid = disk->info.addr.drive.bus; + unitid = disk->info.addr.drive.unit; + break; + + case VIR_DOMAIN_DISK_BUS_FDC: + if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected address type for fdc disk")); + goto error; + } + /* We can only have 1 FDC controller (currently) */ + if (disk->info.addr.drive.controller != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Only 1 %s controller is supported"), bus); + goto error; + } + /* We can only have 1 FDC bus (currently) */ + if (disk->info.addr.drive.bus != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Only 1 %s bus is supported"), bus); + goto error; + } + unitid = disk->info.addr.drive.unit; + + break; + + case VIR_DOMAIN_DISK_BUS_VIRTIO: + /* Each virtio drive is a separate PCI device, no unit/busid or index */ + idx = -1; + break; + + case VIR_DOMAIN_DISK_BUS_XEN: + /* Xen has no address type currently, so assign based on index */ + break; + } + + /* disk->src is NULL when we use nbd disks */ + if (disk->src || (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK && + disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_NBD)) { + if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { + /* QEMU only supports magic FAT format for now */ + if (disk->driverType && + STRNEQ(disk->driverType, "fat")) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk driver type for '%s'"), + disk->driverType); + goto error; + } + if (!disk->readonly) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create virtual FAT disks in read-write mode")); + goto error; + } + if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) + virBufferVSprintf(&opt, "file=fat:floppy:%s,", disk->src); + else + virBufferVSprintf(&opt, "file=fat:%s,", disk->src); + } else if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { + switch (disk->protocol) { + case VIR_DOMAIN_DISK_PROTOCOL_NBD: + if (disk->nhosts != 1) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("NBD accepts only one host")); + goto error; + } + virBufferVSprintf(&opt, "file=nbd:%s:%s,", + disk->hosts->name, disk->hosts->port); + break; + case VIR_DOMAIN_DISK_PROTOCOL_RBD: + /* TODO: set monitor hostnames */ + virBufferVSprintf(&opt, "file=rbd:%s,", disk->src); + break; + case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: + if (disk->nhosts == 0) + virBufferVSprintf(&opt, "file=sheepdog:%s,", disk->src); + else + /* only one host is supported now */ + virBufferVSprintf(&opt, "file=sheepdog:%s:%s:%s,", + disk->hosts->name, disk->hosts->port, + disk->src); + break; + } + } else { + virBufferVSprintf(&opt, "file=%s,", disk->src); + } + } + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) + virBufferAddLit(&opt, "if=none"); + else + virBufferVSprintf(&opt, "if=%s", bus); + + if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) + virBufferAddLit(&opt, ",media=cdrom"); + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + virBufferVSprintf(&opt, ",id=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias); + } else { + if (busid == -1 && unitid == -1) { + if (idx != -1) + virBufferVSprintf(&opt, ",index=%d", idx); + } else { + if (busid != -1) + virBufferVSprintf(&opt, ",bus=%d", busid); + if (unitid != -1) + virBufferVSprintf(&opt, ",unit=%d", unitid); + } + } + if (bootable && + disk->device == VIR_DOMAIN_DISK_DEVICE_DISK && + disk->bus != VIR_DOMAIN_DISK_BUS_IDE) + virBufferAddLit(&opt, ",boot=on"); + if (disk->readonly && + qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_READONLY) + virBufferAddLit(&opt, ",readonly=on"); + if (disk->driverType && *disk->driverType != '\0' && + disk->type != VIR_DOMAIN_DISK_TYPE_DIR && + qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_FORMAT) + virBufferVSprintf(&opt, ",format=%s", disk->driverType); + if (disk->serial && + (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_SERIAL)) { + if (qemuSafeSerialParamValue(disk->serial) < 0) + goto error; + virBufferVSprintf(&opt, ",serial=%s", disk->serial); + } + + if (disk->cachemode) { + const char *mode = + (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_CACHE_V2) ? + qemuDiskCacheV2TypeToString(disk->cachemode) : + qemuDiskCacheV1TypeToString(disk->cachemode); + + virBufferVSprintf(&opt, ",cache=%s", mode); + } else if (disk->shared && !disk->readonly) { + virBufferAddLit(&opt, ",cache=off"); + } + + if (qemuCmdFlags & QEMUD_CMD_FLAG_MONITOR_JSON) { + if (disk->error_policy) { + virBufferVSprintf(&opt, ",werror=%s,rerror=%s", + virDomainDiskErrorPolicyTypeToString(disk->error_policy), + virDomainDiskErrorPolicyTypeToString(disk->error_policy)); + } + } + + if (virBufferError(&opt)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&opt); + +error: + virBufferFreeAndReset(&opt); + return NULL; +} + + +char * +qemuBuildDriveDevStr(virDomainDiskDefPtr disk) +{ + virBuffer opt = VIR_BUFFER_INITIALIZER; + const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); + int idx = virDiskNameToIndex(disk->dst); + + if (idx < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk type '%s'"), disk->dst); + goto error; + } + + switch (disk->bus) { + case VIR_DOMAIN_DISK_BUS_IDE: + virBufferAddLit(&opt, "ide-drive"); + virBufferVSprintf(&opt, ",bus=ide.%d,unit=%d", + disk->info.addr.drive.bus, + disk->info.addr.drive.unit); + break; + case VIR_DOMAIN_DISK_BUS_SCSI: + virBufferAddLit(&opt, "scsi-disk"); + virBufferVSprintf(&opt, ",bus=scsi%d.%d,scsi-id=%d", + disk->info.addr.drive.controller, + disk->info.addr.drive.bus, + disk->info.addr.drive.unit); + break; + case VIR_DOMAIN_DISK_BUS_VIRTIO: + virBufferAddLit(&opt, "virtio-blk-pci"); + qemuBuildDeviceAddressStr(&opt, &disk->info); + break; + case VIR_DOMAIN_DISK_BUS_USB: + virBufferAddLit(&opt, "usb-storage"); + break; + default: + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk bus '%s' with device setup"), bus); + goto error; + } + virBufferVSprintf(&opt, ",drive=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias); + virBufferVSprintf(&opt, ",id=%s", disk->info.alias); + + if (virBufferError(&opt)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&opt); + +error: + virBufferFreeAndReset(&opt); + return NULL; +} + + +char *qemuBuildFSStr(virDomainFSDefPtr fs, + unsigned long long qemuCmdFlags ATTRIBUTE_UNUSED) +{ + virBuffer opt = VIR_BUFFER_INITIALIZER; + + if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("only supports mount filesystem type")); + goto error; + } + + virBufferAddLit(&opt, "local"); + if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_MAPPED) { + virBufferAddLit(&opt, ",security_model=mapped"); + } else if(fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) { + virBufferAddLit(&opt, ",security_model=passthrough"); + } else if(fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_SQUASH) { + virBufferAddLit(&opt, ",security_model=none"); + } + virBufferVSprintf(&opt, ",id=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias); + virBufferVSprintf(&opt, ",path=%s", fs->src); + + if (virBufferError(&opt)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&opt); + +error: + virBufferFreeAndReset(&opt); + return NULL; +} + + +char * +qemuBuildFSDevStr(virDomainFSDefPtr fs) +{ + virBuffer opt = VIR_BUFFER_INITIALIZER; + + if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("can only passthrough directories")); + goto error; + } + + virBufferAddLit(&opt, "virtio-9p-pci"); + virBufferVSprintf(&opt, ",id=%s", fs->info.alias); + virBufferVSprintf(&opt, ",fsdev=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias); + virBufferVSprintf(&opt, ",mount_tag=%s", fs->dst); + qemuBuildDeviceAddressStr(&opt, &fs->info); + + if (virBufferError(&opt)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&opt); + +error: + virBufferFreeAndReset(&opt); + return NULL; +} + + +char * +qemuBuildControllerDevStr(virDomainControllerDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + switch (def->type) { + case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: + virBufferAddLit(&buf, "lsi"); + virBufferVSprintf(&buf, ",id=scsi%d", def->idx); + break; + + case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: + if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + virBufferAddLit(&buf, "virtio-serial-pci"); + } else { + virBufferAddLit(&buf, "virtio-serial"); + } + virBufferVSprintf(&buf, ",id=" QEMU_VIRTIO_SERIAL_PREFIX "%d", + def->idx); + if (def->opts.vioserial.ports != -1) { + virBufferVSprintf(&buf, ",max_ports=%d", + def->opts.vioserial.ports); + } + if (def->opts.vioserial.vectors != -1) { + virBufferVSprintf(&buf, ",vectors=%d", + def->opts.vioserial.vectors); + } + break; + + /* We always get an IDE controller, whether we want it or not. */ + case VIR_DOMAIN_CONTROLLER_TYPE_IDE: + default: + goto error; + } + + if (qemuBuildDeviceAddressStr(&buf, &def->info) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * +qemuBuildNicStr(virDomainNetDefPtr net, + const char *prefix, + int vlan) +{ + char *str; + if (virAsprintf(&str, + "%smacaddr=%02x:%02x:%02x:%02x:%02x:%02x,vlan=%d%s%s%s%s", + prefix ? prefix : "", + net->mac[0], net->mac[1], + net->mac[2], net->mac[3], + net->mac[4], net->mac[5], + vlan, + (net->model ? ",model=" : ""), + (net->model ? net->model : ""), + (net->info.alias ? ",name=" : ""), + (net->info.alias ? net->info.alias : "")) < 0) { + virReportOOMError(); + return NULL; + } + + return str; +} + + +char * +qemuBuildNicDevStr(virDomainNetDefPtr net, int vlan) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *nic; + + if (!net->model) { + nic = "rtl8139"; + } else if (STREQ(net->model, "virtio")) { + nic = "virtio-net-pci"; + } else { + nic = net->model; + } + + virBufferAdd(&buf, nic, strlen(nic)); + if (vlan == -1) + virBufferVSprintf(&buf, ",netdev=host%s", net->info.alias); + else + virBufferVSprintf(&buf, ",vlan=%d", vlan); + virBufferVSprintf(&buf, ",id=%s", net->info.alias); + virBufferVSprintf(&buf, ",mac=%02x:%02x:%02x:%02x:%02x:%02x", + net->mac[0], net->mac[1], + net->mac[2], net->mac[3], + net->mac[4], net->mac[5]); + if (qemuBuildDeviceAddressStr(&buf, &net->info) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * +qemuBuildHostNetStr(virDomainNetDefPtr net, + char type_sep, + int vlan, + const char *tapfd, + const char *vhostfd) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + switch (net->type) { + case VIR_DOMAIN_NET_TYPE_NETWORK: + case VIR_DOMAIN_NET_TYPE_BRIDGE: + case VIR_DOMAIN_NET_TYPE_DIRECT: + virBufferAddLit(&buf, "tap"); + virBufferVSprintf(&buf, "%cfd=%s", type_sep, tapfd); + type_sep = ','; + break; + + case VIR_DOMAIN_NET_TYPE_ETHERNET: + virBufferAddLit(&buf, "tap"); + if (net->ifname) { + virBufferVSprintf(&buf, "%cifname=%s", type_sep, net->ifname); + type_sep = ','; + } + if (net->data.ethernet.script) { + virBufferVSprintf(&buf, "%cscript=%s", type_sep, + net->data.ethernet.script); + type_sep = ','; + } + break; + + case VIR_DOMAIN_NET_TYPE_CLIENT: + case VIR_DOMAIN_NET_TYPE_SERVER: + case VIR_DOMAIN_NET_TYPE_MCAST: + virBufferAddLit(&buf, "socket"); + switch (net->type) { + case VIR_DOMAIN_NET_TYPE_CLIENT: + virBufferVSprintf(&buf, "%cconnect=%s:%d", + type_sep, + net->data.socket.address, + net->data.socket.port); + break; + case VIR_DOMAIN_NET_TYPE_SERVER: + virBufferVSprintf(&buf, "%clisten=%s:%d", + type_sep, + net->data.socket.address, + net->data.socket.port); + break; + case VIR_DOMAIN_NET_TYPE_MCAST: + virBufferVSprintf(&buf, "%cmcast=%s:%d", + type_sep, + net->data.socket.address, + net->data.socket.port); + break; + case VIR_DOMAIN_NET_TYPE_USER: + case VIR_DOMAIN_NET_TYPE_ETHERNET: + case VIR_DOMAIN_NET_TYPE_NETWORK: + case VIR_DOMAIN_NET_TYPE_BRIDGE: + case VIR_DOMAIN_NET_TYPE_INTERNAL: + case VIR_DOMAIN_NET_TYPE_DIRECT: + case VIR_DOMAIN_NET_TYPE_LAST: + break; + } + type_sep = ','; + break; + + case VIR_DOMAIN_NET_TYPE_USER: + default: + virBufferAddLit(&buf, "user"); + break; + } + + if (vlan >= 0) { + virBufferVSprintf(&buf, "%cvlan=%d", type_sep, vlan); + if (net->info.alias) + virBufferVSprintf(&buf, ",name=host%s", + net->info.alias); + } else { + virBufferVSprintf(&buf, "%cid=host%s", + type_sep, net->info.alias); + } + + if (vhostfd && *vhostfd) { + virBufferVSprintf(&buf, ",vhost=on,vhostfd=%s", vhostfd); + } + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + + +char * +qemuBuildWatchdogDevStr(virDomainWatchdogDefPtr dev) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + const char *model = virDomainWatchdogModelTypeToString(dev->model); + if (!model) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing watchdog model")); + goto error; + } + + virBufferVSprintf(&buf, "%s", model); + virBufferVSprintf(&buf, ",id=%s", dev->info.alias); + if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * +qemuBuildMemballoonDevStr(virDomainMemballoonDefPtr dev) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAddLit(&buf, "virtio-balloon-pci"); + virBufferVSprintf(&buf, ",id=%s", dev->info.alias); + if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * +qemuBuildUSBInputDevStr(virDomainInputDefPtr dev) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferVSprintf(&buf, "%s", + dev->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? + "usb-mouse" : "usb-tablet"); + virBufferVSprintf(&buf, ",id=%s", dev->info.alias); + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * +qemuBuildSoundDevStr(virDomainSoundDefPtr sound) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *model = virDomainSoundModelTypeToString(sound->model); + + if (!model) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("invalid sound model")); + goto error; + } + + /* Hack for 2 wierdly unusal devices name in QEMU */ + if (STREQ(model, "es1370")) + model = "ES1370"; + else if (STREQ(model, "ac97")) + model = "AC97"; + + virBufferVSprintf(&buf, "%s", model); + virBufferVSprintf(&buf, ",id=%s", sound->info.alias); + if (qemuBuildDeviceAddressStr(&buf, &sound->info) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +static char * +qemuBuildVideoDevStr(virDomainVideoDefPtr video) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *model = qemuVideoTypeToString(video->type); + + if (!model) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("invalid video model")); + goto error; + } + + virBufferVSprintf(&buf, "%s", model); + virBufferVSprintf(&buf, ",id=%s", video->info.alias); + if (qemuBuildDeviceAddressStr(&buf, &video->info) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +int +qemuOpenPCIConfig(virDomainHostdevDefPtr dev) +{ + char *path = NULL; + int configfd = -1; + + if (virAsprintf(&path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/config", + dev->source.subsys.u.pci.domain, + dev->source.subsys.u.pci.bus, + dev->source.subsys.u.pci.slot, + dev->source.subsys.u.pci.function) < 0) { + virReportOOMError(); + return -1; + } + + configfd = open(path, O_RDWR, 0); + + if (configfd < 0) + virReportSystemError(errno, _("Failed opening %s"), path); + + VIR_FREE(path); + + return configfd; +} + +char * +qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, const char *configfd) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAddLit(&buf, "pci-assign"); + virBufferVSprintf(&buf, ",host=%.2x:%.2x.%.1x", + dev->source.subsys.u.pci.bus, + dev->source.subsys.u.pci.slot, + dev->source.subsys.u.pci.function); + virBufferVSprintf(&buf, ",id=%s", dev->info.alias); + if (configfd && *configfd) + virBufferVSprintf(&buf, ",configfd=%s", configfd); + if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * +qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev) +{ + char *ret = NULL; + + if (virAsprintf(&ret, "host=%.2x:%.2x.%.1x", + dev->source.subsys.u.pci.bus, + dev->source.subsys.u.pci.slot, + dev->source.subsys.u.pci.function) < 0) + virReportOOMError(); + + return ret; +} + + +char * +qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev) +{ + char *ret = NULL; + + if (!dev->source.subsys.u.usb.bus && + !dev->source.subsys.u.usb.device) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("USB host device is missing bus/device information")); + return NULL; + } + + if (virAsprintf(&ret, "usb-host,hostbus=%d,hostaddr=%d,id=%s", + dev->source.subsys.u.usb.bus, + dev->source.subsys.u.usb.device, + dev->info.alias) < 0) + virReportOOMError(); + + return ret; +} + + +char * +qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev) +{ + char *ret = NULL; + + if (!dev->source.subsys.u.usb.bus && + !dev->source.subsys.u.usb.device) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("USB host device is missing bus/device information")); + return NULL; + } + + if (virAsprintf(&ret, "host:%d.%d", + dev->source.subsys.u.usb.bus, + dev->source.subsys.u.usb.device) < 0) + virReportOOMError(); + + return ret; +} + + + +/* This function outputs a -chardev command line option which describes only the + * host side of the character device */ +char * +qemuBuildChrChardevStr(virDomainChrDefPtr dev) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + bool telnet; + + switch(dev->type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + virBufferVSprintf(&buf, "null,id=%s", dev->info.alias); + break; + + case VIR_DOMAIN_CHR_TYPE_VC: + virBufferVSprintf(&buf, "vc,id=%s", dev->info.alias); + break; + + case VIR_DOMAIN_CHR_TYPE_PTY: + virBufferVSprintf(&buf, "pty,id=%s", dev->info.alias); + break; + + case VIR_DOMAIN_CHR_TYPE_DEV: + virBufferVSprintf(&buf, "tty,id=%s,path=%s", dev->info.alias, dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_FILE: + virBufferVSprintf(&buf, "file,id=%s,path=%s", dev->info.alias, dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_PIPE: + virBufferVSprintf(&buf, "pipe,id=%s,path=%s", dev->info.alias, dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_STDIO: + virBufferVSprintf(&buf, "stdio,id=%s", dev->info.alias); + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + virBufferVSprintf(&buf, + "udp,id=%s,host=%s,port=%s,localaddr=%s,localport=%s", + dev->info.alias, + dev->data.udp.connectHost, + dev->data.udp.connectService, + dev->data.udp.bindHost, + dev->data.udp.bindService); + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + telnet = dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + virBufferVSprintf(&buf, + "socket,id=%s,host=%s,port=%s%s%s", + dev->info.alias, + dev->data.tcp.host, + dev->data.tcp.service, + telnet ? ",telnet" : "", + dev->data.tcp.listen ? ",server,nowait" : ""); + break; + + case VIR_DOMAIN_CHR_TYPE_UNIX: + virBufferVSprintf(&buf, + "socket,id=%s,path=%s%s", + dev->info.alias, + dev->data.nix.path, + dev->data.nix.listen ? ",server,nowait" : ""); + break; + } + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * +qemuBuildChrArgStr(virDomainChrDefPtr dev, const char *prefix) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if (prefix) + virBufferAdd(&buf, prefix, strlen(prefix)); + + switch (dev->type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + virBufferAddLit(&buf, "null"); + break; + + case VIR_DOMAIN_CHR_TYPE_VC: + virBufferAddLit(&buf, "vc"); + break; + + case VIR_DOMAIN_CHR_TYPE_PTY: + virBufferAddLit(&buf, "pty"); + break; + + case VIR_DOMAIN_CHR_TYPE_DEV: + virBufferStrcat(&buf, dev->data.file.path, NULL); + break; + + case VIR_DOMAIN_CHR_TYPE_FILE: + virBufferVSprintf(&buf, "file:%s", dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_PIPE: + virBufferVSprintf(&buf, "pipe:%s", dev->data.file.path); + break; + + case VIR_DOMAIN_CHR_TYPE_STDIO: + virBufferAddLit(&buf, "stdio"); + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + virBufferVSprintf(&buf, "udp:%s:%s@%s:%s", + dev->data.udp.connectHost, + dev->data.udp.connectService, + dev->data.udp.bindHost, + dev->data.udp.bindService); + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + if (dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET) { + virBufferVSprintf(&buf, "telnet:%s:%s%s", + dev->data.tcp.host, + dev->data.tcp.service, + dev->data.tcp.listen ? ",server,nowait" : ""); + } else { + virBufferVSprintf(&buf, "tcp:%s:%s%s", + dev->data.tcp.host, + dev->data.tcp.service, + dev->data.tcp.listen ? ",server,nowait" : ""); + } + break; + + case VIR_DOMAIN_CHR_TYPE_UNIX: + virBufferVSprintf(&buf, "unix:%s%s", + dev->data.nix.path, + dev->data.nix.listen ? ",server,nowait" : ""); + break; + } + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +char * +qemuBuildVirtioSerialPortDevStr(virDomainChrDefPtr dev) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + if (dev->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE) + virBufferAddLit(&buf, "virtconsole"); + else + virBufferAddLit(&buf, "virtserialport"); + + if (dev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { + /* Check it's a virtio-serial address */ + if (dev->info.type != + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL) + { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("virtio serial device has invalid address type")); + goto error; + } + + virBufferVSprintf(&buf, + ",bus=" QEMU_VIRTIO_SERIAL_PREFIX "%d.%d", + dev->info.addr.vioserial.controller, + dev->info.addr.vioserial.bus); + virBufferVSprintf(&buf, + ",nr=%d", + dev->info.addr.vioserial.port); + } + + virBufferVSprintf(&buf, ",chardev=%s", dev->info.alias); + if (dev->target.name) { + virBufferVSprintf(&buf, ",name=%s", dev->target.name); + } + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + +static char *qemuBuildSmbiosBiosStr(virSysinfoDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if ((def->bios_vendor == NULL) && (def->bios_version == NULL) && + (def->bios_date == NULL) && (def->bios_release == NULL)) + return(NULL); + + virBufferAddLit(&buf, "type=0"); + + /* 0:Vendor */ + if (def->bios_vendor) + virBufferVSprintf(&buf, ",vendor=%s", def->bios_vendor); + /* 0:BIOS Version */ + if (def->bios_version) + virBufferVSprintf(&buf, ",version=%s", def->bios_version); + /* 0:BIOS Release Date */ + if (def->bios_date) + virBufferVSprintf(&buf, ",date=%s", def->bios_date); + /* 0:System BIOS Major Release and 0:System BIOS Minor Release */ + if (def->bios_release) + virBufferVSprintf(&buf, ",release=%s", def->bios_release); + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return(NULL); +} + +static char *qemuBuildSmbiosSystemStr(virSysinfoDefPtr def, bool skip_uuid) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + if ((def->system_manufacturer == NULL) && (def->system_sku == NULL) && + (def->system_product == NULL) && (def->system_version == NULL) && + (def->system_serial == NULL) && (def->system_family == NULL) && + (def->system_uuid == NULL || skip_uuid)) + return NULL; + + virBufferAddLit(&buf, "type=1"); + + /* 1:Manufacturer */ + if (def->system_manufacturer) + virBufferVSprintf(&buf, ",manufacturer=%s", + def->system_manufacturer); + /* 1:Product Name */ + if (def->system_product) + virBufferVSprintf(&buf, ",product=%s", def->system_product); + /* 1:Version */ + if (def->system_version) + virBufferVSprintf(&buf, ",version=%s", def->system_version); + /* 1:Serial Number */ + if (def->system_serial) + virBufferVSprintf(&buf, ",serial=%s", def->system_serial); + /* 1:UUID */ + if (def->system_uuid && !skip_uuid) + virBufferVSprintf(&buf, ",uuid=%s", def->system_uuid); + /* 1:SKU Number */ + if (def->system_sku) + virBufferVSprintf(&buf, ",sku=%s", def->system_sku); + /* 1:Family */ + if (def->system_family) + virBufferVSprintf(&buf, ",family=%s", def->system_family); + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return(NULL); +} + +static char * +qemuBuildClockArgStr(virDomainClockDefPtr def) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + switch (def->offset) { + case VIR_DOMAIN_CLOCK_OFFSET_UTC: + virBufferAddLit(&buf, "base=utc"); + break; + + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: + virBufferAddLit(&buf, "base=localtime"); + break; + + case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: { + time_t now = time(NULL); + struct tm nowbits; + + now += def->data.adjustment; + gmtime_r(&now, &nowbits); + + virBufferVSprintf(&buf, "base=%d-%02d-%02dT%02d:%02d:%02d", + nowbits.tm_year + 1900, + nowbits.tm_mon + 1, + nowbits.tm_mday, + nowbits.tm_hour, + nowbits.tm_min, + nowbits.tm_sec); + } break; + + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock offset '%s'"), + virDomainClockOffsetTypeToString(def->offset)); + goto error; + } + + /* Look for an 'rtc' timer element, and add in appropriate clock= and driftfix= */ + int i; + for (i = 0; i < def->ntimers; i++) { + if (def->timers[i]->name == VIR_DOMAIN_TIMER_NAME_RTC) { + switch (def->timers[i]->track) { + case -1: /* unspecified - use hypervisor default */ + break; + case VIR_DOMAIN_TIMER_TRACK_BOOT: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc timer track '%s'"), + virDomainTimerTrackTypeToString(def->timers[i]->track)); + goto error; + case VIR_DOMAIN_TIMER_TRACK_GUEST: + virBufferAddLit(&buf, ",clock=vm"); + break; + case VIR_DOMAIN_TIMER_TRACK_WALL: + virBufferAddLit(&buf, ",clock=host"); + break; + } + + switch (def->timers[i]->tickpolicy) { + case -1: + case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: + /* This is the default - missed ticks delivered when + next scheduled, at normal rate */ + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: + /* deliver ticks at a faster rate until caught up */ + virBufferAddLit(&buf, ",driftfix=slew"); + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: + case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc timer tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->timers[i]->tickpolicy)); + goto error; + } + break; /* no need to check other timers - there is only one rtc */ + } + } + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + +static int +qemuBuildCpuArgStr(const struct qemud_driver *driver, + const virDomainDefPtr def, + const char *emulator, + unsigned long long qemuCmdFlags, + const struct utsname *ut, + char **opt, + bool *hasHwVirt) +{ + const virCPUDefPtr host = driver->caps->host.cpu; + virCPUDefPtr guest = NULL; + unsigned int ncpus = 0; + const char **cpus = NULL; + union cpuData *data = NULL; + int ret = -1; + virBuffer buf = VIR_BUFFER_INITIALIZER; + int i; + + *hasHwVirt = false; + + if (def->cpu && def->cpu->model) { + if (qemuCapsProbeCPUModels(emulator, qemuCmdFlags, ut->machine, + &ncpus, &cpus) < 0) + goto cleanup; + + if (!ncpus || !host) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("CPU specification not supported by hypervisor")); + goto cleanup; + } + } + + if (ncpus > 0 && host) { + virCPUCompareResult cmp; + const char *preferred; + int hasSVM; + + cmp = cpuGuestData(host, def->cpu, &data); + switch (cmp) { + case VIR_CPU_COMPARE_INCOMPATIBLE: + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("guest CPU is not compatible with host CPU")); + /* fall through */ + case VIR_CPU_COMPARE_ERROR: + goto cleanup; + + default: + break; + } + + if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(ut->machine))) + goto no_memory; + + if (def->cpu->match == VIR_CPU_MATCH_MINIMUM) + preferred = host->model; + else + preferred = def->cpu->model; + + guest->type = VIR_CPU_TYPE_GUEST; + if (cpuDecode(guest, data, cpus, ncpus, preferred) < 0) + goto cleanup; + + /* Only 'svm' requires --enable-nesting. The nested + * 'vmx' patches now simply hook off the CPU features + */ + hasSVM = cpuHasFeature(guest->arch, data, "svm"); + if (hasSVM < 0) + goto cleanup; + *hasHwVirt = hasSVM > 0 ? true : false; + + virBufferVSprintf(&buf, "%s", guest->model); + for (i = 0; i < guest->nfeatures; i++) { + char sign; + if (guest->features[i].policy == VIR_CPU_FEATURE_DISABLE) + sign = '-'; + else + sign = '+'; + + virBufferVSprintf(&buf, ",%c%s", sign, guest->features[i].name); + } + } + else { + /* + * Need to force a 32-bit guest CPU type if + * + * 1. guest OS is i686 + * 2. host OS is x86_64 + * 3. emulator is qemu-kvm or kvm + * + * Or + * + * 1. guest OS is i686 + * 2. emulator is qemu-system-x86_64 + */ + if (STREQ(def->os.arch, "i686") && + ((STREQ(ut->machine, "x86_64") && + strstr(emulator, "kvm")) || + strstr(emulator, "x86_64"))) + virBufferAddLit(&buf, "qemu32"); + } + + if (virBufferError(&buf)) + goto no_memory; + + *opt = virBufferContentAndReset(&buf); + + ret = 0; + +cleanup: + virCPUDefFree(guest); + cpuDataFree(ut->machine, data); + + if (cpus) { + for (i = 0; i < ncpus; i++) + VIR_FREE(cpus[i]); + VIR_FREE(cpus); + } + + return ret; + +no_memory: + virReportOOMError(); + goto cleanup; +} + +static char * +qemuBuildSmpArgStr(const virDomainDefPtr def, + unsigned long long qemuCmdFlags) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferVSprintf(&buf, "%u", def->vcpus); + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_SMP_TOPOLOGY)) { + if (def->vcpus != def->maxvcpus) + virBufferVSprintf(&buf, ",maxcpus=%u", def->maxvcpus); + /* sockets, cores, and threads are either all zero + * or all non-zero, thus checking one of them is enough */ + if (def->cpu && def->cpu->sockets) { + virBufferVSprintf(&buf, ",sockets=%u", def->cpu->sockets); + virBufferVSprintf(&buf, ",cores=%u", def->cpu->cores); + virBufferVSprintf(&buf, ",threads=%u", def->cpu->threads); + } + else { + virBufferVSprintf(&buf, ",sockets=%u", def->maxvcpus); + virBufferVSprintf(&buf, ",cores=%u", 1); + virBufferVSprintf(&buf, ",threads=%u", 1); + } + } else if (def->vcpus != def->maxvcpus) { + virBufferFreeAndReset(&buf); + /* FIXME - consider hot-unplugging cpus after boot for older qemu */ + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("setting current vcpu count less than maximum is " + "not supported with this QEMU binary")); + return NULL; + } + + if (virBufferError(&buf)) { + virBufferFreeAndReset(&buf); + virReportOOMError(); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + + +/* + * Constructs a argv suitable for launching qemu with config defined + * for a given virtual machine. + * + * XXX 'conn' is only required to resolve network -> bridge name + * figure out how to remove this requirement some day + */ +virCommandPtr +qemuBuildCommandLine(virConnectPtr conn, + struct qemud_driver *driver, + virDomainDefPtr def, + virDomainChrDefPtr monitor_chr, + bool monitor_json, + unsigned long long qemuCmdFlags, + const char *migrateFrom, + virDomainSnapshotObjPtr current_snapshot, + enum virVMOperationType vmop) +{ + int i; + char boot[VIR_DOMAIN_BOOT_LAST+1]; + struct utsname ut; + int disableKQEMU = 0; + int enableKQEMU = 0; + int disableKVM = 0; + int enableKVM = 0; + const char *emulator; + char uuid[VIR_UUID_STRING_BUFLEN]; + char *cpu; + char *smp; + int last_good_net = -1; + bool hasHwVirt = false; + virCommandPtr cmd; + bool has_rbd_hosts = false; + virBuffer rbd_hosts = VIR_BUFFER_INITIALIZER; + + uname_normalize(&ut); + + if (qemuAssignDeviceAliases(def, qemuCmdFlags) < 0) + return NULL; + + virUUIDFormat(def->uuid, uuid); + + /* Migration is very annoying due to wildly varying syntax & capabilities + * over time of KVM / QEMU codebases + */ + if (migrateFrom) { + if (STRPREFIX(migrateFrom, "tcp")) { + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("TCP migration is not supported with this QEMU binary")); + return NULL; + } + } else if (STREQ(migrateFrom, "stdio")) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) { + migrateFrom = "exec:cat"; + } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("STDIO migration is not supported with this QEMU binary")); + return NULL; + } + } else if (STRPREFIX(migrateFrom, "exec")) { + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("STDIO migration is not supported with this QEMU binary")); + return NULL; + } + } + } + + emulator = def->emulator; + + /* + * do not use boot=on for drives when not using KVM since this + * is not supported at all in upstream QEmu. + */ + if ((qemuCmdFlags & QEMUD_CMD_FLAG_KVM) && + (def->virtType == VIR_DOMAIN_VIRT_QEMU) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_BOOT)) + qemuCmdFlags -= QEMUD_CMD_FLAG_DRIVE_BOOT; + + switch (def->virtType) { + case VIR_DOMAIN_VIRT_QEMU: + if (qemuCmdFlags & QEMUD_CMD_FLAG_KQEMU) + disableKQEMU = 1; + if (qemuCmdFlags & QEMUD_CMD_FLAG_KVM) + disableKVM = 1; + break; + + case VIR_DOMAIN_VIRT_KQEMU: + if (qemuCmdFlags & QEMUD_CMD_FLAG_KVM) + disableKVM = 1; + + if (qemuCmdFlags & QEMUD_CMD_FLAG_ENABLE_KQEMU) { + enableKQEMU = 1; + } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_KQEMU)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary %s does not support kqemu"), + emulator); + } + break; + + case VIR_DOMAIN_VIRT_KVM: + if (qemuCmdFlags & QEMUD_CMD_FLAG_KQEMU) + disableKQEMU = 1; + + if (qemuCmdFlags & QEMUD_CMD_FLAG_ENABLE_KVM) { + enableKVM = 1; + } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_KVM)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary %s does not support kvm"), + emulator); + } + break; + + case VIR_DOMAIN_VIRT_XEN: + /* XXX better check for xenner */ + break; + + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary %s does not support %s"), + emulator, virDomainVirtTypeToString(def->virtType)); + break; + } + + cmd = virCommandNewArgList(emulator, "-S", NULL); + + virCommandAddEnvPassCommon(cmd); + + /* This should *never* be NULL, since we always provide + * a machine in the capabilities data for QEMU. So this + * check is just here as a safety in case the unexpected + * happens */ + if (def->os.machine) + virCommandAddArgList(cmd, "-M", def->os.machine, NULL); + + if (qemuBuildCpuArgStr(driver, def, emulator, qemuCmdFlags, + &ut, &cpu, &hasHwVirt) < 0) + goto error; + + if (cpu) { + virCommandAddArgList(cmd, "-cpu", cpu, NULL); + VIR_FREE(cpu); + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NESTING) && + hasHwVirt) + virCommandAddArg(cmd, "-enable-nesting"); + } + + if (disableKQEMU) + virCommandAddArg(cmd, "-no-kqemu"); + else if (enableKQEMU) + virCommandAddArgList(cmd, "-enable-kqemu", "-kernel-kqemu", NULL); + if (disableKVM) + virCommandAddArg(cmd, "-no-kvm"); + if (enableKVM) + virCommandAddArg(cmd, "-enable-kvm"); + + /* Set '-m MB' based on maxmem, because the lower 'memory' limit + * is set post-startup using the balloon driver. If balloon driver + * is not supported, then they're out of luck anyway + */ + virCommandAddArg(cmd, "-m"); + virCommandAddArgFormat(cmd, "%lu", def->mem.max_balloon / 1024); + if (def->mem.hugepage_backed) { + if (!driver->hugetlbfs_mount) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("hugetlbfs filesystem is not mounted")); + goto error; + } + if (!driver->hugepage_path) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("hugepages are disabled by administrator config")); + goto error; + } + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MEM_PATH)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("hugepage backing not supported by '%s'"), + def->emulator); + goto error; + } + virCommandAddArgList(cmd, "-mem-prealloc", "-mem-path", + driver->hugepage_path, NULL); + } + + virCommandAddArg(cmd, "-smp"); + if (!(smp = qemuBuildSmpArgStr(def, qemuCmdFlags))) + goto error; + virCommandAddArg(cmd, smp); + VIR_FREE(smp); + + if (qemuCmdFlags & QEMUD_CMD_FLAG_NAME) { + virCommandAddArg(cmd, "-name"); + if (driver->setProcessName && + (qemuCmdFlags & QEMUD_CMD_FLAG_NAME_PROCESS)) { + virCommandAddArgFormat(cmd, "%s,process=qemu:%s", + def->name, def->name); + } else { + virCommandAddArg(cmd, def->name); + } + } + if (qemuCmdFlags & QEMUD_CMD_FLAG_UUID) + virCommandAddArgList(cmd, "-uuid", uuid, NULL); + if (def->virtType == VIR_DOMAIN_VIRT_XEN || + STREQ(def->os.type, "xen") || + STREQ(def->os.type, "linux")) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_DOMID) { + virCommandAddArg(cmd, "-domid"); + virCommandAddArgFormat(cmd, "%d", def->id); + } else if (qemuCmdFlags & QEMUD_CMD_FLAG_XEN_DOMID) { + virCommandAddArg(cmd, "-xen-attach"); + virCommandAddArg(cmd, "-xen-domid"); + virCommandAddArgFormat(cmd, "%d", def->id); + } else { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("qemu emulator '%s' does not support xen"), + def->emulator); + goto error; + } + } + + if ((def->os.smbios_mode != VIR_DOMAIN_SMBIOS_NONE) && + (def->os.smbios_mode != VIR_DOMAIN_SMBIOS_EMULATE)) { + virSysinfoDefPtr source = NULL; + bool skip_uuid = false; + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_SMBIOS_TYPE)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("the QEMU binary %s does not support smbios settings"), + emulator); + goto error; + } + + /* should we really error out or just warn in those cases ? */ + if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_HOST) { + if (driver->hostsysinfo == NULL) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Host SMBIOS information is not available")); + goto error; + } + source = driver->hostsysinfo; + /* Host and guest uuid must differ, by definition of UUID. */ + skip_uuid = true; + } else if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_SYSINFO) { + if (def->sysinfo == NULL) { + qemuReportError(VIR_ERR_XML_ERROR, + _("Domain '%s' sysinfo are not available"), + def->name); + goto error; + } + source = def->sysinfo; + /* domain_conf guaranteed that system_uuid matches guest uuid. */ + } + if (source != NULL) { + char *smbioscmd; + + smbioscmd = qemuBuildSmbiosBiosStr(source); + if (smbioscmd != NULL) { + virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); + VIR_FREE(smbioscmd); + } + smbioscmd = qemuBuildSmbiosSystemStr(source, skip_uuid); + if (smbioscmd != NULL) { + virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); + VIR_FREE(smbioscmd); + } + } + } + + /* + * NB, -nographic *MUST* come before any serial, or monitor + * or parallel port flags due to QEMU craziness, where it + * decides to change the serial port & monitor to be on stdout + * if you ask for nographic. So we have to make sure we override + * these defaults ourselves... + */ + if (!def->graphics) + virCommandAddArg(cmd, "-nographic"); + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_NODEFCONFIG) + virCommandAddArg(cmd, + "-nodefconfig"); /* Disable global config files */ + virCommandAddArg(cmd, + "-nodefaults"); /* Disable default guest devices */ + } + + if (monitor_chr) { + char *chrdev; + /* Use -chardev if it's available */ + if (qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) { + + virCommandAddArg(cmd, "-chardev"); + if (!(chrdev = qemuBuildChrChardevStr(monitor_chr))) + goto error; + virCommandAddArg(cmd, chrdev); + VIR_FREE(chrdev); + + virCommandAddArg(cmd, "-mon"); + virCommandAddArgFormat(cmd, "chardev=monitor,mode=%s", + monitor_json ? "control" : "readline"); + } else { + const char *prefix = NULL; + if (monitor_json) + prefix = "control,"; + + virCommandAddArg(cmd, "-monitor"); + if (!(chrdev = qemuBuildChrArgStr(monitor_chr, prefix))) + goto error; + virCommandAddArg(cmd, chrdev); + VIR_FREE(chrdev); + } + } + + if (qemuCmdFlags & QEMUD_CMD_FLAG_RTC) { + const char *rtcopt; + virCommandAddArg(cmd, "-rtc"); + if (!(rtcopt = qemuBuildClockArgStr(&def->clock))) + goto error; + virCommandAddArg(cmd, rtcopt); + VIR_FREE(rtcopt); + } else { + switch (def->clock.offset) { + case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: + case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: + virCommandAddArg(cmd, "-localtime"); + break; + + case VIR_DOMAIN_CLOCK_OFFSET_UTC: + /* Nothing, its the default */ + break; + + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported clock offset '%s'"), + virDomainClockOffsetTypeToString(def->clock.offset)); + goto error; + } + } + if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE && + def->clock.data.timezone) { + virCommandAddEnvPair(cmd, "TZ", def->clock.data.timezone); + } + + for (i = 0; i < def->clock.ntimers; i++) { + switch (def->clock.timers[i]->name) { + default: + case VIR_DOMAIN_TIMER_NAME_PLATFORM: + case VIR_DOMAIN_TIMER_NAME_TSC: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported timer type (name) '%s'"), + virDomainTimerNameTypeToString(def->clock.timers[i]->name)); + goto error; + + case VIR_DOMAIN_TIMER_NAME_RTC: + /* This has already been taken care of (in qemuBuildClockArgStr) + if QEMUD_CMD_FLAG_RTC is set (mutually exclusive with + QEMUD_FLAG_RTC_TD_HACK) */ + if (qemuCmdFlags & QEMUD_CMD_FLAG_RTC_TD_HACK) { + switch (def->clock.timers[i]->tickpolicy) { + case -1: + case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: + /* the default - do nothing */ + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: + virCommandAddArg(cmd, "-rtc-td-hack"); + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: + case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; + } + } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_RTC) + && (def->clock.timers[i]->tickpolicy + != VIR_DOMAIN_TIMER_TICKPOLICY_DELAY) + && (def->clock.timers[i]->tickpolicy != -1)) { + /* a non-default rtc policy was given, but there is no + way to implement it in this version of qemu */ + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported rtc tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; + } + break; + + case VIR_DOMAIN_TIMER_NAME_PIT: + switch (def->clock.timers[i]->tickpolicy) { + case -1: + case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: + /* delay is the default if we don't have kernel + (-no-kvm-pit), otherwise, the default is catchup. */ + if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) + virCommandAddArg(cmd, "-no-kvm-pit-reinjection"); + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: + if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) { + /* do nothing - this is default for kvm-pit */ + } else if (qemuCmdFlags & QEMUD_CMD_FLAG_TDF) { + /* -tdf switches to 'catchup' with userspace pit. */ + virCommandAddArg(cmd, "-tdf"); + } else { + /* can't catchup if we have neither pit mode */ + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported pit tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; + } + break; + case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: + case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: + /* no way to support these modes for pit in qemu */ + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("unsupported pit tickpolicy '%s'"), + virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); + goto error; + } + break; + + case VIR_DOMAIN_TIMER_NAME_HPET: + /* the only meaningful attribute for hpet is "present". If + * present is -1, that means it wasn't specified, and + * should be left at the default for the + * hypervisor. "default" when -no-hpet exists is "yes", + * and when -no-hpet doesn't exist is "no". "confusing"? + * "yes"! */ + + if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_HPET) { + if (def->clock.timers[i]->present == 0) + virCommandAddArg(cmd, "-no-hpet"); + } else { + /* no hpet timer available. The only possible action + is to raise an error if present="yes" */ + if (def->clock.timers[i]->present == 1) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("pit timer is not supported")); + } + } + break; + } + } + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT) && + def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART) + virCommandAddArg(cmd, "-no-reboot"); + + if (!(def->features & (1 << VIR_DOMAIN_FEATURE_ACPI))) + virCommandAddArg(cmd, "-no-acpi"); + + if (!def->os.bootloader) { + for (i = 0 ; i < def->os.nBootDevs ; i++) { + switch (def->os.bootDevs[i]) { + case VIR_DOMAIN_BOOT_CDROM: + boot[i] = 'd'; + break; + case VIR_DOMAIN_BOOT_FLOPPY: + boot[i] = 'a'; + break; + case VIR_DOMAIN_BOOT_DISK: + boot[i] = 'c'; + break; + case VIR_DOMAIN_BOOT_NET: + boot[i] = 'n'; + break; + default: + boot[i] = 'c'; + break; + } + } + if (def->os.nBootDevs) { + virBuffer boot_buf = VIR_BUFFER_INITIALIZER; + virCommandAddArg(cmd, "-boot"); + + boot[def->os.nBootDevs] = '\0'; + + if (qemuCmdFlags & QEMUD_CMD_FLAG_BOOT_MENU && + def->os.bootmenu != VIR_DOMAIN_BOOT_MENU_DEFAULT) { + if (def->os.bootmenu == VIR_DOMAIN_BOOT_MENU_ENABLED) + virBufferVSprintf(&boot_buf, "order=%s,menu=on", boot); + else if (def->os.bootmenu == VIR_DOMAIN_BOOT_MENU_DISABLED) + virBufferVSprintf(&boot_buf, "order=%s,menu=off", boot); + } else { + virBufferVSprintf(&boot_buf, "%s", boot); + } + + virCommandAddArgBuffer(cmd, &boot_buf); + } + + if (def->os.kernel) + virCommandAddArgList(cmd, "-kernel", def->os.kernel, NULL); + if (def->os.initrd) + virCommandAddArgList(cmd, "-initrd", def->os.initrd, NULL); + if (def->os.cmdline) + virCommandAddArgList(cmd, "-append", def->os.cmdline, NULL); + } else { + virCommandAddArgList(cmd, "-bootloader", def->os.bootloader, NULL); + } + + for (i = 0 ; i < def->ndisks ; i++) { + virDomainDiskDefPtr disk = def->disks[i]; + + if (disk->driverName != NULL && + !STREQ(disk->driverName, "qemu")) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported driver name '%s' for disk '%s'"), + disk->driverName, disk->src); + goto error; + } + } + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + for (i = 0 ; i < def->ncontrollers ; i++) { + virDomainControllerDefPtr cont = def->controllers[i]; + + /* We don't add an explicit IDE or FD controller because the + * provided PIIX4 device already includes one. It isn't possible to + * remove the PIIX4. */ + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE || + cont->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC) + continue; + + /* QEMU doesn't implement a SATA driver */ + if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("SATA is not supported with this QEMU binary")); + goto error; + } + + virCommandAddArg(cmd, "-device"); + + char *devstr; + if (!(devstr = qemuBuildControllerDevStr(def->controllers[i]))) + goto no_memory; + + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } + } + + /* If QEMU supports -drive param instead of old -hda, -hdb, -cdrom .. */ + if (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE) { + int bootCD = 0, bootFloppy = 0, bootDisk = 0; + + /* If QEMU supports boot=on for -drive param... */ + if (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_BOOT) { + for (i = 0 ; i < def->os.nBootDevs ; i++) { + switch (def->os.bootDevs[i]) { + case VIR_DOMAIN_BOOT_CDROM: + bootCD = 1; + break; + case VIR_DOMAIN_BOOT_FLOPPY: + bootFloppy = 1; + break; + case VIR_DOMAIN_BOOT_DISK: + bootDisk = 1; + break; + } + } + } + + for (i = 0 ; i < def->ndisks ; i++) { + char *optstr; + int bootable = 0; + virDomainDiskDefPtr disk = def->disks[i]; + int withDeviceArg = 0; + int j; + + /* Unless we have -device, then USB disks need special + handling */ + if ((disk->bus == VIR_DOMAIN_DISK_BUS_USB) && + !(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + virCommandAddArg(cmd, "-usbdevice"); + virCommandAddArgFormat(cmd, "disk:%s", disk->src); + } else { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported usb disk type for '%s'"), + disk->src); + goto error; + } + continue; + } + + switch (disk->device) { + case VIR_DOMAIN_DISK_DEVICE_CDROM: + bootable = bootCD; + bootCD = 0; + break; + case VIR_DOMAIN_DISK_DEVICE_FLOPPY: + bootable = bootFloppy; + bootFloppy = 0; + break; + case VIR_DOMAIN_DISK_DEVICE_DISK: + bootable = bootDisk; + bootDisk = 0; + break; + } + + virCommandAddArg(cmd, "-drive"); + + /* Unfortunately it is not possible to use + -device for floppies, or Xen paravirt + devices. Fortunately, those don't need + static PCI addresses, so we don't really + care that we can't use -device */ + if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + (disk->bus != VIR_DOMAIN_DISK_BUS_XEN)) + withDeviceArg = 1; + if (!(optstr = qemuBuildDriveStr(disk, bootable, + (withDeviceArg ? qemuCmdFlags : + (qemuCmdFlags & ~QEMUD_CMD_FLAG_DEVICE))))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + + if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK && + disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_RBD) { + for (j = 0 ; j < disk->nhosts ; j++) { + if (!has_rbd_hosts) { + virBufferAddLit(&rbd_hosts, "CEPH_ARGS=-m "); + has_rbd_hosts = true; + } else { + virBufferAddLit(&rbd_hosts, ","); + } + virDomainDiskHostDefPtr host = &disk->hosts[j]; + if (host->port) { + virBufferVSprintf(&rbd_hosts, "%s:%s", + host->name, + host->port); + } else { + virBufferVSprintf(&rbd_hosts, "%s", + host->name); + } + } + } + + if (withDeviceArg) { + if (disk->bus == VIR_DOMAIN_DISK_BUS_FDC) { + virCommandAddArg(cmd, "-global"); + virCommandAddArgFormat(cmd, "isa-fdc.drive%c=drive-%s", + disk->info.addr.drive.unit + ? 'B' : 'A', + disk->info.alias); + } else { + virCommandAddArg(cmd, "-device"); + + if (!(optstr = qemuBuildDriveDevStr(disk))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + } + } + } + } else { + for (i = 0 ; i < def->ndisks ; i++) { + char dev[NAME_MAX]; + char file[PATH_MAX]; + virDomainDiskDefPtr disk = def->disks[i]; + int j; + + if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { + if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { + virCommandAddArg(cmd, "-usbdevice"); + virCommandAddArgFormat(cmd, "disk:%s", disk->src); + } else { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported usb disk type for '%s'"), + disk->src); + goto error; + } + continue; + } + + if (STREQ(disk->dst, "hdc") && + disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { + if (disk->src) { + snprintf(dev, NAME_MAX, "-%s", "cdrom"); + } else { + continue; + } + } else { + if (STRPREFIX(disk->dst, "hd") || + STRPREFIX(disk->dst, "fd")) { + snprintf(dev, NAME_MAX, "-%s", disk->dst); + } else { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk type '%s'"), disk->dst); + goto error; + } + } + + if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { + /* QEMU only supports magic FAT format for now */ + if (disk->driverType && + STRNEQ(disk->driverType, "fat")) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk driver type for '%s'"), + disk->driverType); + goto error; + } + if (!disk->readonly) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot create virtual FAT disks in read-write mode")); + goto error; + } + if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) + snprintf(file, PATH_MAX, "fat:floppy:%s", disk->src); + else + snprintf(file, PATH_MAX, "fat:%s", disk->src); + } else if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { + switch (disk->protocol) { + case VIR_DOMAIN_DISK_PROTOCOL_NBD: + if (disk->nhosts != 1) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("NBD accepts only one host")); + goto error; + } + snprintf(file, PATH_MAX, "nbd:%s:%s,", + disk->hosts->name, disk->hosts->port); + break; + case VIR_DOMAIN_DISK_PROTOCOL_RBD: + snprintf(file, PATH_MAX, "rbd:%s,", disk->src); + for (j = 0 ; j < disk->nhosts ; j++) { + if (!has_rbd_hosts) { + virBufferAddLit(&rbd_hosts, "CEPH_ARGS=-m "); + has_rbd_hosts = true; + } else { + virBufferAddLit(&rbd_hosts, ","); + } + virDomainDiskHostDefPtr host = &disk->hosts[j]; + if (host->port) { + virBufferVSprintf(&rbd_hosts, "%s:%s", + host->name, + host->port); + } else { + virBufferVSprintf(&rbd_hosts, "%s", + host->name); + } + } + break; + case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: + if (disk->nhosts == 0) + snprintf(file, PATH_MAX, "sheepdog:%s,", disk->src); + else + /* only one host is supported now */ + snprintf(file, PATH_MAX, "sheepdog:%s:%s:%s,", + disk->hosts->name, disk->hosts->port, + disk->src); + break; + } + } else { + snprintf(file, PATH_MAX, "%s", disk->src); + } + + virCommandAddArgList(cmd, dev, file, NULL); + } + } + + if (has_rbd_hosts) + virCommandAddEnvBuffer(cmd, &rbd_hosts); + + if (qemuCmdFlags & QEMUD_CMD_FLAG_FSDEV) { + for (i = 0 ; i < def->nfss ; i++) { + char *optstr; + virDomainFSDefPtr fs = def->fss[i]; + + virCommandAddArg(cmd, "-fsdev"); + if (!(optstr = qemuBuildFSStr(fs, qemuCmdFlags))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + + virCommandAddArg(cmd, "-device"); + if (!(optstr = qemuBuildFSDevStr(fs))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + } + } else { + if (def->nfss) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("filesystem passthrough not supported by this QEMU")); + goto error; + } + } + + if (!def->nnets) { + /* If we have -device, then we set -nodefault already */ + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) + virCommandAddArgList(cmd, "-net", "none", NULL); + } else { + for (i = 0 ; i < def->nnets ; i++) { + virDomainNetDefPtr net = def->nets[i]; + char *nic, *host; + char tapfd_name[50]; + char vhostfd_name[50] = ""; + int vlan; + + /* VLANs are not used with -netdev, so don't record them */ + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) + vlan = -1; + else + vlan = i; + + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK || + net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { + int tapfd = qemuNetworkIfaceConnect(conn, driver, net, + qemuCmdFlags); + if (tapfd < 0) + goto error; + + last_good_net = i; + virCommandTransferFD(cmd, tapfd); + + if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", + tapfd) >= sizeof(tapfd_name)) + goto no_memory; + } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + int tapfd = qemuPhysIfaceConnect(conn, driver, net, + qemuCmdFlags, + def->uuid, + vmop); + if (tapfd < 0) + goto error; + + last_good_net = i; + virCommandTransferFD(cmd, tapfd); + + if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", + tapfd) >= sizeof(tapfd_name)) + goto no_memory; + } + + if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK || + net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || + net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + /* Attempt to use vhost-net mode for these types of + network device */ + int vhostfd = qemuOpenVhostNet(net, qemuCmdFlags); + if (vhostfd >= 0) { + virCommandTransferFD(cmd, vhostfd); + + if (snprintf(vhostfd_name, sizeof(vhostfd_name), "%d", + vhostfd) >= sizeof(vhostfd_name)) + goto no_memory; + } + } + /* Possible combinations: + * + * 1. Old way: -net nic,model=e1000,vlan=1 -net tap,vlan=1 + * 2. Semi-new: -device e1000,vlan=1 -net tap,vlan=1 + * 3. Best way: -netdev type=tap,id=netdev1 -device e1000,id=netdev1 + * + * NB, no support for -netdev without use of -device + */ + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + virCommandAddArg(cmd, "-netdev"); + if (!(host = qemuBuildHostNetStr(net, ',', vlan, + tapfd_name, vhostfd_name))) + goto error; + virCommandAddArg(cmd, host); + VIR_FREE(host); + } + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + virCommandAddArg(cmd, "-device"); + if (!(nic = qemuBuildNicDevStr(net, vlan))) + goto error; + virCommandAddArg(cmd, nic); + VIR_FREE(nic); + } else { + virCommandAddArg(cmd, "-net"); + if (!(nic = qemuBuildNicStr(net, "nic,", vlan))) + goto error; + virCommandAddArg(cmd, nic); + VIR_FREE(nic); + } + if (!((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))) { + virCommandAddArg(cmd, "-net"); + if (!(host = qemuBuildHostNetStr(net, ',', vlan, + tapfd_name, vhostfd_name))) + goto error; + virCommandAddArg(cmd, host); + VIR_FREE(host); + } + } + } + + if (!def->nserials) { + /* If we have -device, then we set -nodefault already */ + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) + virCommandAddArgList(cmd, "-serial", "none", NULL); + } else { + for (i = 0 ; i < def->nserials ; i++) { + virDomainChrDefPtr serial = def->serials[i]; + char *devstr; + + /* Use -chardev with -device if they are available */ + if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(serial))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "isa-serial,chardev=%s", + serial->info.alias); + } else { + virCommandAddArg(cmd, "-serial"); + if (!(devstr = qemuBuildChrArgStr(serial, NULL))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } + } + } + + if (!def->nparallels) { + /* If we have -device, then we set -nodefault already */ + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) + virCommandAddArgList(cmd, "-parallel", "none", NULL); + } else { + for (i = 0 ; i < def->nparallels ; i++) { + virDomainChrDefPtr parallel = def->parallels[i]; + char *devstr; + + /* Use -chardev with -device if they are available */ + if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(parallel))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + virCommandAddArgFormat(cmd, "isa-parallel,chardev=%s", + parallel->info.alias); + } else { + virCommandAddArg(cmd, "-parallel"); + if (!(devstr = qemuBuildChrArgStr(parallel, NULL))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } + } + } + + for (i = 0 ; i < def->nchannels ; i++) { + virDomainChrDefPtr channel = def->channels[i]; + char *devstr; + + switch(channel->targetType) { + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) || + !(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("guestfwd requires QEMU to support -chardev & -device")); + goto error; + } + + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(channel))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + char *addr = virSocketFormatAddr(channel->target.addr); + if (!addr) + goto error; + int port = virSocketGetPort(channel->target.addr); + + virCommandAddArg(cmd, "-netdev"); + virCommandAddArgFormat(cmd, + "user,guestfwd=tcp:%s:%i,chardev=%s,id=user-%s", + addr, port, channel->info.alias, + channel->info.alias); + VIR_FREE(addr); + break; + + case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("virtio channel requires QEMU to support -device")); + goto error; + } + + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(channel))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildVirtioSerialPortDevStr(channel))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + break; + } + } + + /* Explicit console devices */ + if (def->console) { + virDomainChrDefPtr console = def->console; + char *devstr; + + switch(console->targetType) { + case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO: + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + qemuReportError(VIR_ERR_NO_SUPPORT, "%s", + _("virtio channel requires QEMU to support -device")); + goto error; + } + + virCommandAddArg(cmd, "-chardev"); + if (!(devstr = qemuBuildChrChardevStr(console))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildVirtioSerialPortDevStr(console))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + break; + + case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL: + break; + + default: + qemuReportError(VIR_ERR_NO_SUPPORT, + _("unsupported console target type %s"), + NULLSTR(virDomainChrConsoleTargetTypeToString(console->targetType))); + goto error; + } + } + + virCommandAddArg(cmd, "-usb"); + for (i = 0 ; i < def->ninputs ; i++) { + virDomainInputDefPtr input = def->inputs[i]; + + if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + char *optstr; + virCommandAddArg(cmd, "-device"); + if (!(optstr = qemuBuildUSBInputDevStr(input))) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + } else { + virCommandAddArgList(cmd, "-usbdevice", + input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE + ? "mouse" : "tablet", NULL); + } + } + } + + if (def->ngraphics > 1) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("only 1 graphics device is supported")); + goto error; + } + + if ((def->ngraphics == 1) && + def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + virBuffer opt = VIR_BUFFER_INITIALIZER; + + if (qemuCmdFlags & QEMUD_CMD_FLAG_VNC_COLON) { + if (def->graphics[0]->data.vnc.listenAddr) + virBufferAdd(&opt, def->graphics[0]->data.vnc.listenAddr, -1); + else if (driver->vncListen) + virBufferAdd(&opt, driver->vncListen, -1); + + virBufferVSprintf(&opt, ":%d", + def->graphics[0]->data.vnc.port - 5900); + + if (def->graphics[0]->data.vnc.auth.passwd || + driver->vncPassword) + virBufferAddLit(&opt, ",password"); + + if (driver->vncTLS) { + virBufferAddLit(&opt, ",tls"); + if (driver->vncTLSx509verify) { + virBufferVSprintf(&opt, ",x509verify=%s", + driver->vncTLSx509certdir); + } else { + virBufferVSprintf(&opt, ",x509=%s", + driver->vncTLSx509certdir); + } + } + + if (driver->vncSASL) { + virBufferAddLit(&opt, ",sasl"); + + if (driver->vncSASLdir) + virCommandAddEnvPair(cmd, "SASL_CONF_DIR", + driver->vncSASLdir); + + /* TODO: Support ACLs later */ + } + } else { + virBufferVSprintf(&opt, "%d", + def->graphics[0]->data.vnc.port - 5900); + } + + virCommandAddArg(cmd, "-vnc"); + virCommandAddArgBuffer(cmd, &opt); + if (def->graphics[0]->data.vnc.keymap) { + virCommandAddArgList(cmd, "-k", def->graphics[0]->data.vnc.keymap, + NULL); + } + + /* Unless user requested it, set the audio backend to none, to + * prevent it opening the host OS audio devices, since that causes + * security issues and might not work when using VNC. + */ + if (driver->vncAllowHostAudio) { + virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); + } else { + virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none"); + } + } else if ((def->ngraphics == 1) && + def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { + if (def->graphics[0]->data.sdl.xauth) + virCommandAddEnvPair(cmd, "XAUTHORITY", + def->graphics[0]->data.sdl.xauth); + if (def->graphics[0]->data.sdl.display) + virCommandAddEnvPair(cmd, "DISPLAY", + def->graphics[0]->data.sdl.display); + if (def->graphics[0]->data.sdl.fullscreen) + virCommandAddArg(cmd, "-full-screen"); + + /* If using SDL for video, then we should just let it + * use QEMU's host audio drivers, possibly SDL too + * User can set these two before starting libvirtd + */ + virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); + virCommandAddEnvPass(cmd, "SDL_AUDIODRIVER"); + + /* New QEMU has this flag to let us explicitly ask for + * SDL graphics. This is better than relying on the + * default, since the default changes :-( */ + if (qemuCmdFlags & QEMUD_CMD_FLAG_SDL) + virCommandAddArg(cmd, "-sdl"); + + } else if ((def->ngraphics == 1) && + def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + virBuffer opt = VIR_BUFFER_INITIALIZER; + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_SPICE)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("spice graphics are not supported with this QEMU")); + goto error; + } + + virBufferVSprintf(&opt, "port=%u", def->graphics[0]->data.spice.port); + + if (driver->spiceTLS && def->graphics[0]->data.spice.tlsPort != -1) + virBufferVSprintf(&opt, ",tls-port=%u", def->graphics[0]->data.spice.tlsPort); + + if (def->graphics[0]->data.spice.listenAddr) + virBufferVSprintf(&opt, ",addr=%s", def->graphics[0]->data.spice.listenAddr); + else if (driver->spiceListen) + virBufferVSprintf(&opt, ",addr=%s", driver->spiceListen); + + /* In the password case we set it via monitor command, to avoid + * making it visible on CLI, so there's no use of password=XXX + * in this bit of the code */ + if (!def->graphics[0]->data.spice.auth.passwd && + !driver->spicePassword) + virBufferAddLit(&opt, ",disable-ticketing"); + + if (driver->spiceTLS) + virBufferVSprintf(&opt, ",x509-dir=%s", + driver->spiceTLSx509certdir); + + for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) { + int mode = def->graphics[0]->data.spice.channels[i]; + switch (mode) { + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE: + virBufferVSprintf(&opt, ",tls-channel=%s", + virDomainGraphicsSpiceChannelNameTypeToString(i)); + break; + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE: + virBufferVSprintf(&opt, ",plaintext-channel=%s", + virDomainGraphicsSpiceChannelNameTypeToString(i)); + break; + } + } + + virCommandAddArg(cmd, "-spice"); + virCommandAddArgBuffer(cmd, &opt); + if (def->graphics[0]->data.spice.keymap) + virCommandAddArgList(cmd, "-k", + def->graphics[0]->data.spice.keymap, NULL); + /* SPICE includes native support for tunnelling audio, so we + * set the audio backend to point at SPICE's own driver + */ + virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=spice"); + + } else if ((def->ngraphics == 1)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported graphics type '%s'"), + virDomainGraphicsTypeToString(def->graphics[0]->type)); + goto error; + } + + if (def->nvideos > 0) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_VGA) { + if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_XEN) { + /* nothing - vga has no effect on Xen pvfb */ + } else { + if ((def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_QXL) && + !(qemuCmdFlags & QEMUD_CMD_FLAG_VGA_QXL)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU does not support QXL graphics adapters")); + goto error; + } + + const char *vgastr = qemuVideoTypeToString(def->videos[0]->type); + if (!vgastr || STREQ(vgastr, "")) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("video type %s is not supported with QEMU"), + virDomainVideoTypeToString(def->videos[0]->type)); + goto error; + } + + virCommandAddArgList(cmd, "-vga", vgastr, NULL); + } + } else { + + switch (def->videos[0]->type) { + case VIR_DOMAIN_VIDEO_TYPE_VGA: + virCommandAddArg(cmd, "-std-vga"); + break; + + case VIR_DOMAIN_VIDEO_TYPE_VMVGA: + virCommandAddArg(cmd, "-vmwarevga"); + break; + + case VIR_DOMAIN_VIDEO_TYPE_XEN: + case VIR_DOMAIN_VIDEO_TYPE_CIRRUS: + /* No special args - this is the default */ + break; + + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("video type %s is not supported with this QEMU"), + virDomainVideoTypeToString(def->videos[0]->type)); + goto error; + } + } + + if (def->nvideos > 1) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + for (i = 1 ; i < def->nvideos ; i++) { + char *str; + if (def->videos[i]->type != VIR_DOMAIN_VIDEO_TYPE_QXL) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("video type %s is only valid as primary video card"), + virDomainVideoTypeToString(def->videos[0]->type)); + goto error; + } + + virCommandAddArg(cmd, "-device"); + + if (!(str = qemuBuildVideoDevStr(def->videos[i]))) + goto error; + + virCommandAddArg(cmd, str); + VIR_FREE(str); + } + } else { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("only one video card is currently supported")); + goto error; + } + } + + } else { + /* If we have -device, then we set -nodefault already */ + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + (qemuCmdFlags & QEMUD_CMD_FLAG_VGA) && + (qemuCmdFlags & QEMUD_CMD_FLAG_VGA_NONE)) + virCommandAddArgList(cmd, "-vga", "none", NULL); + } + + /* Add sound hardware */ + if (def->nsounds) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + for (i = 0 ; i < def->nsounds ; i++) { + virDomainSoundDefPtr sound = def->sounds[i]; + char *str = NULL; + + /* Sadly pcspk device doesn't use -device syntax. Fortunately + * we don't need to set any PCI address on it, so we don't + * mind too much */ + if (sound->model == VIR_DOMAIN_SOUND_MODEL_PCSPK) { + virCommandAddArgList(cmd, "-soundhw", "pcspk", NULL); + } else { + virCommandAddArg(cmd, "-device"); + + if (!(str = qemuBuildSoundDevStr(sound))) + goto error; + + virCommandAddArg(cmd, str); + VIR_FREE(str); + } + } + } else { + int size = 100; + char *modstr; + if (VIR_ALLOC_N(modstr, size+1) < 0) + goto no_memory; + + for (i = 0 ; i < def->nsounds && size > 0 ; i++) { + virDomainSoundDefPtr sound = def->sounds[i]; + const char *model = virDomainSoundModelTypeToString(sound->model); + if (!model) { + VIR_FREE(modstr); + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("invalid sound model")); + goto error; + } + strncat(modstr, model, size); + size -= strlen(model); + if (i < (def->nsounds - 1)) + strncat(modstr, ",", size--); + } + virCommandAddArgList(cmd, "-soundhw", modstr, NULL); + VIR_FREE(modstr); + } + } + + /* Add watchdog hardware */ + if (def->watchdog) { + virDomainWatchdogDefPtr watchdog = def->watchdog; + char *optstr; + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + virCommandAddArg(cmd, "-device"); + + optstr = qemuBuildWatchdogDevStr(watchdog); + if (!optstr) + goto error; + } else { + virCommandAddArg(cmd, "-watchdog"); + + const char *model = virDomainWatchdogModelTypeToString(watchdog->model); + if (!model) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("missing watchdog model")); + goto error; + } + + if (!(optstr = strdup(model))) + goto no_memory; + } + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + + int act = watchdog->action; + if (act == VIR_DOMAIN_WATCHDOG_ACTION_DUMP) + act = VIR_DOMAIN_WATCHDOG_ACTION_PAUSE; + const char *action = virDomainWatchdogActionTypeToString(act); + if (!action) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("invalid watchdog action")); + goto error; + } + virCommandAddArgList(cmd, "-watchdog-action", action, NULL); + } + + /* Add host passthrough hardware */ + for (i = 0 ; i < def->nhostdevs ; i++) { + virDomainHostdevDefPtr hostdev = def->hostdevs[i]; + char *devstr; + + /* USB */ + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + virCommandAddArg(cmd, "-device"); + if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } else { + virCommandAddArg(cmd, "-usbdevice"); + if (!(devstr = qemuBuildUSBHostdevUsbDevStr(hostdev))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } + } + + /* PCI */ + if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + char *configfd_name = NULL; + if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) { + int configfd = qemuOpenPCIConfig(hostdev); + + if (configfd >= 0) { + if (virAsprintf(&configfd_name, "%d", configfd) < 0) { + VIR_FORCE_CLOSE(configfd); + virReportOOMError(); + goto no_memory; + } + + virCommandTransferFD(cmd, configfd); + } + } + virCommandAddArg(cmd, "-device"); + devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name); + VIR_FREE(configfd_name); + if (!devstr) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) { + virCommandAddArg(cmd, "-pcidevice"); + if (!(devstr = qemuBuildPCIHostdevPCIDevStr(hostdev))) + goto error; + virCommandAddArg(cmd, devstr); + VIR_FREE(devstr); + } else { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("PCI device assignment is not supported by this version of qemu")); + goto error; + } + } + } + + if (migrateFrom) + virCommandAddArgList(cmd, "-incoming", migrateFrom, NULL); + + /* QEMU changed its default behavior to not include the virtio balloon + * device. Explicitly request it to ensure it will be present. + * + * NB: Earlier we declared that VirtIO balloon will always be in + * slot 0x3 on bus 0x0 + */ + if ((def->memballoon) && + (def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_NONE)) { + if (def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("Memory balloon device type '%s' is not supported by this version of qemu"), + virDomainMemballoonModelTypeToString(def->memballoon->model)); + goto error; + } + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + char *optstr; + virCommandAddArg(cmd, "-device"); + + optstr = qemuBuildMemballoonDevStr(def->memballoon); + if (!optstr) + goto error; + virCommandAddArg(cmd, optstr); + VIR_FREE(optstr); + } else if (qemuCmdFlags & QEMUD_CMD_FLAG_BALLOON) { + virCommandAddArgList(cmd, "-balloon", "virtio", NULL); + } + } + + if (current_snapshot && current_snapshot->def->active) + virCommandAddArgList(cmd, "-loadvm", current_snapshot->def->name, + NULL); + + if (def->namespaceData) { + qemuDomainCmdlineDefPtr qemucmd; + + qemucmd = def->namespaceData; + for (i = 0; i < qemucmd->num_args; i++) + virCommandAddArg(cmd, qemucmd->args[i]); + for (i = 0; i < qemucmd->num_env; i++) + virCommandAddEnvPair(cmd, qemucmd->env_name[i], + qemucmd->env_value[i] + ? qemucmd->env_value[i] : ""); + } + + return cmd; + + no_memory: + virReportOOMError(); + error: + for (i = 0; i <= last_good_net; i++) + virDomainConfNWFilterTeardown(def->nets[i]); + virBufferFreeAndReset(&rbd_hosts); + virCommandFree(cmd); + return NULL; +} + + +/* + * This method takes a string representing a QEMU command line ARGV set + * optionally prefixed by a list of environment variables. It then tries + * to split it up into a NULL terminated list of env & argv, splitting + * on space + */ +static int qemuStringToArgvEnv(const char *args, + const char ***retenv, + const char ***retargv) +{ + char **arglist = NULL; + int argcount = 0; + int argalloc = 0; + int envend; + int i; + const char *curr = args; + const char *start; + const char **progenv = NULL; + const char **progargv = NULL; + + /* Iterate over string, splitting on sequences of ' ' */ + while (curr && *curr != '\0') { + char *arg; + const char *next; + + start = curr; + /* accept a space in CEPH_ARGS */ + if (STRPREFIX(curr, "CEPH_ARGS=-m ")) { + start += strlen("CEPH_ARGS=-m "); + } + if (*start == '\'') { + if (start == curr) + curr++; + next = strchr(start + 1, '\''); + } else if (*start == '"') { + if (start == curr) + curr++; + next = strchr(start + 1, '"'); + } else { + next = strchr(start, ' '); + } + if (!next) + next = strchr(curr, '\n'); + + if (next) { + arg = strndup(curr, next-curr); + if (*next == '\'' || + *next == '"') + next++; + } else { + arg = strdup(curr); + } + + if (!arg) + goto no_memory; + + if (argalloc == argcount) { + if (VIR_REALLOC_N(arglist, argalloc+10) < 0) { + VIR_FREE(arg); + goto no_memory; + } + argalloc+=10; + } + + arglist[argcount++] = arg; + + while (next && c_isspace(*next)) + next++; + + curr = next; + } + + /* Iterate over list of args, finding first arg not containing + * the '=' character (eg, skip over env vars FOO=bar) */ + for (envend = 0 ; ((envend < argcount) && + (strchr(arglist[envend], '=') != NULL)); + envend++) + ; /* nada */ + + /* Copy the list of env vars */ + if (envend > 0) { + if (VIR_REALLOC_N(progenv, envend+1) < 0) + goto no_memory; + for (i = 0 ; i < envend ; i++) { + progenv[i] = arglist[i]; + arglist[i] = NULL; + } + progenv[i] = NULL; + } + + /* Copy the list of argv */ + if (VIR_REALLOC_N(progargv, argcount-envend + 1) < 0) + goto no_memory; + for (i = envend ; i < argcount ; i++) + progargv[i-envend] = arglist[i]; + progargv[i-envend] = NULL; + + VIR_FREE(arglist); + + *retenv = progenv; + *retargv = progargv; + + return 0; + +no_memory: + for (i = 0 ; progenv && progenv[i] ; i++) + VIR_FREE(progenv[i]); + VIR_FREE(progenv); + for (i = 0 ; i < argcount ; i++) + VIR_FREE(arglist[i]); + VIR_FREE(arglist); + virReportOOMError(); + return -1; +} + + +/* + * Search for a named env variable, and return the value part + */ +static const char *qemuFindEnv(const char **progenv, + const char *name) +{ + int i; + int len = strlen(name); + + for (i = 0 ; progenv && progenv[i] ; i++) { + if (STREQLEN(progenv[i], name, len) && + progenv[i][len] == '=') + return progenv[i] + len + 1; + } + return NULL; +} + +/* + * Takes a string containing a set of key=value,key=value,key... + * parameters and splits them up, returning two arrays with + * the individual keys and values. If allowEmptyValue is nonzero, + * the "=value" part is optional and if a key with no value is found, + * NULL is be placed into corresponding place in retvalues. + */ +int +qemuParseKeywords(const char *str, + char ***retkeywords, + char ***retvalues, + int allowEmptyValue) +{ + int keywordCount = 0; + int keywordAlloc = 0; + char **keywords = NULL; + char **values = NULL; + const char *start = str; + const char *end; + int i; + + *retkeywords = NULL; + *retvalues = NULL; + end = start + strlen(str); + + while (start) { + const char *separator; + const char *endmark; + char *keyword; + char *value = NULL; + + if (!(endmark = strchr(start, ','))) + endmark = end; + if (!(separator = strchr(start, '='))) + separator = end; + + if (separator >= endmark) { + if (!allowEmptyValue) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("malformed keyword arguments in '%s'"), str); + goto error; + } + separator = endmark; + } + + if (!(keyword = strndup(start, separator - start))) + goto no_memory; + + if (separator < endmark) { + separator++; + if (!(value = strndup(separator, endmark - separator))) { + VIR_FREE(keyword); + goto no_memory; + } + } + + if (keywordAlloc == keywordCount) { + if (VIR_REALLOC_N(keywords, keywordAlloc + 10) < 0 || + VIR_REALLOC_N(values, keywordAlloc + 10) < 0) { + VIR_FREE(keyword); + VIR_FREE(value); + goto no_memory; + } + keywordAlloc += 10; + } + + keywords[keywordCount] = keyword; + values[keywordCount] = value; + keywordCount++; + + start = endmark < end ? endmark + 1 : NULL; + } + + *retkeywords = keywords; + *retvalues = values; + + return keywordCount; + +no_memory: + virReportOOMError(); +error: + for (i = 0 ; i < keywordCount ; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + return -1; +} + +/* + * Tries to parse new style QEMU -drive args. + * + * eg -drive file=/dev/HostVG/VirtData1,if=ide,index=1 + * + * Will fail if not using the 'index' keyword + */ +static virDomainDiskDefPtr +qemuParseCommandLineDisk(virCapsPtr caps, + const char *val, + int nvirtiodisk) +{ + virDomainDiskDefPtr def = NULL; + char **keywords; + char **values; + int nkeywords; + int i; + int idx = -1; + int busid = -1; + int unitid = -1; + + if ((nkeywords = qemuParseKeywords(val, + &keywords, + &values, 0)) < 0) + return NULL; + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + def->bus = VIR_DOMAIN_DISK_BUS_IDE; + def->device = VIR_DOMAIN_DISK_DEVICE_DISK; + def->type = VIR_DOMAIN_DISK_TYPE_FILE; + + for (i = 0 ; i < nkeywords ; i++) { + if (STREQ(keywords[i], "file")) { + if (values[i] && STRNEQ(values[i], "")) { + def->src = values[i]; + values[i] = NULL; + if (STRPREFIX(def->src, "/dev/")) + def->type = VIR_DOMAIN_DISK_TYPE_BLOCK; + else if (STRPREFIX(def->src, "nbd:")) { + char *host, *port; + + def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + def->protocol = VIR_DOMAIN_DISK_PROTOCOL_NBD; + host = def->src + strlen("nbd:"); + port = strchr(host, ':'); + if (!port) { + def = NULL; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse nbd filename '%s'"), def->src); + goto cleanup; + } + *port++ = '\0'; + if (VIR_ALLOC(def->hosts) < 0) { + virReportOOMError(); + goto cleanup; + } + def->nhosts = 1; + def->hosts->name = strdup(host); + if (!def->hosts->name) { + virReportOOMError(); + goto cleanup; + } + def->hosts->port = strdup(port); + if (!def->hosts->port) { + virReportOOMError(); + goto cleanup; + } + + VIR_FREE(def->src); + def->src = NULL; + } else if (STRPREFIX(def->src, "rbd:")) { + char *p = def->src; + + def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + def->protocol = VIR_DOMAIN_DISK_PROTOCOL_RBD; + def->src = strdup(p + strlen("rbd:")); + if (!def->src) { + virReportOOMError(); + goto cleanup; + } + + VIR_FREE(p); + } else if (STRPREFIX(def->src, "sheepdog:")) { + char *p = def->src; + char *port, *vdi; + + def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + def->protocol = VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG; + def->src = strdup(p + strlen("sheepdog:")); + if (!def->src) { + virReportOOMError(); + goto cleanup; + } + + /* def->src must be [vdiname] or [host]:[port]:[vdiname] */ + port = strchr(def->src, ':'); + if (port) { + *port++ = '\0'; + vdi = strchr(port, ':'); + if (!vdi) { + def = NULL; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse sheepdog filename '%s'"), p); + goto cleanup; + } + *vdi++ = '\0'; + if (VIR_ALLOC(def->hosts) < 0) { + virReportOOMError(); + goto cleanup; + } + def->nhosts = 1; + def->hosts->name = def->src; + def->hosts->port = strdup(port); + if (!def->hosts->port) { + virReportOOMError(); + goto cleanup; + } + def->src = strdup(vdi); + if (!def->src) { + virReportOOMError(); + goto cleanup; + } + } + + VIR_FREE(p); + } else + def->type = VIR_DOMAIN_DISK_TYPE_FILE; + } else { + def->type = VIR_DOMAIN_DISK_TYPE_FILE; + } + } else if (STREQ(keywords[i], "if")) { + if (STREQ(values[i], "ide")) + def->bus = VIR_DOMAIN_DISK_BUS_IDE; + else if (STREQ(values[i], "scsi")) + def->bus = VIR_DOMAIN_DISK_BUS_SCSI; + else if (STREQ(values[i], "virtio")) + def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO; + else if (STREQ(values[i], "xen")) + def->bus = VIR_DOMAIN_DISK_BUS_XEN; + } else if (STREQ(keywords[i], "media")) { + if (STREQ(values[i], "cdrom")) { + def->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + def->readonly = 1; + } else if (STREQ(values[i], "floppy")) + def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + } else if (STREQ(keywords[i], "format")) { + def->driverName = strdup("qemu"); + if (!def->driverName) { + virDomainDiskDefFree(def); + def = NULL; + virReportOOMError(); + goto cleanup; + } + def->driverType = values[i]; + values[i] = NULL; + } else if (STREQ(keywords[i], "cache")) { + if (STREQ(values[i], "off") || + STREQ(values[i], "none")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_DISABLE; + else if (STREQ(values[i], "writeback") || + STREQ(values[i], "on")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITEBACK; + else if (STREQ(values[i], "writethrough")) + def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITETHRU; + } else if (STREQ(keywords[i], "werror") || + STREQ(keywords[i], "rerror")) { + if (STREQ(values[i], "stop")) + def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP; + else if (STREQ(values[i], "ignore")) + def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE; + else if (STREQ(values[i], "enospace")) + def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE; + } else if (STREQ(keywords[i], "index")) { + if (virStrToLong_i(values[i], NULL, 10, &idx) < 0) { + virDomainDiskDefFree(def); + def = NULL; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse drive index '%s'"), val); + goto cleanup; + } + } else if (STREQ(keywords[i], "bus")) { + if (virStrToLong_i(values[i], NULL, 10, &busid) < 0) { + virDomainDiskDefFree(def); + def = NULL; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse drive bus '%s'"), val); + goto cleanup; + } + } else if (STREQ(keywords[i], "unit")) { + if (virStrToLong_i(values[i], NULL, 10, &unitid) < 0) { + virDomainDiskDefFree(def); + def = NULL; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse drive unit '%s'"), val); + goto cleanup; + } + } else if (STREQ(keywords[i], "readonly")) { + if ((values[i] == NULL) || STREQ(values[i], "on")) + def->readonly = 1; + } + } + + if (!def->src && + def->device == VIR_DOMAIN_DISK_DEVICE_DISK && + def->type != VIR_DOMAIN_DISK_TYPE_NETWORK) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("missing file parameter in drive '%s'"), val); + virDomainDiskDefFree(def); + def = NULL; + goto cleanup; + } + if (idx == -1 && + def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) + idx = nvirtiodisk; + + if (idx == -1 && + unitid == -1 && + busid == -1) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("missing index/unit/bus parameter in drive '%s'"), val); + virDomainDiskDefFree(def); + def = NULL; + goto cleanup; + } + + if (idx == -1) { + if (unitid == -1) + unitid = 0; + if (busid == -1) + busid = 0; + switch (def->bus) { + case VIR_DOMAIN_DISK_BUS_IDE: + idx = (busid * 2) + unitid; + break; + case VIR_DOMAIN_DISK_BUS_SCSI: + idx = (busid * 7) + unitid; + break; + default: + idx = unitid; + break; + } + } + + if (def->bus == VIR_DOMAIN_DISK_BUS_IDE) { + def->dst = strdup("hda"); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_SCSI) { + def->dst = strdup("sda"); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { + def->dst = strdup("vda"); + } else if (def->bus == VIR_DOMAIN_DISK_BUS_XEN) { + def->dst = strdup("xvda"); + } else { + def->dst = strdup("hda"); + } + + if (!def->dst) { + virDomainDiskDefFree(def); + def = NULL; + virReportOOMError(); + goto cleanup; + } + if (STREQ(def->dst, "xvda")) + def->dst[3] = 'a' + idx; + else + def->dst[2] = 'a' + idx; + + if (virDomainDiskDefAssignAddress(caps, def) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("invalid device name '%s'"), def->dst); + virDomainDiskDefFree(def); + def = NULL; + /* fall through to "cleanup" */ + } + +cleanup: + for (i = 0 ; i < nkeywords ; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + return def; +} + +/* + * Tries to find a NIC definition matching a vlan we want + */ +static const char * +qemuFindNICForVLAN(int nnics, + const char **nics, + int wantvlan) +{ + int i; + for (i = 0 ; i < nnics ; i++) { + int gotvlan; + const char *tmp = strstr(nics[i], "vlan="); + char *end; + if (!tmp) + continue; + + tmp += strlen("vlan="); + + if (virStrToLong_i(tmp, &end, 10, &gotvlan) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse NIC vlan in '%s'"), nics[i]); + return NULL; + } + + if (gotvlan == wantvlan) + return nics[i]; + } + + if (wantvlan == 0 && nnics > 0) + return nics[0]; + + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find NIC definition for vlan %d"), wantvlan); + return NULL; +} + + +/* + * Tries to parse a QEMU -net backend argument. Gets given + * a list of all known -net frontend arguments to try and + * match up against. Horribly complicated stuff + */ +static virDomainNetDefPtr +qemuParseCommandLineNet(virCapsPtr caps, + const char *val, + int nnics, + const char **nics) +{ + virDomainNetDefPtr def = NULL; + char **keywords = NULL; + char **values = NULL; + int nkeywords; + const char *nic; + int wantvlan = 0; + const char *tmp; + int genmac = 1; + int i; + + tmp = strchr(val, ','); + + if (tmp) { + if ((nkeywords = qemuParseKeywords(tmp+1, + &keywords, + &values, 0)) < 0) + return NULL; + } else { + nkeywords = 0; + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + /* 'tap' could turn into libvirt type=ethernet, type=bridge or + * type=network, but we can't tell, so use the generic config */ + if (STRPREFIX(val, "tap,")) + def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + else if (STRPREFIX(val, "socket")) + def->type = VIR_DOMAIN_NET_TYPE_CLIENT; + else if (STRPREFIX(val, "user")) + def->type = VIR_DOMAIN_NET_TYPE_USER; + else + def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + + for (i = 0 ; i < nkeywords ; i++) { + if (STREQ(keywords[i], "vlan")) { + if (virStrToLong_i(values[i], NULL, 10, &wantvlan) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse vlan in '%s'"), val); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && + STREQ(keywords[i], "script") && STRNEQ(values[i], "")) { + def->data.ethernet.script = values[i]; + values[i] = NULL; + } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && + STREQ(keywords[i], "ifname")) { + def->ifname = values[i]; + values[i] = NULL; + } + } + + + /* Done parsing the nic backend. Now to try and find corresponding + * frontend, based off vlan number. NB this assumes a 1-1 mapping + */ + + nic = qemuFindNICForVLAN(nnics, nics, wantvlan); + if (!nic) { + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + + if (!STRPREFIX(nic, "nic")) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse NIC definition '%s'"), nic); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + + for (i = 0 ; i < nkeywords ; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + + if (STRPREFIX(nic, "nic,")) { + if ((nkeywords = qemuParseKeywords(nic + strlen("nic,"), + &keywords, + &values, 0)) < 0) { + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + } else { + nkeywords = 0; + } + + for (i = 0 ; i < nkeywords ; i++) { + if (STREQ(keywords[i], "macaddr")) { + genmac = 0; + if (virParseMacAddr(values[i], def->mac) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to parse mac address '%s'"), + values[i]); + virDomainNetDefFree(def); + def = NULL; + goto cleanup; + } + } else if (STREQ(keywords[i], "model")) { + def->model = values[i]; + values[i] = NULL; + } + } + + if (genmac) + virCapabilitiesGenerateMac(caps, def->mac); + +cleanup: + for (i = 0 ; i < nkeywords ; i++) { + VIR_FREE(keywords[i]); + VIR_FREE(values[i]); + } + VIR_FREE(keywords); + VIR_FREE(values); + return def; +} + + +/* + * Tries to parse a QEMU PCI device + */ +static virDomainHostdevDefPtr +qemuParseCommandLinePCI(const char *val) +{ + virDomainHostdevDefPtr def = NULL; + int bus = 0, slot = 0, func = 0; + const char *start; + char *end; + + if (!STRPREFIX(val, "host=")) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown PCI device syntax '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + + start = val + strlen("host="); + if (virStrToLong_i(start, &end, 16, &bus) < 0 || *end != ':') { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device bus '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + start = end + 1; + if (virStrToLong_i(start, &end, 16, &slot) < 0 || *end != '.') { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device slot '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + start = end + 1; + if (virStrToLong_i(start, NULL, 16, &func) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract PCI device function '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + def->managed = 1; + def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; + def->source.subsys.u.pci.bus = bus; + def->source.subsys.u.pci.slot = slot; + def->source.subsys.u.pci.function = func; + +cleanup: + return def; +} + + +/* + * Tries to parse a QEMU USB device + */ +static virDomainHostdevDefPtr +qemuParseCommandLineUSB(const char *val) +{ + virDomainHostdevDefPtr def = NULL; + int first = 0, second = 0; + const char *start; + char *end; + + if (!STRPREFIX(val, "host:")) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown USB device syntax '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + + start = val + strlen("host:"); + if (strchr(start, ':')) { + if (virStrToLong_i(start, &end, 16, &first) < 0 || *end != ':') { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract USB device vendor '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + start = end + 1; + if (virStrToLong_i(start, NULL, 16, &second) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract USB device product '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + } else { + if (virStrToLong_i(start, &end, 10, &first) < 0 || *end != '.') { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract USB device bus '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + start = end + 1; + if (virStrToLong_i(start, NULL, 10, &second) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot extract USB device address '%s'"), val); + VIR_FREE(def); + goto cleanup; + } + } + + if (VIR_ALLOC(def) < 0) { + virReportOOMError(); + goto cleanup; + } + + def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; + def->managed = 0; + def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB; + if (*end == '.') { + def->source.subsys.u.usb.bus = first; + def->source.subsys.u.usb.device = second; + } else { + def->source.subsys.u.usb.vendor = first; + def->source.subsys.u.usb.product = second; + } + +cleanup: + return def; +} + + +/* + * Tries to parse a QEMU serial/parallel device + */ +static virDomainChrDefPtr +qemuParseCommandLineChr(const char *val) +{ + virDomainChrDefPtr def; + + if (VIR_ALLOC(def) < 0) + goto no_memory; + + if (STREQ(val, "null")) { + def->type = VIR_DOMAIN_CHR_TYPE_NULL; + } else if (STREQ(val, "vc")) { + def->type = VIR_DOMAIN_CHR_TYPE_VC; + } else if (STREQ(val, "pty")) { + def->type = VIR_DOMAIN_CHR_TYPE_PTY; + } else if (STRPREFIX(val, "file:")) { + def->type = VIR_DOMAIN_CHR_TYPE_FILE; + def->data.file.path = strdup(val+strlen("file:")); + if (!def->data.file.path) + goto no_memory; + } else if (STRPREFIX(val, "pipe:")) { + def->type = VIR_DOMAIN_CHR_TYPE_PIPE; + def->data.file.path = strdup(val+strlen("pipe:")); + if (!def->data.file.path) + goto no_memory; + } else if (STREQ(val, "stdio")) { + def->type = VIR_DOMAIN_CHR_TYPE_STDIO; + } else if (STRPREFIX(val, "udp:")) { + const char *svc1, *host2, *svc2; + def->type = VIR_DOMAIN_CHR_TYPE_UDP; + val += strlen("udp:"); + svc1 = strchr(val, ':'); + host2 = svc1 ? strchr(svc1, '@') : NULL; + svc2 = host2 ? strchr(host2, ':') : NULL; + + if (svc1) + def->data.udp.connectHost = strndup(val, svc1-val); + else + def->data.udp.connectHost = strdup(val); + + if (!def->data.udp.connectHost) + goto no_memory; + + if (svc1) { + svc1++; + if (host2) + def->data.udp.connectService = strndup(svc1, host2-svc1); + else + def->data.udp.connectService = strdup(svc1); + + if (!def->data.udp.connectService) + goto no_memory; + } + + if (host2) { + host2++; + if (svc2) + def->data.udp.bindHost = strndup(host2, svc2-host2); + else + def->data.udp.bindHost = strdup(host2); + + if (!def->data.udp.bindHost) + goto no_memory; + } + if (svc2) { + svc2++; + def->data.udp.bindService = strdup(svc2); + if (!def->data.udp.bindService) + goto no_memory; + } + } else if (STRPREFIX(val, "tcp:") || + STRPREFIX(val, "telnet:")) { + const char *opt, *svc; + def->type = VIR_DOMAIN_CHR_TYPE_TCP; + if (STRPREFIX(val, "tcp:")) { + val += strlen("tcp:"); + } else { + val += strlen("telnet:"); + def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + } + svc = strchr(val, ':'); + if (!svc) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot find port number in character device %s"), val); + goto error; + } + opt = strchr(svc, ','); + if (opt && strstr(opt, "server")) + def->data.tcp.listen = 1; + + def->data.tcp.host = strndup(val, svc-val); + if (!def->data.tcp.host) + goto no_memory; + svc++; + if (opt) { + def->data.tcp.service = strndup(svc, opt-svc); + } else { + def->data.tcp.service = strdup(svc); + } + if (!def->data.tcp.service) + goto no_memory; + } else if (STRPREFIX(val, "unix:")) { + const char *opt; + val += strlen("unix:"); + opt = strchr(val, ','); + def->type = VIR_DOMAIN_CHR_TYPE_UNIX; + if (opt) { + if (strstr(opt, "listen")) + def->data.nix.listen = 1; + def->data.nix.path = strndup(val, opt-val); + } else { + def->data.nix.path = strdup(val); + } + if (!def->data.nix.path) + goto no_memory; + + } else if (STRPREFIX(val, "/dev")) { + def->type = VIR_DOMAIN_CHR_TYPE_DEV; + def->data.file.path = strdup(val); + if (!def->data.file.path) + goto no_memory; + } else { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown character device syntax %s"), val); + goto error; + } + + return def; + +no_memory: + virReportOOMError(); +error: + virDomainChrDefFree(def); + return NULL; +} + + +static virCPUDefPtr +qemuInitGuestCPU(virDomainDefPtr dom) +{ + if (!dom->cpu) { + virCPUDefPtr cpu; + + if (VIR_ALLOC(cpu) < 0) { + virReportOOMError(); + return NULL; + } + + cpu->type = VIR_CPU_TYPE_GUEST; + cpu->match = VIR_CPU_MATCH_EXACT; + dom->cpu = cpu; + } + + return dom->cpu; +} + + +static int +qemuParseCommandLineCPU(virDomainDefPtr dom, + const char *val) +{ + virCPUDefPtr cpu; + const char *p = val; + const char *next; + + if (!(cpu = qemuInitGuestCPU(dom))) + goto error; + + do { + if (*p == '\0' || *p == ',') + goto syntax; + + if ((next = strchr(p, ','))) + next++; + + if (!cpu->model) { + if (next) + cpu->model = strndup(p, next - p - 1); + else + cpu->model = strdup(p); + + if (!cpu->model) + goto no_memory; + } + else if (*p == '+' || *p == '-') { + char *feature; + int policy; + int ret; + + if (*p == '+') + policy = VIR_CPU_FEATURE_REQUIRE; + else + policy = VIR_CPU_FEATURE_DISABLE; + + p++; + if (*p == '\0' || *p == ',') + goto syntax; + + if (next) + feature = strndup(p, next - p - 1); + else + feature = strdup(p); + + ret = virCPUDefAddFeature(cpu, feature, policy); + VIR_FREE(feature); + if (ret < 0) + goto error; + } + } while ((p = next)); + + return 0; + +syntax: + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown CPU syntax '%s'"), val); + goto error; + +no_memory: + virReportOOMError(); +error: + return -1; +} + + +static int +qemuParseCommandLineSmp(virDomainDefPtr dom, + const char *val) +{ + unsigned int sockets = 0; + unsigned int cores = 0; + unsigned int threads = 0; + unsigned int maxcpus = 0; + int i; + int nkws; + char **kws; + char **vals; + int n; + char *end; + int ret; + + nkws = qemuParseKeywords(val, &kws, &vals, 1); + if (nkws < 0) + return -1; + + for (i = 0; i < nkws; i++) { + if (vals[i] == NULL) { + if (i > 0 || + virStrToLong_i(kws[i], &end, 10, &n) < 0 || *end != '\0') + goto syntax; + dom->vcpus = n; + } else { + if (virStrToLong_i(vals[i], &end, 10, &n) < 0 || *end != '\0') + goto syntax; + if (STREQ(kws[i], "sockets")) + sockets = n; + else if (STREQ(kws[i], "cores")) + cores = n; + else if (STREQ(kws[i], "threads")) + threads = n; + else if (STREQ(kws[i], "maxcpus")) + maxcpus = n; + else + goto syntax; + } + } + + dom->maxvcpus = maxcpus ? maxcpus : dom->vcpus; + + if (sockets && cores && threads) { + virCPUDefPtr cpu; + + if (!(cpu = qemuInitGuestCPU(dom))) + goto error; + cpu->sockets = sockets; + cpu->cores = cores; + cpu->threads = threads; + } else if (sockets || cores || threads) + goto syntax; + + ret = 0; + +cleanup: + for (i = 0; i < nkws; i++) { + VIR_FREE(kws[i]); + VIR_FREE(vals[i]); + } + VIR_FREE(kws); + VIR_FREE(vals); + + return ret; + +syntax: + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse CPU topology '%s'"), val); +error: + ret = -1; + goto cleanup; +} + + +/* + * Analyse the env and argv settings and reconstruct a + * virDomainDefPtr representing these settings as closely + * as is practical. This is not an exact science.... + */ +virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, + const char **progenv, + const char **progargv) +{ + virDomainDefPtr def; + int i; + int nographics = 0; + int fullscreen = 0; + char *path; + int nnics = 0; + const char **nics = NULL; + int video = VIR_DOMAIN_VIDEO_TYPE_CIRRUS; + int nvirtiodisk = 0; + qemuDomainCmdlineDefPtr cmd; + + if (!progargv[0]) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("no emulator path found")); + return NULL; + } + + if (VIR_ALLOC(def) < 0) + goto no_memory; + + /* allocate the cmdlinedef up-front; if it's unused, we'll free it later */ + if (VIR_ALLOC(cmd) < 0) + goto no_memory; + + virUUIDGenerate(def->uuid); + + def->id = -1; + def->mem.cur_balloon = def->mem.max_balloon = 64 * 1024; + def->maxvcpus = 1; + def->vcpus = 1; + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC; + def->features = (1 << VIR_DOMAIN_FEATURE_ACPI) + /*| (1 << VIR_DOMAIN_FEATURE_APIC)*/; + def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART; + def->onCrash = VIR_DOMAIN_LIFECYCLE_DESTROY; + def->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY; + def->virtType = VIR_DOMAIN_VIRT_QEMU; + if (!(def->emulator = strdup(progargv[0]))) + goto no_memory; + + if (strstr(def->emulator, "kvm")) { + def->virtType = VIR_DOMAIN_VIRT_KVM; + def->features |= (1 << VIR_DOMAIN_FEATURE_PAE); + } + + + if (strstr(def->emulator, "xenner")) { + def->virtType = VIR_DOMAIN_VIRT_KVM; + def->os.type = strdup("xen"); + } else { + def->os.type = strdup("hvm"); + } + if (!def->os.type) + goto no_memory; + + if (STRPREFIX(def->emulator, "qemu")) + path = def->emulator; + else + path = strstr(def->emulator, "qemu"); + if (path && + STRPREFIX(path, "qemu-system-")) + def->os.arch = strdup(path + strlen("qemu-system-")); + else + def->os.arch = strdup("i686"); + if (!def->os.arch) + goto no_memory; + +#define WANT_VALUE() \ + const char *val = progargv[++i]; \ + if (!val) { \ + qemuReportError(VIR_ERR_INTERNAL_ERROR, \ + _("missing value for %s argument"), arg); \ + goto error; \ + } + + /* One initial loop to get list of NICs, so we + * can correlate them later */ + for (i = 1 ; progargv[i] ; i++) { + const char *arg = progargv[i]; + /* Make sure we have a single - for all options to + simplify next logic */ + if (STRPREFIX(arg, "--")) + arg++; + + if (STREQ(arg, "-net")) { + WANT_VALUE(); + if (STRPREFIX(val, "nic")) { + if (VIR_REALLOC_N(nics, nnics+1) < 0) + goto no_memory; + nics[nnics++] = val; + } + } + } + + /* Now the real processing loop */ + for (i = 1 ; progargv[i] ; i++) { + const char *arg = progargv[i]; + /* Make sure we have a single - for all options to + simplify next logic */ + if (STRPREFIX(arg, "--")) + arg++; + + if (STREQ(arg, "-vnc")) { + virDomainGraphicsDefPtr vnc; + char *tmp; + WANT_VALUE(); + if (VIR_ALLOC(vnc) < 0) + goto no_memory; + vnc->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; + + tmp = strchr(val, ':'); + if (tmp) { + char *opts; + if (virStrToLong_i(tmp+1, &opts, 10, &vnc->data.vnc.port) < 0) { + VIR_FREE(vnc); + qemuReportError(VIR_ERR_INTERNAL_ERROR, \ + _("cannot parse VNC port '%s'"), tmp+1); + goto error; + } + vnc->data.vnc.listenAddr = strndup(val, tmp-val); + if (!vnc->data.vnc.listenAddr) { + VIR_FREE(vnc); + goto no_memory; + } + vnc->data.vnc.port += 5900; + vnc->data.vnc.autoport = 0; + } else { + vnc->data.vnc.autoport = 1; + } + + if (VIR_REALLOC_N(def->graphics, def->ngraphics+1) < 0) { + virDomainGraphicsDefFree(vnc); + goto no_memory; + } + def->graphics[def->ngraphics++] = vnc; + } else if (STREQ(arg, "-m")) { + int mem; + WANT_VALUE(); + if (virStrToLong_i(val, NULL, 10, &mem) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, \ + _("cannot parse memory level '%s'"), val); + goto error; + } + def->mem.cur_balloon = def->mem.max_balloon = mem * 1024; + } else if (STREQ(arg, "-smp")) { + WANT_VALUE(); + if (qemuParseCommandLineSmp(def, val) < 0) + goto error; + } else if (STREQ(arg, "-uuid")) { + WANT_VALUE(); + if (virUUIDParse(val, def->uuid) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, \ + _("cannot parse UUID '%s'"), val); + goto error; + } + } else if (STRPREFIX(arg, "-hd") || + STRPREFIX(arg, "-sd") || + STRPREFIX(arg, "-fd") || + STREQ(arg, "-cdrom")) { + WANT_VALUE(); + virDomainDiskDefPtr disk; + if (VIR_ALLOC(disk) < 0) + goto no_memory; + + if (STRPREFIX(val, "/dev/")) + disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; + else if (STRPREFIX(val, "nbd:")) { + disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_NBD; + val += strlen("nbd:"); + } else if (STRPREFIX(val, "rbd:")) { + disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_RBD; + val += strlen("rbd:"); + } else if (STRPREFIX(val, "sheepdog:")) { + disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK; + disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG; + val += strlen("sheepdog:"); + } else + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + if (STREQ(arg, "-cdrom")) { + disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; + disk->dst = strdup("hdc"); + disk->readonly = 1; + } else { + if (STRPREFIX(arg, "-fd")) { + disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; + disk->bus = VIR_DOMAIN_DISK_BUS_FDC; + } else { + disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; + if (STRPREFIX(arg, "-hd")) + disk->bus = VIR_DOMAIN_DISK_BUS_IDE; + else + disk->bus = VIR_DOMAIN_DISK_BUS_SCSI; + } + disk->dst = strdup(arg + 1); + } + disk->src = strdup(val); + + if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { + char *host, *port; + + switch (disk->protocol) { + case VIR_DOMAIN_DISK_PROTOCOL_NBD: + host = disk->src; + port = strchr(host, ':'); + if (!port) { + def = NULL; + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse nbd filename '%s'"), disk->src); + goto error; + } + *port++ = '\0'; + if (VIR_ALLOC(disk->hosts) < 0) { + virReportOOMError(); + goto error; + } + disk->nhosts = 1; + disk->hosts->name = host; + disk->hosts->port = strdup(port); + if (!disk->hosts->port) { + virReportOOMError(); + goto error; + } + disk->src = NULL; + break; + case VIR_DOMAIN_DISK_PROTOCOL_RBD: + /* handled later since the hosts for all disks are in CEPH_ARGS */ + break; + case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: + /* disk->src must be [vdiname] or [host]:[port]:[vdiname] */ + port = strchr(disk->src, ':'); + if (port) { + char *vdi; + + *port++ = '\0'; + vdi = strchr(port, ':'); + if (!vdi) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse sheepdog filename '%s'"), val); + goto error; + } + *vdi++ = '\0'; + if (VIR_ALLOC(disk->hosts) < 0) { + virReportOOMError(); + goto error; + } + disk->nhosts = 1; + disk->hosts->name = disk->src; + disk->hosts->port = strdup(port); + if (!disk->hosts->port) { + virReportOOMError(); + goto error; + } + disk->src = strdup(vdi); + if (!disk->src) { + virReportOOMError(); + goto error; + } + } + break; + } + } + + if (!(disk->src || disk->nhosts > 0) || + !disk->dst) { + virDomainDiskDefFree(disk); + goto no_memory; + } + + if (virDomainDiskDefAssignAddress(caps, disk) < 0) + goto error; + + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { + virDomainDiskDefFree(disk); + goto no_memory; + } + def->disks[def->ndisks++] = disk; + } else if (STREQ(arg, "-no-acpi")) { + def->features &= ~(1 << VIR_DOMAIN_FEATURE_ACPI); + } else if (STREQ(arg, "-no-reboot")) { + def->onReboot = VIR_DOMAIN_LIFECYCLE_DESTROY; + } else if (STREQ(arg, "-no-kvm")) { + def->virtType = VIR_DOMAIN_VIRT_QEMU; + } else if (STREQ(arg, "-nographic")) { + nographics = 1; + } else if (STREQ(arg, "-full-screen")) { + fullscreen = 1; + } else if (STREQ(arg, "-localtime")) { + def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME; + } else if (STREQ(arg, "-kernel")) { + WANT_VALUE(); + if (!(def->os.kernel = strdup(val))) + goto no_memory; + } else if (STREQ(arg, "-initrd")) { + WANT_VALUE(); + if (!(def->os.initrd = strdup(val))) + goto no_memory; + } else if (STREQ(arg, "-append")) { + WANT_VALUE(); + if (!(def->os.cmdline = strdup(val))) + goto no_memory; + } else if (STREQ(arg, "-boot")) { + int n, b = 0; + WANT_VALUE(); + for (n = 0 ; val[n] && b < VIR_DOMAIN_BOOT_LAST ; n++) { + if (val[n] == 'a') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_FLOPPY; + else if (val[n] == 'c') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_DISK; + else if (val[n] == 'd') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_CDROM; + else if (val[n] == 'n') + def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_NET; + else if (val[n] == ',') + break; + } + def->os.nBootDevs = b; + + if (strstr(val, "menu=on")) + def->os.bootmenu = 1; + } else if (STREQ(arg, "-name")) { + char *process; + WANT_VALUE(); + process = strstr(val, ",process="); + if (process == NULL) { + if (!(def->name = strdup(val))) + goto no_memory; + } else { + if (!(def->name = strndup(val, process - val))) + goto no_memory; + } + } else if (STREQ(arg, "-M")) { + WANT_VALUE(); + if (!(def->os.machine = strdup(val))) + goto no_memory; + } else if (STREQ(arg, "-serial")) { + WANT_VALUE(); + if (STRNEQ(val, "none")) { + virDomainChrDefPtr chr; + if (!(chr = qemuParseCommandLineChr(val))) + goto error; + if (VIR_REALLOC_N(def->serials, def->nserials+1) < 0) { + virDomainChrDefFree(chr); + goto no_memory; + } + chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL; + chr->target.port = def->nserials; + def->serials[def->nserials++] = chr; + } + } else if (STREQ(arg, "-parallel")) { + WANT_VALUE(); + if (STRNEQ(val, "none")) { + virDomainChrDefPtr chr; + if (!(chr = qemuParseCommandLineChr(val))) + goto error; + if (VIR_REALLOC_N(def->parallels, def->nparallels+1) < 0) { + virDomainChrDefFree(chr); + goto no_memory; + } + chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL; + chr->target.port = def->nparallels; + def->parallels[def->nparallels++] = chr; + } + } else if (STREQ(arg, "-usbdevice")) { + WANT_VALUE(); + if (STREQ(val, "tablet") || + STREQ(val, "mouse")) { + virDomainInputDefPtr input; + if (VIR_ALLOC(input) < 0) + goto no_memory; + input->bus = VIR_DOMAIN_INPUT_BUS_USB; + if (STREQ(val, "tablet")) + input->type = VIR_DOMAIN_INPUT_TYPE_TABLET; + else + input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE; + if (VIR_REALLOC_N(def->inputs, def->ninputs+1) < 0) { + virDomainInputDefFree(input); + goto no_memory; + } + def->inputs[def->ninputs++] = input; + } else if (STRPREFIX(val, "disk:")) { + virDomainDiskDefPtr disk; + if (VIR_ALLOC(disk) < 0) + goto no_memory; + disk->src = strdup(val + strlen("disk:")); + if (!disk->src) { + virDomainDiskDefFree(disk); + goto no_memory; + } + if (STRPREFIX(disk->src, "/dev/")) + disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; + else + disk->type = VIR_DOMAIN_DISK_TYPE_FILE; + disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; + disk->bus = VIR_DOMAIN_DISK_BUS_USB; + if (!(disk->dst = strdup("sda"))) { + virDomainDiskDefFree(disk); + goto no_memory; + } + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { + virDomainDiskDefFree(disk); + goto no_memory; + } + def->disks[def->ndisks++] = disk; + } else { + virDomainHostdevDefPtr hostdev; + if (!(hostdev = qemuParseCommandLineUSB(val))) + goto error; + if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) { + virDomainHostdevDefFree(hostdev); + goto no_memory; + } + def->hostdevs[def->nhostdevs++] = hostdev; + } + } else if (STREQ(arg, "-net")) { + WANT_VALUE(); + if (!STRPREFIX(val, "nic") && STRNEQ(val, "none")) { + virDomainNetDefPtr net; + if (!(net = qemuParseCommandLineNet(caps, val, nnics, nics))) + goto error; + if (VIR_REALLOC_N(def->nets, def->nnets+1) < 0) { + virDomainNetDefFree(net); + goto no_memory; + } + def->nets[def->nnets++] = net; + } + } else if (STREQ(arg, "-drive")) { + virDomainDiskDefPtr disk; + WANT_VALUE(); + if (!(disk = qemuParseCommandLineDisk(caps, val, nvirtiodisk))) + goto error; + if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { + virDomainDiskDefFree(disk); + goto no_memory; + } + def->disks[def->ndisks++] = disk; + + if (disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) + nvirtiodisk++; + } else if (STREQ(arg, "-pcidevice")) { + virDomainHostdevDefPtr hostdev; + WANT_VALUE(); + if (!(hostdev = qemuParseCommandLinePCI(val))) + goto error; + if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) { + virDomainHostdevDefFree(hostdev); + goto no_memory; + } + def->hostdevs[def->nhostdevs++] = hostdev; + } else if (STREQ(arg, "-soundhw")) { + const char *start; + WANT_VALUE(); + start = val; + while (start) { + const char *tmp = strchr(start, ','); + int type = -1; + if (STRPREFIX(start, "pcspk")) { + type = VIR_DOMAIN_SOUND_MODEL_PCSPK; + } else if (STRPREFIX(start, "sb16")) { + type = VIR_DOMAIN_SOUND_MODEL_SB16; + } else if (STRPREFIX(start, "es1370")) { + type = VIR_DOMAIN_SOUND_MODEL_ES1370; + } else if (STRPREFIX(start, "ac97")) { + type = VIR_DOMAIN_SOUND_MODEL_AC97; + } + + if (type != -1) { + virDomainSoundDefPtr snd; + if (VIR_ALLOC(snd) < 0) + goto no_memory; + snd->model = type; + if (VIR_REALLOC_N(def->sounds, def->nsounds+1) < 0) { + VIR_FREE(snd); + goto no_memory; + } + def->sounds[def->nsounds++] = snd; + } + + start = tmp ? tmp + 1 : NULL; + } + } else if (STREQ(arg, "-watchdog")) { + WANT_VALUE(); + int model = virDomainWatchdogModelTypeFromString (val); + + if (model != -1) { + virDomainWatchdogDefPtr wd; + if (VIR_ALLOC(wd) < 0) + goto no_memory; + wd->model = model; + wd->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET; + def->watchdog = wd; + } + } else if (STREQ(arg, "-watchdog-action") && def->watchdog) { + WANT_VALUE(); + int action = virDomainWatchdogActionTypeFromString (val); + + if (action != -1) + def->watchdog->action = action; + } else if (STREQ(arg, "-bootloader")) { + WANT_VALUE(); + def->os.bootloader = strdup(val); + if (!def->os.bootloader) + goto no_memory; + } else if (STREQ(arg, "-vmwarevga")) { + video = VIR_DOMAIN_VIDEO_TYPE_VMVGA; + } else if (STREQ(arg, "-std-vga")) { + video = VIR_DOMAIN_VIDEO_TYPE_VGA; + } else if (STREQ(arg, "-vga")) { + WANT_VALUE(); + if (STRNEQ(val, "none")) { + video = qemuVideoTypeFromString(val); + if (video < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown video adapter type '%s'"), val); + goto error; + } + } + } else if (STREQ(arg, "-cpu")) { + WANT_VALUE(); + if (qemuParseCommandLineCPU(def, val) < 0) + goto error; + } else if (STREQ(arg, "-domid")) { + WANT_VALUE(); + /* ignore, generted on the fly */ + } else if (STREQ(arg, "-usb")) { + /* ignore, always added by libvirt */ + } else if (STREQ(arg, "-pidfile")) { + WANT_VALUE(); + /* ignore, used by libvirt as needed */ + } else if (STREQ(arg, "-incoming")) { + WANT_VALUE(); + /* ignore, used via restore/migrate APIs */ + } else if (STREQ(arg, "-monitor")) { + WANT_VALUE(); + /* ignore, used internally by libvirt */ + } else if (STREQ(arg, "-S")) { + /* ignore, always added by libvirt */ + } else { + /* something we can't yet parse. Add it to the qemu namespace + * cmdline/environment advanced options and hope for the best + */ + VIR_WARN("unknown QEMU argument '%s', adding to the qemu namespace", + arg); + if (VIR_REALLOC_N(cmd->args, cmd->num_args+1) < 0) + goto no_memory; + cmd->args[cmd->num_args] = strdup(arg); + if (cmd->args[cmd->num_args] == NULL) + goto no_memory; + cmd->num_args++; + } + } + +#undef WANT_VALUE + if (def->ndisks > 0) { + const char *ceph_args = qemuFindEnv(progenv, "CEPH_ARGS"); + if (ceph_args) { + char *hosts, *port, *saveptr, *token; + virDomainDiskDefPtr first_rbd_disk = NULL; + for (i = 0 ; i < def->ndisks ; i++) { + virDomainDiskDefPtr disk = def->disks[i]; + if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK && + disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_RBD) { + first_rbd_disk = disk; + break; + } + } + + if (!first_rbd_disk) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("CEPH_ARGS was set without an rbd disk")); + goto error; + } + + /* CEPH_ARGS should be: -m host1[:port1][,host2[:port2]]... */ + if (!STRPREFIX(ceph_args, "-m ")) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("could not parse CEPH_ARGS '%s'"), ceph_args); + goto error; + } + hosts = strdup(strchr(ceph_args, ' ') + 1); + if (!hosts) + goto no_memory; + first_rbd_disk->nhosts = 0; + token = strtok_r(hosts, ",", &saveptr); + while (token != NULL) { + if (VIR_REALLOC_N(first_rbd_disk->hosts, first_rbd_disk->nhosts + 1) < 0) { + VIR_FREE(hosts); + goto no_memory; + } + port = strchr(token, ':'); + if (port) { + *port++ = '\0'; + port = strdup(port); + if (!port) { + VIR_FREE(hosts); + goto no_memory; + } + } + first_rbd_disk->hosts[first_rbd_disk->nhosts].port = port; + first_rbd_disk->hosts[first_rbd_disk->nhosts].name = strdup(token); + if (!first_rbd_disk->hosts[first_rbd_disk->nhosts].name) { + VIR_FREE(hosts); + goto no_memory; + } + first_rbd_disk->nhosts++; + token = strtok_r(NULL, ",", &saveptr); + } + VIR_FREE(hosts); + + if (first_rbd_disk->nhosts == 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("found no rbd hosts in CEPH_ARGS '%s'"), ceph_args); + goto error; + } + } + } + + if (!nographics && def->ngraphics == 0) { + virDomainGraphicsDefPtr sdl; + const char *display = qemuFindEnv(progenv, "DISPLAY"); + const char *xauth = qemuFindEnv(progenv, "XAUTHORITY"); + if (VIR_ALLOC(sdl) < 0) + goto no_memory; + sdl->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL; + sdl->data.sdl.fullscreen = fullscreen; + if (display && + !(sdl->data.sdl.display = strdup(display))) { + VIR_FREE(sdl); + goto no_memory; + } + if (xauth && + !(sdl->data.sdl.xauth = strdup(xauth))) { + VIR_FREE(sdl); + goto no_memory; + } + + if (VIR_REALLOC_N(def->graphics, def->ngraphics+1) < 0) { + virDomainGraphicsDefFree(sdl); + goto no_memory; + } + def->graphics[def->ngraphics++] = sdl; + } + + if (def->ngraphics) { + virDomainVideoDefPtr vid; + if (VIR_ALLOC(vid) < 0) + goto no_memory; + if (def->virtType == VIR_DOMAIN_VIRT_XEN) + vid->type = VIR_DOMAIN_VIDEO_TYPE_XEN; + else + vid->type = video; + vid->vram = virDomainVideoDefaultRAM(def, vid->type); + vid->heads = 1; + + if (VIR_REALLOC_N(def->videos, def->nvideos+1) < 0) { + virDomainVideoDefFree(vid); + goto no_memory; + } + def->videos[def->nvideos++] = vid; + } + + /* + * having a balloon is the default, define one with type="none" to avoid it + */ + if (!def->memballoon) { + virDomainMemballoonDefPtr memballoon; + if (VIR_ALLOC(memballoon) < 0) + goto no_memory; + memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO; + + def->memballoon = memballoon; + } + + VIR_FREE(nics); + + if (!def->name) { + if (!(def->name = strdup("unnamed"))) + goto no_memory; + } + + if (virDomainDefAddImplicitControllers(def) < 0) + goto error; + + if (cmd->num_args || cmd->num_env) { + def->ns = caps->ns; + def->namespaceData = cmd; + } + else + VIR_FREE(cmd); + + return def; + +no_memory: + virReportOOMError(); +error: + VIR_FREE(cmd); + virDomainDefFree(def); + VIR_FREE(nics); + return NULL; +} + + +virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps, + const char *args) +{ + const char **progenv = NULL; + const char **progargv = NULL; + virDomainDefPtr def = NULL; + int i; + + if (qemuStringToArgvEnv(args, &progenv, &progargv) < 0) + goto cleanup; + + def = qemuParseCommandLine(caps, progenv, progargv); + +cleanup: + for (i = 0 ; progargv && progargv[i] ; i++) + VIR_FREE(progargv[i]); + VIR_FREE(progargv); + + for (i = 0 ; progenv && progenv[i] ; i++) + VIR_FREE(progenv[i]); + VIR_FREE(progenv); + + return def; +} diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h new file mode 100644 index 0000000..da35d3b --- /dev/null +++ b/src/qemu/qemu_command.h @@ -0,0 +1,165 @@ +/* + * qemu_command.h: QEMU command generation + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __QEMU_COMMAND_H__ +# define __QEMU_COMMAND_H__ + +#include "domain_conf.h" +#include "command.h" +#include "capabilities.h" +#include "qemu_conf.h" + +/* Config type for XML import/export conversions */ +# define QEMU_CONFIG_FORMAT_ARGV "qemu-argv" + +# define QEMU_DRIVE_HOST_PREFIX "drive-" +# define QEMU_VIRTIO_SERIAL_PREFIX "virtio-serial" +# define QEMU_FSDEV_HOST_PREFIX "fsdev-" + +virCommandPtr qemuBuildCommandLine(virConnectPtr conn, + struct qemud_driver *driver, + virDomainDefPtr def, + virDomainChrDefPtr monitor_chr, + bool monitor_json, + unsigned long long qemuCmdFlags, + const char *migrateFrom, + virDomainSnapshotObjPtr current_snapshot, + enum virVMOperationType vmop) + ATTRIBUTE_NONNULL(1); + +/* With vlan == -1, use netdev syntax, else old hostnet */ +char * qemuBuildHostNetStr(virDomainNetDefPtr net, + char type_sep, + int vlan, + const char *tapfd, + const char *vhostfd); + +/* Legacy, pre device support */ +char * qemuBuildNicStr(virDomainNetDefPtr net, + const char *prefix, + int vlan); + +/* Current, best practice */ +char * qemuBuildNicDevStr(virDomainNetDefPtr net, + int vlan); + +char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags); + +/* Both legacy & current support */ +char *qemuBuildDriveStr(virDomainDiskDefPtr disk, + int bootable, + unsigned long long qemuCmdFlags); +char *qemuBuildFSStr(virDomainFSDefPtr fs, + unsigned long long qemuCmdFlags); + +/* Current, best practice */ +char * qemuBuildDriveDevStr(virDomainDiskDefPtr disk); +char * qemuBuildFSDevStr(virDomainFSDefPtr fs); +/* Current, best practice */ +char * qemuBuildControllerDevStr(virDomainControllerDefPtr def); + +char * qemuBuildWatchdogDevStr(virDomainWatchdogDefPtr dev); + +char * qemuBuildMemballoonDevStr(virDomainMemballoonDefPtr dev); + +char * qemuBuildUSBInputDevStr(virDomainInputDefPtr dev); + +char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound); + +/* Legacy, pre device support */ +char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev); +/* Current, best practice */ +char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, + const char *configfd); + +int qemuOpenPCIConfig(virDomainHostdevDefPtr dev); + +/* Current, best practice */ +char * qemuBuildChrChardevStr(virDomainChrDefPtr dev); +/* Legacy, pre device support */ +char * qemuBuildChrArgStr(virDomainChrDefPtr dev, const char *prefix); + +char * qemuBuildVirtioSerialPortDevStr(virDomainChrDefPtr dev); + +/* Legacy, pre device support */ +char * qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev); +/* Current, best practice */ +char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev); + + + +int qemuNetworkIfaceConnect(virConnectPtr conn, + struct qemud_driver *driver, + virDomainNetDefPtr net, + unsigned long long qemCmdFlags) + ATTRIBUTE_NONNULL(1); + +int qemuOpenVhostNet(virDomainNetDefPtr net, + unsigned long long qemuCmdFlags); + +int qemuPhysIfaceConnect(virConnectPtr conn, + struct qemud_driver *driver, + virDomainNetDefPtr net, + unsigned long long qemuCmdFlags, + const unsigned char *vmuuid, + enum virVMOperationType vmop); + +int qemudCanonicalizeMachine(struct qemud_driver *driver, + virDomainDefPtr def); + +virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, + const char **progenv, + const char **progargv); +virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps, + const char *args); + +qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def); +int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs, + int slot); +int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs, + virDomainDeviceInfoPtr dev); +int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs, + virDomainDeviceInfoPtr dev); +int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs, + virDomainDeviceInfoPtr dev); +int qemuDomainPCIAddressReleaseAddr(qemuDomainPCIAddressSetPtr addrs, + virDomainDeviceInfoPtr dev); + +void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs); +int qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs); + +int qemuDomainNetVLAN(virDomainNetDefPtr def); +int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx); +int qemuAssignDeviceDiskAlias(virDomainDiskDefPtr def, unsigned long long qemuCmdFlags); +int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr net, int idx); +int qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller); + +int +qemuParseKeywords(const char *str, + char ***retkeywords, + char ***retvalues, + int allowEmptyValue); + + +#endif /* __QEMU_COMMAND_H__*/ diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 0c9c676..e600a5b 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -61,43 +61,6 @@ #define VIR_FROM_THIS VIR_FROM_QEMU -VIR_ENUM_DECL(virDomainDiskQEMUBus) -VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_DOMAIN_DISK_BUS_LAST, - "ide", - "floppy", - "scsi", - "virtio", - "xen", - "usb", - "uml", - "sata") - - -VIR_ENUM_DECL(qemuDiskCacheV1) -VIR_ENUM_DECL(qemuDiskCacheV2) - -VIR_ENUM_IMPL(qemuDiskCacheV1, VIR_DOMAIN_DISK_CACHE_LAST, - "default", - "off", - "off", /* writethrough not supported, so for safety, disable */ - "on"); /* Old 'on' was equivalent to 'writeback' */ - -VIR_ENUM_IMPL(qemuDiskCacheV2, VIR_DOMAIN_DISK_CACHE_LAST, - "default", - "none", - "writethrough", - "writeback"); - -VIR_ENUM_DECL(qemuVideo) - -VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, - "std", - "cirrus", - "vmware", - "", /* no arg needed for xen */ - "", /* don't support vbox */ - "qxl"); - int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename) { virConfPtr conf; @@ -452,5714 +415,3 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, } -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'; -} - - -/** - * qemudPhysIfaceConnect: - * @conn: pointer to virConnect object - * @driver: pointer to the qemud_driver - * @net: pointer to he VM's interface description with direct device type - * @qemuCmdFlags: flags for qemu - * @vmuuid: The UUID of the VM (needed by 802.1Qbh) - * - * Returns a filedescriptor on success or -1 in case of error. - */ -int -qemudPhysIfaceConnect(virConnectPtr conn, - struct qemud_driver *driver, - virDomainNetDefPtr net, - unsigned long long qemuCmdFlags, - const unsigned char *vmuuid, - enum virVMOperationType vmop) -{ - int rc; -#if WITH_MACVTAP - char *res_ifname = NULL; - int vnet_hdr = 0; - int err; - - if (qemuCmdFlags & QEMUD_CMD_FLAG_VNET_HDR && - net->model && STREQ(net->model, "virtio")) - vnet_hdr = 1; - - rc = openMacvtapTap(net->ifname, net->mac, net->data.direct.linkdev, - net->data.direct.mode, vnet_hdr, vmuuid, - &net->data.direct.virtPortProfile, &res_ifname, - vmop); - if (rc >= 0) { - VIR_FREE(net->ifname); - net->ifname = res_ifname; - } - - if (rc >=0 && driver->macFilter) { - if ((err = networkAllowMacOnPort(driver, net->ifname, net->mac))) { - virReportSystemError(err, - _("failed to add ebtables rule to allow MAC address on '%s'"), - net->ifname); - } - } - - if (rc >= 0) { - if ((net->filter) && (net->ifname)) { - err = virDomainConfNWFilterInstantiate(conn, net); - if (err) { - VIR_FORCE_CLOSE(rc); - delMacvtap(net->ifname, net->mac, net->data.direct.linkdev, - &net->data.direct.virtPortProfile); - VIR_FREE(net->ifname); - } - } - } -#else - (void)conn; - (void)net; - (void)qemuCmdFlags; - (void)driver; - (void)vmuuid; - (void)vmop; - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("No support for macvtap device")); - rc = -1; -#endif - return rc; -} - - -int -qemudNetworkIfaceConnect(virConnectPtr conn, - struct qemud_driver *driver, - virDomainNetDefPtr net, - unsigned long long qemuCmdFlags) -{ - char *brname = NULL; - int err; - int tapfd = -1; - int vnet_hdr = 0; - int template_ifname = 0; - unsigned char tapmac[VIR_MAC_BUFLEN]; - - if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { - int active, fail = 0; - virErrorPtr errobj; - virNetworkPtr network = virNetworkLookupByName(conn, - net->data.network.name); - if (!network) - return -1; - - active = virNetworkIsActive(network); - if (active != 1) { - fail = 1; - - if (active == 0) - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Network '%s' is not active."), - net->data.network.name); - } - - if (!fail) { - brname = virNetworkGetBridgeName(network); - if (brname == NULL) - fail = 1; - } - - /* Make sure any above failure is preserved */ - errobj = virSaveLastError(); - virNetworkFree(network); - virSetError(errobj); - virFreeError(errobj); - - if (fail) - return -1; - - } else if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { - if (!(brname = strdup(net->data.bridge.brname))) { - virReportOOMError(); - return -1; - } - } else { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Network type %d is not supported"), net->type); - return -1; - } - - if (!driver->brctl && (err = brInit(&driver->brctl))) { - virReportSystemError(err, "%s", - _("cannot initialize bridge support")); - goto cleanup; - } - - if (!net->ifname || - STRPREFIX(net->ifname, "vnet") || - strchr(net->ifname, '%')) { - VIR_FREE(net->ifname); - if (!(net->ifname = strdup("vnet%d"))) { - virReportOOMError(); - goto cleanup; - } - /* avoid exposing vnet%d in dumpxml or error outputs */ - template_ifname = 1; - } - - if (qemuCmdFlags & QEMUD_CMD_FLAG_VNET_HDR && - net->model && STREQ(net->model, "virtio")) - vnet_hdr = 1; - - memcpy(tapmac, net->mac, VIR_MAC_BUFLEN); - tapmac[0] = 0xFE; /* Discourage bridge from using TAP dev MAC */ - if ((err = brAddTap(driver->brctl, - brname, - &net->ifname, - tapmac, - vnet_hdr, - &tapfd))) { - if (err == ENOTSUP) { - /* In this particular case, give a better diagnostic. */ - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Failed to add tap interface to bridge. " - "%s is not a bridge device"), brname); - } else if (err == ENOENT) { - /* When the tun drive is missing, give a better message. */ - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Failed to add tap interface to bridge. " - "Your kernel is missing the 'tun' module or " - "CONFIG_TUN, or you need to add the " - "/dev/net/tun device node.")); - } else if (template_ifname) { - virReportSystemError(err, - _("Failed to add tap interface to bridge '%s'"), - brname); - } else { - virReportSystemError(err, - _("Failed to add tap interface '%s' to bridge '%s'"), - net->ifname, brname); - } - if (template_ifname) - VIR_FREE(net->ifname); - tapfd = -1; - } - - if (driver->macFilter) { - if ((err = networkAllowMacOnPort(driver, net->ifname, net->mac))) { - virReportSystemError(err, - _("failed to add ebtables rule to allow MAC address on '%s'"), - net->ifname); - } - } - - if (tapfd >= 0) { - if ((net->filter) && (net->ifname)) { - err = virDomainConfNWFilterInstantiate(conn, net); - if (err) - VIR_FORCE_CLOSE(tapfd); - } - } - -cleanup: - VIR_FREE(brname); - - return tapfd; -} - - -int -qemudOpenVhostNet(virDomainNetDefPtr net, - unsigned long long qemuCmdFlags) -{ - - /* If qemu supports vhost-net mode (including the -netdev command - * option), the nic model is virtio, and we can open - * /dev/vhost_net, assume that vhost-net mode is available and - * return the fd to /dev/vhost_net. Otherwise, return -1. - */ - - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_VNET_HOST && - qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV && - qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE && - net->model && STREQ(net->model, "virtio"))) - return -1; - - return open("/dev/vhost-net", O_RDWR, 0); -} - - -static int qemuDomainDeviceAliasIndex(virDomainDeviceInfoPtr info, - const char *prefix) -{ - int idx; - - if (!info->alias) - return -1; - if (!STRPREFIX(info->alias, prefix)) - return -1; - - if (virStrToLong_i(info->alias + strlen(prefix), NULL, 10, &idx) < 0) - return -1; - - return idx; -} - - -int qemuDomainNetVLAN(virDomainNetDefPtr def) -{ - return qemuDomainDeviceAliasIndex(&def->info, "net"); -} - - -/* Names used before -drive existed */ -static int qemuAssignDeviceDiskAliasLegacy(virDomainDiskDefPtr disk) -{ - char *devname; - - if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM && - STREQ(disk->dst, "hdc")) - devname = strdup("cdrom"); - else - devname = strdup(disk->dst); - - if (!devname) { - virReportOOMError(); - return -1; - } - - disk->info.alias = devname; - return 0; -} - - -char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, - unsigned long long qemudCmdFlags) -{ - char *ret; - - if (qemudCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (virAsprintf(&ret, "%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias) < 0) { - virReportOOMError(); - return NULL; - } - } else { - if (!(ret = strdup(disk->info.alias))) { - virReportOOMError(); - return NULL; - } - } - return ret; -} - - -/* Names used before -drive supported the id= option */ -static int qemuAssignDeviceDiskAliasFixed(virDomainDiskDefPtr disk) -{ - int busid, devid; - int ret; - char *devname; - - if (virDiskNameToBusDeviceIndex(disk, &busid, &devid) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot convert disk '%s' to bus/device index"), - disk->dst); - return -1; - } - - switch (disk->bus) { - case VIR_DOMAIN_DISK_BUS_IDE: - if (disk->device== VIR_DOMAIN_DISK_DEVICE_DISK) - ret = virAsprintf(&devname, "ide%d-hd%d", busid, devid); - else - ret = virAsprintf(&devname, "ide%d-cd%d", busid, devid); - break; - case VIR_DOMAIN_DISK_BUS_SCSI: - if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) - ret = virAsprintf(&devname, "scsi%d-hd%d", busid, devid); - else - ret = virAsprintf(&devname, "scsi%d-cd%d", busid, devid); - break; - case VIR_DOMAIN_DISK_BUS_FDC: - ret = virAsprintf(&devname, "floppy%d", devid); - break; - case VIR_DOMAIN_DISK_BUS_VIRTIO: - ret = virAsprintf(&devname, "virtio%d", devid); - break; - case VIR_DOMAIN_DISK_BUS_XEN: - ret = virAsprintf(&devname, "xenblk%d", devid); - break; - default: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Unsupported disk name mapping for bus '%s'"), - virDomainDiskBusTypeToString(disk->bus)); - return -1; - } - - if (ret == -1) { - virReportOOMError(); - return -1; - } - - disk->info.alias = devname; - - return 0; -} - - -/* Our custom -drive naming scheme used with id= */ -static int qemuAssignDeviceDiskAliasCustom(virDomainDiskDefPtr disk) -{ - const char *prefix = virDomainDiskBusTypeToString(disk->bus); - if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { - if (virAsprintf(&disk->info.alias, "%s%d-%d-%d", prefix, - disk->info.addr.drive.controller, - disk->info.addr.drive.bus, - disk->info.addr.drive.unit) < 0) - goto no_memory; - } else { - int idx = virDiskNameToIndex(disk->dst); - if (virAsprintf(&disk->info.alias, "%s-disk%d", prefix, idx) < 0) - goto no_memory; - } - - return 0; - -no_memory: - virReportOOMError(); - return -1; -} - - -int -qemuAssignDeviceDiskAlias(virDomainDiskDefPtr def, unsigned long long qemuCmdFlags) -{ - if (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE) { - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) - return qemuAssignDeviceDiskAliasCustom(def); - else - return qemuAssignDeviceDiskAliasFixed(def); - } else { - return qemuAssignDeviceDiskAliasLegacy(def); - } -} - - -int -qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx) -{ - if (idx == -1) { - int i; - idx = 0; - for (i = 0 ; i < def->nnets ; i++) { - int thisidx; - if ((thisidx = qemuDomainDeviceAliasIndex(&def->nets[i]->info, "net")) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Unable to determine device index for network device")); - return -1; - } - if (thisidx >= idx) - idx = thisidx + 1; - } - } - - if (virAsprintf(&net->info.alias, "net%d", idx) < 0) { - virReportOOMError(); - return -1; - } - - return 0; -} - - -int -qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int idx) -{ - if (idx == -1) { - int i; - idx = 0; - for (i = 0 ; i < def->nhostdevs ; i++) { - int thisidx; - if ((thisidx = qemuDomainDeviceAliasIndex(&def->hostdevs[i]->info, "hostdev")) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Unable to determine device index for hostdevwork device")); - return -1; - } - if (thisidx >= idx) - idx = thisidx + 1; - } - } - - if (virAsprintf(&hostdev->info.alias, "hostdev%d", idx) < 0) { - virReportOOMError(); - return -1; - } - - return 0; -} - - -int -qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller) -{ - const char *prefix = virDomainControllerTypeToString(controller->type); - - if (virAsprintf(&controller->info.alias, "%s%d", prefix, - controller->idx) < 0) { - virReportOOMError(); - return -1; - } - - return 0; -} - - -static int -qemuAssignDeviceAliases(virDomainDefPtr def, unsigned long long qemuCmdFlags) -{ - int i; - - for (i = 0; i < def->ndisks ; i++) { - if (qemuAssignDeviceDiskAlias(def->disks[i], qemuCmdFlags) < 0) - return -1; - } - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NET_NAME) || - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - for (i = 0; i < def->nnets ; i++) { - if (qemuAssignDeviceNetAlias(def, def->nets[i], i) < 0) - return -1; - } - } - - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) - return 0; - - for (i = 0; i < def->nfss ; i++) { - if (virAsprintf(&def->fss[i]->info.alias, "fs%d", i) < 0) - goto no_memory; - } - for (i = 0; i < def->nsounds ; i++) { - if (virAsprintf(&def->sounds[i]->info.alias, "sound%d", i) < 0) - goto no_memory; - } - for (i = 0; i < def->nhostdevs ; i++) { - if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0) - return -1; - } - for (i = 0; i < def->nvideos ; i++) { - if (virAsprintf(&def->videos[i]->info.alias, "video%d", i) < 0) - goto no_memory; - } - for (i = 0; i < def->ncontrollers ; i++) { - if (qemuAssignDeviceControllerAlias(def->controllers[i]) < 0) - return -1; - } - for (i = 0; i < def->ninputs ; i++) { - if (virAsprintf(&def->inputs[i]->info.alias, "input%d", i) < 0) - goto no_memory; - } - for (i = 0; i < def->nparallels ; i++) { - if (virAsprintf(&def->parallels[i]->info.alias, "parallel%d", i) < 0) - goto no_memory; - } - for (i = 0; i < def->nserials ; i++) { - if (virAsprintf(&def->serials[i]->info.alias, "serial%d", i) < 0) - goto no_memory; - } - for (i = 0; i < def->nchannels ; i++) { - if (virAsprintf(&def->channels[i]->info.alias, "channel%d", i) < 0) - goto no_memory; - } - if (def->console) { - if (virAsprintf(&def->console->info.alias, "console%d", i) < 0) - goto no_memory; - } - if (def->watchdog) { - if (virAsprintf(&def->watchdog->info.alias, "watchdog%d", 0) < 0) - goto no_memory; - } - if (def->memballoon) { - if (virAsprintf(&def->memballoon->info.alias, "balloon%d", 0) < 0) - goto no_memory; - } - - return 0; - - no_memory: - virReportOOMError(); - return -1; -} - - -#define QEMU_PCI_ADDRESS_LAST_SLOT 31 -struct _qemuDomainPCIAddressSet { - virHashTablePtr used; - int nextslot; -}; - - -static char *qemuPCIAddressAsString(virDomainDeviceInfoPtr dev) -{ - char *addr; - - if (dev->addr.pci.domain != 0 || - dev->addr.pci.bus != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Only PCI domain 0 and bus 0 are available")); - return NULL; - } - - if (virAsprintf(&addr, "%d:%d:%d", - dev->addr.pci.domain, - dev->addr.pci.bus, - dev->addr.pci.slot) < 0) { - virReportOOMError(); - return NULL; - } - return addr; -} - - -static int qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED, - virDomainDeviceInfoPtr dev, - void *opaque) -{ - qemuDomainPCIAddressSetPtr addrs = opaque; - - if (dev->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - char *addr = qemuPCIAddressAsString(dev); - if (!addr) - return -1; - - VIR_DEBUG("Remembering PCI addr %s", addr); - - if (virHashAddEntry(addrs->used, addr, addr) < 0) { - VIR_FREE(addr); - return -1; - } - } - - return 0; -} - - -qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def) -{ - qemuDomainPCIAddressSetPtr addrs; - - if (VIR_ALLOC(addrs) < 0) - goto no_memory; - - if (!(addrs->used = virHashCreate(10))) - goto no_memory; - - if (virDomainDeviceInfoIterate(def, qemuCollectPCIAddress, addrs) < 0) - goto error; - - return addrs; - -no_memory: - virReportOOMError(); -error: - qemuDomainPCIAddressSetFree(addrs); - return NULL; -} - -int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs, - virDomainDeviceInfoPtr dev) -{ - char *addr; - - addr = qemuPCIAddressAsString(dev); - if (!addr) - return -1; - - VIR_DEBUG("Reserving PCI addr %s", addr); - - if (virHashLookup(addrs->used, addr)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unable to reserve PCI address %s"), addr); - VIR_FREE(addr); - return -1; - } - - if (virHashAddEntry(addrs->used, addr, addr)) { - VIR_FREE(addr); - return -1; - } - - if (dev->addr.pci.slot > addrs->nextslot) { - addrs->nextslot = dev->addr.pci.slot + 1; - if (QEMU_PCI_ADDRESS_LAST_SLOT < addrs->nextslot) - addrs->nextslot = 0; - } - - return 0; -} - -int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs, - int slot) -{ - virDomainDeviceInfo dev; - - dev.addr.pci.domain = 0; - dev.addr.pci.bus = 0; - dev.addr.pci.slot = slot; - - return qemuDomainPCIAddressReserveAddr(addrs, &dev); -} - - -int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs, - virDomainDeviceInfoPtr dev) -{ - int ret = 0; - if (dev->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) - ret = qemuDomainPCIAddressReserveAddr(addrs, dev); - else - ret = qemuDomainPCIAddressSetNextAddr(addrs, dev); - return ret; -} - -static void qemuDomainPCIAddressSetFreeEntry(void *payload, const char *name ATTRIBUTE_UNUSED) -{ - VIR_FREE(payload); -} - - -int qemuDomainPCIAddressReleaseAddr(qemuDomainPCIAddressSetPtr addrs, - virDomainDeviceInfoPtr dev) -{ - char *addr; - int ret; - - addr = qemuPCIAddressAsString(dev); - if (!addr) - return -1; - - ret = virHashRemoveEntry(addrs->used, addr, qemuDomainPCIAddressSetFreeEntry); - - VIR_FREE(addr); - - return ret; -} - - -void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs) -{ - if (!addrs) - return; - - virHashFree(addrs->used, qemuDomainPCIAddressSetFreeEntry); - VIR_FREE(addrs); -} - - -int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs, - virDomainDeviceInfoPtr dev) -{ - int i; - int iteration; - - for (i = addrs->nextslot, iteration = 0; - iteration <= QEMU_PCI_ADDRESS_LAST_SLOT; i++, iteration++) { - virDomainDeviceInfo maybe; - char *addr; - - if (QEMU_PCI_ADDRESS_LAST_SLOT < i) - i = 0; - memset(&maybe, 0, sizeof(maybe)); - maybe.addr.pci.domain = 0; - maybe.addr.pci.bus = 0; - maybe.addr.pci.slot = i; - - if (!(addr = qemuPCIAddressAsString(&maybe))) - return -1; - - if (virHashLookup(addrs->used, addr)) { - VIR_DEBUG("PCI addr %s already in use", addr); - VIR_FREE(addr); - continue; - } - - VIR_DEBUG("Allocating PCI addr %s", addr); - - if (virHashAddEntry(addrs->used, addr, addr) < 0) { - VIR_FREE(addr); - return -1; - } - - dev->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - dev->addr.pci = maybe.addr.pci; - - addrs->nextslot = i + 1; - if (QEMU_PCI_ADDRESS_LAST_SLOT < addrs->nextslot) - addrs->nextslot = 0; - - return 0; - } - - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("No more available PCI addresses")); - return -1; -} - -/* - * This assigns static PCI slots to all configured devices. - * The ordering here is chosen to match the ordering used - * with old QEMU < 0.12, so that if a user updates a QEMU - * host from old QEMU to QEMU >= 0.12, their guests should - * get PCI addresses in the same order as before. - * - * NB, if they previously hotplugged devices then all bets - * are off. Hotplug for old QEMU was unfixably broken wrt - * to stable PCI addressing. - * - * Order is: - * - * - Host bridge (slot 0) - * - PIIX3 ISA bridge, IDE controller, something else unknown, USB controller (slot 1) - * - Video (slot 2) - * - * Incrementally assign slots from 3 onwards: - * - * - Net - * - Sound - * - SCSI controllers - * - VirtIO block - * - VirtIO balloon - * - Host device passthrough - * - Watchdog - * - * Prior to this function being invoked, qemuCollectPCIAddress() will have - * added all existing PCI addresses from the 'def' to 'addrs'. Thus this - * function must only try to reserve addresses if info.type == NONE and - * skip over info.type == PCI - */ -int -qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs) -{ - int i; - bool reservedIDE = false; - - /* Host bridge */ - if (qemuDomainPCIAddressReserveSlot(addrs, 0) < 0) - goto error; - - /* Verify that first IDE controller (if any) is on the PIIX3, fn 1 */ - for (i = 0; i < def->ncontrollers ; i++) { - /* First IDE controller lives on the PIIX3 at slot=1, function=1 */ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && - def->controllers[i]->idx == 0) { - if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - if (def->controllers[i]->info.addr.pci.domain != 0 || - def->controllers[i]->info.addr.pci.bus != 0 || - def->controllers[i]->info.addr.pci.slot != 1 || - def->controllers[i]->info.addr.pci.function != 1) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Primary IDE controller must have PCI address 0:0:1.1")); - goto error; - } - /* If TYPE==PCI, then then qemuCollectPCIAddress() function - * has already reserved the address, so we must skip */ - reservedIDE = true; - } else { - def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - def->controllers[i]->info.addr.pci.domain = 0; - def->controllers[i]->info.addr.pci.bus = 0; - def->controllers[i]->info.addr.pci.slot = 1; - def->controllers[i]->info.addr.pci.function = 1; - } - } - } - - /* PIIX3 (ISA bridge, IDE controller, something else unknown, USB controller) - * hardcoded slot=1, multifunction device - */ - if (!reservedIDE && - qemuDomainPCIAddressReserveSlot(addrs, 1) < 0) - goto error; - - /* First VGA is hardcoded slot=2 */ - if (def->nvideos > 0) { - if (def->videos[0]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - if (def->videos[0]->info.addr.pci.domain != 0 || - def->videos[0]->info.addr.pci.bus != 0 || - def->videos[0]->info.addr.pci.slot != 2 || - def->videos[0]->info.addr.pci.function != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Primary video card must have PCI address 0:0:2.0")); - goto error; - } - } else { - def->videos[0]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - def->videos[0]->info.addr.pci.domain = 0; - def->videos[0]->info.addr.pci.bus = 0; - def->videos[0]->info.addr.pci.slot = 2; - def->videos[0]->info.addr.pci.function = 0; - if (qemuDomainPCIAddressReserveSlot(addrs, 2) < 0) - goto error; - } - } - for (i = 0; i < def->nfss ; i++) { - if (def->fss[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - - /* Only support VirtIO-9p-pci so far. If that changes, - * we might need to skip devices here */ - if (qemuDomainPCIAddressSetNextAddr(addrs, &def->fss[i]->info) < 0) - goto error; - } - - /* Network interfaces */ - for (i = 0; i < def->nnets ; i++) { - if (def->nets[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - if (qemuDomainPCIAddressSetNextAddr(addrs, &def->nets[i]->info) < 0) - goto error; - } - - /* Sound cards */ - for (i = 0; i < def->nsounds ; i++) { - if (def->sounds[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - /* Skip ISA sound card, and PCSPK */ - if (def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_SB16 || - def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_PCSPK) - continue; - - if (qemuDomainPCIAddressSetNextAddr(addrs, &def->sounds[i]->info) < 0) - goto error; - } - - /* Disk controllers (SCSI only for now) */ - for (i = 0; i < def->ncontrollers ; i++) { - /* FDC lives behind the ISA bridge */ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC) - continue; - - /* First IDE controller lives on the PIIX3 at slot=1, function=1, - dealt with earlier on*/ - if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && - def->controllers[i]->idx == 0) - continue; - - if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - if (qemuDomainPCIAddressSetNextAddr(addrs, &def->controllers[i]->info) < 0) - goto error; - } - - /* Disks (VirtIO only for now */ - for (i = 0; i < def->ndisks ; i++) { - if (def->disks[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - - /* Only VirtIO disks use PCI addrs */ - if (def->disks[i]->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) - continue; - - if (qemuDomainPCIAddressSetNextAddr(addrs, &def->disks[i]->info) < 0) - goto error; - } - - /* Host PCI devices */ - for (i = 0; i < def->nhostdevs ; i++) { - if (def->hostdevs[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - if (def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || - def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) - continue; - - if (qemuDomainPCIAddressSetNextAddr(addrs, &def->hostdevs[i]->info) < 0) - goto error; - } - - /* VirtIO balloon */ - if (def->memballoon && - def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO && - def->memballoon->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - if (qemuDomainPCIAddressSetNextAddr(addrs, &def->memballoon->info) < 0) - goto error; - } - - /* A watchdog */ - if (def->watchdog && - def->watchdog->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - if (qemuDomainPCIAddressSetNextAddr(addrs, &def->watchdog->info) < 0) - goto error; - } - - /* Further non-primary video cards */ - for (i = 1; i < def->nvideos ; i++) { - if (def->videos[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) - continue; - if (qemuDomainPCIAddressSetNextAddr(addrs, &def->videos[i]->info) < 0) - goto error; - } - for (i = 0; i < def->ninputs ; i++) { - /* Nada - none are PCI based (yet) */ - } - for (i = 0; i < def->nparallels ; i++) { - /* Nada - none are PCI based (yet) */ - } - for (i = 0; i < def->nserials ; i++) { - /* Nada - none are PCI based (yet) */ - } - for (i = 0; i < def->nchannels ; i++) { - /* Nada - none are PCI based (yet) */ - } - - return 0; - -error: - return -1; -} - - -static int -qemuBuildDeviceAddressStr(virBufferPtr buf, - virDomainDeviceInfoPtr info) -{ - if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - if (info->addr.pci.domain != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Only PCI device addresses with domain=0 are supported")); - return -1; - } - if (info->addr.pci.bus != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Only PCI device addresses with bus=0 are supported")); - return -1; - } - if (info->addr.pci.function != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Only PCI device addresses with function=0 are supported")); - return -1; - } - - /* XXX - * When QEMU grows support for > 1 PCI bus, then pci.0 changes - * to pci.1, pci.2, etc - * When QEMU grows support for > 1 PCI domain, then pci.0 change - * to pciNN.0 where NN is the domain number - */ - virBufferVSprintf(buf, ",bus=pci.0,addr=0x%x", info->addr.pci.slot); - } - return 0; -} - - -#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \ - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" - -static int -qemuSafeSerialParamValue(const char *value) -{ - if (strspn(value, QEMU_SERIAL_PARAM_ACCEPTED_CHARS) != strlen (value)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("driver serial '%s' contains unsafe characters"), - value); - return -1; - } - - return 0; -} - - -char * -qemuBuildDriveStr(virDomainDiskDefPtr disk, - int bootable, - unsigned long long qemuCmdFlags) -{ - virBuffer opt = VIR_BUFFER_INITIALIZER; - const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); - int idx = virDiskNameToIndex(disk->dst); - int busid = -1, unitid = -1; - - if (idx < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk type '%s'"), disk->dst); - goto error; - } - - switch (disk->bus) { - case VIR_DOMAIN_DISK_BUS_SCSI: - if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unexpected address type for scsi disk")); - goto error; - } - - /* Setting bus= attr for SCSI drives, causes a controller - * to be created. Yes this is slightly odd. It is not possible - * to have > 1 bus on a SCSI controller (yet). */ - if (disk->info.addr.drive.bus != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("SCSI controller only supports 1 bus")); - goto error; - } - busid = disk->info.addr.drive.controller; - unitid = disk->info.addr.drive.unit; - break; - - case VIR_DOMAIN_DISK_BUS_IDE: - if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unexpected address type for ide disk")); - goto error; - } - /* We can only have 1 IDE controller (currently) */ - if (disk->info.addr.drive.controller != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Only 1 %s controller is supported"), bus); - goto error; - } - busid = disk->info.addr.drive.bus; - unitid = disk->info.addr.drive.unit; - break; - - case VIR_DOMAIN_DISK_BUS_FDC: - if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unexpected address type for fdc disk")); - goto error; - } - /* We can only have 1 FDC controller (currently) */ - if (disk->info.addr.drive.controller != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Only 1 %s controller is supported"), bus); - goto error; - } - /* We can only have 1 FDC bus (currently) */ - if (disk->info.addr.drive.bus != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Only 1 %s bus is supported"), bus); - goto error; - } - unitid = disk->info.addr.drive.unit; - - break; - - case VIR_DOMAIN_DISK_BUS_VIRTIO: - /* Each virtio drive is a separate PCI device, no unit/busid or index */ - idx = -1; - break; - - case VIR_DOMAIN_DISK_BUS_XEN: - /* Xen has no address type currently, so assign based on index */ - break; - } - - /* disk->src is NULL when we use nbd disks */ - if (disk->src || (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK && - disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_NBD)) { - if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { - /* QEMU only supports magic FAT format for now */ - if (disk->driverType && - STRNEQ(disk->driverType, "fat")) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk driver type for '%s'"), - disk->driverType); - goto error; - } - if (!disk->readonly) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot create virtual FAT disks in read-write mode")); - goto error; - } - if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) - virBufferVSprintf(&opt, "file=fat:floppy:%s,", disk->src); - else - virBufferVSprintf(&opt, "file=fat:%s,", disk->src); - } else if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { - switch (disk->protocol) { - case VIR_DOMAIN_DISK_PROTOCOL_NBD: - if (disk->nhosts != 1) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("NBD accepts only one host")); - goto error; - } - virBufferVSprintf(&opt, "file=nbd:%s:%s,", - disk->hosts->name, disk->hosts->port); - break; - case VIR_DOMAIN_DISK_PROTOCOL_RBD: - /* TODO: set monitor hostnames */ - virBufferVSprintf(&opt, "file=rbd:%s,", disk->src); - break; - case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: - if (disk->nhosts == 0) - virBufferVSprintf(&opt, "file=sheepdog:%s,", disk->src); - else - /* only one host is supported now */ - virBufferVSprintf(&opt, "file=sheepdog:%s:%s:%s,", - disk->hosts->name, disk->hosts->port, - disk->src); - break; - } - } else { - virBufferVSprintf(&opt, "file=%s,", disk->src); - } - } - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) - virBufferAddLit(&opt, "if=none"); - else - virBufferVSprintf(&opt, "if=%s", bus); - - if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) - virBufferAddLit(&opt, ",media=cdrom"); - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - virBufferVSprintf(&opt, ",id=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias); - } else { - if (busid == -1 && unitid == -1) { - if (idx != -1) - virBufferVSprintf(&opt, ",index=%d", idx); - } else { - if (busid != -1) - virBufferVSprintf(&opt, ",bus=%d", busid); - if (unitid != -1) - virBufferVSprintf(&opt, ",unit=%d", unitid); - } - } - if (bootable && - disk->device == VIR_DOMAIN_DISK_DEVICE_DISK && - disk->bus != VIR_DOMAIN_DISK_BUS_IDE) - virBufferAddLit(&opt, ",boot=on"); - if (disk->readonly && - qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_READONLY) - virBufferAddLit(&opt, ",readonly=on"); - if (disk->driverType && *disk->driverType != '\0' && - disk->type != VIR_DOMAIN_DISK_TYPE_DIR && - qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_FORMAT) - virBufferVSprintf(&opt, ",format=%s", disk->driverType); - if (disk->serial && - (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_SERIAL)) { - if (qemuSafeSerialParamValue(disk->serial) < 0) - goto error; - virBufferVSprintf(&opt, ",serial=%s", disk->serial); - } - - if (disk->cachemode) { - const char *mode = - (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_CACHE_V2) ? - qemuDiskCacheV2TypeToString(disk->cachemode) : - qemuDiskCacheV1TypeToString(disk->cachemode); - - virBufferVSprintf(&opt, ",cache=%s", mode); - } else if (disk->shared && !disk->readonly) { - virBufferAddLit(&opt, ",cache=off"); - } - - if (qemuCmdFlags & QEMUD_CMD_FLAG_MONITOR_JSON) { - if (disk->error_policy) { - virBufferVSprintf(&opt, ",werror=%s,rerror=%s", - virDomainDiskErrorPolicyTypeToString(disk->error_policy), - virDomainDiskErrorPolicyTypeToString(disk->error_policy)); - } - } - - if (virBufferError(&opt)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&opt); - -error: - virBufferFreeAndReset(&opt); - return NULL; -} - - -char * -qemuBuildDriveDevStr(virDomainDiskDefPtr disk) -{ - virBuffer opt = VIR_BUFFER_INITIALIZER; - const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus); - int idx = virDiskNameToIndex(disk->dst); - - if (idx < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk type '%s'"), disk->dst); - goto error; - } - - switch (disk->bus) { - case VIR_DOMAIN_DISK_BUS_IDE: - virBufferAddLit(&opt, "ide-drive"); - virBufferVSprintf(&opt, ",bus=ide.%d,unit=%d", - disk->info.addr.drive.bus, - disk->info.addr.drive.unit); - break; - case VIR_DOMAIN_DISK_BUS_SCSI: - virBufferAddLit(&opt, "scsi-disk"); - virBufferVSprintf(&opt, ",bus=scsi%d.%d,scsi-id=%d", - disk->info.addr.drive.controller, - disk->info.addr.drive.bus, - disk->info.addr.drive.unit); - break; - case VIR_DOMAIN_DISK_BUS_VIRTIO: - virBufferAddLit(&opt, "virtio-blk-pci"); - qemuBuildDeviceAddressStr(&opt, &disk->info); - break; - case VIR_DOMAIN_DISK_BUS_USB: - virBufferAddLit(&opt, "usb-storage"); - break; - default: - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk bus '%s' with device setup"), bus); - goto error; - } - virBufferVSprintf(&opt, ",drive=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias); - virBufferVSprintf(&opt, ",id=%s", disk->info.alias); - - if (virBufferError(&opt)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&opt); - -error: - virBufferFreeAndReset(&opt); - return NULL; -} - - -char *qemuBuildFSStr(virDomainFSDefPtr fs, - unsigned long long qemuCmdFlags ATTRIBUTE_UNUSED) -{ - virBuffer opt = VIR_BUFFER_INITIALIZER; - - if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("only supports mount filesystem type")); - goto error; - } - - virBufferAddLit(&opt, "local"); - if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_MAPPED) { - virBufferAddLit(&opt, ",security_model=mapped"); - } else if(fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) { - virBufferAddLit(&opt, ",security_model=passthrough"); - } else if(fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_SQUASH) { - virBufferAddLit(&opt, ",security_model=none"); - } - virBufferVSprintf(&opt, ",id=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias); - virBufferVSprintf(&opt, ",path=%s", fs->src); - - if (virBufferError(&opt)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&opt); - -error: - virBufferFreeAndReset(&opt); - return NULL; -} - - -char * -qemuBuildFSDevStr(virDomainFSDefPtr fs) -{ - virBuffer opt = VIR_BUFFER_INITIALIZER; - - if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("can only passthrough directories")); - goto error; - } - - virBufferAddLit(&opt, "virtio-9p-pci"); - virBufferVSprintf(&opt, ",id=%s", fs->info.alias); - virBufferVSprintf(&opt, ",fsdev=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias); - virBufferVSprintf(&opt, ",mount_tag=%s", fs->dst); - qemuBuildDeviceAddressStr(&opt, &fs->info); - - if (virBufferError(&opt)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&opt); - -error: - virBufferFreeAndReset(&opt); - return NULL; -} - - -char * -qemuBuildControllerDevStr(virDomainControllerDefPtr def) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - switch (def->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: - virBufferAddLit(&buf, "lsi"); - virBufferVSprintf(&buf, ",id=scsi%d", def->idx); - break; - - case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: - if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - virBufferAddLit(&buf, "virtio-serial-pci"); - } else { - virBufferAddLit(&buf, "virtio-serial"); - } - virBufferVSprintf(&buf, ",id=" QEMU_VIRTIO_SERIAL_PREFIX "%d", - def->idx); - if (def->opts.vioserial.ports != -1) { - virBufferVSprintf(&buf, ",max_ports=%d", - def->opts.vioserial.ports); - } - if (def->opts.vioserial.vectors != -1) { - virBufferVSprintf(&buf, ",vectors=%d", - def->opts.vioserial.vectors); - } - break; - - /* We always get an IDE controller, whether we want it or not. */ - case VIR_DOMAIN_CONTROLLER_TYPE_IDE: - default: - goto error; - } - - if (qemuBuildDeviceAddressStr(&buf, &def->info) < 0) - goto error; - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -char * -qemuBuildNicStr(virDomainNetDefPtr net, - const char *prefix, - int vlan) -{ - char *str; - if (virAsprintf(&str, - "%smacaddr=%02x:%02x:%02x:%02x:%02x:%02x,vlan=%d%s%s%s%s", - prefix ? prefix : "", - net->mac[0], net->mac[1], - net->mac[2], net->mac[3], - net->mac[4], net->mac[5], - vlan, - (net->model ? ",model=" : ""), - (net->model ? net->model : ""), - (net->info.alias ? ",name=" : ""), - (net->info.alias ? net->info.alias : "")) < 0) { - virReportOOMError(); - return NULL; - } - - return str; -} - - -char * -qemuBuildNicDevStr(virDomainNetDefPtr net, int vlan) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - const char *nic; - - if (!net->model) { - nic = "rtl8139"; - } else if (STREQ(net->model, "virtio")) { - nic = "virtio-net-pci"; - } else { - nic = net->model; - } - - virBufferAdd(&buf, nic, strlen(nic)); - if (vlan == -1) - virBufferVSprintf(&buf, ",netdev=host%s", net->info.alias); - else - virBufferVSprintf(&buf, ",vlan=%d", vlan); - virBufferVSprintf(&buf, ",id=%s", net->info.alias); - virBufferVSprintf(&buf, ",mac=%02x:%02x:%02x:%02x:%02x:%02x", - net->mac[0], net->mac[1], - net->mac[2], net->mac[3], - net->mac[4], net->mac[5]); - if (qemuBuildDeviceAddressStr(&buf, &net->info) < 0) - goto error; - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -char * -qemuBuildHostNetStr(virDomainNetDefPtr net, - char type_sep, - int vlan, - const char *tapfd, - const char *vhostfd) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - switch (net->type) { - case VIR_DOMAIN_NET_TYPE_NETWORK: - case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_DIRECT: - virBufferAddLit(&buf, "tap"); - virBufferVSprintf(&buf, "%cfd=%s", type_sep, tapfd); - type_sep = ','; - break; - - case VIR_DOMAIN_NET_TYPE_ETHERNET: - virBufferAddLit(&buf, "tap"); - if (net->ifname) { - virBufferVSprintf(&buf, "%cifname=%s", type_sep, net->ifname); - type_sep = ','; - } - if (net->data.ethernet.script) { - virBufferVSprintf(&buf, "%cscript=%s", type_sep, - net->data.ethernet.script); - type_sep = ','; - } - break; - - case VIR_DOMAIN_NET_TYPE_CLIENT: - case VIR_DOMAIN_NET_TYPE_SERVER: - case VIR_DOMAIN_NET_TYPE_MCAST: - virBufferAddLit(&buf, "socket"); - switch (net->type) { - case VIR_DOMAIN_NET_TYPE_CLIENT: - virBufferVSprintf(&buf, "%cconnect=%s:%d", - type_sep, - net->data.socket.address, - net->data.socket.port); - break; - case VIR_DOMAIN_NET_TYPE_SERVER: - virBufferVSprintf(&buf, "%clisten=%s:%d", - type_sep, - net->data.socket.address, - net->data.socket.port); - break; - case VIR_DOMAIN_NET_TYPE_MCAST: - virBufferVSprintf(&buf, "%cmcast=%s:%d", - type_sep, - net->data.socket.address, - net->data.socket.port); - break; - case VIR_DOMAIN_NET_TYPE_USER: - case VIR_DOMAIN_NET_TYPE_ETHERNET: - case VIR_DOMAIN_NET_TYPE_NETWORK: - case VIR_DOMAIN_NET_TYPE_BRIDGE: - case VIR_DOMAIN_NET_TYPE_INTERNAL: - case VIR_DOMAIN_NET_TYPE_DIRECT: - case VIR_DOMAIN_NET_TYPE_LAST: - break; - } - type_sep = ','; - break; - - case VIR_DOMAIN_NET_TYPE_USER: - default: - virBufferAddLit(&buf, "user"); - break; - } - - if (vlan >= 0) { - virBufferVSprintf(&buf, "%cvlan=%d", type_sep, vlan); - if (net->info.alias) - virBufferVSprintf(&buf, ",name=host%s", - net->info.alias); - } else { - virBufferVSprintf(&buf, "%cid=host%s", - type_sep, net->info.alias); - } - - if (vhostfd && *vhostfd) { - virBufferVSprintf(&buf, ",vhost=on,vhostfd=%s", vhostfd); - } - - if (virBufferError(&buf)) { - virBufferFreeAndReset(&buf); - virReportOOMError(); - return NULL; - } - - return virBufferContentAndReset(&buf); -} - - -char * -qemuBuildWatchdogDevStr(virDomainWatchdogDefPtr dev) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - const char *model = virDomainWatchdogModelTypeToString(dev->model); - if (!model) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("missing watchdog model")); - goto error; - } - - virBufferVSprintf(&buf, "%s", model); - virBufferVSprintf(&buf, ",id=%s", dev->info.alias); - if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0) - goto error; - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -char * -qemuBuildMemballoonDevStr(virDomainMemballoonDefPtr dev) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - virBufferAddLit(&buf, "virtio-balloon-pci"); - virBufferVSprintf(&buf, ",id=%s", dev->info.alias); - if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0) - goto error; - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -char * -qemuBuildUSBInputDevStr(virDomainInputDefPtr dev) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - virBufferVSprintf(&buf, "%s", - dev->type == VIR_DOMAIN_INPUT_TYPE_MOUSE ? - "usb-mouse" : "usb-tablet"); - virBufferVSprintf(&buf, ",id=%s", dev->info.alias); - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -char * -qemuBuildSoundDevStr(virDomainSoundDefPtr sound) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - const char *model = virDomainSoundModelTypeToString(sound->model); - - if (!model) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("invalid sound model")); - goto error; - } - - /* Hack for 2 wierdly unusal devices name in QEMU */ - if (STREQ(model, "es1370")) - model = "ES1370"; - else if (STREQ(model, "ac97")) - model = "AC97"; - - virBufferVSprintf(&buf, "%s", model); - virBufferVSprintf(&buf, ",id=%s", sound->info.alias); - if (qemuBuildDeviceAddressStr(&buf, &sound->info) < 0) - goto error; - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -static char * -qemuBuildVideoDevStr(virDomainVideoDefPtr video) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - const char *model = qemuVideoTypeToString(video->type); - - if (!model) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("invalid video model")); - goto error; - } - - virBufferVSprintf(&buf, "%s", model); - virBufferVSprintf(&buf, ",id=%s", video->info.alias); - if (qemuBuildDeviceAddressStr(&buf, &video->info) < 0) - goto error; - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -int -qemudOpenPCIConfig(virDomainHostdevDefPtr dev) -{ - char *path = NULL; - int configfd = -1; - - if (virAsprintf(&path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/config", - dev->source.subsys.u.pci.domain, - dev->source.subsys.u.pci.bus, - dev->source.subsys.u.pci.slot, - dev->source.subsys.u.pci.function) < 0) { - virReportOOMError(); - return -1; - } - - configfd = open(path, O_RDWR, 0); - - if (configfd < 0) - virReportSystemError(errno, _("Failed opening %s"), path); - - VIR_FREE(path); - - return configfd; -} - -char * -qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, const char *configfd) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - virBufferAddLit(&buf, "pci-assign"); - virBufferVSprintf(&buf, ",host=%.2x:%.2x.%.1x", - dev->source.subsys.u.pci.bus, - dev->source.subsys.u.pci.slot, - dev->source.subsys.u.pci.function); - virBufferVSprintf(&buf, ",id=%s", dev->info.alias); - if (configfd && *configfd) - virBufferVSprintf(&buf, ",configfd=%s", configfd); - if (qemuBuildDeviceAddressStr(&buf, &dev->info) < 0) - goto error; - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -char * -qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev) -{ - char *ret = NULL; - - if (virAsprintf(&ret, "host=%.2x:%.2x.%.1x", - dev->source.subsys.u.pci.bus, - dev->source.subsys.u.pci.slot, - dev->source.subsys.u.pci.function) < 0) - virReportOOMError(); - - return ret; -} - - -char * -qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev) -{ - char *ret = NULL; - - if (!dev->source.subsys.u.usb.bus && - !dev->source.subsys.u.usb.device) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("USB host device is missing bus/device information")); - return NULL; - } - - if (virAsprintf(&ret, "usb-host,hostbus=%d,hostaddr=%d,id=%s", - dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device, - dev->info.alias) < 0) - virReportOOMError(); - - return ret; -} - - -char * -qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev) -{ - char *ret = NULL; - - if (!dev->source.subsys.u.usb.bus && - !dev->source.subsys.u.usb.device) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("USB host device is missing bus/device information")); - return NULL; - } - - if (virAsprintf(&ret, "host:%d.%d", - dev->source.subsys.u.usb.bus, - dev->source.subsys.u.usb.device) < 0) - virReportOOMError(); - - return ret; -} - - - -/* This function outputs a -chardev command line option which describes only the - * host side of the character device */ -char * -qemuBuildChrChardevStr(virDomainChrDefPtr dev) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - bool telnet; - - switch(dev->type) { - case VIR_DOMAIN_CHR_TYPE_NULL: - virBufferVSprintf(&buf, "null,id=%s", dev->info.alias); - break; - - case VIR_DOMAIN_CHR_TYPE_VC: - virBufferVSprintf(&buf, "vc,id=%s", dev->info.alias); - break; - - case VIR_DOMAIN_CHR_TYPE_PTY: - virBufferVSprintf(&buf, "pty,id=%s", dev->info.alias); - break; - - case VIR_DOMAIN_CHR_TYPE_DEV: - virBufferVSprintf(&buf, "tty,id=%s,path=%s", dev->info.alias, dev->data.file.path); - break; - - case VIR_DOMAIN_CHR_TYPE_FILE: - virBufferVSprintf(&buf, "file,id=%s,path=%s", dev->info.alias, dev->data.file.path); - break; - - case VIR_DOMAIN_CHR_TYPE_PIPE: - virBufferVSprintf(&buf, "pipe,id=%s,path=%s", dev->info.alias, dev->data.file.path); - break; - - case VIR_DOMAIN_CHR_TYPE_STDIO: - virBufferVSprintf(&buf, "stdio,id=%s", dev->info.alias); - break; - - case VIR_DOMAIN_CHR_TYPE_UDP: - virBufferVSprintf(&buf, - "udp,id=%s,host=%s,port=%s,localaddr=%s,localport=%s", - dev->info.alias, - dev->data.udp.connectHost, - dev->data.udp.connectService, - dev->data.udp.bindHost, - dev->data.udp.bindService); - break; - - case VIR_DOMAIN_CHR_TYPE_TCP: - telnet = dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; - virBufferVSprintf(&buf, - "socket,id=%s,host=%s,port=%s%s%s", - dev->info.alias, - dev->data.tcp.host, - dev->data.tcp.service, - telnet ? ",telnet" : "", - dev->data.tcp.listen ? ",server,nowait" : ""); - break; - - case VIR_DOMAIN_CHR_TYPE_UNIX: - virBufferVSprintf(&buf, - "socket,id=%s,path=%s%s", - dev->info.alias, - dev->data.nix.path, - dev->data.nix.listen ? ",server,nowait" : ""); - break; - } - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -char * -qemuBuildChrArgStr(virDomainChrDefPtr dev, const char *prefix) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - if (prefix) - virBufferAdd(&buf, prefix, strlen(prefix)); - - switch (dev->type) { - case VIR_DOMAIN_CHR_TYPE_NULL: - virBufferAddLit(&buf, "null"); - break; - - case VIR_DOMAIN_CHR_TYPE_VC: - virBufferAddLit(&buf, "vc"); - break; - - case VIR_DOMAIN_CHR_TYPE_PTY: - virBufferAddLit(&buf, "pty"); - break; - - case VIR_DOMAIN_CHR_TYPE_DEV: - virBufferStrcat(&buf, dev->data.file.path, NULL); - break; - - case VIR_DOMAIN_CHR_TYPE_FILE: - virBufferVSprintf(&buf, "file:%s", dev->data.file.path); - break; - - case VIR_DOMAIN_CHR_TYPE_PIPE: - virBufferVSprintf(&buf, "pipe:%s", dev->data.file.path); - break; - - case VIR_DOMAIN_CHR_TYPE_STDIO: - virBufferAddLit(&buf, "stdio"); - break; - - case VIR_DOMAIN_CHR_TYPE_UDP: - virBufferVSprintf(&buf, "udp:%s:%s@%s:%s", - dev->data.udp.connectHost, - dev->data.udp.connectService, - dev->data.udp.bindHost, - dev->data.udp.bindService); - break; - - case VIR_DOMAIN_CHR_TYPE_TCP: - if (dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET) { - virBufferVSprintf(&buf, "telnet:%s:%s%s", - dev->data.tcp.host, - dev->data.tcp.service, - dev->data.tcp.listen ? ",server,nowait" : ""); - } else { - virBufferVSprintf(&buf, "tcp:%s:%s%s", - dev->data.tcp.host, - dev->data.tcp.service, - dev->data.tcp.listen ? ",server,nowait" : ""); - } - break; - - case VIR_DOMAIN_CHR_TYPE_UNIX: - virBufferVSprintf(&buf, "unix:%s%s", - dev->data.nix.path, - dev->data.nix.listen ? ",server,nowait" : ""); - break; - } - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -char * -qemuBuildVirtioSerialPortDevStr(virDomainChrDefPtr dev) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - if (dev->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE) - virBufferAddLit(&buf, "virtconsole"); - else - virBufferAddLit(&buf, "virtserialport"); - - if (dev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { - /* Check it's a virtio-serial address */ - if (dev->info.type != - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL) - { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("virtio serial device has invalid address type")); - goto error; - } - - virBufferVSprintf(&buf, - ",bus=" QEMU_VIRTIO_SERIAL_PREFIX "%d.%d", - dev->info.addr.vioserial.controller, - dev->info.addr.vioserial.bus); - virBufferVSprintf(&buf, - ",nr=%d", - dev->info.addr.vioserial.port); - } - - virBufferVSprintf(&buf, ",chardev=%s", dev->info.alias); - if (dev->target.name) { - virBufferVSprintf(&buf, ",name=%s", dev->target.name); - } - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - -static char *qemuBuildSmbiosBiosStr(virSysinfoDefPtr def) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - if ((def->bios_vendor == NULL) && (def->bios_version == NULL) && - (def->bios_date == NULL) && (def->bios_release == NULL)) - return(NULL); - - virBufferAddLit(&buf, "type=0"); - - /* 0:Vendor */ - if (def->bios_vendor) - virBufferVSprintf(&buf, ",vendor=%s", def->bios_vendor); - /* 0:BIOS Version */ - if (def->bios_version) - virBufferVSprintf(&buf, ",version=%s", def->bios_version); - /* 0:BIOS Release Date */ - if (def->bios_date) - virBufferVSprintf(&buf, ",date=%s", def->bios_date); - /* 0:System BIOS Major Release and 0:System BIOS Minor Release */ - if (def->bios_release) - virBufferVSprintf(&buf, ",release=%s", def->bios_release); - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return(NULL); -} - -static char *qemuBuildSmbiosSystemStr(virSysinfoDefPtr def, bool skip_uuid) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - if ((def->system_manufacturer == NULL) && (def->system_sku == NULL) && - (def->system_product == NULL) && (def->system_version == NULL) && - (def->system_serial == NULL) && (def->system_family == NULL) && - (def->system_uuid == NULL || skip_uuid)) - return NULL; - - virBufferAddLit(&buf, "type=1"); - - /* 1:Manufacturer */ - if (def->system_manufacturer) - virBufferVSprintf(&buf, ",manufacturer=%s", - def->system_manufacturer); - /* 1:Product Name */ - if (def->system_product) - virBufferVSprintf(&buf, ",product=%s", def->system_product); - /* 1:Version */ - if (def->system_version) - virBufferVSprintf(&buf, ",version=%s", def->system_version); - /* 1:Serial Number */ - if (def->system_serial) - virBufferVSprintf(&buf, ",serial=%s", def->system_serial); - /* 1:UUID */ - if (def->system_uuid && !skip_uuid) - virBufferVSprintf(&buf, ",uuid=%s", def->system_uuid); - /* 1:SKU Number */ - if (def->system_sku) - virBufferVSprintf(&buf, ",sku=%s", def->system_sku); - /* 1:Family */ - if (def->system_family) - virBufferVSprintf(&buf, ",family=%s", def->system_family); - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return(NULL); -} - -static char * -qemuBuildClockArgStr(virDomainClockDefPtr def) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - switch (def->offset) { - case VIR_DOMAIN_CLOCK_OFFSET_UTC: - virBufferAddLit(&buf, "base=utc"); - break; - - case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: - case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: - virBufferAddLit(&buf, "base=localtime"); - break; - - case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: { - time_t now = time(NULL); - struct tm nowbits; - - now += def->data.adjustment; - gmtime_r(&now, &nowbits); - - virBufferVSprintf(&buf, "base=%d-%02d-%02dT%02d:%02d:%02d", - nowbits.tm_year + 1900, - nowbits.tm_mon + 1, - nowbits.tm_mday, - nowbits.tm_hour, - nowbits.tm_min, - nowbits.tm_sec); - } break; - - default: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported clock offset '%s'"), - virDomainClockOffsetTypeToString(def->offset)); - goto error; - } - - /* Look for an 'rtc' timer element, and add in appropriate clock= and driftfix= */ - int i; - for (i = 0; i < def->ntimers; i++) { - if (def->timers[i]->name == VIR_DOMAIN_TIMER_NAME_RTC) { - switch (def->timers[i]->track) { - case -1: /* unspecified - use hypervisor default */ - break; - case VIR_DOMAIN_TIMER_TRACK_BOOT: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported rtc timer track '%s'"), - virDomainTimerTrackTypeToString(def->timers[i]->track)); - goto error; - case VIR_DOMAIN_TIMER_TRACK_GUEST: - virBufferAddLit(&buf, ",clock=vm"); - break; - case VIR_DOMAIN_TIMER_TRACK_WALL: - virBufferAddLit(&buf, ",clock=host"); - break; - } - - switch (def->timers[i]->tickpolicy) { - case -1: - case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: - /* This is the default - missed ticks delivered when - next scheduled, at normal rate */ - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: - /* deliver ticks at a faster rate until caught up */ - virBufferAddLit(&buf, ",driftfix=slew"); - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: - case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported rtc timer tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->timers[i]->tickpolicy)); - goto error; - } - break; /* no need to check other timers - there is only one rtc */ - } - } - - if (virBufferError(&buf)) { - virReportOOMError(); - goto error; - } - - return virBufferContentAndReset(&buf); - -error: - virBufferFreeAndReset(&buf); - return NULL; -} - - -static int -qemuBuildCpuArgStr(const struct qemud_driver *driver, - const virDomainDefPtr def, - const char *emulator, - unsigned long long qemuCmdFlags, - const struct utsname *ut, - char **opt, - bool *hasHwVirt) -{ - const virCPUDefPtr host = driver->caps->host.cpu; - virCPUDefPtr guest = NULL; - unsigned int ncpus = 0; - const char **cpus = NULL; - union cpuData *data = NULL; - int ret = -1; - virBuffer buf = VIR_BUFFER_INITIALIZER; - int i; - - *hasHwVirt = false; - - if (def->cpu && def->cpu->model) { - if (qemuCapsProbeCPUModels(emulator, qemuCmdFlags, ut->machine, - &ncpus, &cpus) < 0) - goto cleanup; - - if (!ncpus || !host) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("CPU specification not supported by hypervisor")); - goto cleanup; - } - } - - if (ncpus > 0 && host) { - virCPUCompareResult cmp; - const char *preferred; - int hasSVM; - - cmp = cpuGuestData(host, def->cpu, &data); - switch (cmp) { - case VIR_CPU_COMPARE_INCOMPATIBLE: - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("guest CPU is not compatible with host CPU")); - /* fall through */ - case VIR_CPU_COMPARE_ERROR: - goto cleanup; - - default: - break; - } - - if (VIR_ALLOC(guest) < 0 || !(guest->arch = strdup(ut->machine))) - goto no_memory; - - if (def->cpu->match == VIR_CPU_MATCH_MINIMUM) - preferred = host->model; - else - preferred = def->cpu->model; - - guest->type = VIR_CPU_TYPE_GUEST; - if (cpuDecode(guest, data, cpus, ncpus, preferred) < 0) - goto cleanup; - - /* Only 'svm' requires --enable-nesting. The nested - * 'vmx' patches now simply hook off the CPU features - */ - hasSVM = cpuHasFeature(guest->arch, data, "svm"); - if (hasSVM < 0) - goto cleanup; - *hasHwVirt = hasSVM > 0 ? true : false; - - virBufferVSprintf(&buf, "%s", guest->model); - for (i = 0; i < guest->nfeatures; i++) { - char sign; - if (guest->features[i].policy == VIR_CPU_FEATURE_DISABLE) - sign = '-'; - else - sign = '+'; - - virBufferVSprintf(&buf, ",%c%s", sign, guest->features[i].name); - } - } - else { - /* - * Need to force a 32-bit guest CPU type if - * - * 1. guest OS is i686 - * 2. host OS is x86_64 - * 3. emulator is qemu-kvm or kvm - * - * Or - * - * 1. guest OS is i686 - * 2. emulator is qemu-system-x86_64 - */ - if (STREQ(def->os.arch, "i686") && - ((STREQ(ut->machine, "x86_64") && - strstr(emulator, "kvm")) || - strstr(emulator, "x86_64"))) - virBufferAddLit(&buf, "qemu32"); - } - - if (virBufferError(&buf)) - goto no_memory; - - *opt = virBufferContentAndReset(&buf); - - ret = 0; - -cleanup: - virCPUDefFree(guest); - cpuDataFree(ut->machine, data); - - if (cpus) { - for (i = 0; i < ncpus; i++) - VIR_FREE(cpus[i]); - VIR_FREE(cpus); - } - - return ret; - -no_memory: - virReportOOMError(); - goto cleanup; -} - -static char * -qemuBuildSmpArgStr(const virDomainDefPtr def, - unsigned long long qemuCmdFlags) -{ - virBuffer buf = VIR_BUFFER_INITIALIZER; - - virBufferVSprintf(&buf, "%u", def->vcpus); - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_SMP_TOPOLOGY)) { - if (def->vcpus != def->maxvcpus) - virBufferVSprintf(&buf, ",maxcpus=%u", def->maxvcpus); - /* sockets, cores, and threads are either all zero - * or all non-zero, thus checking one of them is enough */ - if (def->cpu && def->cpu->sockets) { - virBufferVSprintf(&buf, ",sockets=%u", def->cpu->sockets); - virBufferVSprintf(&buf, ",cores=%u", def->cpu->cores); - virBufferVSprintf(&buf, ",threads=%u", def->cpu->threads); - } - else { - virBufferVSprintf(&buf, ",sockets=%u", def->maxvcpus); - virBufferVSprintf(&buf, ",cores=%u", 1); - virBufferVSprintf(&buf, ",threads=%u", 1); - } - } else if (def->vcpus != def->maxvcpus) { - virBufferFreeAndReset(&buf); - /* FIXME - consider hot-unplugging cpus after boot for older qemu */ - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("setting current vcpu count less than maximum is " - "not supported with this QEMU binary")); - return NULL; - } - - if (virBufferError(&buf)) { - virBufferFreeAndReset(&buf); - virReportOOMError(); - return NULL; - } - - return virBufferContentAndReset(&buf); -} - - -/* - * Constructs a argv suitable for launching qemu with config defined - * for a given virtual machine. - * - * XXX 'conn' is only required to resolve network -> bridge name - * figure out how to remove this requirement some day - */ -virCommandPtr -qemudBuildCommandLine(virConnectPtr conn, - struct qemud_driver *driver, - virDomainDefPtr def, - virDomainChrDefPtr monitor_chr, - bool monitor_json, - unsigned long long qemuCmdFlags, - const char *migrateFrom, - virDomainSnapshotObjPtr current_snapshot, - enum virVMOperationType vmop) -{ - int i; - char boot[VIR_DOMAIN_BOOT_LAST+1]; - struct utsname ut; - int disableKQEMU = 0; - int enableKQEMU = 0; - int disableKVM = 0; - int enableKVM = 0; - const char *emulator; - char uuid[VIR_UUID_STRING_BUFLEN]; - char *cpu; - char *smp; - int last_good_net = -1; - bool hasHwVirt = false; - virCommandPtr cmd; - bool has_rbd_hosts = false; - virBuffer rbd_hosts = VIR_BUFFER_INITIALIZER; - - uname_normalize(&ut); - - if (qemuAssignDeviceAliases(def, qemuCmdFlags) < 0) - return NULL; - - virUUIDFormat(def->uuid, uuid); - - /* Migration is very annoying due to wildly varying syntax & capabilities - * over time of KVM / QEMU codebases - */ - if (migrateFrom) { - if (STRPREFIX(migrateFrom, "tcp")) { - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_TCP)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("TCP migration is not supported with this QEMU binary")); - return NULL; - } - } else if (STREQ(migrateFrom, "stdio")) { - if (qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC) { - migrateFrom = "exec:cat"; - } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("STDIO migration is not supported with this QEMU binary")); - return NULL; - } - } else if (STRPREFIX(migrateFrom, "exec")) { - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MIGRATE_QEMU_EXEC)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("STDIO migration is not supported with this QEMU binary")); - return NULL; - } - } - } - - emulator = def->emulator; - - /* - * do not use boot=on for drives when not using KVM since this - * is not supported at all in upstream QEmu. - */ - if ((qemuCmdFlags & QEMUD_CMD_FLAG_KVM) && - (def->virtType == VIR_DOMAIN_VIRT_QEMU) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_BOOT)) - qemuCmdFlags -= QEMUD_CMD_FLAG_DRIVE_BOOT; - - switch (def->virtType) { - case VIR_DOMAIN_VIRT_QEMU: - if (qemuCmdFlags & QEMUD_CMD_FLAG_KQEMU) - disableKQEMU = 1; - if (qemuCmdFlags & QEMUD_CMD_FLAG_KVM) - disableKVM = 1; - break; - - case VIR_DOMAIN_VIRT_KQEMU: - if (qemuCmdFlags & QEMUD_CMD_FLAG_KVM) - disableKVM = 1; - - if (qemuCmdFlags & QEMUD_CMD_FLAG_ENABLE_KQEMU) { - enableKQEMU = 1; - } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_KQEMU)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary %s does not support kqemu"), - emulator); - } - break; - - case VIR_DOMAIN_VIRT_KVM: - if (qemuCmdFlags & QEMUD_CMD_FLAG_KQEMU) - disableKQEMU = 1; - - if (qemuCmdFlags & QEMUD_CMD_FLAG_ENABLE_KVM) { - enableKVM = 1; - } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_KVM)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary %s does not support kvm"), - emulator); - } - break; - - case VIR_DOMAIN_VIRT_XEN: - /* XXX better check for xenner */ - break; - - default: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary %s does not support %s"), - emulator, virDomainVirtTypeToString(def->virtType)); - break; - } - - cmd = virCommandNewArgList(emulator, "-S", NULL); - - virCommandAddEnvPassCommon(cmd); - - /* This should *never* be NULL, since we always provide - * a machine in the capabilities data for QEMU. So this - * check is just here as a safety in case the unexpected - * happens */ - if (def->os.machine) - virCommandAddArgList(cmd, "-M", def->os.machine, NULL); - - if (qemuBuildCpuArgStr(driver, def, emulator, qemuCmdFlags, - &ut, &cpu, &hasHwVirt) < 0) - goto error; - - if (cpu) { - virCommandAddArgList(cmd, "-cpu", cpu, NULL); - VIR_FREE(cpu); - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NESTING) && - hasHwVirt) - virCommandAddArg(cmd, "-enable-nesting"); - } - - if (disableKQEMU) - virCommandAddArg(cmd, "-no-kqemu"); - else if (enableKQEMU) - virCommandAddArgList(cmd, "-enable-kqemu", "-kernel-kqemu", NULL); - if (disableKVM) - virCommandAddArg(cmd, "-no-kvm"); - if (enableKVM) - virCommandAddArg(cmd, "-enable-kvm"); - - /* Set '-m MB' based on maxmem, because the lower 'memory' limit - * is set post-startup using the balloon driver. If balloon driver - * is not supported, then they're out of luck anyway - */ - virCommandAddArg(cmd, "-m"); - virCommandAddArgFormat(cmd, "%lu", def->mem.max_balloon / 1024); - if (def->mem.hugepage_backed) { - if (!driver->hugetlbfs_mount) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("hugetlbfs filesystem is not mounted")); - goto error; - } - if (!driver->hugepage_path) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("hugepages are disabled by administrator config")); - goto error; - } - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_MEM_PATH)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("hugepage backing not supported by '%s'"), - def->emulator); - goto error; - } - virCommandAddArgList(cmd, "-mem-prealloc", "-mem-path", - driver->hugepage_path, NULL); - } - - virCommandAddArg(cmd, "-smp"); - if (!(smp = qemuBuildSmpArgStr(def, qemuCmdFlags))) - goto error; - virCommandAddArg(cmd, smp); - VIR_FREE(smp); - - if (qemuCmdFlags & QEMUD_CMD_FLAG_NAME) { - virCommandAddArg(cmd, "-name"); - if (driver->setProcessName && - (qemuCmdFlags & QEMUD_CMD_FLAG_NAME_PROCESS)) { - virCommandAddArgFormat(cmd, "%s,process=qemu:%s", - def->name, def->name); - } else { - virCommandAddArg(cmd, def->name); - } - } - if (qemuCmdFlags & QEMUD_CMD_FLAG_UUID) - virCommandAddArgList(cmd, "-uuid", uuid, NULL); - if (def->virtType == VIR_DOMAIN_VIRT_XEN || - STREQ(def->os.type, "xen") || - STREQ(def->os.type, "linux")) { - if (qemuCmdFlags & QEMUD_CMD_FLAG_DOMID) { - virCommandAddArg(cmd, "-domid"); - virCommandAddArgFormat(cmd, "%d", def->id); - } else if (qemuCmdFlags & QEMUD_CMD_FLAG_XEN_DOMID) { - virCommandAddArg(cmd, "-xen-attach"); - virCommandAddArg(cmd, "-xen-domid"); - virCommandAddArgFormat(cmd, "%d", def->id); - } else { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("qemu emulator '%s' does not support xen"), - def->emulator); - goto error; - } - } - - if ((def->os.smbios_mode != VIR_DOMAIN_SMBIOS_NONE) && - (def->os.smbios_mode != VIR_DOMAIN_SMBIOS_EMULATE)) { - virSysinfoDefPtr source = NULL; - bool skip_uuid = false; - - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_SMBIOS_TYPE)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("the QEMU binary %s does not support smbios settings"), - emulator); - goto error; - } - - /* should we really error out or just warn in those cases ? */ - if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_HOST) { - if (driver->hostsysinfo == NULL) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Host SMBIOS information is not available")); - goto error; - } - source = driver->hostsysinfo; - /* Host and guest uuid must differ, by definition of UUID. */ - skip_uuid = true; - } else if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_SYSINFO) { - if (def->sysinfo == NULL) { - qemuReportError(VIR_ERR_XML_ERROR, - _("Domain '%s' sysinfo are not available"), - def->name); - goto error; - } - source = def->sysinfo; - /* domain_conf guaranteed that system_uuid matches guest uuid. */ - } - if (source != NULL) { - char *smbioscmd; - - smbioscmd = qemuBuildSmbiosBiosStr(source); - if (smbioscmd != NULL) { - virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); - VIR_FREE(smbioscmd); - } - smbioscmd = qemuBuildSmbiosSystemStr(source, skip_uuid); - if (smbioscmd != NULL) { - virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL); - VIR_FREE(smbioscmd); - } - } - } - - /* - * NB, -nographic *MUST* come before any serial, or monitor - * or parallel port flags due to QEMU craziness, where it - * decides to change the serial port & monitor to be on stdout - * if you ask for nographic. So we have to make sure we override - * these defaults ourselves... - */ - if (!def->graphics) - virCommandAddArg(cmd, "-nographic"); - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuCmdFlags & QEMUD_CMD_FLAG_NODEFCONFIG) - virCommandAddArg(cmd, - "-nodefconfig"); /* Disable global config files */ - virCommandAddArg(cmd, - "-nodefaults"); /* Disable default guest devices */ - } - - if (monitor_chr) { - char *chrdev; - /* Use -chardev if it's available */ - if (qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) { - - virCommandAddArg(cmd, "-chardev"); - if (!(chrdev = qemuBuildChrChardevStr(monitor_chr))) - goto error; - virCommandAddArg(cmd, chrdev); - VIR_FREE(chrdev); - - virCommandAddArg(cmd, "-mon"); - virCommandAddArgFormat(cmd, "chardev=monitor,mode=%s", - monitor_json ? "control" : "readline"); - } else { - const char *prefix = NULL; - if (monitor_json) - prefix = "control,"; - - virCommandAddArg(cmd, "-monitor"); - if (!(chrdev = qemuBuildChrArgStr(monitor_chr, prefix))) - goto error; - virCommandAddArg(cmd, chrdev); - VIR_FREE(chrdev); - } - } - - if (qemuCmdFlags & QEMUD_CMD_FLAG_RTC) { - const char *rtcopt; - virCommandAddArg(cmd, "-rtc"); - if (!(rtcopt = qemuBuildClockArgStr(&def->clock))) - goto error; - virCommandAddArg(cmd, rtcopt); - VIR_FREE(rtcopt); - } else { - switch (def->clock.offset) { - case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: - case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: - virCommandAddArg(cmd, "-localtime"); - break; - - case VIR_DOMAIN_CLOCK_OFFSET_UTC: - /* Nothing, its the default */ - break; - - default: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported clock offset '%s'"), - virDomainClockOffsetTypeToString(def->clock.offset)); - goto error; - } - } - if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE && - def->clock.data.timezone) { - virCommandAddEnvPair(cmd, "TZ", def->clock.data.timezone); - } - - for (i = 0; i < def->clock.ntimers; i++) { - switch (def->clock.timers[i]->name) { - default: - case VIR_DOMAIN_TIMER_NAME_PLATFORM: - case VIR_DOMAIN_TIMER_NAME_TSC: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported timer type (name) '%s'"), - virDomainTimerNameTypeToString(def->clock.timers[i]->name)); - goto error; - - case VIR_DOMAIN_TIMER_NAME_RTC: - /* This has already been taken care of (in qemuBuildClockArgStr) - if QEMUD_CMD_FLAG_RTC is set (mutually exclusive with - QEMUD_FLAG_RTC_TD_HACK) */ - if (qemuCmdFlags & QEMUD_CMD_FLAG_RTC_TD_HACK) { - switch (def->clock.timers[i]->tickpolicy) { - case -1: - case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: - /* the default - do nothing */ - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: - virCommandAddArg(cmd, "-rtc-td-hack"); - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: - case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported rtc tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); - goto error; - } - } else if (!(qemuCmdFlags & QEMUD_CMD_FLAG_RTC) - && (def->clock.timers[i]->tickpolicy - != VIR_DOMAIN_TIMER_TICKPOLICY_DELAY) - && (def->clock.timers[i]->tickpolicy != -1)) { - /* a non-default rtc policy was given, but there is no - way to implement it in this version of qemu */ - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported rtc tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); - goto error; - } - break; - - case VIR_DOMAIN_TIMER_NAME_PIT: - switch (def->clock.timers[i]->tickpolicy) { - case -1: - case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY: - /* delay is the default if we don't have kernel - (-no-kvm-pit), otherwise, the default is catchup. */ - if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) - virCommandAddArg(cmd, "-no-kvm-pit-reinjection"); - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP: - if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_KVM_PIT) { - /* do nothing - this is default for kvm-pit */ - } else if (qemuCmdFlags & QEMUD_CMD_FLAG_TDF) { - /* -tdf switches to 'catchup' with userspace pit. */ - virCommandAddArg(cmd, "-tdf"); - } else { - /* can't catchup if we have neither pit mode */ - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported pit tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); - goto error; - } - break; - case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE: - case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD: - /* no way to support these modes for pit in qemu */ - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("unsupported pit tickpolicy '%s'"), - virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy)); - goto error; - } - break; - - case VIR_DOMAIN_TIMER_NAME_HPET: - /* the only meaningful attribute for hpet is "present". If - * present is -1, that means it wasn't specified, and - * should be left at the default for the - * hypervisor. "default" when -no-hpet exists is "yes", - * and when -no-hpet doesn't exist is "no". "confusing"? - * "yes"! */ - - if (qemuCmdFlags & QEMUD_CMD_FLAG_NO_HPET) { - if (def->clock.timers[i]->present == 0) - virCommandAddArg(cmd, "-no-hpet"); - } else { - /* no hpet timer available. The only possible action - is to raise an error if present="yes" */ - if (def->clock.timers[i]->present == 1) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("pit timer is not supported")); - } - } - break; - } - } - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NO_REBOOT) && - def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART) - virCommandAddArg(cmd, "-no-reboot"); - - if (!(def->features & (1 << VIR_DOMAIN_FEATURE_ACPI))) - virCommandAddArg(cmd, "-no-acpi"); - - if (!def->os.bootloader) { - for (i = 0 ; i < def->os.nBootDevs ; i++) { - switch (def->os.bootDevs[i]) { - case VIR_DOMAIN_BOOT_CDROM: - boot[i] = 'd'; - break; - case VIR_DOMAIN_BOOT_FLOPPY: - boot[i] = 'a'; - break; - case VIR_DOMAIN_BOOT_DISK: - boot[i] = 'c'; - break; - case VIR_DOMAIN_BOOT_NET: - boot[i] = 'n'; - break; - default: - boot[i] = 'c'; - break; - } - } - if (def->os.nBootDevs) { - virBuffer boot_buf = VIR_BUFFER_INITIALIZER; - virCommandAddArg(cmd, "-boot"); - - boot[def->os.nBootDevs] = '\0'; - - if (qemuCmdFlags & QEMUD_CMD_FLAG_BOOT_MENU && - def->os.bootmenu != VIR_DOMAIN_BOOT_MENU_DEFAULT) { - if (def->os.bootmenu == VIR_DOMAIN_BOOT_MENU_ENABLED) - virBufferVSprintf(&boot_buf, "order=%s,menu=on", boot); - else if (def->os.bootmenu == VIR_DOMAIN_BOOT_MENU_DISABLED) - virBufferVSprintf(&boot_buf, "order=%s,menu=off", boot); - } else { - virBufferVSprintf(&boot_buf, "%s", boot); - } - - virCommandAddArgBuffer(cmd, &boot_buf); - } - - if (def->os.kernel) - virCommandAddArgList(cmd, "-kernel", def->os.kernel, NULL); - if (def->os.initrd) - virCommandAddArgList(cmd, "-initrd", def->os.initrd, NULL); - if (def->os.cmdline) - virCommandAddArgList(cmd, "-append", def->os.cmdline, NULL); - } else { - virCommandAddArgList(cmd, "-bootloader", def->os.bootloader, NULL); - } - - for (i = 0 ; i < def->ndisks ; i++) { - virDomainDiskDefPtr disk = def->disks[i]; - - if (disk->driverName != NULL && - !STREQ(disk->driverName, "qemu")) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported driver name '%s' for disk '%s'"), - disk->driverName, disk->src); - goto error; - } - } - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - for (i = 0 ; i < def->ncontrollers ; i++) { - virDomainControllerDefPtr cont = def->controllers[i]; - - /* We don't add an explicit IDE or FD controller because the - * provided PIIX4 device already includes one. It isn't possible to - * remove the PIIX4. */ - if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE || - cont->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC) - continue; - - /* QEMU doesn't implement a SATA driver */ - if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("SATA is not supported with this QEMU binary")); - goto error; - } - - virCommandAddArg(cmd, "-device"); - - char *devstr; - if (!(devstr = qemuBuildControllerDevStr(def->controllers[i]))) - goto no_memory; - - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } - } - - /* If QEMU supports -drive param instead of old -hda, -hdb, -cdrom .. */ - if (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE) { - int bootCD = 0, bootFloppy = 0, bootDisk = 0; - - /* If QEMU supports boot=on for -drive param... */ - if (qemuCmdFlags & QEMUD_CMD_FLAG_DRIVE_BOOT) { - for (i = 0 ; i < def->os.nBootDevs ; i++) { - switch (def->os.bootDevs[i]) { - case VIR_DOMAIN_BOOT_CDROM: - bootCD = 1; - break; - case VIR_DOMAIN_BOOT_FLOPPY: - bootFloppy = 1; - break; - case VIR_DOMAIN_BOOT_DISK: - bootDisk = 1; - break; - } - } - } - - for (i = 0 ; i < def->ndisks ; i++) { - char *optstr; - int bootable = 0; - virDomainDiskDefPtr disk = def->disks[i]; - int withDeviceArg = 0; - int j; - - /* Unless we have -device, then USB disks need special - handling */ - if ((disk->bus == VIR_DOMAIN_DISK_BUS_USB) && - !(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - virCommandAddArg(cmd, "-usbdevice"); - virCommandAddArgFormat(cmd, "disk:%s", disk->src); - } else { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported usb disk type for '%s'"), - disk->src); - goto error; - } - continue; - } - - switch (disk->device) { - case VIR_DOMAIN_DISK_DEVICE_CDROM: - bootable = bootCD; - bootCD = 0; - break; - case VIR_DOMAIN_DISK_DEVICE_FLOPPY: - bootable = bootFloppy; - bootFloppy = 0; - break; - case VIR_DOMAIN_DISK_DEVICE_DISK: - bootable = bootDisk; - bootDisk = 0; - break; - } - - virCommandAddArg(cmd, "-drive"); - - /* Unfortunately it is not possible to use - -device for floppies, or Xen paravirt - devices. Fortunately, those don't need - static PCI addresses, so we don't really - care that we can't use -device */ - if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - (disk->bus != VIR_DOMAIN_DISK_BUS_XEN)) - withDeviceArg = 1; - if (!(optstr = qemuBuildDriveStr(disk, bootable, - (withDeviceArg ? qemuCmdFlags : - (qemuCmdFlags & ~QEMUD_CMD_FLAG_DEVICE))))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - - if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK && - disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_RBD) { - for (j = 0 ; j < disk->nhosts ; j++) { - if (!has_rbd_hosts) { - virBufferAddLit(&rbd_hosts, "CEPH_ARGS=-m "); - has_rbd_hosts = true; - } else { - virBufferAddLit(&rbd_hosts, ","); - } - virDomainDiskHostDefPtr host = &disk->hosts[j]; - if (host->port) { - virBufferVSprintf(&rbd_hosts, "%s:%s", - host->name, - host->port); - } else { - virBufferVSprintf(&rbd_hosts, "%s", - host->name); - } - } - } - - if (withDeviceArg) { - if (disk->bus == VIR_DOMAIN_DISK_BUS_FDC) { - virCommandAddArg(cmd, "-global"); - virCommandAddArgFormat(cmd, "isa-fdc.drive%c=drive-%s", - disk->info.addr.drive.unit - ? 'B' : 'A', - disk->info.alias); - } else { - virCommandAddArg(cmd, "-device"); - - if (!(optstr = qemuBuildDriveDevStr(disk))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - } - } - } - } else { - for (i = 0 ; i < def->ndisks ; i++) { - char dev[NAME_MAX]; - char file[PATH_MAX]; - virDomainDiskDefPtr disk = def->disks[i]; - int j; - - if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) { - if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) { - virCommandAddArg(cmd, "-usbdevice"); - virCommandAddArgFormat(cmd, "disk:%s", disk->src); - } else { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported usb disk type for '%s'"), - disk->src); - goto error; - } - continue; - } - - if (STREQ(disk->dst, "hdc") && - disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) { - if (disk->src) { - snprintf(dev, NAME_MAX, "-%s", "cdrom"); - } else { - continue; - } - } else { - if (STRPREFIX(disk->dst, "hd") || - STRPREFIX(disk->dst, "fd")) { - snprintf(dev, NAME_MAX, "-%s", disk->dst); - } else { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk type '%s'"), disk->dst); - goto error; - } - } - - if (disk->type == VIR_DOMAIN_DISK_TYPE_DIR) { - /* QEMU only supports magic FAT format for now */ - if (disk->driverType && - STRNEQ(disk->driverType, "fat")) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported disk driver type for '%s'"), - disk->driverType); - goto error; - } - if (!disk->readonly) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot create virtual FAT disks in read-write mode")); - goto error; - } - if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) - snprintf(file, PATH_MAX, "fat:floppy:%s", disk->src); - else - snprintf(file, PATH_MAX, "fat:%s", disk->src); - } else if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { - switch (disk->protocol) { - case VIR_DOMAIN_DISK_PROTOCOL_NBD: - if (disk->nhosts != 1) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("NBD accepts only one host")); - goto error; - } - snprintf(file, PATH_MAX, "nbd:%s:%s,", - disk->hosts->name, disk->hosts->port); - break; - case VIR_DOMAIN_DISK_PROTOCOL_RBD: - snprintf(file, PATH_MAX, "rbd:%s,", disk->src); - for (j = 0 ; j < disk->nhosts ; j++) { - if (!has_rbd_hosts) { - virBufferAddLit(&rbd_hosts, "CEPH_ARGS=-m "); - has_rbd_hosts = true; - } else { - virBufferAddLit(&rbd_hosts, ","); - } - virDomainDiskHostDefPtr host = &disk->hosts[j]; - if (host->port) { - virBufferVSprintf(&rbd_hosts, "%s:%s", - host->name, - host->port); - } else { - virBufferVSprintf(&rbd_hosts, "%s", - host->name); - } - } - break; - case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: - if (disk->nhosts == 0) - snprintf(file, PATH_MAX, "sheepdog:%s,", disk->src); - else - /* only one host is supported now */ - snprintf(file, PATH_MAX, "sheepdog:%s:%s:%s,", - disk->hosts->name, disk->hosts->port, - disk->src); - break; - } - } else { - snprintf(file, PATH_MAX, "%s", disk->src); - } - - virCommandAddArgList(cmd, dev, file, NULL); - } - } - - if (has_rbd_hosts) - virCommandAddEnvBuffer(cmd, &rbd_hosts); - - if (qemuCmdFlags & QEMUD_CMD_FLAG_FSDEV) { - for (i = 0 ; i < def->nfss ; i++) { - char *optstr; - virDomainFSDefPtr fs = def->fss[i]; - - virCommandAddArg(cmd, "-fsdev"); - if (!(optstr = qemuBuildFSStr(fs, qemuCmdFlags))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - - virCommandAddArg(cmd, "-device"); - if (!(optstr = qemuBuildFSDevStr(fs))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - } - } else { - if (def->nfss) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("filesystem passthrough not supported by this QEMU")); - goto error; - } - } - - if (!def->nnets) { - /* If we have -device, then we set -nodefault already */ - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) - virCommandAddArgList(cmd, "-net", "none", NULL); - } else { - for (i = 0 ; i < def->nnets ; i++) { - virDomainNetDefPtr net = def->nets[i]; - char *nic, *host; - char tapfd_name[50]; - char vhostfd_name[50] = ""; - int vlan; - - /* VLANs are not used with -netdev, so don't record them */ - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) - vlan = -1; - else - vlan = i; - - if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK || - net->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { - int tapfd = qemudNetworkIfaceConnect(conn, driver, net, - qemuCmdFlags); - if (tapfd < 0) - goto error; - - last_good_net = i; - virCommandTransferFD(cmd, tapfd); - - if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", - tapfd) >= sizeof(tapfd_name)) - goto no_memory; - } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - int tapfd = qemudPhysIfaceConnect(conn, driver, net, - qemuCmdFlags, - def->uuid, - vmop); - if (tapfd < 0) - goto error; - - last_good_net = i; - virCommandTransferFD(cmd, tapfd); - - if (snprintf(tapfd_name, sizeof(tapfd_name), "%d", - tapfd) >= sizeof(tapfd_name)) - goto no_memory; - } - - if (net->type == VIR_DOMAIN_NET_TYPE_NETWORK || - net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || - net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - /* Attempt to use vhost-net mode for these types of - network device */ - int vhostfd = qemudOpenVhostNet(net, qemuCmdFlags); - if (vhostfd >= 0) { - virCommandTransferFD(cmd, vhostfd); - - if (snprintf(vhostfd_name, sizeof(vhostfd_name), "%d", - vhostfd) >= sizeof(vhostfd_name)) - goto no_memory; - } - } - /* Possible combinations: - * - * 1. Old way: -net nic,model=e1000,vlan=1 -net tap,vlan=1 - * 2. Semi-new: -device e1000,vlan=1 -net tap,vlan=1 - * 3. Best way: -netdev type=tap,id=netdev1 -device e1000,id=netdev1 - * - * NB, no support for -netdev without use of -device - */ - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - virCommandAddArg(cmd, "-netdev"); - if (!(host = qemuBuildHostNetStr(net, ',', vlan, - tapfd_name, vhostfd_name))) - goto error; - virCommandAddArg(cmd, host); - VIR_FREE(host); - } - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - virCommandAddArg(cmd, "-device"); - if (!(nic = qemuBuildNicDevStr(net, vlan))) - goto error; - virCommandAddArg(cmd, nic); - VIR_FREE(nic); - } else { - virCommandAddArg(cmd, "-net"); - if (!(nic = qemuBuildNicStr(net, "nic,", vlan))) - goto error; - virCommandAddArg(cmd, nic); - VIR_FREE(nic); - } - if (!((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE))) { - virCommandAddArg(cmd, "-net"); - if (!(host = qemuBuildHostNetStr(net, ',', vlan, - tapfd_name, vhostfd_name))) - goto error; - virCommandAddArg(cmd, host); - VIR_FREE(host); - } - } - } - - if (!def->nserials) { - /* If we have -device, then we set -nodefault already */ - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) - virCommandAddArgList(cmd, "-serial", "none", NULL); - } else { - for (i = 0 ; i < def->nserials ; i++) { - virDomainChrDefPtr serial = def->serials[i]; - char *devstr; - - /* Use -chardev with -device if they are available */ - if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(serial))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - - virCommandAddArg(cmd, "-device"); - virCommandAddArgFormat(cmd, "isa-serial,chardev=%s", - serial->info.alias); - } else { - virCommandAddArg(cmd, "-serial"); - if (!(devstr = qemuBuildChrArgStr(serial, NULL))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } - } - } - - if (!def->nparallels) { - /* If we have -device, then we set -nodefault already */ - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) - virCommandAddArgList(cmd, "-parallel", "none", NULL); - } else { - for (i = 0 ; i < def->nparallels ; i++) { - virDomainChrDefPtr parallel = def->parallels[i]; - char *devstr; - - /* Use -chardev with -device if they are available */ - if ((qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(parallel))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - - virCommandAddArg(cmd, "-device"); - virCommandAddArgFormat(cmd, "isa-parallel,chardev=%s", - parallel->info.alias); - } else { - virCommandAddArg(cmd, "-parallel"); - if (!(devstr = qemuBuildChrArgStr(parallel, NULL))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } - } - } - - for (i = 0 ; i < def->nchannels ; i++) { - virDomainChrDefPtr channel = def->channels[i]; - char *devstr; - - switch(channel->targetType) { - case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV) || - !(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("guestfwd requires QEMU to support -chardev & -device")); - goto error; - } - - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(channel))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - - char *addr = virSocketFormatAddr(channel->target.addr); - if (!addr) - goto error; - int port = virSocketGetPort(channel->target.addr); - - virCommandAddArg(cmd, "-netdev"); - virCommandAddArgFormat(cmd, - "user,guestfwd=tcp:%s:%i,chardev=%s,id=user-%s", - addr, port, channel->info.alias, - channel->info.alias); - VIR_FREE(addr); - break; - - case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("virtio channel requires QEMU to support -device")); - goto error; - } - - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(channel))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - - virCommandAddArg(cmd, "-device"); - if (!(devstr = qemuBuildVirtioSerialPortDevStr(channel))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - break; - } - } - - /* Explicit console devices */ - if (def->console) { - virDomainChrDefPtr console = def->console; - char *devstr; - - switch(console->targetType) { - case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO: - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - qemuReportError(VIR_ERR_NO_SUPPORT, "%s", - _("virtio channel requires QEMU to support -device")); - goto error; - } - - virCommandAddArg(cmd, "-chardev"); - if (!(devstr = qemuBuildChrChardevStr(console))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - - virCommandAddArg(cmd, "-device"); - if (!(devstr = qemuBuildVirtioSerialPortDevStr(console))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - break; - - case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL: - break; - - default: - qemuReportError(VIR_ERR_NO_SUPPORT, - _("unsupported console target type %s"), - NULLSTR(virDomainChrConsoleTargetTypeToString(console->targetType))); - goto error; - } - } - - virCommandAddArg(cmd, "-usb"); - for (i = 0 ; i < def->ninputs ; i++) { - virDomainInputDefPtr input = def->inputs[i]; - - if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) { - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - char *optstr; - virCommandAddArg(cmd, "-device"); - if (!(optstr = qemuBuildUSBInputDevStr(input))) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - } else { - virCommandAddArgList(cmd, "-usbdevice", - input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE - ? "mouse" : "tablet", NULL); - } - } - } - - if (def->ngraphics > 1) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("only 1 graphics device is supported")); - goto error; - } - - if ((def->ngraphics == 1) && - def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { - virBuffer opt = VIR_BUFFER_INITIALIZER; - - if (qemuCmdFlags & QEMUD_CMD_FLAG_VNC_COLON) { - if (def->graphics[0]->data.vnc.listenAddr) - virBufferAdd(&opt, def->graphics[0]->data.vnc.listenAddr, -1); - else if (driver->vncListen) - virBufferAdd(&opt, driver->vncListen, -1); - - virBufferVSprintf(&opt, ":%d", - def->graphics[0]->data.vnc.port - 5900); - - if (def->graphics[0]->data.vnc.auth.passwd || - driver->vncPassword) - virBufferAddLit(&opt, ",password"); - - if (driver->vncTLS) { - virBufferAddLit(&opt, ",tls"); - if (driver->vncTLSx509verify) { - virBufferVSprintf(&opt, ",x509verify=%s", - driver->vncTLSx509certdir); - } else { - virBufferVSprintf(&opt, ",x509=%s", - driver->vncTLSx509certdir); - } - } - - if (driver->vncSASL) { - virBufferAddLit(&opt, ",sasl"); - - if (driver->vncSASLdir) - virCommandAddEnvPair(cmd, "SASL_CONF_DIR", - driver->vncSASLdir); - - /* TODO: Support ACLs later */ - } - } else { - virBufferVSprintf(&opt, "%d", - def->graphics[0]->data.vnc.port - 5900); - } - - virCommandAddArg(cmd, "-vnc"); - virCommandAddArgBuffer(cmd, &opt); - if (def->graphics[0]->data.vnc.keymap) { - virCommandAddArgList(cmd, "-k", def->graphics[0]->data.vnc.keymap, - NULL); - } - - /* Unless user requested it, set the audio backend to none, to - * prevent it opening the host OS audio devices, since that causes - * security issues and might not work when using VNC. - */ - if (driver->vncAllowHostAudio) { - virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); - } else { - virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none"); - } - } else if ((def->ngraphics == 1) && - def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { - if (def->graphics[0]->data.sdl.xauth) - virCommandAddEnvPair(cmd, "XAUTHORITY", - def->graphics[0]->data.sdl.xauth); - if (def->graphics[0]->data.sdl.display) - virCommandAddEnvPair(cmd, "DISPLAY", - def->graphics[0]->data.sdl.display); - if (def->graphics[0]->data.sdl.fullscreen) - virCommandAddArg(cmd, "-full-screen"); - - /* If using SDL for video, then we should just let it - * use QEMU's host audio drivers, possibly SDL too - * User can set these two before starting libvirtd - */ - virCommandAddEnvPass(cmd, "QEMU_AUDIO_DRV"); - virCommandAddEnvPass(cmd, "SDL_AUDIODRIVER"); - - /* New QEMU has this flag to let us explicitly ask for - * SDL graphics. This is better than relying on the - * default, since the default changes :-( */ - if (qemuCmdFlags & QEMUD_CMD_FLAG_SDL) - virCommandAddArg(cmd, "-sdl"); - - } else if ((def->ngraphics == 1) && - def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { - virBuffer opt = VIR_BUFFER_INITIALIZER; - - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_SPICE)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("spice graphics are not supported with this QEMU")); - goto error; - } - - virBufferVSprintf(&opt, "port=%u", def->graphics[0]->data.spice.port); - - if (driver->spiceTLS && def->graphics[0]->data.spice.tlsPort != -1) - virBufferVSprintf(&opt, ",tls-port=%u", def->graphics[0]->data.spice.tlsPort); - - if (def->graphics[0]->data.spice.listenAddr) - virBufferVSprintf(&opt, ",addr=%s", def->graphics[0]->data.spice.listenAddr); - else if (driver->spiceListen) - virBufferVSprintf(&opt, ",addr=%s", driver->spiceListen); - - /* In the password case we set it via monitor command, to avoid - * making it visible on CLI, so there's no use of password=XXX - * in this bit of the code */ - if (!def->graphics[0]->data.spice.auth.passwd && - !driver->spicePassword) - virBufferAddLit(&opt, ",disable-ticketing"); - - if (driver->spiceTLS) - virBufferVSprintf(&opt, ",x509-dir=%s", - driver->spiceTLSx509certdir); - - for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) { - int mode = def->graphics[0]->data.spice.channels[i]; - switch (mode) { - case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE: - virBufferVSprintf(&opt, ",tls-channel=%s", - virDomainGraphicsSpiceChannelNameTypeToString(i)); - break; - case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE: - virBufferVSprintf(&opt, ",plaintext-channel=%s", - virDomainGraphicsSpiceChannelNameTypeToString(i)); - break; - } - } - - virCommandAddArg(cmd, "-spice"); - virCommandAddArgBuffer(cmd, &opt); - if (def->graphics[0]->data.spice.keymap) - virCommandAddArgList(cmd, "-k", - def->graphics[0]->data.spice.keymap, NULL); - /* SPICE includes native support for tunnelling audio, so we - * set the audio backend to point at SPICE's own driver - */ - virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=spice"); - - } else if ((def->ngraphics == 1)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported graphics type '%s'"), - virDomainGraphicsTypeToString(def->graphics[0]->type)); - goto error; - } - - if (def->nvideos > 0) { - if (qemuCmdFlags & QEMUD_CMD_FLAG_VGA) { - if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_XEN) { - /* nothing - vga has no effect on Xen pvfb */ - } else { - if ((def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_QXL) && - !(qemuCmdFlags & QEMUD_CMD_FLAG_VGA_QXL)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("This QEMU does not support QXL graphics adapters")); - goto error; - } - - const char *vgastr = qemuVideoTypeToString(def->videos[0]->type); - if (!vgastr || STREQ(vgastr, "")) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("video type %s is not supported with QEMU"), - virDomainVideoTypeToString(def->videos[0]->type)); - goto error; - } - - virCommandAddArgList(cmd, "-vga", vgastr, NULL); - } - } else { - - switch (def->videos[0]->type) { - case VIR_DOMAIN_VIDEO_TYPE_VGA: - virCommandAddArg(cmd, "-std-vga"); - break; - - case VIR_DOMAIN_VIDEO_TYPE_VMVGA: - virCommandAddArg(cmd, "-vmwarevga"); - break; - - case VIR_DOMAIN_VIDEO_TYPE_XEN: - case VIR_DOMAIN_VIDEO_TYPE_CIRRUS: - /* No special args - this is the default */ - break; - - default: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("video type %s is not supported with this QEMU"), - virDomainVideoTypeToString(def->videos[0]->type)); - goto error; - } - } - - if (def->nvideos > 1) { - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - for (i = 1 ; i < def->nvideos ; i++) { - char *str; - if (def->videos[i]->type != VIR_DOMAIN_VIDEO_TYPE_QXL) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("video type %s is only valid as primary video card"), - virDomainVideoTypeToString(def->videos[0]->type)); - goto error; - } - - virCommandAddArg(cmd, "-device"); - - if (!(str = qemuBuildVideoDevStr(def->videos[i]))) - goto error; - - virCommandAddArg(cmd, str); - VIR_FREE(str); - } - } else { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("only one video card is currently supported")); - goto error; - } - } - - } else { - /* If we have -device, then we set -nodefault already */ - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - (qemuCmdFlags & QEMUD_CMD_FLAG_VGA) && - (qemuCmdFlags & QEMUD_CMD_FLAG_VGA_NONE)) - virCommandAddArgList(cmd, "-vga", "none", NULL); - } - - /* Add sound hardware */ - if (def->nsounds) { - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - for (i = 0 ; i < def->nsounds ; i++) { - virDomainSoundDefPtr sound = def->sounds[i]; - char *str = NULL; - - /* Sadly pcspk device doesn't use -device syntax. Fortunately - * we don't need to set any PCI address on it, so we don't - * mind too much */ - if (sound->model == VIR_DOMAIN_SOUND_MODEL_PCSPK) { - virCommandAddArgList(cmd, "-soundhw", "pcspk", NULL); - } else { - virCommandAddArg(cmd, "-device"); - - if (!(str = qemuBuildSoundDevStr(sound))) - goto error; - - virCommandAddArg(cmd, str); - VIR_FREE(str); - } - } - } else { - int size = 100; - char *modstr; - if (VIR_ALLOC_N(modstr, size+1) < 0) - goto no_memory; - - for (i = 0 ; i < def->nsounds && size > 0 ; i++) { - virDomainSoundDefPtr sound = def->sounds[i]; - const char *model = virDomainSoundModelTypeToString(sound->model); - if (!model) { - VIR_FREE(modstr); - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("invalid sound model")); - goto error; - } - strncat(modstr, model, size); - size -= strlen(model); - if (i < (def->nsounds - 1)) - strncat(modstr, ",", size--); - } - virCommandAddArgList(cmd, "-soundhw", modstr, NULL); - VIR_FREE(modstr); - } - } - - /* Add watchdog hardware */ - if (def->watchdog) { - virDomainWatchdogDefPtr watchdog = def->watchdog; - char *optstr; - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - virCommandAddArg(cmd, "-device"); - - optstr = qemuBuildWatchdogDevStr(watchdog); - if (!optstr) - goto error; - } else { - virCommandAddArg(cmd, "-watchdog"); - - const char *model = virDomainWatchdogModelTypeToString(watchdog->model); - if (!model) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("missing watchdog model")); - goto error; - } - - if (!(optstr = strdup(model))) - goto no_memory; - } - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - - int act = watchdog->action; - if (act == VIR_DOMAIN_WATCHDOG_ACTION_DUMP) - act = VIR_DOMAIN_WATCHDOG_ACTION_PAUSE; - const char *action = virDomainWatchdogActionTypeToString(act); - if (!action) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("invalid watchdog action")); - goto error; - } - virCommandAddArgList(cmd, "-watchdog-action", action, NULL); - } - - /* Add host passthrough hardware */ - for (i = 0 ; i < def->nhostdevs ; i++) { - virDomainHostdevDefPtr hostdev = def->hostdevs[i]; - char *devstr; - - /* USB */ - if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && - hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) { - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - virCommandAddArg(cmd, "-device"); - if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } else { - virCommandAddArg(cmd, "-usbdevice"); - if (!(devstr = qemuBuildUSBHostdevUsbDevStr(hostdev))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } - } - - /* PCI */ - if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && - hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) { - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - char *configfd_name = NULL; - if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) { - int configfd = qemudOpenPCIConfig(hostdev); - - if (configfd >= 0) { - if (virAsprintf(&configfd_name, "%d", configfd) < 0) { - VIR_FORCE_CLOSE(configfd); - virReportOOMError(); - goto no_memory; - } - - virCommandTransferFD(cmd, configfd); - } - } - virCommandAddArg(cmd, "-device"); - devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name); - VIR_FREE(configfd_name); - if (!devstr) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } else if (qemuCmdFlags & QEMUD_CMD_FLAG_PCIDEVICE) { - virCommandAddArg(cmd, "-pcidevice"); - if (!(devstr = qemuBuildPCIHostdevPCIDevStr(hostdev))) - goto error; - virCommandAddArg(cmd, devstr); - VIR_FREE(devstr); - } else { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("PCI device assignment is not supported by this version of qemu")); - goto error; - } - } - } - - if (migrateFrom) - virCommandAddArgList(cmd, "-incoming", migrateFrom, NULL); - - /* QEMU changed its default behavior to not include the virtio balloon - * device. Explicitly request it to ensure it will be present. - * - * NB: Earlier we declared that VirtIO balloon will always be in - * slot 0x3 on bus 0x0 - */ - if ((def->memballoon) && - (def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_NONE)) { - if (def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("Memory balloon device type '%s' is not supported by this version of qemu"), - virDomainMemballoonModelTypeToString(def->memballoon->model)); - goto error; - } - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - char *optstr; - virCommandAddArg(cmd, "-device"); - - optstr = qemuBuildMemballoonDevStr(def->memballoon); - if (!optstr) - goto error; - virCommandAddArg(cmd, optstr); - VIR_FREE(optstr); - } else if (qemuCmdFlags & QEMUD_CMD_FLAG_BALLOON) { - virCommandAddArgList(cmd, "-balloon", "virtio", NULL); - } - } - - if (current_snapshot && current_snapshot->def->active) - virCommandAddArgList(cmd, "-loadvm", current_snapshot->def->name, - NULL); - - if (def->namespaceData) { - qemuDomainCmdlineDefPtr qemucmd; - - qemucmd = def->namespaceData; - for (i = 0; i < qemucmd->num_args; i++) - virCommandAddArg(cmd, qemucmd->args[i]); - for (i = 0; i < qemucmd->num_env; i++) - virCommandAddEnvPair(cmd, qemucmd->env_name[i], - qemucmd->env_value[i] - ? qemucmd->env_value[i] : ""); - } - - return cmd; - - no_memory: - virReportOOMError(); - error: - for (i = 0; i <= last_good_net; i++) - virDomainConfNWFilterTeardown(def->nets[i]); - virBufferFreeAndReset(&rbd_hosts); - virCommandFree(cmd); - return NULL; -} - - -/* - * This method takes a string representing a QEMU command line ARGV set - * optionally prefixed by a list of environment variables. It then tries - * to split it up into a NULL terminated list of env & argv, splitting - * on space - */ -static int qemuStringToArgvEnv(const char *args, - const char ***retenv, - const char ***retargv) -{ - char **arglist = NULL; - int argcount = 0; - int argalloc = 0; - int envend; - int i; - const char *curr = args; - const char *start; - const char **progenv = NULL; - const char **progargv = NULL; - - /* Iterate over string, splitting on sequences of ' ' */ - while (curr && *curr != '\0') { - char *arg; - const char *next; - - start = curr; - /* accept a space in CEPH_ARGS */ - if (STRPREFIX(curr, "CEPH_ARGS=-m ")) { - start += strlen("CEPH_ARGS=-m "); - } - if (*start == '\'') { - if (start == curr) - curr++; - next = strchr(start + 1, '\''); - } else if (*start == '"') { - if (start == curr) - curr++; - next = strchr(start + 1, '"'); - } else { - next = strchr(start, ' '); - } - if (!next) - next = strchr(curr, '\n'); - - if (next) { - arg = strndup(curr, next-curr); - if (*next == '\'' || - *next == '"') - next++; - } else { - arg = strdup(curr); - } - - if (!arg) - goto no_memory; - - if (argalloc == argcount) { - if (VIR_REALLOC_N(arglist, argalloc+10) < 0) { - VIR_FREE(arg); - goto no_memory; - } - argalloc+=10; - } - - arglist[argcount++] = arg; - - while (next && c_isspace(*next)) - next++; - - curr = next; - } - - /* Iterate over list of args, finding first arg not containing - * the '=' character (eg, skip over env vars FOO=bar) */ - for (envend = 0 ; ((envend < argcount) && - (strchr(arglist[envend], '=') != NULL)); - envend++) - ; /* nada */ - - /* Copy the list of env vars */ - if (envend > 0) { - if (VIR_REALLOC_N(progenv, envend+1) < 0) - goto no_memory; - for (i = 0 ; i < envend ; i++) { - progenv[i] = arglist[i]; - arglist[i] = NULL; - } - progenv[i] = NULL; - } - - /* Copy the list of argv */ - if (VIR_REALLOC_N(progargv, argcount-envend + 1) < 0) - goto no_memory; - for (i = envend ; i < argcount ; i++) - progargv[i-envend] = arglist[i]; - progargv[i-envend] = NULL; - - VIR_FREE(arglist); - - *retenv = progenv; - *retargv = progargv; - - return 0; - -no_memory: - for (i = 0 ; progenv && progenv[i] ; i++) - VIR_FREE(progenv[i]); - VIR_FREE(progenv); - for (i = 0 ; i < argcount ; i++) - VIR_FREE(arglist[i]); - VIR_FREE(arglist); - virReportOOMError(); - return -1; -} - - -/* - * Search for a named env variable, and return the value part - */ -static const char *qemuFindEnv(const char **progenv, - const char *name) -{ - int i; - int len = strlen(name); - - for (i = 0 ; progenv && progenv[i] ; i++) { - if (STREQLEN(progenv[i], name, len) && - progenv[i][len] == '=') - return progenv[i] + len + 1; - } - return NULL; -} - -/* - * Takes a string containing a set of key=value,key=value,key... - * parameters and splits them up, returning two arrays with - * the individual keys and values. If allowEmptyValue is nonzero, - * the "=value" part is optional and if a key with no value is found, - * NULL is be placed into corresponding place in retvalues. - */ -int -qemuParseKeywords(const char *str, - char ***retkeywords, - char ***retvalues, - int allowEmptyValue) -{ - int keywordCount = 0; - int keywordAlloc = 0; - char **keywords = NULL; - char **values = NULL; - const char *start = str; - const char *end; - int i; - - *retkeywords = NULL; - *retvalues = NULL; - end = start + strlen(str); - - while (start) { - const char *separator; - const char *endmark; - char *keyword; - char *value = NULL; - - if (!(endmark = strchr(start, ','))) - endmark = end; - if (!(separator = strchr(start, '='))) - separator = end; - - if (separator >= endmark) { - if (!allowEmptyValue) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("malformed keyword arguments in '%s'"), str); - goto error; - } - separator = endmark; - } - - if (!(keyword = strndup(start, separator - start))) - goto no_memory; - - if (separator < endmark) { - separator++; - if (!(value = strndup(separator, endmark - separator))) { - VIR_FREE(keyword); - goto no_memory; - } - } - - if (keywordAlloc == keywordCount) { - if (VIR_REALLOC_N(keywords, keywordAlloc + 10) < 0 || - VIR_REALLOC_N(values, keywordAlloc + 10) < 0) { - VIR_FREE(keyword); - VIR_FREE(value); - goto no_memory; - } - keywordAlloc += 10; - } - - keywords[keywordCount] = keyword; - values[keywordCount] = value; - keywordCount++; - - start = endmark < end ? endmark + 1 : NULL; - } - - *retkeywords = keywords; - *retvalues = values; - - return keywordCount; - -no_memory: - virReportOOMError(); -error: - for (i = 0 ; i < keywordCount ; i++) { - VIR_FREE(keywords[i]); - VIR_FREE(values[i]); - } - VIR_FREE(keywords); - VIR_FREE(values); - return -1; -} - -/* - * Tries to parse new style QEMU -drive args. - * - * eg -drive file=/dev/HostVG/VirtData1,if=ide,index=1 - * - * Will fail if not using the 'index' keyword - */ -static virDomainDiskDefPtr -qemuParseCommandLineDisk(virCapsPtr caps, - const char *val, - int nvirtiodisk) -{ - virDomainDiskDefPtr def = NULL; - char **keywords; - char **values; - int nkeywords; - int i; - int idx = -1; - int busid = -1; - int unitid = -1; - - if ((nkeywords = qemuParseKeywords(val, - &keywords, - &values, 0)) < 0) - return NULL; - - if (VIR_ALLOC(def) < 0) { - virReportOOMError(); - goto cleanup; - } - - def->bus = VIR_DOMAIN_DISK_BUS_IDE; - def->device = VIR_DOMAIN_DISK_DEVICE_DISK; - def->type = VIR_DOMAIN_DISK_TYPE_FILE; - - for (i = 0 ; i < nkeywords ; i++) { - if (STREQ(keywords[i], "file")) { - if (values[i] && STRNEQ(values[i], "")) { - def->src = values[i]; - values[i] = NULL; - if (STRPREFIX(def->src, "/dev/")) - def->type = VIR_DOMAIN_DISK_TYPE_BLOCK; - else if (STRPREFIX(def->src, "nbd:")) { - char *host, *port; - - def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; - def->protocol = VIR_DOMAIN_DISK_PROTOCOL_NBD; - host = def->src + strlen("nbd:"); - port = strchr(host, ':'); - if (!port) { - def = NULL; - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse nbd filename '%s'"), def->src); - goto cleanup; - } - *port++ = '\0'; - if (VIR_ALLOC(def->hosts) < 0) { - virReportOOMError(); - goto cleanup; - } - def->nhosts = 1; - def->hosts->name = strdup(host); - if (!def->hosts->name) { - virReportOOMError(); - goto cleanup; - } - def->hosts->port = strdup(port); - if (!def->hosts->port) { - virReportOOMError(); - goto cleanup; - } - - VIR_FREE(def->src); - def->src = NULL; - } else if (STRPREFIX(def->src, "rbd:")) { - char *p = def->src; - - def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; - def->protocol = VIR_DOMAIN_DISK_PROTOCOL_RBD; - def->src = strdup(p + strlen("rbd:")); - if (!def->src) { - virReportOOMError(); - goto cleanup; - } - - VIR_FREE(p); - } else if (STRPREFIX(def->src, "sheepdog:")) { - char *p = def->src; - char *port, *vdi; - - def->type = VIR_DOMAIN_DISK_TYPE_NETWORK; - def->protocol = VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG; - def->src = strdup(p + strlen("sheepdog:")); - if (!def->src) { - virReportOOMError(); - goto cleanup; - } - - /* def->src must be [vdiname] or [host]:[port]:[vdiname] */ - port = strchr(def->src, ':'); - if (port) { - *port++ = '\0'; - vdi = strchr(port, ':'); - if (!vdi) { - def = NULL; - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse sheepdog filename '%s'"), p); - goto cleanup; - } - *vdi++ = '\0'; - if (VIR_ALLOC(def->hosts) < 0) { - virReportOOMError(); - goto cleanup; - } - def->nhosts = 1; - def->hosts->name = def->src; - def->hosts->port = strdup(port); - if (!def->hosts->port) { - virReportOOMError(); - goto cleanup; - } - def->src = strdup(vdi); - if (!def->src) { - virReportOOMError(); - goto cleanup; - } - } - - VIR_FREE(p); - } else - def->type = VIR_DOMAIN_DISK_TYPE_FILE; - } else { - def->type = VIR_DOMAIN_DISK_TYPE_FILE; - } - } else if (STREQ(keywords[i], "if")) { - if (STREQ(values[i], "ide")) - def->bus = VIR_DOMAIN_DISK_BUS_IDE; - else if (STREQ(values[i], "scsi")) - def->bus = VIR_DOMAIN_DISK_BUS_SCSI; - else if (STREQ(values[i], "virtio")) - def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO; - else if (STREQ(values[i], "xen")) - def->bus = VIR_DOMAIN_DISK_BUS_XEN; - } else if (STREQ(keywords[i], "media")) { - if (STREQ(values[i], "cdrom")) { - def->device = VIR_DOMAIN_DISK_DEVICE_CDROM; - def->readonly = 1; - } else if (STREQ(values[i], "floppy")) - def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; - } else if (STREQ(keywords[i], "format")) { - def->driverName = strdup("qemu"); - if (!def->driverName) { - virDomainDiskDefFree(def); - def = NULL; - virReportOOMError(); - goto cleanup; - } - def->driverType = values[i]; - values[i] = NULL; - } else if (STREQ(keywords[i], "cache")) { - if (STREQ(values[i], "off") || - STREQ(values[i], "none")) - def->cachemode = VIR_DOMAIN_DISK_CACHE_DISABLE; - else if (STREQ(values[i], "writeback") || - STREQ(values[i], "on")) - def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITEBACK; - else if (STREQ(values[i], "writethrough")) - def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITETHRU; - } else if (STREQ(keywords[i], "werror") || - STREQ(keywords[i], "rerror")) { - if (STREQ(values[i], "stop")) - def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP; - else if (STREQ(values[i], "ignore")) - def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE; - else if (STREQ(values[i], "enospace")) - def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE; - } else if (STREQ(keywords[i], "index")) { - if (virStrToLong_i(values[i], NULL, 10, &idx) < 0) { - virDomainDiskDefFree(def); - def = NULL; - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse drive index '%s'"), val); - goto cleanup; - } - } else if (STREQ(keywords[i], "bus")) { - if (virStrToLong_i(values[i], NULL, 10, &busid) < 0) { - virDomainDiskDefFree(def); - def = NULL; - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse drive bus '%s'"), val); - goto cleanup; - } - } else if (STREQ(keywords[i], "unit")) { - if (virStrToLong_i(values[i], NULL, 10, &unitid) < 0) { - virDomainDiskDefFree(def); - def = NULL; - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse drive unit '%s'"), val); - goto cleanup; - } - } else if (STREQ(keywords[i], "readonly")) { - if ((values[i] == NULL) || STREQ(values[i], "on")) - def->readonly = 1; - } - } - - if (!def->src && - def->device == VIR_DOMAIN_DISK_DEVICE_DISK && - def->type != VIR_DOMAIN_DISK_TYPE_NETWORK) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("missing file parameter in drive '%s'"), val); - virDomainDiskDefFree(def); - def = NULL; - goto cleanup; - } - if (idx == -1 && - def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) - idx = nvirtiodisk; - - if (idx == -1 && - unitid == -1 && - busid == -1) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("missing index/unit/bus parameter in drive '%s'"), val); - virDomainDiskDefFree(def); - def = NULL; - goto cleanup; - } - - if (idx == -1) { - if (unitid == -1) - unitid = 0; - if (busid == -1) - busid = 0; - switch (def->bus) { - case VIR_DOMAIN_DISK_BUS_IDE: - idx = (busid * 2) + unitid; - break; - case VIR_DOMAIN_DISK_BUS_SCSI: - idx = (busid * 7) + unitid; - break; - default: - idx = unitid; - break; - } - } - - if (def->bus == VIR_DOMAIN_DISK_BUS_IDE) { - def->dst = strdup("hda"); - } else if (def->bus == VIR_DOMAIN_DISK_BUS_SCSI) { - def->dst = strdup("sda"); - } else if (def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { - def->dst = strdup("vda"); - } else if (def->bus == VIR_DOMAIN_DISK_BUS_XEN) { - def->dst = strdup("xvda"); - } else { - def->dst = strdup("hda"); - } - - if (!def->dst) { - virDomainDiskDefFree(def); - def = NULL; - virReportOOMError(); - goto cleanup; - } - if (STREQ(def->dst, "xvda")) - def->dst[3] = 'a' + idx; - else - def->dst[2] = 'a' + idx; - - if (virDomainDiskDefAssignAddress(caps, def) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("invalid device name '%s'"), def->dst); - virDomainDiskDefFree(def); - def = NULL; - /* fall through to "cleanup" */ - } - -cleanup: - for (i = 0 ; i < nkeywords ; i++) { - VIR_FREE(keywords[i]); - VIR_FREE(values[i]); - } - VIR_FREE(keywords); - VIR_FREE(values); - return def; -} - -/* - * Tries to find a NIC definition matching a vlan we want - */ -static const char * -qemuFindNICForVLAN(int nnics, - const char **nics, - int wantvlan) -{ - int i; - for (i = 0 ; i < nnics ; i++) { - int gotvlan; - const char *tmp = strstr(nics[i], "vlan="); - char *end; - if (!tmp) - continue; - - tmp += strlen("vlan="); - - if (virStrToLong_i(tmp, &end, 10, &gotvlan) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse NIC vlan in '%s'"), nics[i]); - return NULL; - } - - if (gotvlan == wantvlan) - return nics[i]; - } - - if (wantvlan == 0 && nnics > 0) - return nics[0]; - - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot find NIC definition for vlan %d"), wantvlan); - return NULL; -} - - -/* - * Tries to parse a QEMU -net backend argument. Gets given - * a list of all known -net frontend arguments to try and - * match up against. Horribly complicated stuff - */ -static virDomainNetDefPtr -qemuParseCommandLineNet(virCapsPtr caps, - const char *val, - int nnics, - const char **nics) -{ - virDomainNetDefPtr def = NULL; - char **keywords = NULL; - char **values = NULL; - int nkeywords; - const char *nic; - int wantvlan = 0; - const char *tmp; - int genmac = 1; - int i; - - tmp = strchr(val, ','); - - if (tmp) { - if ((nkeywords = qemuParseKeywords(tmp+1, - &keywords, - &values, 0)) < 0) - return NULL; - } else { - nkeywords = 0; - } - - if (VIR_ALLOC(def) < 0) { - virReportOOMError(); - goto cleanup; - } - - /* 'tap' could turn into libvirt type=ethernet, type=bridge or - * type=network, but we can't tell, so use the generic config */ - if (STRPREFIX(val, "tap,")) - def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; - else if (STRPREFIX(val, "socket")) - def->type = VIR_DOMAIN_NET_TYPE_CLIENT; - else if (STRPREFIX(val, "user")) - def->type = VIR_DOMAIN_NET_TYPE_USER; - else - def->type = VIR_DOMAIN_NET_TYPE_ETHERNET; - - for (i = 0 ; i < nkeywords ; i++) { - if (STREQ(keywords[i], "vlan")) { - if (virStrToLong_i(values[i], NULL, 10, &wantvlan) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse vlan in '%s'"), val); - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && - STREQ(keywords[i], "script") && STRNEQ(values[i], "")) { - def->data.ethernet.script = values[i]; - values[i] = NULL; - } else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && - STREQ(keywords[i], "ifname")) { - def->ifname = values[i]; - values[i] = NULL; - } - } - - - /* Done parsing the nic backend. Now to try and find corresponding - * frontend, based off vlan number. NB this assumes a 1-1 mapping - */ - - nic = qemuFindNICForVLAN(nnics, nics, wantvlan); - if (!nic) { - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - - if (!STRPREFIX(nic, "nic")) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse NIC definition '%s'"), nic); - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - - for (i = 0 ; i < nkeywords ; i++) { - VIR_FREE(keywords[i]); - VIR_FREE(values[i]); - } - VIR_FREE(keywords); - VIR_FREE(values); - - if (STRPREFIX(nic, "nic,")) { - if ((nkeywords = qemuParseKeywords(nic + strlen("nic,"), - &keywords, - &values, 0)) < 0) { - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - } else { - nkeywords = 0; - } - - for (i = 0 ; i < nkeywords ; i++) { - if (STREQ(keywords[i], "macaddr")) { - genmac = 0; - if (virParseMacAddr(values[i], def->mac) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unable to parse mac address '%s'"), - values[i]); - virDomainNetDefFree(def); - def = NULL; - goto cleanup; - } - } else if (STREQ(keywords[i], "model")) { - def->model = values[i]; - values[i] = NULL; - } - } - - if (genmac) - virCapabilitiesGenerateMac(caps, def->mac); - -cleanup: - for (i = 0 ; i < nkeywords ; i++) { - VIR_FREE(keywords[i]); - VIR_FREE(values[i]); - } - VIR_FREE(keywords); - VIR_FREE(values); - return def; -} - - -/* - * Tries to parse a QEMU PCI device - */ -static virDomainHostdevDefPtr -qemuParseCommandLinePCI(const char *val) -{ - virDomainHostdevDefPtr def = NULL; - int bus = 0, slot = 0, func = 0; - const char *start; - char *end; - - if (!STRPREFIX(val, "host=")) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown PCI device syntax '%s'"), val); - VIR_FREE(def); - goto cleanup; - } - - start = val + strlen("host="); - if (virStrToLong_i(start, &end, 16, &bus) < 0 || *end != ':') { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract PCI device bus '%s'"), val); - VIR_FREE(def); - goto cleanup; - } - start = end + 1; - if (virStrToLong_i(start, &end, 16, &slot) < 0 || *end != '.') { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract PCI device slot '%s'"), val); - VIR_FREE(def); - goto cleanup; - } - start = end + 1; - if (virStrToLong_i(start, NULL, 16, &func) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract PCI device function '%s'"), val); - VIR_FREE(def); - goto cleanup; - } - - if (VIR_ALLOC(def) < 0) { - virReportOOMError(); - goto cleanup; - } - - def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; - def->managed = 1; - def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI; - def->source.subsys.u.pci.bus = bus; - def->source.subsys.u.pci.slot = slot; - def->source.subsys.u.pci.function = func; - -cleanup: - return def; -} - - -/* - * Tries to parse a QEMU USB device - */ -static virDomainHostdevDefPtr -qemuParseCommandLineUSB(const char *val) -{ - virDomainHostdevDefPtr def = NULL; - int first = 0, second = 0; - const char *start; - char *end; - - if (!STRPREFIX(val, "host:")) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown USB device syntax '%s'"), val); - VIR_FREE(def); - goto cleanup; - } - - start = val + strlen("host:"); - if (strchr(start, ':')) { - if (virStrToLong_i(start, &end, 16, &first) < 0 || *end != ':') { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract USB device vendor '%s'"), val); - VIR_FREE(def); - goto cleanup; - } - start = end + 1; - if (virStrToLong_i(start, NULL, 16, &second) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract USB device product '%s'"), val); - VIR_FREE(def); - goto cleanup; - } - } else { - if (virStrToLong_i(start, &end, 10, &first) < 0 || *end != '.') { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract USB device bus '%s'"), val); - VIR_FREE(def); - goto cleanup; - } - start = end + 1; - if (virStrToLong_i(start, NULL, 10, &second) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot extract USB device address '%s'"), val); - VIR_FREE(def); - goto cleanup; - } - } - - if (VIR_ALLOC(def) < 0) { - virReportOOMError(); - goto cleanup; - } - - def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; - def->managed = 0; - def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB; - if (*end == '.') { - def->source.subsys.u.usb.bus = first; - def->source.subsys.u.usb.device = second; - } else { - def->source.subsys.u.usb.vendor = first; - def->source.subsys.u.usb.product = second; - } - -cleanup: - return def; -} - - -/* - * Tries to parse a QEMU serial/parallel device - */ -static virDomainChrDefPtr -qemuParseCommandLineChr(const char *val) -{ - virDomainChrDefPtr def; - - if (VIR_ALLOC(def) < 0) - goto no_memory; - - if (STREQ(val, "null")) { - def->type = VIR_DOMAIN_CHR_TYPE_NULL; - } else if (STREQ(val, "vc")) { - def->type = VIR_DOMAIN_CHR_TYPE_VC; - } else if (STREQ(val, "pty")) { - def->type = VIR_DOMAIN_CHR_TYPE_PTY; - } else if (STRPREFIX(val, "file:")) { - def->type = VIR_DOMAIN_CHR_TYPE_FILE; - def->data.file.path = strdup(val+strlen("file:")); - if (!def->data.file.path) - goto no_memory; - } else if (STRPREFIX(val, "pipe:")) { - def->type = VIR_DOMAIN_CHR_TYPE_PIPE; - def->data.file.path = strdup(val+strlen("pipe:")); - if (!def->data.file.path) - goto no_memory; - } else if (STREQ(val, "stdio")) { - def->type = VIR_DOMAIN_CHR_TYPE_STDIO; - } else if (STRPREFIX(val, "udp:")) { - const char *svc1, *host2, *svc2; - def->type = VIR_DOMAIN_CHR_TYPE_UDP; - val += strlen("udp:"); - svc1 = strchr(val, ':'); - host2 = svc1 ? strchr(svc1, '@') : NULL; - svc2 = host2 ? strchr(host2, ':') : NULL; - - if (svc1) - def->data.udp.connectHost = strndup(val, svc1-val); - else - def->data.udp.connectHost = strdup(val); - - if (!def->data.udp.connectHost) - goto no_memory; - - if (svc1) { - svc1++; - if (host2) - def->data.udp.connectService = strndup(svc1, host2-svc1); - else - def->data.udp.connectService = strdup(svc1); - - if (!def->data.udp.connectService) - goto no_memory; - } - - if (host2) { - host2++; - if (svc2) - def->data.udp.bindHost = strndup(host2, svc2-host2); - else - def->data.udp.bindHost = strdup(host2); - - if (!def->data.udp.bindHost) - goto no_memory; - } - if (svc2) { - svc2++; - def->data.udp.bindService = strdup(svc2); - if (!def->data.udp.bindService) - goto no_memory; - } - } else if (STRPREFIX(val, "tcp:") || - STRPREFIX(val, "telnet:")) { - const char *opt, *svc; - def->type = VIR_DOMAIN_CHR_TYPE_TCP; - if (STRPREFIX(val, "tcp:")) { - val += strlen("tcp:"); - } else { - val += strlen("telnet:"); - def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; - } - svc = strchr(val, ':'); - if (!svc) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot find port number in character device %s"), val); - goto error; - } - opt = strchr(svc, ','); - if (opt && strstr(opt, "server")) - def->data.tcp.listen = 1; - - def->data.tcp.host = strndup(val, svc-val); - if (!def->data.tcp.host) - goto no_memory; - svc++; - if (opt) { - def->data.tcp.service = strndup(svc, opt-svc); - } else { - def->data.tcp.service = strdup(svc); - } - if (!def->data.tcp.service) - goto no_memory; - } else if (STRPREFIX(val, "unix:")) { - const char *opt; - val += strlen("unix:"); - opt = strchr(val, ','); - def->type = VIR_DOMAIN_CHR_TYPE_UNIX; - if (opt) { - if (strstr(opt, "listen")) - def->data.nix.listen = 1; - def->data.nix.path = strndup(val, opt-val); - } else { - def->data.nix.path = strdup(val); - } - if (!def->data.nix.path) - goto no_memory; - - } else if (STRPREFIX(val, "/dev")) { - def->type = VIR_DOMAIN_CHR_TYPE_DEV; - def->data.file.path = strdup(val); - if (!def->data.file.path) - goto no_memory; - } else { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown character device syntax %s"), val); - goto error; - } - - return def; - -no_memory: - virReportOOMError(); -error: - virDomainChrDefFree(def); - return NULL; -} - - -static virCPUDefPtr -qemuInitGuestCPU(virDomainDefPtr dom) -{ - if (!dom->cpu) { - virCPUDefPtr cpu; - - if (VIR_ALLOC(cpu) < 0) { - virReportOOMError(); - return NULL; - } - - cpu->type = VIR_CPU_TYPE_GUEST; - cpu->match = VIR_CPU_MATCH_EXACT; - dom->cpu = cpu; - } - - return dom->cpu; -} - - -static int -qemuParseCommandLineCPU(virDomainDefPtr dom, - const char *val) -{ - virCPUDefPtr cpu; - const char *p = val; - const char *next; - - if (!(cpu = qemuInitGuestCPU(dom))) - goto error; - - do { - if (*p == '\0' || *p == ',') - goto syntax; - - if ((next = strchr(p, ','))) - next++; - - if (!cpu->model) { - if (next) - cpu->model = strndup(p, next - p - 1); - else - cpu->model = strdup(p); - - if (!cpu->model) - goto no_memory; - } - else if (*p == '+' || *p == '-') { - char *feature; - int policy; - int ret; - - if (*p == '+') - policy = VIR_CPU_FEATURE_REQUIRE; - else - policy = VIR_CPU_FEATURE_DISABLE; - - p++; - if (*p == '\0' || *p == ',') - goto syntax; - - if (next) - feature = strndup(p, next - p - 1); - else - feature = strdup(p); - - ret = virCPUDefAddFeature(cpu, feature, policy); - VIR_FREE(feature); - if (ret < 0) - goto error; - } - } while ((p = next)); - - return 0; - -syntax: - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown CPU syntax '%s'"), val); - goto error; - -no_memory: - virReportOOMError(); -error: - return -1; -} - - -static int -qemuParseCommandLineSmp(virDomainDefPtr dom, - const char *val) -{ - unsigned int sockets = 0; - unsigned int cores = 0; - unsigned int threads = 0; - unsigned int maxcpus = 0; - int i; - int nkws; - char **kws; - char **vals; - int n; - char *end; - int ret; - - nkws = qemuParseKeywords(val, &kws, &vals, 1); - if (nkws < 0) - return -1; - - for (i = 0; i < nkws; i++) { - if (vals[i] == NULL) { - if (i > 0 || - virStrToLong_i(kws[i], &end, 10, &n) < 0 || *end != '\0') - goto syntax; - dom->vcpus = n; - } else { - if (virStrToLong_i(vals[i], &end, 10, &n) < 0 || *end != '\0') - goto syntax; - if (STREQ(kws[i], "sockets")) - sockets = n; - else if (STREQ(kws[i], "cores")) - cores = n; - else if (STREQ(kws[i], "threads")) - threads = n; - else if (STREQ(kws[i], "maxcpus")) - maxcpus = n; - else - goto syntax; - } - } - - dom->maxvcpus = maxcpus ? maxcpus : dom->vcpus; - - if (sockets && cores && threads) { - virCPUDefPtr cpu; - - if (!(cpu = qemuInitGuestCPU(dom))) - goto error; - cpu->sockets = sockets; - cpu->cores = cores; - cpu->threads = threads; - } else if (sockets || cores || threads) - goto syntax; - - ret = 0; - -cleanup: - for (i = 0; i < nkws; i++) { - VIR_FREE(kws[i]); - VIR_FREE(vals[i]); - } - VIR_FREE(kws); - VIR_FREE(vals); - - return ret; - -syntax: - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse CPU topology '%s'"), val); -error: - ret = -1; - goto cleanup; -} - - -/* - * Analyse the env and argv settings and reconstruct a - * virDomainDefPtr representing these settings as closely - * as is practical. This is not an exact science.... - */ -virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, - const char **progenv, - const char **progargv) -{ - virDomainDefPtr def; - int i; - int nographics = 0; - int fullscreen = 0; - char *path; - int nnics = 0; - const char **nics = NULL; - int video = VIR_DOMAIN_VIDEO_TYPE_CIRRUS; - int nvirtiodisk = 0; - qemuDomainCmdlineDefPtr cmd; - - if (!progargv[0]) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("no emulator path found")); - return NULL; - } - - if (VIR_ALLOC(def) < 0) - goto no_memory; - - /* allocate the cmdlinedef up-front; if it's unused, we'll free it later */ - if (VIR_ALLOC(cmd) < 0) - goto no_memory; - - virUUIDGenerate(def->uuid); - - def->id = -1; - def->mem.cur_balloon = def->mem.max_balloon = 64 * 1024; - def->maxvcpus = 1; - def->vcpus = 1; - def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC; - def->features = (1 << VIR_DOMAIN_FEATURE_ACPI) - /*| (1 << VIR_DOMAIN_FEATURE_APIC)*/; - def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART; - def->onCrash = VIR_DOMAIN_LIFECYCLE_DESTROY; - def->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY; - def->virtType = VIR_DOMAIN_VIRT_QEMU; - if (!(def->emulator = strdup(progargv[0]))) - goto no_memory; - - if (strstr(def->emulator, "kvm")) { - def->virtType = VIR_DOMAIN_VIRT_KVM; - def->features |= (1 << VIR_DOMAIN_FEATURE_PAE); - } - - - if (strstr(def->emulator, "xenner")) { - def->virtType = VIR_DOMAIN_VIRT_KVM; - def->os.type = strdup("xen"); - } else { - def->os.type = strdup("hvm"); - } - if (!def->os.type) - goto no_memory; - - if (STRPREFIX(def->emulator, "qemu")) - path = def->emulator; - else - path = strstr(def->emulator, "qemu"); - if (path && - STRPREFIX(path, "qemu-system-")) - def->os.arch = strdup(path + strlen("qemu-system-")); - else - def->os.arch = strdup("i686"); - if (!def->os.arch) - goto no_memory; - -#define WANT_VALUE() \ - const char *val = progargv[++i]; \ - if (!val) { \ - qemuReportError(VIR_ERR_INTERNAL_ERROR, \ - _("missing value for %s argument"), arg); \ - goto error; \ - } - - /* One initial loop to get list of NICs, so we - * can correlate them later */ - for (i = 1 ; progargv[i] ; i++) { - const char *arg = progargv[i]; - /* Make sure we have a single - for all options to - simplify next logic */ - if (STRPREFIX(arg, "--")) - arg++; - - if (STREQ(arg, "-net")) { - WANT_VALUE(); - if (STRPREFIX(val, "nic")) { - if (VIR_REALLOC_N(nics, nnics+1) < 0) - goto no_memory; - nics[nnics++] = val; - } - } - } - - /* Now the real processing loop */ - for (i = 1 ; progargv[i] ; i++) { - const char *arg = progargv[i]; - /* Make sure we have a single - for all options to - simplify next logic */ - if (STRPREFIX(arg, "--")) - arg++; - - if (STREQ(arg, "-vnc")) { - virDomainGraphicsDefPtr vnc; - char *tmp; - WANT_VALUE(); - if (VIR_ALLOC(vnc) < 0) - goto no_memory; - vnc->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC; - - tmp = strchr(val, ':'); - if (tmp) { - char *opts; - if (virStrToLong_i(tmp+1, &opts, 10, &vnc->data.vnc.port) < 0) { - VIR_FREE(vnc); - qemuReportError(VIR_ERR_INTERNAL_ERROR, \ - _("cannot parse VNC port '%s'"), tmp+1); - goto error; - } - vnc->data.vnc.listenAddr = strndup(val, tmp-val); - if (!vnc->data.vnc.listenAddr) { - VIR_FREE(vnc); - goto no_memory; - } - vnc->data.vnc.port += 5900; - vnc->data.vnc.autoport = 0; - } else { - vnc->data.vnc.autoport = 1; - } - - if (VIR_REALLOC_N(def->graphics, def->ngraphics+1) < 0) { - virDomainGraphicsDefFree(vnc); - goto no_memory; - } - def->graphics[def->ngraphics++] = vnc; - } else if (STREQ(arg, "-m")) { - int mem; - WANT_VALUE(); - if (virStrToLong_i(val, NULL, 10, &mem) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, \ - _("cannot parse memory level '%s'"), val); - goto error; - } - def->mem.cur_balloon = def->mem.max_balloon = mem * 1024; - } else if (STREQ(arg, "-smp")) { - WANT_VALUE(); - if (qemuParseCommandLineSmp(def, val) < 0) - goto error; - } else if (STREQ(arg, "-uuid")) { - WANT_VALUE(); - if (virUUIDParse(val, def->uuid) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, \ - _("cannot parse UUID '%s'"), val); - goto error; - } - } else if (STRPREFIX(arg, "-hd") || - STRPREFIX(arg, "-sd") || - STRPREFIX(arg, "-fd") || - STREQ(arg, "-cdrom")) { - WANT_VALUE(); - virDomainDiskDefPtr disk; - if (VIR_ALLOC(disk) < 0) - goto no_memory; - - if (STRPREFIX(val, "/dev/")) - disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; - else if (STRPREFIX(val, "nbd:")) { - disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK; - disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_NBD; - val += strlen("nbd:"); - } else if (STRPREFIX(val, "rbd:")) { - disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK; - disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_RBD; - val += strlen("rbd:"); - } else if (STRPREFIX(val, "sheepdog:")) { - disk->type = VIR_DOMAIN_DISK_TYPE_NETWORK; - disk->protocol = VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG; - val += strlen("sheepdog:"); - } else - disk->type = VIR_DOMAIN_DISK_TYPE_FILE; - if (STREQ(arg, "-cdrom")) { - disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM; - disk->dst = strdup("hdc"); - disk->readonly = 1; - } else { - if (STRPREFIX(arg, "-fd")) { - disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY; - disk->bus = VIR_DOMAIN_DISK_BUS_FDC; - } else { - disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; - if (STRPREFIX(arg, "-hd")) - disk->bus = VIR_DOMAIN_DISK_BUS_IDE; - else - disk->bus = VIR_DOMAIN_DISK_BUS_SCSI; - } - disk->dst = strdup(arg + 1); - } - disk->src = strdup(val); - - if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK) { - char *host, *port; - - switch (disk->protocol) { - case VIR_DOMAIN_DISK_PROTOCOL_NBD: - host = disk->src; - port = strchr(host, ':'); - if (!port) { - def = NULL; - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse nbd filename '%s'"), disk->src); - goto error; - } - *port++ = '\0'; - if (VIR_ALLOC(disk->hosts) < 0) { - virReportOOMError(); - goto error; - } - disk->nhosts = 1; - disk->hosts->name = host; - disk->hosts->port = strdup(port); - if (!disk->hosts->port) { - virReportOOMError(); - goto error; - } - disk->src = NULL; - break; - case VIR_DOMAIN_DISK_PROTOCOL_RBD: - /* handled later since the hosts for all disks are in CEPH_ARGS */ - break; - case VIR_DOMAIN_DISK_PROTOCOL_SHEEPDOG: - /* disk->src must be [vdiname] or [host]:[port]:[vdiname] */ - port = strchr(disk->src, ':'); - if (port) { - char *vdi; - - *port++ = '\0'; - vdi = strchr(port, ':'); - if (!vdi) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse sheepdog filename '%s'"), val); - goto error; - } - *vdi++ = '\0'; - if (VIR_ALLOC(disk->hosts) < 0) { - virReportOOMError(); - goto error; - } - disk->nhosts = 1; - disk->hosts->name = disk->src; - disk->hosts->port = strdup(port); - if (!disk->hosts->port) { - virReportOOMError(); - goto error; - } - disk->src = strdup(vdi); - if (!disk->src) { - virReportOOMError(); - goto error; - } - } - break; - } - } - - if (!(disk->src || disk->nhosts > 0) || - !disk->dst) { - virDomainDiskDefFree(disk); - goto no_memory; - } - - if (virDomainDiskDefAssignAddress(caps, disk) < 0) - goto error; - - if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { - virDomainDiskDefFree(disk); - goto no_memory; - } - def->disks[def->ndisks++] = disk; - } else if (STREQ(arg, "-no-acpi")) { - def->features &= ~(1 << VIR_DOMAIN_FEATURE_ACPI); - } else if (STREQ(arg, "-no-reboot")) { - def->onReboot = VIR_DOMAIN_LIFECYCLE_DESTROY; - } else if (STREQ(arg, "-no-kvm")) { - def->virtType = VIR_DOMAIN_VIRT_QEMU; - } else if (STREQ(arg, "-nographic")) { - nographics = 1; - } else if (STREQ(arg, "-full-screen")) { - fullscreen = 1; - } else if (STREQ(arg, "-localtime")) { - def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME; - } else if (STREQ(arg, "-kernel")) { - WANT_VALUE(); - if (!(def->os.kernel = strdup(val))) - goto no_memory; - } else if (STREQ(arg, "-initrd")) { - WANT_VALUE(); - if (!(def->os.initrd = strdup(val))) - goto no_memory; - } else if (STREQ(arg, "-append")) { - WANT_VALUE(); - if (!(def->os.cmdline = strdup(val))) - goto no_memory; - } else if (STREQ(arg, "-boot")) { - int n, b = 0; - WANT_VALUE(); - for (n = 0 ; val[n] && b < VIR_DOMAIN_BOOT_LAST ; n++) { - if (val[n] == 'a') - def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_FLOPPY; - else if (val[n] == 'c') - def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_DISK; - else if (val[n] == 'd') - def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_CDROM; - else if (val[n] == 'n') - def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_NET; - else if (val[n] == ',') - break; - } - def->os.nBootDevs = b; - - if (strstr(val, "menu=on")) - def->os.bootmenu = 1; - } else if (STREQ(arg, "-name")) { - char *process; - WANT_VALUE(); - process = strstr(val, ",process="); - if (process == NULL) { - if (!(def->name = strdup(val))) - goto no_memory; - } else { - if (!(def->name = strndup(val, process - val))) - goto no_memory; - } - } else if (STREQ(arg, "-M")) { - WANT_VALUE(); - if (!(def->os.machine = strdup(val))) - goto no_memory; - } else if (STREQ(arg, "-serial")) { - WANT_VALUE(); - if (STRNEQ(val, "none")) { - virDomainChrDefPtr chr; - if (!(chr = qemuParseCommandLineChr(val))) - goto error; - if (VIR_REALLOC_N(def->serials, def->nserials+1) < 0) { - virDomainChrDefFree(chr); - goto no_memory; - } - chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL; - chr->target.port = def->nserials; - def->serials[def->nserials++] = chr; - } - } else if (STREQ(arg, "-parallel")) { - WANT_VALUE(); - if (STRNEQ(val, "none")) { - virDomainChrDefPtr chr; - if (!(chr = qemuParseCommandLineChr(val))) - goto error; - if (VIR_REALLOC_N(def->parallels, def->nparallels+1) < 0) { - virDomainChrDefFree(chr); - goto no_memory; - } - chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL; - chr->target.port = def->nparallels; - def->parallels[def->nparallels++] = chr; - } - } else if (STREQ(arg, "-usbdevice")) { - WANT_VALUE(); - if (STREQ(val, "tablet") || - STREQ(val, "mouse")) { - virDomainInputDefPtr input; - if (VIR_ALLOC(input) < 0) - goto no_memory; - input->bus = VIR_DOMAIN_INPUT_BUS_USB; - if (STREQ(val, "tablet")) - input->type = VIR_DOMAIN_INPUT_TYPE_TABLET; - else - input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE; - if (VIR_REALLOC_N(def->inputs, def->ninputs+1) < 0) { - virDomainInputDefFree(input); - goto no_memory; - } - def->inputs[def->ninputs++] = input; - } else if (STRPREFIX(val, "disk:")) { - virDomainDiskDefPtr disk; - if (VIR_ALLOC(disk) < 0) - goto no_memory; - disk->src = strdup(val + strlen("disk:")); - if (!disk->src) { - virDomainDiskDefFree(disk); - goto no_memory; - } - if (STRPREFIX(disk->src, "/dev/")) - disk->type = VIR_DOMAIN_DISK_TYPE_BLOCK; - else - disk->type = VIR_DOMAIN_DISK_TYPE_FILE; - disk->device = VIR_DOMAIN_DISK_DEVICE_DISK; - disk->bus = VIR_DOMAIN_DISK_BUS_USB; - if (!(disk->dst = strdup("sda"))) { - virDomainDiskDefFree(disk); - goto no_memory; - } - if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { - virDomainDiskDefFree(disk); - goto no_memory; - } - def->disks[def->ndisks++] = disk; - } else { - virDomainHostdevDefPtr hostdev; - if (!(hostdev = qemuParseCommandLineUSB(val))) - goto error; - if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) { - virDomainHostdevDefFree(hostdev); - goto no_memory; - } - def->hostdevs[def->nhostdevs++] = hostdev; - } - } else if (STREQ(arg, "-net")) { - WANT_VALUE(); - if (!STRPREFIX(val, "nic") && STRNEQ(val, "none")) { - virDomainNetDefPtr net; - if (!(net = qemuParseCommandLineNet(caps, val, nnics, nics))) - goto error; - if (VIR_REALLOC_N(def->nets, def->nnets+1) < 0) { - virDomainNetDefFree(net); - goto no_memory; - } - def->nets[def->nnets++] = net; - } - } else if (STREQ(arg, "-drive")) { - virDomainDiskDefPtr disk; - WANT_VALUE(); - if (!(disk = qemuParseCommandLineDisk(caps, val, nvirtiodisk))) - goto error; - if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) { - virDomainDiskDefFree(disk); - goto no_memory; - } - def->disks[def->ndisks++] = disk; - - if (disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) - nvirtiodisk++; - } else if (STREQ(arg, "-pcidevice")) { - virDomainHostdevDefPtr hostdev; - WANT_VALUE(); - if (!(hostdev = qemuParseCommandLinePCI(val))) - goto error; - if (VIR_REALLOC_N(def->hostdevs, def->nhostdevs+1) < 0) { - virDomainHostdevDefFree(hostdev); - goto no_memory; - } - def->hostdevs[def->nhostdevs++] = hostdev; - } else if (STREQ(arg, "-soundhw")) { - const char *start; - WANT_VALUE(); - start = val; - while (start) { - const char *tmp = strchr(start, ','); - int type = -1; - if (STRPREFIX(start, "pcspk")) { - type = VIR_DOMAIN_SOUND_MODEL_PCSPK; - } else if (STRPREFIX(start, "sb16")) { - type = VIR_DOMAIN_SOUND_MODEL_SB16; - } else if (STRPREFIX(start, "es1370")) { - type = VIR_DOMAIN_SOUND_MODEL_ES1370; - } else if (STRPREFIX(start, "ac97")) { - type = VIR_DOMAIN_SOUND_MODEL_AC97; - } - - if (type != -1) { - virDomainSoundDefPtr snd; - if (VIR_ALLOC(snd) < 0) - goto no_memory; - snd->model = type; - if (VIR_REALLOC_N(def->sounds, def->nsounds+1) < 0) { - VIR_FREE(snd); - goto no_memory; - } - def->sounds[def->nsounds++] = snd; - } - - start = tmp ? tmp + 1 : NULL; - } - } else if (STREQ(arg, "-watchdog")) { - WANT_VALUE(); - int model = virDomainWatchdogModelTypeFromString (val); - - if (model != -1) { - virDomainWatchdogDefPtr wd; - if (VIR_ALLOC(wd) < 0) - goto no_memory; - wd->model = model; - wd->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET; - def->watchdog = wd; - } - } else if (STREQ(arg, "-watchdog-action") && def->watchdog) { - WANT_VALUE(); - int action = virDomainWatchdogActionTypeFromString (val); - - if (action != -1) - def->watchdog->action = action; - } else if (STREQ(arg, "-bootloader")) { - WANT_VALUE(); - def->os.bootloader = strdup(val); - if (!def->os.bootloader) - goto no_memory; - } else if (STREQ(arg, "-vmwarevga")) { - video = VIR_DOMAIN_VIDEO_TYPE_VMVGA; - } else if (STREQ(arg, "-std-vga")) { - video = VIR_DOMAIN_VIDEO_TYPE_VGA; - } else if (STREQ(arg, "-vga")) { - WANT_VALUE(); - if (STRNEQ(val, "none")) { - video = qemuVideoTypeFromString(val); - if (video < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unknown video adapter type '%s'"), val); - goto error; - } - } - } else if (STREQ(arg, "-cpu")) { - WANT_VALUE(); - if (qemuParseCommandLineCPU(def, val) < 0) - goto error; - } else if (STREQ(arg, "-domid")) { - WANT_VALUE(); - /* ignore, generted on the fly */ - } else if (STREQ(arg, "-usb")) { - /* ignore, always added by libvirt */ - } else if (STREQ(arg, "-pidfile")) { - WANT_VALUE(); - /* ignore, used by libvirt as needed */ - } else if (STREQ(arg, "-incoming")) { - WANT_VALUE(); - /* ignore, used via restore/migrate APIs */ - } else if (STREQ(arg, "-monitor")) { - WANT_VALUE(); - /* ignore, used internally by libvirt */ - } else if (STREQ(arg, "-S")) { - /* ignore, always added by libvirt */ - } else { - /* something we can't yet parse. Add it to the qemu namespace - * cmdline/environment advanced options and hope for the best - */ - VIR_WARN("unknown QEMU argument '%s', adding to the qemu namespace", - arg); - if (VIR_REALLOC_N(cmd->args, cmd->num_args+1) < 0) - goto no_memory; - cmd->args[cmd->num_args] = strdup(arg); - if (cmd->args[cmd->num_args] == NULL) - goto no_memory; - cmd->num_args++; - } - } - -#undef WANT_VALUE - if (def->ndisks > 0) { - const char *ceph_args = qemuFindEnv(progenv, "CEPH_ARGS"); - if (ceph_args) { - char *hosts, *port, *saveptr, *token; - virDomainDiskDefPtr first_rbd_disk = NULL; - for (i = 0 ; i < def->ndisks ; i++) { - virDomainDiskDefPtr disk = def->disks[i]; - if (disk->type == VIR_DOMAIN_DISK_TYPE_NETWORK && - disk->protocol == VIR_DOMAIN_DISK_PROTOCOL_RBD) { - first_rbd_disk = disk; - break; - } - } - - if (!first_rbd_disk) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("CEPH_ARGS was set without an rbd disk")); - goto error; - } - - /* CEPH_ARGS should be: -m host1[:port1][,host2[:port2]]... */ - if (!STRPREFIX(ceph_args, "-m ")) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("could not parse CEPH_ARGS '%s'"), ceph_args); - goto error; - } - hosts = strdup(strchr(ceph_args, ' ') + 1); - if (!hosts) - goto no_memory; - first_rbd_disk->nhosts = 0; - token = strtok_r(hosts, ",", &saveptr); - while (token != NULL) { - if (VIR_REALLOC_N(first_rbd_disk->hosts, first_rbd_disk->nhosts + 1) < 0) { - VIR_FREE(hosts); - goto no_memory; - } - port = strchr(token, ':'); - if (port) { - *port++ = '\0'; - port = strdup(port); - if (!port) { - VIR_FREE(hosts); - goto no_memory; - } - } - first_rbd_disk->hosts[first_rbd_disk->nhosts].port = port; - first_rbd_disk->hosts[first_rbd_disk->nhosts].name = strdup(token); - if (!first_rbd_disk->hosts[first_rbd_disk->nhosts].name) { - VIR_FREE(hosts); - goto no_memory; - } - first_rbd_disk->nhosts++; - token = strtok_r(NULL, ",", &saveptr); - } - VIR_FREE(hosts); - - if (first_rbd_disk->nhosts == 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("found no rbd hosts in CEPH_ARGS '%s'"), ceph_args); - goto error; - } - } - } - - if (!nographics && def->ngraphics == 0) { - virDomainGraphicsDefPtr sdl; - const char *display = qemuFindEnv(progenv, "DISPLAY"); - const char *xauth = qemuFindEnv(progenv, "XAUTHORITY"); - if (VIR_ALLOC(sdl) < 0) - goto no_memory; - sdl->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL; - sdl->data.sdl.fullscreen = fullscreen; - if (display && - !(sdl->data.sdl.display = strdup(display))) { - VIR_FREE(sdl); - goto no_memory; - } - if (xauth && - !(sdl->data.sdl.xauth = strdup(xauth))) { - VIR_FREE(sdl); - goto no_memory; - } - - if (VIR_REALLOC_N(def->graphics, def->ngraphics+1) < 0) { - virDomainGraphicsDefFree(sdl); - goto no_memory; - } - def->graphics[def->ngraphics++] = sdl; - } - - if (def->ngraphics) { - virDomainVideoDefPtr vid; - if (VIR_ALLOC(vid) < 0) - goto no_memory; - if (def->virtType == VIR_DOMAIN_VIRT_XEN) - vid->type = VIR_DOMAIN_VIDEO_TYPE_XEN; - else - vid->type = video; - vid->vram = virDomainVideoDefaultRAM(def, vid->type); - vid->heads = 1; - - if (VIR_REALLOC_N(def->videos, def->nvideos+1) < 0) { - virDomainVideoDefFree(vid); - goto no_memory; - } - def->videos[def->nvideos++] = vid; - } - - /* - * having a balloon is the default, define one with type="none" to avoid it - */ - if (!def->memballoon) { - virDomainMemballoonDefPtr memballoon; - if (VIR_ALLOC(memballoon) < 0) - goto no_memory; - memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO; - - def->memballoon = memballoon; - } - - VIR_FREE(nics); - - if (!def->name) { - if (!(def->name = strdup("unnamed"))) - goto no_memory; - } - - if (virDomainDefAddImplicitControllers(def) < 0) - goto error; - - if (cmd->num_args || cmd->num_env) { - def->ns = caps->ns; - def->namespaceData = cmd; - } - else - VIR_FREE(cmd); - - return def; - -no_memory: - virReportOOMError(); -error: - VIR_FREE(cmd); - virDomainDefFree(def); - VIR_FREE(nics); - return NULL; -} - - -virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps, - const char *args) -{ - const char **progenv = NULL; - const char **progargv = NULL; - virDomainDefPtr def = NULL; - int i; - - if (qemuStringToArgvEnv(args, &progenv, &progargv) < 0) - goto cleanup; - - def = qemuParseCommandLine(caps, progenv, progargv); - -cleanup: - for (i = 0 ; progargv && progargv[i] ; i++) - VIR_FREE(progargv[i]); - VIR_FREE(progargv); - - for (i = 0 ; progenv && progenv[i] ; i++) - VIR_FREE(progenv[i]); - VIR_FREE(progenv); - - return def; -} diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 86c65a6..eac8603 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -148,13 +148,6 @@ struct _qemuDomainCmdlineDef { # define QEMUD_MIGRATION_FIRST_PORT 49152 # define QEMUD_MIGRATION_NUM_PORTS 64 -/* Config type for XML import/export conversions */ -# define QEMU_CONFIG_FORMAT_ARGV "qemu-argv" - -# define QEMU_DRIVE_HOST_PREFIX "drive-" -# define QEMU_VIRTIO_SERIAL_PREFIX "virtio-serial" -# define QEMU_FSDEV_HOST_PREFIX "fsdev-" - # define qemuReportError(code, ...) \ virReportErrorHelper(NULL, VIR_FROM_QEMU, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) @@ -163,131 +156,4 @@ struct _qemuDomainCmdlineDef { int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename); -virCommandPtr qemudBuildCommandLine (virConnectPtr conn, - struct qemud_driver *driver, - virDomainDefPtr def, - virDomainChrDefPtr monitor_chr, - bool monitor_json, - unsigned long long qemuCmdFlags, - const char *migrateFrom, - virDomainSnapshotObjPtr current_snapshot, - enum virVMOperationType vmop) - ATTRIBUTE_NONNULL(1); - -/* With vlan == -1, use netdev syntax, else old hostnet */ -char * qemuBuildHostNetStr(virDomainNetDefPtr net, - char type_sep, - int vlan, - const char *tapfd, - const char *vhostfd); - -/* Legacy, pre device support */ -char * qemuBuildNicStr(virDomainNetDefPtr net, - const char *prefix, - int vlan); - -/* Current, best practice */ -char * qemuBuildNicDevStr(virDomainNetDefPtr net, - int vlan); - -char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk, - unsigned long long qemudCmdFlags); - -/* Both legacy & current support */ -char *qemuBuildDriveStr(virDomainDiskDefPtr disk, - int bootable, - unsigned long long qemuCmdFlags); -char *qemuBuildFSStr(virDomainFSDefPtr fs, - unsigned long long qemuCmdFlags); - -/* Current, best practice */ -char * qemuBuildDriveDevStr(virDomainDiskDefPtr disk); -char * qemuBuildFSDevStr(virDomainFSDefPtr fs); -/* Current, best practice */ -char * qemuBuildControllerDevStr(virDomainControllerDefPtr def); - -char * qemuBuildWatchdogDevStr(virDomainWatchdogDefPtr dev); - -char * qemuBuildMemballoonDevStr(virDomainMemballoonDefPtr dev); - -char * qemuBuildUSBInputDevStr(virDomainInputDefPtr dev); - -char * qemuBuildSoundDevStr(virDomainSoundDefPtr sound); - -/* Legacy, pre device support */ -char * qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev); -/* Current, best practice */ -char * qemuBuildPCIHostdevDevStr(virDomainHostdevDefPtr dev, - const char *configfd); - -int qemudOpenPCIConfig(virDomainHostdevDefPtr dev); - -/* Current, best practice */ -char * qemuBuildChrChardevStr(virDomainChrDefPtr dev); -/* Legacy, pre device support */ -char * qemuBuildChrArgStr(virDomainChrDefPtr dev, const char *prefix); - -char * qemuBuildVirtioSerialPortDevStr(virDomainChrDefPtr dev); - -/* Legacy, pre device support */ -char * qemuBuildUSBHostdevUsbDevStr(virDomainHostdevDefPtr dev); -/* Current, best practice */ -char * qemuBuildUSBHostdevDevStr(virDomainHostdevDefPtr dev); - - - -int qemudNetworkIfaceConnect (virConnectPtr conn, - struct qemud_driver *driver, - virDomainNetDefPtr net, - unsigned long long qemuCmdFlags) - ATTRIBUTE_NONNULL(1); - -int -qemudOpenVhostNet(virDomainNetDefPtr net, - unsigned long long qemuCmdFlags); - -int qemudPhysIfaceConnect(virConnectPtr conn, - struct qemud_driver *driver, - virDomainNetDefPtr net, - unsigned long long qemuCmdFlags, - const unsigned char *vmuuid, - enum virVMOperationType vmop); - -int qemudCanonicalizeMachine (struct qemud_driver *driver, - virDomainDefPtr def); - -virDomainDefPtr qemuParseCommandLine(virCapsPtr caps, - const char **progenv, - const char **progargv); -virDomainDefPtr qemuParseCommandLineString(virCapsPtr caps, - const char *args); - -qemuDomainPCIAddressSetPtr qemuDomainPCIAddressSetCreate(virDomainDefPtr def); -int qemuDomainPCIAddressReserveSlot(qemuDomainPCIAddressSetPtr addrs, - int slot); -int qemuDomainPCIAddressReserveAddr(qemuDomainPCIAddressSetPtr addrs, - virDomainDeviceInfoPtr dev); -int qemuDomainPCIAddressSetNextAddr(qemuDomainPCIAddressSetPtr addrs, - virDomainDeviceInfoPtr dev); -int qemuDomainPCIAddressEnsureAddr(qemuDomainPCIAddressSetPtr addrs, - virDomainDeviceInfoPtr dev); -int qemuDomainPCIAddressReleaseAddr(qemuDomainPCIAddressSetPtr addrs, - virDomainDeviceInfoPtr dev); - -void qemuDomainPCIAddressSetFree(qemuDomainPCIAddressSetPtr addrs); -int qemuAssignDevicePCISlots(virDomainDefPtr def, qemuDomainPCIAddressSetPtr addrs); - -int qemuDomainNetVLAN(virDomainNetDefPtr def); -int qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx); -int qemuAssignDeviceDiskAlias(virDomainDiskDefPtr def, unsigned long long qemuCmdFlags); -int qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr net, int idx); -int qemuAssignDeviceControllerAlias(virDomainControllerDefPtr controller); - -int -qemuParseKeywords(const char *str, - char ***retkeywords, - char ***retvalues, - int allowEmptyValue); - - #endif /* __QEMUD_CONF_H */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index f303075..e1a1179 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -55,6 +55,7 @@ #include "qemu_driver.h" #include "qemu_conf.h" #include "qemu_capabilities.h" +#include "qemu_command.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" #include "c-ctype.h" @@ -4134,10 +4135,10 @@ static int qemudStartVMDaemon(virConnectPtr conn, DEBUG0("Building emulator command line"); vm->def->id = driver->nextvmid++; - if (!(cmd = qemudBuildCommandLine(conn, driver, vm->def, priv->monConfig, - priv->monJSON != 0, qemuCmdFlags, - migrateFrom, - vm->current_snapshot, vmop))) + if (!(cmd = qemuBuildCommandLine(conn, driver, vm->def, priv->monConfig, + priv->monJSON != 0, qemuCmdFlags, + migrateFrom, + vm->current_snapshot, vmop))) goto cleanup; if (qemuDomainSnapshotSetInactive(vm, driver->snapshotDir) < 0) @@ -7474,9 +7475,9 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, if (qemuPrepareMonitorChr(driver, &monConfig, def->name) < 0) goto cleanup; - if (!(cmd = qemudBuildCommandLine(conn, driver, def, - &monConfig, false, qemuCmdFlags, - NULL, NULL, VIR_VM_OP_NO_OP))) + if (!(cmd = qemuBuildCommandLine(conn, driver, def, + &monConfig, false, qemuCmdFlags, + NULL, NULL, VIR_VM_OP_NO_OP))) goto cleanup; ret = virCommandToString(cmd); @@ -8368,7 +8369,7 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, return -1; } - if ((tapfd = qemudNetworkIfaceConnect(conn, driver, net, qemuCmdFlags)) < 0) + if ((tapfd = qemuNetworkIfaceConnect(conn, driver, net, qemuCmdFlags)) < 0) return -1; } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { @@ -8379,10 +8380,10 @@ static int qemudDomainAttachNetDevice(virConnectPtr conn, return -1; } - if ((tapfd = qemudPhysIfaceConnect(conn, driver, net, - qemuCmdFlags, - vm->def->uuid, - VIR_VM_OP_CREATE)) < 0) + if ((tapfd = qemuPhysIfaceConnect(conn, driver, net, + qemuCmdFlags, + vm->def->uuid, + VIR_VM_OP_CREATE)) < 0) return -1; } @@ -8593,7 +8594,7 @@ static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver, if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0) goto error; if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) { - configfd = qemudOpenPCIConfig(hostdev); + configfd = qemuOpenPCIConfig(hostdev); if (configfd >= 0) { if (virAsprintf(&configfd_name, "fd-%s", hostdev->info.alias) < 0) { diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 409f6bd..7877731 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -32,7 +32,7 @@ #include <sys/time.h> #include "qemu_monitor_json.h" -#include "qemu_conf.h" +#include "qemu_command.h" #include "memory.h" #include "logging.h" #include "driver.h" diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 29e21ce..11a9391 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -31,7 +31,7 @@ #include <string.h> #include "qemu_monitor_text.h" -#include "qemu_conf.h" +#include "qemu_command.h" #include "c-ctype.h" #include "memory.h" #include "logging.h" -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
The qemu_conf.c code is doing three jobs, driver config file loading, QEMU capabilities management and QEMU command line management. Move the command line code into its own file
* src/qemu/qemu_command.c, src/qemu/qemu_command.h: New command line management code * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Delete command line code * src/qemu/qemu_conf.h, src/qemu_conf.c: Adapt for API renames * src/Makefile.am: add src/qemu/qemu_command.c * src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_text.c: Add import of qemu_command.h --- src/Makefile.am | 1 + src/qemu/qemu_command.c | 5793 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_command.h | 165 ++ src/qemu/qemu_conf.c | 5748 -----------------------------------------
Wow, more lines moved than in the last patch, but I did due diligence and did a quick scan for any obvious changes, and didn't find any.
+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'; +}
Same nit about sharing this common function with capabilities code. ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 12/16/2010 11:56 AM, Eric Blake wrote:
On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
The qemu_conf.c code is doing three jobs, driver config file loading, QEMU capabilities management and QEMU command line management. Move the command line code into its own file
Same nit about sharing this common function with capabilities code.
ACK.
Also squash this in: diff --git i/po/POTFILES.in w/po/POTFILES.in index c2979bd..f71ce2a 100644 --- i/po/POTFILES.in +++ w/po/POTFILES.in @@ -52,6 +52,7 @@ src/openvz/openvz_driver.c src/phyp/phyp_driver.c src/qemu/qemu_bridge_filter.c src/qemu/qemu_capabilities.c +src/qemu/qemu_command.c src/qemu/qemu_conf.c src/qemu/qemu_driver.c src/qemu/qemu_monitor.c diff --git i/src/qemu/qemu_command.h w/src/qemu/qemu_command.h index da35d3b..7e82ef3 100644 --- i/src/qemu/qemu_command.h +++ w/src/qemu/qemu_command.h @@ -24,10 +24,10 @@ #ifndef __QEMU_COMMAND_H__ # define __QEMU_COMMAND_H__ -#include "domain_conf.h" -#include "command.h" -#include "capabilities.h" -#include "qemu_conf.h" +# include "domain_conf.h" +# include "command.h" +# include "capabilities.h" +# include "qemu_conf.h" /* Config type for XML import/export conversions */ # define QEMU_CONFIG_FORMAT_ARGV "qemu-argv" diff --git i/src/qemu/qemu_conf.c w/src/qemu/qemu_conf.c index e600a5b..9869b40 100644 --- i/src/qemu/qemu_conf.c +++ w/src/qemu/qemu_conf.c @@ -37,7 +37,6 @@ #include <sys/utsname.h> #include <mntent.h> -#include "c-ctype.h" #include "virterror_internal.h" #include "qemu_conf.h" #include "qemu_capabilities.h" @@ -413,5 +412,3 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, virConfFree (conf); return 0; } - - diff --git i/tests/qemuargv2xmltest.c w/tests/qemuargv2xmltest.c index adff05a..dfb9d99 100644 --- i/tests/qemuargv2xmltest.c +++ w/tests/qemuargv2xmltest.c @@ -14,6 +14,7 @@ # include "internal.h" # include "testutils.h" # include "qemu/qemu_conf.h" +# include "qemu/qemu_command.h" # include "testutilsqemu.h" diff --git i/tests/qemuxml2argvtest.c w/tests/qemuxml2argvtest.c index f1cdc30..554da2f 100644 --- i/tests/qemuxml2argvtest.c +++ w/tests/qemuxml2argvtest.c @@ -15,6 +15,7 @@ # include "testutils.h" # include "qemu/qemu_conf.h" # include "qemu/qemu_capabilities.h" +# include "qemu/qemu_command.h" # include "datatypes.h" # include "cpu/cpu_map.h" @@ -111,9 +112,9 @@ static int testCompareXMLToArgvFiles(const char *xml, free(virtTestLogContentAndReset()); virResetLastError(); - if (!(cmd = qemudBuildCommandLine(conn, &driver, - vmdef, &monitor_chr, false, flags, - migrateFrom, NULL, VIR_VM_OP_CREATE))) + if (!(cmd = qemuBuildCommandLine(conn, &driver, + vmdef, &monitor_chr, false, flags, + migrateFrom, NULL, VIR_VM_OP_CREATE))) goto fail; if (!!virGetLastError() != expectError) { -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Allow QEMU driver files access to qemuDomainObjPrivate struct * src/qemu/qemu_conf.h: Add qemuDomainObjPrivate struct * src/qemu/qemu_driver.c: Remove qemuDomainObjPrivate struct --- src/qemu/qemu_driver.c | 45 --------------------------------------------- 1 files changed, 0 insertions(+), 45 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index e1a1179..0489a7a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -100,51 +100,6 @@ #define timeval_to_ms(tv) (((tv).tv_sec * 1000ull) + ((tv).tv_usec / 1000)) -/* Only 1 job is allowed at any time - * A job includes *all* monitor commands, even those just querying - * information, not merely actions */ -enum qemuDomainJob { - QEMU_JOB_NONE = 0, /* Always set to 0 for easy if (jobActive) conditions */ - QEMU_JOB_UNSPECIFIED, - QEMU_JOB_MIGRATION_OUT, - QEMU_JOB_MIGRATION_IN, - QEMU_JOB_SAVE, - QEMU_JOB_DUMP, -}; - -enum qemuDomainJobSignals { - QEMU_JOB_SIGNAL_CANCEL = 1 << 0, /* Request job cancellation */ - QEMU_JOB_SIGNAL_SUSPEND = 1 << 1, /* Request VM suspend to finish live migration offline */ - QEMU_JOB_SIGNAL_MIGRATE_DOWNTIME = 1 << 2, /* Request migration downtime change */ -}; - -struct qemuDomainJobSignalsData { - unsigned long long migrateDowntime; /* Data for QEMU_JOB_SIGNAL_MIGRATE_DOWNTIME */ -}; - -typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate; -typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr; -struct _qemuDomainObjPrivate { - virCond jobCond; /* Use in conjunction with main virDomainObjPtr lock */ - enum qemuDomainJob jobActive; /* Currently running job */ - unsigned int jobSignals; /* Signals for running job */ - struct qemuDomainJobSignalsData jobSignalsData; /* Signal specific data */ - virDomainJobInfo jobInfo; - unsigned long long jobStart; - - qemuMonitorPtr mon; - virDomainChrDefPtr monConfig; - int monJSON; - int monitor_warned; - bool gotShutdown; - - int nvcpupids; - int *vcpupids; - - qemuDomainPCIAddressSetPtr pciaddrs; - int persistentAddrs; -}; - struct watchdogEvent { virDomainObjPtr vm; -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
Allow QEMU driver files access to qemuDomainObjPrivate struct
* src/qemu/qemu_conf.h: Add qemuDomainObjPrivate struct * src/qemu/qemu_driver.c: Remove qemuDomainObjPrivate struct --- src/qemu/qemu_driver.c | 45 --------------------------------------------- 1 files changed, 0 insertions(+), 45 deletions(-)
The diffstat doesn't agree with the commit message. And it fails to compile for me; even after manually doing what your commit message claimed, the failure was that qemuMonitorPtr was not available in qemu_conf.h. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Thu, Dec 16, 2010 at 12:13:51PM -0700, Eric Blake wrote:
On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
Allow QEMU driver files access to qemuDomainObjPrivate struct
* src/qemu/qemu_conf.h: Add qemuDomainObjPrivate struct * src/qemu/qemu_driver.c: Remove qemuDomainObjPrivate struct --- src/qemu/qemu_driver.c | 45 --------------------------------------------- 1 files changed, 0 insertions(+), 45 deletions(-)
The diffstat doesn't agree with the commit message. And it fails to compile for me; even after manually doing what your commit message claimed, the failure was that qemuMonitorPtr was not available in qemu_conf.h.
Hmm, seems this went wrong during a rebase. The qemu_conf.h addition somehow ended up in the next patch. I should just squash the patches 3 & 4 together Daniel

On 12/16/2010 12:26 PM, Daniel P. Berrange wrote:
On Thu, Dec 16, 2010 at 12:13:51PM -0700, Eric Blake wrote:
On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
Allow QEMU driver files access to qemuDomainObjPrivate struct
* src/qemu/qemu_conf.h: Add qemuDomainObjPrivate struct * src/qemu/qemu_driver.c: Remove qemuDomainObjPrivate struct --- src/qemu/qemu_driver.c | 45 --------------------------------------------- 1 files changed, 0 insertions(+), 45 deletions(-)
The diffstat doesn't agree with the commit message. And it fails to compile for me; even after manually doing what your commit message claimed, the failure was that qemuMonitorPtr was not available in qemu_conf.h.
Hmm, seems this went wrong during a rebase. The qemu_conf.h addition somehow ended up in the next patch. I should just squash the patches 3 & 4 together
I wondered that too, at first, but the diffstat for 4 doesn't concur either: src/qemu/qemu_conf.h | 3 - At any rate, if I paste the 45 lines, and add #include "qemu_monitor.h" at the top, then things compile, but I don't know if you wanted qemu_monitor.h dragging that in for all clients of qemu_conf.h. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 12/16/2010 01:13 PM, Eric Blake wrote:
Hmm, seems this went wrong during a rebase. The qemu_conf.h addition somehow ended up in the next patch. I should just squash the patches 3 & 4 together
I wondered that too, at first, but the diffstat for 4 doesn't concur either:
src/qemu/qemu_conf.h | 3 -
At any rate, if I paste the 45 lines, and add #include "qemu_monitor.h" at the top, then things compile, but I don't know if you wanted qemu_monitor.h dragging that in for all clients of qemu_conf.h.
Oh, I see - you moved those lines, _again_, into the new qemu_domain.h for patch 4. In which case, squashing 3 and 4 is the right way to go. ACK to that approach. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

Move the code for handling the QEMU virDomainObjPtr private data, and custom XML namespace into a separate file * src/qemu/qemu_domain.c, src/qemu/qemu_domain.h: New file for private data & namespace code * src/qemu/qemu_driver.c, src/qemu/qemu_driver.h: Remove private data & namespace code * src/qemu/qemu_driver.h, src/qemu/qemu_command.h: Update includes * src/Makefile.am: Add src/qemu/qemu_domain.c --- src/Makefile.am | 1 + src/qemu/qemu_command.h | 1 + src/qemu/qemu_conf.h | 3 - src/qemu/qemu_domain.c | 374 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 83 +++++++++++ src/qemu/qemu_driver.c | 329 +----------------------------------------- src/qemu/qemu_driver.h | 9 - 7 files changed, 461 insertions(+), 339 deletions(-) create mode 100644 src/qemu/qemu_domain.c create mode 100644 src/qemu/qemu_domain.h diff --git a/src/Makefile.am b/src/Makefile.am index d4626d9..d2fcd5f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -269,6 +269,7 @@ VBOX_DRIVER_EXTRA_DIST = vbox/vbox_tmpl.c vbox/README QEMU_DRIVER_SOURCES = \ qemu/qemu_capabilities.c qemu/qemu_capabilities.h\ qemu/qemu_command.c qemu/qemu_command.h \ + qemu/qemu_domain.c qemu/qemu_domain.h \ qemu/qemu_conf.c qemu/qemu_conf.h \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index da35d3b..5545e54 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -28,6 +28,7 @@ #include "command.h" #include "capabilities.h" #include "qemu_conf.h" +#include "qemu_domain.h" /* Config type for XML import/export conversions */ # define QEMU_CONFIG_FORMAT_ARGV "qemu-argv" diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index eac8603..4c61891 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -130,9 +130,6 @@ struct qemud_driver { virSysinfoDefPtr hostsysinfo; }; -typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet; -typedef qemuDomainPCIAddressSet *qemuDomainPCIAddressSetPtr; - typedef struct _qemuDomainCmdlineDef qemuDomainCmdlineDef; typedef qemuDomainCmdlineDef *qemuDomainCmdlineDefPtr; struct _qemuDomainCmdlineDef { diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c new file mode 100644 index 0000000..14364ff --- /dev/null +++ b/src/qemu/qemu_domain.c @@ -0,0 +1,374 @@ +/* + * qemu_domain.h: QEMU domain private state + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "qemu_domain.h" +#include "qemu_command.h" +#include "memory.h" +#include "logging.h" +#include "virterror_internal.h" +#include "c-ctype.h" + +#include <libxml/xpathInternals.h> + +#define VIR_FROM_THIS VIR_FROM_QEMU + +#define QEMU_NAMESPACE_HREF "http://libvirt.org/schemas/domain/qemu/1.0" + +static void *qemuDomainObjPrivateAlloc(void) +{ + qemuDomainObjPrivatePtr priv; + + if (VIR_ALLOC(priv) < 0) + return NULL; + + return priv; +} + +static void qemuDomainObjPrivateFree(void *data) +{ + qemuDomainObjPrivatePtr priv = data; + + qemuDomainPCIAddressSetFree(priv->pciaddrs); + virDomainChrDefFree(priv->monConfig); + VIR_FREE(priv->vcpupids); + + /* This should never be non-NULL if we get here, but just in case... */ + if (priv->mon) { + VIR_ERROR0(_("Unexpected QEMU monitor still active during domain deletion")); + qemuMonitorClose(priv->mon); + } + VIR_FREE(priv); +} + + +static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) +{ + qemuDomainObjPrivatePtr priv = data; + const char *monitorpath; + + /* priv->monitor_chr is set only for qemu */ + if (priv->monConfig) { + switch (priv->monConfig->type) { + case VIR_DOMAIN_CHR_TYPE_UNIX: + monitorpath = priv->monConfig->data.nix.path; + break; + default: + case VIR_DOMAIN_CHR_TYPE_PTY: + monitorpath = priv->monConfig->data.file.path; + break; + } + + virBufferEscapeString(buf, " <monitor path='%s'", monitorpath); + if (priv->monJSON) + virBufferAddLit(buf, " json='1'"); + virBufferVSprintf(buf, " type='%s'/>\n", + virDomainChrTypeToString(priv->monConfig->type)); + } + + + if (priv->nvcpupids) { + int i; + virBufferAddLit(buf, " <vcpus>\n"); + for (i = 0 ; i < priv->nvcpupids ; i++) { + virBufferVSprintf(buf, " <vcpu pid='%d'/>\n", priv->vcpupids[i]); + } + virBufferAddLit(buf, " </vcpus>\n"); + } + + return 0; +} + +static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) +{ + qemuDomainObjPrivatePtr priv = data; + char *monitorpath; + char *tmp; + int n, i; + xmlNodePtr *nodes = NULL; + + if (VIR_ALLOC(priv->monConfig) < 0) { + virReportOOMError(); + goto error; + } + + if (!(priv->monConfig->info.alias = strdup("monitor"))) { + virReportOOMError(); + goto error; + } + + if (!(monitorpath = + virXPathString("string(./monitor[1]/@path)", ctxt))) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("no monitor path")); + goto error; + } + + tmp = virXPathString("string(./monitor[1]/@type)", ctxt); + if (tmp) + priv->monConfig->type = virDomainChrTypeFromString(tmp); + else + priv->monConfig->type = VIR_DOMAIN_CHR_TYPE_PTY; + VIR_FREE(tmp); + + if (virXPathBoolean("count(./monitor[@json = '1']) > 0", ctxt)) { + priv->monJSON = 1; + } else { + priv->monJSON = 0; + } + + switch (priv->monConfig->type) { + case VIR_DOMAIN_CHR_TYPE_PTY: + priv->monConfig->data.file.path = monitorpath; + break; + case VIR_DOMAIN_CHR_TYPE_UNIX: + priv->monConfig->data.nix.path = monitorpath; + break; + default: + VIR_FREE(monitorpath); + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported monitor type '%s'"), + virDomainChrTypeToString(priv->monConfig->type)); + goto error; + } + + n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes); + if (n < 0) + goto error; + if (n) { + priv->nvcpupids = n; + if (VIR_REALLOC_N(priv->vcpupids, priv->nvcpupids) < 0) { + virReportOOMError(); + goto error; + } + + for (i = 0 ; i < n ; i++) { + char *pidstr = virXMLPropString(nodes[i], "pid"); + if (!pidstr) + goto error; + + if (virStrToLong_i(pidstr, NULL, 10, &(priv->vcpupids[i])) < 0) { + VIR_FREE(pidstr); + goto error; + } + VIR_FREE(pidstr); + } + VIR_FREE(nodes); + } + + return 0; + +error: + virDomainChrDefFree(priv->monConfig); + priv->monConfig = NULL; + VIR_FREE(nodes); + return -1; +} + + +static void +qemuDomainDefNamespaceFree(void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + unsigned int i; + + if (!cmd) + return; + + for (i = 0; i < cmd->num_args; i++) + VIR_FREE(cmd->args[i]); + for (i = 0; i < cmd->num_env; i++) { + VIR_FREE(cmd->env_name[i]); + VIR_FREE(cmd->env_value[i]); + } + VIR_FREE(cmd->args); + VIR_FREE(cmd->env_name); + VIR_FREE(cmd->env_value); + VIR_FREE(cmd); +} + +static int +qemuDomainDefNamespaceParse(xmlDocPtr xml, + xmlNodePtr root, + xmlXPathContextPtr ctxt, + void **data) +{ + qemuDomainCmdlineDefPtr cmd = NULL; + xmlNsPtr ns; + xmlNodePtr *nodes = NULL; + int n, i; + + ns = xmlSearchNs(xml, root, BAD_CAST "qemu"); + if (!ns) + /* this is fine; it just means there was no qemu namespace listed */ + return 0; + + if (STRNEQ((const char *)ns->href, QEMU_NAMESPACE_HREF)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Found namespace '%s' doesn't match expected '%s'"), + ns->href, QEMU_NAMESPACE_HREF); + return -1; + } + + if (xmlXPathRegisterNs(ctxt, ns->prefix, ns->href) < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Failed to register xml namespace '%s'"), ns->href); + return -1; + } + + if (VIR_ALLOC(cmd) < 0) { + virReportOOMError(); + return -1; + } + + /* first handle the extra command-line arguments */ + n = virXPathNodeSet("./qemu:commandline/qemu:arg", ctxt, &nodes); + if (n < 0) + /* virXPathNodeSet already set the error */ + goto error; + + if (n && VIR_ALLOC_N(cmd->args, n) < 0) + goto no_memory; + + for (i = 0; i < n; i++) { + cmd->args[cmd->num_args] = virXMLPropString(nodes[i], "value"); + if (cmd->args[cmd->num_args] == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("No qemu command-line argument specified")); + goto error; + } + cmd->num_args++; + } + + VIR_FREE(nodes); + + /* now handle the extra environment variables */ + n = virXPathNodeSet("./qemu:commandline/qemu:env", ctxt, &nodes); + if (n < 0) + /* virXPathNodeSet already set the error */ + goto error; + + if (n && VIR_ALLOC_N(cmd->env_name, n) < 0) + goto no_memory; + + if (n && VIR_ALLOC_N(cmd->env_value, n) < 0) + goto no_memory; + + for (i = 0; i < n; i++) { + char *tmp; + + tmp = virXMLPropString(nodes[i], "name"); + if (tmp == NULL) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("No qemu environment name specified")); + goto error; + } + if (tmp[0] == '\0') { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Empty qemu environment name specified")); + goto error; + } + if (!c_isalpha(tmp[0]) && tmp[0] != '_') { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid environment name, it must begin with a letter or underscore")); + goto error; + } + if (strspn(tmp, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_") != strlen(tmp)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Invalid environment name, it must contain only alphanumerics and underscore")); + goto error; + } + + cmd->env_name[cmd->num_env] = tmp; + + cmd->env_value[cmd->num_env] = virXMLPropString(nodes[i], "value"); + /* a NULL value for command is allowed, since it might be empty */ + cmd->num_env++; + } + + VIR_FREE(nodes); + + *data = cmd; + + return 0; + +no_memory: + virReportOOMError(); + +error: + VIR_FREE(nodes); + qemuDomainDefNamespaceFree(cmd); + return -1; +} + +static int +qemuDomainDefNamespaceFormatXML(virBufferPtr buf, + void *nsdata) +{ + qemuDomainCmdlineDefPtr cmd = nsdata; + unsigned int i; + + if (!cmd->num_args && !cmd->num_env) + return 0; + + virBufferAddLit(buf, " <qemu:commandline>\n"); + for (i = 0; i < cmd->num_args; i++) + virBufferEscapeString(buf, " <qemu:arg value='%s'/>\n", + cmd->args[i]); + for (i = 0; i < cmd->num_env; i++) { + virBufferVSprintf(buf, " <qemu:env name='%s'", cmd->env_name[i]); + if (cmd->env_value[i]) + virBufferEscapeString(buf, " value='%s'", cmd->env_value[i]); + virBufferAddLit(buf, "/>\n"); + } + virBufferAddLit(buf, " </qemu:commandline>\n"); + + return 0; +} + +static const char * +qemuDomainDefNamespaceHref(void) +{ + return "xmlns:qemu='" QEMU_NAMESPACE_HREF "'"; +} + + +void qemuDomainSetPrivateDataHooks(virCapsPtr caps) +{ + /* Domain XML parser hooks */ + caps->privateDataAllocFunc = qemuDomainObjPrivateAlloc; + caps->privateDataFreeFunc = qemuDomainObjPrivateFree; + caps->privateDataXMLFormat = qemuDomainObjPrivateXMLFormat; + caps->privateDataXMLParse = qemuDomainObjPrivateXMLParse; + +} + +void qemuDomainSetNamespaceHooks(virCapsPtr caps) +{ + /* Domain Namespace XML parser hooks */ + caps->ns.parse = qemuDomainDefNamespaceParse; + caps->ns.free = qemuDomainDefNamespaceFree; + caps->ns.format = qemuDomainDefNamespaceFormatXML; + caps->ns.href = qemuDomainDefNamespaceHref; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h new file mode 100644 index 0000000..0d1e222 --- /dev/null +++ b/src/qemu/qemu_domain.h @@ -0,0 +1,83 @@ +/* + * qemu_domain.h: QEMU domain private state + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __QEMU_DOMAIN_H__ +# define __QEMU_DOMAIN_H__ + +# include "threads.h" +# include "domain_conf.h" +# include "qemu_monitor.h" + +/* Only 1 job is allowed at any time + * A job includes *all* monitor commands, even those just querying + * information, not merely actions */ +enum qemuDomainJob { + QEMU_JOB_NONE = 0, /* Always set to 0 for easy if (jobActive) conditions */ + QEMU_JOB_UNSPECIFIED, + QEMU_JOB_MIGRATION_OUT, + QEMU_JOB_MIGRATION_IN, + QEMU_JOB_SAVE, + QEMU_JOB_DUMP, +}; + +enum qemuDomainJobSignals { + QEMU_JOB_SIGNAL_CANCEL = 1 << 0, /* Request job cancellation */ + QEMU_JOB_SIGNAL_SUSPEND = 1 << 1, /* Request VM suspend to finish live migration offline */ + QEMU_JOB_SIGNAL_MIGRATE_DOWNTIME = 1 << 2, /* Request migration downtime change */ +}; + +struct qemuDomainJobSignalsData { + unsigned long long migrateDowntime; /* Data for QEMU_JOB_SIGNAL_MIGRATE_DOWNTIME */ +}; + +typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet; +typedef qemuDomainPCIAddressSet *qemuDomainPCIAddressSetPtr; + +typedef struct _qemuDomainObjPrivate qemuDomainObjPrivate; +typedef qemuDomainObjPrivate *qemuDomainObjPrivatePtr; +struct _qemuDomainObjPrivate { + virCond jobCond; /* Use in conjunction with main virDomainObjPtr lock */ + enum qemuDomainJob jobActive; /* Currently running job */ + unsigned int jobSignals; /* Signals for running job */ + struct qemuDomainJobSignalsData jobSignalsData; /* Signal specific data */ + virDomainJobInfo jobInfo; + unsigned long long jobStart; + + qemuMonitorPtr mon; + virDomainChrDefPtr monConfig; + int monJSON; + int monitor_warned; + bool gotShutdown; + + int nvcpupids; + int *vcpupids; + + qemuDomainPCIAddressSetPtr pciaddrs; + int persistentAddrs; +}; + + +void qemuDomainSetPrivateDataHooks(virCapsPtr caps); +void qemuDomainSetNamespaceHooks(virCapsPtr caps); + +#endif /* __QEMU_DOMAIN_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0489a7a..9ce8fbe 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -47,7 +47,6 @@ #include <sys/ioctl.h> #include <sys/un.h> -#include <libxml/xpathInternals.h> #include "virterror_internal.h" #include "logging.h" @@ -96,7 +95,6 @@ #define QEMU_NB_MEM_PARAM 3 -#define QEMU_NAMESPACE_HREF "http://libvirt.org/schemas/domain/qemu/1.0" #define timeval_to_ms(tv) (((tv).tv_sec * 1000ull) + ((tv).tv_usec / 1000)) @@ -158,158 +156,6 @@ static int qemudVMFiltersInstantiate(virConnectPtr conn, static struct qemud_driver *qemu_driver = NULL; -static void *qemuDomainObjPrivateAlloc(void) -{ - qemuDomainObjPrivatePtr priv; - - if (VIR_ALLOC(priv) < 0) - return NULL; - - return priv; -} - -static void qemuDomainObjPrivateFree(void *data) -{ - qemuDomainObjPrivatePtr priv = data; - - qemuDomainPCIAddressSetFree(priv->pciaddrs); - virDomainChrDefFree(priv->monConfig); - VIR_FREE(priv->vcpupids); - - /* This should never be non-NULL if we get here, but just in case... */ - if (priv->mon) { - VIR_ERROR0(_("Unexpected QEMU monitor still active during domain deletion")); - qemuMonitorClose(priv->mon); - } - VIR_FREE(priv); -} - - -static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) -{ - qemuDomainObjPrivatePtr priv = data; - const char *monitorpath; - - /* priv->monitor_chr is set only for qemu */ - if (priv->monConfig) { - switch (priv->monConfig->type) { - case VIR_DOMAIN_CHR_TYPE_UNIX: - monitorpath = priv->monConfig->data.nix.path; - break; - default: - case VIR_DOMAIN_CHR_TYPE_PTY: - monitorpath = priv->monConfig->data.file.path; - break; - } - - virBufferEscapeString(buf, " <monitor path='%s'", monitorpath); - if (priv->monJSON) - virBufferAddLit(buf, " json='1'"); - virBufferVSprintf(buf, " type='%s'/>\n", - virDomainChrTypeToString(priv->monConfig->type)); - } - - - if (priv->nvcpupids) { - int i; - virBufferAddLit(buf, " <vcpus>\n"); - for (i = 0 ; i < priv->nvcpupids ; i++) { - virBufferVSprintf(buf, " <vcpu pid='%d'/>\n", priv->vcpupids[i]); - } - virBufferAddLit(buf, " </vcpus>\n"); - } - - return 0; -} - -static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) -{ - qemuDomainObjPrivatePtr priv = data; - char *monitorpath; - char *tmp; - int n, i; - xmlNodePtr *nodes = NULL; - - if (VIR_ALLOC(priv->monConfig) < 0) { - virReportOOMError(); - goto error; - } - - if (!(priv->monConfig->info.alias = strdup("monitor"))) { - virReportOOMError(); - goto error; - } - - if (!(monitorpath = - virXPathString("string(./monitor[1]/@path)", ctxt))) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("no monitor path")); - goto error; - } - - tmp = virXPathString("string(./monitor[1]/@type)", ctxt); - if (tmp) - priv->monConfig->type = virDomainChrTypeFromString(tmp); - else - priv->monConfig->type = VIR_DOMAIN_CHR_TYPE_PTY; - VIR_FREE(tmp); - - if (virXPathBoolean("count(./monitor[@json = '1']) > 0", ctxt)) { - priv->monJSON = 1; - } else { - priv->monJSON = 0; - } - - switch (priv->monConfig->type) { - case VIR_DOMAIN_CHR_TYPE_PTY: - priv->monConfig->data.file.path = monitorpath; - break; - case VIR_DOMAIN_CHR_TYPE_UNIX: - priv->monConfig->data.nix.path = monitorpath; - break; - default: - VIR_FREE(monitorpath); - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unsupported monitor type '%s'"), - virDomainChrTypeToString(priv->monConfig->type)); - goto error; - } - - n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes); - if (n < 0) - goto error; - if (n) { - priv->nvcpupids = n; - if (VIR_REALLOC_N(priv->vcpupids, priv->nvcpupids) < 0) { - virReportOOMError(); - goto error; - } - - for (i = 0 ; i < n ; i++) { - char *pidstr = virXMLPropString(nodes[i], "pid"); - if (!pidstr) - goto error; - - if (virStrToLong_i(pidstr, NULL, 10, &(priv->vcpupids[i])) < 0) { - VIR_FREE(pidstr); - goto error; - } - VIR_FREE(pidstr); - } - VIR_FREE(nodes); - } - - return 0; - -error: - virDomainChrDefFree(priv->monConfig); - priv->monConfig = NULL; - VIR_FREE(nodes); - return -1; -} - - - /* * obj must be locked before calling, qemud_driver must NOT be locked * @@ -567,168 +413,6 @@ static int doStopCPUs(struct qemud_driver *driver, virDomainObjPtr vm) return ret; } -void qemuDomainDefNamespaceFree(void *nsdata) -{ - qemuDomainCmdlineDefPtr cmd = nsdata; - unsigned int i; - - if (!cmd) - return; - - for (i = 0; i < cmd->num_args; i++) - VIR_FREE(cmd->args[i]); - for (i = 0; i < cmd->num_env; i++) { - VIR_FREE(cmd->env_name[i]); - VIR_FREE(cmd->env_value[i]); - } - VIR_FREE(cmd->args); - VIR_FREE(cmd->env_name); - VIR_FREE(cmd->env_value); - VIR_FREE(cmd); -} - -int qemuDomainDefNamespaceParse(xmlDocPtr xml, - xmlNodePtr root, - xmlXPathContextPtr ctxt, - void **data) -{ - qemuDomainCmdlineDefPtr cmd = NULL; - xmlNsPtr ns; - xmlNodePtr *nodes = NULL; - int n, i; - - ns = xmlSearchNs(xml, root, BAD_CAST "qemu"); - if (!ns) - /* this is fine; it just means there was no qemu namespace listed */ - return 0; - - if (STRNEQ((const char *)ns->href, QEMU_NAMESPACE_HREF)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Found namespace '%s' doesn't match expected '%s'"), - ns->href, QEMU_NAMESPACE_HREF); - return -1; - } - - if (xmlXPathRegisterNs(ctxt, ns->prefix, ns->href) < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Failed to register xml namespace '%s'"), ns->href); - return -1; - } - - if (VIR_ALLOC(cmd) < 0) { - virReportOOMError(); - return -1; - } - - /* first handle the extra command-line arguments */ - n = virXPathNodeSet("./qemu:commandline/qemu:arg", ctxt, &nodes); - if (n < 0) - /* virXPathNodeSet already set the error */ - goto error; - - if (n && VIR_ALLOC_N(cmd->args, n) < 0) - goto no_memory; - - for (i = 0; i < n; i++) { - cmd->args[cmd->num_args] = virXMLPropString(nodes[i], "value"); - if (cmd->args[cmd->num_args] == NULL) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("No qemu command-line argument specified")); - goto error; - } - cmd->num_args++; - } - - VIR_FREE(nodes); - - /* now handle the extra environment variables */ - n = virXPathNodeSet("./qemu:commandline/qemu:env", ctxt, &nodes); - if (n < 0) - /* virXPathNodeSet already set the error */ - goto error; - - if (n && VIR_ALLOC_N(cmd->env_name, n) < 0) - goto no_memory; - - if (n && VIR_ALLOC_N(cmd->env_value, n) < 0) - goto no_memory; - - for (i = 0; i < n; i++) { - char *tmp; - - tmp = virXMLPropString(nodes[i], "name"); - if (tmp == NULL) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("No qemu environment name specified")); - goto error; - } - if (tmp[0] == '\0') { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Empty qemu environment name specified")); - goto error; - } - if (!c_isalpha(tmp[0]) && tmp[0] != '_') { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Invalid environment name, it must begin with a letter or underscore")); - goto error; - } - if (strspn(tmp, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_") != strlen(tmp)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Invalid environment name, it must contain only alphanumerics and underscore")); - goto error; - } - - cmd->env_name[cmd->num_env] = tmp; - - cmd->env_value[cmd->num_env] = virXMLPropString(nodes[i], "value"); - /* a NULL value for command is allowed, since it might be empty */ - cmd->num_env++; - } - - VIR_FREE(nodes); - - *data = cmd; - - return 0; - -no_memory: - virReportOOMError(); - -error: - VIR_FREE(nodes); - qemuDomainDefNamespaceFree(cmd); - return -1; -} - -int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, - void *nsdata) -{ - qemuDomainCmdlineDefPtr cmd = nsdata; - unsigned int i; - - if (!cmd->num_args && !cmd->num_env) - return 0; - - virBufferAddLit(buf, " <qemu:commandline>\n"); - for (i = 0; i < cmd->num_args; i++) - virBufferEscapeString(buf, " <qemu:arg value='%s'/>\n", - cmd->args[i]); - for (i = 0; i < cmd->num_env; i++) { - virBufferVSprintf(buf, " <qemu:env name='%s'", cmd->env_name[i]); - if (cmd->env_value[i]) - virBufferEscapeString(buf, " value='%s'", cmd->env_value[i]); - virBufferAddLit(buf, "/>\n"); - } - virBufferAddLit(buf, " </qemu:commandline>\n"); - - return 0; -} - -const char *qemuDomainDefNamespaceHref(void) -{ - return "xmlns:qemu='" QEMU_NAMESPACE_HREF "'"; -} - static int qemuCgroupControllerActive(struct qemud_driver *driver, int controller) { @@ -1614,11 +1298,8 @@ qemuCreateCapabilities(virCapsPtr oldcaps, caps->defaultDiskDriverType = "raw"; } - /* Domain XML parser hooks */ - caps->privateDataAllocFunc = qemuDomainObjPrivateAlloc; - caps->privateDataFreeFunc = qemuDomainObjPrivateFree; - caps->privateDataXMLFormat = qemuDomainObjPrivateXMLFormat; - caps->privateDataXMLParse = qemuDomainObjPrivateXMLParse; + qemuDomainSetPrivateDataHooks(caps); + qemuDomainSetNamespaceHooks(caps); if (virGetHostUUID(caps->host.host_uuid)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, @@ -1626,12 +1307,6 @@ qemuCreateCapabilities(virCapsPtr oldcaps, goto err_exit; } - /* Domain Namespace XML parser hooks */ - caps->ns.parse = qemuDomainDefNamespaceParse; - caps->ns.free = qemuDomainDefNamespaceFree; - caps->ns.format = qemuDomainDefNamespaceFormatXML; - caps->ns.href = qemuDomainDefNamespaceHref; - /* Security driver data */ if (driver->securityPrimaryDriver) { const char *doi, *model; diff --git a/src/qemu/qemu_driver.h b/src/qemu/qemu_driver.h index 60131a7..dac0935 100644 --- a/src/qemu/qemu_driver.h +++ b/src/qemu/qemu_driver.h @@ -51,13 +51,4 @@ int qemuRegister(void); -void qemuDomainDefNamespaceFree(void *nsdata); -int qemuDomainDefNamespaceParse(xmlDocPtr xml, - xmlNodePtr root, - xmlXPathContextPtr ctxt, - void **data); -int qemuDomainDefNamespaceFormatXML(virBufferPtr buf, - void *nsdata); -const char *qemuDomainDefNamespaceHref(void); - #endif /* QEMUD_DRIVER_H */ -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
Move the code for handling the QEMU virDomainObjPtr private data, and custom XML namespace into a separate file
* src/qemu/qemu_domain.c, src/qemu/qemu_domain.h: New file for private data & namespace code * src/qemu/qemu_driver.c, src/qemu/qemu_driver.h: Remove private data & namespace code * src/qemu/qemu_driver.h, src/qemu/qemu_command.h: Update includes * src/Makefile.am: Add src/qemu/qemu_domain.c
ACK, once 3 and 4 are squashed together. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On 12/16/2010 01:31 PM, Eric Blake wrote:
On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
Move the code for handling the QEMU virDomainObjPtr private data, and custom XML namespace into a separate file
* src/qemu/qemu_domain.c, src/qemu/qemu_domain.h: New file for private data & namespace code * src/qemu/qemu_driver.c, src/qemu/qemu_driver.h: Remove private data & namespace code * src/qemu/qemu_driver.h, src/qemu/qemu_command.h: Update includes * src/Makefile.am: Add src/qemu/qemu_domain.c
ACK, once 3 and 4 are squashed together.
Needs a bit more work; even after this is squashed in, you marked qemuDomainDefNamespaceParse and friends static, which breaks testutilsqemu; I'm not sure if you want to declare those in src/qemu/qemu_domain.h for everyone, or just manually do it in tests/testutilsqemu.c. diff --git i/po/POTFILES.in w/po/POTFILES.in index f71ce2a..eda4aa3 100644 --- i/po/POTFILES.in +++ w/po/POTFILES.in @@ -54,6 +54,7 @@ src/qemu/qemu_bridge_filter.c src/qemu/qemu_capabilities.c src/qemu/qemu_command.c src/qemu/qemu_conf.c +src/qemu/qemu_domain.c src/qemu/qemu_driver.c src/qemu/qemu_monitor.c src/qemu/qemu_monitor_json.c diff --git i/src/qemu/qemu_command.h w/src/qemu/qemu_command.h index 9252642..4c42a10 100644 --- i/src/qemu/qemu_command.h +++ w/src/qemu/qemu_command.h @@ -28,7 +28,7 @@ # include "command.h" # include "capabilities.h" # include "qemu_conf.h" -#include "qemu_domain.h" +# include "qemu_domain.h" /* Config type for XML import/export conversions */ # define QEMU_CONFIG_FORMAT_ARGV "qemu-argv" diff --git i/tests/testutilsqemu.c w/tests/testutilsqemu.c index 72fc8aa..d01bd8b 100644 --- i/tests/testutilsqemu.c +++ w/tests/testutilsqemu.c @@ -8,6 +8,7 @@ # include "memory.h" # include "cpu_conf.h" # include "qemu/qemu_driver.h" +# include "qemu/qemu_domain.h" static virCapsGuestMachinePtr *testQemuAllocMachines(int *nmachines) { -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

The QEMU driver file is far too large. Move all the audit helper code out into a separate file. No functional change. * src/qemu/qemu_audit.c, src/qemu/qemu_audit.h, src/Makefile.am: Add audit helper file * src/qemu/qemu_driver.c: Delete audit code --- src/Makefile.am | 1 + src/qemu/qemu_audit.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_audit.h | 43 ++++++++++++ src/qemu/qemu_driver.c | 141 +--------------------------------------- 4 files changed, 215 insertions(+), 140 deletions(-) create mode 100644 src/qemu/qemu_audit.c create mode 100644 src/qemu/qemu_audit.h diff --git a/src/Makefile.am b/src/Makefile.am index d2fcd5f..381ca3d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -270,6 +270,7 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_capabilities.c qemu/qemu_capabilities.h\ qemu/qemu_command.c qemu/qemu_command.h \ qemu/qemu_domain.c qemu/qemu_domain.h \ + qemu/qemu_audit.c qemu/qemu_audit.h \ qemu/qemu_conf.c qemu/qemu_conf.h \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ diff --git a/src/qemu/qemu_audit.c b/src/qemu/qemu_audit.c new file mode 100644 index 0000000..e8320d0 --- /dev/null +++ b/src/qemu/qemu_audit.c @@ -0,0 +1,170 @@ +/* + * qemu_audit.c: QEMU audit management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "qemu_audit.h" +#include "virtaudit.h" +#include "uuid.h" +#include "logging.h" +#include "memory.h" + +void qemuDomainDiskAudit(virDomainObjPtr vm, + virDomainDiskDefPtr oldDef, + virDomainDiskDefPtr newDef, + const char *reason, + bool success) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *vmname; + char *oldsrc = NULL; + char *newsrc = NULL; + + virUUIDFormat(vm->def->uuid, uuidstr); + if (!(vmname = virAuditEncode("vm", vm->def->name))) { + VIR_WARN0("OOM while encoding audit message"); + return; + } + + if (!(oldsrc = virAuditEncode("old-disk", + oldDef && oldDef->src ? + oldDef->src : "?"))) { + VIR_WARN0("OOM while encoding audit message"); + goto cleanup; + } + if (!(newsrc = virAuditEncode("new-disk", + newDef && newDef->src ? + newDef->src : "?"))) { + VIR_WARN0("OOM while encoding audit message"); + goto cleanup; + } + + VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, + "resrc=disk reason=%s %s uuid=%s %s %s", + reason, vmname, uuidstr, + oldsrc, newsrc); + +cleanup: + VIR_FREE(vmname); + VIR_FREE(oldsrc); + VIR_FREE(newsrc); +} + + +void qemuDomainNetAudit(virDomainObjPtr vm, + virDomainNetDefPtr oldDef, + virDomainNetDefPtr newDef, + const char *reason, + bool success) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char newMacstr[VIR_MAC_STRING_BUFLEN]; + char oldMacstr[VIR_MAC_STRING_BUFLEN]; + char *vmname; + + virUUIDFormat(vm->def->uuid, uuidstr); + if (oldDef) + virFormatMacAddr(oldDef->mac, oldMacstr); + if (newDef) + virFormatMacAddr(newDef->mac, newMacstr); + if (!(vmname = virAuditEncode("vm", vm->def->name))) { + VIR_WARN0("OOM while encoding audit message"); + return; + } + + VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, + "resrc=net reason=%s %s uuid=%s old-net='%s' new-net='%s'", + reason, vmname, uuidstr, + oldDef ? oldMacstr : "?", + newDef ? newMacstr : "?"); + + VIR_FREE(vmname); +} + + +static void qemuDomainLifecycleAudit(virDomainObjPtr vm, + const char *op, + const char *reason, + bool success) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *vmname; + + virUUIDFormat(vm->def->uuid, uuidstr); + + if (!(vmname = virAuditEncode("vm", vm->def->name))) { + VIR_WARN0("OOM while encoding audit message"); + return; + } + + VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_CONTROL, success, + "op=%s reason=%s %s uuid=%s", op, reason, vmname, uuidstr); + + VIR_FREE(vmname); +} + + +void qemuDomainStartAudit(virDomainObjPtr vm, const char *reason, bool success) +{ + int i; + + for (i = 0 ; i < vm->def->ndisks ; i++) { + virDomainDiskDefPtr disk = vm->def->disks[i]; + if (disk->src) /* Skips CDROM without media initially inserted */ + qemuDomainDiskAudit(vm, NULL, disk, "start", true); + } + + for (i = 0 ; i < vm->def->nnets ; i++) { + virDomainNetDefPtr net = vm->def->nets[i]; + qemuDomainNetAudit(vm, NULL, net, "start", true); + } + + qemuDomainLifecycleAudit(vm, "start", reason, success); +} + + +void qemuDomainStopAudit(virDomainObjPtr vm, const char *reason) +{ + qemuDomainLifecycleAudit(vm, "stop", reason, true); +} + +void qemuDomainSecurityLabelAudit(virDomainObjPtr vm, bool success) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + char *vmname; + + virUUIDFormat(vm->def->uuid, uuidstr); + if (!(vmname = virAuditEncode("vm", vm->def->name))) { + VIR_WARN0("OOM while encoding audit message"); + return; + } + + VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_ID, success, + "%s uuid=%s vm-ctx=%s img-ctx=%s", + vmname, uuidstr, + VIR_AUDIT_STR(vm->def->seclabel.label), + VIR_AUDIT_STR(vm->def->seclabel.imagelabel)); + + VIR_FREE(vmname); +} + diff --git a/src/qemu/qemu_audit.h b/src/qemu/qemu_audit.h new file mode 100644 index 0000000..a4064ba --- /dev/null +++ b/src/qemu/qemu_audit.h @@ -0,0 +1,43 @@ +/* + * qemu_audit.h: QEMU audit management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __QEMU_AUDIT_H__ +# define __QEMU_AUDIT_H__ + +#include "domain_conf.h" + +void qemuDomainStartAudit(virDomainObjPtr vm, const char *reason, bool success); +void qemuDomainStopAudit(virDomainObjPtr vm, const char *reason); +void qemuDomainDiskAudit(virDomainObjPtr vm, + virDomainDiskDefPtr oldDef, + virDomainDiskDefPtr newDef, + const char *reason, + bool success); +void qemuDomainNetAudit(virDomainObjPtr vm, + virDomainNetDefPtr oldDef, + virDomainNetDefPtr newDef, + const char *reason, + bool success); +void qemuDomainSecurityLabelAudit(virDomainObjPtr vm, bool success); + +#endif /* __QEMU_AUDIT_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9ce8fbe..c4afe20 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -57,6 +57,7 @@ #include "qemu_command.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" +#include "qemu_audit.h" #include "c-ctype.h" #include "event.h" #include "buf.h" @@ -82,7 +83,6 @@ #include "domain_nwfilter.h" #include "hooks.h" #include "storage_file.h" -#include "virtaudit.h" #include "files.h" #include "fdstream.h" #include "configmake.h" @@ -139,9 +139,6 @@ static void qemudShutdownVMDaemon(struct qemud_driver *driver, virDomainObjPtr vm, int migrated); -static void qemuDomainStartAudit(virDomainObjPtr vm, const char *reason, bool success); -static void qemuDomainStopAudit(virDomainObjPtr vm, const char *reason); - static int qemudDomainGetMaxVcpus(virDomainPtr dom); static int qemuDetectVcpuPIDs(struct qemud_driver *driver, @@ -3441,142 +3438,6 @@ static int qemuDomainSnapshotSetActive(virDomainObjPtr vm, static int qemuDomainSnapshotSetInactive(virDomainObjPtr vm, char *snapshotDir); -static void qemuDomainDiskAudit(virDomainObjPtr vm, - virDomainDiskDefPtr oldDef, - virDomainDiskDefPtr newDef, - const char *reason, - bool success) -{ - char uuidstr[VIR_UUID_STRING_BUFLEN]; - char *vmname; - char *oldsrc = NULL; - char *newsrc = NULL; - - virUUIDFormat(vm->def->uuid, uuidstr); - if (!(vmname = virAuditEncode("vm", vm->def->name))) { - VIR_WARN0("OOM while encoding audit message"); - return; - } - - if (!(oldsrc = virAuditEncode("old-disk", - oldDef && oldDef->src ? - oldDef->src : "?"))) { - VIR_WARN0("OOM while encoding audit message"); - goto cleanup; - } - if (!(newsrc = virAuditEncode("new-disk", - newDef && newDef->src ? - newDef->src : "?"))) { - VIR_WARN0("OOM while encoding audit message"); - goto cleanup; - } - - VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, - "resrc=disk reason=%s %s uuid=%s %s %s", - reason, vmname, uuidstr, - oldsrc, newsrc); - -cleanup: - VIR_FREE(vmname); - VIR_FREE(oldsrc); - VIR_FREE(newsrc); -} - - -static void qemuDomainNetAudit(virDomainObjPtr vm, - virDomainNetDefPtr oldDef, - virDomainNetDefPtr newDef, - const char *reason, - bool success) -{ - char uuidstr[VIR_UUID_STRING_BUFLEN]; - char newMacstr[VIR_MAC_STRING_BUFLEN]; - char oldMacstr[VIR_MAC_STRING_BUFLEN]; - char *vmname; - - virUUIDFormat(vm->def->uuid, uuidstr); - if (oldDef) - virFormatMacAddr(oldDef->mac, oldMacstr); - if (newDef) - virFormatMacAddr(newDef->mac, newMacstr); - if (!(vmname = virAuditEncode("vm", vm->def->name))) { - VIR_WARN0("OOM while encoding audit message"); - return; - } - - VIR_AUDIT(VIR_AUDIT_RECORD_RESOURCE, success, - "resrc=net reason=%s %s uuid=%s old-net='%s' new-net='%s'", - reason, vmname, uuidstr, - oldDef ? oldMacstr : "?", - newDef ? newMacstr : "?"); - - VIR_FREE(vmname); -} - - -static void qemuDomainLifecycleAudit(virDomainObjPtr vm, - const char *op, - const char *reason, - bool success) -{ - char uuidstr[VIR_UUID_STRING_BUFLEN]; - char *vmname; - - virUUIDFormat(vm->def->uuid, uuidstr); - - if (!(vmname = virAuditEncode("vm", vm->def->name))) { - VIR_WARN0("OOM while encoding audit message"); - return; - } - - VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_CONTROL, success, - "op=%s reason=%s %s uuid=%s", op, reason, vmname, uuidstr); - - VIR_FREE(vmname); -} - -static void qemuDomainStartAudit(virDomainObjPtr vm, const char *reason, bool success) -{ - int i; - - for (i = 0 ; i < vm->def->ndisks ; i++) { - virDomainDiskDefPtr disk = vm->def->disks[i]; - if (disk->src) /* Skips CDROM without media initially inserted */ - qemuDomainDiskAudit(vm, NULL, disk, "start", true); - } - - for (i = 0 ; i < vm->def->nnets ; i++) { - virDomainNetDefPtr net = vm->def->nets[i]; - qemuDomainNetAudit(vm, NULL, net, "start", true); - } - - qemuDomainLifecycleAudit(vm, "start", reason, success); -} - -static void qemuDomainStopAudit(virDomainObjPtr vm, const char *reason) -{ - qemuDomainLifecycleAudit(vm, "stop", reason, true); -} - -static void qemuDomainSecurityLabelAudit(virDomainObjPtr vm, bool success) -{ - char uuidstr[VIR_UUID_STRING_BUFLEN]; - char *vmname; - - virUUIDFormat(vm->def->uuid, uuidstr); - if (!(vmname = virAuditEncode("vm", vm->def->name))) { - VIR_WARN0("OOM while encoding audit message"); - return; - } - - VIR_AUDIT(VIR_AUDIT_RECORD_MACHINE_ID, success, - "%s uuid=%s vm-ctx=%s img-ctx=%s", - vmname, uuidstr, - VIR_AUDIT_STR(vm->def->seclabel.label), - VIR_AUDIT_STR(vm->def->seclabel.imagelabel)); - - VIR_FREE(vmname); -} #define START_POSTFIX ": starting up\n" #define SHUTDOWN_POSTFIX ": shutting down\n" -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
The QEMU driver file is far too large. Move all the audit helper code out into a separate file. No functional change.
* src/qemu/qemu_audit.c, src/qemu/qemu_audit.h, src/Makefile.am: Add audit helper file * src/qemu/qemu_driver.c: Delete audit code --- src/Makefile.am | 1 + src/qemu/qemu_audit.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_audit.h | 43 ++++++++++++ src/qemu/qemu_driver.c | 141 +--------------------------------------- 4 files changed, 215 insertions(+), 140 deletions(-) create mode 100644 src/qemu/qemu_audit.c create mode 100644 src/qemu/qemu_audit.h
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

The QEMU driver file is far too large. Move all the cgroup helper code out into a separate file. No functional change. * src/qemu/qemu_cgroup.c, src/qemu/qemu_cgroup.h, src/Makefile.am: Add cgroup helper file * src/qemu/qemu_driver.c: Delete cgroup code --- src/Makefile.am | 1 + src/qemu/qemu_cgroup.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_cgroup.h | 61 ++++++++ src/qemu/qemu_driver.c | 343 +------------------------------------------- 4 files changed, 440 insertions(+), 342 deletions(-) create mode 100644 src/qemu/qemu_cgroup.c create mode 100644 src/qemu/qemu_cgroup.h diff --git a/src/Makefile.am b/src/Makefile.am index 381ca3d..da3bd6f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -271,6 +271,7 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_command.c qemu/qemu_command.h \ qemu/qemu_domain.c qemu/qemu_domain.h \ qemu/qemu_audit.c qemu/qemu_audit.h \ + qemu/qemu_cgroup.c qemu/qemu_cgroup.c \ qemu/qemu_conf.c qemu/qemu_conf.h \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c new file mode 100644 index 0000000..0fd12ca --- /dev/null +++ b/src/qemu/qemu_cgroup.c @@ -0,0 +1,377 @@ +/* + * qemu_cgroup.c: QEMU cgroup management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "qemu_cgroup.h" +#include "cgroup.h" +#include "logging.h" +#include "memory.h" +#include "virterror_internal.h" +#include "util.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +static const char *const defaultDeviceACL[] = { + "/dev/null", "/dev/full", "/dev/zero", + "/dev/random", "/dev/urandom", + "/dev/ptmx", "/dev/kvm", "/dev/kqemu", + "/dev/rtc", "/dev/hpet", "/dev/net/tun", + NULL, +}; +#define DEVICE_PTY_MAJOR 136 +#define DEVICE_SND_MAJOR 116 + +int qemuCgroupControllerActive(struct qemud_driver *driver, + int controller) +{ + if (driver->cgroup == NULL) + return 0; + if (!virCgroupMounted(driver->cgroup, controller)) + return 0; + if (driver->cgroupControllers & (1 << controller)) + return 1; + return 0; +} + + +int qemuSetupDiskPathAllow(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, + const char *path, + size_t depth ATTRIBUTE_UNUSED, + void *opaque) +{ + virCgroupPtr cgroup = opaque; + int rc; + + VIR_DEBUG("Process path %s for disk", path); + /* XXX RO vs RW */ + rc = virCgroupAllowDevicePath(cgroup, path); + if (rc != 0) { + /* Get this for non-block devices */ + if (rc == -EINVAL) { + VIR_DEBUG("Ignoring EINVAL for %s", path); + } else if (rc == -EACCES) { /* Get this for root squash NFS */ + VIR_DEBUG("Ignoring EACCES for %s", path); + } else { + virReportSystemError(-rc, + _("Unable to allow access for disk path %s"), + path); + return -1; + } + } + return 0; +} + + +int qemuSetupDiskCgroup(struct qemud_driver *driver, + virCgroupPtr cgroup, + virDomainDiskDefPtr disk) +{ + return virDomainDiskDefForeachPath(disk, + driver->allowDiskFormatProbing, + true, + qemuSetupDiskPathAllow, + cgroup); +} + + +int qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, + const char *path, + size_t depth ATTRIBUTE_UNUSED, + void *opaque) +{ + virCgroupPtr cgroup = opaque; + int rc; + + VIR_DEBUG("Process path %s for disk", path); + /* XXX RO vs RW */ + rc = virCgroupDenyDevicePath(cgroup, path); + if (rc != 0) { + /* Get this for non-block devices */ + if (rc == -EINVAL) { + VIR_DEBUG("Ignoring EINVAL for %s", path); + } else if (rc == -EACCES) { /* Get this for root squash NFS */ + VIR_DEBUG("Ignoring EACCES for %s", path); + } else { + virReportSystemError(-rc, + _("Unable to deny access for disk path %s"), + path); + return -1; + } + } + return 0; +} + + +int qemuTeardownDiskCgroup(struct qemud_driver *driver, + virCgroupPtr cgroup, + virDomainDiskDefPtr disk) +{ + return virDomainDiskDefForeachPath(disk, + driver->allowDiskFormatProbing, + true, + qemuTeardownDiskPathDeny, + cgroup); +} + + +int qemuSetupChardevCgroup(virDomainDefPtr def, + virDomainChrDefPtr dev, + void *opaque) +{ + virCgroupPtr cgroup = opaque; + int rc; + + if (dev->type != VIR_DOMAIN_CHR_TYPE_DEV) + return 0; + + + VIR_DEBUG("Process path '%s' for disk", dev->data.file.path); + rc = virCgroupAllowDevicePath(cgroup, dev->data.file.path); + if (rc != 0) { + virReportSystemError(-rc, + _("Unable to allow device %s for %s"), + dev->data.file.path, def->name); + return -1; + } + + return 0; +} + + +int qemuSetupHostUsbDeviceCgroup(usbDevice *dev ATTRIBUTE_UNUSED, + const char *path, + void *opaque) +{ + virCgroupPtr cgroup = opaque; + int rc; + + VIR_DEBUG("Process path '%s' for USB device", path); + rc = virCgroupAllowDevicePath(cgroup, path); + if (rc != 0) { + virReportSystemError(-rc, + _("Unable to allow device %s"), + path); + return -1; + } + + return 0; +} + +int qemuSetupCgroup(struct qemud_driver *driver, + virDomainObjPtr vm) +{ + virCgroupPtr cgroup = NULL; + int rc; + unsigned int i; + const char *const *deviceACL = + driver->cgroupDeviceACL ? + (const char *const *)driver->cgroupDeviceACL : + defaultDeviceACL; + + if (driver->cgroup == NULL) + return 0; /* Not supported, so claim success */ + + rc = virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 1); + if (rc != 0) { + virReportSystemError(-rc, + _("Unable to create cgroup for %s"), + vm->def->name); + goto cleanup; + } + + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + rc = virCgroupDenyAllDevices(cgroup); + if (rc != 0) { + if (rc == -EPERM) { + VIR_WARN0("Group devices ACL is not accessible, disabling whitelisting"); + goto done; + } + + virReportSystemError(-rc, + _("Unable to deny all devices for %s"), vm->def->name); + goto cleanup; + } + + for (i = 0; i < vm->def->ndisks ; i++) { + if (qemuSetupDiskCgroup(driver, cgroup, vm->def->disks[i]) < 0) + goto cleanup; + } + + rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_PTY_MAJOR); + if (rc != 0) { + virReportSystemError(-rc, "%s", + _("unable to allow /dev/pts/ devices")); + goto cleanup; + } + + if (vm->def->nsounds) { + rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_SND_MAJOR); + if (rc != 0) { + virReportSystemError(-rc, "%s", + _("unable to allow /dev/snd/ devices")); + goto cleanup; + } + } + + for (i = 0; deviceACL[i] != NULL ; i++) { + rc = virCgroupAllowDevicePath(cgroup, + deviceACL[i]); + if (rc < 0 && + rc != -ENOENT) { + virReportSystemError(-rc, + _("unable to allow device %s"), + deviceACL[i]); + goto cleanup; + } + } + + if (virDomainChrDefForeach(vm->def, + true, + qemuSetupChardevCgroup, + cgroup) < 0) + goto cleanup; + + for (i = 0; i < vm->def->nhostdevs; i++) { + virDomainHostdevDefPtr hostdev = vm->def->hostdevs[i]; + usbDevice *usb; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device)) == NULL) + goto cleanup; + + if (usbDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, cgroup) < 0 ) + goto cleanup; + } + } + + if ((rc = qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_MEMORY))) { + if (vm->def->mem.hard_limit != 0) { + rc = virCgroupSetMemoryHardLimit(cgroup, vm->def->mem.hard_limit); + if (rc != 0) { + virReportSystemError(-rc, + _("Unable to set memory hard limit for domain %s"), + vm->def->name); + goto cleanup; + } + } + if (vm->def->mem.soft_limit != 0) { + rc = virCgroupSetMemorySoftLimit(cgroup, vm->def->mem.soft_limit); + if (rc != 0) { + virReportSystemError(-rc, + _("Unable to set memory soft limit for domain %s"), + vm->def->name); + goto cleanup; + } + } + + if (vm->def->mem.swap_hard_limit != 0) { + rc = virCgroupSetSwapHardLimit(cgroup, vm->def->mem.swap_hard_limit); + if (rc != 0) { + virReportSystemError(-rc, + _("Unable to set swap hard limit for domain %s"), + vm->def->name); + goto cleanup; + } + } + } else { + VIR_WARN("Memory cgroup is disabled in qemu configuration file: %s", + vm->def->name); + } + +done: + virCgroupFree(&cgroup); + return 0; + +cleanup: + if (cgroup) { + virCgroupRemove(cgroup); + virCgroupFree(&cgroup); + } + return -1; +} + + +int qemuRemoveCgroup(struct qemud_driver *driver, + virDomainObjPtr vm, + int quiet) +{ + virCgroupPtr cgroup; + int rc; + + if (driver->cgroup == NULL) + return 0; /* Not supported, so claim success */ + + rc = virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0); + if (rc != 0) { + if (!quiet) + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find cgroup for %s\n"), + vm->def->name); + return rc; + } + + rc = virCgroupRemove(cgroup); + virCgroupFree(&cgroup); + return rc; +} + +int qemuAddToCgroup(struct qemud_driver *driver, + virDomainDefPtr def) +{ + virCgroupPtr cgroup = NULL; + int ret = -1; + int rc; + + if (driver->cgroup == NULL) + return 0; /* Not supported, so claim success */ + + rc = virCgroupForDomain(driver->cgroup, def->name, &cgroup, 0); + if (rc != 0) { + virReportSystemError(-rc, + _("unable to find cgroup for domain %s"), + def->name); + goto cleanup; + } + + rc = virCgroupAddTask(cgroup, getpid()); + if (rc != 0) { + virReportSystemError(-rc, + _("unable to add domain %s task %d to cgroup"), + def->name, getpid()); + goto cleanup; + } + + ret = 0; + +cleanup: + virCgroupFree(&cgroup); + return ret; +} + + diff --git a/src/qemu/qemu_cgroup.h b/src/qemu/qemu_cgroup.h new file mode 100644 index 0000000..193778a --- /dev/null +++ b/src/qemu/qemu_cgroup.h @@ -0,0 +1,61 @@ +/* + * qemu_cgroup.h: QEMU cgroup management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __QEMU_CGROUP_H__ +# define __QEMU_CGROUP_H__ + +#include "hostusb.h" +#include "domain_conf.h" +#include "qemu_conf.h" + +int qemuCgroupControllerActive(struct qemud_driver *driver, + int controller); +int qemuSetupDiskPathAllow(virDomainDiskDefPtr disk, + const char *path, + size_t depth, + void *opaque); +int qemuSetupDiskCgroup(struct qemud_driver *driver, + virCgroupPtr cgroup, + virDomainDiskDefPtr disk); +int qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk, + const char *path, + size_t depth, + void *opaque); +int qemuTeardownDiskCgroup(struct qemud_driver *driver, + virCgroupPtr cgroup, + virDomainDiskDefPtr disk); +int qemuSetupChardevCgroup(virDomainDefPtr def, + virDomainChrDefPtr dev, + void *opaque); +int qemuSetupHostUsbDeviceCgroup(usbDevice *dev, + const char *path, + void *opaque); +int qemuSetupCgroup(struct qemud_driver *driver, + virDomainObjPtr vm); +int qemuRemoveCgroup(struct qemud_driver *driver, + virDomainObjPtr vm, + int quiet); +int qemuAddToCgroup(struct qemud_driver *driver, + virDomainDefPtr def); + +#endif /* __QEMU_CGROUP_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index c4afe20..585b259 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -55,6 +55,7 @@ #include "qemu_conf.h" #include "qemu_capabilities.h" #include "qemu_command.h" +#include "qemu_cgroup.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" #include "qemu_audit.h" @@ -74,7 +75,6 @@ #include "processinfo.h" #include "qemu_security_stacked.h" #include "qemu_security_dac.h" -#include "cgroup.h" #include "libvirt_internal.h" #include "xml.h" #include "cpu/cpu.h" @@ -410,17 +410,6 @@ static int doStopCPUs(struct qemud_driver *driver, virDomainObjPtr vm) return ret; } -static int qemuCgroupControllerActive(struct qemud_driver *driver, - int controller) -{ - if (driver->cgroup == NULL) - return 0; - if (!virCgroupMounted(driver->cgroup, controller)) - return 0; - if (driver->cgroupControllers & (1 << controller)) - return 1; - return 0; -} static int qemudLogFD(struct qemud_driver *driver, const char* name, bool append) @@ -3050,336 +3039,6 @@ qemuDomainReAttachHostDevices(struct qemud_driver *driver, qemuDomainReAttachHostdevDevices(driver, def->hostdevs, def->nhostdevs); } -static const char *const defaultDeviceACL[] = { - "/dev/null", "/dev/full", "/dev/zero", - "/dev/random", "/dev/urandom", - "/dev/ptmx", "/dev/kvm", "/dev/kqemu", - "/dev/rtc", "/dev/hpet", "/dev/net/tun", - NULL, -}; -#define DEVICE_PTY_MAJOR 136 -#define DEVICE_SND_MAJOR 116 - - -static int qemuSetupDiskPathAllow(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, - const char *path, - size_t depth ATTRIBUTE_UNUSED, - void *opaque) -{ - virCgroupPtr cgroup = opaque; - int rc; - - VIR_DEBUG("Process path %s for disk", path); - /* XXX RO vs RW */ - rc = virCgroupAllowDevicePath(cgroup, path); - if (rc != 0) { - /* Get this for non-block devices */ - if (rc == -EINVAL) { - VIR_DEBUG("Ignoring EINVAL for %s", path); - } else if (rc == -EACCES) { /* Get this for root squash NFS */ - VIR_DEBUG("Ignoring EACCES for %s", path); - } else { - virReportSystemError(-rc, - _("Unable to allow access for disk path %s"), - path); - return -1; - } - } - return 0; -} - - -static int qemuSetupDiskCgroup(struct qemud_driver *driver, - virCgroupPtr cgroup, - virDomainDiskDefPtr disk) -{ - return virDomainDiskDefForeachPath(disk, - driver->allowDiskFormatProbing, - true, - qemuSetupDiskPathAllow, - cgroup); -} - - -static int qemuTeardownDiskPathDeny(virDomainDiskDefPtr disk ATTRIBUTE_UNUSED, - const char *path, - size_t depth ATTRIBUTE_UNUSED, - void *opaque) -{ - virCgroupPtr cgroup = opaque; - int rc; - - VIR_DEBUG("Process path %s for disk", path); - /* XXX RO vs RW */ - rc = virCgroupDenyDevicePath(cgroup, path); - if (rc != 0) { - /* Get this for non-block devices */ - if (rc == -EINVAL) { - VIR_DEBUG("Ignoring EINVAL for %s", path); - } else if (rc == -EACCES) { /* Get this for root squash NFS */ - VIR_DEBUG("Ignoring EACCES for %s", path); - } else { - virReportSystemError(-rc, - _("Unable to deny access for disk path %s"), - path); - return -1; - } - } - return 0; -} - - -static int qemuTeardownDiskCgroup(struct qemud_driver *driver, - virCgroupPtr cgroup, - virDomainDiskDefPtr disk) -{ - return virDomainDiskDefForeachPath(disk, - driver->allowDiskFormatProbing, - true, - qemuTeardownDiskPathDeny, - cgroup); -} - - -static int qemuSetupChardevCgroup(virDomainDefPtr def, - virDomainChrDefPtr dev, - void *opaque) -{ - virCgroupPtr cgroup = opaque; - int rc; - - if (dev->type != VIR_DOMAIN_CHR_TYPE_DEV) - return 0; - - - VIR_DEBUG("Process path '%s' for disk", dev->data.file.path); - rc = virCgroupAllowDevicePath(cgroup, dev->data.file.path); - if (rc != 0) { - virReportSystemError(-rc, - _("Unable to allow device %s for %s"), - dev->data.file.path, def->name); - return -1; - } - - return 0; -} - - -static int qemuSetupHostUsbDeviceCgroup(usbDevice *dev ATTRIBUTE_UNUSED, - const char *path, - void *opaque) -{ - virCgroupPtr cgroup = opaque; - int rc; - - VIR_DEBUG("Process path '%s' for USB device", path); - rc = virCgroupAllowDevicePath(cgroup, path); - if (rc != 0) { - virReportSystemError(-rc, - _("Unable to allow device %s"), - path); - return -1; - } - - return 0; -} - -static int qemuSetupCgroup(struct qemud_driver *driver, - virDomainObjPtr vm) -{ - virCgroupPtr cgroup = NULL; - int rc; - unsigned int i; - const char *const *deviceACL = - driver->cgroupDeviceACL ? - (const char *const *)driver->cgroupDeviceACL : - defaultDeviceACL; - - if (driver->cgroup == NULL) - return 0; /* Not supported, so claim success */ - - rc = virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 1); - if (rc != 0) { - virReportSystemError(-rc, - _("Unable to create cgroup for %s"), - vm->def->name); - goto cleanup; - } - - if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { - rc = virCgroupDenyAllDevices(cgroup); - if (rc != 0) { - if (rc == -EPERM) { - VIR_WARN0("Group devices ACL is not accessible, disabling whitelisting"); - goto done; - } - - virReportSystemError(-rc, - _("Unable to deny all devices for %s"), vm->def->name); - goto cleanup; - } - - for (i = 0; i < vm->def->ndisks ; i++) { - if (qemuSetupDiskCgroup(driver, cgroup, vm->def->disks[i]) < 0) - goto cleanup; - } - - rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_PTY_MAJOR); - if (rc != 0) { - virReportSystemError(-rc, "%s", - _("unable to allow /dev/pts/ devices")); - goto cleanup; - } - - if (vm->def->nsounds) { - rc = virCgroupAllowDeviceMajor(cgroup, 'c', DEVICE_SND_MAJOR); - if (rc != 0) { - virReportSystemError(-rc, "%s", - _("unable to allow /dev/snd/ devices")); - goto cleanup; - } - } - - for (i = 0; deviceACL[i] != NULL ; i++) { - rc = virCgroupAllowDevicePath(cgroup, - deviceACL[i]); - if (rc < 0 && - rc != -ENOENT) { - virReportSystemError(-rc, - _("unable to allow device %s"), - deviceACL[i]); - goto cleanup; - } - } - - if (virDomainChrDefForeach(vm->def, - true, - qemuSetupChardevCgroup, - cgroup) < 0) - goto cleanup; - - for (i = 0; i < vm->def->nhostdevs; i++) { - virDomainHostdevDefPtr hostdev = vm->def->hostdevs[i]; - usbDevice *usb; - - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - continue; - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) - continue; - - if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device)) == NULL) - goto cleanup; - - if (usbDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, cgroup) < 0 ) - goto cleanup; - } - } - - if ((rc = qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_MEMORY))) { - if (vm->def->mem.hard_limit != 0) { - rc = virCgroupSetMemoryHardLimit(cgroup, vm->def->mem.hard_limit); - if (rc != 0) { - virReportSystemError(-rc, - _("Unable to set memory hard limit for domain %s"), - vm->def->name); - goto cleanup; - } - } - if (vm->def->mem.soft_limit != 0) { - rc = virCgroupSetMemorySoftLimit(cgroup, vm->def->mem.soft_limit); - if (rc != 0) { - virReportSystemError(-rc, - _("Unable to set memory soft limit for domain %s"), - vm->def->name); - goto cleanup; - } - } - - if (vm->def->mem.swap_hard_limit != 0) { - rc = virCgroupSetSwapHardLimit(cgroup, vm->def->mem.swap_hard_limit); - if (rc != 0) { - virReportSystemError(-rc, - _("Unable to set swap hard limit for domain %s"), - vm->def->name); - goto cleanup; - } - } - } else { - VIR_WARN("Memory cgroup is disabled in qemu configuration file: %s", - vm->def->name); - } - -done: - virCgroupFree(&cgroup); - return 0; - -cleanup: - if (cgroup) { - virCgroupRemove(cgroup); - virCgroupFree(&cgroup); - } - return -1; -} - - -static int qemuRemoveCgroup(struct qemud_driver *driver, - virDomainObjPtr vm, - int quiet) -{ - virCgroupPtr cgroup; - int rc; - - if (driver->cgroup == NULL) - return 0; /* Not supported, so claim success */ - - rc = virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0); - if (rc != 0) { - if (!quiet) - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to find cgroup for %s\n"), - vm->def->name); - return rc; - } - - rc = virCgroupRemove(cgroup); - virCgroupFree(&cgroup); - return rc; -} - -static int qemuAddToCgroup(struct qemud_driver *driver, - virDomainDefPtr def) -{ - virCgroupPtr cgroup = NULL; - int ret = -1; - int rc; - - if (driver->cgroup == NULL) - return 0; /* Not supported, so claim success */ - - rc = virCgroupForDomain(driver->cgroup, def->name, &cgroup, 0); - if (rc != 0) { - virReportSystemError(-rc, - _("unable to find cgroup for domain %s"), - def->name); - goto cleanup; - } - - rc = virCgroupAddTask(cgroup, getpid()); - if (rc != 0) { - virReportSystemError(-rc, - _("unable to add domain %s task %d to cgroup"), - def->name, getpid()); - goto cleanup; - } - - ret = 0; - -cleanup: - virCgroupFree(&cgroup); - return ret; -} - struct qemudHookData { virConnectPtr conn; -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
The QEMU driver file is far too large. Move all the cgroup helper code out into a separate file. No functional change.
* src/qemu/qemu_cgroup.c, src/qemu/qemu_cgroup.h, src/Makefile.am: Add cgroup helper file * src/qemu/qemu_driver.c: Delete cgroup code --- src/Makefile.am | 1 + src/qemu/qemu_cgroup.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_cgroup.h | 61 ++++++++ src/qemu/qemu_driver.c | 343 +------------------------------------------- 4 files changed, 440 insertions(+), 342 deletions(-) create mode 100644 src/qemu/qemu_cgroup.c create mode 100644 src/qemu/qemu_cgroup.h
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

The QEMU driver file is far too large. Move all the hostdev helper code out into a separate file. No functional change. * src/qemu/qemu_hostdev.c, src/qemu/qemu_hostdev.h, src/Makefile.am: Add hostdev helper file * src/qemu/qemu_driver.c: Delete hostdev code --- src/Makefile.am | 1 + src/qemu/qemu_driver.c | 266 +------------------------------------------- src/qemu/qemu_hostdev.c | 289 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hostdev.h | 45 ++++++++ 4 files changed, 337 insertions(+), 264 deletions(-) create mode 100644 src/qemu/qemu_hostdev.c create mode 100644 src/qemu/qemu_hostdev.h diff --git a/src/Makefile.am b/src/Makefile.am index da3bd6f..d30774b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -272,6 +272,7 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_domain.c qemu/qemu_domain.h \ qemu/qemu_audit.c qemu/qemu_audit.h \ qemu/qemu_cgroup.c qemu/qemu_cgroup.c \ + qemu/qemu_hostdev.c qemu/qemu_hostdev.h \ qemu/qemu_conf.c qemu/qemu_conf.h \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 585b259..5d8aef3 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -56,6 +56,7 @@ #include "qemu_capabilities.h" #include "qemu_command.h" #include "qemu_cgroup.h" +#include "qemu_hostdev.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" #include "qemu_audit.h" @@ -144,9 +145,6 @@ static int qemudDomainGetMaxVcpus(virDomainPtr dom); static int qemuDetectVcpuPIDs(struct qemud_driver *driver, virDomainObjPtr vm); -static int qemuUpdateActivePciHostdevs(struct qemud_driver *driver, - virDomainDefPtr def); - static int qemudVMFiltersInstantiate(virConnectPtr conn, virDomainDefPtr def); @@ -2758,192 +2756,6 @@ cleanup: } -static pciDeviceList * -qemuGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) -{ - pciDeviceList *list; - int i; - - if (!(list = pciDeviceListNew())) - return NULL; - - for (i = 0 ; i < nhostdevs ; i++) { - virDomainHostdevDefPtr hostdev = hostdevs[i]; - pciDevice *dev; - - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - continue; - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) - continue; - - dev = pciGetDevice(hostdev->source.subsys.u.pci.domain, - hostdev->source.subsys.u.pci.bus, - hostdev->source.subsys.u.pci.slot, - hostdev->source.subsys.u.pci.function); - if (!dev) { - pciDeviceListFree(list); - return NULL; - } - - if (pciDeviceListAdd(list, dev) < 0) { - pciFreeDevice(dev); - pciDeviceListFree(list); - return NULL; - } - - pciDeviceSetManaged(dev, hostdev->managed); - } - - return list; -} - -static int -qemuUpdateActivePciHostdevs(struct qemud_driver *driver, - virDomainDefPtr def) -{ - pciDeviceList *pcidevs; - int i; - int ret = -1; - - if (!def->nhostdevs) - return 0; - - if (!(pcidevs = qemuGetPciHostDeviceList(def->hostdevs, def->nhostdevs))) - return -1; - - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - pciDeviceListSteal(pcidevs, dev); - if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) { - pciFreeDevice(dev); - goto cleanup; - } - } - - ret = 0; - -cleanup: - pciDeviceListFree(pcidevs); - return ret; -} - - -static int -qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - pciDeviceList *pcidevs; - int i; - int ret = -1; - - if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) - return -1; - - /* We have to use 3 loops here. *All* devices must - * be detached before we reset any of them, because - * in some cases you have to reset the whole PCI, - * which impacts all devices on it. Also, all devices - * must be reset before being marked as active. - */ - - /* XXX validate that non-managed device isn't in use, eg - * by checking that device is either un-bound, or bound - * to pci-stub.ko - */ - - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - if (!pciDeviceIsAssignable(dev, !driver->relaxedACS)) - goto cleanup; - - if (pciDeviceGetManaged(dev) && - pciDettachDevice(dev, driver->activePciHostdevs) < 0) - goto cleanup; - } - - /* Now that all the PCI hostdevs have be dettached, we can safely - * reset them */ - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) - goto cleanup; - } - - /* Now mark all the devices as active */ - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - pciDeviceListSteal(pcidevs, dev); - if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) { - pciFreeDevice(dev); - goto cleanup; - } - } - - ret = 0; - -cleanup: - pciDeviceListFree(pcidevs); - return ret; -} - -static int -qemuPrepareHostPCIDevices(struct qemud_driver *driver, - virDomainDefPtr def) -{ - return qemuPrepareHostdevPCIDevices(driver, def->hostdevs, def->nhostdevs); -} - - -static int -qemuPrepareHostUSBDevices(struct qemud_driver *driver ATTRIBUTE_UNUSED, - virDomainDefPtr def) -{ - int i; - for (i = 0 ; i < def->nhostdevs ; i++) { - virDomainHostdevDefPtr hostdev = def->hostdevs[i]; - - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) - continue; - if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) - continue; - - /* Resolve a vendor/product to bus/device */ - if (hostdev->source.subsys.u.usb.vendor) { - usbDevice *usb - = usbFindDevice(hostdev->source.subsys.u.usb.vendor, - hostdev->source.subsys.u.usb.product); - - if (!usb) - return -1; - - hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); - hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); - - usbFreeDevice(usb); - } - } - - return 0; -} - -static int -qemuPrepareHostDevices(struct qemud_driver *driver, - virDomainDefPtr def) -{ - if (!def->nhostdevs) - return 0; - - if (qemuPrepareHostPCIDevices(driver, def) < 0) - return -1; - - if (qemuPrepareHostUSBDevices(driver, def) < 0) - return -1; - - return 0; -} - - static int qemuPrepareChardevDevice(virDomainDefPtr def ATTRIBUTE_UNUSED, virDomainChrDefPtr dev, @@ -2966,80 +2778,6 @@ qemuPrepareChardevDevice(virDomainDefPtr def ATTRIBUTE_UNUSED, } -static void -qemudReattachPciDevice(pciDevice *dev, struct qemud_driver *driver) -{ - int retries = 100; - - while (pciWaitForDeviceCleanup(dev, "kvm_assigned_device") - && retries) { - usleep(100*1000); - retries--; - } - - if (pciDeviceGetManaged(dev)) { - if (pciReAttachDevice(dev, driver->activePciHostdevs) < 0) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to re-attach PCI device: %s"), - err ? err->message : _("unknown error")); - virResetError(err); - } - } -} - -static void -qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, - virDomainHostdevDefPtr *hostdevs, - int nhostdevs) -{ - pciDeviceList *pcidevs; - int i; - - if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to allocate pciDeviceList: %s"), - err ? err->message : _("unknown error")); - virResetError(err); - return; - } - - /* Again 3 loops; mark all devices as inactive before reset - * them and reset all the devices before re-attach */ - - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - pciDeviceListDel(driver->activePciHostdevs, dev); - } - - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) { - virErrorPtr err = virGetLastError(); - VIR_ERROR(_("Failed to reset PCI device: %s"), - err ? err->message : _("unknown error")); - virResetError(err); - } - } - - for (i = 0; i < pciDeviceListCount(pcidevs); i++) { - pciDevice *dev = pciDeviceListGet(pcidevs, i); - qemudReattachPciDevice(dev, driver); - } - - pciDeviceListFree(pcidevs); -} - -static void -qemuDomainReAttachHostDevices(struct qemud_driver *driver, - virDomainDefPtr def) -{ - if (!def->nhostdevs) - return; - - qemuDomainReAttachHostdevDevices(driver, def->hostdevs, def->nhostdevs); -} - - struct qemudHookData { virConnectPtr conn; virDomainObjPtr vm; @@ -8767,7 +8505,7 @@ static int qemudDomainDetachHostPciDevice(struct qemud_driver *driver, pciDeviceListDel(driver->activePciHostdevs, pci); if (pciResetDevice(pci, driver->activePciHostdevs, NULL) < 0) ret = -1; - qemudReattachPciDevice(pci, driver); + qemuReattachPciDevice(pci, driver); pciFreeDevice(pci); } diff --git a/src/qemu/qemu_hostdev.c b/src/qemu/qemu_hostdev.c new file mode 100644 index 0000000..f4b2108 --- /dev/null +++ b/src/qemu/qemu_hostdev.c @@ -0,0 +1,289 @@ +/* + * qemu_hostdev.c: QEMU hostdev management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#include <config.h> + +#include "qemu_hostdev.h" +#include "logging.h" +#include "virterror_internal.h" +#include "memory.h" +#include "pci.h" +#include "hostusb.h" + +static pciDeviceList * +qemuGetPciHostDeviceList(virDomainHostdevDefPtr *hostdevs, int nhostdevs) +{ + pciDeviceList *list; + int i; + + if (!(list = pciDeviceListNew())) + return NULL; + + for (i = 0 ; i < nhostdevs ; i++) { + virDomainHostdevDefPtr hostdev = hostdevs[i]; + pciDevice *dev; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + dev = pciGetDevice(hostdev->source.subsys.u.pci.domain, + hostdev->source.subsys.u.pci.bus, + hostdev->source.subsys.u.pci.slot, + hostdev->source.subsys.u.pci.function); + if (!dev) { + pciDeviceListFree(list); + return NULL; + } + + if (pciDeviceListAdd(list, dev) < 0) { + pciFreeDevice(dev); + pciDeviceListFree(list); + return NULL; + } + + pciDeviceSetManaged(dev, hostdev->managed); + } + + return list; +} + + +int qemuUpdateActivePciHostdevs(struct qemud_driver *driver, + virDomainDefPtr def) +{ + pciDeviceList *pcidevs; + int i; + int ret = -1; + + if (!def->nhostdevs) + return 0; + + if (!(pcidevs = qemuGetPciHostDeviceList(def->hostdevs, def->nhostdevs))) + return -1; + + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + pciDeviceListSteal(pcidevs, dev); + if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) { + pciFreeDevice(dev); + goto cleanup; + } + } + + ret = 0; + +cleanup: + pciDeviceListFree(pcidevs); + return ret; +} + + + +int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + pciDeviceList *pcidevs; + int i; + int ret = -1; + + if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) + return -1; + + /* We have to use 3 loops here. *All* devices must + * be detached before we reset any of them, because + * in some cases you have to reset the whole PCI, + * which impacts all devices on it. Also, all devices + * must be reset before being marked as active. + */ + + /* XXX validate that non-managed device isn't in use, eg + * by checking that device is either un-bound, or bound + * to pci-stub.ko + */ + + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (!pciDeviceIsAssignable(dev, !driver->relaxedACS)) + goto cleanup; + + if (pciDeviceGetManaged(dev) && + pciDettachDevice(dev, driver->activePciHostdevs) < 0) + goto cleanup; + } + + /* Now that all the PCI hostdevs have be dettached, we can safely + * reset them */ + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) + goto cleanup; + } + + /* Now mark all the devices as active */ + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + pciDeviceListSteal(pcidevs, dev); + if (pciDeviceListAdd(driver->activePciHostdevs, dev) < 0) { + pciFreeDevice(dev); + goto cleanup; + } + } + + ret = 0; + +cleanup: + pciDeviceListFree(pcidevs); + return ret; +} + +static int +qemuPrepareHostPCIDevices(struct qemud_driver *driver, + virDomainDefPtr def) +{ + return qemuPrepareHostdevPCIDevices(driver, def->hostdevs, def->nhostdevs); +} + + +static int +qemuPrepareHostUSBDevices(struct qemud_driver *driver ATTRIBUTE_UNUSED, + virDomainDefPtr def) +{ + int i; + for (i = 0 ; i < def->nhostdevs ; i++) { + virDomainHostdevDefPtr hostdev = def->hostdevs[i]; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) + continue; + if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + /* Resolve a vendor/product to bus/device */ + if (hostdev->source.subsys.u.usb.vendor) { + usbDevice *usb + = usbFindDevice(hostdev->source.subsys.u.usb.vendor, + hostdev->source.subsys.u.usb.product); + + if (!usb) + return -1; + + hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); + hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); + + usbFreeDevice(usb); + } + } + + return 0; +} + + +int qemuPrepareHostDevices(struct qemud_driver *driver, + virDomainDefPtr def) +{ + if (!def->nhostdevs) + return 0; + + if (qemuPrepareHostPCIDevices(driver, def) < 0) + return -1; + + if (qemuPrepareHostUSBDevices(driver, def) < 0) + return -1; + + return 0; +} + + +void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver) +{ + int retries = 100; + + while (pciWaitForDeviceCleanup(dev, "kvm_assigned_device") + && retries) { + usleep(100*1000); + retries--; + } + + if (pciDeviceGetManaged(dev)) { + if (pciReAttachDevice(dev, driver->activePciHostdevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to re-attach PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + } +} + + +void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs) +{ + pciDeviceList *pcidevs; + int i; + + if (!(pcidevs = qemuGetPciHostDeviceList(hostdevs, nhostdevs))) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to allocate pciDeviceList: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + return; + } + + /* Again 3 loops; mark all devices as inactive before reset + * them and reset all the devices before re-attach */ + + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + pciDeviceListDel(driver->activePciHostdevs, dev); + } + + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + if (pciResetDevice(dev, driver->activePciHostdevs, pcidevs) < 0) { + virErrorPtr err = virGetLastError(); + VIR_ERROR(_("Failed to reset PCI device: %s"), + err ? err->message : _("unknown error")); + virResetError(err); + } + } + + for (i = 0; i < pciDeviceListCount(pcidevs); i++) { + pciDevice *dev = pciDeviceListGet(pcidevs, i); + qemuReattachPciDevice(dev, driver); + } + + pciDeviceListFree(pcidevs); +} + + +void qemuDomainReAttachHostDevices(struct qemud_driver *driver, + virDomainDefPtr def) +{ + if (!def->nhostdevs) + return; + + qemuDomainReAttachHostdevDevices(driver, def->hostdevs, def->nhostdevs); +} diff --git a/src/qemu/qemu_hostdev.h b/src/qemu/qemu_hostdev.h new file mode 100644 index 0000000..96181b5 --- /dev/null +++ b/src/qemu/qemu_hostdev.h @@ -0,0 +1,45 @@ +/* + * qemu_hostdev.h: QEMU hostdev management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __QEMU_HOSTDEV_H__ +# define __QEMU_HOSTDEV_H__ + +#include "qemu_conf.h" +#include "domain_conf.h" + +int qemuUpdateActivePciHostdevs(struct qemud_driver *driver, + virDomainDefPtr def); +int qemuPrepareHostdevPCIDevices(struct qemud_driver *driver, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +int qemuPrepareHostDevices(struct qemud_driver *driver, + virDomainDefPtr def); +void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver); +void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void qemuDomainReAttachHostDevices(struct qemud_driver *driver, + virDomainDefPtr def); + + +#endif /* __QEMU_HOSTDEV_H__ */ -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
The QEMU driver file is far too large. Move all the hostdev helper code out into a separate file. No functional change.
* src/qemu/qemu_hostdev.c, src/qemu/qemu_hostdev.h, src/Makefile.am: Add hostdev helper file * src/qemu/qemu_driver.c: Delete hostdev code --- src/Makefile.am | 1 + src/qemu/qemu_driver.c | 266 +------------------------------------------- src/qemu/qemu_hostdev.c | 289 +++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hostdev.h | 45 ++++++++ 4 files changed, 337 insertions(+), 264 deletions(-) create mode 100644 src/qemu/qemu_hostdev.c create mode 100644 src/qemu/qemu_hostdev.h
ACK, with one nit.
+void qemuReattachPciDevice(pciDevice *dev, struct qemud_driver *driver); +void qemuDomainReAttachHostdevDevices(struct qemud_driver *driver, + virDomainHostdevDefPtr *hostdevs, + int nhostdevs); +void qemuDomainReAttachHostDevices(struct qemud_driver *driver,
Should you use this as an opportunity to s/ReAttach/Reattach/g, for consistency among these three functions? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

To allow their use from other source files, move qemuDriverLock and qemuDriverUnlock to qemu_conf.h and make them non-static * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Add qemuDriverLock qemuDriverUnlock * src/qemu/qemu_driver.c: Remove qemuDriverLock and qemuDriverUnlock --- src/qemu/qemu_conf.c | 10 ++++++++++ src/qemu/qemu_conf.h | 2 ++ src/qemu/qemu_driver.c | 9 --------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index e600a5b..a300a0e 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -61,6 +61,16 @@ #define VIR_FROM_THIS VIR_FROM_QEMU +void qemuDriverLock(struct qemud_driver *driver) +{ + virMutexLock(&driver->lock); +} +void qemuDriverUnlock(struct qemud_driver *driver) +{ + virMutexUnlock(&driver->lock); +} + + int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename) { virConfPtr conf; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 4c61891..83ddedd 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -150,6 +150,8 @@ struct _qemuDomainCmdlineDef { __FUNCTION__, __LINE__, __VA_ARGS__) +void qemuDriverLock(struct qemud_driver *driver); +void qemuDriverUnlock(struct qemud_driver *driver); int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5d8aef3..9f2e536 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -109,15 +109,6 @@ static void processWatchdogEvent(void *data, void *opaque); static int qemudShutdown(void); -static void qemuDriverLock(struct qemud_driver *driver) -{ - virMutexLock(&driver->lock); -} -static void qemuDriverUnlock(struct qemud_driver *driver) -{ - virMutexUnlock(&driver->lock); -} - static void qemuDomainEventFlush(int timer, void *opaque); static void qemuDomainEventQueue(struct qemud_driver *driver, virDomainEventPtr event); -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
To allow their use from other source files, move qemuDriverLock and qemuDriverUnlock to qemu_conf.h and make them non-static
* src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Add qemuDriverLock qemuDriverUnlock * src/qemu/qemu_driver.c: Remove qemuDriverLock and qemuDriverUnlock --- src/qemu/qemu_conf.c | 10 ++++++++++ src/qemu/qemu_conf.h | 2 ++ src/qemu/qemu_driver.c | 9 --------- 3 files changed, 12 insertions(+), 9 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

To allow the APIs to be used from separate files, move the domain lock / job helper code into qemu_domain.c * src/qemu/qemu_domain.c, src/qemu/qemu_domain.h: Add domain lock / job code * src/qemu/qemu_driver.c: Remove domain lock / job code --- src/qemu/qemu_domain.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 16 ++++ src/qemu/qemu_driver.c | 226 ----------------------------------------------- 3 files changed, 246 insertions(+), 226 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 14364ff..27f353f 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -30,12 +30,17 @@ #include "virterror_internal.h" #include "c-ctype.h" +#include <sys/time.h> + #include <libxml/xpathInternals.h> #define VIR_FROM_THIS VIR_FROM_QEMU #define QEMU_NAMESPACE_HREF "http://libvirt.org/schemas/domain/qemu/1.0" +#define timeval_to_ms(tv) (((tv).tv_sec * 1000ull) + ((tv).tv_usec / 1000)) + + static void *qemuDomainObjPrivateAlloc(void) { qemuDomainObjPrivatePtr priv; @@ -372,3 +377,228 @@ void qemuDomainSetNamespaceHooks(virCapsPtr caps) caps->ns.format = qemuDomainDefNamespaceFormatXML; caps->ns.href = qemuDomainDefNamespaceHref; } + +/* + * obj must be locked before calling, qemud_driver must NOT be locked + * + * This must be called by anything that will change the VM state + * in any way, or anything that will use the QEMU monitor. + * + * Upon successful return, the object will have its ref count increased, + * successful calls must be followed by EndJob eventually + */ + +/* Give up waiting for mutex after 30 seconds */ +#define QEMU_JOB_WAIT_TIME (1000ull * 30) + +int qemuDomainObjBeginJob(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + struct timeval now; + unsigned long long then; + + if (gettimeofday(&now, NULL) < 0) { + virReportSystemError(errno, "%s", + _("cannot get time of day")); + return -1; + } + then = timeval_to_ms(now) + QEMU_JOB_WAIT_TIME; + + virDomainObjRef(obj); + + while (priv->jobActive) { + if (virCondWaitUntil(&priv->jobCond, &obj->lock, then) < 0) { + virDomainObjUnref(obj); + if (errno == ETIMEDOUT) + qemuReportError(VIR_ERR_OPERATION_TIMEOUT, + "%s", _("cannot acquire state change lock")); + else + virReportSystemError(errno, + "%s", _("cannot acquire job mutex")); + return -1; + } + } + priv->jobActive = QEMU_JOB_UNSPECIFIED; + priv->jobSignals = 0; + memset(&priv->jobSignalsData, 0, sizeof(priv->jobSignalsData)); + priv->jobStart = timeval_to_ms(now); + memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); + + return 0; +} + +/* + * obj must be locked before calling, qemud_driver must be locked + * + * This must be called by anything that will change the VM state + * in any way, or anything that will use the QEMU monitor. + */ +int qemuDomainObjBeginJobWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + struct timeval now; + unsigned long long then; + + if (gettimeofday(&now, NULL) < 0) { + virReportSystemError(errno, "%s", + _("cannot get time of day")); + return -1; + } + then = timeval_to_ms(now) + QEMU_JOB_WAIT_TIME; + + virDomainObjRef(obj); + qemuDriverUnlock(driver); + + while (priv->jobActive) { + if (virCondWaitUntil(&priv->jobCond, &obj->lock, then) < 0) { + virDomainObjUnref(obj); + if (errno == ETIMEDOUT) + qemuReportError(VIR_ERR_OPERATION_TIMEOUT, + "%s", _("cannot acquire state change lock")); + else + virReportSystemError(errno, + "%s", _("cannot acquire job mutex")); + qemuDriverLock(driver); + return -1; + } + } + priv->jobActive = QEMU_JOB_UNSPECIFIED; + priv->jobSignals = 0; + memset(&priv->jobSignalsData, 0, sizeof(priv->jobSignalsData)); + priv->jobStart = timeval_to_ms(now); + memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); + + virDomainObjUnlock(obj); + qemuDriverLock(driver); + virDomainObjLock(obj); + + return 0; +} + +/* + * obj must be locked before calling, qemud_driver does not matter + * + * To be called after completing the work associated with the + * earlier qemuDomainBeginJob() call + * + * Returns remaining refcount on 'obj', maybe 0 to indicated it + * was deleted + */ +int qemuDomainObjEndJob(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + priv->jobActive = QEMU_JOB_NONE; + priv->jobSignals = 0; + memset(&priv->jobSignalsData, 0, sizeof(priv->jobSignalsData)); + priv->jobStart = 0; + memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); + virCondSignal(&priv->jobCond); + + return virDomainObjUnref(obj); +} + + +/* + * obj must be locked before calling, qemud_driver must be unlocked + * + * To be called immediately before any QEMU monitor API call + * Must have already called qemuDomainObjBeginJob(), and checked + * that the VM is still active. + * + * To be followed with qemuDomainObjExitMonitor() once complete + */ +void qemuDomainObjEnterMonitor(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); + virDomainObjUnlock(obj); +} + + +/* obj must NOT be locked before calling, qemud_driver must be unlocked + * + * Should be paired with an earlier qemuDomainObjEnterMonitor() call + */ +void qemuDomainObjExitMonitor(virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + int refs; + + refs = qemuMonitorUnref(priv->mon); + + if (refs > 0) + qemuMonitorUnlock(priv->mon); + + virDomainObjLock(obj); + + if (refs == 0) { + virDomainObjUnref(obj); + priv->mon = NULL; + } +} + + +/* + * obj must be locked before calling, qemud_driver must be locked + * + * To be called immediately before any QEMU monitor API call + * Must have already called qemuDomainObjBeginJob(). + * + * To be followed with qemuDomainObjExitMonitorWithDriver() once complete + */ +void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + + qemuMonitorLock(priv->mon); + qemuMonitorRef(priv->mon); + virDomainObjUnlock(obj); + qemuDriverUnlock(driver); +} + + +/* obj must NOT be locked before calling, qemud_driver must be unlocked, + * and will be locked after returning + * + * Should be paired with an earlier qemuDomainObjEnterMonitorWithDriver() call + */ +void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj) +{ + qemuDomainObjPrivatePtr priv = obj->privateData; + int refs; + + refs = qemuMonitorUnref(priv->mon); + + if (refs > 0) + qemuMonitorUnlock(priv->mon); + + qemuDriverLock(driver); + virDomainObjLock(obj); + + if (refs == 0) { + virDomainObjUnref(obj); + priv->mon = NULL; + } +} + +void qemuDomainObjEnterRemoteWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj) +{ + virDomainObjRef(obj); + virDomainObjUnlock(obj); + qemuDriverUnlock(driver); +} + +void qemuDomainObjExitRemoteWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj) +{ + qemuDriverLock(driver); + virDomainObjLock(obj); + virDomainObjUnref(obj); +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 0d1e222..870a900 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -27,6 +27,7 @@ # include "threads.h" # include "domain_conf.h" # include "qemu_monitor.h" +# include "qemu_conf.h" /* Only 1 job is allowed at any time * A job includes *all* monitor commands, even those just querying @@ -80,4 +81,19 @@ struct _qemuDomainObjPrivate { void qemuDomainSetPrivateDataHooks(virCapsPtr caps); void qemuDomainSetNamespaceHooks(virCapsPtr caps); +int qemuDomainObjBeginJob(virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; +int qemuDomainObjBeginJobWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; +int qemuDomainObjEndJob(virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; +void qemuDomainObjEnterMonitor(virDomainObjPtr obj); +void qemuDomainObjExitMonitor(virDomainObjPtr obj); +void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj); +void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj); +void qemuDomainObjEnterRemoteWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj); +void qemuDomainObjExitRemoteWithDriver(struct qemud_driver *driver, + virDomainObjPtr obj); + #endif /* __QEMU_DOMAIN_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9f2e536..1c4cced 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -142,232 +142,6 @@ static int qemudVMFiltersInstantiate(virConnectPtr conn, static struct qemud_driver *qemu_driver = NULL; -/* - * obj must be locked before calling, qemud_driver must NOT be locked - * - * This must be called by anything that will change the VM state - * in any way, or anything that will use the QEMU monitor. - * - * Upon successful return, the object will have its ref count increased, - * successful calls must be followed by EndJob eventually - */ - -/* Give up waiting for mutex after 30 seconds */ -#define QEMU_JOB_WAIT_TIME (1000ull * 30) - -static int qemuDomainObjBeginJob(virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; -static int qemuDomainObjBeginJob(virDomainObjPtr obj) -{ - qemuDomainObjPrivatePtr priv = obj->privateData; - struct timeval now; - unsigned long long then; - - if (gettimeofday(&now, NULL) < 0) { - virReportSystemError(errno, "%s", - _("cannot get time of day")); - return -1; - } - then = timeval_to_ms(now) + QEMU_JOB_WAIT_TIME; - - virDomainObjRef(obj); - - while (priv->jobActive) { - if (virCondWaitUntil(&priv->jobCond, &obj->lock, then) < 0) { - virDomainObjUnref(obj); - if (errno == ETIMEDOUT) - qemuReportError(VIR_ERR_OPERATION_TIMEOUT, - "%s", _("cannot acquire state change lock")); - else - virReportSystemError(errno, - "%s", _("cannot acquire job mutex")); - return -1; - } - } - priv->jobActive = QEMU_JOB_UNSPECIFIED; - priv->jobSignals = 0; - memset(&priv->jobSignalsData, 0, sizeof(priv->jobSignalsData)); - priv->jobStart = timeval_to_ms(now); - memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); - - return 0; -} - -/* - * obj must be locked before calling, qemud_driver must be locked - * - * This must be called by anything that will change the VM state - * in any way, or anything that will use the QEMU monitor. - */ -static int qemuDomainObjBeginJobWithDriver(struct qemud_driver *driver, - virDomainObjPtr obj) ATTRIBUTE_RETURN_CHECK; -static int qemuDomainObjBeginJobWithDriver(struct qemud_driver *driver, - virDomainObjPtr obj) -{ - qemuDomainObjPrivatePtr priv = obj->privateData; - struct timeval now; - unsigned long long then; - - if (gettimeofday(&now, NULL) < 0) { - virReportSystemError(errno, "%s", - _("cannot get time of day")); - return -1; - } - then = timeval_to_ms(now) + QEMU_JOB_WAIT_TIME; - - virDomainObjRef(obj); - qemuDriverUnlock(driver); - - while (priv->jobActive) { - if (virCondWaitUntil(&priv->jobCond, &obj->lock, then) < 0) { - virDomainObjUnref(obj); - if (errno == ETIMEDOUT) - qemuReportError(VIR_ERR_OPERATION_TIMEOUT, - "%s", _("cannot acquire state change lock")); - else - virReportSystemError(errno, - "%s", _("cannot acquire job mutex")); - qemuDriverLock(driver); - return -1; - } - } - priv->jobActive = QEMU_JOB_UNSPECIFIED; - priv->jobSignals = 0; - memset(&priv->jobSignalsData, 0, sizeof(priv->jobSignalsData)); - priv->jobStart = timeval_to_ms(now); - memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); - - virDomainObjUnlock(obj); - qemuDriverLock(driver); - virDomainObjLock(obj); - - return 0; -} - -/* - * obj must be locked before calling, qemud_driver does not matter - * - * To be called after completing the work associated with the - * earlier qemuDomainBeginJob() call - * - * Returns remaining refcount on 'obj', maybe 0 to indicated it - * was deleted - */ -static int ATTRIBUTE_RETURN_CHECK qemuDomainObjEndJob(virDomainObjPtr obj) -{ - qemuDomainObjPrivatePtr priv = obj->privateData; - - priv->jobActive = QEMU_JOB_NONE; - priv->jobSignals = 0; - memset(&priv->jobSignalsData, 0, sizeof(priv->jobSignalsData)); - priv->jobStart = 0; - memset(&priv->jobInfo, 0, sizeof(priv->jobInfo)); - virCondSignal(&priv->jobCond); - - return virDomainObjUnref(obj); -} - - -/* - * obj must be locked before calling, qemud_driver must be unlocked - * - * To be called immediately before any QEMU monitor API call - * Must have already called qemuDomainObjBeginJob(), and checked - * that the VM is still active. - * - * To be followed with qemuDomainObjExitMonitor() once complete - */ -static void qemuDomainObjEnterMonitor(virDomainObjPtr obj) -{ - qemuDomainObjPrivatePtr priv = obj->privateData; - - qemuMonitorLock(priv->mon); - qemuMonitorRef(priv->mon); - virDomainObjUnlock(obj); -} - - -/* obj must NOT be locked before calling, qemud_driver must be unlocked - * - * Should be paired with an earlier qemuDomainObjEnterMonitor() call - */ -static void qemuDomainObjExitMonitor(virDomainObjPtr obj) -{ - qemuDomainObjPrivatePtr priv = obj->privateData; - int refs; - - refs = qemuMonitorUnref(priv->mon); - - if (refs > 0) - qemuMonitorUnlock(priv->mon); - - virDomainObjLock(obj); - - if (refs == 0) { - virDomainObjUnref(obj); - priv->mon = NULL; - } -} - - -/* - * obj must be locked before calling, qemud_driver must be locked - * - * To be called immediately before any QEMU monitor API call - * Must have already called qemuDomainObjBeginJob(). - * - * To be followed with qemuDomainObjExitMonitorWithDriver() once complete - */ -static void qemuDomainObjEnterMonitorWithDriver(struct qemud_driver *driver, virDomainObjPtr obj) -{ - qemuDomainObjPrivatePtr priv = obj->privateData; - - qemuMonitorLock(priv->mon); - qemuMonitorRef(priv->mon); - virDomainObjUnlock(obj); - qemuDriverUnlock(driver); -} - - -/* obj must NOT be locked before calling, qemud_driver must be unlocked, - * and will be locked after returning - * - * Should be paired with an earlier qemuDomainObjEnterMonitorWithDriver() call - */ -static void qemuDomainObjExitMonitorWithDriver(struct qemud_driver *driver, virDomainObjPtr obj) -{ - qemuDomainObjPrivatePtr priv = obj->privateData; - int refs; - - refs = qemuMonitorUnref(priv->mon); - - if (refs > 0) - qemuMonitorUnlock(priv->mon); - - qemuDriverLock(driver); - virDomainObjLock(obj); - - if (refs == 0) { - virDomainObjUnref(obj); - priv->mon = NULL; - } -} - -static void qemuDomainObjEnterRemoteWithDriver(struct qemud_driver *driver, - virDomainObjPtr obj) -{ - virDomainObjRef(obj); - virDomainObjUnlock(obj); - qemuDriverUnlock(driver); -} - -static void qemuDomainObjExitRemoteWithDriver(struct qemud_driver *driver, - virDomainObjPtr obj) -{ - qemuDriverLock(driver); - virDomainObjLock(obj); - virDomainObjUnref(obj); -} - static int doStartCPUs(struct qemud_driver *driver, virDomainObjPtr vm, virConnectPtr conn) { int ret; -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
To allow the APIs to be used from separate files, move the domain lock / job helper code into qemu_domain.c
* src/qemu/qemu_domain.c, src/qemu/qemu_domain.h: Add domain lock / job code * src/qemu/qemu_driver.c: Remove domain lock / job code --- src/qemu/qemu_domain.c | 230 ++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 16 ++++ src/qemu/qemu_driver.c | 226 ----------------------------------------------- 3 files changed, 246 insertions(+), 226 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

The QEMU driver file is far too large. Move all the hotplug helper code out into a separate file. No functional change. * src/qemu/qemu_hotplug.c, src/qemu/qemu_hotplug.h, src/Makefile.am: Add hotplug helper file * src/qemu/qemu_driver.c: Delete hotplug code --- src/Makefile.am | 1 + src/qemu/qemu_driver.c | 2019 ++++------------------------------------------ src/qemu/qemu_hotplug.c | 1711 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 103 +++ 4 files changed, 1991 insertions(+), 1843 deletions(-) create mode 100644 src/qemu/qemu_hotplug.c create mode 100644 src/qemu/qemu_hotplug.h diff --git a/src/Makefile.am b/src/Makefile.am index d30774b..64e890f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -273,6 +273,7 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_audit.c qemu/qemu_audit.h \ qemu/qemu_cgroup.c qemu/qemu_cgroup.c \ qemu/qemu_hostdev.c qemu/qemu_hostdev.h \ + qemu/qemu_hotplug.c qemu/qemu_hotplug.h \ qemu/qemu_conf.c qemu/qemu_conf.h \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 1c4cced..b95c405 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -57,6 +57,7 @@ #include "qemu_command.h" #include "qemu_cgroup.h" #include "qemu_hostdev.h" +#include "qemu_hotplug.h" #include "qemu_monitor.h" #include "qemu_bridge_filter.h" #include "qemu_audit.h" @@ -6470,1952 +6471,284 @@ cleanup: } -static int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDiskDefPtr disk, - unsigned long long qemuCmdFlags, - bool force) -{ - virDomainDiskDefPtr origdisk = NULL; - int i; - int ret; - char *driveAlias = NULL; - - origdisk = NULL; - for (i = 0 ; i < vm->def->ndisks ; i++) { - if (vm->def->disks[i]->bus == disk->bus && - STREQ(vm->def->disks[i]->dst, disk->dst)) { - origdisk = vm->def->disks[i]; - break; - } - } - - if (!origdisk) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("No device with bus '%s' and target '%s'"), - virDomainDiskBusTypeToString(disk->bus), - disk->dst); - return -1; - } - - if (!origdisk->info.alias) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("missing disk device alias name for %s"), origdisk->dst); - return -1; - } - - if (origdisk->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY && - origdisk->device != VIR_DOMAIN_DISK_DEVICE_CDROM) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Removable media not supported for %s device"), - virDomainDiskDeviceTypeToString(disk->device)); - return -1; - } - - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityImageLabel && - driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) - return -1; - - if (!(driveAlias = qemuDeviceDriveHostAlias(origdisk, qemuCmdFlags))) - goto error; - - qemuDomainObjPrivatePtr priv = vm->privateData; - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (disk->src) { - const char *format = NULL; - if (disk->type != VIR_DOMAIN_DISK_TYPE_DIR) { - if (disk->driverType) - format = disk->driverType; - else if (origdisk->driverType) - format = origdisk->driverType; - } - ret = qemuMonitorChangeMedia(priv->mon, - driveAlias, - disk->src, format); - } else { - ret = qemuMonitorEjectMedia(priv->mon, driveAlias, force); - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - qemuDomainDiskAudit(vm, origdisk, disk, "update", ret >= 0); - - if (ret < 0) - goto error; - - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, origdisk) < 0) - VIR_WARN("Unable to restore security label on ejected image %s", origdisk->src); - - VIR_FREE(origdisk->src); - origdisk->src = disk->src; - disk->src = NULL; - origdisk->type = disk->type; - - VIR_FREE(driveAlias); - - virDomainDiskDefFree(disk); - - return ret; - -error: - VIR_FREE(driveAlias); - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) - VIR_WARN("Unable to restore security label on new media %s", disk->src); - return -1; -} - - -static int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDiskDefPtr disk, - unsigned long long qemuCmdFlags) -{ - int i, ret; - const char* type = virDomainDiskBusTypeToString(disk->bus); - qemuDomainObjPrivatePtr priv = vm->privateData; - char *devstr = NULL; - char *drivestr = NULL; - - for (i = 0 ; i < vm->def->ndisks ; i++) { - if (STREQ(vm->def->disks[i]->dst, disk->dst)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("target %s already exists"), disk->dst); - return -1; - } - } - - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityImageLabel && - driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) - return -1; - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &disk->info) < 0) - goto error; - if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0) - goto error; - - if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags))) - goto error; - - if (!(devstr = qemuBuildDriveDevStr(disk))) - goto error; - } - - if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { - virReportOOMError(); - goto error; - } - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ret = qemuMonitorAddDrive(priv->mon, drivestr); - if (ret == 0) { - ret = qemuMonitorAddDevice(priv->mon, devstr); - if (ret < 0) { - VIR_WARN("qemuMonitorAddDevice failed on %s (%s)", - drivestr, devstr); - /* XXX should call 'drive_del' on error but this does not - exist yet */ - } - } - } else { - virDomainDevicePCIAddress guestAddr; - ret = qemuMonitorAddPCIDisk(priv->mon, - disk->src, - type, - &guestAddr); - if (ret == 0) { - disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - memcpy(&disk->info.addr.pci, &guestAddr, sizeof(guestAddr)); - } - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0); - - if (ret < 0) - goto error; - - virDomainDiskInsertPreAlloced(vm->def, disk); - - VIR_FREE(devstr); - VIR_FREE(drivestr); - - return 0; - -error: - VIR_FREE(devstr); - VIR_FREE(drivestr); - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && - qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &disk->info) < 0) - VIR_WARN("Unable to release PCI address on %s", disk->src); - - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); - - return -1; -} - - -static int qemudDomainAttachPciControllerDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainControllerDefPtr controller, - unsigned long long qemuCmdFlags) -{ - int i; - int ret = -1; - const char* type = virDomainControllerTypeToString(controller->type); - char *devstr = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; - - for (i = 0 ; i < vm->def->ncontrollers ; i++) { - if ((vm->def->controllers[i]->type == controller->type) && - (vm->def->controllers[i]->idx == controller->idx)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("target %s:%d already exists"), - type, controller->idx); - return -1; - } - } - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &controller->info) < 0) - goto cleanup; - if (qemuAssignDeviceControllerAlias(controller) < 0) - goto cleanup; - - if (!(devstr = qemuBuildControllerDevStr(controller))) { - virReportOOMError(); - goto cleanup; - } - } - - if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers+1) < 0) { - virReportOOMError(); - goto cleanup; - } - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ret = qemuMonitorAddDevice(priv->mon, devstr); - } else { - ret = qemuMonitorAttachPCIDiskController(priv->mon, - type, - &controller->info.addr.pci); - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - if (ret == 0) { - controller->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - virDomainControllerInsertPreAlloced(vm->def, controller); - } - -cleanup: - if ((ret != 0) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - (controller->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && - qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &controller->info) < 0) - VIR_WARN0("Unable to release PCI address on controller"); - - VIR_FREE(devstr); - return ret; -} - - -static virDomainControllerDefPtr -qemuDomainFindOrCreateSCSIDiskController(struct qemud_driver *driver, - virDomainObjPtr vm, - int controller, - unsigned long long qemuCmdFlags) -{ - int i; - virDomainControllerDefPtr cont; - for (i = 0 ; i < vm->def->ncontrollers ; i++) { - cont = vm->def->controllers[i]; - - if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) - continue; - - if (cont->idx == controller) - return cont; - } - - /* No SCSI controller present, for backward compatibility we - * now hotplug a controller */ - if (VIR_ALLOC(cont) < 0) { - virReportOOMError(); - return NULL; - } - cont->type = VIR_DOMAIN_CONTROLLER_TYPE_SCSI; - cont->idx = 0; - cont->model = -1; - - VIR_INFO0("No SCSI controller present, hotplugging one"); - if (qemudDomainAttachPciControllerDevice(driver, - vm, cont, qemuCmdFlags) < 0) { - VIR_FREE(cont); - return NULL; - } - - if (!virDomainObjIsActive(vm)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("guest unexpectedly quit")); - /* cont doesn't need freeing here, since the reference - * now held in def->controllers */ - return NULL; - } - - return cont; -} - - -static int qemudDomainAttachSCSIDisk(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDiskDefPtr disk, - unsigned long long qemuCmdFlags) -{ - int i; - qemuDomainObjPrivatePtr priv = vm->privateData; - virDomainControllerDefPtr cont = NULL; - char *drivestr = NULL; - char *devstr = NULL; - int ret = -1; - - for (i = 0 ; i < vm->def->ndisks ; i++) { - if (STREQ(vm->def->disks[i]->dst, disk->dst)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("target %s already exists"), disk->dst); - return -1; - } - } - - - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityImageLabel && - driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) - return -1; - - /* We should have an address already, so make sure */ - if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected disk address type %s"), - virDomainDeviceAddressTypeToString(disk->info.type)); - goto error; - } - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0) - goto error; - if (!(devstr = qemuBuildDriveDevStr(disk))) - goto error; - } - - if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags))) - goto error; - - for (i = 0 ; i <= disk->info.addr.drive.controller ; i++) { - cont = qemuDomainFindOrCreateSCSIDiskController(driver, vm, i, qemuCmdFlags); - if (!cont) - goto error; - } - - /* Tell clang that "cont" is non-NULL. - This is because disk->info.addr.driver.controller is unsigned, - and hence the above loop must iterate at least once. */ - sa_assert (cont); - - if (cont->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("SCSI controller %d was missing its PCI address"), cont->idx); - goto error; - } - - if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { - virReportOOMError(); - goto error; - } - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ret = qemuMonitorAddDrive(priv->mon, drivestr); - if (ret == 0) { - ret = qemuMonitorAddDevice(priv->mon, devstr); - if (ret < 0) { - VIR_WARN("qemuMonitorAddDevice failed on %s (%s)", - drivestr, devstr); - /* XXX should call 'drive_del' on error but this does not - exist yet */ - } - } - } else { - virDomainDeviceDriveAddress driveAddr; - ret = qemuMonitorAttachDrive(priv->mon, - drivestr, - &cont->info.addr.pci, - &driveAddr); - if (ret == 0) { - /* XXX we should probably validate that the addr matches - * our existing defined addr instead of overwriting */ - disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; - memcpy(&disk->info.addr.drive, &driveAddr, sizeof(driveAddr)); - } - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0); - - if (ret < 0) - goto error; - - virDomainDiskInsertPreAlloced(vm->def, disk); - - VIR_FREE(devstr); - VIR_FREE(drivestr); - - return 0; - -error: - VIR_FREE(devstr); - VIR_FREE(drivestr); - - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); - - return -1; -} - - -static int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDiskDefPtr disk, - unsigned long long qemuCmdFlags) -{ - qemuDomainObjPrivatePtr priv = vm->privateData; - int i, ret; - char *drivestr = NULL; - char *devstr = NULL; - - for (i = 0 ; i < vm->def->ndisks ; i++) { - if (STREQ(vm->def->disks[i]->dst, disk->dst)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("target %s already exists"), disk->dst); - return -1; - } - } - - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityImageLabel && - driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) - return -1; - - if (!disk->src) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("disk source path is missing")); - goto error; - } - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0) - goto error; - if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags))) - goto error; - if (!(devstr = qemuBuildDriveDevStr(disk))) - goto error; - } - - if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { - virReportOOMError(); - goto error; - } - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - ret = qemuMonitorAddDrive(priv->mon, drivestr); - if (ret == 0) { - ret = qemuMonitorAddDevice(priv->mon, devstr); - if (ret < 0) { - VIR_WARN("qemuMonitorAddDevice failed on %s (%s)", - drivestr, devstr); - /* XXX should call 'drive_del' on error but this does not - exist yet */ - } - } - } else { - ret = qemuMonitorAddUSBDisk(priv->mon, disk->src); - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0); - - if (ret < 0) - goto error; - - virDomainDiskInsertPreAlloced(vm->def, disk); - - VIR_FREE(devstr); - VIR_FREE(drivestr); - - return 0; - -error: - VIR_FREE(devstr); - VIR_FREE(drivestr); - - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, disk) < 0) - VIR_WARN("Unable to restore security label on %s", disk->src); - - return -1; -} - - -/* XXX conn required for network -> bridge resolution */ -static int qemudDomainAttachNetDevice(virConnectPtr conn, - struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainNetDefPtr net, - unsigned long long qemuCmdFlags) -{ - qemuDomainObjPrivatePtr priv = vm->privateData; - char *tapfd_name = NULL; - int tapfd = -1; - char *nicstr = NULL; - char *netstr = NULL; - int ret = -1; - virDomainDevicePCIAddress guestAddr; - int vlan; - - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_HOST_NET_ADD)) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("installed qemu version does not support host_net_add")); - return -1; - } - - if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || - net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { - if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("network device type '%s' cannot be attached: " - "qemu is not using a unix socket monitor"), - virDomainNetTypeToString(net->type)); - return -1; - } - - if ((tapfd = qemuNetworkIfaceConnect(conn, driver, net, qemuCmdFlags)) < 0) - return -1; - } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("network device type '%s' cannot be attached: " - "qemu is not using a unix socket monitor"), - virDomainNetTypeToString(net->type)); - return -1; - } - - if ((tapfd = qemuPhysIfaceConnect(conn, driver, net, - qemuCmdFlags, - vm->def->uuid, - VIR_VM_OP_CREATE)) < 0) - return -1; - } - - if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0) - goto no_memory; - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NET_NAME) || - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0) - goto cleanup; - } - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &net->info) < 0) - goto cleanup; - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - vlan = -1; - } else { - vlan = qemuDomainNetVLAN(net); - - if (vlan < 0) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Unable to attach network devices without vlan")); - goto cleanup; - } - } - - if (tapfd != -1) { - if (virAsprintf(&tapfd_name, "fd-%s", net->info.alias) < 0) - goto no_memory; - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorSendFileHandle(priv->mon, tapfd_name, tapfd) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - goto cleanup; - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - if (!virDomainObjIsActive(vm)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("guest unexpectedly quit")); - goto cleanup; - } - } - - /* FIXME - need to support vhost-net here (5th arg) */ - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - if (!(netstr = qemuBuildHostNetStr(net, ',', - -1, tapfd_name, 0))) - goto try_tapfd_close; - } else { - if (!(netstr = qemuBuildHostNetStr(net, ' ', - vlan, tapfd_name, 0))) - goto try_tapfd_close; - } - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - if (qemuMonitorAddNetdev(priv->mon, netstr) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - qemuDomainNetAudit(vm, NULL, net, "attach", false); - goto try_tapfd_close; - } - } else { - if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - qemuDomainNetAudit(vm, NULL, net, "attach", false); - goto try_tapfd_close; - } - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - VIR_FORCE_CLOSE(tapfd); - - if (!virDomainObjIsActive(vm)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("guest unexpectedly quit")); - goto cleanup; - } - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (!(nicstr = qemuBuildNicDevStr(net, vlan))) - goto try_remove; - } else { - if (!(nicstr = qemuBuildNicStr(net, NULL, vlan))) - goto try_remove; - } - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuMonitorAddDevice(priv->mon, nicstr) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - qemuDomainNetAudit(vm, NULL, net, "attach", false); - goto try_remove; - } - } else { - if (qemuMonitorAddPCINetwork(priv->mon, nicstr, - &guestAddr) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - qemuDomainNetAudit(vm, NULL, net, "attach", false); - goto try_remove; - } - net->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - memcpy(&net->info.addr.pci, &guestAddr, sizeof(guestAddr)); - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - qemuDomainNetAudit(vm, NULL, net, "attach", true); - - ret = 0; - - vm->def->nets[vm->def->nnets++] = net; - -cleanup: - if ((ret != 0) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && - qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &net->info) < 0) - VIR_WARN0("Unable to release PCI address on NIC"); - - if (ret != 0) - virDomainConfNWFilterTeardown(net); - - VIR_FREE(nicstr); - VIR_FREE(netstr); - VIR_FREE(tapfd_name); - VIR_FORCE_CLOSE(tapfd); - - return ret; - -try_remove: - if (!virDomainObjIsActive(vm)) - goto cleanup; - - if (vlan < 0) { - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - char *netdev_name; - if (virAsprintf(&netdev_name, "host%s", net->info.alias) < 0) - goto no_memory; - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorRemoveNetdev(priv->mon, netdev_name) < 0) - VIR_WARN("Failed to remove network backend for netdev %s", - netdev_name); - qemuDomainObjExitMonitorWithDriver(driver, vm); - VIR_FREE(netdev_name); - } else { - VIR_WARN0("Unable to remove network backend"); - } - } else { - char *hostnet_name; - if (virAsprintf(&hostnet_name, "host%s", net->info.alias) < 0) - goto no_memory; - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) - VIR_WARN("Failed to remove network backend for vlan %d, net %s", - vlan, hostnet_name); - qemuDomainObjExitMonitorWithDriver(driver, vm); - VIR_FREE(hostnet_name); - } - goto cleanup; - -try_tapfd_close: - if (!virDomainObjIsActive(vm)) - goto cleanup; - - if (tapfd_name) { - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorCloseFileHandle(priv->mon, tapfd_name) < 0) - VIR_WARN("Failed to close tapfd with '%s'", tapfd_name); - qemuDomainObjExitMonitorWithDriver(driver, vm); - } - - goto cleanup; - -no_memory: - virReportOOMError(); - goto cleanup; -} - - -static int qemudDomainAttachHostPciDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainHostdevDefPtr hostdev, - unsigned long long qemuCmdFlags) -{ - qemuDomainObjPrivatePtr priv = vm->privateData; - int ret; - char *devstr = NULL; - int configfd = -1; - char *configfd_name = NULL; - - if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { - virReportOOMError(); - return -1; - } - - if (qemuPrepareHostdevPCIDevices(driver, &hostdev, 1) < 0) - return -1; - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0) - goto error; - if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0) - goto error; - if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) { - configfd = qemuOpenPCIConfig(hostdev); - if (configfd >= 0) { - if (virAsprintf(&configfd_name, "fd-%s", - hostdev->info.alias) < 0) { - virReportOOMError(); - goto error; - } - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorSendFileHandle(priv->mon, configfd_name, - configfd) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - goto error; - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - } - } - - if (!virDomainObjIsActive(vm)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("guest unexpectedly quit during hotplug")); - goto error; - } - - if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name))) - goto error; - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - ret = qemuMonitorAddDevice(priv->mon, devstr); - qemuDomainObjExitMonitorWithDriver(driver, vm); - } else { - virDomainDevicePCIAddress guestAddr; - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - ret = qemuMonitorAddPCIHostDevice(priv->mon, - &hostdev->source.subsys.u.pci, - &guestAddr); - qemuDomainObjExitMonitorWithDriver(driver, vm); - - hostdev->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; - memcpy(&hostdev->info.addr.pci, &guestAddr, sizeof(guestAddr)); - } - if (ret < 0) - goto error; - - vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; - - VIR_FREE(devstr); - VIR_FREE(configfd_name); - VIR_FORCE_CLOSE(configfd); - - return 0; - -error: - if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - (hostdev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && - qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &hostdev->info) < 0) - VIR_WARN0("Unable to release PCI address on host device"); - - qemuDomainReAttachHostdevDevices(driver, &hostdev, 1); - - VIR_FREE(devstr); - VIR_FREE(configfd_name); - VIR_FORCE_CLOSE(configfd); - - return -1; -} - - -static int qemudDomainAttachHostUsbDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainHostdevDefPtr hostdev, - unsigned long long qemuCmdFlags) -{ - int ret; - qemuDomainObjPrivatePtr priv = vm->privateData; - char *devstr = NULL; - - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0) - goto error; - if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev))) - goto error; - } - - if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { - virReportOOMError(); - goto error; - } - - if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { - virCgroupPtr cgroup = NULL; - usbDevice *usb; - - if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=0 ) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to find cgroup for %s\n"), - vm->def->name); - goto error; - } - - if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device)) == NULL) - goto error; - - if (usbDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, cgroup) < 0 ) - goto error; - } - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) - ret = qemuMonitorAddDevice(priv->mon, devstr); - else - ret = qemuMonitorAddUSBDeviceExact(priv->mon, - hostdev->source.subsys.u.usb.bus, - hostdev->source.subsys.u.usb.device); - qemuDomainObjExitMonitorWithDriver(driver, vm); - if (ret < 0) - goto error; - - vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; - - VIR_FREE(devstr); - - return 0; - -error: - VIR_FREE(devstr); - return -1; -} - - -static int qemudDomainAttachHostDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainHostdevDefPtr hostdev, - unsigned long long qemuCmdFlags) -{ - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("hostdev mode '%s' not supported"), - virDomainHostdevModeTypeToString(hostdev->mode)); - return -1; - } - - /* Resolve USB product/vendor to bus/device */ - if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB && - hostdev->source.subsys.u.usb.vendor) { - usbDevice *usb - = usbFindDevice(hostdev->source.subsys.u.usb.vendor, - hostdev->source.subsys.u.usb.product); - - if (!usb) - return -1; - - hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); - hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); - - usbFreeDevice(usb); - } - - - if (driver->securityDriver && - driver->securityDriver->domainSetSecurityHostdevLabel && - driver->securityDriver->domainSetSecurityHostdevLabel(driver->securityDriver, - vm, hostdev) < 0) - return -1; - - switch (hostdev->source.subsys.type) { - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - if (qemudDomainAttachHostPciDevice(driver, vm, - hostdev, qemuCmdFlags) < 0) - goto error; - break; - - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - if (qemudDomainAttachHostUsbDevice(driver, vm, - hostdev, qemuCmdFlags) < 0) - goto error; - break; - - default: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("hostdev subsys type '%s' not supported"), - virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type)); - goto error; - } - - return 0; - -error: - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityHostdevLabel && - driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver, - vm, hostdev) < 0) - VIR_WARN0("Unable to restore host device labelling on hotplug fail"); - - return -1; -} - - -static int qemudDomainAttachDevice(virDomainPtr dom, - const char *xml) -{ - struct qemud_driver *driver = dom->conn->privateData; - virDomainObjPtr vm; - virDomainDeviceDefPtr dev = NULL; - unsigned long long qemuCmdFlags; - virCgroupPtr cgroup = NULL; - int ret = -1; - - qemuDriverLock(driver); - vm = virDomainFindByUUID(&driver->domains, dom->uuid); - if (!vm) { - char uuidstr[VIR_UUID_STRING_BUFLEN]; - virUUIDFormat(dom->uuid, uuidstr); - qemuReportError(VIR_ERR_NO_DOMAIN, - _("no domain with matching uuid '%s'"), uuidstr); - goto cleanup; - } - - if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - qemuReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("cannot attach device on inactive domain")); - goto endjob; - } - - dev = virDomainDeviceDefParse(driver->caps, vm->def, xml, - VIR_DOMAIN_XML_INACTIVE); - if (dev == NULL) - goto endjob; - - if (qemuCapsExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) - goto endjob; - - if (dev->type == VIR_DOMAIN_DEVICE_DISK) { - if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { - if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=0 ) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to find cgroup for %s\n"), - vm->def->name); - goto endjob; - } - if (qemuSetupDiskCgroup(driver, cgroup, dev->data.disk) < 0) - goto endjob; - } - - switch (dev->data.disk->device) { - case VIR_DOMAIN_DISK_DEVICE_CDROM: - case VIR_DOMAIN_DISK_DEVICE_FLOPPY: - ret = qemudDomainChangeEjectableMedia(driver, vm, - dev->data.disk, - qemuCmdFlags, - false); - if (ret == 0) - dev->data.disk = NULL; - break; - - case VIR_DOMAIN_DISK_DEVICE_DISK: - if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) { - ret = qemudDomainAttachUsbMassstorageDevice(driver, vm, - dev->data.disk, qemuCmdFlags); - if (ret == 0) - dev->data.disk = NULL; - } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { - ret = qemudDomainAttachPciDiskDevice(driver, vm, - dev->data.disk, qemuCmdFlags); - if (ret == 0) - dev->data.disk = NULL; - } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) { - ret = qemudDomainAttachSCSIDisk(driver, vm, - dev->data.disk, qemuCmdFlags); - if (ret == 0) - dev->data.disk = NULL; - } else { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk bus '%s' cannot be hotplugged."), - virDomainDiskBusTypeToString(dev->data.disk->bus)); - /* fallthrough */ - } - break; - - default: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk device type '%s' cannot be hotplugged"), - virDomainDiskDeviceTypeToString(dev->data.disk->device)); - /* Fallthrough */ - } - if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", - NULLSTR(dev->data.disk->src)); - } - } else if (dev->type == VIR_DOMAIN_DEVICE_CONTROLLER) { - if (dev->data.controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { - ret = qemudDomainAttachPciControllerDevice(driver, vm, - dev->data.controller, qemuCmdFlags); - if (ret == 0) - dev->data.controller = NULL; - } else { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk controller bus '%s' cannot be hotplugged."), - virDomainControllerTypeToString(dev->data.controller->type)); - /* fallthrough */ - } - } else if (dev->type == VIR_DOMAIN_DEVICE_NET) { - ret = qemudDomainAttachNetDevice(dom->conn, driver, vm, - dev->data.net, qemuCmdFlags); - if (ret == 0) - dev->data.net = NULL; - } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { - ret = qemudDomainAttachHostDevice(driver, vm, - dev->data.hostdev, qemuCmdFlags); - if (ret == 0) - dev->data.hostdev = NULL; - } else { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("device type '%s' cannot be attached"), - virDomainDeviceTypeToString(dev->type)); - goto endjob; - } - - if (!ret && virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) - ret = -1; - -endjob: - if (qemuDomainObjEndJob(vm) == 0) - vm = NULL; - -cleanup: - if (cgroup) - virCgroupFree(&cgroup); - - virDomainDeviceDefFree(dev); - if (vm) - virDomainObjUnlock(vm); - qemuDriverUnlock(driver); - return ret; -} - -static int qemudDomainAttachDeviceFlags(virDomainPtr dom, - const char *xml, - unsigned int flags) { - if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { - qemuReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("cannot modify the persistent configuration of a domain")); - return -1; - } - - return qemudDomainAttachDevice(dom, xml); -} - - -static virDomainGraphicsDefPtr qemuDomainFindGraphics(virDomainObjPtr vm, - virDomainGraphicsDefPtr dev) -{ - int i; - - for (i = 0 ; i < vm->def->ngraphics ; i++) { - if (vm->def->graphics[i]->type == dev->type) - return vm->def->graphics[i]; - } - - return NULL; -} - - -static int -qemuDomainChangeGraphics(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainGraphicsDefPtr dev) -{ - virDomainGraphicsDefPtr olddev = qemuDomainFindGraphics(vm, dev); - qemuDomainObjPrivatePtr priv = vm->privateData; - int ret = -1; - - if (!olddev) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot find existing graphics device to modify")); - return -1; - } - - switch (dev->type) { - case VIR_DOMAIN_GRAPHICS_TYPE_VNC: - if ((olddev->data.vnc.autoport != dev->data.vnc.autoport) || - (!dev->data.vnc.autoport && (olddev->data.vnc.port != dev->data.vnc.port))) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot change port settings on vnc graphics")); - return -1; - } - if (STRNEQ_NULLABLE(olddev->data.vnc.listenAddr, dev->data.vnc.listenAddr)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot change listen address setting on vnc graphics")); - return -1; - } - if (STRNEQ_NULLABLE(olddev->data.vnc.keymap, dev->data.vnc.keymap)) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("cannot change keymap setting on vnc graphics")); - return -1; - } - - if (STRNEQ_NULLABLE(olddev->data.vnc.auth.passwd, dev->data.vnc.auth.passwd)) { - VIR_DEBUG("Updating password on VNC server %p %p", dev->data.vnc.auth.passwd, driver->vncPassword); - qemuDomainObjEnterMonitorWithDriver(driver, vm); - ret = qemuMonitorSetVNCPassword(priv->mon, - dev->data.vnc.auth.passwd ? - dev->data.vnc.auth.passwd : - driver->vncPassword); - qemuDomainObjExitMonitorWithDriver(driver, vm); - - /* Steal the new dev's char * reference */ - VIR_FREE(olddev->data.vnc.auth.passwd); - olddev->data.vnc.auth.passwd = dev->data.vnc.auth.passwd; - dev->data.vnc.auth.passwd = NULL; - } else { - ret = 0; - } - break; - - default: - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("unable to change config on '%s' graphics type"), - virDomainGraphicsTypeToString(dev->type)); - break; - } - - return ret; -} - - -static int qemuDomainUpdateDeviceFlags(virDomainPtr dom, - const char *xml, - unsigned int flags) -{ - struct qemud_driver *driver = dom->conn->privateData; - virDomainObjPtr vm; - virDomainDeviceDefPtr dev = NULL; - unsigned long long qemuCmdFlags; - virCgroupPtr cgroup = NULL; - int ret = -1; - bool force = (flags & VIR_DOMAIN_DEVICE_MODIFY_FORCE) != 0; - - virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT | - VIR_DOMAIN_DEVICE_MODIFY_LIVE | - VIR_DOMAIN_DEVICE_MODIFY_CONFIG | - VIR_DOMAIN_DEVICE_MODIFY_FORCE, -1); - - if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { - qemuReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("cannot modify the persistent configuration of a domain")); - return -1; - } - - qemuDriverLock(driver); - vm = virDomainFindByUUID(&driver->domains, dom->uuid); - if (!vm) { - char uuidstr[VIR_UUID_STRING_BUFLEN]; - virUUIDFormat(dom->uuid, uuidstr); - qemuReportError(VIR_ERR_NO_DOMAIN, - _("no domain with matching uuid '%s'"), uuidstr); - goto cleanup; - } - - if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) - goto cleanup; - - if (!virDomainObjIsActive(vm)) { - qemuReportError(VIR_ERR_OPERATION_INVALID, - "%s", _("cannot attach device on inactive domain")); - goto endjob; - } - - dev = virDomainDeviceDefParse(driver->caps, vm->def, xml, - VIR_DOMAIN_XML_INACTIVE); - if (dev == NULL) - goto endjob; - - if (qemuCapsExtractVersionInfo(vm->def->emulator, - NULL, - &qemuCmdFlags) < 0) - goto endjob; - - switch (dev->type) { - case VIR_DOMAIN_DEVICE_DISK: - if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { - if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=0 ) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to find cgroup for %s\n"), - vm->def->name); - goto endjob; - } - if (qemuSetupDiskCgroup(driver, cgroup, dev->data.disk) < 0) - goto endjob; - } - - switch (dev->data.disk->device) { - case VIR_DOMAIN_DISK_DEVICE_CDROM: - case VIR_DOMAIN_DISK_DEVICE_FLOPPY: - ret = qemudDomainChangeEjectableMedia(driver, vm, - dev->data.disk, - qemuCmdFlags, - force); - if (ret == 0) - dev->data.disk = NULL; - break; - - - default: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk bus '%s' cannot be updated."), - virDomainDiskBusTypeToString(dev->data.disk->bus)); - break; - } - - if (ret != 0 && cgroup) { - if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", - NULLSTR(dev->data.disk->src)); - } - break; - - case VIR_DOMAIN_DEVICE_GRAPHICS: - ret = qemuDomainChangeGraphics(driver, vm, dev->data.graphics); - break; - - default: - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("disk device type '%s' cannot be updated"), - virDomainDiskDeviceTypeToString(dev->data.disk->device)); - break; - } - - if (!ret && virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) - ret = -1; - -endjob: - if (qemuDomainObjEndJob(vm) == 0) - vm = NULL; - -cleanup: - if (cgroup) - virCgroupFree(&cgroup); - - virDomainDeviceDefFree(dev); - if (vm) - virDomainObjUnlock(vm); - qemuDriverUnlock(driver); - return ret; -} - - -static inline int qemudFindDisk(virDomainDefPtr def, const char *dst) -{ - int i; - - for (i = 0 ; i < def->ndisks ; i++) { - if (STREQ(def->disks[i]->dst, dst)) { - return i; - } - } - - return -1; -} - - -static int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - unsigned long long qemuCmdFlags) -{ - int i, ret = -1; - virDomainDiskDefPtr detach = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; - virCgroupPtr cgroup = NULL; - char *drivestr = NULL; - - i = qemudFindDisk(vm->def, dev->data.disk->dst); - - if (i < 0) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("disk %s not found"), dev->data.disk->dst); - goto cleanup; - } - - detach = vm->def->disks[i]; - - if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { - if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to find cgroup for %s\n"), - vm->def->name); - goto cleanup; - } - } - - if (!virDomainDeviceAddressIsValid(&detach->info, - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("device cannot be detached without a PCI address")); - goto cleanup; - } - - /* build the actual drive id string as the disk->info.alias doesn't - * contain the QEMU_DRIVE_HOST_PREFIX that is passed to qemu */ - if (virAsprintf(&drivestr, "%s%s", - QEMU_DRIVE_HOST_PREFIX, detach->info.alias) < 0) { - virReportOOMError(); - goto cleanup; - } - - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { - qemuDomainObjExitMonitor(vm); - goto cleanup; - } - } else { - if (qemuMonitorRemovePCIDevice(priv->mon, - &detach->info.addr.pci) < 0) { - qemuDomainObjExitMonitor(vm); - goto cleanup; - } - } - - /* disconnect guest from host device */ - qemuMonitorDriveDel(priv->mon, drivestr); - - qemuDomainObjExitMonitorWithDriver(driver, vm); - - qemuDomainDiskAudit(vm, detach, NULL, "detach", ret >= 0); - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0) - VIR_WARN("Unable to release PCI address on %s", dev->data.disk->src); - - virDomainDiskRemove(vm->def, i); - - virDomainDiskDefFree(detach); - - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, dev->data.disk) < 0) - VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); - - if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", - NULLSTR(dev->data.disk->src)); - } - - ret = 0; - -cleanup: - VIR_FREE(drivestr); - return ret; -} - -static int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - unsigned long long qemuCmdFlags) +static int qemudDomainAttachDevice(virDomainPtr dom, + const char *xml) { - int i, ret = -1; - virDomainDiskDefPtr detach = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + virDomainDeviceDefPtr dev = NULL; + unsigned long long qemuCmdFlags; virCgroupPtr cgroup = NULL; - char *drivestr = NULL; - - i = qemudFindDisk(vm->def, dev->data.disk->dst); - - if (i < 0) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("disk %s not found"), dev->data.disk->dst); - goto cleanup; - } - - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("Underlying qemu does not support SCSI disk removal")); - goto cleanup; - } - - detach = vm->def->disks[i]; - - if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { - if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to find cgroup for %s\n"), - vm->def->name); - goto cleanup; - } - } - - /* build the actual drive id string as the disk->info.alias doesn't - * contain the QEMU_DRIVE_HOST_PREFIX that is passed to qemu */ - if (virAsprintf(&drivestr, "%s%s", - QEMU_DRIVE_HOST_PREFIX, detach->info.alias) < 0) { - virReportOOMError(); - goto cleanup; - } + int ret = -1; - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { - qemuDomainObjExitMonitor(vm); + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); goto cleanup; } - /* disconnect guest from host device */ - qemuMonitorDriveDel(priv->mon, drivestr); - - qemuDomainObjExitMonitorWithDriver(driver, vm); - - qemuDomainDiskAudit(vm, detach, NULL, "detach", ret >= 0); - - virDomainDiskRemove(vm->def, i); - - virDomainDiskDefFree(detach); - - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityImageLabel && - driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, - vm, dev->data.disk) < 0) - VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); - - if (cgroup != NULL) { - if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) - VIR_WARN("Failed to teardown cgroup for disk path %s", - NULLSTR(dev->data.disk->src)); - } - - ret = 0; - -cleanup: - VIR_FREE(drivestr); - virCgroupFree(&cgroup); - return ret; -} - -static int qemudDomainDetachPciControllerDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - unsigned long long qemuCmdFlags) -{ - int i, ret = -1; - virDomainControllerDefPtr detach = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; - - for (i = 0 ; i < vm->def->ncontrollers ; i++) { - if ((vm->def->controllers[i]->type == dev->data.controller->type) && - (vm->def->controllers[i]->idx == dev->data.controller->idx)) { - detach = vm->def->controllers[i]; - break; - } - } - - if (!detach) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("disk controller %s:%d not found"), - virDomainControllerTypeToString(dev->data.controller->type), - dev->data.controller->idx); + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) goto cleanup; - } - if (!virDomainDeviceAddressIsValid(&detach->info, - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("device cannot be detached without a PCI address")); - goto cleanup; + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot attach device on inactive domain")); + goto endjob; } - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuAssignDeviceControllerAlias(detach) < 0) - goto cleanup; - } + dev = virDomainDeviceDefParse(driver->caps, vm->def, xml, + VIR_DOMAIN_XML_INACTIVE); + if (dev == NULL) + goto endjob; - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuMonitorDelDevice(priv->mon, detach->info.alias)) { - qemuDomainObjExitMonitor(vm); - goto cleanup; - } - } else { - if (qemuMonitorRemovePCIDevice(priv->mon, - &detach->info.addr.pci) < 0) { - qemuDomainObjExitMonitor(vm); - goto cleanup; - } - } - qemuDomainObjExitMonitorWithDriver(driver, vm); + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) + goto endjob; - if (vm->def->ncontrollers > 1) { - memmove(vm->def->controllers + i, - vm->def->controllers + i + 1, - sizeof(*vm->def->controllers) * - (vm->def->ncontrollers - (i + 1))); - vm->def->ncontrollers--; - if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers) < 0) { - /* ignore, harmless */ + if (dev->type == VIR_DOMAIN_DEVICE_DISK) { + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=0 ) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find cgroup for %s\n"), + vm->def->name); + goto endjob; + } + if (qemuSetupDiskCgroup(driver, cgroup, dev->data.disk) < 0) + goto endjob; } - } else { - VIR_FREE(vm->def->controllers); - vm->def->ncontrollers = 0; - } - if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0) - VIR_WARN0("Unable to release PCI address on controller"); - - virDomainControllerDefFree(detach); - - ret = 0; - -cleanup: - return ret; -} - -static int -qemudDomainDetachNetDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - unsigned long long qemuCmdFlags) -{ - int i, ret = -1; - virDomainNetDefPtr detach = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; - int vlan; - char *hostnet_name = NULL; - - for (i = 0 ; i < vm->def->nnets ; i++) { - virDomainNetDefPtr net = vm->def->nets[i]; - - if (!memcmp(net->mac, dev->data.net->mac, sizeof(net->mac))) { - detach = net; + switch (dev->data.disk->device) { + case VIR_DOMAIN_DISK_DEVICE_CDROM: + case VIR_DOMAIN_DISK_DEVICE_FLOPPY: + ret = qemudDomainChangeEjectableMedia(driver, vm, + dev->data.disk, + qemuCmdFlags, + false); + if (ret == 0) + dev->data.disk = NULL; break; - } - } - - if (!detach) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("network device %02x:%02x:%02x:%02x:%02x:%02x not found"), - dev->data.net->mac[0], dev->data.net->mac[1], - dev->data.net->mac[2], dev->data.net->mac[3], - dev->data.net->mac[4], dev->data.net->mac[5]); - goto cleanup; - } - - if (!virDomainDeviceAddressIsValid(&detach->info, - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a PCI address")); - goto cleanup; - } - - if ((vlan = qemuDomainNetVLAN(detach)) < 0) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("unable to determine original VLAN")); - goto cleanup; - } - if (virAsprintf(&hostnet_name, "host%s", detach->info.alias) < 0) { - virReportOOMError(); - goto cleanup; - } + case VIR_DOMAIN_DISK_DEVICE_DISK: + if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) { + ret = qemudDomainAttachUsbMassstorageDevice(driver, vm, + dev->data.disk, qemuCmdFlags); + if (ret == 0) + dev->data.disk = NULL; + } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) { + ret = qemudDomainAttachPciDiskDevice(driver, vm, + dev->data.disk, qemuCmdFlags); + if (ret == 0) + dev->data.disk = NULL; + } else if (dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) { + ret = qemudDomainAttachSCSIDisk(driver, vm, + dev->data.disk, qemuCmdFlags); + if (ret == 0) + dev->data.disk = NULL; + } else { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk bus '%s' cannot be hotplugged."), + virDomainDiskBusTypeToString(dev->data.disk->bus)); + /* fallthrough */ + } + break; - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { - qemuDomainObjExitMonitor(vm); - qemuDomainNetAudit(vm, detach, NULL, "detach", false); - goto cleanup; + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk device type '%s' cannot be hotplugged"), + virDomainDiskDeviceTypeToString(dev->data.disk->device)); + /* Fallthrough */ } - } else { - if (qemuMonitorRemovePCIDevice(priv->mon, - &detach->info.addr.pci) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - qemuDomainNetAudit(vm, detach, NULL, "detach", false); - goto cleanup; + if (ret != 0 && cgroup) { + if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) + VIR_WARN("Failed to teardown cgroup for disk path %s", + NULLSTR(dev->data.disk->src)); } - } - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && - (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - qemuDomainNetAudit(vm, detach, NULL, "detach", false); - goto cleanup; + } else if (dev->type == VIR_DOMAIN_DEVICE_CONTROLLER) { + if (dev->data.controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { + ret = qemudDomainAttachPciControllerDevice(driver, vm, + dev->data.controller, qemuCmdFlags); + if (ret == 0) + dev->data.controller = NULL; + } else { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk controller bus '%s' cannot be hotplugged."), + virDomainControllerTypeToString(dev->data.controller->type)); + /* fallthrough */ } + } else if (dev->type == VIR_DOMAIN_DEVICE_NET) { + ret = qemudDomainAttachNetDevice(dom->conn, driver, vm, + dev->data.net, qemuCmdFlags); + if (ret == 0) + dev->data.net = NULL; + } else if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) { + ret = qemudDomainAttachHostDevice(driver, vm, + dev->data.hostdev, qemuCmdFlags); + if (ret == 0) + dev->data.hostdev = NULL; } else { - if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - qemuDomainNetAudit(vm, detach, NULL, "detach", false); - goto cleanup; - } - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - qemuDomainNetAudit(vm, detach, NULL, "detach", true); - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0) - VIR_WARN0("Unable to release PCI address on NIC"); - - virDomainConfNWFilterTeardown(detach); - -#if WITH_MACVTAP - if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) { - delMacvtap(detach->ifname, detach->mac, detach->data.direct.linkdev, - &detach->data.direct.virtPortProfile); - VIR_FREE(detach->ifname); - } -#endif - - if ((driver->macFilter) && (detach->ifname != NULL)) { - if ((errno = networkDisallowMacOnPort(driver, - detach->ifname, - detach->mac))) { - virReportSystemError(errno, - _("failed to remove ebtables rule on '%s'"), - detach->ifname); - } + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("device type '%s' cannot be attached"), + virDomainDeviceTypeToString(dev->type)); + goto endjob; } - if (vm->def->nnets > 1) { - memmove(vm->def->nets + i, - vm->def->nets + i + 1, - sizeof(*vm->def->nets) * - (vm->def->nnets - (i + 1))); - vm->def->nnets--; - if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets) < 0) { - /* ignore, harmless */ - } - } else { - VIR_FREE(vm->def->nets); - vm->def->nnets = 0; - } - virDomainNetDefFree(detach); + if (!ret && virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) + ret = -1; - ret = 0; +endjob: + if (qemuDomainObjEndJob(vm) == 0) + vm = NULL; cleanup: - VIR_FREE(hostnet_name); + if (cgroup) + virCgroupFree(&cgroup); + + virDomainDeviceDefFree(dev); + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); return ret; } -static int qemudDomainDetachHostPciDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - unsigned long long qemuCmdFlags) -{ - virDomainHostdevDefPtr detach = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; - int i, ret; - pciDevice *pci; - - for (i = 0 ; i < vm->def->nhostdevs ; i++) { - if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || - vm->def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) - continue; - - unsigned domain = vm->def->hostdevs[i]->source.subsys.u.pci.domain; - unsigned bus = vm->def->hostdevs[i]->source.subsys.u.pci.bus; - unsigned slot = vm->def->hostdevs[i]->source.subsys.u.pci.slot; - unsigned function = vm->def->hostdevs[i]->source.subsys.u.pci.function; - - if (dev->data.hostdev->source.subsys.u.pci.domain == domain && - dev->data.hostdev->source.subsys.u.pci.bus == bus && - dev->data.hostdev->source.subsys.u.pci.slot == slot && - dev->data.hostdev->source.subsys.u.pci.function == function) { - detach = vm->def->hostdevs[i]; - break; - } - } - - if (!detach) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("host pci device %.4x:%.2x:%.2x.%.1x not found"), - dev->data.hostdev->source.subsys.u.pci.domain, - dev->data.hostdev->source.subsys.u.pci.bus, - dev->data.hostdev->source.subsys.u.pci.slot, - dev->data.hostdev->source.subsys.u.pci.function); - return -1; - } - - if (!virDomainDeviceAddressIsValid(&detach->info, - VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a PCI address")); +static int qemudDomainAttachDeviceFlags(virDomainPtr dom, + const char *xml, + unsigned int flags) { + if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot modify the persistent configuration of a domain")); return -1; } - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { - if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { - qemuDomainObjExitMonitor(vm); - return -1; - } - } else { - if (qemuMonitorRemovePCIDevice(priv->mon, - &detach->info.addr.pci) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - return -1; - } - } - qemuDomainObjExitMonitorWithDriver(driver, vm); - - ret = 0; - - pci = pciGetDevice(detach->source.subsys.u.pci.domain, - detach->source.subsys.u.pci.bus, - detach->source.subsys.u.pci.slot, - detach->source.subsys.u.pci.function); - if (!pci) - ret = -1; - else { - pciDeviceSetManaged(pci, detach->managed); - pciDeviceListDel(driver->activePciHostdevs, pci); - if (pciResetDevice(pci, driver->activePciHostdevs, NULL) < 0) - ret = -1; - qemuReattachPciDevice(pci, driver); - pciFreeDevice(pci); - } - - if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && - qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0) - VIR_WARN0("Unable to release PCI address on host device"); - - if (vm->def->nhostdevs > 1) { - memmove(vm->def->hostdevs + i, - vm->def->hostdevs + i + 1, - sizeof(*vm->def->hostdevs) * - (vm->def->nhostdevs - (i + 1))); - vm->def->nhostdevs--; - if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) { - /* ignore, harmless */ - } - } else { - VIR_FREE(vm->def->hostdevs); - vm->def->nhostdevs = 0; - } - virDomainHostdevDefFree(detach); - - return ret; + return qemudDomainAttachDevice(dom, xml); } -static int qemudDomainDetachHostUsbDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - unsigned long long qemuCmdFlags) -{ - virDomainHostdevDefPtr detach = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; - int i, ret; - - for (i = 0 ; i < vm->def->nhostdevs ; i++) { - if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || - vm->def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) - continue; - unsigned bus = vm->def->hostdevs[i]->source.subsys.u.usb.bus; - unsigned device = vm->def->hostdevs[i]->source.subsys.u.usb.device; - unsigned product = vm->def->hostdevs[i]->source.subsys.u.usb.product; - unsigned vendor = vm->def->hostdevs[i]->source.subsys.u.usb.vendor; +static int qemuDomainUpdateDeviceFlags(virDomainPtr dom, + const char *xml, + unsigned int flags) +{ + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + virDomainDeviceDefPtr dev = NULL; + unsigned long long qemuCmdFlags; + virCgroupPtr cgroup = NULL; + int ret = -1; + bool force = (flags & VIR_DOMAIN_DEVICE_MODIFY_FORCE) != 0; - if (dev->data.hostdev->source.subsys.u.usb.bus && - dev->data.hostdev->source.subsys.u.usb.device) { - if (dev->data.hostdev->source.subsys.u.usb.bus == bus && - dev->data.hostdev->source.subsys.u.usb.device == device) { - detach = vm->def->hostdevs[i]; - break; - } - } else { - if (dev->data.hostdev->source.subsys.u.usb.product == product && - dev->data.hostdev->source.subsys.u.usb.vendor == vendor) { - detach = vm->def->hostdevs[i]; - break; - } - } - } + virCheckFlags(VIR_DOMAIN_DEVICE_MODIFY_CURRENT | + VIR_DOMAIN_DEVICE_MODIFY_LIVE | + VIR_DOMAIN_DEVICE_MODIFY_CONFIG | + VIR_DOMAIN_DEVICE_MODIFY_FORCE, -1); - if (!detach) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - _("host usb device %03d.%03d not found"), - dev->data.hostdev->source.subsys.u.usb.bus, - dev->data.hostdev->source.subsys.u.usb.device); + if (flags & VIR_DOMAIN_DEVICE_MODIFY_CONFIG) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot modify the persistent configuration of a domain")); return -1; } - if (!detach->info.alias) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached without a device alias")); - return -1; + qemuDriverLock(driver); + vm = virDomainFindByUUID(&driver->domains, dom->uuid); + if (!vm) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(dom->uuid, uuidstr); + qemuReportError(VIR_ERR_NO_DOMAIN, + _("no domain with matching uuid '%s'"), uuidstr); + goto cleanup; } - if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { - qemuReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device cannot be detached with this QEMU version")); - return -1; - } + if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0) + goto cleanup; - qemuDomainObjEnterMonitorWithDriver(driver, vm); - if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { - qemuDomainObjExitMonitorWithDriver(driver, vm); - return -1; + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("cannot attach device on inactive domain")); + goto endjob; } - qemuDomainObjExitMonitorWithDriver(driver, vm); - ret = 0; + dev = virDomainDeviceDefParse(driver->caps, vm->def, xml, + VIR_DOMAIN_XML_INACTIVE); + if (dev == NULL) + goto endjob; + + if (qemuCapsExtractVersionInfo(vm->def->emulator, + NULL, + &qemuCmdFlags) < 0) + goto endjob; - if (vm->def->nhostdevs > 1) { - memmove(vm->def->hostdevs + i, - vm->def->hostdevs + i + 1, - sizeof(*vm->def->hostdevs) * - (vm->def->nhostdevs - (i + 1))); - vm->def->nhostdevs--; - if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) { - /* ignore, harmless */ + switch (dev->type) { + case VIR_DOMAIN_DEVICE_DISK: + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=0 ) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find cgroup for %s\n"), + vm->def->name); + goto endjob; + } + if (qemuSetupDiskCgroup(driver, cgroup, dev->data.disk) < 0) + goto endjob; } - } else { - VIR_FREE(vm->def->hostdevs); - vm->def->nhostdevs = 0; - } - virDomainHostdevDefFree(detach); - return ret; -} + switch (dev->data.disk->device) { + case VIR_DOMAIN_DISK_DEVICE_CDROM: + case VIR_DOMAIN_DISK_DEVICE_FLOPPY: + ret = qemudDomainChangeEjectableMedia(driver, vm, + dev->data.disk, + qemuCmdFlags, + force); + if (ret == 0) + dev->data.disk = NULL; + break; -static int qemudDomainDetachHostDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - unsigned long long qemuCmdFlags) -{ - virDomainHostdevDefPtr hostdev = dev->data.hostdev; - int ret; - if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("hostdev mode '%s' not supported"), - virDomainHostdevModeTypeToString(hostdev->mode)); - return -1; - } + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("disk bus '%s' cannot be updated."), + virDomainDiskBusTypeToString(dev->data.disk->bus)); + break; + } - switch (hostdev->source.subsys.type) { - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: - ret = qemudDomainDetachHostPciDevice(driver, vm, dev, qemuCmdFlags); + if (ret != 0 && cgroup) { + if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) + VIR_WARN("Failed to teardown cgroup for disk path %s", + NULLSTR(dev->data.disk->src)); + } break; - case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: - ret = qemudDomainDetachHostUsbDevice(driver, vm, dev, qemuCmdFlags); + + case VIR_DOMAIN_DEVICE_GRAPHICS: + ret = qemuDomainChangeGraphics(driver, vm, dev->data.graphics); break; + default: qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("hostdev subsys type '%s' not supported"), - virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type)); - return -1; + _("disk device type '%s' cannot be updated"), + virDomainDiskDeviceTypeToString(dev->data.disk->device)); + break; } - if (driver->securityDriver && - driver->securityDriver->domainRestoreSecurityHostdevLabel && - driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver, - vm, dev->data.hostdev) < 0) - VIR_WARN0("Failed to restore host device labelling"); + if (!ret && virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) + ret = -1; + +endjob: + if (qemuDomainObjEndJob(vm) == 0) + vm = NULL; + +cleanup: + if (cgroup) + virCgroupFree(&cgroup); + virDomainDeviceDefFree(dev); + if (vm) + virDomainObjUnlock(vm); + qemuDriverUnlock(driver); return ret; } + static int qemudDomainDetachDevice(virDomainPtr dom, const char *xml) { struct qemud_driver *driver = dom->conn->privateData; diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c new file mode 100644 index 0000000..0b60d27 --- /dev/null +++ b/src/qemu/qemu_hotplug.c @@ -0,0 +1,1711 @@ +/* + * qemu_hotplug.h: QEMU device hotplug management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + + +#include <config.h> + +#include "qemu_hotplug.h" +#include "qemu_capabilities.h" +#include "qemu_domain.h" +#include "qemu_command.h" +#include "qemu_bridge_filter.h" +#include "qemu_audit.h" +#include "qemu_hostdev.h" +#include "domain_nwfilter.h" +#include "logging.h" +#include "virterror_internal.h" +#include "memory.h" +#include "pci.h" +#include "files.h" +#include "qemu_cgroup.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags, + bool force) +{ + virDomainDiskDefPtr origdisk = NULL; + int i; + int ret; + char *driveAlias = NULL; + + origdisk = NULL; + for (i = 0 ; i < vm->def->ndisks ; i++) { + if (vm->def->disks[i]->bus == disk->bus && + STREQ(vm->def->disks[i]->dst, disk->dst)) { + origdisk = vm->def->disks[i]; + break; + } + } + + if (!origdisk) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("No device with bus '%s' and target '%s'"), + virDomainDiskBusTypeToString(disk->bus), + disk->dst); + return -1; + } + + if (!origdisk->info.alias) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("missing disk device alias name for %s"), origdisk->dst); + return -1; + } + + if (origdisk->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY && + origdisk->device != VIR_DOMAIN_DISK_DEVICE_CDROM) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Removable media not supported for %s device"), + virDomainDiskDeviceTypeToString(disk->device)); + return -1; + } + + if (driver->securityDriver && + driver->securityDriver->domainSetSecurityImageLabel && + driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, + vm, disk) < 0) + return -1; + + if (!(driveAlias = qemuDeviceDriveHostAlias(origdisk, qemuCmdFlags))) + goto error; + + qemuDomainObjPrivatePtr priv = vm->privateData; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (disk->src) { + const char *format = NULL; + if (disk->type != VIR_DOMAIN_DISK_TYPE_DIR) { + if (disk->driverType) + format = disk->driverType; + else if (origdisk->driverType) + format = origdisk->driverType; + } + ret = qemuMonitorChangeMedia(priv->mon, + driveAlias, + disk->src, format); + } else { + ret = qemuMonitorEjectMedia(priv->mon, driveAlias, force); + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + qemuDomainDiskAudit(vm, origdisk, disk, "update", ret >= 0); + + if (ret < 0) + goto error; + + if (driver->securityDriver && + driver->securityDriver->domainRestoreSecurityImageLabel && + driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, + vm, origdisk) < 0) + VIR_WARN("Unable to restore security label on ejected image %s", origdisk->src); + + VIR_FREE(origdisk->src); + origdisk->src = disk->src; + disk->src = NULL; + origdisk->type = disk->type; + + VIR_FREE(driveAlias); + + virDomainDiskDefFree(disk); + + return ret; + +error: + VIR_FREE(driveAlias); + if (driver->securityDriver && + driver->securityDriver->domainRestoreSecurityImageLabel && + driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, + vm, disk) < 0) + VIR_WARN("Unable to restore security label on new media %s", disk->src); + return -1; +} + + +int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags) +{ + int i, ret; + const char* type = virDomainDiskBusTypeToString(disk->bus); + qemuDomainObjPrivatePtr priv = vm->privateData; + char *devstr = NULL; + char *drivestr = NULL; + + for (i = 0 ; i < vm->def->ndisks ; i++) { + if (STREQ(vm->def->disks[i]->dst, disk->dst)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("target %s already exists"), disk->dst); + return -1; + } + } + + if (driver->securityDriver && + driver->securityDriver->domainSetSecurityImageLabel && + driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, + vm, disk) < 0) + return -1; + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &disk->info) < 0) + goto error; + if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0) + goto error; + + if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags))) + goto error; + + if (!(devstr = qemuBuildDriveDevStr(disk))) + goto error; + } + + if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { + virReportOOMError(); + goto error; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + ret = qemuMonitorAddDrive(priv->mon, drivestr); + if (ret == 0) { + ret = qemuMonitorAddDevice(priv->mon, devstr); + if (ret < 0) { + VIR_WARN("qemuMonitorAddDevice failed on %s (%s)", + drivestr, devstr); + /* XXX should call 'drive_del' on error but this does not + exist yet */ + } + } + } else { + virDomainDevicePCIAddress guestAddr; + ret = qemuMonitorAddPCIDisk(priv->mon, + disk->src, + type, + &guestAddr); + if (ret == 0) { + disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + memcpy(&disk->info.addr.pci, &guestAddr, sizeof(guestAddr)); + } + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0); + + if (ret < 0) + goto error; + + virDomainDiskInsertPreAlloced(vm->def, disk); + + VIR_FREE(devstr); + VIR_FREE(drivestr); + + return 0; + +error: + VIR_FREE(devstr); + VIR_FREE(drivestr); + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && + qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &disk->info) < 0) + VIR_WARN("Unable to release PCI address on %s", disk->src); + + if (driver->securityDriver && + driver->securityDriver->domainRestoreSecurityImageLabel && + driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, + vm, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + + return -1; +} + + +int qemudDomainAttachPciControllerDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainControllerDefPtr controller, + unsigned long long qemuCmdFlags) +{ + int i; + int ret = -1; + const char* type = virDomainControllerTypeToString(controller->type); + char *devstr = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + + for (i = 0 ; i < vm->def->ncontrollers ; i++) { + if ((vm->def->controllers[i]->type == controller->type) && + (vm->def->controllers[i]->idx == controller->idx)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("target %s:%d already exists"), + type, controller->idx); + return -1; + } + } + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &controller->info) < 0) + goto cleanup; + if (qemuAssignDeviceControllerAlias(controller) < 0) + goto cleanup; + + if (!(devstr = qemuBuildControllerDevStr(controller))) { + virReportOOMError(); + goto cleanup; + } + } + + if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers+1) < 0) { + virReportOOMError(); + goto cleanup; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + ret = qemuMonitorAddDevice(priv->mon, devstr); + } else { + ret = qemuMonitorAttachPCIDiskController(priv->mon, + type, + &controller->info.addr.pci); + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + if (ret == 0) { + controller->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + virDomainControllerInsertPreAlloced(vm->def, controller); + } + +cleanup: + if ((ret != 0) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + (controller->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && + qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &controller->info) < 0) + VIR_WARN0("Unable to release PCI address on controller"); + + VIR_FREE(devstr); + return ret; +} + + +static virDomainControllerDefPtr +qemuDomainFindOrCreateSCSIDiskController(struct qemud_driver *driver, + virDomainObjPtr vm, + int controller, + unsigned long long qemuCmdFlags) +{ + int i; + virDomainControllerDefPtr cont; + for (i = 0 ; i < vm->def->ncontrollers ; i++) { + cont = vm->def->controllers[i]; + + if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI) + continue; + + if (cont->idx == controller) + return cont; + } + + /* No SCSI controller present, for backward compatibility we + * now hotplug a controller */ + if (VIR_ALLOC(cont) < 0) { + virReportOOMError(); + return NULL; + } + cont->type = VIR_DOMAIN_CONTROLLER_TYPE_SCSI; + cont->idx = 0; + cont->model = -1; + + VIR_INFO0("No SCSI controller present, hotplugging one"); + if (qemudDomainAttachPciControllerDevice(driver, + vm, cont, qemuCmdFlags) < 0) { + VIR_FREE(cont); + return NULL; + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest unexpectedly quit")); + /* cont doesn't need freeing here, since the reference + * now held in def->controllers */ + return NULL; + } + + return cont; +} + + +int qemudDomainAttachSCSIDisk(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags) +{ + int i; + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainControllerDefPtr cont = NULL; + char *drivestr = NULL; + char *devstr = NULL; + int ret = -1; + + for (i = 0 ; i < vm->def->ndisks ; i++) { + if (STREQ(vm->def->disks[i]->dst, disk->dst)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("target %s already exists"), disk->dst); + return -1; + } + } + + + if (driver->securityDriver && + driver->securityDriver->domainSetSecurityImageLabel && + driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, + vm, disk) < 0) + return -1; + + /* We should have an address already, so make sure */ + if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unexpected disk address type %s"), + virDomainDeviceAddressTypeToString(disk->info.type)); + goto error; + } + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0) + goto error; + if (!(devstr = qemuBuildDriveDevStr(disk))) + goto error; + } + + if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags))) + goto error; + + for (i = 0 ; i <= disk->info.addr.drive.controller ; i++) { + cont = qemuDomainFindOrCreateSCSIDiskController(driver, vm, i, qemuCmdFlags); + if (!cont) + goto error; + } + + /* Tell clang that "cont" is non-NULL. + This is because disk->info.addr.driver.controller is unsigned, + and hence the above loop must iterate at least once. */ + sa_assert (cont); + + if (cont->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("SCSI controller %d was missing its PCI address"), cont->idx); + goto error; + } + + if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { + virReportOOMError(); + goto error; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + ret = qemuMonitorAddDrive(priv->mon, drivestr); + if (ret == 0) { + ret = qemuMonitorAddDevice(priv->mon, devstr); + if (ret < 0) { + VIR_WARN("qemuMonitorAddDevice failed on %s (%s)", + drivestr, devstr); + /* XXX should call 'drive_del' on error but this does not + exist yet */ + } + } + } else { + virDomainDeviceDriveAddress driveAddr; + ret = qemuMonitorAttachDrive(priv->mon, + drivestr, + &cont->info.addr.pci, + &driveAddr); + if (ret == 0) { + /* XXX we should probably validate that the addr matches + * our existing defined addr instead of overwriting */ + disk->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; + memcpy(&disk->info.addr.drive, &driveAddr, sizeof(driveAddr)); + } + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0); + + if (ret < 0) + goto error; + + virDomainDiskInsertPreAlloced(vm->def, disk); + + VIR_FREE(devstr); + VIR_FREE(drivestr); + + return 0; + +error: + VIR_FREE(devstr); + VIR_FREE(drivestr); + + if (driver->securityDriver && + driver->securityDriver->domainRestoreSecurityImageLabel && + driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, + vm, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + + return -1; +} + + +int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + int i, ret; + char *drivestr = NULL; + char *devstr = NULL; + + for (i = 0 ; i < vm->def->ndisks ; i++) { + if (STREQ(vm->def->disks[i]->dst, disk->dst)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("target %s already exists"), disk->dst); + return -1; + } + } + + if (driver->securityDriver && + driver->securityDriver->domainSetSecurityImageLabel && + driver->securityDriver->domainSetSecurityImageLabel(driver->securityDriver, + vm, disk) < 0) + return -1; + + if (!disk->src) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("disk source path is missing")); + goto error; + } + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0) + goto error; + if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags))) + goto error; + if (!(devstr = qemuBuildDriveDevStr(disk))) + goto error; + } + + if (VIR_REALLOC_N(vm->def->disks, vm->def->ndisks+1) < 0) { + virReportOOMError(); + goto error; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + ret = qemuMonitorAddDrive(priv->mon, drivestr); + if (ret == 0) { + ret = qemuMonitorAddDevice(priv->mon, devstr); + if (ret < 0) { + VIR_WARN("qemuMonitorAddDevice failed on %s (%s)", + drivestr, devstr); + /* XXX should call 'drive_del' on error but this does not + exist yet */ + } + } + } else { + ret = qemuMonitorAddUSBDisk(priv->mon, disk->src); + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + qemuDomainDiskAudit(vm, NULL, disk, "attach", ret >= 0); + + if (ret < 0) + goto error; + + virDomainDiskInsertPreAlloced(vm->def, disk); + + VIR_FREE(devstr); + VIR_FREE(drivestr); + + return 0; + +error: + VIR_FREE(devstr); + VIR_FREE(drivestr); + + if (driver->securityDriver && + driver->securityDriver->domainRestoreSecurityImageLabel && + driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, + vm, disk) < 0) + VIR_WARN("Unable to restore security label on %s", disk->src); + + return -1; +} + + +/* XXX conn required for network -> bridge resolution */ +int qemudDomainAttachNetDevice(virConnectPtr conn, + struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainNetDefPtr net, + unsigned long long qemuCmdFlags) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + char *tapfd_name = NULL; + int tapfd = -1; + char *nicstr = NULL; + char *netstr = NULL; + int ret = -1; + virDomainDevicePCIAddress guestAddr; + int vlan; + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_HOST_NET_ADD)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("installed qemu version does not support host_net_add")); + return -1; + } + + if (net->type == VIR_DOMAIN_NET_TYPE_BRIDGE || + net->type == VIR_DOMAIN_NET_TYPE_NETWORK) { + if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("network device type '%s' cannot be attached: " + "qemu is not using a unix socket monitor"), + virDomainNetTypeToString(net->type)); + return -1; + } + + if ((tapfd = qemuNetworkIfaceConnect(conn, driver, net, qemuCmdFlags)) < 0) + return -1; + } else if (net->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + if (priv->monConfig->type != VIR_DOMAIN_CHR_TYPE_UNIX) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("network device type '%s' cannot be attached: " + "qemu is not using a unix socket monitor"), + virDomainNetTypeToString(net->type)); + return -1; + } + + if ((tapfd = qemuPhysIfaceConnect(conn, driver, net, + qemuCmdFlags, + vm->def->uuid, + VIR_VM_OP_CREATE)) < 0) + return -1; + } + + if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets+1) < 0) + goto no_memory; + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NET_NAME) || + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + if (qemuAssignDeviceNetAlias(vm->def, net, -1) < 0) + goto cleanup; + } + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &net->info) < 0) + goto cleanup; + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + vlan = -1; + } else { + vlan = qemuDomainNetVLAN(net); + + if (vlan < 0) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Unable to attach network devices without vlan")); + goto cleanup; + } + } + + if (tapfd != -1) { + if (virAsprintf(&tapfd_name, "fd-%s", net->info.alias) < 0) + goto no_memory; + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorSendFileHandle(priv->mon, tapfd_name, tapfd) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto cleanup; + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest unexpectedly quit")); + goto cleanup; + } + } + + /* FIXME - need to support vhost-net here (5th arg) */ + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + if (!(netstr = qemuBuildHostNetStr(net, ',', + -1, tapfd_name, 0))) + goto try_tapfd_close; + } else { + if (!(netstr = qemuBuildHostNetStr(net, ' ', + vlan, tapfd_name, 0))) + goto try_tapfd_close; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + if (qemuMonitorAddNetdev(priv->mon, netstr) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + qemuDomainNetAudit(vm, NULL, net, "attach", false); + goto try_tapfd_close; + } + } else { + if (qemuMonitorAddHostNetwork(priv->mon, netstr) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + qemuDomainNetAudit(vm, NULL, net, "attach", false); + goto try_tapfd_close; + } + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + VIR_FORCE_CLOSE(tapfd); + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest unexpectedly quit")); + goto cleanup; + } + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (!(nicstr = qemuBuildNicDevStr(net, vlan))) + goto try_remove; + } else { + if (!(nicstr = qemuBuildNicStr(net, NULL, vlan))) + goto try_remove; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuMonitorAddDevice(priv->mon, nicstr) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + qemuDomainNetAudit(vm, NULL, net, "attach", false); + goto try_remove; + } + } else { + if (qemuMonitorAddPCINetwork(priv->mon, nicstr, + &guestAddr) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + qemuDomainNetAudit(vm, NULL, net, "attach", false); + goto try_remove; + } + net->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + memcpy(&net->info.addr.pci, &guestAddr, sizeof(guestAddr)); + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + qemuDomainNetAudit(vm, NULL, net, "attach", true); + + ret = 0; + + vm->def->nets[vm->def->nnets++] = net; + +cleanup: + if ((ret != 0) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && + qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &net->info) < 0) + VIR_WARN0("Unable to release PCI address on NIC"); + + if (ret != 0) + virDomainConfNWFilterTeardown(net); + + VIR_FREE(nicstr); + VIR_FREE(netstr); + VIR_FREE(tapfd_name); + VIR_FORCE_CLOSE(tapfd); + + return ret; + +try_remove: + if (!virDomainObjIsActive(vm)) + goto cleanup; + + if (vlan < 0) { + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + char *netdev_name; + if (virAsprintf(&netdev_name, "host%s", net->info.alias) < 0) + goto no_memory; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorRemoveNetdev(priv->mon, netdev_name) < 0) + VIR_WARN("Failed to remove network backend for netdev %s", + netdev_name); + qemuDomainObjExitMonitorWithDriver(driver, vm); + VIR_FREE(netdev_name); + } else { + VIR_WARN0("Unable to remove network backend"); + } + } else { + char *hostnet_name; + if (virAsprintf(&hostnet_name, "host%s", net->info.alias) < 0) + goto no_memory; + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) + VIR_WARN("Failed to remove network backend for vlan %d, net %s", + vlan, hostnet_name); + qemuDomainObjExitMonitorWithDriver(driver, vm); + VIR_FREE(hostnet_name); + } + goto cleanup; + +try_tapfd_close: + if (!virDomainObjIsActive(vm)) + goto cleanup; + + if (tapfd_name) { + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorCloseFileHandle(priv->mon, tapfd_name) < 0) + VIR_WARN("Failed to close tapfd with '%s'", tapfd_name); + qemuDomainObjExitMonitorWithDriver(driver, vm); + } + + goto cleanup; + +no_memory: + virReportOOMError(); + goto cleanup; +} + + +int qemudDomainAttachHostPciDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev, + unsigned long long qemuCmdFlags) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + int ret; + char *devstr = NULL; + int configfd = -1; + char *configfd_name = NULL; + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { + virReportOOMError(); + return -1; + } + + if (qemuPrepareHostdevPCIDevices(driver, &hostdev, 1) < 0) + return -1; + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0) + goto error; + if (qemuDomainPCIAddressEnsureAddr(priv->pciaddrs, &hostdev->info) < 0) + goto error; + if (qemuCmdFlags & QEMUD_CMD_FLAG_PCI_CONFIGFD) { + configfd = qemuOpenPCIConfig(hostdev); + if (configfd >= 0) { + if (virAsprintf(&configfd_name, "fd-%s", + hostdev->info.alias) < 0) { + virReportOOMError(); + goto error; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorSendFileHandle(priv->mon, configfd_name, + configfd) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + goto error; + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + } + } + + if (!virDomainObjIsActive(vm)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("guest unexpectedly quit during hotplug")); + goto error; + } + + if (!(devstr = qemuBuildPCIHostdevDevStr(hostdev, configfd_name))) + goto error; + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorAddDevice(priv->mon, devstr); + qemuDomainObjExitMonitorWithDriver(driver, vm); + } else { + virDomainDevicePCIAddress guestAddr; + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorAddPCIHostDevice(priv->mon, + &hostdev->source.subsys.u.pci, + &guestAddr); + qemuDomainObjExitMonitorWithDriver(driver, vm); + + hostdev->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; + memcpy(&hostdev->info.addr.pci, &guestAddr, sizeof(guestAddr)); + } + if (ret < 0) + goto error; + + vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; + + VIR_FREE(devstr); + VIR_FREE(configfd_name); + VIR_FORCE_CLOSE(configfd); + + return 0; + +error: + if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + (hostdev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) && + qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &hostdev->info) < 0) + VIR_WARN0("Unable to release PCI address on host device"); + + qemuDomainReAttachHostdevDevices(driver, &hostdev, 1); + + VIR_FREE(devstr); + VIR_FREE(configfd_name); + VIR_FORCE_CLOSE(configfd); + + return -1; +} + + +int qemudDomainAttachHostUsbDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev, + unsigned long long qemuCmdFlags) +{ + int ret; + qemuDomainObjPrivatePtr priv = vm->privateData; + char *devstr = NULL; + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuAssignDeviceHostdevAlias(vm->def, hostdev, -1) < 0) + goto error; + if (!(devstr = qemuBuildUSBHostdevDevStr(hostdev))) + goto error; + } + + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs+1) < 0) { + virReportOOMError(); + goto error; + } + + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + virCgroupPtr cgroup = NULL; + usbDevice *usb; + + if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) !=0 ) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find cgroup for %s\n"), + vm->def->name); + goto error; + } + + if ((usb = usbGetDevice(hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device)) == NULL) + goto error; + + if (usbDeviceFileIterate(usb, qemuSetupHostUsbDeviceCgroup, cgroup) < 0 ) + goto error; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) + ret = qemuMonitorAddDevice(priv->mon, devstr); + else + ret = qemuMonitorAddUSBDeviceExact(priv->mon, + hostdev->source.subsys.u.usb.bus, + hostdev->source.subsys.u.usb.device); + qemuDomainObjExitMonitorWithDriver(driver, vm); + if (ret < 0) + goto error; + + vm->def->hostdevs[vm->def->nhostdevs++] = hostdev; + + VIR_FREE(devstr); + + return 0; + +error: + VIR_FREE(devstr); + return -1; +} + + +int qemudDomainAttachHostDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev, + unsigned long long qemuCmdFlags) +{ + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev mode '%s' not supported"), + virDomainHostdevModeTypeToString(hostdev->mode)); + return -1; + } + + /* Resolve USB product/vendor to bus/device */ + if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB && + hostdev->source.subsys.u.usb.vendor) { + usbDevice *usb + = usbFindDevice(hostdev->source.subsys.u.usb.vendor, + hostdev->source.subsys.u.usb.product); + + if (!usb) + return -1; + + hostdev->source.subsys.u.usb.bus = usbDeviceGetBus(usb); + hostdev->source.subsys.u.usb.device = usbDeviceGetDevno(usb); + + usbFreeDevice(usb); + } + + + if (driver->securityDriver && + driver->securityDriver->domainSetSecurityHostdevLabel && + driver->securityDriver->domainSetSecurityHostdevLabel(driver->securityDriver, + vm, hostdev) < 0) + return -1; + + switch (hostdev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: + if (qemudDomainAttachHostPciDevice(driver, vm, + hostdev, qemuCmdFlags) < 0) + goto error; + break; + + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + if (qemudDomainAttachHostUsbDevice(driver, vm, + hostdev, qemuCmdFlags) < 0) + goto error; + break; + + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev subsys type '%s' not supported"), + virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type)); + goto error; + } + + return 0; + +error: + if (driver->securityDriver && + driver->securityDriver->domainRestoreSecurityHostdevLabel && + driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver, + vm, hostdev) < 0) + VIR_WARN0("Unable to restore host device labelling on hotplug fail"); + + return -1; +} + + +static virDomainGraphicsDefPtr qemuDomainFindGraphics(virDomainObjPtr vm, + virDomainGraphicsDefPtr dev) +{ + int i; + + for (i = 0 ; i < vm->def->ngraphics ; i++) { + if (vm->def->graphics[i]->type == dev->type) + return vm->def->graphics[i]; + } + + return NULL; +} + + +int +qemuDomainChangeGraphics(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainGraphicsDefPtr dev) +{ + virDomainGraphicsDefPtr olddev = qemuDomainFindGraphics(vm, dev); + qemuDomainObjPrivatePtr priv = vm->privateData; + int ret = -1; + + if (!olddev) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot find existing graphics device to modify")); + return -1; + } + + switch (dev->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + if ((olddev->data.vnc.autoport != dev->data.vnc.autoport) || + (!dev->data.vnc.autoport && (olddev->data.vnc.port != dev->data.vnc.port))) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot change port settings on vnc graphics")); + return -1; + } + if (STRNEQ_NULLABLE(olddev->data.vnc.listenAddr, dev->data.vnc.listenAddr)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot change listen address setting on vnc graphics")); + return -1; + } + if (STRNEQ_NULLABLE(olddev->data.vnc.keymap, dev->data.vnc.keymap)) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("cannot change keymap setting on vnc graphics")); + return -1; + } + + if (STRNEQ_NULLABLE(olddev->data.vnc.auth.passwd, dev->data.vnc.auth.passwd)) { + VIR_DEBUG("Updating password on VNC server %p %p", dev->data.vnc.auth.passwd, driver->vncPassword); + qemuDomainObjEnterMonitorWithDriver(driver, vm); + ret = qemuMonitorSetVNCPassword(priv->mon, + dev->data.vnc.auth.passwd ? + dev->data.vnc.auth.passwd : + driver->vncPassword); + qemuDomainObjExitMonitorWithDriver(driver, vm); + + /* Steal the new dev's char * reference */ + VIR_FREE(olddev->data.vnc.auth.passwd); + olddev->data.vnc.auth.passwd = dev->data.vnc.auth.passwd; + dev->data.vnc.auth.passwd = NULL; + } else { + ret = 0; + } + break; + + default: + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to change config on '%s' graphics type"), + virDomainGraphicsTypeToString(dev->type)); + break; + } + + return ret; +} + + +static inline int qemudFindDisk(virDomainDefPtr def, const char *dst) +{ + int i; + + for (i = 0 ; i < def->ndisks ; i++) { + if (STREQ(def->disks[i]->dst, dst)) { + return i; + } + } + + return -1; +} + + +int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) +{ + int i, ret = -1; + virDomainDiskDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + virCgroupPtr cgroup = NULL; + char *drivestr = NULL; + + i = qemudFindDisk(vm->def, dev->data.disk->dst); + + if (i < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("disk %s not found"), dev->data.disk->dst); + goto cleanup; + } + + detach = vm->def->disks[i]; + + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find cgroup for %s\n"), + vm->def->name); + goto cleanup; + } + } + + if (!virDomainDeviceAddressIsValid(&detach->info, + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("device cannot be detached without a PCI address")); + goto cleanup; + } + + /* build the actual drive id string as the disk->info.alias doesn't + * contain the QEMU_DRIVE_HOST_PREFIX that is passed to qemu */ + if (virAsprintf(&drivestr, "%s%s", + QEMU_DRIVE_HOST_PREFIX, detach->info.alias) < 0) { + virReportOOMError(); + goto cleanup; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { + qemuDomainObjExitMonitor(vm); + goto cleanup; + } + } else { + if (qemuMonitorRemovePCIDevice(priv->mon, + &detach->info.addr.pci) < 0) { + qemuDomainObjExitMonitor(vm); + goto cleanup; + } + } + + /* disconnect guest from host device */ + qemuMonitorDriveDel(priv->mon, drivestr); + + qemuDomainObjExitMonitorWithDriver(driver, vm); + + qemuDomainDiskAudit(vm, detach, NULL, "detach", ret >= 0); + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0) + VIR_WARN("Unable to release PCI address on %s", dev->data.disk->src); + + virDomainDiskRemove(vm->def, i); + + virDomainDiskDefFree(detach); + + if (driver->securityDriver && + driver->securityDriver->domainRestoreSecurityImageLabel && + driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, + vm, dev->data.disk) < 0) + VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); + + if (cgroup != NULL) { + if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) + VIR_WARN("Failed to teardown cgroup for disk path %s", + NULLSTR(dev->data.disk->src)); + } + + ret = 0; + +cleanup: + VIR_FREE(drivestr); + return ret; +} + +int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) +{ + int i, ret = -1; + virDomainDiskDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + virCgroupPtr cgroup = NULL; + char *drivestr = NULL; + + i = qemudFindDisk(vm->def, dev->data.disk->dst); + + if (i < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("disk %s not found"), dev->data.disk->dst); + goto cleanup; + } + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("Underlying qemu does not support SCSI disk removal")); + goto cleanup; + } + + detach = vm->def->disks[i]; + + if (qemuCgroupControllerActive(driver, VIR_CGROUP_CONTROLLER_DEVICES)) { + if (virCgroupForDomain(driver->cgroup, vm->def->name, &cgroup, 0) != 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Unable to find cgroup for %s\n"), + vm->def->name); + goto cleanup; + } + } + + /* build the actual drive id string as the disk->info.alias doesn't + * contain the QEMU_DRIVE_HOST_PREFIX that is passed to qemu */ + if (virAsprintf(&drivestr, "%s%s", + QEMU_DRIVE_HOST_PREFIX, detach->info.alias) < 0) { + virReportOOMError(); + goto cleanup; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { + qemuDomainObjExitMonitor(vm); + goto cleanup; + } + + /* disconnect guest from host device */ + qemuMonitorDriveDel(priv->mon, drivestr); + + qemuDomainObjExitMonitorWithDriver(driver, vm); + + qemuDomainDiskAudit(vm, detach, NULL, "detach", ret >= 0); + + virDomainDiskRemove(vm->def, i); + + virDomainDiskDefFree(detach); + + if (driver->securityDriver && + driver->securityDriver->domainRestoreSecurityImageLabel && + driver->securityDriver->domainRestoreSecurityImageLabel(driver->securityDriver, + vm, dev->data.disk) < 0) + VIR_WARN("Unable to restore security label on %s", dev->data.disk->src); + + if (cgroup != NULL) { + if (qemuTeardownDiskCgroup(driver, cgroup, dev->data.disk) < 0) + VIR_WARN("Failed to teardown cgroup for disk path %s", + NULLSTR(dev->data.disk->src)); + } + + ret = 0; + +cleanup: + VIR_FREE(drivestr); + virCgroupFree(&cgroup); + return ret; +} + +int qemudDomainDetachPciControllerDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) +{ + int i, ret = -1; + virDomainControllerDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + + for (i = 0 ; i < vm->def->ncontrollers ; i++) { + if ((vm->def->controllers[i]->type == dev->data.controller->type) && + (vm->def->controllers[i]->idx == dev->data.controller->idx)) { + detach = vm->def->controllers[i]; + break; + } + } + + if (!detach) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("disk controller %s:%d not found"), + virDomainControllerTypeToString(dev->data.controller->type), + dev->data.controller->idx); + goto cleanup; + } + + if (!virDomainDeviceAddressIsValid(&detach->info, + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, "%s", + _("device cannot be detached without a PCI address")); + goto cleanup; + } + + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuAssignDeviceControllerAlias(detach) < 0) + goto cleanup; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuMonitorDelDevice(priv->mon, detach->info.alias)) { + qemuDomainObjExitMonitor(vm); + goto cleanup; + } + } else { + if (qemuMonitorRemovePCIDevice(priv->mon, + &detach->info.addr.pci) < 0) { + qemuDomainObjExitMonitor(vm); + goto cleanup; + } + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + if (vm->def->ncontrollers > 1) { + memmove(vm->def->controllers + i, + vm->def->controllers + i + 1, + sizeof(*vm->def->controllers) * + (vm->def->ncontrollers - (i + 1))); + vm->def->ncontrollers--; + if (VIR_REALLOC_N(vm->def->controllers, vm->def->ncontrollers) < 0) { + /* ignore, harmless */ + } + } else { + VIR_FREE(vm->def->controllers); + vm->def->ncontrollers = 0; + } + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0) + VIR_WARN0("Unable to release PCI address on controller"); + + virDomainControllerDefFree(detach); + + ret = 0; + +cleanup: + return ret; +} + +int +qemudDomainDetachNetDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) +{ + int i, ret = -1; + virDomainNetDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + int vlan; + char *hostnet_name = NULL; + + for (i = 0 ; i < vm->def->nnets ; i++) { + virDomainNetDefPtr net = vm->def->nets[i]; + + if (!memcmp(net->mac, dev->data.net->mac, sizeof(net->mac))) { + detach = net; + break; + } + } + + if (!detach) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("network device %02x:%02x:%02x:%02x:%02x:%02x not found"), + dev->data.net->mac[0], dev->data.net->mac[1], + dev->data.net->mac[2], dev->data.net->mac[3], + dev->data.net->mac[4], dev->data.net->mac[5]); + goto cleanup; + } + + if (!virDomainDeviceAddressIsValid(&detach->info, + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached without a PCI address")); + goto cleanup; + } + + if ((vlan = qemuDomainNetVLAN(detach)) < 0) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("unable to determine original VLAN")); + goto cleanup; + } + + if (virAsprintf(&hostnet_name, "host%s", detach->info.alias) < 0) { + virReportOOMError(); + goto cleanup; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { + qemuDomainObjExitMonitor(vm); + qemuDomainNetAudit(vm, detach, NULL, "detach", false); + goto cleanup; + } + } else { + if (qemuMonitorRemovePCIDevice(priv->mon, + &detach->info.addr.pci) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + qemuDomainNetAudit(vm, detach, NULL, "detach", false); + goto cleanup; + } + } + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_NETDEV) && + (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + if (qemuMonitorRemoveNetdev(priv->mon, hostnet_name) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + qemuDomainNetAudit(vm, detach, NULL, "detach", false); + goto cleanup; + } + } else { + if (qemuMonitorRemoveHostNetwork(priv->mon, vlan, hostnet_name) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + qemuDomainNetAudit(vm, detach, NULL, "detach", false); + goto cleanup; + } + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + qemuDomainNetAudit(vm, detach, NULL, "detach", true); + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0) + VIR_WARN0("Unable to release PCI address on NIC"); + + virDomainConfNWFilterTeardown(detach); + +#if WITH_MACVTAP + if (detach->type == VIR_DOMAIN_NET_TYPE_DIRECT) { + delMacvtap(detach->ifname, detach->mac, detach->data.direct.linkdev, + &detach->data.direct.virtPortProfile); + VIR_FREE(detach->ifname); + } +#endif + + if ((driver->macFilter) && (detach->ifname != NULL)) { + if ((errno = networkDisallowMacOnPort(driver, + detach->ifname, + detach->mac))) { + virReportSystemError(errno, + _("failed to remove ebtables rule on '%s'"), + detach->ifname); + } + } + + if (vm->def->nnets > 1) { + memmove(vm->def->nets + i, + vm->def->nets + i + 1, + sizeof(*vm->def->nets) * + (vm->def->nnets - (i + 1))); + vm->def->nnets--; + if (VIR_REALLOC_N(vm->def->nets, vm->def->nnets) < 0) { + /* ignore, harmless */ + } + } else { + VIR_FREE(vm->def->nets); + vm->def->nnets = 0; + } + virDomainNetDefFree(detach); + + ret = 0; + +cleanup: + VIR_FREE(hostnet_name); + return ret; +} + +int qemudDomainDetachHostPciDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) +{ + virDomainHostdevDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + int i, ret; + pciDevice *pci; + + for (i = 0 ; i < vm->def->nhostdevs ; i++) { + if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + vm->def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) + continue; + + unsigned domain = vm->def->hostdevs[i]->source.subsys.u.pci.domain; + unsigned bus = vm->def->hostdevs[i]->source.subsys.u.pci.bus; + unsigned slot = vm->def->hostdevs[i]->source.subsys.u.pci.slot; + unsigned function = vm->def->hostdevs[i]->source.subsys.u.pci.function; + + if (dev->data.hostdev->source.subsys.u.pci.domain == domain && + dev->data.hostdev->source.subsys.u.pci.bus == bus && + dev->data.hostdev->source.subsys.u.pci.slot == slot && + dev->data.hostdev->source.subsys.u.pci.function == function) { + detach = vm->def->hostdevs[i]; + break; + } + } + + if (!detach) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("host pci device %.4x:%.2x:%.2x.%.1x not found"), + dev->data.hostdev->source.subsys.u.pci.domain, + dev->data.hostdev->source.subsys.u.pci.bus, + dev->data.hostdev->source.subsys.u.pci.slot, + dev->data.hostdev->source.subsys.u.pci.function); + return -1; + } + + if (!virDomainDeviceAddressIsValid(&detach->info, + VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached without a PCI address")); + return -1; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { + qemuDomainObjExitMonitor(vm); + return -1; + } + } else { + if (qemuMonitorRemovePCIDevice(priv->mon, + &detach->info.addr.pci) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + return -1; + } + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + ret = 0; + + pci = pciGetDevice(detach->source.subsys.u.pci.domain, + detach->source.subsys.u.pci.bus, + detach->source.subsys.u.pci.slot, + detach->source.subsys.u.pci.function); + if (!pci) + ret = -1; + else { + pciDeviceSetManaged(pci, detach->managed); + pciDeviceListDel(driver->activePciHostdevs, pci); + if (pciResetDevice(pci, driver->activePciHostdevs, NULL) < 0) + ret = -1; + qemuReattachPciDevice(pci, driver); + pciFreeDevice(pci); + } + + if ((qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && + qemuDomainPCIAddressReleaseAddr(priv->pciaddrs, &detach->info) < 0) + VIR_WARN0("Unable to release PCI address on host device"); + + if (vm->def->nhostdevs > 1) { + memmove(vm->def->hostdevs + i, + vm->def->hostdevs + i + 1, + sizeof(*vm->def->hostdevs) * + (vm->def->nhostdevs - (i + 1))); + vm->def->nhostdevs--; + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) { + /* ignore, harmless */ + } + } else { + VIR_FREE(vm->def->hostdevs); + vm->def->nhostdevs = 0; + } + virDomainHostdevDefFree(detach); + + return ret; +} + +int qemudDomainDetachHostUsbDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) +{ + virDomainHostdevDefPtr detach = NULL; + qemuDomainObjPrivatePtr priv = vm->privateData; + int i, ret; + + for (i = 0 ; i < vm->def->nhostdevs ; i++) { + if (vm->def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + vm->def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) + continue; + + unsigned bus = vm->def->hostdevs[i]->source.subsys.u.usb.bus; + unsigned device = vm->def->hostdevs[i]->source.subsys.u.usb.device; + unsigned product = vm->def->hostdevs[i]->source.subsys.u.usb.product; + unsigned vendor = vm->def->hostdevs[i]->source.subsys.u.usb.vendor; + + if (dev->data.hostdev->source.subsys.u.usb.bus && + dev->data.hostdev->source.subsys.u.usb.device) { + if (dev->data.hostdev->source.subsys.u.usb.bus == bus && + dev->data.hostdev->source.subsys.u.usb.device == device) { + detach = vm->def->hostdevs[i]; + break; + } + } else { + if (dev->data.hostdev->source.subsys.u.usb.product == product && + dev->data.hostdev->source.subsys.u.usb.vendor == vendor) { + detach = vm->def->hostdevs[i]; + break; + } + } + } + + if (!detach) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + _("host usb device %03d.%03d not found"), + dev->data.hostdev->source.subsys.u.usb.bus, + dev->data.hostdev->source.subsys.u.usb.device); + return -1; + } + + if (!detach->info.alias) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached without a device alias")); + return -1; + } + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE)) { + qemuReportError(VIR_ERR_OPERATION_FAILED, + "%s", _("device cannot be detached with this QEMU version")); + return -1; + } + + qemuDomainObjEnterMonitorWithDriver(driver, vm); + if (qemuMonitorDelDevice(priv->mon, detach->info.alias) < 0) { + qemuDomainObjExitMonitorWithDriver(driver, vm); + return -1; + } + qemuDomainObjExitMonitorWithDriver(driver, vm); + + ret = 0; + + if (vm->def->nhostdevs > 1) { + memmove(vm->def->hostdevs + i, + vm->def->hostdevs + i + 1, + sizeof(*vm->def->hostdevs) * + (vm->def->nhostdevs - (i + 1))); + vm->def->nhostdevs--; + if (VIR_REALLOC_N(vm->def->hostdevs, vm->def->nhostdevs) < 0) { + /* ignore, harmless */ + } + } else { + VIR_FREE(vm->def->hostdevs); + vm->def->nhostdevs = 0; + } + virDomainHostdevDefFree(detach); + + return ret; +} + +int qemudDomainDetachHostDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags) +{ + virDomainHostdevDefPtr hostdev = dev->data.hostdev; + int ret; + + if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev mode '%s' not supported"), + virDomainHostdevModeTypeToString(hostdev->mode)); + return -1; + } + + switch (hostdev->source.subsys.type) { + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: + ret = qemudDomainDetachHostPciDevice(driver, vm, dev, qemuCmdFlags); + break; + case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: + ret = qemudDomainDetachHostUsbDevice(driver, vm, dev, qemuCmdFlags); + break; + default: + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("hostdev subsys type '%s' not supported"), + virDomainHostdevSubsysTypeToString(hostdev->source.subsys.type)); + return -1; + } + + if (driver->securityDriver && + driver->securityDriver->domainRestoreSecurityHostdevLabel && + driver->securityDriver->domainRestoreSecurityHostdevLabel(driver->securityDriver, + vm, dev->data.hostdev) < 0) + VIR_WARN0("Failed to restore host device labelling"); + + return ret; +} + diff --git a/src/qemu/qemu_hotplug.h b/src/qemu/qemu_hotplug.h new file mode 100644 index 0000000..552f509 --- /dev/null +++ b/src/qemu/qemu_hotplug.h @@ -0,0 +1,103 @@ +/* + * qemu_hotplug.h: QEMU device hotplug management + * + * Copyright (C) 2006-2007, 2009-2010 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@redhat.com> + */ + +#ifndef __QEMU_HOTPLUG_H__ +# define __QEMU_HOTPLUG_H__ + +# include <stdbool.h> + +# include "qemu_conf.h" +# include "domain_conf.h" + +int qemudDomainChangeEjectableMedia(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags, + bool force); +int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags); +int qemudDomainAttachPciControllerDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainControllerDefPtr controller, + unsigned long long qemuCmdFlags); +int qemudDomainAttachSCSIDisk(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags); +int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + unsigned long long qemuCmdFlags); +int qemudDomainAttachNetDevice(virConnectPtr conn, + struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainNetDefPtr net, + unsigned long long qemuCmdFlags); +int qemudDomainAttachHostPciDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev, + unsigned long long qemuCmdFlags); +int qemudDomainAttachHostUsbDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev, + unsigned long long qemuCmdFlags); +int qemudDomainAttachHostDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev, + unsigned long long qemuCmdFlags); +int qemuDomainChangeGraphics(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainGraphicsDefPtr dev); +int qemudDomainDetachPciDiskDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags); +int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags); +int qemudDomainDetachPciControllerDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags); +int qemudDomainDetachNetDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags); +int qemudDomainDetachHostPciDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags); +int qemudDomainDetachHostUsbDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags); +int qemudDomainDetachHostDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainDeviceDefPtr dev, + unsigned long long qemuCmdFlags); + + +#endif /* __QEMU_HOTPLUG_H__ */ -- 1.7.2.3

On 12/16/2010 09:50 AM, Daniel P. Berrange wrote:
The QEMU driver file is far too large. Move all the hotplug helper code out into a separate file. No functional change.
* src/qemu/qemu_hotplug.c, src/qemu/qemu_hotplug.h, src/Makefile.am: Add hotplug helper file * src/qemu/qemu_driver.c: Delete hotplug code --- src/Makefile.am | 1 + src/qemu/qemu_driver.c | 2019 ++++------------------------------------------ src/qemu/qemu_hotplug.c | 1711 +++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_hotplug.h | 103 +++ 4 files changed, 1991 insertions(+), 1843 deletions(-) create mode 100644 src/qemu/qemu_hotplug.c create mode 100644 src/qemu/qemu_hotplug.h
-static int qemudDomainDetachSCSIDiskDevice(struct qemud_driver *driver, - virDomainObjPtr vm, - virDomainDeviceDefPtr dev, - unsigned long long qemuCmdFlags) +static int qemudDomainAttachDevice(virDomainPtr dom, + const char *xml) { - int i, ret = -1; - virDomainDiskDefPtr detach = NULL; - qemuDomainObjPrivatePtr priv = vm->privateData; + struct qemud_driver *driver = dom->conn->privateData; + virDomainObjPtr vm; + virDomainDeviceDefPtr dev = NULL; + unsigned long long qemuCmdFlags;
Oh gross. git botched that diff, making this area of the patch harder to review than any of the prior patches. (Possibly because of the renames from qemud to qemu?). But even with that munging, the overall result looks good to me.
+int qemudDomainAttachHostDevice(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainHostdevDefPtr hostdev, + unsigned long long qemuCmdFlags); +int qemuDomainChangeGraphics(struct qemud_driver *driver, + virDomainObjPtr vm, + virDomainGraphicsDefPtr dev);
So, given that some, but not all, of the functions in this file start with qemudDomain, should we change them to all be qemuDomain? Or maybe separate the move and the rename into two separate patches, so that git diff won't botch it so badly? At any rate, ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (2)
-
Daniel P. Berrange
-
Eric Blake