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

[Libvir] [PATCH] Linux-VServer support



This patch implements support for Linux-VServer guests. It is currently
missing vcpu and console support, and the necessary virsh code to support
it, but is otherwise pretty feature complete.

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' interface='dummy0' address='192.168.20.4' />
    </interface>
    <interface type='ethernet'>
      <ip prefix='24' interface='dummy0' type='range'
address='192.168.32.100' address2='192.168.32.200'/>
    </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-09 22:43:23.000000000 +0100
@@ -0,0 +1,1625 @@
+/*
+ * 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"
+                                   "      <ip prefix='%d'", ip->addr.vna_prefix) < 0)
+            goto no_memory;
+        if (*ip->interface)
+            if (virBufferVSprintf(buf, " interface='%s'", ip->interface) < 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)
+{
+    xmlChar *value;
+    char *endptr;
+
+    value = xmlGetProp(node, 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, 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, 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);
+    }
+
+    value = xmlGetProp(node, BAD_CAST "interface");
+    if (value) {
+        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/ip", 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")) {
+        xmlNodePtr child;
+        struct vserver_ip *ip;
+
+        for (child = node->children; child; child = child->next) {
+            if (child->type != XML_ELEMENT_NODE)
+                continue;
+
+            /* This should be an only child, but who knows. */
+            if (xmlStrEqual(child->name, BAD_CAST "ip"))
+                break;
+        }
+        if (!child) {
+            vserverError(domain->conn, domain, NULL, VIR_ERR_XML_ERROR,
+                         _("no <ip> element found"));
+            return -1;
+        }
+
+        ip = calloc(1, sizeof(*ip));
+        if (vserverParseIP(ip, child) == -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]