popen.c.patch   [plain text]


--- popen.c.orig	2009-03-03 02:04:57.000000000 -0800
+++ popen.c	2009-03-03 15:28:31.000000000 -0800
@@ -34,6 +34,10 @@
  * SUCH DAMAGE.
  */
 
+#ifdef VARIANT_DARWINEXTSN
+#define _DARWIN_UNLIMITED_STREAMS
+#endif /* VARIANT_DARWINEXTSN */
+
 #if defined(LIBC_SCCS) && !defined(lint)
 static char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
 #endif /* LIBC_SCCS and not lint */
@@ -43,7 +47,8 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/pop
 #include "namespace.h"
 #include <sys/param.h>
 #include <sys/wait.h>
-
+#include <sys/socket.h>
+#include <wchar.h>		/* fwide() */
 #include <signal.h>
 #include <errno.h>
 #include <unistd.h>
@@ -52,17 +57,29 @@ __FBSDID("$FreeBSD: src/lib/libc/gen/pop
 #include <string.h>
 #include <paths.h>
 #include <pthread.h>
+#include <spawn.h>
 #include "un-namespace.h"
 #include "libc_private.h"
 
-extern char **environ;
+#include <crt_externs.h>
+#define environ (*_NSGetEnviron())
 
-static struct pid {
+/* 3516149 - store file descriptor and use that to close to prevent blocking */
+struct pid {
 	struct pid *next;
 	FILE *fp;
+	int fd;
 	pid_t pid;
-} *pidlist;
-static pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
+};
+#define pidlist		__popen_pidlist
+#define pidlist_mutex	__popen_pidlist_mutex
+#ifndef BUILDING_VARIANT
+__private_extern__ struct pid *pidlist = NULL;
+__private_extern__ pthread_mutex_t pidlist_mutex = PTHREAD_MUTEX_INITIALIZER;
+#else /* BUILDING_VARIANT */
+extern struct pid *pidlist;
+extern pthread_mutex_t pidlist_mutex;
+#endif /* !BUILDING_VARIANT */
 
 #define	THREAD_LOCK()	if (__isthreaded) _pthread_mutex_lock(&pidlist_mutex)
 #define	THREAD_UNLOCK()	if (__isthreaded) _pthread_mutex_unlock(&pidlist_mutex)
@@ -73,85 +90,109 @@ popen(command, type)
 {
 	struct pid *cur;
 	FILE *iop;
-	int pdes[2], pid, twoway;
+	int pdes[2], pid, twoway, other;
 	char *argv[4];
 	struct pid *p;
+	posix_spawn_file_actions_t file_actions;
+	int err;
 
-	/*
-	 * Lite2 introduced two-way popen() pipes using _socketpair().
-	 * FreeBSD's pipe() is bidirectional, so we use that.
-	 */
-	if (strchr(type, '+')) {
+	if (type == NULL) {
+		errno = EINVAL;
+		return (NULL);
+	}
+	if (strcmp(type, "r+") == 0) {
 		twoway = 1;
 		type = "r+";
+		if (socketpair(AF_UNIX, SOCK_STREAM, 0, pdes) < 0)
+			return (NULL);
 	} else  {
 		twoway = 0;
-		if ((*type != 'r' && *type != 'w') || type[1])
+		if ((*type != 'r' && *type != 'w') || type[1]) {
+			errno = EINVAL;
+			return (NULL);
+		}
+		if (pipe(pdes) < 0)
 			return (NULL);
 	}
-	if (pipe(pdes) < 0)
-		return (NULL);
 
-	if ((cur = malloc(sizeof(struct pid))) == NULL) {
+	/* fdopen can now fail */
+	if (*type == 'r') {
+		iop = fdopen(pdes[0], type);
+		other = pdes[1];
+	} else {
+		iop = fdopen(pdes[1], type);
+		other = pdes[0];
+	}
+	if (iop == NULL) {
 		(void)_close(pdes[0]);
 		(void)_close(pdes[1]);
 		return (NULL);
 	}
 
+	if ((cur = malloc(sizeof(struct pid))) == NULL) {
+		(void)fclose(iop);
+		(void)_close(other);
+		return (NULL);
+	}
+
+	if ((err = posix_spawn_file_actions_init(&file_actions)) != 0) {
+		(void)fclose(iop);
+		(void)_close(other);
+		free(cur);
+		errno = err;
+		return (NULL);
+	}
+	if (*type == 'r') {
+		/*
+		 * The dup2() to STDIN_FILENO is repeated to avoid
+		 * writing to pdes[1], which might corrupt the
+		 * parent's copy.  This isn't good enough in
+		 * general, since the _exit() is no return, so
+		 * the compiler is free to corrupt all the local
+		 * variables.
+		 */
+		(void)posix_spawn_file_actions_addclose(&file_actions, pdes[0]);
+		if (pdes[1] != STDOUT_FILENO) {
+			(void)posix_spawn_file_actions_adddup2(&file_actions, pdes[1], STDOUT_FILENO);
+			(void)posix_spawn_file_actions_addclose(&file_actions, pdes[1]);
+			if (twoway)
+				(void)posix_spawn_file_actions_adddup2(&file_actions, STDOUT_FILENO, STDIN_FILENO);
+		} else if (twoway && (pdes[1] != STDIN_FILENO))
+			(void)posix_spawn_file_actions_adddup2(&file_actions, pdes[1], STDIN_FILENO);
+	} else {
+		if (pdes[0] != STDIN_FILENO) {
+			(void)posix_spawn_file_actions_adddup2(&file_actions, pdes[0], STDIN_FILENO);
+			(void)posix_spawn_file_actions_addclose(&file_actions, pdes[0]);
+		}
+		(void)posix_spawn_file_actions_addclose(&file_actions, pdes[1]);
+	}
+	for (p = pidlist; p; p = p->next) {
+		(void)posix_spawn_file_actions_addclose(&file_actions, p->fd);
+	}
+
 	argv[0] = "sh";
 	argv[1] = "-c";
 	argv[2] = (char *)command;
 	argv[3] = NULL;
 
-	THREAD_LOCK();
-	switch (pid = vfork()) {
-	case -1:			/* Error. */
-		THREAD_UNLOCK();
-		(void)_close(pdes[0]);
-		(void)_close(pdes[1]);
+	err = posix_spawn(&pid, _PATH_BSHELL, &file_actions, NULL, argv, environ);
+	posix_spawn_file_actions_destroy(&file_actions);
+
+	if (err == ENOMEM || err == EAGAIN) { /* as if fork failed */
+		(void)fclose(iop);
+		(void)_close(other);
 		free(cur);
+		errno = err;
 		return (NULL);
-		/* NOTREACHED */
-	case 0:				/* Child. */
-		if (*type == 'r') {
-			/*
-			 * The _dup2() to STDIN_FILENO is repeated to avoid
-			 * writing to pdes[1], which might corrupt the
-			 * parent's copy.  This isn't good enough in
-			 * general, since the _exit() is no return, so
-			 * the compiler is free to corrupt all the local
-			 * variables.
-			 */
-			(void)_close(pdes[0]);
-			if (pdes[1] != STDOUT_FILENO) {
-				(void)_dup2(pdes[1], STDOUT_FILENO);
-				(void)_close(pdes[1]);
-				if (twoway)
-					(void)_dup2(STDOUT_FILENO, STDIN_FILENO);
-			} else if (twoway && (pdes[1] != STDIN_FILENO))
-				(void)_dup2(pdes[1], STDIN_FILENO);
-		} else {
-			if (pdes[0] != STDIN_FILENO) {
-				(void)_dup2(pdes[0], STDIN_FILENO);
-				(void)_close(pdes[0]);
-			}
-			(void)_close(pdes[1]);
-		}
-		for (p = pidlist; p; p = p->next) {
-			(void)_close(fileno(p->fp));
-		}
-		_execve(_PATH_BSHELL, argv, environ);
-		_exit(127);
-		/* NOTREACHED */
+	} else if (err != 0) { /* couldn't exec the shell */
+		pid = -1;
 	}
-	THREAD_UNLOCK();
 
-	/* Parent; assume fdopen can't fail. */
 	if (*type == 'r') {
-		iop = fdopen(pdes[0], type);
+		cur->fd = pdes[0];
 		(void)_close(pdes[1]);
 	} else {
-		iop = fdopen(pdes[1], type);
+		cur->fd = pdes[1];
 		(void)_close(pdes[0]);
 	}
 
@@ -162,10 +203,11 @@ popen(command, type)
 	cur->next = pidlist;
 	pidlist = cur;
 	THREAD_UNLOCK();
-
+	fwide(iop, -1);		/* byte stream */
 	return (iop);
 }
 
+#ifndef BUILDING_VARIANT
 /*
  * pclose --
  *	Pclose returns -1 if stream is not associated with a `popened' command,
@@ -198,6 +240,10 @@ pclose(iop)
 
 	(void)fclose(iop);
 
+	if (cur->pid < 0) {
+		free(cur);
+		return W_EXITCODE(127, 0);
+	}
 	do {
 		pid = _wait4(cur->pid, &pstat, 0, (struct rusage *)0);
 	} while (pid == -1 && errno == EINTR);
@@ -206,3 +252,4 @@ pclose(iop)
 
 	return (pid == -1 ? -1 : pstat);
 }
+#endif /* !BUILDING_VARIANT */