]> git.ipfire.org Git - thirdparty/libvirt.git/commitdiff
Initial Linux containers work
authorDaniel Veillard <veillard@redhat.com>
Fri, 21 Mar 2008 15:03:37 +0000 (15:03 +0000)
committerDaniel Veillard <veillard@redhat.com>
Fri, 21 Mar 2008 15:03:37 +0000 (15:03 +0000)
* configure.in include/libvirt/virterror.h src/Makefile.am
  src/driver.h src/lxc_conf.[ch] src/lxc_driver.[ch] src/virterror.c:
  Applied 3 patches from Dave Leskovec for intial support of
  Linux containers, configured off by default, work in progress.
* src/libvirt.c: improve virDomainCreateLinux xmlDesc description
Daniel

ChangeLog
configure.in
include/libvirt/virterror.h
src/Makefile.am
src/driver.h
src/libvirt.c
src/lxc_conf.c [new file with mode: 0644]
src/lxc_conf.h [new file with mode: 0644]
src/lxc_driver.c [new file with mode: 0644]
src/lxc_driver.h [new file with mode: 0644]
src/virterror.c

index f0ee76755be30b4e540d1a5cc3ef67e6ea349ccf..e60bd86568d8b50511c8ff615e957a00278a5a17 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Fri Mar 21 15:59:53 CET 2008 Daniel Veillard <veillard@redhat.com>
+
+       * configure.in include/libvirt/virterror.h src/Makefile.am
+         src/driver.h src/lxc_conf.[ch] src/lxc_driver.[ch] src/virterror.c:
+         Applied 3 patches from Dave Leskovec for intial support of
+         Linux containers, configured off by default, work in progress.
+       * src/libvirt.c: improve virDomainCreateLinux xmlDesc description
+
 Thu Mar 20 12:23:03 CET 2008 Daniel Veillard <veillard@redhat.com>
 
        * src/util.c src/util.h src/xml.c: applied patch from Hiroyuki Kaguchi
index d44e611d53903360f704383b661fc7871fe063c1..f3551d5e5c235b01f53a6d5c580dc8148324bda4 100644 (file)
@@ -132,6 +132,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(lxc,
+[  --with-lxc              add Linux Container support (off)],[],[with_lxc=no])
 AC_ARG_WITH(test,
 [  --with-test             add test driver support (on)],[],[with_test=yes])
 AC_ARG_WITH(remote,
@@ -229,6 +231,9 @@ WITH_XEN=0
 if test "$with_openvz" = "yes" ; then
     LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_OPENVZ"
 fi
+if test "$with_lxc" = "yes" ; then
+    LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_LXC"
+fi
 if test "$with_qemu" = "yes" ; then
     LIBVIRT_FEATURES="$LIBVIRT_FEATURES -DWITH_QEMU"
 fi
@@ -950,6 +955,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([     LXC: $with_lxc])
 AC_MSG_NOTICE([    Test: $with_test])
 AC_MSG_NOTICE([  Remote: $with_remote])
 AC_MSG_NOTICE([Libvirtd: $with_libvirtd])
index b05cd3d2cbdad546f5b2d9ce99aa03f6e7b1a1f1..a4155b346a039ec2eb6416e69d22fb7b9688b817 100644 (file)
@@ -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_LXC,   /* Error from Linux Container driver */
     VIR_FROM_STORAGE,   /* Error from storage driver */
 } virErrorDomain;
 
index d7e4a91edc74948056d93914c51c0ad668d23880..9bcc936c07e0592e4a79937bade671d2e71d1478 100644 (file)
@@ -59,6 +59,8 @@ CLIENT_SOURCES =                                              \
                qemu_conf.c qemu_conf.h                         \
                openvz_conf.c openvz_conf.h                     \
                openvz_driver.c openvz_driver.h                 \
+               lxc_driver.c lxc_driver.h                       \
+               lxc_conf.c lxc_conf.h                           \
                 nodeinfo.h nodeinfo.c                           \
                storage_conf.h storage_conf.c                   \
                storage_driver.h storage_driver.c               \
index 6e0df332a62dcab8e04f68ac083688772e2e8738..efb0887fc197ccbbecee572bb8245d9e2f6dbd55 100644 (file)
@@ -24,6 +24,7 @@ typedef enum {
     VIR_DRV_QEMU = 3,
     VIR_DRV_REMOTE = 4,
     VIR_DRV_OPENVZ = 5,
+    VIR_DRV_LXC = 6
 } virDrvNo;
 
 
index ecb5e261563d178b9a2d1c9d5dc37f3a33a9035e..b8e41195680f5e7757fb6d069db7036a7537a279 100644 (file)
@@ -45,6 +45,9 @@
 #ifdef WITH_OPENVZ
 #include "openvz_driver.h"
 #endif
+#ifdef WITH_LXC
+#include "lxc_driver.h"
+#endif
 
 /*
  * TODO:
@@ -271,6 +274,9 @@ virInitialize(void)
 #endif
 #ifdef WITH_OPENVZ
     if (openvzRegister() == -1) return -1;
+#endif
+#ifdef WITH_LXC
+    if (lxcRegister() == -1) return -1;
 #endif
     if (storageRegister() == -1) return -1;
 #ifdef WITH_REMOTE
@@ -1183,7 +1189,7 @@ virDomainGetConnect (virDomainPtr dom)
 /**
  * virDomainCreateLinux:
  * @conn: pointer to the hypervisor connection
- * @xmlDesc: an XML description of the domain
+ * @xmlDesc: string containing an XML description of the domain
  * @flags: an optional set of virDomainFlags
  *
  * Launch a new Linux guest domain, based on an XML description similar
diff --git a/src/lxc_conf.c b/src/lxc_conf.c
new file mode 100644 (file)
index 0000000..e0896ec
--- /dev/null
@@ -0,0 +1,942 @@
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_conf.c: config functions for managing linux containers
+ *
+ * Authors:
+ *  David L. Leskovec <dlesko at linux.vnet.ibm.com>
+ *
+ * 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
+ *
+ */
+
+/* includes */
+#include <config.h>
+
+#ifdef WITH_LXC
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/uri.h>
+#include <libxml/xpath.h>
+
+#include "buf.h"
+#include "util.h"
+#include "uuid.h"
+
+#include "lxc_conf.h"
+
+/* debug macros */
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+
+/* Functions */
+void lxcError(virConnectPtr conn, virDomainPtr dom, int code,
+              const char *fmt, ...)
+{
+    va_list args;
+    char errorMessage[LXC_MAX_ERROR_LEN];
+    const char *codeErrorMessage;
+
+    if (fmt) {
+        va_start(args, fmt);
+        vsnprintf(errorMessage, LXC_MAX_ERROR_LEN-1, fmt, args);
+        va_end(args);
+    } else {
+        errorMessage[0] = '\0';
+    }
+
+    codeErrorMessage = __virErrorMsg(code, fmt);
+    __virRaiseError(conn, dom, NULL, VIR_FROM_LXC, code, VIR_ERR_ERROR,
+                    codeErrorMessage, errorMessage, NULL, 0, 0,
+                    codeErrorMessage, errorMessage);
+}
+
+static inline int lxcIsEmptyXPathStringObj(xmlXPathObjectPtr xpathObj)
+{
+    if ((xpathObj == NULL) ||
+        (xpathObj->type != XPATH_STRING) ||
+        (xpathObj->stringval == NULL) ||
+        (xpathObj->stringval[0] == 0)) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+static int lxcParseMountXML(virConnectPtr conn, xmlNodePtr nodePtr,
+                            lxc_mount_t *lxcMount)
+{
+    xmlChar *fsType = NULL;
+    xmlNodePtr curNode;
+    xmlChar *mountSource = NULL;
+    xmlChar *mountTarget = NULL;
+    int strLen;
+    int rc = -1;
+
+    if (NULL == (fsType = xmlGetProp(nodePtr, BAD_CAST "type"))) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("missing filesystem type"));
+        goto error;
+    }
+
+    if (xmlStrEqual(fsType, BAD_CAST "mount") == 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("invalid filesystem type"));
+        goto error;
+    }
+
+    for (curNode = nodePtr->children;
+         NULL != curNode;
+         curNode = curNode->next) {
+        if (curNode->type != XML_ELEMENT_NODE) {
+            continue;
+        }
+
+        if ((mountSource == NULL) &&
+            (xmlStrEqual(curNode->name, BAD_CAST "source"))) {
+            mountSource = xmlGetProp(curNode, BAD_CAST "dir");
+        } else if ((mountTarget == NULL) &&
+                  (xmlStrEqual(curNode->name, BAD_CAST "target"))) {
+            mountTarget = xmlGetProp(curNode, BAD_CAST "dir");
+        }
+    }
+
+    if (mountSource == NULL) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("missing mount source"));
+        goto error;
+    }
+
+    strLen = xmlStrlen(mountSource);
+    if ((strLen > (PATH_MAX-1)) || (0 == strLen)) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("empty or invalid mount source"));
+        goto error;
+    }
+
+    strncpy(lxcMount->source, (char *)mountSource, strLen);
+    lxcMount->source[strLen] = '\0';
+
+    if (mountTarget == NULL) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("missing mount target"));
+        goto error;
+    }
+
+    strLen = xmlStrlen(mountTarget);
+    if ((strLen > (PATH_MAX-1)) || (0 == strLen)) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("empty or invalid mount target"));
+        goto error;
+    }
+
+    strncpy(lxcMount->target, (char *)mountTarget, strLen);
+    lxcMount->target[strLen] = '\0';
+
+    rc = 0;
+
+error:
+    xmlFree(mountSource);
+    xmlFree(mountTarget);
+
+    return rc;
+}
+
+static int lxcParseDomainName(virConnectPtr conn, char **name,
+                              xmlXPathContextPtr contextPtr)
+{
+    int rc = -1;
+    xmlXPathObjectPtr xpathObj = NULL;
+
+    xpathObj = xmlXPathEval(BAD_CAST "string(/domain/name[1])", contextPtr);
+    if (lxcIsEmptyXPathStringObj(xpathObj)) {
+        lxcError(conn, NULL, VIR_ERR_NO_NAME, NULL);
+        goto parse_complete;
+    }
+
+    *name = strdup((const char *)xpathObj->stringval);
+    if (NULL == *name) {
+        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, NULL);
+        goto parse_complete;
+    }
+
+
+    rc = 0;
+
+parse_complete:
+    xmlXPathFreeObject(xpathObj);
+    return rc;
+}
+
+static int lxcParseDomainUUID(virConnectPtr conn, unsigned char *uuid,
+                              xmlXPathContextPtr contextPtr)
+{
+    int rc = -1;
+    xmlXPathObjectPtr xpathObj = NULL;
+
+    xpathObj = xmlXPathEval(BAD_CAST "string(/domain/uuid[1])", contextPtr);
+    if (lxcIsEmptyXPathStringObj(xpathObj)) {
+        if ((rc = virUUIDGenerate(uuid))) {
+            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("failed to generate uuid: %s"), strerror(rc));
+            goto parse_complete;
+        }
+    } else {
+        if (virUUIDParse((const char *)xpathObj->stringval, uuid) < 0) {
+            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("invalid uuid element"));
+            goto parse_complete;
+        }
+    }
+
+    rc = 0;
+
+parse_complete:
+    xmlXPathFreeObject(xpathObj);
+    return rc;
+}
+
+static int lxcParseDomainMounts(virConnectPtr conn,
+                                lxc_mount_t **mounts,
+                                xmlXPathContextPtr contextPtr)
+{
+    int rc = -1;
+    xmlXPathObjectPtr xpathObj = NULL;
+    int i;
+    lxc_mount_t *mountObj;
+    lxc_mount_t *prevObj = NULL;
+    int nmounts = 0;
+
+    xpathObj = xmlXPathEval(BAD_CAST "/domain/devices/filesystem",
+                            contextPtr);
+    if ((xpathObj != NULL) &&
+        (xpathObj->type == XPATH_NODESET) &&
+        (xpathObj->nodesetval != NULL) &&
+        (xpathObj->nodesetval->nodeNr >= 0)) {
+        for (i = 0; i < xpathObj->nodesetval->nodeNr; ++i) {
+            mountObj = calloc(1, sizeof(lxc_mount_t));
+            if (NULL == mountObj) {
+                lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "mount");
+                goto parse_complete;
+            }
+
+            rc = lxcParseMountXML(conn, xpathObj->nodesetval->nodeTab[i],
+                                  mountObj);
+            if (0 > rc) {
+                free(mountObj);
+                goto parse_complete;
+            }
+
+            /* set the linked list pointers */
+            nmounts++;
+            mountObj->next = NULL;
+            if (0 == i) {
+                *mounts = mountObj;
+            } else {
+                prevObj->next = mountObj;
+            }
+            prevObj = mountObj;
+        }
+    }
+
+    rc = nmounts;
+
+parse_complete:
+    xmlXPathFreeObject(xpathObj);
+
+    return rc;
+}
+
+static int lxcParseDomainInit(virConnectPtr conn, char* init, xmlXPathContextPtr contextPtr)
+{
+    int rc = -1;
+    xmlXPathObjectPtr xpathObj = NULL;
+
+    xpathObj = xmlXPathEval(BAD_CAST "string(/domain/os/init[1])",
+                           contextPtr);
+    if (lxcIsEmptyXPathStringObj(xpathObj)) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("invalid or missing init element"));
+        goto parse_complete;
+    }
+
+    if (strlen((const char*)xpathObj->stringval) >= PATH_MAX - 1) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("init string too long"));
+        goto parse_complete;
+    }
+
+    strcpy(init, (const char *)xpathObj->stringval);
+
+    rc = 0;
+
+parse_complete:
+    xmlXPathFreeObject(xpathObj);
+
+    return rc;
+}
+
+
+static int lxcParseDomainTty(virConnectPtr conn, char *tty, xmlXPathContextPtr contextPtr)
+{
+    int rc = -1;
+    xmlXPathObjectPtr xpathObj = NULL;
+
+    xpathObj = xmlXPathEval(BAD_CAST "string(/domain/devices/console[1]/@tty)",
+                            contextPtr);
+    if (lxcIsEmptyXPathStringObj(xpathObj)) {
+        /* make sure the tty string is empty */
+        tty[0] = 0x00;
+    } else {
+        /* check the source string length */
+        if (strlen((const char*)xpathObj->stringval) >= LXC_MAX_TTY_NAME - 1) {
+            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("tty name is too long"));
+            goto parse_complete;
+        }
+        strcpy(tty, (const char *)xpathObj->stringval);
+    }
+
+    rc = 0;
+
+parse_complete:
+    xmlXPathFreeObject(xpathObj);
+
+    return rc;
+}
+
+static int lxcParseDomainMemory(virConnectPtr conn, int* memory, xmlXPathContextPtr contextPtr)
+{
+    int rc = -1;
+    xmlXPathObjectPtr xpathObj = NULL;
+    char *endChar = NULL;
+
+    xpathObj = xmlXPathEval(BAD_CAST "string(/domain/memory[1])", contextPtr);
+    if (lxcIsEmptyXPathStringObj(xpathObj)) {
+        /* not an error, default to an invalid value so it's not used */
+        *memory = -1;
+    } else {
+        *memory = strtoll((const char*)xpathObj->stringval,
+                                          &endChar, 10);
+        if ((endChar == (const char*)xpathObj->stringval) ||
+           (*endChar != '\0')) {
+            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("invalid memory value"));
+            goto parse_complete;
+        }
+    }
+
+    rc = 0;
+
+parse_complete:
+    xmlXPathFreeObject(xpathObj);
+
+    return rc;
+}
+
+static lxc_vm_def_t * lxcParseXML(virConnectPtr conn, xmlDocPtr docPtr)
+{
+    xmlNodePtr rootNodePtr = NULL;
+    xmlXPathContextPtr contextPtr = NULL;
+    xmlChar *xmlProp = NULL;
+    lxc_vm_def_t *containerDef;
+
+    if (!(containerDef = calloc(1, sizeof(*containerDef)))) {
+        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "containerDef");
+        return NULL;
+    }
+
+    /* Prepare parser / xpath context */
+    rootNodePtr = xmlDocGetRootElement(docPtr);
+    if ((rootNodePtr == NULL) ||
+       (!xmlStrEqual(rootNodePtr->name, BAD_CAST "domain"))) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("invalid root element"));
+        goto error;
+    }
+
+    contextPtr = xmlXPathNewContext(docPtr);
+    if (contextPtr == NULL) {
+        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "context");
+        goto error;
+    }
+
+    /* Verify the domain type is linuxcontainer */
+    if (!(xmlProp = xmlGetProp(rootNodePtr, BAD_CAST "type"))) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("missing domain type"));
+        goto error;
+    }
+
+    if (!(xmlStrEqual(xmlProp, BAD_CAST LXC_DOMAIN_TYPE))) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("invalid domain type"));
+        goto error;
+    }
+    free(xmlProp);
+    xmlProp = NULL;
+
+    if ((xmlProp = xmlGetProp(rootNodePtr, BAD_CAST "id"))) {
+        if (0 > virStrToLong_i((char*)xmlProp, NULL, 10, &(containerDef->id))) {
+            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                     "invalid domain id");
+            goto error;
+        }
+    } else {
+        containerDef->id = -1;
+    }
+    free(xmlProp);
+    xmlProp = NULL;
+
+    if (lxcParseDomainName(conn, &(containerDef->name), contextPtr) < 0) {
+        goto error;
+    }
+
+    if (lxcParseDomainInit(conn, containerDef->init, contextPtr) < 0) {
+        goto error;
+    }
+
+    if (lxcParseDomainUUID(conn, containerDef->uuid, contextPtr) < 0) {
+        goto error;
+    }
+
+    containerDef->nmounts = lxcParseDomainMounts(conn, &(containerDef->mounts),
+                                                 contextPtr);
+    if (0 > containerDef->nmounts) {
+        goto error;
+    }
+
+    if (lxcParseDomainTty(conn, containerDef->tty, contextPtr) < 0) {
+        goto error;
+    }
+
+    if (lxcParseDomainMemory(conn, &(containerDef->maxMemory), contextPtr) < 0) {
+        goto error;
+    }
+
+    xmlXPathFreeContext(contextPtr);
+
+    return containerDef;
+
+ error:
+    free(xmlProp);
+    xmlXPathFreeContext(contextPtr);
+    lxcFreeVMDef(containerDef);
+
+    return NULL;
+}
+
+
+lxc_vm_def_t * lxcParseVMDef(virConnectPtr conn,
+                             const char* xmlString,
+                             const char* fileName)
+{
+    xmlDocPtr xml;
+    lxc_vm_def_t *containerDef;
+
+    xml = xmlReadDoc(BAD_CAST xmlString,
+                     fileName ? fileName : "domain.xml",
+                     NULL, XML_PARSE_NOENT |
+                     XML_PARSE_NONET | XML_PARSE_NOERROR |
+                     XML_PARSE_NOWARNING);
+    if (!xml) {
+        lxcError(conn, NULL, VIR_ERR_XML_ERROR, NULL);
+        return NULL;
+    }
+
+    containerDef = lxcParseXML(conn, xml);
+
+    xmlFreeDoc(xml);
+
+    return containerDef;
+}
+
+lxc_vm_t * lxcAssignVMDef(virConnectPtr conn,
+                          lxc_driver_t *driver,
+                          lxc_vm_def_t *def)
+{
+    lxc_vm_t *vm = NULL;
+
+    if ((vm = lxcFindVMByName(driver, def->name))) {
+        if (!lxcIsActiveVM(vm)) {
+            lxcFreeVMDef(vm->def);
+            vm->def = def;
+        } else {
+            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("Can't redefine active VM with name %s"), def->name);
+            return NULL;
+        }
+
+        return vm;
+    }
+
+    if (!(vm = calloc(1, sizeof(lxc_vm_t)))) {
+        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "vm");
+        return NULL;
+    }
+
+    vm->pid = -1;
+    vm->def = def;
+    vm->next = driver->vms;
+
+    driver->vms = vm;
+
+    if (lxcIsActiveVM(vm)) {
+        vm->state = VIR_DOMAIN_RUNNING;
+        driver->nactivevms++;
+    } else {
+        vm->state = VIR_DOMAIN_SHUTOFF;
+        driver->ninactivevms++;
+    }
+
+    return vm;
+}
+
+void lxcRemoveInactiveVM(lxc_driver_t *driver,
+                         lxc_vm_t *vm)
+{
+    lxc_vm_t *prevVm = NULL;
+    lxc_vm_t *curVm;
+
+    for (curVm = driver->vms;
+         (curVm != vm) && (NULL != curVm);
+         curVm = curVm->next) {
+        prevVm = curVm;
+    }
+
+    if (curVm) {
+        if (prevVm) {
+            prevVm->next = curVm->next;
+        } else {
+            driver->vms = curVm->next;
+        }
+
+        driver->ninactivevms--;
+    }
+
+    lxcFreeVM(vm);
+}
+
+/* Save a container's config data into a persistent file */
+int lxcSaveConfig(virConnectPtr conn,
+                  lxc_driver_t *driver,
+                  lxc_vm_t *vm,
+                  lxc_vm_def_t *def)
+{
+    int rc = -1;
+    char *xmlDef;
+    int fd = -1;
+    int amtToWrite;
+
+    if (!(xmlDef = lxcGenerateXML(conn, driver, vm, def))) {
+        return -1;
+    }
+
+    if ((fd = open(vm->configFile,
+                  O_WRONLY | O_CREAT | O_TRUNC,
+                  S_IRUSR | S_IWUSR )) < 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("cannot create config file %s: %s"),
+                  vm->configFile, strerror(errno));
+        goto cleanup;
+    }
+
+    amtToWrite = strlen(xmlDef);
+    if (safewrite(fd, xmlDef, amtToWrite) < 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("cannot write config file %s: %s"),
+                 vm->configFile, strerror(errno));
+        goto cleanup;
+    }
+
+    if (close(fd) < 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("cannot save config file %s: %s"),
+                 vm->configFile, strerror(errno));
+        goto cleanup;
+    }
+
+    rc = 0;
+
+ cleanup:
+    if (fd != -1) {
+        close(fd);
+    }
+
+    free(xmlDef);
+
+    return rc;
+}
+
+int lxcSaveVMDef(virConnectPtr conn,
+                 lxc_driver_t *driver,
+                 lxc_vm_t *vm,
+                 lxc_vm_def_t *def)
+{
+    int rc = -1;
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+    if (vm->configFile[0] == '\0') {
+        if ((rc = virFileMakePath(driver->configDir))) {
+            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("cannot create config directory %s: %s"),
+                     driver->configDir, strerror(rc));
+            goto save_complete;
+        }
+
+        virUUIDFormat(def->uuid, uuidstr);
+        if (virFileBuildPath(driver->configDir, uuidstr, ".xml",
+                            vm->configFile, PATH_MAX) < 0) {
+            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("cannot construct config file path"));
+            goto save_complete;
+        }
+
+        strncpy(vm->configFileBase, uuidstr, PATH_MAX);
+        strncat(vm->configFileBase, ".xml", PATH_MAX - strlen(uuidstr));
+
+    }
+
+    rc = lxcSaveConfig(conn, driver, vm, def);
+
+save_complete:
+    return rc;
+}
+
+
+
+static lxc_vm_t * lxcLoadConfig(lxc_driver_t *driver,
+                                const char *file,
+                                const char *fullFilePath,
+                                const char *xmlData)
+{
+    lxc_vm_def_t *containerDef;
+    lxc_vm_t * vm;
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+    containerDef = lxcParseVMDef(NULL, xmlData, file);
+    if (NULL == containerDef) {
+        DEBUG0("Error parsing container config");
+        return NULL;
+    }
+
+    virUUIDFormat(containerDef->uuid, uuidstr);
+    if (!virFileMatchesNameSuffix(file, uuidstr, ".xml")) {
+        DEBUG0("Container uuid does not match config file name");
+        lxcFreeVMDef(containerDef);
+        return NULL;
+    }
+
+    vm = lxcAssignVMDef(NULL, driver, containerDef);
+    if (NULL == vm) {
+        DEBUG0("Failed to load container config");
+        lxcFreeVMDef(containerDef);
+        return NULL;
+    }
+
+    strncpy(vm->configFile, fullFilePath, PATH_MAX);
+    vm->configFile[PATH_MAX-1] = '\0';
+
+    strncpy(vm->configFileBase, file, PATH_MAX);
+    vm->configFile[PATH_MAX-1] = '\0';
+
+    return vm;
+}
+
+int lxcLoadDriverConfig(virConnectPtr conn)
+{
+    lxc_driver_t *driverPtr = (lxc_driver_t*)conn->privateData;
+
+    /* Set the container configuration directory */
+    driverPtr->configDir = strdup(SYSCONF_DIR "/libvirt/lxc");
+    if (NULL == driverPtr->configDir) {
+        lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "configDir");
+        return -1;
+    }
+
+    return 0;
+}
+
+int lxcLoadContainerConfigFile(lxc_driver_t *driver,
+                               const char *file)
+{
+    int rc = -1;
+    char tempPath[PATH_MAX];
+    char* xmlData;
+
+    rc = virFileBuildPath(driver->configDir, file, NULL, tempPath,
+                          PATH_MAX);
+    if (0 > rc) {
+        DEBUG0("config file name too long");
+        goto load_complete;
+    }
+
+    if ((rc = virFileReadAll(tempPath, LXC_MAX_XML_LENGTH, &xmlData)) < 0) {
+        goto load_complete;
+    }
+
+    lxcLoadConfig(driver, file, tempPath, xmlData);
+
+    free(xmlData);
+
+load_complete:
+    return rc;
+}
+
+int lxcLoadContainerInfo(virConnectPtr conn)
+{
+    int rc = -1;
+    lxc_driver_t *driverPtr = (lxc_driver_t*)conn->privateData;
+    DIR *dir;
+    struct dirent *dirEntry;
+
+    if (!(dir = opendir(driverPtr->configDir))) {
+        if (ENOENT == errno) {
+            /* no config dir => no containers */
+            rc = 0;
+        } else {
+            lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                     _("failed to open config directory: %s"), strerror(errno));
+        }
+
+        goto load_complete;
+    }
+
+    while ((dirEntry = readdir(dir))) {
+        if (dirEntry->d_name[0] == '.') {
+            continue;
+        }
+
+        if (!virFileHasSuffix(dirEntry->d_name, ".xml")) {
+            continue;
+        }
+
+        lxcLoadContainerConfigFile(driverPtr, dirEntry->d_name);
+    }
+
+    closedir(dir);
+
+    rc = 0;
+
+load_complete:
+    return rc;
+}
+
+/* Generate an XML document describing the vm's configuration */
+char *lxcGenerateXML(virConnectPtr conn,
+                     lxc_driver_t *driver ATTRIBUTE_UNUSED,
+                     lxc_vm_t *vm,
+                     lxc_vm_def_t *def)
+{
+    virBufferPtr buf = 0;
+    unsigned char *uuid;
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+    lxc_mount_t *mount;
+
+    buf = virBufferNew(LXC_MAX_XML_LENGTH);
+    if (!buf) {
+        goto no_memory;
+    }
+
+    if (lxcIsActiveVM(vm)) {
+        if (virBufferVSprintf(buf, "<domain type='linuxcontainer' id='%d'>\n",
+                             vm->def->id) < 0) {
+            goto no_memory;
+        }
+    } else {
+        if (virBufferAddLit(buf, "<domain type='linuxcontainer'>\n") < 0) {
+            goto no_memory;
+        }
+    }
+
+    if (virBufferVSprintf(buf, "    <name>%s</name>\n", def->name) < 0) {
+        goto no_memory;
+    }
+
+    uuid = def->uuid;
+    virUUIDFormat(uuid, uuidstr);
+    if (virBufferVSprintf(buf, "    <uuid>%s</uuid>\n", uuidstr) < 0) {
+        goto no_memory;
+    }
+
+    if (virBufferAddLit(buf, "    <os>\n") < 0) {
+        goto no_memory;
+    }
+
+    if (virBufferVSprintf(buf, "        <init>%s</init>\n", def->init) < 0) {
+        goto no_memory;
+    }
+
+    if (virBufferAddLit(buf, "    </os>\n") < 0) {
+        goto no_memory;
+    }
+
+    if (virBufferVSprintf(buf, "    <memory>%d</memory>\n", def->maxMemory) < 0) {
+        goto no_memory;
+    }
+
+    if (virBufferAddLit(buf, "    <devices>\n") < 0) {
+        goto no_memory;
+    }
+
+    /* loop adding mounts */
+    for (mount = def->mounts; mount; mount = mount->next) {
+        if (virBufferAddLit(buf, "        <filesystem type='mount'>\n") < 0) {
+            goto no_memory;
+        }
+
+        if (virBufferVSprintf(buf, "            <source dir='%s'/>\n",
+                              mount->source) < 0) {
+            goto no_memory;
+        }
+
+        if (virBufferVSprintf(buf, "            <target dir='%s'/>\n",
+                              mount->target) < 0) {
+            goto no_memory;
+        }
+
+        if (virBufferAddLit(buf, "        </filesystem>\n") < 0) {
+            goto no_memory;
+        }
+
+    }
+
+    if (virBufferVSprintf(buf, "        <console tty='%s'/>\n", def->tty) < 0) {
+        goto no_memory;
+    }
+
+    if (virBufferAddLit(buf, "    </devices>\n") < 0) {
+        goto no_memory;
+    }
+
+    if (virBufferAddLit(buf, "</domain>\n") < 0) {
+        goto no_memory;
+    }
+
+    return virBufferContentAndFree(buf);
+
+no_memory:
+    lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "generateXml");
+    virBufferFree(buf);
+
+    return NULL;
+}
+
+void lxcFreeVMDef(lxc_vm_def_t *vmdef)
+{
+    lxc_mount_t *curMount = vmdef->mounts;
+    lxc_mount_t *nextMount;
+
+    while (curMount) {
+        nextMount = curMount->next;
+        free(curMount);
+        curMount = nextMount;
+    }
+
+    free(vmdef->name);
+    vmdef->name = NULL;
+
+}
+
+void lxcFreeVMs(lxc_vm_t *vms)
+{
+    lxc_vm_t *curVm = vms;
+    lxc_vm_t *nextVm;
+
+    while (curVm) {
+        lxcFreeVM(curVm);
+        nextVm = curVm->next;
+        free(curVm);
+        curVm = nextVm;
+    }
+}
+
+void lxcFreeVM(lxc_vm_t *vm)
+{
+    lxcFreeVMDef(vm->def);
+    free(vm->def);
+}
+
+lxc_vm_t *lxcFindVMByID(const lxc_driver_t *driver, int id)
+{
+    lxc_vm_t *vm;
+
+    for (vm = driver->vms; vm; vm = vm->next) {
+        if (lxcIsActiveVM(vm) && (vm->def->id == id)) {
+            return vm;
+        }
+
+    }
+
+    return NULL;
+}
+
+lxc_vm_t *lxcFindVMByUUID(const lxc_driver_t *driver,
+                          const unsigned char *uuid)
+{
+    lxc_vm_t *vm;
+
+    for (vm = driver->vms; vm; vm = vm->next) {
+        if (!memcmp(vm->def->uuid, uuid, VIR_UUID_BUFLEN)) {
+            return vm;
+        }
+    }
+
+    return NULL;
+}
+
+lxc_vm_t *lxcFindVMByName(const lxc_driver_t *driver,
+                          const char *name)
+{
+    lxc_vm_t *vm;
+
+    for (vm = driver->vms; vm; vm = vm->next) {
+        if (STREQ(vm->def->name, name)) {
+            return vm;
+        }
+    }
+
+    return NULL;
+}
+
+int lxcDeleteConfig(virConnectPtr conn,
+                    lxc_driver_t *driver ATTRIBUTE_UNUSED,
+                    const char *configFile,
+                    const char *name)
+{
+    if (!configFile[0]) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("no config file for %s"), name);
+        return -1;
+    }
+
+    if (unlink(configFile) < 0) {
+        lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+                 _("cannot remove config for %s"), name);
+        return -1;
+    }
+
+    return 0;
+}
+
+#endif /* WITH_LXC */
+
diff --git a/src/lxc_conf.h b/src/lxc_conf.h
new file mode 100644 (file)
index 0000000..85f87d0
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_conf.h: header file for linux container config functions
+ *
+ * Authors:
+ *  David L. Leskovec <dlesko at linux.vnet.ibm.com>
+ *
+ * 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
+ */
+
+#ifndef LXC_CONF_H
+#define LXC_CONF_H
+
+#include <config.h>
+
+#ifdef WITH_LXC
+
+#include "internal.h"
+
+/* Defines */
+#define LXC_MAX_TTY_NAME 32
+#define LXC_MAX_XML_LENGTH 16384
+#define LXC_MAX_ERROR_LEN 1024
+#define LXC_DOMAIN_TYPE "lxc"
+
+typedef struct __lxc_mount lxc_mount_t;
+struct __lxc_mount {
+    char source[PATH_MAX]; /* user's directory */
+    char target[PATH_MAX];
+
+    lxc_mount_t *next;
+};
+
+typedef struct __lxc_vm_def lxc_vm_def_t;
+struct __lxc_vm_def {
+    unsigned char uuid[VIR_UUID_BUFLEN];
+    char* name;
+    int id;
+
+    /* init command string */
+    char init[PATH_MAX];
+
+    int maxMemory;
+
+    /* mounts - list of mount structs */
+    int nmounts;
+    lxc_mount_t *mounts;
+
+    /* tty device */
+    char tty[LXC_MAX_TTY_NAME];
+};
+
+typedef struct __lxc_vm lxc_vm_t;
+struct __lxc_vm {
+    int pid;
+    int state;
+
+    char configFile[PATH_MAX];
+    char configFileBase[PATH_MAX];
+
+    int parentTty;
+
+    lxc_vm_def_t *def;
+
+    lxc_vm_t *next;
+};
+
+typedef struct __lxc_driver lxc_driver_t;
+struct __lxc_driver {
+    lxc_vm_t *vms;
+    int nactivevms;
+    int ninactivevms;
+    char* configDir;
+};
+
+/* Types and structs */
+
+/* Inline Functions */
+static inline int lxcIsActiveVM(lxc_vm_t *vm)
+{
+    return vm->def->id != -1;
+}
+
+/* Function declarations */
+lxc_vm_def_t * lxcParseVMDef(virConnectPtr conn,
+                             const char* xmlString,
+                             const char* fileName);
+int lxcSaveVMDef(virConnectPtr conn,
+                 lxc_driver_t *driver,
+                 lxc_vm_t *vm,
+                 lxc_vm_def_t *def);
+int lxcLoadDriverConfig(virConnectPtr conn);
+int lxcSaveConfig(virConnectPtr conn,
+                  lxc_driver_t *driver,
+                  lxc_vm_t *vm,
+                  lxc_vm_def_t *def);
+int lxcLoadContainerInfo(virConnectPtr conn);
+int lxcLoadContainerConfigFile(lxc_driver_t *driver,
+                               const char *file);
+lxc_vm_t * lxcAssignVMDef(virConnectPtr conn,
+                          lxc_driver_t *driver,
+                          lxc_vm_def_t *def);
+char *lxcGenerateXML(virConnectPtr conn,
+                     lxc_driver_t *driver,
+                     lxc_vm_t *vm,
+                     lxc_vm_def_t *def);
+lxc_vm_t *lxcFindVMByID(const lxc_driver_t *driver, int id);
+lxc_vm_t *lxcFindVMByUUID(const lxc_driver_t *driver,
+                          const unsigned char *uuid);
+lxc_vm_t *lxcFindVMByName(const lxc_driver_t *driver,
+                          const char *name);
+void lxcRemoveInactiveVM(lxc_driver_t *driver,
+                         lxc_vm_t *vm);
+void lxcFreeVMs(lxc_vm_t *vms);
+void lxcFreeVM(lxc_vm_t *vm);
+void lxcFreeVMDef(lxc_vm_def_t *vmdef);
+int lxcDeleteConfig(virConnectPtr conn,
+                    lxc_driver_t *driver,
+                    const char *configFile,
+                    const char *name);
+
+void lxcError(virConnectPtr conn,
+              virDomainPtr dom,
+              int code, const char *fmt, ...)
+    ATTRIBUTE_FORMAT(printf,4,5);
+
+#endif /* WITH_LXC */
+#endif /* LXC_CONF_H */
+
diff --git a/src/lxc_driver.c b/src/lxc_driver.c
new file mode 100644 (file)
index 0000000..699c195
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_driver.c: linux container driver functions
+ *
+ * Authors:
+ *  David L. Leskovec <dlesko at linux.vnet.ibm.com>
+ *
+ * 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
+ */
+
+#include <config.h>
+
+#ifdef WITH_LXC
+
+#include <sched.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <wait.h>
+
+#include "lxc_conf.h"
+#include "lxc_driver.h"
+#include "driver.h"
+#include "internal.h"
+
+/* debug macros */
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+
+static int lxcStartup(virConnectPtr conn);
+static int lxcShutdown(virConnectPtr conn);
+
+/* Functions */
+static int lxcDummyChild( void *argv ATTRIBUTE_UNUSED )
+{
+    exit(0);
+}
+
+static int lxcCheckContainerSupport( void )
+{
+    int rc = 0;
+    int flags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWUSER|
+        CLONE_NEWIPC|SIGCHLD;
+    int cpid;
+    char *childStack;
+    char *stack;
+    int childStatus;
+
+    stack = malloc(getpagesize() * 4);
+    if(!stack) {
+        DEBUG0("Unable to allocate stack");
+        rc = -1;
+        goto check_complete;
+    }
+
+    childStack = stack + (getpagesize() * 4);
+
+    cpid = clone(lxcDummyChild, childStack, flags, NULL);
+    if ((0 > cpid) && (EINVAL == errno)) {
+        DEBUG0("clone call returned EINVAL, container support is not enabled");
+        rc = -1;
+    } else {
+        waitpid(cpid, &childStatus, 0);
+    }
+
+    free(stack);
+
+check_complete:
+    return rc;
+}
+
+static const char *lxcProbe(void)
+{
+#ifdef __linux__
+    if (0 == lxcCheckContainerSupport()) {
+        return("lxc:///");
+    }
+#endif
+    return(NULL);
+}
+
+static virDrvOpenStatus lxcOpen(virConnectPtr conn,
+                                xmlURIPtr uri,
+                                virConnectAuthPtr auth ATTRIBUTE_UNUSED,
+                                int flags ATTRIBUTE_UNUSED)
+{
+    uid_t uid = getuid();
+
+    /* Check that the user is root */
+    if (0 != uid) {
+        goto declineConnection;
+    }
+
+    /* Verify uri was specified */
+    if ((NULL == uri) || (NULL == uri->scheme)) {
+        goto declineConnection;
+    }
+
+    /* Check that the uri scheme is lxc */
+    if (STRNEQ(uri->scheme, "lxc")) {
+        goto declineConnection;
+    }
+
+    /* Check that this is a container enabled kernel */
+    if(0 != lxcCheckContainerSupport()) {
+        goto declineConnection;
+    }
+
+    /* initialize driver data */
+    if (0 > lxcStartup(conn)) {
+        goto declineConnection;
+    }
+
+    return VIR_DRV_OPEN_SUCCESS;
+
+declineConnection:
+    return VIR_DRV_OPEN_DECLINED;
+}
+
+static int lxcClose(virConnectPtr conn)
+{
+    return lxcShutdown(conn);
+}
+
+static virDomainPtr lxcDomainLookupByID(virConnectPtr conn,
+                                        int id)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+    lxc_vm_t *vm = lxcFindVMByID(driver, id);
+    virDomainPtr dom;
+
+    if (!vm) {
+        lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
+        return NULL;
+    }
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) {
+        dom->id = vm->def->id;
+    }
+
+    return dom;
+}
+
+static virDomainPtr lxcDomainLookupByUUID(virConnectPtr conn,
+                                          const unsigned char *uuid)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+    lxc_vm_t *vm = lxcFindVMByUUID(driver, uuid);
+    virDomainPtr dom;
+
+    if (!vm) {
+        lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
+        return NULL;
+    }
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) {
+        dom->id = vm->def->id;
+    }
+
+    return dom;
+}
+
+static virDomainPtr lxcDomainLookupByName(virConnectPtr conn,
+                                          const char *name)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+    lxc_vm_t *vm = lxcFindVMByName(driver, name);
+    virDomainPtr dom;
+
+    if (!vm) {
+        lxcError(conn, NULL, VIR_ERR_NO_DOMAIN, NULL);
+        return NULL;
+    }
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) {
+        dom->id = vm->def->id;
+    }
+
+    return dom;
+}
+
+static int lxcListDomains(virConnectPtr conn, int *ids, int nids)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+    lxc_vm_t *vm;
+    int numDoms = 0;
+
+    for (vm = driver->vms; vm && (numDoms < nids); vm = vm->next) {
+        if (lxcIsActiveVM(vm)) {
+            ids[numDoms] = vm->def->id;
+            numDoms++;
+        }
+    }
+
+    return numDoms;
+}
+
+static int lxcNumDomains(virConnectPtr conn)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+    return driver->nactivevms;
+}
+
+static int lxcListDefinedDomains(virConnectPtr conn,
+                                 char **const names, int nnames)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+    lxc_vm_t *vm;
+    int numDoms = 0;
+    int i;
+
+    for (vm = driver->vms; vm && (numDoms < nnames); vm = vm->next) {
+        if (!lxcIsActiveVM(vm)) {
+            if (!(names[numDoms] = strdup(vm->def->name))) {
+                lxcError(conn, NULL, VIR_ERR_NO_MEMORY, "names");
+                goto cleanup;
+            }
+
+            numDoms++;
+        }
+
+    }
+
+    return numDoms;
+
+ cleanup:
+    for (i = 0 ; i < numDoms ; i++) {
+        free(names[i]);
+    }
+
+    return -1;
+}
+
+
+static int lxcNumDefinedDomains(virConnectPtr conn)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+    return driver->ninactivevms;
+}
+
+static virDomainPtr lxcDomainDefine(virConnectPtr conn, const char *xml)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+    lxc_vm_def_t *def;
+    lxc_vm_t *vm;
+    virDomainPtr dom;
+
+    if (!(def = lxcParseVMDef(conn, xml, NULL))) {
+        return NULL;
+    }
+
+    if (!(vm = lxcAssignVMDef(conn, driver, def))) {
+        lxcFreeVMDef(def);
+        return NULL;
+    }
+
+    if (lxcSaveVMDef(conn, driver, vm, def) < 0) {
+        lxcRemoveInactiveVM(driver, vm);
+        return NULL;
+    }
+
+    dom = virGetDomain(conn, vm->def->name, vm->def->uuid);
+    if (dom) {
+        dom->id = vm->def->id;
+    }
+
+    return dom;
+}
+
+static int lxcDomainUndefine(virDomainPtr dom)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
+    lxc_vm_t *vm = lxcFindVMByUUID(driver, dom->uuid);
+
+    if (!vm) {
+        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
+                 _("no domain with matching uuid"));
+        return -1;
+    }
+
+    if (lxcIsActiveVM(vm)) {
+        lxcError(dom->conn, dom, VIR_ERR_INTERNAL_ERROR,
+                 _("cannot delete active domain"));
+        return -1;
+    }
+
+    if (lxcDeleteConfig(dom->conn, driver, vm->configFile, vm->def->name) < 0) {
+        return -1;
+    }
+
+    vm->configFile[0] = '\0';
+
+    lxcRemoveInactiveVM(driver, vm);
+
+    return 0;
+}
+
+static int lxcDomainGetInfo(virDomainPtr dom,
+                            virDomainInfoPtr info)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
+    lxc_vm_t *vm = lxcFindVMByUUID(driver, dom->uuid);
+
+    if (!vm) {
+        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
+                 _("no domain with matching uuid"));
+        return -1;
+    }
+
+    info->state = vm->state;
+
+    if (!lxcIsActiveVM(vm)) {
+        info->cpuTime = 0;
+    } else {
+        info->cpuTime = 0;
+    }
+
+    info->maxMem = vm->def->maxMemory;
+    info->memory = vm->def->maxMemory;
+    info->nrVirtCpu = 1;
+
+    return 0;
+}
+
+static char *lxcGetOSType(virDomainPtr dom ATTRIBUTE_UNUSED)
+{
+    /* Linux containers only run on Linux */
+    return strdup("linux");
+}
+
+static char *lxcDomainDumpXML(virDomainPtr dom,
+                              int flags ATTRIBUTE_UNUSED)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)dom->conn->privateData;
+    lxc_vm_t *vm = lxcFindVMByUUID(driver, dom->uuid);
+
+    if (!vm) {
+        lxcError(dom->conn, dom, VIR_ERR_INVALID_DOMAIN,
+                 _("no domain with matching uuid"));
+        return NULL;
+    }
+
+    return lxcGenerateXML(dom->conn, driver, vm, vm->def);
+}
+
+static int lxcStartup(virConnectPtr conn)
+{
+    lxc_driver_t *driver;
+
+    driver = calloc(1, sizeof(lxc_driver_t));
+    if (NULL == driver) {
+        return -1;
+    }
+
+    conn->privateData = driver;
+
+    /* Call function to load lxc driver configuration information */
+    if (lxcLoadDriverConfig(conn) < 0) {
+        lxcShutdown(conn);
+        return -1;
+    }
+
+    /* Call function to load the container configuration files */
+    if (lxcLoadContainerInfo(conn) < 0) {
+        lxcShutdown(conn);
+        return -1;
+    }
+
+    return 0;
+}
+
+static void lxcFreeDriver(lxc_driver_t *driver)
+{
+    free(driver->configDir);
+    free(driver);
+}
+
+static int lxcShutdown(virConnectPtr conn)
+{
+    lxc_driver_t *driver = (lxc_driver_t *)conn->privateData;
+    lxc_vm_t *vms = driver->vms;
+
+    lxcFreeVMs(vms);
+    driver->vms = NULL;
+    lxcFreeDriver(driver);
+    conn->privateData = NULL;
+
+    return 0;
+}
+
+/* Function Tables */
+static virDriver lxcDriver = {
+    VIR_DRV_LXC, /* the number virDrvNo */
+    "LXC", /* the name of the driver */
+    LIBVIR_VERSION_NUMBER, /* the version of the backend */
+    lxcProbe, /* probe */
+    lxcOpen, /* open */
+    lxcClose, /* close */
+    NULL, /* supports_feature */
+    NULL, /* type */
+    NULL, /* version */
+    NULL, /* getHostname */
+    NULL, /* getURI */
+    NULL, /* getMaxVcpus */
+    NULL, /* nodeGetInfo */
+    NULL, /* getCapabilities */
+    lxcListDomains, /* listDomains */
+    lxcNumDomains, /* numOfDomains */
+    NULL/*lxcDomainCreateLinux*/, /* domainCreateLinux */
+    lxcDomainLookupByID, /* domainLookupByID */
+    lxcDomainLookupByUUID, /* domainLookupByUUID */
+    lxcDomainLookupByName, /* domainLookupByName */
+    NULL, /* domainSuspend */
+    NULL, /* domainResume */
+    NULL, /* domainShutdown */
+    NULL, /* domainReboot */
+    NULL, /* domainDestroy */
+    lxcGetOSType, /* domainGetOSType */
+    NULL, /* domainGetMaxMemory */
+    NULL, /* domainSetMaxMemory */
+    NULL, /* domainSetMemory */
+    lxcDomainGetInfo, /* domainGetInfo */
+    NULL, /* domainSave */
+    NULL, /* domainRestore */
+    NULL, /* domainCoreDump */
+    NULL, /* domainSetVcpus */
+    NULL, /* domainPinVcpu */
+    NULL, /* domainGetVcpus */
+    NULL, /* domainGetMaxVcpus */
+    lxcDomainDumpXML, /* domainDumpXML */
+    lxcListDefinedDomains, /* listDefinedDomains */
+    lxcNumDefinedDomains, /* numOfDefinedDomains */
+    NULL, /* domainCreate */
+    lxcDomainDefine, /* domainDefineXML */
+    lxcDomainUndefine, /* domainUndefine */
+    NULL, /* domainAttachDevice */
+    NULL, /* domainDetachDevice */
+    NULL, /* domainGetAutostart */
+    NULL, /* domainSetAutostart */
+    NULL, /* domainGetSchedulerType */
+    NULL, /* domainGetSchedulerParameters */
+    NULL, /* domainSetSchedulerParameters */
+    NULL, /* domainMigratePrepare */
+    NULL, /* domainMigratePerform */
+    NULL, /* domainMigrateFinish */
+    NULL, /* domainBlockStats */
+    NULL, /* domainInterfaceStats */
+    NULL, /* nodeGetCellsFreeMemory */
+    NULL, /* getFreeMemory */
+};
+
+int lxcRegister(void)
+{
+    virRegisterDriver(&lxcDriver);
+    return 0;
+}
+
+#endif /* WITH_LXC */
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
diff --git a/src/lxc_driver.h b/src/lxc_driver.h
new file mode 100644 (file)
index 0000000..5a7d76e
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_driver.h: header file for linux container driver functions
+ *
+ * Authors:
+ *  David L. Leskovec <dlesko at linux.vnet.ibm.com>
+ *
+ * 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
+ */
+
+#ifndef LXC_DRIVER_H
+#define LXC_DRIVER_H
+
+#include <config.h>
+
+#ifdef WITH_LXC
+
+/* Function declarations */
+int lxcRegister(void);
+
+#endif /* WITH_LXC */
+
+#endif /* LXC_DRIVER_H */
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ *  c-indent-level: 4
+ *  c-basic-offset: 4
+ *  tab-width: 4
+ * End:
+ */
index 8e47da496b57949feef31342c773bd2a564ced58..1e39be40a4e2497bf16e2d6dcec070ccba560e3f 100644 (file)
@@ -296,6 +296,9 @@ virDefaultErrorFunc(virErrorPtr err)
         case VIR_FROM_STATS_LINUX:
             dom = "Linux Stats ";
             break;
+        case VIR_FROM_LXC:
+            dom = "Linux Container ";
+            break;
         case VIR_FROM_STORAGE:
             dom = "Storage ";
             break;