diff --git a/ChangeLog b/ChangeLog index f0ee76755b..e60bd86568 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Fri Mar 21 15:59:53 CET 2008 Daniel Veillard + + * 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 * src/util.c src/util.h src/xml.c: applied patch from Hiroyuki Kaguchi diff --git a/configure.in b/configure.in index d44e611d53..f3551d5e5c 100644 --- a/configure.in +++ b/configure.in @@ -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]) diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index b05cd3d2cb..a4155b346a 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -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; diff --git a/src/Makefile.am b/src/Makefile.am index d7e4a91edc..9bcc936c07 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/driver.h b/src/driver.h index 6e0df332a6..efb0887fc1 100644 --- a/src/driver.h +++ b/src/driver.h @@ -24,6 +24,7 @@ typedef enum { VIR_DRV_QEMU = 3, VIR_DRV_REMOTE = 4, VIR_DRV_OPENVZ = 5, + VIR_DRV_LXC = 6 } virDrvNo; diff --git a/src/libvirt.c b/src/libvirt.c index ecb5e26156..b8e4119568 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -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 index 0000000000..e0896eccf3 --- /dev/null +++ b/src/lxc_conf.c @@ -0,0 +1,942 @@ +/* + * Copyright IBM Corp. 2008 + * + * lxc_conf.c: config functions for managing linux containers + * + * Authors: + * David L. Leskovec + * + * 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 + +#ifdef WITH_LXC + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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, "\n", + vm->def->id) < 0) { + goto no_memory; + } + } else { + if (virBufferAddLit(buf, "\n") < 0) { + goto no_memory; + } + } + + if (virBufferVSprintf(buf, " %s\n", def->name) < 0) { + goto no_memory; + } + + uuid = def->uuid; + virUUIDFormat(uuid, uuidstr); + if (virBufferVSprintf(buf, " %s\n", uuidstr) < 0) { + goto no_memory; + } + + if (virBufferAddLit(buf, " \n") < 0) { + goto no_memory; + } + + if (virBufferVSprintf(buf, " %s\n", def->init) < 0) { + goto no_memory; + } + + if (virBufferAddLit(buf, " \n") < 0) { + goto no_memory; + } + + if (virBufferVSprintf(buf, " %d\n", def->maxMemory) < 0) { + goto no_memory; + } + + if (virBufferAddLit(buf, " \n") < 0) { + goto no_memory; + } + + /* loop adding mounts */ + for (mount = def->mounts; mount; mount = mount->next) { + if (virBufferAddLit(buf, " \n") < 0) { + goto no_memory; + } + + if (virBufferVSprintf(buf, " \n", + mount->source) < 0) { + goto no_memory; + } + + if (virBufferVSprintf(buf, " \n", + mount->target) < 0) { + goto no_memory; + } + + if (virBufferAddLit(buf, " \n") < 0) { + goto no_memory; + } + + } + + if (virBufferVSprintf(buf, " \n", def->tty) < 0) { + goto no_memory; + } + + if (virBufferAddLit(buf, " \n") < 0) { + goto no_memory; + } + + if (virBufferAddLit(buf, "\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 index 0000000000..85f87d076b --- /dev/null +++ b/src/lxc_conf.h @@ -0,0 +1,142 @@ +/* + * Copyright IBM Corp. 2008 + * + * lxc_conf.h: header file for linux container config functions + * + * Authors: + * David L. Leskovec + * + * 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 + +#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 index 0000000000..699c195976 --- /dev/null +++ b/src/lxc_driver.c @@ -0,0 +1,484 @@ +/* + * Copyright IBM Corp. 2008 + * + * lxc_driver.c: linux container driver functions + * + * Authors: + * David L. Leskovec + * + * 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 + +#ifdef WITH_LXC + +#include +#include +#include +#include +#include +#include + +#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 index 0000000000..5a7d76e453 --- /dev/null +++ b/src/lxc_driver.h @@ -0,0 +1,45 @@ +/* + * Copyright IBM Corp. 2008 + * + * lxc_driver.h: header file for linux container driver functions + * + * Authors: + * David L. Leskovec + * + * 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 + +#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: + */ diff --git a/src/virterror.c b/src/virterror.c index 8e47da496b..1e39be40a4 100644 --- a/src/virterror.c +++ b/src/virterror.c @@ -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;