Commit 05f05512 by dlezcano

Replace lxc_execute by an intermediate lxc_init

From: Daniel Lezcano <dlezcano@fr.ibm.com> The main difference between lxc_start and lxc_execute is the latter creates an intermediate process to wait for all the childs. That allows to support daemons or orphan process group for the pid namespace. Having such difference makes the code to be duplicate between the two functions. So instead of doing this, I create an intermediate <init> program which is in charge to launch the specified command. This command is the lxc-init program taking different options: --mount-procfs : mount the proc filesystem before exec'ing the command --mount-sysfs : mount the sys filesystem before exec'ing the command A double dash indicates the end of the options of lxc-init and the beginning of the command to be launched. To summarize: * lxc_execute function is no more. * lxc-execute command uses the lxc_start function and launch the specified command via lxc-init Signed-off-by: 's avatarDaniel Lezcano <dlezcano@fr.ibm.com>
parent 683c720b
INCLUDES= -I$(top_srcdir)/src -DLXCPATH="\"$(localstatedir)/lxc\""
INCLUDES= -I$(top_srcdir)/src -DLXCPATH="\"$(localstatedir)/lxc\"" -DLXCBINDIR="\"$(bindir)\""
lib_LTLIBRARIES = liblxc.la
pkginclude_HEADERS = \
monitor.h \
......@@ -17,7 +17,6 @@ liblxc_la_SOURCES = \
destroy.c \
start.c \
stop.c \
execute.c \
monitor.c monitor.h \
kill.c \
freezer.c \
......@@ -46,6 +45,7 @@ bin_SCRIPTS = \
lxc-checkconfig
bin_PROGRAMS = \
lxc-init \
lxc-create \
lxc-destroy \
lxc-stop \
......@@ -63,6 +63,9 @@ bin_PROGRAMS = \
lxc-restart \
lxc-version
lxc_init_SOURCES = lxc_init.c
lxc_init_LDADD = liblxc.la
lxc_create_SOURCES = lxc_create.c lxc_config.c lxc_config.h
lxc_create_LDADD = liblxc.la
......
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <dlezcano at fr.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
*/
#define _GNU_SOURCE
#include <stdio.h>
#undef _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <sys/mount.h>
#include <lxc/lxc.h>
LXC_TTY_HANDLER(SIGINT);
LXC_TTY_HANDLER(SIGQUIT);
int lxc_execute(const char *name, int argc, char *argv[],
lxc_callback_t preexec, void *data)
{
char init[MAXPATHLEN];
char *val = NULL, *vinit = "[vinit]";
int fd, lock, sv[2], sync = 0, err = -1;
pid_t pid;
int clone_flags;
lock = lxc_get_lock(name);
if (!lock) {
lxc_log_error("'%s' is busy", name);
return -1;
}
if (lock < 0) {
lxc_log_error("failed to acquire lock on '%s':%s",
name, strerror(-lock));
return -1;
}
if (lxc_setstate(name, STARTING)) {
lxc_log_error("failed to set state %s", lxc_state2str(STARTING));
goto out;
}
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sv)) {
lxc_log_syserror("failed to create communication socketpair");
goto err;
}
LXC_TTY_ADD_HANDLER(SIGINT);
LXC_TTY_ADD_HANDLER(SIGQUIT);
clone_flags = CLONE_NEWPID|CLONE_NEWIPC|CLONE_NEWNS;
if (conf_has_utsname(name))
clone_flags |= CLONE_NEWUTS;
if (conf_has_network(name))
clone_flags |= CLONE_NEWNET;
pid = fork_ns(clone_flags);
if (pid < 0) {
lxc_log_syserror("failed to fork into a new namespace");
goto err_fork_ns;
}
if (!pid) {
pid = fork();
if (pid < 0) {
lxc_log_syserror("failed to fork");
return 1;
}
if (!pid) {
close(sv[1]);
fcntl(sv[0], F_SETFD, FD_CLOEXEC);
if (write(sv[0], &sync, sizeof(sync)) < 0) {
lxc_log_syserror("failed to write socket");
exit(1);
}
if (read(sv[0], &sync, sizeof(sync)) < 0) {
lxc_log_syserror("failed to read socket");
exit(1);
}
if (lxc_setup(name)) {
lxc_log_error("failed to setup the container");
goto error;
}
if (mount("proc", "/proc", "proc", 0, NULL)) {
lxc_log_syserror("failed to mount '/proc'");
goto error;
}
if (clone_flags & CLONE_NEWNET)
if (mount("sysfs", "/sys", "sysfs", 0, NULL)) {
lxc_log_syserror("failed to mount '/sys'");
/* continue: non fatal error until sysfs not per
namespace */
}
if (preexec)
if (preexec(name, argc, argv, data)) {
lxc_log_error("preexec callback has failed");
return -1;
}
execvp(argv[0], argv);
lxc_log_syserror("failed to exec %s", argv[0]);
error:
if (write(sv[0], &sync, sizeof(sync)) < 0)
lxc_log_syserror("failed to write the socket");
exit(1);
}
setsid();
close(0);
close(1);
close(2);
if (prctl(PR_SET_NAME, vinit, 0, 0, 0))
lxc_log_syserror("failed to set process name");
close(sv[0]);
close(sv[1]);
for (;;) {
int status;
if (wait(&status) < 0) {
if (errno == ECHILD)
exit(0);
if (errno == EINTR)
continue;
lxc_log_syserror("failed to wait child");
exit(1);
}
}
}
close(sv[0]);
if (read(sv[1], &sync, sizeof(sync)) < 0) {
lxc_log_syserror("failed to read the socket");
goto err_pipe_read;
}
if (lxc_link_nsgroup(name, pid))
lxc_log_warning("cgroupfs not found: cgroup disabled");
if (clone_flags & CLONE_NEWNET && conf_create_network(name, pid)) {
lxc_log_error("failed to create the configured network");
goto err_create_network;
}
if (write(sv[1], &sync, sizeof(sync)) < 0) {
lxc_log_syserror("failed to write the socket");
goto err_pipe_write;
}
err = read(sv[1], &sync, sizeof(sync));
if (err < 0) {
lxc_log_error("failed to read the socket");
goto err_pipe_read2;
}
if (err > 0) {
lxc_log_error("something went wrong with %d", pid);
/* TODO : check status etc ... */
waitpid(pid, NULL, 0);
goto err_child_failed;
}
snprintf(init, MAXPATHLEN, LXCPATH "/%s/init", name);
fd = open(init, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
if (fd < 0) {
lxc_log_syserror("failed to open %s", init);
goto err_open;
}
if (!asprintf(&val, "%d", pid)) {
lxc_log_syserror("failed to allocate memory");
goto err_open;
}
if (write(fd, val, strlen(val)) < 0) {
lxc_log_syserror("failed to write init pid");
goto err_write;
}
if (lxc_setstate(name, RUNNING)) {
lxc_log_error("failed to set state to %s", lxc_state2str(RUNNING));
goto err_state_failed;
}
wait_again:
if (waitpid(pid, NULL, 0) < 0) {
if (errno == EINTR)
goto wait_again;
lxc_log_syserror("failed to wait the pid %d", pid);
goto err_waitpid_failed;
}
if (lxc_setstate(name, STOPPING))
lxc_log_error("failed to set state %s", lxc_state2str(STOPPING));
if (clone_flags & CLONE_NEWNET && conf_destroy_network(name))
lxc_log_error("failed to destroy the network");
err = 0;
out:
if (lxc_setstate(name, STOPPED))
lxc_log_error("failed to set state %s", lxc_state2str(STOPPED));
lxc_unlink_nsgroup(name);
unlink(init);
free(val);
lxc_put_lock(lock);
return err;
err_write:
close(fd);
err_state_failed:
err_child_failed:
err_pipe_read2:
err_pipe_write:
if (clone_flags & CLONE_NEWNET)
conf_destroy_network(name);
err_create_network:
err_pipe_read:
err_open:
err_waitpid_failed:
if (lxc_setstate(name, ABORTING))
lxc_log_error("failed to set state %s", lxc_state2str(STOPPED));
kill(pid, SIGKILL);
err_fork_ns:
LXC_TTY_DEL_HANDLER(SIGQUIT);
LXC_TTY_DEL_HANDLER(SIGINT);
close(sv[0]);
close(sv[1]);
err:
goto out;
}
......@@ -48,9 +48,6 @@ extern "C" {
struct lxc_mem_stat;
typedef int (*lxc_callback_t)(const char *name, int argc,
char *argv[], void *data);
/*
* Create the container object. Creates the /lxc/<name> directory
* and fills it with the files corresponding to the configuration
......@@ -74,28 +71,10 @@ extern int lxc_destroy(const char *name);
/*
* Start the container previously created with lxc_create.
* @name : the name of the container
* @argc : the number of arguments of the command line
* @argv : an array of char * corresponding to the commande line
* @prestart : hooks will be called just before the command execs
* Returns 0 on sucess, < 0 otherwise
*/
extern int lxc_start(const char *name, int argc, char *argv[],
lxc_callback_t prestart, void *data);
/*
* Create the container and start it directly, using the argc, argv
* parameter. This command is for application container.
* At the end of the exec'ed command, the container will
* automatically autodestroy.
* @name : the name of the container
* @conf : the configuration data
* @argc : the number of arguments of the command line
* @argv : an array of char * corresponding to the commande line
* @preexec : hooks will be called just before the command execs
* Returns 0 on success, < 0 otherwise
*/
extern int lxc_execute(const char *name, int argc, char *argv[],
lxc_callback_t preexec, void *data);
extern int lxc_start(const char *name, char *argv[]);
/*
* Stop the container previously started with lxc_start or lxc_exec
......
......@@ -20,6 +20,7 @@
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <libgen.h>
......@@ -44,7 +45,7 @@ int main(int argc, char *argv[])
{
char opt;
char *name = NULL, *file = NULL;
char **args;
static char **args;
char path[MAXPATHLEN];
int nbargs = 0;
int autodestroy = 0;
......@@ -67,7 +68,6 @@ int main(int argc, char *argv[])
if (!name || !argv[optind] || !strlen(argv[optind]))
usage(argv[0]);
args = &argv[optind];
argc -= nbargs;
if (lxc_config_init(&lxc_conf)) {
......@@ -75,13 +75,9 @@ int main(int argc, char *argv[])
goto out;
}
if (file) {
if (lxc_config_read(file, &lxc_conf)) {
fprintf(stderr, "invalid configuration file\n");
goto out;
}
if (file && lxc_config_read(file, &lxc_conf)) {
fprintf(stderr, "invalid configuration file\n");
goto out;
}
snprintf(path, MAXPATHLEN, LXCPATH "/%s", name);
......@@ -93,13 +89,27 @@ int main(int argc, char *argv[])
autodestroy = 1;
}
if (lxc_execute(name, argc, args, NULL, NULL)) {
/* lxc-init --mount-procfs -- .... */
args = malloc((argc + 3)*sizeof(*args));
if (!args) {
fprintf(stderr, "failed to allocate memory for '%s'\n", name);
goto out;
}
nbargs = 0;
args[nbargs++] = LXCBINDIR "/lxc-init";
args[nbargs++] = "--mount-procfs";
args[nbargs++] = "--";
for (opt = 0; opt < argc; opt++)
args[nbargs++] = argv[optind++];
if (lxc_start(name, args)) {
fprintf(stderr, "failed to execute '%s'\n", name);
goto out;
}
ret = 0;
out:
if (autodestroy) {
if (lxc_destroy(name)) {
......
/*
* lxc: linux Container library
*
* (C) Copyright IBM Corp. 2007, 2008
*
* Authors:
* Daniel Lezcano <dlezcano at fr.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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mount.h>
#define _GNU_SOURCE
#include <getopt.h>
static int mount_sysfs;
static int mount_procfs;
static struct option options[] = {
{ "mount-sysfs", no_argument, &mount_sysfs, 1 },
{ "mount-procfs", no_argument, &mount_procfs, 1 },
};
int main(int argc, char *argv[])
{
pid_t pid;
int nbargs = 0;
char **aargv;
while (1) {
int ret = getopt_long_only(argc, argv, "", options, NULL);
if (ret == -1)
break;
if (ret == '?')
exit(1);
nbargs++;
}
if (!argv[optind]) {
fprintf(stderr, "missing command to launch\n");
exit(1);
}
aargv = &argv[optind];
argc -= nbargs;
pid = fork();
if (pid < 0)
exit(1);
if (!pid) {
if (mount_sysfs && mount("sysfs", "/sys", "sysfs", 0, NULL)) {
fprintf(stderr, "failed to mount '/sys'\n");
exit(1);
}
if (mount_procfs && mount("proc", "/proc", "proc", 0, NULL)) {
fprintf(stderr, "failed to mount '/proc'\n");
exit(1);
}
execvp(aargv[0], aargv);
fprintf(stderr, "failed to exec: %s\n", aargv[0]);
exit(1);
}
for (;;) {
int status;
if (wait(&status) < 0) {
if (errno == ECHILD)
exit(0);
if (errno == EINTR)
continue;
fprintf(stderr, "failed to wait child\n");
return 1;
}
}
return 0;
}
......@@ -64,7 +64,7 @@ int main(int argc, char *argv[])
args = &argv[optind];
argc -= nbargs;
if (lxc_start(name, argc, args, NULL, NULL)) {
if (lxc_start(name, args)) {
fprintf(stderr, "failed to start %s\n", name);
return 1;
}
......
......@@ -43,8 +43,7 @@
LXC_TTY_HANDLER(SIGINT);
LXC_TTY_HANDLER(SIGQUIT);
int lxc_start(const char *name, int argc, char *argv[],
lxc_callback_t prestart, void *data)
int lxc_start(const char *name, char *argv[])
{
char init[MAXPATHLEN];
char *val = NULL;
......@@ -132,13 +131,6 @@ int lxc_start(const char *name, int argc, char *argv[],
goto out_child;
}
/* If a callback has been passed, call it before doing exec */
if (prestart)
if (prestart(name, argc, argv, data)) {
lxc_log_error("prestart callback has failed");
goto out_child;
}
if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0)) {
lxc_log_syserror("failed to remove CAP_SYS_BOOT capability");
goto out_child;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment