]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
unshare: support the switching of namespaces
authorNeil Horman <nhorman@tuxdriver.com>
Fri, 28 Dec 2012 16:22:18 +0000 (11:22 -0500)
committerKarel Zak <kzak@redhat.com>
Mon, 7 Jan 2013 13:56:32 +0000 (14:56 +0100)
In addition to the unshare syscall, there exists the setns syscall, which
allows processes to migrate to the namepsaces of other processes.  Add this
functionality into the unshare command, as they operate in a fairly simmilar
fashion.

Note: There was discussion of adding a path based namespace argument to unshare
in the origional discussion thread, but I opted to leave that out as it didn't
seem to fit in nicely with the current argument pattern.  I figure we can always
add that in later if we need to

[kzak@redhat.com: - fix optional arguments
                  - do not call unshare if no flag specified
                  - use O_CLOEXEC
                  - codding style cleanup]

Signed-off-by: Neil Horman <nhorman@tuxdriver.com>
CC: Karel Zak <kzak@redhat.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/Makemodule.am
sys-utils/unshare.1
sys-utils/unshare.c

index 33112fb68e16592bee9919d440ddeb3892c0c87c..5636f70da37700f5ae25c1e41dffa0660806a7bb 100644 (file)
@@ -287,6 +287,7 @@ if BUILD_UNSHARE
 usrbin_exec_PROGRAMS += unshare
 dist_man_MANS += sys-utils/unshare.1
 unshare_SOURCES = sys-utils/unshare.c
+unshare_LDADD = $(LDADD) libcommon.la
 endif
 
 if BUILD_ARCH
index 1325e3481250753a4d55d6c15464b8dd318ffc00..c37083d41cf5d25f30e29353b7e325163fcfdc08 100644 (file)
@@ -3,15 +3,15 @@
 .\"
 .TH UNSHARE 1 "October 2008" "util-linux" "User Commands"
 .SH NAME
-unshare \- run program with some namespaces unshared from parent
+unshare \- run program with some namespaces unshared or changed from parent
 .SH SYNOPSIS
 .B unshare
 .RI [ options ]
 program
 .RI [ arguments ]
 .SH DESCRIPTION
-Unshares specified namespaces from parent process and then executes specified
-program. Unshareable namespaces are:
+Unshares or migrates specified namespaces from parent process and then executes specified
+program. Available namespaces are:
 .TP
 .BR "mount namespace"
 mounting and unmounting filesystems will not affect rest of the system
@@ -33,31 +33,43 @@ etc. (\fBCLONE_NEWNET\fP flag).
 .TP
 See the \fBclone\fR(2) for exact semantics of the flags.
 .SH OPTIONS
+Note when specifying the optional \fB<pid>\fP argument, the string of option,
+equal sign (=),  and the pid must not contain any blanks or other white space.
+The correct form is for example --ipc=123 or -i=123.
 .TP
 .BR \-h , " \-\-help"
 Print a help message,
 .TP
-.BR \-m , " \-\-mount"
-Unshare the mount namespace,
+.BR \-m , " \-\-mount " \fI[=pid]\fP
+Unshare the mount namespace, or, when a pid is specified, migrate the mount
+namespace to the one attached to the specified pid.
 .TP
-.BR \-u , " \-\-uts"
-Unshare the UTC namespace,
+.BR \-u , " \-\-uts " \fI[=pid]\fP
+Unshare the UTC namespace, or, when a pid is specified, migrate the uts
+namespace to the one attached to the specified pid
 .TP
-.BR \-i , " \-\-ipc"
-Unshare the IPC namespace,
+.BR \-i , " \-\-ipc " \fI[=pid]\fP
+Unshare the IPC namespace, or, when a pid is specified, migrate the ipc
+namespace to the one attached to the specified pid
 .TP
-.BR \-n , " \-\-net"
-Unshare the network namespace.
+.BR \-n , " \-\-net " \fI[=pid]\fP
+Unshare the network namespace, or, when a pid is specified, migrate the net
+namespace to the one attached to the specified pid
 .SH NOTES
 The unshare command drops potential privileges before executing the
 target program. This allows to setuid unshare.
+.P
+Support for migrating processes between mount and pid namespace is available in
+kernels 3.8 and later
 .SH SEE ALSO
 .BR unshare (2),
+.BR setns (2),
 .BR clone (2)
 .SH BUGS
 None known so far.
-.SH AUTHOR
+.SH AUTHORS
 Mikhail Gusarov <dottedmag@dottedmag.net>
+Neil Horman <nhorman@tuxdriver.com>
 .SH AVAILABILITY
 The unshare command is part of the util-linux package and is available from
 ftp://ftp.kernel.org/pub/linux/utils/util-linux/.
index 9de997bdc8ccd6badfa69edef9e537bdb7cb2532..64ea3766ff9813655d0e641787f8cab1a172b26e 100644 (file)
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <getopt.h>
 
 #include "nls.h"
 #include "c.h"
 #include "closestream.h"
+#include "strutils.h"
 
 #ifndef CLONE_NEWSNS
 # define CLONE_NEWNS 0x00020000
@@ -60,10 +62,10 @@ static void usage(int status)
              _(" %s [options] <program> [args...]\n"), program_invocation_short_name);
 
        fputs(USAGE_OPTIONS, out);
-       fputs(_(" -m, --mount       unshare mounts namespace\n"
-               " -u, --uts         unshare UTS namespace (hostname etc)\n"
-               " -i, --ipc         unshare System V IPC namespace\n"
-               " -n, --net         unshare network namespace\n"), out);
+       fputs(_(" -m, --mount [=<pid>]   unshare or migrate mounts namespace\n"
+               " -u, --uts [=<pid>]     unshare or migrate UTS namespace (hostname etc)\n"
+               " -i, --ipc [=<pid>]     unshare or migrate System V IPC namespace\n"
+               " -n, --net [=<pid>]     unshare or migrate network namespace\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
        fputs(USAGE_HELP, out);
@@ -76,17 +78,18 @@ static void usage(int status)
 int main(int argc, char *argv[])
 {
        static const struct option longopts[] = {
-               { "help", no_argument, 0, 'h' },
+               { "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' },
+               { "mount",   optional_argument, 0, 'm' },
+               { "uts",     optional_argument, 0, 'u' },
+               { "ipc",     optional_argument, 0, 'i' },
+               { "net",     optional_argument, 0, 'n' },
                { NULL, 0, 0, 0 }
        };
 
+       int namespaces[128];            /* /proc/#/ns/<name> file descriptors */
+       size_t i, nscount = 0;          /* number of used namespaces[] */
        int unshare_flags = 0;
-
        int c;
 
        setlocale(LC_MESSAGES, "");
@@ -94,7 +97,13 @@ int main(int argc, char *argv[])
        textdomain(PACKAGE);
        atexit(close_stdout);
 
-       while((c = getopt_long(argc, argv, "hVmuin", longopts, NULL)) != -1) {
+       memset(namespaces, 0, sizeof(namespaces));
+
+       while((c = getopt_long(argc, argv,
+                       "hVm::u::i::n::", longopts, NULL)) != -1) {
+
+               const char *ns = NULL;
+
                switch(c) {
                case 'h':
                        usage(EXIT_SUCCESS);
@@ -102,26 +111,58 @@ int main(int argc, char *argv[])
                        printf(UTIL_LINUX_VERSION);
                        return EXIT_SUCCESS;
                case 'm':
-                       unshare_flags |= CLONE_NEWNS;
+                       ns = "mnt";
+                       if (!optarg)
+                               unshare_flags |= CLONE_NEWNS;
                        break;
                case 'u':
-                       unshare_flags |= CLONE_NEWUTS;
+                       ns = "uts";
+                       if (!optarg)
+                               unshare_flags |= CLONE_NEWUTS;
                        break;
                case 'i':
-                       unshare_flags |= CLONE_NEWIPC;
+                       ns = "ipc";
+                       if (!optarg)
+                               unshare_flags |= CLONE_NEWIPC;
                        break;
                case 'n':
-                       unshare_flags |= CLONE_NEWNET;
+                       ns = "net";
+                       if (!optarg)
+                               unshare_flags |= CLONE_NEWNET;
                        break;
                default:
                        usage(EXIT_FAILURE);
                }
+
+               if (ns && optarg) {
+                       pid_t pid;
+                       char path[512];
+
+                       if (nscount >= ARRAY_SIZE(namespaces))
+                               err(EXIT_FAILURE, _("too many new namespaces specified"));
+
+                       if (*optarg == '=')
+                               optarg++;
+
+                       pid = strtoul_or_err(optarg, _("failed to parse pid argument"));
+
+                       sprintf(path, "/proc/%lu/ns/%s", (unsigned long) pid, ns);
+                       namespaces[nscount] = open(path, O_RDONLY | O_CLOEXEC);
+                       if (namespaces[nscount] < 0)
+                               err(EXIT_FAILURE, _("cannot open %s"), path);
+                       nscount++;
+               }
        }
 
-       if(optind >= argc)
+       if (optind >= argc)
                usage(EXIT_FAILURE);
 
-       if(-1 == unshare(unshare_flags))
+       for (i = 0; i < nscount; i++) {
+               if (setns(namespaces[i], 0) != 0)
+                       err(EXIT_FAILURE, _("setns failed"));
+       }
+
+       if (unshare_flags && unshare(unshare_flags) != 0)
                err(EXIT_FAILURE, _("unshare failed"));
 
        /* drop potential root euid/egid if we had been setuid'd */