/* * domain_conf.c: domain XML processing * * Copyright (C) 2006-2016 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 * . */ #include #include #include #include #include "configmake.h" #include "internal.h" #include "virerror.h" #include "checkpoint_conf.h" #include "datatypes.h" #include "domain_addr.h" #include "domain_conf.h" #include "snapshot_conf.h" #include "viralloc.h" #include "virxml.h" #include "viruuid.h" #include "virbuffer.h" #include "virlog.h" #include "nwfilter_conf.h" #include "virnetworkportdef.h" #include "storage_conf.h" #include "virstoragefile.h" #include "virfile.h" #include "virbitmap.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 "virsecret.h" #include "virstring.h" #include "virnetdev.h" #include "virnetdevtap.h" #include "virnetdevmacvlan.h" #include "virhostdev.h" #include "virmdev.h" #include "virdomainsnapshotobjlist.h" #include "virdomaincheckpointobjlist.h" #define VIR_FROM_THIS VIR_FROM_DOMAIN VIR_LOG_INIT("conf.domain_conf"); /* 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 */ virXMLNamespace ns; /* ABI stability callbacks */ virDomainABIStability abi; /* Private data for save image stored in snapshot XML */ virSaveCookieCallbacks saveCookie; /* Snapshot postparse callbacks */ virDomainMomentPostParseCallback momentPostParse; }; #define VIR_DOMAIN_DEF_FORMAT_COMMON_FLAGS \ (VIR_DOMAIN_DEF_FORMAT_SECURE | \ VIR_DOMAIN_DEF_FORMAT_INACTIVE | \ VIR_DOMAIN_DEF_FORMAT_MIGRATABLE) VIR_ENUM_IMPL(virDomainTaint, VIR_DOMAIN_TAINT_LAST, "custom-argv", "custom-monitor", "high-privileges", "shell-scripts", "disk-probing", "external-launch", "host-cpu", "hook-script", "cdrom-passthrough", "custom-dtb", "custom-ga-command", "custom-hypervisor-feature", ); VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST, "none", "qemu", "kqemu", "kvm", "xen", "lxc", "uml", "openvz", "test", "vmware", "hyperv", "vbox", "phyp", "parallels", "bhyve", "vz", ); VIR_ENUM_IMPL(virDomainOS, VIR_DOMAIN_OSTYPE_LAST, "hvm", "xen", "linux", "exe", "uml", "xenpvh", ); VIR_ENUM_IMPL(virDomainBoot, VIR_DOMAIN_BOOT_LAST, "fd", "cdrom", "hd", "network", ); VIR_ENUM_IMPL(virDomainFeature, VIR_DOMAIN_FEATURE_LAST, "acpi", "apic", "pae", "hap", "viridian", "privnet", "hyperv", "kvm", "pvspinlock", "capabilities", "pmu", "vmport", "gic", "smm", "ioapic", "hpt", "vmcoreinfo", "htm", "nested-hv", "msrs", "ccf-assist", ); VIR_ENUM_IMPL(virDomainCapabilitiesPolicy, VIR_DOMAIN_CAPABILITIES_POLICY_LAST, "default", "allow", "deny", ); VIR_ENUM_IMPL(virDomainHyperv, VIR_DOMAIN_HYPERV_LAST, "relaxed", "vapic", "spinlocks", "vpindex", "runtime", "synic", "stimer", "reset", "vendor_id", "frequencies", "reenlightenment", "tlbflush", "ipi", "evmcs", ); VIR_ENUM_IMPL(virDomainKVM, VIR_DOMAIN_KVM_LAST, "hidden", "hint-dedicated", ); VIR_ENUM_IMPL(virDomainMsrsUnknown, VIR_DOMAIN_MSRS_UNKNOWN_LAST, "ignore", "fault", ); 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, "poweroff", "reboot", "crash", ); VIR_ENUM_IMPL(virDomainLifecycleAction, VIR_DOMAIN_LIFECYCLE_ACTION_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", "iommu", "vsock", ); 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", "xenbus", ); VIR_ENUM_IMPL(virDomainControllerModelPCI, VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST, "pci-root", "pcie-root", "pci-bridge", "dmi-to-pci-bridge", "pcie-to-pci-bridge", "pcie-root-port", "pcie-switch-upstream-port", "pcie-switch-downstream-port", "pci-expander-bus", "pcie-expander-bus", ); VIR_ENUM_IMPL(virDomainControllerPCIModelName, VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_LAST, "none", "pci-bridge", "i82801b11-bridge", "ioh3420", "x3130-upstream", "xio3130-downstream", "pxb", "pxb-pcie", "pcie-root-port", "spapr-pci-host-bridge", "pcie-pci-bridge", ); VIR_ENUM_IMPL(virDomainControllerModelSCSI, VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LAST, "auto", "buslogic", "lsilogic", "lsisas1068", "vmpvscsi", "ibmvscsi", "virtio-scsi", "lsisas1078", "virtio-transitional", "virtio-non-transitional", ); 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", "qusb1", "qusb2", "qemu-xhci", "none", ); VIR_ENUM_IMPL(virDomainControllerModelIDE, VIR_DOMAIN_CONTROLLER_MODEL_IDE_LAST, "piix3", "piix4", "ich6", ); VIR_ENUM_IMPL(virDomainControllerModelVirtioSerial, VIR_DOMAIN_CONTROLLER_MODEL_VIRTIO_SERIAL_LAST, "virtio", "virtio-transitional", "virtio-non-transitional", ); VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST, "mount", "block", "file", "template", "ram", "bind", "volume", ); 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(virDomainFSModel, VIR_DOMAIN_FS_MODEL_LAST, "default", "virtio", "virtio-transitional", "virtio-non-transitional", ); VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST, "user", "ethernet", "vhostuser", "server", "client", "mcast", "network", "bridge", "internal", "direct", "hostdev", "udp", ); VIR_ENUM_IMPL(virDomainNetModel, VIR_DOMAIN_NET_MODEL_LAST, "unknown", "netfront", "rtl8139", "virtio", "e1000", "e1000e", "virtio-transitional", "virtio-non-transitional", "usb-net", "spapr-vlan", "lan9118", "scm91c111", "vlance", "vmxnet", "vmxnet2", "vmxnet3", "Am79C970A", "Am79C973", "82540EM", "82545EM", "82543GC", ); 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, "none", "isa-serial", "usb-serial", "pci-serial", "spapr-vio-serial", "system-serial", "sclp-serial", ); VIR_ENUM_IMPL(virDomainChrChannelTarget, VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_LAST, "none", "guestfwd", "virtio", "xen", ); VIR_ENUM_IMPL(virDomainChrConsoleTarget, VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_LAST, "none", "serial", "xen", "uml", "virtio", "lxc", "openvz", "sclp", "sclplm", ); VIR_ENUM_IMPL(virDomainChrSerialTargetModel, VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_LAST, "none", "isa-serial", "usb-serial", "pci-serial", "spapr-vty", "pl011", "sclpconsole", "sclplmconsole", "16550a", ); 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", "output", ); VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST, "sb16", "es1370", "pcspk", "ac97", "ich6", "ich9", "usb", ); VIR_ENUM_IMPL(virDomainKeyWrapCipherName, VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_LAST, "aes", "dea", ); VIR_ENUM_IMPL(virDomainMemballoonModel, VIR_DOMAIN_MEMBALLOON_MODEL_LAST, "virtio", "xen", "none", "virtio-transitional", "virtio-non-transitional", ); VIR_ENUM_IMPL(virDomainSmbiosMode, VIR_DOMAIN_SMBIOS_LAST, "none", "emulate", "host", "sysinfo", ); VIR_ENUM_IMPL(virDomainWatchdogModel, VIR_DOMAIN_WATCHDOG_MODEL_LAST, "i6300esb", "ib700", "diag288", ); VIR_ENUM_IMPL(virDomainWatchdogAction, VIR_DOMAIN_WATCHDOG_ACTION_LAST, "reset", "shutdown", "poweroff", "pause", "dump", "none", "inject-nmi", ); VIR_ENUM_IMPL(virDomainPanicModel, VIR_DOMAIN_PANIC_MODEL_LAST, "default", "isa", "pseries", "hyperv", "s390", ); VIR_ENUM_IMPL(virDomainVideoBackend, VIR_DOMAIN_VIDEO_BACKEND_TYPE_LAST, "default", "qemu", "vhostuser", ); VIR_ENUM_IMPL(virDomainVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "default", "vga", "cirrus", "vmvga", "xen", "vbox", "qxl", "parallels", "virtio", "gop", "none", "bochs", "ramfb", ); VIR_ENUM_IMPL(virDomainVideoVGAConf, VIR_DOMAIN_VIDEO_VGACONF_LAST, "io", "on", "off", ); VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST, "mouse", "tablet", "keyboard", "passthrough", ); VIR_ENUM_IMPL(virDomainInputBus, VIR_DOMAIN_INPUT_BUS_LAST, "ps2", "usb", "xen", "parallels", "virtio", ); VIR_ENUM_IMPL(virDomainInputModel, VIR_DOMAIN_INPUT_MODEL_LAST, "default", "virtio", "virtio-transitional", "virtio-non-transitional", ); VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST, "sdl", "vnc", "rdp", "desktop", "spice", "egl-headless", ); VIR_ENUM_IMPL(virDomainGraphicsListen, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST, "none", "address", "network", "socket", ); 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", "scsi_host", "mdev", ); 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(virDomainHostdevSubsysSCSIHostProtocol, VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_LAST, "none", "vhost", ); VIR_ENUM_IMPL(virDomainHostdevSubsysSCSIVHostModel, VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_VHOST_MODEL_TYPE_LAST, "default", "virtio", "virtio-transitional", "virtio-non-transitional", ); 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", "post-copy", ); 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", "post-copy", "post-copy failed", ); 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", "daemon", ); 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", "virtio-transitional", "virtio-non-transitional", ); VIR_ENUM_IMPL(virDomainRNGBackend, VIR_DOMAIN_RNG_BACKEND_LAST, "random", "egd", ); VIR_ENUM_IMPL(virDomainTPMModel, VIR_DOMAIN_TPM_MODEL_LAST, "tpm-tis", "tpm-crb", ); VIR_ENUM_IMPL(virDomainTPMBackend, VIR_DOMAIN_TPM_TYPE_LAST, "passthrough", "emulator", ); VIR_ENUM_IMPL(virDomainTPMVersion, VIR_DOMAIN_TPM_VERSION_LAST, "default", "1.2", "2.0", ); VIR_ENUM_IMPL(virDomainIOMMUModel, VIR_DOMAIN_IOMMU_MODEL_LAST, "intel", "smmuv3", ); VIR_ENUM_IMPL(virDomainVsockModel, VIR_DOMAIN_VSOCK_MODEL_LAST, "default", "virtio", "virtio-transitional", "virtio-non-transitional", ); VIR_ENUM_IMPL(virDomainDiskDiscard, VIR_DOMAIN_DISK_DISCARD_LAST, "default", "unmap", "ignore", ); VIR_ENUM_IMPL(virDomainDiskDetectZeroes, VIR_DOMAIN_DISK_DETECT_ZEROES_LAST, "default", "off", "on", "unmap", ); VIR_ENUM_IMPL(virDomainDiskModel, VIR_DOMAIN_DISK_MODEL_LAST, "default", "virtio", "virtio-transitional", "virtio-non-transitional", ); VIR_ENUM_IMPL(virDomainDiskMirrorState, VIR_DOMAIN_DISK_MIRROR_STATE_LAST, "none", "yes", "abort", "pivot", ); VIR_ENUM_IMPL(virDomainMemorySource, VIR_DOMAIN_MEMORY_SOURCE_LAST, "none", "file", "anonymous", "memfd", ); VIR_ENUM_IMPL(virDomainMemoryAllocation, VIR_DOMAIN_MEMORY_ALLOCATION_LAST, "none", "immediate", "ondemand", ); VIR_ENUM_IMPL(virDomainLoader, VIR_DOMAIN_LOADER_TYPE_LAST, "none", "rom", "pflash", ); VIR_ENUM_IMPL(virDomainIOAPIC, VIR_DOMAIN_IOAPIC_LAST, "none", "qemu", "kvm", ); VIR_ENUM_IMPL(virDomainHPTResizing, VIR_DOMAIN_HPT_RESIZING_LAST, "none", "enabled", "disabled", "required", ); VIR_ENUM_IMPL(virDomainOsDefFirmware, VIR_DOMAIN_OS_DEF_FIRMWARE_LAST, "none", "bios", "efi", ); /* 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", "nvdimm", ); VIR_ENUM_IMPL(virDomainShmemModel, VIR_DOMAIN_SHMEM_MODEL_LAST, "ivshmem", "ivshmem-plain", "ivshmem-doorbell", ); VIR_ENUM_IMPL(virDomainLaunchSecurity, VIR_DOMAIN_LAUNCH_SECURITY_LAST, "", "sev", ); static virClassPtr virDomainObjClass; static virClassPtr virDomainXMLOptionClass; static void virDomainObjDispose(void *obj); static void virDomainXMLOptionDispose(void *obj); static int virDomainObjOnceInit(void) { if (!VIR_CLASS_NEW(virDomainObj, virClassForObjectLockable())) return -1; if (!VIR_CLASS_NEW(virDomainXMLOption, virClassForObject())) return -1; return 0; } VIR_ONCE_GLOBAL_INIT(virDomainObj); static void virDomainXMLOptionDispose(void *obj) { virDomainXMLOptionPtr xmlopt = obj; if (xmlopt->config.privFree) (xmlopt->config.privFree)(xmlopt->config.priv); } /** * virDomainKeyWrapCipherDefParseXML: * * @def Domain definition * @node An XML cipher node * * Parse the attributes from the cipher node and store the state * attribute in @def. * * A cipher node has the form of * * * * Returns: 0 if the parse succeeded * -1 otherwise */ static int virDomainKeyWrapCipherDefParseXML(virDomainKeyWrapDefPtr keywrap, xmlNodePtr node) { int state_type; int name_type; g_autofree char *name = NULL; g_autofree char *state = NULL; if (!(name = virXMLPropString(node, "name"))) { virReportError(VIR_ERR_CONF_SYNTAX, "%s", _("missing name for cipher")); return -1; } if ((name_type = virDomainKeyWrapCipherNameTypeFromString(name)) < 0) { virReportError(VIR_ERR_CONF_SYNTAX, _("%s is not a supported cipher name"), name); return -1; } if (!(state = virXMLPropString(node, "state"))) { virReportError(VIR_ERR_CONF_SYNTAX, _("missing state for cipher named %s"), name); return -1; } if ((state_type = virTristateSwitchTypeFromString(state)) < 0) { virReportError(VIR_ERR_CONF_SYNTAX, _("%s is not a supported cipher state"), state); return -1; } switch ((virDomainKeyWrapCipherName) name_type) { case VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_AES: if (keywrap->aes != VIR_TRISTATE_SWITCH_ABSENT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("A domain definition can have no more than " "one cipher node with name %s"), virDomainKeyWrapCipherNameTypeToString(name_type)); return -1; } keywrap->aes = state_type; break; case VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_DEA: if (keywrap->dea != VIR_TRISTATE_SWITCH_ABSENT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("A domain definition can have no more than " "one cipher node with name %s"), virDomainKeyWrapCipherNameTypeToString(name_type)); return -1; } keywrap->dea = state_type; break; case VIR_DOMAIN_KEY_WRAP_CIPHER_NAME_LAST: break; } return 0; } static int virDomainKeyWrapDefParseXML(virDomainDefPtr def, xmlXPathContextPtr ctxt) { size_t i; int n; g_autofree xmlNodePtr *nodes = NULL; g_autofree virDomainKeyWrapDefPtr keywrap = NULL; if ((n = virXPathNodeSet("./keywrap/cipher", ctxt, &nodes)) < 0) return n; if (VIR_ALLOC(keywrap) < 0) return -1; for (i = 0; i < n; i++) { if (virDomainKeyWrapCipherDefParseXML(keywrap, nodes[i]) < 0) return -1; } if (keywrap->aes || keywrap->dea) def->keywrap = g_steal_pointer(&keywrap); return 0; } /** * virDomainXMLOptionNew: * * Allocate a new domain XML configuration */ virDomainXMLOptionPtr virDomainXMLOptionNew(virDomainDefParserConfigPtr config, virDomainXMLPrivateDataCallbacksPtr priv, virXMLNamespacePtr xmlns, virDomainABIStabilityPtr abi, virSaveCookieCallbacksPtr saveCookie) { 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; if (abi) xmlopt->abi = *abi; if (saveCookie) xmlopt->saveCookie = *saveCookie; /* 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; */ virXMLNamespacePtr virDomainXMLOptionGetNamespace(virDomainXMLOptionPtr xmlopt) { return &xmlopt->ns; } static int virDomainVirtioOptionsParseXML(xmlNodePtr driver, virDomainVirtioOptionsPtr *virtio) { int val; virDomainVirtioOptionsPtr res; g_autofree char *str = NULL; if (*virtio || !driver) return 0; if (VIR_ALLOC(*virtio) < 0) return -1; res = *virtio; if ((str = virXMLPropString(driver, "iommu"))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid iommu value")); return -1; } res->iommu = val; } VIR_FREE(str); if ((str = virXMLPropString(driver, "ats"))) { if ((val = virTristateSwitchTypeFromString(str)) <= 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid ats value")); return -1; } res->ats = val; } return 0; } virSaveCookieCallbacksPtr virDomainXMLOptionGetSaveCookie(virDomainXMLOptionPtr xmlopt) { return &xmlopt->saveCookie; } void virDomainXMLOptionSetMomentPostParse(virDomainXMLOptionPtr xmlopt, virDomainMomentPostParseCallback cb) { xmlopt->momentPostParse = cb; } int virDomainXMLOptionRunMomentPostParse(virDomainXMLOptionPtr xmlopt, virDomainMomentDefPtr def) { if (!xmlopt->momentPostParse) return virDomainMomentDefPostParse(def); return xmlopt->momentPostParse(def); } 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) { xmlNodePtr node; g_autofree char *c = NULL; node = root->children; while (node) { if (node->type == XML_ELEMENT_NODE) { if (virXMLNodeNameEqual(node, "path") && !dev->path) { dev->path = (char *)xmlNodeGetContent(node); } else if (virXMLNodeNameEqual(node, "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 (virXMLNodeNameEqual(node, "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 (virXMLNodeNameEqual(node, "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 (virXMLNodeNameEqual(node, "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 (virXMLNodeNameEqual(node, "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(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. */ static int virDomainDefCheckUnsupportedMemoryHotplug(virDomainDefPtr def) { /* memory hotplug tunables are not supported by this driver */ if (virDomainDefHasMemoryHotplug(def)) { 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. */ static 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; } bool virDomainObjTaint(virDomainObjPtr obj, virDomainTaintFlags taint) { unsigned int flag = (1 << taint); if (obj->taint & flag) return false; obj->taint |= flag; return true; } 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); VIR_FREE(def->socket); return; } void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) { size_t i; if (!def) return; switch (def->type) { case VIR_DOMAIN_GRAPHICS_TYPE_VNC: 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.rendernode); VIR_FREE(def->data.spice.keymap); virDomainGraphicsAuthDefClear(&def->data.spice.auth); break; case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: VIR_FREE(def->data.egl_headless.rendernode); break; case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } for (i = 0; i < def->nListens; i++) virDomainGraphicsListenDefClear(&def->listens[i]); VIR_FREE(def->listens); virObjectUnref(def->privateData); VIR_FREE(def); } const char *virDomainInputDefGetPath(virDomainInputDefPtr input) { switch ((virDomainInputType) input->type) { case VIR_DOMAIN_INPUT_TYPE_MOUSE: case VIR_DOMAIN_INPUT_TYPE_TABLET: case VIR_DOMAIN_INPUT_TYPE_KBD: case VIR_DOMAIN_INPUT_TYPE_LAST: return NULL; break; case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: return input->source.evdev; } return NULL; } void virDomainInputDefFree(virDomainInputDefPtr def) { if (!def) return; virDomainDeviceInfoClear(&def->info); VIR_FREE(def->source.evdev); VIR_FREE(def->virtio); 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); } static virDomainVcpuDefPtr virDomainVcpuDefNew(virDomainXMLOptionPtr xmlopt) { virDomainVcpuDefPtr ret = NULL; g_autoptr(virObject) priv = NULL; if (xmlopt && xmlopt->privateData.vcpuNew && !(priv = xmlopt->privateData.vcpuNew())) return NULL; if (VIR_ALLOC(ret) < 0) return NULL; ret->privateData = g_steal_pointer(&priv); return ret; } static void virDomainVcpuDefFree(virDomainVcpuDefPtr info) { if (!info) return; virBitmapFree(info->cpumask); info->cpumask = NULL; virObjectUnref(info->privateData); VIR_FREE(info); } int virDomainDefSetVcpusMax(virDomainDefPtr def, unsigned int maxvcpus, virDomainXMLOptionPtr xmlopt) { size_t oldmax = def->maxvcpus; size_t i; if (def->maxvcpus == maxvcpus) return 0; if (def->maxvcpus < maxvcpus) { if (VIR_EXPAND_N(def->vcpus, def->maxvcpus, maxvcpus - def->maxvcpus) < 0) return -1; for (i = oldmax; i < def->maxvcpus; i++) { if (!(def->vcpus[i] = virDomainVcpuDefNew(xmlopt))) return -1; } } else { for (i = maxvcpus; i < def->maxvcpus; i++) virDomainVcpuDefFree(def->vcpus[i]); VIR_SHRINK_N(def->vcpus, def->maxvcpus, def->maxvcpus - maxvcpus); } return 0; } bool virDomainDefHasVcpusOffline(const virDomainDef *def) { size_t i; for (i = 0; i < def->maxvcpus; i++) { if (!def->vcpus[i]->online) return true; } return false; } unsigned int virDomainDefGetVcpusMax(const virDomainDef *def) { return def->maxvcpus; } int virDomainDefSetVcpus(virDomainDefPtr def, unsigned int vcpus) { size_t i; if (vcpus > def->maxvcpus) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("maximum vCPU count must not be less than current " "vCPU count")); return -1; } for (i = 0; i < vcpus; i++) def->vcpus[i]->online = true; for (i = vcpus; i < def->maxvcpus; i++) def->vcpus[i]->online = false; return 0; } unsigned int virDomainDefGetVcpus(const virDomainDef *def) { size_t i; unsigned int ret = 0; for (i = 0; i < def->maxvcpus; i++) { if (def->vcpus[i]->online) ret++; } return ret; } /** * virDomainDefGetOnlineVcpumap: * @def: domain definition * * Returns a bitmap representing state of individual vcpus. */ virBitmapPtr virDomainDefGetOnlineVcpumap(const virDomainDef *def) { virBitmapPtr ret = NULL; size_t i; if (!(ret = virBitmapNew(def->maxvcpus))) return NULL; for (i = 0; i < def->maxvcpus; i++) { if (def->vcpus[i]->online) ignore_value(virBitmapSetBit(ret, i)); } return ret; } virDomainVcpuDefPtr virDomainDefGetVcpu(virDomainDefPtr def, unsigned int vcpu) { if (vcpu >= def->maxvcpus) return NULL; return def->vcpus[vcpu]; } static virDomainThreadSchedParamPtr virDomainDefGetVcpuSched(virDomainDefPtr def, unsigned int vcpu) { virDomainVcpuDefPtr vcpuinfo; if (!(vcpuinfo = virDomainDefGetVcpu(def, vcpu))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("vCPU '%u' is not present in domain definition"), vcpu); return NULL; } return &vcpuinfo->sched; } /** * virDomainDefHasVcpuPin: * @def: domain definition * * This helper returns true if any of the domain's vcpus has cpu pinning set */ static bool virDomainDefHasVcpuPin(const virDomainDef *def) { size_t i; for (i = 0; i < def->maxvcpus; i++) { if (def->vcpus[i]->cpumask) return true; } return false; } /** * virDomainDefGetVcpuPinInfoHelper: * @def: domain definition * @maplen: length of one cpumap passed from caller (@cpumaps) * @ncpumaps: count of cpumaps of @maplen length in @cpumaps * @cpumaps: array of pinning information bitmaps to be filled * @hostcpus: number of cpus in the host * @autoCpuset: Cpu pinning bitmap used in case of automatic cpu pinning * * Fills the @cpumaps array as documented by the virDomainGetVcpuPinInfo API. * In case when automatic cpu pinning is supported, the bitmap should be passed * as @autoCpuset. If @hostcpus is < 0 no error is reported (to pass through * error message). * * Returns number of filled entries or -1 on error. */ int virDomainDefGetVcpuPinInfoHelper(virDomainDefPtr def, int maplen, int ncpumaps, unsigned char *cpumaps, int hostcpus, virBitmapPtr autoCpuset) { int maxvcpus = virDomainDefGetVcpusMax(def); size_t i; g_autoptr(virBitmap) allcpumap = NULL; if (hostcpus < 0) return -1; if (!(allcpumap = virBitmapNew(hostcpus))) return -1; virBitmapSetAll(allcpumap); for (i = 0; i < maxvcpus && i < ncpumaps; i++) { virDomainVcpuDefPtr vcpu = virDomainDefGetVcpu(def, i); virBitmapPtr bitmap = NULL; if (vcpu && vcpu->cpumask) bitmap = vcpu->cpumask; else if (def->placement_mode == VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO && autoCpuset) bitmap = autoCpuset; else if (def->cpumask) bitmap = def->cpumask; else bitmap = allcpumap; virBitmapToDataBuf(bitmap, VIR_GET_CPUMAP(cpumaps, maplen, i), maplen); } return i; } /** * virDomainDeGetVcpusTopology: * @def: domain definition * @maxvcpus: optionally filled with number of vcpus the domain topology describes * * Calculates and validates that the vcpu topology is in sane bounds and * optionally returns the total number of vcpus described by given topology. * * Returns 0 on success, 1 if topology is not configured and -1 on error. */ int virDomainDefGetVcpusTopology(const virDomainDef *def, unsigned int *maxvcpus) { unsigned long long tmp; if (!def->cpu || def->cpu->sockets == 0) return 1; tmp = def->cpu->sockets; /* multiplication of 32bit numbers fits into a 64bit variable */ if ((tmp *= def->cpu->cores) > UINT_MAX || (tmp *= def->cpu->threads) > UINT_MAX) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("cpu topology results in more than %u cpus"), UINT_MAX); return -1; } if (maxvcpus) *maxvcpus = tmp; return 0; } virDomainDiskDefPtr virDomainDiskDefNew(virDomainXMLOptionPtr xmlopt) { virDomainDiskDefPtr ret; if (VIR_ALLOC(ret) < 0) return NULL; if (!(ret->src = virStorageSourceNew())) goto error; if (xmlopt && xmlopt->privateData.diskNew && !(ret->privateData = xmlopt->privateData.diskNew())) goto error; return ret; error: virDomainDiskDefFree(ret); return NULL; } void virDomainDiskDefFree(virDomainDiskDefPtr def) { if (!def) return; virObjectUnref(def->src); VIR_FREE(def->serial); VIR_FREE(def->dst); virObjectUnref(def->mirror); VIR_FREE(def->wwn); VIR_FREE(def->driverName); VIR_FREE(def->vendor); VIR_FREE(def->product); VIR_FREE(def->domain_name); VIR_FREE(def->blkdeviotune.group_name); VIR_FREE(def->virtio); virDomainDeviceInfoClear(&def->info); virObjectUnref(def->privateData); VIR_FREE(def); } int virDomainDiskGetType(virDomainDiskDefPtr def) { return def->src->type; } void virDomainDiskSetType(virDomainDiskDefPtr def, int type) { def->src->type = type; } const char * virDomainDiskGetSource(virDomainDiskDef const *def) { return def->src->path; } int virDomainDiskSetSource(virDomainDiskDefPtr def, const char *src) { char *tmp = g_strdup(src); g_free(def->src->path); def->src->path = tmp; return 0; } void virDomainDiskEmptySource(virDomainDiskDefPtr def) { virStorageSourcePtr src = def->src; bool readonly = src->readonly; virStorageSourceClear(src); src->type = VIR_STORAGE_TYPE_FILE; /* readonly property is necessary for CDROMs and thus can't be cleared */ src->readonly = readonly; } const char * virDomainDiskGetDriver(const virDomainDiskDef *def) { return def->driverName; } int virDomainDiskSetDriver(virDomainDiskDefPtr def, const char *name) { char *tmp = g_strdup(name); g_free(def->driverName); def->driverName = tmp; return 0; } int virDomainDiskGetFormat(virDomainDiskDefPtr def) { return def->src->format; } void virDomainDiskSetFormat(virDomainDiskDefPtr def, int format) { def->src->format = format; } virDomainControllerDefPtr virDomainControllerDefNew(virDomainControllerType type) { virDomainControllerDefPtr def; if (VIR_ALLOC(def) < 0) return NULL; def->type = type; /* initialize anything that has a non-0 default */ def->model = -1; def->idx = -1; switch ((virDomainControllerType) def->type) { case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: def->opts.vioserial.ports = -1; def->opts.vioserial.vectors = -1; break; case VIR_DOMAIN_CONTROLLER_TYPE_USB: def->opts.usbopts.ports = -1; break; case VIR_DOMAIN_CONTROLLER_TYPE_PCI: def->opts.pciopts.chassisNr = -1; def->opts.pciopts.chassis = -1; def->opts.pciopts.port = -1; def->opts.pciopts.busNr = -1; def->opts.pciopts.targetIndex = -1; def->opts.pciopts.numaNode = -1; break; case VIR_DOMAIN_CONTROLLER_TYPE_XENBUS: def->opts.xenbusopts.maxGrantFrames = -1; break; case VIR_DOMAIN_CONTROLLER_TYPE_IDE: case VIR_DOMAIN_CONTROLLER_TYPE_FDC: case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: case VIR_DOMAIN_CONTROLLER_TYPE_SATA: case VIR_DOMAIN_CONTROLLER_TYPE_CCID: case VIR_DOMAIN_CONTROLLER_TYPE_LAST: break; } return def; } void virDomainControllerDefFree(virDomainControllerDefPtr def) { if (!def) return; virDomainDeviceInfoClear(&def->info); VIR_FREE(def->virtio); VIR_FREE(def); } /** * virDomainControllerIsPSeriesPHB: * @cont: controller * * Checks whether @cont is a PCI Host Bridge (PHB), a specific type * of PCI controller used by pSeries guests. * * Returns: true if @cont is a PHB, false otherwise. */ bool virDomainControllerIsPSeriesPHB(const virDomainControllerDef *cont) { virDomainControllerPCIModelName name; /* PHBs are pci-root controllers */ if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_PCI || cont->model != VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) { return false; } name = cont->opts.pciopts.modelName; /* The actual device used for PHBs is spapr-pci-host-bridge */ if (name != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE) return false; return true; } virDomainFSDefPtr virDomainFSDefNew(void) { virDomainFSDefPtr ret; if (VIR_ALLOC(ret) < 0) return NULL; if (!(ret->src = virStorageSourceNew())) goto cleanup; return ret; cleanup: virDomainFSDefFree(ret); return NULL; } void virDomainFSDefFree(virDomainFSDefPtr def) { if (!def) return; virObjectUnref(def->src); VIR_FREE(def->dst); virDomainDeviceInfoClear(&def->info); VIR_FREE(def->virtio); 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); } virDomainVsockDefPtr virDomainVsockDefNew(virDomainXMLOptionPtr xmlopt) { virDomainVsockDefPtr ret = NULL; virDomainVsockDefPtr vsock; if (VIR_ALLOC(vsock) < 0) return NULL; if (xmlopt && xmlopt->privateData.vsockNew && !(vsock->privateData = xmlopt->privateData.vsockNew())) goto cleanup; ret = g_steal_pointer(&vsock); cleanup: virDomainVsockDefFree(vsock); return ret; } void virDomainVsockDefFree(virDomainVsockDefPtr vsock) { if (!vsock) return; virObjectUnref(vsock->privateData); virDomainDeviceInfoClear(&vsock->info); VIR_FREE(vsock); } void virDomainNetDefClear(virDomainNetDefPtr def) { if (!def) return; VIR_FREE(def->modelstr); def->model = VIR_DOMAIN_NET_MODEL_UNKNOWN; switch (def->type) { case VIR_DOMAIN_NET_TYPE_VHOSTUSER: virObjectUnref(def->data.vhostuser); def->data.vhostuser = NULL; break; case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_MCAST: case VIR_DOMAIN_NET_TYPE_UDP: VIR_FREE(def->data.socket.address); VIR_FREE(def->data.socket.localaddr); break; case VIR_DOMAIN_NET_TYPE_NETWORK: VIR_FREE(def->data.network.name); VIR_FREE(def->data.network.portgroup); virDomainActualNetDefFree(def->data.network.actual); def->data.network.actual = NULL; 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_ETHERNET: 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); VIR_FREE(def->virtio); VIR_FREE(def->coalesce); virNetDevIPInfoClear(&def->guestIP); virNetDevIPInfoClear(&def->hostIP); virDomainDeviceInfoClear(&def->info); VIR_FREE(def->filter); virHashFree(def->filterparams); def->filterparams = NULL; virNetDevBandwidthFree(def->bandwidth); def->bandwidth = NULL; virNetDevVlanClear(&def->vlan); } void virDomainNetDefFree(virDomainNetDefPtr def) { if (!def) return; virDomainNetDefClear(def); virObjectUnref(def->privateData); VIR_FREE(def); } const char * virDomainChrSourceDefGetPath(virDomainChrSourceDefPtr chr) { if (!chr) return NULL; switch ((virDomainChrType) chr->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_NMDM: return chr->data.file.path; case VIR_DOMAIN_CHR_TYPE_UNIX: return chr->data.nix.path; case VIR_DOMAIN_CHR_TYPE_TCP: case VIR_DOMAIN_CHR_TYPE_UDP: 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_SPICEPORT: case VIR_DOMAIN_CHR_TYPE_LAST: return NULL; } return NULL; } 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; } VIR_FREE(def->logfile); } /* 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_FILE: case VIR_DOMAIN_CHR_TYPE_PTY: case VIR_DOMAIN_CHR_TYPE_DEV: case VIR_DOMAIN_CHR_TYPE_PIPE: if (src->type == VIR_DOMAIN_CHR_TYPE_FILE) dest->data.file.append = src->data.file.append; dest->data.file.path = g_strdup(src->data.file.path); break; case VIR_DOMAIN_CHR_TYPE_UDP: dest->data.udp.bindHost = g_strdup(src->data.udp.bindHost); dest->data.udp.bindService = g_strdup(src->data.udp.bindService); dest->data.udp.connectHost = g_strdup(src->data.udp.connectHost); dest->data.udp.connectService = g_strdup(src->data.udp.connectService); break; case VIR_DOMAIN_CHR_TYPE_TCP: dest->data.tcp.host = g_strdup(src->data.tcp.host); dest->data.tcp.service = g_strdup(src->data.tcp.service); dest->data.tcp.haveTLS = src->data.tcp.haveTLS; dest->data.tcp.tlsFromConfig = src->data.tcp.tlsFromConfig; dest->data.tcp.reconnect.enabled = src->data.tcp.reconnect.enabled; dest->data.tcp.reconnect.timeout = src->data.tcp.reconnect.timeout; break; case VIR_DOMAIN_CHR_TYPE_UNIX: dest->data.nix.path = g_strdup(src->data.nix.path); dest->data.nix.reconnect.enabled = src->data.nix.reconnect.enabled; dest->data.nix.reconnect.timeout = src->data.nix.reconnect.timeout; break; case VIR_DOMAIN_CHR_TYPE_NMDM: dest->data.nmdm.master = g_strdup(src->data.nmdm.master); dest->data.nmdm.slave = g_strdup(src->data.nmdm.slave); break; } dest->type = src->type; return 0; } static void virDomainChrSourceDefDispose(void *obj) { virDomainChrSourceDefPtr def = obj; size_t i; virDomainChrSourceDefClear(def); virObjectUnref(def->privateData); if (def->seclabels) { for (i = 0; i < def->nseclabels; i++) virSecurityDeviceLabelDefFree(def->seclabels[i]); VIR_FREE(def->seclabels); } } /* 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_FILE: return src->data.file.append == tgt->data.file.append && STREQ_NULLABLE(src->data.file.path, tgt->data.file.path); break; case VIR_DOMAIN_CHR_TYPE_PTY: case VIR_DOMAIN_CHR_TYPE_DEV: 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) && src->data.tcp.reconnect.enabled == tgt->data.tcp.reconnect.enabled && src->data.tcp.reconnect.timeout == tgt->data.tcp.reconnect.timeout; 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) && src->data.nix.reconnect.enabled == tgt->data.nix.reconnect.enabled && src->data.nix.reconnect.timeout == tgt->data.nix.reconnect.timeout; 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) { 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_XEN: case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: VIR_FREE(def->target.name); break; } break; default: break; } virObjectUnref(def->source); virDomainDeviceInfoClear(&def->info); 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: virObjectUnref(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->virtio); VIR_FREE(def); } void virDomainNVRAMDefFree(virDomainNVRAMDefPtr def) { if (!def) return; virDomainDeviceInfoClear(&def->info); VIR_FREE(def); } void virDomainWatchdogDefFree(virDomainWatchdogDefPtr def) { if (!def) return; virDomainDeviceInfoClear(&def->info); VIR_FREE(def); } void virDomainShmemDefFree(virDomainShmemDefPtr def) { if (!def) return; virDomainDeviceInfoClear(&def->info); virDomainChrSourceDefClear(&def->server.chr); VIR_FREE(def->name); VIR_FREE(def); } virDomainVideoDefPtr virDomainVideoDefNew(virDomainXMLOptionPtr xmlopt) { virDomainVideoDefPtr def; if (VIR_ALLOC(def) < 0) return NULL; if (xmlopt && xmlopt->privateData.videoNew && !(def->privateData = xmlopt->privateData.videoNew())) { VIR_FREE(def); return NULL; } def->heads = 1; return def; } void virDomainVideoDefClear(virDomainVideoDefPtr def) { if (!def) return; virDomainDeviceInfoClear(&def->info); if (def->accel) VIR_FREE(def->accel->rendernode); VIR_FREE(def->accel); VIR_FREE(def->virtio); if (def->driver) VIR_FREE(def->driver->vhost_user_binary); VIR_FREE(def->driver); virObjectUnref(def->privateData); memset(def, 0, sizeof(*def)); } void virDomainVideoDefFree(virDomainVideoDefPtr def) { if (!def) return; virDomainVideoDefClear(def); VIR_FREE(def); } virDomainHostdevDefPtr virDomainHostdevDefNew(void) { virDomainHostdevDefPtr def; if (VIR_ALLOC(def) < 0) return NULL; if (VIR_ALLOC(def->info) < 0) goto error; return def; error: VIR_FREE(def->info); VIR_FREE(def); return NULL; } static void virDomainHostdevSubsysSCSIiSCSIClear(virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc) { if (!iscsisrc) return; virObjectUnref(iscsisrc->src); iscsisrc->src = NULL; } static void virDomainHostdevSubsysSCSIClear(virDomainHostdevSubsysSCSIPtr scsisrc) { if (scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) virDomainHostdevSubsysSCSIiSCSIClear(&scsisrc->u.iscsi); else VIR_FREE(scsisrc->u.host.adapter); } void virDomainHostdevDefClear(virDomainHostdevDefPtr def) { if (!def) return; /* Free all resources in the hostdevdef. Currently the only * such resource is the virDomainDeviceInfo. */ /* If there is a parentnet device object, it will handle freeing * def->info. */ if (!def->parentnet) virDomainDeviceInfoFree(def->info); switch (def->mode) { case VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES: switch ((virDomainHostdevCapsType) 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.ifname); virNetDevIPInfoClear(&def->source.caps.u.net.ip); break; case VIR_DOMAIN_HOSTDEV_CAPS_TYPE_LAST: break; } break; case VIR_DOMAIN_HOSTDEV_MODE_SUBSYS: switch ((virDomainHostdevSubsysType) def->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: virDomainHostdevSubsysSCSIClear(&def->source.subsys.u.scsi); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: VIR_FREE(def->source.subsys.u.scsi_host.wwpn); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST: break; } break; } } void virDomainTPMDefFree(virDomainTPMDefPtr def) { if (!def) return; switch (def->type) { case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: virDomainChrSourceDefClear(&def->data.passthrough.source); break; case VIR_DOMAIN_TPM_TYPE_EMULATOR: virDomainChrSourceDefClear(&def->data.emulator.source); VIR_FREE(def->data.emulator.storagepath); VIR_FREE(def->data.emulator.logfile); 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 parentnet device object, it will handle freeing * the memory. */ if (!def->parentnet) VIR_FREE(def); } void virDomainHubDefFree(virDomainHubDefPtr def) { if (!def) return; virDomainDeviceInfoClear(&def->info); VIR_FREE(def); } void virDomainRedirdevDefFree(virDomainRedirdevDefPtr def) { if (!def) return; virObjectUnref(def->source); 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; VIR_FREE(def->nvdimmPath); 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_IOMMU: VIR_FREE(def->data.iommu); break; case VIR_DOMAIN_DEVICE_VSOCK: virDomainVsockDefFree(def->data.vsock); 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); } static bool virDomainIOThreadIDArrayHasPin(virDomainDefPtr def) { size_t i; for (i = 0; i < def->niothreadids; i++) { if (def->iothreadids[i]->cpumask) return true; } return false; } void virDomainIOThreadIDDefFree(virDomainIOThreadIDDefPtr def) { if (!def) return; virBitmapFree(def->cpumask); VIR_FREE(def); } static void virDomainIOThreadIDDefArrayFree(virDomainIOThreadIDDefPtr *def, int nids) { size_t i; if (!def) return; for (i = 0; i < nids; i++) virDomainIOThreadIDDefFree(def[i]); VIR_FREE(def); } static int virDomainIOThreadIDDefArrayInit(virDomainDefPtr def, unsigned int iothreads) { size_t i; ssize_t nxt = -1; virDomainIOThreadIDDefPtr iothrid = NULL; g_autoptr(virBitmap) thrmap = NULL; /* Same value (either 0 or some number), then we have none to fill in or * the iothreadid array was filled from the XML */ if (iothreads == def->niothreadids) return 0; /* iothread's are numbered starting at 1, account for that */ if (!(thrmap = virBitmapNew(iothreads + 1))) return -1; virBitmapSetAll(thrmap); /* Clear 0 since we don't use it, then mark those which are * already provided by the user */ ignore_value(virBitmapClearBit(thrmap, 0)); for (i = 0; i < def->niothreadids; i++) ignore_value(virBitmapClearBit(thrmap, def->iothreadids[i]->iothread_id)); /* resize array */ if (VIR_REALLOC_N(def->iothreadids, iothreads) < 0) return -1; /* Populate iothreadids[] using the set bit number from thrmap */ while (def->niothreadids < iothreads) { if ((nxt = virBitmapNextSetBit(thrmap, nxt)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to populate iothreadids")); return -1; } if (VIR_ALLOC(iothrid) < 0) return -1; iothrid->iothread_id = nxt; iothrid->autofill = true; def->iothreadids[def->niothreadids++] = iothrid; } return 0; } 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); } static void virDomainResctrlMonDefFree(virDomainResctrlMonDefPtr domresmon) { if (!domresmon) return; virBitmapFree(domresmon->vcpus); virObjectUnref(domresmon->instance); VIR_FREE(domresmon); } static void virDomainResctrlDefFree(virDomainResctrlDefPtr resctrl) { size_t i = 0; if (!resctrl) return; for (i = 0; i < resctrl->nmonitors; i++) virDomainResctrlMonDefFree(resctrl->monitors[i]); virObjectUnref(resctrl->alloc); virBitmapFree(resctrl->vcpus); VIR_FREE(resctrl->monitors); VIR_FREE(resctrl); } static void virDomainSEVDefFree(virDomainSEVDefPtr def) { if (!def) return; VIR_FREE(def->dh_cert); VIR_FREE(def->session); VIR_FREE(def); } void virDomainDefFree(virDomainDefPtr def) { size_t i; if (!def) return; virDomainResourceDefFree(def->resource); for (i = 0; i < def->maxvcpus; i++) virDomainVcpuDefFree(def->vcpus[i]); VIR_FREE(def->vcpus); /* 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); for (i = 0; i < def->npanics; i++) virDomainPanicDefFree(def->panics[i]); VIR_FREE(def->panics); VIR_FREE(def->iommu); VIR_FREE(def->idmap.uidmap); VIR_FREE(def->idmap.gidmap); VIR_FREE(def->os.machine); VIR_FREE(def->os.init); for (i = 0; def->os.initargv && def->os.initargv[i]; i++) VIR_FREE(def->os.initargv[i]); VIR_FREE(def->os.initargv); for (i = 0; def->os.initenv && def->os.initenv[i]; i++) { VIR_FREE(def->os.initenv[i]->name); VIR_FREE(def->os.initenv[i]->value); VIR_FREE(def->os.initenv[i]); } VIR_FREE(def->os.initdir); VIR_FREE(def->os.inituser); VIR_FREE(def->os.initgroup); VIR_FREE(def->os.initenv); 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); VIR_FREE(def->os.slic_table); 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); VIR_FREE(def->hyperv_vendor_id); virBlkioDeviceArrayClear(def->blkio.devices, def->blkio.ndevices); VIR_FREE(def->blkio.devices); virDomainWatchdogDefFree(def->watchdog); virDomainMemballoonDefFree(def->memballoon); virDomainNVRAMDefFree(def->nvram); virDomainVsockDefFree(def->vsock); for (i = 0; i < def->mem.nhugepages; i++) virBitmapFree(def->mem.hugepages[i].nodemask); VIR_FREE(def->mem.hugepages); for (i = 0; i < def->nseclabels; i++) virSecurityLabelDefFree(def->seclabels[i]); VIR_FREE(def->seclabels); virCPUDefFree(def->cpu); virDomainIOThreadIDDefArrayFree(def->iothreadids, def->niothreadids); virBitmapFree(def->cputune.emulatorpin); VIR_FREE(def->cputune.emulatorsched); virDomainNumaFree(def->numa); virSysinfoDefFree(def->sysinfo); virDomainRedirFilterDefFree(def->redirfilter); for (i = 0; i < def->nshmems; i++) virDomainShmemDefFree(def->shmems[i]); VIR_FREE(def->shmems); for (i = 0; i < def->nresctrls; i++) virDomainResctrlDefFree(def->resctrls[i]); VIR_FREE(def->resctrls); VIR_FREE(def->keywrap); if (def->namespaceData && def->ns.free) (def->ns.free)(def->namespaceData); virDomainSEVDefFree(def->sev); xmlFreeNode(def->metadata); VIR_FREE(def); } static void virDomainObjDispose(void *obj) { virDomainObjPtr dom = obj; VIR_DEBUG("obj=%p", dom); virCondDestroy(&dom->cond); virDomainDefFree(dom->def); virDomainDefFree(dom->newDef); if (dom->privateDataFreeFunc) (dom->privateDataFreeFunc)(dom->privateData); virDomainSnapshotObjListFree(dom->snapshots); virDomainCheckpointObjListFree(dom->checkpoints); } virDomainObjPtr virDomainObjNew(virDomainXMLOptionPtr xmlopt) { virDomainObjPtr domain; if (virDomainObjInitialize() < 0) return NULL; if (!(domain = virObjectLockableNew(virDomainObjClass))) return NULL; if (virCondInit(&domain->cond) < 0) { virReportSystemError(errno, "%s", _("failed to initialize domain condition")); goto error; } if (xmlopt->privateData.alloc) { domain->privateData = (xmlopt->privateData.alloc)(xmlopt->config.priv); if (!domain->privateData) goto error; domain->privateDataFreeFunc = xmlopt->privateData.free; } if (!(domain->snapshots = virDomainSnapshotObjListNew())) goto error; if (!(domain->checkpoints = virDomainCheckpointObjListNew())) 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; } void virDomainObjAssignDef(virDomainObjPtr domain, virDomainDefPtr def, bool live, virDomainDefPtr *oldDef) { if (oldDef) *oldDef = NULL; if (virDomainObjIsActive(domain)) { if (oldDef) *oldDef = domain->newDef; else virDomainDefFree(domain->newDef); domain->newDef = def; } else { if (live) { /* save current configuration to be restored on domain shutdown */ if (!domain->newDef) domain->newDef = domain->def; else virDomainDefFree(domain->def); domain->def = def; } else { if (oldDef) *oldDef = domain->def; else virDomainDefFree(domain->def); domain->def = def; } } } /** * virDomainObjEndAPI: * @vm: domain object * * Finish working with a domain object in an API. This function * clears whatever was left of a domain that was gathered using * virDomainObjListFindByUUID(). Currently that means only unlocking and * decrementing the reference counter of that domain. And in order to * make sure the caller does not access the domain, the pointer is * cleared. */ void virDomainObjEndAPI(virDomainObjPtr *vm) { if (!*vm) return; virObjectUnlock(*vm); virObjectUnref(*vm); *vm = NULL; } void virDomainObjBroadcast(virDomainObjPtr vm) { virCondBroadcast(&vm->cond); } int virDomainObjWait(virDomainObjPtr vm) { if (virCondWait(&vm->cond, &vm->parent.lock) < 0) { virReportSystemError(errno, "%s", _("failed to wait for domain condition")); return -1; } if (!virDomainObjIsActive(vm)) { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("domain is not running")); return -1; } return 0; } /** * Waits for domain condition to be triggered for a specific period of time. * * Returns: * -1 in case of error * 0 on success * 1 on timeout */ int virDomainObjWaitUntil(virDomainObjPtr vm, unsigned long long whenms) { if (virCondWaitUntil(&vm->cond, &vm->parent.lock, whenms) < 0) { if (errno != ETIMEDOUT) { virReportSystemError(errno, "%s", _("failed to wait for domain condition")); return -1; } return 1; } return 0; } /* * Mark the current 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 * @return 0 on success, -1 on failure */ int virDomainObjSetDefTransient(virCapsPtr caps, virDomainXMLOptionPtr xmlopt, virDomainObjPtr domain, void *parseOpaque) { int ret = -1; if (!domain->persistent) return 0; if (domain->newDef) return 0; if (!(domain->newDef = virDomainDefCopy(domain->def, caps, xmlopt, parseOpaque, false))) goto out; ret = 0; out: return ret; } /* * Remove the running configuration and replace it with the persistent one. * * @param domain domain object pointer */ void virDomainObjRemoveTransientDef(virDomainObjPtr domain) { if (!domain->newDef) return; virDomainDefFree(domain->def); domain->def = domain->newDef; domain->def->id = -1; domain->newDef = NULL; } /* * 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, void *parseOpaque) { if (virDomainObjIsActive(domain) && virDomainObjSetDefTransient(caps, xmlopt, domain, parseOpaque) < 0) return NULL; if (domain->newDef) return domain->newDef; else return domain->def; } /** * virDomainObjUpdateModificationImpact: * * @vm: domain object * @flags: flags to update the modification impact on * * Resolves virDomainModificationImpact flags in @flags so that they correctly * apply to the actual state of @vm. @flags may be modified after call to this * function. * * Returns 0 on success if @flags point to a valid combination for @vm or -1 on * error. */ int virDomainObjUpdateModificationImpact(virDomainObjPtr vm, unsigned int *flags) { bool isActive = virDomainObjIsActive(vm); if ((*flags & (VIR_DOMAIN_AFFECT_LIVE | VIR_DOMAIN_AFFECT_CONFIG)) == VIR_DOMAIN_AFFECT_CURRENT) { if (isActive) *flags |= VIR_DOMAIN_AFFECT_LIVE; else *flags |= VIR_DOMAIN_AFFECT_CONFIG; } if (!isActive && (*flags & VIR_DOMAIN_AFFECT_LIVE)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); return -1; } if (!vm->persistent && (*flags & VIR_DOMAIN_AFFECT_CONFIG)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("transient domains do not have any " "persistent config")); return -1; } return 0; } /** * virDomainObjGetDefs: * * @vm: domain object * @flags: for virDomainModificationImpact * @liveDef: Set to the pointer to the live definition of @vm. * @persDef: Set to the pointer to the config definition of @vm. * * Helper function to resolve @flags and retrieve correct domain pointer * objects. This function should be used only when the hypervisor driver always * creates vm->newDef once the vm is started. (qemu driver does that) * * If @liveDef or @persDef are set it implies that @flags request modification * of thereof. * * Returns 0 on success and sets @liveDef and @persDef; -1 if @flags are * inappropriate. */ int virDomainObjGetDefs(virDomainObjPtr vm, unsigned int flags, virDomainDefPtr *liveDef, virDomainDefPtr *persDef) { if (liveDef) *liveDef = NULL; if (persDef) *persDef = NULL; if (virDomainObjUpdateModificationImpact(vm, &flags) < 0) return -1; if (virDomainObjIsActive(vm)) { if (liveDef && (flags & VIR_DOMAIN_AFFECT_LIVE)) *liveDef = vm->def; if (persDef && (flags & VIR_DOMAIN_AFFECT_CONFIG)) *persDef = vm->newDef; } else { if (persDef) *persDef = vm->def; } return 0; } /** * virDomainObjGetOneDefState: * * @vm: Domain object * @flags: for virDomainModificationImpact * @live: set to true if live config was returned (may be omitted) * * Helper function to resolve @flags and return the correct domain pointer * object. This function returns one of @vm->def or @vm->persistentDef * according to @flags. @live is set to true if the live vm config will be * returned. This helper should be used only in APIs that guarantee * that @flags contains exactly one of VIR_DOMAIN_AFFECT_LIVE or * VIR_DOMAIN_AFFECT_CONFIG and not both. * * Returns the correct definition pointer or NULL on error. */ virDomainDefPtr virDomainObjGetOneDefState(virDomainObjPtr vm, unsigned int flags, bool *live) { if (flags & VIR_DOMAIN_AFFECT_LIVE && flags & VIR_DOMAIN_AFFECT_CONFIG) { virReportInvalidArg(flags, "%s", _("Flags 'VIR_DOMAIN_AFFECT_LIVE' and " "'VIR_DOMAIN_AFFECT_CONFIG' are mutually " "exclusive")); return NULL; } if (virDomainObjUpdateModificationImpact(vm, &flags) < 0) return NULL; if (live) { if (flags & VIR_DOMAIN_AFFECT_LIVE) *live = true; else *live = false; } if (virDomainObjIsActive(vm) && flags & VIR_DOMAIN_AFFECT_CONFIG) return vm->newDef; else return vm->def; } /** * virDomainObjGetOneDef: * * @vm: Domain object * @flags: for virDomainModificationImpact * * Helper function to resolve @flags and return the correct domain pointer * object. This function returns one of @vm->def or @vm->persistentDef * according to @flags. This helper should be used only in APIs that guarantee * that @flags contains exactly one of VIR_DOMAIN_AFFECT_LIVE or * VIR_DOMAIN_AFFECT_CONFIG and not both. * * Returns the correct definition pointer or NULL on error. */ virDomainDefPtr virDomainObjGetOneDef(virDomainObjPtr vm, unsigned int flags) { return virDomainObjGetOneDefState(vm, flags, NULL); } 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; case VIR_DOMAIN_DEVICE_VSOCK: return &device->data.vsock->info; /* The following devices do not contain virDomainDeviceInfo */ case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_GRAPHICS: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_LAST: case VIR_DOMAIN_DEVICE_NONE: break; } return NULL; } /** * virDomainDeviceSetData * @device: virDomainDeviceDefPtr with ->type filled in * @devicedata: *DefPtr data for a device. Ex: virDomainDiskDefPtr * * Set the data.X variable for the device->type value. Basically * a mapping of virDomainDeviceType to the associated name in * the virDomainDeviceDef union */ void virDomainDeviceSetData(virDomainDeviceDefPtr device, void *devicedata) { switch ((virDomainDeviceType) device->type) { case VIR_DOMAIN_DEVICE_DISK: device->data.disk = devicedata; break; case VIR_DOMAIN_DEVICE_NET: device->data.net = devicedata; break; case VIR_DOMAIN_DEVICE_SOUND: device->data.sound = devicedata; break; case VIR_DOMAIN_DEVICE_HOSTDEV: device->data.hostdev = devicedata; break; case VIR_DOMAIN_DEVICE_VIDEO: device->data.video = devicedata; break; case VIR_DOMAIN_DEVICE_CONTROLLER: device->data.controller = devicedata; break; case VIR_DOMAIN_DEVICE_GRAPHICS: device->data.graphics = devicedata; break; case VIR_DOMAIN_DEVICE_SMARTCARD: device->data.smartcard = devicedata; break; case VIR_DOMAIN_DEVICE_CHR: device->data.chr = devicedata; break; case VIR_DOMAIN_DEVICE_INPUT: device->data.input = devicedata; break; case VIR_DOMAIN_DEVICE_FS: device->data.fs = devicedata; break; case VIR_DOMAIN_DEVICE_WATCHDOG: device->data.watchdog = devicedata; break; case VIR_DOMAIN_DEVICE_MEMBALLOON: device->data.memballoon = devicedata; break; case VIR_DOMAIN_DEVICE_RNG: device->data.rng = devicedata; break; case VIR_DOMAIN_DEVICE_NVRAM: device->data.nvram = devicedata; break; case VIR_DOMAIN_DEVICE_HUB: device->data.hub = devicedata; break; case VIR_DOMAIN_DEVICE_SHMEM: device->data.shmem = devicedata; break; case VIR_DOMAIN_DEVICE_TPM: device->data.tpm = devicedata; break; case VIR_DOMAIN_DEVICE_PANIC: device->data.panic = devicedata; break; case VIR_DOMAIN_DEVICE_MEMORY: device->data.memory = devicedata; break; case VIR_DOMAIN_DEVICE_REDIRDEV: device->data.redirdev = devicedata; break; case VIR_DOMAIN_DEVICE_VSOCK: device->data.vsock = devicedata; break; case VIR_DOMAIN_DEVICE_IOMMU: device->data.iommu = devicedata; break; case VIR_DOMAIN_DEVICE_LEASE: device->data.lease = devicedata; break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; } } static int virDomainDefHasDeviceAddressIterator(virDomainDefPtr def G_GNUC_UNUSED, virDomainDeviceDefPtr dev G_GNUC_UNUSED, virDomainDeviceInfoPtr info, void *opaque) { virDomainDeviceInfoPtr needle = opaque; /* break iteration if the info was found */ if (virDomainDeviceInfoAddressIsEqual(info, needle)) return -1; return 0; } static bool virDomainSkipBackcompatConsole(virDomainDefPtr def, size_t idx, bool all) { virDomainChrDefPtr console = def->consoles[idx]; if (!all && idx == 0 && (console->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL || console->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE) && def->os.type == VIR_DOMAIN_OSTYPE_HVM) { return true; } return false; } enum { DOMAIN_DEVICE_ITERATE_ALL_CONSOLES = 1 << 0, DOMAIN_DEVICE_ITERATE_MISSING_INFO = 1 << 1, } virDomainDeviceIterateFlags; /* * Iterates over domain devices calling @cb on each device. The default * behaviour can be altered with virDomainDeviceIterateFlags. */ static int virDomainDeviceInfoIterateInternal(virDomainDefPtr def, virDomainDeviceInfoCallback cb, unsigned int iteratorFlags, void *opaque) { size_t i; int rc; virDomainDeviceDef device; device.type = VIR_DOMAIN_DEVICE_DISK; for (i = 0; i < def->ndisks; i++) { device.data.disk = def->disks[i]; if ((rc = cb(def, &device, &def->disks[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_NET; for (i = 0; i < def->nnets; i++) { device.data.net = def->nets[i]; if ((rc = cb(def, &device, &def->nets[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_SOUND; for (i = 0; i < def->nsounds; i++) { device.data.sound = def->sounds[i]; if ((rc = cb(def, &device, &def->sounds[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_HOSTDEV; for (i = 0; i < def->nhostdevs; i++) { device.data.hostdev = def->hostdevs[i]; if ((rc = cb(def, &device, def->hostdevs[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_VIDEO; for (i = 0; i < def->nvideos; i++) { device.data.video = def->videos[i]; if ((rc = cb(def, &device, &def->videos[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_CONTROLLER; for (i = 0; i < def->ncontrollers; i++) { device.data.controller = def->controllers[i]; if ((rc = cb(def, &device, &def->controllers[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_SMARTCARD; for (i = 0; i < def->nsmartcards; i++) { device.data.smartcard = def->smartcards[i]; if ((rc = cb(def, &device, &def->smartcards[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_CHR; for (i = 0; i < def->nserials; i++) { device.data.chr = def->serials[i]; if ((rc = cb(def, &device, &def->serials[i]->info, opaque)) != 0) return rc; } for (i = 0; i < def->nparallels; i++) { device.data.chr = def->parallels[i]; if ((rc = cb(def, &device, &def->parallels[i]->info, opaque)) != 0) return rc; } for (i = 0; i < def->nchannels; i++) { device.data.chr = def->channels[i]; if ((rc = cb(def, &device, &def->channels[i]->info, opaque)) != 0) return rc; } for (i = 0; i < def->nconsoles; i++) { bool all = iteratorFlags & DOMAIN_DEVICE_ITERATE_ALL_CONSOLES; if (virDomainSkipBackcompatConsole(def, i, all)) continue; device.data.chr = def->consoles[i]; if ((rc = cb(def, &device, &def->consoles[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_INPUT; for (i = 0; i < def->ninputs; i++) { device.data.input = def->inputs[i]; if ((rc = cb(def, &device, &def->inputs[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_FS; for (i = 0; i < def->nfss; i++) { device.data.fs = def->fss[i]; if ((rc = cb(def, &device, &def->fss[i]->info, opaque)) != 0) return rc; } if (def->watchdog) { device.type = VIR_DOMAIN_DEVICE_WATCHDOG; device.data.watchdog = def->watchdog; if ((rc = cb(def, &device, &def->watchdog->info, opaque)) != 0) return rc; } if (def->memballoon) { device.type = VIR_DOMAIN_DEVICE_MEMBALLOON; device.data.memballoon = def->memballoon; if ((rc = cb(def, &device, &def->memballoon->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_RNG; for (i = 0; i < def->nrngs; i++) { device.data.rng = def->rngs[i]; if ((rc = cb(def, &device, &def->rngs[i]->info, opaque)) != 0) return rc; } if (def->nvram) { device.type = VIR_DOMAIN_DEVICE_NVRAM; device.data.nvram = def->nvram; if ((rc = cb(def, &device, &def->nvram->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_HUB; for (i = 0; i < def->nhubs; i++) { device.data.hub = def->hubs[i]; if ((rc = cb(def, &device, &def->hubs[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_SHMEM; for (i = 0; i < def->nshmems; i++) { device.data.shmem = def->shmems[i]; if ((rc = cb(def, &device, &def->shmems[i]->info, opaque)) != 0) return rc; } if (def->tpm) { device.type = VIR_DOMAIN_DEVICE_TPM; device.data.tpm = def->tpm; if ((rc = cb(def, &device, &def->tpm->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_PANIC; for (i = 0; i < def->npanics; i++) { device.data.panic = def->panics[i]; if ((rc = cb(def, &device, &def->panics[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_MEMORY; for (i = 0; i < def->nmems; i++) { device.data.memory = def->mems[i]; if ((rc = cb(def, &device, &def->mems[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_REDIRDEV; for (i = 0; i < def->nredirdevs; i++) { device.data.redirdev = def->redirdevs[i]; if ((rc = cb(def, &device, &def->redirdevs[i]->info, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_VSOCK; if (def->vsock) { device.data.vsock = def->vsock; if ((rc = cb(def, &device, &def->vsock->info, opaque)) != 0) return rc; } /* If the flag below is set, make sure @cb can handle @info being NULL */ if (iteratorFlags & DOMAIN_DEVICE_ITERATE_MISSING_INFO) { device.type = VIR_DOMAIN_DEVICE_GRAPHICS; for (i = 0; i < def->ngraphics; i++) { device.data.graphics = def->graphics[i]; if ((rc = cb(def, &device, NULL, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_LEASE; for (i = 0; i < def->nleases; i++) { device.data.lease = def->leases[i]; if ((rc = cb(def, &device, NULL, opaque)) != 0) return rc; } device.type = VIR_DOMAIN_DEVICE_IOMMU; if (def->iommu) { device.data.iommu = def->iommu; if ((rc = cb(def, &device, NULL, opaque)) != 0) return rc; } } /* 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: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_VSOCK: break; } #endif return 0; } int virDomainDeviceInfoIterate(virDomainDefPtr def, virDomainDeviceInfoCallback cb, void *opaque) { return virDomainDeviceInfoIterateInternal(def, cb, 0, opaque); } bool virDomainDefHasDeviceAddress(virDomainDefPtr def, virDomainDeviceInfoPtr info) { if (virDomainDeviceInfoIterateInternal(def, virDomainDefHasDeviceAddressIterator, DOMAIN_DEVICE_ITERATE_ALL_CONSOLES, 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 (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; } static int virDomainDefRejectDuplicatePanics(virDomainDefPtr def) { bool exists[VIR_DOMAIN_PANIC_MODEL_LAST]; size_t i; for (i = 0; i < VIR_DOMAIN_PANIC_MODEL_LAST; i++) exists[i] = false; for (i = 0; i < def->npanics; i++) { virDomainPanicModel model = def->panics[i]->model; if (exists[model]) { virReportError(VIR_ERR_XML_ERROR, _("Multiple panic devices with model '%s'"), virDomainPanicModelTypeToString(model)); return -1; } exists[model] = true; } return 0; } static int virDomainDefPostParseMemory(virDomainDefPtr def, unsigned int parseFlags) { size_t i; unsigned long long numaMemory = 0; unsigned long long hotplugMemory = 0; /* Attempt to infer the initial memory size from the sum NUMA memory sizes * in case ABI updates are allowed or the element wasn't specified */ if (def->mem.total_memory == 0 || parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE || parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE_MIGRATION) numaMemory = virDomainNumaGetMemorySize(def->numa); /* calculate the sizes of hotplug memory */ for (i = 0; i < def->nmems; i++) hotplugMemory += def->mems[i]->size; if (numaMemory) { /* update the sizes in XML if nothing was set in the XML or ABI update * is supported*/ virDomainDefSetMemoryTotal(def, numaMemory + hotplugMemory); } else { /* verify that the sum of memory modules doesn't exceed the total * memory. This is necessary for virDomainDefGetMemoryInitial to work * properly. */ if (hotplugMemory > def->mem.total_memory) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Total size of memory devices exceeds the total " "memory size")); 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 > virDomainDefGetMemoryTotal(def) || def->mem.cur_balloon == 0) def->mem.cur_balloon = virDomainDefGetMemoryTotal(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 < virDomainDefGetMemoryTotal(def)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("maximum memory size must be equal or greater than " "the actual memory size")); return -1; } return 0; } static void virDomainDefPostParseOs(virDomainDefPtr def) { if (!def->os.loader) return; if (def->os.loader->path && def->os.loader->type == VIR_DOMAIN_LOADER_TYPE_NONE) { /* By default, loader is type of 'rom' */ def->os.loader->type = VIR_DOMAIN_LOADER_TYPE_ROM; } } static void virDomainDefPostParseMemtune(virDomainDefPtr def) { size_t i; if (virDomainNumaGetNodeCount(def->numa) == 0) { /* If guest NUMA is not configured and any hugepage page has nodemask * set to "0" free and clear that nodemas, otherwise we would rise * an error that there is no guest NUMA node configured. */ for (i = 0; i < def->mem.nhugepages; i++) { ssize_t nextBit; if (!def->mem.hugepages[i].nodemask) continue; nextBit = virBitmapNextSetBit(def->mem.hugepages[i].nodemask, 0); if (nextBit < 0) { virBitmapFree(def->mem.hugepages[i].nodemask); def->mem.hugepages[i].nodemask = NULL; } } } } static int virDomainDefAddConsoleCompat(virDomainDefPtr def) { size_t i; /* * Some really crazy backcompat stuff for consoles * * Historically the first (and only) '' element in an HVM guest * was treated as being an alias for a device. * * So if we see that this console device should be a serial device, then we * move the config over to def->serials[0] (or discard it if that already * exists). However, given console can already be filled with aliased data * of def->serials[0]. Keep it then. * * We then fill def->consoles[0] with a stub just so we get sequencing * correct for consoles > 0 */ /* Only the first console (if there are any) can be of type serial, * verify that no other console is of type serial */ for (i = 1; i < def->nconsoles; i++) { virDomainChrDefPtr cons = def->consoles[i]; if (cons->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Only the first console can be a serial port")); return -1; } } if (def->nconsoles > 0 && def->os.type == VIR_DOMAIN_OSTYPE_HVM && (def->consoles[0]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL || def->consoles[0]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE)) { /* If there isn't a corresponding serial port: * - create one and set, the console to be an alias for it * * If there is a corresponding serial port: * - Check if the source definition is equal: * - if yes: leave it as-is * - if no: change the console to be alias of the serial port */ /* create the serial port definition from the console definition */ if (def->nserials == 0) { if (VIR_APPEND_ELEMENT(def->serials, def->nserials, def->consoles[0]) < 0) return -1; /* modify it to be a serial port */ def->serials[0]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL; def->serials[0]->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE; 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 (!(def->consoles[0] = virDomainChrDefNew(NULL))) return -1; /* Create an console alias for the serial port */ def->consoles[0]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE; def->consoles[0]->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; } } else if (def->os.type == VIR_DOMAIN_OSTYPE_HVM && def->nserials > 0 && def->serials[0]->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL) { switch ((virDomainChrSerialTargetType) def->serials[0]->targetType) { case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA: case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO: case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM: case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP: case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE: { /* Create a stub console to match the serial port. * console[0] either does not exist * or has a different type than SERIAL or NONE. */ virDomainChrDefPtr chr; if (!(chr = virDomainChrDefNew(NULL))) return -1; if (VIR_INSERT_ELEMENT(def->consoles, 0, def->nconsoles, chr) < 0) { virDomainChrDefFree(chr); return -1; } def->consoles[0]->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE; def->consoles[0]->targetType = VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL; break; } case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI: case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB: case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST: /* Nothing to do */ break; } } return 0; } static int virDomainDefPostParseTimer(virDomainDefPtr def) { size_t i; /* 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; } } } return 0; } static void virDomainDefPostParseGraphics(virDomainDef *def) { size_t i; for (i = 0; i < def->ngraphics; i++) { virDomainGraphicsDefPtr graphics = def->graphics[i]; /* If spice graphics is configured without ports and with autoport='no' * then we start qemu with Spice to not listen anywhere. Let's convert * this configuration to the new listen type='none' which does the * same. */ if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { virDomainGraphicsListenDefPtr glisten = &graphics->listens[0]; if (glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS && graphics->data.spice.port == 0 && graphics->data.spice.tlsPort == 0 && !graphics->data.spice.autoport) { VIR_FREE(glisten->address); glisten->type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE; } } } } /** * virDomainDriveAddressIsUsedByDisk: * @def: domain definition containing the disks to check * @bus_type: bus type * @addr: address to check for duplicates * * Return true if any disk is already using the given address on the * given bus, false otherwise. */ static bool virDomainDriveAddressIsUsedByDisk(const virDomainDef *def, virDomainDiskBus bus_type, const virDomainDeviceDriveAddress *addr) { virDomainDiskDefPtr disk; size_t i; for (i = 0; i < def->ndisks; i++) { disk = def->disks[i]; if (disk->bus != bus_type || disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) continue; if (disk->info.addr.drive.controller == addr->controller && disk->info.addr.drive.unit == addr->unit && disk->info.addr.drive.bus == addr->bus && disk->info.addr.drive.target == addr->target) return true; } return false; } /** * virDomainDriveAddressIsUsedByHostdev: * @def: domain definition containing the hostdevs to check * @type: bus type * @addr: address to check for duplicates * * Return true if any hostdev is already using the given address on the * given bus, false otherwise. */ static bool virDomainDriveAddressIsUsedByHostdev(const virDomainDef *def, virDomainHostdevSubsysType type, const virDomainDeviceDriveAddress *addr) { virDomainHostdevDefPtr hostdev; size_t i; for (i = 0; i < def->nhostdevs; i++) { hostdev = def->hostdevs[i]; if (hostdev->source.subsys.type != type || hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) continue; if (hostdev->info->addr.drive.controller == addr->controller && hostdev->info->addr.drive.unit == addr->unit && hostdev->info->addr.drive.bus == addr->bus && hostdev->info->addr.drive.target == addr->target) return true; } return false; } /** * virDomainSCSIDriveAddressIsUsed: * @def: domain definition to check against * @addr: address to check for duplicates * * Return true if the SCSI drive address is already in use, false * otherwise. */ bool virDomainSCSIDriveAddressIsUsed(const virDomainDef *def, const virDomainDeviceDriveAddress *addr) { const virDomainControllerDef *cont; cont = virDomainDeviceFindSCSIController(def, addr); if (cont) { int max = -1; int reserved = -1; /* Different controllers have different limits. These limits here are * taken from QEMU source code, but nevertheless they should apply to * other hypervisors too. */ switch ((virDomainControllerModelSCSI) cont->model) { case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI: case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL: case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL: max = 16383; break; case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI: max = 31; reserved = 7; break; case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068: max = 1; break; case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078: max = 255; break; case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC: reserved = 7; break; case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VMPVSCSI: reserved = 7; break; case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_BUSLOGIC: reserved = 7; break; case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT: case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AUTO: case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LAST: break; } if (max != -1 && addr->unit >= max) return true; if (reserved != -1 && addr->unit == reserved) return true; } if (virDomainDriveAddressIsUsedByDisk(def, VIR_DOMAIN_DISK_BUS_SCSI, addr) || virDomainDriveAddressIsUsedByHostdev(def, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI, addr)) return true; return false; } /* Find out the next usable "unit" of a specific controller */ static int virDomainControllerSCSINextUnit(const virDomainDef *def, unsigned int max_unit, unsigned int controller) { size_t i; for (i = 0; i < max_unit; i++) { /* Default to assigning addresses using bus = target = 0 */ const virDomainDeviceDriveAddress addr = {controller, 0, 0, i}; if (!virDomainSCSIDriveAddressIsUsed(def, &addr)) 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; int controller = 0; unsigned int max_unit; if (xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_WIDE_SCSI) max_unit = SCSI_WIDE_BUS_MAX_CONT_UNIT; else max_unit = SCSI_NARROW_BUS_MAX_CONT_UNIT; /* NB: Do not attempt calling virDomainDefMaybeAddController to * automagically add a "new" controller. Doing so will result in * qemuDomainFindOrCreateSCSIDiskController "finding" the controller * in the domain def list and thus not hotplugging the controller as * well as the hostdev in the event that there are either no SCSI * controllers defined or there was no space on an existing one. * * Because we cannot add a controller, then we should not walk the * defined controllers list in order to find empty space. Doing * so fails to return the valid next unit number for the 2nd * hostdev being added to the as yet to be created controller. */ do { next_unit = virDomainControllerSCSINextUnit(def, max_unit, controller); if (next_unit < 0) controller++; } while (next_unit < 0); 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; } /** * virDomainPostParseCheckISCSIPath * @srcpath: Source path read (a/k/a, IQN) either disk or hostdev * * The details of an IQN is defined by RFC 3720 and 3721, but * we just need to make sure there's a lun provided. If not * provided, then default to zero. For an ISCSI LUN that is * is provided by /dev/disk/by-path/... , then that path will * have the specific lun requested. * * Returns 0 on success, -1 on failure */ static int virDomainPostParseCheckISCSIPath(char **srcpath) { char *path = NULL; if (strchr(*srcpath, '/')) return 0; if (virAsprintf(&path, "%s/0", *srcpath) < 0) return -1; VIR_FREE(*srcpath); *srcpath = g_steal_pointer(&path); return 0; } static int virDomainHostdevDefPostParse(virDomainHostdevDefPtr dev, const virDomainDef *def, virDomainXMLOptionPtr xmlopt) { virDomainHostdevSubsysSCSIPtr scsisrc; if (dev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) return 0; switch (dev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: scsisrc = &dev->source.subsys.u.scsi; if (scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) { virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc = &scsisrc->u.iscsi; if (virDomainPostParseCheckISCSIPath(&iscsisrc->src->path) < 0) return -1; } if (dev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && virDomainHostdevAssignAddress(xmlopt, def, dev) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Cannot assign SCSI host device address")); return -1; } else { /* Ensure provided address doesn't conflict with existing * scsi disk drive address */ virDomainDeviceDriveAddressPtr addr = &dev->info->addr.drive; if (virDomainDriveAddressIsUsedByDisk(def, VIR_DOMAIN_DISK_BUS_SCSI, addr)) { virReportError(VIR_ERR_XML_ERROR, _("SCSI host address controller='%u' " "bus='%u' target='%u' unit='%u' in " "use by a SCSI disk"), addr->controller, addr->bus, addr->target, addr->unit); return -1; } } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: { int model = dev->source.subsys.u.mdev.model; if (dev->info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) return 0; if ((model == VIR_MDEV_MODEL_TYPE_VFIO_PCI && dev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) || (model == VIR_MDEV_MODEL_TYPE_VFIO_CCW && dev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)) { virReportError(VIR_ERR_XML_ERROR, _("Unsupported address type '%s' with mediated " "device model '%s'"), virDomainDeviceAddressTypeToString(dev->info->type), virMediatedDeviceModelTypeToString(model)); return -1; } } case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST: break; } return 0; } static int virDomainCheckVirtioOptions(virDomainVirtioOptionsPtr virtio) { if (!virtio) return 0; if (virtio->iommu != VIR_TRISTATE_SWITCH_ABSENT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("iommu driver option is only supported " "for virtio devices")); return -1; } if (virtio->ats != VIR_TRISTATE_SWITCH_ABSENT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("ats driver option is only supported " "for virtio devices")); return -1; } return 0; } static int virDomainChrDefPostParse(virDomainChrDefPtr chr, const virDomainDef *def) { 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; } return 0; } static int virDomainRNGDefPostParse(virDomainRNGDefPtr rng) { /* set default path for virtio-rng "random" backend to /dev/random */ if (rng->backend == VIR_DOMAIN_RNG_BACKEND_RANDOM && !rng->source.file) { rng->source.file = g_strdup("/dev/random"); } return 0; } static int virDomainDiskDefPostParse(virDomainDiskDefPtr disk, const virDomainDef *def, virDomainXMLOptionPtr xmlopt) { /* 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 (disk->src->type == VIR_STORAGE_TYPE_NETWORK && disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI && virDomainPostParseCheckISCSIPath(&disk->src->path) < 0) { return -1; } if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO && virDomainCheckVirtioOptions(disk->virtio) < 0) { return -1; } if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && virDomainDiskDefAssignAddress(xmlopt, disk, def) < 0) { return -1; } return 0; } static int virDomainVideoDefPostParse(virDomainVideoDefPtr video, const virDomainDef *def) { /* Fill out (V)RAM if the driver-specific callback did not do so */ if (video->ram == 0 && video->type == VIR_DOMAIN_VIDEO_TYPE_QXL) video->ram = virDomainVideoDefaultRAM(def, video->type); if (video->vram == 0) video->vram = virDomainVideoDefaultRAM(def, video->type); video->ram = VIR_ROUND_UP_POWER_OF_TWO(video->ram); video->vram = VIR_ROUND_UP_POWER_OF_TWO(video->vram); return 0; } static int virDomainControllerDefPostParse(virDomainControllerDefPtr cdev) { if (cdev->iothread && cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI && cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL && cdev->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'iothread' attribute only supported for " "virtio scsi controllers")); return -1; } return 0; } static int virDomainNetDefPostParse(virDomainNetDefPtr net) { if (!virDomainNetIsVirtioModel(net) && virDomainCheckVirtioOptions(net->virtio) < 0) { return -1; } return 0; } static int virDomainVsockDefPostParse(virDomainVsockDefPtr vsock) { if (vsock->auto_cid == VIR_TRISTATE_BOOL_ABSENT) { if (vsock->guest_cid != 0) vsock->auto_cid = VIR_TRISTATE_BOOL_NO; else vsock->auto_cid = VIR_TRISTATE_BOOL_YES; } return 0; } static int virDomainDeviceDefPostParseCommon(virDomainDeviceDefPtr dev, const virDomainDef *def, virCapsPtr caps G_GNUC_UNUSED, unsigned int parseFlags G_GNUC_UNUSED, virDomainXMLOptionPtr xmlopt) { int ret = -1; switch ((virDomainDeviceType)dev->type) { case VIR_DOMAIN_DEVICE_CHR: ret = virDomainChrDefPostParse(dev->data.chr, def); break; case VIR_DOMAIN_DEVICE_RNG: ret = virDomainRNGDefPostParse(dev->data.rng); break; case VIR_DOMAIN_DEVICE_DISK: ret = virDomainDiskDefPostParse(dev->data.disk, def, xmlopt); break; case VIR_DOMAIN_DEVICE_VIDEO: ret = virDomainVideoDefPostParse(dev->data.video, def); break; case VIR_DOMAIN_DEVICE_HOSTDEV: ret = virDomainHostdevDefPostParse(dev->data.hostdev, def, xmlopt); break; case VIR_DOMAIN_DEVICE_CONTROLLER: ret = virDomainControllerDefPostParse(dev->data.controller); break; case VIR_DOMAIN_DEVICE_NET: ret = virDomainNetDefPostParse(dev->data.net); break; case VIR_DOMAIN_DEVICE_VSOCK: ret = virDomainVsockDefPostParse(dev->data.vsock); break; case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_INPUT: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_WATCHDOG: case VIR_DOMAIN_DEVICE_GRAPHICS: case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_REDIRDEV: case VIR_DOMAIN_DEVICE_SMARTCARD: 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_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: ret = 0; break; case VIR_DOMAIN_DEVICE_NONE: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unexpected VIR_DOMAIN_DEVICE_NONE")); break; case VIR_DOMAIN_DEVICE_LAST: default: virReportEnumRangeError(virDomainDeviceType, dev->type); break; } return ret; } /** * virDomainDefRemoveOfflineVcpuPin: * @def: domain definition * * This function removes vcpu pinning information from offline vcpus. This is * designed to be used for drivers which don't support offline vcpupin. */ static void virDomainDefRemoveOfflineVcpuPin(virDomainDefPtr def) { size_t i; virDomainVcpuDefPtr vcpu; for (i = 0; i < virDomainDefGetVcpusMax(def); i++) { vcpu = virDomainDefGetVcpu(def, i); if (vcpu && !vcpu->online && vcpu->cpumask) { virBitmapFree(vcpu->cpumask); vcpu->cpumask = NULL; VIR_WARN("Ignoring unsupported vcpupin for offline vcpu '%zu'", i); } } } static void virDomainAssignControllerIndexes(virDomainDefPtr def) { /* the index attribute of a controller is optional in the XML, but * is required to be valid at any time after parse. When no index * is provided for a controller, assign one automatically by * looking at what indexes are already used for that controller * type in the domain - the unindexed controller gets the lowest * unused index. */ size_t outer; for (outer = 0; outer < def->ncontrollers; outer++) { virDomainControllerDefPtr cont = def->controllers[outer]; virDomainControllerDefPtr prev = NULL; size_t inner; if (cont->idx != -1) continue; if (outer > 0 && IS_USB2_CONTROLLER(cont)) { /* USB2 controllers are the only exception to the simple * "assign the lowest unused index". A group of USB2 * "companions" should all be at the same index as other * USB2 controllers in the group, but only do this * automatically if it appears to be the intent. To prove * intent: the USB controller on the list just prior to * this one must also be a USB2 controller, and there must * not yet be a controller with the exact same model of * this one and the same index as the previously added * controller (e.g., if this controller is a UHCI1, then * the previous controller must be an EHCI1 or a UHCI[23], * and there must not already be a UHCI1 controller with * the same index as the previous controller). If all of * these are satisfied, set this controller to the same * index as the previous controller. */ int prevIdx; prevIdx = outer - 1; while (prevIdx >= 0 && def->controllers[prevIdx]->type != VIR_DOMAIN_CONTROLLER_TYPE_USB) prevIdx--; if (prevIdx >= 0) prev = def->controllers[prevIdx]; /* if the last USB controller isn't USB2, that breaks * the chain, so we need a new index for this new * controller */ if (prev && !IS_USB2_CONTROLLER(prev)) prev = NULL; /* if prev != NULL, we've found a potential index to * use. Make sure this index isn't already used by an * existing USB2 controller of the same model as the new * one. */ for (inner = 0; prev && inner < def->ncontrollers; inner++) { if (def->controllers[inner]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && def->controllers[inner]->idx == prev->idx && def->controllers[inner]->model == cont->model) { /* we already have a controller of this model with * the proposed index, so we need to move to a new * index for this controller */ prev = NULL; } } if (prev) cont->idx = prev->idx; } /* if none of the above applied, prev will be NULL */ if (!prev) cont->idx = virDomainControllerFindUnusedIndex(def, cont->type); } } #define UNSUPPORTED(FEATURE) (!((FEATURE) & xmlopt->config.features)) /** * virDomainDefPostParseCheckFeatures: * @def: domain definition * @xmlopt: XML parser option object * * This function checks that the domain configuration is supported according to * the supported features for a given hypervisor. See virDomainDefFeatures and * virDomainDefParserConfig. * * Returns 0 on success and -1 on error with an appropriate libvirt error. */ static int virDomainDefPostParseCheckFeatures(virDomainDefPtr def, virDomainXMLOptionPtr xmlopt) { if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG) && virDomainDefCheckUnsupportedMemoryHotplug(def) < 0) return -1; if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_OFFLINE_VCPUPIN)) virDomainDefRemoveOfflineVcpuPin(def); if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_NAME_SLASH)) { if (def->name && strchr(def->name, '/')) { virReportError(VIR_ERR_XML_ERROR, _("name %s cannot contain '/'"), def->name); return -1; } } if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_INDIVIDUAL_VCPUS) && def->individualvcpus) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("individual CPU state configuration is not supported")); return -1; } return 0; } /** * virDomainDeviceDefPostParseCheckFeatures: * @dev: device definition * @xmlopt: XML parser option object * * This function checks that the device configuration is supported according to * the supported features for a given hypervisor. See virDomainDefFeatures and * virDomainDefParserConfig. * * Returns 0 on success and -1 on error with an appropriate libvirt error. */ static int virDomainDeviceDefPostParseCheckFeatures(virDomainDeviceDefPtr dev, virDomainXMLOptionPtr xmlopt) { if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_MEMORY_HOTPLUG) && virDomainDeviceDefCheckUnsupportedMemoryDevice(dev) < 0) return -1; if (UNSUPPORTED(VIR_DOMAIN_DEF_FEATURE_NET_MODEL_STRING) && dev->type == VIR_DOMAIN_DEVICE_NET && dev->data.net->modelstr) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("driver does not support net model '%s'"), dev->data.net->modelstr); return -1; } return 0; } #undef UNSUPPORTED static int virDomainDeviceDefPostParse(virDomainDeviceDefPtr dev, const virDomainDef *def, virCapsPtr caps, unsigned int flags, virDomainXMLOptionPtr xmlopt, void *parseOpaque) { int ret; if (xmlopt->config.devicesPostParseCallback) { ret = xmlopt->config.devicesPostParseCallback(dev, def, caps, flags, xmlopt->config.priv, parseOpaque); if (ret < 0) return ret; } if ((ret = virDomainDeviceDefPostParseCommon(dev, def, caps, flags, xmlopt)) < 0) return ret; if (virDomainDeviceDefPostParseCheckFeatures(dev, xmlopt) < 0) return -1; return 0; } static int virDomainDeviceDefPostParseOne(virDomainDeviceDefPtr dev, const virDomainDef *def, virCapsPtr caps, unsigned int flags, virDomainXMLOptionPtr xmlopt, void *parseOpaque) { void *data = NULL; int ret; if (!parseOpaque && xmlopt->config.domainPostParseDataAlloc) { if (xmlopt->config.domainPostParseDataAlloc(def, caps, flags, xmlopt->config.priv, &data) < 0) return -1; parseOpaque = data; } ret = virDomainDeviceDefPostParse(dev, def, caps, flags, xmlopt, parseOpaque); if (data && xmlopt->config.domainPostParseDataFree) xmlopt->config.domainPostParseDataFree(data); return ret; } struct virDomainDefPostParseDeviceIteratorData { virCapsPtr caps; virDomainXMLOptionPtr xmlopt; void *parseOpaque; unsigned int parseFlags; }; static int virDomainDefPostParseDeviceIterator(virDomainDefPtr def, virDomainDeviceDefPtr dev, virDomainDeviceInfoPtr info G_GNUC_UNUSED, void *opaque) { struct virDomainDefPostParseDeviceIteratorData *data = opaque; return virDomainDeviceDefPostParse(dev, def, data->caps, data->parseFlags, data->xmlopt, data->parseOpaque); } static int virDomainVcpuDefPostParse(virDomainDefPtr def) { virDomainVcpuDefPtr vcpu; size_t maxvcpus = virDomainDefGetVcpusMax(def); size_t i; for (i = 0; i < maxvcpus; i++) { vcpu = virDomainDefGetVcpu(def, i); /* impossible but some compilers don't like it */ if (!vcpu) continue; switch (vcpu->hotpluggable) { case VIR_TRISTATE_BOOL_ABSENT: if (vcpu->online) vcpu->hotpluggable = VIR_TRISTATE_BOOL_NO; else vcpu->hotpluggable = VIR_TRISTATE_BOOL_YES; break; case VIR_TRISTATE_BOOL_NO: if (!vcpu->online) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("vcpu '%zu' is both offline and not " "hotpluggable"), i); return -1; } break; case VIR_TRISTATE_BOOL_YES: case VIR_TRISTATE_BOOL_LAST: break; } } return 0; } static int virDomainDefPostParseCPU(virDomainDefPtr def) { if (!def->cpu) return 0; if (def->cpu->mode == VIR_CPU_MODE_CUSTOM && !def->cpu->model && def->cpu->check != VIR_CPU_CHECK_DEFAULT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("check attribute specified for CPU with no model")); return -1; } return 0; } static int virDomainDefCollectBootOrder(virDomainDefPtr def G_GNUC_UNUSED, virDomainDeviceDefPtr dev G_GNUC_UNUSED, virDomainDeviceInfoPtr info, void *data) { virHashTablePtr bootHash = data; g_autofree char *order = NULL; if (info->bootIndex == 0) return 0; if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV && dev->data.hostdev->parentnet) { /* This hostdev is a child of a higher level device * (e.g. interface), and thus already being counted on the * list for the other device type. */ return 0; } if (virAsprintf(&order, "%u", info->bootIndex) < 0) return -1; if (virHashLookup(bootHash, order)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("boot order '%s' used for more than one device"), order); return -1; } if (virHashAddEntry(bootHash, order, (void *) 1) < 0) return -1; return 0; } static int virDomainDefBootOrderPostParse(virDomainDefPtr def) { virHashTablePtr bootHash = NULL; int ret = -1; if (!(bootHash = virHashCreate(5, NULL))) goto cleanup; if (virDomainDeviceInfoIterate(def, virDomainDefCollectBootOrder, bootHash) < 0) goto cleanup; if (def->os.nBootDevs > 0 && virHashSize(bootHash) > 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("per-device boot elements cannot be used" " together with os/boot elements")); goto cleanup; } if (def->os.nBootDevs == 0 && virHashSize(bootHash) == 0) { def->os.nBootDevs = 1; def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK; } ret = 0; cleanup: virHashFree(bootHash); return ret; } static int virDomainDefPostParseVideo(virDomainDefPtr def, void *opaque) { if (def->nvideos == 0) return 0; if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_NONE) { char *alias; /* we don't want to format any values we automatically fill in for * videos into the XML, so clear them, but retain any user-assigned * alias */ alias = g_steal_pointer(&def->videos[0]->info.alias); virDomainVideoDefClear(def->videos[0]); def->videos[0]->type = VIR_DOMAIN_VIDEO_TYPE_NONE; def->videos[0]->info.alias = g_steal_pointer(&alias); } else { virDomainDeviceDef device = { .type = VIR_DOMAIN_DEVICE_VIDEO, .data.video = def->videos[0], }; /* Mark the first video as primary. If the user specified * primary="yes", the parser already inserted the device at * def->videos[0] */ def->videos[0]->primary = true; /* videos[0] might have been added in AddImplicitDevices, after we've * done the per-device post-parse */ if (virDomainDefPostParseDeviceIterator(def, &device, NULL, opaque) < 0) return -1; } return 0; } static int virDomainDefPostParseCommon(virDomainDefPtr def, struct virDomainDefPostParseDeviceIteratorData *data, virDomainXMLOptionPtr xmlopt) { size_t i; /* verify init path for container based domains */ if (def->os.type == VIR_DOMAIN_OSTYPE_EXE && !def->os.init) { virReportError(VIR_ERR_XML_ERROR, "%s", _("init binary must be specified")); return -1; } if (virDomainVcpuDefPostParse(def) < 0) return -1; if (virDomainDefPostParseMemory(def, data->parseFlags) < 0) return -1; virDomainDefPostParseOs(def); virDomainDefPostParseMemtune(def); if (virDomainDefRejectDuplicateControllers(def) < 0) return -1; if (virDomainDefRejectDuplicatePanics(def) < 0) return -1; if (def->os.type == VIR_DOMAIN_OSTYPE_HVM && !(data->xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_NO_BOOT_ORDER) && virDomainDefBootOrderPostParse(def) < 0) return -1; if (virDomainDefPostParseTimer(def) < 0) return -1; if (virDomainDefAddImplicitDevices(def, xmlopt) < 0) return -1; if (virDomainDefPostParseVideo(def, data) < 0) return -1; if (def->nserials != 0) { virDomainDeviceDef device = { .type = VIR_DOMAIN_DEVICE_CHR, .data.chr = def->serials[0], }; /* serials[0] might have been added in AddImplicitDevices, after we've * done the per-device post-parse */ if (virDomainDefPostParseDeviceIterator(def, &device, NULL, data) < 0) return -1; } /* Implicit SCSI controllers without a defined model might have * been added in AddImplicitDevices, after we've done the per-device * post-parse. */ for (i = 0; i < def->ncontrollers; i++) { if (def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT && def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) { virDomainDeviceDef device = { .type = VIR_DOMAIN_DEVICE_CONTROLLER, .data.controller = def->controllers[i], }; if (virDomainDefPostParseDeviceIterator(def, &device, NULL, data) < 0) return -1; } } /* clean up possibly duplicated metadata entries */ virXMLNodeSanitizeNamespaces(def->metadata); virDomainDefPostParseGraphics(def); if (virDomainDefPostParseCPU(def) < 0) return -1; return 0; } static int virDomainDefPostParseCheckFailure(virDomainDefPtr def, unsigned int parseFlags, int ret) { if (ret != 0) def->postParseFailed = true; if (ret <= 0) return ret; if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_ALLOW_POST_PARSE_FAIL)) return -1; virResetLastError(); return 0; } int virDomainDefPostParse(virDomainDefPtr def, virCapsPtr caps, unsigned int parseFlags, virDomainXMLOptionPtr xmlopt, void *parseOpaque) { int ret = -1; bool localParseOpaque = false; struct virDomainDefPostParseDeviceIteratorData data = { .caps = caps, .xmlopt = xmlopt, .parseFlags = parseFlags, .parseOpaque = parseOpaque, }; def->postParseFailed = false; /* call the basic post parse callback */ if (xmlopt->config.domainPostParseBasicCallback) { ret = xmlopt->config.domainPostParseBasicCallback(def, caps, xmlopt->config.priv); if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0) goto cleanup; } if (!data.parseOpaque && xmlopt->config.domainPostParseDataAlloc) { ret = xmlopt->config.domainPostParseDataAlloc(def, caps, parseFlags, xmlopt->config.priv, &data.parseOpaque); if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0) goto cleanup; localParseOpaque = true; } /* this must be done before the hypervisor-specific callback, * in case presence of a controller at a specific index is checked */ virDomainAssignControllerIndexes(def); /* call the domain config callback */ if (xmlopt->config.domainPostParseCallback) { ret = xmlopt->config.domainPostParseCallback(def, caps, parseFlags, xmlopt->config.priv, data.parseOpaque); if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0) goto cleanup; } /* iterate the devices */ ret = virDomainDeviceInfoIterateInternal(def, virDomainDefPostParseDeviceIterator, DOMAIN_DEVICE_ITERATE_ALL_CONSOLES | DOMAIN_DEVICE_ITERATE_MISSING_INFO, &data); if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0) goto cleanup; if ((ret = virDomainDefPostParseCommon(def, &data, xmlopt)) < 0) goto cleanup; if (xmlopt->config.assignAddressesCallback) { ret = xmlopt->config.assignAddressesCallback(def, caps, parseFlags, xmlopt->config.priv, data.parseOpaque); if (virDomainDefPostParseCheckFailure(def, parseFlags, ret) < 0) goto cleanup; } if ((ret = virDomainDefPostParseCheckFeatures(def, xmlopt)) < 0) goto cleanup; ret = 0; cleanup: if (localParseOpaque && xmlopt->config.domainPostParseDataFree) xmlopt->config.domainPostParseDataFree(data.parseOpaque); if (ret == 1) ret = -1; return ret; } /** * virDomainDiskAddressDiskBusCompatibility: * @bus: disk bus type * @addressType: disk address type * * Check if the specified disk address type @addressType is compatible * with the specified disk bus type @bus. This function checks * compatibility with the bus types SATA, SCSI, FDC, and IDE only, * because only these are handled in common code. * * Returns true if compatible or can't be decided in common code, * false if known to be not compatible. */ static bool virDomainDiskAddressDiskBusCompatibility(virDomainDiskBus bus, virDomainDeviceAddressType addressType) { if (addressType == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) return true; switch (bus) { case VIR_DOMAIN_DISK_BUS_IDE: case VIR_DOMAIN_DISK_BUS_FDC: case VIR_DOMAIN_DISK_BUS_SCSI: case VIR_DOMAIN_DISK_BUS_SATA: return addressType == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; case VIR_DOMAIN_DISK_BUS_VIRTIO: case VIR_DOMAIN_DISK_BUS_XEN: case VIR_DOMAIN_DISK_BUS_USB: case VIR_DOMAIN_DISK_BUS_UML: case VIR_DOMAIN_DISK_BUS_SD: case VIR_DOMAIN_DISK_BUS_LAST: return true; } virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unexpected bus type '%d'"), bus); return true; } static int virDomainDiskDefValidate(const virDomainDiskDef *disk) { /* Validate LUN configuration */ if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) { /* volumes haven't been translated at this point, so accept them */ if (!(disk->src->type == VIR_STORAGE_TYPE_BLOCK || disk->src->type == VIR_STORAGE_TYPE_VOLUME || (disk->src->type == VIR_STORAGE_TYPE_NETWORK && disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("disk '%s' improperly configured for a " "device='lun'"), disk->dst); return -1; } } if (disk->src->pr && disk->device != VIR_DOMAIN_DISK_DEVICE_LUN) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _(" allowed only for lun devices")); return -1; } /* Reject disks with a bus type that is not compatible with the * given address type. The function considers only buses that are * handled in common code. For other bus types it's not possible * to decide compatibility in common code. */ if (!virDomainDiskAddressDiskBusCompatibility(disk->bus, disk->info.type)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid address type '%s' for the disk '%s' with the bus type '%s'"), virDomainDeviceAddressTypeToString(disk->info.type), disk->dst, virDomainDiskBusTypeToString(disk->bus)); return -1; } if (disk->queues && disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("queues attribute in disk driver element is only " "supported by virtio-blk")); return -1; } if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO && (disk->model == VIR_DOMAIN_DISK_MODEL_VIRTIO || disk->model == VIR_DOMAIN_DISK_MODEL_VIRTIO_TRANSITIONAL || disk->model == VIR_DOMAIN_DISK_MODEL_VIRTIO_NON_TRANSITIONAL)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("disk model '%s' not supported for bus '%s'"), virDomainDiskModelTypeToString(disk->model), virDomainDiskBusTypeToString(disk->bus)); return -1; } return 0; } bool virDomainDefHasUSB(const virDomainDef *def) { size_t i; for (i = 0; i < def->ncontrollers; i++) { if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && def->controllers[i]->model != VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) return true; } return false; } #define SERIAL_CHANNEL_NAME_CHARS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-." static int virDomainChrSourceDefValidate(const virDomainChrSourceDef *def, const virDomainChrDef *chr_def) { switch ((virDomainChrType) def->type) { case VIR_DOMAIN_CHR_TYPE_NULL: case VIR_DOMAIN_CHR_TYPE_PTY: 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_FILE: case VIR_DOMAIN_CHR_TYPE_DEV: case VIR_DOMAIN_CHR_TYPE_PIPE: if (!def->data.file.path) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source path attribute for char device")); return -1; } break; case VIR_DOMAIN_CHR_TYPE_NMDM: if (!def->data.nmdm.master) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing master path attribute for nmdm device")); return -1; } if (!def->data.nmdm.slave) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing slave path attribute for nmdm device")); return -1; } break; case VIR_DOMAIN_CHR_TYPE_TCP: if (!def->data.tcp.host) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source host attribute for char device")); return -1; } if (!def->data.tcp.service) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source service attribute for char device")); return -1; } if (def->data.tcp.listen && def->data.tcp.reconnect.enabled) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("chardev reconnect is possible only for connect mode")); return -1; } break; case VIR_DOMAIN_CHR_TYPE_UDP: if (!def->data.udp.connectService) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source service attribute for char device")); return -1; } break; case VIR_DOMAIN_CHR_TYPE_UNIX: /* The source path can be auto generated for certain specific * types of channels, but in most cases we should report an * error if the user didn't provide it */ if (!def->data.nix.path && !(chr_def && chr_def->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL && (chr_def->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN || chr_def->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing source path attribute for char device")); return -1; } if (def->data.nix.listen && def->data.nix.reconnect.enabled) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("chardev reconnect is possible only for connect mode")); return -1; } break; case VIR_DOMAIN_CHR_TYPE_SPICEPORT: if (!def->data.spiceport.channel) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing source channel attribute for char device")); return -1; } if (strspn(def->data.spiceport.channel, SERIAL_CHANNEL_NAME_CHARS) < strlen(def->data.spiceport.channel)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Invalid character in source channel for char device")); return -1; } break; } return 0; } static int virDomainRedirdevDefValidate(const virDomainDef *def, const virDomainRedirdevDef *redirdev) { if (redirdev->bus == VIR_DOMAIN_REDIRDEV_BUS_USB && !virDomainDefHasUSB(def)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("cannot add redirected USB device: " "USB is disabled for this domain")); return -1; } return virDomainChrSourceDefValidate(redirdev->source, NULL); } static int virDomainNetDefValidate(const virDomainNetDef *net) { if ((net->hostIP.nroutes || net->hostIP.nips) && net->type != VIR_DOMAIN_NET_TYPE_ETHERNET) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid attempt to set network interface " "host-side IP route and/or address info on " "interface of type '%s'. This is only supported " "on interfaces of type 'ethernet'"), virDomainNetTypeToString(net->type)); return -1; } if (net->managed_tap == VIR_TRISTATE_BOOL_NO && net->type != VIR_DOMAIN_NET_TYPE_ETHERNET) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unmanaged target dev is not supported on " "interfaces of type '%s'"), virDomainNetTypeToString(net->type)); return -1; } return 0; } static int virDomainControllerDefValidate(const virDomainControllerDef *controller) { if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) { const virDomainPCIControllerOpts *opts = &controller->opts.pciopts; if (controller->idx > 255) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("PCI controller index %d too high, maximum is 255"), controller->idx); return -1; } /* Only validate the target index if it's been set */ if (opts->targetIndex != -1) { if (opts->targetIndex < 0 || opts->targetIndex > 30) { virReportError(VIR_ERR_XML_ERROR, _("PCI controller target index '%d' out of " "range - must be 0-30"), opts->targetIndex); return -1; } if ((controller->idx == 0 && opts->targetIndex != 0) || (controller->idx != 0 && opts->targetIndex == 0)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Only the PCI controller with index 0 can " "have target index 0, and vice versa")); return -1; } } } return 0; } static int virDomainChrDefValidate(const virDomainChrDef *chr) { return virDomainChrSourceDefValidate(chr->source, chr); } static int virDomainSmartcardDefValidate(const virDomainSmartcardDef *smartcard) { if (smartcard->type == VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH) return virDomainChrSourceDefValidate(smartcard->data.passthru, NULL); return 0; } static int virDomainRNGDefValidate(const virDomainRNGDef *rng) { if (rng->backend == VIR_DOMAIN_RNG_BACKEND_EGD) return virDomainChrSourceDefValidate(rng->source.chardev, NULL); return 0; } static int virDomainHostdevDefValidate(const virDomainHostdevDef *hostdev) { if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) { switch ((virDomainHostdevSubsysType) hostdev->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: if (hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("PCI host devices must use 'pci' address type")); return -1; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: if (hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) { virReportError(VIR_ERR_XML_ERROR, "%s", _("SCSI host device must use 'drive' " "address type")); return -1; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: if (hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI && hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) { virReportError(VIR_ERR_XML_ERROR, "%s", _("SCSI_host host device must use 'pci' " "or 'ccw' address type")); return -1; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: if (hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && hostdev->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) { virReportError(VIR_ERR_XML_ERROR, "%s", _("USB host device must use 'usb' address type")); return -1; } break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST: break; } } return 0; } static int virDomainVideoDefValidate(const virDomainVideoDef *video, const virDomainDef *def) { size_t i; if (video->type == VIR_DOMAIN_VIDEO_TYPE_DEFAULT) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing video model and cannot determine default")); return -1; } /* it doesn't make sense to pair video device type 'none' with any other * types, there can be only a single video device in such case */ for (i = 0; i < def->nvideos; i++) { if (def->videos[i]->type == VIR_DOMAIN_VIDEO_TYPE_NONE && def->nvideos > 1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("a 'none' video type must be the only video device " "defined for the domain")); return -1; } } switch (video->backend) { case VIR_DOMAIN_VIDEO_BACKEND_TYPE_VHOSTUSER: if (video->type != VIR_DOMAIN_VIDEO_TYPE_VIRTIO) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("'vhostuser' driver is only supported with 'virtio' device")); return -1; } break; case VIR_DOMAIN_VIDEO_BACKEND_TYPE_DEFAULT: case VIR_DOMAIN_VIDEO_BACKEND_TYPE_QEMU: if (video->accel && video->accel->rendernode) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unsupported rendernode accel attribute without 'vhostuser'")); return -1; } break; case VIR_DOMAIN_VIDEO_BACKEND_TYPE_LAST: default: virReportEnumRangeError(virDomainInputType, video->backend); return -1; } return 0; } static int virDomainMemoryDefValidate(const virDomainMemoryDef *mem) { if (mem->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM && mem->discard == VIR_TRISTATE_BOOL_YES) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("discard is not supported for nvdimms")); return -1; } return 0; } static int virDomainVsockDefValidate(const virDomainVsockDef *vsock) { if (vsock->guest_cid > 0 && vsock->guest_cid <= 2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("guest CIDs must be >= 3")); return -1; } return 0; } static int virDomainInputDefValidate(const virDomainInputDef *input) { switch ((virDomainInputType) input->type) { case VIR_DOMAIN_INPUT_TYPE_MOUSE: case VIR_DOMAIN_INPUT_TYPE_TABLET: case VIR_DOMAIN_INPUT_TYPE_KBD: if (input->source.evdev) { virReportError(VIR_ERR_XML_ERROR, "%s", _("setting source evdev path only supported for " "passthrough input devices")); return -1; } break; case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH: break; case VIR_DOMAIN_INPUT_TYPE_LAST: default: virReportEnumRangeError(virDomainInputType, input->type); return -1; } return 0; } static int virDomainShmemDefValidate(const virDomainShmemDef *shmem) { if (strchr(shmem->name, '/')) { virReportError(VIR_ERR_XML_ERROR, "%s", _("shmem name cannot include '/' character")); return -1; } if (STREQ(shmem->name, ".")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("shmem name cannot be equal to '.'")); return -1; } if (STREQ(shmem->name, "..")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("shmem name cannot be equal to '..'")); return -1; } return 0; } static int virDomainDeviceDefValidateInternal(const virDomainDeviceDef *dev, const virDomainDef *def) { switch ((virDomainDeviceType) dev->type) { case VIR_DOMAIN_DEVICE_DISK: return virDomainDiskDefValidate(dev->data.disk); case VIR_DOMAIN_DEVICE_REDIRDEV: return virDomainRedirdevDefValidate(def, dev->data.redirdev); case VIR_DOMAIN_DEVICE_NET: return virDomainNetDefValidate(dev->data.net); case VIR_DOMAIN_DEVICE_CONTROLLER: return virDomainControllerDefValidate(dev->data.controller); case VIR_DOMAIN_DEVICE_CHR: return virDomainChrDefValidate(dev->data.chr); case VIR_DOMAIN_DEVICE_SMARTCARD: return virDomainSmartcardDefValidate(dev->data.smartcard); case VIR_DOMAIN_DEVICE_RNG: return virDomainRNGDefValidate(dev->data.rng); case VIR_DOMAIN_DEVICE_HOSTDEV: return virDomainHostdevDefValidate(dev->data.hostdev); case VIR_DOMAIN_DEVICE_VIDEO: return virDomainVideoDefValidate(dev->data.video, def); case VIR_DOMAIN_DEVICE_MEMORY: return virDomainMemoryDefValidate(dev->data.memory); case VIR_DOMAIN_DEVICE_VSOCK: return virDomainVsockDefValidate(dev->data.vsock); case VIR_DOMAIN_DEVICE_INPUT: return virDomainInputDefValidate(dev->data.input); case VIR_DOMAIN_DEVICE_SHMEM: return virDomainShmemDefValidate(dev->data.shmem); case VIR_DOMAIN_DEVICE_LEASE: case VIR_DOMAIN_DEVICE_FS: case VIR_DOMAIN_DEVICE_SOUND: case VIR_DOMAIN_DEVICE_WATCHDOG: case VIR_DOMAIN_DEVICE_GRAPHICS: case VIR_DOMAIN_DEVICE_HUB: case VIR_DOMAIN_DEVICE_MEMBALLOON: case VIR_DOMAIN_DEVICE_NVRAM: case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; } return 0; } struct virDomainDefValidateAliasesData { virHashTablePtr aliases; }; static int virDomainDeviceDefValidateAliasesIterator(virDomainDefPtr def, virDomainDeviceDefPtr dev, virDomainDeviceInfoPtr info, void *opaque) { struct virDomainDefValidateAliasesData *data = opaque; const char *alias = info->alias; if (!virDomainDeviceAliasIsUserAlias(alias)) return 0; /* Some crazy backcompat for consoles. */ if (def->nserials && def->nconsoles && def->consoles[0]->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE && def->consoles[0]->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL && dev->type == VIR_DOMAIN_DEVICE_CHR && virDomainChrEquals(def->serials[0], dev->data.chr)) return 0; if (dev->type == VIR_DOMAIN_DEVICE_HOSTDEV && dev->data.hostdev->parentnet) { /* This hostdev is a copy of some previous interface. * Aliases are duplicated. */ return 0; } if (virHashLookup(data->aliases, alias)) { virReportError(VIR_ERR_XML_ERROR, _("non unique alias detected: %s"), alias); return -1; } if (virHashAddEntry(data->aliases, alias, (void *) 1) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to construct table of device aliases")); return -1; } return 0; } /** * virDomainDefValidateAliases: * * Check for uniqueness of device aliases. If @aliases is not * NULL return hash table of all the aliases in it. * * Returns 0 on success, * -1 otherwise (with error reported). */ static int virDomainDefValidateAliases(const virDomainDef *def, virHashTablePtr *aliases) { struct virDomainDefValidateAliasesData data; int ret = -1; /* We are not storing copies of aliases. Don't free them. */ if (!(data.aliases = virHashCreate(10, NULL))) goto cleanup; if (virDomainDeviceInfoIterateInternal((virDomainDefPtr) def, virDomainDeviceDefValidateAliasesIterator, DOMAIN_DEVICE_ITERATE_ALL_CONSOLES, &data) < 0) goto cleanup; if (aliases) *aliases = g_steal_pointer(&data.aliases); ret = 0; cleanup: virHashFree(data.aliases); return ret; } static int virDomainDeviceValidateAliasImpl(const virDomainDef *def, virDomainDeviceDefPtr dev) { virHashTablePtr aliases = NULL; virDomainDeviceInfoPtr info = virDomainDeviceGetInfo(dev); int ret = -1; if (!info || !info->alias) return 0; if (virDomainDefValidateAliases(def, &aliases) < 0) goto cleanup; if (virHashLookup(aliases, info->alias)) { virReportError(VIR_ERR_XML_ERROR, _("non unique alias detected: %s"), info->alias); goto cleanup; } ret = 0; cleanup: virHashFree(aliases); return ret; } int virDomainDeviceValidateAliasForHotplug(virDomainObjPtr vm, virDomainDeviceDefPtr dev, unsigned int flags) { virDomainDefPtr persDef = NULL; virDomainDefPtr liveDef = NULL; if (virDomainObjGetDefs(vm, flags, &liveDef, &persDef) < 0) return -1; if (persDef && virDomainDeviceValidateAliasImpl(persDef, dev) < 0) return -1; if (liveDef && virDomainDeviceValidateAliasImpl(liveDef, dev) < 0) return -1; return 0; } static int virDomainDeviceDefValidate(const virDomainDeviceDef *dev, const virDomainDef *def, unsigned int parseFlags, virDomainXMLOptionPtr xmlopt) { /* validate configuration only in certain places */ if (parseFlags & VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE) return 0; if (xmlopt->config.deviceValidateCallback && xmlopt->config.deviceValidateCallback(dev, def, xmlopt->config.priv)) return -1; if (virDomainDeviceDefValidateInternal(dev, def) < 0) return -1; return 0; } static int virDomainDefValidateDeviceIterator(virDomainDefPtr def, virDomainDeviceDefPtr dev, virDomainDeviceInfoPtr info G_GNUC_UNUSED, void *opaque) { struct virDomainDefPostParseDeviceIteratorData *data = opaque; return virDomainDeviceDefValidate(dev, def, data->parseFlags, data->xmlopt); } static int virDomainDefCheckDuplicateDiskInfo(const virDomainDef *def) { size_t i; size_t j; for (i = 0; i < def->ndisks; i++) { for (j = i + 1; j < def->ndisks; j++) { if (virDomainDiskDefCheckDuplicateInfo(def->disks[i], def->disks[j]) < 0) return -1; } } return 0; } /** * virDomainDefCheckDuplicateDriveAddresses: * @def: domain definition to check against * * This function checks @def for duplicate drive addresses. Drive * addresses are only in use for disks and hostdevs at the moment. * * Returns 0 in case of there are no duplicate drive addresses, -1 * otherwise. */ static int virDomainDefCheckDuplicateDriveAddresses(const virDomainDef *def) { size_t i; size_t j; for (i = 0; i < def->ndisks; i++) { virDomainDiskDefPtr disk_i = def->disks[i]; virDomainDeviceInfoPtr disk_info_i = &disk_i->info; if (disk_info_i->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) continue; for (j = i + 1; j < def->ndisks; j++) { virDomainDiskDefPtr disk_j = def->disks[j]; virDomainDeviceInfoPtr disk_info_j = &disk_j->info; if (disk_i->bus != disk_j->bus) continue; if (disk_info_j->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) continue; if (virDomainDeviceInfoAddressIsEqual(disk_info_i, disk_info_j)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Found duplicate drive address for disk with " "target name '%s' controller='%u' bus='%u' " "target='%u' unit='%u'"), disk_i->dst, disk_info_i->addr.drive.controller, disk_info_i->addr.drive.bus, disk_info_i->addr.drive.target, disk_info_i->addr.drive.unit); return -1; } } /* Note: There is no need to check for conflicts with SCSI * hostdevs above, because conflicts with hostdevs are checked * in the next loop. */ } for (i = 0; i < def->nhostdevs; i++) { virDomainHostdevDefPtr hdev_i = def->hostdevs[i]; virDomainDeviceInfoPtr hdev_info_i = hdev_i->info; virDomainDeviceDriveAddressPtr hdev_addr_i; if (!virHostdevIsSCSIDevice(hdev_i)) continue; if (hdev_i->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) continue; hdev_addr_i = &hdev_info_i->addr.drive; for (j = i + 1; j < def->nhostdevs; j++) { virDomainHostdevDefPtr hdev_j = def->hostdevs[j]; virDomainDeviceInfoPtr hdev_info_j = hdev_j->info; if (!virHostdevIsSCSIDevice(hdev_j)) continue; /* Address type check for hdev_j will be done implicitly * in virDomainDeviceInfoAddressIsEqual() */ if (virDomainDeviceInfoAddressIsEqual(hdev_info_i, hdev_info_j)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("SCSI host address controller='%u' " "bus='%u' target='%u' unit='%u' in " "use by another SCSI host device"), hdev_addr_i->bus, hdev_addr_i->controller, hdev_addr_i->target, hdev_addr_i->unit); return -1; } } if (virDomainDriveAddressIsUsedByDisk(def, VIR_DOMAIN_DISK_BUS_SCSI, hdev_addr_i)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("SCSI host address controller='%u' " "bus='%u' target='%u' unit='%u' in " "use by another SCSI disk"), hdev_addr_i->bus, hdev_addr_i->controller, hdev_addr_i->target, hdev_addr_i->unit); return -1; } } return 0; } bool virDomainDefLifecycleActionAllowed(virDomainLifecycle type, virDomainLifecycleAction action) { switch (type) { case VIR_DOMAIN_LIFECYCLE_POWEROFF: case VIR_DOMAIN_LIFECYCLE_REBOOT: switch (action) { case VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY: case VIR_DOMAIN_LIFECYCLE_ACTION_RESTART: case VIR_DOMAIN_LIFECYCLE_ACTION_RESTART_RENAME: case VIR_DOMAIN_LIFECYCLE_ACTION_PRESERVE: case VIR_DOMAIN_LIFECYCLE_ACTION_LAST: return true; case VIR_DOMAIN_LIFECYCLE_ACTION_COREDUMP_DESTROY: case VIR_DOMAIN_LIFECYCLE_ACTION_COREDUMP_RESTART: break; } break; case VIR_DOMAIN_LIFECYCLE_CRASH: case VIR_DOMAIN_LIFECYCLE_LAST: return true; } virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Lifecycle event '%s' doesn't support '%s' action"), virDomainLifecycleTypeToString(type), virDomainLifecycleActionTypeToString(action)); return false; } static int virDomainDefLifecycleActionValidate(const virDomainDef *def) { if (!virDomainDefLifecycleActionAllowed(VIR_DOMAIN_LIFECYCLE_POWEROFF, def->onPoweroff)) { return -1; } if (!virDomainDefLifecycleActionAllowed(VIR_DOMAIN_LIFECYCLE_REBOOT, def->onReboot)) { return -1; } if (!virDomainDefLifecycleActionAllowed(VIR_DOMAIN_LIFECYCLE_CRASH, def->onCrash)) { return -1; } return 0; } #define CPUTUNE_VALIDATE_PERIOD(name) \ do { \ if (def->cputune.name > 0 && \ (def->cputune.name < 1000 || def->cputune.name > 1000000)) { \ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, \ _("Value of cputune '%s' must be in range " \ "[1000, 1000000]"), #name); \ return -1; \ } \ } while (0) #define CPUTUNE_VALIDATE_QUOTA(name) \ do { \ if (def->cputune.name > 0 && \ (def->cputune.name < 1000 || \ def->cputune.name > 18446744073709551LL)) { \ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, \ _("Value of cputune '%s' must be in range " \ "[1000, 18446744073709551]"), #name); \ return -1; \ } \ } while (0) static int virDomainDefCputuneValidate(const virDomainDef *def) { CPUTUNE_VALIDATE_PERIOD(period); CPUTUNE_VALIDATE_PERIOD(global_period); CPUTUNE_VALIDATE_PERIOD(emulator_period); CPUTUNE_VALIDATE_PERIOD(iothread_period); CPUTUNE_VALIDATE_QUOTA(quota); CPUTUNE_VALIDATE_QUOTA(global_quota); CPUTUNE_VALIDATE_QUOTA(emulator_quota); CPUTUNE_VALIDATE_QUOTA(iothread_quota); return 0; } #undef CPUTUNE_VALIDATE_PERIOD #undef CPUTUNE_VALIDATE_QUOTA static int virDomainDefMemtuneValidate(const virDomainDef *def) { const virDomainMemtune *mem = &(def->mem); size_t i; ssize_t pos = virDomainNumaGetNodeCount(def->numa) - 1; for (i = 0; i < mem->nhugepages; i++) { size_t j; ssize_t nextBit; for (j = 0; j < i; j++) { if (mem->hugepages[i].nodemask && mem->hugepages[j].nodemask && virBitmapOverlaps(mem->hugepages[i].nodemask, mem->hugepages[j].nodemask)) { virReportError(VIR_ERR_XML_DETAIL, _("nodeset attribute of hugepages " "of sizes %llu and %llu intersect"), mem->hugepages[i].size, mem->hugepages[j].size); return -1; } else if (!mem->hugepages[i].nodemask && !mem->hugepages[j].nodemask) { virReportError(VIR_ERR_XML_DETAIL, _("two master hugepages detected: " "%llu and %llu"), mem->hugepages[i].size, mem->hugepages[j].size); return -1; } } if (!mem->hugepages[i].nodemask) { /* This is the master hugepage to use. Skip it as it has no * nodemask anyway. */ continue; } nextBit = virBitmapNextSetBit(mem->hugepages[i].nodemask, pos); if (nextBit >= 0) { virReportError(VIR_ERR_XML_DETAIL, _("hugepages: node %zd not found"), nextBit); return -1; } } return 0; } static int virDomainDefOSValidate(const virDomainDef *def, virDomainXMLOptionPtr xmlopt) { if (!def->os.loader) return 0; if (def->os.firmware && !(xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_FW_AUTOSELECT)) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("firmware auto selection not implemented for this driver")); return -1; } if (!def->os.loader->path && def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_NONE) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("no loader path specified and firmware auto selection disabled")); return -1; } return 0; } static int virDomainDefValidateInternal(const virDomainDef *def, virDomainXMLOptionPtr xmlopt) { if (virDomainDefCheckDuplicateDiskInfo(def) < 0) return -1; if (virDomainDefCheckDuplicateDriveAddresses(def) < 0) return -1; if (virDomainDefGetVcpusTopology(def, NULL) < 0) return -1; if (virDomainDefValidateAliases(def, NULL) < 0) return -1; if (def->iommu && def->iommu->intremap == VIR_TRISTATE_SWITCH_ON && def->features[VIR_DOMAIN_FEATURE_IOAPIC] != VIR_DOMAIN_IOAPIC_QEMU) { virReportError(VIR_ERR_XML_ERROR, "%s", _("IOMMU interrupt remapping requires split I/O APIC " "(ioapic driver='qemu')")); return -1; } if (def->iommu && def->iommu->eim == VIR_TRISTATE_SWITCH_ON && def->iommu->intremap != VIR_TRISTATE_SWITCH_ON) { virReportError(VIR_ERR_XML_ERROR, "%s", _("IOMMU eim requires interrupt remapping to be enabled")); return -1; } if (virDomainDefLifecycleActionValidate(def) < 0) return -1; if (virDomainDefMemtuneValidate(def) < 0) return -1; if (virDomainDefOSValidate(def, xmlopt) < 0) return -1; if (virDomainDefCputuneValidate(def) < 0) return -1; return 0; } /** * virDomainDefValidate: * @def: domain definition * @caps: driver capabilities object * @parseFlags: virDomainDefParseFlags * @xmlopt: XML parser option object * * This validation function is designed to take checks of globally invalid * configurations that the parser needs to accept so that VMs don't vanish upon * daemon restart. Such definition can be rejected upon startup or define, where * this function shall be called. * * Returns 0 if domain definition is valid, -1 on error and reports an * appropriate message. */ int virDomainDefValidate(virDomainDefPtr def, virCapsPtr caps, unsigned int parseFlags, virDomainXMLOptionPtr xmlopt) { struct virDomainDefPostParseDeviceIteratorData data = { .caps = caps, .xmlopt = xmlopt, .parseFlags = parseFlags, }; /* validate configuration only in certain places */ if (parseFlags & VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE) return 0; /* call the domain config callback */ if (xmlopt->config.domainValidateCallback && xmlopt->config.domainValidateCallback(def, caps, xmlopt->config.priv) < 0) return -1; /* iterate the devices */ if (virDomainDeviceInfoIterateInternal(def, virDomainDefValidateDeviceIterator, (DOMAIN_DEVICE_ITERATE_ALL_CONSOLES | DOMAIN_DEVICE_ITERATE_MISSING_INFO), &data) < 0) return -1; if (virDomainDefValidateInternal(def, xmlopt) < 0) return -1; return 0; } int virDomainObjCheckActive(virDomainObjPtr dom) { if (!virDomainObjIsActive(dom)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("domain is not running")); return -1; } return 0; } /** * virDomainDeviceLoadparmIsValid * @loadparm : The string to validate * * The valid set of values for loadparm are [a-zA-Z0-9.] * and blank spaces. * The maximum allowed length is 8 characters. * An empty string is considered invalid */ static bool virDomainDeviceLoadparmIsValid(const char *loadparm) { size_t i; if (virStringIsEmpty(loadparm)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("loadparm cannot be an empty string")); return false; } if (strlen(loadparm) > 8) { virReportError(VIR_ERR_INTERNAL_ERROR, _("loadparm '%s' exceeds 8 characters"), loadparm); return false; } for (i = 0; i < strlen(loadparm); i++) { uint8_t c = loadparm[i]; if (('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || (c == '.') || (c == ' ')) { continue; } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid loadparm char '%c', expecting chars" " in set of [a-zA-Z0-9.] and blank spaces"), c); return false; } } return true; } static void virDomainVirtioOptionsFormat(virBufferPtr buf, virDomainVirtioOptionsPtr virtio) { if (!virtio) return; if (virtio->iommu != VIR_TRISTATE_SWITCH_ABSENT) { virBufferAsprintf(buf, " iommu='%s'", virTristateSwitchTypeToString(virtio->iommu)); } if (virtio->ats != VIR_TRISTATE_SWITCH_ABSENT) { virBufferAsprintf(buf, " ats='%s'", virTristateSwitchTypeToString(virtio->ats)); } } static int ATTRIBUTE_NONNULL(2) virDomainDeviceInfoFormat(virBufferPtr buf, virDomainDeviceInfoPtr info, unsigned int flags) { g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER; g_auto(virBuffer) childBuf = VIR_BUFFER_INITIALIZER; int ret = -1; if ((flags & VIR_DOMAIN_DEF_FORMAT_ALLOW_BOOT) && info->bootIndex) { virBufferAsprintf(buf, "bootIndex); if (info->loadparm) virBufferAsprintf(buf, " loadparm='%s'", info->loadparm); virBufferAddLit(buf, "/>\n"); } if (info->alias) 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->romenabled != VIR_TRISTATE_BOOL_ABSENT || info->rombar != VIR_TRISTATE_SWITCH_ABSENT || info->romfile)) { virBufferAddLit(buf, "romenabled != VIR_TRISTATE_BOOL_ABSENT) { const char *romenabled = virTristateBoolTypeToString(info->romenabled); if (romenabled) virBufferAsprintf(buf, " enabled='%s'", romenabled); } if (info->rombar != VIR_TRISTATE_SWITCH_ABSENT) { const char *rombar = virTristateSwitchTypeToString(info->rombar); if (rombar) virBufferAsprintf(buf, " bar='%s'", rombar); } if (info->romfile) virBufferEscapeString(buf, " file='%s'", info->romfile); virBufferAddLit(buf, "/>\n"); } if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE || info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) { /* We're done here */ ret = 0; goto cleanup; } virBufferAsprintf(&attrBuf, " type='%s'", virDomainDeviceAddressTypeToString(info->type)); switch ((virDomainDeviceAddressType) info->type) { case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI: if (!virPCIDeviceAddressIsEmpty(&info->addr.pci)) { virBufferAsprintf(&attrBuf, " domain='0x%04x' bus='0x%02x' " "slot='0x%02x' function='0x%d'", info->addr.pci.domain, info->addr.pci.bus, info->addr.pci.slot, info->addr.pci.function); } if (info->addr.pci.multi) { virBufferAsprintf(&attrBuf, " multifunction='%s'", virTristateSwitchTypeToString(info->addr.pci.multi)); } if (!virZPCIDeviceAddressIsEmpty(&info->addr.pci.zpci)) { virBufferSetChildIndent(&childBuf, buf); virBufferAsprintf(&childBuf, "\n", info->addr.pci.zpci.uid, info->addr.pci.zpci.fid); } break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE: virBufferAsprintf(&attrBuf, " 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(&attrBuf, " 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(&attrBuf, " controller='%d' slot='%d'", info->addr.ccid.controller, info->addr.ccid.slot); break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB: virBufferAsprintf(&attrBuf, " bus='%d'", info->addr.usb.bus); if (virDomainUSBAddressPortIsValid(info->addr.usb.port)) { virBufferAddLit(&attrBuf, " port='"); virDomainUSBAddressPortFormatBuf(&attrBuf, info->addr.usb.port); virBufferAddLit(&attrBuf, "'"); } break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO: if (info->addr.spaprvio.has_reg) virBufferAsprintf(&attrBuf, " reg='0x%08llx'", info->addr.spaprvio.reg); break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW: virBufferAsprintf(&attrBuf, " 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(&attrBuf, " iobase='0x%x'", info->addr.isa.iobase); if (info->addr.isa.irq > 0) virBufferAsprintf(&attrBuf, " irq='0x%x'", info->addr.isa.irq); break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM: virBufferAsprintf(&attrBuf, " slot='%u'", info->addr.dimm.slot); if (info->addr.dimm.base) virBufferAsprintf(&attrBuf, " 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; } virXMLFormatElement(buf, "address", &attrBuf, &childBuf); ret = 0; cleanup: return ret; } static int virDomainDeviceUSBMasterParseXML(xmlNodePtr node, virDomainDeviceUSBMasterPtr master) { g_autofree char *startport = NULL; 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")); return -1; } return 0; } static int virDomainDeviceBootParseXML(xmlNodePtr node, virDomainDeviceInfoPtr info) { g_autofree char *order = NULL; g_autofree char *loadparm = NULL; if (!(order = virXMLPropString(node, "order"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing boot order attribute")); return -1; } if (virStrToLong_uip(order, NULL, 10, &info->bootIndex) < 0 || info->bootIndex == 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("incorrect boot order '%s', expecting positive integer"), order); return -1; } loadparm = virXMLPropString(node, "loadparm"); if (loadparm) { if (virStringToUpper(&info->loadparm, loadparm) != 1) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Failed to convert loadparm '%s' to upper case"), loadparm); return -1; } if (!virDomainDeviceLoadparmIsValid(info->loadparm)) { VIR_FREE(info->loadparm); return -1; } } return 0; } static int virDomainDeviceISAAddressParseXML(xmlNodePtr node, virDomainDeviceISAAddressPtr addr) { g_autofree char *iobase = NULL; g_autofree char *irq = NULL; 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")); return -1; } if (irq && virStrToLong_uip(irq, NULL, 16, &addr->irq) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Cannot parse
'irq' attribute")); return -1; } return 0; } static int virDomainDeviceDimmAddressParseXML(xmlNodePtr node, virDomainDeviceDimmAddressPtr addr) { g_autofree 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)); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(node, "base"))) { if (virStrToLong_ullp(tmp, NULL, 16, &addr->base) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid dimm base address '%s'"), tmp); return -1; } } return 0; } static int virDomainDeviceAddressParseXML(xmlNodePtr address, virDomainDeviceInfoPtr info) { g_autofree char *type = virXMLPropString(address, "type"); if (type) { if ((info->type = virDomainDeviceAddressTypeFromString(type)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown address type '%s'"), type); return -1; } } else { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No type specified for device address")); return -1; } switch ((virDomainDeviceAddressType) info->type) { case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI: if (virPCIDeviceAddressParseXML(address, &info->addr.pci) < 0) return -1; break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE: if (virDomainDeviceDriveAddressParseXML(address, &info->addr.drive) < 0) return -1; break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL: if (virDomainDeviceVirtioSerialAddressParseXML (address, &info->addr.vioserial) < 0) return -1; break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID: if (virDomainDeviceCcidAddressParseXML(address, &info->addr.ccid) < 0) return -1; break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB: if (virDomainDeviceUSBAddressParseXML(address, &info->addr.usb) < 0) return -1; break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO: if (virDomainDeviceSpaprVioAddressParseXML(address, &info->addr.spaprvio) < 0) return -1; break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW: if (virDomainDeviceCCWAddressParseXML (address, &info->addr.ccw) < 0) return -1; break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO: break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA: if (virDomainDeviceISAAddressParseXML(address, &info->addr.isa) < 0) return -1; break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390: virReportError(VIR_ERR_XML_ERROR, "%s", _("virtio-s390 bus doesn't have an address")); return -1; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM: if (virDomainDeviceDimmAddressParseXML(address, &info->addr.dimm) < 0) return -1; break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE: case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST: break; } return 0; } #define USER_ALIAS_PREFIX "ua-" #define USER_ALIAS_CHARS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" bool virDomainDeviceAliasIsUserAlias(const char *aliasStr) { return aliasStr && STRPREFIX(aliasStr, USER_ALIAS_PREFIX); } /* Parse the XML definition for a device address * @param node XML nodeset to parse for device address definition */ static int virDomainDeviceInfoParseXML(virDomainXMLOptionPtr xmlopt G_GNUC_UNUSED, xmlNodePtr node, virDomainDeviceInfoPtr info, unsigned int flags) { xmlNodePtr cur; xmlNodePtr address = NULL; xmlNodePtr master = NULL; xmlNodePtr alias = NULL; xmlNodePtr boot = NULL; xmlNodePtr rom = NULL; int ret = -1; g_autofree char *romenabled = NULL; g_autofree char *rombar = NULL; g_autofree char *aliasStr = NULL; virDomainDeviceInfoClear(info); cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (alias == NULL && virXMLNodeNameEqual(cur, "alias")) { alias = cur; } else if (address == NULL && virXMLNodeNameEqual(cur, "address")) { address = cur; } else if (master == NULL && virXMLNodeNameEqual(cur, "master")) { master = cur; } else if (boot == NULL && (flags & VIR_DOMAIN_DEF_PARSE_ALLOW_BOOT) && virXMLNodeNameEqual(cur, "boot")) { boot = cur; } else if (rom == NULL && (flags & VIR_DOMAIN_DEF_PARSE_ALLOW_ROM) && virXMLNodeNameEqual(cur, "rom")) { rom = cur; } } cur = cur->next; } if (alias) { aliasStr = virXMLPropString(alias, "name"); if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) || (xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_USER_ALIAS && virDomainDeviceAliasIsUserAlias(aliasStr) && strspn(aliasStr, USER_ALIAS_CHARS) == strlen(aliasStr))) info->alias = g_steal_pointer(&aliasStr); } if (master) { info->mastertype = VIR_DOMAIN_CONTROLLER_MASTER_USB; if (virDomainDeviceUSBMasterParseXML(master, &info->master.usb) < 0) goto cleanup; } if (boot) { if (virDomainDeviceBootParseXML(boot, info)) goto cleanup; } if (rom) { if ((romenabled = virXMLPropString(rom, "enabled")) && ((info->romenabled = virTristateBoolTypeFromString(romenabled)) <= 0)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown rom enabled value '%s'"), romenabled); goto cleanup; } if ((rombar = virXMLPropString(rom, "bar")) && ((info->rombar = virTristateSwitchTypeFromString(rombar)) <= 0)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown rom bar value '%s'"), rombar); goto cleanup; } info->romfile = virXMLPropString(rom, "file"); if (info->romenabled == VIR_TRISTATE_BOOL_NO && (info->rombar != VIR_TRISTATE_SWITCH_ABSENT || info->romfile)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("ROM tuning is not supported when ROM is disabled")); goto cleanup; } } if (address && virDomainDeviceAddressParseXML(address, info) < 0) goto cleanup; ret = 0; cleanup: if (ret < 0) virDomainDeviceInfoClear(info); return ret; } static int virDomainHostdevSubsysUSBDefParseXML(xmlNodePtr node, virDomainHostdevDefPtr def) { int ret = -1; bool got_product, got_vendor; xmlNodePtr cur; virDomainHostdevSubsysUSBPtr usbsrc = &def->source.subsys.u.usb; g_autofree char *startupPolicy = NULL; g_autofree char *autoAddress = NULL; if ((startupPolicy = virXMLPropString(node, "startupPolicy"))) { def->startupPolicy = virDomainStartupPolicyTypeFromString(startupPolicy); if (def->startupPolicy <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown startup policy '%s'"), startupPolicy); goto out; } } if ((autoAddress = virXMLPropString(node, "autoAddress"))) { if (STREQ(autoAddress, "yes")) usbsrc->autoAddress = true; } /* 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 (virXMLNodeNameEqual(cur, "vendor")) { g_autofree 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); goto out; } } else { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("usb vendor needs id")); goto out; } } else if (virXMLNodeNameEqual(cur, "product")) { g_autofree 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); goto out; } } else { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("usb product needs id")); goto out; } } else if (virXMLNodeNameEqual(cur, "address")) { g_autofree char *bus = NULL; g_autofree char *device = NULL; 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); goto out; } } 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); goto out; } } 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 (virXMLNodeNameEqual(cur, "unbind")) { def->states.pci.unbind_from_stub = true; } else if (virXMLNodeNameEqual(cur, "removeslot")) { def->states.pci.remove_slot = true; } else if (virXMLNodeNameEqual(cur, "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 (virXMLNodeNameEqual(cur, "address")) { virPCIDeviceAddressPtr addr = &def->source.subsys.u.pci.addr; if (virPCIDeviceAddressParseXML(cur, addr) < 0) goto out; } else if ((flags & VIR_DOMAIN_DEF_PARSE_PCI_ORIG_STATES) && virXMLNodeNameEqual(cur, "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; } int virDomainStorageNetworkParseHost(xmlNodePtr hostnode, virStorageNetHostDefPtr host) { int ret = -1; g_autofree char *transport = NULL; g_autofree char *port = NULL; memset(host, 0, sizeof(*host)); host->transport = VIR_STORAGE_NET_HOST_TRANS_TCP; /* transport can be tcp (default), unix or rdma. */ if ((transport = virXMLPropString(hostnode, "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(hostnode, "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; } if (host->transport != VIR_STORAGE_NET_HOST_TRANS_UNIX) { if (!(host->name = virXMLPropString(hostnode, "name"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing name for host")); goto cleanup; } if ((port = virXMLPropString(hostnode, "port"))) { if (virStringParsePort(port, &host->port) < 0) goto cleanup; } } ret = 0; cleanup: if (ret < 0) virStorageNetHostDefClear(host); return ret; } static int virDomainStorageNetworkParseHosts(xmlNodePtr node, virStorageNetHostDefPtr *hosts, size_t *nhosts) { xmlNodePtr child; for (child = node->children; child; child = child->next) { if (child->type == XML_ELEMENT_NODE && virXMLNodeNameEqual(child, "host")) { virStorageNetHostDef host; if (virDomainStorageNetworkParseHost(child, &host) < 0) return -1; if (VIR_APPEND_ELEMENT(*hosts, *nhosts, host) < 0) { virStorageNetHostDefClear(&host); return -1; } } } return 0; } static int virDomainHostdevSubsysSCSIHostDefParseXML(xmlNodePtr sourcenode, virDomainHostdevSubsysSCSIPtr scsisrc) { bool got_address = false, got_adapter = false; xmlNodePtr cur; virDomainHostdevSubsysSCSIHostPtr scsihostsrc = &scsisrc->u.host; g_autofree char *bus = NULL; g_autofree char *target = NULL; g_autofree char *unit = NULL; cur = sourcenode->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (virXMLNodeNameEqual(cur, "address")) { if (got_address) { virReportError(VIR_ERR_XML_ERROR, "%s", _("more than one source addresses is " "specified for scsi hostdev")); return -1; } 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")); return -1; } if (virStrToLong_uip(bus, NULL, 0, &scsihostsrc->bus) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse bus '%s'"), bus); return -1; } if (virStrToLong_uip(target, NULL, 0, &scsihostsrc->target) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse target '%s'"), target); return -1; } if (virStrToLong_ullp(unit, NULL, 0, &scsihostsrc->unit) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse unit '%s'"), unit); return -1; } got_address = true; } else if (virXMLNodeNameEqual(cur, "adapter")) { if (got_adapter) { virReportError(VIR_ERR_XML_ERROR, "%s", _("more than one adapters is specified " "for scsi hostdev source")); return -1; } if (!(scsihostsrc->adapter = virXMLPropString(cur, "name"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'adapter' must be specified for scsi hostdev source")); return -1; } got_adapter = true; } else { virReportError(VIR_ERR_XML_ERROR, _("unsupported element '%s' of scsi hostdev source"), cur->name); return -1; } } cur = cur->next; } if (!got_address || !got_adapter) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'adapter' and 'address' must be specified for scsi " "hostdev source")); return -1; } return 0; } static int virDomainHostdevSubsysSCSIiSCSIDefParseXML(xmlNodePtr sourcenode, virDomainHostdevSubsysSCSIPtr def, xmlXPathContextPtr ctxt) { int auth_secret_usage = -1; xmlNodePtr cur; virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc = &def->u.iscsi; g_autoptr(virStorageAuthDef) authdef = NULL; /* For the purposes of command line creation, this needs to look * like a disk storage source */ if (!(iscsisrc->src = virStorageSourceNew())) return -1; iscsisrc->src->type = VIR_STORAGE_TYPE_NETWORK; iscsisrc->src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI; if (!(iscsisrc->src->path = virXMLPropString(sourcenode, "name"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing iSCSI hostdev source path name")); return -1; } if (virDomainStorageNetworkParseHosts(sourcenode, &iscsisrc->src->hosts, &iscsisrc->src->nhosts) < 0) return -1; if (iscsisrc->src->nhosts < 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing the host address for the iSCSI hostdev")); return -1; } if (iscsisrc->src->nhosts > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only one source host address may be specified " "for the iSCSI hostdev")); return -1; } cur = sourcenode->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE && virXMLNodeNameEqual(cur, "auth")) { if (iscsisrc->src->auth) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("an definition already found for " "the iSCSI definition")); return -1; } if (!(authdef = virStorageAuthDefParse(cur, ctxt))) return -1; if ((auth_secret_usage = virSecretUsageTypeFromString(authdef->secrettype)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid secret type %s"), authdef->secrettype); return -1; } if (auth_secret_usage != VIR_SECRET_USAGE_TYPE_ISCSI) { virReportError(VIR_ERR_INTERNAL_ERROR, _("hostdev invalid secret type '%s'"), authdef->secrettype); return -1; } iscsisrc->src->auth = g_steal_pointer(&authdef); } cur = cur->next; } return 0; } static int virDomainHostdevSubsysSCSIDefParseXML(xmlNodePtr sourcenode, virDomainHostdevSubsysSCSIPtr scsisrc, xmlXPathContextPtr ctxt) { g_autofree char *protocol = NULL; if ((protocol = virXMLPropString(sourcenode, "protocol"))) { scsisrc->protocol = virDomainHostdevSubsysSCSIProtocolTypeFromString(protocol); if (scsisrc->protocol < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown SCSI subsystem protocol '%s'"), protocol); return -1; } } if (scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) return virDomainHostdevSubsysSCSIiSCSIDefParseXML(sourcenode, scsisrc, ctxt); return virDomainHostdevSubsysSCSIHostDefParseXML(sourcenode, scsisrc); } static int virDomainHostdevSubsysSCSIVHostDefParseXML(xmlNodePtr sourcenode, virDomainHostdevDefPtr def) { virDomainHostdevSubsysSCSIVHostPtr hostsrc = &def->source.subsys.u.scsi_host; g_autofree char *protocol = NULL; g_autofree char *wwpn = NULL; if (!(protocol = virXMLPropString(sourcenode, "protocol"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing scsi_host subsystem protocol")); return -1; } if ((hostsrc->protocol = virDomainHostdevSubsysSCSIHostProtocolTypeFromString(protocol)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown scsi_host subsystem protocol '%s'"), protocol); return -1; } switch ((virDomainHostdevSubsysSCSIHostProtocolType) hostsrc->protocol) { case VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_VHOST: if (!(wwpn = virXMLPropString(sourcenode, "wwpn"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing vhost-scsi hostdev source wwpn")); return -1; } if (!STRPREFIX(wwpn, "naa.") || !virValidateWWN(wwpn + 4)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("malformed 'wwpn' value")); return -1; } hostsrc->wwpn = g_steal_pointer(&wwpn); break; case VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_NONE: case VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_LAST: virReportError(VIR_ERR_XML_ERROR, _("Invalid hostdev protocol '%s'"), virDomainHostdevSubsysSCSIHostProtocolTypeToString(hostsrc->protocol)); return -1; break; } return 0; } static int virDomainHostdevSubsysMediatedDevDefParseXML(virDomainHostdevDefPtr def, xmlXPathContextPtr ctxt) { unsigned char uuid[VIR_UUID_BUFLEN] = {0}; xmlNodePtr node = NULL; virDomainHostdevSubsysMediatedDevPtr mdevsrc = &def->source.subsys.u.mdev; g_autofree char *uuidxml = NULL; if (!(node = virXPathNode("./source/address", ctxt))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Missing
element")); return -1; } if (!(uuidxml = virXMLPropString(node, "uuid"))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Missing 'uuid' attribute for element
")); return -1; } if (virUUIDParse(uuidxml, uuid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot parse uuid attribute of element
")); return -1; } virUUIDFormat(uuid, mdevsrc->uuidstr); return 0; } static int virDomainHostdevDefParseXMLSubsys(xmlNodePtr node, xmlXPathContextPtr ctxt, const char *type, virDomainHostdevDefPtr def, unsigned int flags) { xmlNodePtr sourcenode; int backend; virDomainHostdevSubsysPCIPtr pcisrc = &def->source.subsys.u.pci; virDomainHostdevSubsysSCSIPtr scsisrc = &def->source.subsys.u.scsi; virDomainHostdevSubsysSCSIVHostPtr scsihostsrc = &def->source.subsys.u.scsi_host; virDomainHostdevSubsysMediatedDevPtr mdevsrc = &def->source.subsys.u.mdev; g_autofree char *managed = NULL; g_autofree char *sgio = NULL; g_autofree char *rawio = NULL; g_autofree char *backendStr = NULL; g_autofree char *model = NULL; g_autofree char *display = NULL; /* @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"); model = virXMLPropString(node, "model"); display = virXMLPropString(node, "display"); /* @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); return -1; } } else { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing source address type")); return -1; } if (!(sourcenode = virXPathNode("./source", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing element in hostdev device")); return -1; } 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")); return -1; } 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")); return -1; } if ((scsisrc->sgio = virDomainDeviceSGIOTypeFromString(sgio)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown sgio mode '%s'"), sgio); return -1; } } 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")); return -1; } if ((scsisrc->rawio = virTristateBoolTypeFromString(rawio)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown hostdev rawio setting '%s'"), rawio); return -1; } } if (def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV && def->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST) { if (model) { virReportError(VIR_ERR_XML_ERROR, _("'model' attribute in is only supported " "when type='%s'"), virDomainHostdevSubsysTypeToString(def->source.subsys.type)); return -1; } } if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST) { if (model && ((scsihostsrc->model = virDomainHostdevSubsysSCSIVHostModelTypeFromString(model)) < 0)) { virReportError(VIR_ERR_XML_ERROR, _("unknown hostdev model '%s'"), model); return -1; } } else if (def->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV) { if (!model) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'model' attribute in mediated device's " " element")); return -1; } if ((mdevsrc->model = virMediatedDeviceModelTypeFromString(model)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown hostdev model '%s'"), model); return -1; } if (display && (mdevsrc->display = virTristateSwitchTypeFromString(display)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown value '%s' for attribute " "'display'"), display); return -1; } } switch (def->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: if (virDomainHostdevSubsysPCIDefParseXML(sourcenode, def, flags) < 0) return -1; 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); return -1; } pcisrc->backend = backend; break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: if (virDomainHostdevSubsysUSBDefParseXML(sourcenode, def) < 0) return -1; break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: if (virDomainHostdevSubsysSCSIDefParseXML(sourcenode, scsisrc, ctxt) < 0) return -1; break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: if (virDomainHostdevSubsysSCSIVHostDefParseXML(sourcenode, def) < 0) return -1; break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: if (virDomainHostdevSubsysMediatedDevDefParseXML(def, ctxt) < 0) return -1; break; default: virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("address type='%s' not supported in hostdev interfaces"), virDomainHostdevSubsysTypeToString(def->source.subsys.type)); return -1; } return 0; } static virNetDevIPAddrPtr virDomainNetIPParseXML(xmlNodePtr node) { /* Parse the prefix in every case */ unsigned int prefixValue = 0; int family = AF_UNSPEC; g_autofree virNetDevIPAddrPtr ip = NULL; g_autofree char *prefixStr = NULL; g_autofree char *familyStr = NULL; g_autofree char *address = NULL; g_autofree char *peer = NULL; if (!(address = virXMLPropString(node, "address"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing required address in ")); return NULL; } 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) return NULL; if (virSocketAddrParse(&ip->address, address, family) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid address '%s' in "), address); return NULL; } prefixStr = virXMLPropString(node, "prefix"); if (prefixStr && ((virStrToLong_ui(prefixStr, NULL, 10, &prefixValue) < 0) || (family == AF_INET6 && prefixValue > 128) || (family == AF_INET && prefixValue > 32))) { virReportError(VIR_ERR_XML_ERROR, _("Invalid prefix value '%s' in "), prefixStr); return NULL; } ip->prefix = prefixValue; if ((peer = virXMLPropString(node, "peer")) != NULL && virSocketAddrParse(&ip->peer, peer, family) < 0) { virReportError(VIR_ERR_INVALID_ARG, _("Invalid peer '%s' in "), peer); return NULL; } return g_steal_pointer(&ip); } /* fill in a virNetDevIPInfoPtr from the and * elements found in the given XML context. * * return 0 on success (including none found) and -1 on failure. */ static int virDomainNetIPInfoParseXML(const char *source, xmlXPathContextPtr ctxt, virNetDevIPInfoPtr def) { virNetDevIPRoutePtr route = NULL; int nnodes; int ret = -1; size_t i; g_autofree xmlNodePtr *nodes = NULL; g_autofree virNetDevIPAddrPtr ip = NULL; if ((nnodes = virXPathNodeSet("./ip", ctxt, &nodes)) < 0) goto cleanup; for (i = 0; i < nnodes; i++) { if (!(ip = virDomainNetIPParseXML(nodes[i])) || VIR_APPEND_ELEMENT(def->ips, def->nips, ip) < 0) goto cleanup; } VIR_FREE(nodes); if ((nnodes = virXPathNodeSet("./route", ctxt, &nodes)) < 0) goto cleanup; for (i = 0; i < nnodes; i++) { if (!(route = virNetDevIPRouteParseXML(source, nodes[i], ctxt)) || VIR_APPEND_ELEMENT(def->routes, def->nroutes, route) < 0) goto cleanup; } ret = 0; cleanup: if (ret < 0) virNetDevIPInfoClear(def); virNetDevIPRouteFree(route); return ret; } static virNetDevCoalescePtr virDomainNetDefCoalesceParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt) { virNetDevCoalescePtr ret = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); unsigned long long tmp = 0; g_autofree char *str = NULL; ctxt->node = node; str = virXPathString("string(./rx/frames/@max)", ctxt); if (!str) goto cleanup; if (VIR_ALLOC(ret) < 0) goto cleanup; if (virStrToLong_ullp(str, NULL, 10, &tmp) < 0) { virReportError(VIR_ERR_XML_DETAIL, _("cannot parse value '%s' for coalesce parameter"), str); goto error; } if (tmp > UINT32_MAX) { virReportError(VIR_ERR_OVERFLOW, _("value '%llu' is too big for coalesce " "parameter, maximum is '%lu'"), tmp, (unsigned long) UINT32_MAX); goto error; } ret->rx_max_coalesced_frames = tmp; cleanup: return ret; error: VIR_FREE(ret); goto cleanup; } static void virDomainNetDefCoalesceFormatXML(virBufferPtr buf, virNetDevCoalescePtr coalesce) { if (!coalesce || !coalesce->rx_max_coalesced_frames) return; virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); virBufferAsprintf(buf, "\n", coalesce->rx_max_coalesced_frames); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } static int virDomainHostdevDefParseXMLCaps(xmlNodePtr node G_GNUC_UNUSED, xmlXPathContextPtr ctxt, const char *type, virDomainHostdevDefPtr def) { xmlNodePtr sourcenode; 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.ifname = virXPathString("string(./source/interface[1])", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing element in hostdev net device")); goto error; } if (virDomainNetIPInfoParseXML(_("Domain hostdev device"), ctxt, &def->source.caps.u.net.ip) < 0) 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: return ret; } virDomainControllerDefPtr virDomainDeviceFindSCSIController(const virDomainDef *def, const virDomainDeviceDriveAddress *addr) { size_t i; for (i = 0; i < def->ncontrollers; i++) { if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI && def->controllers[i]->idx == addr->controller) return def->controllers[i]; } return NULL; } int virDomainDiskDefAssignAddress(virDomainXMLOptionPtr xmlopt, virDomainDiskDefPtr def, const virDomainDef *vmdef) { 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: { virDomainDeviceDriveAddress addr = {0, 0, 0, 0}; unsigned int controller; unsigned int unit; def->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE; if (xmlopt->config.features & VIR_DOMAIN_DEF_FEATURE_WIDE_SCSI) { /* 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. */ controller = idx / 15; unit = idx % 15; /* Skip the SCSI controller at unit 7 */ if (unit >= 7) ++unit; } else { /* For a narrow SCSI bus we define the default mapping to be * 7 units per bus, 1 bus per controller, many controllers */ controller = idx / 7; unit = idx % 7; } addr.controller = controller; addr.unit = unit; if (virDomainDriveAddressIsUsedByHostdev(vmdef, VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI, &addr)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("using disk target name '%s' conflicts with " "SCSI host device address controller='%u' " "bus='%u' target='%u' unit='%u"), def->dst, controller, 0, 0, unit); return -1; } memcpy(&def->info.addr.drive, &addr, sizeof(addr)); 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 = virXMLPropStringLimit(ctxt->node, "model", VIR_SECURITY_MODEL_BUFLEN - 1); if (!(seclabel = virSecurityLabelDefNew(p))) goto error; VIR_FREE(p); /* set default value */ seclabel->type = VIR_DOMAIN_SECLABEL_DYNAMIC; p = virXMLPropStringLimit(ctxt->node, "type", VIR_SECURITY_LABEL_BUFLEN - 1); 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 = virXMLPropStringLimit(ctxt->node, "relabel", VIR_SECURITY_LABEL_BUFLEN-1); if (p) { if (virStringParseYesNo(p, &seclabel->relabel) < 0) { 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 = g_steal_pointer(&p); } /* 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 = g_steal_pointer(&p); } /* 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 = g_steal_pointer(&p); } return seclabel; error: VIR_FREE(p); virSecurityLabelDefFree(seclabel); return NULL; } static int virSecurityLabelDefsParseXML(virDomainDefPtr def, xmlXPathContextPtr ctxt, virCapsPtr caps, unsigned int flags) { VIR_XPATH_NODE_AUTORESTORE(ctxt); size_t i = 0, j; int n; virCapsHostPtr host = &caps->host; g_autofree xmlNodePtr *list = NULL; /* 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; /* 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); def->seclabels[0]->model = g_strdup(host->secModels[0].model); 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: for (; i > 0; i--) virSecurityLabelDefFree(def->seclabels[i - 1]); VIR_FREE(def->seclabels); def->nseclabels = 0; return -1; } /* Parse the from a disk or character device. */ static int virSecurityDeviceLabelDefParseXML(virSecurityDeviceLabelDefPtr **seclabels_rtn, size_t *nseclabels_rtn, xmlXPathContextPtr ctxt, unsigned int flags) { VIR_XPATH_NODE_AUTORESTORE(ctxt); virSecurityDeviceLabelDefPtr *seclabels = NULL; size_t nseclabels = 0; int n; size_t i, j; char *model, *relabel, *label, *labelskip; g_autofree xmlNodePtr *list = NULL; 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) { /* 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; } relabel = virXMLPropString(list[i], "relabel"); if (relabel != NULL) { if (virStringParseYesNo(relabel, &seclabels[i]->relabel) < 0) { 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; } } *nseclabels_rtn = nseclabels; *seclabels_rtn = seclabels; return 0; error: for (i = 0; i < nseclabels; i++) virSecurityDeviceLabelDefFree(seclabels[i]); VIR_FREE(seclabels); return -1; } static int virSecurityDeviceLabelDefValidateXML(virSecurityDeviceLabelDefPtr *seclabels, size_t nseclabels, virSecurityLabelDefPtr *vmSeclabels, size_t nvmSeclabels) { virSecurityDeviceLabelDefPtr seclabel; size_t i; size_t j; for (i = 0; i < nseclabels; i++) { seclabel = seclabels[i]; /* find the security label that it's being overridden */ for (j = 0; j < nvmSeclabels; j++) { if (STRNEQ_NULLABLE(vmSeclabels[j]->model, seclabel->model)) continue; if (!vmSeclabels[j]->relabel) { virReportError(VIR_ERR_XML_ERROR, "%s", _("label overrides require relabeling to be " "enabled at the domain level")); return -1; } } } return 0; } /* Parse the XML definition for a lease */ static virDomainLeaseDefPtr virDomainLeaseDefParseXML(xmlNodePtr node) { virDomainLeaseDefPtr def; xmlNodePtr cur; g_autofree char *lockspace = NULL; g_autofree char *key = NULL; g_autofree char *path = NULL; g_autofree char *offset = NULL; if (VIR_ALLOC(def) < 0) return NULL; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!key && virXMLNodeNameEqual(cur, "key")) { key = (char *)xmlNodeGetContent(cur); } else if (!lockspace && virXMLNodeNameEqual(cur, "lockspace")) { lockspace = (char *)xmlNodeGetContent(cur); } else if (!path && virXMLNodeNameEqual(cur, "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 = g_steal_pointer(&key); def->lockspace = g_steal_pointer(&lockspace); def->path = g_steal_pointer(&path); cleanup: return def; error: virDomainLeaseDefFree(def); def = NULL; goto cleanup; } static int virDomainDiskSourcePoolDefParse(xmlNodePtr node, virStorageSourcePoolDefPtr *srcpool) { virStorageSourcePoolDefPtr source; int ret = -1; g_autofree char *mode = NULL; *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 = g_steal_pointer(&source); ret = 0; cleanup: virStorageSourcePoolDefFree(source); return ret; } static int virDomainDiskSourceNetworkParse(xmlNodePtr node, xmlXPathContextPtr ctxt, virStorageSourcePtr src, unsigned int flags) { int tlsCfgVal; g_autofree char *protocol = NULL; g_autofree char *haveTLS = NULL; g_autofree char *tlsCfg = NULL; if (!(protocol = virXMLPropString(node, "protocol"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing network source protocol type")); return -1; } if ((src->protocol = virStorageNetProtocolTypeFromString(protocol)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown protocol type '%s'"), protocol); return -1; } if (!(src->path = virXMLPropString(node, "name")) && src->protocol != VIR_STORAGE_NET_PROTOCOL_NBD) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing name for disk source")); return -1; } if ((haveTLS = virXMLPropString(node, "tls")) && (src->haveTLS = virTristateBoolTypeFromString(haveTLS)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown disk source 'tls' setting '%s'"), haveTLS); return -1; } if ((flags & VIR_DOMAIN_DEF_PARSE_STATUS) && (tlsCfg = virXMLPropString(node, "tlsFromConfig"))) { if (virStrToLong_i(tlsCfg, NULL, 10, &tlsCfgVal) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid tlsFromConfig value: %s"), tlsCfg); return -1; } src->tlsFromConfig = !!tlsCfgVal; } /* for historical reasons we store the volume and image name in one XML * element although it complicates thing when attempting to access them. */ if (src->path && (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER || src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) { char *tmp; if (!(tmp = strchr(src->path, '/')) || tmp == src->path) { virReportError(VIR_ERR_XML_ERROR, _("can't split path '%s' into pool name and image " "name"), src->path); return -1; } src->volume = src->path; src->path = g_strdup(tmp + 1); 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 (virDomainStorageNetworkParseHosts(node, &src->hosts, &src->nhosts) < 0) return -1; virStorageSourceNetworkAssignDefaultPorts(src); virStorageSourceInitiatorParseXML(ctxt, &src->initiator); return 0; } static int virDomainDiskSourcePRParse(xmlNodePtr node, xmlXPathContextPtr ctxt, virStoragePRDefPtr *pr) { VIR_XPATH_NODE_AUTORESTORE(ctxt); ctxt->node = node; if (!(ctxt->node = virXPathNode("./reservations", ctxt))) return 0; if (!(*pr = virStoragePRDefParseXML(ctxt))) return -1; return 0; } virStorageSourcePtr virDomainStorageSourceParseBase(const char *type, const char *format, const char *index) { g_autoptr(virStorageSource) src = NULL; if (!(src = virStorageSourceNew())) return NULL; src->type = VIR_STORAGE_TYPE_FILE; if (type && (src->type = virStorageTypeFromString(type)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown storage source type '%s'"), type); return NULL; } if (format && (src->format = virStorageFileFormatTypeFromString(format)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown storage source format '%s'"), format); return NULL; } if (index && virStrToLong_uip(index, NULL, 10, &src->id) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid storage source index '%s'"), index); return NULL; } return g_steal_pointer(&src); } /** * virDomainStorageSourceParse: * @node: XML node pointing to the source element to parse * @ctxt: XPath context * @src: filled with parsed data * @flags: XML parser flags * @xmlopt: XML parser callbacks * * Parses @src definition from element pointed to by @node. Note that this * does not parse the 'type' and 'format' attributes of @src and 'type' needs * to be set correctly prior to calling this function. */ int virDomainStorageSourceParse(xmlNodePtr node, xmlXPathContextPtr ctxt, virStorageSourcePtr src, unsigned int flags, virDomainXMLOptionPtr xmlopt) { VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr tmp; 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 (virDomainDiskSourceNetworkParse(node, ctxt, src, flags) < 0) return -1; break; case VIR_STORAGE_TYPE_VOLUME: if (virDomainDiskSourcePoolDefParse(node, &src->srcpool) < 0) return -1; break; case VIR_STORAGE_TYPE_NONE: case VIR_STORAGE_TYPE_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected disk type %s"), virStorageTypeToString(src->type)); return -1; } if ((tmp = virXPathNode("./auth", ctxt)) && !(src->auth = virStorageAuthDefParse(tmp, ctxt))) return -1; if ((tmp = virXPathNode("./encryption", ctxt)) && !(src->encryption = virStorageEncryptionParseNode(tmp, ctxt))) return -1; if (virDomainDiskSourcePRParse(node, ctxt, &src->pr) < 0) return -1; if (virSecurityDeviceLabelDefParseXML(&src->seclabels, &src->nseclabels, ctxt, flags) < 0) return -1; /* 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); if ((flags & VIR_DOMAIN_DEF_PARSE_STATUS) && xmlopt && xmlopt->privateData.storageParse && (tmp = virXPathNode("./privateData", ctxt))) { ctxt->node = tmp; if (xmlopt->privateData.storageParse(ctxt, src) < 0) return -1; } return 0; } int virDomainDiskBackingStoreParse(xmlXPathContextPtr ctxt, virStorageSourcePtr src, unsigned int flags, virDomainXMLOptionPtr xmlopt) { VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr source; g_autoptr(virStorageSource) backingStore = NULL; g_autofree char *type = NULL; g_autofree char *format = NULL; g_autofree char *idx = NULL; if (!(ctxt->node = virXPathNode("./backingStore", ctxt))) return 0; /* terminator does not have a type */ if (!(type = virXMLPropString(ctxt->node, "type"))) { if (!(src->backingStore = virStorageSourceNew())) return -1; return 0; } if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) idx = virXMLPropString(ctxt->node, "index"); if (!(format = virXPathString("string(./format/@type)", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing disk backing store format")); return -1; } if (!(source = virXPathNode("./source", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing disk backing store source")); return -1; } if (!(backingStore = virDomainStorageSourceParseBase(type, format, idx))) return -1; /* backing store is always read-only */ backingStore->readonly = true; if (virDomainStorageSourceParse(source, ctxt, backingStore, flags, xmlopt) < 0 || virDomainDiskBackingStoreParse(ctxt, backingStore, flags, xmlopt) < 0) return -1; src->backingStore = g_steal_pointer(&backingStore); return 0; } #define PARSE_IOTUNE(val) \ if (virXPathULongLong("string(./iotune/" #val ")", \ ctxt, &def->blkdeviotune.val) == -2) { \ virReportError(VIR_ERR_XML_ERROR, \ _("disk iotune field '%s' must be an integer"), #val); \ return -1; \ } static int virDomainDiskDefIotuneParse(virDomainDiskDefPtr def, xmlXPathContextPtr ctxt) { PARSE_IOTUNE(total_bytes_sec); PARSE_IOTUNE(read_bytes_sec); PARSE_IOTUNE(write_bytes_sec); PARSE_IOTUNE(total_iops_sec); PARSE_IOTUNE(read_iops_sec); PARSE_IOTUNE(write_iops_sec); PARSE_IOTUNE(total_bytes_sec_max); PARSE_IOTUNE(read_bytes_sec_max); PARSE_IOTUNE(write_bytes_sec_max); PARSE_IOTUNE(total_iops_sec_max); PARSE_IOTUNE(read_iops_sec_max); PARSE_IOTUNE(write_iops_sec_max); PARSE_IOTUNE(size_iops_sec); PARSE_IOTUNE(total_bytes_sec_max_length); PARSE_IOTUNE(read_bytes_sec_max_length); PARSE_IOTUNE(write_bytes_sec_max_length); PARSE_IOTUNE(total_iops_sec_max_length); PARSE_IOTUNE(read_iops_sec_max_length); PARSE_IOTUNE(write_iops_sec_max_length); def->blkdeviotune.group_name = virXPathString("string(./iotune/group_name)", ctxt); 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")); return -1; } 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")); return -1; } 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")); return -1; } 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")); return -1; } return 0; } #undef PARSE_IOTUNE static int virDomainDiskDefMirrorParse(virDomainDiskDefPtr def, xmlNodePtr cur, xmlXPathContextPtr ctxt, unsigned int flags, virDomainXMLOptionPtr xmlopt) { xmlNodePtr mirrorNode; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *mirrorFormat = NULL; g_autofree char *mirrorType = NULL; g_autofree char *ready = NULL; g_autofree char *blockJob = NULL; g_autofree char *index = NULL; ctxt->node = cur; if ((blockJob = virXMLPropString(cur, "job"))) { if ((def->mirrorJob = virDomainBlockJobTypeFromString(blockJob)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown mirror job type '%s'"), blockJob); return -1; } } else { def->mirrorJob = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY; } if ((mirrorType = virXMLPropString(cur, "type"))) { mirrorFormat = virXPathString("string(./format/@type)", ctxt); index = virXPathString("string(./source/@index)", ctxt); } else { if (def->mirrorJob != VIR_DOMAIN_BLOCK_JOB_TYPE_COPY) { virReportError(VIR_ERR_XML_ERROR, "%s", _("mirror without type only supported " "by copy job")); return -1; } mirrorFormat = virXMLPropString(cur, "format"); } if (!(def->mirror = virDomainStorageSourceParseBase(mirrorType, mirrorFormat, index))) return -1; if (mirrorType) { if (!(mirrorNode = virXPathNode("./source", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("mirror requires source element")); return -1; } if (virDomainStorageSourceParse(mirrorNode, ctxt, def->mirror, flags, xmlopt) < 0) return -1; if (virDomainDiskBackingStoreParse(ctxt, def->mirror, flags, xmlopt) < 0) return -1; } else { /* For back-compat reasons, we handle a file name encoded as * attributes, even though we prefer modern output in the style of * backingStore */ if (!(def->mirror->path = virXMLPropString(cur, "file"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("mirror requires file name")); return -1; } } if ((ready = virXMLPropString(cur, "ready")) && (def->mirrorState = virDomainDiskMirrorStateTypeFromString(ready)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown mirror ready state %s"), ready); return -1; } return 0; } static int virDomainDiskDefGeometryParse(virDomainDiskDefPtr def, xmlNodePtr cur) { g_autofree char *tmp = NULL; if ((tmp = virXMLPropString(cur, "cyls"))) { if (virStrToLong_ui(tmp, NULL, 10, &def->geometry.cylinders) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid geometry settings (cyls)")); return -1; } VIR_FREE(tmp); } if ((tmp = virXMLPropString(cur, "heads"))) { if (virStrToLong_ui(tmp, NULL, 10, &def->geometry.heads) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid geometry settings (heads)")); return -1; } VIR_FREE(tmp); } if ((tmp = virXMLPropString(cur, "secs"))) { if (virStrToLong_ui(tmp, NULL, 10, &def->geometry.sectors) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid geometry settings (secs)")); return -1; } VIR_FREE(tmp); } if ((tmp = virXMLPropString(cur, "trans"))) { def->geometry.trans = virDomainDiskGeometryTransTypeFromString(tmp); if (def->geometry.trans <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid translation value '%s'"), tmp); return -1; } } return 0; } static int virDomainDiskSourceDefParseAuthValidate(const virStorageSource *src) { virStorageAuthDefPtr authdef = src->auth; int actUsage; if (src->type != VIR_STORAGE_TYPE_NETWORK || !authdef) return 0; if ((actUsage = virSecretUsageTypeFromString(authdef->secrettype)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown secret type '%s'"), NULLSTR(authdef->secrettype)); return -1; } if ((src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI && actUsage != VIR_SECRET_USAGE_TYPE_ISCSI) || (src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD && actUsage != VIR_SECRET_USAGE_TYPE_CEPH)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid secret type '%s'"), virSecretUsageTypeToString(actUsage)); return -1; } return 0; } static int virDomainDiskDefParseValidate(const virDomainDiskDef *def, virSecurityLabelDefPtr *vmSeclabels, size_t nvmSeclabels) { virStorageSourcePtr next; if (def->bus != VIR_DOMAIN_DISK_BUS_VIRTIO) { if (def->event_idx != VIR_TRISTATE_SWITCH_ABSENT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("disk event_idx mode supported only for virtio bus")); return -1; } if (def->ioeventfd != VIR_TRISTATE_SWITCH_ABSENT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("disk ioeventfd mode supported only for virtio bus")); return -1; } } if (def->device != VIR_DOMAIN_DISK_DEVICE_LUN) { if (def->rawio != VIR_TRISTATE_BOOL_ABSENT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("rawio can be used only with device='lun'")); return -1; } if (def->sgio != VIR_DOMAIN_DEVICE_SGIO_DEFAULT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("sgio can be used only with device='lun'")); return -1; } } 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"), virDomainDiskBusTypeToString(def->bus)); return -1; } 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"), virDomainDiskBusTypeToString(def->bus)); return -1; } if (def->removable != VIR_TRISTATE_SWITCH_ABSENT && def->bus != VIR_DOMAIN_DISK_BUS_USB) { virReportError(VIR_ERR_XML_ERROR, "%s", _("removable is only valid for usb disks")); return -1; } if (def->startupPolicy != VIR_DOMAIN_STARTUP_POLICY_DEFAULT) { if (def->src->type == VIR_STORAGE_TYPE_NETWORK) { virReportError(VIR_ERR_XML_ERROR, _("Setting disk %s is not allowed for " "disk of network type"), virDomainStartupPolicyTypeToString(def->startupPolicy)); return -1; } if (def->device != VIR_DOMAIN_DISK_DEVICE_CDROM && def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY && def->startupPolicy == VIR_DOMAIN_STARTUP_POLICY_REQUISITE) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Setting disk 'requisite' is allowed only for " "cdrom or floppy")); return -1; } } for (next = def->src; next; next = next->backingStore) { if (virDomainDiskSourceDefParseAuthValidate(next) < 0) return -1; if (next->encryption) { virStorageEncryptionPtr encryption = next->encryption; if (encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS && encryption->encinfo.cipher_name) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("supplying for domain disk definition " "is unnecessary")); return -1; } } if (virSecurityDeviceLabelDefValidateXML(next->seclabels, next->nseclabels, vmSeclabels, nvmSeclabels) < 0) return -1; } return 0; } static int virDomainDiskDefDriverParseXML(virDomainDiskDefPtr def, xmlNodePtr cur) { g_autofree char *tmp = NULL; def->driverName = virXMLPropString(cur, "name"); if ((tmp = virXMLPropString(cur, "cache")) && (def->cachemode = virDomainDiskCacheTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk cache mode '%s'"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "error_policy")) && (def->error_policy = virDomainDiskErrorPolicyTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk error policy '%s'"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "rerror_policy")) && (((def->rerror_policy = virDomainDiskErrorPolicyTypeFromString(tmp)) <= 0) || (def->rerror_policy == VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk read error policy '%s'"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "io")) && (def->iomode = virDomainDiskIoTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk io mode '%s'"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "ioeventfd")) && (def->ioeventfd = virTristateSwitchTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk ioeventfd mode '%s'"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "event_idx")) && (def->event_idx = virTristateSwitchTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk event_idx mode '%s'"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "copy_on_read")) && (def->copy_on_read = virTristateSwitchTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk copy_on_read mode '%s'"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "discard")) && (def->discard = virDomainDiskDiscardTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk discard mode '%s'"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "iothread")) && (virStrToLong_uip(tmp, NULL, 10, &def->iothread) < 0 || def->iothread == 0)) { virReportError(VIR_ERR_XML_ERROR, _("Invalid iothread attribute in disk driver element: %s"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "type"))) { if (STREQ(tmp, "aio")) { /* Xen back-compat */ def->src->format = VIR_STORAGE_FILE_RAW; } else { if ((def->src->format = virStorageFileFormatTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown driver format value '%s'"), tmp); return -1; } } VIR_FREE(tmp); } if ((tmp = virXMLPropString(cur, "detect_zeroes")) && (def->detect_zeroes = virDomainDiskDetectZeroesTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown driver detect_zeroes value '%s'"), tmp); return -1; } VIR_FREE(tmp); if ((tmp = virXMLPropString(cur, "queues")) && virStrToLong_uip(tmp, NULL, 10, &def->queues) < 0) { virReportError(VIR_ERR_XML_ERROR, _("'queues' attribute must be positive number: %s"), tmp); return -1; } return 0; } static int virDomainDiskDefParsePrivateData(xmlXPathContextPtr ctxt, virDomainDiskDefPtr disk, virDomainXMLOptionPtr xmlopt) { xmlNodePtr private_node = virXPathNode("./privateData", ctxt); VIR_XPATH_NODE_AUTORESTORE(ctxt); int ret = -1; if (!xmlopt || !xmlopt->privateData.diskParse || !private_node) return 0; ctxt->node = private_node; if (xmlopt->privateData.diskParse(ctxt, disk) < 0) goto cleanup; ret = 0; cleanup: 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, virSecurityLabelDefPtr* vmSeclabels, int nvmSeclabels, unsigned int flags) { virDomainDiskDefPtr def; xmlNodePtr cur; VIR_XPATH_NODE_AUTORESTORE(ctxt); bool source = false; virStorageEncryptionPtr encryption = NULL; g_autoptr(virStorageAuthDef) authdef = NULL; g_autofree char *tmp = NULL; g_autofree char *snapshot = NULL; g_autofree char *rawio = NULL; g_autofree char *sgio = NULL; g_autofree char *target = NULL; g_autofree char *bus = NULL; g_autofree char *serial = NULL; g_autofree char *startupPolicy = NULL; g_autofree char *tray = NULL; g_autofree char *removable = NULL; g_autofree char *logical_block_size = NULL; g_autofree char *physical_block_size = NULL; g_autofree char *wwn = NULL; g_autofree char *vendor = NULL; g_autofree char *product = NULL; g_autofree char *domain_name = NULL; if (!(def = virDomainDiskDefNew(xmlopt))) return NULL; ctxt->node = node; /* defaults */ def->src->type = VIR_STORAGE_TYPE_FILE; def->device = VIR_DOMAIN_DISK_DEVICE_DISK; if ((tmp = virXMLPropString(node, "type")) && (def->src->type = virStorageTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk type '%s'"), tmp); goto error; } VIR_FREE(tmp); if ((tmp = virXMLPropString(node, "device")) && (def->device = virDomainDiskDeviceTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk device '%s'"), tmp); goto error; } VIR_FREE(tmp); if ((tmp = virXMLPropString(node, "model")) && (def->model = virDomainDiskModelTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk model '%s'"), tmp); goto error; } VIR_FREE(tmp); snapshot = virXMLPropString(node, "snapshot"); rawio = virXMLPropString(node, "rawio"); sgio = virXMLPropString(node, "sgio"); for (cur = node->children; cur != NULL; cur = cur->next) { if (cur->type != XML_ELEMENT_NODE) continue; if (!source && virXMLNodeNameEqual(cur, "source")) { if (virDomainStorageSourceParse(cur, ctxt, def->src, flags, xmlopt) < 0) goto error; /* If we've already found an as a child of and * we find one as a child of , then force an error to * avoid ambiguity */ if (authdef && def->src->auth) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("an definition already found for " "the definition")); goto error; } if (def->src->auth) def->src->authInherited = true; /* Similarly for - it's a child of too * and we cannot find in both places */ if (encryption && def->src->encryption) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("an definition already found for " "the definition")); goto error; } if (def->src->encryption) def->src->encryptionInherited = true; source = true; startupPolicy = virXMLPropString(cur, "startupPolicy"); if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) && (tmp = virXMLPropString(cur, "index")) && virStrToLong_uip(tmp, NULL, 10, &def->src->id) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid disk index '%s'"), tmp); goto error; } VIR_FREE(tmp); } else if (!target && virXMLNodeNameEqual(cur, "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 && virXMLNodeNameEqual(cur, "backenddomain")) { domain_name = virXMLPropString(cur, "name"); } else if (virXMLNodeNameEqual(cur, "geometry")) { if (virDomainDiskDefGeometryParse(def, cur) < 0) goto error; } else if (virXMLNodeNameEqual(cur, "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 (!virDomainDiskGetDriver(def) && virXMLNodeNameEqual(cur, "driver")) { if (virDomainVirtioOptionsParseXML(cur, &def->virtio) < 0) goto error; if (virDomainDiskDefDriverParseXML(def, cur) < 0) goto error; } else if (!def->mirror && virXMLNodeNameEqual(cur, "mirror") && !(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) { if (virDomainDiskDefMirrorParse(def, cur, ctxt, flags, xmlopt) < 0) goto error; } else if (!authdef && virXMLNodeNameEqual(cur, "auth")) { /* If we've already parsed and found an child, * then generate an error to avoid ambiguity */ if (def->src->authInherited) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("an definition already found for " "disk source")); goto error; } if (!(authdef = virStorageAuthDefParse(cur, ctxt))) goto error; } else if (virXMLNodeNameEqual(cur, "iotune")) { if (virDomainDiskDefIotuneParse(def, ctxt) < 0) goto error; } else if (virXMLNodeNameEqual(cur, "readonly")) { def->src->readonly = true; } else if (virXMLNodeNameEqual(cur, "shareable")) { def->src->shared = true; } else if (virXMLNodeNameEqual(cur, "transient")) { def->transient = true; } else if (!encryption && virXMLNodeNameEqual(cur, "encryption")) { /* If we've already parsed and found an child, * then generate an error to avoid ambiguity */ if (def->src->encryptionInherited) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("an definition already found for " "disk source")); goto error; } if (!(encryption = virStorageEncryptionParseNode(cur, ctxt))) goto error; } else if (!serial && virXMLNodeNameEqual(cur, "serial")) { serial = (char *)xmlNodeGetContent(cur); } else if (!wwn && virXMLNodeNameEqual(cur, "wwn")) { wwn = (char *)xmlNodeGetContent(cur); if (!virValidateWWN(wwn)) goto error; } else if (!vendor && virXMLNodeNameEqual(cur, "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 (!virStringIsPrintable(vendor)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("disk vendor is not printable string")); goto error; } } else if (!product && virXMLNodeNameEqual(cur, "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 (!virStringIsPrintable(product)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("disk product is not printable string")); goto error; } } else if (virXMLNodeNameEqual(cur, "boot")) { /* boot is parsed as part of virDomainDeviceInfoParseXML */ } } /* Reset def->src->type in case when 'source' was not present */ if (!source) def->src->type = VIR_STORAGE_TYPE_FILE; /* 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 (!target && !(flags & VIR_DOMAIN_DEF_PARSE_DISK_SOURCE)) { if (def->src->srcpool) { 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) { 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; } } if (removable) { if ((def->removable = virTristateSwitchTypeFromString(removable)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown disk removable status '%s'"), removable); goto error; } } if (virDomainDeviceInfoParseXML(xmlopt, node, &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; } def->startupPolicy = val; } def->dst = g_steal_pointer(&target); if (authdef) def->src->auth = g_steal_pointer(&authdef); if (encryption) def->src->encryption = g_steal_pointer(&encryption); def->domain_name = g_steal_pointer(&domain_name); def->serial = g_steal_pointer(&serial); def->wwn = g_steal_pointer(&wwn); def->vendor = g_steal_pointer(&vendor); def->product = g_steal_pointer(&product); if (virDomainDiskBackingStoreParse(ctxt, def->src, flags, xmlopt) < 0) goto error; if (flags & VIR_DOMAIN_DEF_PARSE_STATUS && virDomainDiskDefParsePrivateData(ctxt, def, xmlopt) < 0) goto error; if (virDomainDiskDefParseValidate(def, vmSeclabels, nvmSeclabels) < 0) goto error; cleanup: virStorageEncryptionFree(encryption); 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) { unsigned long long bytes; g_autofree char *xpath_full = NULL; g_autofree char *unit = NULL; g_autofree char *bytes_str = NULL; *val = 0; if (virAsprintf(&xpath_full, "string(%s)", xpath) < 0) return -1; bytes_str = virXPathString(xpath_full, ctxt); if (!bytes_str) { if (!required) return 0; virReportError(VIR_ERR_XML_ERROR, _("missing element or attribute '%s'"), xpath); return -1; } 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); return -1; } if ((units_xpath && virAsprintf(&xpath_full, "string(%s)", units_xpath) < 0) || (!units_xpath && virAsprintf(&xpath_full, "string(%s/@unit)", xpath) < 0)) return -1; unit = virXPathString(xpath_full, ctxt); if (virScaleInteger(&bytes, unit, scale, max) < 0) return -1; *val = bytes; return 1; } /** * 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) { unsigned long long bytes, max; max = virMemoryMaxValue(capped); if (virDomainParseScaledValue(xpath, units_xpath, ctxt, &bytes, 1024, max, required) < 0) return -1; /* Yes, we really do use kibibytes for our internal sizing. */ *mem = VIR_DIV_UP(bytes, 1024); if (*mem >= VIR_DIV_UP(max, 1024)) { virReportError(VIR_ERR_OVERFLOW, "%s", _("size value too large")); return -1; } return 0; } /** * 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; } bool virDomainDefHasMemoryHotplug(const virDomainDef *def) { return def->mem.memory_slots > 0 || def->mem.max_memory > 0; } /** * virDomainDefGetMemoryInitial: * @def: domain definition * * Returns the size of the initial amount of guest memory. The initial amount * is the memory size excluding possible memory modules. */ unsigned long long virDomainDefGetMemoryInitial(const virDomainDef *def) { size_t i; unsigned long long ret = def->mem.total_memory; for (i = 0; i < def->nmems; i++) ret -= def->mems[i]->size; return ret; } /** * virDomainDefSetMemoryTotal: * @def: domain definition * @size: size to set * * Sets the total memory size in @def. This value needs to include possible * additional memory modules. */ void virDomainDefSetMemoryTotal(virDomainDefPtr def, unsigned long long size) { def->mem.total_memory = size; } /** * virDomainDefGetMemoryTotal: * @def: domain definition * * Returns the current maximum memory size usable by the domain described by * @def. This size includes possible additional memory devices. */ unsigned long long virDomainDefGetMemoryTotal(const virDomainDef *def) { return def->mem.total_memory; } 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); else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE) return virDomainControllerModelIDETypeFromString(model); else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL) return virDomainControllerModelVirtioSerialTypeFromString(model); return -1; } static const char * virDomainControllerModelTypeToString(virDomainControllerDefPtr def, int model) { if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) return virDomainControllerModelSCSITypeToString(model); else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) return virDomainControllerModelUSBTypeToString(model); else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) return virDomainControllerModelPCITypeToString(model); else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE) return virDomainControllerModelIDETypeToString(model); else if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL) return virDomainControllerModelVirtioSerialTypeToString(model); return NULL; } /* Parse the XML definition for a controller * @param node XML nodeset to parse for controller definition */ static virDomainControllerDefPtr virDomainControllerDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainControllerDefPtr def = NULL; int type = 0; xmlNodePtr cur = NULL; bool processedModel = false; bool processedTarget = false; int numaNode = -1; int ports = -1; VIR_XPATH_NODE_AUTORESTORE(ctxt); int rc; g_autofree char *typeStr = NULL; g_autofree char *idx = NULL; g_autofree char *model = NULL; g_autofree char *queues = NULL; g_autofree char *cmd_per_lun = NULL; g_autofree char *max_sectors = NULL; g_autofree char *modelName = NULL; g_autofree char *chassisNr = NULL; g_autofree char *chassis = NULL; g_autofree char *port = NULL; g_autofree char *busNr = NULL; g_autofree char *targetIndex = NULL; g_autofree char *ioeventfd = NULL; g_autofree char *portsStr = NULL; g_autofree char *iothread = NULL; ctxt->node = node; typeStr = virXMLPropString(node, "type"); if (typeStr) { if ((type = virDomainControllerTypeFromString(typeStr)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown controller type '%s'"), typeStr); goto error; } } if (!(def = virDomainControllerDefNew(type))) 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; } } idx = virXMLPropString(node, "index"); if (idx) { unsigned int idxVal; if (virStrToLong_ui(idx, NULL, 10, &idxVal) < 0 || idxVal > INT_MAX) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Cannot parse controller index %s"), idx); goto error; } def->idx = idxVal; } cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (virXMLNodeNameEqual(cur, "driver")) { queues = virXMLPropString(cur, "queues"); cmd_per_lun = virXMLPropString(cur, "cmd_per_lun"); max_sectors = virXMLPropString(cur, "max_sectors"); ioeventfd = virXMLPropString(cur, "ioeventfd"); iothread = virXMLPropString(cur, "iothread"); if (virDomainVirtioOptionsParseXML(cur, &def->virtio) < 0) goto error; } else if (virXMLNodeNameEqual(cur, "model")) { if (processedModel) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Multiple elements in " "controller definition not allowed")); goto error; } modelName = virXMLPropString(cur, "name"); processedModel = true; } else if (virXMLNodeNameEqual(cur, "target")) { if (processedTarget) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Multiple elements in " "controller definition not allowed")); goto error; } chassisNr = virXMLPropString(cur, "chassisNr"); chassis = virXMLPropString(cur, "chassis"); port = virXMLPropString(cur, "port"); busNr = virXMLPropString(cur, "busNr"); targetIndex = virXMLPropString(cur, "index"); processedTarget = true; } } cur = cur->next; } /* node is parsed differently from target attributes because * someone thought it should be a subelement instead... */ rc = virXPathInt("string(./target/node)", ctxt, &numaNode); if (rc == -2 || (rc == 0 && numaNode < 0)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid NUMA node in target")); goto error; } 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 (ioeventfd && (def->ioeventfd = virTristateSwitchTypeFromString(ioeventfd)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Malformed 'ioeventfd' value %s"), ioeventfd); goto error; } if (iothread) { if (virStrToLong_uip(iothread, NULL, 10, &def->iothread) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid 'iothread' value '%s'"), iothread); 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(xmlopt, node, &def->info, flags) < 0) { goto error; } portsStr = virXMLPropString(node, "ports"); if (portsStr) { int r = virStrToLong_i(portsStr, NULL, 10, &ports); if (r != 0 || ports < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid ports: %s"), portsStr); goto error; } } switch (def->type) { case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: { g_autofree char *vectors = virXMLPropString(node, "vectors"); def->opts.vioserial.ports = ports; 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); goto error; } } 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; } def->opts.usbopts.ports = ports; break; } case VIR_DOMAIN_CONTROLLER_TYPE_PCI: switch ((virDomainControllerModelPCI) 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 ((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); } case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE: case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE: case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE: case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT: case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT: case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT: case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS: case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS: case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT: case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST: /* Other controller models don't require extra checks */ break; } if (modelName && (def->opts.pciopts.modelName = virDomainControllerPCIModelNameTypeFromString(modelName)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown PCI controller model name '%s'"), modelName); goto error; } if (chassisNr) { if (virStrToLong_i(chassisNr, NULL, 0, &def->opts.pciopts.chassisNr) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid chassisNr '%s' in PCI controller"), chassisNr); goto error; } if (def->opts.pciopts.chassisNr < 1 || def->opts.pciopts.chassisNr > 255) { virReportError(VIR_ERR_XML_ERROR, _("PCI controller chassisNr '%s' out of range " "- must be 1-255"), chassisNr); goto error; } } if (chassis) { if (virStrToLong_i(chassis, NULL, 0, &def->opts.pciopts.chassis) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid chassis '%s' in PCI controller"), chassis); goto error; } if (def->opts.pciopts.chassis < 0 || def->opts.pciopts.chassis > 255) { virReportError(VIR_ERR_XML_ERROR, _("PCI controller chassis '%s' out of range " "- must be 0-255"), chassis); goto error; } } if (port) { if (virStrToLong_i(port, NULL, 0, &def->opts.pciopts.port) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid port '%s' in PCI controller"), port); goto error; } if (def->opts.pciopts.port < 0 || def->opts.pciopts.port > 255) { virReportError(VIR_ERR_XML_ERROR, _("PCI controller port '%s' out of range " "- must be 0-255"), port); goto error; } } if (busNr) { if (virStrToLong_i(busNr, NULL, 0, &def->opts.pciopts.busNr) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid busNr '%s' in PCI controller"), busNr); goto error; } if (def->opts.pciopts.busNr < 1 || def->opts.pciopts.busNr > 254) { virReportError(VIR_ERR_XML_ERROR, _("PCI controller busNr '%s' out of range " "- must be 1-254"), busNr); goto error; } } if (targetIndex) { if (virStrToLong_i(targetIndex, NULL, 0, &def->opts.pciopts.targetIndex) < 0 || def->opts.pciopts.targetIndex == -1) { virReportError(VIR_ERR_XML_ERROR, _("Invalid target index '%s' in PCI controller"), targetIndex); goto error; } } if (numaNode >= 0) { if (def->idx == 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("The PCI controller with index=0 can't " "be associated with a NUMA node")); goto error; } def->opts.pciopts.numaNode = numaNode; } break; case VIR_DOMAIN_CONTROLLER_TYPE_XENBUS: { g_autofree char *gntframes = virXMLPropString(node, "maxGrantFrames"); if (gntframes) { int r = virStrToLong_i(gntframes, NULL, 10, &def->opts.xenbusopts.maxGrantFrames); if (r != 0 || def->opts.xenbusopts.maxGrantFrames < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid maxGrantFrames: %s"), gntframes); goto error; } } break; } default: break; } cleanup: 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(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { VIR_XPATH_NODE_AUTORESTORE(ctxt); virDomainFSDefPtr def; xmlNodePtr cur; g_autofree char *type = NULL; g_autofree char *fsdriver = NULL; g_autofree char *source = NULL; g_autofree char *target = NULL; g_autofree char *format = NULL; g_autofree char *accessmode = NULL; g_autofree char *wrpolicy = NULL; g_autofree char *usage = NULL; g_autofree char *units = NULL; g_autofree char *model = NULL; ctxt->node = node; if (!(def = virDomainFSDefNew())) 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; } model = virXMLPropString(node, "model"); if (model) { if ((def->model = virDomainFSModelTypeFromString(model)) < 0 || def->model == VIR_DOMAIN_FS_MODEL_DEFAULT) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown model '%s'"), model); goto error; } } 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 && virXMLNodeNameEqual(cur, "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 (def->type == VIR_DOMAIN_FS_TYPE_VOLUME) { def->src->type = VIR_STORAGE_TYPE_VOLUME; if (virDomainDiskSourcePoolDefParse(cur, &def->src->srcpool) < 0) goto error; } } else if (!target && virXMLNodeNameEqual(cur, "target")) { target = virXMLPropString(cur, "dir"); } else if (virXMLNodeNameEqual(cur, "readonly")) { def->readonly = true; } else if (virXMLNodeNameEqual(cur, "driver")) { if (!fsdriver) fsdriver = virXMLPropString(cur, "type"); if (!wrpolicy) wrpolicy = virXMLPropString(cur, "wrpolicy"); if (!format) format = virXMLPropString(cur, "format"); if (virDomainVirtioOptionsParseXML(cur, &def->virtio) < 0) goto error; } } 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 && def->type != VIR_DOMAIN_FS_TYPE_VOLUME) { 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->path = g_steal_pointer(&source); def->dst = g_steal_pointer(&target); if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error; cleanup: 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; VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr bandwidth_node = NULL; xmlNodePtr vlanNode; xmlNodePtr virtPortNode; g_autofree char *type = NULL; g_autofree char *mode = NULL; g_autofree char *addrtype = NULL; g_autofree char *trustGuestRxFilters = NULL; g_autofree 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) { xmlNodePtr sourceNode = virXPathNode("./source[1]", ctxt); if (sourceNode) { actual->data.direct.linkdev = virXMLPropString(sourceNode, "dev"); mode = virXMLPropString(sourceNode, "mode"); 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->parentnet = 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)) addrtype = g_strdup("usb"); 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_BRIDGE || actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) { g_autofree char *class_id = NULL; xmlNodePtr sourceNode; 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); goto error; } sourceNode = virXPathNode("./source", ctxt); if (sourceNode) { char *brname = virXMLPropString(sourceNode, "bridge"); 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 = virXMLPropString(sourceNode, "macTableManager"); 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, NULL, bandwidth_node, actual->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) goto error; vlanNode = virXPathNode("./vlan", ctxt); if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &actual->vlan) < 0) goto error; *def = g_steal_pointer(&actual); ret = 0; error: virDomainActualNetDefFree(actual); return ret; } #define NET_MODEL_CHARS \ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-" int virDomainNetAppendIPAddress(virDomainNetDefPtr def, const char *address, int family, unsigned int prefix) { virNetDevIPAddrPtr 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->guestIP.ips, def->guestIP.nips, ipDef) < 0) goto error; return 0; error: VIR_FREE(ipDef); return -1; } static int virDomainChrSourceReconnectDefParseXML(virDomainChrSourceReconnectDefPtr def, xmlNodePtr node, xmlXPathContextPtr ctxt) { int ret = -1; int tmpVal; VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr cur; g_autofree char *tmp = NULL; ctxt->node = node; if ((cur = virXPathNode("./reconnect", ctxt))) { if ((tmp = virXMLPropString(cur, "enabled"))) { if ((tmpVal = virTristateBoolTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid reconnect enabled value: '%s'"), tmp); goto cleanup; } def->enabled = tmpVal; VIR_FREE(tmp); } if (def->enabled == VIR_TRISTATE_BOOL_YES) { if ((tmp = virXMLPropString(cur, "timeout"))) { if (virStrToLong_ui(tmp, NULL, 10, &def->timeout) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid reconnect timeout value: '%s'"), tmp); goto cleanup; } } else { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing timeout for chardev with " "reconnect enabled")); goto cleanup; } } } ret = 0; cleanup: return ret; } /* 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, char *prefix, unsigned int flags) { virDomainNetDefPtr def; virDomainHostdevDefPtr hostdev; xmlNodePtr cur; xmlNodePtr tmpNode; virHashTablePtr filterparams = NULL; virDomainActualNetDefPtr actual = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); virDomainChrSourceReconnectDef reconnect = {0}; int rv, val; g_autofree char *macaddr = NULL; g_autofree char *type = NULL; g_autofree char *network = NULL; g_autofree char *portgroup = NULL; g_autofree char *portid = NULL; g_autofree char *bridge = NULL; g_autofree char *dev = NULL; g_autofree char *ifname = NULL; g_autofree char *managed_tap = NULL; g_autofree char *ifname_guest = NULL; g_autofree char *ifname_guest_actual = NULL; g_autofree char *script = NULL; g_autofree char *address = NULL; g_autofree char *port = NULL; g_autofree char *localaddr = NULL; g_autofree char *localport = NULL; g_autofree char *model = NULL; g_autofree char *backend = NULL; g_autofree char *txmode = NULL; g_autofree char *ioeventfd = NULL; g_autofree char *event_idx = NULL; g_autofree char *queues = NULL; g_autofree char *rx_queue_size = NULL; g_autofree char *tx_queue_size = NULL; g_autofree char *str = NULL; g_autofree char *filter = NULL; g_autofree char *internal = NULL; g_autofree char *mode = NULL; g_autofree char *linkstate = NULL; g_autofree char *addrtype = NULL; g_autofree char *domain_name = NULL; g_autofree char *vhostuser_mode = NULL; g_autofree char *vhostuser_path = NULL; g_autofree char *vhostuser_type = NULL; g_autofree char *trustGuestRxFilters = NULL; g_autofree char *vhost_path = NULL; if (!(def = virDomainNetDefNew(xmlopt))) 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 (virXMLNodeNameEqual(cur, "source")) { xmlNodePtr tmpnode = ctxt->node; ctxt->node = cur; if (virDomainNetIPInfoParseXML(_("interface host IP"), ctxt, &def->hostIP) < 0) goto error; ctxt->node = tmpnode; } if (!macaddr && virXMLNodeNameEqual(cur, "mac")) { macaddr = virXMLPropString(cur, "address"); } else if (!network && def->type == VIR_DOMAIN_NET_TYPE_NETWORK && virXMLNodeNameEqual(cur, "source")) { network = virXMLPropString(cur, "network"); portgroup = virXMLPropString(cur, "portgroup"); if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) portid = virXMLPropString(cur, "portid"); } else if (!internal && def->type == VIR_DOMAIN_NET_TYPE_INTERNAL && virXMLNodeNameEqual(cur, "source")) { internal = virXMLPropString(cur, "name"); } else if (!bridge && def->type == VIR_DOMAIN_NET_TYPE_BRIDGE && virXMLNodeNameEqual(cur, "source")) { bridge = virXMLPropString(cur, "bridge"); } else if (!dev && def->type == VIR_DOMAIN_NET_TYPE_DIRECT && virXMLNodeNameEqual(cur, "source")) { dev = virXMLPropString(cur, "dev"); mode = virXMLPropString(cur, "mode"); } else if (!dev && def->type == VIR_DOMAIN_NET_TYPE_ETHERNET && virXMLNodeNameEqual(cur, "source")) { /* This clause is only necessary because from 2010 to * 2016 it was possible (but never documented) to * configure the name of the guest-side interface of * an openvz domain with . That * was blatant misuse of , so was likely * (hopefully) never used, but just in case there was * somebody using it, we need to generate an error. If * the openvz driver is ever deprecated, this clause * can be removed from here. */ if ((dev = virXMLPropString(cur, "dev"))) { virReportError(VIR_ERR_XML_ERROR, _("Invalid attempt to set " "device name with . " "Use (for host-side) " "or (for guest-side) instead."), dev, dev, dev); goto error; } } else if (!vhostuser_path && !vhostuser_mode && !vhostuser_type && def->type == VIR_DOMAIN_NET_TYPE_VHOSTUSER && virXMLNodeNameEqual(cur, "source")) { vhostuser_type = virXMLPropString(cur, "type"); vhostuser_path = virXMLPropString(cur, "path"); vhostuser_mode = virXMLPropString(cur, "mode"); if (virDomainChrSourceReconnectDefParseXML(&reconnect, cur, ctxt) < 0) goto error; } else if (!def->virtPortProfile && virXMLNodeNameEqual(cur, "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 || def->type == VIR_DOMAIN_NET_TYPE_UDP) && virXMLNodeNameEqual(cur, "source")) { address = virXMLPropString(cur, "address"); port = virXMLPropString(cur, "port"); if (!localaddr && def->type == VIR_DOMAIN_NET_TYPE_UDP) { xmlNodePtr tmpnode = ctxt->node; ctxt->node = cur; if ((tmpNode = virXPathNode("./local", ctxt))) { localaddr = virXMLPropString(tmpNode, "address"); localport = virXMLPropString(tmpNode, "port"); } ctxt->node = tmpnode; } } else if (!ifname && virXMLNodeNameEqual(cur, "target")) { ifname = virXMLPropString(cur, "dev"); managed_tap = virXMLPropString(cur, "managed"); } else if ((!ifname_guest || !ifname_guest_actual) && virXMLNodeNameEqual(cur, "guest")) { ifname_guest = virXMLPropString(cur, "dev"); ifname_guest_actual = virXMLPropString(cur, "actual"); } else if (!linkstate && virXMLNodeNameEqual(cur, "link")) { linkstate = virXMLPropString(cur, "state"); } else if (!script && virXMLNodeNameEqual(cur, "script")) { script = virXMLPropString(cur, "path"); } else if (!domain_name && virXMLNodeNameEqual(cur, "backenddomain")) { domain_name = virXMLPropString(cur, "name"); } else if (virXMLNodeNameEqual(cur, "model")) { model = virXMLPropString(cur, "type"); } else if (virXMLNodeNameEqual(cur, "driver")) { backend = virXMLPropString(cur, "name"); txmode = virXMLPropString(cur, "txmode"); ioeventfd = virXMLPropString(cur, "ioeventfd"); event_idx = virXMLPropString(cur, "event_idx"); queues = virXMLPropString(cur, "queues"); rx_queue_size = virXMLPropString(cur, "rx_queue_size"); tx_queue_size = virXMLPropString(cur, "tx_queue_size"); if (virDomainVirtioOptionsParseXML(cur, &def->virtio) < 0) goto error; } else if (virXMLNodeNameEqual(cur, "filterref")) { if (filter) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid specification of multiple s " "in a single ")); goto error; } filter = virXMLPropString(cur, "filter"); virHashFree(filterparams); filterparams = virNWFilterParseParamAttributes(cur); } else if (virXMLNodeNameEqual(cur, "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 && virXMLNodeNameEqual(cur, "actual")) { if (virDomainActualNetDefParseXML(cur, ctxt, def, &actual, flags) < 0) { goto error; } } else if (virXMLNodeNameEqual(cur, "bandwidth")) { if (virNetDevBandwidthParse(&def->bandwidth, NULL, cur, def->type == VIR_DOMAIN_NET_TYPE_NETWORK) < 0) goto error; } else if (virXMLNodeNameEqual(cur, "vlan")) { if (virNetDevVlanParse(cur, ctxt, &def->vlan) < 0) goto error; } else if (virXMLNodeNameEqual(cur, "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); def->mac_generated = true; } if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags | VIR_DOMAIN_DEF_PARSE_ALLOW_BOOT | VIR_DOMAIN_DEF_PARSE_ALLOW_ROM) < 0) { goto error; } if (model != NULL && virDomainNetSetModelString(def, model) < 0) 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; } if (portid && virUUIDParse(portid, def->data.network.portid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to parse port id '%s'"), portid); goto error; } def->data.network.name = g_steal_pointer(&network); def->data.network.portgroup = g_steal_pointer(&portgroup); def->data.network.actual = g_steal_pointer(&actual); break; case VIR_DOMAIN_NET_TYPE_VHOSTUSER: if (!virDomainNetIsVirtioModel(def)) { 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 (!(def->data.vhostuser = virDomainChrSourceDefNew(xmlopt))) goto error; def->data.vhostuser->type = VIR_DOMAIN_CHR_TYPE_UNIX; def->data.vhostuser->data.nix.path = g_steal_pointer(&vhostuser_path); if (STREQ(vhostuser_mode, "server")) { def->data.vhostuser->data.nix.listen = true; if (reconnect.enabled == VIR_TRISTATE_BOOL_YES) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("'reconnect' attribute unsupported " "'server' mode for ")); goto error; } } else if (STREQ(vhostuser_mode, "client")) { def->data.vhostuser->data.nix.listen = false; def->data.vhostuser->data.nix.reconnect = reconnect; } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Wrong 'mode' attribute " "specified with ")); goto error; } 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 = g_steal_pointer(&bridge); break; case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_MCAST: case VIR_DOMAIN_NET_TYPE_UDP: 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 || def->type == VIR_DOMAIN_NET_TYPE_UDP) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'address' attribute " "specified with socket interface")); goto error; } } else { def->data.socket.address = g_steal_pointer(&address); } if (def->type != VIR_DOMAIN_NET_TYPE_UDP) break; if (localport == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'port' attribute " "specified with socket interface")); goto error; } if (virStrToLong_i(localport, NULL, 10, &def->data.socket.localport) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot parse 'port' attribute " "with socket interface")); goto error; } if (localaddr == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("No 'address' attribute " "specified with socket interface")); goto error; } else { def->data.socket.localaddr = g_steal_pointer(&localaddr); } 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 = g_steal_pointer(&internal); 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 = g_steal_pointer(&dev); if (ifname && flags & VIR_DOMAIN_DEF_PARSE_INACTIVE && (STRPREFIX(ifname, VIR_NET_GENERATED_MACVTAP_PREFIX) || STRPREFIX(ifname, VIR_NET_GENERATED_MACVLAN_PREFIX))) { VIR_FREE(ifname); } break; case VIR_DOMAIN_NET_TYPE_HOSTDEV: hostdev = &def->data.hostdev.def; hostdev->parentnet = 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)) addrtype = g_strdup("usb"); hostdev->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS; if (virDomainHostdevDefParseXMLSubsys(node, ctxt, addrtype, hostdev, flags) < 0) { goto error; } break; case VIR_DOMAIN_NET_TYPE_ETHERNET: case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_LAST: break; } if (virDomainNetIPInfoParseXML(_("guest interface"), ctxt, &def->guestIP) < 0) goto error; if (managed_tap) { if (STREQ(managed_tap, "no")) { def->managed_tap = VIR_TRISTATE_BOOL_NO; } else if (STREQ(managed_tap, "yes")) { def->managed_tap = VIR_TRISTATE_BOOL_YES; } else { virReportError(VIR_ERR_XML_ERROR, _("invalid 'managed' value '%s'"), managed_tap); goto error; } } if (def->managed_tap != VIR_TRISTATE_BOOL_NO && ifname && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) && (STRPREFIX(ifname, VIR_NET_GENERATED_TAP_PREFIX) || (prefix && STRPREFIX(ifname, prefix)))) { /* An auto-generated target name, blank it out */ VIR_FREE(ifname); } if (script != NULL) def->script = g_steal_pointer(&script); if (domain_name != NULL) def->domain_name = g_steal_pointer(&domain_name); if (ifname != NULL) def->ifname = g_steal_pointer(&ifname); if (ifname_guest != NULL) def->ifname_guest = g_steal_pointer(&ifname_guest); if (ifname_guest_actual != NULL) def->ifname_guest_actual = g_steal_pointer(&ifname_guest_actual); if (def->type != VIR_DOMAIN_NET_TYPE_HOSTDEV && virDomainNetIsVirtioModel(def)) { 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; } if (q > 1) def->driver.virtio.queues = q; } if (rx_queue_size) { unsigned int q; if (virStrToLong_uip(rx_queue_size, NULL, 10, &q) < 0) { virReportError(VIR_ERR_XML_DETAIL, _("'rx_queue_size' attribute must be positive number: %s"), rx_queue_size); goto error; } def->driver.virtio.rx_queue_size = q; } if (tx_queue_size) { unsigned int q; if (virStrToLong_uip(tx_queue_size, NULL, 10, &q) < 0) { virReportError(VIR_ERR_XML_DETAIL, _("'tx_queue_size' attribute must be positive number: %s"), tx_queue_size); goto error; } def->driver.virtio.tx_queue_size = q; } if ((tmpNode = virXPathNode("./driver/host", ctxt))) { if ((str = virXMLPropString(tmpNode, "csum"))) { 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 = virXMLPropString(tmpNode, "gso"))) { 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 = virXMLPropString(tmpNode, "tso4"))) { 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 = virXMLPropString(tmpNode, "tso6"))) { 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 = virXMLPropString(tmpNode, "ecn"))) { 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 = virXMLPropString(tmpNode, "ufo"))) { 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 = virXMLPropString(tmpNode, "mrg_rxbuf"))) { 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 ((tmpNode = virXPathNode("./driver/guest", ctxt))) { if ((str = virXMLPropString(tmpNode, "csum"))) { 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 = virXMLPropString(tmpNode, "tso4"))) { 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 = virXMLPropString(tmpNode, "tso6"))) { 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 = virXMLPropString(tmpNode, "ecn"))) { 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 = virXMLPropString(tmpNode, "ufo"))) { 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 = g_steal_pointer(&vhost_path); } 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 = g_steal_pointer(&filter); def->filterparams = g_steal_pointer(&filterparams); break; case VIR_DOMAIN_NET_TYPE_USER: case VIR_DOMAIN_NET_TYPE_VHOSTUSER: case VIR_DOMAIN_NET_TYPE_SERVER: case VIR_DOMAIN_NET_TYPE_CLIENT: case VIR_DOMAIN_NET_TYPE_MCAST: case VIR_DOMAIN_NET_TYPE_INTERNAL: case VIR_DOMAIN_NET_TYPE_DIRECT: case VIR_DOMAIN_NET_TYPE_HOSTDEV: case VIR_DOMAIN_NET_TYPE_UDP: break; case VIR_DOMAIN_NET_TYPE_LAST: default: virReportEnumRangeError(virDomainNetType, def->type); goto error; } } rv = virXPathULong("string(./tune/sndbuf)", ctxt, &def->tune.sndbuf); if (rv >= 0) { def->tune.sndbuf_specified = true; } else if (rv == -2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("sndbuf must be a positive integer")); goto error; } if (virXPathUInt("string(./mtu/@size)", ctxt, &def->mtu) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("malformed mtu size")); goto error; } node = virXPathNode("./coalesce", ctxt); if (node) { def->coalesce = virDomainNetDefCoalesceParseXML(node, ctxt); if (!def->coalesce) goto error; } cleanup: virDomainActualNetDefFree(actual); virHashFree(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_NONE; case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL: case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST: /* No target type yet*/ break; } return 0; } static int virDomainChrTargetTypeFromString(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; } return ret; } static int virDomainChrTargetModelFromString(int devtype, const char *targetModel) { int ret = -1; if (!targetModel) return 0; switch ((virDomainChrDeviceType) devtype) { case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL: ret = virDomainChrSerialTargetModelTypeFromString(targetModel); break; case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL: case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST: /* Target model not supported yet */ ret = 0; break; } return ret; } static int virDomainChrDefParseTargetXML(virDomainChrDefPtr def, xmlNodePtr cur, unsigned int flags) { xmlNodePtr child; unsigned int port; g_autofree char *targetType = virXMLPropString(cur, "type"); g_autofree char *targetModel = NULL; g_autofree char *addrStr = NULL; g_autofree char *portStr = NULL; g_autofree char *stateStr = NULL; if ((def->targetType = virDomainChrTargetTypeFromString(def->deviceType, targetType)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown target type '%s' specified for character device"), targetType); return -1; } child = cur->children; while (child != NULL) { if (child->type == XML_ELEMENT_NODE && virXMLNodeNameEqual(child, "model")) { targetModel = virXMLPropString(child, "name"); } child = child->next; } if ((def->targetModel = virDomainChrTargetModelFromString(def->deviceType, targetModel)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown target model '%s' specified for character device"), targetModel); return -1; } 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) return -1; if (addrStr == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("guestfwd channel does not " "define a target address")); return -1; } if (virSocketAddrParse(def->target.addr, addrStr, AF_UNSPEC) < 0) return -1; if (def->target.addr->data.stor.ss_family != AF_INET) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("guestfwd channel only supports " "IPv4 addresses")); return -1; } if (portStr == NULL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("guestfwd channel does " "not define a target port")); return -1; } if (virStrToLong_ui(portStr, NULL, 10, &port) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid port number: %s"), portStr); return -1; } virSocketAddrSetPort(def->target.addr, port); break; case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN: case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: def->target.name = virXMLPropString(cur, "name"); if (def->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO && !(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); return -1; } 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); return -1; } def->target.port = port; break; } return 0; } typedef enum { VIR_DOMAIN_CHR_SOURCE_MODE_CONNECT, VIR_DOMAIN_CHR_SOURCE_MODE_BIND, } virDomainChrSourceModeType; /** * virDomainChrSourceDefParseMode: * @source: XML dom node * * Returns: -1 in case of error, * virDomainChrSourceModeType in case of success */ static int virDomainChrSourceDefParseMode(xmlNodePtr source) { g_autofree char *mode = virXMLPropString(source, "mode"); if (!mode || STREQ(mode, "connect")) { return VIR_DOMAIN_CHR_SOURCE_MODE_CONNECT; } else if (STREQ(mode, "bind")) { return VIR_DOMAIN_CHR_SOURCE_MODE_BIND; } virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown source mode '%s'"), mode); return -1; } static int virDomainChrSourceDefParseTCP(virDomainChrSourceDefPtr def, xmlNodePtr source, xmlXPathContextPtr ctxt, unsigned int flags) { int mode; int tmpVal; g_autofree char *tmp = NULL; if ((mode = virDomainChrSourceDefParseMode(source)) < 0) return -1; def->data.tcp.listen = mode == VIR_DOMAIN_CHR_SOURCE_MODE_BIND; def->data.tcp.host = virXMLPropString(source, "host"); def->data.tcp.service = virXMLPropString(source, "service"); if ((tmp = virXMLPropString(source, "tls"))) { if ((def->data.tcp.haveTLS = virTristateBoolTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown chardev 'tls' setting '%s'"), tmp); return -1; } VIR_FREE(tmp); } if ((flags & VIR_DOMAIN_DEF_PARSE_STATUS) && (tmp = virXMLPropString(source, "tlsFromConfig"))) { if (virStrToLong_i(tmp, NULL, 10, &tmpVal) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid tlsFromConfig value: %s"), tmp); return -1; } def->data.tcp.tlsFromConfig = !!tmpVal; } if (virDomainChrSourceReconnectDefParseXML(&def->data.tcp.reconnect, source, ctxt) < 0) { return -1; } return 0; } static int virDomainChrSourceDefParseUDP(virDomainChrSourceDefPtr def, xmlNodePtr source) { int mode; if ((mode = virDomainChrSourceDefParseMode(source)) < 0) return -1; if (mode == VIR_DOMAIN_CHR_SOURCE_MODE_CONNECT && !def->data.udp.connectHost && !def->data.udp.connectService) { def->data.udp.connectHost = virXMLPropString(source, "host"); def->data.udp.connectService = virXMLPropString(source, "service"); } else if (mode == VIR_DOMAIN_CHR_SOURCE_MODE_BIND && !def->data.udp.bindHost && !def->data.udp.bindService) { def->data.udp.bindHost = virXMLPropString(source, "host"); def->data.udp.bindService = virXMLPropString(source, "service"); } return 0; } static int virDomainChrSourceDefParseUnix(virDomainChrSourceDefPtr def, xmlNodePtr source, xmlXPathContextPtr ctxt) { int mode; if ((mode = virDomainChrSourceDefParseMode(source)) < 0) return -1; def->data.nix.listen = mode == VIR_DOMAIN_CHR_SOURCE_MODE_BIND; def->data.nix.path = virXMLPropString(source, "path"); if (virDomainChrSourceReconnectDefParseXML(&def->data.nix.reconnect, source, ctxt) < 0) { return -1; } return 0; } static int virDomainChrSourceDefParseFile(virDomainChrSourceDefPtr def, xmlNodePtr source) { g_autofree char *append = NULL; def->data.file.path = virXMLPropString(source, "path"); if ((append = virXMLPropString(source, "append")) && (def->data.file.append = virTristateSwitchTypeFromString(append)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid append attribute value '%s'"), append); return -1; } return 0; } static int virDomainChrSourceDefParseProtocol(virDomainChrSourceDefPtr def, xmlNodePtr protocol) { g_autofree char *prot = NULL; if (def->type != VIR_DOMAIN_CHR_TYPE_TCP) return 0; if ((prot = virXMLPropString(protocol, "type")) && (def->data.tcp.protocol = virDomainChrTcpProtocolTypeFromString(prot)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown protocol '%s'"), prot); return -1; } return 0; } static int virDomainChrSourceDefParseLog(virDomainChrSourceDefPtr def, xmlNodePtr log) { g_autofree char *append = NULL; def->logfile = virXMLPropString(log, "file"); if ((append = virXMLPropString(log, "append")) && (def->logappend = virTristateSwitchTypeFromString(append)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid append attribute value '%s'"), append); return -1; } return 0; } /* 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, 0 on success. */ static int virDomainChrSourceDefParseXML(virDomainChrSourceDefPtr def, xmlNodePtr cur, unsigned int flags, virDomainChrDefPtr chr_def, xmlXPathContextPtr ctxt, virSecurityLabelDefPtr* vmSeclabels, int nvmSeclabels) { int ret = -1; bool logParsed = false; bool protocolParsed = false; int sourceParsed = 0; for (; cur; cur = cur->next) { if (cur->type != XML_ELEMENT_NODE) continue; if (virXMLNodeNameEqual(cur, "source")) { /* Parse only the first source element since only one is used * for chardev devices, the only exception is UDP type, where * user can specify two source elements. */ if (sourceParsed >= 1 && def->type != VIR_DOMAIN_CHR_TYPE_UDP) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only one source element is allowed for " "character device")); goto error; } else if (sourceParsed >= 2) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only two source elements are allowed for " "character device")); goto error; } sourceParsed++; switch ((virDomainChrType) def->type) { case VIR_DOMAIN_CHR_TYPE_FILE: if (virDomainChrSourceDefParseFile(def, cur) < 0) goto error; break; case VIR_DOMAIN_CHR_TYPE_PTY: /* PTY path is only parsed from live xml. */ if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) def->data.file.path = virXMLPropString(cur, "path"); break; case VIR_DOMAIN_CHR_TYPE_DEV: case VIR_DOMAIN_CHR_TYPE_PIPE: def->data.file.path = virXMLPropString(cur, "path"); break; case VIR_DOMAIN_CHR_TYPE_UNIX: if (virDomainChrSourceDefParseUnix(def, cur, ctxt) < 0) goto error; break; case VIR_DOMAIN_CHR_TYPE_UDP: if (virDomainChrSourceDefParseUDP(def, cur) < 0) goto error; break; case VIR_DOMAIN_CHR_TYPE_TCP: if (virDomainChrSourceDefParseTCP(def, cur, ctxt, flags) < 0) goto error; break; case VIR_DOMAIN_CHR_TYPE_SPICEPORT: def->data.spiceport.channel = virXMLPropString(cur, "channel"); break; case VIR_DOMAIN_CHR_TYPE_NMDM: def->data.nmdm.master = virXMLPropString(cur, "master"); def->data.nmdm.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(&def->seclabels, &def->nseclabels, ctxt, flags) < 0 || virSecurityDeviceLabelDefValidateXML(def->seclabels, def->nseclabels, vmSeclabels, nvmSeclabels) < 0) { ctxt->node = saved_node; goto error; } ctxt->node = saved_node; } } else if (virXMLNodeNameEqual(cur, "log")) { if (logParsed) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only one protocol element is allowed for " "character device")); goto error; } logParsed = true; if (virDomainChrSourceDefParseLog(def, cur) < 0) goto error; } else if (virXMLNodeNameEqual(cur, "protocol")) { if (protocolParsed) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only one log element is allowed for " "character device")); goto error; } protocolParsed = true; if (virDomainChrSourceDefParseProtocol(def, cur) < 0) goto error; } } ret = 0; cleanup: return ret; error: virDomainChrSourceDefClear(def); goto cleanup; } static virClassPtr virDomainChrSourceDefClass; static int virDomainChrSourceDefOnceInit(void) { if (!VIR_CLASS_NEW(virDomainChrSourceDef, virClassForObject())) return -1; return 0; } VIR_ONCE_GLOBAL_INIT(virDomainChrSourceDef); virDomainChrSourceDefPtr virDomainChrSourceDefNew(virDomainXMLOptionPtr xmlopt) { virDomainChrSourceDefPtr def = NULL; if (virDomainChrSourceDefInitialize() < 0) return NULL; if (!(def = virObjectNew(virDomainChrSourceDefClass))) return NULL; if (xmlopt && xmlopt->privateData.chrSourceNew && !(def->privateData = xmlopt->privateData.chrSourceNew())) { virObjectUnref(def); def = NULL; } return def; } /* Create a new character device definition and set * default port. */ virDomainChrDefPtr virDomainChrDefNew(virDomainXMLOptionPtr xmlopt) { virDomainChrDefPtr def = NULL; if (VIR_ALLOC(def) < 0) return NULL; def->target.port = -1; if (!(def->source = virDomainChrSourceDefNew(xmlopt))) VIR_FREE(def); 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(virDomainXMLOptionPtr xmlopt, xmlXPathContextPtr ctxt, xmlNodePtr node, virSecurityLabelDefPtr* vmSeclabels, int nvmSeclabels, unsigned int flags) { xmlNodePtr cur; const char *nodeName; virDomainChrDefPtr def; bool seenTarget = false; g_autofree char *type = NULL; if (!(def = virDomainChrDefNew(xmlopt))) 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 (virXMLNodeNameEqual(cur, "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 (virDomainDeviceInfoParseXML(xmlopt, node, &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: return def; error: virDomainChrDefFree(def); def = NULL; goto cleanup; } static virDomainSmartcardDefPtr virDomainSmartcardDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { xmlNodePtr cur; virDomainSmartcardDefPtr def; size_t i; g_autofree char *mode = NULL; g_autofree char *type = NULL; 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 && virXMLNodeNameEqual(cur, "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 && virXMLNodeNameEqual(cur, "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 = virDomainChrSourceDefNew(xmlopt))) 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, ctxt, 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(xmlopt, node, &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: return def; error: virDomainSmartcardDefFree(def); def = NULL; goto cleanup; } /* Parse the XML definition for a TPM device * * The XML looks like this: * * * * * * * * or like this: * * * * * * Emulator state encryption is supported with the following: * * * * * * */ static virDomainTPMDefPtr virDomainTPMDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainTPMDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); int nbackends; g_autofree char *path = NULL; g_autofree char *model = NULL; g_autofree char *backend = NULL; g_autofree char *version = NULL; g_autofree char *secretuuid = NULL; g_autofree xmlNodePtr *backends = NULL; if (VIR_ALLOC(def) < 0) return NULL; model = virXMLPropString(node, "model"); if (model != NULL && (def->model = virDomainTPMModelTypeFromString(model)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown TPM frontend model '%s'"), model); goto error; } 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 ((def->type = virDomainTPMBackendTypeFromString(backend)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown TPM backend type '%s'"), backend); goto error; } version = virXMLPropString(backends[0], "version"); if (!version) { def->version = VIR_DOMAIN_TPM_VERSION_DEFAULT; } else { if ((def->version = virDomainTPMVersionTypeFromString(version)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported TPM version '%s'"), version); goto error; } } switch (def->type) { case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: path = virXPathString("string(./backend/device/@path)", ctxt); if (!path) path = g_strdup(VIR_DOMAIN_TPM_DEFAULT_DEVICE); def->data.passthrough.source.data.file.path = g_steal_pointer(&path); def->data.passthrough.source.type = VIR_DOMAIN_CHR_TYPE_DEV; break; case VIR_DOMAIN_TPM_TYPE_EMULATOR: secretuuid = virXPathString("string(./backend/encryption/@secret)", ctxt); if (secretuuid) { if (virUUIDParse(secretuuid, def->data.emulator.secretuuid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to parse secret uuid '%s'"), secretuuid); goto error; } def->data.emulator.hassecretuuid = true; } break; case VIR_DOMAIN_TPM_TYPE_LAST: goto error; } if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error; cleanup: return def; error: virDomainTPMDefFree(def); def = NULL; goto cleanup; } static virDomainPanicDefPtr virDomainPanicDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, unsigned int flags) { virDomainPanicDefPtr panic; g_autofree char *model = NULL; if (VIR_ALLOC(panic) < 0) return NULL; if (virDomainDeviceInfoParseXML(xmlopt, node, &panic->info, flags) < 0) goto error; model = virXMLPropString(node, "model"); if (model != NULL && (panic->model = virDomainPanicModelTypeFromString(model)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown panic model '%s'"), model); goto error; } cleanup: return panic; error: virDomainPanicDefFree(panic); panic = NULL; goto cleanup; } /* Parse the XML definition for an input device */ static virDomainInputDefPtr virDomainInputDefParseXML(virDomainXMLOptionPtr xmlopt, const virDomainDef *dom, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { VIR_XPATH_NODE_AUTORESTORE(ctxt); virDomainInputDefPtr def; g_autofree char *evdev = NULL; g_autofree char *type = NULL; g_autofree char *bus = NULL; g_autofree char *model = NULL; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = node; type = virXMLPropString(node, "type"); bus = virXMLPropString(node, "bus"); model = virXMLPropString(node, "model"); 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 (model && ((def->model = virDomainInputModelTypeFromString(model)) < 0 || def->model == VIR_DOMAIN_INPUT_MODEL_DEFAULT)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown input model '%s'"), model); goto error; } if (bus) { if ((def->bus = virDomainInputBusTypeFromString(bus)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown input bus type '%s'"), bus); goto error; } if (dom->os.type == VIR_DOMAIN_OSTYPE_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 (dom->os.type == VIR_DOMAIN_OSTYPE_XEN || dom->os.type == VIR_DOMAIN_OSTYPE_XENPVH) { 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_VZ || 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 (dom->os.type == VIR_DOMAIN_OSTYPE_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 (dom->os.type == VIR_DOMAIN_OSTYPE_XEN || dom->os.type == VIR_DOMAIN_OSTYPE_XENPVH) { def->bus = VIR_DOMAIN_INPUT_BUS_XEN; } else { if ((dom->virtType == VIR_DOMAIN_VIRT_VZ || dom->virtType == VIR_DOMAIN_VIRT_PARALLELS)) def->bus = VIR_DOMAIN_INPUT_BUS_PARALLELS; } } if (virDomainDeviceInfoParseXML(xmlopt, node, &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; } if ((evdev = virXPathString("string(./source/@evdev)", ctxt))) def->source.evdev = virFileSanitizePath(evdev); if (def->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH && !def->source.evdev) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing evdev path for input device passthrough")); goto error; } if (virDomainVirtioOptionsParseXML(virXPathNode("./driver", ctxt), &def->virtio) < 0) goto error; cleanup: return def; error: virDomainInputDefFree(def); def = NULL; goto cleanup; } /* Parse the XML definition for a hub device */ static virDomainHubDefPtr virDomainHubDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, unsigned int flags) { virDomainHubDefPtr def; g_autofree 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(xmlopt, node, &def->info, flags) < 0) goto error; cleanup: return def; error: virDomainHubDefFree(def); def = NULL; goto cleanup; } /* Parse the XML definition for a clock timer */ static virDomainTimerDefPtr virDomainTimerDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt) { virDomainTimerDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr catchup; int ret; g_autofree char *name = NULL; g_autofree char *present = NULL; g_autofree char *tickpolicy = NULL; g_autofree char *track = NULL; g_autofree char *mode = NULL; 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: return def; error: VIR_FREE(def); goto cleanup; } static int virDomainGraphicsAuthDefParseXML(xmlNodePtr node, virDomainGraphicsAuthDefPtr def, int type) { g_autofree char *validTo = NULL; g_autofree 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(def->passwd); return -1; } 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); return -1; } /* 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; } /** * virDomainGraphicsListenDefParseXML: * @def: listen def pointer to be filled * @graphics: graphics def pointer * @node: xml node of element * @parent: xml node of element * @flags: bit-wise or of VIR_DOMAIN_DEF_PARSE_* * * Parses current element from @node to @def. For backward * compatibility the @parent element should contain node of element * for the first element in order to validate attributes from both * elements. */ static int virDomainGraphicsListenDefParseXML(virDomainGraphicsListenDefPtr def, virDomainGraphicsDefPtr graphics, xmlNodePtr node, xmlNodePtr parent, unsigned int flags) { int ret = -1; const char *graphicsType = virDomainGraphicsTypeToString(graphics->type); int tmp, typeVal; g_autofree char *type = virXMLPropString(node, "type"); g_autofree char *address = virXMLPropString(node, "address"); g_autofree char *network = virXMLPropString(node, "network"); g_autofree char *socketPath = virXMLPropString(node, "socket"); g_autofree char *fromConfig = virXMLPropString(node, "fromConfig"); g_autofree char *autoGenerated = virXMLPropString(node, "autoGenerated"); g_autofree char *addressCompat = NULL; g_autofree char *socketCompat = NULL; if (parent) { addressCompat = virXMLPropString(parent, "listen"); socketCompat = virXMLPropString(parent, "socket"); } if (!type) { virReportError(VIR_ERR_XML_ERROR, "%s", _("graphics listen type must be specified")); goto error; } if ((typeVal = virDomainGraphicsListenTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown graphics listen type '%s'"), type); goto error; } def->type = typeVal; switch (def->type) { case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET: if (graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC && graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("listen type 'socket' is not available for " "graphics type '%s'"), graphicsType); goto error; } break; case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE: if (graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_SPICE && graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("listen type 'none' is not available for " "graphics type '%s'"), graphicsType); goto error; } break; case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK: case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST: break; } if (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS) { if (address && addressCompat && STRNEQ(address, addressCompat)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("graphics 'listen' attribute '%s' must match " "'address' attribute of first listen element " "(found '%s')"), addressCompat, address); goto error; } if (!address) address = g_steal_pointer(&addressCompat); } if (def->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { if (socketPath && socketCompat && STRNEQ(socketPath, socketCompat)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("graphics 'socket' attribute '%s' must match " "'socket' attribute of first listen element " "(found '%s')"), socketCompat, socketPath); goto error; } if (!socketPath) socketPath = g_steal_pointer(&socketCompat); } 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 = g_steal_pointer(&address); } if (network && network[0]) { if (def->type != VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'network' attribute is valid only for listen " "type 'network'")); goto error; } def->network = g_steal_pointer(&network); } if (socketPath && socketPath[0]) { if (def->type != VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { virReportError(VIR_ERR_XML_ERROR, "%s", _("'socket' attribute is valid only for listen " "type 'socket'")); goto error; } def->socket = g_steal_pointer(&socketPath); } 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; } if (autoGenerated && flags & VIR_DOMAIN_DEF_PARSE_STATUS) { if (STREQ(autoGenerated, "yes")) { def->autoGenerated = true; } else if (STRNEQ(autoGenerated, "no")) { virReportError(VIR_ERR_XML_ERROR, _("Invalid autoGenerated value: %s"), autoGenerated); goto error; } } ret = 0; error: if (ret < 0) virDomainGraphicsListenDefClear(def); return ret; } static int virDomainGraphicsListensParseXML(virDomainGraphicsDefPtr def, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { VIR_XPATH_NODE_AUTORESTORE(ctxt); virDomainGraphicsListenDef newListen = {0}; int nListens; int ret = -1; g_autofree xmlNodePtr *listenNodes = NULL; g_autofree char *socketPath = NULL; ctxt->node = node; /* parse the subelements for graphics types that support it */ nListens = virXPathNodeSet("./listen", ctxt, &listenNodes); if (nListens < 0) goto cleanup; if (nListens > 0) { size_t i; if (VIR_ALLOC_N(def->listens, nListens) < 0) goto cleanup; for (i = 0; i < nListens; i++) { if (virDomainGraphicsListenDefParseXML(&def->listens[i], def, listenNodes[i], i == 0 ? node : NULL, flags) < 0) goto cleanup; def->nListens++; } } /* If no element was found in XML for backward compatibility * we should try to parse 'listen' or 'socket' attribute from * element. */ if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) socketPath = virXMLPropString(node, "socket"); if (socketPath) { newListen.type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET; newListen.socket = g_steal_pointer(&socketPath); } else { newListen.type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS; newListen.address = virXMLPropString(node, "listen"); if (STREQ_NULLABLE(newListen.address, "")) VIR_FREE(newListen.address); } /* If no element was found add a new one created by parsing * element. */ if (def->nListens == 0) { if (VIR_APPEND_ELEMENT(def->listens, def->nListens, newListen) < 0) goto cleanup; } else { virDomainGraphicsListenDefPtr glisten = &def->listens[0]; /* If the first element is 'address' or 'network' and we found * 'socket' attribute inside element for backward * compatibility we need to replace the first listen by * element based on the 'socket' attribute. */ if ((glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS || glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK) && newListen.type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET) { virDomainGraphicsListenDefClear(glisten); *glisten = newListen; memset(&newListen, 0, sizeof(newListen)); } } ret = 0; cleanup: virDomainGraphicsListenDefClear(&newListen); return ret; } static int virDomainGraphicsDefParseXMLVNC(virDomainGraphicsDefPtr def, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { g_autofree char *port = virXMLPropString(node, "port"); g_autofree char *websocket = virXMLPropString(node, "websocket"); g_autofree char *websocketGenerated = virXMLPropString(node, "websocketGenerated"); g_autofree char *sharePolicy = virXMLPropString(node, "sharePolicy"); g_autofree char *autoport = virXMLPropString(node, "autoport"); if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) return -1; if (port) { if (virStrToLong_i(port, NULL, 10, &def->data.vnc.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse vnc port %s"), port); return -1; } /* 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) { if (STREQ(autoport, "yes")) { if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) def->data.vnc.port = 0; def->data.vnc.autoport = true; } else { def->data.vnc.autoport = false; } } 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); return -1; } } if (websocketGenerated && STREQ(websocketGenerated, "yes")) def->data.vnc.websocketGenerated = true; if (sharePolicy) { int policy = virDomainGraphicsVNCSharePolicyTypeFromString(sharePolicy); if (policy < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown vnc display sharing policy '%s'"), sharePolicy); return -1; } else { def->data.vnc.sharePolicy = policy; } } def->data.vnc.keymap = virXMLPropString(node, "keymap"); if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth, def->type) < 0) return -1; return 0; } static int virDomainGraphicsDefParseXMLSDL(virDomainGraphicsDefPtr def, xmlNodePtr node, xmlXPathContextPtr ctxt) { VIR_XPATH_NODE_AUTORESTORE(ctxt); int enableVal; xmlNodePtr glNode; int ret = -1; g_autofree char *fullscreen = virXMLPropString(node, "fullscreen"); g_autofree char *enable = NULL; ctxt->node = node; if (fullscreen != NULL) { if (virStringParseYesNo(fullscreen, &def->data.sdl.fullscreen) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown fullscreen value '%s'"), fullscreen); goto cleanup; } } else { def->data.sdl.fullscreen = false; } def->data.sdl.xauth = virXMLPropString(node, "xauth"); def->data.sdl.display = virXMLPropString(node, "display"); glNode = virXPathNode("./gl", ctxt); if (glNode) { enable = virXMLPropString(glNode, "enable"); if (!enable) { virReportError(VIR_ERR_XML_ERROR, "%s", _("sdl gl element missing enable")); goto cleanup; } enableVal = virTristateBoolTypeFromString(enable); if (enableVal < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown enable value '%s'"), enable); goto cleanup; } def->data.sdl.gl = enableVal; } ret = 0; cleanup: return ret; } static int virDomainGraphicsDefParseXMLRDP(virDomainGraphicsDefPtr def, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { g_autofree char *port = virXMLPropString(node, "port"); g_autofree char *autoport = virXMLPropString(node, "autoport"); g_autofree char *replaceUser = virXMLPropString(node, "replaceUser"); g_autofree char *multiUser = virXMLPropString(node, "multiUser"); if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) return -1; if (port) { if (virStrToLong_i(port, NULL, 10, &def->data.rdp.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse rdp port %s"), port); return -1; } /* Legacy compat syntax, used -1 for auto-port */ if (def->data.rdp.port == -1) def->data.rdp.autoport = true; } else { def->data.rdp.port = 0; def->data.rdp.autoport = true; } if (STREQ_NULLABLE(autoport, "yes")) def->data.rdp.autoport = true; if (def->data.rdp.autoport && (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) def->data.rdp.port = 0; if (STREQ_NULLABLE(replaceUser, "yes")) def->data.rdp.replaceUser = true; if (STREQ_NULLABLE(multiUser, "yes")) def->data.rdp.multiUser = true; return 0; } static int virDomainGraphicsDefParseXMLDesktop(virDomainGraphicsDefPtr def, xmlNodePtr node) { g_autofree char *fullscreen = virXMLPropString(node, "fullscreen"); if (fullscreen != NULL) { if (virStringParseYesNo(fullscreen, &def->data.desktop.fullscreen) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown fullscreen value '%s'"), fullscreen); return -1; } } else { def->data.desktop.fullscreen = false; } def->data.desktop.display = virXMLPropString(node, "display"); return 0; } static int virDomainGraphicsDefParseXMLSpice(virDomainGraphicsDefPtr def, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { xmlNodePtr cur; int defaultModeVal; g_autofree char *port = virXMLPropString(node, "port"); g_autofree char *tlsPort = virXMLPropString(node, "tlsPort"); g_autofree char *autoport = virXMLPropString(node, "autoport"); g_autofree char *defaultMode = virXMLPropString(node, "defaultMode"); if (virDomainGraphicsListensParseXML(def, node, ctxt, flags) < 0) return -1; if (port) { if (virStrToLong_i(port, NULL, 10, &def->data.spice.port) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse spice port %s"), port); return -1; } } else { def->data.spice.port = 0; } if (tlsPort) { if (virStrToLong_i(tlsPort, NULL, 10, &def->data.spice.tlsPort) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot parse spice tlsPort %s"), tlsPort); return -1; } } else { def->data.spice.tlsPort = 0; } if (STREQ_NULLABLE(autoport, "yes")) def->data.spice.autoport = true; def->data.spice.defaultMode = VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY; if (defaultMode) { if ((defaultModeVal = virDomainGraphicsSpiceChannelModeTypeFromString(defaultMode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown default spice channel mode %s"), defaultMode); return -1; } def->data.spice.defaultMode = defaultModeVal; } 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) return -1; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (virXMLNodeNameEqual(cur, "channel")) { int nameval, modeval; g_autofree char *name = NULL; g_autofree char *mode = NULL; name = virXMLPropString(cur, "name"); mode = virXMLPropString(cur, "mode"); if (!name || !mode) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice channel missing name/mode")); return -1; } if ((nameval = virDomainGraphicsSpiceChannelNameTypeFromString(name)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice channel name %s"), name); return -1; } if ((modeval = virDomainGraphicsSpiceChannelModeTypeFromString(mode)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice channel mode %s"), mode); return -1; } def->data.spice.channels[nameval] = modeval; } else if (virXMLNodeNameEqual(cur, "image")) { int compressionVal; g_autofree char *compression = virXMLPropString(cur, "compression"); if (!compression) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice image missing compression")); return -1; } if ((compressionVal = virDomainGraphicsSpiceImageCompressionTypeFromString(compression)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice image compression %s"), compression); return -1; } def->data.spice.image = compressionVal; } else if (virXMLNodeNameEqual(cur, "jpeg")) { int compressionVal; g_autofree char *compression = virXMLPropString(cur, "compression"); if (!compression) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice jpeg missing compression")); return -1; } if ((compressionVal = virDomainGraphicsSpiceJpegCompressionTypeFromString(compression)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice jpeg compression %s"), compression); return -1; } def->data.spice.jpeg = compressionVal; } else if (virXMLNodeNameEqual(cur, "zlib")) { int compressionVal; g_autofree char *compression = virXMLPropString(cur, "compression"); if (!compression) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice zlib missing compression")); return -1; } if ((compressionVal = virDomainGraphicsSpiceZlibCompressionTypeFromString(compression)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown spice zlib compression %s"), compression); return -1; } def->data.spice.zlib = compressionVal; } else if (virXMLNodeNameEqual(cur, "playback")) { int compressionVal; g_autofree char *compression = virXMLPropString(cur, "compression"); if (!compression) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("spice playback missing compression")); return -1; } if ((compressionVal = virTristateSwitchTypeFromString(compression)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unknown spice playback compression")); return -1; } def->data.spice.playback = compressionVal; } else if (virXMLNodeNameEqual(cur, "streaming")) { int modeVal; g_autofree char *mode = virXMLPropString(cur, "mode"); if (!mode) { virReportError(VIR_ERR_XML_ERROR, "%s", _("spice streaming missing mode")); return -1; } if ((modeVal = virDomainGraphicsSpiceStreamingModeTypeFromString(mode)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("unknown spice streaming mode")); return -1; } def->data.spice.streaming = modeVal; } else if (virXMLNodeNameEqual(cur, "clipboard")) { int copypasteVal; g_autofree char *copypaste = virXMLPropString(cur, "copypaste"); if (!copypaste) { virReportError(VIR_ERR_XML_ERROR, "%s", _("spice clipboard missing copypaste")); return -1; } if ((copypasteVal = virTristateBoolTypeFromString(copypaste)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown copypaste value '%s'"), copypaste); return -1; } def->data.spice.copypaste = copypasteVal; } else if (virXMLNodeNameEqual(cur, "filetransfer")) { int enableVal; g_autofree char *enable = virXMLPropString(cur, "enable"); if (!enable) { virReportError(VIR_ERR_XML_ERROR, "%s", _("spice filetransfer missing enable")); return -1; } if ((enableVal = virTristateBoolTypeFromString(enable)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown enable value '%s'"), enable); return -1; } def->data.spice.filetransfer = enableVal; } else if (virXMLNodeNameEqual(cur, "gl")) { int enableVal; g_autofree char *enable = virXMLPropString(cur, "enable"); g_autofree char *rendernode = virXMLPropString(cur, "rendernode"); if (!enable) { virReportError(VIR_ERR_XML_ERROR, "%s", _("spice gl element missing enable")); return -1; } if ((enableVal = virTristateBoolTypeFromString(enable)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown enable value '%s'"), enable); return -1; } def->data.spice.gl = enableVal; def->data.spice.rendernode = g_steal_pointer(&rendernode); } else if (virXMLNodeNameEqual(cur, "mouse")) { int modeVal; g_autofree char *mode = virXMLPropString(cur, "mode"); if (!mode) { virReportError(VIR_ERR_XML_ERROR, "%s", _("spice mouse missing mode")); return -1; } if ((modeVal = virDomainGraphicsSpiceMouseModeTypeFromString(mode)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown mouse mode value '%s'"), mode); return -1; } def->data.spice.mousemode = modeVal; } } cur = cur->next; } return 0; } static int virDomainGraphicsDefParseXMLEGLHeadless(virDomainGraphicsDefPtr def, xmlNodePtr node, xmlXPathContextPtr ctxt) { VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr glNode; ctxt->node = node; if ((glNode = virXPathNode("./gl", ctxt))) def->data.egl_headless.rendernode = virXMLPropString(glNode, "rendernode"); return 0; } virDomainGraphicsDefPtr virDomainGraphicsDefNew(virDomainXMLOptionPtr xmlopt) { virDomainGraphicsDefPtr def = NULL; if (VIR_ALLOC(def) < 0) return NULL; if (xmlopt && xmlopt->privateData.graphicsNew && !(def->privateData = xmlopt->privateData.graphicsNew())) { VIR_FREE(def); def = NULL; } return def; } virDomainNetDefPtr virDomainNetDefNew(virDomainXMLOptionPtr xmlopt) { virDomainNetDefPtr def = NULL; if (VIR_ALLOC(def) < 0) return NULL; if (xmlopt && xmlopt->privateData.networkNew && !(def->privateData = xmlopt->privateData.networkNew())) { virDomainNetDefFree(def); def = NULL; } return def; } /* Parse the XML definition for a graphics device */ static virDomainGraphicsDefPtr virDomainGraphicsDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainGraphicsDefPtr def; int typeVal; g_autofree char *type = NULL; if (!(def = virDomainGraphicsDefNew(xmlopt))) return NULL; type = virXMLPropString(node, "type"); if (!type) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing graphics device type")); goto error; } if ((typeVal = virDomainGraphicsTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown graphics device type '%s'"), type); goto error; } def->type = typeVal; switch (def->type) { case VIR_DOMAIN_GRAPHICS_TYPE_VNC: if (virDomainGraphicsDefParseXMLVNC(def, node, ctxt, flags) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: if (virDomainGraphicsDefParseXMLSDL(def, node, ctxt) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_RDP: if (virDomainGraphicsDefParseXMLRDP(def, node, ctxt, flags) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: if (virDomainGraphicsDefParseXMLDesktop(def, node) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: if (virDomainGraphicsDefParseXMLSpice(def, node, ctxt, flags) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS: if (virDomainGraphicsDefParseXMLEGLHeadless(def, node, ctxt) < 0) goto error; break; case VIR_DOMAIN_GRAPHICS_TYPE_LAST: break; } cleanup: return def; error: virDomainGraphicsDefFree(def); def = NULL; goto cleanup; } static virDomainSoundCodecDefPtr virDomainSoundCodecDefParseXML(xmlNodePtr node) { virDomainSoundCodecDefPtr def; g_autofree char *type = NULL; 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: return def; error: virDomainSoundCodecDefFree(def); def = NULL; goto cleanup; } static virDomainSoundDefPtr virDomainSoundDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainSoundDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *model = NULL; 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; g_autofree 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) goto error; for (i = 0; i < ncodecs; i++) { virDomainSoundCodecDefPtr codec = virDomainSoundCodecDefParseXML(codecNodes[i]); if (codec == NULL) goto error; codec->cad = def->ncodecs; /* that will do for now */ def->codecs[def->ncodecs++] = codec; } } } if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error; cleanup: return def; error: virDomainSoundDefFree(def); def = NULL; goto cleanup; } static virDomainWatchdogDefPtr virDomainWatchdogDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, unsigned int flags) { virDomainWatchdogDefPtr def; g_autofree char *model = NULL; g_autofree char *action = NULL; 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(xmlopt, node, &def->info, flags) < 0) goto error; cleanup: return def; error: virDomainWatchdogDefFree(def); def = NULL; goto cleanup; } static virDomainRNGDefPtr virDomainRNGDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainRNGDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); int nbackends; g_autofree xmlNodePtr *backends = NULL; g_autofree char *model = NULL; g_autofree char *backend = NULL; g_autofree char *type = NULL; 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); 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 (!(def->source.chardev = virDomainChrSourceDefNew(xmlopt))) 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(xmlopt, node, &def->info, flags) < 0) goto error; if (virDomainVirtioOptionsParseXML(virXPathNode("./driver", ctxt), &def->virtio) < 0) goto error; cleanup: return def; error: virDomainRNGDefFree(def); def = NULL; goto cleanup; } static virDomainMemballoonDefPtr virDomainMemballoonDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainMemballoonDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); unsigned int period = 0; g_autofree char *model = NULL; g_autofree char *deflate = NULL; 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; } if ((deflate = virXMLPropString(node, "autodeflate")) && (def->autodeflate = virTristateSwitchTypeFromString(deflate)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid autodeflate attribute value '%s'"), deflate); 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(xmlopt, node, &def->info, flags) < 0) goto error; if (virDomainVirtioOptionsParseXML(virXPathNode("./driver", ctxt), &def->virtio) < 0) goto error; cleanup: return def; error: virDomainMemballoonDefFree(def); def = NULL; goto cleanup; } static virDomainNVRAMDefPtr virDomainNVRAMDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, unsigned int flags) { virDomainNVRAMDefPtr def; if (VIR_ALLOC(def) < 0) return NULL; if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error; return def; error: virDomainNVRAMDefFree(def); return NULL; } static virDomainShmemDefPtr virDomainShmemDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainShmemDefPtr def = NULL; virDomainShmemDefPtr ret = NULL; xmlNodePtr msi = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr server = NULL; g_autofree char *tmp = NULL; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = node; tmp = virXPathString("string(./model/@type)", ctxt); if (tmp) { /* If there's none, we will automatically have the first one * (as default). Unfortunately this has to be done for * compatibility reasons. */ if ((def->model = virDomainShmemModelTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Unknown shmem model type '%s'"), tmp); goto cleanup; } VIR_FREE(tmp); } 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; def->server.chr.type = VIR_DOMAIN_CHR_TYPE_UNIX; def->server.chr.data.nix.listen = false; if ((tmp = virXMLPropString(server, "path"))) def->server.chr.data.nix.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"))) { int val; if ((val = virTristateSwitchTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid msi ioeventfd setting for shmem: '%s'"), tmp); goto cleanup; } def->msi.ioeventfd = val; } } /* 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(xmlopt, node, &def->info, flags) < 0) goto cleanup; ret = g_steal_pointer(&def); cleanup: virDomainShmemDefFree(def); return ret; } static int virSysinfoBIOSParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virSysinfoBIOSDefPtr *bios) { VIR_XPATH_NODE_AUTORESTORE(ctxt); int ret = -1; virSysinfoBIOSDefPtr def; ctxt->node = node; if (!virXMLNodeNameEqual(node, "bios")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("XML does not contain expected 'bios' element")); return ret; } if (VIR_ALLOC(def) < 0) goto cleanup; def->vendor = virXPathString("string(entry[@name='vendor'])", ctxt); def->version = virXPathString("string(entry[@name='version'])", ctxt); def->date = virXPathString("string(entry[@name='date'])", ctxt); def->release = virXPathString("string(entry[@name='release'])", ctxt); if (def->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->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 cleanup; } } if (!def->vendor && !def->version && !def->date && !def->release) { virSysinfoBIOSDefFree(def); def = NULL; } *bios = g_steal_pointer(&def); ret = 0; cleanup: virSysinfoBIOSDefFree(def); return ret; } static int virSysinfoSystemParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virSysinfoSystemDefPtr *sysdef, unsigned char *domUUID, bool uuid_generated) { VIR_XPATH_NODE_AUTORESTORE(ctxt); int ret = -1; virSysinfoSystemDefPtr def; g_autofree char *tmpUUID = NULL; ctxt->node = node; if (!virXMLNodeNameEqual(node, "system")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("XML does not contain expected 'system' element")); return ret; } if (VIR_ALLOC(def) < 0) goto cleanup; def->manufacturer = virXPathString("string(entry[@name='manufacturer'])", ctxt); def->product = virXPathString("string(entry[@name='product'])", ctxt); def->version = virXPathString("string(entry[@name='version'])", ctxt); def->serial = virXPathString("string(entry[@name='serial'])", ctxt); tmpUUID = virXPathString("string(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 cleanup; } 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 cleanup; } /* 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); def->uuid = g_strdup(uuidstr); } def->sku = virXPathString("string(entry[@name='sku'])", ctxt); def->family = virXPathString("string(entry[@name='family'])", ctxt); if (!def->manufacturer && !def->product && !def->version && !def->serial && !def->uuid && !def->sku && !def->family) { virSysinfoSystemDefFree(def); def = NULL; } *sysdef = g_steal_pointer(&def); ret = 0; cleanup: virSysinfoSystemDefFree(def); return ret; } static int virSysinfoBaseBoardParseXML(xmlXPathContextPtr ctxt, virSysinfoBaseBoardDefPtr *baseBoard, size_t *nbaseBoard) { int ret = -1; size_t i, nboards = 0; VIR_XPATH_NODE_AUTORESTORE(ctxt); int n; g_autofree virSysinfoBaseBoardDefPtr boards = NULL; g_autofree xmlNodePtr *nodes = NULL; if ((n = virXPathNodeSet("./baseBoard", ctxt, &nodes)) < 0) return ret; if (n && VIR_ALLOC_N(boards, n) < 0) goto cleanup; for (i = 0; i < n; i++) { virSysinfoBaseBoardDefPtr def = boards + nboards; ctxt->node = nodes[i]; def->manufacturer = virXPathString("string(entry[@name='manufacturer'])", ctxt); def->product = virXPathString("string(entry[@name='product'])", ctxt); def->version = virXPathString("string(entry[@name='version'])", ctxt); def->serial = virXPathString("string(entry[@name='serial'])", ctxt); def->asset = virXPathString("string(entry[@name='asset'])", ctxt); def->location = virXPathString("string(entry[@name='location'])", ctxt); if (!def->manufacturer && !def->product && !def->version && !def->serial && !def->asset && !def->location) { /* nada */ } else { nboards++; } } *baseBoard = g_steal_pointer(&boards); *nbaseBoard = nboards; ret = 0; cleanup: return ret; } static int virSysinfoOEMStringsParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virSysinfoOEMStringsDefPtr *oem) { VIR_XPATH_NODE_AUTORESTORE(ctxt); int ret = -1; virSysinfoOEMStringsDefPtr def; int nstrings; size_t i; g_autofree xmlNodePtr *strings = NULL; ctxt->node = node; nstrings = virXPathNodeSet("./entry", ctxt, &strings); if (nstrings < 0) return -1; if (nstrings == 0) return 0; if (VIR_ALLOC(def) < 0) goto cleanup; if (VIR_ALLOC_N(def->values, nstrings) < 0) goto cleanup; def->nvalues = nstrings; for (i = 0; i < nstrings; i++) def->values[i] = virXMLNodeContentString(strings[i]); *oem = g_steal_pointer(&def); ret = 0; cleanup: virSysinfoOEMStringsDefFree(def); return ret; } static int virSysinfoChassisParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virSysinfoChassisDefPtr *chassisdef) { VIR_XPATH_NODE_AUTORESTORE(ctxt); int ret = -1; virSysinfoChassisDefPtr def; ctxt->node = node; if (!xmlStrEqual(node->name, BAD_CAST "chassis")) { virReportError(VIR_ERR_XML_ERROR, "%s", _("XML does not contain expected 'chassis' element")); return ret; } if (VIR_ALLOC(def) < 0) goto cleanup; def->manufacturer = virXPathString("string(entry[@name='manufacturer'])", ctxt); def->version = virXPathString("string(entry[@name='version'])", ctxt); def->serial = virXPathString("string(entry[@name='serial'])", ctxt); def->asset = virXPathString("string(entry[@name='asset'])", ctxt); def->sku = virXPathString("string(entry[@name='sku'])", ctxt); if (!def->manufacturer && !def->version && !def->serial && !def->asset && !def->sku) { virSysinfoChassisDefFree(def); def = NULL; } *chassisdef = g_steal_pointer(&def); ret = 0; cleanup: virSysinfoChassisDefFree(def); return ret; } static virSysinfoDefPtr virSysinfoParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned char *domUUID, bool uuid_generated) { virSysinfoDefPtr def; xmlNodePtr tmpnode; g_autofree char *type = NULL; if (!virXMLNodeNameEqual(node, "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 */ if ((tmpnode = virXPathNode("./bios[1]", ctxt)) != NULL) { if (virSysinfoBIOSParseXML(tmpnode, ctxt, &def->bios) < 0) goto error; } /* Extract system related metadata */ if ((tmpnode = virXPathNode("./system[1]", ctxt)) != NULL) { if (virSysinfoSystemParseXML(tmpnode, ctxt, &def->system, domUUID, uuid_generated) < 0) goto error; } /* Extract system base board metadata */ if (virSysinfoBaseBoardParseXML(ctxt, &def->baseBoard, &def->nbaseBoard) < 0) goto error; /* Extract chassis related metadata */ if ((tmpnode = virXPathNode("./chassis[1]", ctxt)) != NULL) { if (virSysinfoChassisParseXML(tmpnode, ctxt, &def->chassis) < 0) goto error; } /* Extract system related metadata */ if ((tmpnode = virXPathNode("./oemStrings[1]", ctxt)) != NULL) { if (virSysinfoOEMStringsParseXML(tmpnode, ctxt, &def->oemStrings) < 0) goto error; } cleanup: return def; error: virSysinfoDefFree(def); def = NULL; goto cleanup; } unsigned int virDomainVideoDefaultRAM(const virDomainDef *def, const virDomainVideoType type) { 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_BOCHS: return 16 * 1024; 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; case VIR_DOMAIN_VIDEO_TYPE_DEFAULT: case VIR_DOMAIN_VIDEO_TYPE_VBOX: case VIR_DOMAIN_VIDEO_TYPE_PARALLELS: case VIR_DOMAIN_VIDEO_TYPE_VIRTIO: case VIR_DOMAIN_VIDEO_TYPE_GOP: case VIR_DOMAIN_VIDEO_TYPE_NONE: case VIR_DOMAIN_VIDEO_TYPE_RAMFB: case VIR_DOMAIN_VIDEO_TYPE_LAST: default: return 0; } } int virDomainVideoDefaultType(const virDomainDef *def) { switch ((virDomainVirtType)def->virtType) { case VIR_DOMAIN_VIRT_TEST: case VIR_DOMAIN_VIRT_XEN: if (def->os.type == VIR_DOMAIN_OSTYPE_XEN || def->os.type == VIR_DOMAIN_OSTYPE_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_VZ: case VIR_DOMAIN_VIRT_PARALLELS: if (def->os.type == VIR_DOMAIN_OSTYPE_HVM) return VIR_DOMAIN_VIDEO_TYPE_VGA; else return VIR_DOMAIN_VIDEO_TYPE_PARALLELS; case VIR_DOMAIN_VIRT_BHYVE: return VIR_DOMAIN_VIDEO_TYPE_GOP; case VIR_DOMAIN_VIRT_QEMU: case VIR_DOMAIN_VIRT_KQEMU: case VIR_DOMAIN_VIRT_KVM: case VIR_DOMAIN_VIRT_LXC: case VIR_DOMAIN_VIRT_UML: case VIR_DOMAIN_VIRT_OPENVZ: case VIR_DOMAIN_VIRT_HYPERV: case VIR_DOMAIN_VIRT_PHYP: case VIR_DOMAIN_VIRT_NONE: case VIR_DOMAIN_VIRT_LAST: default: return VIR_DOMAIN_VIDEO_TYPE_DEFAULT; } } static virDomainVideoAccelDefPtr virDomainVideoAccelDefParseXML(xmlNodePtr node) { xmlNodePtr cur; virDomainVideoAccelDefPtr def; int val; g_autofree char *accel2d = NULL; g_autofree char *accel3d = NULL; g_autofree char *rendernode = NULL; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!accel3d && !accel2d && virXMLNodeNameEqual(cur, "acceleration")) { accel3d = virXMLPropString(cur, "accel3d"); accel2d = virXMLPropString(cur, "accel2d"); rendernode = virXMLPropString(cur, "rendernode"); } } cur = cur->next; } if (!accel3d && !accel2d && !rendernode) return NULL; if (VIR_ALLOC(def) < 0) goto cleanup; if (accel3d) { if ((val = virTristateBoolTypeFromString(accel3d)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown accel3d value '%s'"), accel3d); goto cleanup; } def->accel3d = val; } if (accel2d) { if ((val = virTristateBoolTypeFromString(accel2d)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown accel2d value '%s'"), accel2d); goto cleanup; } def->accel2d = val; } if (rendernode) def->rendernode = virFileSanitizePath(rendernode); cleanup: return def; } static virDomainVideoResolutionDefPtr virDomainVideoResolutionDefParseXML(xmlNodePtr node) { xmlNodePtr cur; virDomainVideoResolutionDefPtr def; g_autofree char *x = NULL; g_autofree char *y = NULL; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!x && !y && virXMLNodeNameEqual(cur, "resolution")) { x = virXMLPropString(cur, "x"); y = virXMLPropString(cur, "y"); } } cur = cur->next; } if (!x || !y) return NULL; if (VIR_ALLOC(def) < 0) goto cleanup; if (x) { if (virStrToLong_uip(x, NULL, 10, &def->x) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("cannot parse video x-resolution '%s'"), x); goto cleanup; } } if (y) { if (virStrToLong_uip(y, NULL, 10, &def->y) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("cannot parse video y-resolution '%s'"), y); goto cleanup; } } cleanup: return def; } static virDomainVideoDriverDefPtr virDomainVideoDriverDefParseXML(xmlNodePtr node) { xmlNodePtr cur; virDomainVideoDriverDefPtr def; int val; g_autofree char *vgaconf = NULL; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!vgaconf && virXMLNodeNameEqual(cur, "driver")) { vgaconf = virXMLPropString(cur, "vgaconf"); } } cur = cur->next; } if (!vgaconf) return NULL; if (VIR_ALLOC(def) < 0) goto cleanup; if ((val = virDomainVideoVGAConfTypeFromString(vgaconf)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown vgaconf value '%s'"), vgaconf); goto cleanup; } def->vgaconf = val; cleanup: return def; } static virDomainVideoDefPtr virDomainVideoDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, const virDomainDef *dom, unsigned int flags) { virDomainVideoDefPtr def; xmlNodePtr cur; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *type = NULL; g_autofree char *driver_name = NULL; g_autofree char *heads = NULL; g_autofree char *vram = NULL; g_autofree char *vram64 = NULL; g_autofree char *ram = NULL; g_autofree char *vgamem = NULL; g_autofree char *primary = NULL; if (!(def = virDomainVideoDefNew(xmlopt))) return NULL; ctxt->node = node; cur = node->children; while (cur != NULL) { if (cur->type == XML_ELEMENT_NODE) { if (!type && !vram && !ram && !heads && virXMLNodeNameEqual(cur, "model")) { type = virXMLPropString(cur, "type"); ram = virXMLPropString(cur, "ram"); vram = virXMLPropString(cur, "vram"); vram64 = virXMLPropString(cur, "vram64"); vgamem = virXMLPropString(cur, "vgamem"); heads = virXMLPropString(cur, "heads"); if ((primary = virXMLPropString(cur, "primary")) != NULL) { if (STREQ(primary, "yes")) def->primary = true; VIR_FREE(primary); } def->accel = virDomainVideoAccelDefParseXML(cur); def->res = virDomainVideoResolutionDefParseXML(cur); } if (virXMLNodeNameEqual(cur, "driver")) { if (virDomainVirtioOptionsParseXML(cur, &def->virtio) < 0) goto error; driver_name = virXMLPropString(cur, "name"); } } cur = cur->next; } if (type) { if ((def->type = virDomainVideoTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown video model '%s'"), type); goto error; } } else { def->type = virDomainVideoDefaultType(dom); } if (driver_name) { int backend; if ((backend = virDomainVideoBackendTypeFromString(driver_name)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown video driver '%s'"), driver_name); goto error; } def->backend = backend; } else { def->backend = VIR_DOMAIN_VIDEO_BACKEND_TYPE_DEFAULT; } 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; } } if (vram) { if (virStrToLong_uip(vram, NULL, 10, &def->vram) < 0) { virReportError(VIR_ERR_XML_ERROR, _("cannot parse video vram '%s'"), vram); goto error; } } if (vram64) { if (def->type != VIR_DOMAIN_VIDEO_TYPE_QXL) { virReportError(VIR_ERR_XML_ERROR, "%s", _("vram64 attribute only supported for type of qxl")); goto error; } if (virStrToLong_uip(vram64, NULL, 10, &def->vram64) < 0) { virReportError(VIR_ERR_XML_ERROR, _("cannot parse video vram64 '%s'"), vram64); goto error; } } 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; } } if (virDomainDeviceInfoParseXML(xmlopt, node, &def->info, flags) < 0) goto error; def->driver = virDomainVideoDriverDefParseXML(node); cleanup: return def; error: virDomainVideoDefFree(def); def = NULL; goto cleanup; } static virDomainHostdevDefPtr virDomainHostdevDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainHostdevDefPtr def; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *mode = virXMLPropString(node, "mode"); g_autofree char *type = virXMLPropString(node, "type"); ctxt->node = node; if (!(def = virDomainHostdevDefNew())) 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(xmlopt, node, 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 ((virDomainHostdevSubsysType) def->source.subsys.type) { case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI: if (virXPathBoolean("boolean(./readonly)", ctxt)) def->readonly = true; if (virXPathBoolean("boolean(./shareable)", ctxt)) def->shareable = true; break; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST: break; } } cleanup: return def; error: virDomainHostdevDefFree(def); def = NULL; goto cleanup; } static virDomainRedirdevDefPtr virDomainRedirdevDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { xmlNodePtr cur; virDomainRedirdevDefPtr def; g_autofree char *bus = NULL; g_autofree char *type = NULL; if (VIR_ALLOC(def) < 0) return NULL; if (!(def->source = virDomainChrSourceDefNew(xmlopt))) goto error; 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->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 */ if (virDomainChrSourceDefParseXML(def->source, cur, flags, NULL, ctxt, NULL, 0) < 0) goto error; if (def->source->type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) def->source->data.spicevmc = VIR_DOMAIN_CHR_SPICEVMC_USBREDIR; if (virDomainDeviceInfoParseXML(xmlopt, node, &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: 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) { virDomainRedirFilterUSBDevDefPtr def; g_autofree char *class = NULL; g_autofree char *vendor = NULL; g_autofree char *product = NULL; g_autofree char *version = NULL; g_autofree char *allow = NULL; 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 (virStringParseYesNo(allow, &def->allow) < 0) { 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: return def; error: VIR_FREE(def); def = NULL; goto cleanup; } static virDomainRedirFilterDefPtr virDomainRedirFilterDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt) { int n; size_t i; VIR_XPATH_NODE_AUTORESTORE(ctxt); virDomainRedirFilterDefPtr def = NULL; g_autofree xmlNodePtr *nodes = 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; } return def; error: virDomainRedirFilterDefFree(def); return NULL; } static int virDomainEventActionParseXML(xmlXPathContextPtr ctxt, const char *name, const char *xpath, int *val, int defaultVal, virEventActionFromStringFunc convFunc) { g_autofree 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); return -1; } } return 0; } static int virDomainPMStateParseXML(xmlXPathContextPtr ctxt, const char *xpath, int *val) { g_autofree char *tmp = virXPathString(xpath, ctxt); if (tmp) { *val = virTristateBoolTypeFromString(tmp); if (*val < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown PM state value %s"), tmp); return -1; } } return 0; } static int virDomainPerfEventDefParseXML(virDomainPerfDefPtr perf, xmlNodePtr node) { int event; g_autofree char *name = NULL; g_autofree char *enabled = NULL; if (!(name = virXMLPropString(node, "name"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing perf event name")); return -1; } if ((event = virPerfEventTypeFromString(name)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("'unsupported perf event '%s'"), name); return -1; } if (perf->events[event] != VIR_TRISTATE_BOOL_ABSENT) { virReportError(VIR_ERR_XML_ERROR, _("perf event '%s' was already specified"), name); return -1; } if (!(enabled = virXMLPropString(node, "enabled"))) { virReportError(VIR_ERR_XML_ERROR, _("missing state of perf event '%s'"), name); return -1; } if ((perf->events[event] = virTristateBoolTypeFromString(enabled)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid state '%s' of perf event '%s'"), enabled, name); return -1; } return 0; } static int virDomainPerfDefParseXML(virDomainDefPtr def, xmlXPathContextPtr ctxt) { size_t i; int n; g_autofree xmlNodePtr *nodes = NULL; if ((n = virXPathNodeSet("./perf/event", ctxt, &nodes)) < 0) return n; for (i = 0; i < n; i++) { if (virDomainPerfEventDefParseXML(&def->perf, nodes[i]) < 0) return -1; } return 0; } static int virDomainMemorySourceDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virDomainMemoryDefPtr def) { int ret = -1; VIR_XPATH_NODE_AUTORESTORE(ctxt); ctxt->node = node; g_autofree char *nodemask = NULL; switch ((virDomainMemoryModel) def->model) { case VIR_DOMAIN_MEMORY_MODEL_DIMM: if (virDomainParseMemory("./pagesize", "./pagesize/@unit", ctxt, &def->pagesize, false, false) < 0) goto cleanup; if ((nodemask = virXPathString("string(./nodemask)", ctxt))) { if (virBitmapParse(nodemask, &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; } } break; case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: if (!(def->nvdimmPath = virXPathString("string(./path)", ctxt))) { virReportError(VIR_ERR_XML_DETAIL, "%s", _("path is required for model 'nvdimm'")); goto cleanup; } if (virDomainParseMemory("./alignsize", "./alignsize/@unit", ctxt, &def->alignsize, false, false) < 0) goto cleanup; if (virXPathBoolean("boolean(./pmem)", ctxt)) def->nvdimmPmem = true; break; case VIR_DOMAIN_MEMORY_MODEL_NONE: case VIR_DOMAIN_MEMORY_MODEL_LAST: break; } ret = 0; cleanup: return ret; } static int virDomainMemoryTargetDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt, virDomainMemoryDefPtr def) { int ret = -1; VIR_XPATH_NODE_AUTORESTORE(ctxt); ctxt->node = node; int rv; /* initialize to value which marks that the user didn't specify it */ def->targetNode = -1; if ((rv = virXPathInt("string(./node)", ctxt, &def->targetNode)) == -2 || (rv == 0 && def->targetNode < 0)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid value of memory device node")); goto cleanup; } if (virDomainParseMemory("./size", "./size/@unit", ctxt, &def->size, true, false) < 0) goto cleanup; if (def->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) { if (virDomainParseMemory("./label/size", "./label/size/@unit", ctxt, &def->labelsize, false, false) < 0) goto cleanup; if (def->labelsize && def->labelsize < 128) { virReportError(VIR_ERR_XML_ERROR, "%s", _("nvdimm label must be at least 128KiB")); goto cleanup; } if (def->labelsize >= def->size) { virReportError(VIR_ERR_XML_ERROR, "%s", _("label size must be smaller than NVDIMM size")); goto cleanup; } if (virXPathBoolean("boolean(./readonly)", ctxt)) def->readonly = true; } ret = 0; cleanup: return ret; } static virDomainSEVDefPtr virDomainSEVDefParseXML(xmlNodePtr sevNode, xmlXPathContextPtr ctxt) { VIR_XPATH_NODE_AUTORESTORE(ctxt); virDomainSEVDefPtr def; unsigned long policy; g_autofree char *type = NULL; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = sevNode; if (!(type = virXMLPropString(sevNode, "type"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing launch security type")); goto error; } def->sectype = virDomainLaunchSecurityTypeFromString(type); switch ((virDomainLaunchSecurity) def->sectype) { case VIR_DOMAIN_LAUNCH_SECURITY_SEV: break; case VIR_DOMAIN_LAUNCH_SECURITY_NONE: case VIR_DOMAIN_LAUNCH_SECURITY_LAST: default: virReportError(VIR_ERR_XML_ERROR, _("unsupported launch security type '%s'"), type); goto error; } if (virXPathUInt("string(./cbitpos)", ctxt, &def->cbitpos) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("failed to get launch security cbitpos")); goto error; } if (virXPathUInt("string(./reducedPhysBits)", ctxt, &def->reduced_phys_bits) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("failed to get launch security reduced-phys-bits")); goto error; } if (virXPathULongHex("string(./policy)", ctxt, &policy) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("failed to get launch security policy")); goto error; } def->policy = policy; def->dh_cert = virXPathString("string(./dhCert)", ctxt); def->session = virXPathString("string(./session)", ctxt); cleanup: return def; error: virDomainSEVDefFree(def); def = NULL; goto cleanup; } static virDomainMemoryDefPtr virDomainMemoryDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr memdevNode, xmlXPathContextPtr ctxt, unsigned int flags) { VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr node; virDomainMemoryDefPtr def; int val; g_autofree char *tmp = NULL; if (VIR_ALLOC(def) < 0) return NULL; ctxt->node = memdevNode; 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); if ((tmp = virXMLPropString(memdevNode, "access"))) { if ((val = virDomainMemoryAccessTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid access mode '%s'"), tmp); goto error; } def->access = val; } VIR_FREE(tmp); if ((tmp = virXMLPropString(memdevNode, "discard"))) { if ((val = virTristateBoolTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid discard value '%s'"), tmp); goto error; } def->discard = val; } /* 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(xmlopt, memdevNode, &def->info, flags) < 0) goto error; return def; error: virDomainMemoryDefFree(def); return NULL; } static virDomainIOMMUDefPtr virDomainIOMMUDefParseXML(xmlNodePtr node, xmlXPathContextPtr ctxt) { virDomainIOMMUDefPtr ret = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr driver; int val; g_autofree char *tmp = NULL; g_autofree virDomainIOMMUDefPtr iommu = NULL; ctxt->node = node; if (VIR_ALLOC(iommu) < 0) goto cleanup; if (!(tmp = virXMLPropString(node, "model"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing model for IOMMU device")); goto cleanup; } if ((val = virDomainIOMMUModelTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown IOMMU model: %s"), tmp); goto cleanup; } iommu->model = val; if ((driver = virXPathNode("./driver", ctxt))) { VIR_FREE(tmp); if ((tmp = virXMLPropString(driver, "intremap"))) { if ((val = virTristateSwitchTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown intremap value: %s"), tmp); goto cleanup; } iommu->intremap = val; } VIR_FREE(tmp); if ((tmp = virXMLPropString(driver, "caching_mode"))) { if ((val = virTristateSwitchTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown caching_mode value: %s"), tmp); goto cleanup; } iommu->caching_mode = val; } VIR_FREE(tmp); if ((tmp = virXMLPropString(driver, "iotlb"))) { if ((val = virTristateSwitchTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown iotlb value: %s"), tmp); goto cleanup; } iommu->iotlb = val; } VIR_FREE(tmp); if ((tmp = virXMLPropString(driver, "eim"))) { if ((val = virTristateSwitchTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown eim value: %s"), tmp); goto cleanup; } iommu->eim = val; } } ret = g_steal_pointer(&iommu); cleanup: return ret; } static virDomainVsockDefPtr virDomainVsockDefParseXML(virDomainXMLOptionPtr xmlopt, xmlNodePtr node, xmlXPathContextPtr ctxt, unsigned int flags) { virDomainVsockDefPtr ret = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); xmlNodePtr cid; int val; g_autofree char *tmp = NULL; g_autofree virDomainVsockDefPtr vsock = NULL; ctxt->node = node; if (!(vsock = virDomainVsockDefNew(xmlopt))) goto cleanup; if ((tmp = virXMLPropString(node, "model"))) { if ((val = virDomainVsockModelTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown vsock model: %s"), tmp); goto cleanup; } vsock->model = val; } cid = virXPathNode("./cid", ctxt); VIR_FREE(tmp); if (cid) { if ((tmp = virXMLPropString(cid, "address"))) { if (virStrToLong_uip(tmp, NULL, 10, &vsock->guest_cid) < 0 || vsock->guest_cid == 0) { virReportError(VIR_ERR_XML_DETAIL, _("'cid' attribute must be a positive number: %s"), tmp); goto cleanup; } } VIR_FREE(tmp); if ((tmp = virXMLPropString(cid, "auto"))) { val = virTristateBoolTypeFromString(tmp); if (val <= 0) { virReportError(VIR_ERR_XML_DETAIL, _("'auto' attribute can be 'yes' or 'no': %s"), tmp); goto cleanup; } vsock->auto_cid = val; } } if (virDomainDeviceInfoParseXML(xmlopt, node, &vsock->info, flags) < 0) goto cleanup; ret = g_steal_pointer(&vsock); cleanup: return ret; } virDomainDeviceDefPtr virDomainDeviceDefParse(const char *xmlStr, const virDomainDef *def, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, void *parseOpaque, unsigned int flags) { g_autoptr(xmlDoc) xml = NULL; xmlNodePtr node; g_autoptr(xmlXPathContext) ctxt = NULL; g_autofree virDomainDeviceDefPtr dev = NULL; char *netprefix; if (!(xml = virXMLParseStringCtxt(xmlStr, _("(device_definition)"), &ctxt))) return NULL; node = ctxt->node; if (VIR_ALLOC(dev) < 0) return NULL; if ((dev->type = virDomainDeviceTypeFromString((const char *) node->name)) < 0) { /* Some crazy mapping of serial, parallel, console and channel to * VIR_DOMAIN_DEVICE_CHR. */ if (virXMLNodeNameEqual(node, "channel") || virXMLNodeNameEqual(node, "console") || virXMLNodeNameEqual(node, "parallel") || virXMLNodeNameEqual(node, "serial")) { dev->type = VIR_DOMAIN_DEVICE_CHR; } else { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown device type '%s'"), node->name); return NULL; } } switch ((virDomainDeviceType) dev->type) { case VIR_DOMAIN_DEVICE_DISK: if (!(dev->data.disk = virDomainDiskDefParseXML(xmlopt, node, ctxt, def->seclabels, def->nseclabels, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_LEASE: if (!(dev->data.lease = virDomainLeaseDefParseXML(node))) return NULL; break; case VIR_DOMAIN_DEVICE_FS: if (!(dev->data.fs = virDomainFSDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_NET: netprefix = caps->host.netprefix; if (!(dev->data.net = virDomainNetDefParseXML(xmlopt, node, ctxt, netprefix, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_INPUT: if (!(dev->data.input = virDomainInputDefParseXML(xmlopt, def, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_SOUND: if (!(dev->data.sound = virDomainSoundDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_WATCHDOG: if (!(dev->data.watchdog = virDomainWatchdogDefParseXML(xmlopt, node, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_VIDEO: if (!(dev->data.video = virDomainVideoDefParseXML(xmlopt, node, ctxt, def, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_HOSTDEV: if (!(dev->data.hostdev = virDomainHostdevDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_CONTROLLER: if (!(dev->data.controller = virDomainControllerDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_GRAPHICS: if (!(dev->data.graphics = virDomainGraphicsDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_HUB: if (!(dev->data.hub = virDomainHubDefParseXML(xmlopt, node, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_REDIRDEV: if (!(dev->data.redirdev = virDomainRedirdevDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_RNG: if (!(dev->data.rng = virDomainRNGDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_CHR: if (!(dev->data.chr = virDomainChrDefParseXML(xmlopt, ctxt, node, def->seclabels, def->nseclabels, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_SMARTCARD: if (!(dev->data.smartcard = virDomainSmartcardDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_MEMBALLOON: if (!(dev->data.memballoon = virDomainMemballoonDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_NVRAM: if (!(dev->data.nvram = virDomainNVRAMDefParseXML(xmlopt, node, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_SHMEM: if (!(dev->data.shmem = virDomainShmemDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_TPM: if (!(dev->data.tpm = virDomainTPMDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_PANIC: if (!(dev->data.panic = virDomainPanicDefParseXML(xmlopt, node, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_MEMORY: if (!(dev->data.memory = virDomainMemoryDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_IOMMU: if (!(dev->data.iommu = virDomainIOMMUDefParseXML(node, ctxt))) return NULL; break; case VIR_DOMAIN_DEVICE_VSOCK: if (!(dev->data.vsock = virDomainVsockDefParseXML(xmlopt, node, ctxt, flags))) return NULL; break; case VIR_DOMAIN_DEVICE_NONE: case VIR_DOMAIN_DEVICE_LAST: break; } /* callback to fill driver specific device aspects */ if (virDomainDeviceDefPostParseOne(dev, def, caps, flags, xmlopt, parseOpaque) < 0) return NULL; /* validate the configuration */ if (virDomainDeviceDefValidate(dev, def, flags, xmlopt) < 0) return NULL; return g_steal_pointer(&dev); } virDomainDiskDefPtr virDomainDiskDefParse(const char *xmlStr, const virDomainDef *def, virDomainXMLOptionPtr xmlopt, unsigned int flags) { g_autoptr(xmlDoc) xml = NULL; g_autoptr(xmlXPathContext) ctxt = NULL; virSecurityLabelDefPtr *seclabels = NULL; size_t nseclabels = 0; if (!(xml = virXMLParseStringCtxt(xmlStr, _("(disk_definition)"), &ctxt))) return NULL; if (!virXMLNodeNameEqual(ctxt->node, "disk")) { virReportError(VIR_ERR_XML_ERROR, _("expecting root element of 'disk', not '%s'"), ctxt->node->name); return NULL; } if (def) { seclabels = def->seclabels; nseclabels = def->nseclabels; } return virDomainDiskDefParseXML(xmlopt, ctxt->node, ctxt, seclabels, nseclabels, flags); } 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->src->hosts[0].name, second_iscsisrc->src->hosts[0].name) && first_iscsisrc->src->hosts[0].port == second_iscsisrc->src->hosts[0].port && STREQ(first_iscsisrc->src->path, second_iscsisrc->src->path)) return 1; return 0; } static int virDomainHostdevMatchSubsysMediatedDev(virDomainHostdevDefPtr a, virDomainHostdevDefPtr b) { virDomainHostdevSubsysMediatedDevPtr src_a = &a->source.subsys.u.mdev; virDomainHostdevSubsysMediatedDevPtr src_b = &b->source.subsys.u.mdev; if (STREQ(src_a->uuidstr, src_b->uuidstr)) return 1; return 0; } static int virDomainHostdevMatchSubsys(virDomainHostdevDefPtr a, virDomainHostdevDefPtr b) { if (a->source.subsys.type != b->source.subsys.type) return 0; switch ((virDomainHostdevSubsysType) 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); case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST: if (a->source.subsys.u.scsi_host.protocol != b->source.subsys.u.scsi_host.protocol) return 0; if (STREQ(a->source.subsys.u.scsi_host.wwpn, b->source.subsys.u.scsi_host.wwpn)) return 1; else return 0; case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV: return virDomainHostdevMatchSubsysMediatedDev(a, b); case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST: return 0; } 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.ifname, b->source.caps.u.net.ifname); } 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; } int virDomainDiskIndexByAddress(virDomainDefPtr def, virPCIDeviceAddressPtr 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 && virPCIDeviceAddressEqual(&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; } virDomainDiskDefPtr virDomainDiskByAddress(virDomainDefPtr def, virPCIDeviceAddressPtr pci_address, unsigned int bus, unsigned int target, unsigned int unit) { int idx = virDomainDiskIndexByAddress(def, pci_address, bus, target, unit); return idx < 0 ? NULL : def->disks[idx]; } 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; } virDomainDiskDefPtr virDomainDiskByName(virDomainDefPtr def, const char *name, bool allow_ambiguous) { int idx = virDomainDiskIndexByName(def, name, allow_ambiguous); return idx < 0 ? NULL : def->disks[idx]; } virDomainDiskDefPtr virDomainDiskByTarget(virDomainDefPtr def, const char *dst) { size_t i; for (i = 0; i < def->ndisks; i++) { if (STREQ(def->disks[i]->dst, dst)) return def->disks[i]; } return NULL; } 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; /* Tentatively 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); } 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: * @def: domain definition * @net: interface definition * * Lookup domain's network interface based on passed @net * definition. If @net's MAC address was auto generated, * the MAC comparison is ignored. * * 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 MACAddrSpecified = !net->mac_generated; bool PCIAddrSpecified = virDomainDeviceAddressIsValid(&net->info, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI); for (i = 0; i < def->nnets; i++) { if (MACAddrSpecified && virMacAddrCmp(&def->nets[i]->mac, &net->mac) != 0) continue; if (PCIAddrSpecified && !virPCIDeviceAddressEqual(&def->nets[i]->info.addr.pci, &net->info.addr.pci)) continue; if (matchidx >= 0) { /* 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. */ if (MACAddrSpecified) { virReportError(VIR_ERR_OPERATION_FAILED, _("multiple devices matching MAC address %s found"), virMacAddrFormat(&net->mac, mac)); } else { virReportError(VIR_ERR_OPERATION_FAILED, "%s", _("multiple matching devices found")); } return -1; } matchidx = i; } if (matchidx < 0) { if (MACAddrSpecified && PCIAddrSpecified) { virReportError(VIR_ERR_DEVICE_MISSING, _("no device matching MAC address %s found on " VIR_PCI_DEVICE_ADDRESS_FMT), 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 if (PCIAddrSpecified) { virReportError(VIR_ERR_DEVICE_MISSING, _("no device found on " VIR_PCI_DEVICE_ADDRESS_FMT), net->info.addr.pci.domain, net->info.addr.pci.bus, net->info.addr.pci.slot, net->info.addr.pci.function); } else if (MACAddrSpecified) { virReportError(VIR_ERR_DEVICE_MISSING, _("no device matching MAC address %s found"), virMacAddrFormat(&net->mac, mac)); } else { virReportError(VIR_ERR_DEVICE_MISSING, "%s", _("no matching device found")); } } 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 (virPCIDeviceAddressEqual(&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 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 virDomainNetUpdate(virDomainDefPtr def, size_t netidx, virDomainNetDefPtr newnet) { size_t hostdevidx; virDomainNetDefPtr oldnet = def->nets[netidx]; virDomainHostdevDefPtr oldhostdev = virDomainNetGetActualHostdev(oldnet); virDomainHostdevDefPtr newhostdev = virDomainNetGetActualHostdev(newnet); /* * if newnet or oldnet has a valid hostdev*, we need to update the * hostdevs list */ if (oldhostdev) { for (hostdevidx = 0; hostdevidx < def->nhostdevs; hostdevidx++) { if (def->hostdevs[hostdevidx] == oldhostdev) break; } } if (oldhostdev && hostdevidx < def->nhostdevs) { if (newhostdev) { /* update existing entry in def->hostdevs */ def->hostdevs[hostdevidx] = newhostdev; } else { /* delete oldhostdev from def->hostdevs */ virDomainHostdevRemove(def, hostdevidx); } } else if (newhostdev) { /* add newhostdev to end of def->hostdevs */ if (VIR_APPEND_ELEMENT(def->hostdevs, def->nhostdevs, newhostdev) < 0) return -1; } def->nets[netidx] = newnet; return 0; } 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; /* Tentatively plan to insert controller at the end. */ int insertAt = -1; virDomainControllerDefPtr current = NULL; /* 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--) { current = def->controllers[idx]; if (current->type == controller->type) { if (controller->idx == -1) { /* If the new controller doesn't have an index set * yet, put it just past this controller, which until * now was the last controller of this type. */ insertAt = idx + 1; break; } if (current->idx > controller->idx) { /* If bus matches and current controller is after * new controller, then new controller should go here * */ insertAt = idx; } else if (controller->info.mastertype == VIR_DOMAIN_CONTROLLER_MASTER_NONE && current->info.mastertype != VIR_DOMAIN_CONTROLLER_MASTER_NONE && current->idx == controller->idx) { /* If bus matches and index matches and new controller is * master and current isn't a master, then new controller * should go here to be placed before its companion */ insertAt = idx; } else if (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(const virDomainDef *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 virDomainControllerFindUnusedIndex(virDomainDef const *def, int type) { int idx = 0; while (virDomainControllerFind(def, type, idx) >= 0) idx++; return idx; } const char * virDomainControllerAliasFind(const virDomainDef *def, int type, int idx) { int contIndex; const char *contTypeStr = virDomainControllerTypeToString(type); if (!contTypeStr) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown controller type %d"), type); return NULL; } contIndex = virDomainControllerFind(def, type, idx); if (contIndex < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Could not find %s controller with index %d " "required for device"), contTypeStr, idx); return NULL; } if (!def->controllers[contIndex]->info.alias) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Device alias was not set for %s controller " "with index %d "), contTypeStr, idx); return NULL; } return def->controllers[contIndex]->info.alias; } 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, virPCIDeviceAddressPtr 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 && virPCIDeviceAddressEqual(&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_XEN: 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->targetType != tgt->targetType) return false; G_GNUC_FALLTHROUGH; 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 int G_GNUC_WARN_UNUSED_RESULT 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; return 0; case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL: *arrPtr = &vmdef->serials; *cntPtr = &vmdef->nserials; return 0; case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE: *arrPtr = &vmdef->consoles; *cntPtr = &vmdef->nconsoles; return 0; case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL: *arrPtr = &vmdef->channels; *cntPtr = &vmdef->nchannels; return 0; case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST: break; } virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown char device type: %d"), type); return -1; } /* 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. */ if (virDomainChrGetDomainPtrsInternal((virDomainDefPtr) vmdef, type, &arrVar, &cntVar) < 0) { *arrPtr = NULL; *cntPtr = 0; } else { *arrPtr = (const virDomainChrDef **) *arrVar; *cntPtr = *cntVar; } } int virDomainChrPreAlloc(virDomainDefPtr vmdef, virDomainChrDefPtr chr) { virDomainChrDefPtr **arrPtr = NULL; size_t *cntPtr = NULL; if (virDomainChrGetDomainPtrsInternal(vmdef, chr->deviceType, &arrPtr, &cntPtr) < 0) return -1; return VIR_REALLOC_N(*arrPtr, *cntPtr + 1); } void virDomainChrInsertPreAlloced(virDomainDefPtr vmdef, virDomainChrDefPtr chr) { virDomainChrDefPtr **arrPtr = NULL; size_t *cntPtr = NULL; if (virDomainChrGetDomainPtrsInternal(vmdef, chr->deviceType, &arrPtr, &cntPtr) < 0) return; VIR_APPEND_ELEMENT_INPLACE(*arrPtr, *cntPtr, chr); } virDomainChrDefPtr virDomainChrRemove(virDomainDefPtr vmdef, virDomainChrDefPtr chr) { virDomainChrDefPtr ret = NULL, **arrPtr = NULL; size_t i, *cntPtr = NULL; if (virDomainChrGetDomainPtrsInternal(vmdef, chr->deviceType, &arrPtr, &cntPtr) < 0) return NULL; 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; } 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; switch ((virDomainMemoryModel) mem->model) { case VIR_DOMAIN_MEMORY_MODEL_DIMM: /* source stuff -> match with device */ if (tmp->pagesize != mem->pagesize) continue; if (!virBitmapEqual(tmp->sourceNodes, mem->sourceNodes)) continue; break; case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: if (STRNEQ(tmp->nvdimmPath, mem->nvdimmPath)) continue; break; case VIR_DOMAIN_MEMORY_MODEL_NONE: case VIR_DOMAIN_MEMORY_MODEL_LAST: break; } 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; } /** * virDomainMemoryInsert: * * Inserts a memory device definition into the domain definition. This helper * should be used only in hot/cold-plug cases as it's blindly modifying the * total memory size. */ int virDomainMemoryInsert(virDomainDefPtr def, virDomainMemoryDefPtr mem) { unsigned long long memory = virDomainDefGetMemoryTotal(def); 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_COPY(def->mems, def->nmems, mem) < 0) return -1; virDomainDefSetMemoryTotal(def, memory + mem->size); return id; } /** * virDomainMemoryRemove: * * Removes a memory device definition from the domain definition. This helper * should be used only in hot/cold-plug cases as it's blindly modifying the * total memory size. */ virDomainMemoryDefPtr virDomainMemoryRemove(virDomainDefPtr def, int idx) { unsigned long long memory = virDomainDefGetMemoryTotal(def); virDomainMemoryDefPtr ret = def->mems[idx]; VIR_DELETE_ELEMENT(def->mems, idx, def->nmems); /* fix total memory size of the domain */ virDomainDefSetMemoryTotal(def, memory - ret->size); return ret; } ssize_t virDomainRedirdevDefFind(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev) { size_t i; for (i = 0; i < def->nredirdevs; i++) { virDomainRedirdevDefPtr tmp = def->redirdevs[i]; if (redirdev->bus != tmp->bus) continue; if (!virDomainChrSourceDefIsEqual(redirdev->source, tmp->source)) continue; if (redirdev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && !virDomainDeviceInfoAddressIsEqual(&redirdev->info, &tmp->info)) continue; if (redirdev->info.alias && STRNEQ_NULLABLE(redirdev->info.alias, tmp->info.alias)) continue; return i; } return -1; } virDomainRedirdevDefPtr virDomainRedirdevDefRemove(virDomainDefPtr def, size_t idx) { virDomainRedirdevDefPtr ret = def->redirdevs[idx]; VIR_DELETE_ELEMENT(def->redirdevs, idx, def->nredirdevs); return ret; } int virDomainShmemDefInsert(virDomainDefPtr def, virDomainShmemDefPtr shmem) { return VIR_APPEND_ELEMENT(def->shmems, def->nshmems, shmem); } bool virDomainShmemDefEquals(virDomainShmemDefPtr src, virDomainShmemDefPtr dst) { if (STRNEQ_NULLABLE(src->name, dst->name)) return false; if (src->size != dst->size) return false; if (src->model != dst->model) return false; if (src->server.enabled != dst->server.enabled) return false; if (src->server.enabled) { if (STRNEQ_NULLABLE(src->server.chr.data.nix.path, dst->server.chr.data.nix.path)) return false; } if (src->msi.enabled != dst->msi.enabled) return false; if (src->msi.enabled) { if (src->msi.vectors != dst->msi.vectors) return false; if (src->msi.ioeventfd != dst->msi.ioeventfd) return false; } if (src->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && !virDomainDeviceInfoAddressIsEqual(&src->info, &dst->info)) return false; return true; } ssize_t virDomainShmemDefFind(virDomainDefPtr def, virDomainShmemDefPtr shmem) { size_t i; for (i = 0; i < def->nshmems; i++) { if (virDomainShmemDefEquals(shmem, def->shmems[i])) return i; } return -1; } virDomainShmemDefPtr virDomainShmemDefRemove(virDomainDefPtr def, size_t idx) { virDomainShmemDefPtr ret = def->shmems[idx]; VIR_DELETE_ELEMENT(def->shmems, idx, def->nshmems); return ret; } static bool virDomainInputDefEquals(const virDomainInputDef *a, const virDomainInputDef *b) { if (a->type != b->type) return false; if (a->bus != b->bus) return false; if (a->type == VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH && STRNEQ_NULLABLE(a->source.evdev, b->source.evdev)) return false; if (a->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && !virDomainDeviceInfoAddressIsEqual(&a->info, &b->info)) return false; return true; } ssize_t virDomainInputDefFind(const virDomainDef *def, const virDomainInputDef *input) { size_t i; for (i = 0; i < def->ninputs; i++) { if (virDomainInputDefEquals(input, def->inputs[i])) return i; } return -1; } bool virDomainVsockDefEquals(const virDomainVsockDef *a, const virDomainVsockDef *b) { if (a->model != b->model) return false; if (a->auto_cid != b->auto_cid) return false; if (a->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE && !virDomainDeviceInfoAddressIsEqual(&a->info, &b->info)) return false; return true; } char * virDomainDefGetDefaultEmulator(virDomainDefPtr def, virCapsPtr caps) { char *retemu; g_autofree virCapsDomainDataPtr capsdata = NULL; if (!(capsdata = virCapabilitiesDomainDataLookup(caps, def->os.type, def->os.arch, def->virtType, NULL, NULL))) return NULL; retemu = g_strdup(capsdata->emulator); return retemu; } static int virDomainDefParseBootXML(xmlXPathContextPtr ctxt, virDomainDefPtr def) { xmlNodePtr node; size_t i; int n; g_autofree char *tmp = NULL; g_autofree xmlNodePtr *nodes = NULL; /* analysis of the boot devices */ if ((n = virXPathNodeSet("./os/boot", ctxt, &nodes)) < 0) return -1; 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")); return -1; } if ((val = virDomainBootTypeFromString(dev)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown boot device '%s'"), dev); VIR_FREE(dev); return -1; } VIR_FREE(dev); def->os.bootDevs[def->os.nBootDevs++] = val; } if ((node = virXPathNode("./os/bootmenu[1]", ctxt))) { tmp = virXMLPropString(node, "enable"); 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 = virXMLPropString(node, "timeout"); 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]")); return -1; } def->os.bm_timeout_set = true; } VIR_FREE(tmp); } if ((node = virXPathNode("./os/bios[1]", ctxt))) { tmp = virXMLPropString(node, "useserial"); if (tmp) { if (STREQ(tmp, "yes")) def->os.bios.useserial = VIR_TRISTATE_BOOL_YES; else def->os.bios.useserial = VIR_TRISTATE_BOOL_NO; VIR_FREE(tmp); } tmp = virXMLPropString(node, "rebootTimeout"); 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]")); return -1; } def->os.bios.rt_set = true; } } return 0; } 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; VIR_XPATH_NODE_AUTORESTORE(ctxt); 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: return idmap; } /* Parse the XML definition for an IOThread ID * * Format is : * * 4 * * * * * * */ static virDomainIOThreadIDDefPtr virDomainIOThreadIDDefParseXML(xmlNodePtr node) { virDomainIOThreadIDDefPtr iothrid; g_autofree char *tmp = NULL; if (VIR_ALLOC(iothrid) < 0) return NULL; if (!(tmp = virXMLPropString(node, "id"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing 'id' attribute in element")); goto error; } if (virStrToLong_uip(tmp, NULL, 10, &iothrid->iothread_id) < 0 || iothrid->iothread_id == 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid iothread 'id' value '%s'"), tmp); goto error; } cleanup: return iothrid; error: virDomainIOThreadIDDefFree(iothrid); iothrid = NULL; goto cleanup; } static int virDomainDefParseIOThreads(virDomainDefPtr def, xmlXPathContextPtr ctxt) { size_t i; int n = 0; unsigned int iothreads = 0; g_autofree char *tmp = NULL; g_autofree xmlNodePtr *nodes = NULL; tmp = virXPathString("string(./iothreads[1])", ctxt); if (tmp && virStrToLong_uip(tmp, NULL, 10, &iothreads) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid iothreads count '%s'"), tmp); return -1; } /* Extract any iothread id's defined */ if ((n = virXPathNodeSet("./iothreadids/iothread", ctxt, &nodes)) < 0) return -1; if (n > iothreads) iothreads = n; if (n && VIR_ALLOC_N(def->iothreadids, n) < 0) return -1; for (i = 0; i < n; i++) { virDomainIOThreadIDDefPtr iothrid = NULL; if (!(iothrid = virDomainIOThreadIDDefParseXML(nodes[i]))) return -1; if (virDomainIOThreadIDFind(def, iothrid->iothread_id)) { virReportError(VIR_ERR_XML_ERROR, _("duplicate iothread id '%u' found"), iothrid->iothread_id); virDomainIOThreadIDDefFree(iothrid); return -1; } def->iothreadids[def->niothreadids++] = iothrid; } return virDomainIOThreadIDDefArrayInit(def, iothreads); } /* Parse the XML definition for a vcpupin * * vcpupin has the form of * */ static int virDomainVcpuPinDefParseXML(virDomainDefPtr def, xmlNodePtr node) { virDomainVcpuDefPtr vcpu; unsigned int vcpuid; g_autofree char *tmp = NULL; if (!(tmp = virXMLPropString(node, "vcpu"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing vcpu id in vcpupin")); return -1; } if (virStrToLong_uip(tmp, NULL, 10, &vcpuid) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid setting for vcpu '%s'"), tmp); return -1; } VIR_FREE(tmp); if (!(vcpu = virDomainDefGetVcpu(def, vcpuid))) { VIR_WARN("Ignoring vcpupin for missing vcpus"); return 0; } if (!(tmp = virXMLPropString(node, "cpuset"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing cpuset for vcpupin")); return -1; } if (vcpu->cpumask) { virReportError(VIR_ERR_INTERNAL_ERROR, _("duplicate vcpupin for vcpu '%d'"), vcpuid); return -1; } if (virBitmapParse(tmp, &vcpu->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0) return -1; if (virBitmapIsAllClear(vcpu->cpumask)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid value of 'cpuset': %s"), tmp); return -1; } return 0; } /* Parse the XML definition for a iothreadpin * and an iothreadspin has the form * */ static int virDomainIOThreadPinDefParseXML(xmlNodePtr node, virDomainDefPtr def) { virDomainIOThreadIDDefPtr iothrid; unsigned int iothreadid; g_autofree char *tmp = NULL; g_autoptr(virBitmap) cpumask = NULL; if (!(tmp = virXMLPropString(node, "iothread"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing iothread id in iothreadpin")); return -1; } if (virStrToLong_uip(tmp, NULL, 10, &iothreadid) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid setting for iothread '%s'"), tmp); return -1; } VIR_FREE(tmp); if (iothreadid == 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("zero is an invalid iothread id value")); return -1; } if (!(iothrid = virDomainIOThreadIDFind(def, iothreadid))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Cannot find 'iothread' : %u"), iothreadid); return -1; } if (!(tmp = virXMLPropString(node, "cpuset"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing cpuset for iothreadpin")); return -1; } if (virBitmapParse(tmp, &cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0) return -1; if (virBitmapIsAllClear(cpumask)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid value of 'cpuset': %s"), tmp); return -1; } if (iothrid->cpumask) { virReportError(VIR_ERR_INTERNAL_ERROR, _("duplicate iothreadpin for same iothread '%u'"), iothreadid); return -1; } iothrid->cpumask = g_steal_pointer(&cpumask); return 0; } /* Parse the XML definition for emulatorpin. * emulatorpin has the form of * */ static virBitmapPtr virDomainEmulatorPinDefParseXML(xmlNodePtr node) { g_autofree char *tmp = NULL; g_autoptr(virBitmap) def = NULL; if (!(tmp = virXMLPropString(node, "cpuset"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing cpuset for emulatorpin")); return NULL; } if (virBitmapParse(tmp, &def, VIR_DOMAIN_CPUMASK_LEN) < 0) return NULL; if (virBitmapIsAllClear(def)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid value of 'cpuset': %s"), tmp); return NULL; } return g_steal_pointer(&def); } virDomainControllerDefPtr virDomainDefAddController(virDomainDefPtr def, int type, int idx, int model) { virDomainControllerDefPtr cont; if (!(cont = virDomainControllerDefNew(type))) return NULL; if (idx < 0) idx = virDomainControllerFindUnusedIndex(def, type); cont->idx = idx; cont->model = model; if (VIR_APPEND_ELEMENT_COPY(def->controllers, def->ncontrollers, cont) < 0) { VIR_FREE(cont); return NULL; } return cont; } /** * virDomainDefAddUSBController: * @def: the domain * @idx: index for new controller (or -1 for "lowest unused index") * @model: VIR_DOMAIN_CONTROLLER_MODEL_USB_* or -1 * * Add a USB controller of the specified model (or default model for * current machinetype if model == -1). If model is ich9-usb-ehci, * also add companion uhci1, uhci2, and uhci3 controllers at the same * index. * * Returns 0 on success, -1 on failure. */ int virDomainDefAddUSBController(virDomainDefPtr def, int idx, int model) { virDomainControllerDefPtr cont; /* this is a *copy* of the DefPtr */ cont = virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_USB, idx, model); if (!cont) return -1; if (model != VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1) return 0; /* When the initial controller is ich9-usb-ehci, also add the * companion controllers */ idx = cont->idx; /* in case original request was "-1" */ if (!(cont = virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_USB, idx, VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1))) return -1; cont->info.mastertype = VIR_DOMAIN_CONTROLLER_MASTER_USB; cont->info.master.usb.startport = 0; if (!(cont = virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_USB, idx, VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2))) return -1; cont->info.mastertype = VIR_DOMAIN_CONTROLLER_MASTER_USB; cont->info.master.usb.startport = 2; if (!(cont = virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_USB, idx, VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3))) return -1; cont->info.mastertype = VIR_DOMAIN_CONTROLLER_MASTER_USB; cont->info.master.usb.startport = 4; return 0; } int virDomainDefMaybeAddController(virDomainDefPtr def, int type, int idx, int model) { /* skip if a specific index was given and it is already * in use for that type of controller */ if (idx >= 0 && virDomainControllerFind(def, type, idx) >= 0) return 0; if (virDomainDefAddController(def, type, idx, model)) 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; VIR_XPATH_NODE_AUTORESTORE(ctxt); g_autofree char *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, &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: return ret; } static virDomainResourceDefPtr virDomainResourceDefParse(xmlNodePtr node, xmlXPathContextPtr ctxt) { VIR_XPATH_NODE_AUTORESTORE(ctxt); virDomainResourceDefPtr def = NULL; 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; } return def; error: virDomainResourceDefFree(def); return NULL; } static int virDomainDefMaybeAddHostdevSCSIcontroller(virDomainDefPtr def) { /* Look for any hostdev scsi dev */ size_t i; int maxController = -1; virDomainHostdevDefPtr hostdev; int newModel = -1; for (i = 0; i < def->nhostdevs; i++) { hostdev = def->hostdevs[i]; if (virHostdevIsSCSIDevice(hostdev) && (int)hostdev->info->addr.drive.controller > maxController) { virDomainControllerDefPtr cont; maxController = hostdev->info->addr.drive.controller; /* We may be creating a new controller because this one is full. * So let's grab the model from it and update the model we're * going to add as long as this one isn't undefined. The premise * being keeping the same controller model for all SCSI hostdevs. */ cont = virDomainDeviceFindSCSIController(def, &hostdev->info->addr.drive); if (cont && cont->model != -1) newModel = cont->model; } } if (maxController == -1) return 0; for (i = 0; i <= maxController; i++) { if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI, i, newModel) < 0) return -1; } return 0; } static int virDomainLoaderDefParseXML(xmlNodePtr node, virDomainLoaderDefPtr loader, bool fwAutoSelect) { g_autofree char *readonly_str = NULL; g_autofree char *secure_str = NULL; g_autofree char *type_str = NULL; secure_str = virXMLPropString(node, "secure"); if (!fwAutoSelect) { readonly_str = virXMLPropString(node, "readonly"); type_str = virXMLPropString(node, "type"); loader->path = (char *) xmlNodeGetContent(node); if (STREQ_NULLABLE(loader->path, "")) VIR_FREE(loader->path); } if (readonly_str && (loader->readonly = virTristateBoolTypeFromString(readonly_str)) <= 0) { virReportError(VIR_ERR_XML_DETAIL, _("unknown readonly value: %s"), readonly_str); return -1; } if (secure_str && (loader->secure = virTristateBoolTypeFromString(secure_str)) <= 0) { virReportError(VIR_ERR_XML_DETAIL, _("unknown secure value: %s"), secure_str); return -1; } if (type_str) { int type; if ((type = virDomainLoaderTypeFromString(type_str)) <= 0) { virReportError(VIR_ERR_XML_DETAIL, _("unknown type value: %s"), type_str); return -1; } loader->type = type; } return 0; } static int virDomainSchedulerParseCommonAttrs(xmlNodePtr node, virProcessSchedPolicy *policy, int *priority) { int pol = 0; g_autofree char *tmp = NULL; if (!(tmp = virXMLPropString(node, "scheduler"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing scheduler attribute")); return -1; } if ((pol = virProcessSchedPolicyTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid scheduler attribute: '%s'"), tmp); return -1; } *policy = pol; VIR_FREE(tmp); if (pol == VIR_PROC_POLICY_FIFO || pol == VIR_PROC_POLICY_RR) { if (!(tmp = virXMLPropString(node, "priority"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing scheduler priority")); return -1; } if (virStrToLong_i(tmp, NULL, 10, priority) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Invalid value for element priority")); return -1; } } return 0; } static int virDomainEmulatorSchedParse(xmlNodePtr node, virDomainDefPtr def) { g_autofree virDomainThreadSchedParamPtr sched = NULL; if (VIR_ALLOC(sched) < 0) return -1; if (virDomainSchedulerParseCommonAttrs(node, &sched->policy, &sched->priority) < 0) return -1; def->cputune.emulatorsched = g_steal_pointer(&sched); return 0; } static virBitmapPtr virDomainSchedulerParse(xmlNodePtr node, const char *name, virProcessSchedPolicy *policy, int *priority) { virBitmapPtr ret = NULL; g_autofree char *tmp = NULL; if (!(tmp = virXMLPropString(node, name))) { virReportError(VIR_ERR_XML_ERROR, _("Missing attribute '%s' in element '%sched'"), name, name); goto error; } if (virBitmapParse(tmp, &ret, VIR_DOMAIN_CPUMASK_LEN) < 0) goto error; if (virBitmapIsAllClear(ret)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("'%s' scheduler bitmap '%s' is empty"), name, tmp); goto error; } if (virDomainSchedulerParseCommonAttrs(node, policy, priority) < 0) goto error; return ret; error: virBitmapFree(ret); return NULL; } static int virDomainThreadSchedParseHelper(xmlNodePtr node, const char *name, virDomainThreadSchedParamPtr (*func)(virDomainDefPtr, unsigned int), virDomainDefPtr def) { ssize_t next = -1; virDomainThreadSchedParamPtr sched = NULL; virProcessSchedPolicy policy = 0; int priority = 0; g_autoptr(virBitmap) map = NULL; if (!(map = virDomainSchedulerParse(node, name, &policy, &priority))) return -1; while ((next = virBitmapNextSetBit(map, next)) > -1) { if (!(sched = func(def, next))) return -1; if (sched->policy != VIR_PROC_POLICY_NONE) { virReportError(VIR_ERR_XML_DETAIL, _("%ssched attributes 'vcpus' must not overlap"), name); return -1; } sched->policy = policy; sched->priority = priority; } return 0; } static int virDomainVcpuThreadSchedParse(xmlNodePtr node, virDomainDefPtr def) { return virDomainThreadSchedParseHelper(node, "vcpus", virDomainDefGetVcpuSched, def); } static virDomainThreadSchedParamPtr virDomainDefGetIOThreadSched(virDomainDefPtr def, unsigned int iothread) { virDomainIOThreadIDDefPtr iothrinfo; if (!(iothrinfo = virDomainIOThreadIDFind(def, iothread))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Cannot find 'iothread' : %u"), iothread); return NULL; } return &iothrinfo->sched; } static int virDomainIOThreadSchedParse(xmlNodePtr node, virDomainDefPtr def) { return virDomainThreadSchedParseHelper(node, "iothreads", virDomainDefGetIOThreadSched, def); } static int virDomainVcpuParse(virDomainDefPtr def, xmlXPathContextPtr ctxt, virDomainXMLOptionPtr xmlopt) { int n; xmlNodePtr vcpuNode; size_t i; unsigned int maxvcpus; unsigned int vcpus; g_autofree char *tmp = NULL; g_autofree xmlNodePtr *nodes = NULL; vcpus = maxvcpus = 1; if ((vcpuNode = virXPathNode("./vcpu[1]", ctxt))) { if ((tmp = virXMLNodeContentString(vcpuNode))) { if (virStrToLong_ui(tmp, NULL, 10, &maxvcpus) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("maximum vcpus count must be an integer")); return -1; } VIR_FREE(tmp); } if ((tmp = virXMLPropString(vcpuNode, "current"))) { if (virStrToLong_ui(tmp, NULL, 10, &vcpus) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("current vcpus count must be an integer")); return -1; } VIR_FREE(tmp); } else { vcpus = maxvcpus; } tmp = virXMLPropString(vcpuNode, "placement"); if (tmp) { if ((def->placement_mode = virDomainCpuPlacementModeTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unsupported CPU placement mode '%s'"), tmp); return -1; } VIR_FREE(tmp); } else { def->placement_mode = VIR_DOMAIN_CPU_PLACEMENT_MODE_STATIC; } if (def->placement_mode != VIR_DOMAIN_CPU_PLACEMENT_MODE_AUTO) { tmp = virXMLPropString(vcpuNode, "cpuset"); if (tmp) { if (virBitmapParse(tmp, &def->cpumask, VIR_DOMAIN_CPUMASK_LEN) < 0) return -1; if (virBitmapIsAllClear(def->cpumask)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Invalid value of 'cpuset': %s"), tmp); return -1; } VIR_FREE(tmp); } } } if (virDomainDefSetVcpusMax(def, maxvcpus, xmlopt) < 0) return -1; if ((n = virXPathNodeSet("./vcpus/vcpu", ctxt, &nodes)) < 0) return -1; if (n) { /* if individual vcpu states are provided take them as master */ def->individualvcpus = true; for (i = 0; i < n; i++) { virDomainVcpuDefPtr vcpu; int state; unsigned int id; unsigned int order; if (!(tmp = virXMLPropString(nodes[i], "id")) || virStrToLong_uip(tmp, NULL, 10, &id) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing or invalid vcpu id")); return -1; } VIR_FREE(tmp); if (id >= def->maxvcpus) { virReportError(VIR_ERR_XML_ERROR, _("vcpu id '%u' is out of range of maximum " "vcpu count"), id); return -1; } vcpu = virDomainDefGetVcpu(def, id); if (!(tmp = virXMLPropString(nodes[i], "enabled"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing vcpu enabled state")); return -1; } if ((state = virTristateBoolTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid vcpu 'enabled' value '%s'"), tmp); return -1; } VIR_FREE(tmp); vcpu->online = state == VIR_TRISTATE_BOOL_YES; if ((tmp = virXMLPropString(nodes[i], "hotpluggable"))) { int hotpluggable; if ((hotpluggable = virTristateBoolTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("invalid vcpu 'hotpluggable' value '%s'"), tmp); return -1; } vcpu->hotpluggable = hotpluggable; VIR_FREE(tmp); } if ((tmp = virXMLPropString(nodes[i], "order"))) { if (virStrToLong_uip(tmp, NULL, 10, &order) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("invalid vcpu order")); return -1; } vcpu->order = order; VIR_FREE(tmp); } } } else { if (virDomainDefSetVcpus(def, vcpus) < 0) return -1; } return 0; } static int virDomainDefParseBootOptions(virDomainDefPtr def, xmlXPathContextPtr ctxt) { char *name = NULL; size_t i; int n; g_autofree xmlNodePtr *nodes = NULL; g_autofree char *tmp = NULL; /* * 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 (def->os.type == VIR_DOMAIN_OSTYPE_EXE) { def->os.init = virXPathString("string(./os/init[1])", ctxt); def->os.cmdline = virXPathString("string(./os/cmdline[1])", ctxt); def->os.initdir = virXPathString("string(./os/initdir[1])", ctxt); def->os.inituser = virXPathString("string(./os/inituser[1])", ctxt); def->os.initgroup = virXPathString("string(./os/initgroup[1])", ctxt); if ((n = virXPathNodeSet("./os/initarg", ctxt, &nodes)) < 0) return -1; if (VIR_ALLOC_N(def->os.initargv, n+1) < 0) return -1; 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")); return -1; } def->os.initargv[i] = g_strdup((const char *)nodes[i]->children->content); } def->os.initargv[n] = NULL; VIR_FREE(nodes); if ((n = virXPathNodeSet("./os/initenv", ctxt, &nodes)) < 0) return -1; if (VIR_ALLOC_N(def->os.initenv, n+1) < 0) return -1; for (i = 0; i < n; i++) { if (!(name = virXMLPropString(nodes[i], "name"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("No name supplied for element")); return -1; } if (!nodes[i]->children || !nodes[i]->children->content) { virReportError(VIR_ERR_XML_ERROR, _("No value supplied for element"), name); return -1; } if (VIR_ALLOC(def->os.initenv[i]) < 0) return -1; def->os.initenv[i]->name = name; def->os.initenv[i]->value = g_strdup((const char *)nodes[i]->children->content); } def->os.initenv[n] = NULL; VIR_FREE(nodes); } if (def->os.type == VIR_DOMAIN_OSTYPE_XEN || def->os.type == VIR_DOMAIN_OSTYPE_XENPVH || def->os.type == VIR_DOMAIN_OSTYPE_HVM || def->os.type == VIR_DOMAIN_OSTYPE_UML) { g_autofree char *firmware = NULL; 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 (def->os.type == VIR_DOMAIN_OSTYPE_HVM && (firmware = virXPathString("string(./os/@firmware)", ctxt))) { int fw = virDomainOsDefFirmwareTypeFromString(firmware); if (fw <= 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown firmware value %s"), firmware); return -1; } def->os.firmware = fw; } if ((loader_node = virXPathNode("./os/loader[1]", ctxt))) { const bool fwAutoSelect = def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_NONE; if (VIR_ALLOC(def->os.loader) < 0) return -1; if (virDomainLoaderDefParseXML(loader_node, def->os.loader, fwAutoSelect) < 0) return -1; def->os.loader->nvram = virXPathString("string(./os/nvram[1])", ctxt); if (!fwAutoSelect) def->os.loader->templt = virXPathString("string(./os/nvram[1]/@template)", ctxt); } } if (def->os.type == VIR_DOMAIN_OSTYPE_HVM) { if ((n = virXPathNodeSet("./os/acpi/table", ctxt, &nodes)) < 0) return -1; if (n > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Only one acpi table is supported")); return -1; } if (n == 1) { tmp = virXMLPropString(nodes[0], "type"); if (!tmp) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing acpi table type")); return -1; } if (STREQ_NULLABLE(tmp, "slic")) { VIR_FREE(tmp); tmp = virXMLNodeContentString(nodes[0]); def->os.slic_table = virFileSanitizePath(tmp); } else { virReportError(VIR_ERR_XML_ERROR, _("Unknown acpi table type: %s"), tmp); return -1; } } if (virDomainDefParseBootXML(ctxt, def) < 0) return -1; } return 0; } static int virDomainResctrlParseVcpus(virDomainDefPtr def, xmlNodePtr node, virBitmapPtr *vcpus) { g_autofree char *vcpus_str = NULL; vcpus_str = virXMLPropString(node, "vcpus"); if (!vcpus_str) { virReportError(VIR_ERR_XML_ERROR, _("Missing %s attribute 'vcpus'"), node->name); return -1; } if (virBitmapParse(vcpus_str, vcpus, VIR_DOMAIN_CPUMASK_LEN) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid %s attribute 'vcpus' value '%s'"), node->name, vcpus_str); return -1; } /* We need to limit the bitmap to number of vCPUs. If there's nothing left, * then we can just clean up and return 0 immediately */ virBitmapShrink(*vcpus, def->maxvcpus); return 0; } static int virDomainResctrlVcpuMatch(virDomainDefPtr def, virBitmapPtr vcpus, virDomainResctrlDefPtr *resctrl) { ssize_t i = 0; for (i = 0; i < def->nresctrls; i++) { /* vcpus group has been created, directly use the existing one. * Just updating memory allocation information of that group */ if (virBitmapEqual(def->resctrls[i]->vcpus, vcpus)) { *resctrl = def->resctrls[i]; break; } if (virBitmapOverlaps(def->resctrls[i]->vcpus, vcpus)) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Overlapping vcpus in resctrls")); return -1; } } return 0; } static int virDomainCachetuneDefParseCache(xmlXPathContextPtr ctxt, xmlNodePtr node, virResctrlAllocPtr alloc) { VIR_XPATH_NODE_AUTORESTORE(ctxt); unsigned int level; unsigned int cache; int type; unsigned long long size; int ret = -1; g_autofree char *tmp = NULL; ctxt->node = node; tmp = virXMLPropString(node, "id"); if (!tmp) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing cachetune attribute 'id'")); goto cleanup; } if (virStrToLong_uip(tmp, NULL, 10, &cache) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid cachetune attribute 'id' value '%s'"), tmp); goto cleanup; } VIR_FREE(tmp); tmp = virXMLPropString(node, "level"); if (!tmp) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing cachetune attribute 'level'")); goto cleanup; } if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid cachetune attribute 'level' value '%s'"), tmp); goto cleanup; } VIR_FREE(tmp); tmp = virXMLPropString(node, "type"); if (!tmp) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing cachetune attribute 'type'")); goto cleanup; } type = virCacheTypeFromString(tmp); if (type < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid cachetune attribute 'type' value '%s'"), tmp); goto cleanup; } if (virDomainParseScaledValue("./@size", "./@unit", ctxt, &size, 1024, ULLONG_MAX, true) < 0) goto cleanup; if (virResctrlAllocSetCacheSize(alloc, level, type, cache, size) < 0) goto cleanup; ret = 0; cleanup: return ret; } /* Checking if the monitor's vcpus and tag is conflicted with existing * allocation and monitors. * * Returns 1 if @monitor->vcpus equals to @resctrl->vcpus, then the monitor * will share the underlying resctrl group with @resctrl->alloc. Returns -1 * if any conflict found. Returns 0 if no conflict and @monitor->vcpus is * not equal to @resctrl->vcpus. */ static int virDomainResctrlValidateMonitor(virDomainResctrlDefPtr resctrl, virDomainResctrlMonDefPtr monitor) { size_t i = 0; int vcpu = -1; bool vcpus_overlap_any = false; bool vcpus_equal_to_resctrl = false; bool vcpus_overlap_no_resctrl = false; bool default_alloc_monitor = virResctrlAllocIsEmpty(resctrl->alloc); if (virBitmapIsAllClear(monitor->vcpus)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("vcpus is empty")); return -1; } while ((vcpu = virBitmapNextSetBit(monitor->vcpus, vcpu)) >= 0) { if (!virBitmapIsBitSet(resctrl->vcpus, vcpu)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Monitor vcpus conflicts with allocation")); return -1; } } vcpus_equal_to_resctrl = virBitmapEqual(monitor->vcpus, resctrl->vcpus); for (i = 0; i < resctrl->nmonitors; i++) { if (virBitmapEqual(monitor->vcpus, resctrl->monitors[i]->vcpus)) { if (monitor->tag != resctrl->monitors[i]->tag) { continue; } else { virReportError(VIR_ERR_INVALID_ARG, "%s", _("Identical vcpus found in same type monitors")); return -1; } } if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus)) vcpus_overlap_any = true; if (vcpus_equal_to_resctrl || virBitmapEqual(resctrl->monitors[i]->vcpus, resctrl->vcpus)) continue; if (virBitmapOverlaps(monitor->vcpus, resctrl->monitors[i]->vcpus)) vcpus_overlap_no_resctrl = true; } if (vcpus_overlap_no_resctrl || (default_alloc_monitor && vcpus_overlap_any)) { virReportError(VIR_ERR_INVALID_ARG, "%s", _("vcpus overlaps in resctrl groups")); return -1; } if (vcpus_equal_to_resctrl && !default_alloc_monitor) return 1; return 0; } #define VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL 3 static int virDomainResctrlMonDefParse(virDomainDefPtr def, xmlXPathContextPtr ctxt, xmlNodePtr node, virResctrlMonitorType tag, virDomainResctrlDefPtr resctrl) { virDomainResctrlMonDefPtr domresmon = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); unsigned int level = 0; size_t i = 0; int n = 0; int rv = -1; int ret = -1; g_autofree xmlNodePtr *nodes = NULL; g_autofree char *tmp = NULL; g_autofree char *id = NULL; ctxt->node = node; if ((n = virXPathNodeSet("./monitor", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot extract monitor nodes")); goto cleanup; } for (i = 0; i < n; i++) { if (VIR_ALLOC(domresmon) < 0) goto cleanup; domresmon->tag = tag; domresmon->instance = virResctrlMonitorNew(); if (!domresmon->instance) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Could not create monitor")); goto cleanup; } if (tag == VIR_RESCTRL_MONITOR_TYPE_CACHE) { tmp = virXMLPropString(nodes[i], "level"); if (!tmp) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing monitor attribute 'level'")); goto cleanup; } if (virStrToLong_uip(tmp, NULL, 10, &level) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid monitor attribute 'level' value '%s'"), tmp); goto cleanup; } if (level != VIR_DOMAIN_RESCTRL_MONITOR_CACHELEVEL) { virReportError(VIR_ERR_XML_ERROR, _("Invalid monitor cache level '%d'"), level); goto cleanup; } VIR_FREE(tmp); } if (virDomainResctrlParseVcpus(def, nodes[i], &domresmon->vcpus) < 0) goto cleanup; rv = virDomainResctrlValidateMonitor(resctrl, domresmon); if (rv < 0) goto cleanup; /* If monitor's vcpu list is identical to the vcpu list of the * associated allocation, set monitor's id to the same value * as the allocation. */ if (rv == 1) { const char *alloc_id = virResctrlAllocGetID(resctrl->alloc); id = g_strdup(alloc_id); } else { if (!(tmp = virBitmapFormat(domresmon->vcpus))) goto cleanup; if (virAsprintf(&id, "vcpus_%s", tmp) < 0) goto cleanup; } virResctrlMonitorSetAlloc(domresmon->instance, resctrl->alloc); if (virResctrlMonitorSetID(domresmon->instance, id) < 0) goto cleanup; if (VIR_APPEND_ELEMENT(resctrl->monitors, resctrl->nmonitors, domresmon) < 0) goto cleanup; VIR_FREE(id); VIR_FREE(tmp); } ret = 0; cleanup: virDomainResctrlMonDefFree(domresmon); return ret; } static virDomainResctrlDefPtr virDomainResctrlNew(xmlNodePtr node, virResctrlAllocPtr alloc, virBitmapPtr vcpus, unsigned int flags) { virDomainResctrlDefPtr resctrl = NULL; virDomainResctrlDefPtr ret = NULL; g_autofree char *vcpus_str = NULL; g_autofree char *alloc_id = NULL; /* We need to format it back because we need to be consistent in the naming * even when users specify some "sub-optimal" string there. */ vcpus_str = virBitmapFormat(vcpus); if (!vcpus_str) return NULL; if (!(flags & VIR_DOMAIN_DEF_PARSE_INACTIVE)) alloc_id = virXMLPropString(node, "id"); if (!alloc_id) { /* The number of allocations is limited and the directory structure is flat, * not hierarchical, so we need to have all same allocations in one * directory, so it's nice to have it named appropriately. For now it's * 'vcpus_...' but it's designed in order for it to be changeable in the * future (it's part of the status XML). */ if (virAsprintf(&alloc_id, "vcpus_%s", vcpus_str) < 0) goto cleanup; } if (virResctrlAllocSetID(alloc, alloc_id) < 0) goto cleanup; if (VIR_ALLOC(resctrl) < 0) goto cleanup; if (!(resctrl->vcpus = virBitmapNewCopy(vcpus))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("failed to copy 'vcpus'")); goto cleanup; } resctrl->alloc = virObjectRef(alloc); ret = g_steal_pointer(&resctrl); cleanup: virDomainResctrlDefFree(resctrl); return ret; } static int virDomainCachetuneDefParse(virDomainDefPtr def, xmlXPathContextPtr ctxt, xmlNodePtr node, unsigned int flags) { VIR_XPATH_NODE_AUTORESTORE(ctxt); virDomainResctrlDefPtr resctrl = NULL; ssize_t i = 0; int n; int ret = -1; g_autoptr(virBitmap) vcpus = NULL; g_autofree xmlNodePtr *nodes = NULL; g_autoptr(virResctrlAlloc) alloc = NULL; ctxt->node = node; if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0) return -1; if (virBitmapIsAllClear(vcpus)) return 0; if ((n = virXPathNodeSet("./cache", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot extract cache nodes under cachetune")); return -1; } if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0) return -1; if (resctrl) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Identical vcpus in cachetunes found")); return -1; } if (!(alloc = virResctrlAllocNew())) return -1; for (i = 0; i < n; i++) { if (virDomainCachetuneDefParseCache(ctxt, nodes[i], alloc) < 0) return -1; } if (!(resctrl = virDomainResctrlNew(node, alloc, vcpus, flags))) return -1; if (virDomainResctrlMonDefParse(def, ctxt, node, VIR_RESCTRL_MONITOR_TYPE_CACHE, resctrl) < 0) goto cleanup; /* If no element or element in , do not * append any resctrl element */ if (!resctrl->nmonitors && n == 0) { ret = 0; goto cleanup; } if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, resctrl) < 0) goto cleanup; ret = 0; cleanup: virDomainResctrlDefFree(resctrl); return ret; } static int virDomainDefParseCaps(virDomainDefPtr def, xmlXPathContextPtr ctxt, virCapsPtr caps, unsigned int flags) { g_autofree char *virttype = NULL; g_autofree char *arch = NULL; g_autofree char *ostype = NULL; g_autofree virCapsDomainDataPtr capsdata = NULL; virttype = virXPathString("string(./@type)", ctxt); ostype = virXPathString("string(./os/type[1])", ctxt); arch = virXPathString("string(./os/type[1]/@arch)", ctxt); def->os.bootloader = virXPathString("string(./bootloader)", ctxt); def->os.bootloaderArgs = virXPathString("string(./bootloader_args)", ctxt); def->os.machine = virXPathString("string(./os/type[1]/@machine)", ctxt); def->emulator = virXPathString("string(./devices/emulator[1])", ctxt); if (!virttype) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing domain type attribute")); return -1; } if ((def->virtType = virDomainVirtTypeFromString(virttype)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid domain type %s"), virttype); return -1; } if (!ostype) { if (def->os.bootloader) { def->os.type = VIR_DOMAIN_OSTYPE_XEN; } else { virReportError(VIR_ERR_XML_ERROR, "%s", _("an os must be specified")); return -1; } } else { if ((def->os.type = virDomainOSTypeFromString(ostype)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown OS type '%s'"), ostype); return -1; } } /* * 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 (def->os.type == VIR_DOMAIN_OSTYPE_LINUX && def->virtType == VIR_DOMAIN_VIRT_XEN) { def->os.type = VIR_DOMAIN_OSTYPE_XEN; } if (arch && !(def->os.arch = virArchFromString(arch))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown architecture %s"), arch); return -1; } if (!(capsdata = virCapabilitiesDomainDataLookup(caps, def->os.type, def->os.arch, def->virtType, NULL, NULL))) { if (!(flags & VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE)) return -1; virResetLastError(); } else { if (!def->os.arch) def->os.arch = capsdata->arch; if (!def->os.machine) def->os.machine = g_strdup(capsdata->machinetype); } return 0; } static int virDomainMemorytuneDefParseMemory(xmlXPathContextPtr ctxt, xmlNodePtr node, virResctrlAllocPtr alloc) { VIR_XPATH_NODE_AUTORESTORE(ctxt); unsigned int id; unsigned int bandwidth; int ret = -1; g_autofree char *tmp = NULL; ctxt->node = node; tmp = virXMLPropString(node, "id"); if (!tmp) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing memorytune attribute 'id'")); goto cleanup; } if (virStrToLong_uip(tmp, NULL, 10, &id) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid memorytune attribute 'id' value '%s'"), tmp); goto cleanup; } VIR_FREE(tmp); tmp = virXMLPropString(node, "bandwidth"); if (!tmp) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing memorytune attribute 'bandwidth'")); goto cleanup; } if (virStrToLong_uip(tmp, NULL, 10, &bandwidth) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Invalid memorytune attribute 'bandwidth' value '%s'"), tmp); goto cleanup; } if (virResctrlAllocSetMemoryBandwidth(alloc, id, bandwidth) < 0) goto cleanup; ret = 0; cleanup: return ret; } static int virDomainMemorytuneDefParse(virDomainDefPtr def, xmlXPathContextPtr ctxt, xmlNodePtr node, unsigned int flags) { VIR_XPATH_NODE_AUTORESTORE(ctxt); virDomainResctrlDefPtr resctrl = NULL; g_autoptr(virBitmap) vcpus = NULL; g_autofree xmlNodePtr *nodes = NULL; g_autoptr(virResctrlAlloc) alloc = NULL; ssize_t i = 0; int n; ctxt->node = node; if (virDomainResctrlParseVcpus(def, node, &vcpus) < 0) return -1; if (virBitmapIsAllClear(vcpus)) return 0; if ((n = virXPathNodeSet("./node", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Cannot extract memory nodes under memorytune")); return -1; } if (virDomainResctrlVcpuMatch(def, vcpus, &resctrl) < 0) return -1; if (resctrl) { alloc = virObjectRef(resctrl->alloc); } else { if (!(alloc = virResctrlAllocNew())) return -1; } for (i = 0; i < n; i++) { if (virDomainMemorytuneDefParseMemory(ctxt, nodes[i], alloc) < 0) return -1; } if (n == 0) return 0; /* * If this is a new allocation, format ID and append to resctrl, otherwise * just update the existing alloc information, which is done in above * virDomainMemorytuneDefParseMemory */ if (!resctrl) { if (!(resctrl = virDomainResctrlNew(node, alloc, vcpus, flags))) return -1; if (VIR_APPEND_ELEMENT(def->resctrls, def->nresctrls, resctrl) < 0) { virDomainResctrlDefFree(resctrl); return -1; } } return 0; } static virDomainDefPtr virDomainDefParseXML(xmlDocPtr xml, xmlXPathContextPtr ctxt, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, unsigned int flags) { xmlNodePtr node = NULL; size_t i, j; int n, gic_version; long id = -1; virDomainDefPtr def; bool uuid_generated = false; bool usb_none = false; bool usb_other = false; bool usb_master = false; char *netprefix = NULL; g_autofree xmlNodePtr *nodes = NULL; g_autofree char *tmp = NULL; if (flags & VIR_DOMAIN_DEF_PARSE_VALIDATE_SCHEMA) { g_autofree char *schema = NULL; schema = virFileFindResource("domain.rng", abs_top_srcdir "/docs/schemas", PKGDATADIR "/schemas"); if (!schema) return NULL; if (virXMLValidateAgainstSchema(schema, xml) < 0) return NULL; } 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; if (virDomainDefParseCaps(def, ctxt, caps, flags) < 0) 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) < 0) { 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 domain genid - a genid can either be provided or generated */ if ((n = virXPathNodeSet("./genid", ctxt, &nodes)) < 0) goto error; if (n > 0) { if (n != 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("element 'genid' can only appear once")); goto error; } def->genidRequested = true; if (!(tmp = virXPathString("string(./genid)", ctxt))) { if (virUUIDGenerate(def->genid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Failed to generate genid")); goto error; } def->genidGenerated = true; } else { if (virUUIDParse(tmp, def->genid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed genid element")); goto error; } VIR_FREE(tmp); } } VIR_FREE(nodes); /* 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 (!(flags & VIR_DOMAIN_DEF_PARSE_SKIP_SECLABEL)) { if (virSecurityLabelDefsParseXML(def, ctxt, caps, flags) == -1) goto error; } /* Extract domain memory */ if (virDomainParseMemory("./memory[1]", NULL, ctxt, &def->mem.total_memory, 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); tmp = virXPathString("string(./memoryBacking/source/@type)", ctxt); if (tmp) { if ((def->mem.source = virDomainMemorySourceTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown memoryBacking/source/type '%s'"), tmp); goto error; } VIR_FREE(tmp); } tmp = virXPathString("string(./memoryBacking/access/@mode)", ctxt); if (tmp) { if ((def->mem.access = virDomainMemoryAccessTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown memoryBacking/access/mode '%s'"), tmp); goto error; } VIR_FREE(tmp); } tmp = virXPathString("string(./memoryBacking/allocation/@mode)", ctxt); if (tmp) { if ((def->mem.allocation = virDomainMemoryAllocationTypeFromString(tmp)) <= 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown memoryBacking/allocation/mode '%s'"), tmp); goto error; } VIR_FREE(tmp); } if (virXPathNode("./memoryBacking/hugepages", ctxt)) { /* hugepages will be used */ 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++; } VIR_FREE(nodes); } else { /* no hugepage pages */ 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; if (virXPathBoolean("boolean(./memoryBacking/discard)", ctxt)) def->mem.discard = VIR_TRISTATE_BOOL_YES; /* 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; if (virDomainVcpuParse(def, ctxt, xmlopt) < 0) goto error; if (virDomainDefParseIOThreads(def, ctxt) < 0) goto error; /* Extract cpu tunables. */ if ((n = virXPathULongLong("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 (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 (virXPathULongLong("string(./cputune/global_period[1])", ctxt, &def->cputune.global_period) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("can't parse cputune global period value")); goto error; } if (virXPathLongLong("string(./cputune/global_quota[1])", ctxt, &def->cputune.global_quota) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("can't parse cputune global quota value")); 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 (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 (virXPathULongLong("string(./cputune/iothread_period[1])", ctxt, &def->cputune.iothread_period) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("can't parse cputune iothread period value")); goto error; } if (virXPathLongLong("string(./cputune/iothread_quota[1])", ctxt, &def->cputune.iothread_quota) < -1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("can't parse cputune iothread quota value")); goto error; } if ((n = virXPathNodeSet("./cputune/vcpupin", ctxt, &nodes)) < 0) goto error; for (i = 0; i < n; i++) { if (virDomainVcpuPinDefParseXML(def, nodes[i])) goto error; } VIR_FREE(nodes); 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; } for (i = 0; i < n; i++) { if (virDomainIOThreadPinDefParseXML(nodes[i], def) < 0) goto error; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./cputune/vcpusched", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract vcpusched nodes")); goto error; } for (i = 0; i < n; i++) { if (virDomainVcpuThreadSchedParse(nodes[i], def) < 0) 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; } for (i = 0; i < n; i++) { if (virDomainIOThreadSchedParse(nodes[i], def) < 0) goto error; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./cputune/emulatorsched", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract emulatorsched nodes")); goto error; } if (n) { if (n > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only one emulatorsched is supported")); VIR_FREE(nodes); goto error; } if (virDomainEmulatorSchedParse(nodes[0], def) < 0) goto error; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./cputune/cachetune", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract cachetune nodes")); goto error; } for (i = 0; i < n; i++) { if (virDomainCachetuneDefParse(def, ctxt, nodes[i], flags) < 0) goto error; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./cputune/memorytune", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("cannot extract memorytune nodes")); goto error; } for (i = 0; i < n; i++) { if (virDomainMemorytuneDefParse(def, ctxt, nodes[i], flags) < 0) goto error; } VIR_FREE(nodes); if (virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &def->cpu) < 0) goto error; if (virDomainNumaDefCPUParseXML(def->numa, ctxt) < 0) goto error; if (virDomainNumaGetCPUCountTotal(def->numa) > virDomainDefGetVcpusMax(def)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Number of CPUs in exceeds the" " count")); goto error; } if (virDomainNumaGetMaxCPUID(def->numa) >= virDomainDefGetVcpusMax(def)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("CPU IDs in exceed 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 && !virDomainDefHasVcpuPin(def) && !def->cputune.emulatorpin && !virDomainIOThreadIDArrayHasPin(def)) 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); } G_GNUC_FALLTHROUGH; case VIR_DOMAIN_FEATURE_ACPI: case VIR_DOMAIN_FEATURE_PAE: case VIR_DOMAIN_FEATURE_VIRIDIAN: case VIR_DOMAIN_FEATURE_PRIVNET: case VIR_DOMAIN_FEATURE_HYPERV: case VIR_DOMAIN_FEATURE_KVM: case VIR_DOMAIN_FEATURE_MSRS: def->features[val] = VIR_TRISTATE_SWITCH_ON; break; case VIR_DOMAIN_FEATURE_CAPABILITIES: if ((tmp = virXMLPropString(nodes[i], "policy"))) { if ((def->features[val] = virDomainCapabilitiesPolicyTypeFromString(tmp)) == -1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown policy attribute '%s' of feature '%s'"), tmp, virDomainFeatureTypeToString(val)); goto error; } VIR_FREE(tmp); } else { def->features[val] = VIR_TRISTATE_SWITCH_ABSENT; } break; case VIR_DOMAIN_FEATURE_VMCOREINFO: case VIR_DOMAIN_FEATURE_HAP: case VIR_DOMAIN_FEATURE_PMU: case VIR_DOMAIN_FEATURE_PVSPINLOCK: case VIR_DOMAIN_FEATURE_VMPORT: case VIR_DOMAIN_FEATURE_SMM: if ((tmp = virXMLPropString(nodes[i], "state"))) { 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; } break; case VIR_DOMAIN_FEATURE_GIC: if ((tmp = virXMLPropString(nodes[i], "version"))) { gic_version = virGICVersionTypeFromString(tmp); if (gic_version < 0 || gic_version == VIR_GIC_VERSION_NONE) { virReportError(VIR_ERR_XML_ERROR, _("malformed gic version: %s"), tmp); goto error; } def->gic_version = gic_version; VIR_FREE(tmp); } def->features[val] = VIR_TRISTATE_SWITCH_ON; break; case VIR_DOMAIN_FEATURE_IOAPIC: tmp = virXMLPropString(nodes[i], "driver"); if (tmp) { int value = virDomainIOAPICTypeFromString(tmp); if (value < 0 || value == VIR_DOMAIN_IOAPIC_NONE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown driver mode: %s"), tmp); goto error; } def->features[val] = value; VIR_FREE(tmp); } break; case VIR_DOMAIN_FEATURE_HPT: tmp = virXMLPropString(nodes[i], "resizing"); if (tmp) { int value = virDomainHPTResizingTypeFromString(tmp); if (value < 0 || value == VIR_DOMAIN_HPT_RESIZING_NONE) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown HPT resizing setting: %s"), tmp); goto error; } def->hpt_resizing = (virDomainHPTResizing) value; VIR_FREE(tmp); } if (virDomainParseScaledValue("./features/hpt/maxpagesize", NULL, ctxt, &def->hpt_maxpagesize, 1024, ULLONG_MAX, false) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Unable to parse HPT maxpagesize setting")); goto error; } def->hpt_maxpagesize = VIR_DIV_UP(def->hpt_maxpagesize, 1024); if (def->hpt_resizing != VIR_DOMAIN_HPT_RESIZING_NONE || def->hpt_maxpagesize > 0) { def->features[val] = VIR_TRISTATE_SWITCH_ON; } break; case VIR_DOMAIN_FEATURE_HTM: case VIR_DOMAIN_FEATURE_NESTED_HV: case VIR_DOMAIN_FEATURE_CCF_ASSIST: if (!(tmp = virXMLPropString(nodes[i], "state"))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("missing state attribute '%s' of feature '%s'"), tmp, virDomainFeatureTypeToString(val)); goto error; } if ((def->features[val] = virTristateSwitchTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown state attribute '%s' of feature '%s'"), tmp, virDomainFeatureTypeToString(val)); goto error; } VIR_FREE(tmp); 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]; if (!(tmp = virXMLPropString(nodes[i], "state"))) { 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; switch ((virDomainHyperv) feature) { case VIR_DOMAIN_HYPERV_RELAXED: case VIR_DOMAIN_HYPERV_VAPIC: case VIR_DOMAIN_HYPERV_VPINDEX: case VIR_DOMAIN_HYPERV_RUNTIME: case VIR_DOMAIN_HYPERV_SYNIC: case VIR_DOMAIN_HYPERV_STIMER: case VIR_DOMAIN_HYPERV_RESET: case VIR_DOMAIN_HYPERV_FREQUENCIES: case VIR_DOMAIN_HYPERV_REENLIGHTENMENT: case VIR_DOMAIN_HYPERV_TLBFLUSH: case VIR_DOMAIN_HYPERV_IPI: case VIR_DOMAIN_HYPERV_EVMCS: break; case VIR_DOMAIN_HYPERV_SPINLOCKS: if (value != VIR_TRISTATE_SWITCH_ON) break; 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; } break; case VIR_DOMAIN_HYPERV_VENDOR_ID: if (value != VIR_TRISTATE_SWITCH_ON) break; if (!(def->hyperv_vendor_id = virXMLPropString(nodes[i], "value"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing 'value' attribute for " "HyperV feature 'vendor_id'")); goto error; } if (strlen(def->hyperv_vendor_id) > VIR_DOMAIN_HYPERV_VENDOR_ID_MAX) { virReportError(VIR_ERR_XML_ERROR, _("HyperV vendor_id value must not be more " "than %d characters."), VIR_DOMAIN_HYPERV_VENDOR_ID_MAX); goto error; } /* ensure that the string can be passed to qemu */ if (strchr(def->hyperv_vendor_id, ',')) { virReportError(VIR_ERR_XML_ERROR, "%s", _("HyperV vendor_id value is invalid")); goto error; } /* coverity[dead_error_begin] */ case VIR_DOMAIN_HYPERV_LAST: break; } } VIR_FREE(nodes); ctxt->node = node; } if (def->features[VIR_DOMAIN_HYPERV_STIMER] == VIR_TRISTATE_SWITCH_ON) { int value; if ((n = virXPathNodeSet("./features/hyperv/stimer/*", ctxt, &nodes)) < 0) goto error; for (i = 0; i < n; i++) { if (STRNEQ((const char *)nodes[i]->name, "direct")) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unsupported Hyper-V stimer feature: %s"), nodes[i]->name); goto error; } if (!(tmp = virXMLPropString(nodes[i], "state"))) { virReportError(VIR_ERR_XML_ERROR, _("missing 'state' attribute for " "Hyper-V stimer '%s' feature"), "direct"); goto error; } if ((value = virTristateSwitchTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid value of state argument " "for Hyper-V stimer '%s' feature"), "direct"); goto error; } VIR_FREE(tmp); def->hyperv_stimer_direct = value; } VIR_FREE(nodes); } if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) { int feature; int value; 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; } switch ((virDomainKVM) feature) { case VIR_DOMAIN_KVM_HIDDEN: case VIR_DOMAIN_KVM_DEDICATED: if (!(tmp = virXMLPropString(nodes[i], "state"))) { 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); } if (def->features[VIR_DOMAIN_FEATURE_SMM] == VIR_TRISTATE_SWITCH_ON) { int rv = virDomainParseScaledValue("string(./features/smm/tseg)", "string(./features/smm/tseg/@unit)", ctxt, &def->tseg_size, 1024 * 1024, /* Defaults to mebibytes */ ULLONG_MAX, false); if (rv < 0) goto error; def->tseg_specified = rv; } if (def->features[VIR_DOMAIN_FEATURE_MSRS] == VIR_TRISTATE_SWITCH_ON) { if ((node = virXPathNode("./features/msrs", ctxt)) == NULL) goto error; if (!(tmp = virXMLPropString(node, "unknown"))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("missing 'unknown' attribute for feature '%s'"), virDomainFeatureTypeToString(VIR_DOMAIN_FEATURE_MSRS)); goto error; } if ((def->msrs_features[VIR_DOMAIN_MSRS_UNKNOWN] = virDomainMsrsUnknownTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown 'unknown' value '%s'"), tmp); goto error; } VIR_FREE(tmp); } 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 ((tmp = virXMLPropString(nodes[i], "state"))) { if ((def->caps_features[val] = virTristateSwitchTypeFromString(tmp)) == -1) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown state attribute '%s' of feature capability '%s'"), tmp, virDomainCapsFeatureTypeToString(val)); goto error; } VIR_FREE(tmp); } else { def->caps_features[val] = VIR_TRISTATE_SWITCH_ON; } } VIR_FREE(nodes); if (virDomainEventActionParseXML(ctxt, "on_reboot", "string(./on_reboot[1])", &def->onReboot, VIR_DOMAIN_LIFECYCLE_ACTION_RESTART, virDomainLifecycleActionTypeFromString) < 0) goto error; if (virDomainEventActionParseXML(ctxt, "on_poweroff", "string(./on_poweroff[1])", &def->onPoweroff, VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY, virDomainLifecycleActionTypeFromString) < 0) goto error; if (virDomainEventActionParseXML(ctxt, "on_crash", "string(./on_crash[1])", &def->onCrash, VIR_DOMAIN_LIFECYCLE_ACTION_DESTROY, virDomainLifecycleActionTypeFromString) < 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 (virDomainPerfDefParseXML(def, ctxt) < 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); if (virDomainDefParseBootOptions(def, ctxt) < 0) goto error; /* 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, 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(xmlopt, 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(xmlopt, 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; netprefix = caps->host.netprefix; for (i = 0; i < n; i++) { virDomainNetDefPtr net = virDomainNetDefParseXML(xmlopt, nodes[i], ctxt, netprefix, 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(xmlopt, nodes[i], ctxt, 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(xmlopt, 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(xmlopt, 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(xmlopt, 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(xmlopt, 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(xmlopt, def, nodes[i], ctxt, 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; } 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(xmlopt, nodes[i], ctxt, flags); if (!graphics) goto error; def->graphics[def->ngraphics++] = graphics; } VIR_FREE(nodes); /* 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(xmlopt, 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++) { virDomainVideoDefPtr video; ssize_t insertAt = -1; if (!(video = virDomainVideoDefParseXML(xmlopt, nodes[i], ctxt, def, flags))) goto error; if (video->primary) { if (def->nvideos != 0 && def->videos[0]->primary) { virDomainVideoDefFree(video); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Only one primary video device is supported")); goto error; } insertAt = 0; } if (VIR_INSERT_ELEMENT_INPLACE(def->videos, insertAt, def->nvideos, video) < 0) { virDomainVideoDefFree(video); goto error; } } VIR_FREE(nodes); /* analysis of the host devices */ if ((n = virXPathNodeSet("./devices/hostdev", ctxt, &nodes)) < 0) goto error; if (n && VIR_REALLOC_N(def->hostdevs, def->nhostdevs + n) < 0) goto error; for (i = 0; i < n; i++) { virDomainHostdevDefPtr hostdev; hostdev = virDomainHostdevDefParseXML(xmlopt, nodes[i], ctxt, flags); if (!hostdev) goto error; if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB && usb_none) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Can't add host USB device: " "USB is disabled in this host")); virDomainHostdevDefFree(hostdev); goto error; } def->hostdevs[def->nhostdevs++] = hostdev; /* For a domain definition, we need to check if the controller * for this hostdev exists yet and if not add it. This cannot be * done during virDomainHostdevAssignAddress (as part of device * post processing) because that will result in the failure to * load the controller during hostdev hotplug. */ if (virDomainDefMaybeAddHostdevSCSIcontroller(def) < 0) goto error; } VIR_FREE(nodes); /* analysis of the watchdog devices */ def->watchdog = NULL; if ((n = virXPathNodeSet("./devices/watchdog", ctxt, &nodes)) < 0) goto error; if (n > 1) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("only a single watchdog device is supported")); goto error; } if (n > 0) { virDomainWatchdogDefPtr watchdog; watchdog = virDomainWatchdogDefParseXML(xmlopt, nodes[0], flags); if (!watchdog) goto error; def->watchdog = watchdog; VIR_FREE(nodes); } /* analysis of the memballoon devices */ def->memballoon = NULL; if ((n = virXPathNodeSet("./devices/memballoon", ctxt, &nodes)) < 0) goto error; if (n > 1) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("only a single memory balloon device is supported")); goto error; } if (n > 0) { virDomainMemballoonDefPtr memballoon; memballoon = virDomainMemballoonDefParseXML(xmlopt, nodes[0], ctxt, flags); if (!memballoon) goto error; def->memballoon = memballoon; VIR_FREE(nodes); } /* Parse the RNG devices */ if ((n = virXPathNodeSet("./devices/rng", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->rngs, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainRNGDefPtr rng = virDomainRNGDefParseXML(xmlopt, nodes[i], ctxt, flags); if (!rng) goto error; def->rngs[def->nrngs++] = rng; } VIR_FREE(nodes); /* Parse the TPM devices */ if ((n = virXPathNodeSet("./devices/tpm", ctxt, &nodes)) < 0) goto error; if (n > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only a single TPM device is supported")); goto error; } if (n > 0) { if (!(def->tpm = virDomainTPMDefParseXML(xmlopt, nodes[0], ctxt, flags))) goto error; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./devices/nvram", ctxt, &nodes)) < 0) goto error; if (n > 1) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("only a single nvram device is supported")); goto error; } else if (n == 1) { virDomainNVRAMDefPtr nvram = virDomainNVRAMDefParseXML(xmlopt, nodes[0], flags); if (!nvram) goto error; def->nvram = nvram; VIR_FREE(nodes); } /* analysis of the hub devices */ if ((n = virXPathNodeSet("./devices/hub", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->hubs, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainHubDefPtr hub; hub = virDomainHubDefParseXML(xmlopt, nodes[i], flags); if (!hub) goto error; if (hub->type == VIR_DOMAIN_HUB_TYPE_USB && usb_none) { virDomainHubDefFree(hub); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Can't add USB hub: " "USB is disabled for this domain")); goto error; } def->hubs[def->nhubs++] = hub; } VIR_FREE(nodes); /* analysis of the redirected devices */ if ((n = virXPathNodeSet("./devices/redirdev", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->redirdevs, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainRedirdevDefPtr redirdev = virDomainRedirdevDefParseXML(xmlopt, nodes[i], ctxt, flags); if (!redirdev) goto error; def->redirdevs[def->nredirdevs++] = redirdev; } VIR_FREE(nodes); /* analysis of the redirection filter rules */ if ((n = virXPathNodeSet("./devices/redirfilter", ctxt, &nodes)) < 0) goto error; if (n > 1) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("only one set of redirection filter rule is supported")); goto error; } if (n) { virDomainRedirFilterDefPtr redirfilter = virDomainRedirFilterDefParseXML(nodes[0], ctxt); if (!redirfilter) goto error; def->redirfilter = redirfilter; } VIR_FREE(nodes); /* analysis of the panic devices */ if ((n = virXPathNodeSet("./devices/panic", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->panics, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainPanicDefPtr panic; panic = virDomainPanicDefParseXML(xmlopt, nodes[i], flags); if (!panic) goto error; def->panics[def->npanics++] = panic; } VIR_FREE(nodes); /* analysis of the shmem devices */ if ((n = virXPathNodeSet("./devices/shmem", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->shmems, n) < 0) goto error; node = ctxt->node; for (i = 0; i < n; i++) { virDomainShmemDefPtr shmem; ctxt->node = nodes[i]; shmem = virDomainShmemDefParseXML(xmlopt, nodes[i], ctxt, flags); if (!shmem) goto error; def->shmems[def->nshmems++] = shmem; } ctxt->node = node; VIR_FREE(nodes); /* Check for SEV feature */ if ((node = virXPathNode("./launchSecurity", ctxt)) != NULL) { def->sev = virDomainSEVDefParseXML(node, ctxt); if (!def->sev) goto error; } /* analysis of memory devices */ if ((n = virXPathNodeSet("./devices/memory", ctxt, &nodes)) < 0) goto error; if (n && VIR_ALLOC_N(def->mems, n) < 0) goto error; for (i = 0; i < n; i++) { virDomainMemoryDefPtr mem = virDomainMemoryDefParseXML(xmlopt, nodes[i], ctxt, flags); if (!mem) goto error; def->mems[def->nmems++] = mem; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./devices/iommu", ctxt, &nodes)) < 0) goto error; if (n > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only a single IOMMU device is supported")); goto error; } if (n > 0) { if (!(def->iommu = virDomainIOMMUDefParseXML(nodes[0], ctxt))) goto error; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./devices/vsock", ctxt, &nodes)) < 0) goto error; if (n > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _("only a single vsock device is supported")); goto error; } if (n > 0) { if (!(def->vsock = virDomainVsockDefParseXML(xmlopt, nodes[0], ctxt, flags))) goto error; } VIR_FREE(nodes); /* analysis of the user namespace mapping */ if ((n = virXPathNodeSet("./idmap/uid", ctxt, &nodes)) < 0) goto error; if (n) { def->idmap.uidmap = virDomainIdmapDefParseXML(ctxt, nodes, n); if (!def->idmap.uidmap) goto error; def->idmap.nuidmap = n; } VIR_FREE(nodes); if ((n = virXPathNodeSet("./idmap/gid", ctxt, &nodes)) < 0) goto error; if (n) { def->idmap.gidmap = virDomainIdmapDefParseXML(ctxt, nodes, n); if (!def->idmap.gidmap) goto error; def->idmap.ngidmap = n; } if ((def->idmap.uidmap && !def->idmap.gidmap) || (!def->idmap.uidmap && def->idmap.gidmap)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("uid and gid should be mapped both")); goto error; } if ((node = virXPathNode("./sysinfo[1]", ctxt)) != NULL) { xmlNodePtr oldnode = ctxt->node; ctxt->node = node; def->sysinfo = virSysinfoParseXML(node, ctxt, def->uuid, uuid_generated); ctxt->node = oldnode; if (def->sysinfo == NULL) goto error; } if ((tmp = virXPathString("string(./os/smbios/@mode)", ctxt))) { int mode; if ((mode = virDomainSmbiosModeTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown smbios mode '%s'"), tmp); goto error; } def->os.smbios_mode = mode; } if (virDomainKeyWrapDefParseXML(def, ctxt) < 0) goto error; /* Extract custom metadata */ if ((node = virXPathNode("./metadata[1]", ctxt)) != NULL) def->metadata = xmlCopyNode(node, 1); /* we have to make a copy of all of the callback pointers here since * we won't have the virCaps structure available during free */ def->ns = xmlopt->ns; if (def->ns.parse) { if (virXMLNamespaceRegister(ctxt, &def->ns) < 0) goto error; if ((def->ns.parse)(ctxt, &def->namespaceData) < 0) goto error; } return def; error: virDomainDefFree(def); return NULL; } static virDomainObjPtr virDomainObjParseXML(xmlDocPtr xml, xmlXPathContextPtr ctxt, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, unsigned int flags) { long val; xmlNodePtr config; xmlNodePtr oldnode; virDomainObjPtr obj; size_t i; int n; int state; int reason = 0; void *parseOpaque = NULL; g_autofree char *tmp = NULL; g_autofree xmlNodePtr *nodes = NULL; if (!(obj = virDomainObjNew(xmlopt))) return NULL; if (!(config = virXPathNode("./domain", ctxt))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("no domain config")); goto error; } oldnode = ctxt->node; ctxt->node = config; obj->def = virDomainDefParseXML(xml, ctxt, caps, xmlopt, flags); ctxt->node = oldnode; if (!obj->def) goto error; if (!(tmp = virXMLPropString(ctxt->node, "state"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing domain state")); goto error; } if ((state = virDomainStateTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid domain state '%s'"), tmp); goto error; } VIR_FREE(tmp); if ((tmp = virXMLPropString(ctxt->node, "reason"))) { if ((reason = virDomainStateReasonFromString(state, tmp)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid domain state reason '%s'"), tmp); goto error; } } virDomainObjSetState(obj, state, reason); if (virXPathLong("string(./@pid)", ctxt, &val) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid pid")); goto error; } obj->pid = (pid_t)val; if ((n = virXPathNodeSet("./taint", ctxt, &nodes)) < 0) goto error; for (i = 0; i < n; i++) { char *str = virXMLPropString(nodes[i], "flag"); if (str) { int flag = virDomainTaintTypeFromString(str); if (flag < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Unknown taint flag %s"), str); VIR_FREE(str); goto error; } VIR_FREE(str); virDomainObjTaint(obj, flag); } } if (xmlopt->privateData.parse && xmlopt->privateData.parse(ctxt, obj, &xmlopt->config) < 0) goto error; if (xmlopt->privateData.getParseOpaque) parseOpaque = xmlopt->privateData.getParseOpaque(obj); /* callback to fill driver specific domain aspects */ if (virDomainDefPostParse(obj->def, caps, flags, xmlopt, parseOpaque) < 0) goto error; /* validate configuration */ if (virDomainDefValidate(obj->def, caps, flags, xmlopt) < 0) goto error; return obj; error: virObjectUnref(obj); return NULL; } static virDomainDefPtr virDomainDefParse(const char *xmlStr, const char *filename, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, void *parseOpaque, unsigned int flags) { xmlDocPtr xml = NULL; virDomainDefPtr def = NULL; int keepBlanksDefault = xmlKeepBlanksDefault(0); xmlNodePtr root; if (!(xml = virXMLParse(filename, xmlStr, _("(domain_definition)")))) goto cleanup; root = xmlDocGetRootElement(xml); if (!virXMLNodeNameEqual(root, "domain")) { virReportError(VIR_ERR_XML_ERROR, _("unexpected root element <%s>, " "expecting "), root->name); goto cleanup; } def = virDomainDefParseNode(xml, root, caps, xmlopt, parseOpaque, flags); cleanup: xmlFreeDoc(xml); xmlKeepBlanksDefault(keepBlanksDefault); return def; } virDomainDefPtr virDomainDefParseString(const char *xmlStr, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, void *parseOpaque, unsigned int flags) { return virDomainDefParse(xmlStr, NULL, caps, xmlopt, parseOpaque, flags); } virDomainDefPtr virDomainDefParseFile(const char *filename, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, void *parseOpaque, unsigned int flags) { return virDomainDefParse(NULL, filename, caps, xmlopt, parseOpaque, flags); } virDomainDefPtr virDomainDefParseNode(xmlDocPtr xml, xmlNodePtr root, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, void *parseOpaque, unsigned int flags) { g_autoptr(xmlXPathContext) ctxt = NULL; g_autoptr(virDomainDef) def = NULL; if (!(ctxt = virXMLXPathContextNew(xml))) return NULL; ctxt->node = root; if (!(def = virDomainDefParseXML(xml, ctxt, caps, xmlopt, flags))) return NULL; /* callback to fill driver specific domain aspects */ if (virDomainDefPostParse(def, caps, flags, xmlopt, parseOpaque) < 0) return NULL; /* validate configuration */ if (virDomainDefValidate(def, caps, flags, xmlopt) < 0) return NULL; return g_steal_pointer(&def); } virDomainObjPtr virDomainObjParseNode(xmlDocPtr xml, xmlNodePtr root, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, unsigned int flags) { g_autoptr(xmlXPathContext) ctxt = NULL; if (!virXMLNodeNameEqual(root, "domstatus")) { virReportError(VIR_ERR_XML_ERROR, _("unexpected root element <%s>, " "expecting "), root->name); return NULL; } if (!(ctxt = virXMLXPathContextNew(xml))) return NULL; ctxt->node = root; return virDomainObjParseXML(xml, ctxt, caps, xmlopt, flags); } virDomainObjPtr virDomainObjParseFile(const char *filename, virCapsPtr caps, virDomainXMLOptionPtr xmlopt, unsigned int flags) { xmlDocPtr xml; virDomainObjPtr obj = NULL; int keepBlanksDefault = xmlKeepBlanksDefault(0); if ((xml = virXMLParseFile(filename))) { obj = virDomainObjParseNode(xml, xmlDocGetRootElement(xml), caps, xmlopt, flags); xmlFreeDoc(xml); } xmlKeepBlanksDefault(keepBlanksDefault); return obj; } static bool virDomainTimerDefCheckABIStability(virDomainTimerDefPtr src, virDomainTimerDefPtr dst) { if (src->name != dst->name) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target timer %s does not match source %s"), virDomainTimerNameTypeToString(dst->name), virDomainTimerNameTypeToString(src->name)); return false; } if (src->present != dst->present) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target timer presence %d does not match source %d"), dst->present, src->present); return false; } if (src->name == VIR_DOMAIN_TIMER_NAME_TSC) { if (src->frequency != dst->frequency) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target TSC frequency %lu does not match source %lu"), dst->frequency, src->frequency); return false; } if (src->mode != dst->mode) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target TSC mode %s does not match source %s"), virDomainTimerModeTypeToString(dst->mode), virDomainTimerModeTypeToString(src->mode)); return false; } } return true; } static bool virDomainDeviceInfoCheckABIStability(virDomainDeviceInfoPtr src, virDomainDeviceInfoPtr dst) { if (src->type != dst->type) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device address type %s does not match source %s"), virDomainDeviceAddressTypeToString(dst->type), virDomainDeviceAddressTypeToString(src->type)); return false; } switch ((virDomainDeviceAddressType) src->type) { case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI: if (src->addr.pci.domain != dst->addr.pci.domain || src->addr.pci.bus != dst->addr.pci.bus || src->addr.pci.slot != dst->addr.pci.slot || src->addr.pci.function != dst->addr.pci.function) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device PCI address " VIR_PCI_DEVICE_ADDRESS_FMT "does not match source " VIR_PCI_DEVICE_ADDRESS_FMT), dst->addr.pci.domain, dst->addr.pci.bus, dst->addr.pci.slot, dst->addr.pci.function, src->addr.pci.domain, src->addr.pci.bus, src->addr.pci.slot, src->addr.pci.function); return false; } break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE: if (src->addr.drive.controller != dst->addr.drive.controller || src->addr.drive.bus != dst->addr.drive.bus || src->addr.drive.unit != dst->addr.drive.unit) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device drive address %d:%d:%d " "does not match source %d:%d:%d"), dst->addr.drive.controller, dst->addr.drive.bus, dst->addr.drive.unit, src->addr.drive.controller, src->addr.drive.bus, src->addr.drive.unit); return false; } break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL: if (src->addr.vioserial.controller != dst->addr.vioserial.controller || src->addr.vioserial.bus != dst->addr.vioserial.bus || src->addr.vioserial.port != dst->addr.vioserial.port) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device virtio serial address %d:%d:%d " "does not match source %d:%d:%d"), dst->addr.vioserial.controller, dst->addr.vioserial.bus, dst->addr.vioserial.port, src->addr.vioserial.controller, src->addr.vioserial.bus, src->addr.vioserial.port); return false; } break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID: if (src->addr.ccid.controller != dst->addr.ccid.controller || src->addr.ccid.slot != dst->addr.ccid.slot) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device ccid address %d:%d " "does not match source %d:%d"), dst->addr.ccid.controller, dst->addr.ccid.slot, src->addr.ccid.controller, src->addr.ccid.slot); return false; } break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA: if (src->addr.isa.iobase != dst->addr.isa.iobase || src->addr.isa.irq != dst->addr.isa.irq) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device isa address %d:%d " "does not match source %d:%d"), dst->addr.isa.iobase, dst->addr.isa.irq, src->addr.isa.iobase, src->addr.isa.irq); return false; } break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM: if (src->addr.dimm.slot != dst->addr.dimm.slot) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device dimm slot %u does not match " "source %u"), dst->addr.dimm.slot, src->addr.dimm.slot); return false; } if (src->addr.dimm.base != dst->addr.dimm.base) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device dimm base address '%llx' does " "not match source '%llx'"), dst->addr.dimm.base, src->addr.dimm.base); return false; } break; case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB: case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO: case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390: case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW: case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO: case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE: case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST: break; } return true; } static bool virDomainVirtioOptionsCheckABIStability(virDomainVirtioOptionsPtr src, virDomainVirtioOptionsPtr dst) { if (src->iommu != dst->iommu) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device iommu option '%s' does not " "match source '%s'"), virTristateSwitchTypeToString(dst->iommu), virTristateSwitchTypeToString(src->iommu)); return false; } if (src->ats != dst->ats) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target device ats option '%s' does not " "match source '%s'"), virTristateSwitchTypeToString(dst->ats), virTristateSwitchTypeToString(src->ats)); return false; } return true; } static bool virDomainDiskDefCheckABIStability(virDomainDiskDefPtr src, virDomainDiskDefPtr dst) { if (src->device != dst->device) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target disk device %s does not match source %s"), virDomainDiskDeviceTypeToString(dst->device), virDomainDiskDeviceTypeToString(src->device)); return false; } if (src->bus != dst->bus) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target disk bus %s does not match source %s"), virDomainDiskBusTypeToString(dst->bus), virDomainDiskBusTypeToString(src->bus)); return false; } if (STRNEQ(src->dst, dst->dst)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target disk %s does not match source %s"), dst->dst, src->dst); return false; } if (STRNEQ_NULLABLE(src->serial, dst->serial)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target disk serial %s does not match source %s"), NULLSTR(dst->serial), NULLSTR(src->serial)); return false; } if (STRNEQ_NULLABLE(src->wwn, dst->wwn)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target disk wwn '%s' does not match source '%s'"), NULLSTR(dst->wwn), NULLSTR(src->wwn)); return false; } if (src->src->readonly != dst->src->readonly) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target disk access mode does not match source")); return false; } if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target disk model %s does not match source %s"), virDomainDiskModelTypeToString(dst->model), virDomainDiskModelTypeToString(src->model)); return false; } if (src->virtio && dst->virtio && !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio)) return false; if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainControllerDefCheckABIStability(virDomainControllerDefPtr src, virDomainControllerDefPtr dst) { if (src->type != dst->type) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target controller type %s does not match source %s"), virDomainControllerTypeToString(dst->type), virDomainControllerTypeToString(src->type)); return false; } if (src->idx != dst->idx) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target controller index %d does not match source %d"), dst->idx, src->idx); return false; } if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target controller model %d does not match source %d"), dst->model, src->model); return false; } if (src->type == VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL) { if (src->opts.vioserial.ports != dst->opts.vioserial.ports) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target controller ports %d does not match source %d"), dst->opts.vioserial.ports, src->opts.vioserial.ports); return false; } if (src->opts.vioserial.vectors != dst->opts.vioserial.vectors) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target controller vectors %d does not match source %d"), dst->opts.vioserial.vectors, src->opts.vioserial.vectors); return false; } } else if (src->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) { if (src->opts.usbopts.ports != dst->opts.usbopts.ports) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target controller ports %d does not match source %d"), dst->opts.usbopts.ports, src->opts.usbopts.ports); return false; } } if (src->virtio && dst->virtio && !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio)) return false; if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainFsDefCheckABIStability(virDomainFSDefPtr src, virDomainFSDefPtr dst) { if (STRNEQ(src->dst, dst->dst)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target filesystem guest target %s does not match source %s"), dst->dst, src->dst); return false; } if (src->readonly != dst->readonly) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target filesystem access mode does not match source")); return false; } if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target filesystem model does not match source")); return false; } if (src->virtio && dst->virtio && !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio)) return false; if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainNetDefCheckABIStability(virDomainNetDefPtr src, virDomainNetDefPtr dst) { char srcmac[VIR_MAC_STRING_BUFLEN]; char dstmac[VIR_MAC_STRING_BUFLEN]; if (virMacAddrCmp(&src->mac, &dst->mac) != 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target network card mac %s" " does not match source %s"), virMacAddrFormat(&dst->mac, dstmac), virMacAddrFormat(&src->mac, srcmac)); return false; } if (STRNEQ_NULLABLE(src->modelstr, dst->modelstr)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target network card model %s does not match source %s"), NULLSTR(dst->modelstr), NULLSTR(src->modelstr)); return false; } if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target network card model %s does not match source %s"), virDomainNetModelTypeToString(dst->model), virDomainNetModelTypeToString(src->model)); return false; } if (src->mtu != dst->mtu) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target network card MTU %d does not match source %d"), dst->mtu, src->mtu); return false; } if (src->virtio && dst->virtio && !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio)) return false; if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainInputDefCheckABIStability(virDomainInputDefPtr src, virDomainInputDefPtr dst) { if (src->type != dst->type) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target input device type %s does not match source %s"), virDomainInputTypeToString(dst->type), virDomainInputTypeToString(src->type)); return false; } if (src->bus != dst->bus) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target input device bus %s does not match source %s"), virDomainInputBusTypeToString(dst->bus), virDomainInputBusTypeToString(src->bus)); return false; } if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target input model %s does not match source %s"), virDomainInputBusTypeToString(dst->model), virDomainInputBusTypeToString(src->model)); return false; } if (src->virtio && dst->virtio && !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio)) return false; if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainSoundDefCheckABIStability(virDomainSoundDefPtr src, virDomainSoundDefPtr dst) { if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target sound card model %s does not match source %s"), virDomainSoundModelTypeToString(dst->model), virDomainSoundModelTypeToString(src->model)); return false; } if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainVideoDefCheckABIStability(virDomainVideoDefPtr src, virDomainVideoDefPtr dst) { if (src->type != dst->type) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target video card model %s does not match source %s"), virDomainVideoTypeToString(dst->type), virDomainVideoTypeToString(src->type)); return false; } if (src->ram != dst->ram) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target video card ram %u does not match source %u"), dst->ram, src->ram); return false; } if (src->vram != dst->vram) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target video card vram %u does not match source %u"), dst->vram, src->vram); return false; } if (src->vram64 != dst->vram64) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target video card vram64 %u does not match source %u"), dst->vram64, src->vram64); return false; } if (src->vgamem != dst->vgamem) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target video card vgamem %u does not match source %u"), dst->vgamem, src->vgamem); return false; } if (src->heads != dst->heads) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target video card heads %u does not match source %u"), dst->heads, src->heads); return false; } if ((src->accel && !dst->accel) || (!src->accel && dst->accel)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target video card acceleration does not match source")); return false; } if (src->accel) { if (src->accel->accel2d != dst->accel->accel2d) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target video card 2d accel %u does not match source %u"), dst->accel->accel2d, src->accel->accel2d); return false; } if (src->accel->accel3d != dst->accel->accel3d) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target video card 3d accel %u does not match source %u"), dst->accel->accel3d, src->accel->accel3d); return false; } } if (src->virtio && dst->virtio && !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio)) return false; if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainHostdevDefCheckABIStability(virDomainHostdevDefPtr src, virDomainHostdevDefPtr dst) { if (src->mode != dst->mode) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target host device mode %s does not match source %s"), virDomainHostdevModeTypeToString(dst->mode), virDomainHostdevModeTypeToString(src->mode)); return false; } if (src->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && src->source.subsys.type != dst->source.subsys.type) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target host device subsystem %s does not match source %s"), virDomainHostdevSubsysTypeToString(dst->source.subsys.type), virDomainHostdevSubsysTypeToString(src->source.subsys.type)); return false; } if (!virDomainDeviceInfoCheckABIStability(src->info, dst->info)) return false; return true; } static bool virDomainSmartcardDefCheckABIStability(virDomainSmartcardDefPtr src, virDomainSmartcardDefPtr dst) { if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainSerialDefCheckABIStability(virDomainChrDefPtr src, virDomainChrDefPtr dst) { if (src->targetType != dst->targetType) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target serial type %s does not match source %s"), virDomainChrSerialTargetTypeToString(dst->targetType), virDomainChrSerialTargetTypeToString(src->targetType)); return false; } if (src->target.port != dst->target.port) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target serial port %d does not match source %d"), dst->target.port, src->target.port); return false; } if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainParallelDefCheckABIStability(virDomainChrDefPtr src, virDomainChrDefPtr dst) { if (src->target.port != dst->target.port) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target parallel port %d does not match source %d"), dst->target.port, src->target.port); return false; } if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainChannelDefCheckABIStability(virDomainChrDefPtr src, virDomainChrDefPtr dst) { if (src->targetType != dst->targetType) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target channel type %s does not match source %s"), virDomainChrChannelTargetTypeToString(dst->targetType), virDomainChrChannelTargetTypeToString(src->targetType)); return false; } switch (src->targetType) { case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_XEN: case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO: if (STRNEQ_NULLABLE(src->target.name, dst->target.name)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target channel name %s does not match source %s"), NULLSTR(dst->target.name), NULLSTR(src->target.name)); return false; } if (src->source->type != dst->source->type && (src->source->type == VIR_DOMAIN_CHR_TYPE_SPICEVMC || dst->source->type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) && !src->target.name) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Changing device type to/from spicevmc would" " change default target channel name")); return false; } break; case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD: if (memcmp(src->target.addr, dst->target.addr, sizeof(*src->target.addr)) != 0) { g_autofree char *saddr = virSocketAddrFormatFull(src->target.addr, true, ":"); g_autofree char *daddr = virSocketAddrFormatFull(dst->target.addr, true, ":"); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target channel addr %s does not match source %s"), NULLSTR(daddr), NULLSTR(saddr)); return false; } break; } if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainConsoleDefCheckABIStability(virDomainChrDefPtr src, virDomainChrDefPtr dst) { if (src->targetType != dst->targetType) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target console type %s does not match source %s"), virDomainChrConsoleTargetTypeToString(dst->targetType), virDomainChrConsoleTargetTypeToString(src->targetType)); return false; } if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainWatchdogDefCheckABIStability(virDomainWatchdogDefPtr src, virDomainWatchdogDefPtr dst) { if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target watchdog model %s does not match source %s"), virDomainWatchdogModelTypeToString(dst->model), virDomainWatchdogModelTypeToString(src->model)); return false; } if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainMemballoonDefCheckABIStability(virDomainMemballoonDefPtr src, virDomainMemballoonDefPtr dst) { if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target balloon model %s does not match source %s"), virDomainMemballoonModelTypeToString(dst->model), virDomainMemballoonModelTypeToString(src->model)); return false; } if (src->autodeflate != dst->autodeflate) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target balloon autodeflate attribute value " "'%s' does not match source '%s'"), virTristateSwitchTypeToString(dst->autodeflate), virTristateSwitchTypeToString(src->autodeflate)); return false; } if (src->virtio && dst->virtio && !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio)) return false; if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainRNGDefCheckABIStability(virDomainRNGDefPtr src, virDomainRNGDefPtr dst) { if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target RNG model '%s' does not match source '%s'"), virDomainRNGModelTypeToString(dst->model), virDomainRNGModelTypeToString(src->model)); return false; } if (src->virtio && dst->virtio && !virDomainVirtioOptionsCheckABIStability(src->virtio, dst->virtio)) return false; if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainHubDefCheckABIStability(virDomainHubDefPtr src, virDomainHubDefPtr dst) { if (src->type != dst->type) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target hub device type %s does not match source %s"), virDomainHubTypeToString(dst->type), virDomainHubTypeToString(src->type)); return false; } if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainRedirdevDefCheckABIStability(virDomainRedirdevDefPtr src, virDomainRedirdevDefPtr dst) { if (src->bus != dst->bus) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target redirected device bus %s does not match " "source %s"), virDomainRedirdevBusTypeToString(dst->bus), virDomainRedirdevBusTypeToString(src->bus)); return false; } switch ((virDomainRedirdevBus) src->bus) { case VIR_DOMAIN_REDIRDEV_BUS_USB: if (src->source->type != dst->source->type) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target redirected device source type %s does " "not match source device source type %s"), virDomainChrTypeToString(dst->source->type), virDomainChrTypeToString(src->source->type)); return false; } break; case VIR_DOMAIN_REDIRDEV_BUS_LAST: break; } if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainRedirFilterDefCheckABIStability(virDomainRedirFilterDefPtr src, virDomainRedirFilterDefPtr dst) { size_t i; if (src->nusbdevs != dst->nusbdevs) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target USB redirection filter rule " "count %zu does not match source %zu"), dst->nusbdevs, src->nusbdevs); return false; } for (i = 0; i < src->nusbdevs; i++) { virDomainRedirFilterUSBDevDefPtr srcUSBDev = src->usbdevs[i]; virDomainRedirFilterUSBDevDefPtr dstUSBDev = dst->usbdevs[i]; if (srcUSBDev->usbClass != dstUSBDev->usbClass) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target USB Class code does not match source")); return false; } if (srcUSBDev->vendor != dstUSBDev->vendor) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target USB vendor ID does not match source")); return false; } if (srcUSBDev->product != dstUSBDev->product) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target USB product ID does not match source")); return false; } if (srcUSBDev->version != dstUSBDev->version) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target USB version does not match source")); return false; } if (srcUSBDev->allow != dstUSBDev->allow) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target USB allow '%s' does not match source '%s'"), dstUSBDev->allow ? "yes" : "no", srcUSBDev->allow ? "yes" : "no"); return false; } } return true; } static bool virDomainDefFeaturesCheckABIStability(virDomainDefPtr src, virDomainDefPtr dst) { size_t i; for (i = 0; i < VIR_DOMAIN_FEATURE_LAST; i++) { const char *featureName = virDomainFeatureTypeToString(i); switch ((virDomainFeature) i) { 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: case VIR_DOMAIN_FEATURE_PVSPINLOCK: case VIR_DOMAIN_FEATURE_PMU: case VIR_DOMAIN_FEATURE_VMPORT: case VIR_DOMAIN_FEATURE_SMM: case VIR_DOMAIN_FEATURE_VMCOREINFO: case VIR_DOMAIN_FEATURE_HTM: case VIR_DOMAIN_FEATURE_NESTED_HV: case VIR_DOMAIN_FEATURE_CCF_ASSIST: if (src->features[i] != dst->features[i]) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of feature '%s' differs: " "source: '%s', destination: '%s'"), featureName, virTristateSwitchTypeToString(src->features[i]), virTristateSwitchTypeToString(dst->features[i])); return false; } break; case VIR_DOMAIN_FEATURE_CAPABILITIES: if (src->features[i] != dst->features[i]) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of feature '%s' differs: " "source: '%s=%s', destination: '%s=%s'"), featureName, "policy", virDomainCapabilitiesPolicyTypeToString(src->features[i]), "policy", virDomainCapabilitiesPolicyTypeToString(dst->features[i])); return false; } break; case VIR_DOMAIN_FEATURE_GIC: if (src->features[i] != dst->features[i] || src->gic_version != dst->gic_version) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of feature '%s' differs: " "source: '%s,%s=%s', destination: '%s,%s=%s'"), featureName, virTristateSwitchTypeToString(src->features[i]), "version", virGICVersionTypeToString(src->gic_version), virTristateSwitchTypeToString(dst->features[i]), "version", virGICVersionTypeToString(dst->gic_version)); return false; } break; case VIR_DOMAIN_FEATURE_HPT: if (src->features[i] != dst->features[i] || src->hpt_resizing != dst->hpt_resizing || src->hpt_maxpagesize != dst->hpt_maxpagesize) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of feature '%s' differs: " "source: '%s,%s=%s,%s=%llu', destination: '%s,%s=%s,%s=%llu'"), featureName, virTristateSwitchTypeToString(src->features[i]), "resizing", virDomainHPTResizingTypeToString(src->hpt_resizing), "maxpagesize", src->hpt_maxpagesize, virTristateSwitchTypeToString(dst->features[i]), "resizing", virDomainHPTResizingTypeToString(dst->hpt_resizing), "maxpagesize", dst->hpt_maxpagesize); return false; } break; case VIR_DOMAIN_FEATURE_APIC: if (src->features[i] != dst->features[i] || src->apic_eoi != dst->apic_eoi) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of feature '%s' differs: " "source: '%s,%s=%s', destination: '%s,%s=%s'"), featureName, virTristateSwitchTypeToString(src->features[i]), "eoi", virTristateSwitchTypeToString(src->apic_eoi), virTristateSwitchTypeToString(dst->features[i]), "eoi", virTristateSwitchTypeToString(dst->apic_eoi)); return false; } break; case VIR_DOMAIN_FEATURE_IOAPIC: if (src->features[i] != dst->features[i]) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of feature '%s' differs: " "source: '%s=%s', destination: '%s=%s'"), featureName, "driver", virDomainIOAPICTypeToString(src->features[i]), "driver", virDomainIOAPICTypeToString(dst->features[i])); return false; } break; case VIR_DOMAIN_FEATURE_MSRS: break; case VIR_DOMAIN_FEATURE_LAST: break; } } /* hyperv */ if (src->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) { for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) { switch ((virDomainHyperv) i) { case VIR_DOMAIN_HYPERV_RELAXED: case VIR_DOMAIN_HYPERV_VAPIC: case VIR_DOMAIN_HYPERV_VPINDEX: case VIR_DOMAIN_HYPERV_RUNTIME: case VIR_DOMAIN_HYPERV_SYNIC: case VIR_DOMAIN_HYPERV_STIMER: case VIR_DOMAIN_HYPERV_RESET: case VIR_DOMAIN_HYPERV_FREQUENCIES: case VIR_DOMAIN_HYPERV_REENLIGHTENMENT: case VIR_DOMAIN_HYPERV_TLBFLUSH: case VIR_DOMAIN_HYPERV_IPI: case VIR_DOMAIN_HYPERV_EVMCS: if (src->hyperv_features[i] != dst->hyperv_features[i]) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of HyperV enlightenment " "feature '%s' differs: " "source: '%s', destination: '%s'"), virDomainHypervTypeToString(i), virTristateSwitchTypeToString(src->hyperv_features[i]), virTristateSwitchTypeToString(dst->hyperv_features[i])); return false; } break; case VIR_DOMAIN_HYPERV_SPINLOCKS: /* spinlock count matters! */ if (src->hyperv_spinlocks != dst->hyperv_spinlocks) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("HyperV spinlock retry count differs: " "source: '%u', destination: '%u'"), src->hyperv_spinlocks, dst->hyperv_spinlocks); return false; } break; case VIR_DOMAIN_HYPERV_VENDOR_ID: if (STRNEQ_NULLABLE(src->hyperv_vendor_id, dst->hyperv_vendor_id)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("HyperV vendor_id differs: " "source: '%s', destination: '%s'"), src->hyperv_vendor_id, dst->hyperv_vendor_id); return false; } break; /* coverity[dead_error_begin] */ case VIR_DOMAIN_HYPERV_LAST: break; } } } if (src->hyperv_features[VIR_DOMAIN_HYPERV_STIMER] == VIR_TRISTATE_SWITCH_ON) { if (src->hyperv_stimer_direct != dst->hyperv_stimer_direct) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of HyperV stimer direct feature differs: " "source: '%s', destination: '%s'"), virTristateSwitchTypeToString(src->hyperv_stimer_direct), virTristateSwitchTypeToString(dst->hyperv_stimer_direct)); return false; } } /* kvm */ if (src->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) { for (i = 0; i < VIR_DOMAIN_KVM_LAST; i++) { switch ((virDomainKVM) i) { case VIR_DOMAIN_KVM_HIDDEN: case VIR_DOMAIN_KVM_DEDICATED: if (src->kvm_features[i] != dst->kvm_features[i]) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of KVM feature '%s' differs: " "source: '%s', destination: '%s'"), virDomainKVMTypeToString(i), virTristateSwitchTypeToString(src->kvm_features[i]), virTristateSwitchTypeToString(dst->kvm_features[i])); return false; } break; /* coverity[dead_error_begin] */ case VIR_DOMAIN_KVM_LAST: break; } } } /* smm */ if (src->features[VIR_DOMAIN_FEATURE_SMM] == VIR_TRISTATE_SWITCH_ON) { if (src->tseg_specified != dst->tseg_specified) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("SMM TSEG differs: source: %s, destination: '%s'"), src->tseg_specified ? _("specified") : _("not specified"), dst->tseg_specified ? _("specified") : _("not specified")); return false; } if (src->tseg_specified && src->tseg_size != dst->tseg_size) { const char *unit_src, *unit_dst; unsigned long long short_size_src = virFormatIntPretty(src->tseg_size, &unit_src); unsigned long long short_size_dst = virFormatIntPretty(dst->tseg_size, &unit_dst); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Size of SMM TSEG size differs: " "source: '%llu %s', destination: '%llu %s'"), short_size_src, unit_src, short_size_dst, unit_dst); return false; } } return true; } static bool virDomainPanicDefCheckABIStability(virDomainPanicDefPtr src, virDomainPanicDefPtr dst) { if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target panic model '%s' does not match source '%s'"), virDomainPanicModelTypeToString(dst->model), virDomainPanicModelTypeToString(src->model)); return false; } return virDomainDeviceInfoCheckABIStability(&src->info, &dst->info); } static bool virDomainShmemDefCheckABIStability(virDomainShmemDefPtr src, virDomainShmemDefPtr dst) { if (STRNEQ_NULLABLE(src->name, dst->name)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target shared memory name '%s' does not match source " "'%s'"), dst->name, src->name); return false; } if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target shared memory model '%s' does not match " "source model '%s'"), virDomainShmemModelTypeToString(dst->model), virDomainShmemModelTypeToString(src->model)); return false; } if (src->size != dst->size) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target shared memory size '%llu' does not match " "source size '%llu'"), dst->size, src->size); return false; } if (src->server.enabled != dst->server.enabled) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target shared memory server usage doesn't match " "source")); return false; } if (src->msi.vectors != dst->msi.vectors || src->msi.enabled != dst->msi.enabled || src->msi.ioeventfd != dst->msi.ioeventfd) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target shared memory MSI configuration doesn't match " "source")); return false; } return virDomainDeviceInfoCheckABIStability(&src->info, &dst->info); } static bool virDomainTPMDefCheckABIStability(virDomainTPMDefPtr src, virDomainTPMDefPtr dst) { if (src->type != dst->type) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target TPM device type doesn't match source")); return false; } if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target TPM device model doesn't match source")); return false; } if (src->version != dst->version) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target TPM version doesn't match source")); return false; } return virDomainDeviceInfoCheckABIStability(&src->info, &dst->info); } static bool virDomainMemtuneCheckABIStability(const virDomainDef *src, const virDomainDef *dst, unsigned int flags) { if (virDomainDefGetMemoryInitial(src) != virDomainDefGetMemoryInitial(dst)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain max memory %lld " "does not match source %lld"), virDomainDefGetMemoryInitial(dst), virDomainDefGetMemoryInitial(src)); return false; } if (!(flags & VIR_DOMAIN_DEF_ABI_CHECK_SKIP_VOLATILE) && src->mem.cur_balloon != dst->mem.cur_balloon) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain current memory %lld " "does not match source %lld"), dst->mem.cur_balloon, src->mem.cur_balloon); return false; } if (src->mem.max_memory != dst->mem.max_memory) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target maximum memory size '%llu' " "doesn't match source '%llu'"), dst->mem.max_memory, src->mem.max_memory); return false; } if (src->mem.memory_slots != dst->mem.memory_slots) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain memory slots " "count '%u' doesn't match source '%u'"), dst->mem.memory_slots, src->mem.memory_slots); return false; } return true; } static bool virDomainMemoryDefCheckABIStability(virDomainMemoryDefPtr src, virDomainMemoryDefPtr dst) { if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target memory device model '%s' " "doesn't match source model '%s'"), virDomainMemoryModelTypeToString(dst->model), virDomainMemoryModelTypeToString(src->model)); return false; } if (src->targetNode != dst->targetNode) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target memory device targetNode '%d' " "doesn't match source targetNode '%d'"), dst->targetNode, src->targetNode); return false; } if (src->size != dst->size) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target memory device size '%llu' doesn't match " "source memory device size '%llu'"), dst->size, src->size); return false; } if (src->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) { if (src->labelsize != dst->labelsize) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target NVDIMM label size '%llu' doesn't match " "source NVDIMM label size '%llu'"), src->labelsize, dst->labelsize); return false; } if (src->alignsize != dst->alignsize) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target NVDIMM alignment '%llu' doesn't match " "source NVDIMM alignment '%llu'"), src->alignsize, dst->alignsize); return false; } if (src->nvdimmPmem != dst->nvdimmPmem) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target NVDIMM pmem flag doesn't match " "source NVDIMM pmem flag")); return false; } if (src->readonly != dst->readonly) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target NVDIMM readonly flag doesn't match " "source NVDIMM readonly flag")); return false; } } return virDomainDeviceInfoCheckABIStability(&src->info, &dst->info); } static bool virDomainIOMMUDefCheckABIStability(virDomainIOMMUDefPtr src, virDomainIOMMUDefPtr dst) { if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain IOMMU device model '%s' " "does not match source '%s'"), virDomainIOMMUModelTypeToString(dst->model), virDomainIOMMUModelTypeToString(src->model)); return false; } if (src->intremap != dst->intremap) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain IOMMU device intremap value '%s' " "does not match source '%s'"), virTristateSwitchTypeToString(dst->intremap), virTristateSwitchTypeToString(src->intremap)); return false; } if (src->caching_mode != dst->caching_mode) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain IOMMU device caching mode '%s' " "does not match source '%s'"), virTristateSwitchTypeToString(dst->caching_mode), virTristateSwitchTypeToString(src->caching_mode)); return false; } if (src->eim != dst->eim) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain IOMMU device eim value '%s' " "does not match source '%s'"), virTristateSwitchTypeToString(dst->eim), virTristateSwitchTypeToString(src->eim)); return false; } if (src->iotlb != dst->iotlb) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain IOMMU device iotlb value '%s' " "does not match source '%s'"), virTristateSwitchTypeToString(dst->iotlb), virTristateSwitchTypeToString(src->iotlb)); return false; } return true; } static bool virDomainVsockDefCheckABIStability(virDomainVsockDefPtr src, virDomainVsockDefPtr dst) { if (src->model != dst->model) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain vsock device model '%s' " "does not match source '%s'"), virDomainVsockModelTypeToString(dst->model), virDomainVsockModelTypeToString(src->model)); return false; } if (!virDomainDeviceInfoCheckABIStability(&src->info, &dst->info)) return false; return true; } static bool virDomainDefVcpuCheckAbiStability(virDomainDefPtr src, virDomainDefPtr dst) { size_t i; if (src->maxvcpus != dst->maxvcpus) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain vCPU max %zu does not match source %zu"), dst->maxvcpus, src->maxvcpus); return false; } for (i = 0; i < src->maxvcpus; i++) { virDomainVcpuDefPtr svcpu = src->vcpus[i]; virDomainVcpuDefPtr dvcpu = dst->vcpus[i]; if (svcpu->online != dvcpu->online) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of vCPU '%zu' differs between source and " "destination definitions"), i); return false; } if (svcpu->order != dvcpu->order) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("vcpu enable order of vCPU '%zu' differs between " "source and destination definitions"), i); return false; } } return true; } /* This compares two configurations and looks for any differences * which will affect the guest ABI. This is primarily to allow * validation of custom XML config passed in during migration */ bool virDomainDefCheckABIStabilityFlags(virDomainDefPtr src, virDomainDefPtr dst, virDomainXMLOptionPtr xmlopt, unsigned int flags) { size_t i; virErrorPtr err; g_autofree char *strSrc = NULL; g_autofree char *strDst = NULL; if (src->virtType != dst->virtType) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain virt type %s does not match source %s"), virDomainVirtTypeToString(dst->virtType), virDomainVirtTypeToString(src->virtType)); goto error; } if (memcmp(src->uuid, dst->uuid, VIR_UUID_BUFLEN) != 0) { char uuidsrc[VIR_UUID_STRING_BUFLEN]; char uuiddst[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(src->uuid, uuidsrc); virUUIDFormat(dst->uuid, uuiddst); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain uuid %s does not match source %s"), uuiddst, uuidsrc); goto error; } if (src->genidRequested != dst->genidRequested) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target domain requested genid does not match source")); goto error; } if (src->genidRequested && memcmp(src->genid, dst->genid, VIR_UUID_BUFLEN) != 0) { char guidsrc[VIR_UUID_STRING_BUFLEN]; char guiddst[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(src->genid, guidsrc); virUUIDFormat(dst->genid, guiddst); virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain genid %s does not match source %s"), guiddst, guidsrc); goto error; } /* Not strictly ABI related, but we want to make sure domains * don't get silently re-named through the backdoor when passing * custom XML into various APIs, since this would create havoc */ if (STRNEQ_NULLABLE(src->name, dst->name)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain name '%s' does not match source '%s'"), dst->name, src->name); goto error; } if (!virDomainMemtuneCheckABIStability(src, dst, flags)) goto error; if (!virDomainNumaCheckABIStability(src->numa, dst->numa)) goto error; if (!virDomainDefVcpuCheckAbiStability(src, dst)) goto error; if (src->os.type != dst->os.type) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain OS type %s does not match source %s"), virDomainOSTypeToString(dst->os.type), virDomainOSTypeToString(src->os.type)); goto error; } if (src->os.arch != dst->os.arch) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain architecture %s does not match source %s"), virArchToString(dst->os.arch), virArchToString(src->os.arch)); goto error; } if (STRNEQ_NULLABLE(src->os.machine, dst->os.machine)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain machine type %s does not match source %s"), dst->os.machine, src->os.machine); goto error; } if (src->os.smbios_mode != dst->os.smbios_mode) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain SMBIOS mode %s does not match source %s"), virDomainSmbiosModeTypeToString(dst->os.smbios_mode), virDomainSmbiosModeTypeToString(src->os.smbios_mode)); goto error; } if (!virDomainDefFeaturesCheckABIStability(src, dst)) goto error; if (src->clock.ntimers != dst->clock.ntimers) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target domain timers do not match source")); goto error; } for (i = 0; i < src->clock.ntimers; i++) { if (!virDomainTimerDefCheckABIStability(src->clock.timers[i], dst->clock.timers[i])) goto error; } if (!virCPUDefIsEqual(src->cpu, dst->cpu, true)) goto error; if (!virSysinfoIsEqual(src->sysinfo, dst->sysinfo)) goto error; if (src->ndisks != dst->ndisks) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain disk count %zu does not match source %zu"), dst->ndisks, src->ndisks); goto error; } for (i = 0; i < src->ndisks; i++) if (!virDomainDiskDefCheckABIStability(src->disks[i], dst->disks[i])) goto error; if (src->ncontrollers != dst->ncontrollers) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain controller count %zu " "does not match source %zu"), dst->ncontrollers, src->ncontrollers); goto error; } for (i = 0; i < src->ncontrollers; i++) if (!virDomainControllerDefCheckABIStability(src->controllers[i], dst->controllers[i])) goto error; if (src->nfss != dst->nfss) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain filesystem count %zu " "does not match source %zu"), dst->nfss, src->nfss); goto error; } for (i = 0; i < src->nfss; i++) if (!virDomainFsDefCheckABIStability(src->fss[i], dst->fss[i])) goto error; if (src->nnets != dst->nnets) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain net card count %zu " "does not match source %zu"), dst->nnets, src->nnets); goto error; } for (i = 0; i < src->nnets; i++) if (!virDomainNetDefCheckABIStability(src->nets[i], dst->nets[i])) goto error; if (src->ninputs != dst->ninputs) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain input device count %zu " "does not match source %zu"), dst->ninputs, src->ninputs); goto error; } for (i = 0; i < src->ninputs; i++) if (!virDomainInputDefCheckABIStability(src->inputs[i], dst->inputs[i])) goto error; if (src->nsounds != dst->nsounds) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain sound card count %zu " "does not match source %zu"), dst->nsounds, src->nsounds); goto error; } for (i = 0; i < src->nsounds; i++) if (!virDomainSoundDefCheckABIStability(src->sounds[i], dst->sounds[i])) goto error; if (src->nvideos != dst->nvideos) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain video card count %zu " "does not match source %zu"), dst->nvideos, src->nvideos); goto error; } for (i = 0; i < src->nvideos; i++) if (!virDomainVideoDefCheckABIStability(src->videos[i], dst->videos[i])) goto error; if (src->nhostdevs != dst->nhostdevs) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain host device count %zu " "does not match source %zu"), dst->nhostdevs, src->nhostdevs); goto error; } for (i = 0; i < src->nhostdevs; i++) if (!virDomainHostdevDefCheckABIStability(src->hostdevs[i], dst->hostdevs[i])) goto error; if (src->nsmartcards != dst->nsmartcards) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain smartcard count %zu " "does not match source %zu"), dst->nsmartcards, src->nsmartcards); goto error; } for (i = 0; i < src->nsmartcards; i++) if (!virDomainSmartcardDefCheckABIStability(src->smartcards[i], dst->smartcards[i])) goto error; if (src->nserials != dst->nserials) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain serial port count %zu " "does not match source %zu"), dst->nserials, src->nserials); goto error; } for (i = 0; i < src->nserials; i++) if (!virDomainSerialDefCheckABIStability(src->serials[i], dst->serials[i])) goto error; if (src->nparallels != dst->nparallels) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain parallel port count %zu " "does not match source %zu"), dst->nparallels, src->nparallels); goto error; } for (i = 0; i < src->nparallels; i++) if (!virDomainParallelDefCheckABIStability(src->parallels[i], dst->parallels[i])) goto error; if (src->nchannels != dst->nchannels) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain channel count %zu " "does not match source %zu"), dst->nchannels, src->nchannels); goto error; } for (i = 0; i < src->nchannels; i++) if (!virDomainChannelDefCheckABIStability(src->channels[i], dst->channels[i])) goto error; if (src->nconsoles != dst->nconsoles) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain console count %zu " "does not match source %zu"), dst->nconsoles, src->nconsoles); goto error; } for (i = 0; i < src->nconsoles; i++) if (!virDomainConsoleDefCheckABIStability(src->consoles[i], dst->consoles[i])) goto error; if (src->nhubs != dst->nhubs) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain hub device count %zu " "does not match source %zu"), dst->nhubs, src->nhubs); goto error; } for (i = 0; i < src->nhubs; i++) if (!virDomainHubDefCheckABIStability(src->hubs[i], dst->hubs[i])) goto error; if (src->nredirdevs != dst->nredirdevs) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain redirected devices count %zu " "does not match source %zu"), dst->nredirdevs, src->nredirdevs); goto error; } for (i = 0; i < src->nredirdevs; i++) { if (!virDomainRedirdevDefCheckABIStability(src->redirdevs[i], dst->redirdevs[i])) goto error; } if ((!src->redirfilter && dst->redirfilter) || (src->redirfilter && !dst->redirfilter)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain USB redirection filter count %d " "does not match source %d"), dst->redirfilter ? 1 : 0, src->redirfilter ? 1 : 0); goto error; } if (src->redirfilter && !virDomainRedirFilterDefCheckABIStability(src->redirfilter, dst->redirfilter)) goto error; if ((!src->watchdog && dst->watchdog) || (src->watchdog && !dst->watchdog)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain watchdog count %d " "does not match source %d"), dst->watchdog ? 1 : 0, src->watchdog ? 1 : 0); goto error; } if (src->watchdog && !virDomainWatchdogDefCheckABIStability(src->watchdog, dst->watchdog)) goto error; if ((!src->memballoon && dst->memballoon) || (src->memballoon && !dst->memballoon)) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain memory balloon count %d " "does not match source %d"), dst->memballoon ? 1 : 0, src->memballoon ? 1 : 0); goto error; } if (src->memballoon && !virDomainMemballoonDefCheckABIStability(src->memballoon, dst->memballoon)) goto error; if (src->nrngs != dst->nrngs) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain RNG device count %zu " "does not match source %zu"), dst->nrngs, src->nrngs); goto error; } for (i = 0; i < src->nrngs; i++) if (!virDomainRNGDefCheckABIStability(src->rngs[i], dst->rngs[i])) goto error; if (src->npanics != dst->npanics) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain panic device count %zu " "does not match source %zu"), dst->npanics, src->npanics); goto error; } for (i = 0; i < src->npanics; i++) { if (!virDomainPanicDefCheckABIStability(src->panics[i], dst->panics[i])) goto error; } if (src->nshmems != dst->nshmems) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain shared memory device count %zu " "does not match source %zu"), dst->nshmems, src->nshmems); goto error; } for (i = 0; i < src->nshmems; i++) { if (!virDomainShmemDefCheckABIStability(src->shmems[i], dst->shmems[i])) goto error; } if (src->tpm && dst->tpm) { if (!virDomainTPMDefCheckABIStability(src->tpm, dst->tpm)) goto error; } else if (src->tpm || dst->tpm) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Either both target and source domains or none of " "them must have TPM device present")); goto error; } if (src->nmems != dst->nmems) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("Target domain memory device count %zu " "does not match source %zu"), dst->nmems, src->nmems); goto error; } for (i = 0; i < src->nmems; i++) { if (!virDomainMemoryDefCheckABIStability(src->mems[i], dst->mems[i])) goto error; } if (!!src->iommu != !!dst->iommu) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target domain IOMMU device count " "does not match source")); goto error; } if (src->iommu && !virDomainIOMMUDefCheckABIStability(src->iommu, dst->iommu)) goto error; if (!!src->vsock != !!dst->vsock) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("Target domain vsock device count " "does not match source")); goto error; } if (src->vsock && !virDomainVsockDefCheckABIStability(src->vsock, dst->vsock)) goto error; if (xmlopt && xmlopt->abi.domain && !xmlopt->abi.domain(src, dst)) goto error; /* 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 check 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_LAST: case VIR_DOMAIN_DEVICE_RNG: case VIR_DOMAIN_DEVICE_TPM: case VIR_DOMAIN_DEVICE_PANIC: case VIR_DOMAIN_DEVICE_SHMEM: case VIR_DOMAIN_DEVICE_MEMORY: case VIR_DOMAIN_DEVICE_IOMMU: case VIR_DOMAIN_DEVICE_VSOCK: break; } #endif return true; error: virErrorPreserveLast(&err); strSrc = virDomainDefFormat(src, NULL, 0); strDst = virDomainDefFormat(dst, NULL, 0); VIR_DEBUG("XMLs that failed stability check were: src=\"%s\", dst=\"%s\"", NULLSTR(strSrc), NULLSTR(strDst)); virErrorRestore(&err); return false; } bool virDomainDefCheckABIStability(virDomainDefPtr src, virDomainDefPtr dst, virDomainXMLOptionPtr xmlopt) { return virDomainDefCheckABIStabilityFlags(src, dst, xmlopt, 0); } static int virDomainDefAddDiskControllersForType(virDomainDefPtr def, int controllerType, int diskBus) { size_t i; int maxController = -1; for (i = 0; i < def->ndisks; i++) { if (def->disks[i]->bus != diskBus) continue; if (def->disks[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) continue; if ((int)def->disks[i]->info.addr.drive.controller > maxController) maxController = def->disks[i]->info.addr.drive.controller; } if (maxController == -1) return 0; for (i = 0; i <= maxController; i++) { if (virDomainDefMaybeAddController(def, controllerType, i, -1) < 0) return -1; } return 0; } static int virDomainDefMaybeAddVirtioSerialController(virDomainDefPtr def) { /* Look for any virtio serial or virtio console devs */ size_t i; for (i = 0; i < def->nchannels; i++) { virDomainChrDefPtr channel = def->channels[i]; if (channel->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO) { int idx = 0; if (channel->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL) idx = channel->info.addr.vioserial.controller; if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL, idx, -1) < 0) return -1; } } for (i = 0; i < def->nconsoles; i++) { virDomainChrDefPtr console = def->consoles[i]; if (console->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO) { int idx = 0; if (console->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL) idx = console->info.addr.vioserial.controller; if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL, idx, -1) < 0) return -1; } } return 0; } static int virDomainDefMaybeAddSmartcardController(virDomainDefPtr def) { /* Look for any smartcard devs */ size_t i; for (i = 0; i < def->nsmartcards; i++) { virDomainSmartcardDefPtr smartcard = def->smartcards[i]; int idx = 0; if (smartcard->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID) { idx = smartcard->info.addr.ccid.controller; } else if (smartcard->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) { size_t j; int max = -1; for (j = 0; j < def->nsmartcards; j++) { virDomainDeviceInfoPtr info = &def->smartcards[j]->info; if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID && info->addr.ccid.controller == 0 && (int) info->addr.ccid.slot > max) max = info->addr.ccid.slot; } smartcard->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID; smartcard->info.addr.ccid.controller = 0; smartcard->info.addr.ccid.slot = max + 1; } if (virDomainDefMaybeAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_CCID, idx, -1) < 0) return -1; } return 0; } /* * Based on the declared
info for any devices, * add necessary drive controllers which are not already present * in the XML. This is for compat with existing apps which will * not know/care about info in the XML */ static int virDomainDefAddImplicitControllers(virDomainDefPtr def) { if (virDomainDefAddDiskControllersForType(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI, VIR_DOMAIN_DISK_BUS_SCSI) < 0) return -1; if (virDomainDefAddDiskControllersForType(def, VIR_DOMAIN_CONTROLLER_TYPE_FDC, VIR_DOMAIN_DISK_BUS_FDC) < 0) return -1; if (virDomainDefAddDiskControllersForType(def, VIR_DOMAIN_CONTROLLER_TYPE_IDE, VIR_DOMAIN_DISK_BUS_IDE) < 0) return -1; if (virDomainDefAddDiskControllersForType(def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, VIR_DOMAIN_DISK_BUS_SATA) < 0) return -1; if (virDomainDefMaybeAddVirtioSerialController(def) < 0) return -1; if (virDomainDefMaybeAddSmartcardController(def) < 0) return -1; if (virDomainDefMaybeAddHostdevSCSIcontroller(def) < 0) return -1; return 0; } static int virDomainDefAddImplicitVideo(virDomainDefPtr def, virDomainXMLOptionPtr xmlopt) { int ret = -1; virDomainVideoDefPtr video = NULL; /* For backwards compatibility, if no