public inbox for util-linux@vger.kernel.org
 help / color / mirror / Atom feed
From: Mike Frysinger <vapier@gentoo.org>
To: util-linux@vger.kernel.org
Cc: ebiederm@xmission.com, mtk.manpages@gmail.com
Subject: [PATCH/RFC] unshare: add --fork/--mount-proc options for pid namespaces
Date: Thu, 27 Jun 2013 20:04:58 -0400	[thread overview]
Message-ID: <1372377898-2602-1-git-send-email-vapier@gentoo.org> (raw)

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

When it comes to pid namespaces, it's also useful for /proc to reflect
the current namespace.  Again, this is easy to pull off, but annoying
to force everyone to do it themselves.  So let's add a --mount-proc to
do the magic for us.  The downside is that this also implies creating
a mount namespace as mounting the new pid namespace /proc over top the
system one will quickly break all other processes on the system.
	$ sudo unshare --fork -p dash --mount-proc
	# ps uaxOT
	USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
	root         1  0.0  0.0   4328   632 pts/15   S    19:51   0:00 dash
	root         2  0.0  0.0  22592  1148 pts/15   R+   19:51   0:00 ps uaxOT

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

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
---
 sys-utils/unshare.1 | 10 ++++++++++
 sys-utils/unshare.c | 41 +++++++++++++++++++++++++++++++++++++----
 2 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/sys-utils/unshare.1 b/sys-utils/unshare.1
index bd0f13e..5e2c27c 100644
--- a/sys-utils/unshare.1
+++ b/sys-utils/unshare.1
@@ -63,6 +63,16 @@ Unshare the UTS namespace.
 .TP
 .BR \-U , " \-\-user"
 Unshare the user namespace.
+.TP
+.BR \-\-fork
+Fork the specified process as a child of unshare rather than running it
+directly.  This is useful when creating a new pid namespace.
+.TP
+.BR \-\-mount\-proc
+Just before running the program, mount the proc filesystem at /proc.  This
+is useful when creating a new pid namespace.  It also implies creating a
+new mount namespace since the /proc mount would otherwise mess up existing
+programs on the system.
 .SH SEE ALSO
 .BR unshare (2),
 .BR clone (2)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 8cc9c46..bc25d76 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -21,9 +21,12 @@
 #include <errno.h>
 #include <getopt.h>
 #include <sched.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
 
 #include "nls.h"
 #include "c.h"
@@ -46,6 +49,8 @@ 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(_("     --fork        fork before launching <program>\n"), out);
+	fputs(_("     --mount-proc  mount /proc first (implies --mount)\n"), out);
 
 	fputs(USAGE_SEPARATOR, out);
 	fputs(USAGE_HELP, out);
@@ -66,12 +71,17 @@ 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, 1 },
+		{ "mount-proc", no_argument, 0, 2 },
 		{ NULL, 0, 0, 0 }
 	};
 
 	int unshare_flags = 0;
 
 	int c;
+	pid_t pid;
+	bool forkit = false;
+	bool mount_proc = false;
 
 	setlocale(LC_MESSAGES, "");
 	bindtextdomain(PACKAGE, LOCALEDIR);
@@ -85,6 +95,9 @@ int main(int argc, char *argv[])
 		case 'V':
 			printf(UTIL_LINUX_VERSION);
 			return EXIT_SUCCESS;
+		case 2: /* --mount-proc */
+			mount_proc = true;
+			/* fall through */
 		case 'm':
 			unshare_flags |= CLONE_NEWNS;
 			break;
@@ -103,6 +116,9 @@ int main(int argc, char *argv[])
 		case 'U':
 			unshare_flags |= CLONE_NEWUSER;
 			break;
+		case 1: /* --fork */
+			forkit = true;
+			break;
 		default:
 			usage(EXIT_FAILURE);
 		}
@@ -111,9 +127,26 @@ int main(int argc, char *argv[])
 	if (-1 == unshare(unshare_flags))
 		err(EXIT_FAILURE, _("unshare failed"));
 
-	if (optind < argc) {
-		execvp(argv[optind], argv + optind);
-		err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]);
+	pid = forkit ? fork() : 1;
+	if (pid == 0) {
+		/* child */
+		if (mount_proc && mount("proc", "/proc", "proc", 0, NULL))
+			err(EXIT_FAILURE, _("mount(/proc) failed"));
+		if (optind < argc) {
+			execvp(argv[optind], argv + optind);
+			err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]);
+		}
+		exec_shell();
+	} else {
+		/* parent */
+		int status;
+		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));
+		/* still here !? */
+		err(EXIT_FAILURE, _("child exit failed"));
 	}
-	exec_shell();
 }
-- 
1.8.2.1


             reply	other threads:[~2013-06-28  0:05 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-06-28  0:04 Mike Frysinger [this message]
2013-07-01 11:40 ` [PATCH/RFC] unshare: add --fork/--mount-proc options for pid namespaces Karel Zak
2013-07-01 14:47   ` Mike Frysinger
2013-07-03 10:36     ` Karel Zak
2013-07-03 17:08       ` Mike Frysinger
2013-07-09  9:08         ` Karel Zak

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1372377898-2602-1-git-send-email-vapier@gentoo.org \
    --to=vapier@gentoo.org \
    --cc=ebiederm@xmission.com \
    --cc=mtk.manpages@gmail.com \
    --cc=util-linux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox