From 0490a6ca26f6e7bf9eed5521d240661adb302ba2 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 9 Apr 2015 11:34:02 +0200 Subject: unshare: allow persisting namespaces For nsenter(1) we already support namespace specification by file (e.g. bind mount to namespace /proc/[pid]/ns/[type] file). For example: # nsenter --uts=/some/path This patch extends unshare(1) to setup the bind mount for specified namespace, for example # touch /some/path # unshare --uts=/some/path hostname FOO # nsenter --uts=/some/path hostname FOO Note that the problem is mount namespace, because create bind mount to ns/mount file within unshared namespace does not make sense. Based on patch from Lubomir Rintel . Signed-off-by: Karel Zak --- sys-utils/unshare.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 81 insertions(+), 12 deletions(-) (limited to 'sys-utils/unshare.c') diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 18a7c7bda..65d3e6109 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -42,6 +42,24 @@ /* 'private' is kernel default */ #define UNSHARE_PROPAGATION_DEFAULT (MS_REC | MS_PRIVATE) +/* /proc namespace files and mountpoints for binds */ +static struct namespace_file { + int type; /* CLONE_NEW* */ + const char *name; /* ns/ */ + const char *target; /* user specified target for bind mount */ +} namespace_files[] = { + { .type = CLONE_NEWUSER, .name = "ns/user" }, + { .type = CLONE_NEWIPC, .name = "ns/ipc" }, + { .type = CLONE_NEWUTS, .name = "ns/uts" }, + { .type = CLONE_NEWNET, .name = "ns/net" }, + { .type = CLONE_NEWPID, .name = "ns/pid" }, + { .type = CLONE_NEWNS, .name = "ns/mnt" }, + { .name = NULL } +}; + +static int npersists; /* number of persistent namespaces */ + + enum { SETGROUPS_NONE = -1, SETGROUPS_DENY = 0, @@ -133,6 +151,40 @@ static void set_propagation(unsigned long flags) err(EXIT_FAILURE, _("cannot change root filesystem propagation")); } + +static int set_ns_target(int type, const char *path) +{ + struct namespace_file *ns; + + for (ns = namespace_files; ns->name; ns++) { + if (ns->type != type) + continue; + ns->target = path; + npersists++; + return 0; + } + + return -EINVAL; +} + +static int bind_ns_files(pid_t pid) +{ + struct namespace_file *ns; + char src[PATH_MAX]; + + for (ns = namespace_files; ns->name; ns++) { + if (!ns->target) + continue; + + snprintf(src, sizeof(src), "/proc/%u/%s", (unsigned) pid, ns->name); + + if (mount(src, ns->target, NULL, MS_BIND, NULL) != 0) + err(EXIT_FAILURE, _("mount %s on %s failed"), src, ns->target); + } + + return 0; +} + static void usage(int status) { FILE *out = status == EXIT_SUCCESS ? stdout : stderr; @@ -145,12 +197,12 @@ static void usage(int status) fputs(_("Run a program with some namespaces unshared from the parent.\n"), out); fputs(USAGE_OPTIONS, out); - fputs(_(" -m, --mount unshare mounts namespace\n"), out); - fputs(_(" -u, --uts unshare UTS namespace (hostname etc)\n"), out); - fputs(_(" -i, --ipc unshare System V IPC namespace\n"), out); - fputs(_(" -n, --net unshare network namespace\n"), out); - fputs(_(" -p, --pid unshare pid namespace\n"), out); - fputs(_(" -U, --user unshare user namespace\n"), out); + fputs(_(" -m, --mount[=] unshare mounts namespace\n"), out); + fputs(_(" -u, --uts[=] unshare UTS namespace (hostname etc)\n"), out); + fputs(_(" -i, --ipc[=] unshare System V IPC namespace\n"), out); + fputs(_(" -n, --net[=] unshare network namespace\n"), out); + fputs(_(" -p, --pid[=] unshare pid namespace\n"), out); + fputs(_(" -U, --user[=] unshare user namespace\n"), out); fputs(_(" -f, --fork fork before launching \n"), out); fputs(_(" --mount-proc[=] mount proc filesystem first (implies --mount)\n"), out); fputs(_(" -r, --map-root-user map current user to root (implies --user)\n"), out); @@ -176,12 +228,14 @@ int main(int argc, char *argv[]) static const struct option longopts[] = { { "help", no_argument, 0, 'h' }, { "version", no_argument, 0, 'V'}, - { "mount", no_argument, 0, 'm' }, - { "uts", no_argument, 0, 'u' }, - { "ipc", no_argument, 0, 'i' }, - { "net", no_argument, 0, 'n' }, - { "pid", no_argument, 0, 'p' }, - { "user", no_argument, 0, 'U' }, + + { "mount", optional_argument, 0, 'm' }, + { "uts", optional_argument, 0, 'u' }, + { "ipc", optional_argument, 0, 'i' }, + { "net", optional_argument, 0, 'n' }, + { "pid", optional_argument, 0, 'p' }, + { "user", optional_argument, 0, 'U' }, + { "fork", no_argument, 0, 'f' }, { "mount-proc", optional_argument, 0, OPT_MOUNTPROC }, { "map-root-user", no_argument, 0, 'r' }, @@ -215,21 +269,33 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; case 'm': unshare_flags |= CLONE_NEWNS; + if (optarg) + set_ns_target(CLONE_NEWNS, optarg); break; case 'u': unshare_flags |= CLONE_NEWUTS; + if (optarg) + set_ns_target(CLONE_NEWUTS, optarg); break; case 'i': unshare_flags |= CLONE_NEWIPC; + if (optarg) + set_ns_target(CLONE_NEWIPC, optarg); break; case 'n': unshare_flags |= CLONE_NEWNET; + if (optarg) + set_ns_target(CLONE_NEWNET, optarg); break; case 'p': unshare_flags |= CLONE_NEWPID; + if (optarg) + set_ns_target(CLONE_NEWPID, optarg); break; case 'U': unshare_flags |= CLONE_NEWUSER; + if (optarg) + set_ns_target(CLONE_NEWUSER, optarg); break; case OPT_MOUNTPROC: unshare_flags |= CLONE_NEWNS; @@ -273,6 +339,9 @@ int main(int argc, char *argv[]) } } + if (npersists) + bind_ns_files(getpid()); + if (maproot) { if (setgrpcmd == SETGROUPS_ALLOW) errx(EXIT_FAILURE, _("options --setgroups=allow and " -- cgit v1.2.3-55-g7522