summaryrefslogtreecommitdiffstats
path: root/sys-utils/unshare.c
diff options
context:
space:
mode:
authorKarel Zak2015-04-09 11:34:02 +0200
committerKarel Zak2015-04-14 11:39:15 +0200
commit0490a6ca26f6e7bf9eed5521d240661adb302ba2 (patch)
tree0788a179e43144219d9226badd190b1a77f6e713 /sys-utils/unshare.c
parentlib/sysfs.c: use fcntl(..F_DUPFD_CLOEXEC) instead of dup(2) (diff)
downloadkernel-qcow2-util-linux-0490a6ca26f6e7bf9eed5521d240661adb302ba2.tar.gz
kernel-qcow2-util-linux-0490a6ca26f6e7bf9eed5521d240661adb302ba2.tar.xz
kernel-qcow2-util-linux-0490a6ca26f6e7bf9eed5521d240661adb302ba2.zip
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 <lkundrak@v3.sk>. Signed-off-by: Karel Zak <kzak@redhat.com>
Diffstat (limited to 'sys-utils/unshare.c')
-rw-r--r--sys-utils/unshare.c93
1 files changed, 81 insertions, 12 deletions
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/<type> */
+ 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[=<file>] unshare mounts namespace\n"), out);
+ fputs(_(" -u, --uts[=<file>] unshare UTS namespace (hostname etc)\n"), out);
+ fputs(_(" -i, --ipc[=<file>] unshare System V IPC namespace\n"), out);
+ fputs(_(" -n, --net[=<file>] unshare network namespace\n"), out);
+ fputs(_(" -p, --pid[=<file>] unshare pid namespace\n"), out);
+ fputs(_(" -U, --user[=<file>] unshare user namespace\n"), out);
fputs(_(" -f, --fork fork before launching <program>\n"), out);
fputs(_(" --mount-proc[=<dir>] 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 "