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@xxxxxxxxx>
+ */
+
+#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@xxxxxxxxx>
+ */
+
+#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@xxxxxxxxx>
+ */
+
+#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@xxxxxxxxx>
+ */
+
+#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
--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list