Module Name:    src
Committed By:   christos
Date:           Tue Jan 20 17:28:00 UTC 2015

Modified Files:
        src/lib/libc/gen: Makefile.inc popen.3 popen.c

Log Message:
Factor out popen() code into separate functions and create popenve()
using the new functions, a safer version of popen() that does not
involve a shell. Correct manual page inaccuracies.


To generate a diff of this commit:
cvs rdiff -u -r1.190 -r1.191 src/lib/libc/gen/Makefile.inc
cvs rdiff -u -r1.18 -r1.19 src/lib/libc/gen/popen.3
cvs rdiff -u -r1.32 -r1.33 src/lib/libc/gen/popen.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/lib/libc/gen/Makefile.inc
diff -u src/lib/libc/gen/Makefile.inc:1.190 src/lib/libc/gen/Makefile.inc:1.191
--- src/lib/libc/gen/Makefile.inc:1.190	Wed Dec 10 11:55:54 2014
+++ src/lib/libc/gen/Makefile.inc	Tue Jan 20 12:28:00 2015
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile.inc,v 1.190 2014/12/10 16:55:54 pooka Exp $
+#	$NetBSD: Makefile.inc,v 1.191 2015/01/20 17:28:00 christos Exp $
 #	from: @(#)Makefile.inc	8.6 (Berkeley) 5/4/95
 
 # gen sources
@@ -147,6 +147,7 @@ MLINKS+=glob.3 glob_pattern_p.3
 MLINKS+=humanize_number.3 dehumanize_number.3
 MLINKS+=makecontext.3 swapcontext.3
 MLINKS+=popen.3 pclose.3
+MLINKS+=popen.3 popenve.3
 MLINKS+=posix_spawn.3 posix_spawnp.3 \
 	posix_spawn_file_actions_addopen.3 posix_spawn_file_actions_addclose.3 \
 	posix_spawn_file_actions_addopen.3 posix_spawn_file_actions_adddup2.3 \

Index: src/lib/libc/gen/popen.3
diff -u src/lib/libc/gen/popen.3:1.18 src/lib/libc/gen/popen.3:1.19
--- src/lib/libc/gen/popen.3:1.18	Mon Jun 27 04:21:07 2011
+++ src/lib/libc/gen/popen.3	Tue Jan 20 12:28:00 2015
@@ -1,4 +1,4 @@
-.\"	$NetBSD: popen.3,v 1.18 2011/06/27 08:21:07 wiz Exp $
+.\"	$NetBSD: popen.3,v 1.19 2015/01/20 17:28:00 christos Exp $
 .\"
 .\" Copyright (c) 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -29,11 +29,12 @@
 .\"
 .\"     @(#)popen.3	8.2 (Berkeley) 5/3/95
 .\"
-.Dd June 24, 2011
+.Dd January 19, 2015
 .Dt POPEN 3
 .Os
 .Sh NAME
 .Nm popen ,
+.Nm popenve ,
 .Nm pclose
 .Nd process
 .Tn I/O
@@ -43,6 +44,8 @@
 .In stdio.h
 .Ft FILE *
 .Fn popen "const char *command" "const char *type"
+.Ft FILE *
+.Fn popenve "const char *path" "char * const *argv" "char * const *envp" "const char *type"
 .Ft int
 .Fn pclose "FILE *stream"
 .Sh DESCRIPTION
@@ -93,8 +96,20 @@ using the
 .Fl c
 flag; interpretation, if any, is performed by the shell.
 .Pp
+.Pp
+The 
+.Fn popenve
+function is similar to
+.Fn popen
+but the first three arguments are passed
+to
+.Xr execve 2
+and there is no shell involved in the command invocation.
+.Pp
 The return value from
 .Fn popen
+and
+.Fn popenve
 is a normal standard
 .Tn I/O
 stream in all respects
@@ -129,12 +144,13 @@ The
 function returns
 .Dv NULL
 if the
-.Xr fork 2 ,
+.Xr vfork 2 ,
 .Xr pipe 2 ,
 or
 .Xr socketpair 2
 calls fail,
-or if it cannot allocate memory.
+or if it cannot allocate memory, preserving
+the errno from those functions.
 .Pp
 The
 .Fn pclose
@@ -147,16 +163,15 @@ command, if
 .Fa stream
 has already been
 .Dq pclosed ,
+setting errno to 
+.Dv ESRCH 
 or if
 .Xr wait4 2
-returns an error.
-.Sh ERRORS
-The
-.Fn popen
-function does not reliably set
-.Va errno .
+returns an error, preserving the errno returned by
+.Xr wait4 2.
 .Sh SEE ALSO
 .Xr sh 1 ,
+.Xr execve 2 ,
 .Xr fork 2 ,
 .Xr pipe 2 ,
 .Xr socketpair 2 ,
@@ -206,3 +221,8 @@ always calls
 .Xr sh 1 ,
 never calls
 .Xr csh 1 .
+.Pp
+The
+.Fn popenve
+function first appeared in
+.Nx 8 .

Index: src/lib/libc/gen/popen.c
diff -u src/lib/libc/gen/popen.c:1.32 src/lib/libc/gen/popen.c:1.33
--- src/lib/libc/gen/popen.c:1.32	Mon Jun 25 18:32:43 2012
+++ src/lib/libc/gen/popen.c	Tue Jan 20 12:28:00 2015
@@ -1,4 +1,4 @@
-/*	$NetBSD: popen.c,v 1.32 2012/06/25 22:32:43 abs Exp $	*/
+/*	$NetBSD: popen.c,v 1.33 2015/01/20 17:28:00 christos Exp $	*/
 
 /*
  * Copyright (c) 1988, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)popen.c	8.3 (Berkeley) 5/3/95";
 #else
-__RCSID("$NetBSD: popen.c,v 1.32 2012/06/25 22:32:43 abs Exp $");
+__RCSID("$NetBSD: popen.c,v 1.33 2015/01/20 17:28:00 christos Exp $");
 #endif
 #endif /* LIBC_SCCS and not lint */
 
@@ -77,95 +77,79 @@ static struct pid {
 static rwlock_t pidlist_lock = RWLOCK_INITIALIZER;
 #endif
 
-FILE *
-popen(const char *command, const char *type)
+static struct pid *
+pdes_get(int *pdes, const char **type)
 {
-	struct pid *cur, *old;
-	FILE *iop;
-	const char * volatile xtype = type;
-	int pdes[2], pid, serrno;
-	volatile int twoway;
-	int flags;
+	struct pid *cur;
+	int flags = strchr(*type, 'e') ? O_CLOEXEC : 0;
+	int serrno;
 
-	_DIAGASSERT(command != NULL);
-	_DIAGASSERT(xtype != NULL);
-
-	flags = strchr(xtype, 'e') ? O_CLOEXEC : 0;
-	if (strchr(xtype, '+')) {
+	if (strchr(*type, '+')) {
 		int stype = flags ? (SOCK_STREAM | SOCK_CLOEXEC) : SOCK_STREAM;
-		twoway = 1;
-		xtype = "r+";
+		*type = "r+";
 		if (socketpair(AF_LOCAL, stype, 0, pdes) < 0)
 			return NULL;
 	} else  {
-		twoway = 0;
-		xtype = strrchr(xtype, 'r') ? "r" : "w";
+		*type = strrchr(*type, 'r') ? "r" : "w";
 		if (pipe2(pdes, flags) == -1)
 			return NULL;
 	}
 
-	if ((cur = malloc(sizeof(struct pid))) == NULL) {
-		(void)close(pdes[0]);
-		(void)close(pdes[1]);
-		errno = ENOMEM;
-		return (NULL);
-	}
+	if ((cur = malloc(sizeof(*cur))) != NULL)
+		return cur;
+	serrno = errno;
+	(void)close(pdes[0]);
+	(void)close(pdes[1]);
+	errno = serrno;
+	return NULL;
+}
 
-	(void)rwlock_rdlock(&pidlist_lock);
-	(void)__readlockenv();
-	switch (pid = vfork()) {
-	case -1:			/* Error. */
-		serrno = errno;
-		(void)__unlockenv();
-		(void)rwlock_unlock(&pidlist_lock);
-		free(cur);
-		(void)close(pdes[0]);
-		(void)close(pdes[1]);
-		errno = serrno;
-		return (NULL);
-		/* NOTREACHED */
-	case 0:				/* Child. */
-		/* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
-		   from previous popen() calls that remain open in the 
-		   parent process are closed in the new child process. */
-		for (old = pidlist; old; old = old->next)
+static void
+pdes_child(int *pdes, const char *type)
+{
+	struct pid *old;
+
+	/* POSIX.2 B.3.2.2 "popen() shall ensure that any streams
+	   from previous popen() calls that remain open in the 
+	   parent process are closed in the new child process. */
+	for (old = pidlist; old; old = old->next)
 #ifdef _REENTRANT
-			close(old->fd); /* don't allow a flush */
+		(void)close(old->fd); /* don't allow a flush */
 #else
-			close(fileno(old->fp)); /* don't allow a flush */
+		(void)close(fileno(old->fp)); /* don't allow a flush */
 #endif
 
-		if (*xtype == 'r') {
-			(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 (type[0] == 'r') {
+		(void)close(pdes[0]);
+		if (pdes[1] != STDOUT_FILENO) {
+			(void)dup2(pdes[1], STDOUT_FILENO);
 			(void)close(pdes[1]);
-			if (pdes[0] != STDIN_FILENO) {
-				(void)dup2(pdes[0], STDIN_FILENO);
-				(void)close(pdes[0]);
-			}
 		}
-
-		execl(_PATH_BSHELL, "sh", "-c", command, NULL);
-		_exit(127);
-		/* NOTREACHED */
+		if (type[1] == '+')
+			(void)dup2(STDOUT_FILENO, STDIN_FILENO);
+	} else {
+		(void)close(pdes[1]);
+		if (pdes[0] != STDIN_FILENO) {
+			(void)dup2(pdes[0], STDIN_FILENO);
+			(void)close(pdes[0]);
+		}
 	}
-	(void)__unlockenv();
+}
+
+static void
+pdes_parent(int *pdes, struct pid *cur, pid_t pid, const char *type)
+{
+	FILE *iop;
 
 	/* Parent; assume fdopen can't fail. */
-	if (*xtype == 'r') {
-		iop = fdopen(pdes[0], xtype);
+	if (*type == 'r') {
+		iop = fdopen(pdes[0], type);
 #ifdef _REENTRANT
 		cur->fd = pdes[0];
 #endif
 		(void)close(pdes[1]);
 	} else {
-		iop = fdopen(pdes[1], xtype);
+		iop = fdopen(pdes[1], type);
 #ifdef _REENTRANT
 		cur->fd = pdes[1];
 #endif
@@ -177,9 +161,100 @@ popen(const char *command, const char *t
 	cur->pid =  pid;
 	cur->next = pidlist;
 	pidlist = cur;
+}
+
+static void
+pdes_error(int *pdes, struct pid *cur)
+{
+	free(cur);
+	(void)close(pdes[0]);
+	(void)close(pdes[1]);
+}
+
+FILE *
+popen(const char *cmd, const char *type)
+{
+	struct pid *cur;
+	int pdes[2], serrno;
+	pid_t pid;
+
+	_DIAGASSERT(cmd != NULL);
+	_DIAGASSERT(type != NULL);
+
+	if ((cur = pdes_get(pdes, &type)) == NULL)
+		return NULL;
+
+#ifdef _REENTRANT
+	(void)rwlock_rdlock(&pidlist_lock);
+#endif
+	(void)__readlockenv();
+	switch (pid = vfork()) {
+	case -1:			/* Error. */
+		serrno = errno;
+		(void)__unlockenv();
+#ifdef _REENTRANT
+		(void)rwlock_unlock(&pidlist_lock);
+#endif
+		errno = serrno;
+		return NULL;
+		/* NOTREACHED */
+	case 0:				/* Child. */
+		pdes_child(pdes, type);
+		execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
+		_exit(127);
+		/* NOTREACHED */
+	}
+	(void)__unlockenv();
+
+	pdes_parent(pdes, cur, pid, type);
+
+#ifdef _REENTRANT
+	(void)rwlock_unlock(&pidlist_lock);
+#endif
+
+	return cur->fp;
+}
+
+FILE *
+popenve(const char *cmd, char *const *argv, char *const *envp, const char *type)
+{
+	struct pid *cur;
+	int pdes[2], serrno;
+	pid_t pid;
+
+	_DIAGASSERT(cmd != NULL);
+	_DIAGASSERT(type != NULL);
+
+	if ((cur = pdes_get(pdes, &type)) == NULL)
+		return NULL;
+
+#ifdef _REENTRANT
+	(void)rwlock_rdlock(&pidlist_lock);
+#endif
+	switch (pid = vfork()) {
+	case -1:			/* Error. */
+		serrno = errno;
+#ifdef _REENTRANT
+		(void)rwlock_unlock(&pidlist_lock);
+#endif
+		pdes_error(pdes, cur);
+		errno = serrno;
+		return NULL;
+		/* NOTREACHED */
+	case 0:				/* Child. */
+		pdes_child(pdes, type);
+		execve(cmd, argv, envp);
+		_exit(127);
+		/* NOTREACHED */
+	}
+
+	pdes_parent(pdes, cur, pid, type);
+
+#ifdef _REENTRANT
 	(void)rwlock_unlock(&pidlist_lock);
+#endif
 
-	return (iop);
+	return cur->fp;
 }
 
 /*
@@ -196,15 +271,20 @@ pclose(FILE *iop)
 
 	_DIAGASSERT(iop != NULL);
 
+#ifdef _REENTRANT
 	rwlock_wrlock(&pidlist_lock);
+#endif
 
 	/* Find the appropriate file pointer. */
 	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
 		if (cur->fp == iop)
 			break;
 	if (cur == NULL) {
+#ifdef _REENTRANT
 		(void)rwlock_unlock(&pidlist_lock);
-		return (-1);
+#endif
+		errno = ESRCH;
+		return -1;
 	}
 
 	(void)fclose(iop);
@@ -215,7 +295,9 @@ pclose(FILE *iop)
 	else
 		last->next = cur->next;
 
+#ifdef _REENTRANT
 	(void)rwlock_unlock(&pidlist_lock);
+#endif
 
 	do {
 		pid = waitpid(cur->pid, &pstat, 0);
@@ -223,5 +305,5 @@ pclose(FILE *iop)
 
 	free(cur);
 
-	return (pid == -1 ? -1 : pstat);
+	return pid == -1 ? -1 : pstat;
 }

Reply via email to