diff --git a/doc/lxc-unshare.sgml.in b/doc/lxc-unshare.sgml.in index 9d3be22edf..678ae9e8e2 100644 --- a/doc/lxc-unshare.sgml.in +++ b/doc/lxc-unshare.sgml.in @@ -52,6 +52,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA lxc-unshare -s namespaces -u user + -H hostname + -i ifname + -d + -M command @@ -105,6 +109,55 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + + + + + + Set the hostname in the new container. Only allowed if + the UTSNAME namespace is set. + + + + + + + + + + + Move the named interface into the container. Only allowed + if the NETWORK namespace is set. You may specify this + argument multiple times to move multiple interfaces into + container. + + + + + + + + + + + Daemonize (do not wait for the container to exit before exiting) + + + + + + + + + + + Mount default filesystems (/proc /dev/shm and /dev/mqueue) + in the container. Only allowed if MOUNT namespace is set. + + + + @@ -131,6 +184,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ps output will show there are no other processes in the namespace. + + To spawn a shell in a new network, pid, mount, and hostname + namespace. + + lxc-unshare -s "NETWORK|PID|MOUNT|UTSNAME" -M -H slave -i veth1 /bin/bash + + + The resulting shell will have pid 1 and will see two network + interfaces (lo and veth1). The hostname will be "slave" and + /proc will have been remounted. ps output will show there are + no other processes in the namespace. + &seealso; diff --git a/src/lxc/lxc_unshare.c b/src/lxc/lxc_unshare.c index 4c82e9e8d7..ed3bf478a7 100644 --- a/src/lxc/lxc_unshare.c +++ b/src/lxc/lxc_unshare.c @@ -33,22 +33,36 @@ #include #include #include +#include +#include #include "caps.h" #include "log.h" #include "namespace.h" +#include "network.h" +#include "utils.h" #include "cgroup.h" #include "error.h" lxc_log_define(lxc_unshare_ui, lxc); +struct my_iflist +{ + char *mi_ifname; + struct my_iflist *mi_next; +}; + static void usage(char *cmd) { fprintf(stderr, "%s command [command_arguments]\n", basename(cmd)); fprintf(stderr, "Options are:\n"); - fprintf(stderr, "\t -s flags: ORed list of flags to unshare:\n" \ + fprintf(stderr, "\t -s flags : ORed list of flags to unshare:\n" \ "\t MOUNT, PID, UTSNAME, IPC, USER, NETWORK\n"); - fprintf(stderr, "\t -u : new id to be set if -s USER is specified\n"); + fprintf(stderr, "\t -u : new id to be set if -s USER is specified\n"); + fprintf(stderr, "\t -i : Interface name to be moved into container (presumably with NETWORK unsharing set)\n"); + fprintf(stderr, "\t -H : Set the hostname in the container\n"); + fprintf(stderr, "\t -d : Daemonize (do not wait for container to exit)\n"); + fprintf(stderr, "\t -M : reMount default fs inside container (/proc /dev/shm /dev/mqueue)\n"); _exit(1); } @@ -88,6 +102,8 @@ struct start_arg { char ***args; int *flags; uid_t *uid; + int want_default_mounts; + const char *want_hostname; }; static int do_start(void *arg) @@ -96,6 +112,17 @@ static int do_start(void *arg) char **args = *start_arg->args; int flags = *start_arg->flags; uid_t uid = *start_arg->uid; + int want_default_mounts = start_arg->want_default_mounts; + const char *want_hostname = start_arg->want_hostname; + + if ((flags & CLONE_NEWNS) && want_default_mounts) + lxc_setup_fs(); + + if ((flags & CLONE_NEWUTS) && want_hostname) + if (sethostname(want_hostname, strlen(want_hostname)) < 0) { + ERROR("failed to set hostname %s: %s", want_hostname, strerror(errno)); + exit(1); + } // Setuid is useful even without a new user id space if ( uid >= 0 && setuid(uid)) { @@ -116,22 +143,44 @@ int main(int argc, char *argv[]) char *namespaces = NULL; char **args; int flags = 0; + int daemonize = 0; uid_t uid = -1; /* valid only if (flags & CLONE_NEWUSER) */ pid_t pid; - + struct my_iflist *tmpif, *my_iflist = NULL; struct start_arg start_arg = { .args = &args, .uid = &uid, .flags = &flags, + .want_hostname = NULL, + .want_default_mounts = 0, }; - while ((opt = getopt(argc, argv, "s:u:h")) != -1) { + while ((opt = getopt(argc, argv, "s:u:hH:i:dM")) != -1) { switch (opt) { case 's': namespaces = optarg; break; + case 'i': + if (!(tmpif = malloc(sizeof(*tmpif)))) { + perror("malloc"); + exit(1); + } + tmpif->mi_ifname = optarg; + tmpif->mi_next = my_iflist; + my_iflist = tmpif; + break; + case 'd': + daemonize = 1; + break; + case 'M': + start_arg.want_default_mounts = 1; + break; + case 'H': + start_arg.want_hostname = optarg; + break; case 'h': usage(argv[0]); + break; case 'u': uid = lookup_user(optarg); if (uid == -1) @@ -154,6 +203,18 @@ int main(int argc, char *argv[]) if (ret) usage(argv[0]); + if (!(flags & CLONE_NEWNET) && my_iflist) { + ERROR("-i needs -s NETWORK option"); + return 1; + } + + if (!(flags & CLONE_NEWUTS) && start_arg.want_hostname) { + ERROR("-H needs -s UTSNAME option"); + return 1; + } + + if (!(flags & CLONE_NEWNS) && start_arg.want_default_mounts) { + ERROR("-M needs -s MOUNT option"); return 1; } @@ -163,6 +224,16 @@ int main(int argc, char *argv[]) return -1; } + if (my_iflist) { + for (tmpif = my_iflist; tmpif; tmpif = tmpif->mi_next) { + if (lxc_netdev_move_by_name(tmpif->mi_ifname, pid) < 0) + fprintf(stderr,"Could not move interface %s into container %d: %s\n", tmpif->mi_ifname, pid, strerror(errno)); + } + } + + if (daemonize) + exit(0); + if (waitpid(pid, &status, 0) < 0) { ERROR("failed to wait for '%d'", pid); return -1;