/*
* capabilities.c: hypervisor capabilities
*
* Copyright (C) 2006-2008, 2010-2011 Red Hat, Inc.
* Copyright (C) 2006-2008 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, see
* .
*
* Author: Daniel P. Berrange
*/
#include
#include
#include "capabilities.h"
#include "buf.h"
#include "memory.h"
#include "util.h"
#include "uuid.h"
#include "cpu_conf.h"
#include "virterror_internal.h"
#define VIR_FROM_THIS VIR_FROM_CAPABILITIES
VIR_ENUM_DECL(virCapsHostPMTarget)
VIR_ENUM_IMPL(virCapsHostPMTarget, VIR_NODE_SUSPEND_TARGET_LAST,
"suspend_mem", "suspend_disk", "suspend_hybrid");
/**
* virCapabilitiesNew:
* @arch: host machine architecture
* @offlineMigrate: non-zero if offline migration is available
* @liveMigrate: non-zero if live migration is available
*
* Allocate a new capabilities object
*/
virCapsPtr
virCapabilitiesNew(const char *arch,
int offlineMigrate,
int liveMigrate)
{
virCapsPtr caps;
if (VIR_ALLOC(caps) < 0)
return NULL;
if ((caps->host.arch = strdup(arch)) == NULL)
goto no_memory;
caps->host.offlineMigrate = offlineMigrate;
caps->host.liveMigrate = liveMigrate;
return caps;
no_memory:
virCapabilitiesFree(caps);
return NULL;
}
static void
virCapabilitiesFreeHostNUMACell(virCapsHostNUMACellPtr cell)
{
if (cell == NULL)
return;
VIR_FREE(cell->cpus);
VIR_FREE(cell);
}
static void
virCapabilitiesFreeGuestMachine(virCapsGuestMachinePtr machine)
{
if (machine == NULL)
return;
VIR_FREE(machine->name);
VIR_FREE(machine->canonical);
VIR_FREE(machine);
}
static void
virCapabilitiesFreeGuestDomain(virCapsGuestDomainPtr dom)
{
int i;
if (dom == NULL)
return;
VIR_FREE(dom->info.emulator);
VIR_FREE(dom->info.loader);
for (i = 0 ; i < dom->info.nmachines ; i++)
virCapabilitiesFreeGuestMachine(dom->info.machines[i]);
VIR_FREE(dom->info.machines);
VIR_FREE(dom->type);
VIR_FREE(dom);
}
static void
virCapabilitiesFreeGuestFeature(virCapsGuestFeaturePtr feature)
{
if (feature == NULL)
return;
VIR_FREE(feature->name);
VIR_FREE(feature);
}
static void
virCapabilitiesFreeGuest(virCapsGuestPtr guest)
{
int i;
if (guest == NULL)
return;
VIR_FREE(guest->ostype);
VIR_FREE(guest->arch.name);
VIR_FREE(guest->arch.defaultInfo.emulator);
VIR_FREE(guest->arch.defaultInfo.loader);
for (i = 0 ; i < guest->arch.defaultInfo.nmachines ; i++)
virCapabilitiesFreeGuestMachine(guest->arch.defaultInfo.machines[i]);
VIR_FREE(guest->arch.defaultInfo.machines);
for (i = 0 ; i < guest->arch.ndomains ; i++)
virCapabilitiesFreeGuestDomain(guest->arch.domains[i]);
VIR_FREE(guest->arch.domains);
for (i = 0 ; i < guest->nfeatures ; i++)
virCapabilitiesFreeGuestFeature(guest->features[i]);
VIR_FREE(guest->features);
VIR_FREE(guest);
}
void
virCapabilitiesFreeNUMAInfo(virCapsPtr caps)
{
int i;
for (i = 0 ; i < caps->host.nnumaCell ; i++)
virCapabilitiesFreeHostNUMACell(caps->host.numaCell[i]);
VIR_FREE(caps->host.numaCell);
caps->host.nnumaCell = 0;
}
/**
* virCapabilitiesFree:
* @caps: object to free
*
* Free all memory associated with capabilities
*/
void
virCapabilitiesFree(virCapsPtr caps) {
int i;
if (caps == NULL)
return;
for (i = 0 ; i < caps->nguests ; i++)
virCapabilitiesFreeGuest(caps->guests[i]);
VIR_FREE(caps->guests);
for (i = 0 ; i < caps->host.nfeatures ; i++)
VIR_FREE(caps->host.features[i]);
VIR_FREE(caps->host.features);
virCapabilitiesFreeNUMAInfo(caps);
for (i = 0 ; i < caps->host.nmigrateTrans ; i++)
VIR_FREE(caps->host.migrateTrans[i]);
VIR_FREE(caps->host.migrateTrans);
VIR_FREE(caps->host.arch);
for (i = 0; i < caps->host.nsecModels; i++) {
VIR_FREE(caps->host.secModels[i].model);
VIR_FREE(caps->host.secModels[i].doi);
}
VIR_FREE(caps->host.secModels);
virCPUDefFree(caps->host.cpu);
VIR_FREE(caps);
}
/**
* virCapabilitiesAddHostFeature:
* @caps: capabilities to extend
* @name: name of new feature
*
* Registers a new host CPU feature, eg 'pae', or 'vmx'
*/
int
virCapabilitiesAddHostFeature(virCapsPtr caps,
const char *name)
{
if (VIR_RESIZE_N(caps->host.features, caps->host.nfeatures_max,
caps->host.nfeatures, 1) < 0)
return -1;
if ((caps->host.features[caps->host.nfeatures] = strdup(name)) == NULL)
return -1;
caps->host.nfeatures++;
return 0;
}
/**
* virCapabilitiesAddHostMigrateTransport:
* @caps: capabilities to extend
* @name: name of migration transport
*
* Registers a new domain migration transport URI
*/
int
virCapabilitiesAddHostMigrateTransport(virCapsPtr caps,
const char *name)
{
if (VIR_RESIZE_N(caps->host.migrateTrans, caps->host.nmigrateTrans_max,
caps->host.nmigrateTrans, 1) < 0)
return -1;
if ((caps->host.migrateTrans[caps->host.nmigrateTrans] = strdup(name)) == NULL)
return -1;
caps->host.nmigrateTrans++;
return 0;
}
/**
* virCapabilitiesAddHostNUMACell:
* @caps: capabilities to extend
* @num: ID number of NUMA cell
* @ncpus: number of CPUs in cell
* @cpus: array of CPU ID numbers for cell
*
* Registers a new NUMA cell for a host, passing in a
* array of CPU IDs belonging to the cell
*/
int
virCapabilitiesAddHostNUMACell(virCapsPtr caps,
int num,
int ncpus,
const int *cpus)
{
virCapsHostNUMACellPtr cell;
if (VIR_RESIZE_N(caps->host.numaCell, caps->host.nnumaCell_max,
caps->host.nnumaCell, 1) < 0)
return -1;
if (VIR_ALLOC(cell) < 0)
return -1;
if (VIR_ALLOC_N(cell->cpus, ncpus) < 0) {
VIR_FREE(cell);
return -1;
}
memcpy(cell->cpus,
cpus,
ncpus * sizeof(*cpus));
cell->ncpus = ncpus;
cell->num = num;
caps->host.numaCell[caps->host.nnumaCell++] = cell;
return 0;
}
/**
* virCapabilitiesSetHostCPU:
* @caps: capabilities to extend
* @cpu: CPU definition
*
* Sets host CPU specification
*/
int
virCapabilitiesSetHostCPU(virCapsPtr caps,
virCPUDefPtr cpu)
{
if (cpu == NULL)
return -1;
caps->host.cpu = cpu;
return 0;
}
/**
* virCapabilitiesAllocMachines:
* @machines: machine variants for emulator ('pc', or 'isapc', etc)
* @nmachines: number of machine variants for emulator
*
* Allocate a table of virCapsGuestMachinePtr from the supplied table
* of machine names.
*/
virCapsGuestMachinePtr *
virCapabilitiesAllocMachines(const char *const *names, int nnames)
{
virCapsGuestMachinePtr *machines;
int i;
if (VIR_ALLOC_N(machines, nnames) < 0)
return NULL;
for (i = 0; i < nnames; i++) {
if (VIR_ALLOC(machines[i]) < 0 ||
!(machines[i]->name = strdup(names[i]))) {
virCapabilitiesFreeMachines(machines, nnames);
return NULL;
}
}
return machines;
}
/**
* virCapabilitiesFreeMachines:
* @machines: table of vircapsGuestMachinePtr
*
* Free a table of virCapsGuestMachinePtr
*/
void
virCapabilitiesFreeMachines(virCapsGuestMachinePtr *machines,
int nmachines)
{
int i;
if (!machines)
return;
for (i = 0; i < nmachines && machines[i]; i++) {
virCapabilitiesFreeGuestMachine(machines[i]);
machines[i] = NULL;
}
VIR_FREE(machines);
}
/**
* virCapabilitiesAddGuest:
* @caps: capabilities to extend
* @ostype: guest operating system type ('hvm' or 'xen')
* @arch: guest CPU architecture ('i686', or 'x86_64', etc)
* @wordsize: number of bits in CPU word
* @emulator: path to default device emulator for arch/ostype
* @loader: path to default BIOS loader for arch/ostype
* @nmachines: number of machine variants for emulator
* @machines: machine variants for emulator ('pc', or 'isapc', etc)
*
* Registers a new guest operating system. This should be
* followed by registration of at least one domain for
* running the guest
*/
virCapsGuestPtr
virCapabilitiesAddGuest(virCapsPtr caps,
const char *ostype,
const char *arch,
int wordsize,
const char *emulator,
const char *loader,
int nmachines,
virCapsGuestMachinePtr *machines)
{
virCapsGuestPtr guest;
if (VIR_ALLOC(guest) < 0)
goto no_memory;
if ((guest->ostype = strdup(ostype)) == NULL)
goto no_memory;
if ((guest->arch.name = strdup(arch)) == NULL)
goto no_memory;
guest->arch.wordsize = wordsize;
if (emulator &&
(guest->arch.defaultInfo.emulator = strdup(emulator)) == NULL)
goto no_memory;
if (loader &&
(guest->arch.defaultInfo.loader = strdup(loader)) == NULL)
goto no_memory;
if (VIR_RESIZE_N(caps->guests, caps->nguests_max,
caps->nguests, 1) < 0)
goto no_memory;
caps->guests[caps->nguests++] = guest;
if (nmachines) {
guest->arch.defaultInfo.nmachines = nmachines;
guest->arch.defaultInfo.machines = machines;
}
return guest;
no_memory:
virCapabilitiesFreeGuest(guest);
return NULL;
}
/**
* virCapabilitiesAddGuestDomain:
* @guest: guest to support
* @hvtype: hypervisor type ('xen', 'qemu', 'kvm')
* @emulator: specialized device emulator for domain
* @loader: specialized BIOS loader for domain
* @nmachines: number of machine variants for emulator
* @machines: specialized machine variants for emulator
*
* Registers a virtual domain capable of running a
* guest operating system
*/
virCapsGuestDomainPtr
virCapabilitiesAddGuestDomain(virCapsGuestPtr guest,
const char *hvtype,
const char *emulator,
const char *loader,
int nmachines,
virCapsGuestMachinePtr *machines)
{
virCapsGuestDomainPtr dom;
if (VIR_ALLOC(dom) < 0)
goto no_memory;
if ((dom->type = strdup(hvtype)) == NULL)
goto no_memory;
if (emulator &&
(dom->info.emulator = strdup(emulator)) == NULL)
goto no_memory;
if (loader &&
(dom->info.loader = strdup(loader)) == NULL)
goto no_memory;
if (VIR_RESIZE_N(guest->arch.domains, guest->arch.ndomains_max,
guest->arch.ndomains, 1) < 0)
goto no_memory;
guest->arch.domains[guest->arch.ndomains] = dom;
guest->arch.ndomains++;
if (nmachines) {
dom->info.nmachines = nmachines;
dom->info.machines = machines;
}
return dom;
no_memory:
virCapabilitiesFreeGuestDomain(dom);
return NULL;
}
/**
* virCapabilitiesAddGuestFeature:
* @guest: guest to associate feature with
* @name: name of feature ('pae', 'acpi', 'apic')
* @defaultOn: non-zero if it defaults to on
* @toggle: non-zero if its state can be toggled
*
* Registers a feature for a guest domain
*/
virCapsGuestFeaturePtr
virCapabilitiesAddGuestFeature(virCapsGuestPtr guest,
const char *name,
int defaultOn,
int toggle)
{
virCapsGuestFeaturePtr feature;
if (VIR_ALLOC(feature) < 0)
goto no_memory;
if ((feature->name = strdup(name)) == NULL)
goto no_memory;
feature->defaultOn = defaultOn;
feature->toggle = toggle;
if (VIR_RESIZE_N(guest->features, guest->nfeatures_max,
guest->nfeatures, 1) < 0)
goto no_memory;
guest->features[guest->nfeatures++] = feature;
return feature;
no_memory:
virCapabilitiesFreeGuestFeature(feature);
return NULL;
}
/**
* virCapabilitiesSupportsGuestArch:
* @caps: capabilities to query
* @arch: Architecture to search for (eg, 'i686', 'x86_64')
*
* Returns non-zero if the capabilities support the
* requested architecture
*/
extern int
virCapabilitiesSupportsGuestArch(virCapsPtr caps,
const char *arch)
{
int i;
for (i = 0 ; i < caps->nguests ; i++) {
if (STREQ(caps->guests[i]->arch.name, arch))
return 1;
}
return 0;
}
/**
* virCapabilitiesSupportsGuestOSType:
* @caps: capabilities to query
* @ostype: OS type to search for (eg 'hvm', 'xen')
*
* Returns non-zero if the capabilities support the
* requested operating system type
*/
extern int
virCapabilitiesSupportsGuestOSType(virCapsPtr caps,
const char *ostype)
{
int i;
for (i = 0 ; i < caps->nguests ; i++) {
if (STREQ(caps->guests[i]->ostype, ostype))
return 1;
}
return 0;
}
/**
* virCapabilitiesSupportsGuestOSTypeArch:
* @caps: capabilities to query
* @ostype: OS type to search for (eg 'hvm', 'xen')
* @arch: Architecture to search for (eg, 'i686', 'x86_64')
*
* Returns non-zero if the capabilities support the
* requested operating system type
*/
extern int
virCapabilitiesSupportsGuestOSTypeArch(virCapsPtr caps,
const char *ostype,
const char *arch)
{
int i;
for (i = 0 ; i < caps->nguests ; i++) {
if (STREQ(caps->guests[i]->ostype, ostype) &&
STREQ(caps->guests[i]->arch.name, arch))
return 1;
}
return 0;
}
/**
* virCapabilitiesDefaultGuestArch:
* @caps: capabilities to query
* @ostype: OS type to search for
*
* Returns the first architecture able to run the
* requested operating system type
*/
extern const char *
virCapabilitiesDefaultGuestArch(virCapsPtr caps,
const char *ostype,
const char *domain)
{
int i, j;
const char *arch = NULL;
for (i = 0 ; i < caps->nguests ; i++) {
if (STREQ(caps->guests[i]->ostype, ostype)) {
for (j = 0 ; j < caps->guests[i]->arch.ndomains ; j++) {
if (STREQ(caps->guests[i]->arch.domains[j]->type, domain)) {
/* Use the first match... */
if (!arch)
arch = caps->guests[i]->arch.name;
/* ...unless we can match the host's architecture. */
if (STREQ(caps->guests[i]->arch.name, caps->host.arch))
return caps->guests[i]->arch.name;
}
}
}
}
return arch;
}
/**
* virCapabilitiesDefaultGuestMachine:
* @caps: capabilities to query
* @ostype: OS type to search for
* @arch: architecture to search for
* @domain: domain type to search for
*
* Returns the first machine variant associated with
* the requested operating system type, architecture
* and domain type
*/
extern const char *
virCapabilitiesDefaultGuestMachine(virCapsPtr caps,
const char *ostype,
const char *arch,
const char *domain)
{
int i;
for (i = 0 ; i < caps->nguests ; i++) {
virCapsGuestPtr guest = caps->guests[i];
int j;
if (!STREQ(guest->ostype, ostype) || !STREQ(guest->arch.name, arch))
continue;
for (j = 0; j < guest->arch.ndomains; j++) {
virCapsGuestDomainPtr dom= guest->arch.domains[j];
if (!STREQ(dom->type, domain))
continue;
if (!dom->info.nmachines)
break;
return dom->info.machines[0]->name;
}
if (guest->arch.defaultInfo.nmachines)
return caps->guests[i]->arch.defaultInfo.machines[0]->name;
}
return NULL;
}
/**
* virCapabilitiesDefaultGuestEmulator:
* @caps: capabilities to query
* @ostype: OS type to search for ('xen', 'hvm')
* @arch: architecture to search for
* @domain: domain type ('xen', 'qemu', 'kvm')
*
* Returns the first emulator path associated with
* the requested operating system type, architecture
* and domain type
*/
extern const char *
virCapabilitiesDefaultGuestEmulator(virCapsPtr caps,
const char *ostype,
const char *arch,
const char *domain)
{
int i, j;
for (i = 0 ; i < caps->nguests ; i++) {
char *emulator;
if (STREQ(caps->guests[i]->ostype, ostype) &&
STREQ(caps->guests[i]->arch.name, arch)) {
emulator = caps->guests[i]->arch.defaultInfo.emulator;
for (j = 0 ; j < caps->guests[i]->arch.ndomains ; j++) {
if (STREQ(caps->guests[i]->arch.domains[j]->type, domain)) {
if (caps->guests[i]->arch.domains[j]->info.emulator)
emulator = caps->guests[i]->arch.domains[j]->info.emulator;
}
}
return emulator;
}
}
return NULL;
}
/**
* virCapabilitiesFormatXML:
* @caps: capabilities to format
*
* Convert the capabilities object into an XML representation
*
* Returns the XML document as a string
*/
char *
virCapabilitiesFormatXML(virCapsPtr caps)
{
virBuffer xml = VIR_BUFFER_INITIALIZER;
int i, j, k;
char host_uuid[VIR_UUID_STRING_BUFLEN];
virBufferAddLit(&xml, "\n\n");
virBufferAddLit(&xml, " \n");
if (virUUIDIsValid(caps->host.host_uuid)) {
virUUIDFormat(caps->host.host_uuid, host_uuid);
virBufferAsprintf(&xml," %s\n", host_uuid);
}
virBufferAddLit(&xml, " \n");
virBufferAsprintf(&xml, " %s\n",
caps->host.arch);
if (caps->host.nfeatures) {
virBufferAddLit(&xml, " \n");
for (i = 0 ; i < caps->host.nfeatures ; i++) {
virBufferAsprintf(&xml, " <%s/>\n",
caps->host.features[i]);
}
virBufferAddLit(&xml, " \n");
}
virBufferAdjustIndent(&xml, 6);
virCPUDefFormatBuf(&xml, caps->host.cpu, 0);
virBufferAdjustIndent(&xml, -6);
virBufferAddLit(&xml, " \n");
/* The PM query was successful. */
if (caps->host.powerMgmt) {
/* The host supports some PM features. */
unsigned int pm = caps->host.powerMgmt;
virBufferAddLit(&xml, " \n");
while (pm) {
int bit = ffs(pm) - 1;
virBufferAsprintf(&xml, " <%s/>\n",
virCapsHostPMTargetTypeToString(bit));
pm &= ~(1U << bit);
}
virBufferAddLit(&xml, " \n");
} else {
/* The host does not support any PM feature. */
virBufferAddLit(&xml, " \n");
}
if (caps->host.offlineMigrate) {
virBufferAddLit(&xml, " \n");
if (caps->host.liveMigrate)
virBufferAddLit(&xml, " \n");
if (caps->host.nmigrateTrans) {
virBufferAddLit(&xml, " \n");
for (i = 0 ; i < caps->host.nmigrateTrans ; i++) {
virBufferAsprintf(&xml, " %s\n",
caps->host.migrateTrans[i]);
}
virBufferAddLit(&xml, " \n");
}
virBufferAddLit(&xml, " \n");
}
if (caps->host.nnumaCell) {
virBufferAddLit(&xml, " \n");
virBufferAsprintf(&xml, " \n",
caps->host.nnumaCell);
for (i = 0 ; i < caps->host.nnumaCell ; i++) {
virBufferAsprintf(&xml, " \n",
caps->host.numaCell[i]->num);
virBufferAsprintf(&xml, " \n",
caps->host.numaCell[i]->ncpus);
for (j = 0 ; j < caps->host.numaCell[i]->ncpus ; j++)
virBufferAsprintf(&xml, " \n",
caps->host.numaCell[i]->cpus[j]);
virBufferAddLit(&xml, " \n");
virBufferAddLit(&xml, " | \n");
}
virBufferAddLit(&xml, " \n");
virBufferAddLit(&xml, " \n");
}
for (i = 0; i < caps->host.nsecModels; i++) {
virBufferAddLit(&xml, " \n");
virBufferAsprintf(&xml, " %s\n",
caps->host.secModels[i].model);
virBufferAsprintf(&xml, " %s\n",
caps->host.secModels[i].doi);
virBufferAddLit(&xml, " \n");
}
virBufferAddLit(&xml, " \n\n");
for (i = 0 ; i < caps->nguests ; i++) {
virBufferAddLit(&xml, " \n");
virBufferAsprintf(&xml, " %s\n",
caps->guests[i]->ostype);
virBufferAsprintf(&xml, " \n",
caps->guests[i]->arch.name);
virBufferAsprintf(&xml, " %d\n",
caps->guests[i]->arch.wordsize);
if (caps->guests[i]->arch.defaultInfo.emulator)
virBufferAsprintf(&xml, " %s\n",
caps->guests[i]->arch.defaultInfo.emulator);
if (caps->guests[i]->arch.defaultInfo.loader)
virBufferAsprintf(&xml, " %s\n",
caps->guests[i]->arch.defaultInfo.loader);
for (j = 0 ; j < caps->guests[i]->arch.defaultInfo.nmachines ; j++) {
virCapsGuestMachinePtr machine = caps->guests[i]->arch.defaultInfo.machines[j];
virBufferAddLit(&xml, " canonical)
virBufferAsprintf(&xml, " canonical='%s'", machine->canonical);
virBufferAsprintf(&xml, ">%s\n", machine->name);
}
for (j = 0 ; j < caps->guests[i]->arch.ndomains ; j++) {
virBufferAsprintf(&xml, " \n",
caps->guests[i]->arch.domains[j]->type);
if (caps->guests[i]->arch.domains[j]->info.emulator)
virBufferAsprintf(&xml, " %s\n",
caps->guests[i]->arch.domains[j]->info.emulator);
if (caps->guests[i]->arch.domains[j]->info.loader)
virBufferAsprintf(&xml, " %s\n",
caps->guests[i]->arch.domains[j]->info.loader);
for (k = 0 ; k < caps->guests[i]->arch.domains[j]->info.nmachines ; k++) {
virCapsGuestMachinePtr machine = caps->guests[i]->arch.domains[j]->info.machines[k];
virBufferAddLit(&xml, " canonical)
virBufferAsprintf(&xml, " canonical='%s'", machine->canonical);
virBufferAsprintf(&xml, ">%s\n", machine->name);
}
virBufferAddLit(&xml, " \n");
}
virBufferAddLit(&xml, " \n");
if (caps->guests[i]->nfeatures) {
virBufferAddLit(&xml, " \n");
for (j = 0 ; j < caps->guests[i]->nfeatures ; j++) {
if (STREQ(caps->guests[i]->features[j]->name, "pae") ||
STREQ(caps->guests[i]->features[j]->name, "nonpae") ||
STREQ(caps->guests[i]->features[j]->name, "ia64_be") ||
STREQ(caps->guests[i]->features[j]->name, "cpuselection") ||
STREQ(caps->guests[i]->features[j]->name, "deviceboot")) {
virBufferAsprintf(&xml, " <%s/>\n",
caps->guests[i]->features[j]->name);
} else {
virBufferAsprintf(&xml, " <%s default='%s' toggle='%s'/>\n",
caps->guests[i]->features[j]->name,
caps->guests[i]->features[j]->defaultOn ? "on" : "off",
caps->guests[i]->features[j]->toggle ? "yes" : "no");
}
}
virBufferAddLit(&xml, " \n");
}
virBufferAddLit(&xml, " \n\n");
}
virBufferAddLit(&xml, "\n");
if (virBufferError(&xml)) {
virBufferFreeAndReset(&xml);
return NULL;
}
return virBufferContentAndReset(&xml);
}
extern void
virCapabilitiesSetMacPrefix(virCapsPtr caps,
const unsigned char prefix[VIR_MAC_PREFIX_BUFLEN])
{
memcpy(caps->macPrefix, prefix, sizeof(caps->macPrefix));
}
extern void
virCapabilitiesGenerateMac(virCapsPtr caps,
virMacAddrPtr mac)
{
virMacAddrGenerate(caps->macPrefix, mac);
}
extern void
virCapabilitiesSetEmulatorRequired(virCapsPtr caps) {
caps->emulatorRequired = 1;
}
extern unsigned int
virCapabilitiesIsEmulatorRequired(virCapsPtr caps) {
return caps->emulatorRequired;
}