openvz_conf.c openvz_conf.h \
openvz_driver.c openvz_driver.h \
lxc_driver.c lxc_driver.h \
+ lxc_controller.c lxc_controller.h \
lxc_conf.c lxc_conf.h \
lxc_container.c lxc_container.h \
veth.c veth.h \
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
+#include <sys/wait.h>
#include <unistd.h>
#include "lxc_container.h"
#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
+/*
+ * GLibc headers are behind the kernel, so we define these
+ * constants if they're not present already.
+ */
+
+#ifndef CLONE_NEWPID
+#define CLONE_NEWPID 0x20000000
+#endif
+#ifndef CLONE_NEWUTS
+#define CLONE_NEWUTS 0x04000000
+#endif
+#ifndef CLONE_NEWUSER
+#define CLONE_NEWUSER 0x10000000
+#endif
+#ifndef CLONE_NEWIPC
+#define CLONE_NEWIPC 0x08000000
+#endif
+#ifndef CLONE_NEWNET
+#define CLONE_NEWNET 0x40000000 /* New network namespace */
+#endif
+
+/* messages between parent and container */
+typedef char lxc_message_t;
+#define LXC_CONTINUE_MSG 'c'
+
+typedef struct __lxc_child_argv lxc_child_argv_t;
+struct __lxc_child_argv {
+ lxc_vm_def_t *config;
+ int monitor;
+ char *ttyPath;
+};
+
+
/**
- * lxcExecContainerInit:
+ * lxcContainerExecInit:
* @vmDef: Ptr to vm definition structure
*
- * Exec the container init string. The container init will replace then
+ * Exec the container init string. The container init will replace then
* be running in the current process
*
- * Returns 0 on success or -1 in case of error
+ * Does not return
*/
-static int lxcExecContainerInit(const lxc_vm_def_t *vmDef)
+static int lxcContainerExecInit(const lxc_vm_def_t *vmDef)
{
- int rc = -1;
- char* execString;
- size_t execStringLen = strlen(vmDef->init) + 1 + 5;
-
- if (VIR_ALLOC_N(execString, execStringLen) < 0) {
- lxcError(NULL, NULL, VIR_ERR_NO_MEMORY,
- _("failed to calloc memory for init string: %s"),
- strerror(errno));
- goto error_out;
- }
-
- strcpy(execString, "exec ");
- strcat(execString, vmDef->init);
+ const char *const argv[] = {
+ vmDef->init,
+ NULL,
+ };
- execl("/bin/sh", "sh", "-c", execString, (char*)NULL);
- lxcError(NULL, NULL, VIR_ERR_NO_MEMORY,
- _("execl failed to exec init: %s"), strerror(errno));
-
-error_out:
- exit(rc);
+ return execve(argv[0], (char **)argv, NULL);
}
/**
- * lxcSetContainerStdio:
- * @ttyName: Name of tty to set as the container console
+ * lxcContainerSetStdio:
+ * @control: the conrol FD
+ * @ttyPath: Name of tty to set as the container console
*
* Sets the given tty as the primary conosole for the container as well as
* stdout, stdin and stderr.
*
* Returns 0 on success or -1 in case of error
*/
-static int lxcSetContainerStdio(const char *ttyPath)
+static int lxcContainerSetStdio(int control, const char *ttyPath)
{
int rc = -1;
int ttyfd;
* close all FDs before executing the container */
open_max = sysconf (_SC_OPEN_MAX);
for (i = 0; i < open_max; i++)
- if (i != ttyfd)
+ if (i != ttyfd && i != control)
close(i);
if (dup2(ttyfd, 0) < 0) {
}
/**
- * lxcExecWithTty:
- * @vm: Ptr to vm structure
+ * lxcContainerSendContinue:
+ * @monitor: control FD to child
*
- * Sets container console and stdio and then execs container init
+ * Sends the continue message via the socket pair stored in the vm
+ * structure.
*
* Returns 0 on success or -1 in case of error
*/
-static int lxcExecWithTty(lxc_vm_def_t *vmDef, char *ttyPath)
+int lxcContainerSendContinue(virConnectPtr conn,
+ int control)
{
int rc = -1;
+ lxc_message_t msg = LXC_CONTINUE_MSG;
+ int writeCount = 0;
- if(lxcSetContainerStdio(ttyPath) < 0) {
- goto exit_with_error;
+ writeCount = safewrite(control, &msg, sizeof(msg));
+ if (writeCount != sizeof(msg)) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("unable to send container continue message: %s"),
+ strerror(errno));
+ goto error_out;
}
- lxcExecContainerInit(vmDef);
+ rc = 0;
-exit_with_error:
- exit(rc);
+error_out:
+ return rc;
}
/**
- * lxcWaitForContinue:
- * @monitor: monitor FD from parent
+ * lxcContainerWaitForContinue:
+ * @control: control FD from parent
*
* This function will wait for the container continue message from the
* parent process. It will send this message on the socket pair stored in
*
* Returns 0 on success or -1 in case of error
*/
-static int lxcWaitForContinue(int monitor)
+static int lxcContainerWaitForContinue(int control)
{
lxc_message_t msg;
int readLen;
- readLen = saferead(monitor, &msg, sizeof(msg));
+ readLen = saferead(control, &msg, sizeof(msg));
if (readLen != sizeof(msg) ||
msg != LXC_CONTINUE_MSG) {
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
strerror(errno));
return -1;
}
+ close(control);
DEBUG0("Received container continue message");
*
* Returns 0 on success or nonzero in case of error
*/
-static int lxcEnableInterfaces(const lxc_vm_def_t *def)
+static int lxcContainerEnableInterfaces(const lxc_vm_def_t *def)
{
int rc = 0;
const lxc_net_def_t *net;
*
* Returns 0 on success or -1 in case of error
*/
-int lxcChild( void *data )
+static int lxcContainerChild( void *data )
{
int rc = -1;
lxc_child_argv_t *argv = data;
if (NULL == vmDef) {
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("lxcChild() passed invalid vm definition"));
- goto cleanup;
+ return -1;
}
/* handle the bind mounts first before doing anything else that may */
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("failed to mount %s at %s for container: %s"),
curMount->source, curMount->target, strerror(errno));
- goto cleanup;
+ return -1;
}
}
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
_("failed to mount /proc for container: %s"),
strerror(errno));
- goto cleanup;
+ return -1;
}
+ if (lxcContainerSetStdio(argv->monitor, argv->ttyPath) < 0)
+ return -1;
+
/* Wait for interface devices to show up */
- if (0 != (rc = lxcWaitForContinue(argv->monitor))) {
- goto cleanup;
- }
+ if (lxcContainerWaitForContinue(argv->monitor) < 0)
+ return -1;
/* enable interfaces */
- if (0 != (rc = lxcEnableInterfaces(vmDef))) {
- goto cleanup;
- }
+ if (lxcContainerEnableInterfaces(vmDef) < 0)
+ return -1;
- rc = lxcExecWithTty(vmDef, argv->ttyPath);
/* this function will only return if an error occured */
+ return lxcContainerExecInit(vmDef);
+}
-cleanup:
- return rc;
+/**
+ * lxcContainerStart:
+ * @conn: pointer to connection
+ * @driver: pointer to driver structure
+ * @vm: pointer to virtual machine structure
+ *
+ * Starts a container process by calling clone() with the namespace flags
+ *
+ * Returns PID of container on success or -1 in case of error
+ */
+int lxcContainerStart(virConnectPtr conn,
+ lxc_vm_def_t *def,
+ int control,
+ char *ttyPath)
+{
+ pid_t pid;
+ int flags;
+ int stacksize = getpagesize() * 4;
+ char *stack, *stacktop;
+ lxc_child_argv_t args = { def, control, ttyPath };
+
+ /* allocate a stack for the container */
+ if (VIR_ALLOC_N(stack, stacksize) < 0) {
+ lxcError(conn, NULL, VIR_ERR_NO_MEMORY,
+ _("unable to allocate container stack"));
+ return -1;
+ }
+ stacktop = stack + stacksize;
+
+ flags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWUSER|CLONE_NEWIPC|SIGCHLD;
+
+ if (def->nets != NULL)
+ flags |= CLONE_NEWNET;
+
+ pid = clone(lxcContainerChild, stacktop, flags, &args);
+ VIR_FREE(stack);
+ DEBUG("clone() returned, %d", pid);
+
+ if (pid < 0) {
+ lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("clone() failed, %s"), strerror(errno));
+ return -1;
+ }
+
+ return pid;
+}
+
+static int lxcContainerDummyChild(void *argv ATTRIBUTE_UNUSED)
+{
+ _exit(0);
+}
+
+int lxcContainerAvailable(int features)
+{
+ int flags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWUSER|
+ CLONE_NEWIPC|SIGCHLD;
+ int cpid;
+ char *childStack;
+ char *stack;
+ int childStatus;
+
+ if (features & LXC_CONTAINER_FEATURE_NET)
+ flags |= CLONE_NEWNET;
+
+ if (VIR_ALLOC_N(stack, getpagesize() * 4) < 0) {
+ DEBUG0("Unable to allocate stack");
+ return -1;
+ }
+
+ childStack = stack + (getpagesize() * 4);
+
+ cpid = clone(lxcContainerDummyChild, childStack, flags, NULL);
+ VIR_FREE(stack);
+ if (cpid < 0) {
+ DEBUG("clone call returned %s, container support is not enabled",
+ strerror(errno));
+ return -1;
+ } else {
+ waitpid(cpid, &childStatus, 0);
+ }
+
+ return 0;
}
#endif /* WITH_LXC */
#ifdef WITH_LXC
-typedef struct __lxc_child_argv lxc_child_argv_t;
-struct __lxc_child_argv {
- lxc_vm_def_t *config;
- int monitor;
- char *ttyPath;
+enum {
+ LXC_CONTAINER_FEATURE_NET = (1 << 0),
};
-/* messages between parent and container */
-typedef char lxc_message_t;
-#define LXC_CONTINUE_MSG 'c'
+int lxcContainerSendContinue(virConnectPtr conn,
+ int control);
+int lxcContainerStart(virConnectPtr conn,
+ lxc_vm_def_t *def,
+ int control,
+ char *ttyPath);
-/* Function declarations */
-int lxcChild( void *argv );
+int lxcContainerAvailable(int features);
#endif /* LXC_DRIVER_H */
--- /dev/null
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_controller.c: linux container process controller
+ *
+ * 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 <sys/epoll.h>
+#include <unistd.h>
+
+#include "internal.h"
+#include "util.h"
+
+#include "lxc_conf.h"
+#include "lxc_controller.h"
+
+
+#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
+
+/**
+ * lxcFdForward:
+ * @readFd: file descriptor to read
+ * @writeFd: file desriptor to write
+ *
+ * Reads 1 byte of data from readFd and writes to writeFd.
+ *
+ * Returns 0 on success, EAGAIN if returned on read, or -1 in case of error
+ */
+static int lxcFdForward(int readFd, int writeFd)
+{
+ int rc = -1;
+ char buf[2];
+
+ if (1 != (saferead(readFd, buf, 1))) {
+ if (EAGAIN == errno) {
+ rc = EAGAIN;
+ goto cleanup;
+ }
+
+ lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("read of fd %d failed: %s"), readFd, strerror(errno));
+ goto cleanup;
+ }
+
+ if (1 != (safewrite(writeFd, buf, 1))) {
+ lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("write to fd %d failed: %s"), writeFd, strerror(errno));
+ goto cleanup;
+ }
+
+ rc = 0;
+
+cleanup:
+ return rc;
+}
+
+typedef struct _lxcTtyForwardFd_t {
+ int fd;
+ int active;
+} lxcTtyForwardFd_t;
+
+/**
+ * lxcTtyForward:
+ * @appPty: Open fd for application facing Pty
+ * @contPty: Open fd for container facing Pty
+ *
+ * Forwards traffic between fds. Data read from appPty will be written to contPty
+ * This process loops forever.
+ * This uses epoll in edge triggered mode to avoid a hard loop on POLLHUP
+ * events when the user disconnects the virsh console via ctrl-]
+ *
+ * Returns 0 on success or -1 in case of error
+ */
+int lxcControllerMain(int appPty, int contPty)
+{
+ int rc = -1;
+ int epollFd;
+ struct epoll_event epollEvent;
+ int numEvents;
+ int numActive = 0;
+ lxcTtyForwardFd_t fdArray[2];
+ int timeout = -1;
+ int curFdOff = 0;
+ int writeFdOff = 0;
+
+ fdArray[0].fd = appPty;
+ fdArray[0].active = 0;
+ fdArray[1].fd = contPty;
+ fdArray[1].active = 0;
+
+ /* create the epoll fild descriptor */
+ epollFd = epoll_create(2);
+ if (0 > epollFd) {
+ lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("epoll_create(2) failed: %s"), strerror(errno));
+ goto cleanup;
+ }
+
+ /* add the file descriptors the epoll fd */
+ memset(&epollEvent, 0x00, sizeof(epollEvent));
+ epollEvent.events = EPOLLIN|EPOLLET; /* edge triggered */
+ epollEvent.data.fd = appPty;
+ epollEvent.data.u32 = 0; /* fdArray position */
+ if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, appPty, &epollEvent)) {
+ lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("epoll_ctl(appPty) failed: %s"), strerror(errno));
+ goto cleanup;
+ }
+ epollEvent.data.fd = contPty;
+ epollEvent.data.u32 = 1; /* fdArray position */
+ if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, contPty, &epollEvent)) {
+ lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("epoll_ctl(contPty) failed: %s"), strerror(errno));
+ goto cleanup;
+ }
+
+ while (1) {
+ /* if active fd's, return if no events, else wait forever */
+ timeout = (numActive > 0) ? 0 : -1;
+ numEvents = epoll_wait(epollFd, &epollEvent, 1, timeout);
+ if (0 < numEvents) {
+ if (epollEvent.events & EPOLLIN) {
+ curFdOff = epollEvent.data.u32;
+ if (!fdArray[curFdOff].active) {
+ fdArray[curFdOff].active = 1;
+ ++numActive;
+ }
+
+ } else if (epollEvent.events & EPOLLHUP) {
+ DEBUG("EPOLLHUP from fd %d", epollEvent.data.fd);
+ continue;
+ } else {
+ lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("error event %d"), epollEvent.events);
+ goto cleanup;
+ }
+
+ } else if (0 == numEvents) {
+ if (2 == numActive) {
+ /* both fds active, toggle between the two */
+ curFdOff ^= 1;
+ } else {
+ /* only one active, if current is active, use it, else it */
+ /* must be the other one (ie. curFd just went inactive) */
+ curFdOff = fdArray[curFdOff].active ? curFdOff : curFdOff ^ 1;
+ }
+
+ } else {
+ if (EINTR == errno) {
+ continue;
+ }
+
+ /* error */
+ lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("epoll_wait() failed: %s"), strerror(errno));
+ goto cleanup;
+
+ }
+
+ if (0 < numActive) {
+ writeFdOff = curFdOff ^ 1;
+ rc = lxcFdForward(fdArray[curFdOff].fd, fdArray[writeFdOff].fd);
+
+ if (EAGAIN == rc) {
+ /* this fd no longer has data, set it as inactive */
+ --numActive;
+ fdArray[curFdOff].active = 0;
+ } else if (-1 == rc) {
+ goto cleanup;
+ }
+
+ }
+
+ }
+
+ rc = 0;
+
+cleanup:
+ close(appPty);
+ close(contPty);
+ close(epollFd);
+ return rc;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright IBM Corp. 2008
+ *
+ * lxc_controller.h: linux container process controller
+ *
+ * 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_CONTROLLER_H
+#define LXC_CONTROLLER_H
+
+#ifdef WITH_LXC
+
+int lxcControllerMain(int appPty, int contPty);
+
+#endif /* WITH_LXC */
+
+#endif /* LXC_CONTROLLER_H */
#ifdef WITH_LXC
#include <fcntl.h>
-#include <sys/epoll.h>
#include <sched.h>
#include <sys/utsname.h>
#include <stdbool.h>
#include "lxc_conf.h"
#include "lxc_container.h"
#include "lxc_driver.h"
+#include "lxc_controller.h"
#include "driver.h"
#include "internal.h"
#include "memory.h"
#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
-/*
- * GLibc headers are behind the kernel, so we define these
- * constants if they're not present already.
- */
-
-#ifndef CLONE_NEWPID
-#define CLONE_NEWPID 0x20000000
-#endif
-#ifndef CLONE_NEWUTS
-#define CLONE_NEWUTS 0x04000000
-#endif
-#ifndef CLONE_NEWUSER
-#define CLONE_NEWUSER 0x10000000
-#endif
-#ifndef CLONE_NEWIPC
-#define CLONE_NEWIPC 0x08000000
-#endif
-#ifndef CLONE_NEWNET
-#define CLONE_NEWNET 0x40000000 /* New network namespace */
-#endif
static int lxcStartup(void);
static int lxcShutdown(void);
static lxc_driver_t *lxc_driver = NULL;
/* Functions */
-static int lxcDummyChild( void *argv ATTRIBUTE_UNUSED )
-{
- exit(0);
-}
-
-static int lxcCheckContainerSupport(int extra_flags)
-{
- int rc = 0;
- int flags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWUSER|
- CLONE_NEWIPC|SIGCHLD|extra_flags;
- int cpid;
- char *childStack;
- char *stack;
- int childStatus;
-
- if (VIR_ALLOC_N(stack, getpagesize() * 4) < 0) {
- 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);
- }
-
- VIR_FREE(stack);
-
-check_complete:
- return rc;
-}
static const char *lxcProbe(void)
{
-#ifdef __linux__
- if (0 == lxcCheckContainerSupport(0)) {
- return("lxc:///");
- }
-#endif
- return(NULL);
+ if (lxcContainerAvailable(0) < 0)
+ return NULL;
+
+ return("lxc:///");
}
static virDrvOpenStatus lxcOpen(virConnectPtr conn,
return 0;
}
-/**
- * lxcSendContainerContinue:
- * @monitor: FD for communicating with child
- *
- * Sends the continue message via the socket pair stored in the vm
- * structure.
- *
- * Returns 0 on success or -1 in case of error
- */
-static int lxcSendContainerContinue(virConnectPtr conn,
- int monitor)
-{
- int rc = -1;
- lxc_message_t msg = LXC_CONTINUE_MSG;
- int writeCount = 0;
-
- writeCount = safewrite(monitor, &msg, sizeof(msg));
- if (writeCount != sizeof(msg)) {
- lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
- _("unable to send container continue message: %s"),
- strerror(errno));
- goto error_out;
- }
-
- rc = 0;
-
-error_out:
- return rc;
-}
-
-/**
- * lxcStartContainer:
- * @conn: pointer to connection
- * @driver: pointer to driver structure
- * @vm: pointer to virtual machine structure
- *
- * Starts a container process by calling clone() with the namespace flags
- *
- * Returns 0 on success or -1 in case of error
- */
-static int lxcStartContainer(virConnectPtr conn,
- lxc_driver_t* driver,
- lxc_vm_t *vm,
- int monitor,
- char *ttyPath)
-{
- int rc = -1;
- int flags;
- int stacksize = getpagesize() * 4;
- char *stack, *stacktop;
- lxc_child_argv_t args = { vm->def, monitor, ttyPath };
-
- /* allocate a stack for the container */
- if (VIR_ALLOC_N(stack, stacksize) < 0) {
- lxcError(conn, NULL, VIR_ERR_NO_MEMORY,
- _("unable to allocate container stack"));
- goto error_exit;
- }
- stacktop = stack + stacksize;
-
- flags = CLONE_NEWPID|CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWUSER|CLONE_NEWIPC|SIGCHLD;
-
- if (vm->def->nets != NULL)
- flags |= CLONE_NEWNET;
-
- vm->def->id = clone(lxcChild, stacktop, flags, &args);
-
- DEBUG("clone() returned, %d", vm->def->id);
-
- if (vm->def->id < 0) {
- lxcError(conn, NULL, VIR_ERR_INTERNAL_ERROR,
- _("clone() failed, %s"), strerror(errno));
- goto error_exit;
- }
-
- lxcSaveConfig(NULL, driver, vm, vm->def);
-
- rc = 0;
-
-error_exit:
- return rc;
-}
-
/**
* lxcOpenTty:
return rc;
}
-/**
- * lxcFdForward:
- * @readFd: file descriptor to read
- * @writeFd: file desriptor to write
- *
- * Reads 1 byte of data from readFd and writes to writeFd.
- *
- * Returns 0 on success, EAGAIN if returned on read, or -1 in case of error
- */
-static int lxcFdForward(int readFd, int writeFd)
-{
- int rc = -1;
- char buf[2];
-
- if (1 != (saferead(readFd, buf, 1))) {
- if (EAGAIN == errno) {
- rc = EAGAIN;
- goto cleanup;
- }
-
- lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("read of fd %d failed: %s"), readFd, strerror(errno));
- goto cleanup;
- }
-
- if (1 != (safewrite(writeFd, buf, 1))) {
- lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("write to fd %d failed: %s"), writeFd, strerror(errno));
- goto cleanup;
- }
-
- rc = 0;
-
-cleanup:
- return rc;
-}
-
-typedef struct _lxcTtyForwardFd_t {
- int fd;
- bool active;
-} lxcTtyForwardFd_t;
-
-/**
- * lxcTtyForward:
- * @fd1: Open fd
- * @fd1: Open fd
- *
- * Forwards traffic between fds. Data read from fd1 will be written to fd2
- * This process loops forever.
- * This uses epoll in edge triggered mode to avoid a hard loop on POLLHUP
- * events when the user disconnects the virsh console via ctrl-]
- *
- * Returns 0 on success or -1 in case of error
- */
-static int lxcTtyForward(int fd1, int fd2)
-{
- int rc = -1;
- int epollFd;
- struct epoll_event epollEvent;
- int numEvents;
- int numActive = 0;
- lxcTtyForwardFd_t fdArray[2];
- int timeout = -1;
- int curFdOff = 0;
- int writeFdOff = 0;
-
- fdArray[0].fd = fd1;
- fdArray[0].active = false;
- fdArray[1].fd = fd2;
- fdArray[1].active = false;
-
- /* create the epoll fild descriptor */
- epollFd = epoll_create(2);
- if (0 > epollFd) {
- lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("epoll_create(2) failed: %s"), strerror(errno));
- goto cleanup;
- }
-
- /* add the file descriptors the epoll fd */
- memset(&epollEvent, 0x00, sizeof(epollEvent));
- epollEvent.events = EPOLLIN|EPOLLET; /* edge triggered */
- epollEvent.data.fd = fd1;
- epollEvent.data.u32 = 0; /* fdArray position */
- if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, fd1, &epollEvent)) {
- lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("epoll_ctl(fd1) failed: %s"), strerror(errno));
- goto cleanup;
- }
- epollEvent.data.fd = fd2;
- epollEvent.data.u32 = 1; /* fdArray position */
- if (0 > epoll_ctl(epollFd, EPOLL_CTL_ADD, fd2, &epollEvent)) {
- lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("epoll_ctl(fd2) failed: %s"), strerror(errno));
- goto cleanup;
- }
-
- while (1) {
- /* if active fd's, return if no events, else wait forever */
- timeout = (numActive > 0) ? 0 : -1;
- numEvents = epoll_wait(epollFd, &epollEvent, 1, timeout);
- if (0 < numEvents) {
- if (epollEvent.events & EPOLLIN) {
- curFdOff = epollEvent.data.u32;
- if (!fdArray[curFdOff].active) {
- fdArray[curFdOff].active = true;
- ++numActive;
- }
-
- } else if (epollEvent.events & EPOLLHUP) {
- DEBUG("EPOLLHUP from fd %d", epollEvent.data.fd);
- continue;
- } else {
- lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("error event %d"), epollEvent.events);
- goto cleanup;
- }
-
- } else if (0 == numEvents) {
- if (2 == numActive) {
- /* both fds active, toggle between the two */
- curFdOff ^= 1;
- } else {
- /* only one active, if current is active, use it, else it */
- /* must be the other one (ie. curFd just went inactive) */
- curFdOff = fdArray[curFdOff].active ? curFdOff : curFdOff ^ 1;
- }
-
- } else {
- if (EINTR == errno) {
- continue;
- }
-
- /* error */
- lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("epoll_wait() failed: %s"), strerror(errno));
- goto cleanup;
-
- }
-
- if (0 < numActive) {
- writeFdOff = curFdOff ^ 1;
- rc = lxcFdForward(fdArray[curFdOff].fd, fdArray[writeFdOff].fd);
-
- if (EAGAIN == rc) {
- /* this fd no longer has data, set it as inactive */
- --numActive;
- fdArray[curFdOff].active = false;
- } else if (-1 == rc) {
- goto cleanup;
- }
-
- }
-
- }
-
- rc = 0;
-
-cleanup:
- close(fd1);
- close(fd2);
- close(epollFd);
- exit(rc);
-}
/**
* lxcVmStart:
if (vm->pid == 0) {
/* child process calls forward routine */
- lxcTtyForward(parentTty, containerTty);
+ lxcControllerMain(parentTty, containerTty);
}
if (lxcStoreTtyPid(driver, vm)) {
/* check this rc */
- rc = lxcStartContainer(conn, driver, vm,
- sockpair[1],
- containerTtyPath);
- if (rc != 0)
+ vm->def->id = lxcContainerStart(conn,
+ vm->def,
+ sockpair[1],
+ containerTtyPath);
+ if (vm->def->id == -1)
goto cleanup;
+ lxcSaveConfig(conn, driver, vm, vm->def);
rc = lxcMoveInterfacesToNetNs(conn, vm);
if (rc != 0)
goto cleanup;
- rc = lxcSendContainerContinue(conn, sockpair[0]);
+ rc = lxcContainerSendContinue(conn, sockpair[0]);
if (rc != 0)
goto cleanup;
{
const char *argv[] = {"ip", "link", "set", "lo", "netns", "-1", NULL};
int ip_rc;
- int user_netns = 0;
- int kern_netns = 0;
- if (virRun(NULL, argv, &ip_rc) == 0)
- user_netns = WIFEXITED(ip_rc) && (WEXITSTATUS(ip_rc) != 255);
+ if (virRun(NULL, argv, &ip_rc) < 0 ||
+ !(WIFEXITED(ip_rc) && (WEXITSTATUS(ip_rc) != 255)))
+ return 0;
- if (lxcCheckContainerSupport(CLONE_NEWNET) == 0)
- kern_netns = 1;
+ if (lxcContainerAvailable(LXC_CONTAINER_FEATURE_NET) < 0)
+ return 0;
- return kern_netns && user_netns;
+ return 1;
}
static int lxcStartup(void)
}
/* Check that this is a container enabled kernel */
- if(0 != lxcCheckContainerSupport(0)) {
+ if(lxcContainerAvailable(0) < 0)
return -1;
- }
lxc_driver->have_netns = lxcCheckNetNsSupport();