/*
* domain_conf.c: domain XML processing
*
* Copyright (C) 2006-2015 Red Hat, Inc.
* Copyright (C) 2006-2008 Daniel P. Berrange
* Copyright (c) 2015 SUSE LINUX Products GmbH, Nuernberg, Germany.
*
* 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
#include
#include
#include "configmake.h"
#include "internal.h"
#include "virerror.h"
#include "datatypes.h"
#include "domain_conf.h"
#include "snapshot_conf.h"
#include "viralloc.h"
#include "virxml.h"
#include "viruuid.h"
#include "virbuffer.h"
#include "virlog.h"
#include "nwfilter_conf.h"
#include "storage_conf.h"
#include "virstoragefile.h"
#include "virfile.h"
#include "virbitmap.h"
#include "count-one-bits.h"
#include "secret_conf.h"
#include "netdev_vport_profile_conf.h"
#include "netdev_bandwidth_conf.h"
#include "netdev_vlan_conf.h"
#include "device_conf.h"
#include "network_conf.h"
#include "virtpm.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_DOMAIN
VIR_LOG_INIT("conf.domain_conf");
struct _virDomainObjList {
virObjectLockable parent;
/* uuid string -> virDomainObj mapping
* for O(1), lockless lookup-by-uuid */
virHashTable *objs;
/* name -> virDomainObj mapping for O(1),
* lockless lookup-by-name */
virHashTable *objsName;
};
/* This structure holds various callbacks and data needed
* while parsing and creating domain XMLs */
struct _virDomainXMLOption {
virObject parent;
/* XML parser callbacks and defaults */
virDomainDefParserConfig config;
/* domain private data management callbacks */
virDomainXMLPrivateDataCallbacks privateData;
/* XML namespace callbacks */
virDomainXMLNamespace ns;
};
#define VIR_DOMAIN_DEF_FORMAT_COMMON_FLAGS \
(VIR_DOMAIN_DEF_FORMAT_SECURE | \
VIR_DOMAIN_DEF_FORMAT_INACTIVE | \
VIR_DOMAIN_DEF_FORMAT_UPDATE_CPU | \
VIR_DOMAIN_DEF_FORMAT_MIGRATABLE)
VIR_ENUM_IMPL(virDomainTaint, VIR_DOMAIN_TAINT_LAST,
"custom-argv",
"custom-monitor",
"high-privileges",
"shell-scripts",
"disk-probing",
"external-launch",
"host-cpu",
"hook-script",
"cdrom-passthrough",
"custom-dtb");
VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST,
"qemu",
"kqemu",
"kvm",
"xen",
"lxc",
"uml",
"openvz",
"test",
"vmware",
"hyperv",
"vbox",
"phyp",
"parallels",
"bhyve",
"vz")
VIR_ENUM_IMPL(virDomainOS, VIR_DOMAIN_OSTYPE_LAST,
"hvm",
"xen",
"linux",
"exe",
"uml")
VIR_ENUM_IMPL(virDomainBoot, VIR_DOMAIN_BOOT_LAST,
"fd",
"cdrom",
"hd",
"network")
VIR_ENUM_IMPL(virDomainFeature, VIR_DOMAIN_FEATURE_LAST,
"acpi",
"apic",
"pae",
"hap",
"viridian",
"privnet",
"hyperv",
"kvm",
"pvspinlock",
"capabilities",
"pmu",
"vmport",
"gic")
VIR_ENUM_IMPL(virDomainCapabilitiesPolicy, VIR_DOMAIN_CAPABILITIES_POLICY_LAST,
"default",
"allow",
"deny")
VIR_ENUM_IMPL(virDomainHyperv, VIR_DOMAIN_HYPERV_LAST,
"relaxed",
"vapic",
"spinlocks")
VIR_ENUM_IMPL(virDomainKVM, VIR_DOMAIN_KVM_LAST,
"hidden")
VIR_ENUM_IMPL(virDomainCapsFeature, VIR_DOMAIN_CAPS_FEATURE_LAST,
"audit_control",
"audit_write",
"block_suspend",
"chown",
"dac_override",
"dac_read_search",
"fowner",
"fsetid",
"ipc_lock",
"ipc_owner",
"kill",
"lease",
"linux_immutable",
"mac_admin",
"mac_override",
"mknod",
"net_admin",
"net_bind_service",
"net_broadcast",
"net_raw",
"setgid",
"setfcap",
"setpcap",
"setuid",
"sys_admin",
"sys_boot",
"sys_chroot",
"sys_module",
"sys_nice",
"sys_pacct",
"sys_ptrace",
"sys_rawio",
"sys_resource",
"sys_time",
"sys_tty_config",
"syslog",
"wake_alarm")
VIR_ENUM_IMPL(virDomainLifecycle, VIR_DOMAIN_LIFECYCLE_LAST,
"destroy",
"restart",
"rename-restart",
"preserve")
VIR_ENUM_IMPL(virDomainLifecycleCrash, VIR_DOMAIN_LIFECYCLE_CRASH_LAST,
"destroy",
"restart",
"rename-restart",
"preserve",
"coredump-destroy",
"coredump-restart")
VIR_ENUM_IMPL(virDomainLockFailure, VIR_DOMAIN_LOCK_FAILURE_LAST,
"default",
"poweroff",
"restart",
"pause",
"ignore")
VIR_ENUM_IMPL(virDomainDevice, VIR_DOMAIN_DEVICE_LAST,
"none",
"disk",
"lease",
"filesystem",
"interface",
"input",
"sound",
"video",
"hostdev",
"watchdog",
"controller",
"graphics",
"hub",
"redirdev",
"smartcard",
"chr",
"memballoon",
"nvram",
"rng",
"shmem",
"tpm",
"panic",
"memory")
VIR_ENUM_IMPL(virDomainDeviceAddress, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
"none",
"pci",
"drive",
"virtio-serial",
"ccid",
"usb",
"spapr-vio",
"virtio-s390",
"ccw",
"virtio-mmio",
"isa",
"dimm")
VIR_ENUM_IMPL(virDomainDiskDevice, VIR_DOMAIN_DISK_DEVICE_LAST,
"disk",
"cdrom",
"floppy",
"lun")
VIR_ENUM_IMPL(virDomainDiskGeometryTrans, VIR_DOMAIN_DISK_TRANS_LAST,
"default",
"none",
"auto",
"lba")
VIR_ENUM_IMPL(virDomainDiskBus, VIR_DOMAIN_DISK_BUS_LAST,
"ide",
"fdc",
"scsi",
"virtio",
"xen",
"usb",
"uml",
"sata",
"sd")
VIR_ENUM_IMPL(virDomainDiskCache, VIR_DOMAIN_DISK_CACHE_LAST,
"default",
"none",
"writethrough",
"writeback",
"directsync",
"unsafe")
VIR_ENUM_IMPL(virDomainDiskErrorPolicy, VIR_DOMAIN_DISK_ERROR_POLICY_LAST,
"default",
"stop",
"report",
"ignore",
"enospace")
VIR_ENUM_IMPL(virDomainDiskIo, VIR_DOMAIN_DISK_IO_LAST,
"default",
"native",
"threads")
VIR_ENUM_IMPL(virDomainDeviceSGIO, VIR_DOMAIN_DEVICE_SGIO_LAST,
"default",
"filtered",
"unfiltered")
VIR_ENUM_IMPL(virDomainController, VIR_DOMAIN_CONTROLLER_TYPE_LAST,
"ide",
"fdc",
"scsi",
"sata",
"virtio-serial",
"ccid",
"usb",
"pci")
VIR_ENUM_IMPL(virDomainControllerModelPCI, VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST,
"pci-root",
"pcie-root",
"pci-bridge",
"dmi-to-pci-bridge",
"pcie-root-port",
"pcie-switch-upstream-port",
"pcie-switch-downstream-port")
VIR_ENUM_IMPL(virDomainControllerPCIModelName,
VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_LAST,
"none",
"pci-bridge",
"i82801b11-bridge",
"ioh3420",
"x3130-upstream",
"xio3130-downstream")
VIR_ENUM_IMPL(virDomainControllerModelSCSI, VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LAST,
"auto",
"buslogic",
"lsilogic",
"lsisas1068",
"vmpvscsi",
"ibmvscsi",
"virtio-scsi",
"lsisas1078");
VIR_ENUM_IMPL(virDomainControllerModelUSB, VIR_DOMAIN_CONTROLLER_MODEL_USB_LAST,
"piix3-uhci",
"piix4-uhci",
"ehci",
"ich9-ehci1",
"ich9-uhci1",
"ich9-uhci2",
"ich9-uhci3",
"vt82c686b-uhci",
"pci-ohci",
"nec-xhci",
"none")
VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST,
"mount",
"block",
"file",
"template",
"ram",
"bind")
VIR_ENUM_IMPL(virDomainFSDriver, VIR_DOMAIN_FS_DRIVER_TYPE_LAST,
"default",
"path",
"handle",
"loop",
"nbd",
"ploop")
VIR_ENUM_IMPL(virDomainFSAccessMode, VIR_DOMAIN_FS_ACCESSMODE_LAST,
"passthrough",
"mapped",
"squash")
VIR_ENUM_IMPL(virDomainFSWrpolicy, VIR_DOMAIN_FS_WRPOLICY_LAST,
"default",
"immediate")
VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST,
"user",
"ethernet",
"vhostuser",
"server",
"client",
"mcast",
"network",
"bridge",
"internal",
"direct",
"hostdev")
VIR_ENUM_IMPL(virDomainNetBackend, VIR_DOMAIN_NET_BACKEND_TYPE_LAST,
"default",
"qemu",
"vhost")
VIR_ENUM_IMPL(virDomainNetVirtioTxMode, VIR_DOMAIN_NET_VIRTIO_TX_MODE_LAST,
"default",
"iothread",
"timer")
VIR_ENUM_IMPL(virDomainNetInterfaceLinkState, VIR_DOMAIN_NET_INTERFACE_LINK_STATE_LAST,
"default",
"up",
"down")
VIR_ENUM_IMPL(virDomainChrDeviceState, VIR_DOMAIN_CHR_DEVICE_STATE_LAST,
"default",
"connected",
"disconnected");
VIR_ENUM_IMPL(virDomainChrSerialTarget,
VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST,
"isa-serial",
"usb-serial",
"pci-serial")
VIR_ENUM_IMPL(virDomainChrChannelTarget,
VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_LAST,
"none",
"guestfwd",
"virtio")
VIR_ENUM_IMPL(virDomainChrConsoleTarget,
VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_LAST,
"none",
"serial",
"xen",
"uml",
"virtio",
"lxc",
"openvz",
"sclp",
"sclplm")
VIR_ENUM_IMPL(virDomainChrDevice, VIR_DOMAIN_CHR_DEVICE_TYPE_LAST,
"parallel",
"serial",
"console",
"channel")
VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST,
"null",
"vc",
"pty",
"dev",
"file",
"pipe",
"stdio",
"udp",
"tcp",
"unix",
"spicevmc",
"spiceport",
"nmdm")
VIR_ENUM_IMPL(virDomainChrTcpProtocol, VIR_DOMAIN_CHR_TCP_PROTOCOL_LAST,
"raw",
"telnet",
"telnets",
"tls")
VIR_ENUM_IMPL(virDomainChrSpicevmc, VIR_DOMAIN_CHR_SPICEVMC_LAST,
"vdagent",
"smartcard",
"usbredir")
VIR_ENUM_IMPL(virDomainSmartcard, VIR_DOMAIN_SMARTCARD_TYPE_LAST,
"host",
"host-certificates",
"passthrough")
VIR_ENUM_IMPL(virDomainSoundCodec, VIR_DOMAIN_SOUND_CODEC_TYPE_LAST,
"duplex",
"micro")
VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST,
"sb16",
"es1370",
"pcspk",
"ac97",
"ich6",
"ich9",
"usb")
VIR_ENUM_IMPL(virDomainKeyWrapCipherName,
VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_LAST,
"aes",
"dea")
VIR_ENUM_IMPL(virDomainMemballoonModel, VIR_DOMAIN_MEMBALLOON_MODEL_LAST,
"virtio",
"xen",
"none")
VIR_ENUM_IMPL(virDomainSmbiosMode, VIR_DOMAIN_SMBIOS_LAST,
"none",
"emulate",
"host",
"sysinfo")
VIR_ENUM_IMPL(virDomainWatchdogModel, VIR_DOMAIN_WATCHDOG_MODEL_LAST,
"i6300esb",
"ib700",
"diag288")
VIR_ENUM_IMPL(virDomainWatchdogAction, VIR_DOMAIN_WATCHDOG_ACTION_LAST,
"reset",
"shutdown",
"poweroff",
"pause",
"dump",
"none",
"inject-nmi")
VIR_ENUM_IMPL(virDomainVideo, VIR_DOMAIN_VIDEO_TYPE_LAST,
"vga",
"cirrus",
"vmvga",
"xen",
"vbox",
"qxl",
"parallels")
VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST,
"mouse",
"tablet",
"keyboard")
VIR_ENUM_IMPL(virDomainInputBus, VIR_DOMAIN_INPUT_BUS_LAST,
"ps2",
"usb",
"xen",
"parallels")
VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST,
"sdl",
"vnc",
"rdp",
"desktop",
"spice")
VIR_ENUM_IMPL(virDomainGraphicsListen, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST,
"none",
"address",
"network")
VIR_ENUM_IMPL(virDomainGraphicsAuthConnected,
VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_LAST,
"default",
"fail",
"disconnect",
"keep")
VIR_ENUM_IMPL(virDomainGraphicsVNCSharePolicy,
VIR_DOMAIN_GRAPHICS_VNC_SHARE_LAST,
"default",
"allow-exclusive",
"force-shared",
"ignore")
VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName,
VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST,
"main",
"display",
"inputs",
"cursor",
"playback",
"record",
"smartcard",
"usbredir");
VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelMode,
VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_LAST,
"any",
"secure",
"insecure");
VIR_ENUM_IMPL(virDomainGraphicsSpiceImageCompression,
VIR_DOMAIN_GRAPHICS_SPICE_IMAGE_COMPRESSION_LAST,
"default",
"auto_glz",
"auto_lz",
"quic",
"glz",
"lz",
"off");
VIR_ENUM_IMPL(virDomainGraphicsSpiceJpegCompression,
VIR_DOMAIN_GRAPHICS_SPICE_JPEG_COMPRESSION_LAST,
"default",
"auto",
"never",
"always");
VIR_ENUM_IMPL(virDomainGraphicsSpiceZlibCompression,
VIR_DOMAIN_GRAPHICS_SPICE_ZLIB_COMPRESSION_LAST,
"default",
"auto",
"never",
"always");
VIR_ENUM_IMPL(virDomainGraphicsSpiceMouseMode,
VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_LAST,
"default",
"server",
"client");
VIR_ENUM_IMPL(virDomainGraphicsSpiceStreamingMode,
VIR_DOMAIN_GRAPHICS_SPICE_STREAMING_MODE_LAST,
"default",
"filter",
"all",
"off");
VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST,
"subsystem",
"capabilities")
VIR_ENUM_IMPL(virDomainHostdevSubsys, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST,
"usb",
"pci",
"scsi")
VIR_ENUM_IMPL(virDomainHostdevSubsysPCIBackend,
VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST,
"default",
"kvm",
"vfio",
"xen")
VIR_ENUM_IMPL(virDomainHostdevSubsysSCSIProtocol,
VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_LAST,
"adapter",
"iscsi")
VIR_ENUM_IMPL(virDomainHostdevCaps, VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST,
"storage",
"misc",
"net")
VIR_ENUM_IMPL(virDomainHub, VIR_DOMAIN_HUB_TYPE_LAST,
"usb")
VIR_ENUM_IMPL(virDomainRedirdevBus, VIR_DOMAIN_REDIRDEV_BUS_LAST,
"usb")
VIR_ENUM_IMPL(virDomainState, VIR_DOMAIN_LAST,
"nostate",
"running",
"blocked",
"paused",
"shutdown",
"shutoff",
"crashed",
"pmsuspended")
VIR_ENUM_IMPL(virDomainNostateReason, VIR_DOMAIN_NOSTATE_LAST,
"unknown")
VIR_ENUM_IMPL(virDomainRunningReason, VIR_DOMAIN_RUNNING_LAST,
"unknown",
"booted",
"migrated",
"restored",
"from snapshot",
"unpaused",
"migration canceled",
"save canceled",
"wakeup",
"crashed")
VIR_ENUM_IMPL(virDomainBlockedReason, VIR_DOMAIN_BLOCKED_LAST,
"unknown")
VIR_ENUM_IMPL(virDomainPausedReason, VIR_DOMAIN_PAUSED_LAST,
"unknown",
"user",
"migration",
"save",
"dump",
"ioerror",
"watchdog",
"from snapshot",
"shutdown",
"snapshot",
"panicked",
"starting up")
VIR_ENUM_IMPL(virDomainShutdownReason, VIR_DOMAIN_SHUTDOWN_LAST,
"unknown",
"user")
VIR_ENUM_IMPL(virDomainShutoffReason, VIR_DOMAIN_SHUTOFF_LAST,
"unknown",
"shutdown",
"destroyed",
"crashed",
"migrated",
"saved",
"failed",
"from snapshot")
VIR_ENUM_IMPL(virDomainCrashedReason, VIR_DOMAIN_CRASHED_LAST,
"unknown",
"panicked")
VIR_ENUM_IMPL(virDomainPMSuspendedReason, VIR_DOMAIN_PMSUSPENDED_LAST,
"unknown")
VIR_ENUM_IMPL(virDomainSeclabel, VIR_DOMAIN_SECLABEL_LAST,
"default",
"none",
"dynamic",
"static")
VIR_ENUM_IMPL(virDomainClockOffset, VIR_DOMAIN_CLOCK_OFFSET_LAST,
"utc",
"localtime",
"variable",
"timezone");
VIR_ENUM_IMPL(virDomainClockBasis, VIR_DOMAIN_CLOCK_BASIS_LAST,
"utc",
"localtime");
VIR_ENUM_IMPL(virDomainTimerName, VIR_DOMAIN_TIMER_NAME_LAST,
"platform",
"pit",
"rtc",
"hpet",
"tsc",
"kvmclock",
"hypervclock");
VIR_ENUM_IMPL(virDomainTimerTrack, VIR_DOMAIN_TIMER_TRACK_LAST,
"boot",
"guest",
"wall");
VIR_ENUM_IMPL(virDomainTimerTickpolicy, VIR_DOMAIN_TIMER_TICKPOLICY_LAST,
"delay",
"catchup",
"merge",
"discard");
VIR_ENUM_IMPL(virDomainTimerMode, VIR_DOMAIN_TIMER_MODE_LAST,
"auto",
"native",
"emulate",
"paravirt",
"smpsafe");
VIR_ENUM_IMPL(virDomainStartupPolicy, VIR_DOMAIN_STARTUP_POLICY_LAST,
"default",
"mandatory",
"requisite",
"optional");
VIR_ENUM_IMPL(virDomainCpuPlacementMode, VIR_DOMAIN_CPU_PLACEMENT_MODE_LAST,
"static",
"auto");
VIR_ENUM_IMPL(virDomainDiskTray, VIR_DOMAIN_DISK_TRAY_LAST,
"closed",
"open");
VIR_ENUM_IMPL(virDomainRNGModel,
VIR_DOMAIN_RNG_MODEL_LAST,
"virtio");
VIR_ENUM_IMPL(virDomainRNGBackend,
VIR_DOMAIN_RNG_BACKEND_LAST,
"random",
"egd");
VIR_ENUM_IMPL(virDomainTPMModel, VIR_DOMAIN_TPM_MODEL_LAST,
"tpm-tis")
VIR_ENUM_IMPL(virDomainTPMBackend, VIR_DOMAIN_TPM_TYPE_LAST,
"passthrough")
VIR_ENUM_IMPL(virDomainDiskDiscard, VIR_DOMAIN_DISK_DISCARD_LAST,
"default",
"unmap",
"ignore")
VIR_ENUM_IMPL(virDomainDiskMirrorState, VIR_DOMAIN_DISK_MIRROR_STATE_LAST,
"none",
"yes",
"abort",
"pivot")
VIR_ENUM_IMPL(virDomainLoader,
VIR_DOMAIN_LOADER_TYPE_LAST,
"rom",
"pflash")
/* Internal mapping: subset of block job types that can be present in
* XML (remaining types are not two-phase). */
VIR_ENUM_DECL(virDomainBlockJob)
VIR_ENUM_IMPL(virDomainBlockJob, VIR_DOMAIN_BLOCK_JOB_TYPE_LAST,
"", "", "copy", "", "active-commit")
VIR_ENUM_IMPL(virDomainMemoryModel, VIR_DOMAIN_MEMORY_MODEL_LAST,
"", "dimm")
static virClassPtr virDomainObjClass;
static virClassPtr virDomainObjListClass;
static virClassPtr virDomainXMLOptionClass;
static void virDomainObjDispose(void *obj);
static void virDomainObjListDispose(void *obj);
static void virDomainXMLOptionClassDispose(void *obj);
static int virDomainObjOnceInit(void)
{
if (!(virDomainObjClass = virClassNew(virClassForObjectLockable(),
"virDomainObj",
sizeof(virDomainObj),
virDomainObjDispose)))
return -1;
if (!(virDomainObjListClass = virClassNew(virClassForObjectLockable(),
"virDomainObjList",
sizeof(virDomainObjList),
virDomainObjListDispose)))
return -1;
if (!(virDomainXMLOptionClass = virClassNew(virClassForObject(),
"virDomainXMLOption",
sizeof(virDomainXMLOption),
virDomainXMLOptionClassDispose)))
return -1;
return 0;
}
VIR_ONCE_GLOBAL_INIT(virDomainObj)
static void
virDomainXMLOptionClassDispose(void *obj)
{
virDomainXMLOptionPtr xmlopt = obj;
if (xmlopt->config.privFree)
(xmlopt->config.privFree)(xmlopt->config.priv);
}
/**
* virDomainKeyWrapCipherDefParseXML:
*
* @def Domain definition
* @node An XML cipher node
* @ctxt The XML context
*
* Parse the attributes from the cipher node and store the state
* attribute in @def.
*
* A cipher node has the form of
*
*
*
* Returns: 0 if the parse succeeded
* -1 otherwise
*/
static int
virDomainKeyWrapCipherDefParseXML(virDomainKeyWrapDefPtr keywrap,
xmlNodePtr node,
xmlXPathContextPtr ctxt)
{
char *name = NULL;
char *state = NULL;
int state_type;
int name_type;
int ret = -1;
xmlNodePtr oldnode = ctxt->node;
ctxt->node = node;
if (!(name = virXPathString("string(./@name)", ctxt))) {
virReportError(VIR_ERR_CONF_SYNTAX, "%s",
_("missing name for cipher"));
goto cleanup;
}
if ((name_type = virDomainKeyWrapCipherNameTypeFromString(name)) < 0) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("%s is not a supported cipher name"), name);
goto cleanup;
}
if (!(state = virXPathString("string(./@state)", ctxt))) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("missing state for cipher named %s"), name);
goto cleanup;
}
if ((state_type = virTristateSwitchTypeFromString(state)) < 0) {
virReportError(VIR_ERR_CONF_SYNTAX,
_("%s is not a supported cipher state"), state);
goto cleanup;
}
switch ((virDomainKeyWrapCipherName) name_type) {
case VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_AES:
if (keywrap->aes != VIR_TRISTATE_SWITCH_ABSENT) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("A domain definition can have no more than "
"one cipher node with name %s"),
virDomainKeyWrapCipherNameTypeToString(name_type));
goto cleanup;
}
keywrap->aes = state_type;
break;
case VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_DEA:
if (keywrap->dea != VIR_TRISTATE_SWITCH_ABSENT) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("A domain definition can have no more than "
"one cipher node with name %s"),
virDomainKeyWrapCipherNameTypeToString(name_type));
goto cleanup;
}
keywrap->dea = state_type;
break;
case VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_LAST:
break;
}
ret = 0;
cleanup:
VIR_FREE(name);
VIR_FREE(state);
ctxt->node = oldnode;
return ret;
}
static int
virDomainKeyWrapDefParseXML(virDomainDefPtr def, xmlXPathContextPtr ctxt)
{
size_t i;
int ret = -1;
xmlNodePtr *nodes = NULL;
int n;
if ((n = virXPathNodeSet("./keywrap/cipher", ctxt, &nodes)) < 0)
return n;
if (VIR_ALLOC(def->keywrap) < 0)
goto cleanup;
for (i = 0; i < n; i++) {
if (virDomainKeyWrapCipherDefParseXML(def->keywrap, nodes[i], ctxt) < 0)
goto cleanup;
}
if (!def->keywrap->aes &&
!def->keywrap->dea)
VIR_FREE(def->keywrap);
ret = 0;
cleanup:
if (ret < 0)
VIR_FREE(def->keywrap);
VIR_FREE(nodes);
return ret;
}
/**
* virDomainXMLOptionNew:
*
* Allocate a new domain XML configuration
*/
virDomainXMLOptionPtr
virDomainXMLOptionNew(virDomainDefParserConfigPtr config,
virDomainXMLPrivateDataCallbacksPtr priv,
virDomainXMLNamespacePtr xmlns)
{
virDomainXMLOptionPtr xmlopt;
if (virDomainObjInitialize() < 0)
return NULL;
if (!(xmlopt = virObjectNew(virDomainXMLOptionClass)))
return NULL;
if (priv)
xmlopt->privateData = *priv;
if (config)
xmlopt->config = *config;
if (xmlns)
xmlopt->ns = *xmlns;
/* Technically this forbids to use one of Xerox's MAC address prefixes in
* our hypervisor drivers. This shouldn't ever be a problem.
*
* Use the KVM prefix as default as it's in the privately administered
* range */
if (xmlopt->config.macPrefix[0] == 0 &&
xmlopt->config.macPrefix[1] == 0 &&
xmlopt->config.macPrefix[2] == 0) {
xmlopt->config.macPrefix[0] = 0x52;
xmlopt->config.macPrefix[1] = 0x54;
}
return xmlopt;
}
/**
* virDomainXMLOptionGetNamespace:
*
* @xmlopt: XML parser configuration object
*
* Returns a pointer to the stored namespace structure.
* The lifetime of the pointer is equal to @xmlopt;
*/
virDomainXMLNamespacePtr
virDomainXMLOptionGetNamespace(virDomainXMLOptionPtr xmlopt)
{
return &xmlopt->ns;
}
void
virBlkioDeviceArrayClear(virBlkioDevicePtr devices,
int ndevices)
{
size_t i;
for (i = 0; i < ndevices; i++)
VIR_FREE(devices[i].path);
}
/**
* virDomainBlkioDeviceParseXML
*
* this function parses a XML node:
*
*
* /fully/qualified/device/path
* weight
* bps
* bps
* iops
* iops
*
*
* and fills a virBlkioDevicePtr struct.
*/
static int
virDomainBlkioDeviceParseXML(xmlNodePtr root,
virBlkioDevicePtr dev)
{
char *c = NULL;
xmlNodePtr node;
node = root->children;
while (node) {
if (node->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(node->name, BAD_CAST "path") && !dev->path) {
dev->path = (char *)xmlNodeGetContent(node);
} else if (xmlStrEqual(node->name, BAD_CAST "weight")) {
c = (char *)xmlNodeGetContent(node);
if (virStrToLong_ui(c, NULL, 10, &dev->weight) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("could not parse weight %s"),
c);
goto error;
}
VIR_FREE(c);
} else if (xmlStrEqual(node->name, BAD_CAST "read_bytes_sec")) {
c = (char *)xmlNodeGetContent(node);
if (virStrToLong_ull(c, NULL, 10, &dev->rbps) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("could not parse read bytes sec %s"),
c);
goto error;
}
VIR_FREE(c);
} else if (xmlStrEqual(node->name, BAD_CAST "write_bytes_sec")) {
c = (char *)xmlNodeGetContent(node);
if (virStrToLong_ull(c, NULL, 10, &dev->wbps) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("could not parse write bytes sec %s"),
c);
goto error;
}
VIR_FREE(c);
} else if (xmlStrEqual(node->name, BAD_CAST "read_iops_sec")) {
c = (char *)xmlNodeGetContent(node);
if (virStrToLong_ui(c, NULL, 10, &dev->riops) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("could not parse read iops sec %s"),
c);
goto error;
}
VIR_FREE(c);
} else if (xmlStrEqual(node->name, BAD_CAST "write_iops_sec")) {
c = (char *)xmlNodeGetContent(node);
if (virStrToLong_ui(c, NULL, 10, &dev->wiops) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("could not parse write iops sec %s"),
c);
goto error;
}
VIR_FREE(c);
}
}
node = node->next;
}
if (!dev->path) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("missing per-device path"));
return -1;
}
return 0;
error:
VIR_FREE(c);
VIR_FREE(dev->path);
return -1;
}
/**
* virDomainDefCheckUnsupportedMemoryHotplug:
* @def: domain definition
*
* Returns -1 if the domain definition would enable memory hotplug via the
* tunable and reports an error. Otherwise returns 0.
*/
int
virDomainDefCheckUnsupportedMemoryHotplug(virDomainDefPtr def)
{
/* memory hotplug tunables are not supported by this driver */
if (def->mem.max_memory > 0 || def->mem.memory_slots > 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("memory hotplug tunables are not "
"supported by this hypervisor driver"));
return -1;
}
return 0;
}
/**
* virDomainDeviceDefCheckUnsupportedMemoryDevice:
* @dev: device definition
*
* Returns -1 if the device definition describes a memory device and reports an
* error. Otherwise returns 0.
*/
int
virDomainDeviceDefCheckUnsupportedMemoryDevice(virDomainDeviceDefPtr dev)
{
/* This driver doesn't yet know how to handle memory devices */
if (dev->type == VIR_DOMAIN_DEVICE_MEMORY) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("memory devices are not supported by this driver"));
return -1;
}
return 0;
}
virDomainObjListPtr virDomainObjListNew(void)
{
virDomainObjListPtr doms;
if (virDomainObjInitialize() < 0)
return NULL;
if (!(doms = virObjectLockableNew(virDomainObjListClass)))
return NULL;
if (!(doms->objs = virHashCreate(50, virObjectFreeHashData)) ||
!(doms->objsName = virHashCreate(50, virObjectFreeHashData))) {
virObjectUnref(doms);
return NULL;
}
return doms;
}
static void virDomainObjListDispose(void *obj)
{
virDomainObjListPtr doms = obj;
virHashFree(doms->objs);
virHashFree(doms->objsName);
}
static int virDomainObjListSearchID(const void *payload,
const void *name ATTRIBUTE_UNUSED,
const void *data)
{
virDomainObjPtr obj = (virDomainObjPtr)payload;
const int *id = data;
int want = 0;
virObjectLock(obj);
if (virDomainObjIsActive(obj) &&
obj->def->id == *id)
want = 1;
virObjectUnlock(obj);
return want;
}
virDomainObjPtr virDomainObjListFindByID(virDomainObjListPtr doms,
int id)
{
virDomainObjPtr obj;
virObjectLock(doms);
obj = virHashSearch(doms->objs, virDomainObjListSearchID, &id);
if (obj) {
virObjectLock(obj);
if (obj->removing) {
virObjectUnlock(obj);
obj = NULL;
}
}
virObjectUnlock(doms);
return obj;
}
static virDomainObjPtr
virDomainObjListFindByUUIDInternal(virDomainObjListPtr doms,
const unsigned char *uuid,
bool ref)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
virDomainObjPtr obj;
virObjectLock(doms);
virUUIDFormat(uuid, uuidstr);
obj = virHashLookup(doms->objs, uuidstr);
if (ref) {
virObjectRef(obj);
virObjectUnlock(doms);
}
if (obj) {
virObjectLock(obj);
if (obj->removing) {
virObjectUnlock(obj);
if (ref)
virObjectUnref(obj);
obj = NULL;
}
}
if (!ref)
virObjectUnlock(doms);
return obj;
}
virDomainObjPtr
virDomainObjListFindByUUID(virDomainObjListPtr doms,
const unsigned char *uuid)
{
return virDomainObjListFindByUUIDInternal(doms, uuid, false);
}
virDomainObjPtr
virDomainObjListFindByUUIDRef(virDomainObjListPtr doms,
const unsigned char *uuid)
{
return virDomainObjListFindByUUIDInternal(doms, uuid, true);
}
virDomainObjPtr virDomainObjListFindByName(virDomainObjListPtr doms,
const char *name)
{
virDomainObjPtr obj;
virObjectLock(doms);
obj = virHashLookup(doms->objsName, name);
virObjectRef(obj);
virObjectUnlock(doms);
if (obj) {
virObjectLock(obj);
if (obj->removing) {
virObjectUnlock(obj);
virObjectUnref(obj);
obj = NULL;
}
}
return obj;
}
bool virDomainObjTaint(virDomainObjPtr obj,
virDomainTaintFlags taint)
{
unsigned int flag = (1 << taint);
if (obj->taint & flag)
return false;
obj->taint |= flag;
return true;
}
static void
virDomainDeviceInfoFree(virDomainDeviceInfoPtr info)
{
if (info) {
virDomainDeviceInfoClear(info);
VIR_FREE(info);
}
}
static void
virDomainGraphicsAuthDefClear(virDomainGraphicsAuthDefPtr def)
{
if (!def)
return;
VIR_FREE(def->passwd);
/* Don't free def */
}
static void
virDomainGraphicsListenDefClear(virDomainGraphicsListenDefPtr def)
{
if (!def)
return;
VIR_FREE(def->address);
VIR_FREE(def->network);
return;
}
void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def)
{
size_t i;
if (!def)
return;
switch ((virDomainGraphicsType)def->type) {
case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
VIR_FREE(def->data.vnc.socket);
VIR_FREE(def->data.vnc.keymap);
virDomainGraphicsAuthDefClear(&def->data.vnc.auth);
break;
case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
VIR_FREE(def->data.sdl.display);
VIR_FREE(def->data.sdl.xauth);
break;
case VIR_DOMAIN_GRAPHICS_TYPE_RDP:
break;
case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP:
VIR_FREE(def->data.desktop.display);
break;
case VIR_DOMAIN_GRAPHICS_TYPE_SPICE:
VIR_FREE(def->data.spice.keymap);
virDomainGraphicsAuthDefClear(&def->data.spice.auth);
break;
case VIR_DOMAIN_GRAPHICS_TYPE_LAST:
break;
}
for (i = 0; i < def->nListens; i++)
virDomainGraphicsListenDefClear(&def->listens[i]);
VIR_FREE(def->listens);
VIR_FREE(def);
}
void virDomainInputDefFree(virDomainInputDefPtr def)
{
if (!def)
return;
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainLeaseDefFree(virDomainLeaseDefPtr def)
{
if (!def)
return;
VIR_FREE(def->lockspace);
VIR_FREE(def->key);
VIR_FREE(def->path);
VIR_FREE(def);
}
virDomainDiskDefPtr
virDomainDiskDefNew(virDomainXMLOptionPtr xmlopt)
{
virDomainDiskDefPtr ret;
if (VIR_ALLOC(ret) < 0)
return NULL;
if (VIR_ALLOC(ret->src) < 0)
goto error;
if (xmlopt &&
xmlopt->privateData.diskNew &&
!(ret->privateData = xmlopt->privateData.diskNew()))
goto error;
return ret;
error:
virDomainDiskDefFree(ret);
return NULL;
}
void
virDomainDiskDefFree(virDomainDiskDefPtr def)
{
if (!def)
return;
virStorageSourceFree(def->src);
VIR_FREE(def->serial);
VIR_FREE(def->dst);
virStorageSourceFree(def->mirror);
VIR_FREE(def->wwn);
VIR_FREE(def->vendor);
VIR_FREE(def->product);
VIR_FREE(def->domain_name);
virDomainDeviceInfoClear(&def->info);
virObjectUnref(def->privateData);
VIR_FREE(def);
}
int
virDomainDiskGetType(virDomainDiskDefPtr def)
{
return def->src->type;
}
void
virDomainDiskSetType(virDomainDiskDefPtr def, int type)
{
def->src->type = type;
}
const char *
virDomainDiskGetSource(virDomainDiskDef const *def)
{
return def->src->path;
}
int
virDomainDiskSetSource(virDomainDiskDefPtr def, const char *src)
{
int ret;
char *tmp = def->src->path;
ret = VIR_STRDUP(def->src->path, src);
if (ret < 0)
def->src->path = tmp;
else
VIR_FREE(tmp);
return ret;
}
const char *
virDomainDiskGetDriver(virDomainDiskDefPtr def)
{
return def->src->driverName;
}
int
virDomainDiskSetDriver(virDomainDiskDefPtr def, const char *name)
{
int ret;
char *tmp = def->src->driverName;
ret = VIR_STRDUP(def->src->driverName, name);
if (ret < 0)
def->src->driverName = tmp;
else
VIR_FREE(tmp);
return ret;
}
int
virDomainDiskGetFormat(virDomainDiskDefPtr def)
{
return def->src->format;
}
void
virDomainDiskSetFormat(virDomainDiskDefPtr def, int format)
{
def->src->format = format;
}
static virDomainControllerDefPtr
virDomainControllerDefNew(virDomainControllerType type)
{
virDomainControllerDefPtr def;
if (VIR_ALLOC(def) < 0)
return NULL;
def->type = type;
def->model = -1;
/* initialize anything that has a non-0 default */
switch ((virDomainControllerType) def->type) {
case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL:
def->opts.vioserial.ports = -1;
def->opts.vioserial.vectors = -1;
break;
case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
def->opts.pciopts.chassisNr = -1;
def->opts.pciopts.chassis = -1;
def->opts.pciopts.port = -1;
break;
case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
case VIR_DOMAIN_CONTROLLER_TYPE_CCID:
case VIR_DOMAIN_CONTROLLER_TYPE_USB:
case VIR_DOMAIN_CONTROLLER_TYPE_LAST:
break;
}
return def;
}
void virDomainControllerDefFree(virDomainControllerDefPtr def)
{
if (!def)
return;
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainFSDefFree(virDomainFSDefPtr def)
{
if (!def)
return;
VIR_FREE(def->src);
VIR_FREE(def->dst);
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void
virDomainActualNetDefFree(virDomainActualNetDefPtr def)
{
if (!def)
return;
switch (def->type) {
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_NETWORK:
VIR_FREE(def->data.bridge.brname);
break;
case VIR_DOMAIN_NET_TYPE_DIRECT:
VIR_FREE(def->data.direct.linkdev);
break;
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
virDomainHostdevDefClear(&def->data.hostdev.def);
break;
default:
break;
}
VIR_FREE(def->virtPortProfile);
virNetDevBandwidthFree(def->bandwidth);
virNetDevVlanClear(&def->vlan);
VIR_FREE(def);
}
void virDomainNetDefFree(virDomainNetDefPtr def)
{
size_t i;
if (!def)
return;
VIR_FREE(def->model);
switch (def->type) {
case VIR_DOMAIN_NET_TYPE_ETHERNET:
VIR_FREE(def->data.ethernet.dev);
break;
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
virDomainChrSourceDefFree(def->data.vhostuser);
break;
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
VIR_FREE(def->data.socket.address);
break;
case VIR_DOMAIN_NET_TYPE_NETWORK:
VIR_FREE(def->data.network.name);
VIR_FREE(def->data.network.portgroup);
virDomainActualNetDefFree(def->data.network.actual);
break;
case VIR_DOMAIN_NET_TYPE_BRIDGE:
VIR_FREE(def->data.bridge.brname);
break;
case VIR_DOMAIN_NET_TYPE_INTERNAL:
VIR_FREE(def->data.internal.name);
break;
case VIR_DOMAIN_NET_TYPE_DIRECT:
VIR_FREE(def->data.direct.linkdev);
break;
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
virDomainHostdevDefClear(&def->data.hostdev.def);
break;
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_LAST:
break;
}
VIR_FREE(def->backend.tap);
VIR_FREE(def->backend.vhost);
VIR_FREE(def->virtPortProfile);
VIR_FREE(def->script);
VIR_FREE(def->domain_name);
VIR_FREE(def->ifname);
VIR_FREE(def->ifname_guest);
VIR_FREE(def->ifname_guest_actual);
for (i = 0; i < def->nips; i++)
VIR_FREE(def->ips[i]);
VIR_FREE(def->ips);
for (i = 0; i < def->nroutes; i++)
virNetworkRouteDefFree(def->routes[i]);
VIR_FREE(def->routes);
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def->filter);
virNWFilterHashTableFree(def->filterparams);
virNetDevBandwidthFree(def->bandwidth);
virNetDevVlanClear(&def->vlan);
VIR_FREE(def);
}
void ATTRIBUTE_NONNULL(1)
virDomainChrSourceDefClear(virDomainChrSourceDefPtr def)
{
switch (def->type) {
case VIR_DOMAIN_CHR_TYPE_PTY:
case VIR_DOMAIN_CHR_TYPE_DEV:
case VIR_DOMAIN_CHR_TYPE_FILE:
case VIR_DOMAIN_CHR_TYPE_PIPE:
VIR_FREE(def->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_NMDM:
VIR_FREE(def->data.nmdm.master);
VIR_FREE(def->data.nmdm.slave);
break;
case VIR_DOMAIN_CHR_TYPE_UDP:
VIR_FREE(def->data.udp.bindHost);
VIR_FREE(def->data.udp.bindService);
VIR_FREE(def->data.udp.connectHost);
VIR_FREE(def->data.udp.connectService);
break;
case VIR_DOMAIN_CHR_TYPE_TCP:
VIR_FREE(def->data.tcp.host);
VIR_FREE(def->data.tcp.service);
break;
case VIR_DOMAIN_CHR_TYPE_UNIX:
VIR_FREE(def->data.nix.path);
break;
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
VIR_FREE(def->data.spiceport.channel);
break;
}
}
/* Deep copies the contents of src into dest. Return -1 and report
* error on failure. */
int
virDomainChrSourceDefCopy(virDomainChrSourceDefPtr dest,
virDomainChrSourceDefPtr src)
{
if (!dest || !src)
return -1;
virDomainChrSourceDefClear(dest);
switch (src->type) {
case VIR_DOMAIN_CHR_TYPE_PTY:
case VIR_DOMAIN_CHR_TYPE_DEV:
case VIR_DOMAIN_CHR_TYPE_FILE:
case VIR_DOMAIN_CHR_TYPE_PIPE:
if (VIR_STRDUP(dest->data.file.path, src->data.file.path) < 0)
return -1;
break;
case VIR_DOMAIN_CHR_TYPE_UDP:
if (VIR_STRDUP(dest->data.udp.bindHost, src->data.udp.bindHost) < 0)
return -1;
if (VIR_STRDUP(dest->data.udp.bindService, src->data.udp.bindService) < 0)
return -1;
if (VIR_STRDUP(dest->data.udp.connectHost, src->data.udp.connectHost) < 0)
return -1;
if (VIR_STRDUP(dest->data.udp.connectService, src->data.udp.connectService) < 0)
return -1;
break;
case VIR_DOMAIN_CHR_TYPE_TCP:
if (VIR_STRDUP(dest->data.tcp.host, src->data.tcp.host) < 0)
return -1;
if (VIR_STRDUP(dest->data.tcp.service, src->data.tcp.service) < 0)
return -1;
break;
case VIR_DOMAIN_CHR_TYPE_UNIX:
if (VIR_STRDUP(dest->data.nix.path, src->data.nix.path) < 0)
return -1;
break;
case VIR_DOMAIN_CHR_TYPE_NMDM:
if (VIR_STRDUP(dest->data.nmdm.master, src->data.nmdm.master) < 0)
return -1;
if (VIR_STRDUP(dest->data.nmdm.slave, src->data.nmdm.slave) < 0)
return -1;
break;
}
dest->type = src->type;
return 0;
}
void virDomainChrSourceDefFree(virDomainChrSourceDefPtr def)
{
if (!def)
return;
virDomainChrSourceDefClear(def);
VIR_FREE(def);
}
/* virDomainChrSourceDefIsEqual:
* @src: Source
* @tgt: Target
*
* Compares source and target if they contain
* the same information.
*/
static bool
virDomainChrSourceDefIsEqual(const virDomainChrSourceDef *src,
const virDomainChrSourceDef *tgt)
{
if (tgt->type != src->type)
return false;
switch ((virDomainChrType)src->type) {
case VIR_DOMAIN_CHR_TYPE_PTY:
case VIR_DOMAIN_CHR_TYPE_DEV:
case VIR_DOMAIN_CHR_TYPE_FILE:
case VIR_DOMAIN_CHR_TYPE_PIPE:
return STREQ_NULLABLE(src->data.file.path, tgt->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_NMDM:
return STREQ_NULLABLE(src->data.nmdm.master, tgt->data.nmdm.master) &&
STREQ_NULLABLE(src->data.nmdm.slave, tgt->data.nmdm.slave);
break;
case VIR_DOMAIN_CHR_TYPE_UDP:
return STREQ_NULLABLE(src->data.udp.bindHost, tgt->data.udp.bindHost) &&
STREQ_NULLABLE(src->data.udp.bindService, tgt->data.udp.bindService) &&
STREQ_NULLABLE(src->data.udp.connectHost, tgt->data.udp.connectHost) &&
STREQ_NULLABLE(src->data.udp.connectService, tgt->data.udp.connectService);
break;
case VIR_DOMAIN_CHR_TYPE_TCP:
return src->data.tcp.listen == tgt->data.tcp.listen &&
src->data.tcp.protocol == tgt->data.tcp.protocol &&
STREQ_NULLABLE(src->data.tcp.host, tgt->data.tcp.host) &&
STREQ_NULLABLE(src->data.tcp.service, tgt->data.tcp.service);
break;
case VIR_DOMAIN_CHR_TYPE_UNIX:
return src->data.nix.listen == tgt->data.nix.listen &&
STREQ_NULLABLE(src->data.nix.path, tgt->data.nix.path);
break;
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
return STREQ_NULLABLE(src->data.spiceport.channel,
tgt->data.spiceport.channel);
break;
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
return src->data.spicevmc == tgt->data.spicevmc;
case VIR_DOMAIN_CHR_TYPE_NULL:
case VIR_DOMAIN_CHR_TYPE_VC:
case VIR_DOMAIN_CHR_TYPE_STDIO:
case VIR_DOMAIN_CHR_TYPE_LAST:
break;
}
return true;
}
void virDomainChrDefFree(virDomainChrDefPtr def)
{
size_t i;
if (!def)
return;
switch (def->deviceType) {
case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL:
switch (def->targetType) {
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD:
VIR_FREE(def->target.addr);
break;
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO:
VIR_FREE(def->target.name);
break;
}
break;
default:
break;
}
virDomainChrSourceDefClear(&def->source);
virDomainDeviceInfoClear(&def->info);
if (def->seclabels) {
for (i = 0; i < def->nseclabels; i++)
virSecurityDeviceLabelDefFree(def->seclabels[i]);
VIR_FREE(def->seclabels);
}
VIR_FREE(def);
}
void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def)
{
size_t i;
if (!def)
return;
switch (def->type) {
case VIR_DOMAIN_SMARTCARD_TYPE_HOST:
break;
case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES:
for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++)
VIR_FREE(def->data.cert.file[i]);
VIR_FREE(def->data.cert.database);
break;
case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
virDomainChrSourceDefClear(&def->data.passthru);
break;
default:
break;
}
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainSoundCodecDefFree(virDomainSoundCodecDefPtr def)
{
if (!def)
return;
VIR_FREE(def);
}
void virDomainSoundDefFree(virDomainSoundDefPtr def)
{
if (!def)
return;
virDomainDeviceInfoClear(&def->info);
size_t i;
for (i = 0; i < def->ncodecs; i++)
virDomainSoundCodecDefFree(def->codecs[i]);
VIR_FREE(def->codecs);
VIR_FREE(def);
}
void virDomainMemballoonDefFree(virDomainMemballoonDefPtr def)
{
if (!def)
return;
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainNVRAMDefFree(virDomainNVRAMDefPtr def)
{
if (!def)
return;
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def)
{
if (!def)
return;
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainShmemDefFree(virDomainShmemDefPtr def)
{
if (!def)
return;
virDomainDeviceInfoClear(&def->info);
virDomainChrSourceDefClear(&def->server.chr);
VIR_FREE(def->name);
VIR_FREE(def);
}
void virDomainVideoDefFree(virDomainVideoDefPtr def)
{
if (!def)
return;
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def->accel);
VIR_FREE(def);
}
virDomainHostdevDefPtr virDomainHostdevDefAlloc(void)
{
virDomainHostdevDefPtr def = NULL;
if (VIR_ALLOC(def) < 0 ||
VIR_ALLOC(def->info) < 0)
VIR_FREE(def);
return def;
}
static void
virDomainHostdevSubsysSCSIiSCSIClear(virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc)
{
if (!iscsisrc)
return;
VIR_FREE(iscsisrc->path);
virStorageNetHostDefFree(iscsisrc->nhosts, iscsisrc->hosts);
virStorageAuthDefFree(iscsisrc->auth);
iscsisrc->auth = NULL;
}
void virDomainHostdevDefClear(virDomainHostdevDefPtr def)
{
size_t i;
if (!def)
return;
/* Free all resources in the hostdevdef. Currently the only
* such resource is the virDomainDeviceInfo.
*/
/* If there is a parent device object, it will handle freeing
* def->info.
*/
if (def->parent.type == VIR_DOMAIN_DEVICE_NONE)
virDomainDeviceInfoFree(def->info);
switch (def->mode) {
case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES:
switch (def->source.caps.type) {
case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE:
VIR_FREE(def->source.caps.u.storage.block);
break;
case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC:
VIR_FREE(def->source.caps.u.misc.chardev);
break;
case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET:
VIR_FREE(def->source.caps.u.net.iface);
for (i = 0; i < def->source.caps.u.net.nips; i++)
VIR_FREE(def->source.caps.u.net.ips[i]);
VIR_FREE(def->source.caps.u.net.ips);
for (i = 0; i < def->source.caps.u.net.nroutes; i++)
virNetworkRouteDefFree(def->source.caps.u.net.routes[i]);
VIR_FREE(def->source.caps.u.net.routes);
break;
}
break;
case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS:
if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
virDomainHostdevSubsysSCSIPtr scsisrc = &def->source.subsys.u.scsi;
if (scsisrc->protocol ==
VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) {
virDomainHostdevSubsysSCSIiSCSIClear(&scsisrc->u.iscsi);
} else {
VIR_FREE(scsisrc->u.host.adapter);
}
}
break;
}
}
void virDomainTPMDefFree(virDomainTPMDefPtr def)
{
if (!def)
return;
switch (def->type) {
case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
VIR_FREE(def->data.passthrough.source.data.file.path);
break;
case VIR_DOMAIN_TPM_TYPE_LAST:
break;
}
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainHostdevDefFree(virDomainHostdevDefPtr def)
{
if (!def)
return;
/* free all subordinate objects */
virDomainHostdevDefClear(def);
/* If there is a parent device object, it will handle freeing
* the memory.
*/
if (def->parent.type == VIR_DOMAIN_DEVICE_NONE)
VIR_FREE(def);
}
void virDomainHubDefFree(virDomainHubDefPtr def)
{
if (!def)
return;
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def)
{
if (!def)
return;
virDomainChrSourceDefClear(&def->source.chr);
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainRedirFilterDefFree(virDomainRedirFilterDefPtr def)
{
size_t i;
if (!def)
return;
for (i = 0; i < def->nusbdevs; i++)
VIR_FREE(def->usbdevs[i]);
VIR_FREE(def->usbdevs);
VIR_FREE(def);
}
void virDomainMemoryDefFree(virDomainMemoryDefPtr def)
{
if (!def)
return;
virBitmapFree(def->sourceNodes);
virDomainDeviceInfoClear(&def->info);
VIR_FREE(def);
}
void virDomainDeviceDefFree(virDomainDeviceDefPtr def)
{
if (!def)
return;
switch ((virDomainDeviceType) def->type) {
case VIR_DOMAIN_DEVICE_DISK:
virDomainDiskDefFree(def->data.disk);
break;
case VIR_DOMAIN_DEVICE_LEASE:
virDomainLeaseDefFree(def->data.lease);
break;
case VIR_DOMAIN_DEVICE_NET:
virDomainNetDefFree(def->data.net);
break;
case VIR_DOMAIN_DEVICE_INPUT:
virDomainInputDefFree(def->data.input);
break;
case VIR_DOMAIN_DEVICE_SOUND:
virDomainSoundDefFree(def->data.sound);
break;
case VIR_DOMAIN_DEVICE_VIDEO:
virDomainVideoDefFree(def->data.video);
break;
case VIR_DOMAIN_DEVICE_HOSTDEV:
virDomainHostdevDefFree(def->data.hostdev);
break;
case VIR_DOMAIN_DEVICE_WATCHDOG:
virDomainWatchdogDefFree(def->data.watchdog);
break;
case VIR_DOMAIN_DEVICE_CONTROLLER:
virDomainControllerDefFree(def->data.controller);
break;
case VIR_DOMAIN_DEVICE_GRAPHICS:
virDomainGraphicsDefFree(def->data.graphics);
break;
case VIR_DOMAIN_DEVICE_HUB:
virDomainHubDefFree(def->data.hub);
break;
case VIR_DOMAIN_DEVICE_REDIRDEV:
virDomainRedirdevDefFree(def->data.redirdev);
break;
case VIR_DOMAIN_DEVICE_RNG:
virDomainRNGDefFree(def->data.rng);
break;
case VIR_DOMAIN_DEVICE_CHR:
virDomainChrDefFree(def->data.chr);
break;
case VIR_DOMAIN_DEVICE_FS:
virDomainFSDefFree(def->data.fs);
break;
case VIR_DOMAIN_DEVICE_SMARTCARD:
virDomainSmartcardDefFree(def->data.smartcard);
break;
case VIR_DOMAIN_DEVICE_MEMBALLOON:
virDomainMemballoonDefFree(def->data.memballoon);
break;
case VIR_DOMAIN_DEVICE_NVRAM:
virDomainNVRAMDefFree(def->data.nvram);
break;
case VIR_DOMAIN_DEVICE_SHMEM:
virDomainShmemDefFree(def->data.shmem);
break;
case VIR_DOMAIN_DEVICE_TPM:
virDomainTPMDefFree(def->data.tpm);
break;
case VIR_DOMAIN_DEVICE_PANIC:
virDomainPanicDefFree(def->data.panic);
break;
case VIR_DOMAIN_DEVICE_MEMORY:
virDomainMemoryDefFree(def->data.memory);
break;
case VIR_DOMAIN_DEVICE_LAST:
case VIR_DOMAIN_DEVICE_NONE:
break;
}
VIR_FREE(def);
}
static void
virDomainClockDefClear(virDomainClockDefPtr def)
{
if (def->offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE)
VIR_FREE(def->data.timezone);
size_t i;
for (i = 0; i < def->ntimers; i++)
VIR_FREE(def->timers[i]);
VIR_FREE(def->timers);
}
virDomainPinDefPtr *
virDomainPinDefCopy(virDomainPinDefPtr *src, int npin)
{
size_t i;
virDomainPinDefPtr *ret = NULL;
if (VIR_ALLOC_N(ret, npin) < 0)
goto error;
for (i = 0; i < npin; i++) {
if (VIR_ALLOC(ret[i]) < 0)
goto error;
ret[i]->id = src[i]->id;
if ((ret[i]->cpumask = virBitmapNewCopy(src[i]->cpumask)) == NULL)
goto error;
}
return ret;
error:
if (ret) {
for (i = 0; i < npin; i++) {
if (ret[i]) {
virBitmapFree(ret[i]->cpumask);
VIR_FREE(ret[i]);
}
}
VIR_FREE(ret);
}
return NULL;
}
static bool
virDomainIOThreadIDArrayHasPin(virDomainDefPtr def)
{
size_t i;
for (i = 0; i < def->niothreadids; i++) {
if (def->iothreadids[i]->cpumask)
return true;
}
return false;
}
void
virDomainIOThreadIDDefFree(virDomainIOThreadIDDefPtr def)
{
if (!def)
return;
virBitmapFree(def->cpumask);
VIR_FREE(def);
}
static void
virDomainIOThreadIDDefArrayFree(virDomainIOThreadIDDefPtr *def,
int nids)
{
size_t i;
if (!def)
return;
for (i = 0; i < nids; i++)
virDomainIOThreadIDDefFree(def[i]);
VIR_FREE(def);
}
void
virDomainPinDefFree(virDomainPinDefPtr def)
{
if (def) {
virBitmapFree(def->cpumask);
VIR_FREE(def);
}
}
void
virDomainPinDefArrayFree(virDomainPinDefPtr *def,
int npin)
{
size_t i;
if (!def)
return;
for (i = 0; i < npin; i++)
virDomainPinDefFree(def[i]);
VIR_FREE(def);
}
void
virDomainResourceDefFree(virDomainResourceDefPtr resource)
{
if (!resource)
return;
VIR_FREE(resource->partition);
VIR_FREE(resource);
}
void
virDomainPanicDefFree(virDomainPanicDefPtr panic)
{
if (!panic)
return;
virDomainDeviceInfoClear(&panic->info);
VIR_FREE(panic);
}
void
virDomainLoaderDefFree(virDomainLoaderDefPtr loader)
{
if (!loader)
return;
VIR_FREE(loader->path);
VIR_FREE(loader->nvram);
VIR_FREE(loader->templt);
VIR_FREE(loader);
}
void virDomainDefFree(virDomainDefPtr def)
{
size_t i;
if (!def)
return;
virDomainResourceDefFree(def->resource);
/* hostdevs must be freed before nets (or any future "intelligent
* hostdevs") because the pointer to the hostdev is really
* pointing into the middle of the higher level device's object,
* so the original object must still be available during the call
* to virDomainHostdevDefFree().
*/
for (i = 0; i < def->nhostdevs; i++)
virDomainHostdevDefFree(def->hostdevs[i]);
VIR_FREE(def->hostdevs);
for (i = 0; i < def->nleases; i++)
virDomainLeaseDefFree(def->leases[i]);
VIR_FREE(def->leases);
for (i = 0; i < def->ngraphics; i++)
virDomainGraphicsDefFree(def->graphics[i]);
VIR_FREE(def->graphics);
for (i = 0; i < def->ninputs; i++)
virDomainInputDefFree(def->inputs[i]);
VIR_FREE(def->inputs);
for (i = 0; i < def->ndisks; i++)
virDomainDiskDefFree(def->disks[i]);
VIR_FREE(def->disks);
for (i = 0; i < def->ncontrollers; i++)
virDomainControllerDefFree(def->controllers[i]);
VIR_FREE(def->controllers);
for (i = 0; i < def->nfss; i++)
virDomainFSDefFree(def->fss[i]);
VIR_FREE(def->fss);
for (i = 0; i < def->nnets; i++)
virDomainNetDefFree(def->nets[i]);
VIR_FREE(def->nets);
for (i = 0; i < def->nsmartcards; i++)
virDomainSmartcardDefFree(def->smartcards[i]);
VIR_FREE(def->smartcards);
for (i = 0; i < def->nserials; i++)
virDomainChrDefFree(def->serials[i]);
VIR_FREE(def->serials);
for (i = 0; i < def->nparallels; i++)
virDomainChrDefFree(def->parallels[i]);
VIR_FREE(def->parallels);
for (i = 0; i < def->nchannels; i++)
virDomainChrDefFree(def->channels[i]);
VIR_FREE(def->channels);
for (i = 0; i < def->nconsoles; i++)
virDomainChrDefFree(def->consoles[i]);
VIR_FREE(def->consoles);
for (i = 0; i < def->nsounds; i++)
virDomainSoundDefFree(def->sounds[i]);
VIR_FREE(def->sounds);
for (i = 0; i < def->nvideos; i++)
virDomainVideoDefFree(def->videos[i]);
VIR_FREE(def->videos);
for (i = 0; i < def->nhubs; i++)
virDomainHubDefFree(def->hubs[i]);
VIR_FREE(def->hubs);
for (i = 0; i < def->nredirdevs; i++)
virDomainRedirdevDefFree(def->redirdevs[i]);
VIR_FREE(def->redirdevs);
for (i = 0; i < def->nrngs; i++)
virDomainRNGDefFree(def->rngs[i]);
VIR_FREE(def->rngs);
for (i = 0; i < def->nmems; i++)
virDomainMemoryDefFree(def->mems[i]);
VIR_FREE(def->mems);
virDomainTPMDefFree(def->tpm);
virDomainPanicDefFree(def->panic);
VIR_FREE(def->idmap.uidmap);
VIR_FREE(def->idmap.gidmap);
VIR_FREE(def->os.machine);
VIR_FREE(def->os.init);
for (i = 0; def->os.initargv && def->os.initargv[i]; i++)
VIR_FREE(def->os.initargv[i]);
VIR_FREE(def->os.initargv);
VIR_FREE(def->os.kernel);
VIR_FREE(def->os.initrd);
VIR_FREE(def->os.cmdline);
VIR_FREE(def->os.dtb);
VIR_FREE(def->os.root);
virDomainLoaderDefFree(def->os.loader);
VIR_FREE(def->os.bootloader);
VIR_FREE(def->os.bootloaderArgs);
virDomainClockDefClear(&def->clock);
VIR_FREE(def->name);
virBitmapFree(def->cpumask);
VIR_FREE(def->emulator);
VIR_FREE(def->description);
VIR_FREE(def->title);
virBlkioDeviceArrayClear(def->blkio.devices,
def->blkio.ndevices);
VIR_FREE(def->blkio.devices);
virDomainWatchdogDefFree(def->watchdog);
virDomainMemballoonDefFree(def->memballoon);
virDomainNVRAMDefFree(def->nvram);
for (i = 0; i < def->mem.nhugepages; i++)
virBitmapFree(def->mem.hugepages[i].nodemask);
VIR_FREE(def->mem.hugepages);
for (i = 0; i < def->nseclabels; i++)
virSecurityLabelDefFree(def->seclabels[i]);
VIR_FREE(def->seclabels);
virCPUDefFree(def->cpu);
virDomainIOThreadIDDefArrayFree(def->iothreadids, def->niothreadids);
virDomainPinDefArrayFree(def->cputune.vcpupin, def->cputune.nvcpupin);
virBitmapFree(def->cputune.emulatorpin);
for (i = 0; i < def->cputune.nvcpusched; i++)
virBitmapFree(def->cputune.vcpusched[i].ids);
VIR_FREE(def->cputune.vcpusched);
for (i = 0; i < def->cputune.niothreadsched; i++)
virBitmapFree(def->cputune.iothreadsched[i].ids);
VIR_FREE(def->cputune.iothreadsched);
virDomainNumaFree(def->numa);
virSysinfoDefFree(def->sysinfo);
virDomainRedirFilterDefFree(def->redirfilter);
for (i = 0; i < def->nshmems; i++)
virDomainShmemDefFree(def->shmems[i]);
VIR_FREE(def->shmems);
VIR_FREE(def->keywrap);
if (def->namespaceData && def->ns.free)
(def->ns.free)(def->namespaceData);
xmlFreeNode(def->metadata);
VIR_FREE(def);
}
static void virDomainObjDispose(void *obj)
{
virDomainObjPtr dom = obj;
VIR_DEBUG("obj=%p", dom);
virCondDestroy(&dom->cond);
virDomainDefFree(dom->def);
virDomainDefFree(dom->newDef);
if (dom->privateDataFreeFunc)
(dom->privateDataFreeFunc)(dom->privateData);
virDomainSnapshotObjListFree(dom->snapshots);
}
virDomainObjPtr
virDomainObjNew(virDomainXMLOptionPtr xmlopt)
{
virDomainObjPtr domain;
if (virDomainObjInitialize() < 0)
return NULL;
if (!(domain = virObjectLockableNew(virDomainObjClass)))
return NULL;
if (virCondInit(&domain->cond) < 0) {
virReportSystemError(errno, "%s",
_("failed to initialize domain condition"));
goto error;
}
if (xmlopt->privateData.alloc) {
if (!(domain->privateData = (xmlopt->privateData.alloc)()))
goto error;
domain->privateDataFreeFunc = xmlopt->privateData.free;
}
if (!(domain->snapshots = virDomainSnapshotObjListNew()))
goto error;
virObjectLock(domain);
virDomainObjSetState(domain, VIR_DOMAIN_SHUTOFF,
VIR_DOMAIN_SHUTOFF_UNKNOWN);
VIR_DEBUG("obj=%p", domain);
return domain;
error:
virObjectUnref(domain);
return NULL;
}
virDomainDefPtr
virDomainDefNew(void)
{
virDomainDefPtr ret;
if (VIR_ALLOC(ret) < 0)
return NULL;
if (!(ret->numa = virDomainNumaNew()))
goto error;
ret->mem.hard_limit = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
ret->mem.soft_limit = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
ret->mem.swap_hard_limit = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED;
return ret;
error:
virDomainDefFree(ret);
return NULL;
}
virDomainDefPtr
virDomainDefNewFull(const char *name,
const unsigned char *uuid,
int id)
{
virDomainDefPtr def;
if (!(def = virDomainDefNew()))
return NULL;
if (VIR_STRDUP(def->name, name) < 0) {
VIR_FREE(def);
return NULL;
}
memcpy(def->uuid, uuid, VIR_UUID_BUFLEN);
def->id = id;
return def;
}
void virDomainObjAssignDef(virDomainObjPtr domain,
virDomainDefPtr def,
bool live,
virDomainDefPtr *oldDef)
{
if (oldDef)
*oldDef = NULL;
if (virDomainObjIsActive(domain)) {
if (oldDef)
*oldDef = domain->newDef;
else
virDomainDefFree(domain->newDef);
domain->newDef = def;
} else {
if (live) {
/* save current configuration to be restored on domain shutdown */
if (!domain->newDef)
domain->newDef = domain->def;
else
virDomainDefFree(domain->def);
domain->def = def;
} else {
if (oldDef)
*oldDef = domain->def;
else
virDomainDefFree(domain->def);
domain->def = def;
}
}
}
/**
* virDomainObjEndAPI:
* @vm: domain object
*
* Finish working with a domain object in an API. This function
* clears whatever was left of a domain that was gathered using
* virDomainObjListFindByUUIDRef(). Currently that means only unlocking and
* decrementing the reference counter of that domain. And in order to
* make sure the caller does not access the domain, the pointer is
* cleared.
*/
void
virDomainObjEndAPI(virDomainObjPtr *vm)
{
if (!*vm)
return;
virObjectUnlock(*vm);
virObjectUnref(*vm);
*vm = NULL;
}
void
virDomainObjBroadcast(virDomainObjPtr vm)
{
virCondBroadcast(&vm->cond);
}
int
virDomainObjWait(virDomainObjPtr vm)
{
if (virCondWait(&vm->cond, &vm->parent.lock) < 0) {
virReportSystemError(errno, "%s",
_("failed to wait for domain condition"));
return -1;
}
return 0;
}
/**
* Waits for domain condition to be triggered for a specific period of time.
*
* Returns:
* -1 in case of error
* 0 on success
* 1 on timeout
*/
int
virDomainObjWaitUntil(virDomainObjPtr vm,
unsigned long long whenms)
{
if (virCondWaitUntil(&vm->cond, &vm->parent.lock, whenms) < 0) {
if (errno != ETIMEDOUT) {
virReportSystemError(errno, "%s",
_("failed to wait for domain condition"));
return -1;
}
return 1;
}
return 0;
}
/*
*
* If flags & VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE then
* this will refuse updating an existing def if the
* current def is Live
*
* If flags & VIR_DOMAIN_OBJ_LIST_ADD_LIVE then
* the @def being added is assumed to represent a
* live config, not a future inactive config
*
*/
static virDomainObjPtr
virDomainObjListAddLocked(virDomainObjListPtr doms,
virDomainDefPtr def,
virDomainXMLOptionPtr xmlopt,
unsigned int flags,
virDomainDefPtr *oldDef)
{
virDomainObjPtr vm;
char uuidstr[VIR_UUID_STRING_BUFLEN];
if (oldDef)
*oldDef = NULL;
virUUIDFormat(def->uuid, uuidstr);
/* See if a VM with matching UUID already exists */
if ((vm = virHashLookup(doms->objs, uuidstr))) {
virObjectLock(vm);
/* UUID matches, but if names don't match, refuse it */
if (STRNEQ(vm->def->name, def->name)) {
virUUIDFormat(vm->def->uuid, uuidstr);
virReportError(VIR_ERR_OPERATION_FAILED,
_("domain '%s' is already defined with uuid %s"),
vm->def->name, uuidstr);
goto error;
}
if (flags & VIR_DOMAIN_OBJ_LIST_ADD_CHECK_LIVE) {
/* UUID & name match, but if VM is already active, refuse it */
if (virDomainObjIsActive(vm)) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("domain '%s' is already active"),
vm->def->name);
goto error;
}
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("domain '%s' is already being started"),
vm->def->name);
goto error;
}
}
virDomainObjAssignDef(vm,
def,
!!(flags & VIR_DOMAIN_OBJ_LIST_ADD_LIVE),
oldDef);
} else {
/* UUID does not match, but if a name matches, refuse it */
if ((vm = virHashLookup(doms->objsName, def->name))) {
virObjectLock(vm);
virUUIDFormat(vm->def->uuid, uuidstr);
virReportError(VIR_ERR_OPERATION_FAILED,
_("domain '%s' already exists with uuid %s"),
def->name, uuidstr);
goto error;
}
if (!(vm = virDomainObjNew(xmlopt)))
goto cleanup;
vm->def = def;
virUUIDFormat(def->uuid, uuidstr);
if (virHashAddEntry(doms->objs, uuidstr, vm) < 0) {
virObjectUnref(vm);
return NULL;
}
if (virHashAddEntry(doms->objsName, def->name, vm) < 0) {
virHashRemoveEntry(doms->objs, uuidstr);
return NULL;
}
/* Since domain is in two hash tables, increment the
* reference counter */
virObjectRef(vm);
}
cleanup:
return vm;
error:
virObjectUnlock(vm);
vm = NULL;
goto cleanup;
}
virDomainObjPtr virDomainObjListAdd(virDomainObjListPtr doms,
virDomainDefPtr def,
virDomainXMLOptionPtr xmlopt,
unsigned int flags,
virDomainDefPtr *oldDef)
{
virDomainObjPtr ret;
virObjectLock(doms);
ret = virDomainObjListAddLocked(doms, def, xmlopt, flags, oldDef);
virObjectUnlock(doms);
return ret;
}
/*
* Mark the running VM config as transient. Ensures transient hotplug
* operations do not persist past shutdown.
*
* @param caps pointer to capabilities info
* @param xmlopt pointer to XML parser configuration object
* @param domain domain object pointer
* @param live if true, run this operation even for an inactive domain.
* this allows freely updated domain->def with runtime defaults before
* starting the VM, which will be discarded on VM shutdown. Any cleanup
* paths need to be sure to handle newDef if the domain is never started.
* @return 0 on success, -1 on failure
*/
int
virDomainObjSetDefTransient(virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
virDomainObjPtr domain,
bool live)
{
int ret = -1;
if (!virDomainObjIsActive(domain) && !live)
return 0;
if (!domain->persistent)
return 0;
if (domain->newDef)
return 0;
if (!(domain->newDef = virDomainDefCopy(domain->def, caps, xmlopt, false)))
goto out;
ret = 0;
out:
return ret;
}
/*
* Return the persistent domain configuration. If domain is transient,
* return the running config.
*
* @param caps pointer to capabilities info
* @param xmlopt pointer to XML parser configuration object
* @param domain domain object pointer
* @return NULL on error, virDOmainDefPtr on success
*/
virDomainDefPtr
virDomainObjGetPersistentDef(virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
virDomainObjPtr domain)
{
if (virDomainObjSetDefTransient(caps, xmlopt, domain, false) < 0)
return NULL;
if (domain->newDef)
return domain->newDef;
else
return domain->def;
}
/**
* virDomainObjUpdateModificationImpact:
*
* @vm: domain object
* @flags: flags to update the modification impact on
*
* Resolves virDomainModificationImpact flags in @flags so that they correctly
* apply to the actual state of @vm. @flags may be modified after call to this
* function.
*
* Returns 0 on success if @flags point to a valid combination for @vm or -1 on
* error.
*/
int
virDomainObjUpdateModificationImpact(virDomainObjPtr vm,
unsigned int *flags)
{
bool isActive = virDomainObjIsActive(vm);
if ((*flags & (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG)) ==
VIR_DOMAIN_AFFECT_CURRENT) {
if (isActive)
*flags |= VIR_DOMAIN_AFFECT_LIVE;
else
*flags |= VIR_DOMAIN_AFFECT_CONFIG;
}
if (!isActive && (*flags & VIR_DOMAIN_AFFECT_LIVE)) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("domain is not running"));
return -1;
}
if (*flags & VIR_DOMAIN_AFFECT_CONFIG) {
if (!vm->persistent) {
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
_("transient domains do not have any "
"persistent config"));
return -1;
}
}
return 0;
}
/*
* Helper method for --current, --live, and --config options, and check
* whether domain is active or can get persistent domain configuration.
*
* Return 0 if success, also change the flags and get the persistent
* domain configuration if needed. Return -1 on error.
*/
int
virDomainLiveConfigHelperMethod(virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
virDomainObjPtr dom,
unsigned int *flags,
virDomainDefPtr *persistentDef)
{
if (virDomainObjUpdateModificationImpact(dom, flags) < 0)
return -1;
if (*flags & VIR_DOMAIN_AFFECT_CONFIG) {
if (!(*persistentDef = virDomainObjGetPersistentDef(caps, xmlopt, dom))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Get persistent config failed"));
return -1;
}
}
return 0;
}
/**
* virDomainObjGetDefs:
*
* @vm: domain object
* @flags: for virDomainModificationImpact
* @liveDef: Set to the pointer to the live definition of @vm.
* @persDef: Set to the pointer to the config definition of @vm.
*
* Helper function to resolve @flags and retrieve correct domain pointer
* objects. This function should be used only when the hypervisor driver always
* creates vm->newDef once the vm is started. (qemu driver does that)
*
* If @liveDef or @persDef are set it implies that @flags request modification
* of thereof.
*
* Returns 0 on success and sets @liveDef and @persDef; -1 if @flags are
* inappropriate.
*/
int
virDomainObjGetDefs(virDomainObjPtr vm,
unsigned int flags,
virDomainDefPtr *liveDef,
virDomainDefPtr *persDef)
{
if (liveDef)
*liveDef = NULL;
if (persDef)
*persDef = NULL;
if (virDomainObjUpdateModificationImpact(vm, &flags) < 0)
return -1;
if (virDomainObjIsActive(vm)) {
if (liveDef && (flags & VIR_DOMAIN_AFFECT_LIVE))
*liveDef = vm->def;
if (persDef && (flags & VIR_DOMAIN_AFFECT_CONFIG))
*persDef = vm->newDef;
} else {
if (persDef)
*persDef = vm->def;
}
return 0;
}
/**
* virDomainObjGetOneDef:
*
* @vm: Domain object
* @flags: for virDomainModificationImpact
*
* Helper function to resolve @flags and return the correct domain pointer
* object. This function returns one of @vm->def or @vm->persistentDef
* according to @flags. This helper should be used only in APIs that guarantee
* that @flags contains exactly one of VIR_DOMAIN_AFFECT_LIVE or
* VIR_DOMAIN_AFFECT_CONFIG and not both.
*
* Returns the correct definition pointer or NULL on error.
*/
virDomainDefPtr
virDomainObjGetOneDef(virDomainObjPtr vm,
unsigned int flags)
{
if (flags & VIR_DOMAIN_AFFECT_LIVE && flags & VIR_DOMAIN_AFFECT_CONFIG) {
virReportInvalidArg(ctl, "%s",
_("Flags 'VIR_DOMAIN_AFFECT_LIVE' and "
"'VIR_DOMAIN_AFFECT_CONFIG' are mutually "
"exclusive"));
return NULL;
}
if (virDomainObjUpdateModificationImpact(vm, &flags) < 0)
return NULL;
if (virDomainObjIsActive(vm) && flags & VIR_DOMAIN_AFFECT_CONFIG)
return vm->newDef;
else
return vm->def;
}
/*
* The caller must hold a lock on the driver owning 'doms',
* and must also have locked 'dom', to ensure no one else
* is either waiting for 'dom' or still using it
*/
void virDomainObjListRemove(virDomainObjListPtr doms,
virDomainObjPtr dom)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
dom->removing = true;
virUUIDFormat(dom->def->uuid, uuidstr);
virObjectRef(dom);
virObjectUnlock(dom);
virObjectLock(doms);
virObjectLock(dom);
virHashRemoveEntry(doms->objs, uuidstr);
virHashRemoveEntry(doms->objsName, dom->def->name);
virObjectUnlock(dom);
virObjectUnref(dom);
virObjectUnlock(doms);
}
/* The caller must hold lock on 'doms' in addition to 'virDomainObjListRemove'
* requirements
*
* Can be used to remove current element while iterating with
* virDomainObjListForEach
*/
void virDomainObjListRemoveLocked(virDomainObjListPtr doms,
virDomainObjPtr dom)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
virUUIDFormat(dom->def->uuid, uuidstr);
virHashRemoveEntry(doms->objs, uuidstr);
virHashRemoveEntry(doms->objsName, dom->def->name);
virObjectUnlock(dom);
}
static int
virDomainDeviceCCWAddressIsValid(virDomainDeviceCCWAddressPtr addr)
{
return addr->cssid <= VIR_DOMAIN_DEVICE_CCW_MAX_CSSID &&
addr->ssid <= VIR_DOMAIN_DEVICE_CCW_MAX_SSID &&
addr->devno <= VIR_DOMAIN_DEVICE_CCW_MAX_DEVNO;
}
int virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
int type)
{
if (info->type != type)
return 0;
switch (info->type) {
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
return virDevicePCIAddressIsValid(&info->addr.pci, false);
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
return 1;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
return 1;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
return virDomainDeviceCCWAddressIsValid(&info->addr.ccw);
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
return 1;
}
return 0;
}
virDomainDeviceInfoPtr
virDomainDeviceGetInfo(virDomainDeviceDefPtr device)
{
switch ((virDomainDeviceType) device->type) {
case VIR_DOMAIN_DEVICE_DISK:
return &device->data.disk->info;
case VIR_DOMAIN_DEVICE_FS:
return &device->data.fs->info;
case VIR_DOMAIN_DEVICE_NET:
return &device->data.net->info;
case VIR_DOMAIN_DEVICE_INPUT:
return &device->data.input->info;
case VIR_DOMAIN_DEVICE_SOUND:
return &device->data.sound->info;
case VIR_DOMAIN_DEVICE_VIDEO:
return &device->data.video->info;
case VIR_DOMAIN_DEVICE_HOSTDEV:
return device->data.hostdev->info;
case VIR_DOMAIN_DEVICE_WATCHDOG:
return &device->data.watchdog->info;
case VIR_DOMAIN_DEVICE_CONTROLLER:
return &device->data.controller->info;
case VIR_DOMAIN_DEVICE_HUB:
return &device->data.hub->info;
case VIR_DOMAIN_DEVICE_REDIRDEV:
return &device->data.redirdev->info;
case VIR_DOMAIN_DEVICE_SMARTCARD:
return &device->data.smartcard->info;
case VIR_DOMAIN_DEVICE_CHR:
return &device->data.chr->info;
case VIR_DOMAIN_DEVICE_MEMBALLOON:
return &device->data.memballoon->info;
case VIR_DOMAIN_DEVICE_NVRAM:
return &device->data.nvram->info;
case VIR_DOMAIN_DEVICE_SHMEM:
return &device->data.shmem->info;
case VIR_DOMAIN_DEVICE_RNG:
return &device->data.rng->info;
case VIR_DOMAIN_DEVICE_TPM:
return &device->data.tpm->info;
case VIR_DOMAIN_DEVICE_PANIC:
return &device->data.panic->info;
case VIR_DOMAIN_DEVICE_MEMORY:
return &device->data.memory->info;
/* The following devices do not contain virDomainDeviceInfo */
case VIR_DOMAIN_DEVICE_LEASE:
case VIR_DOMAIN_DEVICE_GRAPHICS:
case VIR_DOMAIN_DEVICE_LAST:
case VIR_DOMAIN_DEVICE_NONE:
break;
}
return NULL;
}
static bool
virDomainDeviceInfoNeedsFormat(virDomainDeviceInfoPtr info, unsigned int flags)
{
if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
return true;
if (info->alias && !(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE))
return true;
if (info->mastertype != VIR_DOMAIN_CONTROLLER_MASTER_NONE)
return true;
if ((info->rombar != VIR_TRISTATE_SWITCH_ABSENT) ||
info->romfile)
return true;
if (info->bootIndex)
return true;
return false;
}
static bool
virDomainDeviceInfoAddressIsEqual(const virDomainDeviceInfo *a,
const virDomainDeviceInfo *b)
{
if (a->type != b->type)
return false;
switch ((virDomainDeviceAddressType) a->type) {
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE:
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST:
/* address types below don't have any specific data */
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
/* the 'multi' field shouldn't be checked */
if (a->addr.pci.domain != b->addr.pci.domain ||
a->addr.pci.bus != b->addr.pci.bus ||
a->addr.pci.slot != b->addr.pci.slot ||
a->addr.pci.function != b->addr.pci.function)
return false;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
if (memcmp(&a->addr.drive, &b->addr.drive, sizeof(a->addr.drive)))
return false;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL:
if (memcmp(&a->addr.vioserial, &b->addr.vioserial, sizeof(a->addr.vioserial)))
return false;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID:
if (memcmp(&a->addr.ccid, &b->addr.ccid, sizeof(a->addr.ccid)))
return false;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
if (memcmp(&a->addr.usb, &b->addr.usb, sizeof(a->addr.usb)))
return false;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO:
if (memcmp(&a->addr.spaprvio, &b->addr.spaprvio, sizeof(a->addr.spaprvio)))
return false;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
/* the 'assigned' field denotes that the address was generated */
if (a->addr.ccw.cssid != b->addr.ccw.cssid ||
a->addr.ccw.ssid != b->addr.ccw.ssid ||
a->addr.ccw.devno != b->addr.ccw.devno)
return false;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA:
if (memcmp(&a->addr.isa, &b->addr.isa, sizeof(a->addr.isa)))
return false;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM:
if (memcmp(&a->addr.dimm, &b->addr.dimm, sizeof(a->addr.dimm)))
return false;
break;
}
return true;
}
static int
virDomainDefHasDeviceAddressIterator(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr dev ATTRIBUTE_UNUSED,
virDomainDeviceInfoPtr info,
void *opaque)
{
virDomainDeviceInfoPtr needle = opaque;
/* break iteration if the info was found */
if (virDomainDeviceInfoAddressIsEqual(info, needle))
return -1;
return 0;
}
int
virDomainDeviceInfoCopy(virDomainDeviceInfoPtr dst,
virDomainDeviceInfoPtr src)
{
/* Assume that dst is already cleared */
/* first a shallow copy of *everything* */
*dst = *src;
/* then redo the two fields that are pointers */
dst->alias = NULL;
dst->romfile = NULL;
if (VIR_STRDUP(dst->alias, src->alias) < 0 ||
VIR_STRDUP(dst->romfile, src->romfile) < 0)
return -1;
return 0;
}
void virDomainDeviceInfoClear(virDomainDeviceInfoPtr info)
{
VIR_FREE(info->alias);
if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB)
VIR_FREE(info->addr.usb.port);
memset(&info->addr, 0, sizeof(info->addr));
info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE;
VIR_FREE(info->romfile);
}
static int virDomainDeviceInfoClearAlias(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
virDomainDeviceInfoPtr info,
void *opaque ATTRIBUTE_UNUSED)
{
VIR_FREE(info->alias);
return 0;
}
static int virDomainDeviceInfoClearPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
virDomainDeviceInfoPtr info,
void *opaque ATTRIBUTE_UNUSED)
{
if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
memset(&info->addr, 0, sizeof(info->addr));
info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE;
}
return 0;
}
static int
virDomainDeviceInfoClearCCWAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
virDomainDeviceInfoPtr info,
void *opaque ATTRIBUTE_UNUSED)
{
if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
memset(&info->addr, 0, sizeof(info->addr));
info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE;
}
return 0;
}
static int
virDomainDeviceInfoIterateInternal(virDomainDefPtr def,
virDomainDeviceInfoCallback cb,
bool all,
void *opaque)
{
size_t i;
virDomainDeviceDef device;
device.type = VIR_DOMAIN_DEVICE_DISK;
for (i = 0; i < def->ndisks; i++) {
device.data.disk = def->disks[i];
if (cb(def, &device, &def->disks[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_NET;
for (i = 0; i < def->nnets; i++) {
device.data.net = def->nets[i];
if (cb(def, &device, &def->nets[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_SOUND;
for (i = 0; i < def->nsounds; i++) {
device.data.sound = def->sounds[i];
if (cb(def, &device, &def->sounds[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_HOSTDEV;
for (i = 0; i < def->nhostdevs; i++) {
device.data.hostdev = def->hostdevs[i];
if (cb(def, &device, def->hostdevs[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_VIDEO;
for (i = 0; i < def->nvideos; i++) {
device.data.video = def->videos[i];
if (cb(def, &device, &def->videos[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_CONTROLLER;
for (i = 0; i < def->ncontrollers; i++) {
device.data.controller = def->controllers[i];
if (cb(def, &device, &def->controllers[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_SMARTCARD;
for (i = 0; i < def->nsmartcards; i++) {
device.data.smartcard = def->smartcards[i];
if (cb(def, &device, &def->smartcards[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_CHR;
for (i = 0; i < def->nserials; i++) {
device.data.chr = def->serials[i];
if (cb(def, &device, &def->serials[i]->info, opaque) < 0)
return -1;
}
for (i = 0; i < def->nparallels; i++) {
device.data.chr = def->parallels[i];
if (cb(def, &device, &def->parallels[i]->info, opaque) < 0)
return -1;
}
for (i = 0; i < def->nchannels; i++) {
device.data.chr = def->channels[i];
if (cb(def, &device, &def->channels[i]->info, opaque) < 0)
return -1;
}
for (i = 0; i < def->nconsoles; i++) {
if (!all &&
i == 0 &&
(def->consoles[i]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL ||
def->consoles[i]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE) &&
def->os.type == VIR_DOMAIN_OSTYPE_HVM)
continue;
device.data.chr = def->consoles[i];
if (cb(def, &device, &def->consoles[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_INPUT;
for (i = 0; i < def->ninputs; i++) {
device.data.input = def->inputs[i];
if (cb(def, &device, &def->inputs[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_FS;
for (i = 0; i < def->nfss; i++) {
device.data.fs = def->fss[i];
if (cb(def, &device, &def->fss[i]->info, opaque) < 0)
return -1;
}
if (def->watchdog) {
device.type = VIR_DOMAIN_DEVICE_WATCHDOG;
device.data.watchdog = def->watchdog;
if (cb(def, &device, &def->watchdog->info, opaque) < 0)
return -1;
}
if (def->memballoon) {
device.type = VIR_DOMAIN_DEVICE_MEMBALLOON;
device.data.memballoon = def->memballoon;
if (cb(def, &device, &def->memballoon->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_RNG;
for (i = 0; i < def->nrngs; i++) {
device.data.rng = def->rngs[i];
if (cb(def, &device, &def->rngs[i]->info, opaque) < 0)
return -1;
}
if (def->nvram) {
device.type = VIR_DOMAIN_DEVICE_NVRAM;
device.data.nvram = def->nvram;
if (cb(def, &device, &def->nvram->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_HUB;
for (i = 0; i < def->nhubs; i++) {
device.data.hub = def->hubs[i];
if (cb(def, &device, &def->hubs[i]->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_SHMEM;
for (i = 0; i < def->nshmems; i++) {
device.data.shmem = def->shmems[i];
if (cb(def, &device, &def->shmems[i]->info, opaque) < 0)
return -1;
}
if (def->tpm) {
device.type = VIR_DOMAIN_DEVICE_TPM;
device.data.tpm = def->tpm;
if (cb(def, &device, &def->tpm->info, opaque) < 0)
return -1;
}
if (def->panic) {
device.type = VIR_DOMAIN_DEVICE_PANIC;
device.data.panic = def->panic;
if (cb(def, &device, &def->panic->info, opaque) < 0)
return -1;
}
device.type = VIR_DOMAIN_DEVICE_MEMORY;
for (i = 0; i < def->nmems; i++) {
device.data.memory = def->mems[i];
if (cb(def, &device, &def->mems[i]->info, opaque) < 0)
return -1;
}
/* Coverity is not very happy with this - all dead_error_condition */
#if !STATIC_ANALYSIS
/* This switch statement is here to trigger compiler warning when adding
* a new device type. When you are adding a new field to the switch you
* also have to add an iteration statement above. Otherwise the switch
* statement has no real function here and should be optimized out by the
* compiler. */
i = VIR_DOMAIN_DEVICE_LAST;
switch ((virDomainDeviceType) i) {
case VIR_DOMAIN_DEVICE_DISK:
case VIR_DOMAIN_DEVICE_LEASE:
case VIR_DOMAIN_DEVICE_FS:
case VIR_DOMAIN_DEVICE_NET:
case VIR_DOMAIN_DEVICE_INPUT:
case VIR_DOMAIN_DEVICE_SOUND:
case VIR_DOMAIN_DEVICE_VIDEO:
case VIR_DOMAIN_DEVICE_HOSTDEV:
case VIR_DOMAIN_DEVICE_WATCHDOG:
case VIR_DOMAIN_DEVICE_CONTROLLER:
case VIR_DOMAIN_DEVICE_GRAPHICS:
case VIR_DOMAIN_DEVICE_HUB:
case VIR_DOMAIN_DEVICE_REDIRDEV:
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_SMARTCARD:
case VIR_DOMAIN_DEVICE_CHR:
case VIR_DOMAIN_DEVICE_MEMBALLOON:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_SHMEM:
case VIR_DOMAIN_DEVICE_TPM:
case VIR_DOMAIN_DEVICE_PANIC:
case VIR_DOMAIN_DEVICE_LAST:
case VIR_DOMAIN_DEVICE_RNG:
case VIR_DOMAIN_DEVICE_MEMORY:
break;
}
#endif
return 0;
}
int
virDomainDeviceInfoIterate(virDomainDefPtr def,
virDomainDeviceInfoCallback cb,
void *opaque)
{
return virDomainDeviceInfoIterateInternal(def, cb, false, opaque);
}
bool
virDomainDefHasDeviceAddress(virDomainDefPtr def,
virDomainDeviceInfoPtr info)
{
if (virDomainDeviceInfoIterateInternal(def,
virDomainDefHasDeviceAddressIterator,
true,
info) < 0)
return true;
return false;
}
static int
virDomainDefRejectDuplicateControllers(virDomainDefPtr def)
{
int max_idx[VIR_DOMAIN_CONTROLLER_TYPE_LAST];
virBitmapPtr bitmaps[VIR_DOMAIN_CONTROLLER_TYPE_LAST] = { NULL };
virDomainControllerDefPtr cont;
size_t nbitmaps = 0;
int ret = -1;
size_t i;
memset(max_idx, -1, sizeof(max_idx));
for (i = 0; i < def->ncontrollers; i++) {
cont = def->controllers[i];
if ((int) cont->idx > max_idx[cont->type])
max_idx[cont->type] = cont->idx;
}
/* multiple USB controllers with the same index are allowed */
max_idx[VIR_DOMAIN_CONTROLLER_TYPE_USB] = -1;
for (i = 0; i < VIR_DOMAIN_CONTROLLER_TYPE_LAST; i++) {
if (max_idx[i] >= 0 && !(bitmaps[i] = virBitmapNew(max_idx[i] + 1)))
goto cleanup;
nbitmaps++;
}
for (i = 0; i < def->ncontrollers; i++) {
cont = def->controllers[i];
if (max_idx[cont->type] == -1)
continue;
if (virBitmapIsBitSet(bitmaps[cont->type], cont->idx)) {
virReportError(VIR_ERR_XML_ERROR,
_("Multiple '%s' controllers with index '%d'"),
virDomainControllerTypeToString(cont->type),
cont->idx);
goto cleanup;
}
ignore_value(virBitmapSetBit(bitmaps[cont->type], cont->idx));
}
ret = 0;
cleanup:
for (i = 0; i < nbitmaps; i++)
virBitmapFree(bitmaps[i]);
return ret;
}
/**
* virDomainDefRemoveDuplicateMetadata:
* @def: Remove duplicate metadata for this def
*
* This function removes metadata elements in @def that share the namespace.
* The first metadata entry of every duplicate namespace is kept.
*/
static void
virDomainDefRemoveDuplicateMetadata(virDomainDefPtr def)
{
xmlNodePtr child;
xmlNodePtr next;
if (!def || !def->metadata)
return;
for (child = def->metadata->children; child; child = child->next) {
/* check that every other child of @root doesn't share the namespace of
* the current one and delete them possibly */
next = child->next;
while (next) {
xmlNodePtr dupl = NULL;
if (child->ns && next->ns &&
STREQ_NULLABLE((const char *) child->ns->href,
(const char *) next->ns->href))
dupl = next;
next = next->next;
if (dupl) {
xmlUnlinkNode(dupl);
xmlFreeNode(dupl);
}
}
}
}
static int
virDomainDefPostParseInternal(virDomainDefPtr def,
virCapsPtr caps ATTRIBUTE_UNUSED)
{
size_t i;
/* verify init path for container based domains */
if (def->os.type == VIR_DOMAIN_OSTYPE_EXE && !def->os.init) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("init binary must be specified"));
return -1;
}
if (virDomainDefGetMemoryInitial(def) == 0) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Memory size must be specified via or in the "
" configuration"));
return -1;
}
if (def->mem.cur_balloon > virDomainDefGetMemoryActual(def) ||
def->mem.cur_balloon == 0)
def->mem.cur_balloon = virDomainDefGetMemoryActual(def);
if ((def->mem.max_memory || def->mem.memory_slots) &&
!(def->mem.max_memory && def->mem.memory_slots)) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("both maximum memory size and "
"memory slot count must be specified"));
return -1;
}
if (def->mem.max_memory &&
def->mem.max_memory < virDomainDefGetMemoryActual(def)) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("maximum memory size must be equal or greater than "
"the actual memory size"));
return -1;
}
/*
* Some really crazy backcompat stuff for consoles
*
* Historically the first (and only) '' element in an HVM guest
* was treated as being an alias for a device.
*
* So if we see that this console device should be a serial device, then we
* move the config over to def->serials[0] (or discard it if that already
* exists). However, given console can already be filled with aliased data
* of def->serials[0]. Keep it then.
*
* We then fill def->consoles[0] with a stub just so we get sequencing
* correct for consoles > 0
*/
/* Only the first console (if there are any) can be of type serial,
* verify that no other console is of type serial
*/
for (i = 1; i < def->nconsoles; i++) {
virDomainChrDefPtr cons = def->consoles[i];
if (cons->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Only the first console can be a serial port"));
return -1;
}
}
if (def->nconsoles > 0 && def->os.type == VIR_DOMAIN_OSTYPE_HVM &&
(def->consoles[0]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL ||
def->consoles[0]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE)) {
/* If there isn't a corresponding serial port:
* - create one and set, the console to be an alias for it
*
* If there is a corresponding serial port:
* - Check if the source definition is equal:
* - if yes: leave it as-is
* - if no: change the console to be alias of the serial port
*/
/* create the serial port definition from the console definition */
if (def->nserials == 0) {
if (VIR_APPEND_ELEMENT(def->serials,
def->nserials,
def->consoles[0]) < 0)
return -1;
/* modify it to be a serial port */
def->serials[0]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
def->serials[0]->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA;
def->serials[0]->target.port = 0;
} else {
/* if the console source doesn't match */
if (!virDomainChrSourceDefIsEqual(&def->serials[0]->source,
&def->consoles[0]->source)) {
virDomainChrDefFree(def->consoles[0]);
def->consoles[0] = NULL;
}
}
if (!def->consoles[0]) {
/* allocate a new console type for the stolen one */
if (VIR_ALLOC(def->consoles[0]) < 0)
return -1;
/* Create an console alias for the serial port */
def->consoles[0]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE;
def->consoles[0]->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
}
} else if (def->os.type == VIR_DOMAIN_OSTYPE_HVM && def->nserials > 0 &&
def->serials[0]->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
def->serials[0]->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA) {
/* Create a stub console to match the serial port.
* console[0] either does not exist
* or has a different type than SERIAL or NONE.
*/
virDomainChrDefPtr chr;
if (VIR_ALLOC(chr) < 0)
return -1;
if (VIR_INSERT_ELEMENT(def->consoles,
0,
def->nconsoles,
chr) < 0) {
VIR_FREE(chr);
return -1;
}
def->consoles[0]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE;
def->consoles[0]->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
}
if (virDomainDefRejectDuplicateControllers(def) < 0)
return -1;
/* verify settings of guest timers */
for (i = 0; i < def->clock.ntimers; i++) {
virDomainTimerDefPtr timer = def->clock.timers[i];
if (timer->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK ||
timer->name == VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK) {
if (timer->tickpolicy != -1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("timer %s doesn't support setting of "
"timer tickpolicy"),
virDomainTimerNameTypeToString(timer->name));
return -1;
}
}
if (timer->tickpolicy != VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP &&
(timer->catchup.threshold != 0 ||
timer->catchup.limit != 0 ||
timer->catchup.slew != 0)) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("setting of timer catchup policies is only "
"supported with tickpolicy='catchup'"));
return -1;
}
if (timer->name != VIR_DOMAIN_TIMER_NAME_TSC) {
if (timer->frequency != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("timer %s doesn't support setting of "
"timer frequency"),
virDomainTimerNameTypeToString(timer->name));
return -1;
}
if (timer->mode != -1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("timer %s doesn't support setting of "
"timer mode"),
virDomainTimerNameTypeToString(timer->name));
return -1;
}
}
if (timer->name != VIR_DOMAIN_TIMER_NAME_PLATFORM &&
timer->name != VIR_DOMAIN_TIMER_NAME_RTC) {
if (timer->track != -1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("timer %s doesn't support setting of "
"timer track"),
virDomainTimerNameTypeToString(timer->name));
return -1;
}
}
}
/* clean up possibly duplicated metadata entries */
virDomainDefRemoveDuplicateMetadata(def);
return 0;
}
/* Check if a drive type address $controller:$bus:$target:$unit is already
* taken by a disk or not.
*/
static bool
virDomainDriveAddressIsUsedByDisk(const virDomainDef *def,
virDomainDiskBus type,
unsigned int controller,
unsigned int bus,
unsigned int target,
unsigned int unit)
{
virDomainDiskDefPtr disk;
size_t i;
for (i = 0; i < def->ndisks; i++) {
disk = def->disks[i];
if (disk->bus != type ||
disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE)
continue;
if (disk->info.addr.drive.controller == controller &&
disk->info.addr.drive.unit == unit &&
disk->info.addr.drive.bus == bus &&
disk->info.addr.drive.target == target)
return true;
}
return false;
}
/* Check if a drive type address $controller:$target:$bus:$unit is already
* taken by a host device or not.
*/
static bool
virDomainDriveAddressIsUsedByHostdev(const virDomainDef *def,
virDomainHostdevSubsysType type,
unsigned int controller,
unsigned int bus,
unsigned int target,
unsigned int unit)
{
virDomainHostdevDefPtr hostdev;
size_t i;
for (i = 0; i < def->nhostdevs; i++) {
hostdev = def->hostdevs[i];
if (hostdev->source.subsys.type != type ||
hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE)
continue;
if (hostdev->info->addr.drive.controller == controller &&
hostdev->info->addr.drive.unit == unit &&
hostdev->info->addr.drive.bus == bus &&
hostdev->info->addr.drive.target == target)
return true;
}
return false;
}
static bool
virDomainSCSIDriveAddressIsUsed(const virDomainDef *def,
unsigned int controller,
unsigned int bus,
unsigned int target,
unsigned int unit)
{
/* In current implementation, the maximum unit number of a controller
* is either 16 or 7 (narrow SCSI bus), and if the maximum unit number
* is 16, the controller itself is on unit 7 */
if (unit == 7)
return true;
if (virDomainDriveAddressIsUsedByDisk(def, VIR_DOMAIN_DISK_BUS_SCSI,
controller, bus, target, unit) ||
virDomainDriveAddressIsUsedByHostdev(def,
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI,
controller, bus, target, unit))
return true;
return false;
}
/* Find out the next usable "unit" of a specific controller */
static int
virDomainControllerSCSINextUnit(const virDomainDef *def,
unsigned int max_unit,
unsigned int controller)
{
size_t i;
for (i = 0; i < max_unit; i++) {
/* Default to assigning addresses using bus = target = 0 */
if (!virDomainSCSIDriveAddressIsUsed(def, controller, 0, 0, i))
return i;
}
return -1;
}
#define SCSI_WIDE_BUS_MAX_CONT_UNIT 16
#define SCSI_NARROW_BUS_MAX_CONT_UNIT 7
static int
virDomainHostdevAssignAddress(virDomainXMLOptionPtr xmlopt,
const virDomainDef *def,
virDomainHostdevDefPtr hostdev)
{
int next_unit = 0;
unsigned controller = 0;
size_t i;
int ret = -1;
for (i = 0; i < def->ncontrollers; i++) {
if (def->controllers[i]->type != VIR_DOMAIN_CONTROLLER_TYPE_SCSI)
continue;
controller++;
ret = virDomainControllerSCSINextUnit(def,
xmlopt->config.hasWideSCSIBus ?
SCSI_WIDE_BUS_MAX_CONT_UNIT :
SCSI_NARROW_BUS_MAX_CONT_UNIT,
def->controllers[i]->idx);
if (ret >= 0) {
next_unit = ret;
controller = def->controllers[i]->idx;
break;
}
}
/* If failed to find any VIR_DOMAIN_CONTROLLER_TYPE_SCSI or any space
* on existing VIR_DOMAIN_CONTROLLER_TYPE_SCSI controller(s), then
* try to add a new controller resulting in placement of this entry
* as unit=0
*/
if (ret == -1 &&
virDomainDefMaybeAddController((virDomainDefPtr) def,
VIR_DOMAIN_CONTROLLER_TYPE_SCSI,
controller, -1) < 0)
return -1;
hostdev->info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE;
hostdev->info->addr.drive.controller = controller;
hostdev->info->addr.drive.bus = 0;
hostdev->info->addr.drive.target = 0;
hostdev->info->addr.drive.unit = next_unit;
return 0;
}
static int
virDomainDeviceDefPostParseInternal(virDomainDeviceDefPtr dev,
const virDomainDef *def,
virCapsPtr caps ATTRIBUTE_UNUSED,
virDomainXMLOptionPtr xmlopt)
{
if (dev->type == VIR_DOMAIN_DEVICE_CHR) {
virDomainChrDefPtr chr = dev->data.chr;
const virDomainChrDef **arrPtr;
size_t i, cnt;
virDomainChrGetDomainPtrs(def, chr->deviceType, &arrPtr, &cnt);
if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE)
chr->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL;
if (chr->target.port == -1 &&
(chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL ||
chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL ||
chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE)) {
int maxport = -1;
for (i = 0; i < cnt; i++) {
if (arrPtr[i]->target.port > maxport)
maxport = arrPtr[i]->target.port;
}
chr->target.port = maxport + 1;
}
}
/* set default path for virtio-rng "random" backend to /dev/random */
if (dev->type == VIR_DOMAIN_DEVICE_RNG &&
dev->data.rng->backend == VIR_DOMAIN_RNG_BACKEND_RANDOM &&
!dev->data.rng->source.file) {
if (VIR_STRDUP(dev->data.rng->source.file, "/dev/random") < 0)
return -1;
}
/* verify disk source */
if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
virDomainDiskDefPtr disk = dev->data.disk;
/* internal snapshots and config files are currently supported
* only with rbd: */
if (virStorageSourceGetActualType(disk->src) != VIR_STORAGE_TYPE_NETWORK &&
disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_RBD) {
if (disk->src->snapshot) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_(" element is currently supported "
"only with 'rbd' disks"));
return -1;
}
if (disk->src->configFile) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_(" element is currently supported "
"only with 'rbd' disks"));
return -1;
}
}
/* Validate LUN configuration
* NOTE: virStorageTranslateDiskSourcePool is not run yet, so for
* disk "volume"'s, the closest we can get at config time is
* to ensure mode isn't direct since host/default will allow
* lun/block usage. At run time if it's determined the wrong
* voltype and pooltype values are set, then failure occurs
*/
if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN &&
!(disk->src->type == VIR_STORAGE_TYPE_BLOCK ||
(disk->src->type == VIR_STORAGE_TYPE_NETWORK &&
disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI) ||
(disk->src->type == VIR_STORAGE_TYPE_VOLUME &&
disk->src->srcpool &&
disk->src->srcpool->mode !=
VIR_STORAGE_SOURCE_POOL_MODE_DIRECT))) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("disk '%s' improperly configured for a "
"device='lun'"),
disk->dst);
return -1;
}
if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
virDomainDiskDefAssignAddress(xmlopt, disk) < 0)
return -1;
}
if (dev->type == VIR_DOMAIN_DEVICE_VIDEO) {
virDomainVideoDefPtr video = dev->data.video;
video->ram = VIR_ROUND_UP_POWER_OF_TWO(video->ram);
video->vram = VIR_ROUND_UP_POWER_OF_TWO(video->vram);
}
if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV) {
virDomainHostdevDefPtr hdev = dev->data.hostdev;
if (hdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI &&
hdev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
virDomainHostdevAssignAddress(xmlopt, def, hdev) < 0) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Cannot assign SCSI host device address"));
return -1;
}
}
return 0;
}
static int
virDomainDeviceDefPostParse(virDomainDeviceDefPtr dev,
const virDomainDef *def,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt)
{
int ret;
if (xmlopt->config.devicesPostParseCallback) {
ret = xmlopt->config.devicesPostParseCallback(dev, def, caps,
xmlopt->config.priv);
if (ret < 0)
return ret;
}
if ((ret = virDomainDeviceDefPostParseInternal(dev, def, caps, xmlopt)) < 0)
return ret;
return 0;
}
struct virDomainDefPostParseDeviceIteratorData {
virDomainDefPtr def;
virCapsPtr caps;
virDomainXMLOptionPtr xmlopt;
};
static int
virDomainDefPostParseDeviceIterator(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr dev,
virDomainDeviceInfoPtr info ATTRIBUTE_UNUSED,
void *opaque)
{
struct virDomainDefPostParseDeviceIteratorData *data = opaque;
return virDomainDeviceDefPostParse(dev, data->def, data->caps, data->xmlopt);
}
int
virDomainDefPostParse(virDomainDefPtr def,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt)
{
int ret;
struct virDomainDefPostParseDeviceIteratorData data = {
.def = def,
.caps = caps,
.xmlopt = xmlopt,
};
/* call the domain config callback */
if (xmlopt->config.domainPostParseCallback) {
ret = xmlopt->config.domainPostParseCallback(def, caps,
xmlopt->config.priv);
if (ret < 0)
return ret;
}
/* iterate the devices */
if ((ret = virDomainDeviceInfoIterateInternal(def,
virDomainDefPostParseDeviceIterator,
true,
&data)) < 0)
return ret;
if ((ret = virDomainDefPostParseInternal(def, caps)) < 0)
return ret;
return 0;
}
void virDomainDefClearPCIAddresses(virDomainDefPtr def)
{
virDomainDeviceInfoIterate(def, virDomainDeviceInfoClearPCIAddress, NULL);
}
void virDomainDefClearCCWAddresses(virDomainDefPtr def)
{
virDomainDeviceInfoIterate(def, virDomainDeviceInfoClearCCWAddress, NULL);
}
void virDomainDefClearDeviceAliases(virDomainDefPtr def)
{
virDomainDeviceInfoIterate(def, virDomainDeviceInfoClearAlias, NULL);
}
/* Generate a string representation of a device address
* @info address Device address to stringify
*/
static int ATTRIBUTE_NONNULL(2)
virDomainDeviceInfoFormat(virBufferPtr buf,
virDomainDeviceInfoPtr info,
unsigned int flags)
{
if ((flags & VIR_DOMAIN_DEF_FORMAT_ALLOW_BOOT) && info->bootIndex)
virBufferAsprintf(buf, "\n", info->bootIndex);
if (info->alias &&
!(flags & VIR_DOMAIN_DEF_FORMAT_INACTIVE)) {
virBufferAsprintf(buf, "\n", info->alias);
}
if (info->mastertype == VIR_DOMAIN_CONTROLLER_MASTER_USB) {
virBufferAsprintf(buf, "\n",
info->master.usb.startport);
}
if ((flags & VIR_DOMAIN_DEF_FORMAT_ALLOW_ROM) &&
(info->rombar || info->romfile)) {
virBufferAddLit(buf, "rombar) {
const char *rombar = virTristateSwitchTypeToString(info->rombar);
if (!rombar) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected rom bar value %d"),
info->rombar);
return -1;
}
virBufferAsprintf(buf, " bar='%s'", rombar);
}
if (info->romfile)
virBufferEscapeString(buf, " file='%s'", info->romfile);
virBufferAddLit(buf, "/>\n");
}
if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE ||
info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390)
return 0;
/* We'll be in domain/devices/[device type]/ so 3 level indent */
virBufferAsprintf(buf, "type));
switch ((virDomainDeviceAddressType) info->type) {
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
virBufferAsprintf(buf, " domain='0x%.4x' bus='0x%.2x' slot='0x%.2x' function='0x%.1x'",
info->addr.pci.domain,
info->addr.pci.bus,
info->addr.pci.slot,
info->addr.pci.function);
if (info->addr.pci.multi) {
virBufferAsprintf(buf, " multifunction='%s'",
virTristateSwitchTypeToString(info->addr.pci.multi));
}
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
virBufferAsprintf(buf, " controller='%d' bus='%d' target='%d' unit='%d'",
info->addr.drive.controller,
info->addr.drive.bus,
info->addr.drive.target,
info->addr.drive.unit);
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL:
virBufferAsprintf(buf, " controller='%d' bus='%d' port='%d'",
info->addr.vioserial.controller,
info->addr.vioserial.bus,
info->addr.vioserial.port);
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID:
virBufferAsprintf(buf, " controller='%d' slot='%d'",
info->addr.ccid.controller,
info->addr.ccid.slot);
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
virBufferAsprintf(buf, " bus='%d' port='%s'",
info->addr.usb.bus,
info->addr.usb.port);
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO:
if (info->addr.spaprvio.has_reg)
virBufferAsprintf(buf, " reg='0x%llx'", info->addr.spaprvio.reg);
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
virBufferAsprintf(buf, " cssid='0x%x' ssid='0x%x' devno='0x%04x'",
info->addr.ccw.cssid,
info->addr.ccw.ssid,
info->addr.ccw.devno);
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA:
if (info->addr.isa.iobase > 0)
virBufferAsprintf(buf, " iobase='0x%x'", info->addr.isa.iobase);
if (info->addr.isa.irq > 0)
virBufferAsprintf(buf, " irq='0x%x'", info->addr.isa.irq);
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM:
virBufferAsprintf(buf, " slot='%u'", info->addr.dimm.slot);
virBufferAsprintf(buf, " base='0x%llx'", info->addr.dimm.base);
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE:
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST:
break;
}
virBufferAddLit(buf, "/>\n");
return 0;
}
static int
virDomainDeviceDriveAddressParseXML(xmlNodePtr node,
virDomainDeviceDriveAddressPtr addr)
{
char *bus, *unit, *controller, *target;
int ret = -1;
memset(addr, 0, sizeof(*addr));
controller = virXMLPropString(node, "controller");
bus = virXMLPropString(node, "bus");
target = virXMLPropString(node, "target");
unit = virXMLPropString(node, "unit");
if (controller &&
virStrToLong_uip(controller, NULL, 10, &addr->controller) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'controller' attribute"));
goto cleanup;
}
if (bus &&
virStrToLong_uip(bus, NULL, 10, &addr->bus) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'bus' attribute"));
goto cleanup;
}
if (target &&
virStrToLong_uip(target, NULL, 10, &addr->target) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'target' attribute"));
goto cleanup;
}
if (unit &&
virStrToLong_uip(unit, NULL, 10, &addr->unit) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'unit' attribute"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(controller);
VIR_FREE(bus);
VIR_FREE(target);
VIR_FREE(unit);
return ret;
}
static int
virDomainDeviceVirtioSerialAddressParseXML(
xmlNodePtr node,
virDomainDeviceVirtioSerialAddressPtr addr
)
{
char *controller, *bus, *port;
int ret = -1;
memset(addr, 0, sizeof(*addr));
controller = virXMLPropString(node, "controller");
bus = virXMLPropString(node, "bus");
port = virXMLPropString(node, "port");
if (controller &&
virStrToLong_uip(controller, NULL, 10, &addr->controller) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'controller' attribute"));
goto cleanup;
}
if (bus &&
virStrToLong_uip(bus, NULL, 10, &addr->bus) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'bus' attribute"));
goto cleanup;
}
if (port &&
virStrToLong_uip(port, NULL, 10, &addr->port) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'port' attribute"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(controller);
VIR_FREE(bus);
VIR_FREE(port);
return ret;
}
static int
virDomainDeviceCCWAddressParseXML(xmlNodePtr node,
virDomainDeviceCCWAddressPtr addr)
{
int ret = -1;
char *cssid;
char *ssid;
char *devno;
memset(addr, 0, sizeof(*addr));
cssid = virXMLPropString(node, "cssid");
ssid = virXMLPropString(node, "ssid");
devno = virXMLPropString(node, "devno");
if (cssid && ssid && devno) {
if (cssid &&
virStrToLong_uip(cssid, NULL, 0, &addr->cssid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'cssid' attribute"));
goto cleanup;
}
if (ssid &&
virStrToLong_uip(ssid, NULL, 0, &addr->ssid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'ssid' attribute"));
goto cleanup;
}
if (devno &&
virStrToLong_uip(devno, NULL, 0, &addr->devno) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'devno' attribute"));
goto cleanup;
}
if (!virDomainDeviceCCWAddressIsValid(addr)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid specification for virtio ccw"
" address: cssid='%s' ssid='%s' devno='%s'"),
cssid, ssid, devno);
goto cleanup;
}
addr->assigned = true;
} else if (cssid || ssid || devno) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Invalid partial specification for virtio ccw"
" address"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(cssid);
VIR_FREE(ssid);
VIR_FREE(devno);
return ret;
}
static int
virDomainDeviceCcidAddressParseXML(xmlNodePtr node,
virDomainDeviceCcidAddressPtr addr)
{
char *controller, *slot;
int ret = -1;
memset(addr, 0, sizeof(*addr));
controller = virXMLPropString(node, "controller");
slot = virXMLPropString(node, "slot");
if (controller &&
virStrToLong_uip(controller, NULL, 10, &addr->controller) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'controller' attribute"));
goto cleanup;
}
if (slot &&
virStrToLong_uip(slot, NULL, 10, &addr->slot) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'slot' attribute"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(controller);
VIR_FREE(slot);
return ret;
}
static int
virDomainDeviceUSBAddressParseXML(xmlNodePtr node,
virDomainDeviceUSBAddressPtr addr)
{
char *port, *bus, *tmp;
unsigned int p;
int ret = -1;
memset(addr, 0, sizeof(*addr));
port = virXMLPropString(node, "port");
bus = virXMLPropString(node, "bus");
if (port &&
((virStrToLong_uip(port, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.')) ||
(*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) ||
(*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0' && *tmp != '.'))) ||
(*tmp == '.' && (virStrToLong_ui(tmp + 1, &tmp, 10, &p) < 0 || (*tmp != '\0'))))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'port' attribute"));
goto cleanup;
}
addr->port = port;
port = NULL;
if (bus &&
virStrToLong_uip(bus, NULL, 10, &addr->bus) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'bus' attribute"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(bus);
VIR_FREE(port);
return ret;
}
static int
virDomainDeviceSpaprVioAddressParseXML(xmlNodePtr node,
virDomainDeviceSpaprVioAddressPtr addr)
{
char *reg;
int ret;
memset(addr, 0, sizeof(*addr));
reg = virXMLPropString(node, "reg");
if (reg) {
if (virStrToLong_ull(reg, NULL, 16, &addr->reg) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'reg' attribute"));
ret = -1;
goto cleanup;
}
addr->has_reg = true;
}
ret = 0;
cleanup:
VIR_FREE(reg);
return ret;
}
static int
virDomainDeviceUSBMasterParseXML(xmlNodePtr node,
virDomainDeviceUSBMasterPtr master)
{
char *startport;
int ret = -1;
memset(master, 0, sizeof(*master));
startport = virXMLPropString(node, "startport");
if (startport &&
virStrToLong_ui(startport, NULL, 10, &master->startport) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Cannot parse 'startport' attribute"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(startport);
return ret;
}
static int
virDomainDeviceBootParseXML(xmlNodePtr node,
int *bootIndex,
virHashTablePtr bootHash)
{
char *order;
int boot;
int ret = -1;
order = virXMLPropString(node, "order");
if (!order) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing boot order attribute"));
goto cleanup;
} else if (virStrToLong_i(order, NULL, 10, &boot) < 0 ||
boot <= 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("incorrect boot order '%s', expecting positive integer"),
order);
goto cleanup;
}
if (bootHash) {
if (virHashLookup(bootHash, order)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("boot order '%s' used for more than one device"),
order);
goto cleanup;
}
if (virHashAddEntry(bootHash, order, (void *) 1) < 0)
goto cleanup;
}
*bootIndex = boot;
ret = 0;
cleanup:
VIR_FREE(order);
return ret;
}
static int
virDomainDeviceISAAddressParseXML(xmlNodePtr node,
virDomainDeviceISAAddressPtr addr)
{
int ret = -1;
char *iobase;
char *irq;
memset(addr, 0, sizeof(*addr));
iobase = virXMLPropString(node, "iobase");
irq = virXMLPropString(node, "irq");
if (iobase &&
virStrToLong_uip(iobase, NULL, 16, &addr->iobase) < 0) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Cannot parse 'iobase' attribute"));
goto cleanup;
}
if (irq &&
virStrToLong_uip(irq, NULL, 16, &addr->irq) < 0) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Cannot parse 'irq' attribute"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(iobase);
VIR_FREE(irq);
return ret;
}
static int
virDomainDeviceDimmAddressParseXML(xmlNodePtr node,
virDomainDeviceDimmAddressPtr addr)
{
int ret = -1;
char *tmp = NULL;
if (!(tmp = virXMLPropString(node, "slot")) ||
virStrToLong_uip(tmp, NULL, 10, &addr->slot) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("invalid or missing dimm slot id '%s'"),
NULLSTR(tmp));
goto cleanup;
}
VIR_FREE(tmp);
if (!(tmp = virXMLPropString(node, "base")) ||
virStrToLong_ullp(tmp, NULL, 16, &addr->base) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("invalid or missing dimm base address '%s'"),
NULLSTR(tmp));
goto cleanup;
}
VIR_FREE(tmp);
ret = 0;
cleanup:
VIR_FREE(tmp);
return ret;
}
/* Parse the XML definition for a device address
* @param node XML nodeset to parse for device address definition
*/
static int
virDomainDeviceInfoParseXML(xmlNodePtr node,
virHashTablePtr bootHash,
virDomainDeviceInfoPtr info,
unsigned int flags)
{
xmlNodePtr cur;
xmlNodePtr address = NULL;
xmlNodePtr master = NULL;
xmlNodePtr alias = NULL;
xmlNodePtr boot = NULL;
xmlNodePtr rom = NULL;
char *type = NULL;
int ret = -1;
virDomainDeviceInfoClear(info);
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (alias == NULL &&
!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) &&
xmlStrEqual(cur->name, BAD_CAST "alias")) {
alias = cur;
} else if (address == NULL &&
xmlStrEqual(cur->name, BAD_CAST "address")) {
address = cur;
} else if (master == NULL &&
xmlStrEqual(cur->name, BAD_CAST "master")) {
master = cur;
} else if (boot == NULL &&
(flags & VIR_DOMAIN_DEF_PARSE_ALLOW_BOOT) &&
xmlStrEqual(cur->name, BAD_CAST "boot")) {
boot = cur;
} else if (rom == NULL &&
(flags & VIR_DOMAIN_DEF_PARSE_ALLOW_ROM) &&
xmlStrEqual(cur->name, BAD_CAST "rom")) {
rom = cur;
}
}
cur = cur->next;
}
if (alias)
info->alias = virXMLPropString(alias, "name");
if (master) {
info->mastertype = VIR_DOMAIN_CONTROLLER_MASTER_USB;
if (virDomainDeviceUSBMasterParseXML(master, &info->master.usb) < 0)
goto cleanup;
}
if (boot) {
if (virDomainDeviceBootParseXML(boot, &info->bootIndex, bootHash))
goto cleanup;
}
if (rom) {
char *rombar = virXMLPropString(rom, "bar");
if (rombar &&
((info->rombar = virTristateSwitchTypeFromString(rombar)) <= 0)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unknown rom bar value '%s'"), rombar);
VIR_FREE(rombar);
goto cleanup;
}
VIR_FREE(rombar);
info->romfile = virXMLPropString(rom, "file");
}
if (!address)
return 0;
type = virXMLPropString(address, "type");
if (type) {
if ((info->type = virDomainDeviceAddressTypeFromString(type)) <= 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unknown address type '%s'"), type);
goto cleanup;
}
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("No type specified for device address"));
goto cleanup;
}
switch ((virDomainDeviceAddressType) info->type) {
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
if (virDevicePCIAddressParseXML(address, &info->addr.pci) < 0)
goto cleanup;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
if (virDomainDeviceDriveAddressParseXML(address, &info->addr.drive) < 0)
goto cleanup;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL:
if (virDomainDeviceVirtioSerialAddressParseXML
(address, &info->addr.vioserial) < 0)
goto cleanup;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID:
if (virDomainDeviceCcidAddressParseXML(address, &info->addr.ccid) < 0)
goto cleanup;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
if (virDomainDeviceUSBAddressParseXML(address, &info->addr.usb) < 0)
goto cleanup;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO:
if (virDomainDeviceSpaprVioAddressParseXML(address, &info->addr.spaprvio) < 0)
goto cleanup;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
if (virDomainDeviceCCWAddressParseXML
(address, &info->addr.ccw) < 0)
goto cleanup;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA:
if (virDomainDeviceISAAddressParseXML(address, &info->addr.isa) < 0)
goto cleanup;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
virReportError(VIR_ERR_XML_ERROR, "%s",
_("virtio-s390 bus doesn't have an address"));
goto cleanup;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM:
if (virDomainDeviceDimmAddressParseXML(address, &info->addr.dimm) < 0)
goto cleanup;
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE:
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST:
break;
}
ret = 0;
cleanup:
if (ret == -1)
VIR_FREE(info->alias);
VIR_FREE(type);
return ret;
}
static int
virDomainParseLegacyDeviceAddress(char *devaddr,
virDevicePCIAddressPtr pci)
{
char *tmp;
/* expected format: :: */
if (/* domain */
virStrToLong_ui(devaddr, &tmp, 16, &pci->domain) < 0 || *tmp != ':' ||
/* bus */
virStrToLong_ui(tmp + 1, &tmp, 16, &pci->bus) < 0 || *tmp != ':' ||
/* slot */
virStrToLong_ui(tmp + 1, NULL, 16, &pci->slot) < 0)
return -1;
return 0;
}
static int
virDomainHostdevSubsysUSBDefParseXML(xmlNodePtr node,
virDomainHostdevDefPtr def)
{
int ret = -1;
bool got_product, got_vendor;
xmlNodePtr cur;
char *startupPolicy = NULL;
char *autoAddress;
virDomainHostdevSubsysUSBPtr usbsrc = &def->source.subsys.u.usb;
if ((startupPolicy = virXMLPropString(node, "startupPolicy"))) {
def->startupPolicy =
virDomainStartupPolicyTypeFromString(startupPolicy);
if (def->startupPolicy <= 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unknown startup policy '%s'"),
startupPolicy);
VIR_FREE(startupPolicy);
goto out;
}
VIR_FREE(startupPolicy);
}
if ((autoAddress = virXMLPropString(node, "autoAddress"))) {
if (STREQ(autoAddress, "yes"))
usbsrc->autoAddress = true;
VIR_FREE(autoAddress);
}
/* Product can validly be 0, so we need some extra help to determine
* if it is uninitialized*/
got_product = false;
got_vendor = false;
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur->name, BAD_CAST "vendor")) {
char *vendor = virXMLPropString(cur, "id");
if (vendor) {
got_vendor = true;
if (virStrToLong_ui(vendor, NULL, 0, &usbsrc->vendor) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse vendor id %s"), vendor);
VIR_FREE(vendor);
goto out;
}
VIR_FREE(vendor);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("usb vendor needs id"));
goto out;
}
} else if (xmlStrEqual(cur->name, BAD_CAST "product")) {
char* product = virXMLPropString(cur, "id");
if (product) {
got_product = true;
if (virStrToLong_ui(product, NULL, 0,
&usbsrc->product) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse product %s"),
product);
VIR_FREE(product);
goto out;
}
VIR_FREE(product);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("usb product needs id"));
goto out;
}
} else if (xmlStrEqual(cur->name, BAD_CAST "address")) {
char *bus, *device;
bus = virXMLPropString(cur, "bus");
if (bus) {
if (virStrToLong_ui(bus, NULL, 0, &usbsrc->bus) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse bus %s"), bus);
VIR_FREE(bus);
goto out;
}
VIR_FREE(bus);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("usb address needs bus id"));
goto out;
}
device = virXMLPropString(cur, "device");
if (device) {
if (virStrToLong_ui(device, NULL, 0, &usbsrc->device) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse device %s"),
device);
VIR_FREE(device);
goto out;
}
VIR_FREE(device);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("usb address needs device id"));
goto out;
}
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown usb source type '%s'"),
cur->name);
goto out;
}
}
cur = cur->next;
}
if (got_vendor && usbsrc->vendor == 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("vendor cannot be 0."));
goto out;
}
if (!got_vendor && got_product) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing vendor"));
goto out;
}
if (got_vendor && !got_product) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing product"));
goto out;
}
ret = 0;
out:
return ret;
}
/* The internal XML for host PCI device's original states:
*
*
*
*
*
*
*/
static int
virDomainHostdevSubsysPCIOrigStatesDefParseXML(xmlNodePtr node,
virDomainHostdevOrigStatesPtr def)
{
xmlNodePtr cur;
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur->name, BAD_CAST "unbind")) {
def->states.pci.unbind_from_stub = true;
} else if (xmlStrEqual(cur->name, BAD_CAST "removeslot")) {
def->states.pci.remove_slot = true;
} else if (xmlStrEqual(cur->name, BAD_CAST "reprobe")) {
def->states.pci.reprobe = true;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported element '%s' of 'origstates'"),
cur->name);
return -1;
}
}
cur = cur->next;
}
return 0;
}
static int
virDomainHostdevSubsysPCIDefParseXML(xmlNodePtr node,
virDomainHostdevDefPtr def,
unsigned int flags)
{
int ret = -1;
xmlNodePtr cur;
cur = node->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur->name, BAD_CAST "address")) {
virDevicePCIAddressPtr addr =
&def->source.subsys.u.pci.addr;
if (virDevicePCIAddressParseXML(cur, addr) < 0)
goto out;
} else if ((flags & VIR_DOMAIN_DEF_PARSE_STATUS) &&
xmlStrEqual(cur->name, BAD_CAST "state")) {
/* Legacy back-compat. Don't add any more attributes here */
char *devaddr = virXMLPropString(cur, "devaddr");
if (devaddr &&
virDomainParseLegacyDeviceAddress(devaddr,
&def->info->addr.pci) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to parse devaddr parameter '%s'"),
devaddr);
VIR_FREE(devaddr);
goto out;
}
def->info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
} else if ((flags & VIR_DOMAIN_DEF_PARSE_PCI_ORIG_STATES) &&
xmlStrEqual(cur->name, BAD_CAST "origstates")) {
virDomainHostdevOrigStatesPtr states = &def->origstates;
if (virDomainHostdevSubsysPCIOrigStatesDefParseXML(cur, states) < 0)
goto out;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown pci source type '%s'"),
cur->name);
goto out;
}
}
cur = cur->next;
}
ret = 0;
out:
return ret;
}
static int
virDomainStorageHostParse(xmlNodePtr node,
virStorageNetHostDefPtr *hosts,
size_t *nhosts)
{
int ret = -1;
xmlNodePtr child;
char *transport = NULL;
virStorageNetHostDef host;
memset(&host, 0, sizeof(host));
child = node->children;
while (child != NULL) {
if (child->type == XML_ELEMENT_NODE &&
xmlStrEqual(child->name, BAD_CAST "host")) {
host.transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
/* transport can be tcp (default), unix or rdma. */
if ((transport = virXMLPropString(child, "transport"))) {
host.transport = virStorageNetHostTransportTypeFromString(transport);
if (host.transport < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unknown protocol transport type '%s'"),
transport);
goto cleanup;
}
}
host.socket = virXMLPropString(child, "socket");
if (host.transport == VIR_STORAGE_NET_HOST_TRANS_UNIX &&
host.socket == NULL) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("missing socket for unix transport"));
goto cleanup;
}
if (host.transport != VIR_STORAGE_NET_HOST_TRANS_UNIX &&
host.socket != NULL) {
virReportError(VIR_ERR_XML_ERROR,
_("transport '%s' does not support "
"socket attribute"),
transport);
goto cleanup;
}
VIR_FREE(transport);
if (host.transport != VIR_STORAGE_NET_HOST_TRANS_UNIX) {
if (!(host.name = virXMLPropString(child, "name"))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("missing name for host"));
goto cleanup;
}
host.port = virXMLPropString(child, "port");
}
if (VIR_APPEND_ELEMENT(*hosts, *nhosts, host) < 0)
goto cleanup;
}
child = child->next;
}
ret = 0;
cleanup:
virStorageNetHostDefClear(&host);
VIR_FREE(transport);
return ret;
}
static int
virDomainHostdevSubsysSCSIHostDefParseXML(xmlNodePtr sourcenode,
virDomainHostdevSubsysSCSIPtr scsisrc)
{
int ret = -1;
bool got_address = false, got_adapter = false;
xmlNodePtr cur;
char *bus = NULL, *target = NULL, *unit = NULL;
virDomainHostdevSubsysSCSIHostPtr scsihostsrc = &scsisrc->u.host;
cur = sourcenode->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE) {
if (xmlStrEqual(cur->name, BAD_CAST "address")) {
if (got_address) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("more than one source addresses is "
"specified for scsi hostdev"));
goto cleanup;
}
if (!(bus = virXMLPropString(cur, "bus")) ||
!(target = virXMLPropString(cur, "target")) ||
!(unit = virXMLPropString(cur, "unit"))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("'bus', 'target', and 'unit' must be specified "
"for scsi hostdev source address"));
goto cleanup;
}
if (virStrToLong_uip(bus, NULL, 0, &scsihostsrc->bus) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse bus '%s'"), bus);
goto cleanup;
}
if (virStrToLong_uip(target, NULL, 0,
&scsihostsrc->target) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse target '%s'"), target);
goto cleanup;
}
if (virStrToLong_ullp(unit, NULL, 0, &scsihostsrc->unit) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse unit '%s'"), unit);
goto cleanup;
}
got_address = true;
} else if (xmlStrEqual(cur->name, BAD_CAST "adapter")) {
if (got_adapter) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("more than one adapters is specified "
"for scsi hostdev source"));
goto cleanup;
}
if (!(scsihostsrc->adapter = virXMLPropString(cur, "name"))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("'adapter' must be specified for scsi hostdev source"));
goto cleanup;
}
got_adapter = true;
} else {
virReportError(VIR_ERR_XML_ERROR,
_("unsupported element '%s' of scsi hostdev source"),
cur->name);
goto cleanup;
}
}
cur = cur->next;
}
if (!got_address || !got_adapter) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("'adapter' and 'address' must be specified for scsi "
"hostdev source"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(bus);
VIR_FREE(target);
VIR_FREE(unit);
return ret;
}
static int
virDomainHostdevSubsysSCSIiSCSIDefParseXML(xmlNodePtr sourcenode,
virDomainHostdevSubsysSCSIPtr def)
{
int ret = -1;
int auth_secret_usage = -1;
xmlNodePtr cur;
virStorageAuthDefPtr authdef = NULL;
virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc = &def->u.iscsi;
/* Similar to virDomainDiskSourceParse for a VIR_STORAGE_TYPE_NETWORK */
if (!(iscsisrc->path = virXMLPropString(sourcenode, "name"))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("missing iSCSI hostdev source path name"));
goto cleanup;
}
if (virDomainStorageHostParse(sourcenode, &iscsisrc->hosts,
&iscsisrc->nhosts) < 0)
goto cleanup;
if (iscsisrc->nhosts < 1) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("missing the host address for the iSCSI hostdev"));
goto cleanup;
}
if (iscsisrc->nhosts > 1) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("only one source host address may be specified "
"for the iSCSI hostdev"));
goto cleanup;
}
cur = sourcenode->children;
while (cur != NULL) {
if (cur->type == XML_ELEMENT_NODE &&
xmlStrEqual(cur->name, BAD_CAST "auth")) {
if (!(authdef = virStorageAuthDefParse(sourcenode->doc, cur)))
goto cleanup;
if ((auth_secret_usage =
virSecretUsageTypeFromString(authdef->secrettype)) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid secret type %s"),
authdef->secrettype);
goto cleanup;
}
if (auth_secret_usage != VIR_SECRET_USAGE_TYPE_ISCSI) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("hostdev invalid secret type '%s'"),
authdef->secrettype);
goto cleanup;
}
iscsisrc->auth = authdef;
authdef = NULL;
}
cur = cur->next;
}
ret = 0;
cleanup:
virStorageAuthDefFree(authdef);
return ret;
}
static int
virDomainHostdevSubsysSCSIDefParseXML(xmlNodePtr sourcenode,
virDomainHostdevSubsysSCSIPtr scsisrc)
{
char *protocol = NULL;
int ret = -1;
if ((protocol = virXMLPropString(sourcenode, "protocol"))) {
scsisrc->protocol =
virDomainHostdevSubsysSCSIProtocolTypeFromString(protocol);
if (scsisrc->protocol < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unknown SCSI subsystem protocol '%s'"),
protocol);
goto cleanup;
}
}
if (scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI)
ret = virDomainHostdevSubsysSCSIiSCSIDefParseXML(sourcenode, scsisrc);
else
ret = virDomainHostdevSubsysSCSIHostDefParseXML(sourcenode, scsisrc);
cleanup:
VIR_FREE(protocol);
return ret;
}
static int
virDomainHostdevDefParseXMLSubsys(xmlNodePtr node,
xmlXPathContextPtr ctxt,
const char *type,
virDomainHostdevDefPtr def,
unsigned int flags)
{
xmlNodePtr sourcenode;
char *managed = NULL;
char *sgio = NULL;
char *rawio = NULL;
char *backendStr = NULL;
int backend;
int ret = -1;
virDomainHostdevSubsysPCIPtr pcisrc = &def->source.subsys.u.pci;
virDomainHostdevSubsysSCSIPtr scsisrc = &def->source.subsys.u.scsi;
/* @managed can be read from the xml document - it is always an
* attribute of the toplevel element, no matter what type of
* element that might be (pure hostdev, or higher level device
* (e.g. ) with type='hostdev')
*/
if ((managed = virXMLPropString(node, "managed")) != NULL) {
if (STREQ(managed, "yes"))
def->managed = true;
}
sgio = virXMLPropString(node, "sgio");
rawio = virXMLPropString(node, "rawio");
/* @type is passed in from the caller rather than read from the
* xml document, because it is specified in different places for
* different kinds of defs - it is an attribute of
* \n");
if (virBufferCheckError(buf) < 0)
goto error;
return 0;
error:
virBufferFreeAndReset(buf);
virBufferFreeAndReset(&childrenBuf);
return -1;
}
unsigned int virDomainDefFormatConvertXMLFlags(unsigned int flags)
{
unsigned int formatFlags = 0;
if (flags & VIR_DOMAIN_XML_SECURE)
formatFlags |= VIR_DOMAIN_DEF_FORMAT_SECURE;
if (flags & VIR_DOMAIN_XML_INACTIVE)
formatFlags |= VIR_DOMAIN_DEF_FORMAT_INACTIVE;
if (flags & VIR_DOMAIN_XML_UPDATE_CPU)
formatFlags |= VIR_DOMAIN_DEF_FORMAT_UPDATE_CPU;
if (flags & VIR_DOMAIN_XML_MIGRATABLE)
formatFlags |= VIR_DOMAIN_DEF_FORMAT_MIGRATABLE;
return formatFlags;
}
char *
virDomainDefFormat(virDomainDefPtr def, unsigned int flags)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
virCheckFlags(VIR_DOMAIN_DEF_FORMAT_COMMON_FLAGS, NULL);
if (virDomainDefFormatInternal(def, flags, &buf) < 0)
return NULL;
return virBufferContentAndReset(&buf);
}
char *
virDomainObjFormat(virDomainXMLOptionPtr xmlopt,
virDomainObjPtr obj,
unsigned int flags)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
int state;
int reason;
size_t i;
state = virDomainObjGetState(obj, &reason);
virBufferAsprintf(&buf, "\n",
virDomainStateTypeToString(state),
virDomainStateReasonToString(state, reason),
(long long)obj->pid);
virBufferAdjustIndent(&buf, 2);
for (i = 0; i < VIR_DOMAIN_TAINT_LAST; i++) {
if (obj->taint & (1 << i))
virBufferAsprintf(&buf, "\n",
virDomainTaintTypeToString(i));
}
if (xmlopt->privateData.format &&
xmlopt->privateData.format(&buf, obj) < 0)
goto error;
if (virDomainDefFormatInternal(obj->def, flags, &buf) < 0)
goto error;
virBufferAdjustIndent(&buf, -2);
virBufferAddLit(&buf, "\n");
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static bool
virDomainDefHasUSB(virDomainDefPtr def)
{
size_t i;
for (i = 0; i < def->ncontrollers; i++) {
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
def->controllers[i]->model != VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE)
return true;
}
return false;
}
static bool
virDomainDeviceIsUSB(virDomainDeviceDefPtr dev)
{
int t = dev->type;
if ((t == VIR_DOMAIN_DEVICE_DISK &&
dev->data.disk->bus == VIR_DOMAIN_DISK_BUS_USB) ||
(t == VIR_DOMAIN_DEVICE_INPUT &&
dev->data.input->type == VIR_DOMAIN_INPUT_BUS_USB) ||
(t == VIR_DOMAIN_DEVICE_HOSTDEV &&
dev->data.hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
dev->data.hostdev->source.subsys.type ==
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) ||
(t == VIR_DOMAIN_DEVICE_HUB &&
dev->data.hub->type == VIR_DOMAIN_HUB_TYPE_USB) ||
(t == VIR_DOMAIN_DEVICE_REDIRDEV &&
dev->data.redirdev->bus == VIR_DOMAIN_REDIRDEV_BUS_USB))
return true;
return false;
}
static int
virDomainDeviceInfoCheckBootIndex(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
virDomainDeviceInfoPtr info,
void *opaque)
{
virDomainDeviceInfoPtr newinfo = opaque;
if (info->bootIndex == newinfo->bootIndex) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("boot order %d is already used by another device"),
newinfo->bootIndex);
return -1;
}
return 0;
}
/**
* virDomainDefGetDiskByWWN:
* @def: domain definition
* @wwn: wwn of a disk to find
*
* Returns a disk definition pointer corresponding to the given WWN identifier
* or NULL either if @wwn was NULL or if disk with given WWN is not present in
* the domain definition.
*/
static virDomainDiskDefPtr
virDomainDefGetDiskByWWN(virDomainDefPtr def,
const char *wwn)
{
size_t i;
if (!wwn)
return NULL;
for (i = 0; i < def->ndisks; i++) {
if (STREQ_NULLABLE(def->disks[i]->wwn, wwn))
return def->disks[i];
}
return NULL;
}
int
virDomainDefCompatibleDevice(virDomainDefPtr def,
virDomainDeviceDefPtr dev,
virDomainDeviceAction action)
{
virDomainDeviceInfoPtr info = virDomainDeviceGetInfo(dev);
if (action != VIR_DOMAIN_DEVICE_ACTION_ATTACH)
return 0;
if (!virDomainDefHasUSB(def) &&
def->os.type != VIR_DOMAIN_OSTYPE_EXE &&
virDomainDeviceIsUSB(dev)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Device configuration is not compatible: "
"Domain has no USB bus support"));
return -1;
}
if (info && info->bootIndex > 0) {
if (def->os.nBootDevs > 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("per-device boot elements cannot be used"
" together with os/boot elements"));
return -1;
}
if (virDomainDeviceInfoIterate(def,
virDomainDeviceInfoCheckBootIndex,
info) < 0)
return -1;
}
if (dev->type == VIR_DOMAIN_DEVICE_MEMORY) {
unsigned long long sz = dev->data.memory->size;
if ((virDomainDefGetMemoryActual(def) + sz) > def->mem.max_memory) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Attaching memory device with size '%llu' would "
"exceed domain's maxMemory config"), sz);
return -1;
}
}
if (dev->type == VIR_DOMAIN_DEVICE_DISK) {
if (!!virDomainDefGetDiskByWWN(def, dev->data.disk->wwn)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Domain already has a disk with wwn '%s'"),
dev->data.disk->wwn);
return -1;
}
}
return 0;
}
int
virDomainSaveXML(const char *configDir,
virDomainDefPtr def,
const char *xml)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
char *configFile = NULL;
int ret = -1;
if (!configDir)
return 0;
if ((configFile = virDomainConfigFile(configDir, def->name)) == NULL)
goto cleanup;
if (virFileMakePath(configDir) < 0) {
virReportSystemError(errno,
_("cannot create config directory '%s'"),
configDir);
goto cleanup;
}
virUUIDFormat(def->uuid, uuidstr);
ret = virXMLSaveFile(configFile,
virXMLPickShellSafeComment(def->name, uuidstr), "edit",
xml);
cleanup:
VIR_FREE(configFile);
return ret;
}
int
virDomainSaveConfig(const char *configDir,
virDomainDefPtr def)
{
int ret = -1;
char *xml;
if (!(xml = virDomainDefFormat(def, VIR_DOMAIN_DEF_FORMAT_SECURE)))
goto cleanup;
if (virDomainSaveXML(configDir, def, xml))
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(xml);
return ret;
}
int
virDomainSaveStatus(virDomainXMLOptionPtr xmlopt,
const char *statusDir,
virDomainObjPtr obj)
{
unsigned int flags = (VIR_DOMAIN_DEF_FORMAT_SECURE |
VIR_DOMAIN_DEF_FORMAT_STATUS |
VIR_DOMAIN_DEF_FORMAT_ACTUAL_NET |
VIR_DOMAIN_DEF_FORMAT_PCI_ORIG_STATES |
VIR_DOMAIN_DEF_FORMAT_CLOCK_ADJUST);
int ret = -1;
char *xml;
if (!(xml = virDomainObjFormat(xmlopt, obj, flags)))
goto cleanup;
if (virDomainSaveXML(statusDir, obj->def, xml))
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(xml);
return ret;
}
static virDomainObjPtr
virDomainObjListLoadConfig(virDomainObjListPtr doms,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
const char *configDir,
const char *autostartDir,
const char *name,
virDomainLoadConfigNotify notify,
void *opaque)
{
char *configFile = NULL, *autostartLink = NULL;
virDomainDefPtr def = NULL;
virDomainObjPtr dom;
int autostart;
virDomainDefPtr oldDef = NULL;
if ((configFile = virDomainConfigFile(configDir, name)) == NULL)
goto error;
if (!(def = virDomainDefParseFile(configFile, caps, xmlopt,
VIR_DOMAIN_DEF_PARSE_INACTIVE |
VIR_DOMAIN_DEF_PARSE_SKIP_OSTYPE_CHECKS)))
goto error;
if ((autostartLink = virDomainConfigFile(autostartDir, name)) == NULL)
goto error;
if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
goto error;
if (!(dom = virDomainObjListAddLocked(doms, def, xmlopt, 0, &oldDef)))
goto error;
dom->autostart = autostart;
if (notify)
(*notify)(dom, oldDef == NULL, opaque);
virDomainDefFree(oldDef);
VIR_FREE(configFile);
VIR_FREE(autostartLink);
return dom;
error:
VIR_FREE(configFile);
VIR_FREE(autostartLink);
virDomainDefFree(def);
return NULL;
}
static virDomainObjPtr
virDomainObjListLoadStatus(virDomainObjListPtr doms,
const char *statusDir,
const char *name,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
virDomainLoadConfigNotify notify,
void *opaque)
{
char *statusFile = NULL;
virDomainObjPtr obj = NULL;
char uuidstr[VIR_UUID_STRING_BUFLEN];
if ((statusFile = virDomainConfigFile(statusDir, name)) == NULL)
goto error;
if (!(obj = virDomainObjParseFile(statusFile, caps, xmlopt,
VIR_DOMAIN_DEF_PARSE_STATUS |
VIR_DOMAIN_DEF_PARSE_ACTUAL_NET |
VIR_DOMAIN_DEF_PARSE_PCI_ORIG_STATES |
VIR_DOMAIN_DEF_PARSE_CLOCK_ADJUST |
VIR_DOMAIN_DEF_PARSE_SKIP_OSTYPE_CHECKS)))
goto error;
virUUIDFormat(obj->def->uuid, uuidstr);
if (virHashLookup(doms->objs, uuidstr) != NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected domain %s already exists"),
obj->def->name);
goto error;
}
if (virHashAddEntry(doms->objs, uuidstr, obj) < 0)
goto error;
if (virHashAddEntry(doms->objsName, obj->def->name, obj) < 0) {
virHashRemoveEntry(doms->objs, uuidstr);
goto error;
}
/* Since domain is in two hash tables, increment the
* reference counter */
virObjectRef(obj);
if (notify)
(*notify)(obj, 1, opaque);
VIR_FREE(statusFile);
return obj;
error:
virObjectUnref(obj);
VIR_FREE(statusFile);
return NULL;
}
int
virDomainObjListLoadAllConfigs(virDomainObjListPtr doms,
const char *configDir,
const char *autostartDir,
int liveStatus,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
virDomainLoadConfigNotify notify,
void *opaque)
{
DIR *dir;
struct dirent *entry;
int ret = -1;
VIR_INFO("Scanning for configs in %s", configDir);
if (!(dir = opendir(configDir))) {
if (errno == ENOENT)
return 0;
virReportSystemError(errno,
_("Failed to open dir '%s'"),
configDir);
return -1;
}
virObjectLock(doms);
while ((ret = virDirRead(dir, &entry, configDir)) > 0) {
virDomainObjPtr dom;
if (entry->d_name[0] == '.')
continue;
if (!virFileStripSuffix(entry->d_name, ".xml"))
continue;
/* NB: ignoring errors, so one malformed config doesn't
kill the whole process */
VIR_INFO("Loading config file '%s.xml'", entry->d_name);
if (liveStatus)
dom = virDomainObjListLoadStatus(doms,
configDir,
entry->d_name,
caps,
xmlopt,
notify,
opaque);
else
dom = virDomainObjListLoadConfig(doms,
caps,
xmlopt,
configDir,
autostartDir,
entry->d_name,
notify,
opaque);
if (dom) {
if (!liveStatus)
dom->persistent = 1;
virObjectUnlock(dom);
}
}
closedir(dir);
virObjectUnlock(doms);
return ret;
}
int
virDomainDeleteConfig(const char *configDir,
const char *autostartDir,
virDomainObjPtr dom)
{
char *configFile = NULL, *autostartLink = NULL;
int ret = -1;
if ((configFile = virDomainConfigFile(configDir, dom->def->name)) == NULL)
goto cleanup;
if ((autostartLink = virDomainConfigFile(autostartDir,
dom->def->name)) == NULL)
goto cleanup;
/* Not fatal if this doesn't work */
unlink(autostartLink);
dom->autostart = 0;
if (unlink(configFile) < 0 &&
errno != ENOENT) {
virReportSystemError(errno,
_("cannot remove config %s"),
configFile);
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(configFile);
VIR_FREE(autostartLink);
return ret;
}
char
*virDomainConfigFile(const char *dir,
const char *name)
{
char *ret;
ignore_value(virAsprintf(&ret, "%s/%s.xml", dir, name));
return ret;
}
/* Translates a device name of the form (regex) "[fhv]d[a-z]+" into
* the corresponding bus,index combination (e.g. sda => (0,0), sdi (1,1),
* hdd => (1,1), vdaa => (0,26))
* @param disk The disk device
* @param busIdx parsed bus number
* @param devIdx parsed device number
* @return 0 on success, -1 on failure
*/
int
virDiskNameToBusDeviceIndex(virDomainDiskDefPtr disk,
int *busIdx,
int *devIdx)
{
int idx = virDiskNameToIndex(disk->dst);
if (idx < 0)
return -1;
switch (disk->bus) {
case VIR_DOMAIN_DISK_BUS_IDE:
*busIdx = idx / 2;
*devIdx = idx % 2;
break;
case VIR_DOMAIN_DISK_BUS_SCSI:
*busIdx = idx / 7;
*devIdx = idx % 7;
break;
case VIR_DOMAIN_DISK_BUS_FDC:
case VIR_DOMAIN_DISK_BUS_USB:
case VIR_DOMAIN_DISK_BUS_VIRTIO:
case VIR_DOMAIN_DISK_BUS_XEN:
case VIR_DOMAIN_DISK_BUS_SD:
default:
*busIdx = 0;
*devIdx = idx;
break;
}
return 0;
}
int
virDomainFSInsert(virDomainDefPtr def, virDomainFSDefPtr fs)
{
return VIR_APPEND_ELEMENT(def->fss, def->nfss, fs);
}
virDomainFSDefPtr
virDomainFSRemove(virDomainDefPtr def, size_t i)
{
virDomainFSDefPtr fs = def->fss[i];
VIR_DELETE_ELEMENT(def->fss, i, def->nfss);
return fs;
}
virDomainFSDefPtr
virDomainGetFilesystemForTarget(virDomainDefPtr def,
const char *target)
{
size_t i;
for (i = 0; i < def->nfss; i++) {
if (STREQ(def->fss[i]->dst, target))
return def->fss[i];
}
return NULL;
}
struct virDomainObjListData {
virDomainObjListACLFilter filter;
virConnectPtr conn;
bool active;
int count;
};
static void
virDomainObjListCount(void *payload,
const void *name ATTRIBUTE_UNUSED,
void *opaque)
{
virDomainObjPtr obj = payload;
struct virDomainObjListData *data = opaque;
virObjectLock(obj);
if (data->filter &&
!data->filter(data->conn, obj->def))
goto cleanup;
if (virDomainObjIsActive(obj)) {
if (data->active)
data->count++;
} else {
if (!data->active)
data->count++;
}
cleanup:
virObjectUnlock(obj);
}
int
virDomainObjListNumOfDomains(virDomainObjListPtr doms,
bool active,
virDomainObjListACLFilter filter,
virConnectPtr conn)
{
struct virDomainObjListData data = { filter, conn, active, 0 };
virObjectLock(doms);
virHashForEach(doms->objs, virDomainObjListCount, &data);
virObjectUnlock(doms);
return data.count;
}
struct virDomainIDData {
virDomainObjListACLFilter filter;
virConnectPtr conn;
int numids;
int maxids;
int *ids;
};
static void
virDomainObjListCopyActiveIDs(void *payload,
const void *name ATTRIBUTE_UNUSED,
void *opaque)
{
virDomainObjPtr obj = payload;
struct virDomainIDData *data = opaque;
virObjectLock(obj);
if (data->filter &&
!data->filter(data->conn, obj->def))
goto cleanup;
if (virDomainObjIsActive(obj) && data->numids < data->maxids)
data->ids[data->numids++] = obj->def->id;
cleanup:
virObjectUnlock(obj);
}
int
virDomainObjListGetActiveIDs(virDomainObjListPtr doms,
int *ids,
int maxids,
virDomainObjListACLFilter filter,
virConnectPtr conn)
{
struct virDomainIDData data = { filter, conn,
0, maxids, ids };
virObjectLock(doms);
virHashForEach(doms->objs, virDomainObjListCopyActiveIDs, &data);
virObjectUnlock(doms);
return data.numids;
}
struct virDomainNameData {
virDomainObjListACLFilter filter;
virConnectPtr conn;
int oom;
int numnames;
int maxnames;
char **const names;
};
static void
virDomainObjListCopyInactiveNames(void *payload,
const void *name ATTRIBUTE_UNUSED,
void *opaque)
{
virDomainObjPtr obj = payload;
struct virDomainNameData *data = opaque;
if (data->oom)
return;
virObjectLock(obj);
if (data->filter &&
!data->filter(data->conn, obj->def))
goto cleanup;
if (!virDomainObjIsActive(obj) && data->numnames < data->maxnames) {
if (VIR_STRDUP(data->names[data->numnames], obj->def->name) < 0)
data->oom = 1;
else
data->numnames++;
}
cleanup:
virObjectUnlock(obj);
}
int
virDomainObjListGetInactiveNames(virDomainObjListPtr doms,
char **const names,
int maxnames,
virDomainObjListACLFilter filter,
virConnectPtr conn)
{
struct virDomainNameData data = { filter, conn,
0, 0, maxnames, names };
size_t i;
virObjectLock(doms);
virHashForEach(doms->objs, virDomainObjListCopyInactiveNames, &data);
virObjectUnlock(doms);
if (data.oom) {
for (i = 0; i < data.numnames; i++)
VIR_FREE(data.names[i]);
return -1;
}
return data.numnames;
}
struct virDomainListIterData {
virDomainObjListIterator callback;
void *opaque;
int ret;
};
static void
virDomainObjListHelper(void *payload,
const void *name ATTRIBUTE_UNUSED,
void *opaque)
{
struct virDomainListIterData *data = opaque;
if (data->callback(payload, data->opaque) < 0)
data->ret = -1;
}
int
virDomainObjListForEach(virDomainObjListPtr doms,
virDomainObjListIterator callback,
void *opaque)
{
struct virDomainListIterData data = {
callback, opaque, 0,
};
virObjectLock(doms);
virHashForEach(doms->objs, virDomainObjListHelper, &data);
virObjectUnlock(doms);
return data.ret;
}
int
virDomainChrDefForeach(virDomainDefPtr def,
bool abortOnError,
virDomainChrDefIterator iter,
void *opaque)
{
size_t i;
int rc = 0;
for (i = 0; i < def->nserials; i++) {
if ((iter)(def,
def->serials[i],
opaque) < 0)
rc = -1;
if (abortOnError && rc != 0)
goto done;
}
for (i = 0; i < def->nparallels; i++) {
if ((iter)(def,
def->parallels[i],
opaque) < 0)
rc = -1;
if (abortOnError && rc != 0)
goto done;
}
for (i = 0; i < def->nchannels; i++) {
if ((iter)(def,
def->channels[i],
opaque) < 0)
rc = -1;
if (abortOnError && rc != 0)
goto done;
}
for (i = 0; i < def->nconsoles; i++) {
if ((iter)(def,
def->consoles[i],
opaque) < 0)
rc = -1;
if (abortOnError && rc != 0)
goto done;
}
done:
return rc;
}
int
virDomainSmartcardDefForeach(virDomainDefPtr def,
bool abortOnError,
virDomainSmartcardDefIterator iter,
void *opaque)
{
size_t i;
int rc = 0;
for (i = 0; i < def->nsmartcards; i++) {
if ((iter)(def,
def->smartcards[i],
opaque) < 0)
rc = -1;
if (abortOnError && rc != 0)
goto done;
}
done:
return rc;
}
/* Call iter(disk, name, depth, opaque) for each element of disk and
* its backing chain in the pre-populated disk->src.backingStore.
* ignoreOpenFailure determines whether to warn about a chain that
* mentions a backing file without also having metadata on that
* file. */
int
virDomainDiskDefForeachPath(virDomainDiskDefPtr disk,
bool ignoreOpenFailure,
virDomainDiskDefPathIterator iter,
void *opaque)
{
int ret = -1;
size_t depth = 0;
virStorageSourcePtr tmp;
char *brokenRaw = NULL;
if (!ignoreOpenFailure) {
if (virStorageFileChainGetBroken(disk->src, &brokenRaw) < 0)
goto cleanup;
if (brokenRaw) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unable to visit backing chain file %s"),
brokenRaw);
goto cleanup;
}
}
for (tmp = disk->src; tmp; tmp = tmp->backingStore) {
int actualType = virStorageSourceGetActualType(tmp);
/* execute the callback only for local storage */
if (actualType != VIR_STORAGE_TYPE_NETWORK &&
actualType != VIR_STORAGE_TYPE_VOLUME &&
tmp->path) {
if (iter(disk, tmp->path, depth, opaque) < 0)
goto cleanup;
}
depth++;
}
ret = 0;
cleanup:
VIR_FREE(brokenRaw);
return ret;
}
/* Copy src into a new definition; with the quality of the copy
* depending on the migratable flag (false for transitions between
* persistent and active, true for transitions across save files or
* snapshots). */
virDomainDefPtr
virDomainDefCopy(virDomainDefPtr src,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
bool migratable)
{
char *xml;
virDomainDefPtr ret;
unsigned int format_flags = VIR_DOMAIN_DEF_FORMAT_SECURE;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
if (migratable)
format_flags |= VIR_DOMAIN_DEF_FORMAT_INACTIVE | VIR_DOMAIN_DEF_FORMAT_MIGRATABLE;
/* Easiest to clone via a round-trip through XML. */
if (!(xml = virDomainDefFormat(src, format_flags)))
return NULL;
ret = virDomainDefParseString(xml, caps, xmlopt, parse_flags);
VIR_FREE(xml);
return ret;
}
virDomainDefPtr
virDomainObjCopyPersistentDef(virDomainObjPtr dom,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt)
{
virDomainDefPtr cur;
cur = virDomainObjGetPersistentDef(caps, xmlopt, dom);
return virDomainDefCopy(cur, caps, xmlopt, false);
}
virDomainState
virDomainObjGetState(virDomainObjPtr dom, int *reason)
{
if (reason)
*reason = dom->state.reason;
return dom->state.state;
}
void
virDomainObjSetState(virDomainObjPtr dom, virDomainState state, int reason)
{
int last = -1;
switch (state) {
case VIR_DOMAIN_NOSTATE:
last = VIR_DOMAIN_NOSTATE_LAST;
break;
case VIR_DOMAIN_RUNNING:
last = VIR_DOMAIN_RUNNING_LAST;
break;
case VIR_DOMAIN_BLOCKED:
last = VIR_DOMAIN_BLOCKED_LAST;
break;
case VIR_DOMAIN_PAUSED:
last = VIR_DOMAIN_PAUSED_LAST;
break;
case VIR_DOMAIN_SHUTDOWN:
last = VIR_DOMAIN_SHUTDOWN_LAST;
break;
case VIR_DOMAIN_SHUTOFF:
last = VIR_DOMAIN_SHUTOFF_LAST;
break;
case VIR_DOMAIN_CRASHED:
last = VIR_DOMAIN_CRASHED_LAST;
break;
case VIR_DOMAIN_PMSUSPENDED:
last = VIR_DOMAIN_PMSUSPENDED_LAST;
break;
default:
last = -1;
}
if (last < 0) {
VIR_ERROR(_("invalid domain state: %d"), state);
return;
}
dom->state.state = state;
if (reason > 0 && reason < last)
dom->state.reason = reason;
else
dom->state.reason = 0;
}
const char *
virDomainStateReasonToString(virDomainState state, int reason)
{
switch (state) {
case VIR_DOMAIN_NOSTATE:
return virDomainNostateReasonTypeToString(reason);
case VIR_DOMAIN_RUNNING:
return virDomainRunningReasonTypeToString(reason);
case VIR_DOMAIN_BLOCKED:
return virDomainBlockedReasonTypeToString(reason);
case VIR_DOMAIN_PAUSED:
return virDomainPausedReasonTypeToString(reason);
case VIR_DOMAIN_SHUTDOWN:
return virDomainShutdownReasonTypeToString(reason);
case VIR_DOMAIN_SHUTOFF:
return virDomainShutoffReasonTypeToString(reason);
case VIR_DOMAIN_CRASHED:
return virDomainCrashedReasonTypeToString(reason);
case VIR_DOMAIN_PMSUSPENDED:
return virDomainPMSuspendedReasonTypeToString(reason);
case VIR_DOMAIN_LAST:
break;
}
VIR_WARN("Unexpected domain state: %d", state);
return NULL;
}
int
virDomainStateReasonFromString(virDomainState state, const char *reason)
{
switch (state) {
case VIR_DOMAIN_NOSTATE:
return virDomainNostateReasonTypeFromString(reason);
case VIR_DOMAIN_RUNNING:
return virDomainRunningReasonTypeFromString(reason);
case VIR_DOMAIN_BLOCKED:
return virDomainBlockedReasonTypeFromString(reason);
case VIR_DOMAIN_PAUSED:
return virDomainPausedReasonTypeFromString(reason);
case VIR_DOMAIN_SHUTDOWN:
return virDomainShutdownReasonTypeFromString(reason);
case VIR_DOMAIN_SHUTOFF:
return virDomainShutoffReasonTypeFromString(reason);
case VIR_DOMAIN_CRASHED:
return virDomainCrashedReasonTypeFromString(reason);
case VIR_DOMAIN_PMSUSPENDED:
return virDomainPMSuspendedReasonTypeFromString(reason);
case VIR_DOMAIN_LAST:
break;
}
VIR_WARN("Unexpected domain state: %d", state);
return -1;
}
/* Some access functions to gloss over the difference between NetDef
* () and ActualNetDef (). If the NetDef has an
* ActualNetDef, return the requested value from the ActualNetDef,
* otherwise return the value from the NetDef.
*/
int
virDomainNetGetActualType(virDomainNetDefPtr iface)
{
if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
return iface->type;
if (!iface->data.network.actual)
return iface->type;
return iface->data.network.actual->type;
}
const char *
virDomainNetGetActualBridgeName(virDomainNetDefPtr iface)
{
if (iface->type == VIR_DOMAIN_NET_TYPE_BRIDGE)
return iface->data.bridge.brname;
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
iface->data.network.actual &&
(iface->data.network.actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE ||
iface->data.network.actual->type == VIR_DOMAIN_NET_TYPE_NETWORK))
return iface->data.network.actual->data.bridge.brname;
return NULL;
}
int
virDomainNetGetActualBridgeMACTableManager(virDomainNetDefPtr iface)
{
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
iface->data.network.actual &&
(iface->data.network.actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE ||
iface->data.network.actual->type == VIR_DOMAIN_NET_TYPE_NETWORK))
return iface->data.network.actual->data.bridge.macTableManager;
return 0;
}
const char *
virDomainNetGetActualDirectDev(virDomainNetDefPtr iface)
{
if (iface->type == VIR_DOMAIN_NET_TYPE_DIRECT)
return iface->data.direct.linkdev;
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
iface->data.network.actual &&
iface->data.network.actual->type == VIR_DOMAIN_NET_TYPE_DIRECT)
return iface->data.network.actual->data.direct.linkdev;
return NULL;
}
int
virDomainNetGetActualDirectMode(virDomainNetDefPtr iface)
{
if (iface->type == VIR_DOMAIN_NET_TYPE_DIRECT)
return iface->data.direct.mode;
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
iface->data.network.actual &&
iface->data.network.actual->type == VIR_DOMAIN_NET_TYPE_DIRECT)
return iface->data.network.actual->data.direct.mode;
return 0;
}
virDomainHostdevDefPtr
virDomainNetGetActualHostdev(virDomainNetDefPtr iface)
{
if (iface->type == VIR_DOMAIN_NET_TYPE_HOSTDEV)
return &iface->data.hostdev.def;
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
iface->data.network.actual &&
iface->data.network.actual->type == VIR_DOMAIN_NET_TYPE_HOSTDEV)
return &iface->data.network.actual->data.hostdev.def;
return NULL;
}
virNetDevVPortProfilePtr
virDomainNetGetActualVirtPortProfile(virDomainNetDefPtr iface)
{
switch (iface->type) {
case VIR_DOMAIN_NET_TYPE_DIRECT:
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
return iface->virtPortProfile;
case VIR_DOMAIN_NET_TYPE_NETWORK:
if (!iface->data.network.actual)
return NULL;
switch (iface->data.network.actual->type) {
case VIR_DOMAIN_NET_TYPE_DIRECT:
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
return iface->data.network.actual->virtPortProfile;
default:
return NULL;
}
default:
return NULL;
}
}
virNetDevBandwidthPtr
virDomainNetGetActualBandwidth(virDomainNetDefPtr iface)
{
/* if there is an ActualNetDef, *always* return
* its bandwidth rather than the NetDef's bandwidth.
*/
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
iface->data.network.actual)
return iface->data.network.actual->bandwidth;
return iface->bandwidth;
}
virNetDevVlanPtr
virDomainNetGetActualVlan(virDomainNetDefPtr iface)
{
virNetDevVlanPtr vlan = &iface->vlan;
/* if there is an ActualNetDef, *always* return
* its vlan rather than the NetDef's vlan.
*/
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
iface->data.network.actual)
vlan = &iface->data.network.actual->vlan;
if (vlan->nTags > 0)
return vlan;
return NULL;
}
bool
virDomainNetGetActualTrustGuestRxFilters(virDomainNetDefPtr iface)
{
if (iface->type == VIR_DOMAIN_NET_TYPE_NETWORK &&
iface->data.network.actual)
return (iface->data.network.actual->trustGuestRxFilters
== VIR_TRISTATE_BOOL_YES);
return iface->trustGuestRxFilters == VIR_TRISTATE_BOOL_YES;
}
/* Return listens[i] from the appropriate union for the graphics
* type, or NULL if this is an unsuitable type, or the index is out of
* bounds. If force0 is TRUE, i == 0, and there is no listen array,
* allocate one with a single item. */
static virDomainGraphicsListenDefPtr
virDomainGraphicsGetListen(virDomainGraphicsDefPtr def, size_t i, bool force0)
{
if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC ||
def->type == VIR_DOMAIN_GRAPHICS_TYPE_RDP ||
def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
if (!def->listens && (i == 0) && force0) {
if (VIR_ALLOC(def->listens) >= 0)
def->nListens = 1;
}
if (!def->listens || (def->nListens <= i))
return NULL;
return &def->listens[i];
}
/* it's a type that has no listens array */
return NULL;
}
/* Access functions for the fields in a virDomainGraphicsDef's
* "listens" array.
*
* NB: For simple backward compatibility with existing code, any of
* the "Set" functions will auto-create listens[0] to store the new
* setting, when necessary. Auto-creation beyond the first item is not
* supported.
*
* Return values: All "Get" functions return the requested item, or
* 0/NULL. (in the case of returned const char *, the caller should
* make a copy if they want to keep it around). All "Set" functions
* return 0 on success, -1 on failure. */
int
virDomainGraphicsListenGetType(virDomainGraphicsDefPtr def, size_t i)
{
virDomainGraphicsListenDefPtr listenInfo
= virDomainGraphicsGetListen(def, i, false);
if (!listenInfo)
return VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE;
return listenInfo->type;
}
/* NB: This function assumes type has not previously been set. It
* *will not* free any existing address or network based on a change
* in value of type. */
int
virDomainGraphicsListenSetType(virDomainGraphicsDefPtr def, size_t i, int val)
{
virDomainGraphicsListenDefPtr listenInfo
= virDomainGraphicsGetListen(def, i, true);
if (!listenInfo)
return -1;
listenInfo->type = val;
return 0;
}
const char *
virDomainGraphicsListenGetAddress(virDomainGraphicsDefPtr def, size_t i)
{
virDomainGraphicsListenDefPtr listenInfo
= virDomainGraphicsGetListen(def, i, false);
/* even a network can have a listen address */
if (!listenInfo ||
!(listenInfo->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS ||
listenInfo->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK))
return NULL;
return listenInfo->address;
}
/* Make a copy of up to len characters of address, and store it in
* listens[i].address. If setType is true, set the listen's type
* to 'address', otherwise leave type alone. */
int
virDomainGraphicsListenSetAddress(virDomainGraphicsDefPtr def,
size_t i, const char *address,
int len, bool setType)
{
virDomainGraphicsListenDefPtr listenInfo
= virDomainGraphicsGetListen(def, i, true);
if (!listenInfo)
return -1;
if (setType)
listenInfo->type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS;
if (!address) {
VIR_FREE(listenInfo->address);
return 0;
}
if (VIR_STRNDUP(listenInfo->address, address, len) < 0)
return -1;
return 0;
}
const char *
virDomainGraphicsListenGetNetwork(virDomainGraphicsDefPtr def, size_t i)
{
virDomainGraphicsListenDefPtr listenInfo
= virDomainGraphicsGetListen(def, i, false);
if (!listenInfo ||
(listenInfo->type != VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK))
return NULL;
return listenInfo->network;
}
/* Make a copy of up to len characters of address, and store it in
* listens[i].network */
int
virDomainGraphicsListenSetNetwork(virDomainGraphicsDefPtr def,
size_t i, const char *network, int len)
{
virDomainGraphicsListenDefPtr listenInfo
= virDomainGraphicsGetListen(def, i, true);
if (!listenInfo)
return -1;
listenInfo->type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK;
if (!network) {
VIR_FREE(listenInfo->network);
return 0;
}
if (VIR_STRNDUP(listenInfo->network, network, len) < 0)
return -1;
return 0;
}
/**
* virDomainNetFind:
* @def: domain's def
* @device: could be the interface name or MAC address
*
* Finds a domain's net def, given the interface name or MAC address
*
* Returns a pointer to the net def or NULL if not found.
*/
virDomainNetDefPtr
virDomainNetFind(virDomainDefPtr def, const char *device)
{
bool isMac = false;
virDomainNetDefPtr net = NULL;
virMacAddr mac;
size_t i;
if (virMacAddrParse(device, &mac) == 0)
isMac = true;
if (isMac) {
for (i = 0; i < def->nnets; i++) {
if (virMacAddrCmp(&mac, &def->nets[i]->mac) == 0) {
net = def->nets[i];
break;
}
}
} else { /* ifname */
for (i = 0; i < def->nnets; i++) {
if (STREQ_NULLABLE(device, def->nets[i]->ifname)) {
net = def->nets[i];
break;
}
}
}
return net;
}
/**
* virDomainDeviceDefCopy:
* @caps: Capabilities
* @def: Domain definition to which @src belongs
* @src: source to be copied
*
* virDomainDeviceDefCopy does a deep copy of only the parts of a
* DeviceDef that are valid when just the flag VIR_DOMAIN_DEF_PARSE_INACTIVE is
* set. This means that any part of the device xml that is conditionally
* parsed/formatted based on some other flag being set (or on the INACTIVE
* flag being reset) *will not* be copied to the destination. Caveat emptor.
*
* Returns a pointer to copied @src or NULL in case of error.
*/
virDomainDeviceDefPtr
virDomainDeviceDefCopy(virDomainDeviceDefPtr src,
const virDomainDef *def,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt)
{
virDomainDeviceDefPtr ret = NULL;
virBuffer buf = VIR_BUFFER_INITIALIZER;
int flags = VIR_DOMAIN_DEF_FORMAT_INACTIVE | VIR_DOMAIN_DEF_FORMAT_SECURE;
char *xmlStr = NULL;
int rc = -1;
switch ((virDomainDeviceType) src->type) {
case VIR_DOMAIN_DEVICE_DISK:
rc = virDomainDiskDefFormat(&buf, src->data.disk, flags);
break;
case VIR_DOMAIN_DEVICE_LEASE:
rc = virDomainLeaseDefFormat(&buf, src->data.lease);
break;
case VIR_DOMAIN_DEVICE_FS:
rc = virDomainFSDefFormat(&buf, src->data.fs, flags);
break;
case VIR_DOMAIN_DEVICE_NET:
rc = virDomainNetDefFormat(&buf, src->data.net, flags);
break;
case VIR_DOMAIN_DEVICE_INPUT:
rc = virDomainInputDefFormat(&buf, src->data.input, flags);
break;
case VIR_DOMAIN_DEVICE_SOUND:
rc = virDomainSoundDefFormat(&buf, src->data.sound, flags);
break;
case VIR_DOMAIN_DEVICE_VIDEO:
rc = virDomainVideoDefFormat(&buf, src->data.video, flags);
break;
case VIR_DOMAIN_DEVICE_HOSTDEV:
rc = virDomainHostdevDefFormat(&buf, src->data.hostdev, flags);
break;
case VIR_DOMAIN_DEVICE_WATCHDOG:
rc = virDomainWatchdogDefFormat(&buf, src->data.watchdog, flags);
break;
case VIR_DOMAIN_DEVICE_CONTROLLER:
rc = virDomainControllerDefFormat(&buf, src->data.controller, flags);
break;
case VIR_DOMAIN_DEVICE_GRAPHICS:
rc = virDomainGraphicsDefFormat(&buf, src->data.graphics, flags);
break;
case VIR_DOMAIN_DEVICE_HUB:
rc = virDomainHubDefFormat(&buf, src->data.hub, flags);
break;
case VIR_DOMAIN_DEVICE_REDIRDEV:
rc = virDomainRedirdevDefFormat(&buf, src->data.redirdev, flags);
break;
case VIR_DOMAIN_DEVICE_RNG:
rc = virDomainRNGDefFormat(&buf, src->data.rng, flags);
break;
case VIR_DOMAIN_DEVICE_CHR:
rc = virDomainChrDefFormat(&buf, src->data.chr, flags);
break;
case VIR_DOMAIN_DEVICE_TPM:
rc = virDomainTPMDefFormat(&buf, src->data.tpm, flags);
break;
case VIR_DOMAIN_DEVICE_PANIC:
rc = virDomainPanicDefFormat(&buf, src->data.panic);
break;
case VIR_DOMAIN_DEVICE_MEMORY:
rc = virDomainMemoryDefFormat(&buf, src->data.memory, flags);
break;
case VIR_DOMAIN_DEVICE_NONE:
case VIR_DOMAIN_DEVICE_SMARTCARD:
case VIR_DOMAIN_DEVICE_MEMBALLOON:
case VIR_DOMAIN_DEVICE_NVRAM:
case VIR_DOMAIN_DEVICE_SHMEM:
case VIR_DOMAIN_DEVICE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Copying definition of '%d' type "
"is not implemented yet."),
src->type);
goto cleanup;
}
if (rc < 0)
goto cleanup;
xmlStr = virBufferContentAndReset(&buf);
ret = virDomainDeviceDefParse(xmlStr, def, caps, xmlopt,
VIR_DOMAIN_DEF_PARSE_INACTIVE);
cleanup:
VIR_FREE(xmlStr);
return ret;
}
#define MATCH(FLAG) (filter & (FLAG))
static bool
virDomainObjMatchFilter(virDomainObjPtr vm,
unsigned int filter)
{
/* filter by active state */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_ACTIVE) &&
!((MATCH(VIR_CONNECT_LIST_DOMAINS_ACTIVE) &&
virDomainObjIsActive(vm)) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_INACTIVE) &&
!virDomainObjIsActive(vm))))
return false;
/* filter by persistence */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_PERSISTENT) &&
!((MATCH(VIR_CONNECT_LIST_DOMAINS_PERSISTENT) &&
vm->persistent) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_TRANSIENT) &&
!vm->persistent)))
return false;
/* filter by domain state */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_STATE)) {
int st = virDomainObjGetState(vm, NULL);
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_RUNNING) &&
st == VIR_DOMAIN_RUNNING) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_PAUSED) &&
st == VIR_DOMAIN_PAUSED) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_SHUTOFF) &&
st == VIR_DOMAIN_SHUTOFF) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_OTHER) &&
(st != VIR_DOMAIN_RUNNING &&
st != VIR_DOMAIN_PAUSED &&
st != VIR_DOMAIN_SHUTOFF))))
return false;
}
/* filter by existence of managed save state */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_MANAGEDSAVE) &&
!((MATCH(VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE) &&
vm->hasManagedSave) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE) &&
!vm->hasManagedSave)))
return false;
/* filter by autostart option */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_AUTOSTART) &&
!((MATCH(VIR_CONNECT_LIST_DOMAINS_AUTOSTART) && vm->autostart) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART) && !vm->autostart)))
return false;
/* filter by snapshot existence */
if (MATCH(VIR_CONNECT_LIST_DOMAINS_FILTERS_SNAPSHOT)) {
int nsnap = virDomainSnapshotObjListNum(vm->snapshots, NULL, 0);
if (!((MATCH(VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT) && nsnap > 0) ||
(MATCH(VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT) && nsnap <= 0)))
return false;
}
return true;
}
#undef MATCH
struct virDomainListData {
virDomainObjPtr *vms;
size_t nvms;
};
static void
virDomainObjListCollectIterator(void *payload,
const void *name ATTRIBUTE_UNUSED,
void *opaque)
{
struct virDomainListData *data = opaque;
data->vms[data->nvms++] = virObjectRef(payload);
}
static void
virDomainObjListFilter(virDomainObjPtr **list,
size_t *nvms,
virConnectPtr conn,
virDomainObjListACLFilter filter,
unsigned int flags)
{
size_t i = 0;
while (i < *nvms) {
virDomainObjPtr vm = (*list)[i];
virObjectLock(vm);
/* do not list the object if:
* 1) it's being removed.
* 2) connection does not have ACL to see it
* 3) it doesn't match the filter
*/
if (vm->removing ||
(filter && !filter(conn, vm->def)) ||
!virDomainObjMatchFilter(vm, flags)) {
virObjectUnlock(vm);
virObjectUnref(vm);
VIR_DELETE_ELEMENT(*list, i, *nvms);
continue;
}
virObjectUnlock(vm);
i++;
}
}
int
virDomainObjListCollect(virDomainObjListPtr domlist,
virConnectPtr conn,
virDomainObjPtr **vms,
size_t *nvms,
virDomainObjListACLFilter filter,
unsigned int flags)
{
struct virDomainListData data = { NULL, 0 };
virObjectLock(domlist);
sa_assert(domlist->objs);
if (VIR_ALLOC_N(data.vms, virHashSize(domlist->objs)) < 0) {
virObjectUnlock(domlist);
return -1;
}
virHashForEach(domlist->objs, virDomainObjListCollectIterator, &data);
virObjectUnlock(domlist);
virDomainObjListFilter(&data.vms, &data.nvms, conn, filter, flags);
*nvms = data.nvms;
*vms = data.vms;
return 0;
}
int
virDomainObjListConvert(virDomainObjListPtr domlist,
virConnectPtr conn,
virDomainPtr *doms,
size_t ndoms,
virDomainObjPtr **vms,
size_t *nvms,
virDomainObjListACLFilter filter,
unsigned int flags,
bool skip_missing)
{
char uuidstr[VIR_UUID_STRING_BUFLEN];
virDomainObjPtr vm;
size_t i;
*nvms = 0;
*vms = NULL;
virObjectLock(domlist);
for (i = 0; i < ndoms; i++) {
virDomainPtr dom = doms[i];
virUUIDFormat(dom->uuid, uuidstr);
if (!(vm = virHashLookup(domlist->objs, uuidstr))) {
if (skip_missing)
continue;
virObjectUnlock(domlist);
virReportError(VIR_ERR_NO_DOMAIN,
_("no domain with matching uuid '%s' (%s)"),
uuidstr, dom->name);
goto error;
}
virObjectRef(vm);
if (VIR_APPEND_ELEMENT(*vms, *nvms, vm) < 0) {
virObjectUnlock(domlist);
virObjectUnref(vm);
goto error;
}
}
virObjectUnlock(domlist);
sa_assert(*vms);
virDomainObjListFilter(vms, nvms, conn, filter, flags);
return 0;
error:
virObjectListFreeCount(*vms, *nvms);
*vms = NULL;
*nvms = 0;
return -1;
}
int
virDomainObjListExport(virDomainObjListPtr domlist,
virConnectPtr conn,
virDomainPtr **domains,
virDomainObjListACLFilter filter,
unsigned int flags)
{
virDomainObjPtr *vms = NULL;
virDomainPtr *doms = NULL;
size_t nvms = 0;
size_t i;
int ret = -1;
if (virDomainObjListCollect(domlist, conn, &vms, &nvms, filter, flags) < 0)
return -1;
if (domains) {
if (VIR_ALLOC_N(doms, nvms + 1) < 0)
goto cleanup;
for (i = 0; i < nvms; i++) {
virDomainObjPtr vm = vms[i];
virObjectLock(vm);
if (!(doms[i] = virGetDomain(conn, vm->def->name, vm->def->uuid))) {
virObjectUnlock(vm);
goto cleanup;
}
doms[i]->id = vm->def->id;
virObjectUnlock(vm);
}
*domains = doms;
doms = NULL;
}
ret = nvms;
cleanup:
virObjectListFree(doms);
virObjectListFreeCount(vms, nvms);
return ret;
}
virSecurityLabelDefPtr
virDomainDefGetSecurityLabelDef(virDomainDefPtr def, const char *model)
{
size_t i;
virSecurityLabelDefPtr seclabel = NULL;
if (def == NULL || model == NULL)
return NULL;
for (i = 0; i < def->nseclabels; i++) {
if (def->seclabels[i]->model == NULL)
continue;
if (STREQ(def->seclabels[i]->model, model))
return def->seclabels[i];
}
return seclabel;
}
virSecurityDeviceLabelDefPtr
virDomainChrDefGetSecurityLabelDef(virDomainChrDefPtr def, const char *model)
{
size_t i;
if (def == NULL)
return NULL;
for (i = 0; i < def->nseclabels; i++) {
if (STREQ_NULLABLE(def->seclabels[i]->model, model))
return def->seclabels[i];
}
return NULL;
}
typedef struct {
const char *devAlias;
virDomainDeviceDefPtr dev;
} virDomainDefFindDeviceCallbackData;
static int
virDomainDefFindDeviceCallback(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr dev,
virDomainDeviceInfoPtr info,
void *opaque)
{
virDomainDefFindDeviceCallbackData *data = opaque;
if (STREQ_NULLABLE(info->alias, data->devAlias)) {
*data->dev = *dev;
return -1;
}
return 0;
}
int
virDomainDefFindDevice(virDomainDefPtr def,
const char *devAlias,
virDomainDeviceDefPtr dev,
bool reportError)
{
virDomainDefFindDeviceCallbackData data = { devAlias, dev };
dev->type = VIR_DOMAIN_DEVICE_NONE;
virDomainDeviceInfoIterateInternal(def, virDomainDefFindDeviceCallback,
true, &data);
if (dev->type == VIR_DOMAIN_DEVICE_NONE) {
if (reportError) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("no device found with alias %s"), devAlias);
} else {
VIR_DEBUG("no device found with alias %s", devAlias);
}
return -1;
}
return 0;
}
/**
* virDomainDiskSourceIsBlockType:
*
* Check if the disk *source* is of block type. This just tries
* to check from the type of disk def, not to probe the underlying
* storage.
*
* Return true if its source is block type, or false otherwise.
*/
bool
virDomainDiskSourceIsBlockType(virStorageSourcePtr src,
bool report)
{
if (!src->path) {
if (report)
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("source path not found for device='lun' "
"using type='%d'"), src->type);
return false;
}
if (src->type == VIR_STORAGE_TYPE_BLOCK)
return true;
/* For volume types, check the srcpool.
* If it's a block type source pool, then it's possible
*/
if (src->type == VIR_STORAGE_TYPE_VOLUME &&
src->srcpool &&
src->srcpool->voltype == VIR_STORAGE_VOL_BLOCK) {
/* We don't think the volume accessed by remote URI is
* block type source, since we can't/shouldn't manage it
* (e.g. set sgio=filtered|unfiltered for it) in libvirt.
*/
if (src->srcpool->pooltype == VIR_STORAGE_POOL_ISCSI &&
src->srcpool->mode == VIR_STORAGE_SOURCE_POOL_MODE_DIRECT) {
if (report)
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disk device='lun' for iSCSI is not "
"supported with mode='direct'."));
return false;
}
return true;
}
if (report)
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disk device='lun' is only valid for block "
"type disk source"));
return false;
}
char *
virDomainObjGetMetadata(virDomainObjPtr vm,
int type,
const char *uri ATTRIBUTE_UNUSED,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
unsigned int flags)
{
virDomainDefPtr def;
char *ret = NULL;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG, NULL);
if (type >= VIR_DOMAIN_METADATA_LAST) {
virReportError(VIR_ERR_INVALID_ARG,
_("unknown metadata type '%d'"), type);
goto cleanup;
}
if (virDomainLiveConfigHelperMethod(caps, xmlopt, vm, &flags, &def) < 0)
goto cleanup;
/* use correct domain definition according to flags */
if (flags & VIR_DOMAIN_AFFECT_LIVE)
def = vm->def;
switch ((virDomainMetadataType) type) {
case VIR_DOMAIN_METADATA_DESCRIPTION:
if (VIR_STRDUP(ret, def->description) < 0)
goto cleanup;
break;
case VIR_DOMAIN_METADATA_TITLE:
if (VIR_STRDUP(ret, def->title) < 0)
goto cleanup;
break;
case VIR_DOMAIN_METADATA_ELEMENT:
if (!def->metadata)
break;
if (virXMLExtractNamespaceXML(def->metadata, uri, &ret) < 0)
goto cleanup;
break;
/* coverity[dead_error_begin] */
case VIR_DOMAIN_METADATA_LAST:
break;
}
if (!ret)
virReportError(VIR_ERR_NO_DOMAIN_METADATA, "%s",
_("Requested metadata element is not present"));
cleanup:
return ret;
}
static int
virDomainDefSetMetadata(virDomainDefPtr def,
int type,
const char *metadata,
const char *key,
const char *uri)
{
xmlDocPtr doc = NULL;
xmlNodePtr old;
xmlNodePtr new = NULL;
char *tmp;
int ret = -1;
if (type >= VIR_DOMAIN_METADATA_LAST) {
virReportError(VIR_ERR_INVALID_ARG,
_("unknown metadata type '%d'"), type);
goto cleanup;
}
switch ((virDomainMetadataType) type) {
case VIR_DOMAIN_METADATA_DESCRIPTION:
if (VIR_STRDUP(tmp, metadata) < 0)
goto cleanup;
VIR_FREE(def->description);
def->description = tmp;
break;
case VIR_DOMAIN_METADATA_TITLE:
if (VIR_STRDUP(tmp, metadata) < 0)
goto cleanup;
VIR_FREE(def->title);
def->title = tmp;
break;
case VIR_DOMAIN_METADATA_ELEMENT:
if (metadata) {
/* parse and modify the xml from the user */
if (!(doc = virXMLParseString(metadata, _("(metadata_xml)"))))
goto cleanup;
if (virXMLInjectNamespace(doc->children, uri, key) < 0)
goto cleanup;
/* create the root node if needed */
if (!def->metadata &&
!(def->metadata = xmlNewNode(NULL, (unsigned char *)"metadata"))) {
virReportOOMError();
goto cleanup;
}
if (!(new = xmlCopyNode(doc->children, 1))) {
virReportOOMError();
goto cleanup;
}
}
/* remove possible other nodes sharing the namespace */
while ((old = virXMLFindChildNodeByNs(def->metadata, uri))) {
xmlUnlinkNode(old);
xmlFreeNode(old);
}
if (new &&
!(xmlAddChild(def->metadata, new))) {
xmlFreeNode(new);
virReportOOMError();
goto cleanup;
}
break;
/* coverity[dead_error_begin] */
case VIR_DOMAIN_METADATA_LAST:
break;
}
ret = 0;
cleanup:
xmlFreeDoc(doc);
return ret;
}
int
virDomainObjSetMetadata(virDomainObjPtr vm,
int type,
const char *metadata,
const char *key,
const char *uri,
virCapsPtr caps,
virDomainXMLOptionPtr xmlopt,
const char *stateDir,
const char *configDir,
unsigned int flags)
{
virDomainDefPtr persistentDef;
virCheckFlags(VIR_DOMAIN_AFFECT_LIVE |
VIR_DOMAIN_AFFECT_CONFIG, -1);
if (virDomainLiveConfigHelperMethod(caps, xmlopt, vm, &flags,
&persistentDef) < 0)
return -1;
if (flags & VIR_DOMAIN_AFFECT_LIVE) {
if (virDomainDefSetMetadata(vm->def, type, metadata, key, uri) < 0)
return -1;
if (virDomainSaveStatus(xmlopt, stateDir, vm) < 0)
return -1;
}
if (flags & VIR_DOMAIN_AFFECT_CONFIG) {
if (virDomainDefSetMetadata(persistentDef, type, metadata, key,
uri) < 0)
return -1;
if (virDomainSaveConfig(configDir, persistentDef) < 0)
return -1;
}
return 0;
}
bool
virDomainDefNeedsPlacementAdvice(virDomainDefPtr def)
{
if (def->placement_mode == VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO)
return true;
if (virDomainNumatuneHasPlacementAuto(def->numa))
return true;
return false;
}
int
virDomainDefCheckDuplicateDiskWWN(virDomainDefPtr def)
{
size_t i;
size_t j;
for (i = 0; i < def->ndisks; i++) {
if (def->disks[i]->wwn) {
for (j = i + 1; j < def->ndisks; j++) {
if (STREQ_NULLABLE(def->disks[i]->wwn,
def->disks[j]->wwn)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Disks '%s' and '%s' have identical WWN"),
def->disks[i]->dst,
def->disks[j]->dst);
return -1;
}
}
}
}
return 0;
}