<command>lxc-unshare</command>
<arg choice="req">-s <replaceable>namespaces</replaceable></arg>
<arg choice="opt">-u <replaceable>user</replaceable></arg>
+ <arg choice="opt">-H <replaceable>hostname</replaceable></arg>
+ <arg choice="opt">-i <replaceable>ifname</replaceable></arg>
+ <arg choice="opt">-d</arg>
+ <arg choice="opt">-M</arg>
<arg choice="req">command</arg>
</cmdsynopsis>
</refsynopsisdiv>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term>
+ <option>-H <replaceable>hostname</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ Set the hostname in the new container. Only allowed if
+ the UTSNAME namespace is set.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-i <replaceable>interfacename</replaceable></option>
+ </term>
+ <listitem>
+ <para>
+ 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.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-d</option>
+ </term>
+ <listitem>
+ <para>
+ Daemonize (do not wait for the container to exit before exiting)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>
+ <option>-M</option>
+ </term>
+ <listitem>
+ <para>
+ Mount default filesystems (/proc /dev/shm and /dev/mqueue)
+ in the container. Only allowed if MOUNT namespace is set.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
</programlisting>
ps output will show there are no other processes in the namespace.
</para>
+ <para>
+ To spawn a shell in a new network, pid, mount, and hostname
+ namespace.
+ <programlisting>
+ lxc-unshare -s "NETWORK|PID|MOUNT|UTSNAME" -M -H slave -i veth1 /bin/bash
+ </programlisting>
+
+ 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.
+ </para>
</refsect1>
&seealso;
#include <sys/types.h>
#include <sys/wait.h>
#include <pwd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
#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 <options> 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 <id> : new id to be set if -s USER is specified\n");
+ fprintf(stderr, "\t -u <id> : new id to be set if -s USER is specified\n");
+ fprintf(stderr, "\t -i <iface> : Interface name to be moved into container (presumably with NETWORK unsharing set)\n");
+ fprintf(stderr, "\t -H <hostname>: 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);
}
char ***args;
int *flags;
uid_t *uid;
+ int want_default_mounts;
+ const char *want_hostname;
};
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)) {
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)
if (ret)
usage(argv[0]);
+ if (!(flags & CLONE_NEWNET) && my_iflist) {
+ ERROR("-i <interfacename> needs -s NETWORK option");
+ return 1;
+ }
+
+ if (!(flags & CLONE_NEWUTS) && start_arg.want_hostname) {
+ ERROR("-H <hostname> needs -s UTSNAME option");
+ return 1;
+ }
+
+ if (!(flags & CLONE_NEWNS) && start_arg.want_default_mounts) {
+ ERROR("-M needs -s MOUNT option");
return 1;
}
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;