Reimplement popen(3)/pclose(3).
pclose(3) is now an alias for fclose(3). We could add a FORTIFY check
that you use pclose(3) if and only if you used popen(3), but there seems
little value to that when we can just do the right thing.
This patch also adds the missing locking to _fwalk --- we need to lock
both the global list of FILE*s and also each FILE* we touch. POSIX says
that "The popen() function shall ensure that any streams from previous
popen() calls that remain open in the parent process are closed in the
new child process", which we implement via _fwalk(fclose) in the child,
but we might want to just make *all* popen(3) file descriptors O_CLOEXEC
in all cases.
Ignore fewer errors in popen(3) failure cases.
Improve popen(3) test coverage.
Bug: http://b/72470344
Test: ran tests
Change-Id: Ic937594bf28ec88b375f7e5825b9c05f500af438
diff --git a/libc/Android.bp b/libc/Android.bp
index c6991d8..212a1b1 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -333,7 +333,6 @@
srcs: [
"upstream-netbsd/common/lib/libc/stdlib/random.c",
"upstream-netbsd/lib/libc/gen/nice.c",
- "upstream-netbsd/lib/libc/gen/popen.c",
"upstream-netbsd/lib/libc/gen/psignal.c",
"upstream-netbsd/lib/libc/gen/utime.c",
"upstream-netbsd/lib/libc/gen/utmp.c",
@@ -450,7 +449,6 @@
"upstream-openbsd/lib/libc/stdio/fputwc.c",
"upstream-openbsd/lib/libc/stdio/fputws.c",
"upstream-openbsd/lib/libc/stdio/fvwrite.c",
- "upstream-openbsd/lib/libc/stdio/fwalk.c",
"upstream-openbsd/lib/libc/stdio/fwide.c",
"upstream-openbsd/lib/libc/stdio/getdelim.c",
"upstream-openbsd/lib/libc/stdio/gets.c",
diff --git a/libc/NOTICE b/libc/NOTICE
index ae7da18..744f42b 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -1813,38 +1813,6 @@
-------------------------------------------------------------------
-Copyright (c) 1988, 1993
- The Regents of the University of California. All rights reserved.
-
-This code is derived from software written by Ken Arnold and
-published in UNIX Review, Vol. 6, No. 8.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions
-are met:
-1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-3. Neither the name of the University nor the names of its contributors
- may be used to endorse or promote products derived from this software
- without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGE.
-
--------------------------------------------------------------------
-
Copyright (c) 1989 The Regents of the University of California.
All rights reserved.
diff --git a/libc/bionic/thread_private.cpp b/libc/bionic/thread_private.cpp
index 1c04019..94fb8bb 100644
--- a/libc/bionic/thread_private.cpp
+++ b/libc/bionic/thread_private.cpp
@@ -31,16 +31,6 @@
// Some simple glue used to make BSD code thread-safe.
-static pthread_mutex_t g_atexit_lock = PTHREAD_MUTEX_INITIALIZER;
-
-void _thread_atexit_lock() {
- pthread_mutex_lock(&g_atexit_lock);
-}
-
-void _thread_atexit_unlock() {
- pthread_mutex_unlock(&g_atexit_lock);
-}
-
static pthread_mutex_t g_arc4_lock = PTHREAD_MUTEX_INITIALIZER;
void _thread_arc4_lock() {
diff --git a/libc/private/thread_private.h b/libc/private/thread_private.h
index 0081ad0..1f9eeb6 100644
--- a/libc/private/thread_private.h
+++ b/libc/private/thread_private.h
@@ -2,8 +2,7 @@
/* PUBLIC DOMAIN: No Rights Reserved. Marco S Hyman <marc@snafu.org> */
-#ifndef _THREAD_PRIVATE_H_
-#define _THREAD_PRIVATE_H_
+#pragma once
#include <pthread.h>
@@ -16,33 +15,15 @@
* described functions for operation in a non-threaded environment.
*/
-/*
- * helper macro to make unique names in the thread namespace
- */
-#define __THREAD_NAME(name) __CONCAT(_thread_tagname_,name)
-
-struct __thread_private_tag_t {
- pthread_mutex_t _private_lock;
- pthread_key_t _private_key;
-};
-
-#define _THREAD_PRIVATE_MUTEX(name) \
- static struct __thread_private_tag_t __THREAD_NAME(name) = { PTHREAD_MUTEX_INITIALIZER, -1 }
-#define _THREAD_PRIVATE_MUTEX_LOCK(name) \
- pthread_mutex_lock( &__THREAD_NAME(name)._private_lock )
-#define _THREAD_PRIVATE_MUTEX_UNLOCK(name) \
- pthread_mutex_unlock( &__THREAD_NAME(name)._private_lock )
+#define __MUTEX_NAME(name) __CONCAT(__libc_mutex_,name)
+#define _THREAD_PRIVATE_MUTEX(name) static pthread_mutex_t __MUTEX_NAME(name) = PTHREAD_MUTEX_INITIALIZER
+#define _THREAD_PRIVATE_MUTEX_LOCK(name) pthread_mutex_lock(&__MUTEX_NAME(name))
+#define _THREAD_PRIVATE_MUTEX_UNLOCK(name) pthread_mutex_unlock(&__MUTEX_NAME(name))
/* Note that these aren't compatible with the usual OpenBSD ones which lazy-initialize! */
#define _MUTEX_LOCK(l) pthread_mutex_lock((pthread_mutex_t*) l)
#define _MUTEX_UNLOCK(l) pthread_mutex_unlock((pthread_mutex_t*) l)
-__LIBC_HIDDEN__ void _thread_atexit_lock(void);
-__LIBC_HIDDEN__ void _thread_atexit_unlock(void);
-
-#define _ATEXIT_LOCK() _thread_atexit_lock()
-#define _ATEXIT_UNLOCK() _thread_atexit_unlock()
-
__LIBC_HIDDEN__ void _thread_arc4_lock(void);
__LIBC_HIDDEN__ void _thread_arc4_unlock(void);
@@ -53,5 +34,3 @@
extern volatile sig_atomic_t _rs_forked;
__END_DECLS
-
-#endif /* _THREAD_PRIVATE_H_ */
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 441fcfe..d306a21 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -137,6 +137,9 @@
// Equivalent to `_seek` but for _FILE_OFFSET_BITS=64.
// Callers should use this but fall back to `__sFILE::_seek`.
off64_t (*_seek64)(void*, off64_t, int);
+
+ // The pid of the child if this FILE* is from popen(3).
+ pid_t _popen_pid;
};
// Values for `__sFILE::_flags`.
@@ -201,7 +204,6 @@
__LIBC32_LEGACY_PUBLIC__ int _fwalk(int (*)(FILE*));
off64_t __sseek64(void*, off64_t, int);
-int __sflush_locked(FILE*);
int __swhatbuf(FILE*, size_t*, int*);
wint_t __fgetwc_unlock(FILE*);
wint_t __ungetwc(wint_t, FILE*);
diff --git a/libc/stdio/refill.c b/libc/stdio/refill.c
index 1df4191..cfa2bfd 100644
--- a/libc/stdio/refill.c
+++ b/libc/stdio/refill.c
@@ -40,7 +40,7 @@
lflush(FILE *fp)
{
if ((fp->_flags & (__SLBF|__SWR)) == (__SLBF|__SWR))
- return (__sflush_locked(fp)); /* ignored... */
+ return (__sflush(fp)); /* ignored... */
return (0);
}
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index e066e5b..1f08ea1 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -41,7 +41,9 @@
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
+#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <async_safe/log.h>
@@ -64,33 +66,42 @@
va_end(ap); \
return result;
-#define std(flags, file) \
- {0,0,0,flags,file,{0,0},0,__sF+file,__sclose,__sread,nullptr,__swrite, \
- {(unsigned char *)(__sFext+file), 0},nullptr,0,{0},{0},{0,0},0,0}
-
-_THREAD_PRIVATE_MUTEX(__sfp_mutex);
-
-#define SBUF_INIT {}
-#define WCHAR_IO_DATA_INIT {}
+#define MAKE_STD_STREAM(flags, fd) \
+ { \
+ ._flags = flags, ._file = fd, ._cookie = __sF + fd, ._close = __sclose, \
+ ._read = __sread, ._write = __swrite, ._ext = { \
+ ._base = reinterpret_cast<uint8_t*>(__sFext + fd) \
+ } \
+ }
static struct __sfileext __sFext[3] = {
- { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
- { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
- { SBUF_INIT, WCHAR_IO_DATA_INIT, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, false, __sseek64 },
+ {._lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
+ ._caller_handles_locking = false,
+ ._seek64 = __sseek64,
+ ._popen_pid = 0},
+ {._lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
+ ._caller_handles_locking = false,
+ ._seek64 = __sseek64,
+ ._popen_pid = 0},
+ {._lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
+ ._caller_handles_locking = false,
+ ._seek64 = __sseek64,
+ ._popen_pid = 0},
};
// __sF is exported for backwards compatibility. Until M, we didn't have symbols
// for stdin/stdout/stderr; they were macros accessing __sF.
FILE __sF[3] = {
- std(__SRD, STDIN_FILENO),
- std(__SWR, STDOUT_FILENO),
- std(__SWR|__SNBF, STDERR_FILENO),
+ MAKE_STD_STREAM(__SRD, STDIN_FILENO),
+ MAKE_STD_STREAM(__SWR, STDOUT_FILENO),
+ MAKE_STD_STREAM(__SWR|__SNBF, STDERR_FILENO),
};
FILE* stdin = &__sF[0];
FILE* stdout = &__sF[1];
FILE* stderr = &__sF[2];
+static pthread_mutex_t __stdio_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
struct glue __sglue = { nullptr, 3, __sF };
static struct glue* lastglue = &__sglue;
@@ -108,8 +119,6 @@
};
static glue* moreglue(int n) {
- static FILE empty;
-
char* data = new char[sizeof(glue) + ALIGNBYTES + n * sizeof(FILE) + n * sizeof(__sfileext)];
if (data == nullptr) return nullptr;
@@ -120,7 +129,7 @@
g->niobs = n;
g->iobs = p;
while (--n >= 0) {
- *p = empty;
+ *p = {};
_FILEEXT_SETUP(p, pext);
p++;
pext++;
@@ -143,7 +152,7 @@
int n;
struct glue *g;
- _THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
+ pthread_mutex_lock(&__stdio_mutex);
for (g = &__sglue; g != nullptr; g = g->next) {
for (fp = g->iobs, n = g->niobs; --n >= 0; fp++)
if (fp->_flags == 0)
@@ -151,15 +160,15 @@
}
/* release lock while mallocing */
- _THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
+ pthread_mutex_unlock(&__stdio_mutex);
if ((g = moreglue(NDYNAMIC)) == nullptr) return nullptr;
- _THREAD_PRIVATE_MUTEX_LOCK(__sfp_mutex);
+ pthread_mutex_lock(&__stdio_mutex);
lastglue->next = g;
lastglue = g;
fp = g->iobs;
found:
fp->_flags = 1; /* reserve this slot; caller sets real flags */
- _THREAD_PRIVATE_MUTEX_UNLOCK(__sfp_mutex);
+ pthread_mutex_unlock(&__stdio_mutex);
fp->_p = nullptr; /* no current pointer */
fp->_w = 0; /* nothing to read or write */
fp->_r = 0;
@@ -183,9 +192,20 @@
return fp;
}
-extern "C" __LIBC_HIDDEN__ void __libc_stdio_cleanup(void) {
- // Equivalent to fflush(nullptr), but without all the locking since we're shutting down anyway.
- _fwalk(__sflush);
+int _fwalk(int (*callback)(FILE*)) {
+ pthread_mutex_lock(&__stdio_mutex);
+ int result = 0;
+ for (glue* g = &__sglue; g != nullptr; g = g->next) {
+ FILE* fp = g->iobs;
+ for (int n = g->niobs; --n >= 0; ++fp) {
+ ScopedFileLock sfl(fp);
+ if (fp->_flags != 0 && (fp->_flags & __SIGN) == 0) {
+ result |= (*callback)(fp);
+ }
+ }
+ }
+ pthread_mutex_unlock(&__stdio_mutex);
+ return result;
}
static FILE* __fopen(int fd, int flags) {
@@ -383,6 +403,16 @@
if (HASUB(fp)) FREEUB(fp);
free_fgetln_buffer(fp);
+ // If we were created by popen(3), wait for the child.
+ pid_t pid = _EXT(fp)->_popen_pid;
+ if (pid > 0) {
+ int status;
+ if (TEMP_FAILURE_RETRY(wait4(pid, &status, 0, nullptr)) != -1) {
+ r = status;
+ }
+ }
+ _EXT(fp)->_popen_pid = 0;
+
// Poison this FILE so accesses after fclose will be obvious.
fp->_file = -1;
fp->_r = fp->_w = 0;
@@ -391,6 +421,7 @@
fp->_flags = 0;
return r;
}
+__strong_alias(pclose, fclose);
int fileno_unlocked(FILE* fp) {
CHECK_FP(fp);
@@ -465,11 +496,6 @@
return 0;
}
-int __sflush_locked(FILE* fp) {
- ScopedFileLock sfl(fp);
- return __sflush(fp);
-}
-
int __sread(void* cookie, char* buf, int n) {
FILE* fp = reinterpret_cast<FILE*>(cookie);
return TEMP_FAILURE_RETRY(read(fp->_file, buf, n));
@@ -707,18 +733,16 @@
return getc_unlocked(fp);
}
-/*
- * Read at most n-1 characters from the given file.
- * Stop when a newline has been read, or the count runs out.
- * Return first argument, or NULL if no characters were read.
- * Do not return NULL if n == 1.
- */
char* fgets(char* buf, int n, FILE* fp) {
CHECK_FP(fp);
ScopedFileLock sfl(fp);
return fgets_unlocked(buf, n, fp);
}
+// Reads at most n-1 characters from the given file.
+// Stops when a newline has been read, or the count runs out.
+// Returns first argument, or nullptr if no characters were read.
+// Does not return nullptr if n == 1.
char* fgets_unlocked(char* buf, int n, FILE* fp) {
if (n <= 0) {
errno = EINVAL;
@@ -1013,7 +1037,7 @@
}
static int fflush_all() {
- return _fwalk(__sflush_locked);
+ return _fwalk(__sflush);
}
int fflush(FILE* fp) {
@@ -1122,6 +1146,80 @@
return (__sfvwrite(fp, &uio) == 0) ? count : ((n - uio.uio_resid) / size);
}
+static int __close_if_popened(FILE* fp) {
+ if (_EXT(fp)->_popen_pid > 0) close(fileno(fp));
+ return 0;
+}
+
+static FILE* __popen_fail(int fds[2]) {
+ ErrnoRestorer errno_restorer;
+ close(fds[0]);
+ close(fds[1]);
+ return nullptr;
+}
+
+FILE* popen(const char* cmd, const char* mode) {
+ bool close_on_exec = (strchr(mode, 'e') != nullptr);
+
+ // Was the request for a socketpair or just a pipe?
+ int fds[2];
+ bool bidirectional = false;
+ if (strchr(mode, '+') != nullptr) {
+ if (socketpair(AF_LOCAL, SOCK_CLOEXEC | SOCK_STREAM, 0, fds) == -1) return nullptr;
+ bidirectional = true;
+ mode = "r+";
+ } else {
+ if (pipe2(fds, O_CLOEXEC) == -1) return nullptr;
+ mode = strrchr(mode, 'r') ? "r" : "w";
+ }
+
+ // If the parent wants to read, the child's fd needs to be stdout.
+ int parent, child, desired_child_fd;
+ if (*mode == 'r') {
+ parent = 0;
+ child = 1;
+ desired_child_fd = STDOUT_FILENO;
+ } else {
+ parent = 1;
+ child = 0;
+ desired_child_fd = STDIN_FILENO;
+ }
+
+ // Ensure that the child fd isn't the desired child fd.
+ if (fds[child] == desired_child_fd) {
+ int new_fd = fcntl(fds[child], F_DUPFD_CLOEXEC, 0);
+ if (new_fd == -1) return __popen_fail(fds);
+ close(fds[child]);
+ fds[child] = new_fd;
+ }
+
+ pid_t pid = vfork();
+ if (pid == -1) return __popen_fail(fds);
+
+ if (pid == 0) {
+ close(fds[parent]);
+ // POSIX says "The popen() function shall ensure that any streams from previous popen() calls
+ // that remain open in the parent process are closed in the new child process."
+ _fwalk(__close_if_popened);
+ // dup2 so that the child fd isn't closed on exec.
+ if (dup2(fds[child], desired_child_fd) == -1) _exit(127);
+ close(fds[child]);
+ if (bidirectional) dup2(STDOUT_FILENO, STDIN_FILENO);
+ execl(_PATH_BSHELL, "sh", "-c", cmd, nullptr);
+ _exit(127);
+ }
+
+ FILE* fp = fdopen(fds[parent], mode);
+ if (fp == nullptr) return __popen_fail(fds);
+
+ // The caller didn't ask for their pipe to be O_CLOEXEC, so flip it back now the child has forked.
+ if (!close_on_exec) fcntl(fds[parent], F_SETFD, 0);
+ close(fds[child]);
+
+ _EXT(fp)->_popen_pid = pid;
+ return fp;
+}
+
namespace {
namespace phony {
diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c
index c817b63..a26bee4 100644
--- a/libc/stdlib/atexit.c
+++ b/libc/stdlib/atexit.c
@@ -29,18 +29,23 @@
*
*/
-#include <sys/types.h>
-#include <sys/mman.h>
+#include "atexit.h"
+
+#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#include "atexit.h"
-#include "private/thread_private.h"
+#include <sys/mman.h>
+#include <sys/types.h>
/* BEGIN android-changed */
#include "private/bionic_prctl.h"
/* END android-changed */
+static pthread_mutex_t g_atexit_lock = PTHREAD_MUTEX_INITIALIZER;
+#define _ATEXIT_LOCK() pthread_mutex_lock(&g_atexit_lock)
+#define _ATEXIT_UNLOCK() pthread_mutex_unlock(&g_atexit_lock)
+
struct atexit {
struct atexit *next; /* next in list */
int ind; /* next index in this table */
@@ -79,15 +84,14 @@
int
__cxa_atexit(void (*func)(void *), void *arg, void *dso)
{
- struct atexit *p = __atexit;
struct atexit_fn *fnp;
size_t pgsize = getpagesize();
int ret = -1;
- if (pgsize < sizeof(*p))
+ if (pgsize < sizeof(struct atexit))
return (-1);
_ATEXIT_LOCK();
- p = __atexit;
+ struct atexit *p = __atexit;
if (p != NULL) {
if (p->ind + 1 >= p->max)
p = NULL;
@@ -185,8 +189,7 @@
}
_ATEXIT_UNLOCK();
- extern void __libc_stdio_cleanup(void);
- __libc_stdio_cleanup();
+ fflush(NULL);
/* BEGIN android-changed: call __unregister_atfork if dso is not null */
if (dso != NULL) {
diff --git a/libc/upstream-netbsd/lib/libc/gen/popen.c b/libc/upstream-netbsd/lib/libc/gen/popen.c
deleted file mode 100644
index 593e346..0000000
--- a/libc/upstream-netbsd/lib/libc/gen/popen.c
+++ /dev/null
@@ -1,227 +0,0 @@
-/* $NetBSD: popen.c,v 1.32 2012/06/25 22:32:43 abs Exp $ */
-
-/*
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software written by Ken Arnold and
- * published in UNIX Review, Vol. 6, No. 8.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <sys/cdefs.h>
-#if defined(LIBC_SCCS) && !defined(lint)
-#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 $");
-#endif
-#endif /* LIBC_SCCS and not lint */
-
-#include "namespace.h"
-#include <sys/param.h>
-#include <sys/wait.h>
-#include <sys/socket.h>
-
-#include <assert.h>
-#include <errno.h>
-#include <paths.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#include "env.h"
-#include "reentrant.h"
-
-#ifdef __weak_alias
-__weak_alias(popen,_popen)
-__weak_alias(pclose,_pclose)
-#endif
-
-static struct pid {
- struct pid *next;
- FILE *fp;
-#ifdef _REENTRANT
- int fd;
-#endif
- pid_t pid;
-} *pidlist;
-
-#ifdef _REENTRANT
-static rwlock_t pidlist_lock = RWLOCK_INITIALIZER;
-#endif
-
-FILE *
-popen(const char *command, const char *type)
-{
- struct pid *cur, *old;
- FILE *iop;
- const char * volatile xtype = type;
- int pdes[2], pid, serrno;
- volatile int twoway;
- int flags;
-
- _DIAGASSERT(command != NULL);
- _DIAGASSERT(xtype != NULL);
-
- flags = strchr(xtype, 'e') ? O_CLOEXEC : 0;
- if (strchr(xtype, '+')) {
- int stype = flags ? (SOCK_STREAM | SOCK_CLOEXEC) : SOCK_STREAM;
- twoway = 1;
- xtype = "r+";
- if (socketpair(AF_LOCAL, stype, 0, pdes) < 0)
- return NULL;
- } else {
- twoway = 0;
- xtype = strrchr(xtype, '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);
- }
-
- (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)
-#ifdef _REENTRANT
- close(old->fd); /* don't allow a flush */
-#else
- 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 {
- (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 */
- }
- (void)__unlockenv();
-
- /* Parent; assume fdopen can't fail. */
- if (*xtype == 'r') {
- iop = fdopen(pdes[0], xtype);
-#ifdef _REENTRANT
- cur->fd = pdes[0];
-#endif
- (void)close(pdes[1]);
- } else {
- iop = fdopen(pdes[1], xtype);
-#ifdef _REENTRANT
- cur->fd = pdes[1];
-#endif
- (void)close(pdes[0]);
- }
-
- /* Link into list of file descriptors. */
- cur->fp = iop;
- cur->pid = pid;
- cur->next = pidlist;
- pidlist = cur;
- (void)rwlock_unlock(&pidlist_lock);
-
- return (iop);
-}
-
-/*
- * pclose --
- * Pclose returns -1 if stream is not associated with a `popened' command,
- * if already `pclosed', or waitpid returns an error.
- */
-int
-pclose(FILE *iop)
-{
- struct pid *cur, *last;
- int pstat;
- pid_t pid;
-
- _DIAGASSERT(iop != NULL);
-
- rwlock_wrlock(&pidlist_lock);
-
- /* Find the appropriate file pointer. */
- for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
- if (cur->fp == iop)
- break;
- if (cur == NULL) {
- (void)rwlock_unlock(&pidlist_lock);
- return (-1);
- }
-
- (void)fclose(iop);
-
- /* Remove the entry from the linked list. */
- if (last == NULL)
- pidlist = cur->next;
- else
- last->next = cur->next;
-
- (void)rwlock_unlock(&pidlist_lock);
-
- do {
- pid = waitpid(cur->pid, &pstat, 0);
- } while (pid == -1 && errno == EINTR);
-
- free(cur);
-
- return (pid == -1 ? -1 : pstat);
-}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/fwalk.c b/libc/upstream-openbsd/lib/libc/stdio/fwalk.c
deleted file mode 100644
index 4b1aa43..0000000
--- a/libc/upstream-openbsd/lib/libc/stdio/fwalk.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/* $OpenBSD: fwalk.c,v 1.12 2016/05/23 00:21:48 guenther Exp $ */
-/*-
- * Copyright (c) 1990, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Chris Torek.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include "local.h"
-#include "glue.h"
-
-int
-_fwalk(int (*function)(FILE *))
-{
- FILE *fp;
- int n, ret;
- struct glue *g;
-
- ret = 0;
- for (g = &__sglue; g != NULL; g = g->next)
- for (fp = g->iobs, n = g->niobs; --n >= 0; fp++) {
- if ((fp->_flags != 0) && ((fp->_flags & __SIGN) == 0))
- ret |= (*function)(fp);
- }
- return (ret);
-}