mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-02 11:21:12 +00:00
a7cfd709f4
Our HACKING discourages use of malloc and free, for at least a couple of years now. But we weren't enforcing it, until now :) For now, I've exempted python and tests, and will clean those up in subsequent patches. Examples should be permanently exempt, since anyone copying our examples won't have use of our internal-only memory.h via libvirt_util.la. * cfg.mk (sc_prohibit_raw_allocation): New rule. (exclude_file_name_regexp--sc_prohibit_raw_allocation): and exemptions. * src/cpu/cpu.c (cpuDataFree): Avoid false positive. * src/conf/network_conf.c (virNetworkDNSSrvDefParseXML): Fix offenders. * src/libxl/libxl_conf.c (libxlMakeDomBuildInfo, libxlMakeVfb) (libxlMakeDeviceModelInfo): Likewise. * src/rpc/virnetmessage.c (virNetMessageSaveError): Likewise. * tools/virsh.c (_vshMalloc, _vshCalloc): Likewise.
976 lines
30 KiB
C
976 lines
30 KiB
C
/*---------------------------------------------------------------------------*/
|
|
/* Copyright (C) 2012 Red Hat, Inc.
|
|
* Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany.
|
|
* Copyright (C) 2011 Univention GmbH.
|
|
*
|
|
* 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
|
|
*
|
|
* Authors:
|
|
* Jim Fehlig <jfehlig@novell.com>
|
|
* Markus Groß <gross@univention.de>
|
|
*/
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <regex.h>
|
|
#include <libxl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/utsname.h>
|
|
|
|
#include "internal.h"
|
|
#include "logging.h"
|
|
#include "virterror_internal.h"
|
|
#include "datatypes.h"
|
|
#include "virfile.h"
|
|
#include "memory.h"
|
|
#include "uuid.h"
|
|
#include "capabilities.h"
|
|
#include "libxl_driver.h"
|
|
#include "libxl_conf.h"
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_LIBXL
|
|
|
|
/* see xen-unstable.hg/xen/include/asm-x86/cpufeature.h */
|
|
#define LIBXL_X86_FEATURE_PAE_MASK 0x40
|
|
|
|
|
|
struct guest_arch {
|
|
const char *model;
|
|
int bits;
|
|
int hvm;
|
|
int pae;
|
|
int nonpae;
|
|
int ia64_be;
|
|
};
|
|
|
|
static const char *xen_cap_re = "(xen|hvm)-[[:digit:]]+\\.[[:digit:]]+-(x86_32|x86_64|ia64|powerpc64)(p|be)?";
|
|
static regex_t xen_cap_rec;
|
|
|
|
|
|
static int
|
|
libxlNextFreeVncPort(libxlDriverPrivatePtr driver, int startPort)
|
|
{
|
|
int i;
|
|
|
|
for (i = startPort ; i < LIBXL_VNC_PORT_MAX; i++) {
|
|
int fd;
|
|
int reuse = 1;
|
|
struct sockaddr_in addr;
|
|
bool used = false;
|
|
|
|
if (virBitmapGetBit(driver->reservedVNCPorts,
|
|
i - LIBXL_VNC_PORT_MIN, &used) < 0)
|
|
VIR_DEBUG("virBitmapGetBit failed on bit %d", i - LIBXL_VNC_PORT_MIN);
|
|
|
|
if (used)
|
|
continue;
|
|
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(i);
|
|
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
fd = socket(PF_INET, SOCK_STREAM, 0);
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse)) < 0) {
|
|
VIR_FORCE_CLOSE(fd);
|
|
break;
|
|
}
|
|
|
|
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
|
|
/* Not in use, lets grab it */
|
|
VIR_FORCE_CLOSE(fd);
|
|
/* Add port to bitmap of reserved ports */
|
|
if (virBitmapSetBit(driver->reservedVNCPorts,
|
|
i - LIBXL_VNC_PORT_MIN) < 0) {
|
|
VIR_DEBUG("virBitmapSetBit failed on bit %d",
|
|
i - LIBXL_VNC_PORT_MIN);
|
|
}
|
|
return i;
|
|
}
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
if (errno == EADDRINUSE) {
|
|
/* In use, try next */
|
|
continue;
|
|
}
|
|
/* Some other bad failure, get out.. */
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int libxlDefaultConsoleType(const char *ostype)
|
|
{
|
|
if (STREQ(ostype, "hvm"))
|
|
return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
|
|
else
|
|
return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN;
|
|
}
|
|
|
|
static virCapsPtr
|
|
libxlBuildCapabilities(const char *hostmachine,
|
|
int host_pae,
|
|
struct guest_arch *guest_archs,
|
|
int nr_guest_archs)
|
|
{
|
|
virCapsPtr caps;
|
|
int i;
|
|
|
|
if ((caps = virCapabilitiesNew(hostmachine, 1, 1)) == NULL)
|
|
goto no_memory;
|
|
|
|
virCapabilitiesSetMacPrefix(caps, (unsigned char[]){ 0x00, 0x16, 0x3e });
|
|
|
|
if (host_pae &&
|
|
virCapabilitiesAddHostFeature(caps, "pae") < 0)
|
|
goto no_memory;
|
|
|
|
for (i = 0; i < nr_guest_archs; ++i) {
|
|
virCapsGuestPtr guest;
|
|
char const *const xen_machines[] = {guest_archs[i].hvm ? "xenfv" : "xenpv"};
|
|
virCapsGuestMachinePtr *machines;
|
|
|
|
if ((machines = virCapabilitiesAllocMachines(xen_machines, 1)) == NULL)
|
|
goto no_memory;
|
|
|
|
if ((guest = virCapabilitiesAddGuest(caps,
|
|
guest_archs[i].hvm ? "hvm" : "xen",
|
|
guest_archs[i].model,
|
|
guest_archs[i].bits,
|
|
(STREQ(hostmachine, "x86_64") ?
|
|
"/usr/lib64/xen/bin/qemu-dm" :
|
|
"/usr/lib/xen/bin/qemu-dm"),
|
|
(guest_archs[i].hvm ?
|
|
"/usr/lib/xen/boot/hvmloader" :
|
|
NULL),
|
|
1,
|
|
machines)) == NULL) {
|
|
virCapabilitiesFreeMachines(machines, 1);
|
|
goto no_memory;
|
|
}
|
|
machines = NULL;
|
|
|
|
if (virCapabilitiesAddGuestDomain(guest,
|
|
"xen",
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NULL) == NULL)
|
|
goto no_memory;
|
|
|
|
if (guest_archs[i].pae &&
|
|
virCapabilitiesAddGuestFeature(guest,
|
|
"pae",
|
|
1,
|
|
0) == NULL)
|
|
goto no_memory;
|
|
|
|
if (guest_archs[i].nonpae &&
|
|
virCapabilitiesAddGuestFeature(guest,
|
|
"nonpae",
|
|
1,
|
|
0) == NULL)
|
|
goto no_memory;
|
|
|
|
if (guest_archs[i].ia64_be &&
|
|
virCapabilitiesAddGuestFeature(guest,
|
|
"ia64_be",
|
|
1,
|
|
0) == NULL)
|
|
goto no_memory;
|
|
|
|
if (guest_archs[i].hvm) {
|
|
if (virCapabilitiesAddGuestFeature(guest,
|
|
"acpi",
|
|
1,
|
|
1) == NULL)
|
|
goto no_memory;
|
|
|
|
if (virCapabilitiesAddGuestFeature(guest, "apic",
|
|
1,
|
|
0) == NULL)
|
|
goto no_memory;
|
|
|
|
if (virCapabilitiesAddGuestFeature(guest,
|
|
"hap",
|
|
0,
|
|
1) == NULL)
|
|
goto no_memory;
|
|
}
|
|
}
|
|
|
|
caps->defaultConsoleTargetType = libxlDefaultConsoleType;
|
|
|
|
return caps;
|
|
|
|
no_memory:
|
|
virCapabilitiesFree(caps);
|
|
return NULL;
|
|
}
|
|
|
|
static virCapsPtr
|
|
libxlMakeCapabilitiesInternal(const char *hostmachine,
|
|
libxl_physinfo *phy_info,
|
|
char *capabilities)
|
|
{
|
|
char *str, *token;
|
|
regmatch_t subs[4];
|
|
char *saveptr = NULL;
|
|
int i;
|
|
|
|
int host_pae = 0;
|
|
struct guest_arch guest_archs[32];
|
|
int nr_guest_archs = 0;
|
|
virCapsPtr caps = NULL;
|
|
|
|
memset(guest_archs, 0, sizeof(guest_archs));
|
|
|
|
/* hw_caps is an array of 32-bit words whose meaning is listed in
|
|
* xen-unstable.hg/xen/include/asm-x86/cpufeature.h. Each feature
|
|
* is defined in the form X*32+Y, corresponding to the Y'th bit in
|
|
* the X'th 32-bit word of hw_cap.
|
|
*/
|
|
host_pae = phy_info->hw_cap[0] & LIBXL_X86_FEATURE_PAE_MASK;
|
|
|
|
/* Format of capabilities string is documented in the code in
|
|
* xen-unstable.hg/xen/arch/.../setup.c.
|
|
*
|
|
* It is a space-separated list of supported guest architectures.
|
|
*
|
|
* For x86:
|
|
* TYP-VER-ARCH[p]
|
|
* ^ ^ ^ ^
|
|
* | | | +-- PAE supported
|
|
* | | +------- x86_32 or x86_64
|
|
* | +----------- the version of Xen, eg. "3.0"
|
|
* +--------------- "xen" or "hvm" for para or full virt respectively
|
|
*
|
|
* For IA64:
|
|
* TYP-VER-ARCH[be]
|
|
* ^ ^ ^ ^
|
|
* | | | +-- Big-endian supported
|
|
* | | +------- always "ia64"
|
|
* | +----------- the version of Xen, eg. "3.0"
|
|
* +--------------- "xen" or "hvm" for para or full virt respectively
|
|
*/
|
|
|
|
/* Split capabilities string into tokens. strtok_r is OK here because
|
|
* we "own" the buffer. Parse out the features from each token.
|
|
*/
|
|
for (str = capabilities, nr_guest_archs = 0;
|
|
nr_guest_archs < sizeof(guest_archs) / sizeof(guest_archs[0])
|
|
&& (token = strtok_r(str, " ", &saveptr)) != NULL;
|
|
str = NULL) {
|
|
if (regexec(&xen_cap_rec, token, sizeof(subs) / sizeof(subs[0]),
|
|
subs, 0) == 0) {
|
|
int hvm = STRPREFIX(&token[subs[1].rm_so], "hvm");
|
|
const char *model;
|
|
int bits, pae = 0, nonpae = 0, ia64_be = 0;
|
|
|
|
if (STRPREFIX(&token[subs[2].rm_so], "x86_32")) {
|
|
model = "i686";
|
|
bits = 32;
|
|
if (subs[3].rm_so != -1 &&
|
|
STRPREFIX(&token[subs[3].rm_so], "p"))
|
|
pae = 1;
|
|
else
|
|
nonpae = 1;
|
|
}
|
|
else if (STRPREFIX(&token[subs[2].rm_so], "x86_64")) {
|
|
model = "x86_64";
|
|
bits = 64;
|
|
}
|
|
else if (STRPREFIX(&token[subs[2].rm_so], "ia64")) {
|
|
model = "ia64";
|
|
bits = 64;
|
|
if (subs[3].rm_so != -1 &&
|
|
STRPREFIX(&token[subs[3].rm_so], "be"))
|
|
ia64_be = 1;
|
|
}
|
|
else if (STRPREFIX(&token[subs[2].rm_so], "powerpc64")) {
|
|
model = "ppc64";
|
|
bits = 64;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
/* Search for existing matching (model,hvm) tuple */
|
|
for (i = 0 ; i < nr_guest_archs ; i++) {
|
|
if (STREQ(guest_archs[i].model, model) &&
|
|
guest_archs[i].hvm == hvm) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Too many arch flavours - highly unlikely ! */
|
|
if (i >= ARRAY_CARDINALITY(guest_archs))
|
|
continue;
|
|
/* Didn't find a match, so create a new one */
|
|
if (i == nr_guest_archs)
|
|
nr_guest_archs++;
|
|
|
|
guest_archs[i].model = model;
|
|
guest_archs[i].bits = bits;
|
|
guest_archs[i].hvm = hvm;
|
|
|
|
/* Careful not to overwrite a previous positive
|
|
setting with a negative one here - some archs
|
|
can do both pae & non-pae, but Xen reports
|
|
separately capabilities so we're merging archs */
|
|
if (pae)
|
|
guest_archs[i].pae = pae;
|
|
if (nonpae)
|
|
guest_archs[i].nonpae = nonpae;
|
|
if (ia64_be)
|
|
guest_archs[i].ia64_be = ia64_be;
|
|
}
|
|
}
|
|
|
|
if ((caps = libxlBuildCapabilities(hostmachine,
|
|
host_pae,
|
|
guest_archs,
|
|
nr_guest_archs)) == NULL)
|
|
goto no_memory;
|
|
|
|
return caps;
|
|
|
|
no_memory:
|
|
virReportOOMError();
|
|
virCapabilitiesFree(caps);
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
libxlMakeDomCreateInfo(virDomainDefPtr def, libxl_domain_create_info *c_info)
|
|
{
|
|
char uuidstr[VIR_UUID_STRING_BUFLEN];
|
|
|
|
libxl_init_create_info(c_info);
|
|
|
|
c_info->hvm = STREQ(def->os.type, "hvm");
|
|
if ((c_info->name = strdup(def->name)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
|
|
virUUIDFormat(def->uuid, uuidstr);
|
|
if (libxl_uuid_from_string(&c_info->uuid, uuidstr) ) {
|
|
libxlError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxenlight failed to parse UUID '%s'"), uuidstr);
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
libxl_domain_create_info_destroy(c_info);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
libxlMakeDomBuildInfo(virDomainDefPtr def, libxl_domain_config *d_config)
|
|
{
|
|
libxl_domain_build_info *b_info = &d_config->b_info;
|
|
int hvm = STREQ(def->os.type, "hvm");
|
|
int i;
|
|
|
|
/* Currently, libxenlight only supports 32 vcpus per domain.
|
|
* cur_vcpus member of struct libxl_domain_build_info is defined
|
|
* as an int, but its semantic is a bitmap of online vcpus, so
|
|
* only 32 can be represented.
|
|
*/
|
|
if (def->maxvcpus > 32 || def->vcpus > 32) {
|
|
libxlError(VIR_ERR_INTERNAL_ERROR,
|
|
_("This version of libxenlight only supports 32 "
|
|
"vcpus per domain"));
|
|
return -1;
|
|
}
|
|
|
|
libxl_init_build_info(b_info, &d_config->c_info);
|
|
|
|
b_info->hvm = hvm;
|
|
b_info->max_vcpus = def->maxvcpus;
|
|
if (def->vcpus == 32)
|
|
b_info->cur_vcpus = (uint32_t) -1;
|
|
else
|
|
b_info->cur_vcpus = (1 << def->vcpus) - 1;
|
|
if (def->clock.ntimers > 0 &&
|
|
def->clock.timers[0]->name == VIR_DOMAIN_TIMER_NAME_TSC) {
|
|
switch (def->clock.timers[0]->mode) {
|
|
case VIR_DOMAIN_TIMER_MODE_NATIVE:
|
|
b_info->tsc_mode = 2;
|
|
break;
|
|
case VIR_DOMAIN_TIMER_MODE_PARAVIRT:
|
|
b_info->tsc_mode = 3;
|
|
break;
|
|
default:
|
|
b_info->tsc_mode = 1;
|
|
}
|
|
}
|
|
b_info->max_memkb = def->mem.max_balloon;
|
|
b_info->target_memkb = def->mem.cur_balloon;
|
|
if (hvm) {
|
|
b_info->u.hvm.pae = def->features & (1 << VIR_DOMAIN_FEATURE_PAE);
|
|
b_info->u.hvm.apic = def->features & (1 << VIR_DOMAIN_FEATURE_APIC);
|
|
b_info->u.hvm.acpi = def->features & (1 << VIR_DOMAIN_FEATURE_ACPI);
|
|
for (i = 0; i < def->clock.ntimers; i++) {
|
|
if (def->clock.timers[i]->name == VIR_DOMAIN_TIMER_NAME_HPET &&
|
|
def->clock.timers[i]->present == 1) {
|
|
b_info->u.hvm.hpet = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The following comment and calculation were taken directly from
|
|
* libxenlight's internal function libxl_get_required_shadow_memory():
|
|
*
|
|
* 256 pages (1MB) per vcpu, plus 1 page per MiB of RAM for the P2M map,
|
|
* plus 1 page per MiB of RAM to shadow the resident processes.
|
|
*/
|
|
b_info->shadow_memkb = 4 * (256 * b_info->cur_vcpus +
|
|
2 * (b_info->max_memkb / 1024));
|
|
} else {
|
|
if (def->os.bootloader) {
|
|
if ((b_info->u.pv.bootloader = strdup(def->os.bootloader)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
}
|
|
if (def->os.bootloaderArgs) {
|
|
if ((b_info->u.pv.bootloader_args = strdup(def->os.bootloaderArgs)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
}
|
|
if (def->os.cmdline) {
|
|
if ((b_info->u.pv.cmdline = strdup(def->os.cmdline)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
}
|
|
if (def->os.kernel) {
|
|
/* libxl_init_build_info() sets kernel.path = strdup("hvmloader") */
|
|
VIR_FREE(b_info->kernel.path);
|
|
if ((b_info->kernel.path = strdup(def->os.kernel)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
}
|
|
if (def->os.initrd) {
|
|
if ((b_info->u.pv.ramdisk.path = strdup(def->os.initrd)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
libxl_domain_build_info_destroy(b_info);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
libxlMakeDisk(virDomainDefPtr def, virDomainDiskDefPtr l_disk,
|
|
libxl_device_disk *x_disk)
|
|
{
|
|
if (l_disk->src && (x_disk->pdev_path = strdup(l_disk->src)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
|
|
if (l_disk->dst && (x_disk->vdev = strdup(l_disk->dst)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
|
|
if (l_disk->driverName) {
|
|
if (STREQ(l_disk->driverName, "tap") ||
|
|
STREQ(l_disk->driverName, "tap2")) {
|
|
if (l_disk->driverType) {
|
|
if (STREQ(l_disk->driverType, "qcow")) {
|
|
x_disk->format = DISK_FORMAT_QCOW;
|
|
x_disk->backend = DISK_BACKEND_QDISK;
|
|
} else if (STREQ(l_disk->driverType, "qcow2")) {
|
|
x_disk->format = DISK_FORMAT_QCOW2;
|
|
x_disk->backend = DISK_BACKEND_QDISK;
|
|
} else if (STREQ(l_disk->driverType, "vhd")) {
|
|
x_disk->format = DISK_FORMAT_VHD;
|
|
x_disk->backend = DISK_BACKEND_TAP;
|
|
} else if (STREQ(l_disk->driverType, "aio") ||
|
|
STREQ(l_disk->driverType, "raw")) {
|
|
x_disk->format = DISK_FORMAT_RAW;
|
|
x_disk->backend = DISK_BACKEND_TAP;
|
|
}
|
|
} else {
|
|
/* No subtype specified, default to raw/tap */
|
|
x_disk->format = DISK_FORMAT_RAW;
|
|
x_disk->backend = DISK_BACKEND_TAP;
|
|
}
|
|
} else if (STREQ(l_disk->driverName, "file")) {
|
|
x_disk->format = DISK_FORMAT_RAW;
|
|
x_disk->backend = DISK_BACKEND_TAP;
|
|
} else if (STREQ(l_disk->driverName, "phy")) {
|
|
x_disk->format = DISK_FORMAT_RAW;
|
|
x_disk->backend = DISK_BACKEND_PHY;
|
|
} else {
|
|
libxlError(VIR_ERR_INTERNAL_ERROR,
|
|
_("libxenlight does not support disk driver %s"),
|
|
l_disk->driverName);
|
|
return -1;
|
|
}
|
|
} else {
|
|
/* No driverName - default to raw/tap?? */
|
|
x_disk->format = DISK_FORMAT_RAW;
|
|
x_disk->backend = DISK_BACKEND_TAP;
|
|
}
|
|
|
|
/* How to set unpluggable? */
|
|
x_disk->unpluggable = 1;
|
|
x_disk->readwrite = !l_disk->readonly;
|
|
x_disk->is_cdrom = l_disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM ? 1 : 0;
|
|
if (l_disk->transient) {
|
|
libxlError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("libxenlight does not support transient disks"));
|
|
return -1;
|
|
}
|
|
|
|
x_disk->domid = def->id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlMakeDiskList(virDomainDefPtr def, libxl_domain_config *d_config)
|
|
{
|
|
virDomainDiskDefPtr *l_disks = def->disks;
|
|
int ndisks = def->ndisks;
|
|
libxl_device_disk *x_disks;
|
|
int i;
|
|
|
|
if (VIR_ALLOC_N(x_disks, ndisks) < 0) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < ndisks; i++) {
|
|
if (libxlMakeDisk(def, l_disks[i], &x_disks[i]) < 0)
|
|
goto error;
|
|
}
|
|
|
|
d_config->disks = x_disks;
|
|
d_config->num_disks = ndisks;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
for (i = 0; i < ndisks; i++)
|
|
libxl_device_disk_destroy(&x_disks[i]);
|
|
VIR_FREE(x_disks);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
libxlMakeNic(virDomainDefPtr def, virDomainNetDefPtr l_nic,
|
|
libxl_device_nic *x_nic)
|
|
{
|
|
// TODO: Where is mtu stored?
|
|
//x_nics[i].mtu = 1492;
|
|
|
|
x_nic->domid = def->id;
|
|
memcpy(x_nic->mac, l_nic->mac, sizeof(libxl_mac));
|
|
|
|
if (l_nic->model && !STREQ(l_nic->model, "netfront")) {
|
|
if ((x_nic->model = strdup(l_nic->model)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
x_nic->nictype = NICTYPE_IOEMU;
|
|
} else {
|
|
x_nic->nictype = NICTYPE_VIF;
|
|
}
|
|
|
|
if (l_nic->ifname && (x_nic->ifname = strdup(l_nic->ifname)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
|
|
if (l_nic->type == VIR_DOMAIN_NET_TYPE_BRIDGE) {
|
|
if (l_nic->data.bridge.brname &&
|
|
(x_nic->bridge = strdup(l_nic->data.bridge.brname)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
if (l_nic->script &&
|
|
(x_nic->script = strdup(l_nic->script)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (l_nic->script) {
|
|
libxlError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("scripts are not supported on interfaces of type %s"),
|
|
virDomainNetTypeToString(l_nic->type));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlMakeNicList(virDomainDefPtr def, libxl_domain_config *d_config)
|
|
{
|
|
virDomainNetDefPtr *l_nics = def->nets;
|
|
int nnics = def->nnets;
|
|
libxl_device_nic *x_nics;
|
|
int i;
|
|
|
|
if (VIR_ALLOC_N(x_nics, nnics) < 0) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < nnics; i++) {
|
|
x_nics[i].devid = i;
|
|
|
|
if (libxlMakeNic(def, l_nics[i], &x_nics[i]))
|
|
goto error;
|
|
}
|
|
|
|
d_config->vifs = x_nics;
|
|
d_config->num_vifs = nnics;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
for (i = 0; i < nnics; i++)
|
|
libxl_device_nic_destroy(&x_nics[i]);
|
|
VIR_FREE(x_nics);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
libxlMakeVfb(libxlDriverPrivatePtr driver, virDomainDefPtr def,
|
|
virDomainGraphicsDefPtr l_vfb, libxl_device_vfb *x_vfb)
|
|
{
|
|
int port;
|
|
const char *listenAddr;
|
|
|
|
switch (l_vfb->type) {
|
|
case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
|
|
x_vfb->sdl = 1;
|
|
if (l_vfb->data.sdl.display &&
|
|
(x_vfb->display = strdup(l_vfb->data.sdl.display)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
if (l_vfb->data.sdl.xauth &&
|
|
(x_vfb->xauthority =
|
|
strdup(l_vfb->data.sdl.xauth)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
|
|
x_vfb->vnc = 1;
|
|
/* driver handles selection of free port */
|
|
x_vfb->vncunused = 0;
|
|
if (l_vfb->data.vnc.autoport) {
|
|
port = libxlNextFreeVncPort(driver, LIBXL_VNC_PORT_MIN);
|
|
if (port < 0) {
|
|
libxlError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("Unable to find an unused VNC port"));
|
|
return -1;
|
|
}
|
|
l_vfb->data.vnc.port = port;
|
|
}
|
|
x_vfb->vncdisplay = l_vfb->data.vnc.port - LIBXL_VNC_PORT_MIN;
|
|
|
|
listenAddr = virDomainGraphicsListenGetAddress(l_vfb, 0);
|
|
if (listenAddr) {
|
|
/* libxl_device_vfb_init() does strdup("127.0.0.1") */
|
|
VIR_FREE(x_vfb->vnclisten);
|
|
if ((x_vfb->vnclisten = strdup(listenAddr)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
}
|
|
if (l_vfb->data.vnc.keymap &&
|
|
(x_vfb->keymap =
|
|
strdup(l_vfb->data.vnc.keymap)) == NULL) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
x_vfb->domid = def->id;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlMakeVfbList(libxlDriverPrivatePtr driver,
|
|
virDomainDefPtr def, libxl_domain_config *d_config)
|
|
{
|
|
virDomainGraphicsDefPtr *l_vfbs = def->graphics;
|
|
int nvfbs = def->ngraphics;
|
|
libxl_device_vfb *x_vfbs;
|
|
libxl_device_vkb *x_vkbs;
|
|
int i;
|
|
|
|
if (nvfbs == 0)
|
|
return 0;
|
|
|
|
if (VIR_ALLOC_N(x_vfbs, nvfbs) < 0) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
if (VIR_ALLOC_N(x_vkbs, nvfbs) < 0) {
|
|
virReportOOMError();
|
|
VIR_FREE(x_vfbs);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < nvfbs; i++) {
|
|
libxl_device_vfb_init(&x_vfbs[i], i);
|
|
libxl_device_vkb_init(&x_vkbs[i], i);
|
|
|
|
if (libxlMakeVfb(driver, def, l_vfbs[i], &x_vfbs[i]) < 0)
|
|
goto error;
|
|
}
|
|
|
|
d_config->vfbs = x_vfbs;
|
|
d_config->vkbs = x_vkbs;
|
|
d_config->num_vfbs = d_config->num_vkbs = nvfbs;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
for (i = 0; i < nvfbs; i++) {
|
|
libxl_device_vfb_destroy(&x_vfbs[i]);
|
|
libxl_device_vkb_destroy(&x_vkbs[i]);
|
|
}
|
|
VIR_FREE(x_vfbs);
|
|
VIR_FREE(x_vkbs);
|
|
return -1;
|
|
}
|
|
|
|
static int
|
|
libxlMakeChrdevStr(virDomainChrDefPtr def, char **buf)
|
|
{
|
|
const char *type = virDomainChrTypeToString(def->source.type);
|
|
|
|
if (!type) {
|
|
libxlError(VIR_ERR_INTERNAL_ERROR,
|
|
"%s", _("unexpected chr device type"));
|
|
return -1;
|
|
}
|
|
|
|
switch (def->source.type) {
|
|
case VIR_DOMAIN_CHR_TYPE_NULL:
|
|
case VIR_DOMAIN_CHR_TYPE_STDIO:
|
|
case VIR_DOMAIN_CHR_TYPE_VC:
|
|
case VIR_DOMAIN_CHR_TYPE_PTY:
|
|
if (virAsprintf(buf, "%s", type) < 0) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_FILE:
|
|
case VIR_DOMAIN_CHR_TYPE_PIPE:
|
|
if (virAsprintf(buf, "%s:%s", type,
|
|
def->source.data.file.path) < 0) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_DEV:
|
|
if (virAsprintf(buf, "%s", def->source.data.file.path) < 0) {
|
|
virReportOOMError();
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
libxlMakeDeviceModelInfo(virDomainDefPtr def, libxl_domain_config *d_config)
|
|
{
|
|
libxl_device_model_info *dm_info = &d_config->dm_info;
|
|
int i;
|
|
char b_order[VIR_DOMAIN_BOOT_LAST+1];
|
|
|
|
libxl_init_dm_info(dm_info, &d_config->c_info, &d_config->b_info);
|
|
|
|
if (d_config->b_info.hvm) {
|
|
/* HVM-specific device model info */
|
|
dm_info->type = XENFV;
|
|
if (def->os.nBootDevs > 0) {
|
|
VIR_FREE(dm_info->boot);
|
|
for (i = 0; i < def->os.nBootDevs; i++) {
|
|
switch (def->os.bootDevs[i]) {
|
|
case VIR_DOMAIN_BOOT_FLOPPY:
|
|
b_order[i] = 'a';
|
|
break;
|
|
default:
|
|
case VIR_DOMAIN_BOOT_DISK:
|
|
b_order[i] = 'c';
|
|
break;
|
|
case VIR_DOMAIN_BOOT_CDROM:
|
|
b_order[i] = 'd';
|
|
break;
|
|
case VIR_DOMAIN_BOOT_NET:
|
|
b_order[i] = 'n';
|
|
break;
|
|
}
|
|
}
|
|
b_order[def->os.nBootDevs] = '\0';
|
|
if ((dm_info->boot = strdup(b_order)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
}
|
|
if (def->serials &&
|
|
(libxlMakeChrdevStr(def->serials[0], &dm_info->serial) < 0))
|
|
goto error;
|
|
} else {
|
|
/* PV-specific device model info */
|
|
dm_info->type = XENPV;
|
|
}
|
|
|
|
/* Build qemu graphics options from previously parsed vfb */
|
|
if (d_config->num_vfbs > 0) {
|
|
if (d_config->vfbs[0].vnc) {
|
|
dm_info->vnc = 1;
|
|
/* driver handles selection of free port */
|
|
dm_info->vncunused = 0;
|
|
if (d_config->vfbs[0].vnclisten) {
|
|
VIR_FREE(dm_info->vnclisten);
|
|
if ((dm_info->vnclisten =
|
|
strdup(d_config->vfbs[0].vnclisten)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
}
|
|
if (d_config->vfbs[0].keymap &&
|
|
(dm_info->keymap = strdup(d_config->vfbs[0].keymap)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
dm_info->vncdisplay = d_config->vfbs[0].vncdisplay;
|
|
if (d_config->vfbs[0].vncpasswd &&
|
|
(dm_info->vncpasswd =
|
|
strdup(d_config->vfbs[0].vncpasswd)) == NULL) {
|
|
virReportOOMError();
|
|
goto error;
|
|
}
|
|
} else if (d_config->vfbs[0].sdl) {
|
|
dm_info->sdl = 1;
|
|
dm_info->vnc = 0;
|
|
}
|
|
} else if (d_config->num_vfbs == 0) {
|
|
dm_info->nographic = 1;
|
|
dm_info->vnc = 0;
|
|
}
|
|
|
|
// TODO
|
|
//dm_info->usb = ;
|
|
//dm_info->usbdevice = ;
|
|
//dm_info->soundhw = ;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
libxl_device_model_info_destroy(dm_info);
|
|
return -1;
|
|
}
|
|
|
|
virCapsPtr
|
|
libxlMakeCapabilities(libxl_ctx *ctx)
|
|
{
|
|
libxl_physinfo phy_info;
|
|
const libxl_version_info *ver_info;
|
|
struct utsname utsname;
|
|
|
|
regcomp (&xen_cap_rec, xen_cap_re, REG_EXTENDED);
|
|
|
|
if (libxl_get_physinfo(ctx, &phy_info) != 0) {
|
|
libxlError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to get node physical info from libxenlight"));
|
|
return NULL;
|
|
}
|
|
|
|
if ((ver_info = libxl_get_version_info(ctx)) == NULL) {
|
|
libxlError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Failed to get version info from libxenlight"));
|
|
return NULL;
|
|
}
|
|
|
|
uname(&utsname);
|
|
|
|
return libxlMakeCapabilitiesInternal(utsname.machine,
|
|
&phy_info,
|
|
ver_info->capabilities);
|
|
}
|
|
|
|
int
|
|
libxlBuildDomainConfig(libxlDriverPrivatePtr driver,
|
|
virDomainDefPtr def, libxl_domain_config *d_config)
|
|
{
|
|
|
|
if (libxlMakeDomCreateInfo(def, &d_config->c_info) < 0)
|
|
return -1;
|
|
|
|
if (libxlMakeDomBuildInfo(def, d_config) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
if (libxlMakeDiskList(def, d_config) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
if (libxlMakeNicList(def, d_config) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
if (libxlMakeVfbList(driver, def, d_config) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
if (libxlMakeDeviceModelInfo(def, d_config) < 0) {
|
|
goto error;
|
|
}
|
|
|
|
d_config->on_reboot = def->onReboot;
|
|
d_config->on_poweroff = def->onPoweroff;
|
|
d_config->on_crash = def->onCrash;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
libxl_domain_config_destroy(d_config);
|
|
return -1;
|
|
}
|