We currently have five drivers which handle the domain XML containing duplicated parsers and formatters for the XML with varying degrees of buginess, and often very similar structs. This patch introduces a new general purpose internal API for parsing and formatting network XML, and representing it as a series of structs. This code is derived from the current equivalent code in the QEMU driver for domains, but with alot of extra config parsing added for stuff needed by the Xen drivers. I have not yet added the extra bits needed by the container based drivers, LXC and OpenVZ, but don't anticipate any problems in this regard. Again the naming conventions I'm adopting in this patch follow those in the storage and network drivers: - virDomainPtr - the public opaque object in libvirt.h - virDomainObjPtr - the primary internal object for domain state - virDomainDefPtr - the configuration data for a domain A virDomainObjPtr contains a reference to one or two virDomainDefPtr objects - the current live config, and potentially a secondary inactive config which will become live at the next restart. The structs are defined in domain_conf.h, along with a bunch of APIs for dealing with them. These APIs are the same as similarly named ones from the current qemu driver, but I'll go over them again for a reminder: virDomainObjPtr virDomainFindByUUID(const virDomainObjPtr doms, const unsigned char *uuid); virDomainObjPtr virDomainFindByName(const virDomainObjPtr doms, const char *name); virDomainObjPtr virDomainFindByID(const virDomainObjPtr doms, const char *id); Allow lookup of a virDomainObjPtr object based on its name, ID or UUID, as typically obtained from the public virDomainPtr object. The 'doms' parameter to both of thse is a linked list of domains which are currently known to the driver using this API. Not all drivers will use these APIs - the Xen driver will just query XenD - these are only for drivers which maintain state internally with the libvirt daemon. void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def); void virDomainInputDefFree(virDomainInputDefPtr def); void virDomainDiskDefFree(virDomainDiskDefPtr def); void virDomainNetDefFree(virDomainNetDefPtr def); void virDomainChrDefFree(virDomainChrDefPtr def); void virDomainSoundDefFree(virDomainSoundDefPtr def); void virDomainDefFree(virDomainDefPtr def); void virDomainObjFree(virDomainObjPtr net); Convenience APIs to totally free the memory associated with these objects. virDomainObjPtr virDomainAssignDef(virConnectPtr conn, virDomainObjPtr *doms, const virDomainDefPtr def); Given a virDomainDefPtr object, it'll search for a pre-existing virDomainObjPtr object with matching config. If one is found, its config will be updated, otherwise a new object will be allocated. void virDomainRemoveInactive(virDomainObjPtr *doms, const virDomainObjPtr dom); Convenience for removing and free'ing a virDomainObjPtr object in the current list of active domains. virDomainDefPtr virDomainDefParseString(virConnectPtr conn, virCapsPtr caps, const char *xmlStr); virDomainDefPtr virDomainDefParseFile(virConnectPtr conn, virCapsPtr caps, const char *filename); virDomainDefPtr virDomainDefParseNode(virConnectPtr conn, virCapsPtr caps, xmlDocPtr doc, xmlNodePtr root); Based on DV's suggestion the parsing methods have changed slightly so instead of only allowing a string to be parsed, we can directly parse a file, or a XML node object. This is a little more advanced than the network parser because the various hypervisor drivers have slightly varying capabilities. So we pass a virCapsPtr object in so the parser can validate against the actual driver's capabilities. I'd like to extend the capabilities format to cover things like the boot parameters supported - eg PXE vs kernel+initrd vs bootloader vs BIOS, so we can further validate at time of parsing, instead of time of use. char *virDomainDefFormat(virConnectPtr conn, const virDomainDefPtr def, int secure); Given a virDomainDefPtr object, generate a XML document describing the domain. The secure flag controls whether passwords are included in the XML output. int virDomainCpuSetParse(virConnectPtr conn, const char **str, char sep, char *cpuset, int maxcpu); char *virDomainCpuSetFormat(virConnectPtr conn, char *cpuset, int maxcpu); These are just the APIs for dealing with CPU ranges moved from the xml.c file so they are in the expected place. A future patch will remove them from xml.c when the Xen driver is ported to this API. VIR_ENUM_DECL(virDomainVirt) VIR_ENUM_DECL(virDomainBoot) VIR_ENUM_DECL(virDomainFeature) VIR_ENUM_DECL(virDomainLifecycle) VIR_ENUM_DECL(virDomainDisk) VIR_ENUM_DECL(virDomainDiskDevice) VIR_ENUM_DECL(virDomainDiskBus) VIR_ENUM_DECL(virDomainNet) VIR_ENUM_DECL(virDomainChr) VIR_ENUM_DECL(virDomainSoundModel) VIR_ENUM_DECL(virDomainInput) VIR_ENUM_DECL(virDomainInputBus) VIR_ENUM_DECL(virDomainGraphics) This provides prototypes for all the enumerations useful to drivers using this API and struct definitions. For drivers which no other persistent storage native to their virt type, there are helper methods for loading and saving the XML configs to files. This is primarily for QEMU and LXC drivers int virDomainSaveConfig(virConnectPtr conn, const char *configDir, const char *autostartDir, virDomainObjPtr dom); virDomainObjPtr virDomainLoadConfig(virConnectPtr conn, virCapsPtr caps, virDomainObjPtr *doms, const char *configDir, const char *autostartDir, const char *file); int virDomainLoadAllConfigs(virConnectPtr conn, virCapsPtr caps, virDomainObjPtr *doms, const char *configDir, const char *autostartDir); int virDomainDeleteConfig(virConnectPtr conn, virDomainObjPtr dom); As a mentioned earlier, the impl of these APIs is just copied from the QEMU driver, but instead of using pre-declared char[PATH_MAX] fields, we allocate memory for strings as required. This dramatically reduces the memory needs of the QEMU driver (when ported to this API). It also switches to use the xml.c convenience routines instead of the regular libxml2 APIs, and in doing so I added a couple more convenience routines. There is no test suite explicitly against these parsers because when the QEMU and Xen drivers are ported to this API their existing test suites exercise nearly all the code. b/src/domain_conf.c | 2884 ++++++++++++++++++++++++++++++++++++++++++++ b/src/domain_conf.h | 489 +++++++ include/libvirt/virterror.h | 1 po/POTFILES.in | 1 src/Makefile.am | 1 src/util.h | 2 src/virterror.c | 3 src/xml.c | 18 src/xml.h | 10 9 files changed, 3403 insertions(+), 6 deletions(-) Daniel diff -r a77ea50ba693 include/libvirt/virterror.h --- a/include/libvirt/virterror.h Tue Jul 08 15:26:26 2008 +0100 +++ b/include/libvirt/virterror.h Tue Jul 08 15:26:51 2008 +0100 @@ -57,6 +57,7 @@ VIR_FROM_LXC, /* Error from Linux Container driver */ VIR_FROM_STORAGE, /* Error from storage driver */ VIR_FROM_NETWORK, /* Error from network config */ + VIR_FROM_DOMAIN, /* Error from domain config */ } virErrorDomain; diff -r a77ea50ba693 po/POTFILES.in --- a/po/POTFILES.in Tue Jul 08 15:26:26 2008 +0100 +++ b/po/POTFILES.in Tue Jul 08 15:26:51 2008 +0100 @@ -3,6 +3,7 @@ qemud/remote.c src/conf.c src/console.c +src/domain_conf.c src/hash.c src/iptables.c src/libvirt.c diff -r a77ea50ba693 src/Makefile.am --- a/src/Makefile.am Tue Jul 08 15:26:26 2008 +0100 +++ b/src/Makefile.am Tue Jul 08 15:26:51 2008 +0100 @@ -39,6 +39,7 @@ test.c test.h \ buf.c buf.h \ qparams.c qparams.h \ + domain_conf.c domain_conf.h \ capabilities.c capabilities.h \ xml.c xml.h \ event.c event.h \ diff -r a77ea50ba693 src/domain_conf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/domain_conf.c Tue Jul 08 15:26:51 2008 +0100 @@ -0,0 +1,2884 @@ +/* + * domain_conf.c: domain XML processing + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#include <config.h> + +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> + +#include "internal.h" + +#include "domain_conf.h" +#include "memory.h" +#include "verify.h" +#include "xml.h" +#include "uuid.h" +#include "util.h" +#include "buf.h" +#include "c-ctype.h" + +VIR_ENUM_IMPL(virDomainVirt, VIR_DOMAIN_VIRT_LAST, + "qemu", + "kqemu", + "kvm", + "xen", + "lxc", + "uml", + "openvz", + "vserver", + "ldom", + "test", + "vmware", + "hyperv") + +VIR_ENUM_IMPL(virDomainBoot, VIR_DOMAIN_BOOT_LAST, + "fd", + "cdrom", + "hd", + "network") + +VIR_ENUM_IMPL(virDomainFeature, VIR_DOMAIN_FEATURE_LAST, + "acpi", + "apic", + "pae") + +VIR_ENUM_IMPL(virDomainLifecycle, VIR_DOMAIN_LIFECYCLE_LAST, + "destroy", + "restart", + "rename-restart", + "preserve") + +VIR_ENUM_IMPL(virDomainDisk, VIR_DOMAIN_DISK_TYPE_LAST, + "block", + "file") + +VIR_ENUM_IMPL(virDomainDiskDevice, VIR_DOMAIN_DISK_DEVICE_LAST, + "disk", + "cdrom", + "floppy") + +VIR_ENUM_IMPL(virDomainDiskBus, VIR_DOMAIN_DISK_BUS_LAST, + "ide", + "fdc", + "scsi", + "virtio", + "xen") + +VIR_ENUM_IMPL(virDomainNet, VIR_DOMAIN_NET_TYPE_LAST, + "user", + "ethernet", + "server", + "client", + "mcast", + "network", + "bridge") + +VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST, + "null", + "vc", + "pty", + "dev", + "file", + "pipe", + "stdio", + "udp", + "tcp", + "unix") + +VIR_ENUM_IMPL(virDomainSoundModel, VIR_DOMAIN_SOUND_MODEL_LAST, + "sb16", + "es1370", + "pcspk"); + +VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST, + "mouse", + "tablet") + +VIR_ENUM_IMPL(virDomainInputBus, VIR_DOMAIN_INPUT_BUS_LAST, + "ps2", + "usb", + "xen") + +VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST, + "sdl", + "vnc") + + +static void virDomainReportError(virConnectPtr conn, + int code, const char *fmt, ...) +{ + va_list args; + char errorMessage[1024]; + const char *virerr; + + if (fmt) { + va_start(args, fmt); + vsnprintf(errorMessage, sizeof(errorMessage)-1, fmt, args); + va_end(args); + } else { + errorMessage[0] = '\0'; + } + + virerr = __virErrorMsg(code, (errorMessage[0] ? errorMessage : NULL)); + __virRaiseError(conn, NULL, NULL, VIR_FROM_DOMAIN, code, VIR_ERR_ERROR, + virerr, errorMessage, NULL, -1, -1, virerr, errorMessage); +} + + +virDomainObjPtr virDomainFindByID(const virDomainObjPtr doms, + int id) +{ + virDomainObjPtr dom = doms; + while (dom) { + if (virDomainIsActive(dom) && dom->def->id == id) + return dom; + dom = dom->next; + } + + return NULL; +} + + +virDomainObjPtr virDomainFindByUUID(const virDomainObjPtr doms, + const unsigned char *uuid) +{ + virDomainObjPtr dom = doms; + + while (dom) { + if (!memcmp(dom->def->uuid, uuid, VIR_UUID_BUFLEN)) + return dom; + dom = dom->next; + } + + return NULL; +} + +virDomainObjPtr virDomainFindByName(const virDomainObjPtr doms, + const char *name) +{ + virDomainObjPtr dom = doms; + + while (dom) { + if (STREQ(dom->def->name, name)) + return dom; + dom = dom->next; + } + + return NULL; +} + +void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) +{ + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + VIR_FREE(def->data.vnc.listenAddr); + VIR_FREE(def->data.vnc.keymap); + VIR_FREE(def->data.vnc.passwd); + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + VIR_FREE(def->data.sdl.display); + VIR_FREE(def->data.sdl.xauth); + break; + } + + VIR_FREE(def); +} + +void virDomainInputDefFree(virDomainInputDefPtr def) +{ + if (!def) + return; + + virDomainInputDefFree(def->next); + VIR_FREE(def); +} + +void virDomainDiskDefFree(virDomainDiskDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->src); + VIR_FREE(def->dst); + VIR_FREE(def->driverName); + VIR_FREE(def->driverType); + + virDomainDiskDefFree(def->next); + VIR_FREE(def); +} + +void virDomainNetDefFree(virDomainNetDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->model); + + switch (def->type) { + case VIR_DOMAIN_NET_TYPE_ETHERNET: + VIR_FREE(def->data.ethernet.dev); + VIR_FREE(def->data.ethernet.script); + VIR_FREE(def->data.ethernet.ipaddr); + break; + + case VIR_DOMAIN_NET_TYPE_SERVER: + case VIR_DOMAIN_NET_TYPE_CLIENT: + case VIR_DOMAIN_NET_TYPE_MCAST: + VIR_FREE(def->data.socket.address); + break; + + case VIR_DOMAIN_NET_TYPE_NETWORK: + VIR_FREE(def->data.network.name); + break; + + case VIR_DOMAIN_NET_TYPE_BRIDGE: + VIR_FREE(def->data.bridge.brname); + break; + } + + VIR_FREE(def->ifname); + virDomainNetDefFree(def->next); + VIR_FREE(def); +} + +void virDomainChrDefFree(virDomainChrDefPtr def) +{ + if (!def) + return; + + 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_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; + } + + virDomainChrDefFree(def->next); + VIR_FREE(def); +} + +void virDomainSoundDefFree(virDomainSoundDefPtr def) +{ + if (!def) + return; + + virDomainSoundDefFree(def->next); + VIR_FREE(def); +} + +void virDomainDeviceDefFree(virDomainDeviceDefPtr def) +{ + if (!def) + return; + + switch (def->type) { + case VIR_DOMAIN_DEVICE_DISK: + virDomainDiskDefFree(def->data.disk); + 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; + } + + VIR_FREE(def); +} + +void virDomainDefFree(virDomainDefPtr def) +{ + if (!def) + return; + + virDomainGraphicsDefFree(def->graphics); + virDomainInputDefFree(def->inputs); + virDomainDiskDefFree(def->disks); + virDomainNetDefFree(def->nets); + virDomainChrDefFree(def->serials); + virDomainChrDefFree(def->parallels); + virDomainChrDefFree(def->console); + virDomainSoundDefFree(def->sounds); + + + VIR_FREE(def->os.type); + VIR_FREE(def->os.arch); + VIR_FREE(def->os.machine); + VIR_FREE(def->os.kernel); + VIR_FREE(def->os.initrd); + VIR_FREE(def->os.cmdline); + VIR_FREE(def->os.root); + VIR_FREE(def->os.loader); + VIR_FREE(def->os.bootloader); + VIR_FREE(def->os.bootloaderArgs); + + VIR_FREE(def->name); + VIR_FREE(def->cpumask); + VIR_FREE(def->emulator); + + VIR_FREE(def); +} + +void virDomainObjFree(virDomainObjPtr dom) +{ + if (!dom) + return; + + virDomainDefFree(dom->def); + virDomainDefFree(dom->newDef); + + VIR_FREE(dom->vcpupids); + VIR_FREE(dom->configFile); + VIR_FREE(dom->autostartLink); + + VIR_FREE(dom); +} + +virDomainObjPtr virDomainAssignDef(virConnectPtr conn, + virDomainObjPtr *doms, + const virDomainDefPtr def) +{ + virDomainObjPtr domain; + + if ((domain = virDomainFindByName(*doms, def->name))) { + if (!virDomainIsActive(domain)) { + virDomainDefFree(domain->def); + domain->def = def; + } else { + if (domain->newDef) + virDomainDefFree(domain->newDef); + domain->newDef = def; + } + + return domain; + } + + if (VIR_ALLOC(domain) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + + domain->def = def; + domain->next = *doms; + + *doms = domain; + + return domain; +} + +void virDomainRemoveInactive(virDomainObjPtr *doms, + virDomainObjPtr dom) +{ + virDomainObjPtr prev = NULL; + virDomainObjPtr curr = *doms; + + while (curr && + curr != dom) { + prev = curr; + curr = curr->next; + } + + if (curr) { + if (prev) + prev->next = curr->next; + else + *doms = curr->next; + } + + virDomainObjFree(dom); +} + +static int virDomainDiskCompare(virDomainDiskDefPtr a, + virDomainDiskDefPtr b) { + if (a->bus == b->bus) + return virDiskNameToIndex(a->dst) - virDiskNameToIndex(b->dst); + else + return a->bus - b->bus; +} + + +/* Parse the XML definition for a disk + * @param node XML nodeset to parse for disk definition + */ +static virDomainDiskDefPtr +virDomainDiskDefParseXML(virConnectPtr conn, + xmlNodePtr node) { + virDomainDiskDefPtr def; + xmlNodePtr cur; + char *type = NULL; + char *device = NULL; + char *driverName = NULL; + char *driverType = NULL; + char *source = NULL; + char *target = NULL; + char *bus = NULL; + + if (VIR_ALLOC(def) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + + type = virXMLPropString(node, "type"); + if (type) { + if ((def->type = virDomainDiskTypeFromString(type)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown disk type '%s'"), type); + goto error; + } + } else { + def->type = VIR_DOMAIN_DISK_TYPE_FILE; + } + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if ((source == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "source"))) { + + if (def->type == VIR_DOMAIN_DISK_TYPE_FILE) + source = virXMLPropString(cur, "file"); + else + source = virXMLPropString(cur, "dev"); + } else if ((target == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "target"))) { + target = virXMLPropString(cur, "dev"); + bus = virXMLPropString(cur, "bus"); + } else if ((driverName == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "driver"))) { + driverName = virXMLPropString(cur, "name"); + driverType = virXMLPropString(cur, "type"); + } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) { + def->readonly = 1; + } else if (xmlStrEqual(cur->name, BAD_CAST "shareable")) { + def->shared = 1; + } + } + cur = cur->next; + } + + device = virXMLPropString(node, "device"); + if (device) { + if ((def->device = virDomainDiskDeviceTypeFromString(device)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown disk device '%s'"), device); + goto error; + } + } else { + def->device = VIR_DOMAIN_DISK_DEVICE_DISK; + } + + /* Only CDROM and Floppy devices are allowed missing source path + * to indicate no media present */ + if (source == NULL && + def->device != VIR_DOMAIN_DISK_DEVICE_CDROM && + def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY) { + virDomainReportError(conn, VIR_ERR_NO_SOURCE, + target ? "%s" : NULL, target); + goto error; + } + + if (target == NULL) { + virDomainReportError(conn, VIR_ERR_NO_TARGET, + source ? "%s" : NULL, source); + goto error; + } + + if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY && + !STRPREFIX(target, "fd")) { + virDomainReportError(conn, 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->readonly = 1; + + if (def->device == VIR_DOMAIN_DISK_DEVICE_DISK && + !STRPREFIX((const char *)target, "hd") && + !STRPREFIX((const char *)target, "sd") && + !STRPREFIX((const char *)target, "vd") && + !STRPREFIX((const char *)target, "xvd")) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Invalid harddisk device name: %s"), target); + goto error; + } + + if (bus) { + if ((def->bus = virDomainDiskBusTypeFromString(bus)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("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 (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 + def->bus = VIR_DOMAIN_DISK_BUS_IDE; + } + } + + if (def->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY && + def->bus != VIR_DOMAIN_DISK_BUS_FDC) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Invalid bus type '%s' for floppy disk"), bus); + goto error; + } + if (def->device != VIR_DOMAIN_DISK_DEVICE_FLOPPY && + def->bus == VIR_DOMAIN_DISK_BUS_FDC) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Invalid bus type '%s' for disk"), bus); + goto error; + } + + def->src = source; + source = NULL; + def->dst = target; + target = NULL; + def->driverName = driverName; + driverName = NULL; + def->driverType = driverType; + driverType = NULL; + +cleanup: + VIR_FREE(bus); + VIR_FREE(type); + VIR_FREE(target); + VIR_FREE(source); + VIR_FREE(device); + VIR_FREE(driverType); + VIR_FREE(driverName); + + return def; + + error: + virDomainDiskDefFree(def); + def = NULL; + goto cleanup; +} + + +static void virDomainNetRandomMAC(virDomainNetDefPtr def) { + /* XXX there different vendor prefixes per hypervisor */ + def->mac[0] = 0x52; + def->mac[1] = 0x54; + def->mac[2] = 0x00; + def->mac[3] = 1 + (int)(256*(rand()/(RAND_MAX+1.0))); + def->mac[4] = 1 + (int)(256*(rand()/(RAND_MAX+1.0))); + def->mac[5] = 1 + (int)(256*(rand()/(RAND_MAX+1.0))); +} + + +/* 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(virConnectPtr conn, + xmlNodePtr node) { + virDomainNetDefPtr def; + xmlNodePtr cur; + char *macaddr = NULL; + char *type = NULL; + char *network = NULL; + char *bridge = NULL; + char *dev = NULL; + char *ifname = NULL; + char *script = NULL; + char *address = NULL; + char *port = NULL; + char *model = NULL; + + if (VIR_ALLOC(def) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + + type = virXMLPropString(node, "type"); + if (type != NULL) { + if ((def->type = virDomainNetTypeFromString(type)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown interface type '%s'"), type); + goto error; + } + } else { + def->type = VIR_DOMAIN_NET_TYPE_USER; + } + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if ((macaddr == NULL) && + (xmlStrEqual(cur->name, BAD_CAST "mac"))) { + macaddr = virXMLPropString(cur, "address"); + } else if ((network == NULL) && + (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) && + (xmlStrEqual(cur->name, BAD_CAST "source"))) { + network = virXMLPropString(cur, "network"); + } else if ((network == NULL) && + (def->type == VIR_DOMAIN_NET_TYPE_BRIDGE) && + (xmlStrEqual(cur->name, BAD_CAST "source"))) { + bridge = virXMLPropString(cur, "bridge"); + } else if ((dev == NULL) && + (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET) && + xmlStrEqual(cur->name, BAD_CAST "source")) { + dev = virXMLPropString(cur, "dev"); + } else if ((network == NULL) && + ((def->type == VIR_DOMAIN_NET_TYPE_SERVER) || + (def->type == VIR_DOMAIN_NET_TYPE_CLIENT) || + (def->type == VIR_DOMAIN_NET_TYPE_MCAST)) && + (xmlStrEqual(cur->name, BAD_CAST "source"))) { + address = virXMLPropString(cur, "address"); + port = virXMLPropString(cur, "port"); + } else if ((address == NULL) && + (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET) && + (xmlStrEqual(cur->name, BAD_CAST "ip"))) { + address = virXMLPropString(cur, "address"); + } else if ((ifname == NULL) && + xmlStrEqual(cur->name, BAD_CAST "target")) { + ifname = virXMLPropString(cur, "dev"); + if (STRPREFIX((const char*)ifname, "vnet")) { + /* An auto-generated target name, blank it out */ + VIR_FREE(ifname); + ifname = NULL; + } + } else if ((script == NULL) && + (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET) && + xmlStrEqual(cur->name, BAD_CAST "script")) { + script = virXMLPropString(cur, "path"); + } else if (xmlStrEqual (cur->name, BAD_CAST "model")) { + model = virXMLPropString(cur, "type"); + } + } + cur = cur->next; + } + + if (macaddr) { + unsigned int mac[6]; + sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x", + (unsigned int*)&mac[0], + (unsigned int*)&mac[1], + (unsigned int*)&mac[2], + (unsigned int*)&mac[3], + (unsigned int*)&mac[4], + (unsigned int*)&mac[5]); + def->mac[0] = mac[0]; + def->mac[1] = mac[1]; + def->mac[2] = mac[2]; + def->mac[3] = mac[3]; + def->mac[4] = mac[4]; + def->mac[5] = mac[5]; + } else { + virDomainNetRandomMAC(def); + } + + switch (def->type) { + case VIR_DOMAIN_NET_TYPE_NETWORK: + if (network == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("No <source> 'network' attribute specified with <interface type='network'/>")); + goto error; + } + def->data.network.name = network; + network = NULL; + break; + + case VIR_DOMAIN_NET_TYPE_ETHERNET: + + if (script != NULL) { + def->data.ethernet.script = script; + script = NULL; + } + if (dev != NULL) { + def->data.ethernet.dev = dev; + dev = NULL; + } + if (address != NULL) { + def->data.ethernet.ipaddr = address; + address = NULL; + } + break; + + case VIR_DOMAIN_NET_TYPE_BRIDGE: + if (bridge == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("No <source> 'dev' attribute specified with <interface type='bridge'/>")); + goto error; + } + def->data.bridge.brname = bridge; + bridge = NULL; + break; + + case VIR_DOMAIN_NET_TYPE_CLIENT: + case VIR_DOMAIN_NET_TYPE_SERVER: + case VIR_DOMAIN_NET_TYPE_MCAST: + if (port == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("No <source> 'port' attribute specified with socket interface")); + goto error; + } + if (virStrToLong_i(port, NULL, 10, &def->data.socket.port) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Cannot parse <source> '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) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("No <source> 'address' attribute specified with socket interface")); + goto error; + } + } else { + def->data.socket.address = address; + address = NULL; + } + } + + if (ifname != NULL) { + def->ifname = ifname; + ifname = NULL; + } + + /* NIC model (see -net nic,model=?). We only check that it looks + * reasonable, not that it is a supported NIC type. FWIW kvm + * supports these types as of April 2008: + * i82551 i82557b i82559er ne2k_pci pcnet rtl8139 e1000 virtio + */ + if (model != NULL) { + int i; + for (i = 0 ; i < strlen(model) ; i++) { + int char_ok = c_isalnum(model[i]) || model[i] == '_'; + if (!char_ok) { + virDomainReportError (conn, VIR_ERR_INVALID_ARG, "%s", + _("Model name contains invalid characters")); + goto error; + } + } + def->model = model; + model = NULL; + } + +cleanup: + VIR_FREE(macaddr); + VIR_FREE(network); + VIR_FREE(address); + VIR_FREE(port); + VIR_FREE(ifname); + VIR_FREE(dev); + VIR_FREE(script); + VIR_FREE(bridge); + VIR_FREE(model); + VIR_FREE(type); + + return def; + +error: + virDomainNetDefFree(def); + def = NULL; + goto cleanup; +} + + +/* 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 + * + * <serial type="pty"> + * <source path="/dev/pts/3"/> + * <target port="1"/> + * </serial> + * + * <serial type="dev"> + * <source path="/dev/ttyS0"/> + * <target port="1"/> + * </serial> + * + * <serial type="tcp"> + * <source mode="connect" host="0.0.0.0" service="2445"/> + * <target port="1"/> + * </serial> + * + * <serial type="tcp"> + * <source mode="bind" host="0.0.0.0" service="2445"/> + * <target port="1"/> + * </serial> + * + * <serial type="udp"> + * <source mode="bind" host="0.0.0.0" service="2445"/> + * <source mode="connect" host="0.0.0.0" service="2445"/> + * <target port="1"/> + * </serial> + * + * <serial type="unix"> + * <source mode="bind" path="/tmp/foo"/> + * <target port="1"/> + * </serial> + * + */ +static virDomainChrDefPtr +virDomainChrDefParseXML(virConnectPtr conn, + xmlNodePtr node) { + xmlNodePtr cur; + char *type = NULL; + char *bindHost = NULL; + char *bindService = NULL; + char *connectHost = NULL; + char *connectService = NULL; + char *path = NULL; + char *mode = NULL; + char *protocol = NULL; + virDomainChrDefPtr def; + + if (VIR_ALLOC(def) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + + def->type = VIR_DOMAIN_CHR_TYPE_PTY; + type = virXMLPropString(node, "type"); + if (type != NULL) { + if (STREQ(type, "null")) + def->type = VIR_DOMAIN_CHR_TYPE_NULL; + else if (STREQ(type, "vc")) + def->type = VIR_DOMAIN_CHR_TYPE_VC; + else if (STREQ(type, "pty")) + def->type = VIR_DOMAIN_CHR_TYPE_PTY; + else if (STREQ(type, "dev")) + def->type = VIR_DOMAIN_CHR_TYPE_DEV; + else if (STREQ(type, "file")) + def->type = VIR_DOMAIN_CHR_TYPE_FILE; + else if (STREQ(type, "pipe")) + def->type = VIR_DOMAIN_CHR_TYPE_PIPE; + else if (STREQ(type, "stdio")) + def->type = VIR_DOMAIN_CHR_TYPE_STDIO; + else if (STREQ(type, "udp")) + def->type = VIR_DOMAIN_CHR_TYPE_UDP; + else if (STREQ(type, "tcp")) + def->type = VIR_DOMAIN_CHR_TYPE_TCP; + else if (STREQ(type, "unix")) + def->type = VIR_DOMAIN_CHR_TYPE_UNIX; + else + def->type = VIR_DOMAIN_CHR_TYPE_NULL; + } + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "source")) { + if (mode == NULL) + mode = virXMLPropString(cur, "mode"); + + 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: + case VIR_DOMAIN_CHR_TYPE_UNIX: + if (path == NULL) + path = virXMLPropString(cur, "path"); + + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + case VIR_DOMAIN_CHR_TYPE_TCP: + if (mode == NULL || + STREQ((const char *)mode, "connect")) { + + if (connectHost == NULL) + connectHost = virXMLPropString(cur, "host"); + if (connectService == NULL) + connectService = virXMLPropString(cur, "service"); + } else { + if (bindHost == NULL) + bindHost = virXMLPropString(cur, "host"); + if (bindService == NULL) + bindService = virXMLPropString(cur, "service"); + } + + if (def->type == VIR_DOMAIN_CHR_TYPE_UDP) { + VIR_FREE(mode); + mode = NULL; + } + } + } else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) { + if (protocol == NULL) + protocol = virXMLPropString(cur, "type"); + } + } + cur = cur->next; + } + + + switch (def->type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + /* Nada */ + break; + + case VIR_DOMAIN_CHR_TYPE_VC: + break; + + case VIR_DOMAIN_CHR_TYPE_PTY: + /* @path attribute is an output only property - pty is auto-allocted */ + break; + + case VIR_DOMAIN_CHR_TYPE_DEV: + case VIR_DOMAIN_CHR_TYPE_FILE: + case VIR_DOMAIN_CHR_TYPE_PIPE: + if (path == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source path attribute for char device")); + goto error; + } + + def->data.file.path = path; + path = NULL; + break; + + case VIR_DOMAIN_CHR_TYPE_STDIO: + /* Nada */ + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + if (mode == NULL || + STREQ(mode, "connect")) { + if (connectHost == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source host attribute for char device")); + goto error; + } + if (connectService == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source service attribute for char device")); + goto error; + } + + def->data.tcp.host = connectHost; + connectHost = NULL; + def->data.tcp.service = connectService; + connectService = NULL; + def->data.tcp.listen = 0; + } else { + if (bindHost == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source host attribute for char device")); + goto error; + } + if (bindService == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source service attribute for char device")); + goto error; + } + + def->data.tcp.host = bindHost; + bindHost = NULL; + def->data.tcp.service = bindService; + bindService = NULL; + def->data.tcp.listen = 1; + } + if (protocol != NULL && + STREQ(protocol, "telnet")) + def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; + else + def->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW; + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + if (connectService == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source service attribute for char device")); + goto error; + } + + def->data.udp.connectHost = connectHost; + connectHost = NULL; + def->data.udp.connectService = connectService; + connectService = NULL; + + def->data.udp.bindHost = bindHost; + bindHost = NULL; + def->data.udp.bindService = bindService; + bindService = NULL; + break; + + case VIR_DOMAIN_CHR_TYPE_UNIX: + if (path == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("Missing source path attribute for char device")); + goto error; + } + + if (mode != NULL && + STRNEQ(mode, "connect")) + def->data.nix.listen = 1; + else + def->data.nix.listen = 0; + + def->data.nix.path = path; + path = NULL; + break; + } + +cleanup: + VIR_FREE(mode); + VIR_FREE(protocol); + VIR_FREE(type); + VIR_FREE(bindHost); + VIR_FREE(bindService); + VIR_FREE(connectHost); + VIR_FREE(connectService); + VIR_FREE(path); + + return def; + +error: + virDomainChrDefFree(def); + def = NULL; + goto cleanup; +} + +/* Parse the XML definition for a network interface */ +static virDomainInputDefPtr +virDomainInputDefParseXML(virConnectPtr conn, + const char *ostype, + xmlNodePtr node) { + virDomainInputDefPtr def; + char *type = NULL; + char *bus = NULL; + + if (VIR_ALLOC(def) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + + type = virXMLPropString(node, "type"); + bus = virXMLPropString(node, "bus"); + + if (!type) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing input device type")); + goto error; + } + + if ((def->type = virDomainInputTypeFromString(type)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown input device type '%s'"), type); + goto error; + } + + if (bus) { + if ((def->bus = virDomainInputBusTypeFromString(bus)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown input bus type '%s'"), bus); + goto error; + } + + if (STREQ(ostype, "hvm")) { + if (def->bus == VIR_DOMAIN_INPUT_BUS_PS2 && /* Only allow mouse for ps2 */ + def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("ps2 bus does not support %s input device"), + type); + goto error; + } + if (def->bus == VIR_DOMAIN_INPUT_BUS_XEN) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported input bus %s"), + bus); + goto error; + } + } else { + if (def->bus != VIR_DOMAIN_INPUT_BUS_XEN) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unsupported input bus %s"), + bus); + } + if (def->type != VIR_DOMAIN_INPUT_TYPE_MOUSE) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("xen bus does not support %s input device"), + type); + goto error; + } + } + } else { + if (STREQ(ostype, "hvm")) { + if (def->type == VIR_DOMAIN_INPUT_TYPE_MOUSE) + def->bus = VIR_DOMAIN_INPUT_BUS_PS2; + else + def->bus = VIR_DOMAIN_INPUT_BUS_USB; + } else { + def->bus = VIR_DOMAIN_INPUT_BUS_XEN; + } + } + +cleanup: + VIR_FREE(type); + VIR_FREE(bus); + + return def; + +error: + virDomainInputDefFree(def); + def = NULL; + goto cleanup; +} + + +/* Parse the XML definition for a graphics device */ +static virDomainGraphicsDefPtr +virDomainGraphicsDefParseXML(virConnectPtr conn, + xmlNodePtr node) { + virDomainGraphicsDefPtr def; + char *type = NULL; + + if (VIR_ALLOC(def) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + + type = virXMLPropString(node, "type"); + + if (!type) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing graphics device type")); + goto error; + } + + if ((def->type = virDomainGraphicsTypeFromString(type)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown graphics device type '%s'"), type); + goto error; + } + + if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) { + char *port = virXMLPropString(node, "port"); + char *autoport; + + if (port) { + if (virStrToLong_i(port, NULL, 10, &def->data.vnc.port) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot parse vnc port %s"), port); + VIR_FREE(port); + goto error; + } + VIR_FREE(port); + /* Legacy compat syntax, used -1 for auto-port */ + if (def->data.vnc.port == -1) { + def->data.vnc.port = 0; + def->data.vnc.autoport = 1; + } + } else { + def->data.vnc.port = 0; + def->data.vnc.autoport = 1; + } + + if ((autoport = virXMLPropString(node, "autoport")) != NULL) { + if (STREQ(autoport, "yes")) { + def->data.vnc.port = 0; + def->data.vnc.autoport = 1; + } + VIR_FREE(autoport); + } + + def->data.vnc.listenAddr = virXMLPropString(node, "listen"); + def->data.vnc.passwd = virXMLPropString(node, "passwd"); + def->data.vnc.keymap = virXMLPropString(node, "keymap"); + } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { + def->data.sdl.xauth = virXMLPropString(node, "xauth"); + def->data.sdl.display = virXMLPropString(node, "display"); + } + +cleanup: + VIR_FREE(type); + + return def; + +error: + virDomainGraphicsDefFree(def); + def = NULL; + goto cleanup; +} + + +static virDomainSoundDefPtr +virDomainSoundDefParseXML(virConnectPtr conn, + const xmlNodePtr node) { + + char *model; + virDomainSoundDefPtr def; + + if (VIR_ALLOC(def) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + + model = virXMLPropString(node, "model"); + if ((def->model = virDomainSoundModelTypeFromString(model)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown sound model '%s'"), model); + goto error; + } + +cleanup: + VIR_FREE(model); + + return def; + +error: + virDomainSoundDefFree(def); + def = NULL; + goto cleanup; +} + + +static int virDomainLifecycleParseXML(virConnectPtr conn, + xmlXPathContextPtr ctxt, + const char *xpath, + int *val, + int defaultVal) +{ + char *tmp = virXPathString(xpath, ctxt); + if (tmp == NULL) { + *val = defaultVal; + } else { + *val = virDomainLifecycleTypeFromString(tmp); + if (*val < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown lifecycle action %s"), tmp); + VIR_FREE(tmp); + return -1; + } + VIR_FREE(tmp); + } + return 0; +} + + +virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn, + const virDomainDefPtr def, + const char *xmlStr) +{ + xmlDocPtr xml; + xmlNodePtr node; + virDomainDeviceDefPtr dev = NULL; + + if (!(xml = xmlReadDoc(BAD_CAST xmlStr, "device.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + virDomainReportError(conn, VIR_ERR_XML_ERROR, NULL); + goto error; + } + + node = xmlDocGetRootElement(xml); + if (node == NULL) { + virDomainReportError(conn, VIR_ERR_XML_ERROR, + "%s", _("missing root element")); + goto error; + } + + if (VIR_ALLOC(dev) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + + if (xmlStrEqual(node->name, BAD_CAST "disk")) { + dev->type = VIR_DOMAIN_DEVICE_DISK; + if (!(dev->data.disk = virDomainDiskDefParseXML(conn, node))) + goto error; + } else if (xmlStrEqual(node->name, BAD_CAST "interface")) { + dev->type = VIR_DOMAIN_DEVICE_NET; + if (!(dev->data.net = virDomainNetDefParseXML(conn, node))) + goto error; + } else if (xmlStrEqual(node->name, BAD_CAST "input")) { + dev->type = VIR_DOMAIN_DEVICE_DISK; + if (!(dev->data.input = virDomainInputDefParseXML(conn, def->os.type, node))) + goto error; + } else if (xmlStrEqual(node->name, BAD_CAST "sound")) { + dev->type = VIR_DOMAIN_DEVICE_SOUND; + if (!(dev->data.sound = virDomainSoundDefParseXML(conn, node))) + goto error; + } else { + virDomainReportError(conn, VIR_ERR_XML_ERROR, + "%s", _("unknown device type")); + goto error; + } + + xmlFreeDoc(xml); + + return dev; + + error: + xmlFreeDoc(xml); + VIR_FREE(dev); + return NULL; +} + + +static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn, + virCapsPtr caps, + xmlXPathContextPtr ctxt) +{ + xmlNodePtr *nodes = NULL, node = NULL; + char *tmp = NULL; + int i, n; + virDomainDefPtr def; + + if (VIR_ALLOC(def) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, + "%s", _("failed to allocate space for xmlXPathContext")); + return NULL; + } + def->id = -1; + + /* Find out what type of QEMU virtualization to use */ + if (!(tmp = virXPathString("string(./@type)", ctxt))) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing domain type attribute")); + goto error; + } + + if ((def->virtType = virDomainVirtTypeFromString(tmp)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("invalid domain type %s"), tmp); + goto error; + } + VIR_FREE(tmp); + + /* Extract domain name */ + if (!(def->name = virXPathString("string(./name[1])", ctxt))) { + virDomainReportError(conn, VIR_ERR_NO_NAME, NULL); + goto error; + } + + /* Extract domain uuid */ + tmp = virXPathString("string(./uuid[1])", ctxt); + if (!tmp) { + int err; + if ((err = virUUIDGenerate(def->uuid))) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to generate UUID: %s"), + strerror(err)); + goto error; + } + } else { + if (virUUIDParse(tmp, def->uuid) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("malformed uuid element")); + goto error; + } + VIR_FREE(tmp); + } + + /* Extract domain memory */ + if (virXPathULong("string(./memory[1])", ctxt, &def->maxmem) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing memory element")); + goto error; + } + + if (virXPathULong("string(./currentMemory[1])", ctxt, &def->memory) < 0) + def->memory = def->maxmem; + + if (virXPathULong("string(./vcpu[1])", ctxt, &def->vcpus) < 0) + def->vcpus = 1; + + tmp = virXPathString("string(./vcpu[1]/@cpuset)", ctxt); + if (tmp) { + char *set = tmp; + def->cpumasklen = VIR_DOMAIN_CPUMASK_LEN; + if (VIR_ALLOC_N(def->cpumask, def->cpumasklen) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + if (virDomainCpuSetParse(conn, (const char **)&set, + 0, def->cpumask, + def->cpumasklen) < 0) + goto error; + VIR_FREE(tmp); + } + + if ((n = virXPathNodeSet("./features/*", ctxt, &nodes)) > 0) { + for (i = 0 ; i < n ; i++) { + int val = virDomainFeatureTypeFromString((const char *)nodes[i]->name); + if (val < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected feature %s"), + nodes[i]->name); + goto error; + } + def->features |= (1 << val); + } + } + VIR_FREE(nodes); + + if (virDomainLifecycleParseXML(conn, ctxt, "string(./on_reboot[1])", + &def->onReboot, VIR_DOMAIN_LIFECYCLE_RESTART) < 0) + goto error; + + if (virDomainLifecycleParseXML(conn, ctxt, "string(./on_poweroff[1])", + &def->onPoweroff, VIR_DOMAIN_LIFECYCLE_DESTROY) < 0) + goto error; + + if (virDomainLifecycleParseXML(conn, ctxt, "string(./on_crash[1])", + &def->onCrash, VIR_DOMAIN_LIFECYCLE_DESTROY) < 0) + goto error; + + + tmp = virXPathString("string(./clock/@offset)", ctxt); + if (tmp && STREQ(tmp, "localtime")) + def->localtime = 1; + VIR_FREE(tmp); + + def->os.bootloader = virXPathString("string(./bootloader)", ctxt); + def->os.bootloaderArgs = virXPathString("string(./bootloader_args)", ctxt); + + def->os.type = virXPathString("string(./os/type[1])", ctxt); + if (!def->os.type) { + if (def->os.bootloader) { + def->os.type = strdup("xen"); + if (!def->os.type) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + } else { + virDomainReportError(conn, VIR_ERR_OS_TYPE, + "%s", _("no OS type")); + goto error; + } + } + /* + * HACK: For xen driver we previously used bogus 'linux' as the + * os type for paravirt, whereas capabilities declare it to + * be 'xen'. So we accept the former and convert + */ + if (STREQ(def->os.type, "linux") && + def->virtType == VIR_DOMAIN_VIRT_XEN) { + VIR_FREE(def->os.type); + if (!(def->os.type = strdup("xen"))) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + } + + if (!virCapabilitiesSupportsGuestOSType(caps, def->os.type)) { + virDomainReportError(conn, VIR_ERR_OS_TYPE, + "%s", def->os.type); + goto error; + } + + def->os.arch = virXPathString("string(./os/type[1]/@arch)", ctxt); + if (!def->os.arch) { + const char *defaultArch = virCapabilitiesDefaultGuestArch(caps, def->os.type); + if (defaultArch == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("no supported architecture for os type '%s'"), + def->os.type); + goto error; + } + if (!(def->os.arch = strdup(defaultArch))) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + } + + def->os.machine = virXPathString("string(./os/type[1]/@machine)", ctxt); + if (!def->os.machine) { + const char *defaultMachine = virCapabilitiesDefaultGuestMachine(caps, + def->os.type, + def->os.arch); + if (defaultMachine != NULL) { + if (!(def->os.machine = strdup(defaultMachine))) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + } + } + + if (!def->os.bootloader) { + 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.root = virXPathString("string(./os/root[1])", ctxt); + def->os.loader = virXPathString("string(./os/loader[1])", ctxt); + + /* analysis of the boot devices */ + if ((n = virXPathNodeSet("./os/boot", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract boot device")); + goto error; + } + for (i = 0 ; i < n && i < VIR_DOMAIN_BOOT_LAST ; i++) { + int val; + char *dev = virXMLPropString(nodes[i], "dev"); + if (!dev) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing boot device")); + goto error; + } + if ((val = virDomainBootTypeFromString(dev)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unknown boot device '%s'"), + dev); + VIR_FREE(dev); + goto error; + } + VIR_FREE(dev); + def->os.bootDevs[def->os.nBootDevs++] = val; + } + if (def->os.nBootDevs == 0) { + def->os.nBootDevs = 1; + def->os.bootDevs[0] = VIR_DOMAIN_BOOT_DISK; + } + VIR_FREE(nodes); + } + + def->emulator = virXPathString("string(./devices/emulator[1])", ctxt); + if (!def->emulator) { + const char *type = virDomainVirtTypeToString(def->virtType); + if (!type) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("unknown virt type")); + goto error; + } + const char *emulator = virCapabilitiesDefaultGuestEmulator(caps, + def->os.type, + def->os.arch, + type); + if (!emulator) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("unsupported guest type")); + goto error; + } + if (!(def->emulator = strdup(emulator))) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + } + + /* analysis of the disk devices */ + if ((n = virXPathNodeSet("./devices/disk", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract disk devices")); + goto error; + } + for (i = 0 ; i < n ; i++) { + virDomainDiskDefPtr disk = virDomainDiskDefParseXML(conn, + nodes[i]); + if (!disk) + goto error; + + /* Maintain list in sorted order according to target device name */ + if (def->disks == NULL) { + disk->next = def->disks; + def->disks = disk; + } else { + virDomainDiskDefPtr ptr = def->disks; + while (ptr) { + if (!ptr->next || virDomainDiskCompare(disk, ptr->next) < 0) { + disk->next = ptr->next; + ptr->next = disk; + break; + } + ptr = ptr->next; + } + } + } + VIR_FREE(nodes); + + /* analysis of the network devices */ + if ((n = virXPathNodeSet("./devices/interface", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract network devices")); + goto error; + } + for (i = n - 1 ; i >= 0 ; i--) { + virDomainNetDefPtr net = virDomainNetDefParseXML(conn, + nodes[i]); + if (!net) + goto error; + + net->next = def->nets; + def->nets = net; + } + VIR_FREE(nodes); + + + /* analysis of the character devices */ + if ((n = virXPathNodeSet("./devices/parallel", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract parallel devices")); + goto error; + } + for (i = n - 1 ; i >= 0 ; i--) { + virDomainChrDefPtr chr = virDomainChrDefParseXML(conn, + nodes[i]); + if (!chr) + goto error; + + chr->dstPort = i; + chr->next = def->parallels; + def->parallels = chr; + } + VIR_FREE(nodes); + + if ((n = virXPathNodeSet("./devices/serial", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract serial devices")); + goto error; + } + for (i = n - 1 ; i >= 0 ; i--) { + virDomainChrDefPtr chr = virDomainChrDefParseXML(conn, + nodes[i]); + if (!chr) + goto error; + + chr->dstPort = i; + chr->next = def->serials; + def->serials = chr; + } + VIR_FREE(nodes); + + /* + * If no serial devices were listed, then look for console + * devices which is the legacy syntax for the same thing + */ + if (def->serials == NULL) { + if ((node = virXPathNode("./devices/console[1]", ctxt)) != NULL) { + virDomainChrDefPtr chr = virDomainChrDefParseXML(conn, + node); + if (!chr) + goto error; + + chr->dstPort = 0; + /* + * For HVM console actually created a serial device + * while for non-HVM it was a parvirt console + */ + if (STREQ(def->os.type, "hvm")) { + chr->next = def->serials; + def->serials = chr; + } else { + def->console = chr; + } + } + } + + + /* analysis of the input devices */ + if ((n = virXPathNodeSet("./devices/input", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract input devices")); + goto error; + } + for (i = n - 1 ; i >= 0 ; i--) { + virDomainInputDefPtr input = virDomainInputDefParseXML(conn, + def->os.type, + nodes[i]); + if (!input) + goto error; + + + /* With QEMU / KVM / Xen graphics, mouse + PS/2 is implicit + * with graphics, so don't store it. + * XXX will this be true for other virt types ? */ + if ((STREQ(def->os.type, "hvm") && + input->bus == VIR_DOMAIN_INPUT_BUS_PS2 && + input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE) || + (STRNEQ(def->os.type, "hvm") && + input->bus == VIR_DOMAIN_INPUT_BUS_XEN && + input->type == VIR_DOMAIN_INPUT_TYPE_MOUSE)) { + virDomainInputDefFree(input); + continue; + } + + input->next = def->inputs; + def->inputs = input; + } + VIR_FREE(nodes); + + /* analysis of the input devices */ + if ((n = virXPathNodeSet("./devices/graphics", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract graphics devices")); + goto error; + } + if (n > 0) { + virDomainGraphicsDefPtr graphics = virDomainGraphicsDefParseXML(conn, + nodes[0]); + if (!graphics) + goto error; + + def->graphics = graphics; + } + VIR_FREE(nodes); + + /* If graphics are enabled, there's an implicit PS2 mouse */ + if (def->graphics != NULL) { + virDomainInputDefPtr input; + + if (VIR_ALLOC(input) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + if (STREQ(def->os.type, "hvm")) { + input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE; + input->bus = VIR_DOMAIN_INPUT_BUS_PS2; + } else { + input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE; + input->bus = VIR_DOMAIN_INPUT_BUS_XEN; + } + input->next = def->inputs; + def->inputs = input; + } + + + /* analysis of the sound devices */ + if ((n = virXPathNodeSet("./devices/sound", ctxt, &nodes)) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("cannot extract sound devices")); + goto error; + } + for (i = n - 1 ; i >= 0 ; i--) { + int collision = 0; + virDomainSoundDefPtr check; + virDomainSoundDefPtr sound = virDomainSoundDefParseXML(conn, + nodes[i]); + if (!sound) + goto error; + + /* Verify there's no duplicated sound card */ + check = def->sounds; + while (check) { + if (check->model == sound->model) + collision = 1; + check = check->next; + } + if (collision) { + virDomainSoundDefFree(sound); + continue; + } + + sound->next = def->sounds; + def->sounds = sound; + } + VIR_FREE(nodes); + + return def; + + error: + VIR_FREE(tmp); + VIR_FREE(nodes); + virDomainDefFree(def); + return NULL; +} + + +virDomainDefPtr virDomainDefParseString(virConnectPtr conn, + virCapsPtr caps, + const char *xmlStr) +{ + xmlDocPtr xml; + xmlNodePtr root; + virDomainDefPtr def = NULL; + + if (!(xml = xmlReadDoc(BAD_CAST xmlStr, "network.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + virDomainReportError(conn, VIR_ERR_XML_ERROR, NULL); + return NULL; + } + + if ((root = xmlDocGetRootElement(xml)) == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing root element")); + xmlFreeDoc(xml); + return NULL; + } + + def = virDomainDefParseNode(conn, caps, xml, root); + + xmlFreeDoc(xml); + return def; +} + +virDomainDefPtr virDomainDefParseFile(virConnectPtr conn, + virCapsPtr caps, + const char *filename) +{ + xmlDocPtr xml; + xmlNodePtr root; + virDomainDefPtr def = NULL; + + if (!(xml = xmlReadFile(filename, NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) { + virDomainReportError(conn, VIR_ERR_XML_ERROR, NULL); + return NULL; + } + + if ((root = xmlDocGetRootElement(xml)) == NULL) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("missing root element")); + xmlFreeDoc(xml); + return NULL; + } + + def = virDomainDefParseNode(conn, caps, xml, root); + + xmlFreeDoc(xml); + return def; +} + + +virDomainDefPtr virDomainDefParseNode(virConnectPtr conn, + virCapsPtr caps, + xmlDocPtr xml, + xmlNodePtr root) +{ + xmlXPathContextPtr ctxt = NULL; + virDomainDefPtr def = NULL; + + if (!xmlStrEqual(root->name, BAD_CAST "domain")) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + "%s", _("incorrect root element")); + goto cleanup; + } + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto cleanup; + } + + ctxt->node = root; + def = virDomainDefParseXML(conn, caps, ctxt); + +cleanup: + xmlXPathFreeContext(ctxt); + return def; +} + + +/************************************************************************ + * * + * Parser and converter for the CPUset strings used in libvirt * + * * + ************************************************************************/ +/** + * virDomainCpuNumberParse + * @str: pointer to the char pointer used + * @maxcpu: maximum CPU number allowed + * + * Parse a CPU number + * + * Returns the CPU number or -1 in case of error. @str will be + * updated to skip the number. + */ +static int +virDomainCpuNumberParse(const char **str, int maxcpu) +{ + int ret = 0; + const char *cur = *str; + + if (!c_isdigit(*cur)) + return (-1); + + while (c_isdigit(*cur)) { + ret = ret * 10 + (*cur - '0'); + if (ret >= maxcpu) + return (-1); + cur++; + } + *str = cur; + return (ret); +} + +/** + * virDomainCpuSetFormat: + * @conn: connection + * @cpuset: pointer to a char array for the CPU set + * @maxcpu: number of elements available in @cpuset + * + * Serialize the cpuset to a string + * + * Returns the new string NULL in case of error. The string need to be + * freed by the caller. + */ +char * +virDomainCpuSetFormat(virConnectPtr conn, char *cpuset, int maxcpu) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + int start, cur; + int first = 1; + + if ((cpuset == NULL) || (maxcpu <= 0) || (maxcpu > 100000)) + return (NULL); + + cur = 0; + start = -1; + while (cur < maxcpu) { + if (cpuset[cur]) { + if (start == -1) + start = cur; + } else if (start != -1) { + if (!first) + virBufferAddLit(&buf, ","); + else + first = 0; + if (cur == start + 1) + virBufferVSprintf(&buf, "%d", start); + else + virBufferVSprintf(&buf, "%d-%d", start, cur - 1); + start = -1; + } + cur++; + } + if (start != -1) { + if (!first) + virBufferAddLit(&buf, ","); + if (maxcpu == start + 1) + virBufferVSprintf(&buf, "%d", start); + else + virBufferVSprintf(&buf, "%d-%d", start, maxcpu - 1); + } + + if (virBufferError(&buf)) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + return NULL; + } + + return virBufferContentAndReset(&buf); +} + +/** + * virDomainCpuSetParse: + * @conn: connection + * @str: pointer to a CPU set string pointer + * @sep: potential character used to mark the end of string if not 0 + * @cpuset: pointer to a char array for the CPU set + * @maxcpu: number of elements available in @cpuset + * + * Parse the cpu set, it will set the value for enabled CPUs in the @cpuset + * to 1, and 0 otherwise. The syntax allows coma separated entries each + * can be either a CPU number, ^N to unset that CPU or N-M for ranges. + * + * Returns the number of CPU found in that set, or -1 in case of error. + * @cpuset is modified accordingly to the value parsed. + * @str is updated to the end of the part parsed + */ +int +virDomainCpuSetParse(virConnectPtr conn, const char **str, char sep, + char *cpuset, int maxcpu) +{ + const char *cur; + int ret = 0; + int i, start, last; + int neg = 0; + + if ((str == NULL) || (cpuset == NULL) || (maxcpu <= 0) || + (maxcpu > 100000)) + return (-1); + + cur = *str; + virSkipSpaces(&cur); + if (*cur == 0) + goto parse_error; + + /* initialize cpumap to all 0s */ + for (i = 0; i < maxcpu; i++) + cpuset[i] = 0; + ret = 0; + + while ((*cur != 0) && (*cur != sep)) { + /* + * 3 constructs are allowed: + * - N : a single CPU number + * - N-M : a range of CPU numbers with N < M + * - ^N : remove a single CPU number from the current set + */ + if (*cur == '^') { + cur++; + neg = 1; + } + + if (!c_isdigit(*cur)) + goto parse_error; + start = virDomainCpuNumberParse(&cur, maxcpu); + if (start < 0) + goto parse_error; + virSkipSpaces(&cur); + if ((*cur == ',') || (*cur == 0) || (*cur == sep)) { + if (neg) { + if (cpuset[start] == 1) { + cpuset[start] = 0; + ret--; + } + } else { + if (cpuset[start] == 0) { + cpuset[start] = 1; + ret++; + } + } + } else if (*cur == '-') { + if (neg) + goto parse_error; + cur++; + virSkipSpaces(&cur); + last = virDomainCpuNumberParse(&cur, maxcpu); + if (last < start) + goto parse_error; + for (i = start; i <= last; i++) { + if (cpuset[i] == 0) { + cpuset[i] = 1; + ret++; + } + } + virSkipSpaces(&cur); + } + if (*cur == ',') { + cur++; + virSkipSpaces(&cur); + neg = 0; + } else if ((*cur == 0) || (*cur == sep)) { + break; + } else + goto parse_error; + } + *str = cur; + return (ret); + + parse_error: + virDomainReportError(conn, VIR_ERR_XEN_CALL, + "%s", _("topology cpuset syntax error")); + return (-1); +} + + +static int +virDomainLifecycleDefFormat(virConnectPtr conn, + virBufferPtr buf, + int type, + const char *name) +{ + const char *typeStr = virDomainLifecycleTypeToString(type); + if (!typeStr) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected lifecycle type %d"), type); + return -1; + } + + virBufferVSprintf(buf, " <%s>%s</%s>\n", name, typeStr, name); + + return 0; +} + + +static int +virDomainDiskDefFormat(virConnectPtr conn, + virBufferPtr buf, + virDomainDiskDefPtr def) +{ + const char *type = virDomainDiskTypeToString(def->type); + const char *device = virDomainDiskDeviceTypeToString(def->device); + const char *bus = virDomainDiskBusTypeToString(def->bus); + + if (!type) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected disk type %d"), def->type); + return -1; + } + if (!device) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected disk device %d"), def->device); + return -1; + } + if (!bus) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected disk bus %d"), def->bus); + return -1; + } + + virBufferVSprintf(buf, + " <disk type='%s' device='%s'>\n", + type, device); + + if (def->driverName) { + if (def->driverType) + virBufferVSprintf(buf, + " <driver name='%s' type='%s'/>\n", + def->driverName, def->driverType); + else + virBufferVSprintf(buf, + " <driver name='%s'/>\n", + def->driverName); + } + + if (def->src) { + if (def->type == VIR_DOMAIN_DISK_TYPE_FILE) + virBufferEscapeString(buf, " <source file='%s'/>\n", + def->src); + else + virBufferEscapeString(buf, " <source dev='%s'/>\n", + def->src); + } + + virBufferVSprintf(buf, " <target dev='%s' bus='%s'/>\n", + def->dst, bus); + + if (def->readonly) + virBufferAddLit(buf, " <readonly/>\n"); + if (def->shared) + virBufferAddLit(buf, " <shareable/>\n"); + + virBufferAddLit(buf, " </disk>\n"); + + return 0; +} + +static int +virDomainNetDefFormat(virConnectPtr conn, + virBufferPtr buf, + virDomainNetDefPtr def) +{ + const char *type = virDomainNetTypeToString(def->type); + + if (!type) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected net type %d"), def->type); + return -1; + } + + virBufferVSprintf(buf, " <interface type='%s'>\n", type); + + virBufferVSprintf(buf, + " <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n", + def->mac[0], def->mac[1], def->mac[2], + def->mac[3], def->mac[4], def->mac[5]); + + switch (def->type) { + case VIR_DOMAIN_NET_TYPE_NETWORK: + virBufferEscapeString(buf, " <source network='%s'/>\n", + def->data.network.name); + break; + + case VIR_DOMAIN_NET_TYPE_ETHERNET: + if (def->data.ethernet.dev) + virBufferEscapeString(buf, " <source dev='%s'/>\n", + def->data.ethernet.dev); + if (def->data.ethernet.ipaddr) + virBufferVSprintf(buf, " <ip address='%s'/>\n", + def->data.ethernet.ipaddr); + if (def->data.ethernet.script) + virBufferEscapeString(buf, " <script path='%s'/>\n", + def->data.ethernet.script); + break; + + case VIR_DOMAIN_NET_TYPE_BRIDGE: + virBufferEscapeString(buf, " <source bridge='%s'/>\n", + def->data.bridge.brname); + break; + + case VIR_DOMAIN_NET_TYPE_SERVER: + case VIR_DOMAIN_NET_TYPE_CLIENT: + case VIR_DOMAIN_NET_TYPE_MCAST: + if (def->data.socket.address) + virBufferVSprintf(buf, " <source address='%s' port='%d'/>\n", + def->data.socket.address, def->data.socket.port); + else + virBufferVSprintf(buf, " <source port='%d'/>\n", + def->data.socket.port); + } + + if (def->ifname) + virBufferEscapeString(buf, " <target dev='%s'/>\n", + def->ifname); + if (def->model) + virBufferEscapeString(buf, " <model type='%s'/>\n", + def->model); + + virBufferAddLit(buf, " </interface>\n"); + + return 0; +} + + +static int +virDomainChrDefFormat(virConnectPtr conn, + virBufferPtr buf, + virDomainChrDefPtr def, + const char *name) +{ + const char *type = virDomainChrTypeToString(def->type); + + if (!type) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected char type %d"), def->type); + return -1; + } + + /* Compat with legacy <console tty='/dev/pts/5'/> syntax */ + virBufferVSprintf(buf, " <%s type='%s'", + name, type); + if (STREQ(name, "console") && + def->type == VIR_DOMAIN_CHR_TYPE_PTY && + def->data.file.path) { + virBufferEscapeString(buf, " tty='%s'>\n", + def->data.file.path); + } else { + virBufferAddLit(buf, ">\n"); + } + + switch (def->type) { + case VIR_DOMAIN_CHR_TYPE_NULL: + case VIR_DOMAIN_CHR_TYPE_VC: + case VIR_DOMAIN_CHR_TYPE_STDIO: + /* nada */ + break; + + case VIR_DOMAIN_CHR_TYPE_PTY: + case VIR_DOMAIN_CHR_TYPE_DEV: + case VIR_DOMAIN_CHR_TYPE_FILE: + case VIR_DOMAIN_CHR_TYPE_PIPE: + if (def->type != VIR_DOMAIN_CHR_TYPE_PTY || + def->data.file.path) { + virBufferEscapeString(buf, " <source path='%s'/>\n", + def->data.file.path); + } + break; + + case VIR_DOMAIN_CHR_TYPE_UDP: + if (def->data.udp.bindService && + def->data.udp.bindHost) { + virBufferVSprintf(buf, " <source mode='bind' host='%s' service='%s'/>\n", + def->data.udp.bindHost, + def->data.udp.bindService); + } else if (def->data.udp.bindHost) { + virBufferVSprintf(buf, " <source mode='bind' host='%s'/>\n", + def->data.udp.bindHost); + } else if (def->data.udp.bindService) { + virBufferVSprintf(buf, " <source mode='bind' service='%s'/>\n", + def->data.udp.bindService); + } + + if (def->data.udp.connectService && + def->data.udp.connectHost) { + virBufferVSprintf(buf, " <source mode='connect' host='%s' service='%s'/>\n", + def->data.udp.connectHost, + def->data.udp.connectService); + } else if (def->data.udp.connectHost) { + virBufferVSprintf(buf, " <source mode='connect' host='%s'/>\n", + def->data.udp.connectHost); + } else if (def->data.udp.connectService) { + virBufferVSprintf(buf, " <source mode='connect' service='%s'/>\n", + def->data.udp.connectService); + } + break; + + case VIR_DOMAIN_CHR_TYPE_TCP: + virBufferVSprintf(buf, " <source mode='%s' host='%s' service='%s'/>\n", + def->data.tcp.listen ? "bind" : "connect", + def->data.tcp.host, + def->data.tcp.service); + virBufferVSprintf(buf, " <protocol type='%s'/>\n", + def->data.tcp.protocol == + VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET + ? "telnet" : "raw"); + break; + + case VIR_DOMAIN_CHR_TYPE_UNIX: + virBufferVSprintf(buf, " <source mode='%s'", + def->data.nix.listen ? "bind" : "connect"); + virBufferEscapeString(buf, " path='%s'/>\n", + def->data.nix.path); + break; + } + + virBufferVSprintf(buf, " <target port='%d'/>\n", + def->dstPort); + + virBufferVSprintf(buf, " </%s>\n", + name); + + return 0; +} + +static int +virDomainSoundDefFormat(virConnectPtr conn, + virBufferPtr buf, + virDomainSoundDefPtr def) +{ + const char *model = virDomainSoundModelTypeToString(def->model); + + if (!model) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected sound model %d"), def->model); + return -1; + } + + virBufferVSprintf(buf, " <sound model='%s'/>\n", + model); + + return 0; +} + +static int +virDomainInputDefFormat(virConnectPtr conn, + virBufferPtr buf, + virDomainInputDefPtr def) +{ + const char *type = virDomainInputTypeToString(def->type); + const char *bus = virDomainInputBusTypeToString(def->bus); + + if (!type) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected input type %d"), def->type); + return -1; + } + if (!bus) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected input bus type %d"), def->bus); + return -1; + } + + virBufferVSprintf(buf, " <input type='%s' bus='%s'/>\n", + type, bus); + + return 0; +} + + +static int +virDomainGraphicsDefFormat(virConnectPtr conn, + virBufferPtr buf, + virDomainGraphicsDefPtr def, + int flags) +{ + const char *type = virDomainGraphicsTypeToString(def->type); + + if (!type) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected net type %d"), def->type); + return -1; + } + + virBufferVSprintf(buf, " <graphics type='%s'", type); + + switch (def->type) { + case VIR_DOMAIN_GRAPHICS_TYPE_VNC: + if (def->data.vnc.autoport) + virBufferAddLit(buf, " port='-1'"); + else if (def->data.vnc.port) + virBufferVSprintf(buf, " port='%d'", + def->data.vnc.port); + + virBufferVSprintf(buf, " autoport='%s'", + def->data.vnc.autoport ? "yes" : "no"); + + if (def->data.vnc.listenAddr) + virBufferVSprintf(buf, " listen='%s'", + def->data.vnc.listenAddr); + + if (def->data.vnc.keymap) + virBufferEscapeString(buf, " keymap='%s'", + def->data.vnc.keymap); + + if (def->data.vnc.passwd && + (flags & VIR_DOMAIN_XML_SECURE)) + virBufferEscapeString(buf, " passwd='%s'", + def->data.vnc.passwd); + + break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SDL: + if (def->data.sdl.display) + virBufferEscapeString(buf, " display='%s'", + def->data.sdl.display); + + if (def->data.sdl.xauth) + virBufferEscapeString(buf, " xauth='%s'", + def->data.sdl.xauth); + break; + } + + virBufferAddLit(buf, "/>\n"); + + return 0; +} + +char *virDomainDefFormat(virConnectPtr conn, + virDomainDefPtr def, + int flags) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + unsigned char *uuid; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virDomainDiskDefPtr disk; + virDomainNetDefPtr net; + virDomainSoundDefPtr sound; + virDomainInputDefPtr input; + virDomainChrDefPtr chr; + const char *type = NULL, *tmp; + int n, allones = 1; + + if (!(type = virDomainVirtTypeToString(def->virtType))) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected domain type %d"), def->virtType); + goto cleanup; + } + + if (def->id >= 0) + virBufferVSprintf(&buf, "<domain type='%s' id='%d'>\n", type, def->id); + else + virBufferVSprintf(&buf, "<domain type='%s'>\n", type); + + virBufferEscapeString(&buf, " <name>%s</name>\n", def->name); + + uuid = def->uuid; + virUUIDFormat(uuid, uuidstr); + virBufferVSprintf(&buf, " <uuid>%s</uuid>\n", uuidstr); + + virBufferVSprintf(&buf, " <memory>%lu</memory>\n", def->maxmem); + virBufferVSprintf(&buf, " <currentMemory>%lu</currentMemory>\n", + def->memory); + + for (n = 0 ; n < def->cpumasklen ; n++) + if (def->cpumask[n] != 1) + allones = 0; + + if (allones) { + virBufferVSprintf(&buf, " <vcpu>%lu</vcpu>\n", def->vcpus); + } else { + char *cpumask = NULL; + if ((cpumask = + virDomainCpuSetFormat(conn, def->cpumask, def->cpumasklen)) == NULL) + goto cleanup; + virBufferVSprintf(&buf, " <vcpu cpuset='%s'>%lu</vcpu>\n", + cpumask, def->vcpus); + VIR_FREE(cpumask); + } + + if (def->os.bootloader) { + virBufferEscapeString(&buf, " <bootloader>%s</bootloader>\n", + def->os.bootloader); + if (def->os.bootloaderArgs) + virBufferEscapeString(&buf, " <bootloader_args>%s</bootloader_args>\n", + def->os.bootloaderArgs); + } + virBufferAddLit(&buf, " <os>\n"); + + virBufferAddLit(&buf, " <type"); + if (def->os.arch) + virBufferVSprintf(&buf, " arch='%s'", def->os.arch); + if (def->os.machine) + virBufferVSprintf(&buf, " machine='%s'", def->os.machine); + /* + * HACK: For xen driver we previously used bogus 'linux' as the + * os type for paravirt, whereas capabilities declare it to + * be 'xen'. So we convert to the former for backcompat + */ + if (def->virtType == VIR_DOMAIN_VIRT_XEN && + STREQ(def->os.type, "xen")) + virBufferVSprintf(&buf, ">%s</type>\n", "linux"); + else + virBufferVSprintf(&buf, ">%s</type>\n", def->os.type); + + if (def->os.loader) + virBufferEscapeString(&buf, " <loader>%s</loader>\n", + def->os.loader); + if (def->os.kernel) + virBufferEscapeString(&buf, " <kernel>%s</kernel>\n", + def->os.kernel); + if (def->os.initrd) + virBufferEscapeString(&buf, " <initrd>%s</initrd>\n", + def->os.initrd); + if (def->os.cmdline) + virBufferEscapeString(&buf, " <cmdline>%s</cmdline>\n", + def->os.cmdline); + if (def->os.root) + virBufferEscapeString(&buf, " <root>%s</root>\n", + def->os.root); + + if (!def->os.bootloader) { + for (n = 0 ; n < def->os.nBootDevs ; n++) { + const char *boottype = + virDomainBootTypeToString(def->os.bootDevs[n]); + if (!boottype) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected boot device type %d"), + def->os.bootDevs[n]); + goto cleanup; + } + virBufferVSprintf(&buf, " <boot dev='%s'/>\n", boottype); + } + } + + virBufferAddLit(&buf, " </os>\n"); + + if (def->features) { + int i; + virBufferAddLit(&buf, " <features>\n"); + for (i = 0 ; i < VIR_DOMAIN_FEATURE_LAST ; i++) { + if (def->features & (1 << i)) { + const char *name = virDomainFeatureTypeToString(i); + if (!name) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("unexpected feature %d"), i); + goto cleanup; + } + virBufferVSprintf(&buf, " <%s/>\n", name); + } + } + virBufferAddLit(&buf, " </features>\n"); + } + + virBufferVSprintf(&buf, " <clock offset='%s'/>\n", + def->localtime ? "localtime" : "utc"); + + if (virDomainLifecycleDefFormat(conn, &buf, def->onPoweroff, + "on_poweroff") < 0) + goto cleanup; + if (virDomainLifecycleDefFormat(conn, &buf, def->onReboot, + "on_reboot") < 0) + goto cleanup; + if (virDomainLifecycleDefFormat(conn, &buf, def->onCrash, + "on_crash") < 0) + goto cleanup; + + virBufferAddLit(&buf, " <devices>\n"); + + if (def->emulator) + virBufferEscapeString(&buf, " <emulator>%s</emulator>\n", + def->emulator); + + disk = def->disks; + while (disk) { + if (virDomainDiskDefFormat(conn, &buf, disk) < 0) + goto cleanup; + disk = disk->next; + } + + net = def->nets; + while (net) { + if (virDomainNetDefFormat(conn, &buf, net) < 0) + goto cleanup; + net = net->next; + } + + + chr = def->serials; + while (chr) { + if (virDomainChrDefFormat(conn, &buf, chr, "serial") < 0) + goto cleanup; + chr = chr->next; + } + + chr = def->parallels; + while (chr) { + if (virDomainChrDefFormat(conn, &buf, chr, "parallel") < 0) + goto cleanup; + chr = chr->next; + } + + /* If there's a PV console that's preferred.. */ + if (def->console) { + if (virDomainChrDefFormat(conn, &buf, def->console, "console") < 0) + goto cleanup; + } else if (def->serials != NULL) { + /* ..else for legacy compat duplicate the serial device as a console */ + if (virDomainChrDefFormat(conn, &buf, def->serials, "console") < 0) + goto cleanup; + } + + input = def->inputs; + while (input) { + if (input->bus == VIR_DOMAIN_INPUT_BUS_USB && + virDomainInputDefFormat(conn, &buf, input) < 0) + goto cleanup; + input = input->next; + } + + if (def->graphics) { + /* If graphics is enabled, add the implicit mouse */ + virDomainInputDef autoInput = { + VIR_DOMAIN_INPUT_TYPE_MOUSE, + STREQ(def->os.type, "hvm") ? + VIR_DOMAIN_INPUT_BUS_PS2 : VIR_DOMAIN_INPUT_BUS_XEN, + NULL }; + + if (virDomainInputDefFormat(conn, &buf, &autoInput) < 0) + goto cleanup; + + if (virDomainGraphicsDefFormat(conn, &buf, def->graphics, flags) < 0) + goto cleanup; + } + + sound = def->sounds; + while(sound) { + if (virDomainSoundDefFormat(conn, &buf, sound) < 0) + goto cleanup; + sound = sound->next; + } + + virBufferAddLit(&buf, " </devices>\n"); + virBufferAddLit(&buf, "</domain>\n"); + + if (virBufferError(&buf)) + goto no_memory; + + return virBufferContentAndReset(&buf); + + no_memory: + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + cleanup: + tmp = virBufferContentAndReset(&buf); + VIR_FREE(tmp); + return NULL; +} + + +int virDomainSaveConfig(virConnectPtr conn, + const char *configDir, + const char *autostartDir, + virDomainObjPtr dom) +{ + char *xml; + int fd = -1, ret = -1; + int towrite; + int err; + + if (!dom->configFile && + asprintf(&dom->configFile, "%s/%s.xml", + configDir, dom->def->name) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto cleanup; + } + if (!dom->autostartLink && + asprintf(&dom->autostartLink, "%s/%s.xml", + autostartDir, dom->def->name) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto cleanup; + } + + if (!(xml = virDomainDefFormat(conn, + dom->newDef ? dom->newDef : dom->def, + VIR_DOMAIN_XML_SECURE))) + return -1; + + if ((err = virFileMakePath(configDir))) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create config directory %s: %s"), + configDir, strerror(err)); + goto cleanup; + } + + if ((err = virFileMakePath(autostartDir))) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create autostart directory %s: %s"), + autostartDir, strerror(err)); + goto cleanup; + } + + if ((fd = open(dom->configFile, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR )) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create config file %s: %s"), + dom->configFile, strerror(errno)); + goto cleanup; + } + + towrite = strlen(xml); + if (safewrite(fd, xml, towrite) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot write config file %s: %s"), + dom->configFile, strerror(errno)); + goto cleanup; + } + + if (close(fd) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot save config file %s: %s"), + dom->configFile, strerror(errno)); + goto cleanup; + } + + ret = 0; + + cleanup: + VIR_FREE(xml); + close(fd); + + return ret; +} + +virDomainObjPtr virDomainLoadConfig(virConnectPtr conn, + virCapsPtr caps, + virDomainObjPtr *doms, + const char *configDir, + const char *autostartDir, + const char *file) +{ + char *configFile, *autostartLink; + virDomainDefPtr def = NULL; + virDomainObjPtr dom; + int autostart; + + if (asprintf(&configFile, "%s/%s", + configDir, file) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + if (asprintf(&autostartLink, "%s/%s", + autostartDir, file) < 0) { + virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL); + goto error; + } + + if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0) + goto error; + + if (!(def = virDomainDefParseFile(conn, caps, file))) + goto error; + + if (!virFileMatchesNameSuffix(file, def->name, ".xml")) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Domain config filename '%s'" + " does not match domain name '%s'"), + configFile, def->name); + goto error; + } + + if (!(dom = virDomainAssignDef(conn, doms, def))) + goto error; + + dom->configFile = configFile; + dom->autostartLink = autostartLink; + dom->autostart = autostart; + + return dom; + +error: + VIR_FREE(configFile); + VIR_FREE(autostartLink); + virDomainDefFree(def); + return NULL; +} + +int virDomainLoadAllConfigs(virConnectPtr conn, + virCapsPtr caps, + virDomainObjPtr *doms, + const char *configDir, + const char *autostartDir) +{ + DIR *dir; + struct dirent *entry; + + if (!(dir = opendir(configDir))) { + if (errno == ENOENT) + return 0; + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("Failed to open dir '%s': %s"), + configDir, strerror(errno)); + return -1; + } + + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') + continue; + + if (!virFileHasSuffix(entry->d_name, ".xml")) + continue; + + /* NB: ignoring errors, so one malformed config doesn't + kill the whole process */ + virDomainLoadConfig(conn, + caps, + doms, + configDir, + autostartDir, + entry->d_name); + } + + closedir(dir); + + return 0; +} + +int virDomainDeleteConfig(virConnectPtr conn, + virDomainObjPtr dom) +{ + if (!dom->configFile || !dom->autostartLink) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("no config file for %s"), dom->def->name); + return -1; + } + + /* Not fatal if this doesn't work */ + unlink(dom->autostartLink); + + if (unlink(dom->configFile) < 0) { + virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot remove config for %s"), dom->def->name); + return -1; + } + + return 0; +} diff -r a77ea50ba693 src/domain_conf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/domain_conf.h Tue Jul 08 15:26:51 2008 +0100 @@ -0,0 +1,489 @@ +/* + * domain_conf.h: domain XML processing + * + * Copyright (C) 2006-2008 Red Hat, Inc. + * Copyright (C) 2006-2008 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#ifndef __DOMAIN_CONF_H +#define __DOMAIN_CONF_H + +#include <config.h> + +#include "internal.h" +#include "capabilities.h" +#include "util.h" + +/* Different types of hypervisor */ +/* NB: Keep in sync with virDomainVirtTypeToString impl */ +enum virDomainVirtType { + VIR_DOMAIN_VIRT_QEMU, + VIR_DOMAIN_VIRT_KQEMU, + VIR_DOMAIN_VIRT_KVM, + VIR_DOMAIN_VIRT_XEN, + VIR_DOMAIN_VIRT_LXC, + VIR_DOMAIN_VIRT_UML, + VIR_DOMAIN_VIRT_OPENVZ, + VIR_DOMAIN_VIRT_VSERVER, + VIR_DOMAIN_VIRT_LDOM, + VIR_DOMAIN_VIRT_TEST, + VIR_DOMAIN_VIRT_VMWARE, + VIR_DOMAIN_VIRT_HYPERV, + + VIR_DOMAIN_VIRT_LAST, +}; + +/* Two types of disk backends */ +enum virDomainDiskType { + VIR_DOMAIN_DISK_TYPE_BLOCK, + VIR_DOMAIN_DISK_TYPE_FILE, + + VIR_DOMAIN_DISK_TYPE_LAST +}; + +/* Three types of disk frontend */ +enum virDomainDiskDevice { + VIR_DOMAIN_DISK_DEVICE_DISK, + VIR_DOMAIN_DISK_DEVICE_CDROM, + VIR_DOMAIN_DISK_DEVICE_FLOPPY, + + VIR_DOMAIN_DISK_DEVICE_LAST +}; + +enum virDomainDiskBus { + VIR_DOMAIN_DISK_BUS_IDE, + VIR_DOMAIN_DISK_BUS_FDC, + VIR_DOMAIN_DISK_BUS_SCSI, + VIR_DOMAIN_DISK_BUS_VIRTIO, + VIR_DOMAIN_DISK_BUS_XEN, + + VIR_DOMAIN_DISK_BUS_LAST +}; + +/* Stores the virtual disk configuration */ +typedef struct _virDomainDiskDef virDomainDiskDef; +typedef virDomainDiskDef *virDomainDiskDefPtr; +struct _virDomainDiskDef { + int type; + int device; + int bus; + char *src; + char *dst; + char *driverName; + char *driverType; + unsigned int readonly : 1; + unsigned int shared : 1; + + virDomainDiskDefPtr next; +}; + + +/* 5 different types of networking config */ +enum virDomainNetType { + VIR_DOMAIN_NET_TYPE_USER, + VIR_DOMAIN_NET_TYPE_ETHERNET, + VIR_DOMAIN_NET_TYPE_SERVER, + VIR_DOMAIN_NET_TYPE_CLIENT, + VIR_DOMAIN_NET_TYPE_MCAST, + VIR_DOMAIN_NET_TYPE_NETWORK, + VIR_DOMAIN_NET_TYPE_BRIDGE, + + VIR_DOMAIN_NET_TYPE_LAST, +}; + + +#define VIR_DOMAIN_NET_MAC_SIZE 6 + +/* Stores the virtual network interface configuration */ +typedef struct _virDomainNetDef virDomainNetDef; +typedef virDomainNetDef *virDomainNetDefPtr; +struct _virDomainNetDef { + int type; + unsigned char mac[VIR_DOMAIN_NET_MAC_SIZE]; + char *model; + union { + struct { + char *dev; + char *script; + char *ipaddr; + } ethernet; + struct { + char *address; + int port; + } socket; /* any of NET_CLIENT or NET_SERVER or NET_MCAST */ + struct { + char *name; + } network; + struct { + char *brname; + } bridge; + } data; + char *ifname; + + virDomainNetDefPtr next; +}; + +enum virDomainChrSrcType { + VIR_DOMAIN_CHR_TYPE_NULL, + VIR_DOMAIN_CHR_TYPE_VC, + VIR_DOMAIN_CHR_TYPE_PTY, + VIR_DOMAIN_CHR_TYPE_DEV, + VIR_DOMAIN_CHR_TYPE_FILE, + VIR_DOMAIN_CHR_TYPE_PIPE, + VIR_DOMAIN_CHR_TYPE_STDIO, + VIR_DOMAIN_CHR_TYPE_UDP, + VIR_DOMAIN_CHR_TYPE_TCP, + VIR_DOMAIN_CHR_TYPE_UNIX, + + VIR_DOMAIN_CHR_TYPE_LAST, +}; + +enum virDomainChrTcpProtocol { + VIR_DOMAIN_CHR_TCP_PROTOCOL_RAW, + VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET, + + VIR_DOMAIN_CHR_TCP_PROTOCOL_LAST, +}; + +typedef struct _virDomainChrDef virDomainChrDef; +typedef virDomainChrDef *virDomainChrDefPtr; +struct _virDomainChrDef { + int dstPort; + + int type; + union { + struct { + char *path; + } file; /* pty, file, pipe, or device */ + struct { + char *host; + char *service; + int listen; + int protocol; + } tcp; + struct { + char *bindHost; + char *bindService; + char *connectHost; + char *connectService; + } udp; + struct { + char *path; + int listen; + } nix; + } data; + + virDomainChrDefPtr next; +}; + +enum virDomainInputType { + VIR_DOMAIN_INPUT_TYPE_MOUSE, + VIR_DOMAIN_INPUT_TYPE_TABLET, + + VIR_DOMAIN_INPUT_TYPE_LAST, +}; + +enum virDomainInputBus { + VIR_DOMAIN_INPUT_BUS_PS2, + VIR_DOMAIN_INPUT_BUS_USB, + VIR_DOMAIN_INPUT_BUS_XEN, + + VIR_DOMAIN_INPUT_BUS_LAST +}; + +typedef struct _virDomainInputDef virDomainInputDef; +typedef virDomainInputDef *virDomainInputDefPtr; +struct _virDomainInputDef { + int type; + int bus; + virDomainInputDefPtr next; +}; + +enum virDomainSoundModel { + VIR_DOMAIN_SOUND_MODEL_SB16, + VIR_DOMAIN_SOUND_MODEL_ES1370, + VIR_DOMAIN_SOUND_MODEL_PCSPK, + + VIR_DOMAIN_SOUND_MODEL_LAST +}; + +typedef struct _virDomainSoundDef virDomainSoundDef; +typedef virDomainSoundDef *virDomainSoundDefPtr; +struct _virDomainSoundDef { + int model; + virDomainSoundDefPtr next; +}; + +/* 3 possible graphics console modes */ +enum virDomainGraphicsType { + VIR_DOMAIN_GRAPHICS_TYPE_SDL, + VIR_DOMAIN_GRAPHICS_TYPE_VNC, + + VIR_DOMAIN_GRAPHICS_TYPE_LAST, +}; + +typedef struct _virDomainGraphicsDef virDomainGraphicsDef; +typedef virDomainGraphicsDef *virDomainGraphicsDefPtr; +struct _virDomainGraphicsDef { + int type; + union { + struct { + int port; + int autoport : 1; + char *listenAddr; + char *keymap; + char *passwd; + } vnc; + struct { + char *display; + char *xauth; + } sdl; + } data; +}; + + + +/* Flags for the 'type' field in next struct */ +enum virDomainDeviceType { + VIR_DOMAIN_DEVICE_DISK, + VIR_DOMAIN_DEVICE_NET, + VIR_DOMAIN_DEVICE_INPUT, + VIR_DOMAIN_DEVICE_SOUND, +}; + +typedef struct _virDomainDeviceDef virDomainDeviceDef; +typedef virDomainDeviceDef *virDomainDeviceDefPtr; +struct _virDomainDeviceDef { + int type; + union { + virDomainDiskDefPtr disk; + virDomainNetDefPtr net; + virDomainInputDefPtr input; + virDomainSoundDefPtr sound; + } data; +}; + + +#define VIR_DOMAIN_MAX_BOOT_DEVS 4 + +/* 3 possible boot devices */ +enum virDomainBootOrder { + VIR_DOMAIN_BOOT_FLOPPY, + VIR_DOMAIN_BOOT_CDROM, + VIR_DOMAIN_BOOT_DISK, + VIR_DOMAIN_BOOT_NET, + + VIR_DOMAIN_BOOT_LAST, +}; + +enum virDomainFeature { + VIR_DOMAIN_FEATURE_ACPI, + VIR_DOMAIN_FEATURE_APIC, + VIR_DOMAIN_FEATURE_PAE, + + VIR_DOMAIN_FEATURE_LAST +}; + +enum virDomainLifecycleAction { + VIR_DOMAIN_LIFECYCLE_DESTROY, + VIR_DOMAIN_LIFECYCLE_RESTART, + VIR_DOMAIN_LIFECYCLE_RESTART_RENAME, + VIR_DOMAIN_LIFECYCLE_PRESERVE, + + VIR_DOMAIN_LIFECYCLE_LAST +}; + +/* Operating system configuration data & machine / arch */ +typedef struct _virDomainOSDef virDomainOSDef; +typedef virDomainOSDef *virDomainOSDefPtr; +struct _virDomainOSDef { + char *type; + char *arch; + char *machine; + int nBootDevs; + int bootDevs[VIR_DOMAIN_BOOT_LAST]; + char *kernel; + char *initrd; + char *cmdline; + char *root; + char *loader; + char *bootloader; + char *bootloaderArgs; +}; + +#define VIR_DOMAIN_CPUMASK_LEN 1024 + +/* Guest VM main configuration */ +typedef struct _virDomainDef virDomainDef; +typedef virDomainDef *virDomainDefPtr; +struct _virDomainDef { + int virtType; + int id; + unsigned char uuid[VIR_UUID_BUFLEN]; + char *name; + + unsigned long memory; + unsigned long maxmem; + unsigned long vcpus; + int cpumasklen; + char *cpumask; + + /* These 3 are based on virDomainLifeCycleAction enum flags */ + int onReboot; + int onPoweroff; + int onCrash; + + virDomainOSDef os; + char *emulator; + int features; + + int localtime; + + virDomainGraphicsDefPtr graphics; + virDomainDiskDefPtr disks; + virDomainNetDefPtr nets; + virDomainInputDefPtr inputs; + virDomainSoundDefPtr sounds; + virDomainChrDefPtr serials; + virDomainChrDefPtr parallels; + virDomainChrDefPtr console; +}; + +/* Guest VM runtime state */ +typedef struct _virDomainObj virDomainObj; +typedef virDomainObj *virDomainObjPtr; +struct _virDomainObj { + int stdin; + int stdout; + int stderr; + int monitor; + int logfile; + int pid; + int state; + + int nvcpupids; + int *vcpupids; + + unsigned int autostart : 1; + unsigned int persistent : 1; + + char *configFile; + char *autostartLink; + + virDomainDefPtr def; /* The current definition */ + virDomainDefPtr newDef; /* New definition to activate at shutdown */ + + virDomainObjPtr next; +}; + + +static inline int +virDomainIsActive(virDomainObjPtr dom) +{ + return dom->def->id != -1; +} + + +virDomainObjPtr virDomainFindByID(const virDomainObjPtr doms, + int id); +virDomainObjPtr virDomainFindByUUID(const virDomainObjPtr doms, + const unsigned char *uuid); +virDomainObjPtr virDomainFindByName(const virDomainObjPtr doms, + const char *name); + + +void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def); +void virDomainInputDefFree(virDomainInputDefPtr def); +void virDomainDiskDefFree(virDomainDiskDefPtr def); +void virDomainNetDefFree(virDomainNetDefPtr def); +void virDomainChrDefFree(virDomainChrDefPtr def); +void virDomainSoundDefFree(virDomainSoundDefPtr def); +void virDomainDeviceDefFree(virDomainDeviceDefPtr def); +void virDomainDefFree(virDomainDefPtr vm); +void virDomainObjFree(virDomainObjPtr vm); + +virDomainObjPtr virDomainAssignDef(virConnectPtr conn, + virDomainObjPtr *doms, + const virDomainDefPtr def); +void virDomainRemoveInactive(virDomainObjPtr *doms, + virDomainObjPtr dom); + +virDomainDeviceDefPtr virDomainDeviceDefParse(virConnectPtr conn, + const virDomainDefPtr def, + const char *xmlStr); +virDomainDefPtr virDomainDefParseString(virConnectPtr conn, + virCapsPtr caps, + const char *xmlStr); +virDomainDefPtr virDomainDefParseFile(virConnectPtr conn, + virCapsPtr caps, + const char *filename); +virDomainDefPtr virDomainDefParseNode(virConnectPtr conn, + virCapsPtr caps, + xmlDocPtr doc, + xmlNodePtr root); +char *virDomainDefFormat(virConnectPtr conn, + virDomainDefPtr def, + int flags); + +int virDomainCpuSetParse(virConnectPtr conn, + const char **str, + char sep, + char *cpuset, + int maxcpu); +char *virDomainCpuSetFormat(virConnectPtr conn, + char *cpuset, + int maxcpu); + + +int virDomainSaveConfig(virConnectPtr conn, + const char *configDir, + const char *autostartDir, + virDomainObjPtr dom); + +virDomainObjPtr virDomainLoadConfig(virConnectPtr conn, + virCapsPtr caps, + virDomainObjPtr *doms, + const char *configDir, + const char *autostartDir, + const char *file); + +int virDomainLoadAllConfigs(virConnectPtr conn, + virCapsPtr caps, + virDomainObjPtr *doms, + const char *configDir, + const char *autostartDir); + +int virDomainDeleteConfig(virConnectPtr conn, + virDomainObjPtr dom); + +VIR_ENUM_DECL(virDomainVirt) +VIR_ENUM_DECL(virDomainBoot) +VIR_ENUM_DECL(virDomainFeature) +VIR_ENUM_DECL(virDomainLifecycle) +VIR_ENUM_DECL(virDomainDisk) +VIR_ENUM_DECL(virDomainDiskDevice) +VIR_ENUM_DECL(virDomainDiskBus) +VIR_ENUM_DECL(virDomainNet) +VIR_ENUM_DECL(virDomainChr) +VIR_ENUM_DECL(virDomainSoundModel) +VIR_ENUM_DECL(virDomainInput) +VIR_ENUM_DECL(virDomainInputBus) +VIR_ENUM_DECL(virDomainGraphics) + +#endif /* __DOMAIN_CONF_H */ diff -r a77ea50ba693 src/util.h --- a/src/util.h Tue Jul 08 15:26:26 2008 +0100 +++ b/src/util.h Tue Jul 08 15:26:51 2008 +0100 @@ -100,7 +100,7 @@ #define VIR_ENUM_IMPL(name, lastVal, ...) \ static const char const *name ## TypeList[] = { __VA_ARGS__ }; \ - verify(ARRAY_CARDINALITY(name ## TypeList) == lastVal); \ + extern int (* name ## Verify (void)) [verify_true (ARRAY_CARDINALITY(name ## TypeList) == lastVal)]; \ const char *name ## TypeToString(int type) { \ return virEnumToString(name ## TypeList, \ ARRAY_CARDINALITY(name ## TypeList), \ diff -r a77ea50ba693 src/virterror.c --- a/src/virterror.c Tue Jul 08 15:26:26 2008 +0100 +++ b/src/virterror.c Tue Jul 08 15:26:51 2008 +0100 @@ -306,6 +306,9 @@ break; case VIR_FROM_NETWORK: dom = "Network Config "; + break; + case VIR_FROM_DOMAIN: + dom = "Domain Config "; break; } diff -r a77ea50ba693 src/xml.c --- a/src/xml.c Tue Jul 08 15:26:26 2008 +0100 +++ b/src/xml.c Tue Jul 08 15:26:51 2008 +0100 @@ -551,6 +551,13 @@ return (ret); } +char * +virXMLPropString(xmlNodePtr node, + const char *name) +{ + return (char *)xmlGetProp(node, BAD_CAST name); +} + /** * virXPathBoolean: * @xpath: the XPath string to evaluate @@ -648,20 +655,21 @@ _("Invalid parameter to virXPathNodeSet()"), 0); return (-1); } + + if (list != NULL) + *list = NULL; + relnode = ctxt->node; obj = xmlXPathEval(BAD_CAST xpath, ctxt); if ((obj == NULL) || (obj->type != XPATH_NODESET) || - (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr <= 0) || - (obj->nodesetval->nodeTab == NULL)) { + (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr < 0)) { xmlXPathFreeObject(obj); - if (list != NULL) - *list = NULL; ctxt->node = relnode; return (-1); } ret = obj->nodesetval->nodeNr; - if (list != NULL) { + if (list != NULL && ret) { if (VIR_ALLOC_N(*list, ret) < 0) { virXMLError(NULL, VIR_ERR_NO_MEMORY, _("allocate string array"), diff -r a77ea50ba693 src/xml.h --- a/src/xml.h Tue Jul 08 15:26:26 2008 +0100 +++ b/src/xml.h Tue Jul 08 15:26:51 2008 +0100 @@ -23,6 +23,12 @@ int virXPathNumber (const char *xpath, xmlXPathContextPtr ctxt, double *value); +int virXPathInt (const char *xpath, + xmlXPathContextPtr ctxt, + int *value); +int virXPathUInt (const char *xpath, + xmlXPathContextPtr ctxt, + unsigned int *value); int virXPathLong (const char *xpath, xmlXPathContextPtr ctxt, long *value); @@ -34,6 +40,10 @@ int virXPathNodeSet (const char *xpath, xmlXPathContextPtr ctxt, xmlNodePtr **list); + +char * virXMLPropString(xmlNodePtr node, + const char *name); + #if WITH_XEN || WITH_QEMU int virParseCpuSet (virConnectPtr conn, -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list