Allow reading the jailed process' stdout and stderr.

Also fix some nits while in there.

BUG=None
TEST=libminijail_unittest on alex and lumpy.

Change-Id: I1bd227f196618d275da6e5da4ce91e90a370baa2
Reviewed-on: https://gerrit.chromium.org/gerrit/43460
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Tested-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Commit-Queue: Jorge Lucangeli Obes <jorgelo@chromium.org>
diff --git a/libminijail.c b/libminijail.c
index bc65829..9b8d465 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -862,32 +862,64 @@
 	return 0;
 }
 
+int setup_pipe_end(int fds[2], size_t index)
+{
+	if (index > 1)
+		return -1;
+
+	close(fds[1 - index]);
+	return fds[index];
+}
+
+int setup_and_dupe_pipe_end(int fds[2], size_t index, int fd)
+{
+	if (index > 1)
+		return -1;
+
+	close(fds[1 - index]);
+	/* dup2(2) the corresponding end of the pipe into |fd|. */
+	return dup2(fds[index], fd);
+}
+
 int API minijail_run(struct minijail *j, const char *filename,
 		     char *const argv[])
 {
-	return minijail_run_pid_pipe(j, filename, argv, NULL, NULL);
+	return minijail_run_pid_pipes(j, filename, argv,
+				      NULL, NULL, NULL, NULL);
 }
 
 int API minijail_run_pid(struct minijail *j, const char *filename,
 			 char *const argv[], pid_t *pchild_pid)
 {
-	return minijail_run_pid_pipe(j, filename, argv, pchild_pid, NULL);
+	return minijail_run_pid_pipes(j, filename, argv, pchild_pid,
+				      NULL, NULL, NULL);
 }
 
 int API minijail_run_pipe(struct minijail *j, const char *filename,
 			  char *const argv[], int *pstdin_fd)
 {
-	return minijail_run_pid_pipe(j, filename, argv, NULL, pstdin_fd);
+	return minijail_run_pid_pipes(j, filename, argv, NULL, pstdin_fd,
+				      NULL, NULL);
 }
 
 int API minijail_run_pid_pipe(struct minijail *j, const char *filename,
 			      char *const argv[], pid_t *pchild_pid,
 			      int *pstdin_fd)
 {
+	return minijail_run_pid_pipes(j, filename, argv, pchild_pid, pstdin_fd,
+				      NULL, NULL);
+}
+
+int API minijail_run_pid_pipes(struct minijail *j, const char *filename,
+			      char *const argv[], pid_t *pchild_pid,
+			      int *pstdin_fd, int *pstdout_fd, int *pstderr_fd)
+{
 	char *oldenv, *oldenv_copy = NULL;
 	pid_t child_pid;
 	int pipe_fds[2];
 	int stdin_fds[2];
+	int stdout_fds[2];
+	int stderr_fds[2];
 	int ret;
 	/* We need to remember this across the minijail_preexec() call. */
 	int pid_namespace = j->flags.pids;
@@ -918,6 +950,24 @@
 			return -EFAULT;
 	}
 
+	/*
+	 * If we want to read from the child process' standard output,
+	 * create the pipe(2) now.
+	 */
+	if (pstdout_fd) {
+		if (pipe(stdout_fds))
+			return -EFAULT;
+	}
+
+	/*
+	 * If we want to read from the child process' standard error,
+	 * create the pipe(2) now.
+	 */
+	if (pstderr_fd) {
+		if (pipe(stderr_fds))
+			return -EFAULT;
+	}
+
 	/* Use sys_clone() if and only if we're creating a pid namespace.
 	 *
 	 * tl;dr: WARNING: do not mix pid namespaces and multithreading.
@@ -996,10 +1046,25 @@
 		 * If we want to write to the child process' standard input,
 		 * set up the write end of the pipe.
 		 */
-		if (pstdin_fd) {
-			close(stdin_fds[0]);	/* read endpoint */
-			*pstdin_fd = stdin_fds[1];
-		}
+		if (pstdin_fd)
+			*pstdin_fd = setup_pipe_end(stdin_fds,
+						    1	/* write end */);
+
+		/*
+		 * If we want to read from the child process' standard output,
+		 * set up the read end of the pipe.
+		 */
+		if (pstdout_fd)
+			*pstdout_fd = setup_pipe_end(stdout_fds,
+						     0	/* read end */);
+
+		/*
+		 * If we want to read from the child process' standard error,
+		 * set up the read end of the pipe.
+		 */
+		if (pstderr_fd)
+			*pstderr_fd = setup_pipe_end(stderr_fds,
+						     0	/* read end */);
 
 		return 0;
 	}
@@ -1010,12 +1075,31 @@
 	 * set up the read end of the pipe.
 	 */
 	if (pstdin_fd) {
-		close(stdin_fds[1]);	/* write endpoint */
-		/* dup2(2) the read end of the pipe into stdin. */
-		if (dup2(stdin_fds[0], 0))
+		if (setup_and_dupe_pipe_end(stdin_fds, 0 /* read end */,
+					    STDIN_FILENO) < 0)
 			die("failed to set up stdin pipe");
 	}
 
+	/*
+	 * If we want to read from the jailed process' standard output,
+	 * set up the write end of the pipe.
+	 */
+	if (pstdout_fd) {
+		if (setup_and_dupe_pipe_end(stdout_fds, 1 /* write end */,
+					    STDOUT_FILENO) < 0)
+			die("failed to set up stdout pipe");
+	}
+
+	/*
+	 * If we want to read from the jailed process' standard error,
+	 * set up the write end of the pipe.
+	 */
+	if (pstderr_fd) {
+		if (setup_and_dupe_pipe_end(stderr_fds, 1 /* write end */,
+					    STDERR_FILENO) < 0)
+			die("failed to set up stderr pipe");
+	}
+
 	/* Drop everything that cannot be inherited across execve. */
 	minijail_preexec(j);
 	/* Jail this process and its descendants... */