--- /dev/null
+/*
+ * 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@redhat.com>
+ */
+
+#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;
+ size_t towrite;
+ int err;
+
+ if (!dom->configFile &&
+ asprintf(&dom->configFile, "%s/%s.xml",
+ configDir, dom->def->name) < 0) {
+ dom->configFile = NULL;
+ virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+ goto cleanup;
+ }
+ if (!dom->autostartLink &&
+ asprintf(&dom->autostartLink, "%s/%s.xml",
+ autostartDir, dom->def->name) < 0) {
+ dom->autostartLink = NULL;
+ virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+ goto cleanup;
+ }
+
+ if (!(xml = virDomainDefFormat(conn,
+ dom->newDef ? dom->newDef : dom->def,
+ VIR_DOMAIN_XML_SECURE)))
+ goto cleanup;
+
+ 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);
+ if (fd != -1)
+ close(fd);
+
+ return ret;
+}
+
+virDomainObjPtr virDomainLoadConfig(virConnectPtr conn,
+ virCapsPtr caps,
+ virDomainObjPtr *doms,
+ const char *configDir,
+ const char *autostartDir,
+ const char *file)
+{
+ char *configFile = NULL, *autostartLink = NULL;
+ virDomainDefPtr def = NULL;
+ virDomainObjPtr dom;
+ int autostart;
+
+ if (asprintf(&configFile, "%s/%s",
+ configDir, file) < 0) {
+ configFile = NULL;
+ virDomainReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+ goto error;
+ }
+ if (asprintf(&autostartLink, "%s/%s",
+ autostartDir, file) < 0) {
+ autostartLink = NULL;
+ 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: %s"),
+ dom->def->name, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}