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