/* * 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 #include "configmake.h" #include "internal.h" #include "virerror.h" #include "datatypes.h" #include "domain_conf.h" #include "snapshot_conf.h" #include "viralloc.h" #include "verify.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"); /* virDomainVirtType is used to set bits in the expectedVirtTypes bitmask, * verify that it doesn't overflow an unsigned int when shifting */ verify(VIR_DOMAIN_VIRT_LAST <= 32); struct _virDomainObjList { virObjectLockable parent; /* uuid string -> virDomainObj mapping * for O(1), lockless lookup-by-uuid */ virHashTable *objs; }; /* 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"); VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST, "qemu", "kqemu", "kvm", "xen", "lxc", "uml", "openvz", "test", "vmware", "hyperv", "vbox", "phyp", "parallels", "bhyve") VIR_ENUM_IMPL(virDomainOS, VIR_DOMAIN_OSTYPE_LAST, "hvm", "xen", "linux", "exe", "uml", "aix") 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") 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") 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") 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(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") VIR_ENUM_IMPL(virDomainWatchdogAction, VIR_DOMAIN_WATCHDOG_ACTION_LAST, "reset", "shutdown", "poweroff", "pause", "dump", "none") 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); } /** * 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; } static void virDomainObjListDataFree(void *payload, const void *name ATTRIBUTE_UNUSED) { virDomainObjPtr obj = payload; virObjectUnref(obj); } virDomainObjListPtr virDomainObjListNew(void) { virDomainObjListPtr doms; if (virDomainObjInitialize() < 0) return NULL; if (!(doms = virObjectLockableNew(virDomainObjListClass))) return NULL; if (!(doms->objs = virHashCreate(50, virDomainObjListDataFree))) { virObjectUnref(doms); return NULL; } return doms; } static void virDomainObjListDispose(void *obj) { virDomainObjListPtr doms = obj; virHashFree(doms->objs); } 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); } static int virDomainObjListSearchName(const void *payload, const void *name ATTRIBUTE_UNUSED, const void *data) { virDomainObjPtr obj = (virDomainObjPtr)payload; int want = 0; virObjectLock(obj); if (STREQ(obj->def->name, (const char *)data)) want = 1; virObjectUnlock(obj); return want; } virDomainObjPtr virDomainObjListFindByName(virDomainObjListPtr doms, const char *name) { virDomainObjPtr obj; virObjectLock(doms); obj = virHashSearch(doms->objs, virDomainObjListSearchName, name); if (obj) { virObjectLock(obj); if (obj->removing) { virObjectUnlock(obj); obj = NULL; } } virObjectUnlock(doms); 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(void) { virDomainDiskDefPtr ret; if (VIR_ALLOC(ret) < 0) return NULL; if (VIR_ALLOC(ret->src) < 0) goto error; if (virCondInit(&ret->blockJobSyncCond) < 0) { virReportSystemError(errno, "%s", _("Failed to initialize condition")); goto error; } return ret; error: virStorageSourceFree(ret->src); VIR_FREE(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); virCondDestroy(&def->blockJobSyncCond); VIR_FREE(def); } int virDomainDiskGetType(virDomainDiskDefPtr def) { return def->src->type; } void virDomainDiskSetType(virDomainDiskDefPtr def, int type) { def->src->type = type; } const char * virDomainDiskGetSource(virDomainDiskDefPtr 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; } 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); VIR_FREE(def->server.path); 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++) VIR_FREE(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; } 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.type); 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); virDomainPinDefArrayFree(def->cputune.vcpupin, def->cputune.nvcpupin); virDomainPinDefFree(def->cputune.emulatorpin); virDomainPinDefArrayFree(def->cputune.iothreadspin, def->cputune.niothreadspin); 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); 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); 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 (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; } } } /* * * 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 = false; 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 = virHashSearch(doms->objs, virDomainObjListSearchName, 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; } } 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; } /* * 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) { bool isActive; int ret = -1; isActive = virDomainObjIsActive(dom); 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")); goto cleanup; } if (*flags & VIR_DOMAIN_AFFECT_CONFIG) { if (!dom->persistent) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("transient domains do not have any " "persistent config")); goto cleanup; } if (!(*persistentDef = virDomainObjGetPersistentDef(caps, xmlopt, dom))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Get persistent config failed")); goto cleanup; } } ret = 0; cleanup: return ret; } /* * 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); 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); virObjectUnlock(dom); virHashRemoveEntry(doms->objs, uuidstr); } 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); 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) && STREQ_NULLABLE(def->os.type, "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; if (!def->os.type) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("hypervisor type must be specified")); return -1; } /* verify init path for container based domains */ if (STREQ(def->os.type, "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)) { /* Older libvirt could get into this situation due to * rounding; if the discrepancy is less than 4MiB, we silently * round down, otherwise we flag the issue. */ if (VIR_DIV_UP(def->mem.cur_balloon, 4096) > VIR_DIV_UP(virDomainDefGetMemoryActual(def), 4096)) { virReportError(VIR_ERR_XML_ERROR, _("current memory '%lluk' exceeds " "maximum '%lluk'"), def->mem.cur_balloon, virDomainDefGetMemoryActual(def)); return -1; } else { VIR_DEBUG("Truncating current %lluk to maximum %lluk", def->mem.cur_balloon, virDomainDefGetMemoryActual(def)); def->mem.cur_balloon = virDomainDefGetMemoryActual(def); } } else if (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 && STREQ(def->os.type, "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; } } 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; } static int virDomainDeviceDefPostParseInternal(virDomainDeviceDefPtr dev, const virDomainDef *def, virCapsPtr caps ATTRIBUTE_UNUSED) { 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; } } } 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); } return 0; } static int virDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, const virDomainDef *def, virCapsPtr caps, virDomainXMLOptionPtr xmlopt) { int ret; if (xmlopt && xmlopt->config.devicesPostParseCallback) { ret = xmlopt->config.devicesPostParseCallback(dev, def, caps, xmlopt->config.priv); if (ret < 0) return ret; } if ((ret = virDomainDeviceDefPostParseInternal(dev, def, caps)) < 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 && 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) virBufferAsprintf(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_ui(bus, NULL, 0, &scsihostsrc->bus) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse bus '%s'"), bus); goto cleanup; } if (virStrToLong_ui(target, NULL, 0, &scsihostsrc->target) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse target '%s'"), target); goto cleanup; } if (virStrToLong_ui(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; } /* Check if a drive type address $controller:0:0:$unit is already * taken by a disk or not. */ static bool virDomainDriveAddressIsUsedByDisk(const virDomainDef *def, virDomainDiskBus type, unsigned int controller, 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 == 0 && disk->info.addr.drive.target == 0) return true; } return false; } /* Check if a drive type address $controller:0:0:$unit is already * taken by a host device or not. */ static bool virDomainDriveAddressIsUsedByHostdev(const virDomainDef *def, virDomainHostdevSubsysType type, unsigned int controller, 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) continue; if (hostdev->info->addr.drive.controller == controller && hostdev->info->addr.drive.unit == unit && hostdev->info->addr.drive.bus == 0 && hostdev->info->addr.drive.target == 0) return true; } return false; } static bool virDomainSCSIDriveAddressIsUsed(const virDomainDef *def, unsigned int controller, 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, unit) || virDomainDriveAddressIsUsedByHostdev(def, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI, controller, 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++) { if (!virDomainSCSIDriveAddressIsUsed(def, controller, 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; if (hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) return -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; } } 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 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 * /
for an intelligent hostdev (), * but an attribute of the toplevel element for a standard * . (the functions we're going to call expect address * type to already be known). */ if (type) { if ((def->source.subsys.type = virDomainHostdevSubsysTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown host device source address type '%s'"), type); goto error; } } else { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing source address type")); goto error; } if (!(sourcenode = virXPathNode("./source", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing element in hostdev device")); goto error; } if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB && virXPathBoolean("boolean(./source/@startupPolicy)", ctxt)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Setting startupPolicy is only allowed for USB" " devices")); goto error; } if (sgio) { if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) { virReportError(VIR_ERR_XML_ERROR, "%s", _("sgio is only supported for scsi host device")); goto error; } if ((scsisrc->sgio = virDomainDeviceSGIOTypeFromString(sgio)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown sgio mode '%s'"), sgio); goto error; } } if (rawio) { if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) { virReportError(VIR_ERR_XML_ERROR, "%s", _("rawio is only supported for scsi host device")); goto error; } if ((scsisrc->rawio = virTristateBoolTypeFromString(rawio)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown hostdev rawio setting '%s'"), rawio); goto error; } } switch (def->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: if (virDomainHostdevSubsysPCIDefParseXML(sourcenode, def, flags) < 0) goto error; backend = VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT; if ((backendStr = virXPathString("string(./driver/@name)", ctxt)) && (((backend = virDomainHostdevSubsysPCIBackendTypeFromString(backendStr)) < 0) || backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown PCI device " "has been specified"), backendStr); goto error; } pcisrc->backend = backend; break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: if (virDomainHostdevSubsysUSBDefParseXML(sourcenode, def) < 0) goto error; break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: if (virDomainHostdevSubsysSCSIDefParseXML(sourcenode, scsisrc) < 0) goto error; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("address type='%s' not supported in hostdev interfaces"), virDomainHostdevSubsysTypeToString(def->source.subsys.type)); goto error; } ret = 0; error: VIR_FREE(managed); VIR_FREE(sgio); VIR_FREE(rawio); VIR_FREE(backendStr); return ret; } static virDomainNetIpDefPtr virDomainNetIpParseXML(xmlNodePtr node) { /* Parse the prefix in every case */ virDomainNetIpDefPtr ip = NULL; char *prefixStr = NULL; unsigned int prefixValue = 0; char *familyStr = NULL; int family = AF_UNSPEC; char *address = NULL; if (!(prefixStr = virXMLPropString(node, "prefix")) || (virStrToLong_ui(prefixStr, NULL, 10, &prefixValue) < 0)) { // Don't shout, as some old config may not have a prefix VIR_DEBUG("Missing or invalid network prefix"); } if (!(address = virXMLPropString(node, "address"))) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Missing network address")); goto error; } familyStr = virXMLPropString(node, "family"); if (familyStr && STREQ(familyStr, "ipv4")) family = AF_INET; else if (familyStr && STREQ(familyStr, "ipv6")) family = AF_INET6; else family = virSocketAddrNumericFamily(address); if (VIR_ALLOC(ip) < 0) goto error; if (virSocketAddrParse(&ip->address, address, family) < 0) { virReportError(VIR_ERR_INVALID_ARG, _("Failed to parse IP address: '%s'"), address); goto error; } ip->prefix = prefixValue; return ip; error: VIR_FREE(prefixStr); VIR_FREE(familyStr); VIR_FREE(address); VIR_FREE(ip); return NULL; } static int virDomainHostdevDefParseXMLCaps(xmlNodePtr node ATTRIBUTE_UNUSED, xmlXPathContextPtr ctxt, const char *type, virDomainHostdevDefPtr def) { xmlNodePtr sourcenode; xmlNodePtr *ipnodes = NULL; int nipnodes; xmlNodePtr *routenodes = NULL; int nroutenodes; int ret = -1; /* @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 * /
for an intelligent hostdev (), * but an attribute of the toplevel element for a standard * . (the functions we're going to call expect address * type to already be known). */ if (type) { if ((def->source.caps.type = virDomainHostdevCapsTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown host device source address type '%s'"), type); goto error; } } else { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing source address type")); goto error; } if (!(sourcenode = virXPathNode("./source", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing element in hostdev device")); goto error; } switch (def->source.caps.type) { case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: if (!(def->source.caps.u.storage.block = virXPathString("string(./source/block[1])", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing element in hostdev storage device")); goto error; } break; case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: if (!(def->source.caps.u.misc.chardev = virXPathString("string(./source/char[1])", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing element in hostdev character device")); goto error; } break; case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET: if (!(def->source.caps.u.net.iface = virXPathString("string(./source/interface[1])", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing element in hostdev net device")); goto error; } /* Parse possible IP addresses */ if ((nipnodes = virXPathNodeSet("./ip", ctxt, &ipnodes)) < 0) goto error; if (nipnodes) { size_t i; for (i = 0; i < nipnodes; i++) { virDomainNetIpDefPtr ip = virDomainNetIpParseXML(ipnodes[i]); if (!ip) goto error; if (VIR_APPEND_ELEMENT(def->source.caps.u.net.ips, def->source.caps.u.net.nips, ip) < 0) { VIR_FREE(ip); goto error; } } } /* Look for possible gateways */ if ((nroutenodes = virXPathNodeSet("./route", ctxt, &routenodes)) < 0) goto error; if (nroutenodes) { size_t i; for (i = 0; i < nroutenodes; i++) { virNetworkRouteDefPtr route = NULL; if (!(route = virNetworkRouteDefParseXML(_("Domain hostdev device"), routenodes[i], ctxt))) goto error; if (VIR_APPEND_ELEMENT(def->source.caps.u.net.routes, def->source.caps.u.net.nroutes, route) < 0) { virNetworkRouteDefFree(route); goto error; } } } break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("address type='%s' not supported in hostdev interfaces"), virDomainHostdevCapsTypeToString(def->source.caps.type)); goto error; } ret = 0; error: VIR_FREE(ipnodes); VIR_FREE(routenodes); return ret; } int virDomainDeviceFindControllerModel(virDomainDefPtr def, virDomainDeviceInfoPtr info, int controllerType) { int model = -1; size_t i; for (i = 0; i < def->ncontrollers; i++) { if (def->controllers[i]->type == controllerType && def->controllers[i]->idx == info->addr.drive.controller) model = def->controllers[i]->model; } return model; } virDomainDiskDefPtr virDomainDiskFindByBusAndDst(virDomainDefPtr def, int bus, char *dst) { size_t i; if (!dst) return NULL; for (i = 0; i < def->ndisks; i++) { if (def->disks[i]->bus == bus && STREQ(def->disks[i]->dst, dst)) { return def->disks[i]; } } return NULL; } int virDomainDiskDefAssignAddress(virDomainXMLOptionPtr xmlopt, virDomainDiskDefPtr def) { int idx = virDiskNameToIndex(def->dst); if (idx < 0) { virReportError(VIR_ERR_XML_ERROR, _("Unknown disk name '%s' and no address specified"), def->dst); return -1; } switch (def->bus) { case VIR_DOMAIN_DISK_BUS_SCSI: def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; if (xmlopt->config.hasWideSCSIBus) { /* For a wide SCSI bus we define the default mapping to be * 16 units per bus, 1 bus per controller, many controllers. * Unit 7 is the SCSI controller itself. Therefore unit 7 * cannot be assigned to disks and is skipped. */ def->info.addr.drive.controller = idx / 15; def->info.addr.drive.bus = 0; def->info.addr.drive.unit = idx % 15; /* Skip the SCSI controller at unit 7 */ if (def->info.addr.drive.unit >= 7) ++def->info.addr.drive.unit; } else { /* For a narrow SCSI bus we define the default mapping to be * 7 units per bus, 1 bus per controller, many controllers */ def->info.addr.drive.controller = idx / 7; def->info.addr.drive.bus = 0; def->info.addr.drive.unit = idx % 7; } break; case VIR_DOMAIN_DISK_BUS_IDE: /* For IDE we define the default mapping to be 2 units * per bus, 2 bus per controller, many controllers */ def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; def->info.addr.drive.controller = idx / 4; def->info.addr.drive.bus = (idx % 4) / 2; def->info.addr.drive.unit = (idx % 2); break; case VIR_DOMAIN_DISK_BUS_SATA: /* For SATA we define the default mapping to be 6 units * per bus, 1 bus per controller, many controllers */ def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; def->info.addr.drive.controller = idx / 6; def->info.addr.drive.bus = 0; def->info.addr.drive.unit = idx % 6; break; case VIR_DOMAIN_DISK_BUS_FDC: /* For FDC we define the default mapping to be 2 units * per bus, 1 bus per controller, many controllers */ def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; def->info.addr.drive.controller = idx / 2; def->info.addr.drive.bus = 0; def->info.addr.drive.unit = idx % 2; break; default: /* Other disk bus's aren't controller based */ break; } return 0; } static virSecurityLabelDefPtr virSecurityLabelDefParseXML(xmlXPathContextPtr ctxt, unsigned int flags) { char *p; virSecurityLabelDefPtr seclabel = NULL; p = virXPathStringLimit("string(./@model)", VIR_SECURITY_MODEL_BUFLEN - 1, ctxt); if (!(seclabel = virSecurityLabelDefNew(p))) goto error; VIR_FREE(p); /* set default value */ seclabel->type = VIR_DOMAIN_SECLABEL_DYNAMIC; p = virXPathStringLimit("string(./@type)", VIR_SECURITY_LABEL_BUFLEN - 1, ctxt); if (p) { seclabel->type = virDomainSeclabelTypeFromString(p); if (seclabel->type <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid security type '%s'"), p); goto error; } } if (seclabel->type == VIR_DOMAIN_SECLABEL_STATIC || seclabel->type == VIR_DOMAIN_SECLABEL_NONE) seclabel->relabel = false; VIR_FREE(p); p = virXPathStringLimit("string(./@relabel)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p) { if (STREQ(p, "yes")) { seclabel->relabel = true; } else if (STREQ(p, "no")) { seclabel->relabel = false; } else { virReportError(VIR_ERR_XML_ERROR, _("invalid security relabel value %s"), p); goto error; } } VIR_FREE(p); if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC && !seclabel->relabel) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("dynamic label type must use resource relabeling")); goto error; } if (seclabel->type == VIR_DOMAIN_SECLABEL_NONE && seclabel->relabel) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("resource relabeling is not compatible with 'none' label type")); goto error; } /* For the model 'none' none of the following labels is going to be * present. Hence, return now. */ if (STREQ_NULLABLE(seclabel->model, "none")) { if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) { /* Fix older configurations */ seclabel->type = VIR_DOMAIN_SECLABEL_NONE; seclabel->relabel = false; } else { if (seclabel->type != VIR_DOMAIN_SECLABEL_NONE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unsupported type='%s' to model 'none'"), virDomainSeclabelTypeToString(seclabel->type)); goto error; } /* combination of relabel='yes' and type='static' * is checked a few lines above. */ } return seclabel; } /* Only parse label, if using static labels, or * if the 'live' VM XML is requested */ if (seclabel->type == VIR_DOMAIN_SECLABEL_STATIC || (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) && seclabel->type != VIR_DOMAIN_SECLABEL_NONE)) { p = virXPathStringLimit("string(./label[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("security label is missing")); goto error; } seclabel->label = p; p = NULL; } /* Only parse imagelabel, if requested live XML with relabeling */ if (seclabel->relabel && (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) && seclabel->type != VIR_DOMAIN_SECLABEL_NONE)) { p = virXPathStringLimit("string(./imagelabel[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); if (p == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("security imagelabel is missing")); goto error; } seclabel->imagelabel = p; p = NULL; } /* Only parse baselabel for dynamic label type */ if (seclabel->type == VIR_DOMAIN_SECLABEL_DYNAMIC) { p = virXPathStringLimit("string(./baselabel[1])", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); seclabel->baselabel = p; p = NULL; } return seclabel; error: VIR_FREE(p); virSecurityLabelDefFree(seclabel); return NULL; } static int virSecurityLabelDefsParseXML(virDomainDefPtr def, xmlXPathContextPtr ctxt, virCapsPtr caps, unsigned int flags) { size_t i = 0, j; int n; xmlNodePtr *list = NULL, saved_node; virCapsHostPtr host = &caps->host; /* Check args and save context */ if (def == NULL || ctxt == NULL) return 0; saved_node = ctxt->node; /* Allocate a security labels based on XML */ if ((n = virXPathNodeSet("./seclabel", ctxt, &list)) < 0) goto error; if (n == 0) return 0; if (VIR_ALLOC_N(def->seclabels, n) < 0) goto error; /* Parse each "seclabel" tag */ for (i = 0; i < n; i++) { virSecurityLabelDefPtr seclabel; ctxt->node = list[i]; if (!(seclabel = virSecurityLabelDefParseXML(ctxt, flags))) goto error; for (j = 0; j < i; j++) { if (STREQ_NULLABLE(seclabel->model, def->seclabels[j]->model)) { virReportError(VIR_ERR_XML_DETAIL, _("seclabel for model %s is already provided"), seclabel->model); virSecurityLabelDefFree(seclabel); goto error; } } def->seclabels[i] = seclabel; } def->nseclabels = n; ctxt->node = saved_node; VIR_FREE(list); /* libvirt versions prior to 0.10.0 support just a single seclabel element * in guest's XML and model attribute can be suppressed if type is none or * type is dynamic, baselabel is not defined and INACTIVE flag is set. * * To avoid compatibility issues, for this specific case the first model * defined in host's capabilities is used as model for the seclabel. */ if (def->nseclabels == 1 && !def->seclabels[0]->model && host->nsecModels > 0) { if (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_NONE || (def->seclabels[0]->type == VIR_DOMAIN_SECLABEL_DYNAMIC && !def->seclabels[0]->baselabel && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))) { /* Copy model from host. */ VIR_DEBUG("Found seclabel without a model, using '%s'", host->secModels[0].model); if (VIR_STRDUP(def->seclabels[0]->model, host->secModels[0].model) < 0) goto error; if (STREQ(def->seclabels[0]->model, "none") && flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) { /* Fix older configurations */ def->seclabels[0]->type = VIR_DOMAIN_SECLABEL_NONE; def->seclabels[0]->relabel = false; } } else { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing security model in domain seclabel")); goto error; } } /* Checking missing model information */ if (def->nseclabels > 1) { for (; n; n--) { if (def->seclabels[n - 1]->model == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing security model " "when using multiple labels")); goto error; } } } return 0; error: ctxt->node = saved_node; for (; i > 0; i--) virSecurityLabelDefFree(def->seclabels[i - 1]); VIR_FREE(def->seclabels); def->nseclabels = 0; VIR_FREE(list); return -1; } /* Parse the from a disk or character device. */ static int virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr **seclabels_rtn, size_t *nseclabels_rtn, virSecurityLabelDefPtr *vmSeclabels, int nvmSeclabels, xmlXPathContextPtr ctxt, unsigned int flags) { virSecurityDeviceLabelDefPtr *seclabels = NULL; size_t nseclabels = 0; int n; size_t i, j; xmlNodePtr *list = NULL; virSecurityLabelDefPtr vmDef = NULL; char *model, *relabel, *label, *labelskip; if ((n = virXPathNodeSet("./seclabel", ctxt, &list)) < 0) goto error; if (n == 0) return 0; if (VIR_ALLOC_N(seclabels, n) < 0) goto error; nseclabels = n; for (i = 0; i < n; i++) { if (VIR_ALLOC(seclabels[i]) < 0) goto error; } for (i = 0; i < n; i++) { /* get model associated to this override */ model = virXMLPropString(list[i], "model"); if (model) { /* find the security label that it's being overridden */ for (j = 0; j < nvmSeclabels; j++) { if (STREQ(vmSeclabels[j]->model, model)) { vmDef = vmSeclabels[j]; break; } } /* check for duplicate seclabels */ for (j = 0; j < i; j++) { if (STREQ_NULLABLE(model, seclabels[j]->model)) { virReportError(VIR_ERR_XML_DETAIL, _("seclabel for model %s is already provided"), model); goto error; } } seclabels[i]->model = model; } /* Can't use overrides if top-level doesn't allow relabeling. */ if (vmDef && !vmDef->relabel) { virReportError(VIR_ERR_XML_ERROR, "%s", _("label overrides require relabeling to be " "enabled at the domain level")); goto error; } relabel = virXMLPropString(list[i], "relabel"); if (relabel != NULL) { if (STREQ(relabel, "yes")) { seclabels[i]->relabel = true; } else if (STREQ(relabel, "no")) { seclabels[i]->relabel = false; } else { virReportError(VIR_ERR_XML_ERROR, _("invalid security relabel value %s"), relabel); VIR_FREE(relabel); goto error; } VIR_FREE(relabel); } else { seclabels[i]->relabel = true; } /* labelskip is only parsed on live images */ labelskip = virXMLPropString(list[i], "labelskip"); seclabels[i]->labelskip = false; if (labelskip && !(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) seclabels[i]->labelskip = STREQ(labelskip, "yes"); VIR_FREE(labelskip); ctxt->node = list[i]; label = virXPathStringLimit("string(./label)", VIR_SECURITY_LABEL_BUFLEN-1, ctxt); seclabels[i]->label = label; if (label && !seclabels[i]->relabel) { virReportError(VIR_ERR_XML_ERROR, _("Cannot specify a label if relabelling is " "turned off. model=%s"), NULLSTR(seclabels[i]->model)); goto error; } } VIR_FREE(list); *nseclabels_rtn = nseclabels; *seclabels_rtn = seclabels; return 0; error: for (i = 0; i < nseclabels; i++) virSecurityDeviceLabelDefFree(seclabels[i]); VIR_FREE(seclabels); VIR_FREE(list); return -1; } /* Parse the XML definition for a lease */ static virDomainLeaseDefPtr virDomainLeaseDefParseXML(xmlNodePtr node) { virDomainLeaseDefPtr def; xmlNodePtr cur; char *lockspace = NULL; char *key = NULL; char *path = NULL; char *offset = NULL; if (VIR_ALLOC(def) < 0) return NULL; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!key && xmlStrEqual(cur->name, BAD_CAST "key")) { key = (char *)xmlNodeGetContent(cur); } else if (!lockspace && xmlStrEqual(cur->name, BAD_CAST "lockspace")) { lockspace = (char *)xmlNodeGetContent(cur); } else if (!path && xmlStrEqual(cur->name, BAD_CAST "target")) { path = virXMLPropString(cur, "path"); offset = virXMLPropString(cur, "offset"); } } cur = cur->next; } if (!key) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'key' element for lease")); goto error; } if (!path) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'target' element for lease")); goto error; } if (offset && virStrToLong_ull(offset, NULL, 10, &def->offset) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Malformed lease target offset %s"), offset); goto error; } def->key = key; def->lockspace = lockspace; def->path = path; path = key = lockspace = NULL; cleanup: VIR_FREE(lockspace); VIR_FREE(key); VIR_FREE(path); VIR_FREE(offset); return def; error: virDomainLeaseDefFree(def); def = NULL; goto cleanup; } static int virDomainDiskSourcePoolDefParse(xmlNodePtr node, virStorageSourcePoolDefPtr *srcpool) { char *mode = NULL; virStorageSourcePoolDefPtr source; int ret = -1; *srcpool = NULL; if (VIR_ALLOC(source) < 0) return -1; source->pool = virXMLPropString(node, "pool"); source->volume = virXMLPropString(node, "volume"); mode = virXMLPropString(node, "mode"); /* CD-ROM and Floppy allows no source */ if (!source->pool && !source->volume) { ret = 0; goto cleanup; } if (!source->pool || !source->volume) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'pool' and 'volume' must be specified together " "for 'pool' type source")); goto cleanup; } if (mode && (source->mode = virStorageSourcePoolModeTypeFromString(mode)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown source mode '%s' for volume type disk"), mode); goto cleanup; } *srcpool = source; source = NULL; ret = 0; cleanup: virStorageSourcePoolDefFree(source); VIR_FREE(mode); return ret; } int virDomainDiskSourceParse(xmlNodePtr node, xmlXPathContextPtr ctxt, virStorageSourcePtr src) { int ret = -1; char *protocol = NULL; xmlNodePtr saveNode = ctxt->node; ctxt->node = node; switch ((virStorageType)src->type) { case VIR_STORAGE_TYPE_FILE: src->path = virXMLPropString(node, "file"); break; case VIR_STORAGE_TYPE_BLOCK: src->path = virXMLPropString(node, "dev"); break; case VIR_STORAGE_TYPE_DIR: src->path = virXMLPropString(node, "dir"); break; case VIR_STORAGE_TYPE_NETWORK: if (!(protocol = virXMLPropString(node, "protocol"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing network source protocol type")); goto cleanup; } if ((src->protocol = virStorageNetProtocolTypeFromString(protocol)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown protocol type '%s'"), protocol); goto cleanup; } if (!(src->path = virXMLPropString(node, "name")) && src->protocol != VIR_STORAGE_NET_PROTOCOL_NBD) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing name for disk source")); goto cleanup; } /* for historical reasons the volume name for gluster volume is stored * as a part of the path. This is hard to work with when dealing with * relative names. Split out the volume into a separate variable */ if (src->path && src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER) { char *tmp; if (!(tmp = strchr(src->path, '/')) || tmp == src->path) { virReportError(VIR_ERR_XML_ERROR, _("missing volume name or file name in " "gluster source path '%s'"), src->path); goto cleanup; } src->volume = src->path; if (VIR_STRDUP(src->path, tmp) < 0) goto cleanup; tmp[0] = '\0'; } /* snapshot currently works only for remote disks */ src->snapshot = virXPathString("string(./snapshot/@name)", ctxt); /* config file currently only works with remote disks */ src->configFile = virXPathString("string(./config/@file)", ctxt); if (virDomainStorageHostParse(node, &src->hosts, &src->nhosts) < 0) goto cleanup; break; case VIR_STORAGE_TYPE_VOLUME: if (virDomainDiskSourcePoolDefParse(node, &src->srcpool) < 0) goto cleanup; break; case VIR_STORAGE_TYPE_NONE: case VIR_STORAGE_TYPE_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected disk type %s"), virStorageTypeToString(src->type)); goto cleanup; } /* People sometimes pass a bogus '' source path when they mean to omit the * source element completely (e.g. CDROM without media). This is just a * little compatibility check to help those broken apps */ if (src->path && !*src->path) VIR_FREE(src->path); ret = 0; cleanup: VIR_FREE(protocol); ctxt->node = saveNode; return ret; } static int virDomainDiskBackingStoreParse(xmlXPathContextPtr ctxt, virStorageSourcePtr src) { virStorageSourcePtr backingStore = NULL; xmlNodePtr save_ctxt = ctxt->node; xmlNodePtr source; char *type = NULL; char *format = NULL; int ret = -1; if (!(ctxt->node = virXPathNode("./backingStore[*]", ctxt))) { ret = 0; goto cleanup; } if (VIR_ALLOC(backingStore) < 0) goto cleanup; if (!(type = virXMLPropString(ctxt->node, "type"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing disk backing store type")); goto cleanup; } backingStore->type = virStorageTypeFromString(type); if (backingStore->type <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk backing store type '%s'"), type); goto cleanup; } if (!(format = virXPathString("string(./format/@type)", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing disk backing store format")); goto cleanup; } backingStore->format = virStorageFileFormatTypeFromString(format); if (backingStore->format <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk backing store format '%s'"), format); goto cleanup; } if (!(source = virXPathNode("./source", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing disk backing store source")); goto cleanup; } if (virDomainDiskSourceParse(source, ctxt, backingStore) < 0 || virDomainDiskBackingStoreParse(ctxt, backingStore) < 0) goto cleanup; src->backingStore = backingStore; ret = 0; cleanup: if (ret < 0) virStorageSourceFree(backingStore); VIR_FREE(type); VIR_FREE(format); ctxt->node = save_ctxt; return ret; } #define VENDOR_LEN 8 #define PRODUCT_LEN 16 /* Parse the XML definition for a disk * @param node XML nodeset to parse for disk definition */ static virDomainDiskDefPtr virDomainDiskDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, virHashTablePtr bootHash, virSecurityLabelDefPtr* vmSeclabels, int nvmSeclabels, unsigned int flags) { virDomainDiskDefPtr def; xmlNodePtr sourceNode = NULL; xmlNodePtr cur; xmlNodePtr save_ctxt = ctxt->node; char *type = NULL; char *device = NULL; char *snapshot = NULL; char *rawio = NULL; char *sgio = NULL; char *driverName = NULL; char *driverType = NULL; bool source = false; char *target = NULL; char *trans = NULL; char *bus = NULL; char *cachetag = NULL; char *error_policy = NULL; char *rerror_policy = NULL; char *iotag = NULL; char *ioeventfd = NULL; char *event_idx = NULL; char *copy_on_read = NULL; char *driverIOThread = NULL; char *devaddr = NULL; virStorageEncryptionPtr encryption = NULL; char *serial = NULL; char *startupPolicy = NULL; virStorageAuthDefPtr authdef = NULL; char *tray = NULL; char *removable = NULL; char *logical_block_size = NULL; char *physical_block_size = NULL; char *wwn = NULL; char *vendor = NULL; char *product = NULL; char *discard = NULL; char *mirrorFormat = NULL; char *mirrorType = NULL; char *domain_name = NULL; int expected_secret_usage = -1; int auth_secret_usage = -1; int ret = 0; if (!(def = virDomainDiskDefNew())) return NULL; def->geometry.cylinders = 0; def->geometry.heads = 0; def->geometry.sectors = 0; def->geometry.trans = VIR_DOMAIN_DISK_TRANS_DEFAULT; def->blockio.logical_block_size = 0; def->blockio.physical_block_size = 0; ctxt->node = node; type = virXMLPropString(node, "type"); if (type) { if ((def->src->type = virStorageTypeFromString(type)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk type '%s'"), type); goto error; } } else { def->src->type = VIR_STORAGE_TYPE_FILE; } snapshot = virXMLPropString(node, "snapshot"); rawio = virXMLPropString(node, "rawio"); sgio = virXMLPropString(node, "sgio"); cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!source && xmlStrEqual(cur->name, BAD_CAST "source")) { sourceNode = cur; if (virDomainDiskSourceParse(cur, ctxt, def->src) < 0) goto error; source = true; if (def->src->type == VIR_STORAGE_TYPE_NETWORK) { if (def->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI) expected_secret_usage = VIR_SECRET_USAGE_TYPE_ISCSI; else if (def->src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD) expected_secret_usage = VIR_SECRET_USAGE_TYPE_CEPH; } startupPolicy = virXMLPropString(cur, "startupPolicy"); } else if (!target && xmlStrEqual(cur->name, BAD_CAST "target")) { target = virXMLPropString(cur, "dev"); bus = virXMLPropString(cur, "bus"); tray = virXMLPropString(cur, "tray"); removable = virXMLPropString(cur, "removable"); /* HACK: Work around for compat with Xen * driver in previous libvirt releases */ if (target && STRPREFIX(target, "ioemu:")) memmove(target, target+6, strlen(target)-5); } else if (!domain_name && xmlStrEqual(cur->name, BAD_CAST "backenddomain")) { domain_name = virXMLPropString(cur, "name"); } else if (xmlStrEqual(cur->name, BAD_CAST "geometry")) { if (virXPathUInt("string(./geometry/@cyls)", ctxt, &def->geometry.cylinders) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid geometry settings (cyls)")); goto error; } if (virXPathUInt("string(./geometry/@heads)", ctxt, &def->geometry.heads) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid geometry settings (heads)")); goto error; } if (virXPathUInt("string(./geometry/@secs)", ctxt, &def->geometry.sectors) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid geometry settings (secs)")); goto error; } trans = virXMLPropString(cur, "trans"); if (trans) { def->geometry.trans = virDomainDiskGeometryTransTypeFromString(trans); if (def->geometry.trans <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid translation value '%s'"), trans); goto error; } } } else if (xmlStrEqual(cur->name, BAD_CAST "blockio")) { logical_block_size = virXMLPropString(cur, "logical_block_size"); if (logical_block_size && virStrToLong_ui(logical_block_size, NULL, 0, &def->blockio.logical_block_size) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid logical block size '%s'"), logical_block_size); goto error; } physical_block_size = virXMLPropString(cur, "physical_block_size"); if (physical_block_size && virStrToLong_ui(physical_block_size, NULL, 0, &def->blockio.physical_block_size) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid physical block size '%s'"), physical_block_size); goto error; } } else if (!driverName && xmlStrEqual(cur->name, BAD_CAST "driver")) { driverName = virXMLPropString(cur, "name"); driverType = virXMLPropString(cur, "type"); if (STREQ_NULLABLE(driverType, "aio")) { /* In-place conversion to "raw", for Xen back-compat */ driverType[0] = 'r'; driverType[1] = 'a'; driverType[2] = 'w'; } cachetag = virXMLPropString(cur, "cache"); error_policy = virXMLPropString(cur, "error_policy"); rerror_policy = virXMLPropString(cur, "rerror_policy"); iotag = virXMLPropString(cur, "io"); ioeventfd = virXMLPropString(cur, "ioeventfd"); event_idx = virXMLPropString(cur, "event_idx"); copy_on_read = virXMLPropString(cur, "copy_on_read"); discard = virXMLPropString(cur, "discard"); driverIOThread = virXMLPropString(cur, "iothread"); } else if (!def->mirror && xmlStrEqual(cur->name, BAD_CAST "mirror") && !(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) { char *ready; char *blockJob; if (VIR_ALLOC(def->mirror) < 0) goto error; blockJob = virXMLPropString(cur, "job"); if (blockJob) { def->mirrorJob = virDomainBlockJobTypeFromString(blockJob); if (def->mirrorJob <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown mirror job type '%s'"), blockJob); VIR_FREE(blockJob); goto error; } VIR_FREE(blockJob); } else { def->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY; } mirrorType = virXMLPropString(cur, "type"); if (mirrorType) { def->mirror->type = virStorageTypeFromString(mirrorType); if (def->mirror->type <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown mirror backing store " "type '%s'"), mirrorType); goto error; } mirrorFormat = virXPathString("string(./mirror/format/@type)", ctxt); } else { /* For back-compat reasons, we handle a file name * encoded as attributes, even though we prefer * modern output in the style of backingStore */ def->mirror->type = VIR_STORAGE_TYPE_FILE; def->mirror->path = virXMLPropString(cur, "file"); if (!def->mirror->path) { virReportError(VIR_ERR_XML_ERROR, "%s", _("mirror requires file name")); goto error; } if (def->mirrorJob != VIR_DOMAIN_BLOCK_JOB_TYPE_COPY) { virReportError(VIR_ERR_XML_ERROR, "%s", _("mirror without type only supported " "by copy job")); goto error; } mirrorFormat = virXMLPropString(cur, "format"); } if (mirrorFormat) { def->mirror->format = virStorageFileFormatTypeFromString(mirrorFormat); if (def->mirror->format <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown mirror format value '%s'"), mirrorFormat); goto error; } } if (mirrorType) { xmlNodePtr mirrorNode; if (!(mirrorNode = virXPathNode("./mirror/source", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("mirror requires source element")); goto error; } if (virDomainDiskSourceParse(mirrorNode, ctxt, def->mirror) < 0) goto error; } ready = virXMLPropString(cur, "ready"); if (ready) { if ((def->mirrorState = virDomainDiskMirrorStateTypeFromString(ready)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown mirror ready state %s"), ready); VIR_FREE(ready); goto error; } VIR_FREE(ready); } } else if (!authdef && xmlStrEqual(cur->name, BAD_CAST "auth")) { if (!(authdef = virStorageAuthDefParse(node->doc, cur))) goto error; if ((auth_secret_usage = virSecretUsageTypeFromString(authdef->secrettype)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid secret type %s"), authdef->secrettype); goto error; } } else if (xmlStrEqual(cur->name, BAD_CAST "iotune")) { ret = virXPathULongLong("string(./iotune/total_bytes_sec)", ctxt, &def->blkdeviotune.total_bytes_sec); if (ret == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("total throughput limit must be an integer")); goto error; } else if (ret < 0) { def->blkdeviotune.total_bytes_sec = 0; } ret = virXPathULongLong("string(./iotune/read_bytes_sec)", ctxt, &def->blkdeviotune.read_bytes_sec); if (ret == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("read throughput limit must be an integer")); goto error; } else if (ret < 0) { def->blkdeviotune.read_bytes_sec = 0; } ret = virXPathULongLong("string(./iotune/write_bytes_sec)", ctxt, &def->blkdeviotune.write_bytes_sec); if (ret == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("write throughput limit must be an integer")); goto error; } else if (ret < 0) { def->blkdeviotune.write_bytes_sec = 0; } ret = virXPathULongLong("string(./iotune/total_iops_sec)", ctxt, &def->blkdeviotune.total_iops_sec); if (ret == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("total I/O operations limit must be an integer")); goto error; } else if (ret < 0) { def->blkdeviotune.total_iops_sec = 0; } ret = virXPathULongLong("string(./iotune/read_iops_sec)", ctxt, &def->blkdeviotune.read_iops_sec); if (ret == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("read I/O operations limit must be an integer")); goto error; } else if (ret < 0) { def->blkdeviotune.read_iops_sec = 0; } ret = virXPathULongLong("string(./iotune/write_iops_sec)", ctxt, &def->blkdeviotune.write_iops_sec); if (ret == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("write I/O operations limit must be an integer")); goto error; } else if (ret < 0) { def->blkdeviotune.write_iops_sec = 0; } if (virXPathULongLong("string(./iotune/total_bytes_sec_max)", ctxt, &def->blkdeviotune.total_bytes_sec_max) < 0) { def->blkdeviotune.total_bytes_sec_max = 0; } if (virXPathULongLong("string(./iotune/read_bytes_sec_max)", ctxt, &def->blkdeviotune.read_bytes_sec_max) < 0) { def->blkdeviotune.read_bytes_sec_max = 0; } if (virXPathULongLong("string(./iotune/write_bytes_sec_max)", ctxt, &def->blkdeviotune.write_bytes_sec_max) < 0) { def->blkdeviotune.write_bytes_sec_max = 0; } if (virXPathULongLong("string(./iotune/total_iops_sec_max)", ctxt, &def->blkdeviotune.total_iops_sec_max) < 0) { def->blkdeviotune.total_iops_sec_max = 0; } if (virXPathULongLong("string(./iotune/read_iops_sec_max)", ctxt, &def->blkdeviotune.read_iops_sec_max) < 0) { def->blkdeviotune.read_iops_sec_max = 0; } if (virXPathULongLong("string(./iotune/write_iops_sec_max)", ctxt, &def->blkdeviotune.write_iops_sec_max) < 0) { def->blkdeviotune.write_iops_sec_max = 0; } if (virXPathULongLong("string(./iotune/size_iops_sec)", ctxt, &def->blkdeviotune.size_iops_sec) < 0) { def->blkdeviotune.size_iops_sec = 0; } if ((def->blkdeviotune.total_bytes_sec && def->blkdeviotune.read_bytes_sec) || (def->blkdeviotune.total_bytes_sec && def->blkdeviotune.write_bytes_sec)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("total and read/write bytes_sec " "cannot be set at the same time")); goto error; } if ((def->blkdeviotune.total_iops_sec && def->blkdeviotune.read_iops_sec) || (def->blkdeviotune.total_iops_sec && def->blkdeviotune.write_iops_sec)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("total and read/write iops_sec " "cannot be set at the same time")); goto error; } if ((def->blkdeviotune.total_bytes_sec_max && def->blkdeviotune.read_bytes_sec_max) || (def->blkdeviotune.total_bytes_sec_max && def->blkdeviotune.write_bytes_sec_max)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("total and read/write bytes_sec_max " "cannot be set at the same time")); goto error; } if ((def->blkdeviotune.total_iops_sec_max && def->blkdeviotune.read_iops_sec_max) || (def->blkdeviotune.total_iops_sec_max && def->blkdeviotune.write_iops_sec_max)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("total and read/write iops_sec_max " "cannot be set at the same time")); goto error; } } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) { def->src->readonly = true; } else if (xmlStrEqual(cur->name, BAD_CAST "shareable")) { def->src->shared = true; } else if (xmlStrEqual(cur->name, BAD_CAST "transient")) { def->transient = true; } else if ((flags & VIR_DOMAIN_DEF_PARSE_STATUS) && xmlStrEqual(cur->name, BAD_CAST "state")) { /* Legacy back-compat. Don't add any more attributes here */ devaddr = virXMLPropString(cur, "devaddr"); } else if (encryption == NULL && xmlStrEqual(cur->name, BAD_CAST "encryption")) { encryption = virStorageEncryptionParseNode(node->doc, cur); if (encryption == NULL) goto error; } else if (!serial && xmlStrEqual(cur->name, BAD_CAST "serial")) { serial = (char *)xmlNodeGetContent(cur); } else if (!wwn && xmlStrEqual(cur->name, BAD_CAST "wwn")) { wwn = (char *)xmlNodeGetContent(cur); if (!virValidateWWN(wwn)) goto error; } else if (!vendor && xmlStrEqual(cur->name, BAD_CAST "vendor")) { vendor = (char *)xmlNodeGetContent(cur); if (strlen(vendor) > VENDOR_LEN) { virReportError(VIR_ERR_XML_ERROR, "%s", _("disk vendor is more than 8 characters")); goto error; } if (!virStrIsPrint(vendor)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("disk vendor is not printable string")); goto error; } } else if (!product && xmlStrEqual(cur->name, BAD_CAST "product")) { product = (char *)xmlNodeGetContent(cur); if (strlen(product) > PRODUCT_LEN) { virReportError(VIR_ERR_XML_ERROR, "%s", _("disk product is more than 16 characters")); goto error; } if (!virStrIsPrint(product)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("disk product is not printable string")); goto error; } } else if (xmlStrEqual(cur->name, BAD_CAST "boot")) { /* boot is parsed as part of virDomainDeviceInfoParseXML */ } } cur = cur->next; } if (auth_secret_usage != -1 && auth_secret_usage != expected_secret_usage) { virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid secret type '%s'"), virSecretUsageTypeToString(auth_secret_usage)); goto error; } device = virXMLPropString(node, "device"); if (device) { if ((def->device = virDomainDiskDeviceTypeFromString(device)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk device '%s'"), device); goto error; } } else { def->device = VIR_DOMAIN_DISK_DEVICE_DISK; } /* Only CDROM and Floppy devices are allowed missing source path * to indicate no media present. LUN is for raw access CD-ROMs * that are not attached to a physical device presently */ if (virStorageSourceIsEmpty(def->src) && (def->device == VIR_DOMAIN_DISK_DEVICE_DISK || (flags & VIR_DOMAIN_DEF_PARSE_DISK_SOURCE))) { virReportError(VIR_ERR_NO_SOURCE, target ? "%s" : NULL, target); goto error; } /* If source is present, check for an optional seclabel override. */ if (sourceNode) { xmlNodePtr saved_node = ctxt->node; ctxt->node = sourceNode; if (virSecurityDeviceLabelDefParseXML(&def->src->seclabels, &def->src->nseclabels, vmSeclabels, nvmSeclabels, ctxt, flags) < 0) goto error; ctxt->node = saved_node; } if (!target && !(flags & VIR_DOMAIN_DEF_PARSE_DISK_SOURCE)) { if (def->src->srcpool) { char *tmp; if (virAsprintf(&tmp, "pool = '%s', volume = '%s'", def->src->srcpool->pool, def->src->srcpool->volume) < 0) goto error; virReportError(VIR_ERR_NO_TARGET, "%s", tmp); VIR_FREE(tmp); } else { virReportError(VIR_ERR_NO_TARGET, def->src->path ? "%s" : NULL, def->src->path); } goto error; } if (!(flags & VIR_DOMAIN_DEF_PARSE_DISK_SOURCE)) { if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY && !STRPREFIX(target, "fd")) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid floppy device name: %s"), target); goto error; } /* Force CDROM to be listed as read only */ if (def->device == VIR_DOMAIN_DISK_DEVICE_CDROM) def->src->readonly = true; if ((def->device == VIR_DOMAIN_DISK_DEVICE_DISK || def->device == VIR_DOMAIN_DISK_DEVICE_LUN) && !STRPREFIX((const char *)target, "hd") && !STRPREFIX((const char *)target, "sd") && !STRPREFIX((const char *)target, "vd") && !STRPREFIX((const char *)target, "xvd") && !STRPREFIX((const char *)target, "ubd")) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid harddisk device name: %s"), target); goto error; } } if (snapshot) { def->snapshot = virDomainSnapshotLocationTypeFromString(snapshot); if (def->snapshot <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk snapshot setting '%s'"), snapshot); goto error; } } else if (def->src->readonly) { def->snapshot = VIR_DOMAIN_SNAPSHOT_LOCATION_NONE; } if ((rawio || sgio) && (def->device != VIR_DOMAIN_DISK_DEVICE_LUN)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("rawio or sgio can be used only with " "device='lun'")); goto error; } if (rawio) { if ((def->rawio = virTristateBoolTypeFromString(rawio)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown disk rawio setting '%s'"), rawio); goto error; } } if (sgio) { if ((def->sgio = virDomainDeviceSGIOTypeFromString(sgio)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk sgio mode '%s'"), sgio); goto error; } } if (bus) { if ((def->bus = virDomainDiskBusTypeFromString(bus)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk bus type '%s'"), bus); goto error; } } else { if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) { def->bus = VIR_DOMAIN_DISK_BUS_FDC; } else if (!(flags & VIR_DOMAIN_DEF_PARSE_DISK_SOURCE)) { if (STRPREFIX(target, "hd")) def->bus = VIR_DOMAIN_DISK_BUS_IDE; else if (STRPREFIX(target, "sd")) def->bus = VIR_DOMAIN_DISK_BUS_SCSI; else if (STRPREFIX(target, "vd")) def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO; else if (STRPREFIX(target, "xvd")) def->bus = VIR_DOMAIN_DISK_BUS_XEN; else if (STRPREFIX(target, "ubd")) def->bus = VIR_DOMAIN_DISK_BUS_UML; else def->bus = VIR_DOMAIN_DISK_BUS_IDE; } } if (tray) { if ((def->tray_status = virDomainDiskTrayTypeFromString(tray)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk tray status '%s'"), tray); goto error; } if (def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY && def->device != VIR_DOMAIN_DISK_DEVICE_CDROM) { virReportError(VIR_ERR_XML_ERROR, "%s", _("tray is only valid for cdrom and floppy")); goto error; } } else { if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY || def->device == VIR_DOMAIN_DISK_DEVICE_CDROM) def->tray_status = VIR_DOMAIN_DISK_TRAY_CLOSED; } if (removable) { if ((def->removable = virTristateSwitchTypeFromString(removable)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk removable status '%s'"), removable); goto error; } if (def->bus != VIR_DOMAIN_DISK_BUS_USB) { virReportError(VIR_ERR_XML_ERROR, "%s", _("removable is only valid for usb disks")); goto error; } } else { if (def->bus == VIR_DOMAIN_DISK_BUS_USB) def->removable = VIR_TRISTATE_SWITCH_ABSENT; } if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY && def->bus != VIR_DOMAIN_DISK_BUS_FDC) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid bus type '%s' for floppy disk"), bus); goto error; } if (def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY && def->bus == VIR_DOMAIN_DISK_BUS_FDC) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid bus type '%s' for disk"), bus); goto error; } if (cachetag && (def->cachemode = virDomainDiskCacheTypeFromString(cachetag)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk cache mode '%s'"), cachetag); goto error; } if (error_policy && (def->error_policy = virDomainDiskErrorPolicyTypeFromString(error_policy)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk error policy '%s'"), error_policy); goto error; } if (rerror_policy && (((def->rerror_policy = virDomainDiskErrorPolicyTypeFromString(rerror_policy)) <= 0) || (def->rerror_policy == VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk read error policy '%s'"), rerror_policy); goto error; } if (iotag) { if ((def->iomode = virDomainDiskIoTypeFromString(iotag)) < 0 || def->iomode == VIR_DOMAIN_DISK_IO_DEFAULT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk io mode '%s'"), iotag); goto error; } } if (ioeventfd) { int val; if (def->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("disk ioeventfd mode supported " "only for virtio bus")); goto error; } if ((val = virTristateSwitchTypeFromString(ioeventfd)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk ioeventfd mode '%s'"), ioeventfd); goto error; } def->ioeventfd = val; } if (event_idx) { if (def->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("disk event_idx mode supported " "only for virtio bus")); goto error; } int idx; if ((idx = virTristateSwitchTypeFromString(event_idx)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk event_idx mode '%s'"), event_idx); goto error; } def->event_idx = idx; } if (copy_on_read) { int cor; if ((cor = virTristateSwitchTypeFromString(copy_on_read)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk copy_on_read mode '%s'"), copy_on_read); goto error; } def->copy_on_read = cor; } if (discard) { if ((def->discard = virDomainDiskDiscardTypeFromString(discard)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk discard mode '%s'"), discard); goto error; } } if (driverIOThread) { if (virStrToLong_uip(driverIOThread, NULL, 10, &def->iothread) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid iothread attribute in disk driver " "element: %s"), driverIOThread); goto error; } } if (devaddr) { if (virDomainParseLegacyDeviceAddress(devaddr, &def->info.addr.pci) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to parse devaddr parameter '%s'"), devaddr); goto error; } def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; } else { if (virDomainDeviceInfoParseXML(node, bootHash, &def->info, flags | VIR_DOMAIN_DEF_PARSE_ALLOW_BOOT) < 0) goto error; } if (startupPolicy) { int val; if ((val = virDomainStartupPolicyTypeFromString(startupPolicy)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown startupPolicy value '%s'"), startupPolicy); goto error; } if (def->src->type == VIR_STORAGE_TYPE_NETWORK) { virReportError(VIR_ERR_XML_ERROR, _("Setting disk %s is not allowed for " "disk of network type"), startupPolicy); goto error; } if (def->device != VIR_DOMAIN_DISK_DEVICE_CDROM && def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY && val == VIR_DOMAIN_STARTUP_POLICY_REQUISITE) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Setting disk 'requisite' is allowed only for " "cdrom or floppy")); goto error; } def->startupPolicy = val; } def->dst = target; target = NULL; def->src->auth = authdef; authdef = NULL; def->src->driverName = driverName; driverName = NULL; def->src->encryption = encryption; encryption = NULL; def->domain_name = domain_name; domain_name = NULL; def->serial = serial; serial = NULL; def->wwn = wwn; wwn = NULL; def->vendor = vendor; vendor = NULL; def->product = product; product = NULL; if (driverType) { def->src->format = virStorageFileFormatTypeFromString(driverType); if (def->src->format <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown driver format value '%s'"), driverType); goto error; } } if (!(flags & VIR_DOMAIN_DEF_PARSE_DISK_SOURCE)) { if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && virDomainDiskDefAssignAddress(xmlopt, def) < 0) goto error; if (virDomainDiskBackingStoreParse(ctxt, def->src) < 0) goto error; } cleanup: VIR_FREE(bus); VIR_FREE(type); VIR_FREE(snapshot); VIR_FREE(rawio); VIR_FREE(sgio); VIR_FREE(target); VIR_FREE(tray); VIR_FREE(removable); VIR_FREE(trans); VIR_FREE(device); virStorageAuthDefFree(authdef); VIR_FREE(driverType); VIR_FREE(driverName); VIR_FREE(cachetag); VIR_FREE(error_policy); VIR_FREE(rerror_policy); VIR_FREE(iotag); VIR_FREE(ioeventfd); VIR_FREE(event_idx); VIR_FREE(copy_on_read); VIR_FREE(discard); VIR_FREE(driverIOThread); VIR_FREE(devaddr); VIR_FREE(serial); virStorageEncryptionFree(encryption); VIR_FREE(startupPolicy); VIR_FREE(logical_block_size); VIR_FREE(physical_block_size); VIR_FREE(wwn); VIR_FREE(vendor); VIR_FREE(product); VIR_FREE(mirrorType); VIR_FREE(mirrorFormat); VIR_FREE(domain_name); ctxt->node = save_ctxt; return def; error: virDomainDiskDefFree(def); def = NULL; goto cleanup; } /** * virDomainParseScaledValue: * @xpath: XPath to memory amount * @units_xpath: XPath to units attribute * @ctxt: XPath context * @val: scaled value is stored here * @scale: default scale for @val * @max: maximal @val allowed * @required: is the value required? * * Parse a value located at @xpath within @ctxt, and store the * result into @val. The value is scaled by units located at * @units_xpath (or the 'unit' attribute under @xpath if * @units_xpath is NULL). If units are not present, the default * @scale is used. If @required is set, then the value must * exist; otherwise, the value is optional. The resulting value * is in bytes. * * Returns 1 on success, * 0 if the value was not present and !@required, * -1 on failure after issuing error. */ static int virDomainParseScaledValue(const char *xpath, const char *units_xpath, xmlXPathContextPtr ctxt, unsigned long long *val, unsigned long long scale, unsigned long long max, bool required) { char *xpath_full = NULL; char *unit = NULL; char *bytes_str = NULL; int ret = -1; unsigned long long bytes; *val = 0; if (virAsprintf(&xpath_full, "string(%s)", xpath) < 0) goto cleanup; bytes_str = virXPathString(xpath_full, ctxt); if (!bytes_str) { if (!required) { ret = 0; } else { virReportError(VIR_ERR_XML_ERROR, _("missing element or attribute '%s'"), xpath); } goto cleanup; } VIR_FREE(xpath_full); if (virStrToLong_ullp(bytes_str, NULL, 10, &bytes) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid value '%s' for element or attribute '%s'"), bytes_str, xpath); goto cleanup; } if ((units_xpath && virAsprintf(&xpath_full, "string(%s)", units_xpath) < 0) || (!units_xpath && virAsprintf(&xpath_full, "string(%s/@unit)", xpath) < 0)) goto cleanup; unit = virXPathString(xpath_full, ctxt); if (virScaleInteger(&bytes, unit, scale, max) < 0) goto cleanup; *val = bytes; ret = 1; cleanup: VIR_FREE(bytes_str); VIR_FREE(xpath_full); VIR_FREE(unit); return ret; } /** * virDomainParseMemory: * @xpath: XPath to memory amount * @units_xpath: XPath to units attribute * @ctxt: XPath context * @mem: scaled memory amount is stored here * @required: whether value is required * @capped: whether scaled value must fit within unsigned long * * Parse a memory element or attribute located at @xpath within * @ctxt, and store the result into @mem, in blocks of 1024. The * value is scaled by units located at @units_xpath (or the * 'unit' attribute under @xpath if @units_xpath is NULL). If * units are not present, he default scale of 1024 is used. If * @required is set, then the value must exist; otherwise, the * value is optional. The value must not exceed * VIR_DOMAIN_MEMORY_PARAM_UNLIMITED once scaled; additionally, * if @capped is true, the value must fit within an unsigned long * (only matters on 32-bit platforms). * * Return 0 on success, -1 on failure after issuing error. */ int virDomainParseMemory(const char *xpath, const char *units_xpath, xmlXPathContextPtr ctxt, unsigned long long *mem, bool required, bool capped) { int ret = -1; unsigned long long bytes, max; /* On 32-bit machines, our bound is 0xffffffff * KiB. On 64-bit * machines, our bound is off_t (2^63). */ if (capped && sizeof(unsigned long) < sizeof(long long)) max = 1024ull * ULONG_MAX; else max = LLONG_MAX; ret = virDomainParseScaledValue(xpath, units_xpath, ctxt, &bytes, 1024, max, required); if (ret < 0) goto cleanup; /* Yes, we really do use kibibytes for our internal sizing. */ *mem = VIR_DIV_UP(bytes, 1024); ret = 0; cleanup: return ret; } /** * virDomainParseMemoryLimit: * * @xpath: XPath to memory amount * @units_xpath: XPath to units attribute * @ctxt: XPath context * @mem: scaled memory amount is stored here * * Parse a memory element or attribute located at @xpath within @ctxt, and * store the result into @mem, in blocks of 1024. The value is scaled by * units located at @units_xpath (or the 'unit' attribute under @xpath if * @units_xpath is NULL). If units are not present, he default scale of 1024 * is used. The value must not exceed VIR_DOMAIN_MEMORY_PARAM_UNLIMITED * once scaled. * * This helper should be used only on *_limit memory elements. * * Return 0 on success, -1 on failure after issuing error. */ static int virDomainParseMemoryLimit(const char *xpath, const char *units_xpath, xmlXPathContextPtr ctxt, unsigned long long *mem) { int ret; unsigned long long bytes; ret = virDomainParseScaledValue(xpath, units_xpath, ctxt, &bytes, 1024, VIR_DOMAIN_MEMORY_PARAM_UNLIMITED << 10, false); if (ret < 0) return -1; if (ret == 0) *mem = VIR_DOMAIN_MEMORY_PARAM_UNLIMITED; else *mem = virMemoryLimitTruncate(VIR_DIV_UP(bytes, 1024)); return 0; } /** * virDomainDefGetMemoryInitial: * @def: domain definition * * Returns the size of the initial amount of guest memory. The initial amount * is the memory size is either the configured amount in the element * or the sum of memory sizes of NUMA nodes in case NUMA is enabled in @def. */ unsigned long long virDomainDefGetMemoryInitial(virDomainDefPtr def) { unsigned long long ret; size_t i; /* return NUMA memory size total in case numa is enabled */ if ((ret = virDomainNumaGetMemorySize(def->numa)) > 0) { return ret; } else { ret = def->mem.max_balloon; for (i = 0; i < def->nmems; i++) ret -= def->mems[i]->size; } return def->mem.max_balloon; } /** * virDomainDefSetMemoryInitial: * @def: domain definition * @size: size to set * * Sets the initial memory size in @def. */ void virDomainDefSetMemoryInitial(virDomainDefPtr def, unsigned long long size) { def->mem.max_balloon = size; } /** * virDomainDefGetMemoryActual: * @def: domain definition * * Returns the current maximum memory size usable by the domain described by * @def. This size is a sum of size returned by virDomainDefGetMemoryInitial * and possible additional memory devices. */ unsigned long long virDomainDefGetMemoryActual(virDomainDefPtr def) { unsigned long long ret; size_t i; if ((ret = virDomainNumaGetMemorySize(def->numa)) > 0) { for (i = 0; i < def->nmems; i++) ret += def->mems[i]->size; } else { ret = def->mem.max_balloon; } return ret; } static int virDomainControllerModelTypeFromString(const virDomainControllerDef *def, const char *model) { if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) return virDomainControllerModelSCSITypeFromString(model); else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) return virDomainControllerModelUSBTypeFromString(model); else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) return virDomainControllerModelPCITypeFromString(model); return -1; } /* Parse the XML definition for a controller * @param node XML nodeset to parse for controller definition */ static virDomainControllerDefPtr virDomainControllerDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainControllerDefPtr def; xmlNodePtr cur = NULL; char *type = NULL; char *idx = NULL; char *model = NULL; char *queues = NULL; char *cmd_per_lun = NULL; char *max_sectors = NULL; xmlNodePtr saved = ctxt->node; int rc; ctxt->node = node; if (VIR_ALLOC(def) < 0) return NULL; type = virXMLPropString(node, "type"); if (type) { if ((def->type = virDomainControllerTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown controller type '%s'"), type); goto error; } } idx = virXMLPropString(node, "index"); if (idx) { if (virStrToLong_ui(idx, NULL, 10, &def->idx) < 0 || def->idx > INT_MAX) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot parse controller index %s"), idx); goto error; } } model = virXMLPropString(node, "model"); if (model) { if ((def->model = virDomainControllerModelTypeFromString(def, model)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown model type '%s'"), model); goto error; } } else { def->model = -1; } cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (xmlStrEqual(cur->name, BAD_CAST "driver")) { queues = virXMLPropString(cur, "queues"); cmd_per_lun = virXMLPropString(cur, "cmd_per_lun"); max_sectors = virXMLPropString(cur, "max_sectors"); } } cur = cur->next; } if (queues && virStrToLong_ui(queues, NULL, 10, &def->queues) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Malformed 'queues' value '%s'"), queues); goto error; } if (cmd_per_lun && virStrToLong_ui(cmd_per_lun, NULL, 10, &def->cmd_per_lun) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Malformed 'cmd_per_lun' value '%s'"), cmd_per_lun); goto error; } if (max_sectors && virStrToLong_ui(max_sectors, NULL, 10, &def->max_sectors) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Malformed 'max_sectors' value %s'"), max_sectors); goto error; } if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && def->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) { VIR_DEBUG("Ignoring device address for none model usb controller"); } else if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) { goto error; } switch (def->type) { case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: { char *ports = virXMLPropString(node, "ports"); if (ports) { int r = virStrToLong_i(ports, NULL, 10, &def->opts.vioserial.ports); if (r != 0 || def->opts.vioserial.ports < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid ports: %s"), ports); VIR_FREE(ports); goto error; } } else { def->opts.vioserial.ports = -1; } VIR_FREE(ports); char *vectors = virXMLPropString(node, "vectors"); if (vectors) { int r = virStrToLong_i(vectors, NULL, 10, &def->opts.vioserial.vectors); if (r != 0 || def->opts.vioserial.vectors < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid vectors: %s"), vectors); VIR_FREE(vectors); goto error; } } else { def->opts.vioserial.vectors = -1; } VIR_FREE(vectors); break; } case VIR_DOMAIN_CONTROLLER_TYPE_USB: { /* If the XML has a uhci1, uhci2, uhci3 controller and no * master port was given, we should set a sensible one */ int masterPort = -1; switch (def->model) { case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1: masterPort = 0; break; case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2: masterPort = 2; break; case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3: masterPort = 4; break; } if (masterPort != -1 && def->info.mastertype == VIR_DOMAIN_CONTROLLER_MASTER_NONE) { def->info.mastertype = VIR_DOMAIN_CONTROLLER_MASTER_USB; def->info.master.usb.startport = masterPort; } break; } case VIR_DOMAIN_CONTROLLER_TYPE_PCI: switch (def->model) { case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT: case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT: { unsigned long long bytes; if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { virReportError(VIR_ERR_XML_ERROR, "%s", _("pci-root and pcie-root controllers should not " "have an address")); goto error; } if (def->idx != 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("pci-root and pcie-root controllers " "should have index 0")); goto error; } if ((rc = virDomainParseScaledValue("./pcihole64", NULL, ctxt, &bytes, 1024, 1024ULL * ULONG_MAX, false)) < 0) goto error; if (rc == 1) def->opts.pciopts.pcihole64 = true; def->opts.pciopts.pcihole64size = VIR_DIV_UP(bytes, 1024); } } default: break; } if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390 && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Controllers must use the 'pci' address type")); goto error; } cleanup: ctxt->node = saved; VIR_FREE(type); VIR_FREE(idx); VIR_FREE(model); VIR_FREE(queues); VIR_FREE(cmd_per_lun); VIR_FREE(max_sectors); return def; error: virDomainControllerDefFree(def); def = NULL; goto cleanup; } void virDomainNetGenerateMAC(virDomainXMLOptionPtr xmlopt, virMacAddrPtr mac) { virMacAddrGenerate(xmlopt->config.macPrefix, mac); } /* Parse the XML definition for a disk * @param node XML nodeset to parse for disk definition */ static virDomainFSDefPtr virDomainFSDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainFSDefPtr def; xmlNodePtr cur, save_node = ctxt->node; char *type = NULL; char *fsdriver = NULL; char *source = NULL; char *target = NULL; char *format = NULL; char *accessmode = NULL; char *wrpolicy = NULL; char *usage = NULL; char *units = NULL; ctxt->node = node; if (VIR_ALLOC(def) < 0) return NULL; type = virXMLPropString(node, "type"); if (type) { if ((def->type = virDomainFSTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown filesystem type '%s'"), type); goto error; } } else { def->type = VIR_DOMAIN_FS_TYPE_MOUNT; } accessmode = virXMLPropString(node, "accessmode"); if (accessmode) { if ((def->accessmode = virDomainFSAccessModeTypeFromString(accessmode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown accessmode '%s'"), accessmode); goto error; } } else { def->accessmode = VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH; } if (virDomainParseScaledValue("./space_hard_limit[1]", NULL, ctxt, &def->space_hard_limit, 1, ULLONG_MAX, false) < 0) goto error; if (virDomainParseScaledValue("./space_soft_limit[1]", NULL, ctxt, &def->space_soft_limit, 1, ULLONG_MAX, false) < 0) goto error; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!source && xmlStrEqual(cur->name, BAD_CAST "source")) { if (def->type == VIR_DOMAIN_FS_TYPE_MOUNT || def->type == VIR_DOMAIN_FS_TYPE_BIND) { source = virXMLPropString(cur, "dir"); } else if (def->type == VIR_DOMAIN_FS_TYPE_FILE) { source = virXMLPropString(cur, "file"); } else if (def->type == VIR_DOMAIN_FS_TYPE_BLOCK) { source = virXMLPropString(cur, "dev"); } else if (def->type == VIR_DOMAIN_FS_TYPE_TEMPLATE) { source = virXMLPropString(cur, "name"); } else if (def->type == VIR_DOMAIN_FS_TYPE_RAM) { usage = virXMLPropString(cur, "usage"); units = virXMLPropString(cur, "units"); } } else if (!target && xmlStrEqual(cur->name, BAD_CAST "target")) { target = virXMLPropString(cur, "dir"); } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) { def->readonly = true; } else if (xmlStrEqual(cur->name, BAD_CAST "driver")) { if (!fsdriver) fsdriver = virXMLPropString(cur, "type"); if (!wrpolicy) wrpolicy = virXMLPropString(cur, "wrpolicy"); if (!format) format = virXMLPropString(cur, "format"); } } cur = cur->next; } if (fsdriver) { if ((def->fsdriver = virDomainFSDriverTypeFromString(fsdriver)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown fs driver type '%s'"), fsdriver); goto error; } } if (format) { if ((def->format = virStorageFileFormatTypeFromString(format)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown driver format value '%s'"), format); goto error; } } if (wrpolicy) { if ((def->wrpolicy = virDomainFSWrpolicyTypeFromString(wrpolicy)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown filesystem write policy '%s'"), wrpolicy); goto error; } } else { def->wrpolicy = VIR_DOMAIN_FS_WRPOLICY_DEFAULT; } if (source == NULL && def->type != VIR_DOMAIN_FS_TYPE_RAM) { virReportError(VIR_ERR_NO_SOURCE, target ? "%s" : NULL, target); goto error; } if (target == NULL) { virReportError(VIR_ERR_NO_TARGET, source ? "%s" : NULL, source); goto error; } if (def->type == VIR_DOMAIN_FS_TYPE_RAM) { if (!usage) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing 'usage' attribute for RAM filesystem")); goto error; } if (virStrToLong_ull(usage, NULL, 10, &def->usage) < 0) { virReportError(VIR_ERR_XML_ERROR, _("cannot parse usage '%s' for RAM filesystem"), usage); goto error; } if (virScaleInteger(&def->usage, units, 1024, ULLONG_MAX) < 0) goto error; } def->src = source; source = NULL; def->dst = target; target = NULL; if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; cleanup: ctxt->node = save_node; VIR_FREE(type); VIR_FREE(fsdriver); VIR_FREE(target); VIR_FREE(source); VIR_FREE(accessmode); VIR_FREE(wrpolicy); VIR_FREE(usage); VIR_FREE(units); VIR_FREE(format); return def; error: virDomainFSDefFree(def); def = NULL; goto cleanup; } static int virDomainActualNetDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virDomainNetDefPtr parent, virDomainActualNetDefPtr *def, unsigned int flags) { virDomainActualNetDefPtr actual = NULL; int ret = -1; xmlNodePtr save_ctxt = ctxt->node; xmlNodePtr bandwidth_node = NULL; xmlNodePtr vlanNode; xmlNodePtr virtPortNode; char *type = NULL; char *mode = NULL; char *addrtype = NULL; char *trustGuestRxFilters = NULL; char *macTableManager = NULL; if (VIR_ALLOC(actual) < 0) return -1; ctxt->node = node; type = virXMLPropString(node, "type"); if (!type) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing type attribute in interface's element")); goto error; } if ((actual->type = virDomainNetTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown type '%s' in interface's element"), type); goto error; } if (actual->type != VIR_DOMAIN_NET_TYPE_BRIDGE && actual->type != VIR_DOMAIN_NET_TYPE_DIRECT && actual->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && actual->type != VIR_DOMAIN_NET_TYPE_NETWORK) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported type '%s' in interface's element"), type); goto error; } trustGuestRxFilters = virXMLPropString(node, "trustGuestRxFilters"); if (trustGuestRxFilters && ((actual->trustGuestRxFilters = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown trustGuestRxFilters value '%s'"), trustGuestRxFilters); goto error; } virtPortNode = virXPathNode("./virtualport", ctxt); if (virtPortNode) { if (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE || actual->type == VIR_DOMAIN_NET_TYPE_DIRECT || actual->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) { /* the virtualport in should always already * have an instanceid/interfaceid if its required, * so don't let the parser generate one */ if (!(actual->virtPortProfile = virNetDevVPortProfileParse(virtPortNode, VIR_VPORT_XML_REQUIRE_ALL_ATTRIBUTES | VIR_VPORT_XML_REQUIRE_TYPE))) { goto error; } } else { virReportError(VIR_ERR_INTERNAL_ERROR, _(" element unsupported for type='%s'" " in interface's element"), type); goto error; } } if (actual->type == VIR_DOMAIN_NET_TYPE_DIRECT) { actual->data.direct.linkdev = virXPathString("string(./source[1]/@dev)", ctxt); mode = virXPathString("string(./source[1]/@mode)", ctxt); if (mode) { int m; if ((m = virNetDevMacVLanModeTypeFromString(mode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown mode '%s' in interface element"), mode); goto error; } actual->data.direct.mode = m; } } else if (actual->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) { virDomainHostdevDefPtr hostdev = &actual->data.hostdev.def; hostdev->parent.type = VIR_DOMAIN_DEVICE_NET; hostdev->parent.data.net = parent; hostdev->info = &parent->info; /* The helper function expects type to already be found and * passed in as a string, since it is in a different place in * NetDef vs HostdevDef. */ addrtype = virXPathString("string(./source/address/@type)", ctxt); /* if not explicitly stated, source/vendor implies usb device */ if (!addrtype && virXPathNode("./source/vendor", ctxt) && VIR_STRDUP(addrtype, "usb") < 0) goto error; hostdev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; if (virDomainHostdevDefParseXMLSubsys(node, ctxt, addrtype, hostdev, flags) < 0) { goto error; } } else if (actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) { char *class_id = virXPathString("string(./class/@id)", ctxt); if (class_id && virStrToLong_ui(class_id, NULL, 10, &actual->class_id) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to parse class id '%s'"), class_id); VIR_FREE(class_id); goto error; } VIR_FREE(class_id); } if (actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE || actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) { char *brname = virXPathString("string(./source/@bridge)", ctxt); if (!brname && actual->type == VIR_DOMAIN_NET_TYPE_BRIDGE) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing element with bridge name in " "interface's element")); goto error; } actual->data.bridge.brname = brname; macTableManager = virXPathString("string(./source/@macTableManager)", ctxt); if (macTableManager && (actual->data.bridge.macTableManager = virNetworkBridgeMACTableManagerTypeFromString(macTableManager)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid macTableManager setting '%s' " "in domain interface's element"), macTableManager); goto error; } } bandwidth_node = virXPathNode("./bandwidth", ctxt); if (bandwidth_node && virNetDevBandwidthParse(&actual->bandwidth, bandwidth_node, actual->type) < 0) goto error; vlanNode = virXPathNode("./vlan", ctxt); if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &actual->vlan) < 0) goto error; *def = actual; actual = NULL; ret = 0; error: VIR_FREE(type); VIR_FREE(mode); VIR_FREE(addrtype); VIR_FREE(trustGuestRxFilters); VIR_FREE(macTableManager); virDomainActualNetDefFree(actual); ctxt->node = save_ctxt; return ret; } #define NET_MODEL_CHARS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" int virDomainNetAppendIpAddress(virDomainNetDefPtr def, const char *address, int family, unsigned int prefix) { virDomainNetIpDefPtr ipDef = NULL; if (VIR_ALLOC(ipDef) < 0) return -1; if (virSocketAddrParse(&ipDef->address, address, family) < 0) goto error; ipDef->prefix = prefix; if (VIR_APPEND_ELEMENT(def->ips, def->nips, ipDef) < 0) goto error; return 0; error: VIR_FREE(ipDef); return -1; } /* Parse the XML definition for a network interface * @param node XML nodeset to parse for net definition * @return 0 on success, -1 on failure */ static virDomainNetDefPtr virDomainNetDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, virHashTablePtr bootHash, unsigned int flags) { virDomainNetDefPtr def; virDomainHostdevDefPtr hostdev; xmlNodePtr cur; char *macaddr = NULL; char *type = NULL; char *network = NULL; char *portgroup = NULL; char *bridge = NULL; char *dev = NULL; char *ifname = NULL; char *ifname_guest = NULL; char *ifname_guest_actual = NULL; char *script = NULL; char *address = NULL; char *port = NULL; char *model = NULL; char *backend = NULL; char *txmode = NULL; char *ioeventfd = NULL; char *event_idx = NULL; char *queues = NULL; char *str = NULL; char *filter = NULL; char *internal = NULL; char *devaddr = NULL; char *mode = NULL; char *linkstate = NULL; char *addrtype = NULL; char *domain_name = NULL; char *vhostuser_mode = NULL; char *vhostuser_path = NULL; char *vhostuser_type = NULL; char *trustGuestRxFilters = NULL; char *vhost_path = NULL; virNWFilterHashTablePtr filterparams = NULL; virDomainActualNetDefPtr actual = NULL; xmlNodePtr oldnode = ctxt->node; int ret, val; size_t i; size_t nips = 0; virDomainNetIpDefPtr *ips = NULL; size_t nroutes = 0; virNetworkRouteDefPtr *routes = NULL; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = node; type = virXMLPropString(node, "type"); if (type != NULL) { if ((int)(def->type = virDomainNetTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown interface type '%s'"), type); goto error; } } else { def->type = VIR_DOMAIN_NET_TYPE_USER; } trustGuestRxFilters = virXMLPropString(node, "trustGuestRxFilters"); if (trustGuestRxFilters && ((def->trustGuestRxFilters = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown trustGuestRxFilters value '%s'"), trustGuestRxFilters); goto error; } cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!macaddr && xmlStrEqual(cur->name, BAD_CAST "mac")) { macaddr = virXMLPropString(cur, "address"); } else if (!network && def->type == VIR_DOMAIN_NET_TYPE_NETWORK && xmlStrEqual(cur->name, BAD_CAST "source")) { network = virXMLPropString(cur, "network"); portgroup = virXMLPropString(cur, "portgroup"); } else if (!internal && def->type == VIR_DOMAIN_NET_TYPE_INTERNAL && xmlStrEqual(cur->name, BAD_CAST "source")) { internal = virXMLPropString(cur, "name"); } else if (!bridge && def->type == VIR_DOMAIN_NET_TYPE_BRIDGE && xmlStrEqual(cur->name, BAD_CAST "source")) { bridge = virXMLPropString(cur, "bridge"); } else if (!dev && (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET || def->type == VIR_DOMAIN_NET_TYPE_DIRECT) && xmlStrEqual(cur->name, BAD_CAST "source")) { dev = virXMLPropString(cur, "dev"); mode = virXMLPropString(cur, "mode"); } else if (!vhostuser_path && !vhostuser_mode && !vhostuser_type && def->type == VIR_DOMAIN_NET_TYPE_VHOSTUSER && xmlStrEqual(cur->name, BAD_CAST "source")) { vhostuser_type = virXMLPropString(cur, "type"); vhostuser_path = virXMLPropString(cur, "path"); vhostuser_mode = virXMLPropString(cur, "mode"); } else if (!def->virtPortProfile && xmlStrEqual(cur->name, BAD_CAST "virtualport")) { if (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) { if (!(def->virtPortProfile = virNetDevVPortProfileParse(cur, VIR_VPORT_XML_GENERATE_MISSING_DEFAULTS))) { goto error; } } else if (def->type == VIR_DOMAIN_NET_TYPE_BRIDGE || def->type == VIR_DOMAIN_NET_TYPE_DIRECT || def->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) { if (!(def->virtPortProfile = virNetDevVPortProfileParse(cur, VIR_VPORT_XML_GENERATE_MISSING_DEFAULTS| VIR_VPORT_XML_REQUIRE_ALL_ATTRIBUTES| VIR_VPORT_XML_REQUIRE_TYPE))) { goto error; } } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _(" element unsupported for" " "), type); goto error; } } else if (!address && (def->type == VIR_DOMAIN_NET_TYPE_SERVER || def->type == VIR_DOMAIN_NET_TYPE_CLIENT || def->type == VIR_DOMAIN_NET_TYPE_MCAST) && xmlStrEqual(cur->name, BAD_CAST "source")) { address = virXMLPropString(cur, "address"); port = virXMLPropString(cur, "port"); } else if (xmlStrEqual(cur->name, BAD_CAST "ip")) { virDomainNetIpDefPtr ip = NULL; if (!(ip = virDomainNetIpParseXML(cur))) goto error; if (VIR_APPEND_ELEMENT(ips, nips, ip) < 0) goto error; } else if (xmlStrEqual(cur->name, BAD_CAST "route")) { virNetworkRouteDefPtr route = NULL; if (!(route = virNetworkRouteDefParseXML(_("Domain interface"), cur, ctxt))) goto error; if (VIR_APPEND_ELEMENT(routes, nroutes, route) < 0) { virNetworkRouteDefFree(route); goto error; } } else if (!ifname && xmlStrEqual(cur->name, BAD_CAST "target")) { ifname = virXMLPropString(cur, "dev"); if (ifname && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) && STRPREFIX(ifname, VIR_NET_GENERATED_PREFIX)) { /* An auto-generated target name, blank it out */ VIR_FREE(ifname); } } else if ((!ifname_guest || !ifname_guest_actual) && xmlStrEqual(cur->name, BAD_CAST "guest")) { ifname_guest = virXMLPropString(cur, "dev"); ifname_guest_actual = virXMLPropString(cur, "actual"); } else if (!linkstate && xmlStrEqual(cur->name, BAD_CAST "link")) { linkstate = virXMLPropString(cur, "state"); } else if (!script && xmlStrEqual(cur->name, BAD_CAST "script")) { script = virXMLPropString(cur, "path"); } else if (!domain_name && xmlStrEqual(cur->name, BAD_CAST "backenddomain")) { domain_name = virXMLPropString(cur, "name"); } else if (xmlStrEqual(cur->name, BAD_CAST "model")) { model = virXMLPropString(cur, "type"); } else if (xmlStrEqual(cur->name, BAD_CAST "driver")) { backend = virXMLPropString(cur, "name"); txmode = virXMLPropString(cur, "txmode"); ioeventfd = virXMLPropString(cur, "ioeventfd"); event_idx = virXMLPropString(cur, "event_idx"); queues = virXMLPropString(cur, "queues"); } else if (xmlStrEqual(cur->name, BAD_CAST "filterref")) { if (filter) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid specification of multiple s " "in a single ")); goto error; } filter = virXMLPropString(cur, "filter"); virNWFilterHashTableFree(filterparams); filterparams = virNWFilterParseParamAttributes(cur); } else if ((flags & VIR_DOMAIN_DEF_PARSE_STATUS) && xmlStrEqual(cur->name, BAD_CAST "state")) { /* Legacy back-compat. Don't add any more attributes here */ devaddr = virXMLPropString(cur, "devaddr"); } else if (xmlStrEqual(cur->name, BAD_CAST "boot")) { /* boot is parsed as part of virDomainDeviceInfoParseXML */ } else if (!actual && (flags & VIR_DOMAIN_DEF_PARSE_ACTUAL_NET) && def->type == VIR_DOMAIN_NET_TYPE_NETWORK && xmlStrEqual(cur->name, BAD_CAST "actual")) { if (virDomainActualNetDefParseXML(cur, ctxt, def, &actual, flags) < 0) { goto error; } } else if (xmlStrEqual(cur->name, BAD_CAST "bandwidth")) { if (virNetDevBandwidthParse(&def->bandwidth, cur, def->type) < 0) goto error; } else if (xmlStrEqual(cur->name, BAD_CAST "vlan")) { if (virNetDevVlanParse(cur, ctxt, &def->vlan) < 0) goto error; } else if (xmlStrEqual(cur->name, BAD_CAST "backend")) { char *tmp = NULL; if ((tmp = virXMLPropString(cur, "tap"))) def->backend.tap = virFileSanitizePath(tmp); VIR_FREE(tmp); if (!vhost_path && (tmp = virXMLPropString(cur, "vhost"))) vhost_path = virFileSanitizePath(tmp); VIR_FREE(tmp); } } cur = cur->next; } if (macaddr) { if (virMacAddrParse((const char *)macaddr, &def->mac) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unable to parse mac address '%s'"), (const char *)macaddr); goto error; } if (virMacAddrIsMulticast(&def->mac)) { virReportError(VIR_ERR_XML_ERROR, _("expected unicast mac address, found multicast '%s'"), (const char *)macaddr); goto error; } } else { virDomainNetGenerateMAC(xmlopt, &def->mac); } if (devaddr) { if (virDomainParseLegacyDeviceAddress(devaddr, &def->info.addr.pci) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to parse devaddr parameter '%s'"), devaddr); goto error; } def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI; } else { if (virDomainDeviceInfoParseXML(node, bootHash, &def->info, flags | VIR_DOMAIN_DEF_PARSE_ALLOW_BOOT | VIR_DOMAIN_DEF_PARSE_ALLOW_ROM) < 0) goto error; } /* XXX what about ISA/USB based NIC models - once we support * them we should make sure address type is correct */ if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390 && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Network interfaces must use 'pci' address type")); goto error; } switch (def->type) { case VIR_DOMAIN_NET_TYPE_NETWORK: if (network == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'network' attribute " "specified with ")); goto error; } def->data.network.name = network; network = NULL; def->data.network.portgroup = portgroup; portgroup = NULL; def->data.network.actual = actual; actual = NULL; break; case VIR_DOMAIN_NET_TYPE_VHOSTUSER: if (STRNEQ_NULLABLE(model, "virtio")) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Wrong or no 'type' attribute " "specified with . " "vhostuser requires the virtio-net* frontend")); goto error; } if (STRNEQ_NULLABLE(vhostuser_type, "unix")) { if (vhostuser_type) virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Type='%s' unsupported for" " "), vhostuser_type); else virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("No 'type' attribute " "specified for ")); goto error; } if (vhostuser_path == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'path' attribute " "specified with ")); goto error; } if (vhostuser_mode == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'mode' attribute " "specified with ")); goto error; } if (VIR_ALLOC(def->data.vhostuser) < 0) goto error; def->data.vhostuser->type = VIR_DOMAIN_CHR_TYPE_UNIX; def->data.vhostuser->data.nix.path = vhostuser_path; vhostuser_path = NULL; if (STREQ(vhostuser_mode, "server")) { def->data.vhostuser->data.nix.listen = true; } else if (STREQ(vhostuser_mode, "client")) { def->data.vhostuser->data.nix.listen = false; } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Wrong 'mode' attribute " "specified with ")); goto error; } break; case VIR_DOMAIN_NET_TYPE_ETHERNET: if (dev != NULL) { def->data.ethernet.dev = dev; dev = NULL; } break; case VIR_DOMAIN_NET_TYPE_BRIDGE: if (bridge == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'bridge' attribute " "specified with ")); goto error; } def->data.bridge.brname = bridge; bridge = NULL; break; case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_MCAST: if (port == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'port' attribute " "specified with socket interface")); goto error; } if (virStrToLong_i(port, NULL, 10, &def->data.socket.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot parse 'port' attribute " "with socket interface")); goto error; } if (address == NULL) { if (def->type == VIR_DOMAIN_NET_TYPE_CLIENT || def->type == VIR_DOMAIN_NET_TYPE_MCAST) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'address' attribute " "specified with socket interface")); goto error; } } else { def->data.socket.address = address; address = NULL; } break; case VIR_DOMAIN_NET_TYPE_INTERNAL: if (internal == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'name' attribute specified " "with ")); goto error; } def->data.internal.name = internal; internal = NULL; break; case VIR_DOMAIN_NET_TYPE_DIRECT: if (dev == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'dev' attribute specified " "with ")); goto error; } if (mode != NULL) { if ((val = virNetDevMacVLanModeTypeFromString(mode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Unknown mode has been specified")); goto error; } def->data.direct.mode = val; } else { def->data.direct.mode = VIR_NETDEV_MACVLAN_MODE_VEPA; } def->data.direct.linkdev = dev; dev = NULL; if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) VIR_FREE(ifname); break; case VIR_DOMAIN_NET_TYPE_HOSTDEV: hostdev = &def->data.hostdev.def; hostdev->parent.type = VIR_DOMAIN_DEVICE_NET; hostdev->parent.data.net = def; hostdev->info = &def->info; /* The helper function expects type to already be found and * passed in as a string, since it is in a different place in * NetDef vs HostdevDef. */ addrtype = virXPathString("string(./source/address/@type)", ctxt); /* if not explicitly stated, source/vendor implies usb device */ if (!addrtype && virXPathNode("./source/vendor", ctxt) && VIR_STRDUP(addrtype, "usb") < 0) goto error; hostdev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; if (virDomainHostdevDefParseXMLSubsys(node, ctxt, addrtype, hostdev, flags) < 0) { goto error; } break; case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_LAST: break; } for (i = 0; i < nips; i++) { if (VIR_APPEND_ELEMENT(def->ips, def->nips, ips[i]) < 0) goto error; } def->nroutes = nroutes; def->routes = routes; if (script != NULL) { def->script = script; script = NULL; } if (domain_name != NULL) { def->domain_name = domain_name; domain_name = NULL; } if (ifname != NULL) { def->ifname = ifname; ifname = NULL; } if (ifname_guest != NULL) { def->ifname_guest = ifname_guest; ifname_guest = NULL; } if (ifname_guest_actual != NULL) { def->ifname_guest_actual = ifname_guest_actual; ifname_guest_actual = NULL; } /* NIC model (see -net nic,model=?). We only check that it looks * reasonable, not that it is a supported NIC type. FWIW kvm * supports these types as of April 2008: * i82551 i82557b i82559er ne2k_pci pcnet rtl8139 e1000 virtio * QEMU PPC64 supports spapr-vlan */ if (model != NULL) { if (strspn(model, NET_MODEL_CHARS) < strlen(model)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Model name contains invalid characters")); goto error; } def->model = model; model = NULL; } if (def->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && STREQ_NULLABLE(def->model, "virtio")) { if (backend != NULL) { if ((val = virDomainNetBackendTypeFromString(backend)) < 0 || val == VIR_DOMAIN_NET_BACKEND_TYPE_DEFAULT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown interface " "has been specified"), backend); goto error; } def->driver.virtio.name = val; } if (txmode != NULL) { if ((val = virDomainNetVirtioTxModeTypeFromString(txmode)) < 0 || val == VIR_DOMAIN_NET_VIRTIO_TX_MODE_DEFAULT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown interface " "has been specified"), txmode); goto error; } def->driver.virtio.txmode = val; } if (ioeventfd) { if ((val = virTristateSwitchTypeFromString(ioeventfd)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown interface ioeventfd mode '%s'"), ioeventfd); goto error; } def->driver.virtio.ioeventfd = val; } if (event_idx) { if ((val = virTristateSwitchTypeFromString(event_idx)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown interface event_idx mode '%s'"), event_idx); goto error; } def->driver.virtio.event_idx = val; } if (queues) { unsigned int q; if (virStrToLong_uip(queues, NULL, 10, &q) < 0) { virReportError(VIR_ERR_XML_DETAIL, _("'queues' attribute must be positive number: %s"), queues); goto error; } def->driver.virtio.queues = q; } if ((str = virXPathString("string(./driver/host/@csum)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown host csum mode '%s'"), str); goto error; } def->driver.virtio.host.csum = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/host/@gso)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown host gso mode '%s'"), str); goto error; } def->driver.virtio.host.gso = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/host/@tso4)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown host tso4 mode '%s'"), str); goto error; } def->driver.virtio.host.tso4 = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/host/@tso6)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown host tso6 mode '%s'"), str); goto error; } def->driver.virtio.host.tso6 = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/host/@ecn)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown host ecn mode '%s'"), str); goto error; } def->driver.virtio.host.ecn = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/host/@ufo)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown host ufo mode '%s'"), str); goto error; } def->driver.virtio.host.ufo = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/host/@mrg_rxbuf)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown host mrg_rxbuf mode '%s'"), str); goto error; } def->driver.virtio.host.mrg_rxbuf = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/guest/@csum)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown guest csum mode '%s'"), str); goto error; } def->driver.virtio.guest.csum = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/guest/@tso4)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown guest tso4 mode '%s'"), str); goto error; } def->driver.virtio.guest.tso4 = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/guest/@tso6)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown guest tso6 mode '%s'"), str); goto error; } def->driver.virtio.guest.tso6 = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/guest/@ecn)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown guest ecn mode '%s'"), str); goto error; } def->driver.virtio.guest.ecn = val; } VIR_FREE(str); if ((str = virXPathString("string(./driver/guest/@ufo)", ctxt))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown guest ufo mode '%s'"), str); goto error; } def->driver.virtio.guest.ufo = val; } def->backend.vhost = vhost_path; vhost_path = NULL; } def->linkstate = VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DEFAULT; if (linkstate != NULL) { if ((def->linkstate = virDomainNetInterfaceLinkStateTypeFromString(linkstate)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown interface link state '%s'"), linkstate); goto error; } } if (filter != NULL) { switch (def->type) { case VIR_DOMAIN_NET_TYPE_ETHERNET: case VIR_DOMAIN_NET_TYPE_NETWORK: case VIR_DOMAIN_NET_TYPE_BRIDGE: def->filter = filter; filter = NULL; def->filterparams = filterparams; filterparams = NULL; break; default: break; } } ret = virXPathULong("string(./tune/sndbuf)", ctxt, &def->tune.sndbuf); if (ret >= 0) { def->tune.sndbuf_specified = true; } else if (ret == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("sndbuf must be a positive integer")); goto error; } cleanup: ctxt->node = oldnode; VIR_FREE(macaddr); VIR_FREE(network); VIR_FREE(portgroup); VIR_FREE(address); VIR_FREE(port); VIR_FREE(vhostuser_type); VIR_FREE(vhostuser_path); VIR_FREE(vhostuser_mode); VIR_FREE(ifname); VIR_FREE(ifname_guest); VIR_FREE(ifname_guest_actual); VIR_FREE(dev); virDomainActualNetDefFree(actual); VIR_FREE(script); VIR_FREE(bridge); VIR_FREE(model); VIR_FREE(backend); VIR_FREE(txmode); VIR_FREE(ioeventfd); VIR_FREE(event_idx); VIR_FREE(queues); VIR_FREE(str); VIR_FREE(filter); VIR_FREE(type); VIR_FREE(internal); VIR_FREE(devaddr); VIR_FREE(mode); VIR_FREE(linkstate); VIR_FREE(addrtype); VIR_FREE(domain_name); VIR_FREE(trustGuestRxFilters); VIR_FREE(ips); VIR_FREE(vhost_path); virNWFilterHashTableFree(filterparams); return def; error: virDomainNetDefFree(def); def = NULL; goto cleanup; } static int virDomainChrDefaultTargetType(int devtype) { switch ((virDomainChrDeviceType) devtype) { case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: virReportError(VIR_ERR_XML_ERROR, _("target type must be specified for %s device"), virDomainChrDeviceTypeToString(devtype)); return -1; case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: return VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE; case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL: return VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA; case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL: case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST: /* No target type yet*/ break; } return 0; } static int virDomainChrTargetTypeFromString(virDomainChrDefPtr def, int devtype, const char *targetType) { int ret = -1; if (!targetType) return virDomainChrDefaultTargetType(devtype); switch ((virDomainChrDeviceType) devtype) { case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: ret = virDomainChrChannelTargetTypeFromString(targetType); break; case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: ret = virDomainChrConsoleTargetTypeFromString(targetType); break; case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL: ret = virDomainChrSerialTargetTypeFromString(targetType); break; case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL: case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST: /* No target type yet*/ ret = 0; break; } def->targetTypeAttr = true; return ret; } static int virDomainChrDefParseTargetXML(virDomainChrDefPtr def, xmlNodePtr cur, unsigned int flags) { int ret = -1; unsigned int port; char *targetType = virXMLPropString(cur, "type"); char *addrStr = NULL; char *portStr = NULL; char *stateStr = NULL; if ((def->targetType = virDomainChrTargetTypeFromString(def, def->deviceType, targetType)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown target type '%s' specified for character device"), targetType); goto error; } switch (def->deviceType) { case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: switch (def->targetType) { case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: addrStr = virXMLPropString(cur, "address"); portStr = virXMLPropString(cur, "port"); if (VIR_ALLOC(def->target.addr) < 0) goto error; if (addrStr == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("guestfwd channel does not " "define a target address")); goto error; } if (virSocketAddrParse(def->target.addr, addrStr, AF_UNSPEC) < 0) goto error; if (def->target.addr->data.stor.ss_family != AF_INET) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("guestfwd channel only supports " "IPv4 addresses")); goto error; } if (portStr == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("guestfwd channel does " "not define a target port")); goto error; } if (virStrToLong_ui(portStr, NULL, 10, &port) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid port number: %s"), portStr); goto error; } virSocketAddrSetPort(def->target.addr, port); break; case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: def->target.name = virXMLPropString(cur, "name"); if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) && (stateStr = virXMLPropString(cur, "state"))) { int tmp; if ((tmp = virDomainChrDeviceStateTypeFromString(stateStr)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid channel state value '%s'"), stateStr); goto error; } def->state = tmp; } break; } break; default: portStr = virXMLPropString(cur, "port"); if (portStr == NULL) { /* Set to negative value to indicate we should set it later */ def->target.port = -1; break; } if (virStrToLong_ui(portStr, NULL, 10, &port) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid port number: %s"), portStr); goto error; } def->target.port = port; break; } ret = 0; error: VIR_FREE(targetType); VIR_FREE(addrStr); VIR_FREE(portStr); VIR_FREE(stateStr); return ret; } #define SERIAL_CHANNEL_NAME_CHARS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-." /* Parse the source half of the XML definition for a character device, * where node is the first element of node->children of the parent * element. def->type must already be valid. Return -1 on failure, * otherwise the number of ignored children (this intentionally skips * , which is used by but not ). */ static int virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, xmlNodePtr cur, unsigned int flags, virDomainChrDefPtr chr_def, xmlXPathContextPtr ctxt, virSecurityLabelDefPtr* vmSeclabels, int nvmSeclabels) { char *bindHost = NULL; char *bindService = NULL; char *connectHost = NULL; char *connectService = NULL; char *path = NULL; char *mode = NULL; char *protocol = NULL; char *channel = NULL; char *master = NULL; char *slave = NULL; int remaining = 0; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (xmlStrEqual(cur->name, BAD_CAST "source")) { if (!mode) mode = virXMLPropString(cur, "mode"); switch ((virDomainChrType) 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: case VIR_DOMAIN_CHR_TYPE_UNIX: /* PTY path is only parsed from live xml. */ if (!path && (def->type != VIR_DOMAIN_CHR_TYPE_PTY || !(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE))) path = virXMLPropString(cur, "path"); break; case VIR_DOMAIN_CHR_TYPE_UDP: case VIR_DOMAIN_CHR_TYPE_TCP: if (!mode || STREQ(mode, "connect")) { if (!connectHost) connectHost = virXMLPropString(cur, "host"); if (!connectService) connectService = virXMLPropString(cur, "service"); } else if (STREQ(mode, "bind")) { if (!bindHost) bindHost = virXMLPropString(cur, "host"); if (!bindService) bindService = virXMLPropString(cur, "service"); } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown source mode '%s'"), mode); goto error; } if (def->type == VIR_DOMAIN_CHR_TYPE_UDP) VIR_FREE(mode); break; case VIR_DOMAIN_CHR_TYPE_SPICEPORT: if (!channel) channel = virXMLPropString(cur, "channel"); break; case VIR_DOMAIN_CHR_TYPE_NMDM: if (!master) master = virXMLPropString(cur, "master"); if (!slave) slave = virXMLPropString(cur, "slave"); break; case VIR_DOMAIN_CHR_TYPE_LAST: case VIR_DOMAIN_CHR_TYPE_NULL: case VIR_DOMAIN_CHR_TYPE_VC: case VIR_DOMAIN_CHR_TYPE_STDIO: case VIR_DOMAIN_CHR_TYPE_SPICEVMC: break; } /* Check for an optional seclabel override in . */ if (chr_def) { xmlNodePtr saved_node = ctxt->node; ctxt->node = cur; if (virSecurityDeviceLabelDefParseXML(&chr_def->seclabels, &chr_def->nseclabels, vmSeclabels, nvmSeclabels, ctxt, flags) < 0) { ctxt->node = saved_node; goto error; } ctxt->node = saved_node; } } else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) { if (!protocol) protocol = virXMLPropString(cur, "type"); } else { remaining++; } } cur = cur->next; } switch ((virDomainChrType) def->type) { case VIR_DOMAIN_CHR_TYPE_NULL: case VIR_DOMAIN_CHR_TYPE_VC: case VIR_DOMAIN_CHR_TYPE_STDIO: case VIR_DOMAIN_CHR_TYPE_SPICEVMC: case VIR_DOMAIN_CHR_TYPE_LAST: break; 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 (!path && def->type != VIR_DOMAIN_CHR_TYPE_PTY) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source path attribute for char device")); goto error; } def->data.file.path = path; path = NULL; break; case VIR_DOMAIN_CHR_TYPE_NMDM: if (!master) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing master path attribute for nmdm device")); goto error; } if (!slave) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing slave path attribute for nmdm device")); goto error; } def->data.nmdm.master = master; def->data.nmdm.slave = slave; master = NULL; slave = NULL; break; case VIR_DOMAIN_CHR_TYPE_TCP: if (!mode || STREQ(mode, "connect")) { if (!connectHost) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source host attribute for char device")); goto error; } if (!connectService) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source service attribute for char device")); goto error; } def->data.tcp.host = connectHost; connectHost = NULL; def->data.tcp.service = connectService; connectService = NULL; def->data.tcp.listen = false; } else { if (!bindHost) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source host attribute for char device")); goto error; } if (!bindService) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source service attribute for char device")); goto error; } def->data.tcp.host = bindHost; bindHost = NULL; def->data.tcp.service = bindService; bindService = NULL; def->data.tcp.listen = true; } if (!protocol) def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW; else if ((def->data.tcp.protocol = virDomainChrTcpProtocolTypeFromString(protocol)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown protocol '%s'"), protocol); goto error; } break; case VIR_DOMAIN_CHR_TYPE_UDP: if (!connectService) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source service attribute for char device")); goto error; } def->data.udp.connectHost = connectHost; connectHost = NULL; def->data.udp.connectService = connectService; connectService = NULL; def->data.udp.bindHost = bindHost; bindHost = NULL; def->data.udp.bindService = bindService; bindService = NULL; break; case VIR_DOMAIN_CHR_TYPE_UNIX: /* path can be auto generated */ if (!path && (!chr_def || chr_def->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source path attribute for char device")); goto error; } def->data.nix.listen = mode != NULL && STRNEQ(mode, "connect"); def->data.nix.path = path; path = NULL; break; case VIR_DOMAIN_CHR_TYPE_SPICEPORT: if (!channel) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing source channel attribute for char device")); goto error; } if (strspn(channel, SERIAL_CHANNEL_NAME_CHARS) < strlen(channel)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid character in source channel for char device")); goto error; } def->data.spiceport.channel = channel; channel = NULL; break; } cleanup: VIR_FREE(mode); VIR_FREE(protocol); VIR_FREE(bindHost); VIR_FREE(bindService); VIR_FREE(connectHost); VIR_FREE(connectService); VIR_FREE(path); VIR_FREE(channel); return remaining; error: virDomainChrSourceDefClear(def); remaining = -1; goto cleanup; } /* Create a new character device definition and set * default port. */ virDomainChrDefPtr virDomainChrDefNew(void) { virDomainChrDefPtr def = NULL; if (VIR_ALLOC(def) < 0) return NULL; def->target.port = -1; return def; } /* Parse the XML definition for a character device * @param node XML nodeset to parse for net definition * * The XML we're dealing with looks like * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static virDomainChrDefPtr virDomainChrDefParseXML(xmlXPathContextPtr ctxt, xmlNodePtr node, virSecurityLabelDefPtr* vmSeclabels, int nvmSeclabels, unsigned int flags) { xmlNodePtr cur; char *type = NULL; const char *nodeName; virDomainChrDefPtr def; bool seenTarget = false; if (!(def = virDomainChrDefNew())) return NULL; type = virXMLPropString(node, "type"); if (type == NULL) { def->source.type = VIR_DOMAIN_CHR_TYPE_PTY; } else if ((def->source.type = virDomainChrTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown type presented to host for character device: %s"), type); goto error; } nodeName = (const char *) node->name; if ((def->deviceType = virDomainChrDeviceTypeFromString(nodeName)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown character device type: %s"), nodeName); goto error; } cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (xmlStrEqual(cur->name, BAD_CAST "target")) { seenTarget = true; if (virDomainChrDefParseTargetXML(def, cur, flags) < 0) goto error; } } cur = cur->next; } if (!seenTarget && ((def->targetType = virDomainChrDefaultTargetType(def->deviceType)) < 0)) goto error; if (virDomainChrSourceDefParseXML(&def->source, node->children, flags, def, ctxt, vmSeclabels, nvmSeclabels) < 0) goto error; if (def->source.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) { if (def->targetType != VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("spicevmc device type only supports " "virtio")); goto error; } else { def->source.data.spicevmc = VIR_DOMAIN_CHR_SPICEVMC_VDAGENT; } } if (def->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && def->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD) { VIR_DEBUG("Ignoring device address for gustfwd channel"); } else if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) { goto error; } if (def->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && def->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("usb-serial requires address of usb type")); goto error; } cleanup: VIR_FREE(type); return def; error: virDomainChrDefFree(def); def = NULL; goto cleanup; } static virDomainSmartcardDefPtr virDomainSmartcardDefParseXML(xmlNodePtr node, unsigned int flags) { xmlNodePtr cur; char *mode = NULL; char *type = NULL; virDomainSmartcardDefPtr def; size_t i; if (VIR_ALLOC(def) < 0) return NULL; mode = virXMLPropString(node, "mode"); if (mode == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing smartcard device mode")); goto error; } if ((def->type = virDomainSmartcardTypeFromString(mode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown smartcard device mode: %s"), mode); goto error; } switch (def->type) { case VIR_DOMAIN_SMARTCARD_TYPE_HOST: break; case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES: i = 0; cur = node->children; while (cur) { if (cur->type == XML_ELEMENT_NODE && xmlStrEqual(cur->name, BAD_CAST "certificate")) { if (i == 3) { virReportError(VIR_ERR_XML_ERROR, "%s", _("host-certificates mode needs " "exactly three certificates")); goto error; } def->data.cert.file[i] = (char *)xmlNodeGetContent(cur); if (!def->data.cert.file[i]) { virReportOOMError(); goto error; } i++; } else if (cur->type == XML_ELEMENT_NODE && xmlStrEqual(cur->name, BAD_CAST "database") && !def->data.cert.database) { def->data.cert.database = (char *)xmlNodeGetContent(cur); if (!def->data.cert.database) { virReportOOMError(); goto error; } if (*def->data.cert.database != '/') { virReportError(VIR_ERR_XML_ERROR, _("expecting absolute path: %s"), def->data.cert.database); goto error; } } cur = cur->next; } if (i < 3) { virReportError(VIR_ERR_XML_ERROR, "%s", _("host-certificates mode needs " "exactly three certificates")); goto error; } break; case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH: type = virXMLPropString(node, "type"); if (type == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("passthrough mode requires a character " "device type attribute")); goto error; } if ((def->data.passthru.type = virDomainChrTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown type presented to host for " "character device: %s"), type); goto error; } cur = node->children; if (virDomainChrSourceDefParseXML(&def->data.passthru, cur, flags, NULL, NULL, NULL, 0) < 0) goto error; if (def->data.passthru.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) { def->data.passthru.data.spicevmc = VIR_DOMAIN_CHR_SPICEVMC_SMARTCARD; } break; default: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unknown smartcard mode")); goto error; } if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Controllers must use the 'ccid' address type")); goto error; } cleanup: VIR_FREE(mode); VIR_FREE(type); return def; error: virDomainSmartcardDefFree(def); def = NULL; goto cleanup; } /* Parse the XML definition for a TPM device * * The XML looks like this: * * * * * * * */ static virDomainTPMDefPtr virDomainTPMDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { char *type = NULL; char *path = NULL; char *model = NULL; char *backend = NULL; virDomainTPMDefPtr def; xmlNodePtr save = ctxt->node; xmlNodePtr *backends = NULL; int nbackends; if (VIR_ALLOC(def) < 0) return NULL; model = virXMLPropString(node, "model"); if (model != NULL && (int)(def->model = virDomainTPMModelTypeFromString(model)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown TPM frontend model '%s'"), model); goto error; } else { def->model = VIR_DOMAIN_TPM_MODEL_TIS; } ctxt->node = node; if ((nbackends = virXPathNodeSet("./backend", ctxt, &backends)) < 0) goto error; if (nbackends > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only one TPM backend is supported")); goto error; } if (nbackends == 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing TPM device backend")); goto error; } if (!(backend = virXMLPropString(backends[0], "type"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing TPM device backend type")); goto error; } if ((int)(def->type = virDomainTPMBackendTypeFromString(backend)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown TPM backend type '%s'"), backend); goto error; } switch (def->type) { case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: path = virXPathString("string(./backend/device/@path)", ctxt); if (!path && VIR_STRDUP(path, VIR_DOMAIN_TPM_DEFAULT_DEVICE) < 0) goto error; def->data.passthrough.source.data.file.path = path; def->data.passthrough.source.type = VIR_DOMAIN_CHR_TYPE_DEV; path = NULL; break; case VIR_DOMAIN_TPM_TYPE_LAST: goto error; } if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; cleanup: VIR_FREE(type); VIR_FREE(path); VIR_FREE(model); VIR_FREE(backend); VIR_FREE(backends); ctxt->node = save; return def; error: virDomainTPMDefFree(def); def = NULL; goto cleanup; } static virDomainPanicDefPtr virDomainPanicDefParseXML(xmlNodePtr node) { virDomainPanicDefPtr panic; if (VIR_ALLOC(panic) < 0) return NULL; if (virDomainDeviceInfoParseXML(node, NULL, &panic->info, 0) < 0) goto error; return panic; error: virDomainPanicDefFree(panic); return NULL; } /* Parse the XML definition for an input device */ static virDomainInputDefPtr virDomainInputDefParseXML(const virDomainDef *dom, xmlNodePtr node, unsigned int flags) { virDomainInputDefPtr def; char *type = NULL; char *bus = NULL; if (VIR_ALLOC(def) < 0) return NULL; type = virXMLPropString(node, "type"); bus = virXMLPropString(node, "bus"); if (!type) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing input device type")); goto error; } if ((def->type = virDomainInputTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown input device type '%s'"), type); goto error; } if (bus) { if ((def->bus = virDomainInputBusTypeFromString(bus)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown input bus type '%s'"), bus); goto error; } if (STREQ(dom->os.type, "hvm")) { if (def->bus == VIR_DOMAIN_INPUT_BUS_PS2 && def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE && def->type != VIR_DOMAIN_INPUT_TYPE_KBD) { virReportError(VIR_ERR_INTERNAL_ERROR, _("ps2 bus does not support %s input device"), type); goto error; } if (def->bus == VIR_DOMAIN_INPUT_BUS_XEN) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported input bus %s"), bus); goto error; } } else if (STREQ(dom->os.type, "xen")) { if (def->bus != VIR_DOMAIN_INPUT_BUS_XEN) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported input bus %s"), bus); goto error; } if (def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE && def->type != VIR_DOMAIN_INPUT_TYPE_KBD) { virReportError(VIR_ERR_INTERNAL_ERROR, _("xen bus does not support %s input device"), type); goto error; } } else { if (dom->virtType == VIR_DOMAIN_VIRT_PARALLELS) { if (def->bus != VIR_DOMAIN_INPUT_BUS_PARALLELS) { virReportError(VIR_ERR_INTERNAL_ERROR, _("parallels containers don't support " "input bus %s"), bus); goto error; } if (def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE && def->type != VIR_DOMAIN_INPUT_TYPE_KBD) { virReportError(VIR_ERR_INTERNAL_ERROR, _("parallels bus does not support " "%s input device"), type); goto error; } } else { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Input devices are not supported by this " "virtualization driver.")); goto error; } } } else { if (STREQ(dom->os.type, "hvm")) { if ((def->type == VIR_DOMAIN_INPUT_TYPE_MOUSE || def->type == VIR_DOMAIN_INPUT_TYPE_KBD) && (ARCH_IS_X86(dom->os.arch) || dom->os.arch == VIR_ARCH_NONE)) { def->bus = VIR_DOMAIN_INPUT_BUS_PS2; } else { def->bus = VIR_DOMAIN_INPUT_BUS_USB; } } else if (STREQ(dom->os.type, "xen")) { def->bus = VIR_DOMAIN_INPUT_BUS_XEN; } else { if ((dom->virtType == VIR_DOMAIN_VIRT_PARALLELS)) def->bus = VIR_DOMAIN_INPUT_BUS_PARALLELS; } } if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; if (def->bus == VIR_DOMAIN_INPUT_BUS_USB && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid address for a USB device")); goto error; } cleanup: VIR_FREE(type); VIR_FREE(bus); return def; error: virDomainInputDefFree(def); def = NULL; goto cleanup; } /* Parse the XML definition for a hub device */ static virDomainHubDefPtr virDomainHubDefParseXML(xmlNodePtr node, unsigned int flags) { virDomainHubDefPtr def; char *type = NULL; if (VIR_ALLOC(def) < 0) return NULL; type = virXMLPropString(node, "type"); if (!type) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing hub device type")); goto error; } if ((def->type = virDomainHubTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown hub device type '%s'"), type); goto error; } if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; cleanup: VIR_FREE(type); return def; error: virDomainHubDefFree(def); def = NULL; goto cleanup; } /* Parse the XML definition for a clock timer */ static virDomainTimerDefPtr virDomainTimerDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt) { char *name = NULL; char *present = NULL; char *tickpolicy = NULL; char *track = NULL; char *mode = NULL; virDomainTimerDefPtr def; xmlNodePtr oldnode = ctxt->node; xmlNodePtr catchup; int ret; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = node; name = virXMLPropString(node, "name"); if (name == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing timer name")); goto error; } if ((def->name = virDomainTimerNameTypeFromString(name)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown timer name '%s'"), name); goto error; } def->present = -1; /* unspecified */ if ((present = virXMLPropString(node, "present")) != NULL) { if (STREQ(present, "yes")) { def->present = 1; } else if (STREQ(present, "no")) { def->present = 0; } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown timer present value '%s'"), present); goto error; } } def->tickpolicy = -1; tickpolicy = virXMLPropString(node, "tickpolicy"); if (tickpolicy != NULL) { if ((def->tickpolicy = virDomainTimerTickpolicyTypeFromString(tickpolicy)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown timer tickpolicy '%s'"), tickpolicy); goto error; } } def->track = -1; track = virXMLPropString(node, "track"); if (track != NULL) { if ((def->track = virDomainTimerTrackTypeFromString(track)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown timer track '%s'"), track); goto error; } } ret = virXPathULong("string(./frequency)", ctxt, &def->frequency); if (ret == -1) { def->frequency = 0; } else if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid timer frequency")); goto error; } def->mode = -1; mode = virXMLPropString(node, "mode"); if (mode != NULL) { if ((def->mode = virDomainTimerModeTypeFromString(mode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown timer mode '%s'"), mode); goto error; } } catchup = virXPathNode("./catchup", ctxt); if (catchup != NULL) { ret = virXPathULong("string(./catchup/@threshold)", ctxt, &def->catchup.threshold); if (ret == -1) { def->catchup.threshold = 0; } else if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid catchup threshold")); goto error; } ret = virXPathULong("string(./catchup/@slew)", ctxt, &def->catchup.slew); if (ret == -1) { def->catchup.slew = 0; } else if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid catchup slew")); goto error; } ret = virXPathULong("string(./catchup/@limit)", ctxt, &def->catchup.limit); if (ret == -1) { def->catchup.limit = 0; } else if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid catchup limit")); goto error; } } cleanup: VIR_FREE(name); VIR_FREE(present); VIR_FREE(tickpolicy); VIR_FREE(track); VIR_FREE(mode); ctxt->node = oldnode; return def; error: VIR_FREE(def); goto cleanup; } static int virDomainGraphicsAuthDefParseXML(xmlNodePtr node, virDomainGraphicsAuthDefPtr def, int type) { char *validTo = NULL; char *connected = virXMLPropString(node, "connected"); def->passwd = virXMLPropString(node, "passwd"); if (!def->passwd) return 0; validTo = virXMLPropString(node, "passwdValidTo"); if (validTo) { char *tmp; struct tm tm; memset(&tm, 0, sizeof(tm)); /* Expect: YYYY-MM-DDTHH:MM:SS (%d-%d-%dT%d:%d:%d) eg 2010-11-28T14:29:01 */ if (/* year */ virStrToLong_i(validTo, &tmp, 10, &tm.tm_year) < 0 || *tmp != '-' || /* month */ virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_mon) < 0 || *tmp != '-' || /* day */ virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_mday) < 0 || *tmp != 'T' || /* hour */ virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_hour) < 0 || *tmp != ':' || /* minute */ virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_min) < 0 || *tmp != ':' || /* second */ virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_sec) < 0 || *tmp != '\0') { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse password validity time '%s', expect YYYY-MM-DDTHH:MM:SS"), validTo); VIR_FREE(validTo); VIR_FREE(def->passwd); return -1; } VIR_FREE(validTo); tm.tm_year -= 1900; /* Human epoch starts at 0 BC, not 1900BC */ tm.tm_mon--; /* Humans start months at 1, computers at 0 */ def->validTo = timegm(&tm); def->expires = true; } if (connected) { int action = virDomainGraphicsAuthConnectedTypeFromString(connected); if (action <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown connected value %s"), connected); VIR_FREE(connected); return -1; } VIR_FREE(connected); /* VNC supports connected='keep' only */ if (type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && action != VIR_DOMAIN_GRAPHICS_AUTH_CONNECTED_KEEP) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("VNC supports connected='keep' only")); return -1; } def->connected = action; } return 0; } static int virDomainGraphicsListenDefParseXML(virDomainGraphicsListenDefPtr def, xmlNodePtr node, unsigned int flags) { int ret = -1; char *type = virXMLPropString(node, "type"); char *address = virXMLPropString(node, "address"); char *network = virXMLPropString(node, "network"); char *fromConfig = virXMLPropString(node, "fromConfig"); int tmp; if (!type) { virReportError(VIR_ERR_XML_ERROR, "%s", _("graphics listen type must be specified")); goto error; } if ((def->type = virDomainGraphicsListenTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown graphics listen type '%s'"), type); goto error; } /* address is recognized if either type='address', or if * type='network' and we're looking at live XML (i.e. *not* * inactive). It is otherwise ignored. */ if (address && address[0] && (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS || (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK && !(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)))) { def->address = address; address = NULL; } if (network && network[0]) { if (def->type != VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK) { /* network='xxx' never makes sense with anything except * type='network' */ virReportError(VIR_ERR_XML_ERROR, "%s", _("network attribute not allowed when listen type is not network")); goto error; } def->network = network; network = NULL; } if (fromConfig && flags & VIR_DOMAIN_DEF_PARSE_STATUS) { if (virStrToLong_i(fromConfig, NULL, 10, &tmp) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid fromConfig value: %s"), fromConfig); goto error; } def->fromConfig = tmp != 0; } ret = 0; error: if (ret < 0) virDomainGraphicsListenDefClear(def); VIR_FREE(type); VIR_FREE(address); VIR_FREE(network); VIR_FREE(fromConfig); return ret; } /* Parse the XML definition for a graphics device */ static virDomainGraphicsDefPtr virDomainGraphicsDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainGraphicsDefPtr def; char *type = NULL; int nListens; xmlNodePtr *listenNodes = NULL; char *listenAddr = NULL; xmlNodePtr save = ctxt->node; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = node; type = virXMLPropString(node, "type"); if (!type) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing graphics device type")); goto error; } if ((def->type = virDomainGraphicsTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown graphics device type '%s'"), type); goto error; } if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC || def->type == VIR_DOMAIN_GRAPHICS_TYPE_RDP || def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { /* parse the subelements for graphics types that support it */ nListens = virXPathNodeSet("./listen", ctxt, &listenNodes); if (nListens < 0) goto error; if (nListens > 0) { size_t i; if (VIR_ALLOC_N(def->listens, nListens) < 0) goto error; for (i = 0; i < nListens; i++) { int ret = virDomainGraphicsListenDefParseXML(&def->listens[i], listenNodes[i], flags); if (ret < 0) goto error; def->nListens++; } VIR_FREE(listenNodes); } /* listen attribute of is also supported by these, * but must match the 'address' attribute of the first listen * that is type='address' (if present) */ listenAddr = virXMLPropString(node, "listen"); if (listenAddr && !listenAddr[0]) VIR_FREE(listenAddr); if (listenAddr) { if (def->nListens == 0) { /* There were no elements, so we can just * directly set listenAddr as listens[0]->address */ if (virDomainGraphicsListenSetAddress(def, 0, listenAddr, -1, true) < 0) goto error; } else { /* There is at least 1 listen element, so we look for * the first listen of type='address', and make sure * its address matches the listen attribute from * graphics. */ bool matched = false; const char *found = NULL; size_t i; for (i = 0; i < nListens; i++) { if (virDomainGraphicsListenGetType(def, i) == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS) { found = virDomainGraphicsListenGetAddress(def, i); if (STREQ_NULLABLE(found, listenAddr)) matched = true; break; } } if (found && !matched) { virReportError(VIR_ERR_XML_ERROR, _("graphics listen attribute %s must match address " "attribute of first listen element (found %s)"), listenAddr, found); goto error; } else if (!found) { /* quietly ignore listen address if none of the listens * are of type address */ VIR_FREE(listenAddr); } } } } if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { char *port = virXMLPropString(node, "port"); char *websocket = virXMLPropString(node, "websocket"); char *sharePolicy = virXMLPropString(node, "sharePolicy"); char *autoport; if (port) { if (virStrToLong_i(port, NULL, 10, &def->data.vnc.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse vnc port %s"), port); VIR_FREE(port); goto error; } VIR_FREE(port); /* Legacy compat syntax, used -1 for auto-port */ if (def->data.vnc.port == -1) { if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) def->data.vnc.port = 0; def->data.vnc.autoport = true; } } else { def->data.vnc.port = 0; def->data.vnc.autoport = true; } if ((autoport = virXMLPropString(node, "autoport")) != NULL) { if (STREQ(autoport, "yes")) { if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) def->data.vnc.port = 0; def->data.vnc.autoport = true; } VIR_FREE(autoport); } if (websocket) { if (virStrToLong_i(websocket, NULL, 10, &def->data.vnc.websocket) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse vnc WebSocket port %s"), websocket); VIR_FREE(websocket); goto error; } VIR_FREE(websocket); } if (sharePolicy) { int policy = virDomainGraphicsVNCSharePolicyTypeFromString(sharePolicy); if (policy < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown vnc display sharing policy '%s'"), sharePolicy); VIR_FREE(sharePolicy); goto error; } else { def->data.vnc.sharePolicy = policy; } VIR_FREE(sharePolicy); } def->data.vnc.socket = virXMLPropString(node, "socket"); def->data.vnc.keymap = virXMLPropString(node, "keymap"); if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth, def->type) < 0) goto error; } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { char *fullscreen = virXMLPropString(node, "fullscreen"); if (fullscreen != NULL) { if (STREQ(fullscreen, "yes")) { def->data.sdl.fullscreen = true; } else if (STREQ(fullscreen, "no")) { def->data.sdl.fullscreen = false; } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown fullscreen value '%s'"), fullscreen); VIR_FREE(fullscreen); goto error; } VIR_FREE(fullscreen); } else { def->data.sdl.fullscreen = false; } def->data.sdl.xauth = virXMLPropString(node, "xauth"); def->data.sdl.display = virXMLPropString(node, "display"); } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_RDP) { char *port = virXMLPropString(node, "port"); char *autoport; char *replaceUser; char *multiUser; if (port) { if (virStrToLong_i(port, NULL, 10, &def->data.rdp.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse rdp port %s"), port); VIR_FREE(port); goto error; } /* Legacy compat syntax, used -1 for auto-port */ if (def->data.rdp.port == -1) def->data.rdp.autoport = true; VIR_FREE(port); } else { def->data.rdp.port = 0; def->data.rdp.autoport = true; } if ((autoport = virXMLPropString(node, "autoport")) != NULL) { if (STREQ(autoport, "yes")) def->data.rdp.autoport = true; VIR_FREE(autoport); } if (def->data.rdp.autoport && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) def->data.rdp.port = 0; if ((replaceUser = virXMLPropString(node, "replaceUser")) != NULL) { if (STREQ(replaceUser, "yes")) def->data.rdp.replaceUser = true; VIR_FREE(replaceUser); } if ((multiUser = virXMLPropString(node, "multiUser")) != NULL) { if (STREQ(multiUser, "yes")) def->data.rdp.multiUser = true; VIR_FREE(multiUser); } } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP) { char *fullscreen = virXMLPropString(node, "fullscreen"); if (fullscreen != NULL) { if (STREQ(fullscreen, "yes")) { def->data.desktop.fullscreen = true; } else if (STREQ(fullscreen, "no")) { def->data.desktop.fullscreen = false; } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown fullscreen value '%s'"), fullscreen); VIR_FREE(fullscreen); goto error; } VIR_FREE(fullscreen); } else { def->data.desktop.fullscreen = false; } def->data.desktop.display = virXMLPropString(node, "display"); } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { xmlNodePtr cur; char *port = virXMLPropString(node, "port"); char *tlsPort; char *autoport; char *defaultMode; int defaultModeVal; if (port) { if (virStrToLong_i(port, NULL, 10, &def->data.spice.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse spice port %s"), port); VIR_FREE(port); goto error; } VIR_FREE(port); } else { def->data.spice.port = 0; } tlsPort = virXMLPropString(node, "tlsPort"); if (tlsPort) { if (virStrToLong_i(tlsPort, NULL, 10, &def->data.spice.tlsPort) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse spice tlsPort %s"), tlsPort); VIR_FREE(tlsPort); goto error; } VIR_FREE(tlsPort); } else { def->data.spice.tlsPort = 0; } if ((autoport = virXMLPropString(node, "autoport")) != NULL) { if (STREQ(autoport, "yes")) def->data.spice.autoport = true; VIR_FREE(autoport); } def->data.spice.defaultMode = VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY; if ((defaultMode = virXMLPropString(node, "defaultMode")) != NULL) { if ((defaultModeVal = virDomainGraphicsSpiceChannelModeTypeFromString(defaultMode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown default spice channel mode %s"), defaultMode); VIR_FREE(defaultMode); goto error; } def->data.spice.defaultMode = defaultModeVal; VIR_FREE(defaultMode); } if (def->data.spice.port == -1 && def->data.spice.tlsPort == -1) { /* Legacy compat syntax, used -1 for auto-port */ def->data.spice.autoport = true; } if (def->data.spice.autoport && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) { def->data.spice.port = 0; def->data.spice.tlsPort = 0; } def->data.spice.keymap = virXMLPropString(node, "keymap"); if (virDomainGraphicsAuthDefParseXML(node, &def->data.spice.auth, def->type) < 0) goto error; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (xmlStrEqual(cur->name, BAD_CAST "channel")) { char *name, *mode; int nameval, modeval; name = virXMLPropString(cur, "name"); mode = virXMLPropString(cur, "mode"); if (!name || !mode) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice channel missing name/mode")); VIR_FREE(name); VIR_FREE(mode); goto error; } if ((nameval = virDomainGraphicsSpiceChannelNameTypeFromString(name)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice channel name %s"), name); VIR_FREE(name); VIR_FREE(mode); goto error; } if ((modeval = virDomainGraphicsSpiceChannelModeTypeFromString(mode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice channel mode %s"), mode); VIR_FREE(name); VIR_FREE(mode); goto error; } VIR_FREE(name); VIR_FREE(mode); def->data.spice.channels[nameval] = modeval; } else if (xmlStrEqual(cur->name, BAD_CAST "image")) { char *compression = virXMLPropString(cur, "compression"); int compressionVal; if (!compression) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice image missing compression")); goto error; } if ((compressionVal = virDomainGraphicsSpiceImageCompressionTypeFromString(compression)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice image compression %s"), compression); VIR_FREE(compression); goto error; } VIR_FREE(compression); def->data.spice.image = compressionVal; } else if (xmlStrEqual(cur->name, BAD_CAST "jpeg")) { char *compression = virXMLPropString(cur, "compression"); int compressionVal; if (!compression) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice jpeg missing compression")); goto error; } if ((compressionVal = virDomainGraphicsSpiceJpegCompressionTypeFromString(compression)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice jpeg compression %s"), compression); VIR_FREE(compression); goto error; } VIR_FREE(compression); def->data.spice.jpeg = compressionVal; } else if (xmlStrEqual(cur->name, BAD_CAST "zlib")) { char *compression = virXMLPropString(cur, "compression"); int compressionVal; if (!compression) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice zlib missing compression")); goto error; } if ((compressionVal = virDomainGraphicsSpiceZlibCompressionTypeFromString(compression)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice zlib compression %s"), compression); VIR_FREE(compression); goto error; } VIR_FREE(compression); def->data.spice.zlib = compressionVal; } else if (xmlStrEqual(cur->name, BAD_CAST "playback")) { char *compression = virXMLPropString(cur, "compression"); int compressionVal; if (!compression) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice playback missing compression")); goto error; } if ((compressionVal = virTristateSwitchTypeFromString(compression)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unknown spice playback compression")); VIR_FREE(compression); goto error; } VIR_FREE(compression); def->data.spice.playback = compressionVal; } else if (xmlStrEqual(cur->name, BAD_CAST "streaming")) { char *mode = virXMLPropString(cur, "mode"); int modeVal; if (!mode) { virReportError(VIR_ERR_XML_ERROR, "%s", _("spice streaming missing mode")); goto error; } if ((modeVal = virDomainGraphicsSpiceStreamingModeTypeFromString(mode)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unknown spice streaming mode")); VIR_FREE(mode); goto error; } VIR_FREE(mode); def->data.spice.streaming = modeVal; } else if (xmlStrEqual(cur->name, BAD_CAST "clipboard")) { char *copypaste = virXMLPropString(cur, "copypaste"); int copypasteVal; if (!copypaste) { virReportError(VIR_ERR_XML_ERROR, "%s", _("spice clipboard missing copypaste")); goto error; } if ((copypasteVal = virTristateBoolTypeFromString(copypaste)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown copypaste value '%s'"), copypaste); VIR_FREE(copypaste); goto error; } VIR_FREE(copypaste); def->data.spice.copypaste = copypasteVal; } else if (xmlStrEqual(cur->name, BAD_CAST "filetransfer")) { char *enable = virXMLPropString(cur, "enable"); int enableVal; if (!enable) { virReportError(VIR_ERR_XML_ERROR, "%s", _("spice filetransfer missing enable")); goto error; } if ((enableVal = virTristateBoolTypeFromString(enable)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown enable value '%s'"), enable); VIR_FREE(enable); goto error; } VIR_FREE(enable); def->data.spice.filetransfer = enableVal; } else if (xmlStrEqual(cur->name, BAD_CAST "mouse")) { char *mode = virXMLPropString(cur, "mode"); int modeVal; if (!mode) { virReportError(VIR_ERR_XML_ERROR, "%s", _("spice mouse missing mode")); goto error; } if ((modeVal = virDomainGraphicsSpiceMouseModeTypeFromString(mode)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown mouse mode value '%s'"), mode); VIR_FREE(mode); goto error; } VIR_FREE(mode); def->data.spice.mousemode = modeVal; } } cur = cur->next; } } cleanup: VIR_FREE(type); VIR_FREE(listenNodes); VIR_FREE(listenAddr); ctxt->node = save; return def; error: virDomainGraphicsDefFree(def); def = NULL; goto cleanup; } static virDomainSoundCodecDefPtr virDomainSoundCodecDefParseXML(xmlNodePtr node) { char *type; virDomainSoundCodecDefPtr def; if (VIR_ALLOC(def) < 0) return NULL; type = virXMLPropString(node, "type"); if ((def->type = virDomainSoundCodecTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown codec type '%s'"), type); goto error; } cleanup: VIR_FREE(type); return def; error: virDomainSoundCodecDefFree(def); def = NULL; goto cleanup; } static virDomainSoundDefPtr virDomainSoundDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { char *model; virDomainSoundDefPtr def; xmlNodePtr save = ctxt->node; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = node; model = virXMLPropString(node, "model"); if ((def->model = virDomainSoundModelTypeFromString(model)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown sound model '%s'"), model); goto error; } if (def->model == VIR_DOMAIN_SOUND_MODEL_ICH6 || def->model == VIR_DOMAIN_SOUND_MODEL_ICH9) { int ncodecs; xmlNodePtr *codecNodes = NULL; /* parse the subelements for sound models that support it */ ncodecs = virXPathNodeSet("./codec", ctxt, &codecNodes); if (ncodecs < 0) goto error; if (ncodecs > 0) { size_t i; if (VIR_ALLOC_N(def->codecs, ncodecs) < 0) { VIR_FREE(codecNodes); goto error; } for (i = 0; i < ncodecs; i++) { virDomainSoundCodecDefPtr codec = virDomainSoundCodecDefParseXML(codecNodes[i]); if (codec == NULL) { VIR_FREE(codecNodes); goto error; } codec->cad = def->ncodecs; /* that will do for now */ def->codecs[def->ncodecs++] = codec; } VIR_FREE(codecNodes); } } if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; cleanup: VIR_FREE(model); ctxt->node = save; return def; error: virDomainSoundDefFree(def); def = NULL; goto cleanup; } static virDomainWatchdogDefPtr virDomainWatchdogDefParseXML(xmlNodePtr node, unsigned int flags) { char *model = NULL; char *action = NULL; virDomainWatchdogDefPtr def; if (VIR_ALLOC(def) < 0) return NULL; model = virXMLPropString(node, "model"); if (model == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("watchdog must contain model name")); goto error; } def->model = virDomainWatchdogModelTypeFromString(model); if (def->model < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown watchdog model '%s'"), model); goto error; } action = virXMLPropString(node, "action"); if (action == NULL) { def->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET; } else { def->action = virDomainWatchdogActionTypeFromString(action); if (def->action < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown watchdog action '%s'"), action); goto error; } } if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; cleanup: VIR_FREE(action); VIR_FREE(model); return def; error: virDomainWatchdogDefFree(def); def = NULL; goto cleanup; } static virDomainRNGDefPtr virDomainRNGDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { char *model = NULL; char *backend = NULL; char *type = NULL; virDomainRNGDefPtr def; xmlNodePtr save = ctxt->node; xmlNodePtr *backends = NULL; int nbackends; if (VIR_ALLOC(def) < 0) return NULL; if (!(model = virXMLPropString(node, "model"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing RNG device model")); goto error; } if ((def->model = virDomainRNGModelTypeFromString(model)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown RNG model '%s'"), model); goto error; } ctxt->node = node; if (virXPathUInt("string(./rate/@bytes)", ctxt, &def->rate) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid RNG rate bytes value")); goto error; } if (def->rate > 0 && virXPathUInt("string(./rate/@period)", ctxt, &def->period) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid RNG rate period value")); goto error; } if ((nbackends = virXPathNodeSet("./backend", ctxt, &backends)) < 0) goto error; if (nbackends != 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only one RNG backend is supported")); goto error; } if (!(backend = virXMLPropString(backends[0], "model"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing RNG device backend model")); goto error; } if ((def->backend = virDomainRNGBackendTypeFromString(backend)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown RNG backend model '%s'"), backend); goto error; } switch ((virDomainRNGBackend) def->backend) { case VIR_DOMAIN_RNG_BACKEND_RANDOM: def->source.file = virXPathString("string(./backend)", ctxt); if (def->source.file && STRNEQ(def->source.file, "/dev/random") && STRNEQ(def->source.file, "/dev/hwrng")) { virReportError(VIR_ERR_XML_ERROR, _("file '%s' is not a supported random source"), def->source.file); goto error; } break; case VIR_DOMAIN_RNG_BACKEND_EGD: if (!(type = virXMLPropString(backends[0], "type"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing EGD backend type")); goto error; } if (VIR_ALLOC(def->source.chardev) < 0) goto error; def->source.chardev->type = virDomainChrTypeFromString(type); if (def->source.chardev->type < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown backend type '%s' for egd"), type); goto error; } if (virDomainChrSourceDefParseXML(def->source.chardev, backends[0]->children, flags, NULL, ctxt, NULL, 0) < 0) goto error; break; case VIR_DOMAIN_RNG_BACKEND_LAST: break; } if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; cleanup: VIR_FREE(model); VIR_FREE(backend); VIR_FREE(type); VIR_FREE(backends); ctxt->node = save; return def; error: virDomainRNGDefFree(def); def = NULL; goto cleanup; } static virDomainMemballoonDefPtr virDomainMemballoonDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { char *model; virDomainMemballoonDefPtr def; xmlNodePtr save = ctxt->node; unsigned int period = 0; if (VIR_ALLOC(def) < 0) return NULL; model = virXMLPropString(node, "model"); if (model == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("balloon memory must contain model name")); goto error; } if ((def->model = virDomainMemballoonModelTypeFromString(model)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown memory balloon model '%s'"), model); goto error; } ctxt->node = node; if (virXPathUInt("string(./stats/@period)", ctxt, &period) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid statistics collection period")); goto error; } def->period = period; if (def->period < 0) def->period = 0; if (def->model == VIR_DOMAIN_MEMBALLOON_MODEL_NONE) VIR_DEBUG("Ignoring device address for none model Memballoon"); else if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; cleanup: VIR_FREE(model); ctxt->node = save; return def; error: virDomainMemballoonDefFree(def); def = NULL; goto cleanup; } static virDomainNVRAMDefPtr virDomainNVRAMDefParseXML(xmlNodePtr node, unsigned int flags) { virDomainNVRAMDefPtr def; if (VIR_ALLOC(def) < 0) return NULL; if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; return def; error: virDomainNVRAMDefFree(def); return NULL; } static virDomainShmemDefPtr virDomainShmemDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { char *tmp = NULL; virDomainShmemDefPtr def = NULL; virDomainShmemDefPtr ret = NULL; xmlNodePtr msi = NULL; xmlNodePtr save = ctxt->node; xmlNodePtr server = NULL; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = node; if (!(def->name = virXMLPropString(node, "name"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("shmem element must contain 'name' attribute")); goto cleanup; } if (virDomainParseScaledValue("./size[1]", NULL, ctxt, &def->size, 1, ULLONG_MAX, false) < 0) goto cleanup; if ((server = virXPathNode("./server[1]", ctxt))) { def->server.enabled = true; if ((tmp = virXMLPropString(server, "path"))) def->server.path = virFileSanitizePath(tmp); VIR_FREE(tmp); } if ((msi = virXPathNode("./msi[1]", ctxt))) { def->msi.enabled = true; if ((tmp = virXMLPropString(msi, "vectors")) && virStrToLong_uip(tmp, NULL, 0, &def->msi.vectors) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid number of vectors for shmem: '%s'"), tmp); goto cleanup; } VIR_FREE(tmp); if ((tmp = virXMLPropString(msi, "ioeventfd")) && (def->msi.ioeventfd = virTristateSwitchTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid msi ioeventfd setting for shmem: '%s'"), tmp); goto cleanup; } VIR_FREE(tmp); } /* msi option is only relevant with a server */ if (def->msi.enabled && !def->server.enabled) { virReportError(VIR_ERR_XML_ERROR, "%s", _("msi option is only supported with a server")); goto cleanup; } if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto cleanup; ret = def; def = NULL; cleanup: ctxt->node = save; VIR_FREE(tmp); virDomainShmemDefFree(def); return ret; } static virSysinfoDefPtr virSysinfoParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned char *domUUID, bool uuid_generated) { virSysinfoDefPtr def; char *type; char *tmpUUID = NULL; if (!xmlStrEqual(node->name, BAD_CAST "sysinfo")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("XML does not contain expected 'sysinfo' element")); return NULL; } if (VIR_ALLOC(def) < 0) return NULL; type = virXMLPropString(node, "type"); if (type == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("sysinfo must contain a type attribute")); goto error; } if ((def->type = virSysinfoTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown sysinfo type '%s'"), type); goto error; } /* Extract BIOS related metadata */ def->bios_vendor = virXPathString("string(bios/entry[@name='vendor'])", ctxt); def->bios_version = virXPathString("string(bios/entry[@name='version'])", ctxt); def->bios_date = virXPathString("string(bios/entry[@name='date'])", ctxt); if (def->bios_date != NULL) { char *ptr; int month, day, year; /* Validate just the format of the date * Expect mm/dd/yyyy or mm/dd/yy, * where yy must be 00->99 and would be assumed to be 19xx * a yyyy date should be 1900 and beyond */ if (virStrToLong_i(def->bios_date, &ptr, 10, &month) < 0 || *ptr != '/' || virStrToLong_i(ptr + 1, &ptr, 10, &day) < 0 || *ptr != '/' || virStrToLong_i(ptr + 1, &ptr, 10, &year) < 0 || *ptr != '\0' || (month < 1 || month > 12) || (day < 1 || day > 31) || (year < 0 || (year >= 100 && year < 1900))) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("Invalid BIOS 'date' format")); goto error; } } def->bios_release = virXPathString("string(bios/entry[@name='release'])", ctxt); /* Extract system related metadata */ def->system_manufacturer = virXPathString("string(system/entry[@name='manufacturer'])", ctxt); def->system_product = virXPathString("string(system/entry[@name='product'])", ctxt); def->system_version = virXPathString("string(system/entry[@name='version'])", ctxt); def->system_serial = virXPathString("string(system/entry[@name='serial'])", ctxt); tmpUUID = virXPathString("string(system/entry[@name='uuid'])", ctxt); if (tmpUUID) { unsigned char uuidbuf[VIR_UUID_BUFLEN]; char uuidstr[VIR_UUID_STRING_BUFLEN]; if (virUUIDParse(tmpUUID, uuidbuf) < 0) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("malformed uuid element")); goto error; } if (uuid_generated) { memcpy(domUUID, uuidbuf, VIR_UUID_BUFLEN); } else if (memcmp(domUUID, uuidbuf, VIR_UUID_BUFLEN) != 0) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("UUID mismatch between and " "")); goto error; } /* Although we've validated the UUID as good, virUUIDParse() is * lax with respect to allowing extraneous "-" and " ", but the * underlying hypervisor may be less forgiving. Use virUUIDFormat() * to validate format in xml is right. If not, then format it * properly so that it's used correctly later. */ virUUIDFormat(uuidbuf, uuidstr); if (VIR_STRDUP(def->system_uuid, uuidstr) < 0) goto error; } def->system_sku = virXPathString("string(system/entry[@name='sku'])", ctxt); def->system_family = virXPathString("string(system/entry[@name='family'])", ctxt); cleanup: VIR_FREE(type); VIR_FREE(tmpUUID); return def; error: virSysinfoDefFree(def); def = NULL; goto cleanup; } unsigned int virDomainVideoDefaultRAM(const virDomainDef *def, const virDomainVideoType type) { /* Defer setting default vram to the Xen drivers */ if (def->virtType == VIR_DOMAIN_VIRT_XEN) return 0; switch (type) { case VIR_DOMAIN_VIDEO_TYPE_VGA: case VIR_DOMAIN_VIDEO_TYPE_CIRRUS: case VIR_DOMAIN_VIDEO_TYPE_VMVGA: if (def->virtType == VIR_DOMAIN_VIRT_VBOX) return 8 * 1024; else if (def->virtType == VIR_DOMAIN_VIRT_VMWARE) return 4 * 1024; else return 16 * 1024; break; case VIR_DOMAIN_VIDEO_TYPE_XEN: /* Original Xen PVFB hardcoded to 4 MB */ return 4 * 1024; case VIR_DOMAIN_VIDEO_TYPE_QXL: /* QEMU use 64M as the minimal video memory for qxl device */ return 64 * 1024; default: return 0; } } int virDomainVideoDefaultType(const virDomainDef *def) { switch (def->virtType) { case VIR_DOMAIN_VIRT_TEST: case VIR_DOMAIN_VIRT_QEMU: case VIR_DOMAIN_VIRT_KQEMU: case VIR_DOMAIN_VIRT_KVM: case VIR_DOMAIN_VIRT_XEN: if (def->os.type && (STREQ(def->os.type, "xen") || STREQ(def->os.type, "linux"))) return VIR_DOMAIN_VIDEO_TYPE_XEN; else if ARCH_IS_PPC64(def->os.arch) return VIR_DOMAIN_VIDEO_TYPE_VGA; else return VIR_DOMAIN_VIDEO_TYPE_CIRRUS; case VIR_DOMAIN_VIRT_VBOX: return VIR_DOMAIN_VIDEO_TYPE_VBOX; case VIR_DOMAIN_VIRT_VMWARE: return VIR_DOMAIN_VIDEO_TYPE_VMVGA; case VIR_DOMAIN_VIRT_PARALLELS: if (def->os.type) { if (STREQ(def->os.type, "hvm")) return VIR_DOMAIN_VIDEO_TYPE_VGA; else return VIR_DOMAIN_VIDEO_TYPE_PARALLELS; } else { return VIR_DOMAIN_VIDEO_TYPE_VGA; } default: return -1; } } static virDomainVideoAccelDefPtr virDomainVideoAccelDefParseXML(xmlNodePtr node) { xmlNodePtr cur; virDomainVideoAccelDefPtr def; char *support3d = NULL; char *support2d = NULL; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!support3d && !support2d && xmlStrEqual(cur->name, BAD_CAST "acceleration")) { support3d = virXMLPropString(cur, "accel3d"); support2d = virXMLPropString(cur, "accel2d"); } } cur = cur->next; } if (!support3d && !support2d) return NULL; if (VIR_ALLOC(def) < 0) return NULL; if (support3d) { if (STREQ(support3d, "yes")) def->support3d = true; else def->support3d = false; VIR_FREE(support3d); } if (support2d) { if (STREQ(support2d, "yes")) def->support2d = true; else def->support2d = false; VIR_FREE(support2d); } return def; } static virDomainVideoDefPtr virDomainVideoDefParseXML(xmlNodePtr node, const virDomainDef *dom, unsigned int flags) { virDomainVideoDefPtr def; xmlNodePtr cur; char *type = NULL; char *heads = NULL; char *vram = NULL; char *ram = NULL; char *vgamem = NULL; char *primary = NULL; if (VIR_ALLOC(def) < 0) return NULL; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!type && !vram && !ram && !heads && xmlStrEqual(cur->name, BAD_CAST "model")) { type = virXMLPropString(cur, "type"); ram = virXMLPropString(cur, "ram"); vram = virXMLPropString(cur, "vram"); vgamem = virXMLPropString(cur, "vgamem"); heads = virXMLPropString(cur, "heads"); if ((primary = virXMLPropString(cur, "primary")) != NULL) { if (STREQ(primary, "yes")) def->primary = 1; VIR_FREE(primary); } def->accel = virDomainVideoAccelDefParseXML(cur); } } cur = cur->next; } if (type) { if ((def->type = virDomainVideoTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown video model '%s'"), type); goto error; } } else { if ((def->type = virDomainVideoDefaultType(dom)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing video model and cannot determine default")); goto error; } } if (ram) { if (def->type != VIR_DOMAIN_VIDEO_TYPE_QXL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("ram attribute only supported for type of qxl")); goto error; } if (virStrToLong_uip(ram, NULL, 10, &def->ram) < 0) { virReportError(VIR_ERR_XML_ERROR, _("cannot parse video ram '%s'"), ram); goto error; } } else if (def->type == VIR_DOMAIN_VIDEO_TYPE_QXL) { def->ram = virDomainVideoDefaultRAM(dom, def->type); } if (vram) { if (virStrToLong_uip(vram, NULL, 10, &def->vram) < 0) { virReportError(VIR_ERR_XML_ERROR, _("cannot parse video vram '%s'"), vram); goto error; } } else { def->vram = virDomainVideoDefaultRAM(dom, def->type); } if (vgamem) { if (def->type != VIR_DOMAIN_VIDEO_TYPE_QXL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("vgamem attribute only supported for type of qxl")); goto error; } if (virStrToLong_uip(vgamem, NULL, 10, &def->vgamem) < 0) { virReportError(VIR_ERR_XML_ERROR, _("cannot parse video vgamem '%s'"), vgamem); goto error; } } if (heads) { if (virStrToLong_uip(heads, NULL, 10, &def->heads) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse video heads '%s'"), heads); goto error; } } else { def->heads = 1; } if (virDomainDeviceInfoParseXML(node, NULL, &def->info, flags) < 0) goto error; VIR_FREE(type); VIR_FREE(ram); VIR_FREE(vram); VIR_FREE(vgamem); VIR_FREE(heads); return def; error: virDomainVideoDefFree(def); VIR_FREE(type); VIR_FREE(ram); VIR_FREE(vram); VIR_FREE(vgamem); VIR_FREE(heads); return NULL; } static virDomainHostdevDefPtr virDomainHostdevDefParseXML(virDomainXMLOptionPtr xmlopt, const virDomainDef *vmdef, xmlNodePtr node, xmlXPathContextPtr ctxt, virHashTablePtr bootHash, unsigned int flags) { virDomainHostdevDefPtr def; xmlNodePtr save = ctxt->node; char *mode = virXMLPropString(node, "mode"); char *type = virXMLPropString(node, "type"); ctxt->node = node; if (!(def = virDomainHostdevDefAlloc())) goto error; if (mode) { if ((def->mode = virDomainHostdevModeTypeFromString(mode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown hostdev mode '%s'"), mode); goto error; } } else { def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; } switch (def->mode) { case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: /* parse managed/mode/type, and the element */ if (virDomainHostdevDefParseXMLSubsys(node, ctxt, type, def, flags) < 0) goto error; break; case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: /* parse managed/mode/type, and the element */ if (virDomainHostdevDefParseXMLCaps(node, ctxt, type, def) < 0) goto error; break; default: virReportError(VIR_ERR_INTERNAL_ERROR, _("Unexpected hostdev mode %d"), def->mode); goto error; } if (def->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { if (virDomainDeviceInfoParseXML(node, bootHash, def->info, flags | VIR_DOMAIN_DEF_PARSE_ALLOW_BOOT | VIR_DOMAIN_DEF_PARSE_ALLOW_ROM) < 0) goto error; } if (def->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { switch (def->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: if (def->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && def->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("PCI host devices must use 'pci' address type")); goto error; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: if (def->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && virDomainHostdevAssignAddress(xmlopt, vmdef, def) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("SCSI host devices must have address specified")); goto error; } if (virXPathBoolean("boolean(./readonly)", ctxt)) def->readonly = true; if (virXPathBoolean("boolean(./shareable)", ctxt)) def->shareable = true; break; } } cleanup: VIR_FREE(type); VIR_FREE(mode); ctxt->node = save; return def; error: virDomainHostdevDefFree(def); def = NULL; goto cleanup; } static virDomainRedirdevDefPtr virDomainRedirdevDefParseXML(xmlNodePtr node, virHashTablePtr bootHash, unsigned int flags) { xmlNodePtr cur; virDomainRedirdevDefPtr def; char *bus, *type = NULL; int remaining; if (VIR_ALLOC(def) < 0) return NULL; bus = virXMLPropString(node, "bus"); if (bus) { if ((def->bus = virDomainRedirdevBusTypeFromString(bus)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown redirdev bus '%s'"), bus); goto error; } } else { def->bus = VIR_DOMAIN_REDIRDEV_BUS_USB; } type = virXMLPropString(node, "type"); if (type) { if ((def->source.chr.type = virDomainChrTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown redirdev character device type '%s'"), type); goto error; } } else { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing type in redirdev")); goto error; } cur = node->children; /* boot gets parsed in virDomainDeviceInfoParseXML * source gets parsed in virDomainChrSourceDefParseXML * we don't know any of the elements that might remain */ remaining = virDomainChrSourceDefParseXML(&def->source.chr, cur, flags, NULL, NULL, NULL, 0); if (remaining < 0) goto error; if (def->source.chr.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) def->source.chr.data.spicevmc = VIR_DOMAIN_CHR_SPICEVMC_USBREDIR; if (virDomainDeviceInfoParseXML(node, bootHash, &def->info, flags | VIR_DOMAIN_DEF_PARSE_ALLOW_BOOT) < 0) goto error; if (def->bus == VIR_DOMAIN_REDIRDEV_BUS_USB && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid address for a USB device")); goto error; } cleanup: VIR_FREE(bus); VIR_FREE(type); return def; error: virDomainRedirdevDefFree(def); def = NULL; goto cleanup; } /* * This is the helper function to convert USB device version from a * format of JJ.MN to a format of 0xJJMN where JJ is the major * version number, M is the minor version number and N is the * sub minor version number. * e.g. USB version 2.0 is reported as 0x0200, * USB version 4.07 as 0x0407 */ static int virDomainRedirFilterUSBVersionHelper(const char *version, virDomainRedirFilterUSBDevDefPtr def) { unsigned int major, minor; char *s = NULL; if ((virStrToLong_ui(version, &s, 10, &major)) < 0 || *s++ != '.' || (virStrToLong_ui(s, NULL, 10, &minor)) < 0) goto error; if (major >= 100 || minor >= 100) goto error; /* Treat JJ.M as JJ.M0, not JJ.0M */ if (strlen(s) == 1) minor *= 10; def->version = (major / 10) << 12 | (major % 10) << 8 | (minor / 10) << 4 | (minor % 10) << 0; return 0; error: virReportError(VIR_ERR_XML_ERROR, _("Cannot parse USB device version %s"), version); return -1; } static virDomainRedirFilterUSBDevDefPtr virDomainRedirFilterUSBDevDefParseXML(xmlNodePtr node) { char *class; char *vendor = NULL, *product = NULL; char *version = NULL, *allow = NULL; virDomainRedirFilterUSBDevDefPtr def; if (VIR_ALLOC(def) < 0) return NULL; class = virXMLPropString(node, "class"); if (class) { if ((virStrToLong_i(class, NULL, 0, &def->usbClass)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Cannot parse USB Class code %s"), class); goto error; } if (def->usbClass != -1 && def->usbClass &~ 0xFF) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid USB Class code %s"), class); goto error; } } else { def->usbClass = -1; } vendor = virXMLPropString(node, "vendor"); if (vendor) { if ((virStrToLong_i(vendor, NULL, 0, &def->vendor)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Cannot parse USB vendor ID %s"), vendor); goto error; } } else { def->vendor = -1; } product = virXMLPropString(node, "product"); if (product) { if ((virStrToLong_i(product, NULL, 0, &def->product)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Cannot parse USB product ID %s"), product); goto error; } } else { def->product = -1; } version = virXMLPropString(node, "version"); if (version) { if (STREQ(version, "-1")) def->version = -1; else if ((virDomainRedirFilterUSBVersionHelper(version, def)) < 0) goto error; } else { def->version = -1; } allow = virXMLPropString(node, "allow"); if (allow) { if (STREQ(allow, "yes")) { def->allow = true; } else if (STREQ(allow, "no")) { def->allow = false; } else { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Invalid allow value, either 'yes' or 'no'")); goto error; } } else { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing allow attribute for USB redirection filter")); goto error; } cleanup: VIR_FREE(class); VIR_FREE(vendor); VIR_FREE(product); VIR_FREE(version); VIR_FREE(allow); return def; error: VIR_FREE(def); def = NULL; goto cleanup; } static virDomainRedirFilterDefPtr virDomainRedirFilterDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt) { int n; size_t i; xmlNodePtr *nodes = NULL; xmlNodePtr save = ctxt->node; virDomainRedirFilterDefPtr def = NULL; if (VIR_ALLOC(def) < 0) goto error; ctxt->node = node; if ((n = virXPathNodeSet("./usbdev", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->usbdevs, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainRedirFilterUSBDevDefPtr usbdev = virDomainRedirFilterUSBDevDefParseXML(nodes[i]); if (!usbdev) goto error; def->usbdevs[def->nusbdevs++] = usbdev; } VIR_FREE(nodes); ctxt->node = save; return def; error: VIR_FREE(nodes); virDomainRedirFilterDefFree(def); return NULL; } static int virDomainEventActionParseXML(xmlXPathContextPtr ctxt, const char *name, const char *xpath, int *val, int defaultVal, virEventActionFromStringFunc convFunc) { char *tmp = virXPathString(xpath, ctxt); if (tmp == NULL) { *val = defaultVal; } else { *val = convFunc(tmp); if (*val < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown %s action: %s"), name, tmp); VIR_FREE(tmp); return -1; } VIR_FREE(tmp); } return 0; } static int virDomainPMStateParseXML(xmlXPathContextPtr ctxt, const char *xpath, int *val) { int ret = -1; char *tmp = virXPathString(xpath, ctxt); if (tmp) { *val = virTristateBoolTypeFromString(tmp); if (*val < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown PM state value %s"), tmp); goto cleanup; } } ret = 0; cleanup: VIR_FREE(tmp); return ret; } static int virDomainMemorySourceDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virDomainMemoryDefPtr def) { int ret = -1; char *nodemask = NULL; xmlNodePtr save = ctxt->node; ctxt->node = node; if (virDomainParseMemory("./pagesize", "./pagesize/@unit", ctxt, &def->pagesize, false, false) < 0) goto cleanup; if ((nodemask = virXPathString("string(./nodemask)", ctxt))) { if (virBitmapParse(nodemask, 0, &def->sourceNodes, VIR_DOMAIN_CPUMASK_LEN) < 0) goto cleanup; if (virBitmapIsAllClear(def->sourceNodes)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid value of 'nodemask': %s"), nodemask); goto cleanup; } } ret = 0; cleanup: VIR_FREE(nodemask); ctxt->node = save; return ret; } static int virDomainMemoryTargetDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virDomainMemoryDefPtr def) { int ret = -1; xmlNodePtr save = ctxt->node; ctxt->node = node; if (virXPathUInt("string(./node)", ctxt, &def->targetNode) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid or missing value of memory device node")); goto cleanup; } if (virDomainParseMemory("./size", "./size/@unit", ctxt, &def->size, true, false) < 0) goto cleanup; ret = 0; cleanup: ctxt->node = save; return ret; } static virDomainMemoryDefPtr virDomainMemoryDefParseXML(xmlNodePtr memdevNode, xmlXPathContextPtr ctxt, unsigned int flags) { char *tmp = NULL; xmlNodePtr save = ctxt->node; xmlNodePtr node; virDomainMemoryDefPtr def; ctxt->node = memdevNode; if (VIR_ALLOC(def) < 0) return NULL; if (!(tmp = virXMLPropString(memdevNode, "model"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing memory model")); goto error; } if ((def->model = virDomainMemoryModelTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid memory model '%s'"), tmp); goto error; } VIR_FREE(tmp); /* source */ if ((node = virXPathNode("./source", ctxt)) && virDomainMemorySourceDefParseXML(node, ctxt, def) < 0) goto error; /* target */ if (!(node = virXPathNode("./target", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing element for device")); goto error; } if (virDomainMemoryTargetDefParseXML(node, ctxt, def) < 0) goto error; if (virDomainDeviceInfoParseXML(memdevNode, NULL, &def->info, flags) < 0) goto error; return def; error: VIR_FREE(tmp); virDomainMemoryDefFree(def); ctxt->node = save; return NULL; } virDomainDeviceDefPtr virDomainDeviceDefParse(const char *xmlStr, const virDomainDef *def, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, unsigned int flags) { xmlDocPtr xml; xmlNodePtr node; xmlXPathContextPtr ctxt = NULL; virDomainDeviceDefPtr dev = NULL; if (!(xml = virXMLParseStringCtxt(xmlStr, _("(device_definition)"), &ctxt))) goto error; node = ctxt->node; if (VIR_ALLOC(dev) < 0) goto error; if ((dev->type = virDomainDeviceTypeFromString((const char *) node->name)) < 0) { /* Some crazy mapping of serial, parallel, console and channel to * VIR_DOMAIN_DEVICE_CHR. */ if (xmlStrEqual(node->name, BAD_CAST "channel") || xmlStrEqual(node->name, BAD_CAST "console") || xmlStrEqual(node->name, BAD_CAST "parallel") || xmlStrEqual(node->name, BAD_CAST "serial")) { dev->type = VIR_DOMAIN_DEVICE_CHR; } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown device type '%s'"), node->name); goto error; } } switch ((virDomainDeviceType) dev->type) { case VIR_DOMAIN_DEVICE_DISK: if (!(dev->data.disk = virDomainDiskDefParseXML(xmlopt, node, ctxt, NULL, def->seclabels, def->nseclabels, flags))) goto error; break; case VIR_DOMAIN_DEVICE_LEASE: if (!(dev->data.lease = virDomainLeaseDefParseXML(node))) goto error; break; case VIR_DOMAIN_DEVICE_FS: if (!(dev->data.fs = virDomainFSDefParseXML(node, ctxt, flags))) goto error; break; case VIR_DOMAIN_DEVICE_NET: if (!(dev->data.net = virDomainNetDefParseXML(xmlopt, node, ctxt, NULL, flags))) goto error; break; case VIR_DOMAIN_DEVICE_INPUT: if (!(dev->data.input = virDomainInputDefParseXML(def, node, flags))) goto error; break; case VIR_DOMAIN_DEVICE_SOUND: if (!(dev->data.sound = virDomainSoundDefParseXML(node, ctxt, flags))) goto error; break; case VIR_DOMAIN_DEVICE_WATCHDOG: if (!(dev->data.watchdog = virDomainWatchdogDefParseXML(node, flags))) goto error; break; case VIR_DOMAIN_DEVICE_VIDEO: if (!(dev->data.video = virDomainVideoDefParseXML(node, def, flags))) goto error; break; case VIR_DOMAIN_DEVICE_HOSTDEV: if (!(dev->data.hostdev = virDomainHostdevDefParseXML(xmlopt, def, node, ctxt, NULL, flags))) goto error; break; case VIR_DOMAIN_DEVICE_CONTROLLER: if (!(dev->data.controller = virDomainControllerDefParseXML(node, ctxt, flags))) goto error; break; case VIR_DOMAIN_DEVICE_GRAPHICS: if (!(dev->data.graphics = virDomainGraphicsDefParseXML(node, ctxt, flags))) goto error; break; case VIR_DOMAIN_DEVICE_HUB: if (!(dev->data.hub = virDomainHubDefParseXML(node, flags))) goto error; break; case VIR_DOMAIN_DEVICE_REDIRDEV: if (!(dev->data.redirdev = virDomainRedirdevDefParseXML(node, NULL, flags))) goto error; break; case VIR_DOMAIN_DEVICE_RNG: if (!(dev->data.rng = virDomainRNGDefParseXML(node, ctxt, flags))) goto error; break; case VIR_DOMAIN_DEVICE_CHR: if (!(dev->data.chr = virDomainChrDefParseXML(ctxt, node, def->seclabels, def->nseclabels, flags))) goto error; break; case VIR_DOMAIN_DEVICE_SMARTCARD: if (!(dev->data.smartcard = virDomainSmartcardDefParseXML(node, flags))) goto error; break; case VIR_DOMAIN_DEVICE_MEMBALLOON: if (!(dev->data.memballoon = virDomainMemballoonDefParseXML(node, ctxt, flags))) goto error; break; case VIR_DOMAIN_DEVICE_NVRAM: if (!(dev->data.nvram = virDomainNVRAMDefParseXML(node, flags))) goto error; break; case VIR_DOMAIN_DEVICE_SHMEM: if (!(dev->data.shmem = virDomainShmemDefParseXML(node, ctxt, flags))) goto error; break; case VIR_DOMAIN_DEVICE_TPM: if (!(dev->data.tpm = virDomainTPMDefParseXML(node, ctxt, flags))) goto error; break; case VIR_DOMAIN_DEVICE_PANIC: if (!(dev->data.panic = virDomainPanicDefParseXML(node))) goto error; break; case VIR_DOMAIN_DEVICE_MEMORY: if (!(dev->data.memory = virDomainMemoryDefParseXML(node, ctxt, flags))) goto error; break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; } /* callback to fill driver specific device aspects */ if (virDomainDeviceDefPostParse(dev, def, caps, xmlopt) < 0) goto error; cleanup: xmlFreeDoc(xml); xmlXPathFreeContext(ctxt); return dev; error: VIR_FREE(dev); goto cleanup; } virStorageSourcePtr virDomainDiskDefSourceParse(const char *xmlStr, const virDomainDef *def, virDomainXMLOptionPtr xmlopt, unsigned int flags) { xmlDocPtr xml; xmlNodePtr node; xmlXPathContextPtr ctxt = NULL; virDomainDiskDefPtr disk = NULL; virStorageSourcePtr ret = NULL; if (!(xml = virXMLParseStringCtxt(xmlStr, _("(disk_definition)"), &ctxt))) goto cleanup; node = ctxt->node; if (!xmlStrEqual(node->name, BAD_CAST "disk")) { virReportError(VIR_ERR_XML_ERROR, _("expecting root element of 'disk', not '%s'"), node->name); goto cleanup; } flags |= VIR_DOMAIN_DEF_PARSE_DISK_SOURCE; if (!(disk = virDomainDiskDefParseXML(xmlopt, node, ctxt, NULL, def->seclabels, def->nseclabels, flags))) goto cleanup; ret = disk->src; disk->src = NULL; cleanup: virDomainDiskDefFree(disk); xmlFreeDoc(xml); xmlXPathFreeContext(ctxt); return ret; } static const char * virDomainChrTargetTypeToString(int deviceType, int targetType) { const char *type = NULL; switch (deviceType) { case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: type = virDomainChrChannelTargetTypeToString(targetType); break; case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: type = virDomainChrConsoleTargetTypeToString(targetType); break; case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL: type = virDomainChrSerialTargetTypeToString(targetType); break; default: break; } return type; } int virDomainHostdevInsert(virDomainDefPtr def, virDomainHostdevDefPtr hostdev) { return VIR_APPEND_ELEMENT(def->hostdevs, def->nhostdevs, hostdev); } virDomainHostdevDefPtr virDomainHostdevRemove(virDomainDefPtr def, size_t i) { virDomainHostdevDefPtr hostdev = def->hostdevs[i]; VIR_DELETE_ELEMENT(def->hostdevs, i, def->nhostdevs); return hostdev; } static int virDomainHostdevMatchSubsysUSB(virDomainHostdevDefPtr first, virDomainHostdevDefPtr second) { virDomainHostdevSubsysUSBPtr first_usbsrc = &first->source.subsys.u.usb; virDomainHostdevSubsysUSBPtr second_usbsrc = &second->source.subsys.u.usb; if (first_usbsrc->bus && first_usbsrc->device) { /* specified by bus location on host */ if (first_usbsrc->bus == second_usbsrc->bus && first_usbsrc->device == second_usbsrc->device) return 1; } else { /* specified by product & vendor id */ if (first_usbsrc->product == second_usbsrc->product && first_usbsrc->vendor == second_usbsrc->vendor) return 1; } return 0; } static int virDomainHostdevMatchSubsysPCI(virDomainHostdevDefPtr first, virDomainHostdevDefPtr second) { virDomainHostdevSubsysPCIPtr first_pcisrc = &first->source.subsys.u.pci; virDomainHostdevSubsysPCIPtr second_pcisrc = &second->source.subsys.u.pci; if (first_pcisrc->addr.domain == second_pcisrc->addr.domain && first_pcisrc->addr.bus == second_pcisrc->addr.bus && first_pcisrc->addr.slot == second_pcisrc->addr.slot && first_pcisrc->addr.function == second_pcisrc->addr.function) return 1; return 0; } static int virDomainHostdevMatchSubsysSCSIHost(virDomainHostdevDefPtr first, virDomainHostdevDefPtr second) { virDomainHostdevSubsysSCSIHostPtr first_scsihostsrc = &first->source.subsys.u.scsi.u.host; virDomainHostdevSubsysSCSIHostPtr second_scsihostsrc = &second->source.subsys.u.scsi.u.host; if (STREQ(first_scsihostsrc->adapter, second_scsihostsrc->adapter) && first_scsihostsrc->bus == second_scsihostsrc->bus && first_scsihostsrc->target == second_scsihostsrc->target && first_scsihostsrc->unit == second_scsihostsrc->unit) return 1; return 0; } static int virDomainHostdevMatchSubsysSCSIiSCSI(virDomainHostdevDefPtr first, virDomainHostdevDefPtr second) { virDomainHostdevSubsysSCSIiSCSIPtr first_iscsisrc = &first->source.subsys.u.scsi.u.iscsi; virDomainHostdevSubsysSCSIiSCSIPtr second_iscsisrc = &second->source.subsys.u.scsi.u.iscsi; if (STREQ(first_iscsisrc->hosts[0].name, second_iscsisrc->hosts[0].name) && STREQ(first_iscsisrc->hosts[0].port, second_iscsisrc->hosts[0].port) && STREQ(first_iscsisrc->path, second_iscsisrc->path)) return 1; return 0; } static int virDomainHostdevMatchSubsys(virDomainHostdevDefPtr a, virDomainHostdevDefPtr b) { if (a->source.subsys.type != b->source.subsys.type) return 0; switch (a->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: return virDomainHostdevMatchSubsysPCI(a, b); case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: return virDomainHostdevMatchSubsysUSB(a, b); case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: if (a->source.subsys.u.scsi.protocol != b->source.subsys.u.scsi.protocol) return 0; if (a->source.subsys.u.scsi.protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) return virDomainHostdevMatchSubsysSCSIiSCSI(a, b); else return virDomainHostdevMatchSubsysSCSIHost(a, b); } return 0; } static int virDomainHostdevMatchCapsStorage(virDomainHostdevDefPtr a, virDomainHostdevDefPtr b) { return STREQ_NULLABLE(a->source.caps.u.storage.block, b->source.caps.u.storage.block); } static int virDomainHostdevMatchCapsMisc(virDomainHostdevDefPtr a, virDomainHostdevDefPtr b) { return STREQ_NULLABLE(a->source.caps.u.misc.chardev, b->source.caps.u.misc.chardev); } static int virDomainHostdevMatchCapsNet(virDomainHostdevDefPtr a, virDomainHostdevDefPtr b) { return STREQ_NULLABLE(a->source.caps.u.net.iface, b->source.caps.u.net.iface); } static int virDomainHostdevMatchCaps(virDomainHostdevDefPtr a, virDomainHostdevDefPtr b) { if (a->source.caps.type != b->source.caps.type) return 0; switch (a->source.caps.type) { case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_STORAGE: return virDomainHostdevMatchCapsStorage(a, b); case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_MISC: return virDomainHostdevMatchCapsMisc(a, b); case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_NET: return virDomainHostdevMatchCapsNet(a, b); } return 0; } static int virDomainHostdevMatch(virDomainHostdevDefPtr a, virDomainHostdevDefPtr b) { if (a->mode != b->mode) return 0; switch (a->mode) { case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: return virDomainHostdevMatchSubsys(a, b); case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: return virDomainHostdevMatchCaps(a, b); } return 0; } /* Find an entry in hostdevs that matches the source spec in * @match. return pointer to the entry in @found (if found is * non-NULL). Returns index (within hostdevs) of matched entry, or -1 * if no match was found. */ int virDomainHostdevFind(virDomainDefPtr def, virDomainHostdevDefPtr match, virDomainHostdevDefPtr *found) { virDomainHostdevDefPtr local_found; size_t i; if (!found) found = &local_found; *found = NULL; for (i = 0; i < def->nhostdevs; i++) { if (virDomainHostdevMatch(match, def->hostdevs[i])) { *found = def->hostdevs[i]; break; } } return *found ? i : -1; } static bool virDomainDiskControllerMatch(int controller_type, int disk_bus) { if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI && disk_bus == VIR_DOMAIN_DISK_BUS_SCSI) return true; if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_FDC && disk_bus == VIR_DOMAIN_DISK_BUS_FDC) return true; if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && disk_bus == VIR_DOMAIN_DISK_BUS_IDE) return true; if (controller_type == VIR_DOMAIN_CONTROLLER_TYPE_SATA && disk_bus == VIR_DOMAIN_DISK_BUS_SATA) return true; return false; } /* Return true if there's a duplicate disk[]->dst name for the same bus */ bool virDomainDiskDefDstDuplicates(virDomainDefPtr def) { size_t i, j; /* optimization */ if (def->ndisks <= 1) return false; for (i = 1; i < def->ndisks; i++) { for (j = 0; j < i; j++) { if (def->disks[i]->bus == def->disks[j]->bus && STREQ(def->disks[i]->dst, def->disks[j]->dst)) { virReportError(VIR_ERR_XML_ERROR, _("target '%s' duplicated for disk sources " "'%s' and '%s'"), def->disks[i]->dst, NULLSTR(virDomainDiskGetSource(def->disks[i])), NULLSTR(virDomainDiskGetSource(def->disks[j]))); return true; } } } return false; } int virDomainDiskIndexByAddress(virDomainDefPtr def, virDevicePCIAddressPtr pci_address, unsigned int bus, unsigned int target, unsigned int unit) { virDomainDiskDefPtr vdisk; virDomainControllerDefPtr controller = NULL; size_t i; int cidx; if ((cidx = virDomainControllerFindByPCIAddress(def, pci_address)) >= 0) controller = def->controllers[cidx]; for (i = 0; i < def->ndisks; i++) { vdisk = def->disks[i]; if (vdisk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && virDevicePCIAddressEqual(&vdisk->info.addr.pci, pci_address)) return i; if (vdisk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { virDomainDeviceDriveAddressPtr drive = &vdisk->info.addr.drive; if (controller && virDomainDiskControllerMatch(controller->type, vdisk->bus) && drive->controller == controller->idx && drive->bus == bus && drive->target == target && drive->unit == unit) return i; } } return -1; } int virDomainDiskIndexByName(virDomainDefPtr def, const char *name, bool allow_ambiguous) { virDomainDiskDefPtr vdisk; size_t i; int candidate = -1; /* We prefer the name (it's shorter, required * for all disks, and should be unambiguous), but also support * (if unambiguous). Assume dst if there is * no leading slash, source name otherwise. */ for (i = 0; i < def->ndisks; i++) { vdisk = def->disks[i]; if (*name != '/') { if (STREQ(vdisk->dst, name)) return i; } else if (STREQ_NULLABLE(virDomainDiskGetSource(vdisk), name)) { if (allow_ambiguous) return i; if (candidate >= 0) return -1; candidate = i; } } return candidate; } /* Return the path to a disk image if a string identifies at least one * disk belonging to the domain (both device strings 'vda' and paths * '/path/to/file' are converted into '/path/to/file'). */ const char * virDomainDiskPathByName(virDomainDefPtr def, const char *name) { int idx = virDomainDiskIndexByName(def, name, true); return idx < 0 ? NULL : virDomainDiskGetSource(def->disks[idx]); } int virDomainDiskInsert(virDomainDefPtr def, virDomainDiskDefPtr disk) { if (VIR_REALLOC_N(def->disks, def->ndisks+1) < 0) return -1; virDomainDiskInsertPreAlloced(def, disk); return 0; } void virDomainDiskInsertPreAlloced(virDomainDefPtr def, virDomainDiskDefPtr disk) { int idx; /* Tenatively plan to insert disk at the end. */ int insertAt = -1; /* Then work backwards looking for disks on * the same bus. If we find a disk with a drive * index greater than the new one, insert at * that position */ for (idx = (def->ndisks - 1); idx >= 0; idx--) { /* If bus matches and current disk is after * new disk, then new disk should go here */ if (def->disks[idx]->bus == disk->bus && (virDiskNameToIndex(def->disks[idx]->dst) > virDiskNameToIndex(disk->dst))) { insertAt = idx; } else if (def->disks[idx]->bus == disk->bus && insertAt == -1) { /* Last disk with match bus is before the * new disk, then put new disk just after */ insertAt = idx + 1; } } /* VIR_INSERT_ELEMENT_INPLACE will never return an error here. */ ignore_value(VIR_INSERT_ELEMENT_INPLACE(def->disks, insertAt, def->ndisks, disk)); } virDomainDiskDefPtr virDomainDiskRemove(virDomainDefPtr def, size_t i) { virDomainDiskDefPtr disk = def->disks[i]; VIR_DELETE_ELEMENT(def->disks, i, def->ndisks); return disk; } virDomainDiskDefPtr virDomainDiskRemoveByName(virDomainDefPtr def, const char *name) { int idx = virDomainDiskIndexByName(def, name, false); if (idx < 0) return NULL; return virDomainDiskRemove(def, idx); } /** * virDomainHasBlockjob: * @vm: domain object * @copy_only: Reject only block copy job * * Return true if @vm has at least one disk involved in a current block * copy/commit/pull job. If @copy_only is true this returns true only if the * disk is involved in a block copy. * */ bool virDomainHasBlockjob(virDomainObjPtr vm, bool copy_only) { size_t i; for (i = 0; i < vm->def->ndisks; i++) { if (!copy_only && vm->def->disks[i]->blockjob) return true; if (vm->def->disks[i]->mirror && vm->def->disks[i]->mirrorJob == VIR_DOMAIN_BLOCK_JOB_TYPE_COPY) return true; } return false; } int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net) { /* hostdev net devices must also exist in the hostdevs array */ if (net->type == VIR_DOMAIN_NET_TYPE_HOSTDEV && virDomainHostdevInsert(def, &net->data.hostdev.def) < 0) return -1; if (VIR_APPEND_ELEMENT(def->nets, def->nnets, net) < 0) { /* virDomainHostdevInsert just appends new hostdevs, so we are sure * that the hostdev we've added a few lines above is at the end of * array. Although, devices are indexed from zero ... */ virDomainHostdevRemove(def, def->nhostdevs - 1); return -1; } return 0; } /* virDomainNetFindIdx: search according to mac address and guest side * PCI address (if specified) * * Return: index of match if unique match found * -1 otherwise and an error is logged */ int virDomainNetFindIdx(virDomainDefPtr def, virDomainNetDefPtr net) { size_t i; int matchidx = -1; char mac[VIR_MAC_STRING_BUFLEN]; bool PCIAddrSpecified = virDomainDeviceAddressIsValid(&net->info, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI); for (i = 0; i < def->nnets; i++) { if (virMacAddrCmp(&def->nets[i]->mac, &net->mac)) continue; if ((matchidx >= 0) && !PCIAddrSpecified) { /* there were multiple matches on mac address, and no * qualifying guest-side PCI address was given, so we must * fail (NB: a USB address isn't adequate, since it may * specify only vendor and product ID, and there may be * multiples of those. */ virReportError(VIR_ERR_OPERATION_FAILED, _("multiple devices matching mac address %s found"), virMacAddrFormat(&net->mac, mac)); return -1; } if (PCIAddrSpecified) { if (virDevicePCIAddressEqual(&def->nets[i]->info.addr.pci, &net->info.addr.pci)) { /* exit early if the pci address was specified and * it matches, as this guarantees no duplicates. */ matchidx = i; break; } } else { /* no PCI address given, so there may be multiple matches */ matchidx = i; } } if (matchidx < 0) { if (PCIAddrSpecified) { virReportError(VIR_ERR_OPERATION_FAILED, _("no device matching mac address %s found on " "%.4x:%.2x:%.2x.%.1x"), virMacAddrFormat(&net->mac, mac), net->info.addr.pci.domain, net->info.addr.pci.bus, net->info.addr.pci.slot, net->info.addr.pci.function); } else { virReportError(VIR_ERR_OPERATION_FAILED, _("no device matching mac address %s found"), virMacAddrFormat(&net->mac, mac)); } } return matchidx; } bool virDomainHasNet(virDomainDefPtr def, virDomainNetDefPtr net) { size_t i; bool PCIAddrSpecified = virDomainDeviceAddressIsValid(&net->info, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI); for (i = 0; i < def->nnets; i++) { if (virMacAddrCmp(&def->nets[i]->mac, &net->mac)) continue; if (PCIAddrSpecified) { if (virDevicePCIAddressEqual(&def->nets[i]->info.addr.pci, &net->info.addr.pci)) return true; } else { return true; } } return false; } void virDomainNetRemoveHostdev(virDomainDefPtr def, virDomainNetDefPtr net) { /* hostdev net devices are normally also be in the hostdevs * array, but might have already been removed by the time we * get here. */ virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net); size_t i; if (hostdev) { for (i = 0; i < def->nhostdevs; i++) { if (def->hostdevs[i] == hostdev) { virDomainHostdevRemove(def, i); break; } } } } virDomainNetDefPtr virDomainNetRemove(virDomainDefPtr def, size_t i) { virDomainNetDefPtr net = def->nets[i]; virDomainNetRemoveHostdev(def, net); VIR_DELETE_ELEMENT(def->nets, i, def->nnets); return net; } int virDomainControllerInsert(virDomainDefPtr def, virDomainControllerDefPtr controller) { if (VIR_REALLOC_N(def->controllers, def->ncontrollers+1) < 0) return -1; virDomainControllerInsertPreAlloced(def, controller); return 0; } void virDomainControllerInsertPreAlloced(virDomainDefPtr def, virDomainControllerDefPtr controller) { int idx; /* Tenatively plan to insert controller at the end. */ int insertAt = -1; /* Then work backwards looking for controllers of * the same type. If we find a controller with a * index greater than the new one, insert at * that position */ for (idx = (def->ncontrollers - 1); idx >= 0; idx--) { /* If bus matches and current controller is after * new controller, then new controller should go here */ if (def->controllers[idx]->type == controller->type && def->controllers[idx]->idx > controller->idx) { insertAt = idx; } else if (def->controllers[idx]->type == controller->type && insertAt == -1) { /* Last controller with match bus is before the * new controller, then put new controller just after */ insertAt = idx + 1; } } /* VIR_INSERT_ELEMENT_INPLACE will never return an error here. */ ignore_value(VIR_INSERT_ELEMENT_INPLACE(def->controllers, insertAt, def->ncontrollers, controller)); } int virDomainControllerFind(virDomainDefPtr def, int type, int idx) { size_t i; for (i = 0; i < def->ncontrollers; i++) { if ((def->controllers[i]->type == type) && (def->controllers[i]->idx == idx)) { return i; } } return -1; } int virDomainControllerFindByType(virDomainDefPtr def, int type) { size_t i; for (i = 0; i < def->ncontrollers; i++) { if (def->controllers[i]->type == type) return i; } return -1; } int virDomainControllerFindByPCIAddress(virDomainDefPtr def, virDevicePCIAddressPtr addr) { size_t i; for (i = 0; i < def->ncontrollers; i++) { virDomainDeviceInfoPtr info = &def->controllers[i]->info; if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && virDevicePCIAddressEqual(&info->addr.pci, addr)) return i; } return -1; } virDomainControllerDefPtr virDomainControllerRemove(virDomainDefPtr def, size_t i) { virDomainControllerDefPtr controller = def->controllers[i]; VIR_DELETE_ELEMENT(def->controllers, i, def->ncontrollers); return controller; } int virDomainLeaseIndex(virDomainDefPtr def, virDomainLeaseDefPtr lease) { virDomainLeaseDefPtr vlease; size_t i; for (i = 0; i < def->nleases; i++) { vlease = def->leases[i]; /* Either both must have lockspaces present which match.. */ if (vlease->lockspace && lease->lockspace) { if (STRNEQ(vlease->lockspace, lease->lockspace)) continue; /* ...or neither must have a lockspace present */ } else if (vlease->lockspace || lease->lockspace) { continue; } if (STREQ(vlease->key, lease->key)) return i; } return -1; } int virDomainLeaseInsertPreAlloc(virDomainDefPtr def) { return VIR_EXPAND_N(def->leases, def->nleases, 1); } int virDomainLeaseInsert(virDomainDefPtr def, virDomainLeaseDefPtr lease) { if (virDomainLeaseInsertPreAlloc(def) < 0) return -1; virDomainLeaseInsertPreAlloced(def, lease); return 0; } void virDomainLeaseInsertPreAlloced(virDomainDefPtr def, virDomainLeaseDefPtr lease) { if (lease == NULL) VIR_SHRINK_N(def->leases, def->nleases, 1); else def->leases[def->nleases-1] = lease; } virDomainLeaseDefPtr virDomainLeaseRemoveAt(virDomainDefPtr def, size_t i) { virDomainLeaseDefPtr lease = def->leases[i]; VIR_DELETE_ELEMENT(def->leases, i, def->nleases); return lease; } virDomainLeaseDefPtr virDomainLeaseRemove(virDomainDefPtr def, virDomainLeaseDefPtr lease) { int idx = virDomainLeaseIndex(def, lease); if (idx < 0) return NULL; return virDomainLeaseRemoveAt(def, idx); } bool virDomainChrEquals(virDomainChrDefPtr src, virDomainChrDefPtr tgt) { if (!src || !tgt) return src == tgt; if (src->deviceType != tgt->deviceType || !virDomainChrSourceDefIsEqual(&src->source, &tgt->source)) return false; switch ((virDomainChrDeviceType) src->deviceType) { case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: if (src->targetType != tgt->targetType) return false; switch ((virDomainChrChannelTargetType) src->targetType) { case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: return STREQ_NULLABLE(src->target.name, tgt->target.name); break; case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: if (!src->target.addr || !tgt->target.addr) return src->target.addr == tgt->target.addr; return memcmp(src->target.addr, tgt->target.addr, sizeof(*src->target.addr)) == 0; break; case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_NONE: case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_LAST: /* shouldn't happen */ break; } break; case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL: if (src->targetTypeAttr != tgt->targetTypeAttr) return false; case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL: return src->target.port == tgt->target.port; break; case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST: /* shouldn't happen */ break; } return false; } virDomainChrDefPtr virDomainChrFind(virDomainDefPtr def, virDomainChrDefPtr target) { virDomainChrDefPtr chr; const virDomainChrDef **arrPtr; size_t i, cnt; virDomainChrGetDomainPtrs(def, target->deviceType, &arrPtr, &cnt); for (i = 0; i < cnt; i++) { /* Cast away const */ chr = (virDomainChrDefPtr) arrPtr[i]; if (virDomainChrEquals(chr, target)) return chr; } return NULL; } /* Return the address within vmdef to be modified when working with a * chrdefptr of the given type. */ static void virDomainChrGetDomainPtrsInternal(virDomainDefPtr vmdef, virDomainChrDeviceType type, virDomainChrDefPtr ***arrPtr, size_t **cntPtr) { switch (type) { case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL: *arrPtr = &vmdef->parallels; *cntPtr = &vmdef->nparallels; break; case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL: *arrPtr = &vmdef->serials; *cntPtr = &vmdef->nserials; break; case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: *arrPtr = &vmdef->consoles; *cntPtr = &vmdef->nconsoles; break; case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: *arrPtr = &vmdef->channels; *cntPtr = &vmdef->nchannels; break; case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST: *arrPtr = NULL; *cntPtr = NULL; break; } } /* Return the array within vmdef that can contain a chrdefptr of the * given type. */ void virDomainChrGetDomainPtrs(const virDomainDef *vmdef, virDomainChrDeviceType type, const virDomainChrDef ***arrPtr, size_t *cntPtr) { virDomainChrDef ***arrVar = NULL; size_t *cntVar = NULL; /* Cast away const; we add it back in the final assignment. */ virDomainChrGetDomainPtrsInternal((virDomainDefPtr) vmdef, type, &arrVar, &cntVar); if (arrVar) { *arrPtr = (const virDomainChrDef **) *arrVar; *cntPtr = *cntVar; } else { *arrPtr = NULL; *cntPtr = 0; } } int virDomainChrPreAlloc(virDomainDefPtr vmdef, virDomainChrDefPtr chr) { virDomainChrDefPtr **arrPtr = NULL; size_t *cntPtr = NULL; virDomainChrGetDomainPtrsInternal(vmdef, chr->deviceType, &arrPtr, &cntPtr); return VIR_REALLOC_N(*arrPtr, *cntPtr + 1); } void virDomainChrInsertPreAlloced(virDomainDefPtr vmdef, virDomainChrDefPtr chr) { virDomainChrDefPtr **arrPtr = NULL; size_t *cntPtr = NULL; virDomainChrGetDomainPtrsInternal(vmdef, chr->deviceType, &arrPtr, &cntPtr); ignore_value(VIR_APPEND_ELEMENT_INPLACE(*arrPtr, *cntPtr, chr)); } virDomainChrDefPtr virDomainChrRemove(virDomainDefPtr vmdef, virDomainChrDefPtr chr) { virDomainChrDefPtr ret, **arrPtr = NULL; size_t i, *cntPtr = NULL; virDomainChrGetDomainPtrsInternal(vmdef, chr->deviceType, &arrPtr, &cntPtr); for (i = 0; i < *cntPtr; i++) { ret = (*arrPtr)[i]; if (virDomainChrEquals(ret, chr)) break; } if (i == *cntPtr) return NULL; VIR_DELETE_ELEMENT(*arrPtr, i, *cntPtr); return ret; } int virDomainRNGInsert(virDomainDefPtr def, virDomainRNGDefPtr rng, bool inplace) { if (inplace) return VIR_APPEND_ELEMENT_INPLACE(def->rngs, def->nrngs, rng); else return VIR_APPEND_ELEMENT(def->rngs, def->nrngs, rng); } ssize_t virDomainRNGFind(virDomainDefPtr def, virDomainRNGDefPtr rng) { size_t i; for (i = 0; i < def->nrngs; i++) { virDomainRNGDefPtr tmp = def->rngs[i]; if (rng->model != tmp->model || rng->backend != tmp->backend) continue; if (rng->rate != tmp->rate || rng->period != tmp->period) continue; switch ((virDomainRNGBackend) rng->backend) { case VIR_DOMAIN_RNG_BACKEND_RANDOM: if (STRNEQ_NULLABLE(rng->source.file, tmp->source.file)) continue; break; case VIR_DOMAIN_RNG_BACKEND_EGD: if (!virDomainChrSourceDefIsEqual(rng->source.chardev, tmp->source.chardev)) continue; break; case VIR_DOMAIN_RNG_BACKEND_LAST: break; } if (rng->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && !virDomainDeviceInfoAddressIsEqual(&rng->info, &tmp->info)) continue; break; } if (i < def->nrngs) return i; return -1; } virDomainRNGDefPtr virDomainRNGRemove(virDomainDefPtr def, size_t idx) { virDomainRNGDefPtr ret = def->rngs[idx]; VIR_DELETE_ELEMENT(def->rngs, idx, def->nrngs); return ret; } static int virDomainMemoryFindByDefInternal(virDomainDefPtr def, virDomainMemoryDefPtr mem, bool allowAddressFallback) { size_t i; for (i = 0; i < def->nmems; i++) { virDomainMemoryDefPtr tmp = def->mems[i]; /* address, if present */ if (allowAddressFallback) { if (tmp->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) continue; } else { if (mem->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && !virDomainDeviceInfoAddressIsEqual(&tmp->info, &mem->info)) continue; } /* alias, if present */ if (mem->info.alias && STRNEQ_NULLABLE(tmp->info.alias, mem->info.alias)) continue; /* target info -> always present */ if (tmp->model != mem->model || tmp->targetNode != mem->targetNode || tmp->size != mem->size) continue; /* source stuff -> match with device */ if (tmp->pagesize != mem->pagesize) continue; if (!virBitmapEqual(tmp->sourceNodes, mem->sourceNodes)) continue; break; } if (i == def->nmems) return -1; return i; } int virDomainMemoryFindByDef(virDomainDefPtr def, virDomainMemoryDefPtr mem) { return virDomainMemoryFindByDefInternal(def, mem, false); } int virDomainMemoryFindInactiveByDef(virDomainDefPtr def, virDomainMemoryDefPtr mem) { int ret; if ((ret = virDomainMemoryFindByDefInternal(def, mem, false)) < 0) ret = virDomainMemoryFindByDefInternal(def, mem, true); return ret; } int virDomainMemoryInsert(virDomainDefPtr def, virDomainMemoryDefPtr mem) { int id = def->nmems; if (mem->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && virDomainDefHasDeviceAddress(def, &mem->info)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("Domain already contains a device with the same " "address")); return -1; } if (VIR_APPEND_ELEMENT(def->mems, def->nmems, mem) < 0) return -1; return id; } virDomainMemoryDefPtr virDomainMemoryRemove(virDomainDefPtr def, int idx) { virDomainMemoryDefPtr ret = def->mems[idx]; VIR_DELETE_ELEMENT(def->mems, idx, def->nmems); return ret; } char * virDomainDefGetDefaultEmulator(virDomainDefPtr def, virCapsPtr caps) { const char *type; const char *emulator; char *retemu; type = virDomainVirtTypeToString(def->virtType); if (!type) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unknown virt type")); return NULL; } emulator = virCapabilitiesDefaultGuestEmulator(caps, def->os.type, def->os.arch, type); if (!emulator) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no emulator for domain %s os type %s " "on architecture %s"), type, def->os.type, virArchToString(def->os.arch)); return NULL; } ignore_value(VIR_STRDUP(retemu, emulator)); return retemu; } static int virDomainDefParseBootXML(xmlXPathContextPtr ctxt, virDomainDefPtr def) { xmlNodePtr *nodes = NULL; size_t i; int n; char *tmp = NULL; int ret = -1; unsigned long deviceBoot, serialPorts; if (virXPathULong("count(./devices/disk[boot]" "|./devices/interface[boot]" "|./devices/hostdev[boot]" "|./devices/redirdev[boot])", ctxt, &deviceBoot) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot count boot devices")); goto cleanup; } /* analysis of the boot devices */ if ((n = virXPathNodeSet("./os/boot", ctxt, &nodes)) < 0) goto cleanup; if (n > 0 && deviceBoot) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("per-device boot elements cannot be used" " together with os/boot elements")); goto cleanup; } for (i = 0; i < n && i < VIR_DOMAIN_BOOT_LAST; i++) { int val; char *dev = virXMLPropString(nodes[i], "dev"); if (!dev) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing boot device")); goto cleanup; } if ((val = virDomainBootTypeFromString(dev)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown boot device '%s'"), dev); VIR_FREE(dev); goto cleanup; } VIR_FREE(dev); def->os.bootDevs[def->os.nBootDevs++] = val; } if (def->os.nBootDevs == 0 && !deviceBoot) { def->os.nBootDevs = 1; def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK; } tmp = virXPathString("string(./os/bootmenu[1]/@enable)", ctxt); if (tmp) { def->os.bootmenu = virTristateBoolTypeFromString(tmp); if (def->os.bootmenu <= 0) { /* In order not to break misconfigured machines, this * should not emit an error, but rather set the bootmenu * to disabled */ VIR_WARN("disabling bootmenu due to unknown option '%s'", tmp); def->os.bootmenu = VIR_TRISTATE_BOOL_NO; } VIR_FREE(tmp); } tmp = virXPathString("string(./os/bootmenu[1]/@timeout)", ctxt); if (tmp && def->os.bootmenu == VIR_TRISTATE_BOOL_YES) { if (virStrToLong_uip(tmp, NULL, 0, &def->os.bm_timeout) < 0 || def->os.bm_timeout > 65535) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("invalid value for boot menu timeout, " "must be in range [0,65535]")); goto cleanup; } def->os.bm_timeout_set = true; } VIR_FREE(tmp); tmp = virXPathString("string(./os/bios[1]/@useserial)", ctxt); if (tmp) { if (STREQ(tmp, "yes")) { if (virXPathULong("count(./devices/serial)", ctxt, &serialPorts) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("need at least one serial port " "for useserial")); goto cleanup; } def->os.bios.useserial = VIR_TRISTATE_BOOL_YES; } else { def->os.bios.useserial = VIR_TRISTATE_BOOL_NO; } VIR_FREE(tmp); } tmp = virXPathString("string(./os/bios[1]/@rebootTimeout)", ctxt); if (tmp) { /* that was really just for the check if it is there */ if (virStrToLong_i(tmp, NULL, 0, &def->os.bios.rt_delay) < 0 || def->os.bios.rt_delay < -1 || def->os.bios.rt_delay > 65535) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("invalid value for rebootTimeout, " "must be in range [-1,65535]")); goto cleanup; } def->os.bios.rt_set = true; } ret = 0; cleanup: VIR_FREE(tmp); VIR_FREE(nodes); return ret; } static int virDomainIdMapEntrySort(const void *a, const void *b) { const virDomainIdMapEntry *entrya = a; const virDomainIdMapEntry *entryb = b; if (entrya->start > entryb->start) return 1; else if (entrya->start < entryb->start) return -1; else return 0; } /* Parse the XML definition for user namespace id map. * * idmap has the form of * * * */ static virDomainIdMapEntryPtr virDomainIdmapDefParseXML(xmlXPathContextPtr ctxt, xmlNodePtr *node, size_t num) { size_t i; virDomainIdMapEntryPtr idmap = NULL; xmlNodePtr save_ctxt = ctxt->node; if (VIR_ALLOC_N(idmap, num) < 0) goto cleanup; for (i = 0; i < num; i++) { ctxt->node = node[i]; if (virXPathUInt("string(./@start)", ctxt, &idmap[i].start) < 0 || virXPathUInt("string(./@target)", ctxt, &idmap[i].target) < 0 || virXPathUInt("string(./@count)", ctxt, &idmap[i].count) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid idmap start/target/count settings")); VIR_FREE(idmap); goto cleanup; } } qsort(idmap, num, sizeof(idmap[0]), virDomainIdMapEntrySort); if (idmap[0].start != 0) { /* Root user of container hasn't been mapped to any user of host, * return error. */ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("You must map the root user of container")); VIR_FREE(idmap); goto cleanup; } cleanup: ctxt->node = save_ctxt; return idmap; } /* Parse the XML definition for a vcpupin * * vcpupin has the form of * */ static virDomainPinDefPtr virDomainVcpuPinDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt) { virDomainPinDefPtr def; xmlNodePtr oldnode = ctxt->node; unsigned int vcpuid; char *tmp = NULL; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = node; if (!(tmp = virXPathString("string(./@vcpu)", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing vcpu id in vcpupin")); goto error; } if (virStrToLong_uip(tmp, NULL, 10, &vcpuid) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid setting for vcpu '%s'"), tmp); goto error; } VIR_FREE(tmp); def->id = vcpuid; if (!(tmp = virXMLPropString(node, "cpuset"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing cpuset for vcpupin")); goto error; } if (virBitmapParse(tmp, 0, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0) goto error; cleanup: VIR_FREE(tmp); ctxt->node = oldnode; return def; error: VIR_FREE(def); goto cleanup; } /* Parse the XML definition for a iothreadpin * and an iothreadspin has the form * */ static virDomainPinDefPtr virDomainIOThreadPinDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, int iothreads) { virDomainPinDefPtr def; xmlNodePtr oldnode = ctxt->node; unsigned int iothreadid; char *tmp = NULL; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = node; if (!(tmp = virXPathString("string(./@iothread)", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing iothread id in iothreadpin")); goto error; } if (virStrToLong_uip(tmp, NULL, 10, &iothreadid) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid setting for iothread '%s'"), tmp); goto error; } VIR_FREE(tmp); if (iothreadid == 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("zero is an invalid iothread id value")); goto error; } /* IOThreads are numbered "iothread1...iothread", where * "n" is the iothreads value */ if (iothreadid > iothreads) { virReportError(VIR_ERR_XML_ERROR, "%s", _("iothread id must not exceed iothreads")); goto error; } def->id = iothreadid; if (!(tmp = virXMLPropString(node, "cpuset"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing cpuset for iothreadpin")); goto error; } if (virBitmapParse(tmp, 0, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0) goto error; if (virBitmapIsAllClear(def->cpumask)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid value of 'cpuset': %s"), tmp); goto error; } cleanup: VIR_FREE(tmp); ctxt->node = oldnode; return def; error: VIR_FREE(def); goto cleanup; } /* Parse the XML definition for emulatorpin. * emulatorpin has the form of * */ static virDomainPinDefPtr virDomainEmulatorPinDefParseXML(xmlNodePtr node) { virDomainPinDefPtr def; char *tmp = NULL; if (VIR_ALLOC(def) < 0) return NULL; if (!(tmp = virXMLPropString(node, "cpuset"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing cpuset for emulatorpin")); goto error; } if (virBitmapParse(tmp, 0, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0) goto error; VIR_FREE(tmp); return def; error: VIR_FREE(tmp); VIR_FREE(def); return NULL; } int virDomainDefMaybeAddController(virDomainDefPtr def, int type, int idx, int model) { size_t i; virDomainControllerDefPtr cont; for (i = 0; i < def->ncontrollers; i++) { if (def->controllers[i]->type == type && def->controllers[i]->idx == idx) return 0; } if (VIR_ALLOC(cont) < 0) return -1; cont->type = type; cont->idx = idx; cont->model = model; if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL) { cont->opts.vioserial.ports = -1; cont->opts.vioserial.vectors = -1; } if (VIR_APPEND_ELEMENT(def->controllers, def->ncontrollers, cont) < 0) { VIR_FREE(cont); return -1; } return 1; } int virDomainDefMaybeAddInput(virDomainDefPtr def, int type, int bus) { size_t i; virDomainInputDefPtr input; for (i = 0; i < def->ninputs; i++) { if (def->inputs[i]->type == type && def->inputs[i]->bus == bus) return 0; } if (VIR_ALLOC(input) < 0) return -1; input->type = type; input->bus = bus; if (VIR_APPEND_ELEMENT(def->inputs, def->ninputs, input) < 0) { VIR_FREE(input); return -1; } return 0; } static int virDomainHugepagesParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virDomainHugePagePtr hugepage) { int ret = -1; xmlNodePtr oldnode = ctxt->node; char *unit = NULL, *nodeset = NULL; ctxt->node = node; if (virDomainParseMemory("./@size", "./@unit", ctxt, &hugepage->size, true, false) < 0) goto cleanup; if (!hugepage->size) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("hugepage size can't be zero")); goto cleanup; } if ((nodeset = virXMLPropString(node, "nodeset"))) { if (virBitmapParse(nodeset, 0, &hugepage->nodemask, VIR_DOMAIN_CPUMASK_LEN) < 0) goto cleanup; if (virBitmapIsAllClear(hugepage->nodemask)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid value of 'nodeset': %s"), nodeset); goto cleanup; } } ret = 0; cleanup: VIR_FREE(unit); VIR_FREE(nodeset); ctxt->node = oldnode; return ret; } static virDomainResourceDefPtr virDomainResourceDefParse(xmlNodePtr node, xmlXPathContextPtr ctxt) { virDomainResourceDefPtr def = NULL; xmlNodePtr tmp = ctxt->node; ctxt->node = node; if (VIR_ALLOC(def) < 0) goto error; /* Find out what type of virtualization to use */ if (!(def->partition = virXPathString("string(./partition)", ctxt))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing resource partition attribute")); goto error; } ctxt->node = tmp; return def; error: ctxt->node = tmp; virDomainResourceDefFree(def); return NULL; } static int virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def) { /* Look for any hostdev scsi dev */ size_t i; int maxController = -1; virDomainHostdevDefPtr hostdev; for (i = 0; i < def->nhostdevs; i++) { hostdev = def->hostdevs[i]; if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI && (int)hostdev->info->addr.drive.controller > maxController) { maxController = hostdev->info->addr.drive.controller; } } if (maxController == -1) return 0; for (i = 0; i <= maxController; i++) { if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI, i, -1) < 0) return -1; } return 0; } static int virDomainLoaderDefParseXML(xmlNodePtr node, virDomainLoaderDefPtr loader) { int ret = -1; char *readonly_str = NULL; char *type_str = NULL; readonly_str = virXMLPropString(node, "readonly"); type_str = virXMLPropString(node, "type"); loader->path = (char *) xmlNodeGetContent(node); if (readonly_str && (loader->readonly = virTristateBoolTypeFromString(readonly_str)) <= 0) { virReportError(VIR_ERR_XML_DETAIL, _("unknown readonly value: %s"), readonly_str); goto cleanup; } if (type_str) { int type; if ((type = virDomainLoaderTypeFromString(type_str)) < 0) { virReportError(VIR_ERR_XML_DETAIL, _("unknown type value: %s"), type_str); goto cleanup; } loader->type = type; } ret = 0; cleanup: VIR_FREE(readonly_str); VIR_FREE(type_str); return ret; } static int virDomainThreadSchedParse(xmlNodePtr node, unsigned int minid, unsigned int maxid, const char *name, virDomainThreadSchedParamPtr sp) { char *tmp = NULL; int pol = 0; tmp = virXMLPropString(node, name); if (!tmp) { virReportError(VIR_ERR_XML_ERROR, _("Missing attribute '%s' in element '%sched'"), name, name); goto error; } if (virBitmapParse(tmp, 0, &sp->ids, VIR_DOMAIN_CPUMASK_LEN) < 0) goto error; if (virBitmapIsAllClear(sp->ids) || virBitmapNextSetBit(sp->ids, -1) < minid || virBitmapLastSetBit(sp->ids) > maxid) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid value of '%s': %s"), name, tmp); goto error; } VIR_FREE(tmp); tmp = virXMLPropString(node, "scheduler"); if (tmp) { if ((pol = virProcessSchedPolicyTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid scheduler attribute: '%s'"), tmp); goto error; } sp->policy = pol; VIR_FREE(tmp); if (sp->policy == VIR_PROC_POLICY_FIFO || sp->policy == VIR_PROC_POLICY_RR) { tmp = virXMLPropString(node, "priority"); if (!tmp) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing scheduler priority")); goto error; } if (virStrToLong_i(tmp, NULL, 10, &sp->priority) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid value for element priority")); goto error; } VIR_FREE(tmp); } } return 0; error: VIR_FREE(tmp); return -1; } static virDomainDefPtr virDomainDefParseXML(xmlDocPtr xml, xmlNodePtr root, xmlXPathContextPtr ctxt, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, unsigned int expectedVirtTypes, unsigned int flags) { xmlNodePtr *nodes = NULL, node = NULL; char *tmp = NULL; size_t i, j; int n; long id = -1; virDomainDefPtr def; unsigned long count; bool uuid_generated = false; virHashTablePtr bootHash = NULL; bool usb_none = false; bool usb_other = false; bool usb_master = false; bool primaryVideo = false; if (flags & VIR_DOMAIN_DEF_PARSE_VALIDATE) { char *schema = virFileFindResource("domain.rng", abs_topsrcdir "/docs/schemas", PKGDATADIR "/schemas"); if (!schema) return NULL; if (virXMLValidateAgainstSchema(schema, xml) < 0) { VIR_FREE(schema); return NULL; } VIR_FREE(schema); } if (!(def = virDomainDefNew())) return NULL; if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) if (virXPathLong("string(./@id)", ctxt, &id) < 0) id = -1; def->id = (int)id; /* Find out what type of virtualization to use */ if (!(tmp = virXPathString("string(./@type)", ctxt))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing domain type attribute")); goto error; } if ((def->virtType = virDomainVirtTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid domain type %s"), tmp); goto error; } VIR_FREE(tmp); if ((expectedVirtTypes & (1 << def->virtType)) == 0) { if (count_one_bits(expectedVirtTypes) == 1) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected domain type %s, expecting %s"), virDomainVirtTypeToString(def->virtType), virDomainVirtTypeToString(ffs(expectedVirtTypes) - 1)); } else { virBuffer buffer = VIR_BUFFER_INITIALIZER; char *string; for (i = 0; i < VIR_DOMAIN_VIRT_LAST; ++i) { if ((expectedVirtTypes & (1 << i)) != 0) { if (virBufferUse(&buffer) > 0) virBufferAddLit(&buffer, ", "); virBufferAdd(&buffer, virDomainVirtTypeToString(i), -1); } } if (virBufferCheckError(&buffer) < 0) goto error; string = virBufferContentAndReset(&buffer); virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected domain type %s, " "expecting one of these: %s"), virDomainVirtTypeToString(def->virtType), string); VIR_FREE(string); } goto error; } /* Extract domain name */ if (!(def->name = virXPathString("string(./name[1])", ctxt))) { virReportError(VIR_ERR_NO_NAME, NULL); goto error; } /* Extract domain uuid. If both uuid and sysinfo/system/entry/uuid * exist, they must match; and if only the latter exists, it can * also serve as the uuid. */ tmp = virXPathString("string(./uuid[1])", ctxt); if (!tmp) { if (virUUIDGenerate(def->uuid)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to generate UUID")); goto error; } uuid_generated = true; } else { if (virUUIDParse(tmp, def->uuid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed uuid element")); goto error; } VIR_FREE(tmp); } /* Extract short description of domain (title) */ def->title = virXPathString("string(./title[1])", ctxt); if (def->title && strchr(def->title, '\n')) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Domain title can't contain newlines")); goto error; } /* Extract documentation if present */ def->description = virXPathString("string(./description[1])", ctxt); /* analysis of security label, done early even though we format it * late, so devices can refer to this for defaults */ if (virSecurityLabelDefsParseXML(def, ctxt, caps, flags) == -1) goto error; /* Extract domain memory */ if (virDomainParseMemory("./memory[1]", NULL, ctxt, &def->mem.max_balloon, false, true) < 0) goto error; if (virDomainParseMemory("./currentMemory[1]", NULL, ctxt, &def->mem.cur_balloon, false, true) < 0) goto error; if (virDomainParseMemory("./maxMemory[1]", NULL, ctxt, &def->mem.max_memory, false, false) < 0) goto error; if (virXPathUInt("string(./maxMemory[1]/@slots)", ctxt, &def->mem.memory_slots) == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Failed to parse memory slot count")); goto error; } /* and info about it */ if ((tmp = virXPathString("string(./memory[1]/@dumpCore)", ctxt)) && (def->mem.dump_core = virTristateSwitchTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid memory core dump attribute value '%s'"), tmp); goto error; } VIR_FREE(tmp); if ((n = virXPathNodeSet("./memoryBacking/hugepages/page", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract hugepages nodes")); goto error; } if (n) { if (VIR_ALLOC_N(def->mem.hugepages, n) < 0) goto error; for (i = 0; i < n; i++) { if (virDomainHugepagesParseXML(nodes[i], ctxt, &def->mem.hugepages[i]) < 0) goto error; def->mem.nhugepages++; for (j = 0; j < i; j++) { if (def->mem.hugepages[i].nodemask && def->mem.hugepages[j].nodemask && virBitmapOverlaps(def->mem.hugepages[i].nodemask, def->mem.hugepages[j].nodemask)) { virReportError(VIR_ERR_XML_DETAIL, _("nodeset attribute of hugepages " "of sizes %llu and %llu intersect"), def->mem.hugepages[i].size, def->mem.hugepages[j].size); goto error; } else if (!def->mem.hugepages[i].nodemask && !def->mem.hugepages[j].nodemask) { virReportError(VIR_ERR_XML_DETAIL, _("two master hugepages detected: " "%llu and %llu"), def->mem.hugepages[i].size, def->mem.hugepages[j].size); goto error; } } } VIR_FREE(nodes); } else { if ((node = virXPathNode("./memoryBacking/hugepages", ctxt))) { if (VIR_ALLOC(def->mem.hugepages) < 0) goto error; def->mem.nhugepages = 1; } } if ((node = virXPathNode("./memoryBacking/nosharepages", ctxt))) def->mem.nosharepages = true; if (virXPathBoolean("boolean(./memoryBacking/locked)", ctxt)) def->mem.locked = true; /* Extract blkio cgroup tunables */ if (virXPathUInt("string(./blkiotune/weight)", ctxt, &def->blkio.weight) < 0) def->blkio.weight = 0; if ((n = virXPathNodeSet("./blkiotune/device", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract blkiotune nodes")); goto error; } if (n && VIR_ALLOC_N(def->blkio.devices, n) < 0) goto error; for (i = 0; i < n; i++) { if (virDomainBlkioDeviceParseXML(nodes[i], &def->blkio.devices[i]) < 0) goto error; def->blkio.ndevices++; for (j = 0; j < i; j++) { if (STREQ(def->blkio.devices[j].path, def->blkio.devices[i].path)) { virReportError(VIR_ERR_XML_ERROR, _("duplicate blkio device path '%s'"), def->blkio.devices[i].path); goto error; } } } VIR_FREE(nodes); /* Extract other memory tunables */ if (virDomainParseMemoryLimit("./memtune/hard_limit[1]", NULL, ctxt, &def->mem.hard_limit) < 0) goto error; if (virDomainParseMemoryLimit("./memtune/soft_limit[1]", NULL, ctxt, &def->mem.soft_limit) < 0) goto error; if (virDomainParseMemory("./memtune/min_guarantee[1]", NULL, ctxt, &def->mem.min_guarantee, false, false) < 0) goto error; if (virDomainParseMemoryLimit("./memtune/swap_hard_limit[1]", NULL, ctxt, &def->mem.swap_hard_limit) < 0) goto error; n = virXPathULong("string(./vcpu[1])", ctxt, &count); if (n == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("maximum vcpus must be an integer")); goto error; } else if (n < 0) { def->maxvcpus = 1; } else { def->maxvcpus = count; if (count == 0 || (unsigned short) count != count) { virReportError(VIR_ERR_XML_ERROR, _("invalid maximum number of vCPUs '%lu'"), count); goto error; } } n = virXPathULong("string(./vcpu[1]/@current)", ctxt, &count); if (n == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("current vcpus must be an integer")); goto error; } else if (n < 0) { def->vcpus = def->maxvcpus; } else { def->vcpus = count; if (count == 0 || (unsigned short) count != count) { virReportError(VIR_ERR_XML_ERROR, _("invalid current number of vCPUs '%lu'"), count); goto error; } if (def->maxvcpus < count) { virReportError(VIR_ERR_INTERNAL_ERROR, _("maxvcpus must not be less than current vcpus " "(%d < %lu)"), def->maxvcpus, count); goto error; } } tmp = virXPathString("string(./vcpu[1]/@placement)", ctxt); if (tmp) { if ((def->placement_mode = virDomainCpuPlacementModeTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported CPU placement mode '%s'"), tmp); goto error; } VIR_FREE(tmp); } else { def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC; } if (def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) { tmp = virXPathString("string(./vcpu[1]/@cpuset)", ctxt); if (tmp) { if (virBitmapParse(tmp, 0, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0) goto error; if (virBitmapIsAllClear(def->cpumask)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid value of 'cpuset': %s"), tmp); goto error; } VIR_FREE(tmp); } } /* Optional - iothreads */ tmp = virXPathString("string(./iothreads[1])", ctxt); if (tmp && virStrToLong_uip(tmp, NULL, 10, &def->iothreads) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid iothreads count '%s'"), tmp); goto error; } VIR_FREE(tmp); /* Extract cpu tunables. */ if ((n = virXPathULong("string(./cputune/shares[1])", ctxt, &def->cputune.shares)) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("can't parse cputune shares value")); goto error; } else if (n == 0) { def->cputune.sharesSpecified = true; } if (virXPathULongLong("string(./cputune/period[1])", ctxt, &def->cputune.period) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("can't parse cputune period value")); goto error; } if (def->cputune.period > 0 && (def->cputune.period < 1000 || def->cputune.period > 1000000)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Value of cputune period must be in range " "[1000, 1000000]")); goto error; } if (virXPathLongLong("string(./cputune/quota[1])", ctxt, &def->cputune.quota) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("can't parse cputune quota value")); goto error; } if (def->cputune.quota > 0 && (def->cputune.quota < 1000 || def->cputune.quota > 18446744073709551LL)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Value of cputune quota must be in range " "[1000, 18446744073709551]")); goto error; } if (virXPathULongLong("string(./cputune/emulator_period[1])", ctxt, &def->cputune.emulator_period) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("can't parse cputune emulator period value")); goto error; } if (def->cputune.emulator_period > 0 && (def->cputune.emulator_period < 1000 || def->cputune.emulator_period > 1000000)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Value of cputune emulator_period must be in range " "[1000, 1000000]")); goto error; } if (virXPathLongLong("string(./cputune/emulator_quota[1])", ctxt, &def->cputune.emulator_quota) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("can't parse cputune emulator quota value")); goto error; } if (def->cputune.emulator_quota > 0 && (def->cputune.emulator_quota < 1000 || def->cputune.emulator_quota > 18446744073709551LL)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Value of cputune emulator_quota must be in range " "[1000, 18446744073709551]")); goto error; } if ((n = virXPathNodeSet("./cputune/vcpupin", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->cputune.vcpupin, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainPinDefPtr vcpupin; if (!(vcpupin = virDomainVcpuPinDefParseXML(nodes[i], ctxt))) goto error; if (virDomainPinIsDuplicate(def->cputune.vcpupin, def->cputune.nvcpupin, vcpupin->id)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("duplicate vcpupin for same vcpu")); virDomainPinDefFree(vcpupin); goto error; } if (vcpupin->id >= def->vcpus) { /* To avoid the regression when daemon loading * domain confs, we can't simply error out if * nodes greater than current vcpus, * ignoring them instead. */ VIR_WARN("Ignore vcpupin for missing vcpus"); virDomainPinDefFree(vcpupin); } else { def->cputune.vcpupin[def->cputune.nvcpupin++] = vcpupin; } } VIR_FREE(nodes); /* Initialize the pinning policy for vcpus which doesn't has * the policy specified explicitly as def->cpuset. */ if (def->cpumask) { if (VIR_REALLOC_N(def->cputune.vcpupin, def->vcpus) < 0) goto error; for (i = 0; i < def->vcpus; i++) { if (virDomainPinIsDuplicate(def->cputune.vcpupin, def->cputune.nvcpupin, i)) continue; virDomainPinDefPtr vcpupin = NULL; if (VIR_ALLOC(vcpupin) < 0) goto error; if (!(vcpupin->cpumask = virBitmapNew(VIR_DOMAIN_CPUMASK_LEN))) { VIR_FREE(vcpupin); goto error; } virBitmapCopy(vcpupin->cpumask, def->cpumask); vcpupin->id = i; def->cputune.vcpupin[def->cputune.nvcpupin++] = vcpupin; } } if ((n = virXPathNodeSet("./cputune/emulatorpin", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract emulatorpin nodes")); goto error; } if (n) { if (n > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only one emulatorpin is supported")); VIR_FREE(nodes); goto error; } if (!(def->cputune.emulatorpin = virDomainEmulatorPinDefParseXML(nodes[0]))) goto error; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./cputune/iothreadpin", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract iothreadpin nodes")); goto error; } if (n && VIR_ALLOC_N(def->cputune.iothreadspin, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainPinDefPtr iothreadpin = NULL; iothreadpin = virDomainIOThreadPinDefParseXML(nodes[i], ctxt, def->iothreads); if (!iothreadpin) goto error; if (virDomainPinIsDuplicate(def->cputune.iothreadspin, def->cputune.niothreadspin, iothreadpin->id)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("duplicate iothreadpin for same iothread")); virDomainPinDefFree(iothreadpin); goto error; } def->cputune.iothreadspin[def->cputune.niothreadspin++] = iothreadpin; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./cputune/vcpusched", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract vcpusched nodes")); goto error; } if (n) { if (n > def->maxvcpus) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("too many vcpusched nodes in cputune")); goto error; } if (VIR_ALLOC_N(def->cputune.vcpusched, n) < 0) goto error; def->cputune.nvcpusched = n; for (i = 0; i < def->cputune.nvcpusched; i++) { if (virDomainThreadSchedParse(nodes[i], 0, def->maxvcpus - 1, "vcpus", &def->cputune.vcpusched[i]) < 0) goto error; for (j = 0; j < i; j++) { if (virBitmapOverlaps(def->cputune.vcpusched[i].ids, def->cputune.vcpusched[j].ids)) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("vcpusched attributes 'vcpus' " "must not overlap")); goto error; } } } } VIR_FREE(nodes); if ((n = virXPathNodeSet("./cputune/iothreadsched", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract iothreadsched nodes")); goto error; } if (n) { if (n > def->iothreads) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("too many iothreadsched nodes in cputune")); goto error; } if (VIR_ALLOC_N(def->cputune.iothreadsched, n) < 0) goto error; def->cputune.niothreadsched = n; for (i = 0; i < def->cputune.niothreadsched; i++) { if (virDomainThreadSchedParse(nodes[i], 1, def->iothreads, "iothreads", &def->cputune.iothreadsched[i]) < 0) goto error; for (j = 0; j < i; j++) { if (virBitmapOverlaps(def->cputune.iothreadsched[i].ids, def->cputune.iothreadsched[j].ids)) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("iothreadsched attributes 'iothreads' " "must not overlap")); goto error; } } } } VIR_FREE(nodes); /* analysis of cpu handling */ if ((node = virXPathNode("./cpu[1]", ctxt)) != NULL) { xmlNodePtr oldnode = ctxt->node; ctxt->node = node; def->cpu = virCPUDefParseXML(node, ctxt, VIR_CPU_TYPE_GUEST); ctxt->node = oldnode; if (def->cpu == NULL) goto error; if (def->cpu->sockets && def->maxvcpus > def->cpu->sockets * def->cpu->cores * def->cpu->threads) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("Maximum CPUs greater than topology limit")); goto error; } } if (virDomainNumaDefCPUParseXML(def->numa, ctxt) < 0) goto error; if (virDomainNumaGetCPUCountTotal(def->numa) > def->maxvcpus) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Number of CPUs in exceeds the" " count")); goto error; } if (virDomainNumatuneParseXML(def->numa, def->placement_mode == VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC, ctxt) < 0) goto error; if (virDomainNumatuneHasPlacementAuto(def->numa) && !def->cpumask && !def->cputune.vcpupin && !def->cputune.emulatorpin && !def->cputune.iothreadspin) def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO; if ((n = virXPathNodeSet("./resource", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract resource nodes")); goto error; } if (n > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only one resource element is supported")); goto error; } if (n && !(def->resource = virDomainResourceDefParse(nodes[0], ctxt))) goto error; VIR_FREE(nodes); if ((n = virXPathNodeSet("./features/*", ctxt, &nodes)) < 0) goto error; for (i = 0; i < n; i++) { int val = virDomainFeatureTypeFromString((const char *)nodes[i]->name); if (val < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unexpected feature '%s'"), nodes[i]->name); goto error; } switch ((virDomainFeature) val) { case VIR_DOMAIN_FEATURE_APIC: if ((tmp = virXPathString("string(./features/apic/@eoi)", ctxt))) { int eoi; if ((eoi = virTristateSwitchTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown value for attribute eoi: '%s'"), tmp); goto error; } def->apic_eoi = eoi; VIR_FREE(tmp); } /* fallthrough */ case VIR_DOMAIN_FEATURE_ACPI: case VIR_DOMAIN_FEATURE_PAE: case VIR_DOMAIN_FEATURE_HAP: case VIR_DOMAIN_FEATURE_VIRIDIAN: case VIR_DOMAIN_FEATURE_PRIVNET: case VIR_DOMAIN_FEATURE_HYPERV: case VIR_DOMAIN_FEATURE_KVM: def->features[val] = VIR_TRISTATE_SWITCH_ON; break; case VIR_DOMAIN_FEATURE_CAPABILITIES: node = ctxt->node; ctxt->node = nodes[i]; if ((tmp = virXPathString("string(./@policy)", ctxt))) { if ((def->features[val] = virDomainCapabilitiesPolicyTypeFromString(tmp)) == -1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown state attribute '%s' of feature '%s'"), tmp, virDomainFeatureTypeToString(val)); goto error; } VIR_FREE(tmp); } else { def->features[val] = VIR_TRISTATE_SWITCH_ABSENT; } ctxt->node = node; break; case VIR_DOMAIN_FEATURE_PMU: case VIR_DOMAIN_FEATURE_PVSPINLOCK: node = ctxt->node; ctxt->node = nodes[i]; if ((tmp = virXPathString("string(./@state)", ctxt))) { if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) == -1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown state attribute '%s' of feature '%s'"), tmp, virDomainFeatureTypeToString(val)); goto error; } VIR_FREE(tmp); } else { def->features[val] = VIR_TRISTATE_SWITCH_ON; } ctxt->node = node; break; /* coverity[dead_error_begin] */ case VIR_DOMAIN_FEATURE_LAST: break; } } VIR_FREE(nodes); if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) { int feature; int value; node = ctxt->node; if ((n = virXPathNodeSet("./features/hyperv/*", ctxt, &nodes)) < 0) goto error; for (i = 0; i < n; i++) { feature = virDomainHypervTypeFromString((const char *)nodes[i]->name); if (feature < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unsupported HyperV Enlightenment feature: %s"), nodes[i]->name); goto error; } ctxt->node = nodes[i]; switch ((virDomainHyperv) feature) { case VIR_DOMAIN_HYPERV_RELAXED: case VIR_DOMAIN_HYPERV_VAPIC: if (!(tmp = virXPathString("string(./@state)", ctxt))) { virReportError(VIR_ERR_XML_ERROR, _("missing 'state' attribute for " "HyperV Enlightenment feature '%s'"), nodes[i]->name); goto error; } if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid value of state argument " "for HyperV Enlightenment feature '%s'"), nodes[i]->name); goto error; } VIR_FREE(tmp); def->hyperv_features[feature] = value; break; case VIR_DOMAIN_HYPERV_SPINLOCKS: if (!(tmp = virXPathString("string(./@state)", ctxt))) { virReportError(VIR_ERR_XML_ERROR, _("missing 'state' attribute for " "HyperV Enlightenment feature '%s'"), nodes[i]->name); goto error; } if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid value of state argument " "for HyperV Enlightenment feature '%s'"), nodes[i]->name); goto error; } VIR_FREE(tmp); if (value == VIR_TRISTATE_SWITCH_ON) { if (virXPathUInt("string(./@retries)", ctxt, &def->hyperv_spinlocks) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid HyperV spinlock retry count")); goto error; } if (def->hyperv_spinlocks < 0xFFF) { virReportError(VIR_ERR_XML_ERROR, "%s", _("HyperV spinlock retry count must be " "at least 4095")); goto error; } } def->hyperv_features[feature] = value; break; /* coverity[dead_error_begin] */ case VIR_DOMAIN_HYPERV_LAST: break; } } VIR_FREE(nodes); ctxt->node = node; } if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) { int feature; int value; node = ctxt->node; if ((n = virXPathNodeSet("./features/kvm/*", ctxt, &nodes)) < 0) goto error; for (i = 0; i < n; i++) { feature = virDomainKVMTypeFromString((const char *)nodes[i]->name); if (feature < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unsupported KVM feature: %s"), nodes[i]->name); goto error; } ctxt->node = nodes[i]; switch ((virDomainKVM) feature) { case VIR_DOMAIN_KVM_HIDDEN: if (!(tmp = virXPathString("string(./@state)", ctxt))) { virReportError(VIR_ERR_XML_ERROR, _("missing 'state' attribute for " "KVM feature '%s'"), nodes[i]->name); goto error; } if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid value of state argument " "for KVM feature '%s'"), nodes[i]->name); goto error; } VIR_FREE(tmp); def->kvm_features[feature] = value; break; /* coverity[dead_error_begin] */ case VIR_DOMAIN_KVM_LAST: break; } } VIR_FREE(nodes); ctxt->node = node; } if ((n = virXPathNodeSet("./features/capabilities/*", ctxt, &nodes)) < 0) goto error; for (i = 0; i < n; i++) { int val = virDomainCapsFeatureTypeFromString((const char *)nodes[i]->name); if (val < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unexpected capability feature '%s'"), nodes[i]->name); goto error; } if (val >= 0 && val < VIR_DOMAIN_CAPS_FEATURE_LAST) { node = ctxt->node; ctxt->node = nodes[i]; if ((tmp = virXPathString("string(./@state)", ctxt))) { if ((def->caps_features[val] = virTristateSwitchTypeFromString(tmp)) == -1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown state attribute '%s' of feature capability '%s'"), tmp, virDomainFeatureTypeToString(val)); goto error; } VIR_FREE(tmp); } else { def->caps_features[val] = VIR_TRISTATE_SWITCH_ON; } ctxt->node = node; } } VIR_FREE(nodes); if (virDomainEventActionParseXML(ctxt, "on_reboot", "string(./on_reboot[1])", &def->onReboot, VIR_DOMAIN_LIFECYCLE_RESTART, virDomainLifecycleTypeFromString) < 0) goto error; if (virDomainEventActionParseXML(ctxt, "on_poweroff", "string(./on_poweroff[1])", &def->onPoweroff, VIR_DOMAIN_LIFECYCLE_DESTROY, virDomainLifecycleTypeFromString) < 0) goto error; if (virDomainEventActionParseXML(ctxt, "on_crash", "string(./on_crash[1])", &def->onCrash, VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY, virDomainLifecycleCrashTypeFromString) < 0) goto error; if (virDomainEventActionParseXML(ctxt, "on_lockfailure", "string(./on_lockfailure[1])", &def->onLockFailure, VIR_DOMAIN_LOCK_FAILURE_DEFAULT, virDomainLockFailureTypeFromString) < 0) goto error; if (virDomainPMStateParseXML(ctxt, "string(./pm/suspend-to-mem/@enabled)", &def->pm.s3) < 0) goto error; if (virDomainPMStateParseXML(ctxt, "string(./pm/suspend-to-disk/@enabled)", &def->pm.s4) < 0) goto error; if ((tmp = virXPathString("string(./clock/@offset)", ctxt)) && (def->clock.offset = virDomainClockOffsetTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown clock offset '%s'"), tmp); goto error; } VIR_FREE(tmp); switch (def->clock.offset) { case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: case VIR_DOMAIN_CLOCK_OFFSET_UTC: tmp = virXPathString("string(./clock/@adjustment)", ctxt); if (tmp) { if (STREQ(tmp, "reset")) { def->clock.data.utc_reset = true; } else { if (virStrToLong_ll(tmp, NULL, 10, &def->clock.data.variable.adjustment) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown clock adjustment '%s'"), tmp); goto error; } switch (def->clock.offset) { case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME: def->clock.data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_LOCALTIME; break; case VIR_DOMAIN_CLOCK_OFFSET_UTC: def->clock.data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC; break; } def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_VARIABLE; } VIR_FREE(tmp); } else { def->clock.data.utc_reset = false; } break; case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: if (virXPathLongLong("number(./clock/@adjustment)", ctxt, &def->clock.data.variable.adjustment) < 0) def->clock.data.variable.adjustment = 0; if (virXPathLongLong("number(./clock/@adjustment0)", ctxt, &def->clock.data.variable.adjustment0) < 0) def->clock.data.variable.adjustment0 = 0; tmp = virXPathString("string(./clock/@basis)", ctxt); if (tmp) { if ((def->clock.data.variable.basis = virDomainClockBasisTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown clock basis '%s'"), tmp); goto error; } VIR_FREE(tmp); } else { def->clock.data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC; } break; case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE: def->clock.data.timezone = virXPathString("string(./clock/@timezone)", ctxt); if (!def->clock.data.timezone) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing 'timezone' attribute for clock with offset='timezone'")); goto error; } break; } if ((n = virXPathNodeSet("./clock/timer", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->clock.timers, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainTimerDefPtr timer = virDomainTimerDefParseXML(nodes[i], ctxt); if (!timer) goto error; def->clock.timers[def->clock.ntimers++] = timer; } VIR_FREE(nodes); def->os.bootloader = virXPathString("string(./bootloader)", ctxt); def->os.bootloaderArgs = virXPathString("string(./bootloader_args)", ctxt); def->os.type = virXPathString("string(./os/type[1])", ctxt); if (!def->os.type) { if (def->os.bootloader) { if (VIR_STRDUP(def->os.type, "xen") < 0) goto error; } else { virReportError(VIR_ERR_XML_ERROR, "%s", _("an os must be specified")); goto error; } } /* * HACK: For xen driver we previously used bogus 'linux' as the * os type for paravirt, whereas capabilities declare it to * be 'xen'. So we accept the former and convert */ if (STREQ(def->os.type, "linux") && def->virtType == VIR_DOMAIN_VIRT_XEN) { VIR_FREE(def->os.type); if (VIR_STRDUP(def->os.type, "xen") < 0) goto error; } tmp = virXPathString("string(./os/type[1]/@arch)", ctxt); if (tmp && !(def->os.arch = virArchFromString(tmp))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown architecture %s"), tmp); goto error; } VIR_FREE(tmp); def->os.machine = virXPathString("string(./os/type[1]/@machine)", ctxt); if (!(flags & VIR_DOMAIN_DEF_PARSE_SKIP_OSTYPE_CHECKS)) { if (!virCapabilitiesSupportsGuestOSType(caps, def->os.type)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("no support found for os '%s'"), def->os.type); goto error; } if (def->os.arch) { if (!virCapabilitiesSupportsGuestArch(caps, def->os.arch)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("No guest options available for arch '%s'"), virArchToString(def->os.arch)); goto error; } if (!virCapabilitiesSupportsGuestOSTypeArch(caps, def->os.type, def->os.arch)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("No os type '%s' available for arch '%s'"), def->os.type, virArchToString(def->os.arch)); goto error; } } else { def->os.arch = virCapabilitiesDefaultGuestArch(caps, def->os.type, virDomainVirtTypeToString(def->virtType)); if (!def->os.arch) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no supported architecture for os type '%s'"), def->os.type); goto error; } } if (!def->os.machine) { const char *defaultMachine = virCapabilitiesDefaultGuestMachine(caps, def->os.type, def->os.arch, virDomainVirtTypeToString(def->virtType)); if (VIR_STRDUP(def->os.machine, defaultMachine) < 0) goto error; } } /* * Booting options for different OS types.... * * - A bootloader (and optional kernel+initrd) (xen) * - A kernel + initrd (xen) * - A boot device (and optional kernel+initrd) (hvm) * - An init script (exe) */ if (STREQ(def->os.type, "exe")) { def->os.init = virXPathString("string(./os/init[1])", ctxt); def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt); if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0) goto error; if (VIR_ALLOC_N(def->os.initargv, n+1) < 0) goto error; for (i = 0; i < n; i++) { if (!nodes[i]->children || !nodes[i]->children->content) { virReportError(VIR_ERR_XML_ERROR, "%s", _("No data supplied for element")); goto error; } if (VIR_STRDUP(def->os.initargv[i], (const char*) nodes[i]->children->content) < 0) goto error; } def->os.initargv[n] = NULL; VIR_FREE(nodes); } if (STREQ(def->os.type, "xen") || STREQ(def->os.type, "hvm") || STREQ(def->os.type, "uml")) { xmlNodePtr loader_node; def->os.kernel = virXPathString("string(./os/kernel[1])", ctxt); def->os.initrd = virXPathString("string(./os/initrd[1])", ctxt); def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt); def->os.dtb = virXPathString("string(./os/dtb[1])", ctxt); def->os.root = virXPathString("string(./os/root[1])", ctxt); if ((loader_node = virXPathNode("./os/loader[1]", ctxt))) { if (VIR_ALLOC(def->os.loader) < 0) goto error; if (virDomainLoaderDefParseXML(loader_node, def->os.loader) < 0) goto error; def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt); def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt); } } if (STREQ(def->os.type, "hvm")) { if (virDomainDefParseBootXML(ctxt, def) < 0) goto error; if (!(bootHash = virHashCreate(5, NULL))) goto error; } def->emulator = virXPathString("string(./devices/emulator[1])", ctxt); /* analysis of the disk devices */ if ((n = virXPathNodeSet("./devices/disk", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->disks, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainDiskDefPtr disk = virDomainDiskDefParseXML(xmlopt, nodes[i], ctxt, bootHash, def->seclabels, def->nseclabels, flags); if (!disk) goto error; virDomainDiskInsertPreAlloced(def, disk); } VIR_FREE(nodes); /* analysis of the controller devices */ if ((n = virXPathNodeSet("./devices/controller", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->controllers, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainControllerDefPtr controller = virDomainControllerDefParseXML(nodes[i], ctxt, flags); if (!controller) goto error; /* sanitize handling of "none" usb controller */ if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) { if (controller->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) { if (usb_other || usb_none) { virDomainControllerDefFree(controller); virReportError(VIR_ERR_XML_DETAIL, "%s", _("Can't add another USB controller: " "USB is disabled for this domain")); goto error; } usb_none = true; } else { if (usb_none) { virDomainControllerDefFree(controller); virReportError(VIR_ERR_XML_DETAIL, "%s", _("Can't add another USB controller: " "USB is disabled for this domain")); goto error; } usb_other = true; } if (controller->info.mastertype == VIR_DOMAIN_CONTROLLER_MASTER_NONE) usb_master = true; } virDomainControllerInsertPreAlloced(def, controller); } VIR_FREE(nodes); if (usb_other && !usb_master) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("No master USB controller specified")); goto error; } /* analysis of the resource leases */ if ((n = virXPathNodeSet("./devices/lease", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract device leases")); goto error; } if (n && VIR_ALLOC_N(def->leases, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainLeaseDefPtr lease = virDomainLeaseDefParseXML(nodes[i]); if (!lease) goto error; def->leases[def->nleases++] = lease; } VIR_FREE(nodes); /* analysis of the filesystems */ if ((n = virXPathNodeSet("./devices/filesystem", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->fss, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainFSDefPtr fs = virDomainFSDefParseXML(nodes[i], ctxt, flags); if (!fs) goto error; def->fss[def->nfss++] = fs; } VIR_FREE(nodes); /* analysis of the network devices */ if ((n = virXPathNodeSet("./devices/interface", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->nets, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainNetDefPtr net = virDomainNetDefParseXML(xmlopt, nodes[i], ctxt, bootHash, flags); if (!net) goto error; def->nets[def->nnets++] = net; /* (and * where the actual network type is already known to be * hostdev) must also be in the hostdevs array. */ if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV && virDomainHostdevInsert(def, virDomainNetGetActualHostdev(net)) < 0) { goto error; } } VIR_FREE(nodes); /* analysis of the smartcard devices */ if ((n = virXPathNodeSet("./devices/smartcard", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->smartcards, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainSmartcardDefPtr card = virDomainSmartcardDefParseXML(nodes[i], flags); if (!card) goto error; def->smartcards[def->nsmartcards++] = card; } VIR_FREE(nodes); /* analysis of the character devices */ if ((n = virXPathNodeSet("./devices/parallel", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->parallels, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainChrDefPtr chr = virDomainChrDefParseXML(ctxt, nodes[i], def->seclabels, def->nseclabels, flags); if (!chr) goto error; if (chr->target.port == -1) { int maxport = -1; for (j = 0; j < i; j++) { if (def->parallels[j]->target.port > maxport) maxport = def->parallels[j]->target.port; } chr->target.port = maxport + 1; } def->parallels[def->nparallels++] = chr; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./devices/serial", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->serials, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainChrDefPtr chr = virDomainChrDefParseXML(ctxt, nodes[i], def->seclabels, def->nseclabels, flags); if (!chr) goto error; if (chr->target.port == -1) { int maxport = -1; for (j = 0; j < i; j++) { if (def->serials[j]->target.port > maxport) maxport = def->serials[j]->target.port; } chr->target.port = maxport + 1; } def->serials[def->nserials++] = chr; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./devices/console", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract console devices")); goto error; } if (n && VIR_ALLOC_N(def->consoles, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainChrDefPtr chr = virDomainChrDefParseXML(ctxt, nodes[i], def->seclabels, def->nseclabels, flags); if (!chr) goto error; chr->target.port = i; def->consoles[def->nconsoles++] = chr; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./devices/channel", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->channels, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainChrDefPtr chr = virDomainChrDefParseXML(ctxt, nodes[i], def->seclabels, def->nseclabels, flags); if (!chr) goto error; def->channels[def->nchannels++] = chr; } VIR_FREE(nodes); /* analysis of the input devices */ if ((n = virXPathNodeSet("./devices/input", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->inputs, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainInputDefPtr input = virDomainInputDefParseXML(def, nodes[i], flags); if (!input) goto error; /* Check if USB bus is required */ if (input->bus == VIR_DOMAIN_INPUT_BUS_USB && usb_none) { virDomainInputDefFree(input); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Can't add USB input device. " "USB bus is disabled")); goto error; } /* With QEMU / KVM / Xen graphics, mouse + PS/2 is implicit * with graphics, so don't store it. * XXX will this be true for other virt types ? */ if ((STREQ(def->os.type, "hvm") && input->bus == VIR_DOMAIN_INPUT_BUS_PS2 && (input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE || input->type == VIR_DOMAIN_INPUT_TYPE_KBD)) || (STREQ(def->os.type, "xen") && input->bus == VIR_DOMAIN_INPUT_BUS_XEN && (input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE || input->type == VIR_DOMAIN_INPUT_TYPE_KBD)) || (STREQ(def->os.type, "exe") && def->virtType == VIR_DOMAIN_VIRT_PARALLELS && input->bus == VIR_DOMAIN_INPUT_BUS_PARALLELS && (input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE || input->type == VIR_DOMAIN_INPUT_TYPE_KBD))) { virDomainInputDefFree(input); continue; } def->inputs[def->ninputs++] = input; } VIR_FREE(nodes); /* analysis of the graphics devices */ if ((n = virXPathNodeSet("./devices/graphics", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->graphics, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainGraphicsDefPtr graphics = virDomainGraphicsDefParseXML(nodes[i], ctxt, flags); if (!graphics) goto error; def->graphics[def->ngraphics++] = graphics; } VIR_FREE(nodes); /* If graphics are enabled, there's an implicit PS2 mouse */ if (def->ngraphics > 0 && (ARCH_IS_X86(def->os.arch) || def->os.arch == VIR_ARCH_NONE)) { int input_bus = VIR_DOMAIN_INPUT_BUS_XEN; if (STREQ(def->os.type, "hvm")) input_bus = VIR_DOMAIN_INPUT_BUS_PS2; if (STREQ(def->os.type, "exe") && def->virtType == VIR_DOMAIN_VIRT_PARALLELS) input_bus = VIR_DOMAIN_INPUT_BUS_PARALLELS; if (virDomainDefMaybeAddInput(def, VIR_DOMAIN_INPUT_TYPE_MOUSE, input_bus) < 0) goto error; if (virDomainDefMaybeAddInput(def, VIR_DOMAIN_INPUT_TYPE_KBD, input_bus) < 0) goto error; } /* analysis of the sound devices */ if ((n = virXPathNodeSet("./devices/sound", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->sounds, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainSoundDefPtr sound = virDomainSoundDefParseXML(nodes[i], ctxt, flags); if (!sound) goto error; def->sounds[def->nsounds++] = sound; } VIR_FREE(nodes); /* analysis of the video devices */ if ((n = virXPathNodeSet("./devices/video", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->videos, n) < 0) goto error; for (i = 0; i < n; i++) { j = def->nvideos; virDomainVideoDefPtr video = virDomainVideoDefParseXML(nodes[j], def, flags); if (!video) goto error; if (video->primary) { if (primaryVideo) { virDomainVideoDefFree(video); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Only one primary video device is supported")); goto error; } j = 0; primaryVideo = true; } if (VIR_INSERT_ELEMENT_INPLACE(def->videos, j, def->nvideos, video) < 0) { virDomainVideoDefFree(video); goto error; } } VIR_FREE(nodes); /* For backwards compatibility, if no