[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [Libvir] [PATCH] Linux-VServer support



This update has moved the interface name to
/domain/devices/interface/target/@dev.

> This is an XML dump from one of my guests:
> <domain type='vserver' id='40010'>
>   <name>lenny</name>
>   <uuid>19e12957-261a-5a06-d6d0-89917d6d439f</uuid>
>   <memory>2048000</memory>
>   <os>
>     <hostname>lenny.test</hostname>
>     <type arch='i686'>vserver</type>
>   </os>
>   <devices>
>     <interface type='ethernet'>
>       <ip prefix='24' address='192.168.20.4' />
>       <target dev='dummy0'/>
>     </interface>
>     <interface type='ethernet'>
>       <ip prefix='24' type='range'
> address='192.168.32.100' address2='192.168.32.200'/>
>       <target dev='dummy0'/>
>     </interface>
>     <disk type='directory' device='directory'>
>       <source directory='/vservers/lenny' type='auto'
options='defaults'/> <target directory='/'/>
>     </disk>
>     <disk type='directory' device='directory'>
>       <source directory='/srv' type='ext3' options='bind,ro'/>
>       <target directory='/srv'/>
>     </disk>
>     <disk type='block' device='directory'>
>        <source dev='/dev/mapper/test' type='ext3' options='defaults'/>
<target directory='/mnt'/>
>     </disk>
>   </devices>
> </domain>

-- 
Daniel Hokka Zakrisson
diff -Nurp libvirt-0.4.0.orig/configure.in libvirt-0.4.0.vserver/configure.in
--- libvirt-0.4.0.orig/configure.in	2007-12-18 00:07:47.000000000 +0100
+++ libvirt-0.4.0.vserver/configure.in	2007-12-29 17:59:05.000000000 +0100
@@ -127,6 +127,8 @@ AC_ARG_WITH(qemu,
 [  --with-qemu             add QEMU/KVM support (on)],[],[with_qemu=yes])
 AC_ARG_WITH(openvz,
 [  --with-openvz           add OpenVZ support (off)],[],[with_openvz=no])
+AC_ARG_WITH(vserver,
+[  --with-vserver          add Linux-VServer support (on, if available)],[],[with_vserver=yes])
 AC_ARG_WITH(test,
 [  --with-test             add test driver support (on)],[],[with_test=yes])
 AC_ARG_WITH(remote,
@@ -234,6 +236,38 @@ fi
 if test "$with_qemu" = "yes" ; then
     LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_QEMU"
 fi
+if test "$with_vserver" = "yes" ; then
+    AC_CHECK_LIB(vserver, [vc_rlimit_stat],, [with_vserver=no])
+    AC_CHECK_TYPES([xid_t, nid_t, tag_t])
+    AC_CHECK_HEADER(vserver.h,, [with_vserver=no], [AC_INCLUDES_DEFAULT()
+#ifndef HAVE_XID_T
+typedef unsigned int xid_t;
+#endif
+#ifndef HAVE_NID_T
+typedef unsigned int nid_t;
+#endif
+#ifndef HAVE_TAG_T
+typedef unsigned int tag_t;
+#endif])
+
+    AC_MSG_CHECKING([for vserver configuration directory])
+    VSERVER_CONF_DIR=`env PATH="/sbin:/usr/sbin:/usr/local/sbin:$PATH" \
+        vserver-info - SYSINFO | sed -n 's/^.*cfg-Directory:.//p'`
+    AC_MSG_RESULT([$VSERVER_CONF_DIR])
+    AC_DEFINE_UNQUOTED([VSERVER_CONF_DIR], ["$VSERVER_CONF_DIR"],
+        [The default path to the Linux-VServer configuration files])
+
+    AC_MSG_CHECKING([for the vserver program])
+    PROG_VSERVER=`env PATH="/sbin:/usr/sbin:/usr/local/sbin:$PATH" \
+        which vserver 2> /dev/null` || with_vserver=no
+    AC_MSG_RESULT([$PROG_VSERVER])
+    AC_DEFINE_UNQUOTED([PROG_VSERVER], ["$PROG_VSERVER"],
+        [The path to the vserver program])
+
+    if test "$with_vserver" = "yes" ; then
+        LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_VSERVER"
+    fi
+fi
 
 if test "$with_test" = "yes" ; then
     LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_TEST"
@@ -694,6 +728,7 @@ AC_MSG_NOTICE([     Xen: $with_xen])
 AC_MSG_NOTICE([   Proxy: $with_xen_proxy])
 AC_MSG_NOTICE([    QEMU: $with_qemu])
 AC_MSG_NOTICE([  OpenVZ: $with_openvz])
+AC_MSG_NOTICE([ VServer: $with_vserver])
 AC_MSG_NOTICE([    Test: $with_test])
 AC_MSG_NOTICE([  Remote: $with_remote])
 AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
diff -Nurp libvirt-0.4.0.orig/include/libvirt/virterror.h libvirt-0.4.0.vserver/include/libvirt/virterror.h
--- libvirt-0.4.0.orig/include/libvirt/virterror.h	2007-12-05 21:33:00.000000000 +0100
+++ libvirt-0.4.0.vserver/include/libvirt/virterror.h	2007-12-29 17:59:40.000000000 +0100
@@ -54,6 +54,7 @@ typedef enum {
     VIR_FROM_OPENVZ,    /* Error from OpenVZ driver */
     VIR_FROM_XENXM,	/* Error at Xen XM layer */
     VIR_FROM_STATS_LINUX, /* Error in the Linux Stats code */
+    VIR_FROM_VSERVER,	/* Error from Linux-VServer driver */
 } virErrorDomain;
 
 
diff -Nurp libvirt-0.4.0.orig/src/driver.h libvirt-0.4.0.vserver/src/driver.h
--- libvirt-0.4.0.orig/src/driver.h	2007-12-05 21:33:01.000000000 +0100
+++ libvirt-0.4.0.vserver/src/driver.h	2007-12-29 17:59:05.000000000 +0100
@@ -24,6 +24,7 @@ typedef enum {
     VIR_DRV_QEMU = 3,
     VIR_DRV_REMOTE = 4,
     VIR_DRV_OPENVZ = 5,
+    VIR_DRV_VSERVER = 6,
 } virDrvNo;
 
 
diff -Nurp libvirt-0.4.0.orig/src/libvirt.c libvirt-0.4.0.vserver/src/libvirt.c
--- libvirt-0.4.0.orig/src/libvirt.c	2007-12-17 22:51:09.000000000 +0100
+++ libvirt-0.4.0.vserver/src/libvirt.c	2007-12-29 17:59:05.000000000 +0100
@@ -40,6 +40,7 @@
 #ifdef WITH_OPENVZ
 #include "openvz_driver.h"
 #endif
+#include "vserver_driver.h"
 
 /*
  * TODO:
@@ -207,6 +208,9 @@ virInitialize(void)
 #ifdef WITH_OPENVZ
     if (openvzRegister() == -1) return -1;
 #endif
+#ifdef WITH_VSERVER
+    if (vserverRegister() == -1) return -1;
+#endif
 #ifdef WITH_REMOTE
     if (remoteRegister () == -1) return -1;
 #endif
diff -Nurp libvirt-0.4.0.orig/src/Makefile.am libvirt-0.4.0.vserver/src/Makefile.am
--- libvirt-0.4.0.orig/src/Makefile.am	2007-12-17 22:51:09.000000000 +0100
+++ libvirt-0.4.0.vserver/src/Makefile.am	2008-01-04 19:38:49.000000000 +0100
@@ -56,6 +56,8 @@ CLIENT_SOURCES =						\
 		qemu_conf.c qemu_conf.h				\
 		openvz_conf.c openvz_conf.h			\
 		openvz_driver.c openvz_driver.h 		\
+		vserver_driver.c vserver_driver.h		\
+		vserver_conf.c vserver_conf.h			\
                 nodeinfo.h nodeinfo.c                           \
 		util.c util.h
 
diff -Nurp libvirt-0.4.0.orig/src/virterror.c libvirt-0.4.0.vserver/src/virterror.c
--- libvirt-0.4.0.orig/src/virterror.c	2007-12-05 23:01:07.000000000 +0100
+++ libvirt-0.4.0.vserver/src/virterror.c	2007-12-30 00:47:20.000000000 +0100
@@ -300,6 +300,9 @@ virDefaultErrorFunc(virErrorPtr err)
         case VIR_FROM_STATS_LINUX:
             dom = "Linux Stats ";
             break;
+        case VIR_FROM_VSERVER:
+            dom = "VServer ";
+            break;
 
     }
     if ((err->dom != NULL) && (err->code != VIR_ERR_INVALID_DOMAIN)) {
diff -Nurp libvirt-0.4.0.orig/src/vserver_conf.c libvirt-0.4.0.vserver/src/vserver_conf.c
--- libvirt-0.4.0.orig/src/vserver_conf.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-0.4.0.vserver/src/vserver_conf.c	2008-01-09 08:52:11.000000000 +0100
@@ -0,0 +1,751 @@
+/*
+ * Configuration handling for Linux-VServer guests
+ *
+ * Copyright (C) 2007-2008 Daniel Hokka Zakrisson
+ *
+ * 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 Hokka Zakrisson <daniel hozac com>
+ */
+
+#ifdef WITH_VSERVER
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#include <libvirt/virterror.h>
+
+#define IN_VSERVER 1
+#include "internal.h"
+#include "vserver_driver.h"
+#include "vserver_conf.h"
+#include "uuid.h"
+
+static int
+readStr(char *dst, size_t len, const char *name, const char *setting)
+{
+    char file[strlen(name) + strlen(setting) + 2];
+    int fd;
+    ssize_t bytes;
+    char *p;
+
+    sprintf(file, "%s/%s", name, setting);
+
+    /* a non-existant file is not a failure */
+    *dst = '\0';
+    if ((fd = open(file, O_RDONLY)) == -1)
+        return 1;
+
+    if ((bytes = read(fd, dst, len - 1)) == -1) {
+        close(fd);
+        return -1;
+    }
+    close(fd);
+
+    dst[bytes] = '\0';
+
+    if ((p = strchr(dst, '\n')) != NULL)
+        *p = '\0';
+
+    return 0;
+}
+
+static int
+readLong(void *v_dst, const char *name, const char *setting)
+{
+    char buf[128], *endptr;
+    int ret;
+    long *dst = v_dst;
+
+    ret = readStr(buf, sizeof(buf), name, setting);
+    if (ret)
+        return ret;
+
+    *dst = strtol(buf, &endptr, 0);
+    if (endptr && *endptr != '\0')
+        return -1;
+
+    return 0;
+}
+
+static int
+readInt(void *v_dst, const char *name, const char *setting)
+{
+    long val;
+    int *dst = v_dst;
+    int ret;
+
+    ret = readLong(&val, name, setting);
+    if (ret)
+        return ret;
+
+    *dst = (int) val;
+
+    return 0;
+}
+
+static int
+readBool(char *dst, const char *name, const char *setting)
+{
+    char file[strlen(name) + strlen(setting) + 2];
+
+    sprintf(file, "%s/%s", name, setting);
+
+    *dst = access(file, F_OK) != -1;
+
+    return 0;
+}
+
+int
+vserverWriteToConfig(struct vserver_guest *guest, const char *filename,
+                     const char *format, ...)
+{
+    char path[strlen(guest->path) + 1 + strlen(filename) + 1];
+    va_list va;
+    char value[1024];
+    int fd;
+    ssize_t len, off, ret;
+    char *dir;
+
+    sprintf(path, "%s/%s", guest->path, filename);
+
+    /* ensure the directory exists */
+    dir = strrchr(path, '/');
+    *dir = '\0';
+    if (mkdir(path, 0755) && errno != EEXIST)
+        return -1;
+    *dir = '/';
+
+    va_start(va, format);
+    if (strcmp(format, "%b") == 0) {
+        /* bool, this is a special case */
+        if (va_arg(va, int)) {
+            *value = '\0';
+            len = 0;
+        }
+        else {
+            if (unlink(path) == -1 && errno != ENOENT)
+                return -1;
+            else
+                return 0;
+        }
+    }
+    else
+        len = vsnprintf(value, sizeof(value), format, va);
+    va_end(va);
+
+    fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, 0644);
+    if (fd == -1)
+        return -1;
+
+    for (off = 0; off < len; off += ret) {
+        ret = write(fd, value + off, len - off);
+        if (ret == -1) {
+            close(fd);
+            return -1;
+        }
+    }
+
+    if (close(fd) == -1)
+        return -1;
+    return 0;
+}
+
+#define WRITE(...)  if (vserverWriteToConfig(guest, __VA_ARGS__) == -1)    \
+                        return -1
+#define WRITE_SCHED_VAL(sched, file, mask, format, value)           \
+    if ((sched)->set_mask & mask) {                                 \
+        WRITE(file, format, value);                                 \
+    }
+#define WRITE_SCHED(dir, sched)                                     \
+    do {                                                            \
+        WRITE_SCHED_VAL(sched, dir "/fill-rate",                    \
+                        VC_VXSM_FILL_RATE, "%u\n",                  \
+                        (sched)->fill_rate);                        \
+        WRITE_SCHED_VAL(sched, dir "/interval",                     \
+                        VC_VXSM_INTERVAL, "%u\n",                   \
+                        (sched)->interval);                         \
+        WRITE_SCHED_VAL(sched, dir "/fill-rate2",                   \
+                        VC_VXSM_FILL_RATE2, "%u\n",                 \
+                        (sched)->fill_rate2);                       \
+        WRITE_SCHED_VAL(sched, dir "/interval2",                    \
+                        VC_VXSM_INTERVAL2, "%u\n",                  \
+                        (sched)->interval2);                        \
+        WRITE_SCHED_VAL(sched, dir "/tokens-min",                   \
+                        VC_VXSM_TOKENS_MIN, "%u\n",                 \
+                        (sched)->tokens_min);                       \
+        WRITE_SCHED_VAL(sched, dir "/tokens-max",                   \
+                        VC_VXSM_TOKENS_MAX, "%u\n",                 \
+                        (sched)->tokens_max);                       \
+        WRITE_SCHED_VAL(sched, dir "/priority-bias",                \
+                        VC_VXSM_PRIO_BIAS, "%u\n",                  \
+                        (sched)->priority_bias);                    \
+        WRITE(dir "/idle-time", "%b",                               \
+              (sched)->set_mask & VC_VXSM_IDLE_TIME);               \
+    } while (0)
+#define WRITE_IP(filename, v4var, v6var)                            \
+            strcpy(file, filename);                                 \
+            switch (type) {                                         \
+            case VC_NXA_TYPE_IPV4:                                  \
+                inet_ntop(AF_INET, (v4var), buf, sizeof(buf));      \
+                break;                                              \
+            case VC_NXA_TYPE_IPV6:                                  \
+                inet_ntop(AF_INET6, (v6var), buf, sizeof(buf));     \
+                break;                                              \
+            }                                                       \
+            WRITE(dir, "%s\n", buf);
+
+static int
+writeInterfaces(struct vserver_guest *guest)
+{
+    char name[strlen(guest->path) + sizeof("/interfaces/XXXXXX/prefix")],
+         *dir, *file;
+    char buf[128];
+    struct vserver_ip *ip;
+    unsigned int i;
+
+    snprintf(name, sizeof(name), "%s/interfaces", guest->path);
+    if (vserverRunCommand(0, "rm", "-fr", name, NULL) != 0)
+        return -1;
+    if (mkdir(name, 0755) == -1)
+        return -1;
+
+    dir = name + strlen(guest->path) + 1;
+    for (ip = guest->ips, i = 0; ip; ip = ip->next, i++) {
+        uint16_t type;
+
+        /* Yeah right... */
+        if (i > 999999) {
+            errno = EOVERFLOW;
+            return -1;
+        }
+
+        file = dir;
+        file += snprintf(dir, sizeof(name) - (dir - name), "interfaces/%u", i);
+        if (mkdir(name, 0755) == -1)
+            return -1;
+
+        type = ip->addr.vna_type & (VC_NXA_TYPE_IPV4|VC_NXA_TYPE_IPV6);
+
+        strcpy(file, "/prefix");
+        WRITE(dir, "%u\n", ip->addr.vna_prefix);
+
+        if (*ip->interface) {
+            strcpy(file, "/dev");
+            WRITE(dir, "%s\n", ip->interface);
+        }
+        else {
+            strcpy(file, "/nodev");
+            WRITE(dir, "%b", 1);
+        }
+
+        WRITE_IP("/ip", &ip->addr.vna_v4_ip, &ip->addr.vna_v6_ip);
+        if (ip->addr.vna_type & VC_NXA_TYPE_RANGE) {
+            WRITE_IP("/ip2", &ip->addr.vna_v4_ip2, &ip->addr.vna_v6_ip2);
+        }
+        if (ip->addr.vna_type & (VC_NXA_TYPE_RANGE|VC_NXA_TYPE_MASK)) {
+            WRITE_IP("/mask", &ip->addr.vna_v4_mask, &ip->addr.vna_v6_mask);
+        }
+    }
+    return 0;
+}
+
+static int
+writeFstab(struct vserver_guest *guest)
+{
+    FILE *fp;
+    struct vserver_fstab *ent;
+    char file[strlen(guest->path) + sizeof("/fstab")];
+
+    sprintf(file, "%s/fstab", guest->path);
+    fp = fopen(file, "w");
+    if (fp == NULL)
+        return -1;
+
+    for (ent = guest->fstab; ent; ent = ent->next) {
+        if (!(ent->flags & VSERVER_FSTAB_OUTPUT))
+            continue;
+        fprintf(fp, "%s\t%s\t%s\t%s\t%s\n", ent->source, ent->target,
+                ent->fstype, ent->options, (ent->rest ? ent->rest : ""));
+    }
+
+    if (fclose(fp) == -1)
+        return -1;
+
+    return 0;
+}
+
+int
+vserverWriteGuestToConfig(struct vserver_guest *guest)
+{
+    char buf[128];
+
+    WRITE("context", "%u\n", guest->xid);
+    WRITE("name", "%s\n", guest->name);
+    virUUIDFormat(guest->uuid, buf);
+    WRITE("uuid", "%s\n", buf);
+    if (*guest->uts.hostname)
+        WRITE("uts/nodename", "%s\n", guest->uts.hostname);
+    if (*guest->uts.machine)
+        WRITE("uts/machine", "%s\n", guest->uts.machine);
+    if (*guest->uts.release)
+        WRITE("uts/release", "%s\n", guest->uts.release);
+    if (*guest->uts.version)
+        WRITE("uts/version", "%s\n", guest->uts.version);
+    if (guest->rss_hard > 0)
+        WRITE("rlimits/rss.hard", "%lu\n", guest->rss_hard);
+    WRITE("apps/init/mark", (guest->autostart ? "default\n" : "\n"));
+    WRITE_SCHED("sched", &guest->sched);
+
+    if (writeInterfaces(guest) == -1)
+        return -1;
+
+    if (writeFstab(guest) == -1)
+        return -1;
+
+    return 0;
+}
+#undef WRITE_IP
+#undef WRITE_SCHED
+#undef WRITE_SCHED_VAL
+#undef WRITE
+
+static int
+parseInterfaces(struct vserver_guest *guest, const char *directory)
+{
+    DIR *interfaces;
+    struct dirent *interface;
+    struct vserver_ip *ip = NULL;
+    char ipath[256], *subdir;
+    size_t spare;
+    char s_ip[48], s_prefix[4], s_mask[48], s_dev[32], s_ip2[48];
+
+    subdir = ipath;
+    subdir += snprintf(ipath, sizeof(ipath), "%s/%s", guest->name, directory);
+    spare = sizeof(ipath) - (subdir - ipath);
+
+    interfaces = opendir(ipath);
+    if (!interfaces)
+        return 0;
+
+    while ((interface = readdir(interfaces)) != NULL) {
+        if (*interface->d_name == '.')
+            continue;
+        /* would overflow */
+        if (strlen(interface->d_name) + sizeof("//disabled") > spare)
+            continue;
+
+        snprintf(subdir, spare, "/%s/disabled", interface->d_name);
+        if (access(ipath, F_OK) != -1)
+            continue;
+
+        snprintf(subdir, spare, "/%s", interface->d_name);
+        *s_mask = '\0';
+        *s_prefix = '\0';
+        if (readStr(s_ip, sizeof(s_ip), ipath, "ip") == 0 &&
+            (readStr(s_prefix, sizeof(s_prefix), ipath, "prefix") == 0 ||
+             readStr(s_mask, sizeof(s_mask), ipath, "mask") == 0)) {
+            if (readStr(s_dev, sizeof(s_dev), ipath, "dev") != 0)
+                *s_dev = '\0';
+            if (readStr(s_ip2, sizeof(s_ip2), ipath, "ip2") != 0)
+                *s_ip2 = '\0';
+        }
+        else
+            continue;
+
+        if (!ip)
+            guest->ips = ip = calloc(1, sizeof(*ip));
+        else {
+            ip->next = calloc(1, sizeof(*ip));
+            ip = ip->next;
+        }
+        if (!ip)
+            goto cleanup;
+
+        strcpy(ip->interface, s_dev);
+
+        if (inet_pton(AF_INET6, s_ip, &ip->addr.vna_v6_ip) > 0)
+            ip->addr.vna_type = VC_NXA_TYPE_IPV6;
+        else if (inet_pton(AF_INET, s_ip, &ip->addr.vna_v4_ip) > 0)
+            ip->addr.vna_type = VC_NXA_TYPE_IPV4;
+        else
+            goto cleanup;
+
+        if (ip->addr.vna_type == VC_NXA_TYPE_IPV6) {
+            if (*s_mask) {
+                if (inet_pton(AF_INET6, s_mask, &ip->addr.vna_v6_mask) <= 0)
+                    goto cleanup;
+            }
+            if (*s_ip2) {
+                if (inet_pton(AF_INET6, s_ip2, &ip->addr.vna_v6_ip2) <= 0)
+                    goto cleanup;
+                ip->addr.vna_type |= VC_NXA_TYPE_RANGE;
+            }
+            else
+                ip->addr.vna_type |= VC_NXA_TYPE_ADDR;
+        }
+        else if (ip->addr.vna_type == VC_NXA_TYPE_IPV4) {
+            if (*s_mask) {
+                if (inet_pton(AF_INET, s_mask, &ip->addr.vna_v4_mask) <= 0)
+                    goto cleanup;
+            }
+            if (*s_ip2) {
+                if (inet_pton(AF_INET, s_ip2, &ip->addr.vna_v4_ip2) <= 0)
+                    goto cleanup;
+                ip->addr.vna_type |= VC_NXA_TYPE_RANGE;
+            }
+            else
+                ip->addr.vna_type |= VC_NXA_TYPE_ADDR;
+        }
+
+        if (*s_prefix) {
+            char *endptr;
+            ip->addr.vna_prefix = strtoul(s_prefix, &endptr, 0);
+            if (*endptr != '\n' && *endptr)
+                goto cleanup;
+        }
+    }
+
+    closedir(interfaces);
+    return 0;
+
+cleanup:
+    closedir(interfaces);
+    return -1;
+}
+
+static inline char *
+endOfField(char *start)
+{
+    char *end;
+    for (end = start; *(end - 1) != '\\' &&
+                      !isspace(*end) &&
+                      *end != '\0'; end++)
+        ;
+    return end;
+}
+
+static inline char *
+startOfField(char *end)
+{
+    char *start;
+    for (start = end + 1; isspace(*start) &&
+                          *start != '\0'; start++)
+        ;
+    return start;
+}
+
+static const struct {
+    int mode;
+    const char *type;
+} source_types[] = {
+    { S_IFBLK, "block" },
+    { S_IFREG, "file" },
+    { S_IFDIR, "directory" },
+};
+
+const char *
+vserverFindSourceType(const char *source)
+{
+    struct stat st;
+    int mode, i;
+    if (stat(source, &st) == -1)
+        mode = -1;
+    else
+        mode = st.st_mode & S_IFMT;
+    for (i = 0; i < (sizeof(source_types) / sizeof(*source_types)); i++) {
+        if (mode == source_types[i].mode)
+            return source_types[i].type;
+    }
+    return NULL;
+}
+
+static int
+parseFstab(struct vserver_guest *guest, const char *filename)
+{
+    FILE *fp;
+    char buf[256], *start, *end;
+    struct vserver_fstab *ent = NULL;
+    char path[strlen(guest->name) + 2 + strlen(filename)];
+
+    sprintf(path, "%s/%s", guest->name, filename);
+    if ((fp = fopen(path, "r")) == NULL)
+        return 0;
+
+    while (fgets(buf, sizeof(buf), fp) != NULL) {
+        if (!ent)
+            guest->fstab = ent = calloc(1, sizeof(*ent));
+        else {
+            ent->next = calloc(1, sizeof(*ent));
+            ent = ent->next;
+        }
+        if (!ent)
+            goto err;
+
+        start = buf;
+        end = endOfField(start);
+        *end = '\0';
+        ent->source = strdup(start);
+        ent->source_type = vserverFindSourceType(start);
+
+        ent->flags |= VSERVER_FSTAB_OUTPUT;
+        if (ent->source_type)
+            ent->flags |= VSERVER_FSTAB_SHOW;
+
+        start = startOfField(end);
+        end = endOfField(start);
+        *end = '\0';
+        ent->target = strdup(start);
+
+        start = startOfField(end);
+        end = endOfField(start);
+        *end = '\0';
+        ent->fstype = strdup(start);
+
+        start = startOfField(end);
+        end = endOfField(start);
+        if (*end != '\0') {
+            char *ptr;
+            ent->rest = strdup(end + 1);
+            ptr = strchr(ent->rest, '\n');
+            if (ptr)
+                *ptr = '\0';
+        }
+        else
+            ent->rest = NULL;
+        *end = '\0';
+        ent->options = strdup(start);
+
+        if (!ent->source || !ent->target || !ent->fstype || !ent->options)
+            goto err;
+    }
+
+    fclose(fp);
+    return 0;
+
+err:
+    fclose(fp);
+    return -1;
+}
+
+virDrvOpenStatus
+vserverInitializeDriver(virConnectPtr conn, struct vserver_driver *driver,
+                        const char *path)
+{
+    struct vserver_guest *guest = NULL;
+    DIR *dp = NULL;
+    struct dirent *de;
+    int cwd;
+    const char *conf_dir = (*path ? path : VSERVER_CONF_DIR);
+
+    if (driver->initialized == 1)
+        return VIR_DRV_OPEN_SUCCESS;
+
+    driver->guests = NULL;
+    driver->inactive_guests = driver->active_guests = 0;
+
+    cwd = open(".", O_RDONLY);
+    if (cwd == -1 ||
+        chdir(conf_dir) == -1 ||
+        (dp = opendir(".")) == NULL) {
+        vserverError(conn, NULL, NULL, VIR_ERR_OPEN_FAILED, conf_dir);
+        goto error;
+    }
+
+    while ((de = readdir(dp)) != NULL) {
+        char uuidstr[VIR_UUID_STRING_BUFLEN + 1];
+        char mark[32];
+        char tmp;
+
+        if (*de->d_name == '.')
+            continue;
+
+        if (driver->guests == NULL)
+            driver->guests = guest = calloc(1, sizeof(struct vserver_guest));
+        else {
+            guest->next = calloc(1, sizeof(struct vserver_guest));
+            guest = guest->next;
+        }
+
+        if (!guest) {
+            vserverError(conn, NULL, NULL, VIR_ERR_NO_MEMORY,
+                         "vserver_guest");
+            goto error;
+        }
+
+        guest->path = calloc(strlen(conf_dir) + 1 + strlen(de->d_name) + 1, sizeof(char));
+        if (!guest->path)
+            goto nomem;
+        sprintf(guest->path, "%s/%s", conf_dir, de->d_name);
+
+#define CHECK(x)                                                            \
+    if ((x) == -1) {                                                        \
+        goto parseerror;                                                    \
+    }
+#define DO_SCHED(type, var, file, bit)                                      \
+    do {                                                                    \
+        int ret;                                                            \
+        CHECK(ret = read ## type((var), de->d_name, (file)));               \
+        if (ret == 0)                                                       \
+            guest->sched.set_mask |= (bit);                                 \
+    } while (0)
+
+        /* Parse simple values */
+        CHECK(readInt(&guest->xid, de->d_name, "context"));
+        CHECK(readStr(guest->name, sizeof(guest->name), de->d_name, "name"));
+        CHECK(readStr(guest->uts.hostname, sizeof(guest->uts.hostname), de->d_name,
+                      "uts/nodename"));
+        CHECK(readStr(guest->uts.machine, sizeof(guest->uts.machine), de->d_name,
+                      "uts/machine"));
+        CHECK(readStr(guest->uts.release, sizeof(guest->uts.release), de->d_name,
+                      "uts/release"));
+        CHECK(readStr(guest->uts.version, sizeof(guest->uts.version), de->d_name,
+                      "uts/version"));
+        CHECK(readStr(mark, sizeof(mark), de->d_name, "apps/init/mark"));
+
+        DO_SCHED(Int, &guest->sched.fill_rate,     "sched/fill-rate",
+                 VC_VXSM_FILL_RATE);
+        DO_SCHED(Int, &guest->sched.interval,      "sched/interval",
+                 VC_VXSM_INTERVAL);
+        DO_SCHED(Int, &guest->sched.fill_rate2,    "sched/fill-rate2",
+                 VC_VXSM_FILL_RATE2);
+        DO_SCHED(Int, &guest->sched.interval2,     "sched/interval2",
+                 VC_VXSM_INTERVAL2);
+        DO_SCHED(Int, &guest->sched.tokens_min,    "sched/tokens-min",
+                 VC_VXSM_TOKENS_MIN);
+        DO_SCHED(Int, &guest->sched.tokens_max,    "sched/tokens-max",
+                 VC_VXSM_TOKENS_MAX);
+        DO_SCHED(Int, &guest->sched.priority_bias, "sched/priority-bias",
+                 VC_VXSM_PRIO_BIAS);
+        if (readBool(&tmp, de->d_name, "sched/idle-time") == 0) {
+            if (tmp)
+                guest->sched.set_mask |= VC_VXSM_IDLE_TIME;
+        }
+
+        CHECK(readLong(&guest->rss_hard, de->d_name, "rlimits/rss.hard"));
+
+        /* Generate a UUID if one doesn't already exist */
+        switch (readStr(uuidstr, sizeof(uuidstr), de->d_name, "uuid")) {
+        case 1:
+            CHECK(virUUIDGenerate(guest->uuid));
+            virUUIDFormat(guest->uuid, uuidstr);
+            CHECK(vserverWriteToConfig(guest, "uuid", "%s\n", uuidstr));
+            break;
+        case 0:
+            CHECK(virUUIDParse(uuidstr, guest->uuid));
+            break;
+        case -1:
+            goto parseerror;
+        }
+
+        /* Parse interfaces */
+        if (parseInterfaces(guest, "interfaces") == -1)
+            goto parseerror;
+
+        /* Parse fstab */
+        if (parseFstab(guest, "fstab") == -1)
+            goto parseerror;
+
+        /* Make sure the guest has the / directory in the disk output */
+        do {
+            struct vserver_fstab *ent;
+            int has_root = 0;
+            for (ent = guest->fstab; ent; ent = ent->next) {
+                if (strcmp(ent->target, "/") == 0) {
+                    has_root = 1;
+                    break;
+                }
+            }
+            if (!has_root) {
+                char vdir[strlen(de->d_name) + sizeof("/vdir")];
+
+                ent = calloc(1, sizeof(*ent));
+                if (!ent)
+                    goto nomem;
+
+                sprintf(vdir, "%s/vdir", de->d_name);
+                ent->source = realpath(vdir, NULL);
+
+                ent->flags = VSERVER_FSTAB_SHOW;
+                ent->source_type = vserverFindSourceType("/");
+                ent->target = strdup("/");
+                ent->fstype = strdup("auto");
+                ent->options = strdup("defaults");
+                if (!ent->source || !ent->target || !ent->fstype || !ent->options)
+                    goto nomem;
+                ent->next = guest->fstab;
+                guest->fstab = ent;
+            }
+        } while (0);
+#undef DO_SCHED
+#undef CHECK
+
+        if (STREQ(mark, "default"))
+            guest->autostart = 1;
+        if (vserverContextIsRunning(guest->path)) {
+            struct vc_ctx_flags flags;
+            if (vc_get_cflags(guest->xid, &flags) == 0 &&
+                (flags.flagword & VC_VXF_SCHED_PAUSE))
+                guest->status = VIR_DOMAIN_PAUSED;
+            else
+                guest->status = VIR_DOMAIN_RUNNING;
+            driver->active_guests++;
+        }
+        else {
+            guest->status = VIR_DOMAIN_SHUTOFF;
+            driver->inactive_guests++;
+        }
+    }
+
+    closedir(dp);
+    if (fchdir(cwd) == -1 || close(cwd) == -1) {
+        /* do nothing, we succeeded with everything else... */
+    }
+    driver->initialized = 1;
+
+    return VIR_DRV_OPEN_SUCCESS;
+
+nomem:
+    vserverError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, NULL);
+    goto error;
+parseerror:
+    vserverError(conn, NULL, NULL, VIR_ERR_PARSE_FAILED, de->d_name);
+error:
+    if (dp)
+        closedir(dp);
+
+    if (fchdir(cwd) == -1 || close(cwd) == -1) {
+        /* we're already failing, nothing to do */
+    }
+    return VIR_DRV_OPEN_ERROR;
+}
+
+#endif
diff -Nurp libvirt-0.4.0.orig/src/vserver_conf.h libvirt-0.4.0.vserver/src/vserver_conf.h
--- libvirt-0.4.0.orig/src/vserver_conf.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-0.4.0.vserver/src/vserver_conf.h	2008-01-05 02:10:20.000000000 +0100
@@ -0,0 +1,36 @@
+/*
+ * Configuration handling for Linux-VServer guests
+ *
+ * Copyright (C) 2007-2008 Daniel Hokka Zakrisson
+ *
+ * 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 Hokka Zakrisson <daniel hozac com>
+ */
+
+#ifdef WITH_VSERVER
+
+/* Kind of a hack */
+#ifdef IN_VSERVER
+int vserverWriteToConfig(struct vserver_guest *guest, const char *filename,
+                         const char *format, ...);
+int vserverWriteGuestToConfig(struct vserver_guest *guest);
+virDrvOpenStatus vserverInitializeDriver(virConnectPtr conn,
+                                         struct vserver_driver *driver,
+                                         const char *path);
+const char *vserverFindSourceType(const char *source);
+#endif
+
+#endif
diff -Nurp libvirt-0.4.0.orig/src/vserver_driver.c libvirt-0.4.0.vserver/src/vserver_driver.c
--- libvirt-0.4.0.orig/src/vserver_driver.c	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-0.4.0.vserver/src/vserver_driver.c	2008-01-15 21:49:28.000000000 +0100
@@ -0,0 +1,1624 @@
+/*
+ * Core driver for managing Linux-VServer guests
+ *
+ * Copyright (C) 2007-2008 Daniel Hokka Zakrisson
+ *
+ * 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 Hokka Zakrisson <daniel hozac com>
+ */
+
+#ifdef WITH_VSERVER
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <sys/syscall.h>
+
+#include <libxml/uri.h>
+#include <libxml/xpath.h>
+#include <libxml/tree.h>
+
+#include <libvirt/virterror.h>
+
+#define IN_VSERVER 1
+#include "internal.h"
+#include "vserver_driver.h"
+#include "vserver_conf.h"
+#include "buf.h"
+#include "uuid.h"
+#include "xml.h"
+#include "nodeinfo.h"
+
+#define GET_DOMAIN(dom, ret)                                            \
+    struct vserver_driver *driver = (struct vserver_driver *)           \
+                                    (dom)->conn->privateData;           \
+    struct vserver_guest *guest = getGuestByUUID(driver, (dom)->uuid);  \
+                                                                        \
+    if (!guest) {                                                       \
+        vserverError((dom)->conn, (dom), NULL, VIR_ERR_INVALID_ARG,     \
+                     __FUNCTION__);                                     \
+        return (ret);                                                   \
+    }
+
+#ifndef MNT_DETACH
+#  define MNT_DETACH    0x00000002
+#endif
+
+static int PAGE_SHIFT_TO_KIBI = 2;
+
+static inline int sys_umount(const char *path, int flags)
+{
+    return syscall(__NR_umount2, path, flags);
+}
+
+static inline struct vserver_guest *
+getGuestByID(struct vserver_driver *driver,
+             int id)
+{
+    struct vserver_guest *res;
+    for (res = driver->guests; res; res = res->next) {
+        if (res->xid == id)
+            break;
+    }
+    return res;
+}
+
+static inline struct vserver_guest *
+getGuestByUUID(struct vserver_driver *driver,
+               const unsigned char *uuid)
+{
+    struct vserver_guest *res;
+    for (res = driver->guests; res; res = res->next) {
+        if (memcmp(res->uuid, uuid, VIR_UUID_BUFLEN) == 0)
+            break;
+    }
+    return res;
+}
+
+static inline struct vserver_guest *
+getGuestByName(struct vserver_driver *driver,
+               const char *name)
+{
+    struct vserver_guest *res;
+    for (res = driver->guests; res; res = res->next) {
+        if (STREQ(res->name, name))
+            break;
+    }
+    return res;
+}
+
+void
+vserverError(virConnectPtr con,
+             virDomainPtr dom,
+             virNetworkPtr net,
+             virErrorNumber error,
+             const char *info)
+{
+    const char *errmsg;
+
+    if (error == VIR_ERR_OK)
+        return;
+
+    errmsg = __virErrorMsg(error, info);
+    __virRaiseError(con, dom, net, VIR_FROM_VSERVER, error, VIR_ERR_ERROR,
+                    errmsg, info, NULL, 0, 0, errmsg, info, 0);
+}
+
+/* like execl, but uses /dev/null and handles forking/waiting/etc. */
+int
+vserverRunCommand(int do_daemon, const char *cmd, ...)
+{
+    pid_t pid;
+    va_list va;
+    char **argv;
+    int i, argc;
+    sighandler_t sigchld;
+    int ret;
+
+    va_start(va, cmd);
+    argc = 15;
+    argv = calloc(argc, sizeof(*argv));
+    argv[0] = (char *) cmd;
+    for (i = 1; (argv[i] = va_arg(va, char *)) != NULL; i++) {
+        if (i == argc-1) {
+            argc += 5;
+            argv = realloc(argv, sizeof(*argv) * argc);
+        }
+    }
+
+    va_end(va);
+
+    /* XXX: This is bad, since another child could die
+     *      between here and where we restore it */
+    sigchld = signal(SIGCHLD, SIG_DFL);
+    if ((pid = fork()) == 0) {
+        int fd;
+        if (do_daemon) {
+            pid_t not_a_zombie;
+
+            if (setsid() == -1)
+                goto error;
+
+            not_a_zombie = fork();
+            if (not_a_zombie == -1)
+                goto error;
+            else if (not_a_zombie > 0)
+                _exit(0);
+        }
+        if ((fd = open("/dev/null", O_RDWR)) == -1)
+            goto error;
+        if (dup2(fd, 0) == -1 || dup2(fd, 1) == -1 || dup2(fd, 2) == -1)
+            goto error;
+        if (fd > 2 && close(fd) == -1)
+            goto error;
+        execvp(cmd, argv);
+    error:
+        _exit(1);
+    }
+    else if (pid == -1) {
+        ret =  -1;
+    }
+    else {
+        int status;
+        if (waitpid(pid, &status, 0) == -1)
+            ret = -1;
+        else if (WEXITSTATUS(status) != 0)
+            ret = 1;
+        else
+            ret = 0;
+    }
+
+    signal(SIGCHLD, sigchld);
+    return ret;
+}
+
+int
+vserverContextIsRunning(const char *path)
+{
+    return vc_getVserverCtx(path, vcCFG_AUTO, false, 0, vcCTX_XID) != VC_NOCTX;
+}
+
+static void
+vserverSetPagesize(void)
+{
+    long page_size = sysconf(_SC_PAGESIZE);
+    PAGE_SHIFT_TO_KIBI = page_size / 1024;
+}
+
+static virDrvOpenStatus
+vserverOpen(virConnectPtr conn, xmlURIPtr uri,
+            virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+            int flags ATTRIBUTE_UNUSED)
+{
+    virDrvOpenStatus ret;
+    struct vserver_driver *driver;
+
+    if (!uri)
+        return VIR_DRV_OPEN_DECLINED;
+
+    if (getuid() != 0)
+        return VIR_DRV_OPEN_DECLINED;
+
+    if (!uri->scheme || STRNEQ(uri->scheme, "vserver"))
+        return VIR_DRV_OPEN_DECLINED;
+
+    if (uri->server)
+        return VIR_DRV_OPEN_DECLINED;
+
+    /* nothing else supported right now */
+    if (strncmp(uri->path, "/system", 7) != 0)
+        return VIR_DRV_OPEN_DECLINED;
+
+    /* make sure it's a Linux-VServer kernel */
+    if (vc_get_version() == -1)
+        return VIR_DRV_OPEN_DECLINED;
+
+    if (!conn->privateData)
+        conn->privateData = calloc(1, sizeof(struct vserver_driver));
+
+    ret = vserverInitializeDriver(conn, conn->privateData, uri->path + 7);
+    if (ret == VIR_DRV_OPEN_SUCCESS) {
+        driver = conn->privateData;
+        driver->uri = xmlSaveUri(uri);
+    }
+    else {
+        free(conn->privateData);
+    }
+
+    vserverSetPagesize();
+
+    return ret;
+}
+
+static void
+freeFstab(struct vserver_fstab *ent)
+{
+    free(ent->source);
+    free(ent->target);
+    free(ent->fstype);
+    free(ent->options);
+    free(ent);
+}
+
+static void
+freeGuest(struct vserver_guest *guest)
+{
+    struct vserver_ip *ip, *pip = NULL;
+    struct vserver_fstab *ent, *pent = NULL;
+    for (ip = guest->ips; ip; ip = ip->next) {
+        if (pip)
+            free(pip);
+        pip = ip;
+    }
+    if (pip)
+        free(pip);
+    for (ent = guest->fstab; ent; ent = ent->next) {
+        if (pent)
+            freeFstab(pent);
+        pent = ent;
+    }
+    if (pent)
+        freeFstab(pent);
+    free(guest->path);
+    free(guest);
+}
+
+static int
+vserverClose(virConnectPtr conn)
+{
+    struct vserver_driver *driver = conn->privateData;
+    struct vserver_guest *guest, *pguest;
+
+    if (!driver || !driver->initialized)
+        return 0;
+
+    for (guest = driver->guests, pguest = NULL; guest; guest = guest->next) {
+        if (pguest)
+            freeGuest(pguest);
+        pguest = guest;
+    }
+    if (pguest)
+        freeGuest(pguest);
+
+    xmlFree(driver->uri);
+    free(driver);
+    conn->privateData = NULL;
+
+    return 0;
+}
+
+static const char *
+vserverGetType(virConnectPtr conn ATTRIBUTE_UNUSED)
+{
+    return "Linux-VServer";
+}
+
+static int
+vserverGetVersion(virConnectPtr conn ATTRIBUTE_UNUSED, unsigned long *version)
+{
+    *version = 1;
+    return 0;
+}
+
+static char *
+vserverGetHostname(virConnectPtr conn)
+{
+    char *ret;
+
+    ret = calloc(VSERVER_UTS_MAX + 1, sizeof(char));
+    if (!ret) {
+        vserverError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "gethostname");
+        return NULL;
+    }
+
+    if (gethostname(ret, VSERVER_UTS_MAX) == -1) {
+        free(ret);
+        vserverError(conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR, strerror(errno));
+        return NULL;
+    }
+
+    return ret;
+}
+
+static char *
+vserverGetURI(virConnectPtr conn)
+{
+    struct vserver_driver *driver = conn->privateData;
+    return (char *) xmlStrdup(driver->uri);
+}
+
+static int
+vserverNodeGetInfo(virConnectPtr conn, virNodeInfoPtr nodeinfo)
+{
+    return virNodeInfoPopulate(conn, nodeinfo);
+}
+
+static int
+vserverListDomains(virConnectPtr conn, int *ids, int nids)
+{
+    struct vserver_driver *driver = conn->privateData;
+    struct vserver_guest *guest;
+    int i;
+
+    for (guest = driver->guests, i = 0; guest && i < nids;
+         guest = guest->next) {
+        if (guest->status != VIR_DOMAIN_SHUTOFF)
+            ids[i++] = guest->xid;
+    }
+
+    return i;
+}
+
+static int
+vserverNumOfDomains(virConnectPtr conn)
+{
+    struct vserver_driver *driver = conn->privateData;
+
+    return driver->active_guests;
+}
+
+static virDomainPtr
+vserverDomainLookupByID(virConnectPtr conn, int id)
+{
+    struct vserver_driver *driver = conn->privateData;
+    struct vserver_guest *guest;
+    virDomainPtr domain;
+
+    if ((guest = getGuestByID(driver, id)) == NULL) {
+        vserverError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+                     _("No domain by that ID found"));
+        return NULL;
+    }
+
+    domain = virGetDomain(conn, guest->name, guest->uuid);
+    if (!domain) {
+        vserverError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "virGetDomain");
+        return NULL;
+    }
+
+    if (vserverIsRunning(guest))
+        domain->id = guest->xid;
+
+    return domain;
+}
+
+static virDomainPtr
+vserverDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
+{
+    struct vserver_driver *driver = conn->privateData;
+    struct vserver_guest *guest;
+    virDomainPtr domain;
+
+    if ((guest = getGuestByUUID(driver, uuid)) == NULL) {
+        vserverError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+                     _("No domain by that UUID found"));
+        return NULL;
+    }
+
+    domain = virGetDomain(conn, guest->name, guest->uuid);
+    if (!domain) {
+        vserverError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "virGetDomain");
+        return NULL;
+    }
+
+    if (vserverIsRunning(guest))
+        domain->id = guest->xid;
+
+    return domain;
+}
+
+static virDomainPtr
+vserverDomainLookupByName(virConnectPtr conn, const char *name)
+{
+    struct vserver_driver *driver = conn->privateData;
+    struct vserver_guest *guest;
+    virDomainPtr domain;
+
+    if ((guest = getGuestByName(driver, name)) == NULL) {
+        vserverError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+                     _("No domain by that name found"));
+        return NULL;
+    }
+
+    domain = virGetDomain(conn, guest->name, guest->uuid);
+    if (!domain) {
+        vserverError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "virGetDomain");
+        return NULL;
+    }
+
+    if (vserverIsRunning(guest))
+        domain->id = guest->xid;
+
+    return domain;
+}
+
+static int
+vserverDomainSuspend(virDomainPtr domain)
+{
+    struct vc_ctx_flags flags = {
+        .flagword = VC_VXF_SCHED_PAUSE,
+        .mask = VC_VXF_SCHED_PAUSE,
+    };
+    GET_DOMAIN(domain, -1);
+
+    if (guest->status == VIR_DOMAIN_PAUSED)
+        return 0;
+    if (guest->status != VIR_DOMAIN_RUNNING) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_OPERATION_FAILED,
+                     _("domain is not running"));
+        return -1;
+    }
+
+    if (vc_set_cflags(guest->xid, &flags) == -1) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                     strerror(errno));
+        return -1;
+    }
+    guest->status = VIR_DOMAIN_PAUSED;
+
+    return 0;
+}
+
+static int
+vserverDomainResume(virDomainPtr domain)
+{
+    struct vc_ctx_flags flags = {
+        .flagword = 0,
+        .mask = VC_VXF_SCHED_PAUSE,
+    };
+    GET_DOMAIN(domain, -1);
+
+    if (guest->status == VIR_DOMAIN_RUNNING)
+        return 0;
+    if (guest->status != VIR_DOMAIN_PAUSED) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_OPERATION_FAILED,
+                     _("domain is not running"));
+        return -1;
+    }
+
+    if (vc_set_cflags(guest->xid, &flags) == -1) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                     strerror(errno));
+        return -1;
+    }
+    guest->status = VIR_DOMAIN_RUNNING;
+
+    return 0;
+}
+
+static int
+vserverDomainShutdown(virDomainPtr domain)
+{
+    GET_DOMAIN(domain, -1);
+
+    if (vserverRunCommand(1, PROG_VSERVER, guest->path, "stop", NULL) != 0) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                     "vserver stop");
+        return -1;
+    }
+
+    driver->active_guests--;
+    guest->status = VIR_DOMAIN_SHUTOFF;
+    driver->inactive_guests++;
+    return 0;
+}
+
+static int
+vserverDomainReboot(virDomainPtr domain, unsigned int flags ATTRIBUTE_UNUSED)
+{
+    GET_DOMAIN(domain, -1);
+
+    if (vserverRunCommand(1, PROG_VSERVER, guest->path, "restart", NULL) != 0) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                     "vserver restart");
+        return -1;
+    }
+
+    guest->status = VIR_DOMAIN_RUNNING;
+    return 0;
+}
+
+static int
+vserverDomainDestroy(virDomainPtr domain)
+{
+    GET_DOMAIN(domain, -1);
+
+    if (vserverDomainShutdown(domain) == -1)
+        return -1;
+
+    virFreeDomain(domain->conn, domain);
+
+    return 0;
+}
+
+static char *
+vserverDomainGetOSType(virDomainPtr domain ATTRIBUTE_UNUSED)
+{
+    return strdup("Linux");
+}
+
+static unsigned long
+vserverDomainGetMaxMemory(virDomainPtr domain)
+{
+    GET_DOMAIN(domain, (unsigned long) -1);
+
+    return guest->rss_hard << PAGE_SHIFT_TO_KIBI;
+}
+
+static int
+vserverDomainSetMaxMemory(virDomainPtr domain, unsigned long memory)
+{
+    struct vc_rlimit limit = {
+        .min = 0,
+    };
+    GET_DOMAIN(domain, -1);
+
+    guest->rss_hard = memory >> PAGE_SHIFT_TO_KIBI;
+    limit.soft = limit.hard = guest->rss_hard;
+    if (vserverIsRunning(guest) &&
+        vc_set_rlimit(guest->xid, RLIMIT_RSS, &limit) == -1) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR, strerror(errno));
+        return -1;
+    }
+
+    if (vserverWriteToConfig(guest, "rlimits/rss.hard", "%lu\n", guest->rss_hard) == -1) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                     strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+static int
+vserverDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
+{
+    struct vc_sched_info sinfo = { .user_msec = 0, .sys_msec = 0 };
+    struct vc_rlimit_stat rss_stat = { .value = 0 };
+    GET_DOMAIN(domain, -1);
+
+    if (vserverIsRunning(guest)) {
+        if (vc_sched_info(guest->xid, &sinfo) == -1) {
+            vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                         strerror(errno));
+            return -1;
+        }
+        if (vc_rlimit_stat(guest->xid, RLIMIT_RSS, &rss_stat) == -1) {
+            vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                         strerror(errno));
+            return -1;
+        }
+    }
+
+    info->state = guest->status;
+    info->maxMem = guest->rss_hard << PAGE_SHIFT_TO_KIBI;
+    info->nrVirtCpu = -1;
+    info->memory = rss_stat.value;
+    info->cpuTime = (sinfo.user_msec + sinfo.sys_msec) * 1000000ULL;
+
+    return 0;
+}
+
+static char *
+vserverDomainDumpXML(virDomainPtr domain, int flags ATTRIBUTE_UNUSED)
+{
+    virBufferPtr buf;
+    char uuid[VIR_UUID_STRING_BUFLEN + 1];
+    struct vserver_ip *ip;
+    GET_DOMAIN(domain, NULL);
+
+    buf = virBufferNew(4096);
+    if (!buf)
+        goto no_memory;
+
+    virUUIDFormat(guest->uuid, uuid);
+    if (virBufferVSprintf(buf,
+"<domain type='vserver' id='%d'>\n"
+"  <name>%s</name>\n"
+"  <uuid>%s</uuid>\n"
+                             , guest->xid, guest->name, uuid
+                             ) < 0)
+        goto no_memory;
+    if (guest->rss_hard > 0)
+        if (virBufferVSprintf(buf, "  <memory>%lu</memory>\n",
+                              guest->rss_hard << PAGE_SHIFT_TO_KIBI) < 0)
+            goto no_memory;
+
+    if (virBufferVSprintf(buf, "  <os>\n"
+                               "    <hostname>%s</hostname>\n"
+                               , guest->uts.hostname) < 0)
+        goto no_memory;
+    if (virBufferVSprintf(buf, "    <type") < 0)
+        goto no_memory;
+    if (*guest->uts.machine)
+        if (virBufferVSprintf(buf, " arch='%s'", guest->uts.machine) < 0)
+            goto no_memory;
+    if (virBufferVSprintf(buf, ">vserver</type>\n") < 0)
+        goto no_memory;
+    if (*guest->uts.release)
+        if (virBufferVSprintf(buf, "    <release>%s</release>\n",
+                              guest->uts.release) < 0)
+            goto no_memory;
+    if (*guest->uts.version)
+        if (virBufferVSprintf(buf, "    <version>%s</version>\n",
+                              guest->uts.version) < 0)
+            goto no_memory;
+    if (virBufferVSprintf(buf, "  </os>\n"
+                               "  <devices>\n") < 0)
+        goto no_memory;
+
+    for (ip = guest->ips; ip; ip = ip->next) {
+        char addrbuf[128];
+
+        if (virBufferVSprintf(buf, "    <interface type='ethernet'>\n") < 0)
+            goto no_memory;
+        if (*ip->interface)
+            if (virBufferVSprintf(buf, "      <target dev='%s'/>\n",
+                                  ip->interface) < 0)
+                goto no_memory;
+        if (virBufferVSprintf(buf, "      <ip prefix='%d'", ip->addr.vna_prefix) < 0)
+            goto no_memory;
+
+        switch (ip->addr.vna_type & (VC_NXA_TYPE_IPV4|VC_NXA_TYPE_IPV6)) {
+            case VC_NXA_TYPE_IPV4:
+                inet_ntop(AF_INET, &ip->addr.vna_v4_ip, addrbuf, sizeof(addrbuf));
+                if (virBufferVSprintf(buf, " address='%s'", addrbuf) < 0)
+                    goto no_memory;
+                if (ip->addr.vna_type == (VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_RANGE)) {
+                    inet_ntop(AF_INET, &ip->addr.vna_v4_ip2, addrbuf, sizeof(addrbuf));
+                    if (virBufferVSprintf(buf, " address2='%s' type='range'", addrbuf) < 0)
+                        goto no_memory;
+                }
+                if (ip->addr.vna_type == (VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_MASK)) {
+                    if (virBufferVSprintf(buf, " type='mask'") < 0)
+                        goto no_memory;
+                }
+                if (ip->addr.vna_type == (VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_RANGE) ||
+                    ip->addr.vna_type == (VC_NXA_TYPE_IPV4 | VC_NXA_TYPE_MASK)) {
+                    inet_ntop(AF_INET, &ip->addr.vna_v4_mask, addrbuf, sizeof(addrbuf));
+                    if (virBufferVSprintf(buf, " mask='%s'", addrbuf) < 0)
+                        goto no_memory;
+                }
+                break;
+
+            case VC_NXA_TYPE_IPV6:
+                inet_ntop(AF_INET6, &ip->addr.vna_v6_ip, addrbuf, sizeof(addrbuf));
+                if (virBufferVSprintf(buf, " address='%s'", addrbuf) < 0)
+                    goto no_memory;
+                if (ip->addr.vna_type == (VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_RANGE)) {
+                    inet_ntop(AF_INET6, &ip->addr.vna_v6_ip2, addrbuf, sizeof(addrbuf));
+                    if (virBufferVSprintf(buf, " address2='%s' type='range'", addrbuf) < 0)
+                        goto no_memory;
+                }
+                if (ip->addr.vna_type == (VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_MASK)) {
+                    if (virBufferVSprintf(buf, " type='mask'") < 0)
+                        goto no_memory;
+                }
+                if (ip->addr.vna_type == (VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_RANGE) ||
+                    ip->addr.vna_type == (VC_NXA_TYPE_IPV6 | VC_NXA_TYPE_MASK)) {
+                    inet_ntop(AF_INET6, &ip->addr.vna_v6_mask, addrbuf, sizeof(addrbuf));
+                    if (virBufferVSprintf(buf, " mask='%s'", addrbuf) < 0)
+                        goto no_memory;
+                }
+                break;
+        }
+
+        if (virBufferVSprintf(buf, " />\n"
+                                   "    </interface>\n") < 0)
+            goto no_memory;
+    }
+
+    if (guest->fstab) {
+        struct vserver_fstab *ent;
+        for (ent = guest->fstab; ent; ent = ent->next) {
+            /* Skip things like proc */
+            if (!(ent->flags & VSERVER_FSTAB_SHOW))
+                continue;
+            if (virBufferVSprintf(buf, "    <disk type='%s' device='directory'>\n"
+                                       "      <source %s='%s' type='%s' options='%s'/>\n"
+                                       "      <target directory='%s'/>\n"
+                                       "    </disk>\n",
+                                  ent->source_type,
+                                  (strcmp(ent->source_type, "block") == 0 ?
+                                   "dev" : ent->source_type),
+                                  ent->source,
+                                  ent->fstype,
+                                  ent->options,
+                                  ent->target) < 0)
+                goto no_memory;
+        }
+    }
+
+    if (virBufferVSprintf(buf, "  </devices>\n"
+                               "</domain>\n") < 0)
+        goto no_memory;
+
+    return virBufferContentAndFree(buf);
+
+no_memory:
+    if (buf)
+        virBufferFree(buf);
+    vserverError(domain->conn, domain, NULL, VIR_ERR_NO_MEMORY,
+                 "xml");
+    return NULL;
+}
+
+static int
+vserverParseIP(struct vserver_ip *ip, xmlNodePtr node)
+{
+    xmlNodePtr node_ip = NULL, node_target = NULL, child;
+    xmlChar *value = NULL;
+    char *endptr;
+
+    for (child = node->children; child && (!node_ip || !node_target);
+         child = child->next) {
+        if (xmlStrEqual(child->name, BAD_CAST "ip"))
+            node_ip = child;
+        else if (xmlStrEqual(child->name, BAD_CAST "target"))
+            node_target = child;
+    }
+    if (!node_ip)
+        goto err;
+
+    value = xmlGetProp(node_ip, BAD_CAST "address");
+    if (!value)
+        goto err;
+    if (inet_pton(AF_INET6, (char *) value, &ip->addr.vna_v6_ip) > 0)
+        ip->addr.vna_type = VC_NXA_TYPE_IPV6;
+    else if (inet_pton(AF_INET, (char *) value, &ip->addr.vna_v4_ip) > 0)
+        ip->addr.vna_type = VC_NXA_TYPE_IPV4;
+    else
+        goto err;
+
+    value = xmlGetProp(node_ip, BAD_CAST "prefix");
+    if (!value)
+        goto err;
+    ip->addr.vna_prefix = strtol((char *) value, &endptr, 0);
+    if (*endptr)
+        goto err;
+    xmlFree(value);
+
+    value = xmlGetProp(node_ip, BAD_CAST "type");
+    if (!value)
+        ip->addr.vna_type |= VC_NXA_TYPE_ADDR;
+    else {
+        if (xmlStrcasecmp(value, BAD_CAST "address") == 0)
+            ip->addr.vna_type |= VC_NXA_TYPE_ADDR;
+        else if (xmlStrcasecmp(value, BAD_CAST "mask") == 0)
+            ip->addr.vna_type |= VC_NXA_TYPE_MASK;
+        else if (xmlStrcasecmp(value, BAD_CAST "range") == 0)
+            ip->addr.vna_type |= VC_NXA_TYPE_RANGE;
+        else
+            goto err;
+        xmlFree(value);
+    }
+
+    if (node_target &&
+        (value = xmlGetProp(node_target, BAD_CAST "dev")) != NULL) {
+        strncpy(ip->interface, (char *) value, IFNAMSIZ);
+        xmlFree(value);
+    }
+
+    return 0;
+
+err:
+    if (value)
+        xmlFree(value);
+    return -1;
+}
+
+static const char *
+diskTypeToAttr(const char *type)
+{
+    return (strcmp(type, "block") == 0 ? "dev" : type);
+}
+
+static int
+vserverParseDisk(struct vserver_fstab *ent, xmlNodePtr node)
+{
+    xmlChar *type = NULL, *value = NULL;
+    xmlNodePtr iter, source = NULL, target = NULL;
+
+    for (iter = node->children; iter && (!source || !target); iter = iter->next) {
+        if (iter->type != XML_ELEMENT_NODE)
+            continue;
+        if (xmlStrEqual(iter->name, BAD_CAST "source"))
+            source = iter;
+        else if (xmlStrEqual(iter->name, BAD_CAST "target"))
+            target = iter;
+    }
+    if (!target || !source)
+        goto err;
+
+    value = xmlGetProp(node, BAD_CAST "device");
+    if (!value || !xmlStrEqual(value, BAD_CAST "directory"))
+        goto err;
+    xmlFree(value);
+
+    ent->target = (char *) xmlGetProp(target, BAD_CAST "directory");
+    if (!value)
+        goto err;
+
+    type = xmlGetProp(node, BAD_CAST "type");
+    if (!type)
+        goto err;
+    ent->source = (char *) xmlGetProp(source, BAD_CAST diskTypeToAttr((char *) type));
+    xmlFree(type);
+    if (!ent->source)
+        goto err;
+
+    ent->source_type = vserverFindSourceType(ent->source);
+    if (!ent->source_type)
+        goto err;
+
+    ent->fstype = (char *) xmlGetProp(source, BAD_CAST "type");
+    if (!ent->fstype)
+        ent->fstype = strdup("auto");
+
+    ent->options = (char *) xmlGetProp(source, BAD_CAST "options");
+    if (!ent->options) {
+        if (strcmp(ent->source_type, "file") == 0)
+            ent->options = strdup("defaults,loop");
+        else
+            ent->options = strdup("defaults");
+    }
+
+    ent->flags = VSERVER_FSTAB_SHOW | VSERVER_FSTAB_OUTPUT;
+
+    return 0;
+
+err:
+    if (ent->fstype)
+        xmlFree(ent->fstype);
+    if (ent->source)
+        xmlFree(ent->source);
+    if (ent->target)
+        xmlFree(ent->target);
+    if (value)
+        xmlFree(value);
+    return -1;
+}
+
+static int
+vserverParseXML(struct vserver_guest *guest, xmlDocPtr doc)
+{
+    xmlNodePtr root;
+    xmlXPathContextPtr xpath = NULL;
+    xmlXPathObjectPtr obj;
+    char *str;
+    long l_tmp;
+
+    /* FIXME: This could use some better error reporting... */
+    root = xmlDocGetRootElement(doc);
+    if (!root || !xmlStrEqual(root->name, BAD_CAST "domain"))
+        goto err;
+
+    xpath = xmlXPathNewContext(doc);
+    if (!xpath)
+        goto err;
+
+    if (virXPathLong("string(/domain[1]/@id)", xpath, &l_tmp) != 0)
+        goto err;
+    guest->xid = (int) l_tmp;
+
+    str = virXPathString("string(/domain/name[1])", xpath);
+    if (!str)
+        goto err;
+    strncpy(guest->name, str, VSERVER_NAME_MAX - 1);
+
+    str = virXPathString("string(/domain/uuid[1])", xpath);
+    if (!str) {
+        if (virUUIDGenerate(guest->uuid) != 0)
+            goto err;
+    }
+    else if (virUUIDParse(str, guest->uuid) < 0)
+        goto err;
+
+    guest->rss_hard = 0;
+    if (virXPathLong("string(/domain/memory[1])", xpath, (long *) &guest->rss_hard) == -2)
+        goto err;
+    guest->rss_hard >>= PAGE_SHIFT_TO_KIBI;
+
+    str = virXPathString("string(/domain/os[1]/hostname[1])", xpath);
+    if (str)
+        strncpy(guest->uts.hostname, str, VSERVER_UTS_MAX - 1);
+
+    str = virXPathString("string(/domain/os[1]/type[1]/@arch)", xpath);
+    if (str)
+        strncpy(guest->uts.machine, str, VSERVER_UTS_MAX - 1);
+
+    str = virXPathString("string(/domain/os[1]/release[1])", xpath);
+    if (str)
+        strncpy(guest->uts.machine, str, VSERVER_UTS_MAX - 1);
+
+    str = virXPathString("string(/domain/os[1]/version[1])", xpath);
+    if (str)
+        strncpy(guest->uts.machine, str, VSERVER_UTS_MAX - 1);
+
+    str = virXPathString("string(/domain/container/initstyle[1]/@type)", xpath);
+    guest->initstyle = VSERVER_INIT_SYSV;
+    if (str) {
+        if (strcmp(str, "plain") == 0)
+            guest->initstyle = VSERVER_INIT_PLAIN;
+        else if (strcmp(str, "gentoo") == 0)
+            guest->initstyle = VSERVER_INIT_GENTOO;
+        else if (strcmp(str, "minit") == 0)
+            guest->initstyle = VSERVER_INIT_MINIT;
+        else if (strcmp(str, "sysv") == 0)
+            guest->initstyle = VSERVER_INIT_SYSV;
+    }
+
+    obj = xmlXPathEval(BAD_CAST "/domain/devices[1]/interface", xpath);
+    if (obj != NULL && obj->type == XPATH_NODESET && obj->nodesetval != NULL &&
+        obj->nodesetval->nodeNr > 0) {
+        int i;
+        struct vserver_ip *ip = NULL;
+
+        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+            if (ip == NULL)
+                guest->ips = ip = calloc(1, sizeof(*ip));
+            else {
+                ip->next = calloc(1, sizeof(*ip));
+                ip = ip->next;
+            }
+
+            if (vserverParseIP(ip, obj->nodesetval->nodeTab[i]) == -1)
+                goto nodeset_err;
+        }
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    obj = xmlXPathEval(BAD_CAST "/domain/devices[1]/disk", xpath);
+    if (obj != NULL && obj->type == XPATH_NODESET && obj->nodesetval != NULL &&
+        obj->nodesetval->nodeNr > 0) {
+        int i;
+        struct vserver_fstab *ent = NULL;
+
+        for (i = 0; i < obj->nodesetval->nodeNr; i++) {
+            if (ent == NULL)
+                guest->fstab = ent = calloc(1, sizeof(*ent));
+            else {
+                ent->next = calloc(1, sizeof(*ent));
+                ent = ent->next;
+            }
+
+            if (vserverParseDisk(ent, obj->nodesetval->nodeTab[i]) == -1)
+                goto nodeset_err;
+        }
+    }
+    if (obj)
+        xmlXPathFreeObject(obj);
+
+    xmlXPathFreeContext(xpath);
+    xpath = NULL;
+
+    return 0;
+
+nodeset_err:
+    xmlXPathFreeObject(obj);
+err:
+    if (xpath)
+        xmlXPathFreeContext(xpath);
+    return -1;
+}
+
+static virDomainPtr
+vserverDomainDefineXML(virConnectPtr conn, const char *xml)
+{
+    struct vserver_driver *driver = conn->privateData;
+    struct vserver_guest *guest, *tail;
+    virDomainPtr domain;
+    xmlDocPtr doc;
+    char buf[128];
+
+    if ((guest = calloc(1, sizeof(struct vserver_guest))) == NULL) {
+        vserverError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "vserver_guest");
+        return NULL;
+    }
+
+    if ((doc = xmlReadDoc(BAD_CAST xml, "domain.xml", NULL,
+                          XML_PARSE_NOENT | XML_PARSE_NONET |
+                          XML_PARSE_NOWARNING | XML_PARSE_NOERROR)) == NULL) {
+        vserverError(conn, NULL, NULL, VIR_ERR_XML_ERROR, _("domain"));
+        free(guest);
+        return NULL;
+    }
+
+    if (vserverParseXML(guest, doc) == -1) {
+        vserverError(conn, NULL, NULL, VIR_ERR_XML_ERROR, _("domain"));
+        xmlFreeDoc(doc);
+        free(guest);
+        return NULL;
+    }
+
+    xmlFreeDoc(doc);
+
+    guest->path = calloc(sizeof(VSERVER_CONF_DIR "/") + strlen(guest->name), sizeof(char));
+    sprintf(guest->path, VSERVER_CONF_DIR "/%s", guest->name);
+    guest->status = VIR_DOMAIN_SHUTOFF;
+    if (vserverRunCommand(0, PROG_VSERVER, guest->name, "build", "-m",
+                   "skeleton", "--confdir", guest->path, NULL) != 0) {
+        vserverError(conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR, "vserver build");
+        free(guest);
+        return NULL;
+    }
+    virUUIDFormat(guest->uuid, buf);
+    if (vserverWriteGuestToConfig(guest) == -1) {
+        vserverError(conn, NULL, NULL, VIR_ERR_SYSTEM_ERROR, "vserverWriteToConfig");
+        free(guest);
+        return NULL;
+    }
+
+    /* add it to the list */
+    for (tail = driver->guests; tail && tail->next; tail = tail->next)
+        ;
+    if (!tail)
+        driver->guests = guest;
+    else
+        tail->next = guest;
+
+    driver->inactive_guests++;
+
+    domain = virGetDomain(conn, guest->name, guest->uuid);
+    if (!domain)
+        vserverError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "virGetDomain");
+
+    return domain;
+}
+
+static int
+vserverListDefinedDomains(virConnectPtr conn, char **const names,
+                          int maxnames)
+{
+    struct vserver_driver *driver = conn->privateData;
+    struct vserver_guest *guest;
+    int i = 0;
+
+    for (guest = driver->guests; guest && i < maxnames; guest = guest->next) {
+        if (guest->status == VIR_DOMAIN_SHUTOFF) {
+            if ((names[i++] = strdup(guest->name)) == NULL) {
+                vserverError(conn, NULL, NULL, VIR_ERR_NO_MEMORY, "names");
+                return -1;
+            }
+        }
+    }
+
+    return i;
+}
+
+static int
+vserverNumOfDefinedDomains(virConnectPtr conn)
+{
+    struct vserver_driver *driver = conn->privateData;
+
+    return driver->inactive_guests;
+}
+
+static int
+vserverDomainCreate(virDomainPtr domain)
+{
+    GET_DOMAIN(domain, -1);
+
+    if (vserverRunCommand(1, PROG_VSERVER, guest->path, "start", NULL) != 0) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                     "vserver start");
+        return -1;
+    }
+
+    driver->inactive_guests--;
+    guest->status = VIR_DOMAIN_RUNNING;
+    driver->active_guests++;
+    return 0;
+}
+
+static virDomainPtr
+vserverDomainCreateLinux(virConnectPtr conn, const char *xml,
+                         unsigned int flags ATTRIBUTE_UNUSED)
+{
+    virDomainPtr domain;
+
+    domain = vserverDomainDefineXML(conn, xml);
+    if (!domain)
+        return NULL;
+
+    if (vserverDomainCreate(domain) == -1)
+        return NULL;
+
+    return domain;
+}
+
+static int
+vserverDomainUndefine(virDomainPtr domain)
+{
+    struct vserver_guest *prev;
+    GET_DOMAIN(domain, -1);
+
+    for (prev = driver->guests; prev; prev = prev->next) {
+        if (prev->next == guest)
+            break;
+    }
+    if (!prev) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("domain not found"));
+        return -1;
+    }
+    if (vserverIsRunning(guest)) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_INVALID_ARG,
+                     _("domain is running"));
+        return -1;
+    }
+
+    driver->inactive_guests--;
+    prev->next = guest->next;
+    /* XXX: deletes the domain's contents as well */
+    vserverRunCommand(0, PROG_VSERVER, "--silent", guest->path, "delete", NULL);
+    free(guest);
+
+    return 0;
+}
+
+static int
+doMount(int do_umount, struct vserver_guest *guest, struct vserver_fstab *ent)
+{
+    pid_t child;
+    int status;
+
+    if ((child = fork()) == 0) {
+        if (vc_enter_namespace(guest->xid, CLONE_NEWNS|CLONE_FS) == -1)
+            _exit(errno);
+        if (do_umount) {
+            if (sys_umount(ent->target, MNT_DETACH) == -1)
+                _exit(errno);
+        }
+        else {
+            char target[strlen(guest->path) + sizeof("/vdir/") + strlen(ent->target)];
+            sprintf(target, "%s/vdir/%s", guest->path, ent->target);
+            if (vserverRunCommand(0, "mount", "-t", ent->fstype, "-n", "-o",
+                                  ent->options, ent->source, target, NULL))
+                _exit(errno);
+        }
+        _exit(0);
+    }
+    else if (child == -1)
+        return -1;
+    else {
+        if (waitpid(child, &status, 0) == -1)
+            return -1;
+        if (WEXITSTATUS(status) != 0) {
+            errno = WEXITSTATUS(status);
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int
+vserverDomainHandleDevice(virDomainPtr domain, const char *xml, int attach)
+{
+    xmlDocPtr doc;
+    xmlNodePtr node;
+    GET_DOMAIN(domain, -1);
+
+    if ((doc = xmlReadDoc(BAD_CAST xml, "device.xml", NULL,
+                          XML_PARSE_NOENT | XML_PARSE_NONET |
+                          XML_PARSE_NOWARNING | XML_PARSE_NOERROR)) == NULL) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_XML_ERROR, _("device"));
+        return -1;
+    }
+
+    node = xmlDocGetRootElement(doc);
+    if (node == NULL) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_XML_ERROR,
+                     _("missing root element"));
+        return -1;
+    }
+
+    if (xmlStrEqual(node->name, BAD_CAST "interface")) {
+        struct vserver_ip *ip;
+
+        ip = calloc(1, sizeof(*ip));
+        if (vserverParseIP(ip, node) == -1) {
+            vserverError(domain->conn, domain, NULL, VIR_ERR_XML_ERROR,
+                         _("parsing IP failed"));
+            return -1;
+        }
+
+        if (attach) {
+            list_add_tail(guest->ips, ip);
+            if (vserverIsRunning(guest)) {
+                if (vc_net_add(guest->xid, &ip->addr) == -1) {
+                    vserverError(domain->conn, domain, NULL,
+                                 VIR_ERR_SYSTEM_ERROR, strerror(errno));
+                    return -1;
+                }
+            }
+        }
+
+        else /* detach */ {
+            struct vserver_ip *i, *p = NULL;
+            for (i = guest->ips; i; i = i->next) {
+                if (strcmp(ip->interface, i->interface) == 0 &&
+                    memcmp(&ip->addr, &i->addr, sizeof(ip->addr)) == 0)
+                    break;
+                p = i;
+            }
+            if (i) {
+                if (p)
+                    p->next = i->next;
+                else
+                    guest->ips = i->next;
+                if (vserverIsRunning(guest)) {
+                    /* Not a lot of kernels support this, so don't fail. */
+                    vc_net_remove(guest->xid, &ip->addr);
+                }
+                free(i);
+            }
+            free(ip);
+        }
+    }
+    else if (xmlStrEqual(node->name, BAD_CAST "disk")) {
+        struct vserver_fstab *ent = calloc(1, sizeof(*ent));
+        if (!ent) {
+            vserverError(domain->conn, domain, NULL, VIR_ERR_NO_MEMORY, NULL);
+            return -1;
+        }
+
+        if (vserverParseDisk(ent, node) == -1) {
+            vserverError(domain->conn, domain, NULL, VIR_ERR_XML_ERROR, "disk");
+            return -1;
+        }
+
+        if (attach) {
+            list_add_tail(guest->fstab, ent);
+            if (vserverIsRunning(guest)) {
+                if (doMount(0, guest, ent) == -1) {
+                    vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                                 strerror(errno));
+                    return -1;
+                }
+            }
+        }
+
+        else /* detach */ {
+            struct vserver_fstab *i, *p = NULL;
+            for (i = guest->fstab; i; i = i->next) {
+                /* These are the fields we care about */
+                if (strcmp(ent->source, i->source) == 0 &&
+                    strcmp(ent->target, i->target) == 0 &&
+                    strcmp(ent->source_type, i->source_type) == 0)
+                    break;
+                p = i;
+            }
+            if (i) {
+                if (p)
+                    p->next = i->next;
+                else
+                    guest->fstab = i->next;
+                if (vserverIsRunning(guest)) {
+                    if (doMount(1, guest, ent) == -1) {
+                        vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                                     strerror(errno));
+                        return -1;
+                    }
+                }
+                freeFstab(i);
+            }
+            freeFstab(ent);
+        }
+    }
+    else {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_XML_ERROR,
+                     _("unknown device type"));
+        return -1;
+    }
+
+    /* Both fstab and interfaces need a lot of writing, so write the whole thing */
+    if (vserverWriteGuestToConfig(guest) == -1) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_WRITE_FAILED, guest->name);
+        return -1;
+    }
+
+    xmlFreeDoc(doc);
+
+    return 0;
+}
+
+static int
+vserverDomainAttachDevice(virDomainPtr domain, const char *xml)
+{
+    return vserverDomainHandleDevice(domain, xml, 1);
+}
+
+static int
+vserverDomainDetachDevice(virDomainPtr domain, const char *xml)
+{
+    return vserverDomainHandleDevice(domain, xml, 0);
+}
+
+static int
+vserverDomainGetAutostart(virDomainPtr domain, int *autostart)
+{
+    GET_DOMAIN(domain, -1);
+
+    *autostart = guest->autostart;
+    return 0;
+}
+
+static int
+vserverDomainSetAutostart(virDomainPtr domain, int autostart)
+{
+    GET_DOMAIN(domain, -1);
+
+    if (vserverWriteToConfig(guest, "apps/init/mark",
+                      (autostart ? "default\n" : "\n")) == -1) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_WRITE_FAILED,
+                     "apps/init/mark");
+        return -1;
+    }
+    guest->autostart = autostart;
+    return 0;
+}
+
+static const struct {
+    unsigned int id;
+    const char *field;
+    int type;
+} sched_fields[] = {
+    { VC_VXSM_FILL_RATE,  "fill_rate1",   VIR_DOMAIN_SCHED_FIELD_UINT },
+    { VC_VXSM_INTERVAL,   "interval1",    VIR_DOMAIN_SCHED_FIELD_UINT },
+    { VC_VXSM_FILL_RATE2, "fill_rate2",   VIR_DOMAIN_SCHED_FIELD_UINT },
+    { VC_VXSM_INTERVAL2,  "interval2",    VIR_DOMAIN_SCHED_FIELD_UINT },
+    { VC_VXSM_TOKENS_MIN, "tokens_min",   VIR_DOMAIN_SCHED_FIELD_UINT },
+    { VC_VXSM_TOKENS_MAX, "tokens_max",   VIR_DOMAIN_SCHED_FIELD_UINT },
+    { VC_VXSM_PRIO_BIAS,  "prio_bias",    VIR_DOMAIN_SCHED_FIELD_INT  },
+    { VC_VXSM_IDLE_TIME,  "idle_time",    VIR_DOMAIN_SCHED_FIELD_BOOLEAN },
+    { 0, NULL, 0 }
+};
+
+static char *
+vserverDomainGetSchedulerType(virDomainPtr domain, int *nparams)
+{
+    char *ret;
+    int i;
+    GET_DOMAIN(domain, NULL);
+
+    ret = strdup(guest->sched.set_mask & VC_VXSM_IDLE_TIME ? "fair" : "hard");
+    if (!ret) {
+        vserverError(domain->conn, domain, NULL, VIR_ERR_NO_MEMORY,
+                     _("scheduler"));
+        return NULL;
+    }
+
+    *nparams = 0;
+    for (i = 0; sched_fields[i].field != NULL; i++) {
+        /* only returns fields which are set */
+        if (guest->sched.set_mask & sched_fields[i].id)
+            (*nparams)++;
+    }
+
+    return ret;
+}
+
+static int
+vserverDomainGetSchedulerParams(virDomainPtr domain,
+                                virSchedParameterPtr params,
+                                int *nparams)
+{
+    int i, j;
+    GET_DOMAIN(domain, -1);
+
+    for (i = 0, j = 0; sched_fields[i].field != NULL && i < *nparams; i++) {
+        /* skip unset fields */
+        if (!(guest->sched.set_mask & sched_fields[i].id))
+            continue;
+
+        params[j].type = sched_fields[i].type;
+        strncpy(params[j].field, sched_fields[i].field, sizeof(params[j].field) - 1);
+        switch (sched_fields[i].id) {
+            case VC_VXSM_FILL_RATE:
+                params[j].value.ui = guest->sched.fill_rate;
+                break;
+            case VC_VXSM_INTERVAL:
+                params[j].value.ui = guest->sched.interval;
+                break;
+            case VC_VXSM_FILL_RATE2:
+                params[j].value.ui = guest->sched.fill_rate2;
+                break;
+            case VC_VXSM_INTERVAL2:
+                params[j].value.ui = guest->sched.interval2;
+                break;
+            case VC_VXSM_TOKENS_MIN:
+                params[j].value.ui = guest->sched.tokens_min;
+                break;
+            case VC_VXSM_TOKENS_MAX:
+                params[j].value.ui = guest->sched.tokens_max;
+                break;
+            case VC_VXSM_PRIO_BIAS:
+                params[j].value.i = guest->sched.priority_bias;
+                break;
+            case VC_VXSM_IDLE_TIME:
+                params[j].value.b = guest->sched.set_mask & VC_VXSM_IDLE_TIME;
+                break;
+        }
+        j++;
+    }
+    *nparams = j;
+
+    return 0;
+}
+
+static int
+vserverDomainSetSchedulerParams(virDomainPtr domain,
+                                virSchedParameterPtr params,
+                                int nparams)
+{
+    int i, j, cret;
+    GET_DOMAIN(domain, -1);
+
+    for (i = 0; i < nparams; i++) {
+        for (j = 0; sched_fields[j].field != NULL; j++) {
+            if (STREQ(sched_fields[j].field, params[i].field))
+                break;
+        }
+        if (sched_fields[j].field == NULL) {
+            vserverError(domain->conn, domain, NULL, VIR_ERR_INVALID_ARG, "field");
+            return -1;
+        }
+        if (sched_fields[j].type != params[i].type) {
+            vserverError(domain->conn, domain, NULL, VIR_ERR_INVALID_ARG, "type");
+            return -1;
+        }
+        switch (sched_fields[j].id) {
+            case VC_VXSM_FILL_RATE:
+                guest->sched.fill_rate = params[i].value.ui;
+                cret = vserverWriteToConfig(guest, "sched/fill-rate", "%u\n",
+                                     params[i].value.ui);
+                break;
+            case VC_VXSM_INTERVAL:
+                guest->sched.interval = params[i].value.ui;
+                cret = vserverWriteToConfig(guest, "sched/interval", "%u\n",
+                                     params[i].value.ui);
+                break;
+            case VC_VXSM_FILL_RATE2:
+                guest->sched.fill_rate2 = params[i].value.ui;
+                cret = vserverWriteToConfig(guest, "sched/fill-rate2", "%u\n",
+                                     params[i].value.ui);
+                break;
+            case VC_VXSM_INTERVAL2:
+                guest->sched.interval2 = params[i].value.ui;
+                cret = vserverWriteToConfig(guest, "sched/interval2", "%u\n",
+                                     params[i].value.ui);
+                break;
+            case VC_VXSM_TOKENS_MIN:
+                guest->sched.tokens_min = params[i].value.ui;
+                cret = vserverWriteToConfig(guest, "sched/tokens-min", "%u\n",
+                                     params[i].value.ui);
+                break;
+            case VC_VXSM_TOKENS_MAX:
+                guest->sched.tokens_max = params[i].value.ui;
+                cret = vserverWriteToConfig(guest, "sched/tokens-max", "%u\n",
+                                     params[i].value.ui);
+                break;
+            case VC_VXSM_PRIO_BIAS:
+                guest->sched.priority_bias = params[i].value.i;
+                cret = vserverWriteToConfig(guest, "sched/prio-bias", "%d\n",
+                                     params[i].value.i);
+                break;
+            case VC_VXSM_IDLE_TIME:
+                cret = vserverWriteToConfig(guest, "sched/idle-time", "%b",
+                                     params[i].value.b);
+                break;
+            default:
+                vserverError(domain->conn, domain, NULL, VIR_ERR_INTERNAL_ERROR,
+                             "Unknown scheduler parameter");
+                return -1;
+        }
+        if (cret == -1) {
+            vserverError(domain->conn, domain, NULL, VIR_ERR_SYSTEM_ERROR,
+                         "vserverWriteToConfig");
+            return -1;
+        }
+        guest->sched.set_mask |= sched_fields[j].id;
+    }
+
+    return 0;
+}
+
+
+virDriver vserverDriver = {
+    VIR_DRV_VSERVER,
+    "Linux-VServer",
+    LIBVIR_VERSION_NUMBER,
+    vserverOpen,
+    vserverClose,
+    NULL,   /* supports_feature */
+    vserverGetType,
+    vserverGetVersion,
+    vserverGetHostname,
+    vserverGetURI,
+    NULL,   /* getMaxVcpus */
+    vserverNodeGetInfo,
+    NULL,   /* getCapabilties */
+    vserverListDomains,
+    vserverNumOfDomains,
+    vserverDomainCreateLinux,
+    vserverDomainLookupByID,
+    vserverDomainLookupByUUID,
+    vserverDomainLookupByName,
+    vserverDomainSuspend,
+    vserverDomainResume,
+    vserverDomainShutdown,
+    vserverDomainReboot,
+    vserverDomainDestroy,
+    vserverDomainGetOSType,
+    vserverDomainGetMaxMemory,
+    vserverDomainSetMaxMemory,
+    NULL,   /* domainSetMemory */
+    vserverDomainGetInfo,
+    NULL,   /* domainSave */
+    NULL,   /* domainRestore */
+    NULL,   /* domainCoreDump */
+    NULL,   /* domainSetVcpus */
+    NULL,   /* domainPinVcpu */
+    NULL,   /* domainGetVcpus */
+    NULL,   /* domainGetMaxVcpus */
+    vserverDomainDumpXML,
+    vserverListDefinedDomains,
+    vserverNumOfDefinedDomains,
+    vserverDomainCreate,
+    vserverDomainDefineXML,
+    vserverDomainUndefine,
+    vserverDomainAttachDevice,
+    vserverDomainDetachDevice,
+    vserverDomainGetAutostart,
+    vserverDomainSetAutostart,
+    vserverDomainGetSchedulerType,
+    vserverDomainGetSchedulerParams,
+    vserverDomainSetSchedulerParams,
+    NULL,   /* domainMigratePrepare  */
+    NULL,   /* domainMigratePerform */
+    NULL,   /* domainMigrateFinish */
+    NULL,   /* domainBlockStats */
+    NULL,   /* domainInterfaceStats */
+    NULL,   /* nodeGetCellsFreeMemory */
+    NULL,   /* getFreeMemory */
+};
+
+int vserverRegister(void)
+{
+    if (virRegisterDriver(&vserverDriver) < 0)
+        return -1;
+    return 0;
+}
+
+#endif
diff -Nurp libvirt-0.4.0.orig/src/vserver_driver.h libvirt-0.4.0.vserver/src/vserver_driver.h
--- libvirt-0.4.0.orig/src/vserver_driver.h	1970-01-01 01:00:00.000000000 +0100
+++ libvirt-0.4.0.vserver/src/vserver_driver.h	2008-01-09 19:14:08.000000000 +0100
@@ -0,0 +1,137 @@
+/*
+ * Core driver for managing Linux-VServer guests
+ *
+ * Copyright (C) 2007 Daniel Hokka Zakrisson
+ *
+ * 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 Hokka Zakrisson <daniel hozac com>
+ */
+
+#ifdef WITH_VSERVER
+
+/* Kind of a hack */
+#ifdef IN_VSERVER
+#ifndef HAVE_XID_T
+typedef unsigned int xid_t;
+#endif
+#ifndef HAVE_NID_T
+typedef unsigned int nid_t;
+#endif
+#ifndef HAVE_TAG_T
+typedef unsigned int tag_t;
+#endif
+#include <vserver.h>
+
+#define VSERVER_NAME_MAX       	    64
+#define VSERVER_UTS_MAX             255
+
+enum vserver_initstyle {
+    VSERVER_INIT_NONE,
+    VSERVER_INIT_SYSV,
+    VSERVER_INIT_PLAIN,
+    VSERVER_INIT_GENTOO,
+    VSERVER_INIT_MINIT,
+};
+
+struct vserver_ip {
+    char interface[IFNAMSIZ+1];
+    struct vc_net_addr addr;
+    struct vserver_ip *next;
+};
+
+enum fstab_flags {
+    VSERVER_FSTAB_SHOW=1,
+    VSERVER_FSTAB_OUTPUT=2,
+};
+struct vserver_fstab {
+    char *source;
+    const char *source_type;
+    char *target;
+    char *fstype;
+    char *options;
+    char *rest;
+    int flags;
+    struct vserver_fstab *next;
+};
+
+struct vserver_guest {
+    xid_t xid;
+    char name[VSERVER_NAME_MAX];
+    unsigned char uuid[VIR_UUID_BUFLEN];
+    char *path;
+
+    struct {
+        char hostname[VSERVER_UTS_MAX];
+        char machine[VSERVER_UTS_MAX];
+        char release[VSERVER_UTS_MAX];
+        char version[VSERVER_UTS_MAX];
+    } uts;
+
+    struct vserver_ip *ips;
+
+    struct vc_set_sched sched;
+    unsigned long rss_hard;
+
+    char autostart;
+    enum vserver_initstyle initstyle;
+
+    int status;
+
+    struct vserver_fstab *fstab;
+
+    struct vserver_guest *next;
+};
+
+struct vserver_driver {
+    struct vserver_guest *guests;
+    xmlChar *uri;
+    unsigned int active_guests;
+    unsigned int inactive_guests;
+    unsigned int initialized : 1;
+};
+
+
+#define list_add_tail(list, elem)                               \
+    do {                                                        \
+        __typeof__(list) iter;                                  \
+        if (list) {                                             \
+            for (iter = list; iter->next; iter = iter->next)    \
+                ;                                               \
+            iter->next = elem;                                  \
+        }                                                       \
+        else                                                    \
+            list = elem;                                        \
+    } while (0)
+
+static inline int
+vserverIsRunning(struct vserver_guest *guest) {
+    if (guest->status == VIR_DOMAIN_RUNNING || guest->status == VIR_DOMAIN_PAUSED)
+        return 1;
+    else
+        return 0;
+}
+
+
+int vserverContextIsRunning(const char *path);
+int vserverRunCommand(int do_daemon, const char *cmd, ...);
+void vserverError(virConnectPtr con, virDomainPtr dom, virNetworkPtr net,
+                  virErrorNumber error, const char *info);
+#endif
+
+
+extern int vserverRegister(void);
+
+#endif

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]