]> xenbits.xensource.com Git - libvirt.git/commitdiff
Added QEMU support
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 14 Feb 2007 01:40:09 +0000 (01:40 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 14 Feb 2007 01:40:09 +0000 (01:40 +0000)
24 files changed:
ChangeLog
Makefile.am
configure.in
include/libvirt/virterror.h
qemud/.cvsignore [new file with mode: 0644]
qemud/Makefile.am [new file with mode: 0644]
qemud/conf.c [new file with mode: 0644]
qemud/conf.h [new file with mode: 0644]
qemud/config.c [new file with mode: 0644]
qemud/config.h [new file with mode: 0644]
qemud/dispatch.c [new file with mode: 0644]
qemud/dispatch.h [new file with mode: 0644]
qemud/driver.c [new file with mode: 0644]
qemud/driver.h [new file with mode: 0644]
qemud/internal.h [new file with mode: 0644]
qemud/protocol.h [new file with mode: 0644]
qemud/qemud.c [new file with mode: 0644]
src/Makefile.am
src/driver.h
src/libvirt.c
src/qemu_internal.c [new file with mode: 0644]
src/qemu_internal.h [new file with mode: 0644]
src/virsh.c
src/virterror.c

index c73e17961f84910d883d944b53641728e3e55c6f..21ff3bdc1c21d65768dc74d2d4830d3ceb6f1d21 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+Tue Feb 13 19:29:35 EST 2007 Daniel Berrange <berrange@redhat.com>
+
+       * src/qemu_internal.h, src/qemu_internal.c, src/Makefile.am,
+       src/driver.h, src/libvirt.c: Added a new driver to talk to
+       the QEMU daemon
+
+       * src/virterror.c, include/libvirt/virterror.c: Added new
+       error domain for QEMU.
+
+       * qemud/*: Added a daemon service for managing QEMU machines
+       via the libvirt qemu_internal driver
+
+       * src/virsh.c: use a read-write connection by default for QEMU
+       urls.
+
+       * configure.in, Makefile.am: Added qemud subdirectory.
+
 Thu Feb  8 12:59:14 EST 2007 Daniel Berrange <berrange@redhat.com>
 
        * src/xml.c, src/xend_internal.c, src/xend_internal.h: Remove
index dcc80679d503f5fdd27aa7f1e90bb21cf6478015..ddf52ca9f9ab62c6ee1a9ae69cbd5542fe786886 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = src include docs @PYTHON_SUBDIR@ tests proxy po
+SUBDIRS = src qemud proxy include docs @PYTHON_SUBDIR@ tests po
 
 ACLOCAL_AMFLAGS = -I m4
 
index 7947ed4059a640389eeea5dd87726a00f039a558..c1bade40aabe343e9135485e91c39b21bb0a2a30 100644 (file)
@@ -288,6 +288,7 @@ AC_OUTPUT(Makefile src/Makefile include/Makefile docs/Makefile \
           po/Makefile.in \
          include/libvirt/Makefile include/libvirt/libvirt.h \
          python/Makefile python/tests/Makefile \
+          qemud/Makefile \
           tests/Makefile proxy/Makefile \
           tests/xml2sexprdata/Makefile \
           tests/sexpr2xmldata/Makefile \
index e2d536919579f6a7f9a01fcee62d2ac5cd4c3b20..8a7959eb5da778533a2756e2e5d38445872d92e4 100644 (file)
@@ -46,7 +46,8 @@ typedef enum {
     VIR_FROM_DOM,      /* Error when operating on a domain */
     VIR_FROM_RPC,      /* Error in the XML-RPC code */
     VIR_FROM_PROXY,    /* Error in the proxy code */
-    VIR_FROM_CONF      /* Error in the configuration file handling */
+    VIR_FROM_CONF,     /* Error in the configuration file handling */
+    VIR_FROM_QEMU,      /* Error at the QEMU daemon */
 } virErrorDomain;
 
 
diff --git a/qemud/.cvsignore b/qemud/.cvsignore
new file mode 100644 (file)
index 0000000..eddc7eb
--- /dev/null
@@ -0,0 +1,7 @@
+Makefile
+Makefile.in
+.deps
+.libs
+*.lo
+*.la
+libvirt_qemud
diff --git a/qemud/Makefile.am b/qemud/Makefile.am
new file mode 100644 (file)
index 0000000..d86cb23
--- /dev/null
@@ -0,0 +1,19 @@
+## Process this file with automake to produce Makefile.in
+
+INCLUDES = @LIBXML_CFLAGS@
+
+libexec_PROGRAMS = libvirt_qemud
+
+libvirt_qemud_SOURCES = qemud.c internal.h protocol.h \
+                driver.c driver.h \
+                dispatch.c dispatch.h \
+                config.c config.h
+#-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_POSIX_C_SOURCE=199506L
+libvirt_qemud_CFLAGS = \
+        -I$(top_srcdir)/include -I$(top_builddir)/include $(LIBXML_CFLAGS) \
+        -Werror -Wall -Wextra -DLOCAL_STATE_DIR="\"$(localstatedir)\"" \
+        -DSYSCONF_DIR="\"$(sysconfdir)\""
+libvirt_qemud_LDFLAGS = $(LIBXML_LIBS)
+libvirt_qemud_DEPENDENCIES =
+libvirt_qemud_LDADD =
+
diff --git a/qemud/conf.c b/qemud/conf.c
new file mode 100644 (file)
index 0000000..216c791
--- /dev/null
@@ -0,0 +1,1429 @@
+/*
+ * config.c: VM configuration management
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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 <dirent.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/uri.h>
+
+#include <libvirt/virterror.h>
+
+#include "protocol.h"
+#include "internal.h"
+#include "config.h"
+#include "driver.h"
+
+static int qemudParseUUID(const char *uuid,
+                          unsigned char *rawuuid) {
+    const char *cur;
+    int i;
+
+    /*
+     * do a liberal scan allowing '-' and ' ' anywhere between character
+     * pairs as long as there is 32 of them in the end.
+     */
+    cur = uuid;
+    for (i = 0;i < 16;) {
+        rawuuid[i] = 0;
+        if (*cur == 0)
+            goto error;
+        if ((*cur == '-') || (*cur == ' ')) {
+            cur++;
+            continue;
+        }
+        if ((*cur >= '0') && (*cur <= '9'))
+            rawuuid[i] = *cur - '0';
+        else if ((*cur >= 'a') && (*cur <= 'f'))
+            rawuuid[i] = *cur - 'a' + 10;
+        else if ((*cur >= 'A') && (*cur <= 'F'))
+            rawuuid[i] = *cur - 'A' + 10;
+        else
+            goto error;
+        rawuuid[i] *= 16;
+        cur++;
+        if (*cur == 0)
+            goto error;
+        if ((*cur >= '0') && (*cur <= '9'))
+            rawuuid[i] += *cur - '0';
+        else if ((*cur >= 'a') && (*cur <= 'f'))
+            rawuuid[i] += *cur - 'a' + 10;
+        else if ((*cur >= 'A') && (*cur <= 'F'))
+            rawuuid[i] += *cur - 'A' + 10;
+        else
+            goto error;
+        i++;
+        cur++;
+    }
+
+    return 0;
+
+ error:
+    return -1;
+}
+
+
+struct qemu_arch_info {
+    const char *arch;
+    const char **machines;
+    const char *binary;
+};
+
+/* The list of possible machine types for various architectures,
+   as supported by QEMU - taken from 'qemu -M ?' for each arch */
+static const char *arch_info_x86_machines[] = {
+    "pc", "isapc"
+};
+static const char *arch_info_mips_machines[] = {
+    "mips"
+};
+static const char *arch_info_sparc_machines[] = {
+    "sun4m"
+};
+static const char *arch_info_ppc_machines[] = {
+    "g3bw", "mac99", "prep"
+};
+
+/* The archicture tables for supported QEMU archs */
+static struct qemu_arch_info archs[] = { 
+    {  "i686", arch_info_x86_machines, "qemu" },
+    {  "x86_64", arch_info_x86_machines, "qemu-system-x86_64" },
+    {  "mips", arch_info_mips_machines, "qemu-system-mips" },
+    {  "mipsel", arch_info_mips_machines, "qemu-system-mipsel" },
+    {  "sparc", arch_info_sparc_machines, "qemu-system-sparc" },
+    {  "ppc", arch_info_ppc_machines, "qemu-system-ppc" },
+};
+
+/* Return the default architecture if none is explicitly requested*/
+static const char *qemudDefaultArch(void) {
+    return archs[0].arch;
+}
+
+/* Return the default machine type for a given architecture */
+static const char *qemudDefaultMachineForArch(const char *arch) {
+    int i;
+
+    for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) {
+        if (!strcmp(archs[i].arch, arch)) {
+            return archs[i].machines[0];
+        }
+    }
+
+    return NULL;
+}
+
+/* Return the default binary name for a particular architecture */
+static const char *qemudDefaultBinaryForArch(const char *arch) {
+    int i;
+
+    for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) {
+        if (!strcmp(archs[i].arch, arch)) {
+            return archs[i].binary;
+        }
+    }
+
+    return NULL;
+}
+
+/* Find the fully qualified path to the binary for an architecture */
+static char *qemudLocateBinaryForArch(struct qemud_server *server,
+                                      int virtType, const char *arch) {
+    const char *name;
+    char *path;
+
+    if (virtType == QEMUD_VIRT_KVM)
+        name = "qemu-kvm";
+    else
+        name = qemudDefaultBinaryForArch(arch);
+
+    if (!name) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot determin binary for architecture %s", arch);
+        return NULL;
+    }
+
+    /* XXX lame. should actually use $PATH ... */
+    path = malloc(strlen(name) + strlen("/usr/bin/") + 1);
+    if (!path) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "path");
+        return NULL;
+    }
+    strcpy(path, "/usr/bin/");
+    strcat(path, name);
+    return path;
+}
+
+/* Parse the XML definition for a disk */
+static struct qemud_vm_disk_def *qemudParseDiskXML(struct qemud_server *server,
+                                                   xmlNodePtr node) {
+    struct qemud_vm_disk_def *disk = calloc(1, sizeof(struct qemud_vm_disk_def));
+    xmlNodePtr cur;
+    xmlChar *device = NULL;
+    xmlChar *source = NULL;
+    xmlChar *target = NULL;
+    xmlChar *type = NULL;
+    int typ = 0;
+
+    if (!disk) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "disk");
+        return NULL;
+    }
+
+    type = xmlGetProp(node, BAD_CAST "type");
+    if (type != NULL) {
+        if (xmlStrEqual(type, BAD_CAST "file"))
+            typ = QEMUD_DISK_FILE;
+        else if (xmlStrEqual(type, BAD_CAST "block"))
+            typ = QEMUD_DISK_BLOCK;
+        else {
+            typ = QEMUD_DISK_FILE;
+        }
+        xmlFree(type);
+        type = NULL;
+    }
+
+    device = xmlGetProp(node, BAD_CAST "device");
+  
+    cur = node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if ((source == NULL) &&
+                (xmlStrEqual(cur->name, BAD_CAST "source"))) {
+       
+                if (typ == QEMUD_DISK_FILE)
+                    source = xmlGetProp(cur, BAD_CAST "file");
+                else
+                    source = xmlGetProp(cur, BAD_CAST "dev");
+            } else if ((target == NULL) &&
+                       (xmlStrEqual(cur->name, BAD_CAST "target"))) {
+                target = xmlGetProp(cur, BAD_CAST "dev");
+            } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
+                disk->readonly = 1;
+            }
+        }
+        cur = cur->next;
+    }
+
+    if (source == NULL) {
+        qemudReportError(server, VIR_ERR_NO_SOURCE, target ? "%s" : NULL, target);
+        goto error;
+    }
+    if (target == NULL) {
+        qemudReportError(server, VIR_ERR_NO_TARGET, source ? "%s" : NULL, source);
+        goto error;
+    }
+
+    if (device &&
+        !strcmp((const char *)device, "floppy") &&
+        strcmp((const char *)target, "fda") &&
+        strcmp((const char *)target, "fdb")) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid floppy device name: %s", target);
+        goto error;
+    }
+  
+    if (device &&
+        !strcmp((const char *)device, "cdrom") &&
+        strcmp((const char *)target, "hdc")) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid cdrom device name: %s", target);
+        goto error;
+    }
+
+    if (device &&
+        !strcmp((const char *)device, "cdrom"))
+        disk->readonly = 1;
+
+    if ((!device || !strcmp((const char *)device, "disk")) &&
+        strcmp((const char *)target, "hda") &&
+        strcmp((const char *)target, "hdb") &&
+        strcmp((const char *)target, "hdc") &&
+        strcmp((const char *)target, "hdd")) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid harddisk device name: %s", target);
+        goto error;
+    }
+
+    strncpy(disk->src, (const char *)source, NAME_MAX-1);
+    disk->src[NAME_MAX-1] = '\0';
+
+    strncpy(disk->dst, (const char *)target, NAME_MAX-1);
+    disk->dst[NAME_MAX-1] = '\0';
+    disk->type = typ;
+
+    if (!device)
+        disk->device = QEMUD_DISK_DISK;
+    else if (!strcmp((const char *)device, "disk"))
+        disk->device = QEMUD_DISK_DISK;
+    else if (!strcmp((const char *)device, "cdrom"))
+        disk->device = QEMUD_DISK_CDROM;
+    else if (!strcmp((const char *)device, "floppy"))
+        disk->device = QEMUD_DISK_FLOPPY;
+    else {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid device type: %s", device);
+        goto error;
+    }
+
+    xmlFree(device);
+    xmlFree(target);
+    xmlFree(source);
+
+    return disk;
+
+ error:
+    if (type)
+        xmlFree(type);
+    if (target)
+        xmlFree(target);
+    if (source)
+        xmlFree(source);
+    if (device)
+        xmlFree(device);
+    free(disk);
+    return NULL;
+}
+
+
+/* Parse the XML definition for a network interface */
+static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *server,
+                                                       xmlNodePtr node) {
+    struct qemud_vm_net_def *net = calloc(1, sizeof(struct qemud_vm_net_def));
+    xmlNodePtr cur;
+    xmlChar *macaddr = NULL;
+    xmlChar *type = NULL;
+
+    if (!net) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "net");
+        return NULL;
+    }
+
+    net->type = QEMUD_NET_USER;
+
+    type = xmlGetProp(node, BAD_CAST "type");
+    if (type != NULL) {
+        if (xmlStrEqual(type, BAD_CAST "user"))
+            net->type = QEMUD_NET_USER;
+        else if (xmlStrEqual(type, BAD_CAST "tap"))
+            net->type = QEMUD_NET_TAP;
+        else if (xmlStrEqual(type, BAD_CAST "server"))
+            net->type = QEMUD_NET_SERVER;
+        else if (xmlStrEqual(type, BAD_CAST "client"))
+            net->type = QEMUD_NET_CLIENT;
+        else if (xmlStrEqual(type, BAD_CAST "mcast"))
+            net->type = QEMUD_NET_MCAST;
+        /*
+        else if (xmlStrEqual(type, BAD_CAST "vde"))
+          typ = QEMUD_NET_VDE;
+        */
+        else
+            net->type = QEMUD_NET_USER;
+        xmlFree(type);
+        type = NULL;
+    }
+
+    cur = node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if ((macaddr == NULL) &&
+                (xmlStrEqual(cur->name, BAD_CAST "mac"))) {
+                macaddr = xmlGetProp(cur, BAD_CAST "address");
+            }
+        }
+        cur = cur->next;
+    }
+
+    net->vlan = 0;
+
+    if (macaddr) {
+        sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
+               (unsigned int*)&net->mac[0],
+               (unsigned int*)&net->mac[1],
+               (unsigned int*)&net->mac[2],
+               (unsigned int*)&net->mac[3],
+               (unsigned int*)&net->mac[4],
+               (unsigned int*)&net->mac[5]);
+
+        xmlFree(macaddr);
+    }
+
+    return net;
+}
+
+
+/*
+ * Parses a libvirt XML definition of a guest, and populates the
+ * the qemud_vm struct with matching data about the guests config
+ */
+static int qemudParseXML(struct qemud_server *server,
+                         xmlDocPtr xml,
+                         struct qemud_vm *vm) {
+    xmlNodePtr root = NULL;
+    xmlChar *prop = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlXPathObjectPtr obj = NULL;
+    char *conv = NULL;
+    int i;
+
+    /* Prepare parser / xpath context */
+    root = xmlDocGetRootElement(xml);
+    if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element");
+        goto error;
+    }
+
+    ctxt = xmlXPathNewContext(xml);
+    if (ctxt == NULL) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "xmlXPathContext");
+        goto error;
+    }
+
+
+    /* Find out what type of QEMU virtualization to use */
+    if (!(prop = xmlGetProp(root, BAD_CAST "type"))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing domain type attribute");
+        goto error;
+    }
+
+    if (!strcmp((char *)prop, "qemu"))
+        vm->def.virtType = QEMUD_VIRT_QEMU;
+    else if (!strcmp((char *)prop, "kqemu"))
+        vm->def.virtType = QEMUD_VIRT_KQEMU;
+    else if (!strcmp((char *)prop, "kvm"))
+        vm->def.virtType = QEMUD_VIRT_KVM;
+    else {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "invalid domain type attribute");
+        goto error;
+    }
+    free(prop);
+    prop = NULL;
+
+
+    /* Extract domain name */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        qemudReportError(server, VIR_ERR_NO_NAME, NULL);
+        goto error;
+    }
+    if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "domain name length too long");
+        goto error;
+    }
+    strcpy(vm->def.name, (const char *)obj->stringval);
+    xmlXPathFreeObject(obj);
+
+
+    /* Extract domain uuid */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/uuid[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        /* XXX auto-generate a UUID */
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing uuid element");
+        goto error;
+    }
+    if (qemudParseUUID((const char *)obj->stringval, vm->def.uuid) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element");
+        goto error;
+    }
+    xmlXPathFreeObject(obj);
+
+
+    /* Extract domain memory */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/memory[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing memory element");
+        goto error;
+    } else {
+        conv = NULL;
+        vm->def.maxmem = strtoll((const char*)obj->stringval, &conv, 10);
+        if (conv == (const char*)obj->stringval) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information");
+            goto error;
+        }
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    /* Extract domain memory */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/currentMemory[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        vm->def.memory = vm->def.maxmem;
+    } else {
+        conv = NULL;
+        vm->def.memory = strtoll((const char*)obj->stringval, &conv, 10);
+        if (vm->def.memory > vm->def.maxmem)
+            vm->def.memory = vm->def.maxmem;
+        if (conv == (const char*)obj->stringval) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information");
+            goto error;
+        }
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    /* Extract domain vcpu info */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/vcpu[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        vm->def.vcpus = 1;
+    } else {
+        conv = NULL;
+        vm->def.vcpus = strtoll((const char*)obj->stringval, &conv, 10);
+        if (conv == (const char*)obj->stringval) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed vcpu information");
+            goto error;
+        }
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    /* See if ACPI feature is requested */
+    obj = xmlXPathEval(BAD_CAST "/domain/features/acpi", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr == 1)) {
+        vm->def.features |= QEMUD_FEATURE_ACPI;
+    }
+
+    /* Extract OS type info */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        qemudReportError(server, VIR_ERR_OS_TYPE, NULL);
+        goto error;
+    }
+    if (strcmp((const char *)obj->stringval, "hvm")) {
+        qemudReportError(server, VIR_ERR_OS_TYPE, "%s", obj->stringval);
+        goto error;
+    }
+    strcpy(vm->def.os.type, (const char *)obj->stringval);
+    xmlXPathFreeObject(obj);
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@arch)", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        const char *defaultArch = qemudDefaultArch();
+        if (strlen(defaultArch) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
+            goto error;
+        }
+        strcpy(vm->def.os.arch, defaultArch);
+    } else {
+        if (strlen((const char *)obj->stringval) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
+            goto error;
+        }
+        strcpy(vm->def.os.arch, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@machine)", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        const char *defaultMachine = qemudDefaultMachineForArch(vm->def.os.arch);
+        if (strlen(defaultMachine) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "machine type too long");
+            goto error;
+        }
+        strcpy(vm->def.os.machine, defaultMachine);
+    } else {
+        if (strlen((const char *)obj->stringval) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
+            goto error;
+        }
+        strcpy(vm->def.os.machine, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/kernel[1])", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
+        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
+        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "kernel path too long");
+            goto error;
+        }
+        strcpy(vm->def.os.kernel, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/initrd[1])", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
+        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
+        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "initrd path too long");
+            goto error;
+        }
+        strcpy(vm->def.os.initrd, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/cmdline[1])", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
+        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
+        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "cmdline arguments too long");
+            goto error;
+        }
+        strcpy(vm->def.os.cmdline, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    /* analysis of the disk devices */
+    obj = xmlXPathEval(BAD_CAST "/domain/os/boot", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
+        for (i = 0; i < obj->nodesetval->nodeNr && i < QEMUD_MAX_BOOT_DEVS ; i++) {
+            prop = xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "dev");
+            if (!strcmp((char *)prop, "hd")) {
+                vm->def.os.bootDevs[vm->def.os.nBootDevs++] = QEMUD_BOOT_DISK;
+            } else if (!strcmp((char *)prop, "fd")) {
+                vm->def.os.bootDevs[vm->def.os.nBootDevs++] = QEMUD_BOOT_FLOPPY;
+            } else if (!strcmp((char *)prop, "cdrom")) {
+                vm->def.os.bootDevs[vm->def.os.nBootDevs++] = QEMUD_BOOT_CDROM;
+            } else if (!strcmp((char *)prop, "net")) {
+                vm->def.os.bootDevs[vm->def.os.nBootDevs++] = QEMUD_BOOT_NET;
+            } else {
+                goto error;
+            }
+        }
+    }
+    xmlXPathFreeObject(obj);
+    if (vm->def.os.nBootDevs == 0) {
+        vm->def.os.nBootDevs = 1;
+        vm->def.os.bootDevs[0] = QEMUD_BOOT_DISK;
+    }
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/devices/emulator[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        char *tmp = qemudLocateBinaryForArch(server, vm->def.virtType, vm->def.os.arch);
+        if (!tmp) {
+            goto error;
+        }
+        strcpy(vm->def.os.binary, tmp);
+        free(tmp);
+    } else {
+        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "emulator path too long");
+            goto error;
+        }
+        strcpy(vm->def.os.binary, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
+        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
+        vm->def.graphicsType = QEMUD_GRAPHICS_NONE;
+    } else {
+        prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "type");
+        if (!strcmp((char *)prop, "vnc")) {
+            vm->def.graphicsType = QEMUD_GRAPHICS_VNC;
+            prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "port");
+            if (prop) {
+                conv = NULL;
+                vm->def.vncPort = strtoll((const char*)prop, &conv, 10);
+            } else {
+                vm->def.vncPort = -1;
+            }
+        } else if (!strcmp((char *)prop, "sdl")) {
+            vm->def.graphicsType = QEMUD_GRAPHICS_SDL;
+        } else {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Unsupported graphics type %s", prop);
+            goto error;
+        }
+    }
+
+    /* analysis of the disk devices */
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
+        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+            struct qemud_vm_disk_def *disk;
+            if (!(disk = qemudParseDiskXML(server, obj->nodesetval->nodeTab[i]))) {
+                goto error;
+            }
+            vm->def.ndisks++;
+            disk->next = vm->def.disks;
+            vm->def.disks = disk;
+        }
+    }
+    xmlXPathFreeObject(obj);
+
+
+    /* analysis of the network devices */
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
+        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+            struct qemud_vm_net_def *net;
+            if (!(net = qemudParseInterfaceXML(server, obj->nodesetval->nodeTab[i]))) {
+                goto error;
+            }
+            vm->def.nnets++;
+            net->next = vm->def.nets;
+            vm->def.nets = net;
+        }
+    }
+    xmlXPathFreeObject(obj);
+    xmlXPathFreeContext(ctxt);
+
+    return 0;
+
+ error:
+    if (prop)
+        free(prop);
+    if (obj)
+        xmlXPathFreeObject(obj);
+    if (ctxt)
+        xmlXPathFreeContext(ctxt);
+    return -1;
+}
+
+
+/*
+ * Constructs a argv suitable for launching qemu with config defined
+ * for a given virtual machine.
+ */
+int qemudBuildCommandLine(struct qemud_server *server,
+                          struct qemud_vm *vm,
+                          char ***argv,
+                          int *argc) {
+    int n = -1, i;
+    char memory[50];
+    char vcpus[50];
+    char boot[QEMUD_MAX_BOOT_DEVS+1];
+    struct qemud_vm_disk_def *disk = vm->def.disks;
+    struct qemud_vm_net_def *net = vm->def.nets;
+
+    *argc = 1 + /* qemu */
+        2 + /* machine type */
+        (vm->def.virtType == QEMUD_VIRT_QEMU ? 1 : 0) + /* Disable kqemu */
+        2 * vm->def.ndisks + /* disks*/
+        (vm->def.nnets > 0 ? (4 * vm->def.nnets) : 2) + /* networks */
+        2 + /* memory*/
+        2 + /* cpus */
+        2 + /* boot device */
+        2 + /* monitor */
+        (vm->def.features & QEMUD_FEATURE_ACPI ? 0 : 1) + /* acpi */
+        (vm->def.os.kernel[0] ? 2 : 0) + /* kernel */
+        (vm->def.os.initrd[0] ? 2 : 0) + /* initrd */
+        (vm->def.os.cmdline[0] ? 2 : 0) + /* cmdline */
+        (vm->def.graphicsType == QEMUD_GRAPHICS_VNC ? 2 :
+         (vm->def.graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)); /* graphics */
+
+    sprintf(memory, "%d", vm->def.memory/1024);
+    sprintf(vcpus, "%d", vm->def.vcpus);
+
+    if (!(*argv = malloc(sizeof(char *) * (*argc +1))))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(vm->def.os.binary)))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup("-M")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(vm->def.os.machine)))
+        goto no_memory;
+    if (vm->def.virtType == QEMUD_VIRT_QEMU) {
+        if (!((*argv)[++n] = strdup("-no-kqemu")))
+        goto no_memory;
+    }
+    if (!((*argv)[++n] = strdup("-m")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(memory)))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup("-smp")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(vcpus)))
+        goto no_memory;
+
+    if (!((*argv)[++n] = strdup("-monitor")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup("pty")))
+        goto no_memory;
+
+    if (!(vm->def.features & QEMUD_FEATURE_ACPI)) {
+    if (!((*argv)[++n] = strdup("-no-acpi")))
+        goto no_memory;
+    }
+
+    for (i = 0 ; i < vm->def.os.nBootDevs ; i++) {
+        switch (vm->def.os.bootDevs[i]) {
+        case QEMUD_BOOT_CDROM:
+            boot[i] = 'd';
+            break;
+        case QEMUD_BOOT_FLOPPY:
+            boot[i] = 'a';
+            break;
+        case QEMUD_BOOT_DISK:
+            boot[i] = 'c';
+            break;
+        case QEMUD_BOOT_NET:
+            boot[i] = 'n';
+            break;
+        default:
+            boot[i] = 'c';
+            break;
+        }
+    }
+    boot[vm->def.os.nBootDevs] = '\0';
+    if (!((*argv)[++n] = strdup("-boot")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(boot)))
+        goto no_memory;
+
+    if (vm->def.os.kernel[0]) {
+        if (!((*argv)[++n] = strdup("-kernel")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(vm->def.os.kernel)))
+            goto no_memory;
+    }
+    if (vm->def.os.initrd[0]) {
+        if (!((*argv)[++n] = strdup("-initrd")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(vm->def.os.initrd)))
+            goto no_memory;
+    }
+    if (vm->def.os.cmdline[0]) {
+        if (!((*argv)[++n] = strdup("-append")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(vm->def.os.cmdline)))
+            goto no_memory;
+    }
+
+    while (disk) {
+        char dev[NAME_MAX];
+        char file[PATH_MAX];
+        if (!strcmp(disk->dst, "hdc") &&
+            disk->device == QEMUD_DISK_CDROM)
+            snprintf(dev, NAME_MAX, "-%s", "cdrom");
+        else
+            snprintf(dev, NAME_MAX, "-%s", disk->dst);
+        snprintf(file, PATH_MAX, "%s", disk->src);
+
+        if (!((*argv)[++n] = strdup(dev)))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(file)))
+            goto no_memory;
+
+        disk = disk->next;
+    }
+
+    if (!net) {
+        if (!((*argv)[++n] = strdup("-net")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup("none")))
+            goto no_memory;
+    } else {
+        while (net) {
+            char nic[3+1+7+1+17+1];
+            sprintf(nic, "nic,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+                    net->mac[0], net->mac[1],
+                    net->mac[2], net->mac[3],
+                    net->mac[4], net->mac[5]);
+
+            if (!((*argv)[++n] = strdup("-net")))
+                goto no_memory;
+            if (!((*argv)[++n] = strdup(nic)))
+                goto no_memory;
+            if (!((*argv)[++n] = strdup("-net")))
+                goto no_memory;
+            /* XXX don't hardcode user */
+            if (!((*argv)[++n] = strdup("user")))
+                goto no_memory;
+
+            net = net->next;
+        }
+    }
+
+    if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) {
+        char port[10];
+        snprintf(port, 10, "%d", vm->def.vncActivePort - 5900);
+        if (!((*argv)[++n] = strdup("-vnc")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(port)))
+            goto no_memory;
+    } else if (vm->def.graphicsType == QEMUD_GRAPHICS_NONE) {
+        if (!((*argv)[++n] = strdup("-nographic")))
+            goto no_memory;
+    } else {
+        /* SDL is the default. no args needed */
+    }
+
+    (*argv)[++n] = NULL;
+
+    return 0;
+
+ no_memory:
+    if (argv) {
+        for (i = 0 ; i < n ; i++)
+            free(argv[i]);
+        free(argv);
+    }
+    qemudReportError(server, VIR_ERR_NO_MEMORY, "argv");
+    return -1;
+}
+
+/* Free all memory associated with a struct qemud_vm object */
+void qemudFreeVM(struct qemud_vm *vm) {
+    struct qemud_vm_disk_def *disk = vm->def.disks;
+    struct qemud_vm_net_def *net = vm->def.nets;
+
+    while (disk) {
+        struct qemud_vm_disk_def *prev = disk;
+        disk = disk->next;
+        free(prev);
+    }
+    while (net) {
+        struct qemud_vm_net_def *prev = net;
+        net = net->next;
+        free(prev);
+    }
+
+    free(vm);
+}
+
+/* Build up a fully qualified path for a config file to be
+ * associated with a persistent guest */
+static
+int qemudMakeConfigPath(struct qemud_server *server,
+                        const char *name,
+                        const char *ext,
+                        char *buf,
+                        unsigned int buflen) {
+    if ((strlen(server->configDir) + 1 + strlen(name) + (ext ? strlen(ext) : 0) + 1) > buflen)
+        return -1;
+
+    strcpy(buf, server->configDir);
+    strcat(buf, "/");
+    strcat(buf, name);
+    if (ext)
+        strcat(buf, ext);
+    return 0;
+}
+
+
+/* Save a guest's config data into a persistent file */
+static int qemudSaveConfig(struct qemud_server *server,
+                           struct qemud_vm *vm) {
+    char *xml;
+    int fd = -1, ret = -1;
+    int towrite;
+    struct stat sb;
+
+    if (!(xml = qemudGenerateXML(server, vm))) {
+        return -1;
+    }
+
+    if (stat(server->configDir, &sb) < 0) {
+        if (errno == ENOENT) {
+            if (mkdir(server->configDir, 0700) < 0) {
+                qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                                 "cannot create config directory %s",
+                                 server->configDir);
+                return -1;
+            }
+        } else {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                             "cannot stat config directory %s",
+                             server->configDir);
+            return -1;
+        }
+    } else if (!S_ISDIR(sb.st_mode)) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "config directory %s is not a directory",
+                         server->configDir);
+        return -1;
+    }
+
+    if ((fd = open(vm->configFile,
+                   O_WRONLY | O_CREAT | O_TRUNC,
+                   S_IRUSR | S_IWUSR )) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "cannot create config file %s",
+                         vm->configFile);
+        goto cleanup;
+    }
+
+    towrite = strlen(xml);
+    if (write(fd, xml, towrite) != towrite) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "cannot write config file %s",
+                         vm->configFile);
+        goto cleanup;
+    }
+
+    if (close(fd) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "cannot save config file %s",
+                         vm->configFile);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    if (fd != -1)
+        close(fd);
+
+    free(xml);
+
+    return ret;
+}
+
+
+/* Create a qemud_vm instance, populating it based on the data
+ * in a libvirt XML document describing the guest */
+struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server,
+                                    const char *file,
+                                    const char *doc,
+                                    int save) {
+    struct qemud_vm *vm = NULL;
+    xmlDocPtr xml;
+
+    if (!(xml = xmlReadDoc(BAD_CAST doc, file ? file : "domain.xml", NULL,
+                           XML_PARSE_NOENT | XML_PARSE_NONET |
+                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
+        qemudReportError(server, VIR_ERR_XML_ERROR, NULL);
+        return NULL;
+    }
+
+    if (!(vm = calloc(1, sizeof(struct qemud_vm)))) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "vm");
+        return NULL;
+    }
+
+    vm->stdout = -1;
+    vm->stderr = -1;
+    vm->monitor = -1;
+    vm->pid = -1;
+    vm->def.id = -1;
+
+    if (qemudParseXML(server, xml, vm) < 0) {
+        xmlFreeDoc(xml);
+        qemudFreeVM(vm);
+        return NULL;
+    }
+    xmlFreeDoc(xml);
+
+    if (qemudFindVMByUUID(server, vm->def.uuid)) {
+        qemudReportError(server, VIR_ERR_DOM_EXIST, vm->def.name);
+        qemudFreeVM(vm);
+        return NULL;
+    }
+
+    if (qemudFindVMByName(server, vm->def.name)) {
+        qemudReportError(server, VIR_ERR_DOM_EXIST, vm->def.name);
+        qemudFreeVM(vm);
+        return NULL;
+    }
+
+    if (file) {
+        strncpy(vm->configFile, file, PATH_MAX);
+        vm->configFile[PATH_MAX-1] = '\0';
+    } else {
+        if (save) {
+            if (qemudMakeConfigPath(server, vm->def.name, ".xml", vm->configFile, PATH_MAX) < 0) {
+                qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                                 "cannot construct config file path");
+                qemudFreeVM(vm);
+                return NULL;
+            }
+
+            if (qemudSaveConfig(server, vm) < 0) {
+                qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                                 "cannot save config file for guest");
+                qemudFreeVM(vm);
+                return NULL;
+            }
+        } else {
+            vm->configFile[0] = '\0';
+        }
+    }
+
+    return vm;
+}
+
+
+/* Load a guest from its persistent config file */
+static void qemudLoadConfig(struct qemud_server *server,
+                            const char *file) {
+    FILE *fh;
+    struct stat st;
+    struct qemud_vm *vm;
+    char xml[QEMUD_MAX_XML_LEN];
+    int ret;
+
+    if (!(fh = fopen(file, "r"))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open guest config file %s", file);
+        return;
+    }
+
+    if (fstat(fileno(fh), &st) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot stat config file %s", file);
+        goto cleanup;
+    }
+
+    if (st.st_size >= QEMUD_MAX_XML_LEN) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "guest config too large in file %s", file);
+        goto cleanup;
+    }
+
+    if ((ret = fread(xml, st.st_size, 1, fh)) != 1) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot read config file %s", file);
+        goto cleanup;
+    }
+    xml[st.st_size] = '\0';
+
+    if ((vm = qemudLoadConfigXML(server, file, xml, 1))) {
+        vm->next = server->inactivevms;
+        server->inactivevms = vm;
+        server->ninactivevms++;
+    }
+  
+ cleanup:
+    fclose(fh);
+}
+
+
+/* Scan for all guest config files */
+int qemudScanConfigs(struct qemud_server *server) {
+    DIR *dir;
+    struct dirent *entry;
+
+    if (!(dir = opendir(server->configDir))) {
+        if (errno == ENOENT)
+            return 0;
+        return -1;
+    }
+
+    while ((entry = readdir(dir))) {
+        char file[PATH_MAX];
+        if (entry->d_name[0] == '.')
+            continue;
+
+        if (qemudMakeConfigPath(server, entry->d_name, NULL, file, PATH_MAX) < 0)
+            continue;
+
+        qemudLoadConfig(server, file);
+    }
+
+    closedir(dir);
+    return 0;
+}
+
+
+/* Simple grow-on-demand string buffer */
+/* XXX re-factor to shared library */
+struct qemudBuffer {
+    char *data;
+    int len;
+    int used;
+};
+
+static
+int qemudBufferAdd(struct qemudBuffer *buf, const char *str) {
+    int need = strlen(str);
+  
+    if ((need+1) > (buf->len-buf->used)) {
+        return -1;
+    }
+  
+    memcpy(buf->data + buf->used, str, need+1);
+    buf->used += need;
+
+    return 0;
+}
+
+
+static
+int qemudBufferPrintf(struct qemudBuffer *buf,
+                      const char *format, ...) {
+    int size, count;
+    va_list locarg, argptr;
+
+    if ((format == NULL) || (buf == NULL)) {
+        return -1;
+    }
+    size = buf->len - buf->used - 1;
+    va_start(argptr, format);
+    va_copy(locarg, argptr);
+
+    if ((count = vsnprintf(&buf->data[buf->used],
+                           size,
+                           format,
+                           locarg)) >= size) {
+        return -1;
+    }
+    va_end(locarg);
+    buf->used += count;
+
+    buf->data[buf->used] = '\0';
+    return 0;
+}
+
+/* Generate an XML document describing the guest's configuration */
+char *qemudGenerateXML(struct qemud_server *server, struct qemud_vm *vm) {
+    struct qemudBuffer buf;
+    unsigned char *uuid;
+    struct qemud_vm_disk_def *disk;
+    struct qemud_vm_net_def *net;
+    const char *type = NULL;
+    int n;
+
+    buf.len = QEMUD_MAX_XML_LEN;
+    buf.used = 0;
+    buf.data = malloc(buf.len);
+
+    switch (vm->def.virtType) {
+    case QEMUD_VIRT_QEMU:
+        type = "qemu";
+        break;
+    case QEMUD_VIRT_KQEMU:
+        type = "kqemu";
+        break;
+    case QEMUD_VIRT_KVM:
+        type = "kvm";
+        break;
+    }
+    if (!type) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "unexpected domain type %d", vm->def.virtType);
+        goto cleanup;
+    }
+
+    if (vm->def.id >= 0) {
+        if (qemudBufferPrintf(&buf, "<domain type='%s' id='%d'>\n", type, vm->def.id) < 0)
+            goto no_memory;
+    } else {
+        if (qemudBufferPrintf(&buf, "<domain type='%s'>\n", type) < 0)
+            goto no_memory;
+    }
+
+    if (qemudBufferPrintf(&buf, "  <name>%s</name>\n", vm->def.name) < 0)
+        goto no_memory;
+
+    uuid = vm->def.uuid;
+    if (qemudBufferPrintf(&buf, "  <uuid>%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x</uuid>\n",
+                          uuid[0], uuid[1], uuid[2], uuid[3],
+                          uuid[4], uuid[5], uuid[6], uuid[7],
+                          uuid[8], uuid[9], uuid[10], uuid[11],
+                          uuid[12], uuid[13], uuid[14], uuid[15]) < 0)
+        goto no_memory;
+    if (qemudBufferPrintf(&buf, "  <memory>%d</memory>\n", vm->def.maxmem) < 0)
+        goto no_memory;
+    if (qemudBufferPrintf(&buf, "  <currentMemory>%d</currentMemory>\n", vm->def.memory) < 0)
+        goto no_memory;
+    if (qemudBufferPrintf(&buf, "  <vcpu>%d</vcpu>\n", vm->def.vcpus) < 0)
+        goto no_memory;
+
+    if (qemudBufferAdd(&buf, "  <os>\n") < 0)
+        goto no_memory;
+
+    if (vm->def.virtType == QEMUD_VIRT_QEMU) {
+        if (qemudBufferPrintf(&buf, "    <type arch='%s' machine='%s'>%s</type>\n",
+                              vm->def.os.arch, vm->def.os.machine, vm->def.os.type) < 0)
+            goto no_memory;
+    } else {
+        if (qemudBufferPrintf(&buf, "    <type>%s</type>\n", vm->def.os.type) < 0)
+            goto no_memory;
+    }
+
+    if (vm->def.os.kernel[0])
+        if (qemudBufferPrintf(&buf, "    <kernel>%s</kernel>\n", vm->def.os.kernel) < 0)
+            goto no_memory;
+    if (vm->def.os.initrd[0])
+        if (qemudBufferPrintf(&buf, "    <initrd>%s</initrd>\n", vm->def.os.initrd) < 0)
+            goto no_memory;
+    if (vm->def.os.cmdline[0])
+        if (qemudBufferPrintf(&buf, "    <cmdline>%s</cmdline>\n", vm->def.os.cmdline) < 0)
+            goto no_memory;
+
+    if (vm->def.features & QEMUD_FEATURE_ACPI) {
+        if (qemudBufferAdd(&buf, "  <features>\n") < 0)
+            goto no_memory;
+        if (qemudBufferAdd(&buf, "    <acpi>\n") < 0)
+            goto no_memory;
+        if (qemudBufferAdd(&buf, "  </features>\n") < 0)
+            goto no_memory;
+    }
+
+
+    for (n = 0 ; n < vm->def.os.nBootDevs ; n++) {
+        const char *boottype = "hd";
+        switch (vm->def.os.bootDevs[n]) {
+        case QEMUD_BOOT_FLOPPY:
+            boottype = "fd";
+            break;
+        case QEMUD_BOOT_DISK:
+            boottype = "hd";
+            break;
+        case QEMUD_BOOT_CDROM:
+            boottype = "cdrom";
+            break;
+        case QEMUD_BOOT_NET:
+            boottype = "net";
+            break;
+        }
+        if (qemudBufferPrintf(&buf, "    <boot dev='%s'/>\n", boottype) < 0)
+            goto no_memory;
+    }
+
+    if (qemudBufferAdd(&buf, "  </os>\n") < 0)
+        goto no_memory;
+
+    if (qemudBufferAdd(&buf, "  <devices>\n") < 0)
+        goto no_memory;
+
+    if (qemudBufferPrintf(&buf, "    <emulator>%s</emulator>\n", vm->def.os.binary) < 0)
+        goto no_memory;
+
+    disk = vm->def.disks;
+    while (disk) {
+        const char *types[] = {
+            "block",
+            "file",
+        };
+        const char *typeAttrs[] = {
+            "dev",
+            "file",
+        };
+        const char *devices[] = {
+            "disk",
+            "cdrom",
+            "floppy",
+        };
+        if (qemudBufferPrintf(&buf, "    <disk type='%s' device='%s'>\n",
+                              types[disk->type], devices[disk->device]) < 0)
+            goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "      <source %s='%s'/>\n", typeAttrs[disk->type], disk->src) < 0)
+            goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "      <target dev='%s'/>\n", disk->dst) < 0)
+            goto no_memory;
+
+        if (disk->readonly)
+            if (qemudBufferAdd(&buf, "      <readonly/>\n") < 0)
+                goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "    </disk>\n") < 0)
+            goto no_memory;
+
+        disk = disk->next;
+    }
+
+    net = vm->def.nets;
+    disk = vm->def.disks;
+    while (disk) {
+        const char *types[] = {
+            "user",
+            "tap",
+            "server",
+            "client",
+            "mcast",
+            "vde",
+        };
+        if (qemudBufferPrintf(&buf, "    <interface type='%s'>\n",
+                              types[net->type]) < 0)
+            goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "      <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n",
+                              net->mac[0], net->mac[1], net->mac[2],
+                              net->mac[3], net->mac[4], net->mac[5]) < 0)
+            goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "    </interface>\n") < 0)
+            goto no_memory;
+
+        disk = disk->next;
+    }
+
+    if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) {
+        if (vm->def.vncPort) {
+            qemudBufferPrintf(&buf, "    <graphics type='vnc' port='%d'/>\n",
+                              vm->def.id == -1 ? vm->def.vncPort : vm->def.vncActivePort);
+        } else {
+            qemudBufferPrintf(&buf, "    <graphics type='vnc'/>\n");
+        }
+    }
+
+    if (qemudBufferAdd(&buf, "  </devices>\n") < 0)
+        goto no_memory;
+
+
+    if (qemudBufferAdd(&buf, "</domain>\n") < 0)
+        goto no_memory;
+
+    return buf.data;
+
+ no_memory:
+    qemudReportError(server, VIR_ERR_NO_MEMORY, "xml");
+ cleanup:
+    free(buf.data);
+    return NULL;
+}
+
+
+int qemudDeleteConfigXML(struct qemud_server *server, struct qemud_vm *vm) {
+    if (!vm->configFile[0]) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "no config file for guest %s", vm->def.name);
+        return -1;
+    }
+
+    if (unlink(vm->configFile) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot remove config for guest %s", vm->def.name);
+        return -1;
+    }
+
+    vm->configFile[0] = '\0';
+
+    return 0;
+}
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/conf.h b/qemud/conf.h
new file mode 100644 (file)
index 0000000..1ca1f3e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * config.h: VM configuration management
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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>
+ */
+
+#ifndef __QEMUD_CONFIG_H
+#define __QEMUD_CONFIG_H
+
+#include "internal.h"
+
+int qemudBuildCommandLine(struct qemud_server *server,
+                          struct qemud_vm *vm,
+                          char ***argv,
+                          int *argc);
+
+void qemudFreeVM(struct qemud_vm *vm);
+struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server,
+                                    const char *file,
+                                    const char *doc,
+                                    int persist);
+int qemudScanConfigs(struct qemud_server *server);
+char *qemudGenerateXML(struct qemud_server *server,
+                       struct qemud_vm *vm);
+
+int qemudDeleteConfigXML(struct qemud_server *server,
+                         struct qemud_vm *vm);
+
+
+#endif
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/config.c b/qemud/config.c
new file mode 100644 (file)
index 0000000..216c791
--- /dev/null
@@ -0,0 +1,1429 @@
+/*
+ * config.c: VM configuration management
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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 <dirent.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/uri.h>
+
+#include <libvirt/virterror.h>
+
+#include "protocol.h"
+#include "internal.h"
+#include "config.h"
+#include "driver.h"
+
+static int qemudParseUUID(const char *uuid,
+                          unsigned char *rawuuid) {
+    const char *cur;
+    int i;
+
+    /*
+     * do a liberal scan allowing '-' and ' ' anywhere between character
+     * pairs as long as there is 32 of them in the end.
+     */
+    cur = uuid;
+    for (i = 0;i < 16;) {
+        rawuuid[i] = 0;
+        if (*cur == 0)
+            goto error;
+        if ((*cur == '-') || (*cur == ' ')) {
+            cur++;
+            continue;
+        }
+        if ((*cur >= '0') && (*cur <= '9'))
+            rawuuid[i] = *cur - '0';
+        else if ((*cur >= 'a') && (*cur <= 'f'))
+            rawuuid[i] = *cur - 'a' + 10;
+        else if ((*cur >= 'A') && (*cur <= 'F'))
+            rawuuid[i] = *cur - 'A' + 10;
+        else
+            goto error;
+        rawuuid[i] *= 16;
+        cur++;
+        if (*cur == 0)
+            goto error;
+        if ((*cur >= '0') && (*cur <= '9'))
+            rawuuid[i] += *cur - '0';
+        else if ((*cur >= 'a') && (*cur <= 'f'))
+            rawuuid[i] += *cur - 'a' + 10;
+        else if ((*cur >= 'A') && (*cur <= 'F'))
+            rawuuid[i] += *cur - 'A' + 10;
+        else
+            goto error;
+        i++;
+        cur++;
+    }
+
+    return 0;
+
+ error:
+    return -1;
+}
+
+
+struct qemu_arch_info {
+    const char *arch;
+    const char **machines;
+    const char *binary;
+};
+
+/* The list of possible machine types for various architectures,
+   as supported by QEMU - taken from 'qemu -M ?' for each arch */
+static const char *arch_info_x86_machines[] = {
+    "pc", "isapc"
+};
+static const char *arch_info_mips_machines[] = {
+    "mips"
+};
+static const char *arch_info_sparc_machines[] = {
+    "sun4m"
+};
+static const char *arch_info_ppc_machines[] = {
+    "g3bw", "mac99", "prep"
+};
+
+/* The archicture tables for supported QEMU archs */
+static struct qemu_arch_info archs[] = { 
+    {  "i686", arch_info_x86_machines, "qemu" },
+    {  "x86_64", arch_info_x86_machines, "qemu-system-x86_64" },
+    {  "mips", arch_info_mips_machines, "qemu-system-mips" },
+    {  "mipsel", arch_info_mips_machines, "qemu-system-mipsel" },
+    {  "sparc", arch_info_sparc_machines, "qemu-system-sparc" },
+    {  "ppc", arch_info_ppc_machines, "qemu-system-ppc" },
+};
+
+/* Return the default architecture if none is explicitly requested*/
+static const char *qemudDefaultArch(void) {
+    return archs[0].arch;
+}
+
+/* Return the default machine type for a given architecture */
+static const char *qemudDefaultMachineForArch(const char *arch) {
+    int i;
+
+    for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) {
+        if (!strcmp(archs[i].arch, arch)) {
+            return archs[i].machines[0];
+        }
+    }
+
+    return NULL;
+}
+
+/* Return the default binary name for a particular architecture */
+static const char *qemudDefaultBinaryForArch(const char *arch) {
+    int i;
+
+    for (i = 0 ; i < (int)(sizeof(archs) / sizeof(struct qemu_arch_info)) ; i++) {
+        if (!strcmp(archs[i].arch, arch)) {
+            return archs[i].binary;
+        }
+    }
+
+    return NULL;
+}
+
+/* Find the fully qualified path to the binary for an architecture */
+static char *qemudLocateBinaryForArch(struct qemud_server *server,
+                                      int virtType, const char *arch) {
+    const char *name;
+    char *path;
+
+    if (virtType == QEMUD_VIRT_KVM)
+        name = "qemu-kvm";
+    else
+        name = qemudDefaultBinaryForArch(arch);
+
+    if (!name) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot determin binary for architecture %s", arch);
+        return NULL;
+    }
+
+    /* XXX lame. should actually use $PATH ... */
+    path = malloc(strlen(name) + strlen("/usr/bin/") + 1);
+    if (!path) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "path");
+        return NULL;
+    }
+    strcpy(path, "/usr/bin/");
+    strcat(path, name);
+    return path;
+}
+
+/* Parse the XML definition for a disk */
+static struct qemud_vm_disk_def *qemudParseDiskXML(struct qemud_server *server,
+                                                   xmlNodePtr node) {
+    struct qemud_vm_disk_def *disk = calloc(1, sizeof(struct qemud_vm_disk_def));
+    xmlNodePtr cur;
+    xmlChar *device = NULL;
+    xmlChar *source = NULL;
+    xmlChar *target = NULL;
+    xmlChar *type = NULL;
+    int typ = 0;
+
+    if (!disk) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "disk");
+        return NULL;
+    }
+
+    type = xmlGetProp(node, BAD_CAST "type");
+    if (type != NULL) {
+        if (xmlStrEqual(type, BAD_CAST "file"))
+            typ = QEMUD_DISK_FILE;
+        else if (xmlStrEqual(type, BAD_CAST "block"))
+            typ = QEMUD_DISK_BLOCK;
+        else {
+            typ = QEMUD_DISK_FILE;
+        }
+        xmlFree(type);
+        type = NULL;
+    }
+
+    device = xmlGetProp(node, BAD_CAST "device");
+  
+    cur = node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if ((source == NULL) &&
+                (xmlStrEqual(cur->name, BAD_CAST "source"))) {
+       
+                if (typ == QEMUD_DISK_FILE)
+                    source = xmlGetProp(cur, BAD_CAST "file");
+                else
+                    source = xmlGetProp(cur, BAD_CAST "dev");
+            } else if ((target == NULL) &&
+                       (xmlStrEqual(cur->name, BAD_CAST "target"))) {
+                target = xmlGetProp(cur, BAD_CAST "dev");
+            } else if (xmlStrEqual(cur->name, BAD_CAST "readonly")) {
+                disk->readonly = 1;
+            }
+        }
+        cur = cur->next;
+    }
+
+    if (source == NULL) {
+        qemudReportError(server, VIR_ERR_NO_SOURCE, target ? "%s" : NULL, target);
+        goto error;
+    }
+    if (target == NULL) {
+        qemudReportError(server, VIR_ERR_NO_TARGET, source ? "%s" : NULL, source);
+        goto error;
+    }
+
+    if (device &&
+        !strcmp((const char *)device, "floppy") &&
+        strcmp((const char *)target, "fda") &&
+        strcmp((const char *)target, "fdb")) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid floppy device name: %s", target);
+        goto error;
+    }
+  
+    if (device &&
+        !strcmp((const char *)device, "cdrom") &&
+        strcmp((const char *)target, "hdc")) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid cdrom device name: %s", target);
+        goto error;
+    }
+
+    if (device &&
+        !strcmp((const char *)device, "cdrom"))
+        disk->readonly = 1;
+
+    if ((!device || !strcmp((const char *)device, "disk")) &&
+        strcmp((const char *)target, "hda") &&
+        strcmp((const char *)target, "hdb") &&
+        strcmp((const char *)target, "hdc") &&
+        strcmp((const char *)target, "hdd")) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid harddisk device name: %s", target);
+        goto error;
+    }
+
+    strncpy(disk->src, (const char *)source, NAME_MAX-1);
+    disk->src[NAME_MAX-1] = '\0';
+
+    strncpy(disk->dst, (const char *)target, NAME_MAX-1);
+    disk->dst[NAME_MAX-1] = '\0';
+    disk->type = typ;
+
+    if (!device)
+        disk->device = QEMUD_DISK_DISK;
+    else if (!strcmp((const char *)device, "disk"))
+        disk->device = QEMUD_DISK_DISK;
+    else if (!strcmp((const char *)device, "cdrom"))
+        disk->device = QEMUD_DISK_CDROM;
+    else if (!strcmp((const char *)device, "floppy"))
+        disk->device = QEMUD_DISK_FLOPPY;
+    else {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Invalid device type: %s", device);
+        goto error;
+    }
+
+    xmlFree(device);
+    xmlFree(target);
+    xmlFree(source);
+
+    return disk;
+
+ error:
+    if (type)
+        xmlFree(type);
+    if (target)
+        xmlFree(target);
+    if (source)
+        xmlFree(source);
+    if (device)
+        xmlFree(device);
+    free(disk);
+    return NULL;
+}
+
+
+/* Parse the XML definition for a network interface */
+static struct qemud_vm_net_def *qemudParseInterfaceXML(struct qemud_server *server,
+                                                       xmlNodePtr node) {
+    struct qemud_vm_net_def *net = calloc(1, sizeof(struct qemud_vm_net_def));
+    xmlNodePtr cur;
+    xmlChar *macaddr = NULL;
+    xmlChar *type = NULL;
+
+    if (!net) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "net");
+        return NULL;
+    }
+
+    net->type = QEMUD_NET_USER;
+
+    type = xmlGetProp(node, BAD_CAST "type");
+    if (type != NULL) {
+        if (xmlStrEqual(type, BAD_CAST "user"))
+            net->type = QEMUD_NET_USER;
+        else if (xmlStrEqual(type, BAD_CAST "tap"))
+            net->type = QEMUD_NET_TAP;
+        else if (xmlStrEqual(type, BAD_CAST "server"))
+            net->type = QEMUD_NET_SERVER;
+        else if (xmlStrEqual(type, BAD_CAST "client"))
+            net->type = QEMUD_NET_CLIENT;
+        else if (xmlStrEqual(type, BAD_CAST "mcast"))
+            net->type = QEMUD_NET_MCAST;
+        /*
+        else if (xmlStrEqual(type, BAD_CAST "vde"))
+          typ = QEMUD_NET_VDE;
+        */
+        else
+            net->type = QEMUD_NET_USER;
+        xmlFree(type);
+        type = NULL;
+    }
+
+    cur = node->children;
+    while (cur != NULL) {
+        if (cur->type == XML_ELEMENT_NODE) {
+            if ((macaddr == NULL) &&
+                (xmlStrEqual(cur->name, BAD_CAST "mac"))) {
+                macaddr = xmlGetProp(cur, BAD_CAST "address");
+            }
+        }
+        cur = cur->next;
+    }
+
+    net->vlan = 0;
+
+    if (macaddr) {
+        sscanf((const char *)macaddr, "%02x:%02x:%02x:%02x:%02x:%02x",
+               (unsigned int*)&net->mac[0],
+               (unsigned int*)&net->mac[1],
+               (unsigned int*)&net->mac[2],
+               (unsigned int*)&net->mac[3],
+               (unsigned int*)&net->mac[4],
+               (unsigned int*)&net->mac[5]);
+
+        xmlFree(macaddr);
+    }
+
+    return net;
+}
+
+
+/*
+ * Parses a libvirt XML definition of a guest, and populates the
+ * the qemud_vm struct with matching data about the guests config
+ */
+static int qemudParseXML(struct qemud_server *server,
+                         xmlDocPtr xml,
+                         struct qemud_vm *vm) {
+    xmlNodePtr root = NULL;
+    xmlChar *prop = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    xmlXPathObjectPtr obj = NULL;
+    char *conv = NULL;
+    int i;
+
+    /* Prepare parser / xpath context */
+    root = xmlDocGetRootElement(xml);
+    if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "domain"))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "incorrect root element");
+        goto error;
+    }
+
+    ctxt = xmlXPathNewContext(xml);
+    if (ctxt == NULL) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "xmlXPathContext");
+        goto error;
+    }
+
+
+    /* Find out what type of QEMU virtualization to use */
+    if (!(prop = xmlGetProp(root, BAD_CAST "type"))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing domain type attribute");
+        goto error;
+    }
+
+    if (!strcmp((char *)prop, "qemu"))
+        vm->def.virtType = QEMUD_VIRT_QEMU;
+    else if (!strcmp((char *)prop, "kqemu"))
+        vm->def.virtType = QEMUD_VIRT_KQEMU;
+    else if (!strcmp((char *)prop, "kvm"))
+        vm->def.virtType = QEMUD_VIRT_KVM;
+    else {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "invalid domain type attribute");
+        goto error;
+    }
+    free(prop);
+    prop = NULL;
+
+
+    /* Extract domain name */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        qemudReportError(server, VIR_ERR_NO_NAME, NULL);
+        goto error;
+    }
+    if (strlen((const char *)obj->stringval) >= (QEMUD_MAX_NAME_LEN-1)) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "domain name length too long");
+        goto error;
+    }
+    strcpy(vm->def.name, (const char *)obj->stringval);
+    xmlXPathFreeObject(obj);
+
+
+    /* Extract domain uuid */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/uuid[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        /* XXX auto-generate a UUID */
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing uuid element");
+        goto error;
+    }
+    if (qemudParseUUID((const char *)obj->stringval, vm->def.uuid) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed uuid element");
+        goto error;
+    }
+    xmlXPathFreeObject(obj);
+
+
+    /* Extract domain memory */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/memory[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "missing memory element");
+        goto error;
+    } else {
+        conv = NULL;
+        vm->def.maxmem = strtoll((const char*)obj->stringval, &conv, 10);
+        if (conv == (const char*)obj->stringval) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information");
+            goto error;
+        }
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    /* Extract domain memory */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/currentMemory[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        vm->def.memory = vm->def.maxmem;
+    } else {
+        conv = NULL;
+        vm->def.memory = strtoll((const char*)obj->stringval, &conv, 10);
+        if (vm->def.memory > vm->def.maxmem)
+            vm->def.memory = vm->def.maxmem;
+        if (conv == (const char*)obj->stringval) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed memory information");
+            goto error;
+        }
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    /* Extract domain vcpu info */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/vcpu[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        vm->def.vcpus = 1;
+    } else {
+        conv = NULL;
+        vm->def.vcpus = strtoll((const char*)obj->stringval, &conv, 10);
+        if (conv == (const char*)obj->stringval) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "malformed vcpu information");
+            goto error;
+        }
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    /* See if ACPI feature is requested */
+    obj = xmlXPathEval(BAD_CAST "/domain/features/acpi", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr == 1)) {
+        vm->def.features |= QEMUD_FEATURE_ACPI;
+    }
+
+    /* Extract OS type info */
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        qemudReportError(server, VIR_ERR_OS_TYPE, NULL);
+        goto error;
+    }
+    if (strcmp((const char *)obj->stringval, "hvm")) {
+        qemudReportError(server, VIR_ERR_OS_TYPE, "%s", obj->stringval);
+        goto error;
+    }
+    strcpy(vm->def.os.type, (const char *)obj->stringval);
+    xmlXPathFreeObject(obj);
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@arch)", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        const char *defaultArch = qemudDefaultArch();
+        if (strlen(defaultArch) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
+            goto error;
+        }
+        strcpy(vm->def.os.arch, defaultArch);
+    } else {
+        if (strlen((const char *)obj->stringval) >= (QEMUD_OS_TYPE_MAX_LEN-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
+            goto error;
+        }
+        strcpy(vm->def.os.arch, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/type[1]/@machine)", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        const char *defaultMachine = qemudDefaultMachineForArch(vm->def.os.arch);
+        if (strlen(defaultMachine) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "machine type too long");
+            goto error;
+        }
+        strcpy(vm->def.os.machine, defaultMachine);
+    } else {
+        if (strlen((const char *)obj->stringval) >= (QEMUD_OS_MACHINE_MAX_LEN-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "architecture type too long");
+            goto error;
+        }
+        strcpy(vm->def.os.machine, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/kernel[1])", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
+        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
+        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "kernel path too long");
+            goto error;
+        }
+        strcpy(vm->def.os.kernel, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/initrd[1])", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
+        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
+        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "initrd path too long");
+            goto error;
+        }
+        strcpy(vm->def.os.initrd, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/os/cmdline[1])", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_STRING) &&
+        (obj->stringval != NULL) && (obj->stringval[0] != 0)) {
+        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "cmdline arguments too long");
+            goto error;
+        }
+        strcpy(vm->def.os.cmdline, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+
+    /* analysis of the disk devices */
+    obj = xmlXPathEval(BAD_CAST "/domain/os/boot", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
+        for (i = 0; i < obj->nodesetval->nodeNr && i < QEMUD_MAX_BOOT_DEVS ; i++) {
+            prop = xmlGetProp(obj->nodesetval->nodeTab[i], BAD_CAST "dev");
+            if (!strcmp((char *)prop, "hd")) {
+                vm->def.os.bootDevs[vm->def.os.nBootDevs++] = QEMUD_BOOT_DISK;
+            } else if (!strcmp((char *)prop, "fd")) {
+                vm->def.os.bootDevs[vm->def.os.nBootDevs++] = QEMUD_BOOT_FLOPPY;
+            } else if (!strcmp((char *)prop, "cdrom")) {
+                vm->def.os.bootDevs[vm->def.os.nBootDevs++] = QEMUD_BOOT_CDROM;
+            } else if (!strcmp((char *)prop, "net")) {
+                vm->def.os.bootDevs[vm->def.os.nBootDevs++] = QEMUD_BOOT_NET;
+            } else {
+                goto error;
+            }
+        }
+    }
+    xmlXPathFreeObject(obj);
+    if (vm->def.os.nBootDevs == 0) {
+        vm->def.os.nBootDevs = 1;
+        vm->def.os.bootDevs[0] = QEMUD_BOOT_DISK;
+    }
+
+
+    obj = xmlXPathEval(BAD_CAST "string(/domain/devices/emulator[1])", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_STRING) ||
+        (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
+        char *tmp = qemudLocateBinaryForArch(server, vm->def.virtType, vm->def.os.arch);
+        if (!tmp) {
+            goto error;
+        }
+        strcpy(vm->def.os.binary, tmp);
+        free(tmp);
+    } else {
+        if (strlen((const char *)obj->stringval) >= (PATH_MAX-1)) {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "%s", "emulator path too long");
+            goto error;
+        }
+        strcpy(vm->def.os.binary, (const char *)obj->stringval);
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/graphics", ctxt);
+    if ((obj == NULL) || (obj->type != XPATH_NODESET) ||
+        (obj->nodesetval == NULL) || (obj->nodesetval->nodeNr == 0)) {
+        vm->def.graphicsType = QEMUD_GRAPHICS_NONE;
+    } else {
+        prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "type");
+        if (!strcmp((char *)prop, "vnc")) {
+            vm->def.graphicsType = QEMUD_GRAPHICS_VNC;
+            prop = xmlGetProp(obj->nodesetval->nodeTab[0], BAD_CAST "port");
+            if (prop) {
+                conv = NULL;
+                vm->def.vncPort = strtoll((const char*)prop, &conv, 10);
+            } else {
+                vm->def.vncPort = -1;
+            }
+        } else if (!strcmp((char *)prop, "sdl")) {
+            vm->def.graphicsType = QEMUD_GRAPHICS_SDL;
+        } else {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "Unsupported graphics type %s", prop);
+            goto error;
+        }
+    }
+
+    /* analysis of the disk devices */
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/disk", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
+        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+            struct qemud_vm_disk_def *disk;
+            if (!(disk = qemudParseDiskXML(server, obj->nodesetval->nodeTab[i]))) {
+                goto error;
+            }
+            vm->def.ndisks++;
+            disk->next = vm->def.disks;
+            vm->def.disks = disk;
+        }
+    }
+    xmlXPathFreeObject(obj);
+
+
+    /* analysis of the network devices */
+    obj = xmlXPathEval(BAD_CAST "/domain/devices/interface", ctxt);
+    if ((obj != NULL) && (obj->type == XPATH_NODESET) &&
+        (obj->nodesetval != NULL) && (obj->nodesetval->nodeNr >= 0)) {
+        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+            struct qemud_vm_net_def *net;
+            if (!(net = qemudParseInterfaceXML(server, obj->nodesetval->nodeTab[i]))) {
+                goto error;
+            }
+            vm->def.nnets++;
+            net->next = vm->def.nets;
+            vm->def.nets = net;
+        }
+    }
+    xmlXPathFreeObject(obj);
+    xmlXPathFreeContext(ctxt);
+
+    return 0;
+
+ error:
+    if (prop)
+        free(prop);
+    if (obj)
+        xmlXPathFreeObject(obj);
+    if (ctxt)
+        xmlXPathFreeContext(ctxt);
+    return -1;
+}
+
+
+/*
+ * Constructs a argv suitable for launching qemu with config defined
+ * for a given virtual machine.
+ */
+int qemudBuildCommandLine(struct qemud_server *server,
+                          struct qemud_vm *vm,
+                          char ***argv,
+                          int *argc) {
+    int n = -1, i;
+    char memory[50];
+    char vcpus[50];
+    char boot[QEMUD_MAX_BOOT_DEVS+1];
+    struct qemud_vm_disk_def *disk = vm->def.disks;
+    struct qemud_vm_net_def *net = vm->def.nets;
+
+    *argc = 1 + /* qemu */
+        2 + /* machine type */
+        (vm->def.virtType == QEMUD_VIRT_QEMU ? 1 : 0) + /* Disable kqemu */
+        2 * vm->def.ndisks + /* disks*/
+        (vm->def.nnets > 0 ? (4 * vm->def.nnets) : 2) + /* networks */
+        2 + /* memory*/
+        2 + /* cpus */
+        2 + /* boot device */
+        2 + /* monitor */
+        (vm->def.features & QEMUD_FEATURE_ACPI ? 0 : 1) + /* acpi */
+        (vm->def.os.kernel[0] ? 2 : 0) + /* kernel */
+        (vm->def.os.initrd[0] ? 2 : 0) + /* initrd */
+        (vm->def.os.cmdline[0] ? 2 : 0) + /* cmdline */
+        (vm->def.graphicsType == QEMUD_GRAPHICS_VNC ? 2 :
+         (vm->def.graphicsType == QEMUD_GRAPHICS_SDL ? 0 : 1)); /* graphics */
+
+    sprintf(memory, "%d", vm->def.memory/1024);
+    sprintf(vcpus, "%d", vm->def.vcpus);
+
+    if (!(*argv = malloc(sizeof(char *) * (*argc +1))))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(vm->def.os.binary)))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup("-M")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(vm->def.os.machine)))
+        goto no_memory;
+    if (vm->def.virtType == QEMUD_VIRT_QEMU) {
+        if (!((*argv)[++n] = strdup("-no-kqemu")))
+        goto no_memory;
+    }
+    if (!((*argv)[++n] = strdup("-m")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(memory)))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup("-smp")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(vcpus)))
+        goto no_memory;
+
+    if (!((*argv)[++n] = strdup("-monitor")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup("pty")))
+        goto no_memory;
+
+    if (!(vm->def.features & QEMUD_FEATURE_ACPI)) {
+    if (!((*argv)[++n] = strdup("-no-acpi")))
+        goto no_memory;
+    }
+
+    for (i = 0 ; i < vm->def.os.nBootDevs ; i++) {
+        switch (vm->def.os.bootDevs[i]) {
+        case QEMUD_BOOT_CDROM:
+            boot[i] = 'd';
+            break;
+        case QEMUD_BOOT_FLOPPY:
+            boot[i] = 'a';
+            break;
+        case QEMUD_BOOT_DISK:
+            boot[i] = 'c';
+            break;
+        case QEMUD_BOOT_NET:
+            boot[i] = 'n';
+            break;
+        default:
+            boot[i] = 'c';
+            break;
+        }
+    }
+    boot[vm->def.os.nBootDevs] = '\0';
+    if (!((*argv)[++n] = strdup("-boot")))
+        goto no_memory;
+    if (!((*argv)[++n] = strdup(boot)))
+        goto no_memory;
+
+    if (vm->def.os.kernel[0]) {
+        if (!((*argv)[++n] = strdup("-kernel")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(vm->def.os.kernel)))
+            goto no_memory;
+    }
+    if (vm->def.os.initrd[0]) {
+        if (!((*argv)[++n] = strdup("-initrd")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(vm->def.os.initrd)))
+            goto no_memory;
+    }
+    if (vm->def.os.cmdline[0]) {
+        if (!((*argv)[++n] = strdup("-append")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(vm->def.os.cmdline)))
+            goto no_memory;
+    }
+
+    while (disk) {
+        char dev[NAME_MAX];
+        char file[PATH_MAX];
+        if (!strcmp(disk->dst, "hdc") &&
+            disk->device == QEMUD_DISK_CDROM)
+            snprintf(dev, NAME_MAX, "-%s", "cdrom");
+        else
+            snprintf(dev, NAME_MAX, "-%s", disk->dst);
+        snprintf(file, PATH_MAX, "%s", disk->src);
+
+        if (!((*argv)[++n] = strdup(dev)))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(file)))
+            goto no_memory;
+
+        disk = disk->next;
+    }
+
+    if (!net) {
+        if (!((*argv)[++n] = strdup("-net")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup("none")))
+            goto no_memory;
+    } else {
+        while (net) {
+            char nic[3+1+7+1+17+1];
+            sprintf(nic, "nic,macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
+                    net->mac[0], net->mac[1],
+                    net->mac[2], net->mac[3],
+                    net->mac[4], net->mac[5]);
+
+            if (!((*argv)[++n] = strdup("-net")))
+                goto no_memory;
+            if (!((*argv)[++n] = strdup(nic)))
+                goto no_memory;
+            if (!((*argv)[++n] = strdup("-net")))
+                goto no_memory;
+            /* XXX don't hardcode user */
+            if (!((*argv)[++n] = strdup("user")))
+                goto no_memory;
+
+            net = net->next;
+        }
+    }
+
+    if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) {
+        char port[10];
+        snprintf(port, 10, "%d", vm->def.vncActivePort - 5900);
+        if (!((*argv)[++n] = strdup("-vnc")))
+            goto no_memory;
+        if (!((*argv)[++n] = strdup(port)))
+            goto no_memory;
+    } else if (vm->def.graphicsType == QEMUD_GRAPHICS_NONE) {
+        if (!((*argv)[++n] = strdup("-nographic")))
+            goto no_memory;
+    } else {
+        /* SDL is the default. no args needed */
+    }
+
+    (*argv)[++n] = NULL;
+
+    return 0;
+
+ no_memory:
+    if (argv) {
+        for (i = 0 ; i < n ; i++)
+            free(argv[i]);
+        free(argv);
+    }
+    qemudReportError(server, VIR_ERR_NO_MEMORY, "argv");
+    return -1;
+}
+
+/* Free all memory associated with a struct qemud_vm object */
+void qemudFreeVM(struct qemud_vm *vm) {
+    struct qemud_vm_disk_def *disk = vm->def.disks;
+    struct qemud_vm_net_def *net = vm->def.nets;
+
+    while (disk) {
+        struct qemud_vm_disk_def *prev = disk;
+        disk = disk->next;
+        free(prev);
+    }
+    while (net) {
+        struct qemud_vm_net_def *prev = net;
+        net = net->next;
+        free(prev);
+    }
+
+    free(vm);
+}
+
+/* Build up a fully qualified path for a config file to be
+ * associated with a persistent guest */
+static
+int qemudMakeConfigPath(struct qemud_server *server,
+                        const char *name,
+                        const char *ext,
+                        char *buf,
+                        unsigned int buflen) {
+    if ((strlen(server->configDir) + 1 + strlen(name) + (ext ? strlen(ext) : 0) + 1) > buflen)
+        return -1;
+
+    strcpy(buf, server->configDir);
+    strcat(buf, "/");
+    strcat(buf, name);
+    if (ext)
+        strcat(buf, ext);
+    return 0;
+}
+
+
+/* Save a guest's config data into a persistent file */
+static int qemudSaveConfig(struct qemud_server *server,
+                           struct qemud_vm *vm) {
+    char *xml;
+    int fd = -1, ret = -1;
+    int towrite;
+    struct stat sb;
+
+    if (!(xml = qemudGenerateXML(server, vm))) {
+        return -1;
+    }
+
+    if (stat(server->configDir, &sb) < 0) {
+        if (errno == ENOENT) {
+            if (mkdir(server->configDir, 0700) < 0) {
+                qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                                 "cannot create config directory %s",
+                                 server->configDir);
+                return -1;
+            }
+        } else {
+            qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                             "cannot stat config directory %s",
+                             server->configDir);
+            return -1;
+        }
+    } else if (!S_ISDIR(sb.st_mode)) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "config directory %s is not a directory",
+                         server->configDir);
+        return -1;
+    }
+
+    if ((fd = open(vm->configFile,
+                   O_WRONLY | O_CREAT | O_TRUNC,
+                   S_IRUSR | S_IWUSR )) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "cannot create config file %s",
+                         vm->configFile);
+        goto cleanup;
+    }
+
+    towrite = strlen(xml);
+    if (write(fd, xml, towrite) != towrite) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "cannot write config file %s",
+                         vm->configFile);
+        goto cleanup;
+    }
+
+    if (close(fd) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                         "cannot save config file %s",
+                         vm->configFile);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    if (fd != -1)
+        close(fd);
+
+    free(xml);
+
+    return ret;
+}
+
+
+/* Create a qemud_vm instance, populating it based on the data
+ * in a libvirt XML document describing the guest */
+struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server,
+                                    const char *file,
+                                    const char *doc,
+                                    int save) {
+    struct qemud_vm *vm = NULL;
+    xmlDocPtr xml;
+
+    if (!(xml = xmlReadDoc(BAD_CAST doc, file ? file : "domain.xml", NULL,
+                           XML_PARSE_NOENT | XML_PARSE_NONET |
+                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
+        qemudReportError(server, VIR_ERR_XML_ERROR, NULL);
+        return NULL;
+    }
+
+    if (!(vm = calloc(1, sizeof(struct qemud_vm)))) {
+        qemudReportError(server, VIR_ERR_NO_MEMORY, "vm");
+        return NULL;
+    }
+
+    vm->stdout = -1;
+    vm->stderr = -1;
+    vm->monitor = -1;
+    vm->pid = -1;
+    vm->def.id = -1;
+
+    if (qemudParseXML(server, xml, vm) < 0) {
+        xmlFreeDoc(xml);
+        qemudFreeVM(vm);
+        return NULL;
+    }
+    xmlFreeDoc(xml);
+
+    if (qemudFindVMByUUID(server, vm->def.uuid)) {
+        qemudReportError(server, VIR_ERR_DOM_EXIST, vm->def.name);
+        qemudFreeVM(vm);
+        return NULL;
+    }
+
+    if (qemudFindVMByName(server, vm->def.name)) {
+        qemudReportError(server, VIR_ERR_DOM_EXIST, vm->def.name);
+        qemudFreeVM(vm);
+        return NULL;
+    }
+
+    if (file) {
+        strncpy(vm->configFile, file, PATH_MAX);
+        vm->configFile[PATH_MAX-1] = '\0';
+    } else {
+        if (save) {
+            if (qemudMakeConfigPath(server, vm->def.name, ".xml", vm->configFile, PATH_MAX) < 0) {
+                qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                                 "cannot construct config file path");
+                qemudFreeVM(vm);
+                return NULL;
+            }
+
+            if (qemudSaveConfig(server, vm) < 0) {
+                qemudReportError(server, VIR_ERR_INTERNAL_ERROR,
+                                 "cannot save config file for guest");
+                qemudFreeVM(vm);
+                return NULL;
+            }
+        } else {
+            vm->configFile[0] = '\0';
+        }
+    }
+
+    return vm;
+}
+
+
+/* Load a guest from its persistent config file */
+static void qemudLoadConfig(struct qemud_server *server,
+                            const char *file) {
+    FILE *fh;
+    struct stat st;
+    struct qemud_vm *vm;
+    char xml[QEMUD_MAX_XML_LEN];
+    int ret;
+
+    if (!(fh = fopen(file, "r"))) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open guest config file %s", file);
+        return;
+    }
+
+    if (fstat(fileno(fh), &st) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot stat config file %s", file);
+        goto cleanup;
+    }
+
+    if (st.st_size >= QEMUD_MAX_XML_LEN) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "guest config too large in file %s", file);
+        goto cleanup;
+    }
+
+    if ((ret = fread(xml, st.st_size, 1, fh)) != 1) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot read config file %s", file);
+        goto cleanup;
+    }
+    xml[st.st_size] = '\0';
+
+    if ((vm = qemudLoadConfigXML(server, file, xml, 1))) {
+        vm->next = server->inactivevms;
+        server->inactivevms = vm;
+        server->ninactivevms++;
+    }
+  
+ cleanup:
+    fclose(fh);
+}
+
+
+/* Scan for all guest config files */
+int qemudScanConfigs(struct qemud_server *server) {
+    DIR *dir;
+    struct dirent *entry;
+
+    if (!(dir = opendir(server->configDir))) {
+        if (errno == ENOENT)
+            return 0;
+        return -1;
+    }
+
+    while ((entry = readdir(dir))) {
+        char file[PATH_MAX];
+        if (entry->d_name[0] == '.')
+            continue;
+
+        if (qemudMakeConfigPath(server, entry->d_name, NULL, file, PATH_MAX) < 0)
+            continue;
+
+        qemudLoadConfig(server, file);
+    }
+
+    closedir(dir);
+    return 0;
+}
+
+
+/* Simple grow-on-demand string buffer */
+/* XXX re-factor to shared library */
+struct qemudBuffer {
+    char *data;
+    int len;
+    int used;
+};
+
+static
+int qemudBufferAdd(struct qemudBuffer *buf, const char *str) {
+    int need = strlen(str);
+  
+    if ((need+1) > (buf->len-buf->used)) {
+        return -1;
+    }
+  
+    memcpy(buf->data + buf->used, str, need+1);
+    buf->used += need;
+
+    return 0;
+}
+
+
+static
+int qemudBufferPrintf(struct qemudBuffer *buf,
+                      const char *format, ...) {
+    int size, count;
+    va_list locarg, argptr;
+
+    if ((format == NULL) || (buf == NULL)) {
+        return -1;
+    }
+    size = buf->len - buf->used - 1;
+    va_start(argptr, format);
+    va_copy(locarg, argptr);
+
+    if ((count = vsnprintf(&buf->data[buf->used],
+                           size,
+                           format,
+                           locarg)) >= size) {
+        return -1;
+    }
+    va_end(locarg);
+    buf->used += count;
+
+    buf->data[buf->used] = '\0';
+    return 0;
+}
+
+/* Generate an XML document describing the guest's configuration */
+char *qemudGenerateXML(struct qemud_server *server, struct qemud_vm *vm) {
+    struct qemudBuffer buf;
+    unsigned char *uuid;
+    struct qemud_vm_disk_def *disk;
+    struct qemud_vm_net_def *net;
+    const char *type = NULL;
+    int n;
+
+    buf.len = QEMUD_MAX_XML_LEN;
+    buf.used = 0;
+    buf.data = malloc(buf.len);
+
+    switch (vm->def.virtType) {
+    case QEMUD_VIRT_QEMU:
+        type = "qemu";
+        break;
+    case QEMUD_VIRT_KQEMU:
+        type = "kqemu";
+        break;
+    case QEMUD_VIRT_KVM:
+        type = "kvm";
+        break;
+    }
+    if (!type) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "unexpected domain type %d", vm->def.virtType);
+        goto cleanup;
+    }
+
+    if (vm->def.id >= 0) {
+        if (qemudBufferPrintf(&buf, "<domain type='%s' id='%d'>\n", type, vm->def.id) < 0)
+            goto no_memory;
+    } else {
+        if (qemudBufferPrintf(&buf, "<domain type='%s'>\n", type) < 0)
+            goto no_memory;
+    }
+
+    if (qemudBufferPrintf(&buf, "  <name>%s</name>\n", vm->def.name) < 0)
+        goto no_memory;
+
+    uuid = vm->def.uuid;
+    if (qemudBufferPrintf(&buf, "  <uuid>%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x</uuid>\n",
+                          uuid[0], uuid[1], uuid[2], uuid[3],
+                          uuid[4], uuid[5], uuid[6], uuid[7],
+                          uuid[8], uuid[9], uuid[10], uuid[11],
+                          uuid[12], uuid[13], uuid[14], uuid[15]) < 0)
+        goto no_memory;
+    if (qemudBufferPrintf(&buf, "  <memory>%d</memory>\n", vm->def.maxmem) < 0)
+        goto no_memory;
+    if (qemudBufferPrintf(&buf, "  <currentMemory>%d</currentMemory>\n", vm->def.memory) < 0)
+        goto no_memory;
+    if (qemudBufferPrintf(&buf, "  <vcpu>%d</vcpu>\n", vm->def.vcpus) < 0)
+        goto no_memory;
+
+    if (qemudBufferAdd(&buf, "  <os>\n") < 0)
+        goto no_memory;
+
+    if (vm->def.virtType == QEMUD_VIRT_QEMU) {
+        if (qemudBufferPrintf(&buf, "    <type arch='%s' machine='%s'>%s</type>\n",
+                              vm->def.os.arch, vm->def.os.machine, vm->def.os.type) < 0)
+            goto no_memory;
+    } else {
+        if (qemudBufferPrintf(&buf, "    <type>%s</type>\n", vm->def.os.type) < 0)
+            goto no_memory;
+    }
+
+    if (vm->def.os.kernel[0])
+        if (qemudBufferPrintf(&buf, "    <kernel>%s</kernel>\n", vm->def.os.kernel) < 0)
+            goto no_memory;
+    if (vm->def.os.initrd[0])
+        if (qemudBufferPrintf(&buf, "    <initrd>%s</initrd>\n", vm->def.os.initrd) < 0)
+            goto no_memory;
+    if (vm->def.os.cmdline[0])
+        if (qemudBufferPrintf(&buf, "    <cmdline>%s</cmdline>\n", vm->def.os.cmdline) < 0)
+            goto no_memory;
+
+    if (vm->def.features & QEMUD_FEATURE_ACPI) {
+        if (qemudBufferAdd(&buf, "  <features>\n") < 0)
+            goto no_memory;
+        if (qemudBufferAdd(&buf, "    <acpi>\n") < 0)
+            goto no_memory;
+        if (qemudBufferAdd(&buf, "  </features>\n") < 0)
+            goto no_memory;
+    }
+
+
+    for (n = 0 ; n < vm->def.os.nBootDevs ; n++) {
+        const char *boottype = "hd";
+        switch (vm->def.os.bootDevs[n]) {
+        case QEMUD_BOOT_FLOPPY:
+            boottype = "fd";
+            break;
+        case QEMUD_BOOT_DISK:
+            boottype = "hd";
+            break;
+        case QEMUD_BOOT_CDROM:
+            boottype = "cdrom";
+            break;
+        case QEMUD_BOOT_NET:
+            boottype = "net";
+            break;
+        }
+        if (qemudBufferPrintf(&buf, "    <boot dev='%s'/>\n", boottype) < 0)
+            goto no_memory;
+    }
+
+    if (qemudBufferAdd(&buf, "  </os>\n") < 0)
+        goto no_memory;
+
+    if (qemudBufferAdd(&buf, "  <devices>\n") < 0)
+        goto no_memory;
+
+    if (qemudBufferPrintf(&buf, "    <emulator>%s</emulator>\n", vm->def.os.binary) < 0)
+        goto no_memory;
+
+    disk = vm->def.disks;
+    while (disk) {
+        const char *types[] = {
+            "block",
+            "file",
+        };
+        const char *typeAttrs[] = {
+            "dev",
+            "file",
+        };
+        const char *devices[] = {
+            "disk",
+            "cdrom",
+            "floppy",
+        };
+        if (qemudBufferPrintf(&buf, "    <disk type='%s' device='%s'>\n",
+                              types[disk->type], devices[disk->device]) < 0)
+            goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "      <source %s='%s'/>\n", typeAttrs[disk->type], disk->src) < 0)
+            goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "      <target dev='%s'/>\n", disk->dst) < 0)
+            goto no_memory;
+
+        if (disk->readonly)
+            if (qemudBufferAdd(&buf, "      <readonly/>\n") < 0)
+                goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "    </disk>\n") < 0)
+            goto no_memory;
+
+        disk = disk->next;
+    }
+
+    net = vm->def.nets;
+    disk = vm->def.disks;
+    while (disk) {
+        const char *types[] = {
+            "user",
+            "tap",
+            "server",
+            "client",
+            "mcast",
+            "vde",
+        };
+        if (qemudBufferPrintf(&buf, "    <interface type='%s'>\n",
+                              types[net->type]) < 0)
+            goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "      <mac address='%02x:%02x:%02x:%02x:%02x:%02x'/>\n",
+                              net->mac[0], net->mac[1], net->mac[2],
+                              net->mac[3], net->mac[4], net->mac[5]) < 0)
+            goto no_memory;
+
+        if (qemudBufferPrintf(&buf, "    </interface>\n") < 0)
+            goto no_memory;
+
+        disk = disk->next;
+    }
+
+    if (vm->def.graphicsType == QEMUD_GRAPHICS_VNC) {
+        if (vm->def.vncPort) {
+            qemudBufferPrintf(&buf, "    <graphics type='vnc' port='%d'/>\n",
+                              vm->def.id == -1 ? vm->def.vncPort : vm->def.vncActivePort);
+        } else {
+            qemudBufferPrintf(&buf, "    <graphics type='vnc'/>\n");
+        }
+    }
+
+    if (qemudBufferAdd(&buf, "  </devices>\n") < 0)
+        goto no_memory;
+
+
+    if (qemudBufferAdd(&buf, "</domain>\n") < 0)
+        goto no_memory;
+
+    return buf.data;
+
+ no_memory:
+    qemudReportError(server, VIR_ERR_NO_MEMORY, "xml");
+ cleanup:
+    free(buf.data);
+    return NULL;
+}
+
+
+int qemudDeleteConfigXML(struct qemud_server *server, struct qemud_vm *vm) {
+    if (!vm->configFile[0]) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "no config file for guest %s", vm->def.name);
+        return -1;
+    }
+
+    if (unlink(vm->configFile) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot remove config for guest %s", vm->def.name);
+        return -1;
+    }
+
+    vm->configFile[0] = '\0';
+
+    return 0;
+}
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/config.h b/qemud/config.h
new file mode 100644 (file)
index 0000000..1ca1f3e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * config.h: VM configuration management
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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>
+ */
+
+#ifndef __QEMUD_CONFIG_H
+#define __QEMUD_CONFIG_H
+
+#include "internal.h"
+
+int qemudBuildCommandLine(struct qemud_server *server,
+                          struct qemud_vm *vm,
+                          char ***argv,
+                          int *argc);
+
+void qemudFreeVM(struct qemud_vm *vm);
+struct qemud_vm *qemudLoadConfigXML(struct qemud_server *server,
+                                    const char *file,
+                                    const char *doc,
+                                    int persist);
+int qemudScanConfigs(struct qemud_server *server);
+char *qemudGenerateXML(struct qemud_server *server,
+                       struct qemud_vm *vm);
+
+int qemudDeleteConfigXML(struct qemud_server *server,
+                         struct qemud_vm *vm);
+
+
+#endif
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/dispatch.c b/qemud/dispatch.c
new file mode 100644 (file)
index 0000000..f7fad2c
--- /dev/null
@@ -0,0 +1,580 @@
+/*
+ * dispatch.c: (De-)marshall wire messages to driver functions.
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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 <limits.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/utsname.h>
+#include <libvirt/virterror.h>
+
+#include "internal.h"
+#include "driver.h"
+#include "dispatch.h"
+
+
+static int qemudDispatchFailure(struct qemud_server *server ATTRIBUTE_UNUSED,
+                                struct qemud_client *client ATTRIBUTE_UNUSED,
+                                struct qemud_packet *out) {
+    out->header.type = QEMUD_PKT_FAILURE;
+    out->header.dataSize = sizeof(out->data.failureReply);
+    out->data.failureReply.code = server->errorCode;
+    strcpy(out->data.failureReply.message, server->errorMessage);
+    return 0;
+}
+
+
+static int qemudDispatchGetVersion(struct qemud_server *server, struct qemud_client *client,
+                                   struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != 0)
+        return -1;
+
+    int version = qemudGetVersion(server);
+    if (version < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_GET_VERSION;
+        out->header.dataSize = sizeof(out->data.getVersionReply);
+        out->data.getVersionReply.version = version;
+    }
+    return 0;
+}
+
+static int qemudDispatchGetNodeInfo(struct qemud_server *server, struct qemud_client *client,
+                                    struct qemud_packet *in, struct qemud_packet *out) {
+    struct utsname info;
+
+    if (in->header.dataSize != 0)
+        return -1;
+
+    if (uname(&info) < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+        return 0;
+    }
+
+    if (qemudGetCPUInfo(&out->data.getNodeInfoReply.cpus,
+                        &out->data.getNodeInfoReply.mhz,
+                        &out->data.getNodeInfoReply.nodes,
+                        &out->data.getNodeInfoReply.sockets,
+                        &out->data.getNodeInfoReply.cores,
+                        &out->data.getNodeInfoReply.threads) < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+        return 0;
+    }
+    if (qemudGetMemInfo(&out->data.getNodeInfoReply.memory) < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+        return 0;
+    }
+
+    out->header.type = QEMUD_PKT_GET_NODEINFO;
+    out->header.dataSize = sizeof(out->data.getNodeInfoReply);
+    strncpy(out->data.getNodeInfoReply.model, info.machine, sizeof(out->data.getNodeInfoReply.model));
+    out->data.getNodeInfoReply.model[sizeof(out->data.getNodeInfoReply.model)-1] = '\0';
+
+    return 0;
+}
+
+static int qemudDispatchListDomains(struct qemud_server *server, struct qemud_client *client,
+                                    struct qemud_packet *in, struct qemud_packet *out) {
+    int i, ndomains, domains[QEMUD_MAX_NUM_DOMAINS];
+    if (in->header.dataSize != 0)
+        return -1;
+
+    ndomains = qemudListDomains(server,
+                                domains,
+                                QEMUD_MAX_NUM_DOMAINS);
+    if (ndomains < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_LIST_DOMAINS;
+        out->header.dataSize = sizeof(out->data.listDomainsReply);
+        for (i = 0 ; i < ndomains ; i++) {
+            out->data.listDomainsReply.domains[i] = domains[i];
+        }
+        out->data.listDomainsReply.numDomains = ndomains;
+    }
+    return 0;
+}
+
+static int qemudDispatchNumDomains(struct qemud_server *server, struct qemud_client *client,
+                                   struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != 0)
+        return -1;
+
+    int ndomains = qemudNumDomains(server);
+    if (ndomains < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_NUM_DOMAINS;
+        out->header.dataSize = sizeof(out->data.numDomainsReply);
+        out->data.numDomainsReply.numDomains = ndomains;
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainCreate(struct qemud_server *server, struct qemud_client *client,
+                                     struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainCreateRequest))
+        return -1;
+
+    in->data.domainCreateRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0';
+
+    struct qemud_vm *vm = qemudDomainCreate(server, in->data.domainCreateRequest.xml);
+    if (!vm) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_CREATE;
+        out->header.dataSize = sizeof(out->data.domainCreateReply);
+        out->data.domainCreateReply.id = vm->def.id;
+        memcpy(out->data.domainCreateReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN);
+        strncpy(out->data.domainCreateReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1);
+        out->data.domainCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainLookupByID(struct qemud_server *server, struct qemud_client *client,
+                                         struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainLookupByIDRequest))
+        return -1;
+
+    struct qemud_vm *vm = qemudFindVMByID(server, in->data.domainLookupByIDRequest.id);
+    if (!vm) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_ID;
+        out->header.dataSize = sizeof(out->data.domainLookupByIDReply);
+        memcpy(out->data.domainLookupByIDReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN);
+        strncpy(out->data.domainLookupByIDReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1);
+        out->data.domainLookupByIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainLookupByUUID(struct qemud_server *server, struct qemud_client *client,
+                                           struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainLookupByUUIDRequest))
+        return -1;
+
+    struct qemud_vm *vm = qemudFindVMByUUID(server, in->data.domainLookupByUUIDRequest.uuid);
+    if (!vm) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID;
+        out->header.dataSize = sizeof(out->data.domainLookupByUUIDReply);
+        out->data.domainLookupByUUIDReply.id = vm->def.id;
+        strncpy(out->data.domainLookupByUUIDReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1);
+        out->data.domainLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainLookupByName(struct qemud_server *server, struct qemud_client *client,
+                                           struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainLookupByNameRequest))
+        return -1;
+
+    /* Paranoia NULL termination */
+    in->data.domainLookupByNameRequest.name[QEMUD_MAX_NAME_LEN-1] = '\0';
+    struct qemud_vm *vm = qemudFindVMByName(server, in->data.domainLookupByNameRequest.name);
+    if (!vm) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME;
+        out->header.dataSize = sizeof(out->data.domainLookupByNameReply);
+        out->data.domainLookupByNameReply.id = vm->def.id;
+        memcpy(out->data.domainLookupByNameReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN);
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainSuspend(struct qemud_server *server, struct qemud_client *client,
+                                      struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainSuspendRequest))
+        return -1;
+
+    int ret = qemudDomainSuspend(server, in->data.domainSuspendRequest.id);
+    if (ret < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_SUSPEND;
+        out->header.dataSize = 0;
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainResume(struct qemud_server *server, struct qemud_client *client,
+                                     struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainResumeRequest))
+        return -1;
+
+    int ret = qemudDomainResume(server, in->data.domainResumeRequest.id);
+    if (ret < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_RESUME;
+        out->header.dataSize = 0;
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainDestroy(struct qemud_server *server, struct qemud_client *client,
+                                      struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainDestroyRequest))
+        return -1;
+
+    int ret = qemudDomainDestroy(server, in->data.domainDestroyRequest.id);
+    if (ret < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_DESTROY;
+        out->header.dataSize = 0;
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainGetInfo(struct qemud_server *server, struct qemud_client *client,
+                                      struct qemud_packet *in, struct qemud_packet *out) {
+    int runstate;
+    unsigned long long cpuTime;
+    unsigned long memory;
+    unsigned long maxmem;
+    unsigned int nrVirtCpu;
+
+    if (in->header.dataSize != sizeof(in->data.domainGetInfoRequest))
+        return -1;
+
+    int ret = qemudDomainGetInfo(server, in->data.domainGetInfoRequest.uuid,
+                                 &runstate,
+                                 &cpuTime,
+                                 &maxmem,
+                                 &memory,
+                                 &nrVirtCpu);
+    if (ret < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_GET_INFO;
+        out->header.dataSize = sizeof(out->data.domainGetInfoReply);
+        out->data.domainGetInfoReply.runstate = runstate;
+        out->data.domainGetInfoReply.cpuTime = cpuTime;
+        out->data.domainGetInfoReply.maxmem = maxmem;
+        out->data.domainGetInfoReply.memory = memory;
+        out->data.domainGetInfoReply.nrVirtCpu = nrVirtCpu;
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainSave(struct qemud_server *server, struct qemud_client *client,
+                                   struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainSaveRequest))
+        return -1;
+
+    /* Paranoia NULL termination */
+    in->data.domainSaveRequest.file[PATH_MAX-1] ='\0';
+
+    int ret = qemudDomainSave(server,
+                              in->data.domainSaveRequest.id,
+                              in->data.domainSaveRequest.file);
+    if (ret < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_SAVE;
+        out->header.dataSize = 0;
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainRestore(struct qemud_server *server, struct qemud_client *client,
+                                      struct qemud_packet *in, struct qemud_packet *out) {
+    int id;
+    if (in->header.dataSize != sizeof(in->data.domainRestoreRequest))
+        return -1;
+
+    /* Paranoia null termination */
+    in->data.domainRestoreRequest.file[PATH_MAX-1] ='\0';
+
+    id = qemudDomainRestore(server, in->data.domainRestoreRequest.file);
+    if (id < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_RESTORE;
+        out->header.dataSize = sizeof(out->data.domainRestoreReply);
+        out->data.domainRestoreReply.id = id;
+    }
+    return 0;
+}
+
+static int qemudDispatchDumpXML(struct qemud_server *server, struct qemud_client *client,
+                                struct qemud_packet *in, struct qemud_packet *out) {
+    int ret;
+    if (in->header.dataSize != sizeof(in->data.domainDumpXMLRequest))
+        return -1;
+
+    ret = qemudDomainDumpXML(server,
+                             in->data.domainDumpXMLRequest.uuid,
+                             out->data.domainDumpXMLReply.xml,
+                             QEMUD_MAX_XML_LEN);
+    if (ret < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DUMP_XML;
+        out->header.dataSize = sizeof(out->data.domainDumpXMLReply);
+    }
+    return 0;
+}
+
+static int qemudDispatchListDefinedDomains(struct qemud_server *server, struct qemud_client *client,
+                                           struct qemud_packet *in, struct qemud_packet *out) {
+    char **names;
+    int i, ndomains;
+    if (in->header.dataSize != 0)
+        return -1;
+
+    if (!(names = malloc(sizeof(char *)*QEMUD_MAX_NUM_DOMAINS)))
+        return -1;
+
+    for (i = 0 ; i < QEMUD_MAX_NUM_DOMAINS ; i++) {
+        names[i] = out->data.listDefinedDomainsReply.domains[i];
+    }
+
+    ndomains = qemudListDefinedDomains(server,
+                                       names,
+                                       QEMUD_MAX_NUM_DOMAINS);
+    free(names);
+    if (ndomains < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_LIST_DEFINED_DOMAINS;
+        out->header.dataSize = sizeof(out->data.listDefinedDomainsReply);
+        out->data.listDefinedDomainsReply.numDomains = ndomains;
+    }
+    return 0;
+}
+
+static int qemudDispatchNumDefinedDomains(struct qemud_server *server, struct qemud_client *client,
+                                          struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != 0)
+        return -1;
+
+    int ndomains = qemudNumDefinedDomains(server);
+    if (ndomains < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_NUM_DEFINED_DOMAINS;
+        out->header.dataSize = sizeof(out->data.numDefinedDomainsReply);
+        out->data.numDefinedDomainsReply.numDomains = ndomains;
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainStart(struct qemud_server *server, struct qemud_client *client,
+                                    struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainStartRequest))
+        return -1;
+
+    struct qemud_vm *vm = qemudFindVMByUUID(server, in->data.domainStartRequest.uuid);
+    if (!vm || qemudDomainStart(server, vm) < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_START;
+        out->header.dataSize = sizeof(out->data.domainStartReply);
+        out->data.domainStartReply.id = vm->def.id;
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainDefine(struct qemud_server *server, struct qemud_client *client,
+                                     struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainDefineRequest))
+        return -1;
+
+    in->data.domainDefineRequest.xml[QEMUD_MAX_XML_LEN-1] ='\0';
+
+    struct qemud_vm *vm = qemudDomainDefine(server, in->data.domainDefineRequest.xml);
+    if (!vm) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_DEFINE;
+        out->header.dataSize = sizeof(out->data.domainDefineReply);
+        memcpy(out->data.domainDefineReply.uuid, vm->def.uuid, QEMUD_UUID_RAW_LEN);
+        strncpy(out->data.domainDefineReply.name, vm->def.name, QEMUD_MAX_NAME_LEN-1);
+        out->data.domainDefineReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
+    }
+    return 0;
+}
+
+static int qemudDispatchDomainUndefine(struct qemud_server *server, struct qemud_client *client,
+                                       struct qemud_packet *in, struct qemud_packet *out) {
+    if (in->header.dataSize != sizeof(in->data.domainUndefineRequest))
+        return -1;
+
+    int ret = qemudDomainUndefine(server, in->data.domainUndefineRequest.uuid);
+    if (ret < 0) {
+        if (qemudDispatchFailure(server, client, out) < 0)
+            return -1;
+    } else {
+        out->header.type = QEMUD_PKT_DOMAIN_UNDEFINE;
+        out->header.dataSize = 0;
+    }
+    return 0;
+}
+
+
+typedef int (*clientFunc)(struct qemud_server *server, struct qemud_client *client,
+                          struct qemud_packet *in, struct qemud_packet *out);
+
+
+/* One per message type recorded in qemud_packet_type enum */
+
+clientFunc funcsTransmitRW[QEMUD_PKT_MAX] = {
+    NULL, /* FAILURE code */
+    qemudDispatchGetVersion,
+    qemudDispatchGetNodeInfo,
+    qemudDispatchListDomains,
+    qemudDispatchNumDomains,
+    qemudDispatchDomainCreate,
+    qemudDispatchDomainLookupByID,
+    qemudDispatchDomainLookupByUUID,
+    qemudDispatchDomainLookupByName,
+    qemudDispatchDomainSuspend,
+    qemudDispatchDomainResume,
+    qemudDispatchDomainDestroy,
+    qemudDispatchDomainGetInfo,
+    qemudDispatchDomainSave,
+    qemudDispatchDomainRestore,
+    qemudDispatchDumpXML,
+    qemudDispatchListDefinedDomains,
+    qemudDispatchNumDefinedDomains,
+    qemudDispatchDomainStart,
+    qemudDispatchDomainDefine,
+    qemudDispatchDomainUndefine
+};
+
+clientFunc funcsTransmitRO[QEMUD_PKT_MAX] = {
+    NULL, /* FAILURE code */
+    qemudDispatchGetVersion,
+    qemudDispatchGetNodeInfo,
+    qemudDispatchListDomains,
+    qemudDispatchNumDomains,
+    NULL,
+    qemudDispatchDomainLookupByID,
+    qemudDispatchDomainLookupByUUID,
+    qemudDispatchDomainLookupByName,
+    NULL,
+    NULL,
+    NULL,
+    qemudDispatchDomainGetInfo,
+    NULL,
+    NULL,
+    qemudDispatchDumpXML,
+    qemudDispatchListDefinedDomains,
+    qemudDispatchNumDefinedDomains,
+    NULL,
+    NULL,
+    NULL,
+};
+
+/*
+ * Returns -1 if message processing failed - eg, illegal header sizes,
+ * a memory error dealing with stuff, or any other bad stuff which
+ * should trigger immediate client disconnect
+ *
+ * Return 0 if message processing succeeded. NB, this does not mean
+ * the operation itself succeeded - success/failure of the operation
+ * is recorded by the return message type - either it matches the
+ * incoming type, or is QEMUD_PKT_FAILURE
+ */
+int qemudDispatch(struct qemud_server *server, struct qemud_client *client,
+                  struct qemud_packet *in, struct qemud_packet *out) {
+    clientFunc *funcs;
+    unsigned int type = in->header.type;
+    QEMUD_DEBUG("> Dispatching request %d readonly ? %d\n", type, client->readonly);
+
+    server->errorCode = 0;
+    server->errorMessage[0] = '\0';
+
+    memset(out, 0, sizeof(struct qemud_packet));
+
+    if (type >= QEMUD_PKT_MAX) {
+        QEMUD_DEBUG("Illegal request type\n");
+        return -1;
+    }
+
+    if (type == QEMUD_PKT_FAILURE) {
+        QEMUD_DEBUG("Illegal request type\n");
+        return -1;
+    }
+
+    if (client->readonly)
+        funcs = funcsTransmitRO;
+    else
+        funcs = funcsTransmitRW;
+
+    if (!funcs[type]) {
+        QEMUD_DEBUG("Illegal operation requested\n");
+        qemudReportError(server, VIR_ERR_OPERATION_DENIED, NULL);
+        qemudDispatchFailure(server, client, out);
+    } else {
+        if ((funcs[type])(server, client, in, out) < 0) {
+            QEMUD_DEBUG("Dispatch failed\n");
+            return -1;
+        }
+    }
+
+    QEMUD_DEBUG("< Returning reply %d (%d bytes)\n",
+           out->header.type,
+           out->header.dataSize);
+
+    return 0;
+}
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/dispatch.h b/qemud/dispatch.h
new file mode 100644 (file)
index 0000000..9e1dbd7
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * dispatch.h: (De-)marshall wire messages to driver functions.
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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>
+ */
+
+
+#ifndef QEMUD_DISPATCH_H
+#define QEMUD_DISPATCH_H
+
+#include "internal.h"
+
+
+int qemudDispatch(struct qemud_server *server, struct qemud_client *client,
+                  struct qemud_packet *in, struct qemud_packet *out);
+
+#endif
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/driver.c b/qemud/driver.c
new file mode 100644 (file)
index 0000000..f3c725d
--- /dev/null
@@ -0,0 +1,554 @@
+/*
+ * driver.c: core driver methods for managing qemu guests
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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 <sys/types.h>
+#include <sys/poll.h>
+#include <dirent.h>
+#include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <strings.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <libvirt/virterror.h>
+
+#include "internal.h"
+#include "driver.h"
+#include "config.h"
+
+void qemudReportError(struct qemud_server *server,
+                      int code, const char *fmt, ...) {
+    va_list args;
+    server->errorCode = code;
+    if (fmt) {
+        va_start(args, fmt);
+        vsnprintf(server->errorMessage, QEMUD_MAX_ERROR_LEN-1, fmt, args);
+        va_end(args);
+    } else {
+        server->errorMessage[0] = '\0';
+    }
+}
+
+int qemudMonitorCommand(struct qemud_server *server ATTRIBUTE_UNUSED,
+                        struct qemud_vm *vm,
+                        const char *cmd,
+                        char **reply) {
+    int size = 0;
+    char *buf = NULL;
+    if (write(vm->monitor, cmd, strlen(cmd)) < 0) {
+        return -1;
+    }
+
+    *reply = NULL;
+
+    for (;;) {
+        struct pollfd fd = { vm->monitor, POLLIN | POLLERR | POLLHUP, 0 };
+        char *tmp;
+
+        /* Read all the data QEMU has sent thus far */
+        for (;;) {
+            char data[1024];
+            int got = read(vm->monitor, data, sizeof(data));
+            if (got < 0) {
+                if (errno == EINTR)
+                    continue;
+                if (errno == EAGAIN)
+                    break;
+
+                free(buf);
+                return -1;
+            }
+            if (!(buf = realloc(buf, size+got+1)))
+                return -1;
+            memmove(buf+size, data, got);
+            buf[size+got] = '\0';
+            size += got;
+        }
+        if (buf)
+            QEMUD_DEBUG("Mon [%s]\n", buf);
+        /* Look for QEMU prompt to indicate completion */
+        if (buf && ((tmp = strstr(buf, "\n(qemu)")) != NULL)) {
+            tmp[0] = '\0';
+            break;
+        }
+    pollagain:
+        /* Need to wait for more data */
+        if (poll(&fd, 1, -1) < 0) {
+            if (errno == EINTR)
+                goto pollagain;
+
+            free(buf);
+            return -1;
+        }
+    }
+
+    *reply = buf;
+    return 0;
+}
+
+int qemudGetMemInfo(unsigned int *memory) {
+    FILE *meminfo = fopen("/proc/meminfo", "r");
+    char line[1024];
+
+    *memory = 0;
+
+    if (!meminfo) {
+        return -1;
+    }
+
+    /* XXX NUMA and hyperthreads ? */
+    while (fgets(line, sizeof(line), meminfo) != NULL) {
+        if (!strncmp(line, "MemTotal:", 9)) {
+            *memory = (unsigned int)strtol(line + 10, NULL, 10);
+        }
+    }
+    fclose(meminfo);
+    return 0;
+}
+
+int qemudGetCPUInfo(unsigned int *cpus, unsigned int *mhz,
+                    unsigned int *nodes, unsigned int *sockets,
+                    unsigned int *cores, unsigned int *threads) {
+    FILE *cpuinfo = fopen("/proc/cpuinfo", "r");
+    char line[1024];
+
+    *cpus = 0;
+    *mhz = 0;
+    *nodes = *sockets = *cores = *threads = 1;
+
+    if (!cpuinfo) {
+        return -1;
+    }
+
+    /* XXX NUMA and hyperthreads ? */
+    while (fgets(line, sizeof(line), cpuinfo) != NULL) {
+        if (!strncmp(line, "processor\t", 10)) { /* aka a single logical CPU */
+            (*cpus)++;
+        } else if (!strncmp(line, "cpu MHz\t", 8)) {
+            char *offset = index(line, ':');
+            if (!offset)
+                continue;
+            offset++;
+            if (!*offset)
+                continue;
+            *mhz = (unsigned int)strtol(offset, NULL, 10);
+        } else if (!strncmp(line, "physical id\t", 12)) { /* aka socket */
+            unsigned int id;
+            char *offset = index(line, ':');
+            if (!offset)
+                continue;
+            offset++;
+            if (!*offset)
+                continue;
+            id = (unsigned int)strtol(offset, NULL, 10);
+            if ((id+1) > *sockets)
+                *sockets = (id + 1);
+        } else if (!strncmp(line, "cpu cores\t", 9)) { /* aka cores */
+            unsigned int id;
+            char *offset = index(line, ':');
+            if (!offset)
+                continue;
+            offset++;
+            if (!*offset)
+                continue;
+            id = (unsigned int)strtol(offset, NULL, 10);
+            if (id > *cores)
+                *cores = id;
+        }
+    }
+    fclose(cpuinfo);
+
+    return 0;
+}
+
+static int qemudGetProcessInfo(unsigned long long *cpuTime, int pid) {
+    char proc[PATH_MAX];
+    FILE *pidinfo;
+    unsigned long usertime, systime;
+
+    if (snprintf(proc, sizeof(proc), "/proc/%d/stat", pid) >= (int)sizeof(proc)) {
+        return -1;
+    }
+
+    if (!(pidinfo = fopen(proc, "r"))) {
+        /*printf("cannnot read pid info");*/
+        /* VM probably shut down, so fake 0 */
+        *cpuTime = 0;
+        return 0;
+    }
+
+    if (fscanf(pidinfo, "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %lu %lu", &usertime, &systime) != 2) {
+        QEMUD_DEBUG("not enough arg\n");
+        return -1;
+    }
+
+    /* We got jiffies
+     * We want nanoseconds
+     * _SC_CLK_TCK is jiffies per second
+     * So calulate thus....
+     */
+    *cpuTime = 1000 * 1000 * 1000 * (usertime + systime) / sysconf(_SC_CLK_TCK);
+
+    QEMUD_DEBUG("Got %lu %lu %lld\n", usertime, systime, *cpuTime);
+
+    fclose(pidinfo);
+
+    return 0;
+}
+
+struct qemud_vm *qemudFindVMByID(const struct qemud_server *server, int id) {
+    struct qemud_vm *vm = server->activevms;
+
+    while (vm) {
+        if (vm->def.id == id)
+            return vm;
+        vm = vm->next;
+    }
+
+    return NULL;
+}
+
+struct qemud_vm *qemudFindVMByUUID(const struct qemud_server *server,
+                                   const unsigned char *uuid) {
+    struct qemud_vm *vm = server->activevms;
+
+    while (vm) {
+        if (!memcmp(vm->def.uuid, uuid, QEMUD_UUID_RAW_LEN))
+            return vm;
+        vm = vm->next;
+    }
+
+    vm = server->inactivevms;
+    while (vm) {
+        if (!memcmp(vm->def.uuid, uuid, QEMUD_UUID_RAW_LEN))
+            return vm;
+        vm = vm->next;
+    }
+
+    return NULL;
+}
+
+struct qemud_vm *qemudFindVMByName(const struct qemud_server *server,
+                                   const char *name) {
+    struct qemud_vm *vm = server->activevms;
+
+    while (vm) {
+        if (!strcmp(vm->def.name, name))
+            return vm;
+        vm = vm->next;
+    }
+
+    vm = server->inactivevms;
+    while (vm) {
+        if (!strcmp(vm->def.name, name))
+            return vm;
+        vm = vm->next;
+    }
+
+    return NULL;
+}
+
+int qemudGetVersion(struct qemud_server *server) {
+    return server->qemuVersion;
+}
+
+int qemudListDomains(struct qemud_server *server, int *ids, int nids) {
+    struct qemud_vm *vm = server->activevms;
+    int got = 0;
+    while (vm && got < nids) {
+        ids[got] = vm->def.id;
+        vm = vm->next;
+        got++;
+    }
+    return got;
+}
+int qemudNumDomains(struct qemud_server *server) {
+    return server->nactivevms;
+}
+struct qemud_vm *qemudDomainCreate(struct qemud_server *server, const char *xml) {
+    struct qemud_vm *vm;
+
+    if (!(vm = qemudLoadConfigXML(server, NULL, xml, 0))) {
+        return NULL;
+    }
+
+    if (qemudStartVMDaemon(server, vm) < 0) {
+        qemudFreeVM(vm);
+        return NULL;
+    }
+
+    vm->next = server->activevms;
+    server->activevms = vm;
+    server->nactivevms++;
+    server->nvmfds += 2;
+
+    return vm;
+}
+
+
+int qemudDomainSuspend(struct qemud_server *server, int id) {
+    char *info;
+    struct qemud_vm *vm = qemudFindVMByID(server, id);
+    if (!vm) {
+        qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id);
+        return -1;
+    }
+    if (vm->pid == -1) {
+        qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running");
+        return -1;
+    }
+
+    if (qemudMonitorCommand(server, vm, "stop\n", &info) < 0) {
+        qemudReportError(server, VIR_ERR_OPERATION_FAILED, "suspend operation failed");
+        return -1;
+    }
+    printf("Reply %s\n", info);
+    free(info);
+    return 0;
+}
+
+
+int qemudDomainResume(struct qemud_server *server, int id) {
+    char *info;
+    struct qemud_vm *vm = qemudFindVMByID(server, id);
+    if (!vm) {
+        qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id);
+        return -1;
+    }
+    if (vm->pid == -1) {
+        qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running");
+        return -1;
+    }
+    if (qemudMonitorCommand(server, vm, "cont\n", &info) < 0) {
+        qemudReportError(server, VIR_ERR_OPERATION_FAILED, "resume operation failed");
+        return -1;
+    }
+    printf("Reply %s\n", info);
+    free(info);
+    return -1;
+}
+
+
+int qemudDomainDestroy(struct qemud_server *server, int id) {
+    struct qemud_vm *vm = qemudFindVMByID(server, id);
+    if (!vm) {
+        qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id);
+        return -1;
+    }
+    if (vm->pid == -1) {
+        qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running");
+        return -1;
+    }
+
+    if (qemudShutdownVMDaemon(server, vm) < 0)
+        return -1;
+    return 0;
+}
+
+
+int qemudDomainGetInfo(struct qemud_server *server, const unsigned char *uuid,
+                       int *runstate,
+                       unsigned long long *cputime,
+                       unsigned long *maxmem,
+                       unsigned long *memory,
+                       unsigned int *nrVirtCpu) {
+    struct qemud_vm *vm = qemudFindVMByUUID(server, uuid);
+    if (!vm) {
+        qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid");
+        return -1;
+    }
+
+    if (vm->pid == -1) {
+        *runstate = QEMUD_STATE_STOPPED;
+    } else {
+        /* XXX in future need to add PAUSED */
+        *runstate = QEMUD_STATE_RUNNING;
+    }
+
+    if (vm->pid == -1) {
+        *cputime = 0;
+    } else {
+        if (qemudGetProcessInfo(cputime, vm->pid) < 0) {
+            qemudReportError(server, VIR_ERR_OPERATION_FAILED, "cannot read cputime for domain");
+            return -1;
+        }
+    }
+
+    *maxmem = vm->def.maxmem;
+    *memory = vm->def.memory;
+    *nrVirtCpu = vm->def.vcpus;
+    return 0;
+}
+
+
+int qemudDomainSave(struct qemud_server *server, int id,
+                    const char *path ATTRIBUTE_UNUSED) {
+    struct qemud_vm *vm = qemudFindVMByID(server, id);
+    if (!vm) {
+        qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching id %d", id);
+        return -1;
+    }
+    if (vm->pid == -1) {
+        qemudReportError(server, VIR_ERR_OPERATION_FAILED, "domain is not running");
+        return -1;
+    }
+    qemudReportError(server, VIR_ERR_OPERATION_FAILED, "save is not supported");
+    return -1;
+}
+
+
+int qemudDomainRestore(struct qemud_server *server,
+                       const char *path ATTRIBUTE_UNUSED) {
+    qemudReportError(server, VIR_ERR_OPERATION_FAILED, "restore is not supported");
+    return -1;
+}
+
+
+int qemudDomainDumpXML(struct qemud_server *server, const unsigned char *uuid, char *xml, int xmllen) {
+    struct qemud_vm *vm = qemudFindVMByUUID(server, uuid);
+    char *vmxml;
+    if (!vm) {
+        qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid");
+        return -1;
+    }
+
+    vmxml = qemudGenerateXML(server, vm);
+    if (!vmxml)
+        return -1;
+
+    strncpy(xml, vmxml, xmllen);
+    xml[xmllen-1] = '\0';
+
+    return 0;
+}
+
+
+int qemudListDefinedDomains(struct qemud_server *server, char *const*names, int nnames) {
+    struct qemud_vm *vm = server->inactivevms;
+    int got = 0;
+    while (vm && got < nnames) {
+        strncpy(names[got], vm->def.name, QEMUD_MAX_NAME_LEN-1);
+        names[got][QEMUD_MAX_NAME_LEN-1] = '\0';
+        vm = vm->next;
+        got++;
+    }
+    return got;
+}
+
+
+int qemudNumDefinedDomains(struct qemud_server *server) {
+    return server->ninactivevms;
+}
+
+
+int qemudDomainStart(struct qemud_server *server, struct qemud_vm *vm) {
+    struct qemud_vm *prev = NULL, *curr = server->inactivevms;
+    if (qemudStartVMDaemon(server, vm) < 0) {
+        return 1;
+    }
+
+    while (curr) {
+        if (curr == vm) {
+            if (prev)
+                prev->next = curr->next;
+            else
+                server->inactivevms = curr->next;
+            server->ninactivevms--;
+            break;
+        }
+        prev = curr;
+        curr = curr->next;
+    }
+
+    vm->next = server->activevms;
+    server->activevms = vm;
+    server->nactivevms++;
+    server->nvmfds += 2;
+
+    return 0;
+}
+
+
+struct qemud_vm *qemudDomainDefine(struct qemud_server *server, const char *xml) {
+    struct qemud_vm *vm;
+
+    if (!(vm = qemudLoadConfigXML(server, NULL, xml, 1))) {
+        return NULL;
+    }
+
+    vm->next = server->inactivevms;
+    server->inactivevms = vm;
+    server->ninactivevms++;
+
+    return vm;
+}
+
+int qemudDomainUndefine(struct qemud_server *server, const unsigned char *uuid) {
+    struct qemud_vm *vm = qemudFindVMByUUID(server, uuid);
+    struct qemud_vm *prev = NULL, *curr = server->inactivevms;
+
+    if (!vm) {
+        qemudReportError(server, VIR_ERR_INVALID_DOMAIN, "no domain with matching uuid");
+        return -1;
+    }
+
+    if (vm->pid != -1) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot delete active domain");
+        return -1;
+    }
+
+    if (qemudDeleteConfigXML(server, vm) < 0)
+        return -1;
+
+    while (curr) {
+        if (curr == vm) {
+            if (prev) {
+                prev->next = curr->next;
+            } else {
+                server->inactivevms = curr->next;
+            }
+            server->ninactivevms--;
+            break;
+        }
+
+        prev = curr;
+        curr = curr->next;
+    }
+
+    qemudFreeVM(vm);
+
+    return 0;
+}
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/driver.h b/qemud/driver.h
new file mode 100644 (file)
index 0000000..1b264c3
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * driver.h: core driver methods for managing qemu guests
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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>
+ */
+
+
+#ifndef QEMUD_DRIVER_H
+#define QEMUD_DRIVER_H
+
+#include "internal.h"
+
+void qemudReportError(struct qemud_server *server,
+                      int code, const char *fmt, ...);
+
+int qemudGetCPUInfo(unsigned int *cpus, unsigned int *mhz,
+                    unsigned int *nodes, unsigned int *sockets,
+                    unsigned int *cores, unsigned int *threads);
+int qemudGetMemInfo(unsigned int *memory);
+int qemudMonitorCommand(struct qemud_server *server,
+                        struct qemud_vm *vm,
+                        const char *cmd,
+                        char **reply);
+
+struct qemud_vm *qemudFindVMByID(const struct qemud_server *server,
+                                 int id);
+struct qemud_vm *qemudFindVMByUUID(const struct qemud_server *server,
+                                   const unsigned char *uuid);
+struct qemud_vm *qemudFindVMByName(const struct qemud_server *server,
+                                   const char *name);
+
+int qemudGetVersion(struct qemud_server *server);
+int qemudListDomains(struct qemud_server *server,
+                     int *ids,
+                     int nids);
+int qemudNumDomains(struct qemud_server *server);
+struct qemud_vm *qemudDomainCreate(struct qemud_server *server,
+                                   const char *xml);
+int qemudDomainSuspend(struct qemud_server *server,
+                       int id);
+int qemudDomainResume(struct qemud_server *server,
+                      int id);
+int qemudDomainDestroy(struct qemud_server *server,
+                       int id);
+int qemudDomainGetInfo(struct qemud_server *server,
+                       const unsigned char *uuid,
+                       int *runstate,
+                       unsigned long long *cputime,
+                       unsigned long *maxmem,
+                       unsigned long *memory,
+                       unsigned int *nrVirtCpu);
+int qemudDomainSave(struct qemud_server *server,
+                    int id,
+                    const char *path);
+int qemudDomainRestore(struct qemud_server *server,
+                       const char *path);
+int qemudDomainDumpXML(struct qemud_server *server,
+                       const unsigned char *uuid,
+                       char *xml,
+                       int xmllen);
+int qemudListDefinedDomains(struct qemud_server *server,
+                            char *const*names,
+                            int nnames);
+int qemudNumDefinedDomains(struct qemud_server *server);
+int qemudDomainStart(struct qemud_server *server,
+                     struct qemud_vm *vm);
+struct qemud_vm *qemudDomainDefine(struct qemud_server *server,
+                                   const char *xml);
+int qemudDomainUndefine(struct qemud_server *server,
+                        const unsigned char *uuid);
+
+#endif
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/internal.h b/qemud/internal.h
new file mode 100644 (file)
index 0000000..b308f7c
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * internal.h: daemon data structure definitions
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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>
+ */
+
+
+#ifndef QEMUD_INTERNAL_H__
+#define QEMUD_INTERNAL_H__
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <gnutls/gnutls.h>
+
+#include "protocol.h"
+
+#ifdef __GNUC__
+#ifdef HAVE_ANSIDECL_H
+#include <ansidecl.h>
+#endif
+#ifndef ATTRIBUTE_UNUSED
+#define ATTRIBUTE_UNUSED __attribute__((unused))
+#endif
+#else
+#define ATTRIBUTE_UNUSED
+#endif
+
+#ifdef DEBUG
+#define QEMUD_DEBUG(args...) fprintf(stderr, args)
+#else
+#define QEMUD_DEBUG(args...) do {} while(0)
+#endif
+
+#define UUID_LEN 16
+
+/* Different types of QEMU acceleration possible */
+enum qemud_vm_virt_type {
+    QEMUD_VIRT_QEMU,
+    QEMUD_VIRT_KQEMU,
+    QEMUD_VIRT_KVM,
+};
+
+
+/* Two types of disk backends */
+enum qemud_vm_disk_type {
+    QEMUD_DISK_BLOCK,
+    QEMUD_DISK_FILE
+};
+
+/* Three types of disk frontend */
+enum qemud_vm_disk_device {
+    QEMUD_DISK_DISK,
+    QEMUD_DISK_CDROM,
+    QEMUD_DISK_FLOPPY,
+};
+
+/* Stores the virtual disk configuration */
+struct qemud_vm_disk_def {
+    int type;
+    int device;
+    char src[PATH_MAX];
+    char dst[NAME_MAX];
+    int readonly;
+
+    struct qemud_vm_disk_def *next;
+};
+
+#define QEMUD_MAC_ADDRESS_LEN 6
+#define QEMUD_OS_TYPE_MAX_LEN 10
+#define QEMUD_OS_ARCH_MAX_LEN 10
+#define QEMUD_OS_MACHINE_MAX_LEN 10
+
+/* 5 different types of networking config */
+enum qemud_vm_net_type {
+    QEMUD_NET_USER,
+    QEMUD_NET_TAP,
+    QEMUD_NET_SERVER,
+    QEMUD_NET_CLIENT,
+    QEMUD_NET_MCAST,
+    /*  QEMUD_NET_VDE*/
+};
+
+/* Stores the virtual network interface configuration */
+struct qemud_vm_net_def {
+    int type;
+    int vlan;
+    unsigned char mac[QEMUD_MAC_ADDRESS_LEN];
+    union {
+        struct {
+            char ifname[NAME_MAX];
+            char script[PATH_MAX];
+        } tap;
+        struct {
+            struct sockaddr_in listen;
+            int port;
+        } server;
+        struct {
+            struct sockaddr_in connect;
+            int port;
+        } client;
+        struct {
+            struct sockaddr_in group;
+            int port;
+        } mcast;
+        struct {
+            char vlan[PATH_MAX];
+        } vde;
+    } dst;
+
+    struct qemud_vm_net_def *next;
+};
+
+#define QEMUD_MAX_BOOT_DEVS 4
+
+/* 3 possible boot devices */
+enum qemud_vm_boot_order {
+    QEMUD_BOOT_FLOPPY,
+    QEMUD_BOOT_CDROM,
+    QEMUD_BOOT_DISK,
+    QEMUD_BOOT_NET,
+};
+/* 3 possible graphics console modes */
+enum qemud_vm_grapics_type {
+    QEMUD_GRAPHICS_NONE,
+    QEMUD_GRAPHICS_SDL,
+    QEMUD_GRAPHICS_VNC,
+};
+
+enum qemud_vm_features {
+    QEMUD_FEATURE_ACPI = 1,
+};
+
+/* Operating system configuration data & machine / arch */
+struct qemud_vm_os_def {
+    char type[QEMUD_OS_TYPE_MAX_LEN];
+    char arch[QEMUD_OS_ARCH_MAX_LEN];
+    char machine[QEMUD_OS_MACHINE_MAX_LEN];
+    int nBootDevs;
+    int bootDevs[QEMUD_MAX_BOOT_DEVS];
+    char kernel[PATH_MAX];
+    char initrd[PATH_MAX];
+    char cmdline[PATH_MAX];
+    char binary[PATH_MAX];
+};
+
+/* Guest VM main configuration */
+struct qemud_vm_def {
+    int id;
+    int virtType;
+    unsigned char uuid[QEMUD_UUID_RAW_LEN];
+    char name[QEMUD_MAX_NAME_LEN];
+
+    int memory;
+    int maxmem;
+    int vcpus;
+
+    struct qemud_vm_os_def os;
+
+    int features;
+    int graphicsType;
+    int vncPort;
+    int vncActivePort;
+
+    int ndisks;
+    struct qemud_vm_disk_def *disks;
+
+    int nnets;
+    struct qemud_vm_net_def *nets;
+};
+
+/* Guest VM runtime state */
+struct qemud_vm {
+    int stdout;
+    int stderr;
+    int monitor;
+    int pid;
+
+    char configFile[PATH_MAX];
+
+    struct qemud_vm_def def;
+
+    struct qemud_vm *next;
+};
+
+/* Stores the per-client connection state */
+struct qemud_client {
+    int fd;
+    int readonly;
+    struct qemud_packet incoming;
+    unsigned int incomingReceived;
+    struct qemud_packet outgoing;
+    unsigned int outgoingSent;
+    int tx;
+    struct qemud_client *next;
+};
+
+
+struct qemud_socket {
+    int fd;
+    int readonly;
+    struct qemud_socket *next;
+};
+
+/* Main server state */
+struct qemud_server {
+    int nsockets;
+    struct qemud_socket *sockets;
+    int qemuVersion;
+    int nclients;
+    struct qemud_client *clients;
+    int nvmfds;
+    int nactivevms;
+    struct qemud_vm *activevms;
+    int ninactivevms;
+    struct qemud_vm *inactivevms;
+    int nextvmid;
+    char configDir[PATH_MAX];
+    char errorMessage[QEMUD_MAX_ERROR_LEN];
+    int errorCode;
+};
+
+int qemudStartVMDaemon(struct qemud_server *server,
+                       struct qemud_vm *vm);
+
+int qemudShutdownVMDaemon(struct qemud_server *server,
+                          struct qemud_vm *vm);
+
+#endif
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/protocol.h b/qemud/protocol.h
new file mode 100644 (file)
index 0000000..85392ba
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * protocol.h: wire protocol message format & data structures
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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>
+ */
+
+
+#ifndef QEMUD_PROTOCOL_H__
+#define QEMUD_PROTOCOL_H__
+
+#include <stdint.h>
+
+/* List of different packet types which can be sent */
+enum {
+    QEMUD_PKT_FAILURE = 0,
+    QEMUD_PKT_GET_VERSION,
+    QEMUD_PKT_GET_NODEINFO,
+    QEMUD_PKT_LIST_DOMAINS,
+    QEMUD_PKT_NUM_DOMAINS,
+    QEMUD_PKT_DOMAIN_CREATE,
+    QEMUD_PKT_DOMAIN_LOOKUP_BY_ID,
+    QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID,
+    QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME,
+    QEMUD_PKT_DOMAIN_SUSPEND,
+    QEMUD_PKT_DOMAIN_RESUME,
+    QEMUD_PKT_DOMAIN_DESTROY,
+    QEMUD_PKT_DOMAIN_GET_INFO,
+    QEMUD_PKT_DOMAIN_SAVE,
+    QEMUD_PKT_DOMAIN_RESTORE,
+    QEMUD_PKT_DUMP_XML,
+    QEMUD_PKT_LIST_DEFINED_DOMAINS,
+    QEMUD_PKT_NUM_DEFINED_DOMAINS,
+    QEMUD_PKT_DOMAIN_START,
+    QEMUD_PKT_DOMAIN_DEFINE,
+    QEMUD_PKT_DOMAIN_UNDEFINE,
+
+    QEMUD_PKT_MAX,
+} qemud_packet_type;
+
+
+#define QEMUD_PROTOCOL_VERSION_MAJOR 1
+#define QEMUD_PROTOCOL_VERSION_MINOR 0
+
+#define QEMUD_UUID_RAW_LEN 16
+#define QEMUD_MAX_NAME_LEN 50
+#define QEMUD_MAX_XML_LEN 4096
+#define QEMUD_MAX_NUM_DOMAINS 100
+#define QEMUD_MAX_ERROR_LEN 1024
+
+/* Possible guest VM states */
+enum {
+    QEMUD_STATE_RUNNING = 1,
+    QEMUD_STATE_PAUSED,
+    QEMUD_STATE_STOPPED,
+} qemud_domain_runstate;
+
+/* Each packets has at least a fixed size header.
+ *
+ * All data required to be network byte order
+ * to 32-bit boundaries */
+struct qemud_packet_header {
+    uint32_t type;
+    /* Stores the size of the data struct matching
+       the type arg.
+       Must be <= sizeof(union qemudPacketData) */
+    uint32_t dataSize;
+};
+
+/* Most packets also have some message specific data
+ * All data required to be network byte order, padded
+ * to 32-bit boundaries */
+union qemud_packet_data {
+    struct {
+        int32_t code;
+        char message[QEMUD_MAX_ERROR_LEN];
+    } failureReply;
+    struct {
+        int32_t version;
+    } getVersionReply;
+    struct {
+        char model[32];
+        uint32_t memory;
+        uint32_t cpus;
+        uint32_t mhz;
+        uint32_t nodes;
+        uint32_t sockets;
+        uint32_t cores;
+        uint32_t threads;
+    } getNodeInfoReply;
+    struct {
+        int32_t numDomains;
+        int32_t domains[QEMUD_MAX_NUM_DOMAINS];
+    } listDomainsReply;
+    struct {
+        int32_t numDomains;
+    } numDomainsReply;
+    struct {
+        char xml[QEMUD_MAX_XML_LEN];
+    } domainCreateRequest;
+    struct {
+        int32_t id;
+        unsigned char uuid[QEMUD_UUID_RAW_LEN];
+        char name[QEMUD_MAX_NAME_LEN];
+    } domainCreateReply;
+    struct {
+        int32_t id;
+    } domainLookupByIDRequest;
+    struct {
+        unsigned char uuid[QEMUD_UUID_RAW_LEN];
+        char name[QEMUD_MAX_NAME_LEN];
+    } domainLookupByIDReply;
+    struct {
+        char name[QEMUD_MAX_NAME_LEN];
+    } domainLookupByNameRequest;
+    struct {
+        int32_t id;
+        unsigned char uuid[QEMUD_UUID_RAW_LEN];
+    } domainLookupByNameReply;
+    struct {
+        unsigned char uuid[QEMUD_UUID_RAW_LEN];
+    } domainLookupByUUIDRequest;
+    struct {
+        int32_t id;
+        char name[QEMUD_MAX_NAME_LEN];
+    } domainLookupByUUIDReply;
+    struct {
+        int32_t id;
+    } domainSuspendRequest;
+    struct {
+        int32_t id;
+    } domainResumeRequest;
+    struct {
+    } domainResumeReply;
+    struct {
+        int32_t id;
+    } domainDestroyRequest;
+    struct {
+        unsigned char uuid[QEMUD_UUID_RAW_LEN];
+    } domainGetInfoRequest;
+    struct {
+        uint64_t cpuTime;
+        int32_t runstate;
+        uint32_t memory;
+        uint32_t maxmem;
+        uint32_t nrVirtCpu;
+    } domainGetInfoReply;
+    struct {
+        int32_t id;
+        char file[PATH_MAX];
+    } domainSaveRequest;
+    struct {
+        char file[PATH_MAX];
+    } domainRestoreRequest;
+    struct {
+        int32_t id;
+    } domainRestoreReply;
+    struct {
+        unsigned char uuid[QEMUD_UUID_RAW_LEN];
+    } domainDumpXMLRequest;
+    struct {
+        char xml[QEMUD_MAX_XML_LEN];
+    } domainDumpXMLReply;
+    struct {
+        int32_t numDomains;
+        char domains[QEMUD_MAX_NUM_DOMAINS][QEMUD_MAX_NAME_LEN];
+    } listDefinedDomainsReply;
+    struct {
+        int32_t numDomains;
+    } numDefinedDomainsReply;
+    struct {
+        unsigned char uuid[QEMUD_UUID_RAW_LEN];
+    } domainStartRequest;
+    struct {
+        int32_t id;
+    } domainStartReply;
+    struct {
+        char xml[QEMUD_MAX_XML_LEN];
+    } domainDefineRequest;
+    struct {
+        unsigned char uuid[QEMUD_UUID_RAW_LEN];
+        char name[QEMUD_MAX_NAME_LEN];
+    } domainDefineReply;
+    struct {
+        unsigned char uuid[QEMUD_UUID_RAW_LEN];
+    } domainUndefineRequest;
+};
+
+/* Each packet has header & data */
+struct qemud_packet {
+    struct qemud_packet_header header;
+    union qemud_packet_data data;
+};
+
+
+#endif
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/qemud/qemud.c b/qemud/qemud.c
new file mode 100644 (file)
index 0000000..a916b80
--- /dev/null
@@ -0,0 +1,958 @@
+/*
+ * qemud.c: daemon start of day, guest process & i/o management
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006 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 <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/poll.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include <libvirt/virterror.h>
+
+#include <gnutls/x509.h>
+
+#include "internal.h"
+#include "dispatch.h"
+#include "driver.h"
+#include "config.h"
+
+static void reapchild(int sig ATTRIBUTE_UNUSED) {
+    /* We explicitly waitpid the child later */
+}
+static int qemudSetCloseExec(int fd) {
+    int flags;
+    if ((flags = fcntl(fd, F_GETFD)) < 0) {
+        return -1;
+    }
+    flags |= FD_CLOEXEC;
+    if ((fcntl(fd, F_SETFD, flags)) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+
+static int qemudSetNonBlock(int fd) {
+    int flags;
+    if ((flags = fcntl(fd, F_GETFL)) < 0) {
+        return -1;
+    }
+    flags |= O_NONBLOCK;
+    if ((fcntl(fd, F_SETFL, flags)) < 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static int qemudGoDaemon(void) {
+    int pid = fork();
+    switch (pid) {
+    case 0:
+        {
+            int stdinfd = -1;
+            int stdoutfd = -1;
+            int i, open_max, nextpid;
+
+            if ((stdinfd = open(_PATH_DEVNULL, O_RDONLY)) < 0)
+                goto cleanup;
+            if ((stdoutfd = open(_PATH_DEVNULL, O_WRONLY)) < 0)
+                goto cleanup;
+            if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
+                goto cleanup;
+            if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO)
+                goto cleanup;
+            if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO)
+                goto cleanup;
+            if (close(stdinfd) < 0)
+                goto cleanup;
+            stdinfd = -1;
+            if (close(stdoutfd) < 0)
+                goto cleanup;
+            stdoutfd = -1;
+
+            open_max = sysconf (_SC_OPEN_MAX);
+            for (i = 0; i < open_max; i++)
+                if (i != STDIN_FILENO &&
+                    i != STDOUT_FILENO &&
+                    i != STDERR_FILENO)
+                    close(i);
+
+            if (setsid() < 0)
+                goto cleanup;
+
+            nextpid = fork();
+            switch (nextpid) {
+            case 0:
+                return 0;
+            case -1:
+                return -1;
+            default:
+                return nextpid;
+            }
+
+        cleanup:
+            if (stdoutfd != -1)
+                close(stdoutfd);
+            if (stdinfd != -1)
+                close(stdinfd);
+            return -1;
+
+        }
+
+    case -1:
+        return -1;
+
+    default:
+        {
+            int got, status = 0;
+            /* We wait to make sure the next child forked
+               successfully */
+            if ((got = waitpid(pid, &status, 0)) < 0 ||
+                got != pid ||
+                status != 0) {
+                return -1;
+            }
+      
+            return pid;
+        }
+    }
+}
+
+static int qemudListenUnix(struct qemud_server *server,
+                           const char *path, int readonly) {
+    struct qemud_socket *sock = calloc(1, sizeof(struct qemud_socket));
+    struct sockaddr_un addr;
+    mode_t oldmask;
+
+    if (!sock)
+        return -1;
+
+    sock->readonly = readonly;
+    sock->next = server->sockets;
+    server->sockets = sock;
+    server->nsockets++;
+
+    if ((sock->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+        return -1;
+
+    if (qemudSetCloseExec(sock->fd) < 0)
+        return -1;
+    if (qemudSetNonBlock(sock->fd) < 0)
+        return -1;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
+    if (addr.sun_path[0] == '@')
+        addr.sun_path[0] = '\0';
+
+
+    if (readonly)
+        oldmask = umask(~(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH));
+    else
+        oldmask = umask(~(S_IRUSR | S_IWUSR));
+    if (bind(sock->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+        return -1;
+    umask(oldmask);
+
+    if (listen(sock->fd, 30) < 0)
+        return -1;
+
+    return 0;
+}
+
+static int qemudListen(struct qemud_server *server, int sys) {
+    char sockname[PATH_MAX];
+
+    if (sys) {
+        if (snprintf(sockname, sizeof(sockname), "%s/run/qemud/sock", LOCAL_STATE_DIR) >= (int)sizeof(sockname)) {
+            return -1;
+        }
+        unlink(sockname);
+        if (qemudListenUnix(server, sockname, 0) < 0)
+            return -1;
+
+
+        if (snprintf(sockname, sizeof(sockname), "%s/run/qemud/sock-ro", LOCAL_STATE_DIR) >= (int)sizeof(sockname)) {
+            return -1;
+        }
+        unlink(sockname);
+        if (qemudListenUnix(server, sockname, 1) < 0)
+            return -1;
+    } else {
+        struct passwd *pw;
+        int uid;
+
+        if ((uid = geteuid()) < 0) {
+            return -1;
+        }
+
+        if (!(pw = getpwuid(uid)))
+            return -1;
+
+        if (snprintf(sockname, sizeof(sockname), "@%s/.qemud/sock", pw->pw_dir) >= (int)sizeof(sockname)) {
+            return -1;
+        }
+
+        if (qemudListenUnix(server, sockname, 0) < 0)
+            return -1;
+    }
+
+    return 0;
+}
+
+static struct qemud_server *qemudInitialize(int sys) {
+    struct qemud_server *server;
+
+    if (!(server = calloc(1, sizeof(struct qemud_server))))
+        return NULL;
+
+    /* XXX extract actual version */
+    server->qemuVersion = (0*1000000)+(8*1000)+(0);
+    /* We don't have a dom-0, so start from 1 */
+    server->nextvmid = 1;
+
+    if (sys) {
+        if (snprintf(server->configDir, sizeof(server->configDir), "%s/qemud", SYSCONF_DIR) >= (int)sizeof(server->configDir)) {
+            goto cleanup;
+        }
+    } else {
+        struct passwd *pw;
+        int uid;
+        if ((uid = geteuid()) < 0) {
+            goto cleanup;
+        }
+        if (!(pw = getpwuid(uid))) {
+            goto cleanup;
+        }
+
+        if (snprintf(server->configDir, sizeof(server->configDir), "%s/.qemud", pw->pw_dir) >= (int)sizeof(server->configDir)) {
+            goto cleanup;
+        }
+    }
+
+    if (qemudListen(server, sys) < 0) {
+        goto cleanup;
+    }
+
+    if (qemudScanConfigs(server) < 0) {
+        goto cleanup;
+    }
+
+    return server;
+
+ cleanup:
+    if (server) {
+        struct qemud_socket *sock = server->sockets;
+        while (sock) {
+            close(sock->fd);
+            sock = sock->next;
+        }
+
+        free(server);
+    }
+    return NULL;
+}
+
+
+static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket *sock) {
+    int fd;
+    struct sockaddr_storage addr;
+    unsigned int addrlen = sizeof(addr);
+    struct qemud_client *client;
+
+    if ((fd = accept(sock->fd, (struct sockaddr *)&addr, &addrlen)) < 0) {
+        if (errno == EAGAIN)
+            return 0;
+        return -1;
+    }
+
+    if (qemudSetCloseExec(fd) < 0) {
+        close(fd);
+        return -1;
+    }
+
+    if (qemudSetNonBlock(fd) < 0) {
+        close(fd);
+        return -1;
+    }
+
+    client = calloc(1, sizeof(struct qemud_client));
+    client->fd = fd;
+    client->readonly = sock->readonly;
+
+    client->next = server->clients;
+    server->clients = client;
+    server->nclients++;
+
+    return 0;
+}
+
+
+int qemudStartVMDaemon(struct qemud_server *server,
+                       struct qemud_vm *vm) {
+    char **argv = NULL;
+    int argc = 0;
+    int pid;
+    int i, ret = -1;
+    int stdinfd = -1;
+    int pipeout[2] = {-1,-1};
+    int pipeerr[2] = {-1,-1};
+
+    if (vm->def.vncPort < 0)
+        vm->def.vncActivePort = 5900 + server->nextvmid;
+    else
+        vm->def.vncActivePort = vm->def.vncPort;
+
+    if (qemudBuildCommandLine(server, vm, &argv, &argc) < 0)
+        return -1;
+
+    if (1) { /* XXX debug stuff */
+        QEMUD_DEBUG("Spawn QEMU '");
+        for (i = 0 ; i < argc; i++) {
+            QEMUD_DEBUG("%s", argv[i]);
+            if (i == (argc-1)) {
+                QEMUD_DEBUG("'\n");
+            } else {
+                QEMUD_DEBUG(" ");
+            }
+        }
+    }
+
+    if ((stdinfd = open(_PATH_DEVNULL, O_RDONLY)) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot open %s", _PATH_DEVNULL);
+        goto cleanup;
+    }
+
+    if (pipe(pipeout) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create pipe");
+        goto cleanup;
+    }
+
+    if (pipe(pipeerr) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot create pipe");
+        goto cleanup;
+    }
+
+    if ((pid = fork()) < 0) {
+        qemudReportError(server, VIR_ERR_INTERNAL_ERROR, "cannot fork child process");
+        goto cleanup;
+    }
+
+    if (pid) { /* parent */
+        close(stdinfd);
+        close(pipeout[1]);
+        close(pipeerr[1]);
+        qemudSetNonBlock(pipeout[0]);
+        qemudSetNonBlock(pipeerr[0]);
+        vm->def.id = server->nextvmid++;
+        vm->pid = pid;
+        vm->stdout = pipeout[0];
+        vm->stderr = pipeerr[0];
+
+    } else { /* child */
+        int null;
+        if ((null = open(_PATH_DEVNULL, O_RDONLY)) < 0)
+            _exit(1);
+
+        if (close(pipeout[0]) < 0)
+            _exit(1);
+        if (close(pipeerr[0]) < 0)
+            _exit(1);
+
+        if (dup2(stdinfd, STDIN_FILENO) < 0)
+            _exit(1);
+        if (dup2(pipeout[1], STDOUT_FILENO) < 0)
+            _exit(1);
+        if (dup2(pipeerr[1], STDERR_FILENO) < 0)
+            _exit(1);
+
+        int open_max = sysconf (_SC_OPEN_MAX);
+        for (i = 0; i < open_max; i++)
+            if (i != STDOUT_FILENO &&
+                i != STDERR_FILENO &&
+                i != STDIN_FILENO)
+                close(i);
+
+        execvp(argv[0], argv);
+
+        _exit(1);
+    }
+
+    ret = 0;
+
+ cleanup:
+  
+    for (i = 0 ; i < argc ; i++) {
+        free(argv[i]);
+    }
+    free(argv);
+
+    return ret;
+}
+
+
+static void qemudDispatchClientFailure(struct qemud_server *server, struct qemud_client *client) {
+    struct qemud_client *tmp = server->clients;
+    struct qemud_client *prev = NULL;
+    while (tmp) {
+        if (tmp == client) {
+            if (prev == NULL)
+                server->clients = client->next;
+            else
+                prev->next = client->next;
+            server->nclients--;
+            break;
+        }
+        prev = tmp;
+        tmp = tmp->next;
+    }
+    close(client->fd);
+    free(client);
+}
+
+
+static int qemudDispatchClientRequest(struct qemud_server *server, struct qemud_client *client) {
+    if (qemudDispatch(server,
+                      client,
+                      &client->incoming,
+                      &client->outgoing) < 0) {
+        return -1;
+    }
+
+    client->outgoingSent = 0;
+    client->tx = 1;
+    client->incomingReceived = 0;
+
+    return 0;
+}
+
+static int qemudClientRead(struct qemud_server *server,
+                           struct qemud_client *client,
+                           char *buf, size_t want) {
+    int ret;
+    if ((ret = read(client->fd, buf, want)) <= 0) {
+        QEMUD_DEBUG("Plain read error %d\n", ret);
+        if (!ret || errno != EAGAIN)
+            qemudDispatchClientFailure(server, client);
+        return -1;
+    }
+    QEMUD_DEBUG("Plain data read %d\n", ret);
+    return ret;
+}
+
+static void qemudDispatchClientRead(struct qemud_server *server, struct qemud_client *client) {
+    char *data = (char *)&client->incoming;
+    unsigned int got = client->incomingReceived;
+    int want;
+    int ret;
+
+ restart:
+    if (got >= sizeof(struct qemud_packet_header)) {
+        want = sizeof(struct qemud_packet_header) + client->incoming.header.dataSize - got;
+    } else {
+        want = sizeof(struct qemud_packet_header) - got;
+    }
+
+    if ((ret = qemudClientRead(server, client, data+got, want)) < 0) {
+        return;
+    }
+    got += ret;
+    client->incomingReceived += ret;
+
+    /* If we've finished header, move onto body */
+    if (client->incomingReceived == sizeof(struct qemud_packet_header)) {
+        QEMUD_DEBUG("Type %d, data %d\n",
+                    client->incoming.header.type,
+                    client->incoming.header.dataSize);
+        /* Client lied about dataSize */
+        if (client->incoming.header.dataSize > sizeof(union qemud_packet_data)) {
+            QEMUD_DEBUG("Bogus data size %u\n", client->incoming.header.dataSize);
+            qemudDispatchClientFailure(server, client);
+            return;
+        }
+        if (client->incoming.header.dataSize) {
+            QEMUD_DEBUG("- Restarting recv to process body (%d bytes)\n",
+                        client->incoming.header.dataSize);
+            goto restart;
+        }
+    }
+
+    /* If we've finished body, dispatch the request */
+    if (ret == want) {
+        if (qemudDispatchClientRequest(server, client) < 0)
+            qemudDispatchClientFailure(server, client);
+        QEMUD_DEBUG("Dispatch\n");
+    }
+}
+
+
+static int qemudClientWrite(struct qemud_server *server,
+                           struct qemud_client *client,
+                           char *buf, size_t want) {
+    int ret;
+    if ((ret = write(client->fd, buf, want)) < 0) {
+        QEMUD_DEBUG("Plain write error %d\n", ret);
+        if (errno != EAGAIN)
+            qemudDispatchClientFailure(server, client);
+        return -1;
+    }
+    QEMUD_DEBUG("Plain data write %d\n", ret);
+    return ret;
+}
+
+
+static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) {
+    char *data = (char *)&client->outgoing;
+    int sent = client->outgoingSent;
+    int todo = sizeof(struct qemud_packet_header) + client->outgoing.header.dataSize - sent;
+    int ret;
+    if ((ret = qemudClientWrite(server, client, data+sent, todo)) < 0) {
+        return;
+    }
+    client->outgoingSent += ret;
+    QEMUD_DEBUG("Done %d %d\n", todo, ret);
+    if (todo == ret)
+        client->tx = 0;
+}
+
+static int qemudVMData(struct qemud_server *server ATTRIBUTE_UNUSED,
+                       struct qemud_vm *vm, int fd) {
+    char buf[4096];
+    if (vm->pid < 0)
+        return 0;
+
+    for (;;) {
+        int ret = read(fd, buf, sizeof(buf)-1);
+        if (ret < 0) {
+            if (errno == EAGAIN)
+                return 0;
+            return -1;
+        }
+        if (ret == 0) {
+            return 0;
+        }
+        buf[ret] = '\0';
+
+        /*
+         * XXX this is bad - we should wait for tty and open the
+         * monitor when actually starting the guest, so we can
+         * reliably trap startup failures
+         */
+        if (vm->monitor == -1) {
+            char monitor[20];
+            /* Fairly lame assuming we receive the data all in one chunk.
+               This isn't guarenteed, but in practice it seems good enough.
+               This will probably bite me in the future.... */
+            if (sscanf(buf, "char device redirected to %19s", monitor) == 1) {
+                int monfd;
+
+                if (!(monfd = open(monitor, O_RDWR))) {
+                    perror("cannot open monitor");
+                    return -1;
+                }
+                if (qemudSetCloseExec(monfd) < 0) {
+                    close(monfd);
+                    return -1;
+                }
+                if (qemudSetNonBlock(monfd) < 0) {
+                    close(monfd);
+                    return -1;
+                }
+
+                /* Consume & discard the initial greeting */
+                /* XXX this is broken, we need to block until
+                   we see the initial prompt to ensure startup
+                   has completed */
+                for(;;) {
+                    char line[1024];
+                    if (read(monfd, line, sizeof(line)) < 0) {
+                        if (errno == EAGAIN) {
+                            break;
+                        }
+                        close(monfd);
+                        return -1;
+                    }
+                    QEMUD_DEBUG("[%s]\n", line);
+                }
+                vm->monitor = monfd;
+            }
+        }
+        QEMUD_DEBUG("[%s]\n", buf);
+    }
+}
+
+int qemudShutdownVMDaemon(struct qemud_server *server, struct qemud_vm *vm) {
+    struct qemud_vm *prev = NULL, *curr = server->activevms;
+
+    /* Already cleaned-up */
+    if (vm->pid < 0)
+        return 0;
+
+    kill(vm->pid, SIGTERM);
+
+    /* Move it to inactive vm list */
+    while (curr) {
+        if (curr == vm) {
+            if (prev) {
+                prev->next = curr->next;
+            } else {
+                server->activevms = curr->next;
+            }
+            server->nactivevms--;
+
+            curr->next = server->inactivevms;
+            server->inactivevms = curr;
+            server->ninactivevms++;
+            break;
+        }
+        prev = curr;
+        curr = curr->next;
+    }
+
+    if (!curr) {
+        QEMUD_DEBUG("Could not find VM to shutdown\n");
+        return 0;
+    }
+
+    qemudVMData(server, vm, curr->stdout);
+    qemudVMData(server, vm, curr->stderr);
+    close(curr->stdout);
+    close(curr->stderr);
+    if (curr->monitor != -1)
+        close(curr->monitor);
+    curr->stdout = -1;
+    curr->stderr = -1;
+    curr->monitor = -1;
+    server->nvmfds -= 2;
+
+    if (waitpid(vm->pid, NULL, WNOHANG) != vm->pid) {
+        kill(vm->pid, SIGKILL);
+        if (waitpid(vm->pid, NULL, 0) != vm->pid) {
+            QEMUD_DEBUG("Got unexpected pid, damn\n");
+        }
+    }
+
+    vm->pid = -1;
+    vm->def.id = -1;
+
+    return 0;
+}
+
+static int qemudDispatchVMLog(struct qemud_server *server, struct qemud_vm *vm, int fd) {
+    if (qemudVMData(server, vm, fd) < 0)
+        if (qemudShutdownVMDaemon(server, vm) < 0)
+            return -1;
+    return 0;
+}
+
+static int qemudDispatchVMFailure(struct qemud_server *server, struct qemud_vm *vm,
+                                  int fd ATTRIBUTE_UNUSED) {
+    if (qemudShutdownVMDaemon(server, vm) < 0)
+        return -1;
+    return 0;
+}
+
+
+static int qemudDispatchPoll(struct qemud_server *server, struct pollfd *fds) {
+    struct qemud_socket *sock = server->sockets;
+    struct qemud_client *client = server->clients;
+    struct qemud_vm *vm = server->activevms;
+    struct qemud_vm *tmp;
+    int ret = 0;
+    int fd = 0;
+
+    while (sock) {
+        struct qemud_socket *next = sock->next;
+        if (fds[fd].revents)
+            if (qemudDispatchServer(server, sock) < 0)
+                return -1;
+        fd++;
+        sock = next;
+    }
+
+    while (client) {
+        struct qemud_client *next = client->next;
+        if (fds[fd].revents) {
+            QEMUD_DEBUG("Poll data normal\n");
+            if (fds[fd].revents == POLLOUT)
+                qemudDispatchClientWrite(server, client);
+            else if (fds[fd].revents == POLLIN)
+                qemudDispatchClientRead(server, client);
+            else
+                qemudDispatchClientFailure(server, client);
+        }
+        fd++;
+        client = next;
+    }
+    while (vm) {
+        struct qemud_vm *next = vm->next;
+        int failed = 0,
+            stdoutfd = vm->stdout,
+            stderrfd = vm->stderr;
+
+        if (stdoutfd != -1) {
+            if (fds[fd].revents) {
+                if (fds[fd].revents == POLLIN) {
+                    if (qemudDispatchVMLog(server, vm, fds[fd].fd) < 0)
+                        failed = 1;
+                } else {
+                    if (qemudDispatchVMFailure(server, vm, fds[fd].fd) < 0)
+                        failed = 1;
+                }
+            }
+            fd++;
+        }
+        if (stderrfd != -1) {
+            if (!failed) {
+                if (fds[fd].revents) {
+                    if (fds[fd].revents == POLLIN) {
+                        if (qemudDispatchVMLog(server, vm, fds[fd].fd) < 0)
+                            failed = 1;
+                    } else {
+                        if (qemudDispatchVMFailure(server, vm, fds[fd].fd) < 0)
+                            failed = 1;
+                    }
+                }
+            }
+            fd++;
+        }
+        vm = next;
+        if (failed)
+            ret = -1;
+    }
+
+    /* Cleanup any VMs which shutdown & dont have an associated
+       config file */
+    vm = server->inactivevms;
+    tmp = NULL;
+    while (vm) {
+        if (!vm->configFile[0]) {
+            struct qemud_vm *next = vm->next;
+            if (tmp) {
+                tmp->next = next;
+            } else {
+                server->inactivevms = next;
+            }
+            qemudFreeVM(vm);
+            vm = next;
+        } else {
+            tmp = vm;
+            vm = vm->next;
+        }
+    }
+
+    return ret;
+}
+
+static void qemudPreparePoll(struct qemud_server *server, struct pollfd *fds) {
+    int  fd = 0;
+    struct qemud_socket *sock;
+    struct qemud_client *client;
+    struct qemud_vm *vm;
+
+    for (sock = server->sockets ; sock ; sock = sock->next) {
+        fds[fd].fd = sock->fd;
+        fds[fd].events = POLLIN;
+        fd++;
+    }
+
+    for (client = server->clients ; client ; client = client->next) {
+        fds[fd].fd = client->fd;
+        /* Refuse to read more from client if tx is pending to
+           rate limit */
+        if (client->tx)
+            fds[fd].events = POLLOUT | POLLERR | POLLHUP;
+        else
+            fds[fd].events = POLLIN | POLLERR | POLLHUP;
+        fd++;
+    }
+    for (vm = server->activevms ; vm ; vm = vm->next) {
+        if (vm->stdout != -1) {
+            fds[fd].fd = vm->stdout;
+            fds[fd].events = POLLIN | POLLERR | POLLHUP;
+            fd++;
+        }
+        if (vm->stderr != -1) {
+            fds[fd].fd = vm->stderr;
+            fds[fd].events = POLLIN | POLLERR | POLLHUP;
+            fd++;
+        }
+    }
+}
+
+
+
+static int qemudOneLoop(struct qemud_server *server, int timeout) {
+    int nfds = server->nsockets + server->nclients + server->nvmfds;
+    struct pollfd fds[nfds];
+    int thistimeout = -1;
+    int ret;
+
+    /* If we have no clients or vms, then timeout after
+       30 seconds, letting daemon exit */
+    if (timeout > 0 &&
+        !server->nclients &&
+        !server->nactivevms)
+        thistimeout = timeout;
+
+    qemudPreparePoll(server, fds);
+
+ retry:
+
+    if ((ret = poll(fds, nfds, thistimeout * 1000)) < 0) {
+        if (errno == EINTR) {
+            goto retry;
+        }
+        return -1;
+    }
+
+    /* Must have timed out */
+    if (ret == 0)
+        return -1;
+
+    if (qemudDispatchPoll(server, fds) < 0)
+        return -1;
+
+    return 0;
+}
+
+static int qemudRunLoop(struct qemud_server *server, int timeout) {
+    int ret;
+
+    while ((ret = qemudOneLoop(server, timeout)) == 0)
+        ;
+
+    return ret == -1 ? -1 : 0;
+}
+
+static void qemudCleanup(struct qemud_server *server) {
+    struct qemud_socket *sock = server->sockets;
+    while (sock) {
+        close(sock->fd);
+        sock = sock->next;
+    }
+    free(server);
+}
+
+#define MAX_LISTEN 5
+int main(int argc, char **argv) {
+    int godaemon = 0;
+    int verbose = 0;
+    int sys = 0;
+    int timeout = -1;
+    struct qemud_server *server;
+
+    struct option opts[] = {
+        { "verbose", no_argument, &verbose, 1},
+        { "daemon", no_argument, &godaemon, 1},
+        { "system", no_argument, &sys, 1},
+        { "timeout", required_argument, 0, 't'},
+        {0, 0, 0, 0}
+    };
+
+    while (1) {
+        int optidx = 0;
+        int c;
+        char *tmp;
+
+        c = getopt_long(argc, argv, "vsdt:", opts, &optidx);
+
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 0:
+            /* Got one of the flags */
+            break;
+        case 'v':
+            verbose = 1;
+            break;
+        case 'd':
+            godaemon = 1;
+            break;
+        case 's':
+            sys = 1;
+            break;
+
+        case 't':
+            timeout = strtol(optarg, &tmp, 10);
+            if (!tmp)
+                timeout = -1;
+            if (timeout <= 0)
+                timeout = -1;
+            break;
+        case '?':
+            return 2;
+            break;
+
+        default:
+            abort();
+        }
+    }
+
+    if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
+        return 3;
+    if (signal(SIGCHLD, reapchild) == SIG_ERR)
+        return 3;
+
+    if (godaemon) {
+        int pid = qemudGoDaemon();
+        if (pid < 0)
+            return 1;
+        if (pid > 0)
+            return 0;
+    }
+
+    if (!(server = qemudInitialize(sys)))
+        return 2;
+
+    qemudRunLoop(server, timeout);
+
+    qemudCleanup(server);
+
+    return 0;
+}
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
index 26b31cee43a54f6223d8ef120f6cc01833b6730d..df210f753a1c738a792d08efc5b8625a9a4eb5e0 100644 (file)
@@ -1,7 +1,8 @@
 ## Process this file with automake to produce Makefile.in
 
-INCLUDES = -I$(top_builddir)/include -I@top_srcdir@/include @LIBXML_CFLAGS@ \
+INCLUDES = -I$(top_builddir)/include -I@top_srcdir@/include @LIBXML_CFLAGS@ -I@top_srcdir@/qemud \
           -DBINDIR=\""$(libexecdir)"\" -DLOCALEBASEDIR=\""$(datadir)/locale"\" \
+           -DLOCAL_STATE_DIR=\""$(localstatedir)"\" \
            -DGETTEXT_PACKAGE=\"$(PACKAGE)\"
 DEPS = libvirt.la
 LDADDS = @STATIC_BINARIES@ libvirt.la
@@ -11,7 +12,6 @@ EXTRA_DIST = libvirt_sym.version
 
 lib_LTLIBRARIES = libvirt.la
 libvirt_la_LIBADD = @LIBXML_LIBS@
-
 libvirt_la_LDFLAGS = -Wl,--version-script=$(srcdir)/libvirt_sym.version \
                     -version-info @LIBVIRT_VERSION_INFO@
 
@@ -28,7 +28,8 @@ libvirt_la_SOURCES =                                          \
                driver.h                                        \
                proxy_internal.c proxy_internal.h               \
                conf.c conf.h                                   \
-               xm_internal.c xm_internal.h
+               xm_internal.c xm_internal.h                     \
+               qemu_internal.c qemu_internal.h
 
 bin_PROGRAMS = virsh
 
index 4e708a3a9f4dc5936f1286a7a9d0846fcb7570e9..838f70eb0cfca347310609a49ddd4771bc5a799f 100644 (file)
@@ -22,7 +22,8 @@ typedef enum {
     VIR_DRV_XEN_DAEMON = 3,
     VIR_DRV_TEST = 4,
     VIR_DRV_XEN_PROXY = 5,
-    VIR_DRV_XEN_XM = 6
+    VIR_DRV_XEN_XM = 6,
+    VIR_DRV_QEMU = 7
 } virDrvNo;
 
 
index b06bf43ff600458273f72feb1ff4dd6faae16a94..b0ecafb50c9f997dbc7b42fed3b22f6d11b78a7b 100644 (file)
@@ -32,6 +32,7 @@
 #include "proxy_internal.h"
 #include "xml.h"
 #include "test.h"
+#include "qemu_internal.h"
 
 /*
  * TODO:
@@ -79,6 +80,8 @@ virInitialize(void)
     xenStoreRegister();
     xenXMRegister();
     testRegister();
+    qemuRegister();
+
     return(0);
 }
 
@@ -441,6 +444,7 @@ virConnectGetVersion(virConnectPtr conn, unsigned long *hvVer)
                return(0);
        }
     }
+
     return (-1);
 }
 
diff --git a/src/qemu_internal.c b/src/qemu_internal.c
new file mode 100644 (file)
index 0000000..c5e226b
--- /dev/null
@@ -0,0 +1,864 @@
+/*
+ * qemu_internal.c: A backend for managing QEMU machines
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2006 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 <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xpath.h>
+#include <libxml/uri.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <limits.h>
+#include <paths.h>
+
+#include "internal.h"
+#include "qemu_internal.h"
+#include "xml.h"
+#include "protocol.h"
+
+
+
+static void
+qemuError(virConnectPtr con,
+           virDomainPtr dom,
+           virErrorNumber error,
+           const char *info)
+{
+    const char *errmsg;
+
+    if (error == VIR_ERR_OK)
+        return;
+
+    errmsg = __virErrorMsg(error, info);
+    __virRaiseError(con, dom, VIR_FROM_QEMU, error, VIR_ERR_ERROR,
+                    errmsg, info, NULL, 0, 0, errmsg, info, 0);
+}
+
+static void qemuPacketError(virConnectPtr con,
+                             virDomainPtr dom,
+                             struct qemud_packet *pkt) {
+    if (!pkt) {
+        qemuError(con, dom, VIR_ERR_INTERNAL_ERROR, "Malformed data packet");
+        return;
+    }
+    if (pkt->header.type == QEMUD_PKT_FAILURE) {
+        /* Paranoia in case remote side didn't terminate it */
+        if (pkt->data.failureReply.message[0])
+            pkt->data.failureReply.message[QEMUD_MAX_ERROR_LEN-1] = '\0';
+
+        qemuError(con,
+                   dom,
+                   pkt->data.failureReply.code,
+                   pkt->data.failureReply.message[0] ?
+                   pkt->data.failureReply.message : NULL);
+    } else {
+        qemuError(con, dom, VIR_ERR_INTERNAL_ERROR, "Incorrect reply type");
+    }
+}
+
+
+/**
+ * qemuFindServerPath:
+ *
+ * Tries to find the path to the qemu binary.
+ * 
+ * Returns path on success or NULL in case of error.
+ */
+static const char *
+qemuFindServerPath(void)
+{
+    static const char *serverPaths[] = {
+        BINDIR "/libvirt_qemu",
+        BINDIR "/libvirt_qemu_dbg",
+        NULL
+    };
+    int i;
+    const char *debugQemu = getenv("LIBVIRT_QEMU_SERVER");
+
+    if (debugQemu)
+        return(debugQemu);
+
+    for (i = 0; serverPaths[i]; i++) {
+        if (access(serverPaths[i], X_OK | R_OK) == 0) {
+            return serverPaths[i];
+        }
+    }
+    return NULL;
+}
+
+
+/**
+ * qemuForkServer:
+ *
+ * Forks and try to launch the qemu server
+ *
+ * Returns 0 in case of success or -1 in case of detected error.
+ */
+static int
+qemuForkServer(void)
+{
+    const char *proxyPath = qemuFindServerPath();
+    int ret, pid, status;
+
+    if (!proxyPath) {
+        fprintf(stderr, "failed to find qemu\n");
+        return(-1);
+    }
+
+    /* Become a daemon */
+    pid = fork();
+    if (pid == 0) {
+        int stdinfd = -1;
+        int stdoutfd = -1;
+        int i, open_max;
+        if ((stdinfd = open(_PATH_DEVNULL, O_RDONLY)) < 0)
+            goto cleanup;
+        if ((stdoutfd = open(_PATH_DEVNULL, O_WRONLY)) < 0)
+            goto cleanup;
+        if (dup2(stdinfd, STDIN_FILENO) != STDIN_FILENO)
+            goto cleanup;
+        if (dup2(stdoutfd, STDOUT_FILENO) != STDOUT_FILENO)
+            goto cleanup;
+        if (dup2(stdoutfd, STDERR_FILENO) != STDERR_FILENO)
+            goto cleanup;
+        if (close(stdinfd) < 0)
+            goto cleanup;
+        stdinfd = -1;
+        if (close(stdoutfd) < 0)
+            goto cleanup;
+        stdoutfd = -1;
+
+        open_max = sysconf (_SC_OPEN_MAX);
+        for (i = 0; i < open_max; i++)
+            if (i != STDIN_FILENO &&
+                i != STDOUT_FILENO &&
+                i != STDERR_FILENO)
+                close(i);
+
+        setsid();
+        if (fork() == 0) {
+            /* Run daemon in auto-shutdown mode, so it goes away when
+               no longer needed by an active guest, or client */
+            execl(proxyPath, proxyPath, "--timeout", "30", NULL);
+            fprintf(stderr, "failed to exec %s\n", proxyPath);
+        }
+        /*
+         * calling exit() generate troubles for termination handlers
+         */
+        _exit(0);
+
+    cleanup:
+        if (stdoutfd != -1)
+            close(stdoutfd);
+        if (stdinfd != -1)
+            close(stdinfd);
+        _exit(-1);
+    }
+
+    /*
+     * do a waitpid on the intermediate process to avoid zombies.
+     */
+ retry_wait:
+    ret = waitpid(pid, &status, 0);
+    if (ret < 0) {
+        if (errno == EINTR)
+            goto retry_wait;
+    }
+
+    return (0);
+}
+
+/**
+ * qemuOpenClientUNIX:
+ * @path: the fileame for the socket
+ *
+ * try to connect to the socket open by qemu
+ *
+ * Returns the associated file descriptor or -1 in case of failure
+ */
+static int
+qemuOpenClientUNIX(virConnectPtr conn, const char *path, int autostart) {
+    int fd;
+    struct sockaddr_un addr;
+    int trials = 0;
+
+ retry:
+    fd = socket(PF_UNIX, SOCK_STREAM, 0);
+    if (fd < 0) {
+        return(-1);
+    }
+
+    /*
+     * Abstract socket do not hit the filesystem, way more secure and
+     * garanteed to be atomic
+     */
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
+    if (addr.sun_path[0] == '@')
+        addr.sun_path[0] = '\0';
+
+    /*
+     * now bind the socket to that address and listen on it
+     */
+    if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+        close(fd);
+        if (autostart && trials < 3) {
+            if (qemuForkServer() < 0)
+                return(-1);
+            trials++;
+            usleep(5000 * trials * trials);
+            goto retry;
+        }
+        return (-1);
+    }
+
+    conn->handle = fd;
+
+    return (0);
+}
+
+
+/* Takes a single request packet, does a blocking send on it.
+ * then blocks until the complete reply has come back, or
+ * connection closes.
+ */
+static int qemuProcessRequest(virConnectPtr conn,
+                               virDomainPtr dom,
+                               struct qemud_packet *req,
+                               struct qemud_packet *reply) {
+    char *out = (char *)req;
+    int outDone = 0;
+    int outLeft = sizeof(struct qemud_packet_header) + req->header.dataSize;
+    char *in = (char *)reply;
+    int inGot = 0;
+    int inLeft = sizeof(struct qemud_packet_header);
+
+    /* printf("Send request %d\n", req->header.type); */
+
+    /* Block sending entire outgoing packet */
+    while (outLeft) {
+        int got = write(conn->handle, out+outDone, outLeft);
+        if (got < 0) {
+            return -1;
+        }
+        outDone += got;
+        outLeft -= got;
+    }
+
+    /* Block waiting for header to come back */
+    while (inLeft) {
+        int done = read(conn->handle, in+inGot, inLeft);
+        if (done <= 0) {
+            return -1;
+        }
+        inGot += done;
+        inLeft -= done;
+    }
+
+    /* Validate header isn't bogus (bigger than
+       maximum defined packet size) */
+    if (reply->header.dataSize > sizeof(union qemud_packet_data)) {
+        /*
+        printf("Got type %ds body %d (max %ld)\n",
+               reply->header.type,
+               reply->header.dataSize,
+               sizeof(union qemud_packet_data));
+        printf("%ld == %ld + %ld\n",
+               sizeof(struct qemud_packet),
+               sizeof(struct qemud_packet_header),
+               sizeof(union qemud_packet_data));
+        */
+        qemuPacketError(conn, dom, NULL);
+        return -1;
+    }
+
+    /* Now block reading in body */
+    inLeft = reply->header.dataSize;
+    while (inLeft) {
+        int done = read(conn->handle, in+inGot, inLeft);
+        if (done <= 0) {
+            return -1;
+        }
+        inGot += done;
+        inLeft -= done;
+    }
+
+    if (reply->header.type != req->header.type) {
+        qemuPacketError(conn, dom, reply);
+        return -1;
+    }
+
+    return 0;
+}
+
+
+/*
+ * Open a connection to the libvirt QEMU daemon
+ */
+static int qemuOpenConnection(virConnectPtr conn, xmlURIPtr uri, int readonly ATTRIBUTE_UNUSED) {
+    char path[PATH_MAX];
+
+    if (uri->server != NULL) {
+        return -1;
+    }
+
+    if (!strcmp(uri->path, "/system")) {
+        if (readonly) {
+            if (snprintf(path, sizeof(path), "%s/run/qemud/sock-ro", LOCAL_STATE_DIR) >= (int)sizeof(path)) {
+                return -1;
+            }
+        } else {
+            if (snprintf(path, sizeof(path), "%s/run/qemud/sock", LOCAL_STATE_DIR) >= (int)sizeof(path)) {
+                return -1;
+            }
+        }
+    } else if (!strcmp(uri->path, "/session")) {
+        struct passwd *pw;
+        int uid;
+
+        if ((uid = geteuid()) < 0) {
+            return -1;
+        }
+
+        if (!(pw = getpwuid(uid)))
+            return -1;
+
+        if (snprintf(path, sizeof(path), "@%s/.qemud/sock", pw->pw_dir) == sizeof(path)) {
+            return -1;
+        }
+    }
+    return qemuOpenClientUNIX(conn, path, 1);
+}
+
+
+/*
+ * Open a connection to the QEMU manager
+ */
+static int qemuOpen(virConnectPtr conn,
+                    const char *name,
+                    int flags){
+    xmlURIPtr uri;
+
+    if (!name) {
+        return -1;
+    }
+
+    uri = xmlParseURI(name);
+    if (uri == NULL) {
+        if (!(flags & VIR_DRV_OPEN_QUIET))
+            qemuError(conn, NULL, VIR_ERR_NO_SUPPORT, name);
+        return(-1);
+    }
+
+    if (!uri->scheme ||
+        strcmp(uri->scheme, "qemu") ||
+        !uri->path) {
+        xmlFreeURI(uri);
+        return -1;
+    }
+
+    conn->handle = -1;
+    qemuOpenConnection(conn, uri, flags & VIR_DRV_OPEN_RO ? 1 : 0);
+    xmlFreeURI(uri);
+
+    if (conn->handle < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+
+static int qemuClose  (virConnectPtr conn) {
+    if (conn->handle != -1) {
+        close(conn->handle);
+        conn->handle = -1;
+    }
+    return 0;
+}
+
+
+static int qemuGetVersion(virConnectPtr conn,
+                          unsigned long *hvVer) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_GET_VERSION;
+    req.header.dataSize = 0;
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    *hvVer = reply.data.getVersionReply.version;
+    return 0;
+}
+
+
+static int qemuNodeGetInfo(virConnectPtr conn,
+                           virNodeInfoPtr info) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_GET_NODEINFO;
+    req.header.dataSize = 0;
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    info->cores = reply.data.getNodeInfoReply.cores;
+    info->threads = reply.data.getNodeInfoReply.threads;
+    info->sockets = reply.data.getNodeInfoReply.sockets;
+    info->nodes = reply.data.getNodeInfoReply.nodes;
+    strncpy(info->model, reply.data.getNodeInfoReply.model, sizeof(info->model));
+    info->mhz = reply.data.getNodeInfoReply.mhz;
+    info->cpus = reply.data.getNodeInfoReply.cpus;
+    info->memory = reply.data.getNodeInfoReply.memory;
+    return 0;
+}
+
+
+static int qemuNumOfDomains(virConnectPtr conn) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_NUM_DOMAINS;
+    req.header.dataSize = 0;
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    return reply.data.numDomainsReply.numDomains;
+}
+
+
+static int qemuListDomains(virConnectPtr conn,
+                           int *ids,
+                           int maxids) {
+    struct qemud_packet req, reply;
+    int i, nDomains;
+
+    req.header.type = QEMUD_PKT_LIST_DOMAINS;
+    req.header.dataSize = 0;
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    nDomains = reply.data.listDomainsReply.numDomains;
+    if (nDomains > maxids)
+        nDomains = maxids;
+
+    for (i = 0 ; i < nDomains ; i++) {
+        ids[i] = reply.data.listDomainsReply.domains[i];
+    }
+
+    return nDomains;
+}
+
+
+static virDomainPtr
+qemuDomainCreateLinux(virConnectPtr conn, const char *xmlDesc,
+                       unsigned int flags ATTRIBUTE_UNUSED) {
+    struct qemud_packet req, reply;
+    virDomainPtr dom;
+    int len = strlen(xmlDesc);
+
+    if (len > (QEMUD_MAX_XML_LEN-1)) {
+        return NULL;
+    }
+
+    req.header.type = QEMUD_PKT_DOMAIN_CREATE;
+    req.header.dataSize = sizeof(req.data.domainCreateRequest);
+    strcpy(req.data.domainCreateRequest.xml, xmlDesc);
+    req.data.domainCreateRequest.xml[QEMUD_MAX_XML_LEN-1] = '\0';
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return NULL;
+    }
+
+    reply.data.domainCreateReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
+
+    if (!(dom = virGetDomain(conn,
+                             reply.data.domainCreateReply.name,
+                             reply.data.domainCreateReply.uuid)))
+        return NULL;
+
+    dom->id = reply.data.domainCreateReply.id;
+    return dom;
+}
+
+
+static virDomainPtr qemuLookupDomainByID(virConnectPtr conn,
+                                         int id) {
+    struct qemud_packet req, reply;
+    virDomainPtr dom;
+
+    req.header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_ID;
+    req.header.dataSize = sizeof(req.data.domainLookupByIDRequest);
+    req.data.domainLookupByIDRequest.id = id;
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return NULL;
+    }
+
+    reply.data.domainLookupByIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
+
+    if (!(dom = virGetDomain(conn,
+                             reply.data.domainLookupByIDReply.name,
+                             reply.data.domainLookupByIDReply.uuid)))
+        return NULL;
+
+    dom->id = id;
+    return dom;
+}
+
+
+static virDomainPtr qemuLookupDomainByUUID(virConnectPtr conn,
+                                           const unsigned char *uuid) {
+    struct qemud_packet req, reply;
+    virDomainPtr dom;
+
+    req.header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_UUID;
+    req.header.dataSize = sizeof(req.data.domainLookupByUUIDRequest);
+    memmove(req.data.domainLookupByUUIDRequest.uuid, uuid, QEMUD_UUID_RAW_LEN);
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return NULL;
+    }
+
+    reply.data.domainLookupByUUIDReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
+
+    if (!(dom = virGetDomain(conn,
+                             reply.data.domainLookupByUUIDReply.name,
+                             uuid)))
+        return NULL;
+
+    dom->id = reply.data.domainLookupByUUIDReply.id;
+    return dom;
+}
+
+
+static virDomainPtr qemuLookupDomainByName(virConnectPtr conn,
+                                           const char *name) {
+    struct qemud_packet req, reply;
+    virDomainPtr dom;
+
+    if (strlen(name) > (QEMUD_MAX_NAME_LEN-1))
+        return NULL;
+
+    req.header.type = QEMUD_PKT_DOMAIN_LOOKUP_BY_NAME;
+    req.header.dataSize = sizeof(req.data.domainLookupByNameRequest);
+    strcpy(req.data.domainLookupByNameRequest.name, name);
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return NULL;
+    }
+
+    if (!(dom = virGetDomain(conn,
+                             name,
+                             reply.data.domainLookupByNameReply.uuid)))
+        return NULL;
+
+    dom->id = reply.data.domainLookupByNameReply.id;
+    return dom;
+}
+
+static int qemuDestroyDomain(virDomainPtr domain) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_DOMAIN_DESTROY;
+    req.header.dataSize = sizeof(req.data.domainDestroyRequest);
+    req.data.domainDestroyRequest.id = domain->id;
+
+    if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qemuShutdownDomain(virDomainPtr domain) {
+    return qemuDestroyDomain(domain);
+}
+
+static int qemuResumeDomain(virDomainPtr domain ATTRIBUTE_UNUSED) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_DOMAIN_RESUME;
+    req.header.dataSize = sizeof(req.data.domainResumeRequest);
+    req.data.domainResumeRequest.id = domain->id;
+
+    if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qemuPauseDomain(virDomainPtr domain ATTRIBUTE_UNUSED) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_DOMAIN_SUSPEND;
+    req.header.dataSize = sizeof(req.data.domainSuspendRequest);
+    req.data.domainSuspendRequest.id = domain->id;
+
+    if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int qemuGetDomainInfo(virDomainPtr domain,
+                             virDomainInfoPtr info) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_DOMAIN_GET_INFO;
+    req.header.dataSize = sizeof(req.data.domainGetInfoRequest);
+    memmove(req.data.domainGetInfoRequest.uuid, domain->uuid, QEMUD_UUID_RAW_LEN);
+
+    if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    memset(info, 0, sizeof(virDomainInfo));
+    switch (reply.data.domainGetInfoReply.runstate) {
+    case QEMUD_STATE_RUNNING:
+        info->state = VIR_DOMAIN_RUNNING;
+        break;
+
+    case QEMUD_STATE_PAUSED:
+        info->state = VIR_DOMAIN_PAUSED;
+        break;
+
+    case QEMUD_STATE_STOPPED:
+        info->state = VIR_DOMAIN_SHUTOFF;
+        break;
+
+    default:
+        return -1;
+    }
+    info->maxMem = reply.data.domainGetInfoReply.maxmem;
+    info->memory = reply.data.domainGetInfoReply.memory;
+    info->nrVirtCpu = reply.data.domainGetInfoReply.nrVirtCpu;
+    info->cpuTime = reply.data.domainGetInfoReply.cpuTime;
+
+    return 0;
+}
+
+static char *qemuDomainDumpXML(virDomainPtr domain, int flags ATTRIBUTE_UNUSED) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_DUMP_XML;
+    req.header.dataSize = sizeof(req.data.domainDumpXMLRequest);
+    memmove(req.data.domainDumpXMLRequest.uuid, domain->uuid, QEMUD_UUID_RAW_LEN);
+
+    if (qemuProcessRequest(domain->conn, NULL, &req, &reply) < 0) {
+        return NULL;
+    }
+
+    reply.data.domainDumpXMLReply.xml[QEMUD_MAX_XML_LEN-1] = '\0';
+
+    return strdup(reply.data.domainDumpXMLReply.xml);
+}
+
+static int qemuSaveDomain(virDomainPtr domain ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED) {
+    return -1;
+}
+
+static int qemuRestoreDomain(virConnectPtr conn ATTRIBUTE_UNUSED, const char *file ATTRIBUTE_UNUSED) {
+    return -1;
+}
+
+
+static int qemuNumOfDefinedDomains(virConnectPtr conn) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_NUM_DEFINED_DOMAINS;
+    req.header.dataSize = 0;
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    return reply.data.numDefinedDomainsReply.numDomains;
+}
+
+static int qemuListDefinedDomains(virConnectPtr conn,
+                                  const char **names,
+                                  int maxnames){
+    struct qemud_packet req, reply;
+    int i, nDomains;
+
+    req.header.type = QEMUD_PKT_LIST_DEFINED_DOMAINS;
+    req.header.dataSize = 0;
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    nDomains = reply.data.listDefinedDomainsReply.numDomains;
+    if (nDomains > maxnames)
+        nDomains = maxnames;
+
+    for (i = 0 ; i < nDomains ; i++) {
+        reply.data.listDefinedDomainsReply.domains[i][QEMUD_MAX_NAME_LEN-1] = '\0';
+        names[i] = strdup(reply.data.listDefinedDomainsReply.domains[i]);
+    }
+
+    return nDomains;
+}
+
+static int qemuDomainCreate(virDomainPtr dom) {
+    struct qemud_packet req, reply;
+
+    req.header.type = QEMUD_PKT_DOMAIN_START;
+    req.header.dataSize = sizeof(req.data.domainStartRequest);
+    memcpy(req.data.domainStartRequest.uuid, dom->uuid, QEMUD_UUID_RAW_LEN);
+
+    if (qemuProcessRequest(dom->conn, NULL, &req, &reply) < 0) {
+        return -1;
+    }
+
+    dom->id = reply.data.domainStartReply.id;
+
+    return 0;
+}
+
+static virDomainPtr qemuDomainDefineXML(virConnectPtr conn, const char *xml) {
+    struct qemud_packet req, reply;
+    virDomainPtr dom;
+    int len = strlen(xml);
+
+    if (len > (QEMUD_MAX_XML_LEN-1)) {
+        return NULL;
+    }
+
+    req.header.type = QEMUD_PKT_DOMAIN_DEFINE;
+    req.header.dataSize = sizeof(req.data.domainDefineRequest);
+    strcpy(req.data.domainDefineRequest.xml, xml);
+    req.data.domainDefineRequest.xml[QEMUD_MAX_XML_LEN-1] = '\0';
+
+    if (qemuProcessRequest(conn, NULL, &req, &reply) < 0) {
+        return NULL;
+    }
+
+    reply.data.domainDefineReply.name[QEMUD_MAX_NAME_LEN-1] = '\0';
+
+    if (!(dom = virGetDomain(conn,
+                             reply.data.domainDefineReply.name,
+                             reply.data.domainDefineReply.uuid)))
+        return NULL;
+
+    dom->id = -1;
+    return dom;
+}
+
+static int qemuUndefine(virDomainPtr dom) {
+    struct qemud_packet req, reply;
+    int ret = 0;
+
+    req.header.type = QEMUD_PKT_DOMAIN_UNDEFINE;
+    req.header.dataSize = sizeof(req.data.domainUndefineRequest);
+    memcpy(req.data.domainUndefineRequest.uuid, dom->uuid, QEMUD_UUID_RAW_LEN);
+
+    if (qemuProcessRequest(dom->conn, NULL, &req, &reply) < 0) {
+        ret = -1;
+        goto cleanup;
+    }
+
+ cleanup:
+    if (virFreeDomain(dom->conn, dom) < 0)
+        ret = -1;
+
+    return ret;
+}
+
+
+static virDriver qemuDriver = {
+    VIR_DRV_QEMU,
+    "QEMU",
+    LIBVIR_VERSION_NUMBER,
+    NULL, /* init */
+    qemuOpen, /* open */
+    qemuClose, /* close */
+    NULL, /* type */
+    qemuGetVersion, /* version */
+    qemuNodeGetInfo, /* nodeGetInfo */
+    qemuListDomains, /* listDomains */
+    qemuNumOfDomains, /* numOfDomains */
+    qemuDomainCreateLinux, /* domainCreateLinux */
+    qemuLookupDomainByID, /* domainLookupByID */
+    qemuLookupDomainByUUID, /* domainLookupByUUID */
+    qemuLookupDomainByName, /* domainLookupByName */
+    qemuPauseDomain, /* domainSuspend */
+    qemuResumeDomain, /* domainResume */
+    qemuShutdownDomain, /* domainShutdown */
+    NULL, /* domainReboot */
+    qemuDestroyDomain, /* domainDestroy */
+    NULL, /* domainGetOSType */
+    NULL, /* domainGetMaxMemory */
+    NULL, /* domainSetMaxMemory */
+    NULL, /* domainSetMemory */
+    qemuGetDomainInfo, /* domainGetInfo */
+    qemuSaveDomain, /* domainSave */
+    qemuRestoreDomain, /* domainRestore */
+    NULL, /* domainCoreDump */
+    NULL, /* domainSetVcpus */
+    NULL, /* domainPinVcpu */
+    NULL, /* domainGetVcpus */
+    qemuDomainDumpXML, /* domainDumpXML */
+    qemuListDefinedDomains, /* listDomains */
+    qemuNumOfDefinedDomains, /* numOfDomains */
+    qemuDomainCreate, /* domainCreate */
+    qemuDomainDefineXML, /* domainDefineXML */
+    qemuUndefine, /* domainUndefine */
+    NULL, /* domainAttachDevice */
+    NULL, /* domainDetachDevice */
+};
+
+void qemuRegister(void) {
+    virRegisterDriver(&qemuDriver);
+}
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/src/qemu_internal.h b/src/qemu_internal.h
new file mode 100644 (file)
index 0000000..ae2833d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * qemu_internal.h: A backend for managing QEMU machines
+ *
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2006 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>
+ */
+
+#ifndef __VIR_QEMU_INTERNAL_H__
+#define __VIR_QEMU_INTERNAL_H__
+
+#include <libvirt/virterror.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    void qemuRegister(void);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __VIR_QEMU_INTERNAL_H__ */
+
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
index 212d6d6b45e2cb5e89a124d05e4d64542b9c9aba..390788d56ac1b4bcc692a994ae7abdc67e807924 100644 (file)
@@ -2529,7 +2529,9 @@ vshInit(vshControl * ctl)
     /* basic connection to hypervisor, for Xen connections unless
        we're root open a read only connections. Allow 'test' HV
        to be RW all the time though */
-    if (ctl->uid == 0 || (ctl->name && !strncmp(ctl->name, "test", 4)))
+    if (ctl->uid == 0 || (ctl->name && 
+                         (!strncmp(ctl->name, "test", 4) ||
+                          !strncmp(ctl->name, "qemu", 4))))
         ctl->conn = virConnectOpen(ctl->name);
     else
         ctl->conn = virConnectOpenReadOnly(ctl->name);
index 5d3a99a3c111996b5c6ba90fe4b0e9d5dfff495e..b180c4e72a064870704699f61f705b7cacda876c 100644 (file)
@@ -268,6 +268,9 @@ virDefaultErrorFunc(virErrorPtr err)
         case VIR_FROM_RPC:
             dom = "XML-RPC ";
             break;
+        case VIR_FROM_QEMU:
+            dom = "QEMU ";
+            break;
     }
     if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
         domain = err->dom->name;