]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
unshare: add --fork options for pid namespaces
authorMike Frysinger <vapier@gentoo.org>
Fri, 28 Jun 2013 00:04:58 +0000 (20:04 -0400)
committerKarel Zak <kzak@redhat.com>
Tue, 9 Jul 2013 09:02:16 +0000 (11:02 +0200)
The ability of unshare to launch a new pid namespace is a bit limited.
The first process in the namespace is expected to be the "init" for it.
When it's not, you get bad behavior.

For example, trying to launch a shell in a new pid namespace fails very
quickly:
$ sudo unshare -p dash
# uname -r
3.8.3
# uname -m
dash: 2: Cannot fork
# ls -ld /
dash: 3: Cannot fork
# echo $$
1324

For this to work smoothly, we need an init process to actively watch over
things.  But forcing people to re-use an existing init or write their own
mini init is a bit overkill.  So let's add a --fork option to unshare to
do this common bit of book keeping.  Now we can do:
$ sudo unshare -p --fork dash
# uname -r
3.8.3
# uname -m
x86_64
# ls -ld /
drwxr-xr-x 22 root root 4096 May  4 14:01 /
# echo $$
1

Thanks to Michael Kerrisk for his namespace articles on lwn.net

[kzak@redhat.com: - fix "forkif logic, remove --mount-proc]

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Karel Zak <kzak@redhat.com>
sys-utils/unshare.1
sys-utils/unshare.c

index bd0f13eb56844f64e762c67bf85ff582d9132f29..c387cebfd01edc3a7ca98b2e4b6771c58c29bd62 100644 (file)
@@ -56,13 +56,17 @@ Unshare the mount namespace.
 Unshare the network namespace.
 .TP
 .BR \-p , " \-\-pid"
-Unshare the pid namespace.
+Unshare the pid namespace. See also \fB--fork\fP option.
 .TP
 .BR \-u , " \-\-uts"
 Unshare the UTS namespace.
 .TP
 .BR \-U , " \-\-user"
 Unshare the user namespace.
+.TP
+.BR \-f , " \-\-fork"
+Fork the specified process as a child of unshare rather than running it
+directly.  This is useful when creating a new pid namespace.
 .SH SEE ALSO
 .BR unshare (2),
 .BR clone (2)
index 8cc9c46c8435279b0f26d3e620940b7348150fb0..a889eee9f944c3f9598b6320ced5407d479c6262 100644 (file)
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/wait.h>
 
 #include "nls.h"
 #include "c.h"
@@ -46,6 +47,7 @@ static void usage(int status)
        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 <program>\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
        fputs(USAGE_HELP, out);
@@ -66,20 +68,23 @@ int main(int argc, char *argv[])
                { "net", no_argument, 0, 'n' },
                { "pid", no_argument, 0, 'p' },
                { "user", no_argument, 0, 'U' },
+               { "fork", no_argument, 0, 'f' },
                { NULL, 0, 0, 0 }
        };
 
        int unshare_flags = 0;
-
-       int c;
+       int c, forkit = 0;
 
        setlocale(LC_MESSAGES, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
        atexit(close_stdout);
 
-       while ((c = getopt_long(argc, argv, "hVmuinpU", longopts, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "fhVmuinpU", longopts, NULL)) != -1) {
                switch (c) {
+               case 'f':
+                       forkit = 1;
+                       break;
                case 'h':
                        usage(EXIT_SUCCESS);
                case 'V':
@@ -111,6 +116,26 @@ int main(int argc, char *argv[])
        if (-1 == unshare(unshare_flags))
                err(EXIT_FAILURE, _("unshare failed"));
 
+       if (forkit) {
+               int status;
+               pid_t pid = fork();
+
+               switch(pid) {
+               case -1:
+                       err(EXIT_FAILURE, _("fork failed"));
+               case 0: /* child */
+                       break;
+               default: /* parent */
+                       if (waitpid(pid, &status, 0) == -1)
+                               err(EXIT_FAILURE, _("waitpid failed"));
+                       if (WIFEXITED(status))
+                               return WEXITSTATUS(status);
+                       else if (WIFSIGNALED(status))
+                               kill(getpid(), WTERMSIG(status));
+                       err(EXIT_FAILURE, _("child exit failed"));
+               }
+       }
+
        if (optind < argc) {
                execvp(argv[optind], argv + optind);
                err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]);