]> xenbits.xensource.com Git - libvirt.git/commitdiff
Add an initial User Mode Linux Driver
authorDaniel P. Berrange <berrange@redhat.com>
Wed, 19 Nov 2008 16:58:23 +0000 (16:58 +0000)
committerDaniel P. Berrange <berrange@redhat.com>
Wed, 19 Nov 2008 16:58:23 +0000 (16:58 +0000)
17 files changed:
ChangeLog
configure.in
docs/drvuml.html.in [new file with mode: 0644]
docs/sitemap.html.in
include/libvirt/virterror.h
qemud/Makefile.am
qemud/qemud.c
src/Makefile.am
src/domain_conf.c
src/domain_conf.h
src/driver.h
src/qemu_conf.c
src/uml_conf.c [new file with mode: 0644]
src/uml_conf.h [new file with mode: 0644]
src/uml_driver.c [new file with mode: 0644]
src/uml_driver.h [new file with mode: 0644]
src/virterror.c

index 364fa0febbed0327823ea17118d01408fe1da801..71a511ec7af72bb2e534681c55cc9f558c8155aa 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+Wed Nov 19 16:51:00 GMT 2008 Daniel Berrange <berrange@redhat.com>
+
+       Add User Mode Linux driver
+       * src/uml_driver.c, src/uml_driver.h, src/uml_conf.c,
+       src/uml_conf.h: Core UML driver code
+       * src/domain_conf.c, src/domain_conf.h: Add UML disk
+       bus type. Fix paravirt console handling for non-HVM domains
+       * src/driver.h: Add UML driver number
+       * src/virterror.h: Add error code for UML driver
+       * src/qemu_conf.c: Add disk bus enum entry for UML
+       * src/Makefile.am: Build UML driver
+       * qemud/qemud.c: Initialize UML driver
+       * qemud/Makefile.am: Link to UML driver library
+       * docs/drvuml.html.in: Basic docs for the UML driver
+       * configure.in: Flag for enabling UML driver
+       * include/libvirt/virterror.h: Add constant for UML driver
+
 Wed Nov 19 16:22:00 GMT 2008 Daniel Berrange <berrange@redhat.com>
 
        Add virFreeCallback to event loop APIs.
index 085cae695200ea87e22381ae307a74feff59e400..21931a131da188a446cbbbb62811a43d95cdf00e 100644 (file)
@@ -149,6 +149,8 @@ AC_ARG_WITH([xen],
 [  --with-xen              add XEN support (on)],[],[with_xen=yes])
 AC_ARG_WITH([qemu],
 [  --with-qemu             add QEMU/KVM support (on)],[],[with_qemu=yes])
+AC_ARG_WITH([uml],
+[  --with-uml              add UML support (on)],[],[with_uml=yes])
 AC_ARG_WITH([openvz],
 [  --with-openvz           add OpenVZ support (on)],[],[with_openvz=yes])
 AC_ARG_WITH([lxc],
@@ -253,6 +255,11 @@ if test "$with_qemu" = "yes" ; then
 fi
 AM_CONDITIONAL([WITH_QEMU], [test "$with_qemu" = "yes"])
 
+if test "$with_uml" = "yes" ; then
+    AC_DEFINE_UNQUOTED([WITH_UML], 1, [whether UML driver is enabled])
+fi
+AM_CONDITIONAL([WITH_UML], [test "$with_uml" = "yes"])
+
 if test "$with_test" = "yes" ; then
     AC_DEFINE_UNQUOTED([WITH_TEST], 1, [whether Test driver is enabled])
 fi
@@ -1098,6 +1105,7 @@ AC_MSG_NOTICE([])
 AC_MSG_NOTICE([     Xen: $with_xen])
 AC_MSG_NOTICE([   Proxy: $with_xen_proxy])
 AC_MSG_NOTICE([    QEMU: $with_qemu])
+AC_MSG_NOTICE([     UML: $with_uml])
 AC_MSG_NOTICE([  OpenVZ: $with_openvz])
 AC_MSG_NOTICE([     LXC: $with_lxc])
 AC_MSG_NOTICE([    Test: $with_test])
diff --git a/docs/drvuml.html.in b/docs/drvuml.html.in
new file mode 100644 (file)
index 0000000..8f4429a
--- /dev/null
@@ -0,0 +1,79 @@
+<html>
+  <body>
+    <h1>User Mode Linux driver</h1>
+
+    <p>
+    The UML driver for libvirt allows use and management of paravirtualized
+    guests built for User Mode Linux. UML requires no special support in
+    the host kernel, so can be used by any user of any linux system, provided
+    they have enough free RAM for their guest's needs, though there are
+    certain restrictions on network connectivity unless the adminstrator
+    has pre-created TAP devices.
+    </p>
+
+    <h2>Connections to UML driver</h2>
+
+    <p>
+    The libvirt UML driver follows the QEMU driver in providing two
+    types of connection. There is one privileged instance per host,
+    which runs as root. This is called the "system" instance, and allows
+    full use of all host resources. Then, there is a per-user unprivileged
+    "session", instance. This has more restricted capabilities, and may
+    require the host administrator to setup certain resources ahead of
+    time to allow full integration with the network. Example connection
+    URIs are
+    </p>
+
+    <pre>
+    uml:///system                        (local access, system instance)
+    uml:///session                       (local access, session instance)
+    uml://example.com/system             (remote access, TLS/x509)
+    uml+tcp://example.com/system         (remote access, SASl/Kerberos)
+    uml+ssh://root@example.com/system    (remote access, SSH tunnelled)
+    </pre>
+
+    <h2>Example XML configuration</h2>
+
+    <p>
+      User mode Linux driver only supports directly kernel boot at
+      this time. A future driver enhancement may allow a paravirt
+      bootloader in a similar style to Xen's pygrub. For now though,
+      the UML kernel must be stored on the host and referenced
+      explicitly in the "os" element. Since UML is a paravirtualized
+      technology, the kernel "type" is set to "uml"
+    </p>
+
+    <p>
+      There is not yet support for networking in the driver, but
+      disks can be specified in the usual libvirt manner. The main
+      variation is the target device naming scheme "ubd0", and
+      bus type of "uml".
+    </p>
+
+    <p>
+      Once booted the primary console is connected toa PTY, and
+      thus accessible with "virsh console" or equivalent tools
+    </p>
+
+    <pre>
+&lt;domain type='uml'&gt;
+  &lt;name&gt;demo&lt;/name&gt;
+  &lt;uuid&gt;b4433fc2-a22e-ffb3-0a3d-9c173b395800&lt;/uuid&gt;
+  &lt;memory&gt;500000&lt;/memory&gt;
+  &lt;currentMemory&gt;500000&lt;/currentMemory&gt;
+  &lt;vcpu&gt;1&lt;/vcpu&gt;
+  &lt;os&gt;
+    &lt;type arch='x86_64'&gt;uml&lt;/type&gt;
+    &lt;kernel&gt;/home/berrange/linux-uml-2.6.26-x86_64&lt;/kernel&gt;
+  &lt;/os&gt;
+  &lt;devices&gt;
+    &lt;disk type='file' device='disk'&gt;
+      &lt;source file='/home/berrange/FedoraCore6-AMD64-root_fs'/&gt;
+      &lt;target dev='ubd0' bus='uml'/&gt;
+    &lt;/disk&gt;
+    &lt;console type='pty'/&gt;
+  &lt;/devices&gt;
+&lt;/domain&gt;
+    </pre>
+  </body>
+</html>
index 2b7ed164c877f3befbf14043ee5b1392da173522..0e801666bff1f7c6861481706ffb59a697858d3c 100644 (file)
                 <a href="drvopenvz.html">OpenVZ</a>
                 <span>Driver for the OpenVZ container technology</span>
               </li>
+              <li>
+                <a href="drvuml.html">UML</a>
+                <span>Driver for the User Mode Linux technology</span>
+              </li>
               <li>
                 <a href="storage.html">Storage</a>
                 <span>Driver for the storage management APIs</span>
index 8e24708e9614856098a57f78f03d91140de9f50f..868f1a3b32287a47450593960df82c89e9ccedaa 100644 (file)
@@ -58,6 +58,7 @@ typedef enum {
     VIR_FROM_STORAGE,   /* Error from storage driver */
     VIR_FROM_NETWORK,   /* Error from network config */
     VIR_FROM_DOMAIN,    /* Error from domain config */
+    VIR_FROM_UML,       /* Error at the UML driver */
 } virErrorDomain;
 
 
index 8d2dc5a5829542d6a798283430ceaf9715d7d72a..b271796221caf7ec0621cdc0715e1c17f1b710a3 100644 (file)
@@ -99,6 +99,10 @@ if WITH_LXC
 libvirtd_LDADD += ../src/libvirt_driver_lxc.la
 endif
 
+if WITH_UML
+libvirtd_LDADD += ../src/libvirt_driver_uml.la
+endif
+
 if WITH_STORAGE_DIR
 libvirtd_LDADD += ../src/libvirt_driver_storage.la
 endif
index fd4f3c0625e619dad8ef0cf89a5cb37c979289f0..bb5cfcdcc16b7f90274ff1b7ab439ec7e9ba77a0 100644 (file)
@@ -67,6 +67,9 @@
 #ifdef WITH_LXC
 #include "lxc_driver.h"
 #endif
+#ifdef WITH_UML
+#include "uml_driver.h"
+#endif
 #ifdef WITH_NETWORK
 #include "network_driver.h"
 #endif
@@ -751,6 +754,9 @@ static struct qemud_server *qemudInitialize(int sigread) {
 #ifdef WITH_LXC
     lxcRegister();
 #endif
+#ifdef WITH_UML
+    umlRegister();
+#endif
 #ifdef WITH_NETWORK
     networkRegister();
 #endif
index 3e6e726394e900c11899ad519072fd1c1cb9c701..b2c7e3f6ef9a4144dc531b66045c6160fe4dab4f 100644 (file)
@@ -117,6 +117,10 @@ QEMU_DRIVER_SOURCES =                                              \
                qemu_conf.c qemu_conf.h                         \
                qemu_driver.c qemu_driver.h
 
+UML_DRIVER_SOURCES =                                           \
+               uml_conf.c uml_conf.h                           \
+               uml_driver.c uml_driver.h
+
 NETWORK_DRIVER_SOURCES =                                       \
                network_driver.h network_driver.c
 
@@ -210,6 +214,12 @@ noinst_LTLIBRARIES += libvirt_driver_lxc.la
 libvirt_driver_lxc_la_SOURCES = $(LXC_DRIVER_SOURCES)
 endif
 
+if WITH_UML
+noinst_LTLIBRARIES += libvirt_driver_uml.la
+# Stateful, so linked to daemon instead
+#libvirt_la_LIBADD += libvirt_driver_uml.la
+libvirt_driver_uml_la_SOURCES = $(UML_DRIVER_SOURCES)
+endif
 
 if WITH_NETWORK
 noinst_LTLIBRARIES += libvirt_driver_network.la
@@ -247,6 +257,7 @@ EXTRA_DIST +=                                                       \
                $(XEN_DRIVER_SOURCES)                           \
                $(QEMU_DRIVER_SOURCES)                          \
                $(LXC_DRIVER_SOURCES)                           \
+               $(UML_DRIVER_SOURCES)                           \
                $(OPENVZ_DRIVER_SOURCES)                        \
                $(NETWORK_DRIVER_SOURCES)                       \
                $(STORAGE_DRIVER_SOURCES)                       \
index 7a5e5019ecb87ac0b584d197c1961fa868740b3c..39e60d45978203863a77e4382cdc0f7c5a79d96d 100644 (file)
@@ -86,7 +86,8 @@ VIR_ENUM_IMPL(virDomainDiskBus, VIR_DOMAIN_DISK_BUS_LAST,
               "scsi",
               "virtio",
               "xen",
-              "usb")
+              "usb",
+              "uml")
 
 VIR_ENUM_IMPL(virDomainFS, VIR_DOMAIN_FS_TYPE_LAST,
               "mount",
@@ -610,7 +611,8 @@ virDomainDiskDefParseXML(virConnectPtr conn,
         !STRPREFIX((const char *)target, "hd") &&
         !STRPREFIX((const char *)target, "sd") &&
         !STRPREFIX((const char *)target, "vd") &&
-        !STRPREFIX((const char *)target, "xvd")) {
+        !STRPREFIX((const char *)target, "xvd") &&
+        !STRPREFIX((const char *)target, "ubd")) {
         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              _("Invalid harddisk device name: %s"), target);
         goto error;
@@ -634,6 +636,8 @@ virDomainDiskDefParseXML(virConnectPtr conn,
                 def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO;
             else if (STRPREFIX(target, "xvd"))
                 def->bus = VIR_DOMAIN_DISK_BUS_XEN;
+            else if (STRPREFIX(target, "ubd"))
+                def->bus = VIR_DOMAIN_DISK_BUS_UML;
             else
                 def->bus = VIR_DOMAIN_DISK_BUS_IDE;
         }
@@ -1879,13 +1883,16 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
     }
 
     if (STREQ(def->os.type, "xen") ||
-        STREQ(def->os.type, "hvm")) {
+        STREQ(def->os.type, "hvm") ||
+        STREQ(def->os.type, "uml")) {
         def->os.kernel = virXPathString(conn, "string(./os/kernel[1])", ctxt);
         def->os.initrd = virXPathString(conn, "string(./os/initrd[1])", ctxt);
         def->os.cmdline = virXPathString(conn, "string(./os/cmdline[1])", ctxt);
         def->os.root = virXPathString(conn, "string(./os/root[1])", ctxt);
         def->os.loader = virXPathString(conn, "string(./os/loader[1])", ctxt);
+    }
 
+    if (STREQ(def->os.type, "hvm")) {
         /* analysis of the boot devices */
         if ((n = virXPathNodeSet(conn, "./os/boot", ctxt, &nodes)) < 0) {
             virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
@@ -2016,32 +2023,30 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
     }
     VIR_FREE(nodes);
 
-    /*
-     * If no serial devices were listed, then look for console
-     * devices which is the legacy syntax for the same thing
-     */
-    if (def->nserials == 0) {
-        if ((node = virXPathNode(conn, "./devices/console[1]", ctxt)) != NULL) {
-            virDomainChrDefPtr chr = virDomainChrDefParseXML(conn,
-                                                             node);
-            if (!chr)
-                goto error;
+    if ((node = virXPathNode(conn, "./devices/console[1]", ctxt)) != NULL) {
+        virDomainChrDefPtr chr = virDomainChrDefParseXML(conn,
+                                                         node);
+        if (!chr)
+            goto error;
 
-            chr->dstPort = 0;
-            /*
-             * For HVM console actually created a serial device
-             * while for non-HVM it was a parvirt console
-             */
-            if (STREQ(def->os.type, "hvm")) {
+        chr->dstPort = 0;
+        /*
+         * For HVM console actually created a serial device
+         * while for non-HVM it was a parvirt console
+         */
+        if (STREQ(def->os.type, "hvm")) {
+            if (def->nserials != 0) {
+                virDomainChrDefFree(chr);
+            } else {
                 if (VIR_ALLOC_N(def->serials, 1) < 0) {
                     virDomainChrDefFree(chr);
                     goto no_memory;
                 }
                 def->nserials = 1;
                 def->serials[0] = chr;
-            } else {
-                def->console = chr;
             }
+        } else {
+            def->console = chr;
         }
     }
 
index 88fd6d5da96beb944d5cbf3ec4d6ee34742ea48a..084c44852dc5cf1ddd50e6233425125ee3bc67aa 100644 (file)
@@ -75,6 +75,7 @@ enum virDomainDiskBus {
     VIR_DOMAIN_DISK_BUS_VIRTIO,
     VIR_DOMAIN_DISK_BUS_XEN,
     VIR_DOMAIN_DISK_BUS_USB,
+    VIR_DOMAIN_DISK_BUS_UML,
 
     VIR_DOMAIN_DISK_BUS_LAST
 };
index 8962affb88f8d5909b1cceb39c403b63de593612..c79df95848c4ed6360b3297f0490a3448f0b7992 100644 (file)
@@ -17,7 +17,8 @@ typedef enum {
     VIR_DRV_QEMU = 3,
     VIR_DRV_REMOTE = 4,
     VIR_DRV_OPENVZ = 5,
-    VIR_DRV_LXC = 6
+    VIR_DRV_LXC = 6,
+    VIR_DRV_UML = 7,
 } virDrvNo;
 
 
index 0e3b9598842f1b3ec1b7543d94927e11f95a8a2b..c66cef9e39549ae17e4ebc8498fcf6b931401398 100644 (file)
@@ -56,7 +56,8 @@ VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_DOMAIN_DISK_BUS_LAST,
               "scsi",
               "virtio",
               "xen",
-              "usb")
+              "usb",
+              "uml")
 
 
 #define qemudLog(level, msg...) fprintf(stderr, msg)
diff --git a/src/uml_conf.c b/src/uml_conf.c
new file mode 100644 (file)
index 0000000..674830d
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * uml_conf.c: UML driver configuration
+ *
+ * Copyright (C) 2006, 2007, 2008 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 <config.h>
+
+#include <dirent.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <sys/utsname.h>
+
+#if HAVE_NUMACTL
+#include <numa.h>
+#endif
+
+#include "uml_conf.h"
+#include "uuid.h"
+#include "buf.h"
+#include "conf.h"
+#include "util.h"
+#include "memory.h"
+#include "verify.h"
+
+
+#define umlLog(level, msg...) fprintf(stderr, msg)
+
+
+
+#if HAVE_NUMACTL
+#define MAX_CPUS 4096
+#define MAX_CPUS_MASK_SIZE (sizeof(unsigned long))
+#define MAX_CPUS_MASK_LEN (MAX_CPUS / MAX_CPUS_MASK_SIZE)
+#define MAX_CPUS_MASK_BYTES (MAX_CPUS / 8)
+
+#define MASK_CPU_ISSET(mask, cpu) \
+    (((mask)[((cpu) / MAX_CPUS_MASK_SIZE)] >> ((cpu) % MAX_CPUS_MASK_SIZE)) & 1)
+
+static int
+umlCapsInitNUMA(virCapsPtr caps)
+{
+    int n, i;
+    unsigned long *mask = NULL;
+    int ncpus;
+    int *cpus = NULL;
+    int ret = -1;
+
+    if (numa_available() < 0)
+        return 0;
+
+    if (VIR_ALLOC_N(mask, MAX_CPUS_MASK_LEN) < 0)
+        goto cleanup;
+
+    for (n = 0 ; n <= numa_max_node() ; n++) {
+        if (numa_node_to_cpus(n, mask, MAX_CPUS_MASK_BYTES) < 0)
+            goto cleanup;
+
+        for (ncpus = 0, i = 0 ; i < MAX_CPUS ; i++)
+            if (MASK_CPU_ISSET(mask, i))
+                ncpus++;
+
+        if (VIR_ALLOC_N(cpus, ncpus) < 0)
+            goto cleanup;
+
+        for (ncpus = 0, i = 0 ; i < MAX_CPUS ; i++)
+            if (MASK_CPU_ISSET(mask, i))
+                cpus[ncpus++] = i;
+
+        if (virCapabilitiesAddHostNUMACell(caps,
+                                           n,
+                                           ncpus,
+                                           cpus) < 0)
+            goto cleanup;
+
+        VIR_FREE(cpus);
+    }
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(cpus);
+    VIR_FREE(mask);
+    return ret;
+}
+#else
+static int umlCapsInitNUMA(virCapsPtr caps ATTRIBUTE_UNUSED) { return 0; }
+#endif
+
+virCapsPtr umlCapsInit(void) {
+    struct utsname utsname;
+    virCapsPtr caps;
+    virCapsGuestPtr guest;
+
+    /* Really, this never fails - look at the man-page. */
+    uname (&utsname);
+
+    if ((caps = virCapabilitiesNew(utsname.machine,
+                                   0, 0)) == NULL)
+        goto no_memory;
+
+    if (umlCapsInitNUMA(caps) < 0)
+        goto no_memory;
+
+    if ((guest = virCapabilitiesAddGuest(caps,
+                                         "uml",
+                                         utsname.machine,
+                                         STREQ(utsname.machine, "x86_64") ? 64 : 32,
+                                         NULL,
+                                         NULL,
+                                         0,
+                                         NULL)) == NULL)
+        goto no_memory;
+
+    if (virCapabilitiesAddGuestDomain(guest,
+                                      "uml",
+                                      NULL,
+                                      NULL,
+                                      0,
+                                      NULL) == NULL)
+        goto no_memory;
+
+    return caps;
+
+ no_memory:
+    virCapabilitiesFree(caps);
+    return NULL;
+}
+
+
+static char *
+umlBuildCommandLineChr(virConnectPtr conn,
+                       virDomainChrDefPtr def,
+                       const char *dev)
+{
+    char *ret;
+
+    switch (def->type) {
+    case VIR_DOMAIN_CHR_TYPE_NULL:
+        if (asprintf(&ret, "%s%d=null", dev, def->dstPort) < 0) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+            return NULL;
+        }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_PTY:
+        if (asprintf(&ret, "%s%d=pts", dev, def->dstPort) < 0) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+            return NULL;
+        }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_DEV:
+        if (asprintf(&ret, "%s%d=tty:%s", dev, def->dstPort,
+                     def->data.file.path) < 0) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+            return NULL;
+        }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_STDIO:
+        if (asprintf(&ret, "%s%d=fd:0,fd:1", dev, def->dstPort) < 0) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+            return NULL;
+        }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_TCP:
+        if (def->data.tcp.listen != 1) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("only TCP listen is supported for chr device"));
+            return NULL;
+        }
+
+        if (asprintf(&ret, "%s%d=port:%s", dev, def->dstPort,
+                     def->data.tcp.service) < 0) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+            return NULL;
+        }
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_FILE:
+    case VIR_DOMAIN_CHR_TYPE_PIPE:
+        /* XXX could open the file/pipe & just pass the FDs */
+
+    case VIR_DOMAIN_CHR_TYPE_VC:
+    case VIR_DOMAIN_CHR_TYPE_UDP:
+    case VIR_DOMAIN_CHR_TYPE_UNIX:
+    default:
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("unsupported chr device type %d"), def->type);
+        break;
+    }
+
+    return ret;
+}
+
+/*
+ * Constructs a argv suitable for launching uml with config defined
+ * for a given virtual machine.
+ */
+int umlBuildCommandLine(virConnectPtr conn,
+                        struct uml_driver *driver ATTRIBUTE_UNUSED,
+                        virDomainObjPtr vm,
+                        const char ***retargv,
+                        const char ***retenv,
+                        int **tapfds,
+                        int *ntapfds) {
+    int i, j;
+    char memory[50];
+    struct utsname ut;
+    int qargc = 0, qarga = 0;
+    const char **qargv = NULL;
+    int qenvc = 0, qenva = 0;
+    const char **qenv = NULL;
+
+    uname(&ut);
+
+#define ADD_ARG_SPACE                                                   \
+    do {                                                                \
+        if (qargc == qarga) {                                           \
+            qarga += 10;                                                \
+            if (VIR_REALLOC_N(qargv, qarga) < 0)                        \
+                goto no_memory;                                         \
+        }                                                               \
+    } while (0)
+
+#define ADD_ARG(thisarg)                                                \
+    do {                                                                \
+        ADD_ARG_SPACE;                                                  \
+        qargv[qargc++] = thisarg;                                       \
+    } while (0)
+
+#define ADD_ARG_LIT(thisarg)                                            \
+    do {                                                                \
+        ADD_ARG_SPACE;                                                  \
+        if ((qargv[qargc++] = strdup(thisarg)) == NULL)                 \
+            goto no_memory;                                             \
+    } while (0)
+
+#define ADD_ARG_PAIR(key,val)                                           \
+    do {                                                                \
+        char *arg;                                                      \
+        ADD_ARG_SPACE;                                                  \
+        if (asprintf(&arg, "%s=%s", key, val) < 0)                      \
+            goto no_memory;                                             \
+        qargv[qargc++] = arg;                                            \
+    } while (0)
+
+
+#define ADD_ENV_SPACE                                                   \
+    do {                                                                \
+        if (qenvc == qenva) {                                           \
+            qenva += 10;                                                \
+            if (VIR_REALLOC_N(qenv, qenva) < 0)                         \
+                goto no_memory;                                         \
+        }                                                               \
+    } while (0)
+
+#define ADD_ENV(thisarg)                                                \
+    do {                                                                \
+        ADD_ENV_SPACE;                                                  \
+        qenv[qenvc++] = thisarg;                                        \
+    } while (0)
+
+#define ADD_ENV_LIT(thisarg)                                            \
+    do {                                                                \
+        ADD_ENV_SPACE;                                                  \
+        if ((qenv[qenvc++] = strdup(thisarg)) == NULL)                  \
+            goto no_memory;                                             \
+    } while (0)
+
+#define ADD_ENV_COPY(envname)                                           \
+    do {                                                                \
+        char *val = getenv(envname);                                    \
+        char *envval;                                                   \
+        ADD_ENV_SPACE;                                                  \
+        if (val != NULL) {                                              \
+            if (asprintf(&envval, "%s=%s", envname, val) < 0)           \
+                goto no_memory;                                         \
+            qenv[qenvc++] = envval;                                     \
+        }                                                               \
+    } while (0)
+
+    snprintf(memory, sizeof(memory), "%luK", vm->def->memory);
+
+    ADD_ENV_LIT("LC_ALL=C");
+
+    ADD_ENV_COPY("LD_PRELOAD");
+    ADD_ENV_COPY("LD_LIBRARY_PATH");
+    ADD_ENV_COPY("PATH");
+    ADD_ENV_COPY("HOME");
+    ADD_ENV_COPY("USER");
+    ADD_ENV_COPY("LOGNAME");
+    ADD_ENV_COPY("TMPDIR");
+
+    ADD_ARG_LIT(vm->def->os.kernel);
+    //ADD_ARG_PAIR("con0", "fd:0,fd:1");
+    ADD_ARG_PAIR("mem", memory);
+    ADD_ARG_PAIR("umid", vm->def->name);
+
+    if (vm->def->os.root)
+        ADD_ARG_PAIR("root", vm->def->os.root);
+
+    for (i = 0 ; i < vm->def->ndisks ; i++) {
+        virDomainDiskDefPtr disk = vm->def->disks[i];
+
+        if (!STRPREFIX(disk->dst, "ubd")) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                           _("unsupported disk type '%s'"), disk->dst);
+            goto error;
+        }
+
+        ADD_ARG_PAIR(disk->dst, disk->src);
+    }
+
+    for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
+        char *ret;
+        if (i == 0 && vm->def->console)
+            ret = umlBuildCommandLineChr(conn, vm->def->console, "con");
+        else
+            if (asprintf(&ret, "con%d=none", i) < 0)
+                goto no_memory;
+        ADD_ARG(ret);
+    }
+
+    for (i = 0 ; i < UML_MAX_CHAR_DEVICE ; i++) {
+        virDomainChrDefPtr chr = NULL;
+        char *ret;
+        for (j = 0 ; j < vm->def->nserials ; j++)
+            if (vm->def->serials[j]->dstPort == i)
+                chr = vm->def->serials[j];
+        if (chr)
+            ret = umlBuildCommandLineChr(conn, chr, "ssl");
+        else
+            if (asprintf(&ret, "ssl%d=none", i) < 0)
+                goto no_memory;
+        ADD_ARG(ret);
+    }
+
+    ADD_ARG(NULL);
+    ADD_ENV(NULL);
+
+    *retargv = qargv;
+    *retenv = qenv;
+    return 0;
+
+ no_memory:
+    umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
+                     "%s", _("failed to allocate space for argv string"));
+ error:
+    if (tapfds &&
+        *tapfds) {
+        for (i = 0; i < *ntapfds; i++)
+            close((*tapfds)[i]);
+        VIR_FREE(*tapfds);
+        *ntapfds = 0;
+    }
+    if (qargv) {
+        for (i = 0 ; i < qargc ; i++)
+            VIR_FREE((qargv)[i]);
+        VIR_FREE(qargv);
+    }
+    if (qenv) {
+        for (i = 0 ; i < qenvc ; i++)
+            VIR_FREE((qenv)[i]);
+        VIR_FREE(qenv);
+    }
+    return -1;
+
+#undef ADD_ARG
+#undef ADD_ARG_LIT
+#undef ADD_ARG_SPACE
+#undef ADD_USBDISK
+#undef ADD_ENV
+#undef ADD_ENV_COPY
+#undef ADD_ENV_LIT
+#undef ADD_ENV_SPACE
+}
diff --git a/src/uml_conf.h b/src/uml_conf.h
new file mode 100644 (file)
index 0000000..1213df1
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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 __UML_CONF_H
+#define __UML_CONF_H
+
+#include "internal.h"
+#include "bridge.h"
+#include "capabilities.h"
+#include "network_conf.h"
+#include "domain_conf.h"
+#include "virterror_internal.h"
+
+#define umlDebug(fmt, ...) do {} while(0)
+
+#define UML_CPUMASK_LEN CPU_SETSIZE
+
+#define UML_MAX_CHAR_DEVICE 16
+
+/* Main driver state */
+struct uml_driver {
+    unsigned int umlVersion;
+    int nextvmid;
+
+    virDomainObjList domains;
+
+    brControl *brctl;
+    char *configDir;
+    char *autostartDir;
+    char *logDir;
+    char *monitorDir;
+
+    int inotifyFD;
+    int inotifyWatch;
+
+    virCapsPtr caps;
+};
+
+
+#define umlReportError(conn, dom, net, code, fmt...)                    \
+    virReportErrorHelper(conn, VIR_FROM_UML, code, __FILE__,            \
+                         __FUNCTION__, __LINE__, fmt)
+
+virCapsPtr  umlCapsInit               (void);
+
+int         umlBuildCommandLine       (virConnectPtr conn,
+                                      struct uml_driver *driver,
+                                      virDomainObjPtr dom,
+                                      const char ***retargv,
+                                      const char ***retenv,
+                                      int **tapfds,
+                                      int *ntapfds);
+
+#endif /* __UML_CONF_H */
diff --git a/src/uml_driver.c b/src/uml_driver.c
new file mode 100644 (file)
index 0000000..ac0c32c
--- /dev/null
@@ -0,0 +1,1676 @@
+/*
+ * uml_driver.c: core driver methods for managing UML guests
+ *
+ * Copyright (C) 2006, 2007, 2008 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <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 <sys/utsname.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/inotify.h>
+
+#if HAVE_NUMACTL
+#include <numa.h>
+#endif
+
+#include "uml_driver.h"
+#include "uml_conf.h"
+#include "c-ctype.h"
+#include "event.h"
+#include "buf.h"
+#include "util.h"
+#include "nodeinfo.h"
+#include "stats_linux.h"
+#include "capabilities.h"
+#include "memory.h"
+#include "uuid.h"
+#include "domain_conf.h"
+#include "datatypes.h"
+
+/* For storing short-lived temporary files. */
+#define TEMPDIR LOCAL_STATE_DIR "/cache/libvirt"
+
+static int umlShutdown(void);
+
+/* umlDebug statements should be changed to use this macro instead. */
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+
+#define umlLog(level, msg...) fprintf(stderr, msg)
+
+
+static int umlOpenMonitor(virConnectPtr conn,
+                          struct uml_driver *driver,
+                          virDomainObjPtr vm);
+static int umlReadPidFile(virConnectPtr conn,
+                          struct uml_driver *driver,
+                          virDomainObjPtr vm);
+
+static int umlSetCloseExec(int fd) {
+    int flags;
+    if ((flags = fcntl(fd, F_GETFD)) < 0)
+        goto error;
+    flags |= FD_CLOEXEC;
+    if ((fcntl(fd, F_SETFD, flags)) < 0)
+        goto error;
+    return 0;
+ error:
+    umlLog(UML_ERR,
+             "%s", _("Failed to set close-on-exec file descriptor flag\n"));
+    return -1;
+}
+
+static int umlStartVMDaemon(virConnectPtr conn,
+                            struct uml_driver *driver,
+                            virDomainObjPtr vm);
+
+static void umlShutdownVMDaemon(virConnectPtr conn,
+                                struct uml_driver *driver,
+                                virDomainObjPtr vm);
+
+
+static int umlMonitorCommand (virConnectPtr conn,
+                              const struct uml_driver *driver,
+                              const virDomainObjPtr vm,
+                              const char *cmd,
+                              char **reply);
+
+static struct uml_driver *uml_driver = NULL;
+
+
+static void
+umlAutostartConfigs(struct uml_driver *driver) {
+    unsigned int i;
+
+    for (i = 0 ; i < driver->domains.count ; i++) {
+        if (driver->domains.objs[i]->autostart &&
+            !virDomainIsActive(driver->domains.objs[i]) &&
+            umlStartVMDaemon(NULL, driver, driver->domains.objs[i]) < 0) {
+            virErrorPtr err = virGetLastError();
+            umlLog(UML_ERR, _("Failed to autostart VM '%s': %s\n"),
+                     driver->domains.objs[i]->def->name, err->message);
+        }
+    }
+}
+
+
+static int
+umlIdentifyOneChrPTY(virConnectPtr conn,
+                     struct uml_driver *driver,
+                     virDomainObjPtr dom,
+                     virDomainChrDefPtr def,
+                     const char *dev)
+{
+    char *cmd;
+    char *res = NULL;
+    int retries = 0;
+    if (asprintf(&cmd, "config %s%d", dev, def->dstPort) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return -1;
+    }
+requery:
+    umlMonitorCommand(NULL, driver, dom, cmd, &res);
+
+    if (STRPREFIX(res, "pts:")) {
+        VIR_FREE(def->data.file.path);
+        if ((def->data.file.path = strdup(res + 4)) == NULL) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+            VIR_FREE(res);
+            VIR_FREE(cmd);
+            return -1;
+        }
+    } else if (STRPREFIX(res, "pts")) {
+        /* It can take a while to startup, so retry for
+           upto 5 seconds */
+        /* XXX should do this in a better non-blocking
+           way somehow ...perhaps register a timer */
+        if (retries++ < 50) {
+            usleep(1000*10);
+            goto requery;
+        }
+    }
+
+    VIR_FREE(cmd);
+    VIR_FREE(res);
+    return 0;
+}
+
+static int
+umlIdentifyChrPTY(virConnectPtr conn,
+                  struct uml_driver *driver,
+                  virDomainObjPtr dom)
+{
+    int i;
+
+    if (dom->def->console &&
+        dom->def->console->type == VIR_DOMAIN_CHR_TYPE_PTY)
+        if (umlIdentifyOneChrPTY(conn, driver, dom,
+                                 dom->def->console, "con") < 0)
+            return -1;
+
+    for (i = 0 ; i < dom->def->nserials; i++)
+        if (dom->def->serials[i]->type == VIR_DOMAIN_CHR_TYPE_PTY &&
+            umlIdentifyOneChrPTY(conn, driver, dom,
+                                 dom->def->serials[i], "ssl") < 0)
+            return -1;
+
+    return 0;
+}
+
+static void
+umlInotifyEvent(int watch,
+                int fd,
+                int events ATTRIBUTE_UNUSED,
+                void *data)
+{
+    char buf[1024];
+    struct inotify_event *e;
+    int got;
+    char *tmp, *name;
+    struct uml_driver *driver = data;
+    virDomainObjPtr dom;
+
+    if (watch != driver->inotifyWatch)
+        return;
+
+reread:
+    got = read(fd, buf, sizeof(buf));
+    if (got == -1) {
+        if (errno == EINTR)
+            goto reread;
+        return;
+    }
+
+    tmp = buf;
+    while (got) {
+        if (got < sizeof(struct inotify_event))
+            return; /* bad */
+
+        e = (struct inotify_event *)tmp;
+        tmp += sizeof(struct inotify_event);
+        got -= sizeof(struct inotify_event);
+
+        if (got < e->len)
+            return;
+
+        tmp += e->len;
+        got -= e->len;
+
+        name = (char *)&(e->name);
+
+        dom = virDomainFindByName(&driver->domains, name);
+
+        if (!dom) {
+            continue;
+        }
+
+        if (e->mask & IN_DELETE) {
+            if (!virDomainIsActive(dom)) {
+                continue;
+            }
+
+            dom->def->id = -1;
+            dom->pid = -1;
+            if (dom->newDef) {
+                virDomainDefFree(dom->def);
+                dom->def = dom->newDef;
+            }
+            dom->state = VIR_DOMAIN_SHUTOFF;
+        } else if (e->mask & (IN_CREATE | IN_MODIFY)) {
+            if (virDomainIsActive(dom)) {
+                continue;
+            }
+
+            if (umlReadPidFile(NULL, driver, dom) < 0) {
+                continue;
+            }
+
+            dom->def->id = driver->nextvmid++;
+            dom->state = VIR_DOMAIN_RUNNING;
+
+            if (umlOpenMonitor(NULL, driver, dom) < 0)
+                umlShutdownVMDaemon(NULL, driver, dom);
+
+            if (umlIdentifyChrPTY(NULL, driver, dom) < 0)
+                umlShutdownVMDaemon(NULL, driver, dom);
+        }
+    }
+}
+
+/**
+ * umlStartup:
+ *
+ * Initialization function for the Uml daemon
+ */
+static int
+umlStartup(void) {
+    uid_t uid = geteuid();
+    struct passwd *pw;
+    char *base = NULL;
+    char driverConf[PATH_MAX];
+
+    if (VIR_ALLOC(uml_driver) < 0)
+        return -1;
+
+    /* Don't have a dom0 so start from 1 */
+    uml_driver->nextvmid = 1;
+
+    if (!(pw = getpwuid(uid))) {
+        umlLog(UML_ERR, _("Failed to find user record for uid '%d': %s\n"),
+               uid, strerror(errno));
+        goto out_nouid;
+    }
+
+    if (!uid) {
+        if (asprintf(&uml_driver->logDir,
+                     "%s/log/libvirt/uml", LOCAL_STATE_DIR) == -1)
+            goto out_of_memory;
+
+        if ((base = strdup (SYSCONF_DIR "/libvirt")) == NULL)
+            goto out_of_memory;
+    } else {
+        if (asprintf(&uml_driver->logDir,
+                     "%s/.libvirt/uml/log", pw->pw_dir) == -1)
+            goto out_of_memory;
+
+        if (asprintf (&base, "%s/.libvirt", pw->pw_dir) == -1)
+            goto out_of_memory;
+    }
+
+    if (asprintf (&uml_driver->monitorDir,
+                  "%s/.uml", pw->pw_dir) == -1)
+        goto out_of_memory;
+
+    /* Configuration paths are either ~/.libvirt/uml/... (session) or
+     * /etc/libvirt/uml/... (system).
+     */
+    if (snprintf (driverConf, sizeof(driverConf), "%s/uml.conf", base) == -1)
+        goto out_of_memory;
+    driverConf[sizeof(driverConf)-1] = '\0';
+
+    if (asprintf (&uml_driver->configDir, "%s/uml", base) == -1)
+        goto out_of_memory;
+
+    if (asprintf (&uml_driver->autostartDir, "%s/uml/autostart", base) == -1)
+        goto out_of_memory;
+
+    VIR_FREE(base);
+
+    if ((uml_driver->caps = umlCapsInit()) == NULL)
+        goto out_of_memory;
+
+
+    if ((uml_driver->inotifyFD = inotify_init()) < 0) {
+        umlLog(UML_ERR, "%s", _("cannot initialize inotify"));
+        goto out_nouid;
+    }
+
+    if (virFileMakePath(uml_driver->monitorDir) < 0) {
+        umlLog(UML_ERR, _("Failed to create monitor directory %s: %s"),
+               uml_driver->monitorDir, strerror(errno));
+        umlShutdown();
+        return -1;
+    }
+
+    if ((uml_driver->inotifyWatch =
+         inotify_add_watch(uml_driver->inotifyFD,
+                           uml_driver->monitorDir,
+                           IN_CREATE | IN_MODIFY | IN_DELETE)) < 0) {
+        umlShutdown();
+        return -1;
+    }
+
+    if (virEventAddHandle(uml_driver->inotifyFD, POLLIN,
+                          umlInotifyEvent, uml_driver, NULL) < 0) {
+        umlShutdown();
+        return -1;
+    }
+
+    if (virDomainLoadAllConfigs(NULL,
+                                uml_driver->caps,
+                                &uml_driver->domains,
+                                uml_driver->configDir,
+                                uml_driver->autostartDir,
+                                NULL, NULL) < 0) {
+        umlShutdown();
+        return -1;
+    }
+    umlAutostartConfigs(uml_driver);
+
+    return 0;
+
+ out_of_memory:
+    umlLog (UML_ERR,
+              "%s", _("umlStartup: out of memory\n"));
+ out_nouid:
+    VIR_FREE(base);
+    VIR_FREE(uml_driver);
+    return -1;
+}
+
+/**
+ * umlReload:
+ *
+ * Function to restart the Uml daemon, it will recheck the configuration
+ * files and update its state and the networking
+ */
+static int
+umlReload(void) {
+    if (!uml_driver)
+        return 0;
+
+    virDomainLoadAllConfigs(NULL,
+                            uml_driver->caps,
+                            &uml_driver->domains,
+                            uml_driver->configDir,
+                            uml_driver->autostartDir,
+                            NULL, NULL);
+
+    umlAutostartConfigs(uml_driver);
+
+    return 0;
+}
+
+/**
+ * umlActive:
+ *
+ * Checks if the Uml daemon is active, i.e. has an active domain or
+ * an active network
+ *
+ * Returns 1 if active, 0 otherwise
+ */
+static int
+umlActive(void) {
+    unsigned int i;
+
+    if (!uml_driver)
+        return 0;
+
+    for (i = 0 ; i < uml_driver->domains.count ; i++)
+        if (virDomainIsActive(uml_driver->domains.objs[i]))
+            return 1;
+
+    /* Otherwise we're happy to deal with a shutdown */
+    return 0;
+}
+
+/**
+ * umlShutdown:
+ *
+ * Shutdown the Uml daemon, it will stop all active domains and networks
+ */
+static int
+umlShutdown(void) {
+    unsigned int i;
+
+    if (!uml_driver)
+        return -1;
+
+    virEventRemoveHandle(uml_driver->inotifyWatch);
+    close(uml_driver->inotifyFD);
+    virCapabilitiesFree(uml_driver->caps);
+
+    /* shutdown active VMs */
+    for (i = 0 ; i < uml_driver->domains.count ; i++) {
+        virDomainObjPtr dom = uml_driver->domains.objs[i];
+        if (virDomainIsActive(dom))
+            umlShutdownVMDaemon(NULL, uml_driver, dom);
+        if (!dom->persistent)
+            virDomainRemoveInactive(&uml_driver->domains,
+                                    dom);
+    }
+
+    virDomainObjListFree(&uml_driver->domains);
+
+    VIR_FREE(uml_driver->logDir);
+    VIR_FREE(uml_driver->configDir);
+    VIR_FREE(uml_driver->autostartDir);
+    VIR_FREE(uml_driver->monitorDir);
+
+    if (uml_driver->brctl)
+        brShutdown(uml_driver->brctl);
+
+    VIR_FREE(uml_driver);
+
+    return 0;
+}
+
+
+static int umlReadPidFile(virConnectPtr conn,
+                          struct uml_driver *driver,
+                          virDomainObjPtr vm)
+{
+    int rc = -1;
+    FILE *file;
+    char *pidfile = NULL;
+    int retries = 0;
+
+    vm->pid = -1;
+    if (asprintf(&pidfile, "%s/%s/pid",
+                 driver->monitorDir, vm->def->name) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return -1;
+    }
+
+reopen:
+    if (!(file = fopen(pidfile, "r"))) {
+        if (errno == ENOENT &&
+            retries++ < 50) {
+            usleep(1000 * 100);
+            goto reopen;
+        }
+        goto cleanup;
+    }
+
+    if (fscanf(file, "%d", &vm->pid) != 1) {
+        errno = EINVAL;
+        goto cleanup;
+    }
+
+    if (fclose(file) < 0)
+        goto cleanup;
+
+    rc = 0;
+
+ cleanup:
+    if (rc != 0)
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("failed to read pid: %s: %s"),
+                       pidfile, strerror(errno));
+    VIR_FREE(pidfile);
+    return rc;
+}
+
+static int umlMonitorAddress(virConnectPtr conn,
+                             const struct uml_driver *driver,
+                             virDomainObjPtr vm,
+                             struct sockaddr_un *addr) {
+    char *sockname;
+
+    if (asprintf(&sockname, "%s/%s/mconsole",
+                 driver->monitorDir, vm->def->name) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return -1;
+    }
+
+    memset(addr, 0, sizeof *addr);
+    addr->sun_family = AF_UNIX;
+    strncpy(addr->sun_path, sockname, sizeof(addr->sun_path)-1);
+    NUL_TERMINATE(addr->sun_path);
+    VIR_FREE(sockname);
+    return 0;
+}
+
+static int umlOpenMonitor(virConnectPtr conn,
+                          struct uml_driver *driver,
+                          virDomainObjPtr vm) {
+    struct sockaddr_un addr;
+    struct stat sb;
+    int retries = 0;
+
+    if (umlMonitorAddress(conn, driver, vm, &addr) < 0)
+        return -1;
+
+restat:
+    if (stat(addr.sun_path, &sb) < 0) {
+        if (errno == ENOENT &&
+            retries < 50) {
+            usleep(1000 * 100);
+            goto restat;
+        }
+        return -1;
+    }
+
+    if ((vm->monitor = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("cannot open socket %s"), strerror(errno));
+        return -1;
+    }
+
+    memset(addr.sun_path, 0, sizeof addr.sun_path);
+    sprintf(addr.sun_path + 1, "%u", getpid());
+    if (bind(vm->monitor, (struct sockaddr *)&addr, sizeof addr) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("cannot bind socket %s"), strerror(errno));
+        close(vm->monitor);
+        vm->monitor = -1;
+        return -1;
+    }
+
+    return 0;
+}
+
+
+#define MONITOR_MAGIC 0xcafebabe
+#define MONITOR_BUFLEN 512
+#define MONITOR_VERSION 2
+
+struct monitor_request {
+    uint32_t magic;
+    uint32_t version;
+    uint32_t length;
+    char data[MONITOR_BUFLEN];
+};
+
+struct monitor_response {
+    uint32_t error;
+    uint32_t extra;
+    uint32_t length;
+    char data[MONITOR_BUFLEN];
+};
+
+
+static int umlMonitorCommand(virConnectPtr conn,
+                             const struct uml_driver *driver,
+                             const virDomainObjPtr vm,
+                             const char *cmd,
+                             char **reply)
+{
+    struct monitor_request req;
+    struct monitor_response res;
+    char *retdata = NULL;
+    int retlen = 0, ret = 0;
+    struct sockaddr_un addr;
+    unsigned int addrlen;
+
+    *reply = NULL;
+
+    if (umlMonitorAddress(conn, driver, vm, &addr) < 0)
+        return -1;
+
+    memset(&req, 0, sizeof(req));
+    req.magic = MONITOR_MAGIC;
+    req.version = MONITOR_VERSION;
+    req.length = strlen(cmd);
+    if (req.length > (MONITOR_BUFLEN-1)) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("cannot send too long command %s: %s"),
+                       cmd, strerror(EINVAL));
+        return -1;
+    }
+    strncpy(req.data, cmd, req.length);
+    req.data[req.length] = '\0';
+
+    if (sendto(vm->monitor, &req, sizeof req, 0,
+               (struct sockaddr *)&addr, sizeof addr) != (sizeof req)) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("cannot send command %s: %s"),
+                       cmd, strerror(errno));
+        return -1;
+    }
+
+    do {
+        addrlen = sizeof(addr);
+        if (recvfrom(vm->monitor, &res, sizeof res, 0,
+                     (struct sockaddr *)&addr, &addrlen) < 0) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                           _("cannot read reply %s: %s"),
+                           cmd, strerror(errno));
+            goto error;
+        }
+
+        if (VIR_REALLOC_N(retdata, retlen + res.length) < 0) {
+            umlReportError(conn, NULL, NULL,
+                           VIR_ERR_NO_MEMORY, NULL);
+            goto error;
+        }
+        memcpy(retdata + retlen, res.data, res.length);
+        retlen += res.length - 1;
+        retdata[retlen] = '\0';
+
+        if (res.error)
+            ret = -1;
+
+    } while (res.extra);
+
+    *reply = retdata;
+
+    return ret;
+
+error:
+    VIR_FREE(retdata);
+    return -1;
+}
+
+
+static int umlStartVMDaemon(virConnectPtr conn,
+                            struct uml_driver *driver,
+                            virDomainObjPtr vm) {
+    const char **argv = NULL, **tmp;
+    const char **progenv = NULL;
+    int i, ret, pid;
+    char *logfile;
+    int logfd = -1;
+    struct stat sb;
+    int *tapfds = NULL;
+    int ntapfds = 0;
+    fd_set keepfd;
+
+    FD_ZERO(&keepfd);
+
+    if (virDomainIsActive(vm)) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                         "%s", _("VM is already active"));
+        return -1;
+    }
+
+    if (!vm->def->os.kernel) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("no kernel specified"));
+        return -1;
+    }
+    /* Make sure the binary we are about to try exec'ing exists.
+     * Technically we could catch the exec() failure, but that's
+     * in a sub-process so its hard to feed back a useful error
+     */
+    if (stat(vm->def->os.kernel, &sb) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("Cannot find UML kernel %s: %s"),
+                       vm->def->os.kernel, strerror(errno));
+        return -1;
+    }
+
+    if (virFileMakePath(driver->logDir) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("cannot create log directory %s"),
+                       driver->logDir);
+        return -1;
+    }
+
+    if (asprintf(&logfile, "%s/%s.log",
+                 driver->logDir, vm->def->name) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+        return -1;
+    }
+
+    if ((logfd = open(logfile, O_CREAT | O_TRUNC | O_WRONLY,
+                      S_IRUSR | S_IWUSR)) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("failed to create logfile %s: %s"),
+                       logfile, strerror(errno));
+        VIR_FREE(logfile);
+        return -1;
+    }
+    VIR_FREE(logfile);
+
+    if (umlSetCloseExec(logfd) < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to set VM logfile close-on-exec flag %s"),
+                       strerror(errno));
+        close(logfd);
+        return -1;
+    }
+
+    if (umlBuildCommandLine(conn, driver, vm,
+                            &argv, &progenv,
+                            &tapfds, &ntapfds) < 0) {
+        close(logfd);
+        return -1;
+    }
+
+    tmp = progenv;
+    while (*tmp) {
+        if (safewrite(logfd, *tmp, strlen(*tmp)) < 0)
+            umlLog(UML_WARN, _("Unable to write envv to logfile %d: %s\n"),
+                   errno, strerror(errno));
+        if (safewrite(logfd, " ", 1) < 0)
+            umlLog(UML_WARN, _("Unable to write envv to logfile %d: %s\n"),
+                   errno, strerror(errno));
+        tmp++;
+    }
+    tmp = argv;
+    while (*tmp) {
+        if (safewrite(logfd, *tmp, strlen(*tmp)) < 0)
+            umlLog(UML_WARN, _("Unable to write argv to logfile %d: %s\n"),
+                   errno, strerror(errno));
+        if (safewrite(logfd, " ", 1) < 0)
+            umlLog(UML_WARN, _("Unable to write argv to logfile %d: %s\n"),
+                   errno, strerror(errno));
+        tmp++;
+    }
+    if (safewrite(logfd, "\n", 1) < 0)
+        umlLog(UML_WARN, _("Unable to write argv to logfile %d: %s\n"),
+                 errno, strerror(errno));
+
+    vm->monitor = -1;
+    vm->stdin_fd = -1;
+    vm->stdout_fd = vm->stderr_fd = logfd;
+
+    for (i = 0 ; i < ntapfds ; i++)
+        FD_SET(tapfds[i], &keepfd);
+
+    ret = virExec(conn, argv, progenv, &keepfd, &pid,
+                  vm->stdin_fd, &vm->stdout_fd, &vm->stderr_fd,
+                  VIR_EXEC_DAEMON);
+    close(logfd);
+
+    /* Cleanup intermediate proces */
+    if (waitpid(pid, NULL, 0) != pid)
+        umlLog(UML_WARN, _("failed to wait on process: %d: %s\n"),
+               pid, strerror(errno));
+
+    for (i = 0 ; argv[i] ; i++)
+        VIR_FREE(argv[i]);
+    VIR_FREE(argv);
+
+    for (i = 0 ; progenv[i] ; i++)
+        VIR_FREE(progenv[i]);
+    VIR_FREE(progenv);
+
+    if (tapfds) {
+        for (i = 0 ; i < ntapfds ; i++) {
+            close(tapfds[i]);
+        }
+        VIR_FREE(tapfds);
+    }
+
+    /* NB we don't mark it running here - we do that async
+       with inotify */
+
+    return ret;
+}
+
+static void umlShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED,
+                                struct uml_driver *driver ATTRIBUTE_UNUSED,
+                                virDomainObjPtr vm)
+{
+    int ret;
+    if (!virDomainIsActive(vm) ||
+        vm->pid <= 1)
+        return;
+
+
+    kill(vm->pid, SIGTERM);
+
+    if (vm->monitor != -1)
+        close(vm->monitor);
+    vm->monitor = -1;
+
+    if ((ret = waitpid(vm->pid, NULL, 0)) != vm->pid) {
+        umlLog(UML_WARN,
+               _("Got unexpected pid %d != %d\n"),
+               ret, vm->pid);
+    }
+
+    vm->pid = -1;
+    vm->def->id = -1;
+    vm->state = VIR_DOMAIN_SHUTOFF;
+    VIR_FREE(vm->vcpupids);
+    vm->nvcpupids = 0;
+
+    if (vm->newDef) {
+        virDomainDefFree(vm->def);
+        vm->def = vm->newDef;
+        vm->def->id = -1;
+        vm->newDef = NULL;
+    }
+}
+
+
+static virDrvOpenStatus umlOpen(virConnectPtr conn,
+                                virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                                int flags ATTRIBUTE_UNUSED) {
+    uid_t uid = getuid();
+
+    if (uml_driver == NULL)
+        goto decline;
+
+    if (conn->uri != NULL) {
+        if (conn->uri->scheme == NULL || conn->uri->path == NULL)
+            goto decline;
+
+        if (STRNEQ (conn->uri->scheme, "uml"))
+            goto decline;
+
+        if (uid != 0) {
+            if (STRNEQ (conn->uri->path, "/session"))
+                goto decline;
+        } else { /* root */
+            if (STRNEQ (conn->uri->path, "/system") &&
+                STRNEQ (conn->uri->path, "/session"))
+                goto decline;
+        }
+    } else {
+        conn->uri = xmlParseURI(uid ? "uml:///session" : "uml:///system");
+        if (!conn->uri) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,NULL);
+            return VIR_DRV_OPEN_ERROR;
+        }
+    }
+
+    conn->privateData = uml_driver;
+
+    return VIR_DRV_OPEN_SUCCESS;
+
+ decline:
+    return VIR_DRV_OPEN_DECLINED;
+}
+
+static int umlClose(virConnectPtr conn) {
+    /*struct uml_driver *driver = (struct uml_driver *)conn->privateData;*/
+
+    conn->privateData = NULL;
+
+    return 0;
+}
+
+static const char *umlGetType(virConnectPtr conn ATTRIBUTE_UNUSED) {
+    return "UML";
+}
+
+static int umlGetNodeInfo(virConnectPtr conn,
+                          virNodeInfoPtr nodeinfo) {
+    return virNodeInfoPopulate(conn, nodeinfo);
+}
+
+
+static char *umlGetCapabilities(virConnectPtr conn) {
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+    char *xml;
+
+    if ((xml = virCapabilitiesFormatXML(driver->caps)) == NULL) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
+                 "%s", _("failed to allocate space for capabilities support"));
+        return NULL;
+    }
+
+    return xml;
+}
+
+
+#if HAVE_NUMACTL
+static int
+umlNodeGetCellsFreeMemory(virConnectPtr conn,
+                            unsigned long long *freeMems,
+                            int startCell,
+                            int maxCells)
+{
+    int n, lastCell, numCells;
+
+    if (numa_available() < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT,
+                         "%s", _("NUMA not supported on this host"));
+        return -1;
+    }
+    lastCell = startCell + maxCells - 1;
+    if (lastCell > numa_max_node())
+        lastCell = numa_max_node();
+
+    for (numCells = 0, n = startCell ; n <= lastCell ; n++) {
+        long long mem;
+        if (numa_node_size64(n, &mem) < 0) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("Failed to query NUMA free memory"));
+            return -1;
+        }
+        freeMems[numCells++] = mem;
+    }
+    return numCells;
+}
+
+static unsigned long long
+umlNodeGetFreeMemory (virConnectPtr conn)
+{
+    unsigned long long freeMem = 0;
+    int n;
+    if (numa_available() < 0) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT,
+                         "%s", _("NUMA not supported on this host"));
+        return -1;
+    }
+
+    for (n = 0 ; n <= numa_max_node() ; n++) {
+        long long mem;
+        if (numa_node_size64(n, &mem) < 0) {
+            umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("Failed to query NUMA free memory"));
+            return -1;
+        }
+        freeMem += mem;
+    }
+
+    return freeMem;
+}
+
+#endif
+
+static int umlGetProcessInfo(unsigned long long *cpuTime, int pid) {
+    char proc[PATH_MAX];
+    FILE *pidinfo;
+    unsigned long long usertime, systime;
+
+    if (snprintf(proc, sizeof(proc), "/proc/%d/stat", pid) >= (int)sizeof(proc)) {
+        return -1;
+    }
+
+    if (!(pidinfo = fopen(proc, "r"))) {
+        /*printf("cannot 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 %llu %llu", &usertime, &systime) != 2) {
+        umlDebug("not enough arg");
+        return -1;
+    }
+
+    /* We got jiffies
+     * We want nanoseconds
+     * _SC_CLK_TCK is jiffies per second
+     * So calulate thus....
+     */
+    *cpuTime = 1000ull * 1000ull * 1000ull * (usertime + systime) / (unsigned long long)sysconf(_SC_CLK_TCK);
+
+    umlDebug("Got %llu %llu %llu", usertime, systime, *cpuTime);
+
+    fclose(pidinfo);
+
+    return 0;
+}
+
+
+static virDomainPtr umlDomainLookupByID(virConnectPtr conn,
+                                          int id) {
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+    virDomainObjPtr vm = virDomainFindByID(&driver->domains, id);
+    virDomainPtr dom;
+
+    if (!vm) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_DOMAIN, NULL);
+        return NULL;
+    }
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) dom->id = vm->def->id;
+    return dom;
+}
+static virDomainPtr umlDomainLookupByUUID(virConnectPtr conn,
+                                            const unsigned char *uuid) {
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, uuid);
+    virDomainPtr dom;
+
+    if (!vm) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_DOMAIN, NULL);
+        return NULL;
+    }
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) dom->id = vm->def->id;
+    return dom;
+}
+static virDomainPtr umlDomainLookupByName(virConnectPtr conn,
+                                            const char *name) {
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+    virDomainObjPtr vm = virDomainFindByName(&driver->domains, name);
+    virDomainPtr dom;
+
+    if (!vm) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_NO_DOMAIN, NULL);
+        return NULL;
+    }
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) dom->id = vm->def->id;
+    return dom;
+}
+
+static int umlGetVersion(virConnectPtr conn, unsigned long *version) {
+    struct utsname ut;
+    int major, minor, micro;
+
+    uname(&ut);
+
+    if (sscanf(ut.release, "%u.%u.%u",
+               &major, &minor, &micro) != 3) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                       _("cannot parse version %s"), ut.release);
+        return -1;
+    }
+
+    *version = uml_driver->umlVersion;
+    return 0;
+}
+
+static char *
+umlGetHostname (virConnectPtr conn)
+{
+    int r;
+    char hostname[HOST_NAME_MAX+1], *str;
+
+    r = gethostname (hostname, HOST_NAME_MAX+1);
+    if (r == -1) {
+        umlReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR,
+                          "%s", strerror (errno));
+        return NULL;
+    }
+    /* Caller frees this string. */
+    str = strdup (hostname);
+    if (str == NULL) {
+        umlReportError (conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR,
+                         "%s", strerror (errno));
+        return NULL;
+    }
+    return str;
+}
+
+static int umlListDomains(virConnectPtr conn, int *ids, int nids) {
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+    int got = 0, i;
+
+    for (i = 0 ; i < driver->domains.count && got < nids ; i++)
+        if (virDomainIsActive(driver->domains.objs[i]))
+            ids[got++] = driver->domains.objs[i]->def->id;
+
+    return got;
+}
+static int umlNumDomains(virConnectPtr conn) {
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+    int n = 0, i;
+
+    for (i = 0 ; i < driver->domains.count ; i++)
+        if (virDomainIsActive(driver->domains.objs[i]))
+            n++;
+
+    return n;
+}
+static virDomainPtr umlDomainCreate(virConnectPtr conn, const char *xml,
+                                      unsigned int flags ATTRIBUTE_UNUSED) {
+    virDomainDefPtr def;
+    virDomainObjPtr vm;
+    virDomainPtr dom;
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+
+    if (!(def = virDomainDefParseString(conn, driver->caps, xml)))
+        return NULL;
+
+    vm = virDomainFindByName(&driver->domains, def->name);
+    if (vm) {
+        umlReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                         _("domain '%s' is already defined"),
+                         def->name);
+        virDomainDefFree(def);
+        return NULL;
+    }
+    vm = virDomainFindByUUID(&driver->domains, def->uuid);
+    if (vm) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+        virUUIDFormat(def->uuid, uuidstr);
+        umlReportError(conn, NULL, NULL, VIR_ERR_OPERATION_FAILED,
+                         _("domain with uuid '%s' is already defined"),
+                         uuidstr);
+        virDomainDefFree(def);
+        return NULL;
+    }
+
+    if (!(vm = virDomainAssignDef(conn,
+                                  &driver->domains,
+                                  def))) {
+        virDomainDefFree(def);
+        return NULL;
+    }
+
+    if (umlStartVMDaemon(conn, driver, vm) < 0) {
+        virDomainRemoveInactive(&driver->domains,
+                                vm);
+        return NULL;
+    }
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) dom->id = vm->def->id;
+    return dom;
+}
+
+
+static int umlDomainShutdown(virDomainPtr dom) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByID(&driver->domains, dom->id);
+    char* info;
+
+    if (!vm) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("no domain with matching id %d"), dom->id);
+        return -1;
+    }
+
+#if 0
+    if (umlMonitorCommand(driver, vm, "system_powerdown", &info) < 0) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED,
+                         "%s", _("shutdown operation failed"));
+        return -1;
+    }
+#endif
+    VIR_FREE(info);
+    return 0;
+
+}
+
+
+static int umlDomainDestroy(virDomainPtr dom) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByID(&driver->domains, dom->id);
+
+    if (!vm) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("no domain with matching id %d"), dom->id);
+        return -1;
+    }
+
+    umlShutdownVMDaemon(dom->conn, driver, vm);
+    if (!vm->persistent)
+        virDomainRemoveInactive(&driver->domains,
+                                vm);
+
+    return 0;
+}
+
+
+static char *umlDomainGetOSType(virDomainPtr dom) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    char *type;
+
+    if (!vm) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "%s", _("no domain with matching uuid"));
+        return NULL;
+    }
+
+    if (!(type = strdup(vm->def->os.type))) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_NO_MEMORY,
+                         "%s", _("failed to allocate space for ostype"));
+        return NULL;
+    }
+    return type;
+}
+
+/* Returns max memory in kb, 0 if error */
+static unsigned long umlDomainGetMaxMemory(virDomainPtr dom) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+    if (!vm) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+        virUUIDFormat(dom->uuid, uuidstr);
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("no domain with matching uuid '%s'"), uuidstr);
+        return 0;
+    }
+
+    return vm->def->maxmem;
+}
+
+static int umlDomainSetMaxMemory(virDomainPtr dom, unsigned long newmax) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+    if (!vm) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+        virUUIDFormat(dom->uuid, uuidstr);
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("no domain with matching uuid '%s'"), uuidstr);
+        return -1;
+    }
+
+    if (newmax < vm->def->memory) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+                         "%s", _("cannot set max memory lower than current memory"));
+        return -1;
+    }
+
+    vm->def->maxmem = newmax;
+    return 0;
+}
+
+static int umlDomainSetMemory(virDomainPtr dom, unsigned long newmem) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+    if (!vm) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+        virUUIDFormat(dom->uuid, uuidstr);
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         _("no domain with matching uuid '%s'"), uuidstr);
+        return -1;
+    }
+
+    if (virDomainIsActive(vm)) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_NO_SUPPORT,
+                         "%s", _("cannot set memory of an active domain"));
+        return -1;
+    }
+
+    if (newmem > vm->def->maxmem) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+                         "%s", _("cannot set memory higher than max memory"));
+        return -1;
+    }
+
+    vm->def->memory = newmem;
+    return 0;
+}
+
+static int umlDomainGetInfo(virDomainPtr dom,
+                              virDomainInfoPtr info) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    if (!vm) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "%s", _("no domain with matching uuid"));
+        return -1;
+    }
+
+    info->state = vm->state;
+
+    if (!virDomainIsActive(vm)) {
+        info->cpuTime = 0;
+    } else {
+        if (umlGetProcessInfo(&(info->cpuTime), vm->pid) < 0) {
+            umlReportError(dom->conn, dom, NULL, VIR_ERR_OPERATION_FAILED, ("cannot read cputime for domain"));
+            return -1;
+        }
+    }
+
+    info->maxMem = vm->def->maxmem;
+    info->memory = vm->def->memory;
+    info->nrVirtCpu = vm->def->vcpus;
+    return 0;
+}
+
+
+static char *umlDomainDumpXML(virDomainPtr dom,
+                                int flags ATTRIBUTE_UNUSED) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    if (!vm) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "%s", _("no domain with matching uuid"));
+        return NULL;
+    }
+
+    return virDomainDefFormat(dom->conn,
+                              (flags & VIR_DOMAIN_XML_INACTIVE) && vm->newDef ?
+                              vm->newDef : vm->def,
+                              flags);
+}
+
+
+static int umlListDefinedDomains(virConnectPtr conn,
+                            char **const names, int nnames) {
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+    int got = 0, i;
+
+    for (i = 0 ; i < driver->domains.count && got < nnames ; i++) {
+        if (!virDomainIsActive(driver->domains.objs[i])) {
+            if (!(names[got++] = strdup(driver->domains.objs[i]->def->name))) {
+                umlReportError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
+                                 "%s", _("failed to allocate space for VM name string"));
+                goto cleanup;
+            }
+        }
+    }
+
+    return got;
+
+ cleanup:
+    for (i = 0 ; i < got ; i++)
+        VIR_FREE(names[i]);
+    return -1;
+}
+
+static int umlNumDefinedDomains(virConnectPtr conn) {
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+    int n = 0, i;
+
+    for (i = 0 ; i < driver->domains.count ; i++)
+        if (!virDomainIsActive(driver->domains.objs[i]))
+            n++;
+
+    return n;
+}
+
+
+static int umlDomainStart(virDomainPtr dom) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+    if (!vm) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "%s", _("no domain with matching uuid"));
+        return -1;
+    }
+
+    return umlStartVMDaemon(dom->conn, driver, vm);
+}
+
+
+static virDomainPtr umlDomainDefine(virConnectPtr conn, const char *xml) {
+    struct uml_driver *driver = (struct uml_driver *)conn->privateData;
+    virDomainDefPtr def;
+    virDomainObjPtr vm;
+    virDomainPtr dom;
+
+    if (!(def = virDomainDefParseString(conn, driver->caps, xml)))
+        return NULL;
+
+    if (!(vm = virDomainAssignDef(conn,
+                                  &driver->domains,
+                                  def))) {
+        virDomainDefFree(def);
+        return NULL;
+    }
+    vm->persistent = 1;
+
+    if (virDomainSaveConfig(conn,
+                            driver->configDir,
+                            vm->newDef ? vm->newDef : vm->def) < 0) {
+        virDomainRemoveInactive(&driver->domains,
+                                vm);
+        return NULL;
+    }
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) dom->id = vm->def->id;
+    return dom;
+}
+
+static int umlDomainUndefine(virDomainPtr dom) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+    if (!vm) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "%s", _("no domain with matching uuid"));
+        return -1;
+    }
+
+    if (virDomainIsActive(vm)) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         "%s", _("cannot delete active domain"));
+        return -1;
+    }
+
+    if (!vm->persistent) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         "%s", _("cannot undefine transient domain"));
+        return -1;
+    }
+
+    if (virDomainDeleteConfig(dom->conn, driver->configDir, driver->autostartDir, vm) < 0)
+        return -1;
+
+    virDomainRemoveInactive(&driver->domains,
+                            vm);
+
+    return 0;
+}
+
+
+
+static int umlDomainGetAutostart(virDomainPtr dom,
+                            int *autostart) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+
+    if (!vm) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "%s", _("no domain with matching uuid"));
+        return -1;
+    }
+
+    *autostart = vm->autostart;
+
+    return 0;
+}
+
+static int umlDomainSetAutostart(virDomainPtr dom,
+                                   int autostart) {
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    char *configFile = NULL, *autostartLink = NULL;
+    int ret = -1;
+
+    if (!vm) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                         "%s", _("no domain with matching uuid"));
+        return -1;
+    }
+
+    if (!vm->persistent) {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                         "%s", _("cannot set autostart for transient domain"));
+        return -1;
+    }
+
+    autostart = (autostart != 0);
+
+    if (vm->autostart == autostart)
+        return 0;
+
+    if ((configFile = virDomainConfigFile(dom->conn, driver->configDir, vm->def->name)) == NULL)
+        goto cleanup;
+    if ((autostartLink = virDomainConfigFile(dom->conn, driver->autostartDir, vm->def->name)) == NULL)
+        goto cleanup;
+
+    if (autostart) {
+        int err;
+
+        if ((err = virFileMakePath(driver->autostartDir))) {
+            umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                             _("cannot create autostart directory %s: %s"),
+                             driver->autostartDir, strerror(err));
+            goto cleanup;
+        }
+
+        if (symlink(configFile, autostartLink) < 0) {
+            umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                             _("Failed to create symlink '%s to '%s': %s"),
+                             autostartLink, configFile, strerror(errno));
+            goto cleanup;
+        }
+    } else {
+        if (unlink(autostartLink) < 0 && errno != ENOENT && errno != ENOTDIR) {
+            umlReportError(dom->conn, dom, NULL, VIR_ERR_INTERNAL_ERROR,
+                             _("Failed to delete symlink '%s': %s"),
+                             autostartLink, strerror(errno));
+            goto cleanup;
+        }
+    }
+
+    vm->autostart = autostart;
+    ret = 0;
+
+cleanup:
+    VIR_FREE(configFile);
+    VIR_FREE(autostartLink);
+
+    return ret;
+}
+
+
+static int
+umlDomainBlockPeek (virDomainPtr dom,
+                      const char *path,
+                      unsigned long long offset, size_t size,
+                      void *buffer,
+                      unsigned int flags ATTRIBUTE_UNUSED)
+{
+    struct uml_driver *driver = (struct uml_driver *)dom->conn->privateData;
+    virDomainObjPtr vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+    int fd, ret = -1, i;
+
+    if (!vm) {
+        umlReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_DOMAIN,
+                          _("no domain with matching uuid"));
+        return -1;
+    }
+
+    if (!path || path[0] == '\0') {
+        umlReportError(dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+                         _("NULL or empty path"));
+        return -1;
+    }
+
+    /* Check the path belongs to this domain. */
+    for (i = 0 ; i < vm->def->ndisks ; i++) {
+        if (vm->def->disks[i]->src != NULL &&
+            STREQ (vm->def->disks[i]->src, path))
+            goto found;
+    }
+    umlReportError (dom->conn, dom, NULL, VIR_ERR_INVALID_ARG,
+                      _("invalid path"));
+    return -1;
+
+found:
+    /* The path is correct, now try to open it and get its size. */
+    fd = open (path, O_RDONLY);
+    if (fd == -1) {
+        umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
+                          "%s", strerror (errno));
+        goto done;
+    }
+
+    /* Seek and read. */
+    /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should
+     * be 64 bits on all platforms.
+     */
+    if (lseek (fd, offset, SEEK_SET) == (off_t) -1 ||
+        saferead (fd, buffer, size) == (ssize_t) -1) {
+        umlReportError (dom->conn, dom, NULL, VIR_ERR_SYSTEM_ERROR,
+                          "%s", strerror (errno));
+        goto done;
+    }
+
+    ret = 0;
+ done:
+    if (fd >= 0) close (fd);
+    return ret;
+}
+
+
+
+static virDriver umlDriver = {
+    VIR_DRV_UML,
+    "UML",
+    LIBVIR_VERSION_NUMBER,
+    umlOpen, /* open */
+    umlClose, /* close */
+    NULL, /* supports_feature */
+    umlGetType, /* type */
+    umlGetVersion, /* version */
+    umlGetHostname, /* hostname */
+    NULL, /* URI  */
+    NULL, /* getMaxVcpus */
+    umlGetNodeInfo, /* nodeGetInfo */
+    umlGetCapabilities, /* getCapabilities */
+    umlListDomains, /* listDomains */
+    umlNumDomains, /* numOfDomains */
+    umlDomainCreate, /* domainCreateXML */
+    umlDomainLookupByID, /* domainLookupByID */
+    umlDomainLookupByUUID, /* domainLookupByUUID */
+    umlDomainLookupByName, /* domainLookupByName */
+    NULL, /* domainSuspend */
+    NULL, /* domainResume */
+    umlDomainShutdown, /* domainShutdown */
+    NULL, /* domainReboot */
+    umlDomainDestroy, /* domainDestroy */
+    umlDomainGetOSType, /* domainGetOSType */
+    umlDomainGetMaxMemory, /* domainGetMaxMemory */
+    umlDomainSetMaxMemory, /* domainSetMaxMemory */
+    umlDomainSetMemory, /* domainSetMemory */
+    umlDomainGetInfo, /* domainGetInfo */
+    NULL, /* domainSave */
+    NULL, /* domainRestore */
+    NULL, /* domainCoreDump */
+    NULL, /* domainSetVcpus */
+    NULL, /* domainPinVcpu */
+    NULL, /* domainGetVcpus */
+    NULL, /* domainGetMaxVcpus */
+    umlDomainDumpXML, /* domainDumpXML */
+    umlListDefinedDomains, /* listDomains */
+    umlNumDefinedDomains, /* numOfDomains */
+    umlDomainStart, /* domainCreate */
+    umlDomainDefine, /* domainDefineXML */
+    umlDomainUndefine, /* domainUndefine */
+    NULL, /* domainAttachDevice */
+    NULL, /* domainDetachDevice */
+    umlDomainGetAutostart, /* domainGetAutostart */
+    umlDomainSetAutostart, /* domainSetAutostart */
+    NULL, /* domainGetSchedulerType */
+    NULL, /* domainGetSchedulerParameters */
+    NULL, /* domainSetSchedulerParameters */
+    NULL, /* domainMigratePrepare */
+    NULL, /* domainMigratePerform */
+    NULL, /* domainMigrateFinish */
+    NULL, /* domainBlockStats */
+    NULL, /* domainInterfaceStats */
+    umlDomainBlockPeek, /* domainBlockPeek */
+    NULL, /* domainMemoryPeek */
+#if HAVE_NUMACTL
+    umlNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */
+    umlNodeGetFreeMemory,  /* getFreeMemory */
+#else
+    NULL, /* nodeGetCellsFreeMemory */
+    NULL, /* getFreeMemory */
+#endif
+    NULL, /* domainEventRegister */
+    NULL, /* domainEventUnregister */
+    NULL, /* domainMigratePrepare2 */
+    NULL, /* domainMigrateFinish2 */
+};
+
+
+static virStateDriver umlStateDriver = {
+    .initialize = umlStartup,
+    .cleanup = umlShutdown,
+    .reload = umlReload,
+    .active = umlActive,
+};
+
+int umlRegister(void) {
+    virRegisterDriver(&umlDriver);
+    virRegisterStateDriver(&umlStateDriver);
+    return 0;
+}
+
diff --git a/src/uml_driver.h b/src/uml_driver.h
new file mode 100644 (file)
index 0000000..2195410
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * uml_driver.h: user mode Linux driver
+ *
+ * Copyright (C) 2006, 2007 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+
+#ifndef UML_DRIVER_H
+#define UML_DRIVER_H
+
+#include "internal.h"
+
+int umlRegister(void);
+
+#endif /* UML_DRIVER_H */
index 15eb0a19110901098a23ecf3f8daa0863ea42354..46a7d15426c0c081e3c151e3f86ee41d7cdedcef 100644 (file)
@@ -310,7 +310,9 @@ virDefaultErrorFunc(virErrorPtr err)
         case VIR_FROM_DOMAIN:
             dom = "Domain Config ";
             break;
-
+        case VIR_FROM_UML:
+            dom = "UML ";
+            break;
     }
     if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
         domain = err->dom->name;