mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-06-14 10:35:34 +00:00
Merge pull request #92 from majek/share-namespaces
Add options to `lxc-start` to inherit network, ipc and uts namespaces
This commit is contained in:
commit
7faa7f5200
@ -57,6 +57,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
<arg choice="opt">-p <replaceable>pid_file</replaceable></arg>
|
||||
<arg choice="opt">-s KEY=VAL</arg>
|
||||
<arg choice="opt">-C</arg>
|
||||
<arg choice="opt">--share-[net|ipc|uts] <replaceable>name|pid</replaceable></arg>
|
||||
<arg choice="opt">command</arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
@ -186,6 +187,50 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<option>--share-net <replaceable>name|pid</replaceable></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Inherit a network namespace from
|
||||
a <replaceable>name</replaceable> container or
|
||||
a <replaceable>pid</replaceable>. The network namespace
|
||||
will continue to be managed by the original owner. The
|
||||
network configuration of the starting container is ignored
|
||||
and the up/down scripts won't be executed.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<option>--share-ipc <replaceable>name|pid</replaceable></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Inherit an IPC namespace from
|
||||
a <replaceable>name</replaceable> container or
|
||||
a <replaceable>pid</replaceable>.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<option>--share-uts <replaceable>name|pid</replaceable></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Inherit a UTS namespace from
|
||||
a <replaceable>name</replaceable> container or
|
||||
a <replaceable>pid</replaceable>. The starting LXC will
|
||||
not set the hostname, but the container OS may do it
|
||||
anyway.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
@ -53,6 +53,9 @@ struct lxc_arguments {
|
||||
/* set to 0 to accept only 1 lxcpath, -1 for unlimited */
|
||||
int lxcpath_additional;
|
||||
|
||||
/* for lxc-start */
|
||||
const char *share_ns[32]; // size must be greater than LXC_NS_MAX
|
||||
|
||||
/* for lxc-checkpoint/restart */
|
||||
const char *statefile;
|
||||
int statefd;
|
||||
|
@ -2630,6 +2630,9 @@ struct lxc_conf *lxc_conf_init(void)
|
||||
new->lsm_se_context = NULL;
|
||||
new->lsm_umount_proc = 0;
|
||||
|
||||
for (i = 0; i < LXC_NS_MAX; i++)
|
||||
new->inherit_ns_fd[i] = -1;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
@ -3461,9 +3464,11 @@ int check_autodev( const char *rootfs, void *data )
|
||||
|
||||
int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath, struct cgroup_process_info *cgroup_info, void *data)
|
||||
{
|
||||
if (setup_utsname(lxc_conf->utsname)) {
|
||||
ERROR("failed to setup the utsname for '%s'", name);
|
||||
return -1;
|
||||
if (lxc_conf->inherit_ns_fd[LXC_NS_UTS] == -1) {
|
||||
if (setup_utsname(lxc_conf->utsname)) {
|
||||
ERROR("failed to setup the utsname for '%s'", name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (setup_network(&lxc_conf->network)) {
|
||||
|
@ -320,6 +320,8 @@ struct lxc_conf {
|
||||
// store the config file specified values here.
|
||||
char *logfile; // the logfile as specifed in config
|
||||
int loglevel; // loglevel as specifed in config (if any)
|
||||
|
||||
int inherit_ns_fd[LXC_NS_MAX];
|
||||
};
|
||||
|
||||
int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf,
|
||||
|
@ -51,6 +51,10 @@
|
||||
#include "confile.h"
|
||||
#include "arguments.h"
|
||||
|
||||
#define OPT_SHARE_NET OPT_USAGE+1
|
||||
#define OPT_SHARE_IPC OPT_USAGE+2
|
||||
#define OPT_SHARE_UTS OPT_USAGE+3
|
||||
|
||||
lxc_log_define(lxc_start_ui, lxc_start);
|
||||
|
||||
static struct lxc_list defines;
|
||||
@ -91,6 +95,53 @@ err:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int pid_from_lxcname(const char *lxcname_or_pid, const char *lxcpath) {
|
||||
char *eptr;
|
||||
int pid = strtol(lxcname_or_pid, &eptr, 10);
|
||||
if (*eptr != '\0' || pid < 1) {
|
||||
struct lxc_container *s;
|
||||
s = lxc_container_new(lxcname_or_pid, lxcpath);
|
||||
if (!s) {
|
||||
SYSERROR("'%s' is not a valid pid nor a container name", lxcname_or_pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!s->may_control(s)) {
|
||||
SYSERROR("Insufficient privileges to control container '%s'", s->name);
|
||||
lxc_container_put(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid = s->init_pid(s);
|
||||
if (pid < 1) {
|
||||
SYSERROR("Is container '%s' running?", s->name);
|
||||
lxc_container_put(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
lxc_container_put(s);
|
||||
}
|
||||
if (kill(pid, 0) < 0) {
|
||||
SYSERROR("Can't send signal to pid %d", pid);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static int open_ns(int pid, const char *ns_proc_name) {
|
||||
int fd;
|
||||
char path[MAXPATHLEN];
|
||||
snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns_proc_name);
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
SYSERROR("failed to open %s", path);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||
{
|
||||
switch (c) {
|
||||
@ -101,6 +152,9 @@ static int my_parser(struct lxc_arguments* args, int c, char* arg)
|
||||
case 'C': args->close_all_fds = 1; break;
|
||||
case 's': return lxc_config_define_add(&defines, arg);
|
||||
case 'p': args->pidfile = arg; break;
|
||||
case OPT_SHARE_NET: args->share_ns[LXC_NS_NET] = arg; break;
|
||||
case OPT_SHARE_IPC: args->share_ns[LXC_NS_IPC] = arg; break;
|
||||
case OPT_SHARE_UTS: args->share_ns[LXC_NS_UTS] = arg; break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -113,6 +167,9 @@ static const struct option my_longopts[] = {
|
||||
{"console-log", required_argument, 0, 'L'},
|
||||
{"close-all-fds", no_argument, 0, 'C'},
|
||||
{"pidfile", required_argument, 0, 'p'},
|
||||
{"share-net", required_argument, 0, OPT_SHARE_NET},
|
||||
{"share-ipc", required_argument, 0, OPT_SHARE_IPC},
|
||||
{"share-uts", required_argument, 0, OPT_SHARE_UTS},
|
||||
LXC_COMMON_OPTIONS
|
||||
};
|
||||
|
||||
@ -133,7 +190,9 @@ Options :\n\
|
||||
-C, --close-all-fds If any fds are inherited, close them\n\
|
||||
If not specified, exit with failure instead\n\
|
||||
Note: --daemon implies --close-all-fds\n\
|
||||
-s, --define KEY=VAL Assign VAL to configuration variable KEY\n",
|
||||
-s, --define KEY=VAL Assign VAL to configuration variable KEY\n\
|
||||
--share-[net|ipc|uts]=NAME Share a namespace with another container or pid\n\
|
||||
",
|
||||
.options = my_longopts,
|
||||
.parser = my_parser,
|
||||
.checker = NULL,
|
||||
@ -250,6 +309,21 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < LXC_NS_MAX; i++) {
|
||||
if (my_args.share_ns[i] == NULL)
|
||||
continue;
|
||||
|
||||
int pid = pid_from_lxcname(my_args.share_ns[i], lxcpath);
|
||||
if (pid < 1)
|
||||
goto out;
|
||||
|
||||
int fd = open_ns(pid, ns_info[i].proc_name);
|
||||
if (fd < 0)
|
||||
goto out;
|
||||
conf->inherit_ns_fd[i] = fd;
|
||||
}
|
||||
|
||||
if (my_args.daemonize) {
|
||||
c->want_daemonize(c);
|
||||
}
|
||||
|
143
src/lxc/start.c
143
src/lxc/start.c
@ -75,6 +75,78 @@
|
||||
|
||||
lxc_log_define(lxc_start, lxc);
|
||||
|
||||
const struct ns_info ns_info[LXC_NS_MAX] = {
|
||||
[LXC_NS_MNT] = {"mnt", CLONE_NEWNS},
|
||||
[LXC_NS_PID] = {"pid", CLONE_NEWPID},
|
||||
[LXC_NS_UTS] = {"uts", CLONE_NEWUTS},
|
||||
[LXC_NS_IPC] = {"ipc", CLONE_NEWIPC},
|
||||
[LXC_NS_USER] = {"user", CLONE_NEWUSER},
|
||||
[LXC_NS_NET] = {"net", CLONE_NEWNET}
|
||||
};
|
||||
|
||||
static void close_ns(int ns_fd[LXC_NS_MAX]) {
|
||||
int i;
|
||||
|
||||
process_lock();
|
||||
for (i = 0; i < LXC_NS_MAX; i++) {
|
||||
if (ns_fd[i] > -1) {
|
||||
close(ns_fd[i]);
|
||||
ns_fd[i] = -1;
|
||||
}
|
||||
}
|
||||
process_unlock();
|
||||
}
|
||||
|
||||
static int preserve_ns(int ns_fd[LXC_NS_MAX], int clone_flags) {
|
||||
int i, saved_errno;
|
||||
char path[MAXPATHLEN];
|
||||
|
||||
if (access("/proc/self/ns", X_OK)) {
|
||||
ERROR("Does this kernel version support 'attach'?");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < LXC_NS_MAX; i++)
|
||||
ns_fd[i] = -1;
|
||||
|
||||
for (i = 0; i < LXC_NS_MAX; i++) {
|
||||
if ((clone_flags & ns_info[i].clone_flag) == 0)
|
||||
continue;
|
||||
snprintf(path, MAXPATHLEN, "/proc/self/ns/%s", ns_info[i].proc_name);
|
||||
process_lock();
|
||||
ns_fd[i] = open(path, O_RDONLY | O_CLOEXEC);
|
||||
process_unlock();
|
||||
if (ns_fd[i] < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
saved_errno = errno;
|
||||
close_ns(ns_fd);
|
||||
errno = saved_errno;
|
||||
SYSERROR("failed to open '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int attach_ns(const int ns_fd[LXC_NS_MAX]) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < LXC_NS_MAX; i++) {
|
||||
if (ns_fd[i] < 0)
|
||||
continue;
|
||||
|
||||
if (setns(ns_fd[i], 0) != 0)
|
||||
goto error;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
SYSERROR("failed to set namespace '%s'", ns_info[i].proc_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int match_fd(int fd)
|
||||
{
|
||||
return (fd == 0 || fd == 1 || fd == 2);
|
||||
@ -645,44 +717,68 @@ int lxc_spawn(struct lxc_handler *handler)
|
||||
const char *name = handler->name;
|
||||
struct cgroup_meta_data *cgroup_meta = NULL;
|
||||
const char *cgroup_pattern = NULL;
|
||||
int saved_ns_fd[LXC_NS_MAX];
|
||||
int preserve_mask = 0, i;
|
||||
|
||||
for (i = 0; i < LXC_NS_MAX; i++)
|
||||
if (handler->conf->inherit_ns_fd[i] != -1)
|
||||
preserve_mask |= ns_info[i].clone_flag;
|
||||
|
||||
if (lxc_sync_init(handler))
|
||||
return -1;
|
||||
|
||||
handler->clone_flags = CLONE_NEWUTS|CLONE_NEWPID|CLONE_NEWIPC|CLONE_NEWNS;
|
||||
handler->clone_flags = CLONE_NEWPID|CLONE_NEWNS;
|
||||
if (!lxc_list_empty(&handler->conf->id_map)) {
|
||||
INFO("Cloning a new user namespace");
|
||||
handler->clone_flags |= CLONE_NEWUSER;
|
||||
}
|
||||
if (!lxc_list_empty(&handler->conf->network)) {
|
||||
|
||||
handler->clone_flags |= CLONE_NEWNET;
|
||||
if (handler->conf->inherit_ns_fd[LXC_NS_NET] == -1) {
|
||||
if (!lxc_list_empty(&handler->conf->network)) {
|
||||
|
||||
/* Find gateway addresses from the link device, which is
|
||||
* no longer accessible inside the container. Do this
|
||||
* before creating network interfaces, since goto
|
||||
* out_delete_net does not work before lxc_clone. */
|
||||
if (lxc_find_gateway_addresses(handler)) {
|
||||
ERROR("failed to find gateway addresses");
|
||||
lxc_sync_fini(handler);
|
||||
return -1;
|
||||
handler->clone_flags |= CLONE_NEWNET;
|
||||
|
||||
/* Find gateway addresses from the link device, which is
|
||||
* no longer accessible inside the container. Do this
|
||||
* before creating network interfaces, since goto
|
||||
* out_delete_net does not work before lxc_clone. */
|
||||
if (lxc_find_gateway_addresses(handler)) {
|
||||
ERROR("failed to find gateway addresses");
|
||||
lxc_sync_fini(handler);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* that should be done before the clone because we will
|
||||
* fill the netdev index and use them in the child
|
||||
*/
|
||||
if (lxc_create_network(handler)) {
|
||||
ERROR("failed to create the network");
|
||||
lxc_sync_fini(handler);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* that should be done before the clone because we will
|
||||
* fill the netdev index and use them in the child
|
||||
*/
|
||||
if (lxc_create_network(handler)) {
|
||||
ERROR("failed to create the network");
|
||||
lxc_sync_fini(handler);
|
||||
return -1;
|
||||
if (save_phys_nics(handler->conf)) {
|
||||
ERROR("failed to save physical nic info");
|
||||
goto out_abort;
|
||||
}
|
||||
} else {
|
||||
INFO("Inheriting a net namespace");
|
||||
}
|
||||
|
||||
if (save_phys_nics(handler->conf)) {
|
||||
ERROR("failed to save physical nic info");
|
||||
goto out_abort;
|
||||
if (handler->conf->inherit_ns_fd[LXC_NS_IPC] == -1) {
|
||||
handler->clone_flags |= CLONE_NEWIPC;
|
||||
} else {
|
||||
INFO("Inheriting an IPC namespace");
|
||||
}
|
||||
|
||||
if (handler->conf->inherit_ns_fd[LXC_NS_UTS] == -1) {
|
||||
handler->clone_flags |= CLONE_NEWUTS;
|
||||
} else {
|
||||
INFO("Inheriting a UTS namespace");
|
||||
}
|
||||
|
||||
|
||||
cgroup_meta = lxc_cgroup_load_meta();
|
||||
if (!cgroup_meta) {
|
||||
ERROR("failed to detect cgroup metadata");
|
||||
@ -716,6 +812,9 @@ int lxc_spawn(struct lxc_handler *handler)
|
||||
if (handler->pinfd == -1)
|
||||
INFO("failed to pin the container's rootfs");
|
||||
|
||||
preserve_ns(saved_ns_fd, preserve_mask);
|
||||
attach_ns(handler->conf->inherit_ns_fd);
|
||||
|
||||
/* Create a process in a new set of namespaces */
|
||||
handler->pid = lxc_clone(do_start, handler, handler->clone_flags);
|
||||
if (handler->pid < 0) {
|
||||
@ -723,6 +822,8 @@ int lxc_spawn(struct lxc_handler *handler)
|
||||
goto out_delete_net;
|
||||
}
|
||||
|
||||
attach_ns(saved_ns_fd);
|
||||
|
||||
lxc_sync_fini_child(handler);
|
||||
|
||||
if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
#include <lxc/state.h>
|
||||
#include <sys/param.h>
|
||||
#include "namespace.h"
|
||||
|
||||
struct lxc_conf;
|
||||
|
||||
@ -39,6 +40,23 @@ struct lxc_operations {
|
||||
|
||||
struct cgroup_desc;
|
||||
|
||||
enum {
|
||||
LXC_NS_MNT,
|
||||
LXC_NS_PID,
|
||||
LXC_NS_UTS,
|
||||
LXC_NS_IPC,
|
||||
LXC_NS_USER,
|
||||
LXC_NS_NET,
|
||||
LXC_NS_MAX
|
||||
};
|
||||
|
||||
struct ns_info {
|
||||
const char *proc_name;
|
||||
int clone_flag;
|
||||
};
|
||||
|
||||
const struct ns_info ns_info[LXC_NS_MAX];
|
||||
|
||||
struct lxc_handler {
|
||||
pid_t pid;
|
||||
char *name;
|
||||
|
Loading…
Reference in New Issue
Block a user