public inbox for util-linux@vger.kernel.org
 help / color / mirror / Atom feed
From: Karel Zak <kzak@redhat.com>
To: Mike Frysinger <vapier@gentoo.org>
Cc: util-linux@vger.kernel.org, ebiederm@xmission.com,
	mtk.manpages@gmail.com
Subject: Re: [PATCH/RFC] unshare: add --fork/--mount-proc options for pid namespaces
Date: Mon, 1 Jul 2013 13:40:08 +0200	[thread overview]
Message-ID: <20130701114008.GA1946@x2.net.home> (raw)
In-Reply-To: <1372377898-2602-1-git-send-email-vapier@gentoo.org>

On Thu, Jun 27, 2013 at 08:04:58PM -0400, Mike Frysinger wrote:
> 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.  

 This is not so easy. For example on Fedora 18 the default is "shared":

 # grep /proc /proc/self/mountinfo
 14 33 0:3 / /proc rw,nosuid,nodev,noexec,relatime shared:5 - proc proc rw
 
 it means that unshare( CLONE_NEWNS ) has no expected effect and the
 following mount(/proc) has horrible impact for all system. You have
 to use (for example):

    mount --make-rprivate /proc
    unshare --fork --mount-proc --pid

 The --fork option makes sense, but I have doubts about --mount-proc.

 It would be better to keep unshare(1) simple and stupid rather than
 expect that we can setup usable container by the util.

 It's easy to call "mount proc /proc -t proc" after "unshare --pid
 --mount --fork".

> -	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();
>  }

 I guess it's bug that exec_shell() only when forkit is set and
 waitpid() is called always.

 See the patch below. 

    Karel



>From 535b754f0003b60b9d7c0358cc4366fae169dd19 Mon Sep 17 00:00:00 2001
From: Mike Frysinger <vapier@gentoo.org>
Date: Thu, 27 Jun 2013 20:04:58 -0400
Subject: [PATCH] unshare: add --fork options for pid namespaces

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 |  6 +++++-
 sys-utils/unshare.c | 31 ++++++++++++++++++++++++++++---
 2 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/sys-utils/unshare.1 b/sys-utils/unshare.1
index bd0f13e..c387ceb 100644
--- a/sys-utils/unshare.1
+++ b/sys-utils/unshare.1
@@ -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)
diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 8cc9c46..a889eee 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -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]);
-- 
1.8.1.4


  reply	other threads:[~2013-07-01 11:40 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-06-28  0:04 [PATCH/RFC] unshare: add --fork/--mount-proc options for pid namespaces Mike Frysinger
2013-07-01 11:40 ` Karel Zak [this message]
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=20130701114008.GA1946@x2.net.home \
    --to=kzak@redhat.com \
    --cc=ebiederm@xmission.com \
    --cc=mtk.manpages@gmail.com \
    --cc=util-linux@vger.kernel.org \
    --cc=vapier@gentoo.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