Merge "Remove workarounds for old versions of clang and GCC."
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);
-}
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index 6f8c403..1ee8b53 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -73,27 +73,21 @@
return g_envp;
}
-namespace testing {
-namespace internal {
+static constexpr const char* COLOR_RESET = "\033[m";
+static constexpr const char* COLOR_RED = "\033[0;31m";
+static constexpr const char* COLOR_GREEN = "\033[0;32m";
+static constexpr const char* COLOR_YELLOW = "\033[0;33m";
-// Reuse of testing::internal::ColoredPrintf in gtest.
-enum GTestColor {
- COLOR_DEFAULT,
- COLOR_RED,
- COLOR_GREEN,
- COLOR_YELLOW
-};
+static void ColoredPrintf(const char* const color, const char* fmt, ...) {
+ va_list args;
+ va_start(args, fmt);
-void ColoredPrintf(GTestColor color, const char* fmt, ...);
+ printf("%s", color);
+ vprintf(fmt, args);
+ printf("%s", COLOR_RESET);
-} // namespace internal
-} // namespace testing
-
-using testing::internal::GTestColor;
-using testing::internal::COLOR_RED;
-using testing::internal::COLOR_GREEN;
-using testing::internal::COLOR_YELLOW;
-using testing::internal::ColoredPrintf;
+ va_end(args);
+}
constexpr int DEFAULT_GLOBAL_TEST_RUN_DEADLINE_MS = 90000;
constexpr int DEFAULT_GLOBAL_TEST_RUN_SLOW_THRESHOLD_MS = 2000;
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 33514d4..6282ec3 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -22,6 +22,7 @@
#include <math.h>
#include <stdio.h>
#include <sys/types.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <wchar.h>
@@ -888,7 +889,7 @@
ASSERT_EQ(-1, fclose(fp));
}
-TEST(STDIO_TEST, popen) {
+TEST(STDIO_TEST, popen_r) {
FILE* fp = popen("cat /proc/version", "r");
ASSERT_TRUE(fp != NULL);
@@ -900,6 +901,63 @@
ASSERT_EQ(0, pclose(fp));
}
+TEST(STDIO_TEST, popen_socketpair) {
+ FILE* fp = popen("cat", "r+");
+ ASSERT_TRUE(fp != NULL);
+
+ fputs("hello\nworld\n", fp);
+ fflush(fp);
+
+ char buf[16];
+ ASSERT_NE(nullptr, fgets(buf, sizeof(buf), fp));
+ EXPECT_STREQ("hello\n", buf);
+ ASSERT_NE(nullptr, fgets(buf, sizeof(buf), fp));
+ EXPECT_STREQ("world\n", buf);
+
+ ASSERT_EQ(0, pclose(fp));
+}
+
+TEST(STDIO_TEST, popen_socketpair_shutdown) {
+ FILE* fp = popen("uniq -c", "r+");
+ ASSERT_TRUE(fp != NULL);
+
+ fputs("a\na\na\na\nb\n", fp);
+ fflush(fp);
+ ASSERT_EQ(0, shutdown(fileno(fp), SHUT_WR));
+
+ char buf[16];
+ ASSERT_NE(nullptr, fgets(buf, sizeof(buf), fp));
+ EXPECT_STREQ(" 4 a\n", buf);
+ ASSERT_NE(nullptr, fgets(buf, sizeof(buf), fp));
+ EXPECT_STREQ(" 1 b\n", buf);
+
+ ASSERT_EQ(0, pclose(fp));
+}
+
+TEST(STDIO_TEST, popen_return_value_0) {
+ FILE* fp = popen("true", "r");
+ ASSERT_TRUE(fp != NULL);
+ int status = pclose(fp);
+ EXPECT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(0, WEXITSTATUS(status));
+}
+
+TEST(STDIO_TEST, popen_return_value_1) {
+ FILE* fp = popen("false", "r");
+ ASSERT_TRUE(fp != NULL);
+ int status = pclose(fp);
+ EXPECT_TRUE(WIFEXITED(status));
+ EXPECT_EQ(1, WEXITSTATUS(status));
+}
+
+TEST(STDIO_TEST, popen_return_value_signal) {
+ FILE* fp = popen("kill -7 $$", "r");
+ ASSERT_TRUE(fp != NULL);
+ int status = pclose(fp);
+ EXPECT_TRUE(WIFSIGNALED(status));
+ EXPECT_EQ(7, WTERMSIG(status));
+}
+
TEST(STDIO_TEST, getc) {
FILE* fp = fopen("/proc/version", "r");
ASSERT_TRUE(fp != NULL);