--- /dev/null
+/*
+ * 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 */
+
--- /dev/null
+/*
+ * 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:
+ */