diff --git a/doc/lxc-start.sgml.in b/doc/lxc-start.sgml.in index 09f917164..07f9845b8 100644 --- a/doc/lxc-start.sgml.in +++ b/doc/lxc-start.sgml.in @@ -57,6 +57,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -p pid_file -s KEY=VAL -C + --share-[net|ipc|uts] name|pid command @@ -186,6 +187,50 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + + + + + + Inherit a network namespace from + a name container or + a pid. 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. + + + + + + + + + + + Inherit an IPC namespace from + a name container or + a pid. + + + + + + + + + + + Inherit a UTS namespace from + a name container or + a pid. The starting LXC will + not set the hostname, but the container OS may do it + anyway. + + + + diff --git a/src/lxc/arguments.h b/src/lxc/arguments.h index dc2d117eb..021f552e2 100644 --- a/src/lxc/arguments.h +++ b/src/lxc/arguments.h @@ -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; diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 860fc5b63..534e6e6fb 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -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)) { diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 2ce4843f2..84ffb20db 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -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, diff --git a/src/lxc/lxc_start.c b/src/lxc/lxc_start.c index fe859dbee..e5378469b 100644 --- a/src/lxc/lxc_start.c +++ b/src/lxc/lxc_start.c @@ -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); } diff --git a/src/lxc/start.c b/src/lxc/start.c index 3d2bc86f9..2ffd7f2ea 100644 --- a/src/lxc/start.c +++ b/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)) diff --git a/src/lxc/start.h b/src/lxc/start.h index c35c5c481..c1f790901 100644 --- a/src/lxc/start.h +++ b/src/lxc/start.h @@ -27,6 +27,7 @@ #include #include +#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;