mirror of
git://github.com/lxc/lxc
synced 2025-09-03 16:19:56 +00:00
Teach lxc_unshare about interfaces, mounts, hostname, daemonize
lxc_unshare now takes one or more '-i interfacename' arguments which will move the named interfaces into the created container. lxc_unshare now takes -M argument which will cause the standard mounts (/proc /dev/shm /dev/mqueue) to be auto-mounted inside container. lxc_unshare now takes '-H hostname' argument to automatically set the hostname in the container. lxc_unshare now takes -D argument to automatically daemonize and detach from the created container, instead of waiting for the container to exit Signed-off-by: Seth Robertson <srobertson@appcomsci.com> Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
This commit is contained in:
committed by
Serge Hallyn
parent
13d8bde96f
commit
c1bb25a831
@@ -52,6 +52,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
<command>lxc-unshare</command>
|
<command>lxc-unshare</command>
|
||||||
<arg choice="req">-s <replaceable>namespaces</replaceable></arg>
|
<arg choice="req">-s <replaceable>namespaces</replaceable></arg>
|
||||||
<arg choice="opt">-u <replaceable>user</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>
|
<arg choice="req">command</arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
@@ -105,6 +109,55 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
@@ -131,6 +184,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
ps output will show there are no other processes in the namespace.
|
ps output will show there are no other processes in the namespace.
|
||||||
</para>
|
</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>
|
</refsect1>
|
||||||
|
|
||||||
&seealso;
|
&seealso;
|
||||||
|
@@ -33,22 +33,36 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#include "caps.h"
|
#include "caps.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "namespace.h"
|
#include "namespace.h"
|
||||||
|
#include "network.h"
|
||||||
|
#include "utils.h"
|
||||||
#include "cgroup.h"
|
#include "cgroup.h"
|
||||||
#include "error.h"
|
#include "error.h"
|
||||||
|
|
||||||
lxc_log_define(lxc_unshare_ui, lxc);
|
lxc_log_define(lxc_unshare_ui, lxc);
|
||||||
|
|
||||||
|
struct my_iflist
|
||||||
|
{
|
||||||
|
char *mi_ifname;
|
||||||
|
struct my_iflist *mi_next;
|
||||||
|
};
|
||||||
|
|
||||||
static void usage(char *cmd)
|
static void usage(char *cmd)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s <options> command [command_arguments]\n", basename(cmd));
|
fprintf(stderr, "%s <options> command [command_arguments]\n", basename(cmd));
|
||||||
fprintf(stderr, "Options are:\n");
|
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");
|
"\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);
|
_exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +102,8 @@ struct start_arg {
|
|||||||
char ***args;
|
char ***args;
|
||||||
int *flags;
|
int *flags;
|
||||||
uid_t *uid;
|
uid_t *uid;
|
||||||
|
int want_default_mounts;
|
||||||
|
const char *want_hostname;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int do_start(void *arg)
|
static int do_start(void *arg)
|
||||||
@@ -96,6 +112,17 @@ static int do_start(void *arg)
|
|||||||
char **args = *start_arg->args;
|
char **args = *start_arg->args;
|
||||||
int flags = *start_arg->flags;
|
int flags = *start_arg->flags;
|
||||||
uid_t uid = *start_arg->uid;
|
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
|
// Setuid is useful even without a new user id space
|
||||||
if ( uid >= 0 && setuid(uid)) {
|
if ( uid >= 0 && setuid(uid)) {
|
||||||
@@ -116,22 +143,44 @@ int main(int argc, char *argv[])
|
|||||||
char *namespaces = NULL;
|
char *namespaces = NULL;
|
||||||
char **args;
|
char **args;
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
|
int daemonize = 0;
|
||||||
uid_t uid = -1; /* valid only if (flags & CLONE_NEWUSER) */
|
uid_t uid = -1; /* valid only if (flags & CLONE_NEWUSER) */
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
|
struct my_iflist *tmpif, *my_iflist = NULL;
|
||||||
struct start_arg start_arg = {
|
struct start_arg start_arg = {
|
||||||
.args = &args,
|
.args = &args,
|
||||||
.uid = &uid,
|
.uid = &uid,
|
||||||
.flags = &flags,
|
.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) {
|
switch (opt) {
|
||||||
case 's':
|
case 's':
|
||||||
namespaces = optarg;
|
namespaces = optarg;
|
||||||
break;
|
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':
|
case 'h':
|
||||||
usage(argv[0]);
|
usage(argv[0]);
|
||||||
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
uid = lookup_user(optarg);
|
uid = lookup_user(optarg);
|
||||||
if (uid == -1)
|
if (uid == -1)
|
||||||
@@ -154,6 +203,18 @@ int main(int argc, char *argv[])
|
|||||||
if (ret)
|
if (ret)
|
||||||
usage(argv[0]);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,6 +224,16 @@ int main(int argc, char *argv[])
|
|||||||
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) {
|
if (waitpid(pid, &status, 0) < 0) {
|
||||||
ERROR("failed to wait for '%d'", pid);
|
ERROR("failed to wait for '%d'", pid);
|
||||||
return -1;
|
return -1;
|
||||||
|
Reference in New Issue
Block a user