2008-04-10 07:30:52 +00:00
|
|
|
/*
|
|
|
|
* Copyright IBM Corp. 2008
|
|
|
|
*
|
|
|
|
* lxc_container.c: file description
|
|
|
|
*
|
|
|
|
* 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 <fcntl.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/mount.h>
|
2008-08-13 10:25:34 +00:00
|
|
|
#include <sys/wait.h>
|
2008-04-10 07:30:52 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "lxc_container.h"
|
|
|
|
#include "util.h"
|
2008-06-06 11:09:57 +00:00
|
|
|
#include "memory.h"
|
2008-06-26 16:09:48 +00:00
|
|
|
#include "veth.h"
|
2008-04-10 07:30:52 +00:00
|
|
|
|
|
|
|
#define DEBUG(fmt,...) VIR_DEBUG(__FILE__, fmt, __VA_ARGS__)
|
|
|
|
#define DEBUG0(msg) VIR_DEBUG(__FILE__, "%s", msg)
|
|
|
|
|
2008-08-13 10:25:34 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2008-04-10 07:30:52 +00:00
|
|
|
/**
|
2008-08-13 10:25:34 +00:00
|
|
|
* lxcContainerExecInit:
|
2008-04-10 07:30:52 +00:00
|
|
|
* @vmDef: Ptr to vm definition structure
|
|
|
|
*
|
2008-08-13 10:25:34 +00:00
|
|
|
* Exec the container init string. The container init will replace then
|
2008-04-10 07:30:52 +00:00
|
|
|
* be running in the current process
|
|
|
|
*
|
2008-08-13 10:25:34 +00:00
|
|
|
* Does not return
|
2008-04-10 07:30:52 +00:00
|
|
|
*/
|
2008-08-13 10:25:34 +00:00
|
|
|
static int lxcContainerExecInit(const lxc_vm_def_t *vmDef)
|
2008-04-10 07:30:52 +00:00
|
|
|
{
|
2008-08-13 10:25:34 +00:00
|
|
|
const char *const argv[] = {
|
|
|
|
vmDef->init,
|
|
|
|
NULL,
|
|
|
|
};
|
2008-04-10 07:30:52 +00:00
|
|
|
|
2008-08-13 10:25:34 +00:00
|
|
|
return execve(argv[0], (char **)argv, NULL);
|
2008-04-10 07:30:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-08-13 10:25:34 +00:00
|
|
|
* lxcContainerSetStdio:
|
|
|
|
* @control: the conrol FD
|
|
|
|
* @ttyPath: Name of tty to set as the container console
|
2008-04-10 07:30:52 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
2008-08-13 10:25:34 +00:00
|
|
|
static int lxcContainerSetStdio(int control, const char *ttyPath)
|
2008-04-10 07:30:52 +00:00
|
|
|
{
|
|
|
|
int rc = -1;
|
|
|
|
int ttyfd;
|
2008-08-13 10:14:47 +00:00
|
|
|
int open_max, i;
|
2008-04-10 07:30:52 +00:00
|
|
|
|
|
|
|
if (setsid() < 0) {
|
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("setsid failed: %s"), strerror(errno));
|
|
|
|
goto error_out;
|
|
|
|
}
|
|
|
|
|
2008-08-13 10:14:47 +00:00
|
|
|
ttyfd = open(ttyPath, O_RDWR|O_NOCTTY);
|
2008-04-10 07:30:52 +00:00
|
|
|
if (ttyfd < 0) {
|
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
2008-08-13 10:14:47 +00:00
|
|
|
_("open(%s) failed: %s"), ttyPath, strerror(errno));
|
2008-04-10 07:30:52 +00:00
|
|
|
goto error_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(ttyfd, TIOCSCTTY, NULL) < 0) {
|
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("ioctl(TIOCSTTY) failed: %s"), strerror(errno));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2008-08-13 10:14:47 +00:00
|
|
|
/* Just in case someone forget to set FD_CLOEXEC, explicitly
|
|
|
|
* close all FDs before executing the container */
|
|
|
|
open_max = sysconf (_SC_OPEN_MAX);
|
|
|
|
for (i = 0; i < open_max; i++)
|
2008-08-13 10:25:34 +00:00
|
|
|
if (i != ttyfd && i != control)
|
2008-08-13 10:14:47 +00:00
|
|
|
close(i);
|
2008-04-10 07:30:52 +00:00
|
|
|
|
|
|
|
if (dup2(ttyfd, 0) < 0) {
|
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("dup2(stdin) failed: %s"), strerror(errno));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dup2(ttyfd, 1) < 0) {
|
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("dup2(stdout) failed: %s"), strerror(errno));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dup2(ttyfd, 2) < 0) {
|
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("dup2(stderr) failed: %s"), strerror(errno));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
close(ttyfd);
|
|
|
|
|
|
|
|
error_out:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2008-08-13 10:25:34 +00:00
|
|
|
* lxcContainerSendContinue:
|
|
|
|
* @monitor: control FD to child
|
2008-04-10 07:30:52 +00:00
|
|
|
*
|
2008-08-13 10:25:34 +00:00
|
|
|
* Sends the continue message via the socket pair stored in the vm
|
|
|
|
* structure.
|
2008-04-10 07:30:52 +00:00
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of error
|
|
|
|
*/
|
2008-08-13 10:25:34 +00:00
|
|
|
int lxcContainerSendContinue(virConnectPtr conn,
|
|
|
|
int control)
|
2008-04-10 07:30:52 +00:00
|
|
|
{
|
|
|
|
int rc = -1;
|
2008-08-13 10:25:34 +00:00
|
|
|
lxc_message_t msg = LXC_CONTINUE_MSG;
|
|
|
|
int writeCount = 0;
|
2008-04-10 07:30:52 +00:00
|
|
|
|
2008-08-13 10:25:34 +00:00
|
|
|
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;
|
2008-04-10 07:30:52 +00:00
|
|
|
}
|
|
|
|
|
2008-08-13 10:25:34 +00:00
|
|
|
rc = 0;
|
2008-04-10 07:30:52 +00:00
|
|
|
|
2008-08-13 10:25:34 +00:00
|
|
|
error_out:
|
|
|
|
return rc;
|
2008-04-10 07:30:52 +00:00
|
|
|
}
|
|
|
|
|
2008-06-26 16:09:48 +00:00
|
|
|
/**
|
2008-08-13 10:25:34 +00:00
|
|
|
* lxcContainerWaitForContinue:
|
|
|
|
* @control: control FD from parent
|
2008-06-26 16:09:48 +00:00
|
|
|
*
|
|
|
|
* This function will wait for the container continue message from the
|
|
|
|
* parent process. It will send this message on the socket pair stored in
|
|
|
|
* the vm structure once it has completed the post clone container setup.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of error
|
|
|
|
*/
|
2008-08-13 10:25:34 +00:00
|
|
|
static int lxcContainerWaitForContinue(int control)
|
2008-06-26 16:09:48 +00:00
|
|
|
{
|
|
|
|
lxc_message_t msg;
|
|
|
|
int readLen;
|
|
|
|
|
2008-08-13 10:25:34 +00:00
|
|
|
readLen = saferead(control, &msg, sizeof(msg));
|
2008-08-13 10:14:47 +00:00
|
|
|
if (readLen != sizeof(msg) ||
|
|
|
|
msg != LXC_CONTINUE_MSG) {
|
2008-06-26 16:09:48 +00:00
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Failed to read the container continue message: %s"),
|
|
|
|
strerror(errno));
|
2008-08-13 10:14:47 +00:00
|
|
|
return -1;
|
2008-06-26 16:09:48 +00:00
|
|
|
}
|
2008-08-13 10:25:34 +00:00
|
|
|
close(control);
|
2008-06-26 16:09:48 +00:00
|
|
|
|
|
|
|
DEBUG0("Received container continue message");
|
|
|
|
|
2008-08-13 10:14:47 +00:00
|
|
|
return 0;
|
2008-06-26 16:09:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* lxcEnableInterfaces:
|
|
|
|
* @vm: Pointer to vm structure
|
|
|
|
*
|
|
|
|
* This function will enable the interfaces for this container.
|
|
|
|
*
|
|
|
|
* Returns 0 on success or nonzero in case of error
|
|
|
|
*/
|
2008-08-13 10:25:34 +00:00
|
|
|
static int lxcContainerEnableInterfaces(const lxc_vm_def_t *def)
|
2008-06-26 16:09:48 +00:00
|
|
|
{
|
|
|
|
int rc = 0;
|
|
|
|
const lxc_net_def_t *net;
|
|
|
|
|
2008-08-13 10:14:47 +00:00
|
|
|
for (net = def->nets; net; net = net->next) {
|
2008-06-26 16:09:48 +00:00
|
|
|
DEBUG("Enabling %s", net->containerVeth);
|
|
|
|
rc = vethInterfaceUpOrDown(net->containerVeth, 1);
|
|
|
|
if (0 != rc) {
|
|
|
|
goto error_out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* enable lo device only if there were other net devices */
|
2008-08-13 10:14:47 +00:00
|
|
|
if (def->nets)
|
2008-06-26 16:09:48 +00:00
|
|
|
rc = vethInterfaceUpOrDown("lo", 1);
|
|
|
|
|
|
|
|
error_out:
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2008-04-10 07:30:52 +00:00
|
|
|
/**
|
|
|
|
* lxcChild:
|
|
|
|
* @argv: Pointer to container arguments
|
|
|
|
*
|
|
|
|
* This function is run in the process clone()'d in lxcStartContainer.
|
|
|
|
* Perform a number of container setup tasks:
|
|
|
|
* Setup container file system
|
|
|
|
* mount container /proca
|
|
|
|
* Then exec's the container init
|
|
|
|
*
|
|
|
|
* Returns 0 on success or -1 in case of error
|
|
|
|
*/
|
2008-08-13 10:25:34 +00:00
|
|
|
static int lxcContainerChild( void *data )
|
2008-04-10 07:30:52 +00:00
|
|
|
{
|
|
|
|
int rc = -1;
|
2008-08-13 10:14:47 +00:00
|
|
|
lxc_child_argv_t *argv = data;
|
|
|
|
lxc_vm_def_t *vmDef = argv->config;
|
2008-04-10 07:30:52 +00:00
|
|
|
lxc_mount_t *curMount;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (NULL == vmDef) {
|
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("lxcChild() passed invalid vm definition"));
|
2008-08-13 10:25:34 +00:00
|
|
|
return -1;
|
2008-04-10 07:30:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* handle the bind mounts first before doing anything else that may */
|
|
|
|
/* then access those mounted dirs */
|
|
|
|
curMount = vmDef->mounts;
|
|
|
|
for (i = 0; curMount; curMount = curMount->next) {
|
|
|
|
rc = mount(curMount->source,
|
|
|
|
curMount->target,
|
|
|
|
NULL,
|
|
|
|
MS_BIND,
|
|
|
|
NULL);
|
|
|
|
if (0 != rc) {
|
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("failed to mount %s at %s for container: %s"),
|
|
|
|
curMount->source, curMount->target, strerror(errno));
|
2008-08-13 10:25:34 +00:00
|
|
|
return -1;
|
2008-04-10 07:30:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mount /proc */
|
|
|
|
rc = mount("lxcproc", "/proc", "proc", 0, NULL);
|
|
|
|
if (0 != rc) {
|
|
|
|
lxcError(NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("failed to mount /proc for container: %s"),
|
|
|
|
strerror(errno));
|
2008-08-13 10:25:34 +00:00
|
|
|
return -1;
|
2008-04-10 07:30:52 +00:00
|
|
|
}
|
|
|
|
|
2008-08-13 10:25:34 +00:00
|
|
|
if (lxcContainerSetStdio(argv->monitor, argv->ttyPath) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2008-06-26 16:09:48 +00:00
|
|
|
/* Wait for interface devices to show up */
|
2008-08-13 10:25:34 +00:00
|
|
|
if (lxcContainerWaitForContinue(argv->monitor) < 0)
|
|
|
|
return -1;
|
2008-06-26 16:09:48 +00:00
|
|
|
|
|
|
|
/* enable interfaces */
|
2008-08-13 10:25:34 +00:00
|
|
|
if (lxcContainerEnableInterfaces(vmDef) < 0)
|
|
|
|
return -1;
|
2008-06-26 16:09:48 +00:00
|
|
|
|
2008-04-10 07:30:52 +00:00
|
|
|
/* this function will only return if an error occured */
|
2008-08-13 10:25:34 +00:00
|
|
|
return lxcContainerExecInit(vmDef);
|
|
|
|
}
|
2008-04-10 07:30:52 +00:00
|
|
|
|
2008-08-13 10:25:34 +00:00
|
|
|
/**
|
|
|
|
* 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;
|
2008-04-10 07:30:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* WITH_LXC */
|