Greatly expand system() shell code features

The cosmopolitan command interpreter now has 13 builtin commands,
variable support, support for ; / && / || syntax, asynchronous support,
and plenty of unit tests with bug fixes.

This change fixes a bug in posix_spawn() with null envp arg. strace
logging now uses atomic writes for scatter functions. Breaking change
renaming GetCpuCount() to _getcpucount(). TurfWar is now updated to use
the new token bucket algorithm. WIN32 affinity masks now inherit across
fork() and execve().
This commit is contained in:
Justine Tunney 2022-10-11 21:06:27 -07:00
parent e7329b7cba
commit b41f91c658
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
80 changed files with 1370 additions and 344 deletions

View file

@ -121,6 +121,7 @@ include third_party/compiler_rt/compiler_rt.mk # │
include libc/str/str.mk # │
include third_party/xed/xed.mk # │
include third_party/zlib/zlib.mk # │
include third_party/double-conversion/dc.mk # │
include libc/elf/elf.mk # │
include ape/ape.mk # │
include libc/fmt/fmt.mk # │
@ -168,7 +169,7 @@ include test/tool/args/test.mk
include third_party/linenoise/linenoise.mk
include third_party/maxmind/maxmind.mk
include net/finger/finger.mk
include third_party/double-conversion/double-conversion.mk
include third_party/double-conversion/test/test.mk
include third_party/lua/lua.mk
include third_party/sed/sed.mk
include third_party/awk/awk.mk

View file

@ -73,6 +73,7 @@ EXAMPLES_DIRECTDEPS = \
NET_HTTPS \
THIRD_PARTY_COMPILER_RT \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_DOUBLECONVERSION \
THIRD_PARTY_GDTOA \
THIRD_PARTY_GETOPT \
THIRD_PARTY_LIBCXX \

View file

@ -8,8 +8,8 @@
*/
#endif
#include "libc/intrin/kprintf.h"
#include "libc/runtime/sysconf.h"
#include "libc/runtime/runtime.h"
int main() {
kprintf("%d\n", GetCpuCount());
kprintf("%d\n", _getcpucount());
}

View file

@ -28,7 +28,6 @@
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/sysconf.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/sock/struct/sockaddr.h"
@ -299,7 +298,7 @@ int main(int argc, char *argv[]) {
PORT);
}
threads = argc > 1 ? atoi(argv[1]) : GetCpuCount();
threads = argc > 1 ? atoi(argv[1]) : _getcpucount();
if (!(1 <= threads && threads <= 100000)) {
kprintf("error: invalid number of threads: %d\n", threads);
exit(1);

View file

@ -99,7 +99,7 @@ textwindows int sys_execve_nt(const char *program, char *const argv[],
startinfo.hStdError = __getfdhandleactual(2);
// spawn the process
rc = ntspawn(program, argv, envp, 0, 0, 0, 1, 0, 0, &startinfo, &procinfo);
rc = ntspawn(program, argv, envp, 0, 0, 0, true, 0, 0, &startinfo, &procinfo);
if (rc == -1) {
STRACE("panic: unrecoverable ntspawn(%#s) error: %m", program);
__imp_ExitProcess(6543);

View file

@ -34,7 +34,7 @@
#define HW_NCPUONLINE_NETBSD 16
#define ALL_PROCESSOR_GROUPS 0xffff
static unsigned GetCpuCountLinux(void) {
static unsigned _getcpucount_linux(void) {
cpu_set_t s = {0};
if (sys_sched_getaffinity(0, sizeof(s), &s) != -1) {
return CPU_COUNT(&s);
@ -43,7 +43,7 @@ static unsigned GetCpuCountLinux(void) {
}
}
static unsigned GetCpuCountBsd(void) {
static unsigned _getcpucount_bsd(void) {
size_t n;
int c, cmd[2];
n = sizeof(c);
@ -62,12 +62,12 @@ static unsigned GetCpuCountBsd(void) {
}
}
static unsigned GetCpuCountImpl(void) {
static unsigned _getcpucount_impl(void) {
if (!IsWindows()) {
if (!IsBsd()) {
return GetCpuCountLinux();
return _getcpucount_linux();
} else {
return GetCpuCountBsd();
return _getcpucount_bsd();
}
} else {
return GetMaximumProcessorCount(ALL_PROCESSOR_GROUPS);
@ -77,18 +77,24 @@ static unsigned GetCpuCountImpl(void) {
static int g_cpucount;
// precompute because process affinity on linux may change later
__attribute__((__constructor__)) static void init(void) {
g_cpucount = GetCpuCountImpl();
__attribute__((__constructor__)) static void _getcpucount_init(void) {
g_cpucount = _getcpucount_impl();
}
/**
* Returns number of CPUs in system.
*
* This is the same as the standard interface:
*
* sysconf(_SC_NPROCESSORS_ONLN);
*
* Except this function isn't a bloated diamond dependency.
*
* On Intel systems with HyperThreading this will return the number of
* cores multiplied by two.
*
* @return cpu count or 0 if it couldn't be determined
*/
unsigned GetCpuCount(void) {
unsigned _getcpucount(void) {
return g_cpucount;
}

View file

@ -20,10 +20,10 @@
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
#include "libc/thread/thread.h"
#include "libc/macros.internal.h"
#include "libc/nt/accounting.h"
#include "libc/runtime/sysconf.h"
#include "libc/runtime/runtime.h"
#include "libc/thread/thread.h"
#define FT(x) (x.dwLowDateTime | (uint64_t)x.dwHighDateTime << 32)
@ -60,7 +60,7 @@ static textstartup void sys_getloadavg_nt_init(void) {
double a[3];
if (IsWindows()) {
load = 1;
cpus = GetCpuCount() / 2;
cpus = _getcpucount() / 2;
cpus = MAX(1, cpus);
GetSystemTimes(&idle1, &kern1, &user1);
}

View file

@ -16,10 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/pushpop.h"
#include "libc/calls/ntspawn.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/pushpop.h"
#include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h"
#include "libc/nt/enum/filemapflags.h"
#include "libc/nt/enum/pageflags.h"
@ -87,7 +88,8 @@ textwindows int ntspawn(
mkntenvblock(block->envvars, envp, extravar, block->buf) != -1 &&
CreateProcess(prog16, block->cmdline, opt_lpProcessAttributes,
opt_lpThreadAttributes, bInheritHandles,
dwCreationFlags | kNtCreateUnicodeEnvironment,
dwCreationFlags | kNtCreateUnicodeEnvironment |
kNtInheritParentAffinity,
block->envvars, opt_lpCurrentDirectory, lpStartupInfo,
opt_out_lpProcessInformation)) {
rc = 0;

View file

@ -129,12 +129,7 @@ static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
ssize_t preadv(int fd, struct iovec *iov, int iovlen, int64_t off) {
ssize_t rc;
rc = Preadv(fd, iov, iovlen, off);
#if defined(SYSDEBUG) && _DATATRACE
if (UNLIKELY(__strace > 0)) {
kprintf(STRACE_PROLOGUE "preadv(%d, [", fd);
DescribeIov(iov, iovlen, rc != -1 ? rc : 0);
kprintf("], %d, %'ld) → %'ld% m\n", iovlen, off, rc);
}
#endif
STRACE("preadv(%d, [%s], %d, %'ld) → %'ld% m", fd,
DescribeIovec(rc, iov, iovlen), iovlen, off, rc);
return rc;
}

View file

@ -135,12 +135,7 @@ static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen,
ssize_t pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) {
ssize_t rc;
rc = Pwritev(fd, iov, iovlen, off);
#if defined(SYSDEBUG) && _DATATRACE
if (UNLIKELY(__strace > 0)) {
kprintf(STRACE_PROLOGUE "pwritev(%d, ", fd);
DescribeIov(iov, iovlen, rc != -1 ? rc : 0);
kprintf(", %d, %'ld) → %'ld% m\n", iovlen, off, rc);
}
#endif
STRACE("pwritev(%d, %s, %d, %'ld) → %'ld% m", fd,
DescribeIovec(rc != -1 ? rc : -2, iov, iovlen), iovlen, off, rc);
return rc;
}

View file

@ -73,17 +73,7 @@ ssize_t readv(int fd, const struct iovec *iov, int iovlen) {
rc = einval();
}
#if defined(SYSDEBUG) && _DATATRACE
if (UNLIKELY(__strace > 0)) {
if (rc == -1 && errno == EFAULT) {
STRACE("readv(%d, %p, %d) → %'zd% m", fd, iov, iovlen, rc);
} else {
kprintf(STRACE_PROLOGUE "readv(%d, [", fd);
DescribeIov(iov, iovlen, rc != -1 ? rc : 0);
kprintf("], %d) → %'ld% m\n", iovlen, rc);
}
}
#endif
STRACE("readv(%d, [%s], %d) → %'ld% m", fd, DescribeIovec(rc, iov, iovlen),
iovlen, rc);
return rc;
}

View file

@ -60,6 +60,8 @@ static dontinline textwindows int sys_sched_setaffinity_nt(
/**
* Asks kernel to only schedule process on particular CPUs.
*
* Affinity masks are inherited across fork() and execve() boundaries.
*
* @param pid is the process or process id (or 0 for caller)
* @param size is bytes in bitset, which should be `sizeof(cpuset_t)`
* @return 0 on success, or -1 w/ errno

View file

@ -29,6 +29,7 @@
*
* @return 0 on success, or -1 w/ errno
* @raise ENOSYS on XNU, Windows, OpenBSD
* @vforksafe
*/
int sched_setparam(int pid, const struct sched_param *param) {
int rc, policy;

View file

@ -91,6 +91,7 @@
* @raise EINVAL if `param` is NULL
* @raise EINVAL if `policy` is invalid
* @raise EINVAL if `param` has value out of ranges defined by `policy`
* @vforksafe
*/
int sched_setscheduler(int pid, int policy, const struct sched_param *param) {
int rc, old;

View file

@ -13,7 +13,6 @@ ssize_t pwritev(int, const struct iovec *, int, int64_t);
ssize_t readv(int, const struct iovec *, int);
ssize_t vmsplice(int, const struct iovec *, int64_t, uint32_t);
ssize_t writev(int, const struct iovec *, int);
void DescribeIov(const struct iovec *, int, ssize_t);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -2,6 +2,7 @@
#define COSMOPOLITAN_LIBC_CALLS_STRUCT_IOVEC_INTERNAL_H_
#include "libc/calls/struct/fd.internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/mem/alloca.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
@ -24,6 +25,9 @@ ssize_t sys_send_nt(int, const struct iovec *, size_t, uint32_t) hidden;
ssize_t sys_sendto_nt(int, const struct iovec *, size_t, uint32_t, void *,
uint32_t) hidden;
const char *DescribeIovec(char[300], ssize_t, const struct iovec *, int);
#define DescribeIovec(x, y, z) DescribeIovec(alloca(300), x, y, z)
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_IOVEC_INTERNAL_H_ */

View file

@ -16,7 +16,7 @@ int sys_fchmodat_nt(int, const char *, uint32_t, int) hidden;
int sys_fcntl_nt(int, int, uintptr_t) hidden;
int sys_fdatasync_nt(int) hidden;
int sys_flock_nt(int, int) hidden;
int sys_fork_nt(void) hidden;
int sys_fork_nt(uint32_t) hidden;
int sys_ftruncate_nt(int64_t, uint64_t) hidden;
int sys_getloadavg_nt(double *, int) hidden;
int sys_getppid_nt(void) hidden;

View file

@ -24,6 +24,7 @@ void cosmo2flock(uintptr_t) hidden;
void flock2cosmo(uintptr_t) hidden;
int _ptsname(int, char *, size_t) hidden;
int _isptmaster(int) hidden;
int _fork(uint32_t) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -77,13 +77,7 @@ ssize_t writev(int fd, const struct iovec *iov, int iovlen) {
rc = einval();
}
#if defined(SYSDEBUG) && _DATATRACE
if (UNLIKELY(__strace > 0)) {
kprintf(STRACE_PROLOGUE "writev(%d, ", fd);
DescribeIov(iov, iovlen, rc != -1 ? rc : 0);
kprintf(", %d) → %'ld% m\n", iovlen, rc);
}
#endif
STRACE("writev(%d, %s, %d) → %'ld% m", fd,
DescribeIovec(rc != -1 ? rc : -2, iov, iovlen), iovlen, rc);
return rc;
}

View file

@ -45,6 +45,8 @@
* on the the prefixes 0 (octal), 0x (hexadecimal), 0b (binary), or
* decimal (base 10) by default
* @return the decoded signed saturated number
* @raise EINVAL if `base` isn't 0 or 2..36
* @raise ERANGE on overflow
*/
long strtol(const char *s, char **endptr, int base) {
char t = 0;

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_FMT_STRTOL_H_
#define COSMOPOLITAN_LIBC_FMT_STRTOL_H_
#include "libc/errno.h"
#define CONSUME_SPACES(s, c) \
if (endptr) *endptr = s; \
@ -10,7 +11,7 @@
if (c == '-' || c == '+') c = *++s
#define GET_RADIX(s, c, r) \
if (!(2 <= r && r <= 36)) { \
if (!r) { \
if (c == '0') { \
t |= 1; \
c = *++s; \
@ -26,6 +27,9 @@
} else { \
r = 10; \
} \
} else if (!(2 <= r && r <= 36)) { \
errno = EINVAL; \
return 0; \
} else if (c == '0') { \
t |= 1; \
c = *++s; \

View file

@ -19,6 +19,7 @@
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/strtol.internal.h"
#include "libc/limits.h"
#include "libc/str/str.h"
#include "libc/str/tab.internal.h"
@ -45,8 +46,12 @@ unsigned long wcstoul(const wchar_t *s, wchar_t **endptr, int base) {
if ((c = kBase36[c & 255]) && --c < base) {
t |= 1;
do {
x *= base;
x += c;
if (__builtin_mul_overflow(x, base, &x) ||
__builtin_add_overflow(x, c, &x)) {
if (endptr) *endptr = s + 1;
errno = ERANGE;
return ULONG_MAX;
}
} while ((c = kBase36[*++s & 255]) && --c < base);
}
if (t && endptr) *endptr = s;

View file

@ -16,10 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/strace.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/process.h"
#include "libc/nt/runtime.h"
#include "libc/nt/thunk/msabi.h"
__msabi extern typeof(CreateProcess) *const __imp_CreateProcessW;

View file

@ -0,0 +1,68 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2021 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/limits.h"
#include "libc/macros.internal.h"
#define N 300
#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__)
const char *(DescribeIovec)(char buf[N], ssize_t rc, const struct iovec *iov,
int iovlen) {
const char *d;
int i, j, o = 0;
if (!iov) return "NULL";
if (rc == -1) return "n/a";
if (rc == -2) rc = SSIZE_MAX;
if ((!IsAsan() && kisdangerous(iov)) ||
(IsAsan() && !__asan_is_valid(iov, sizeof(*iov) * iovlen))) {
ksnprintf(buf, N, "%p", iov);
return buf;
}
append("{");
for (i = 0; rc && i < iovlen; ++i) {
if (iov[i].iov_len < rc) {
j = iov[i].iov_len;
rc -= iov[i].iov_len;
} else {
j = rc;
rc = 0;
}
if (j > 40) {
j = 40;
d = "...";
} else {
d = "";
}
append("%s{%#.*hhs%s, %'zu}", i ? ", " : "", j, iov[i].iov_base, d,
iov[i].iov_len);
}
append("%s}", iovlen > 2 ? ", ..." : "");
return buf;
}

5
libc/isystem/monetary.h Normal file
View file

@ -0,0 +1,5 @@
#ifndef COSMOPOLITAN_LIBC_ISYSTEM_MONETARY_H_
#define COSMOPOLITAN_LIBC_ISYSTEM_MONETARY_H_
#include "libc/str/locale.h"
#include "libc/str/str.h"
#endif /* COSMOPOLITAN_LIBC_ISYSTEM_MONETARY_H_ */

View file

@ -2,6 +2,7 @@
#define LIBC_ISYSTEM_SYS_SOCKET_H_
#include "libc/calls/weirdtypes.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/cmsghdr.h"
#include "libc/sock/struct/linger.h"
#include "libc/sock/struct/msghdr.h"
#include "libc/sock/struct/sockaddr.h"

View file

@ -16,27 +16,32 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/mem/internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/errfuns.h"
/**
* Copies variable to environment.
*
* @return 0 on success, or -1 w/ errno
* @raise EINVAL if `name` is empty or contains `'='`
* @raise ENOMEM if we require more vespene gas
* @see putenv(), getenv()
*/
int setenv(const char *name, const char *value, int overwrite) {
int rc;
char *s;
size_t namelen, valuelen;
if (isempty(name) || strchr(name, '=')) return einval();
namelen = strlen(name);
valuelen = strlen(value);
s = malloc(namelen + valuelen + 2);
if (!(s = malloc(namelen + valuelen + 2))) return -1;
memcpy(mempcpy(mempcpy(s, name, namelen), "=", 1), value, valuelen + 1);
rc = PutEnvImpl(s, overwrite);
STRACE("setenv(%#s, %#s, %d) → %d", name, value, overwrite, rc);
STRACE("setenv(%#s, %#s, %d) → %d% m", name, value, overwrite, rc);
return rc;
}

View file

@ -16,39 +16,51 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/nt/enum/processcreationflags.h"
#include "libc/paths.h"
#include "libc/runtime/runtime.h"
#include "libc/sysv/consts/o.h"
/**
* Daemonizes process.
* Runs process in background.
*
* On Unix this calls fork() and setsid(). On Windows this is
* implemented using CreateProcess(kNtDetachedProcess).
*
* @return 0 on success, or -1 w/ errno
*/
int daemon(int nochdir, int noclose) {
int fd;
switch (fork()) {
switch (_fork(kNtDetachedProcess)) {
case -1:
return (-1);
return -1;
case 0:
break;
default:
_Exit(0);
}
if (setsid() == -1) {
return -1;
if (!IsWindows()) {
if (setsid() == -1) {
return -1;
}
}
if (!nochdir) {
chdir("/");
_unassert(!chdir("/"));
}
if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR)) != -1) {
dup2(fd, 0);
dup2(fd, 1);
dup2(fd, 2);
_unassert(dup2(fd, 0) == 0);
_unassert(dup2(fd, 1) == 1);
_unassert(dup2(fd, 2) == 2);
if (fd > 2) {
close(fd);
_unassert(!close(fd));
}
}

View file

@ -19,6 +19,7 @@
#include "libc/calls/internal.h"
#include "libc/calls/ntspawn.h"
#include "libc/calls/state.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall_support-nt.internal.h"
#include "libc/calls/wincrash.internal.h"
#include "libc/errno.h"
@ -260,7 +261,7 @@ textwindows void WinMainForked(void) {
longjmp(jb, 1);
}
textwindows int sys_fork_nt(void) {
textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
bool ok;
jmp_buf jb;
uint32_t oldprot;
@ -302,7 +303,7 @@ textwindows int sys_fork_nt(void) {
}
#endif
if (ntspawn(GetProgramExecutableName(), args, environ, forkvar, 0, 0,
true, 0, 0, &startinfo, &procinfo) != -1) {
true, dwCreationFlags, 0, &startinfo, &procinfo) != -1) {
CloseHandle(procinfo.hThread);
ok = WriteAll(writer, jb, sizeof(jb)) &&
WriteAll(writer, &_mmi.i, sizeof(_mmi.i)) &&

View file

@ -21,6 +21,7 @@
#include "libc/calls/struct/sigset.internal.h"
#include "libc/calls/syscall-nt.internal.h"
#include "libc/calls/syscall-sysv.internal.h"
#include "libc/calls/syscall_support-sysv.internal.h"
#include "libc/dce.h"
#include "libc/intrin/strace.internal.h"
#include "libc/nt/process.h"
@ -28,15 +29,7 @@
#include "libc/sysv/consts/sig.h"
#include "libc/thread/tls.h"
/**
* Creates new process.
*
* @return 0 to child, child pid to parent, or -1 w/ errno
* @raise EAGAIN if `RLIMIT_NPROC` was exceeded or system lacked resources
* @raise ENOMEM if we require more vespene gas
* @asyncsignalsafe
*/
int fork(void) {
int _fork(uint32_t dwCreationFlags) {
axdx_t ad;
sigset_t old, all;
int ax, dx, parent;
@ -52,7 +45,7 @@ int fork(void) {
ax &= dx - 1;
}
} else {
ax = sys_fork_nt();
ax = sys_fork_nt(dwCreationFlags);
}
if (!ax) {
if (!IsWindows()) {
@ -73,3 +66,15 @@ int fork(void) {
}
return ax;
}
/**
* Creates new process.
*
* @return 0 to child, child pid to parent, or -1 w/ errno
* @raise EAGAIN if `RLIMIT_NPROC` was exceeded or system lacked resources
* @raise ENOMEM if we require more vespene gas
* @asyncsignalsafe
*/
int fork(void) {
return _fork(0);
}

View file

@ -103,6 +103,7 @@ void _intsort(int *, size_t);
void _longsort(long *, size_t);
bool _isheap(void *);
int NtGetVersion(void) pureconst;
unsigned _getcpucount(void) pureconst;
long _missingno();
void __oom_hook(size_t);
void _loadxmm(void *);

View file

@ -49,7 +49,7 @@ long sysconf(int name) {
case _SC_PAGESIZE:
return FRAMESIZE;
case _SC_NPROCESSORS_ONLN:
n = GetCpuCount();
n = _getcpucount();
return n > 0 ? n : -1;
default:
return -1;

View file

@ -147,7 +147,6 @@
COSMOPOLITAN_C_START_
long sysconf(int);
unsigned GetCpuCount(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -35,7 +35,7 @@
// @return pid of child process or 0 if forked process
// @returnstwice
// @vforksafe
vfork:
vfork: xor %edi,%edi # dwCreationFlags
#ifdef __SANITIZE_ADDRESS__
jmp fork # TODO: asan and vfork don't mix?
.endfn vfork,globl

View file

@ -47,7 +47,7 @@
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
*/
ssize_t recvfrom(int fd, void *buf, size_t size, uint32_t flags,
ssize_t recvfrom(int fd, void *buf, size_t size, int flags,
struct sockaddr *opt_out_srcaddr,
uint32_t *opt_inout_srcaddrsize) {
ssize_t rc;

View file

@ -32,6 +32,8 @@
/**
* Sends a message from a socket.
*
* Note: Ancillary data currently isn't polyfilled across platforms.
*
* @param fd is the file descriptor returned by socket()
* @param msg is a pointer to a struct msghdr containing all the allocated
* buffers where to store incoming data.
@ -46,6 +48,7 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) {
ssize_t rc, got;
struct msghdr msg2;
union sockaddr_storage_bsd bsd;
if (IsAsan() && !__asan_is_valid_msghdr(msg)) {
rc = efault();
} else if (!IsWindows()) {
@ -88,6 +91,7 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) {
} else {
rc = ebadf();
}
#if defined(SYSDEBUG) && _DATATRACE
if (__strace > 0) {
if (!msg || (rc == -1 && errno == EFAULT)) {
@ -99,11 +103,11 @@ ssize_t recvmsg(int fd, struct msghdr *msg, int flags) {
if (msg->msg_controllen)
kprintf(".control=%#.*hhs, ", msg->msg_controllen, msg->msg_control);
if (msg->msg_flags) kprintf(".flags=%#x, ", msg->msg_flags);
kprintf(".iov=", fd);
DescribeIov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0);
kprintf("}], %#x) → %'ld% m\n", flags, rc);
kprintf(".iov=%s", DescribeIovec(rc, msg->msg_iov, msg->msg_iovlen));
kprintf("], %#x) → %'ld% m\n", flags, rc);
}
}
#endif
return rc;
}

View file

@ -18,13 +18,13 @@
*/
#include "libc/assert.h"
#include "libc/calls/internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/calls/struct/iovec.h"
#include "libc/calls/struct/iovec.internal.h"
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/intrin/strace.internal.h"
#include "libc/sock/internal.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/msghdr.h"
@ -91,9 +91,8 @@ ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) {
if (msg->msg_controllen)
kprintf(", .control=%#.*hhs, ", msg->msg_controllen, msg->msg_control);
if (msg->msg_flags) kprintf(".flags=%#x, ", msg->msg_flags);
kprintf(", .iov=", fd);
DescribeIov(msg->msg_iov, msg->msg_iovlen, rc != -1 ? rc : 0);
kprintf("}");
kprintf(", .iov=%s",
DescribeIovec(rc != -1 ? rc : -2, msg->msg_iov, msg->msg_iovlen));
}
kprintf(", %#x) → %'ld% m\n", flags, rc);
}

View file

@ -52,7 +52,7 @@
* @asyncsignalsafe
* @restartable (unless SO_RCVTIMEO)
*/
ssize_t sendto(int fd, const void *buf, size_t size, uint32_t flags,
ssize_t sendto(int fd, const void *buf, size_t size, int flags,
const struct sockaddr *opt_addr, uint32_t addrsize) {
ssize_t rc;
uint32_t bsdaddrsize;

View file

@ -0,0 +1,42 @@
#ifndef COSMOPOLITAN_LIBC_SOCK_STRUCT_CMSGHDR_H_
#define COSMOPOLITAN_LIBC_SOCK_STRUCT_CMSGHDR_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
#define CMSG_DATA(cmsg) ((unsigned char *)(((struct cmsghdr *)(cmsg)) + 1))
#define CMSG_FIRSTHDR(mhdr) \
((size_t)(mhdr)->msg_controllen >= sizeof(struct cmsghdr) \
? (struct cmsghdr *)(mhdr)->msg_control \
: (struct cmsghdr *)0)
#define CMSG_NXTHDR(mhdr, cmsg) \
((cmsg)->cmsg_len < sizeof(struct cmsghdr) || \
__CMSG_LEN(cmsg) + sizeof(struct cmsghdr) >= \
__MHDR_END(mhdr) - (unsigned char *)(cmsg) \
? 0 \
: (struct cmsghdr *)__CMSG_NEXT(cmsg))
#define CMSG_ALIGN(len) \
(((len) + sizeof(size_t) - 1) & (size_t) ~(sizeof(size_t) - 1))
#define CMSG_SPACE(len) (CMSG_ALIGN(len) + CMSG_ALIGN(sizeof(struct cmsghdr)))
#define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len))
#define __CMSG_LEN(cmsg) \
(((cmsg)->cmsg_len + sizeof(long) - 1) & ~(long)(sizeof(long) - 1))
#define __CMSG_NEXT(cmsg) ((unsigned char *)(cmsg) + __CMSG_LEN(cmsg))
#define __MHDR_END(mhdr) \
((unsigned char *)(mhdr)->msg_control + (mhdr)->msg_controllen)
struct cmsghdr { /* linux abi */
uint32_t cmsg_len;
uint32_t __pad1;
int32_t cmsg_level;
int32_t cmsg_type;
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SOCK_STRUCT_CMSGHDR_H_ */

View file

@ -0,0 +1,14 @@
#ifndef COSMOPOLITAN_LIBC_SOCK_STRUCT_CMSGHDR_INTERNAL_H_
#define COSMOPOLITAN_LIBC_SOCK_STRUCT_CMSGHDR_INTERNAL_H_
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
struct cmsghdr_bsd {
uint32_t cmsg_len;
int32_t cmsg_level;
int32_t cmsg_type;
};
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SOCK_STRUCT_CMSGHDR_INTERNAL_H_ */

View file

@ -8,9 +8,9 @@ struct msghdr { /* Linux+NT ABI */
void *msg_name; /* optional address */
uint32_t msg_namelen; /* size of msg_name */
struct iovec *msg_iov; /* scatter/gather array */
int msg_iovlen; /* iovec count */
void *msg_control; /* credentials and stuff */
uint32_t msg_controllen; /* size of msg_control */
int msg_iovlen; /* # elements in msg_iov */
void *msg_control; /* ancillary data c. cmsghdr */
uint32_t msg_controllen; /* ancillary data buffer len */
uint32_t __pad0; /* reconcile abi */
int msg_flags; /* MSG_XXX */
};

View file

@ -41,8 +41,8 @@ int bind(int, const struct sockaddr *, uint32_t);
int connect(int, const struct sockaddr *, uint32_t);
int getsockname(int, struct sockaddr *, uint32_t *);
int getpeername(int, struct sockaddr *, uint32_t *);
ssize_t recvfrom(int, void *, size_t, uint32_t, struct sockaddr *, uint32_t *);
ssize_t sendto(int, const void *, size_t, uint32_t, const struct sockaddr *,
ssize_t recvfrom(int, void *, size_t, int, struct sockaddr *, uint32_t *);
ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *,
uint32_t);
COSMOPOLITAN_C_END_

View file

@ -16,15 +16,26 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/timespec.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/itoa.h"
#include "libc/fmt/magnumstrs.internal.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/macros.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/append.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/s.h"
#include "libc/sysv/consts/sig.h"
#include "libc/x/x.h"
#include "third_party/double-conversion/wrapper.h"
/**
* @fileoverview Cosmopolitan Command Interpreter
@ -33,27 +44,53 @@
* enough shell script language support to support our build config.
*/
#define STATE_SHELL 0
#define STATE_STR 1
#define STATE_QUO 2
#define STATE_CMD 0
#define STATE_VAR 1
#define STATE_SINGLE 2
#define STATE_QUOTED 3
#define STATE_QUOTED_VAR 4
#define STATE_WHITESPACE 5
static char *p;
static char *q;
static int vari;
static size_t n;
static char *cmd;
static char var[32];
static int lastchild;
static int exitstatus;
static char *args[8192];
static const char *prog;
static char errbuf[512];
static char argbuf[ARG_MAX];
static bool unsupported[256];
static ssize_t Write(int fd, const char *s) {
return write(fd, s, strlen(s));
}
static void Log(const char *s, ...) {
va_list va;
va_start(va, s);
errbuf[0] = 0;
do {
strlcat(errbuf, s, sizeof(argbuf));
} while ((s = va_arg(va, const char *)));
strlcat(errbuf, "\n", sizeof(argbuf));
Write(2, errbuf);
va_end(va);
}
static wontreturn void Wexit(int rc, const char *s, ...) {
va_list va;
va_start(va, s);
errbuf[0] = 0;
do {
write(2, s, strlen(s));
strlcat(errbuf, s, sizeof(argbuf));
} while ((s = va_arg(va, const char *)));
Write(2, errbuf);
va_end(va);
exit(rc);
_Exit(rc);
}
static wontreturn void UnsupportedSyntax(unsigned char c) {
@ -86,86 +123,391 @@ static void Open(const char *path, int fd, int flags) {
}
static wontreturn void Exec(void) {
const char *s;
if (!n) {
Wexit(5, prog, ": error: too few args\n", 0);
}
_npassert(args[0][0]);
if (!n) Wexit(5, prog, ": error: too few args\n", 0);
execvp(args[0], args);
SysExit(127, "execve", args[0]);
}
static int GetSignalByName(const char *s) {
for (int i = 0; kSignalNames[i].x != MAGNUM_TERMINATOR; ++i) {
if (!strcmp(s, MAGNUM_STRING(kSignalNames, i) + 3)) {
return MAGNUM_NUMBER(kSignalNames, i);
}
}
return 0;
}
static int True(void) {
return 0;
}
static int False(void) {
return 1;
}
static wontreturn void Exit(void) {
_Exit(n > 1 ? atoi(args[1]) : 0);
}
static int Wait(void) {
char ibuf[12];
int e, rc, ws, pid;
if (n > 1) {
if (waitpid(atoi(args[1]), &ws, 0) == -1) {
SysExit(22, "waitpid", prog);
}
rc = WIFEXITED(ws) ? WEXITSTATUS(ws) : 128 + WTERMSIG(ws);
exitstatus = rc;
} else {
for (e = errno;;) {
if (waitpid(-1, &ws, 0) == -1) {
if (errno == ECHILD) {
errno = e;
break;
}
SysExit(22, "waitpid", prog);
}
}
rc = 0;
}
return rc;
}
static int Echo(void) {
int i = 1;
bool once = false;
const char *l = " ";
if (i < n && !strcmp(args[i], "-l")) {
++i, l = "\n";
}
for (; i < n; ++i) {
if (once) {
Write(1, l);
} else {
once = true;
}
Write(1, args[i]);
}
Write(1, "\n");
return 0;
}
static int Read(void) {
char *b = 0;
unsigned char c;
int a = 1, rc = 1;
if (n >= 3 && !strcmp(args[1], "-p")) {
Write(1, args[2]);
a = 3;
}
appendr(&b, 0);
while (read(0, &c, 1) > 0) {
if (c == '\n') {
rc = 0;
break;
}
appendw(&b, c);
}
if (a < n) {
setenv(args[1], b, true);
}
free(b);
return rc;
}
static int Cd(void) {
const char *s = n > 1 ? args[1] : getenv("HOME");
if (s) {
if (!chdir(s)) {
return 0;
} else {
Log("chdir: ", s, ": ", _strerdoc(errno), 0);
return 1;
}
} else {
Log("chdir: missing argument", 0);
return 1;
}
}
static int Mkdir(void) {
int i = 1;
int (*f)(const char *, unsigned) = mkdir;
if (n >= 3 && !strcmp(args[1], "-p")) ++i, f = makedirs;
for (; i < n; ++i) {
if (f(args[i], 0755)) {
Log("mkdir: ", args[i], ": ", _strerdoc(errno), 0);
return errno;
}
}
return 0;
}
static int Kill(void) {
int sig, rc = 0, i = 1;
if (i < n && args[i][0] == '-') {
sig = GetSignalByName(args[i++] + 1);
if (!sig) return 1;
} else {
sig = SIGTERM;
}
for (; i < n; ++i) {
if (kill(atoi(args[i]), sig)) {
Log("kill: ", args[i], ": ", _strerdoc(errno), 0);
rc = 1;
}
}
return rc;
}
static int Toupper(void) {
int i, n;
char b[512];
while ((n = read(0, b, 512)) > 0) {
for (i = 0; i < n; ++i) {
b[i] = toupper(b[i]);
}
write(1, b, n);
}
return 0;
}
static int Usleep(void) {
struct timespec t, *p = 0;
if (n > 1) {
t = _timespec_frommicros(atoi(args[1]));
p = &t;
}
return clock_nanosleep(0, 0, p, 0);
}
static int Test(void) {
struct stat st;
if (n && !strcmp(args[n - 1], "]")) --n;
if (n == 4 && !strcmp(args[2], "=")) {
return !!strcmp(args[1], args[3]);
} else if (n == 4 && !strcmp(args[2], "!=")) {
return !strcmp(args[1], args[3]);
} else if (n == 3 && !strcmp(args[1], "-n")) {
return !(strlen(args[2]) > 0);
} else if (n == 3 && !strcmp(args[1], "-z")) {
return !(strlen(args[2]) == 0);
} else if (n == 3 && !strcmp(args[1], "-e")) {
return !!stat(args[2], &st);
} else if (n == 3 && !strcmp(args[1], "-f")) {
return !stat(args[2], &st) && S_ISREG(st.st_mode);
} else if (n == 3 && !strcmp(args[1], "-d")) {
return !stat(args[2], &st) && S_ISDIR(st.st_mode);
} else if (n == 3 && !strcmp(args[1], "-h")) {
return !stat(args[2], &st) && S_ISLNK(st.st_mode);
} else {
return 1;
}
}
static int TryBuiltin(void) {
if (!n) return 0;
if (!strcmp(args[0], "exit")) Exit();
if (!strcmp(args[0], "cd")) return Cd();
if (!strcmp(args[0], "[")) return Test();
if (!strcmp(args[0], "wait")) return Wait();
if (!strcmp(args[0], "echo")) return Echo();
if (!strcmp(args[0], "read")) return Read();
if (!strcmp(args[0], "true")) return True();
if (!strcmp(args[0], "test")) return Test();
if (!strcmp(args[0], "kill")) return Kill();
if (!strcmp(args[0], "mkdir")) return Mkdir();
if (!strcmp(args[0], "false")) return False();
if (!strcmp(args[0], "usleep")) return Usleep();
if (!strcmp(args[0], "toupper")) return Toupper();
return -1;
}
static wontreturn void Launch(void) {
int rc;
if ((rc = TryBuiltin()) != -1) _Exit(rc);
Exec();
}
static void Pipe(void) {
int pid, pfds[2];
if (pipe2(pfds, O_CLOEXEC)) {
SysExit(8, "pipe2", prog);
}
if ((pid = vfork()) == -1) {
SysExit(9, "vfork", prog);
}
if (pipe2(pfds, O_CLOEXEC)) SysExit(8, "pipe2", prog);
if ((pid = fork()) == -1) SysExit(9, "vfork", prog);
if (!pid) {
dup2(pfds[1], 1);
Exec();
_unassert(dup2(pfds[1], 1) == 1);
// we can't rely on cloexec because builtins
if (pfds[0] != 1) _unassert(!close(pfds[0]));
if (pfds[1] != 1) _unassert(!close(pfds[1]));
Launch();
}
dup2(pfds[0], 0);
_unassert(!dup2(pfds[0], 0));
if (pfds[1]) _unassert(!close(pfds[1]));
n = 0;
}
static int Run(void) {
int exitstatus, ws, pid;
if ((exitstatus = TryBuiltin()) == -1) {
if ((pid = vfork()) == -1) SysExit(21, "vfork", prog);
if (!pid) Exec();
if (waitpid(pid, &ws, 0) == -1) SysExit(22, "waitpid", prog);
exitstatus = WIFEXITED(ws) ? WEXITSTATUS(ws) : 128 + WTERMSIG(ws);
}
n = 0;
return exitstatus;
}
static void Async(void) {
if ((lastchild = fork()) == -1) SysExit(21, "vfork", prog);
if (!lastchild) Launch();
n = 0;
}
static const char *IntToStr(int x) {
static char ibuf[12];
FormatInt32(ibuf, x);
return ibuf;
}
static const char *GetEnv(const char *key) {
if (key[0] == '$' && !key[1]) {
return IntToStr(getpid());
} else if (key[0] == '!' && !key[1]) {
return IntToStr(lastchild);
} else if (key[0] == '?' && !key[1]) {
return IntToStr(exitstatus);
} else {
return getenv(key);
}
}
static bool IsVarName(int c) {
return isalnum(c) || c == '_' ||
(!vari && (c == '?' || c == '!' || c == '$'));
}
static inline void Append(int c) {
_unassert(q + 1 < argbuf + sizeof(argbuf));
*q++ = c;
}
static char *Tokenize(void) {
char *r;
int c, t;
while (*p == ' ' || *p == '\t' || *p == '\n' ||
(p[0] == '\\' && p[1] == '\n')) {
++p;
}
if (!*p) return 0;
t = STATE_SHELL;
for (r = q;; ++p) {
const char *s;
int c, t, j, k, rc;
for (r = q, t = STATE_WHITESPACE;; ++p) {
switch (t) {
case STATE_SHELL:
case STATE_WHITESPACE:
if (!*p) return 0;
if (*p == ' ' || *p == '\t' || *p == '\n' ||
(p[0] == '\\' && p[1] == '\n')) {
continue;
}
t = STATE_CMD;
// fallthrough
case STATE_CMD:
if (unsupported[*p & 255]) {
UnsupportedSyntax(*p);
}
if (!*p || *p == ' ' || *p == '\t') {
*q++ = 0;
Append(0);
return r;
} else if (*p == '"') {
t = STATE_QUO;
t = STATE_QUOTED;
} else if (*p == '\'') {
t = STATE_STR;
t = STATE_SINGLE;
} else if (*p == '$') {
t = STATE_VAR;
var[(vari = 0)] = 0;
} else if (*p == '\\') {
if (!p[1]) UnsupportedSyntax(*p);
*q++ = *++p;
Append(*++p);
} else if (*p == '|') {
if (q > r) {
*q = 0;
Append(0);
return r;
} else if (p[1] == '|') {
rc = Run();
if (!rc) {
_Exit(0);
} else {
++p;
t = STATE_WHITESPACE;
}
} else {
Pipe();
++p;
t = STATE_WHITESPACE;
}
} else if (*p == ';') {
if (q > r) {
Append(0);
return r;
} else {
Run();
t = STATE_WHITESPACE;
}
} else if (*p == '&') {
if (q > r) {
Append(0);
return r;
} else if (p[1] == '&') {
rc = Run();
if (!rc) {
++p;
t = STATE_WHITESPACE;
} else {
_Exit(rc);
}
} else {
Async();
t = STATE_WHITESPACE;
}
} else {
*q++ = *p;
Append(*p);
}
break;
case STATE_STR:
if (!*p) {
Wexit(6, "cmd: error: unterminated single string\n", 0);
case STATE_VAR:
if (IsVarName(*p)) {
_unassert(vari + 1 < sizeof(var));
var[vari++] = *p;
var[vari] = 0;
} else {
// XXX: we need to find a simple elegant way to break up
// unquoted variable expansions into multiple args.
if ((s = GetEnv(var))) {
if ((j = strlen(s))) {
_unassert(q + j < argbuf + sizeof(argbuf));
q = mempcpy(q, s, j);
}
}
--p;
t = STATE_CMD;
}
break;
case STATE_SINGLE:
if (!*p) goto UnterminatedString;
if (*p == '\'') {
t = STATE_SHELL;
t = STATE_CMD;
} else {
*q++ = *p;
}
break;
case STATE_QUO:
if (!*p) {
Wexit(6, "cmd: error: unterminated quoted string\n", 0);
}
UnterminatedString:
Wexit(6, "cmd: error: unterminated string\n", 0);
case STATE_QUOTED:
if (!*p) goto UnterminatedString;
if (*p == '"') {
t = STATE_SHELL;
t = STATE_CMD;
} else if (p[0] == '$') {
t = STATE_QUOTED_VAR;
var[(vari = 0)] = 0;
} else if (p[0] == '\\') {
switch ((c = *++p)) {
case 0:
@ -187,6 +529,24 @@ static char *Tokenize(void) {
}
break;
case STATE_QUOTED_VAR:
if (!*p) goto UnterminatedString;
if (IsVarName(*p)) {
_unassert(vari + 1 < sizeof(var));
var[vari++] = *p;
var[vari] = 0;
} else {
if ((s = GetEnv(var))) {
if ((j = strlen(s))) {
_unassert(q + j < argbuf + sizeof(argbuf));
q = mempcpy(q, s, j);
}
}
--p;
t = STATE_QUOTED;
}
break;
default:
unreachable;
}
@ -219,13 +579,9 @@ int cocmd(int argc, char *argv[]) {
unsupported['*'] = true;
unsupported['('] = true;
unsupported[')'] = true;
unsupported['['] = true;
unsupported[']'] = true;
unsupported['{'] = true;
unsupported['}'] = true;
unsupported[';'] = true;
unsupported['?'] = true;
unsupported['!'] = true;
if (argc != 3) {
Wexit(10, prog, ": error: wrong number of args\n", 0);
@ -270,5 +626,5 @@ int cocmd(int argc, char *argv[]) {
}
}
Exec();
Launch();
}

View file

@ -18,11 +18,17 @@
*/
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/intrin/strace.internal.h"
#include "libc/fmt/conv.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h"
#include "libc/x/x.h"
static char *DirName(const char *path) {
char *dirp;
if (!(path = strdup(path))) return 0;
dirp = strdup(dirname(path));
free(path);
return dirp;
}
static int MakeDirs(const char *path, unsigned mode, int e) {
int rc;
@ -34,7 +40,9 @@ static int MakeDirs(const char *path, unsigned mode, int e) {
if (errno != ENOENT) {
return -1;
}
dir = xdirname(path);
if (!(dir = DirName(path))) {
return -1;
}
if (strcmp(dir, path)) {
rc = MakeDirs(dir, mode, e);
} else {

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/paths.h"
@ -31,14 +32,8 @@
/**
* Spawns subprocess and returns pipe stream.
*
* This embeds the cocmd.com shell interpreter which supports a limited
* subset of the bourne shell that's significantly faster:
*
* - pipelines
* - single quotes
* - double quotes
* - input redirection, e.g. `<path`
* - output redirection, e.g. `>path`, `>>append`, `2>err.txt, `2>&1`
* This embeds the Cosmopolitan Command Interpreter which provides
* Bourne-like syntax on all platforms including Windows.
*
* @see pclose()
*/
@ -54,28 +49,30 @@ FILE *popen(const char *cmdline, const char *mode) {
einval();
return NULL;
}
if (pipe(pipefds) == -1) return NULL;
fcntl(pipefds[dir], F_SETFD, FD_CLOEXEC);
if (pipe2(pipefds, O_CLOEXEC) == -1) return NULL;
if ((f = fdopen(pipefds[dir], mode))) {
switch ((pid = fork())) {
case 0:
dup2(pipefds[!dir], !dir);
_unassert(dup2(pipefds[!dir], !dir) == !dir);
// we can't rely on cloexec because cocmd builtins don't execev
if (pipefds[0] != !dir) _unassert(!close(pipefds[0]));
if (pipefds[1] != !dir) _unassert(!close(pipefds[1]));
_Exit(cocmd(3, (char *[]){"popen", "-c", cmdline, 0}));
default:
f->pid = pid;
close(pipefds[!dir]);
_unassert(!close(pipefds[!dir]));
return f;
case -1:
e = errno;
fclose(f);
close(pipefds[!dir]);
_unassert(!fclose(f));
_unassert(!close(pipefds[!dir]));
errno = e;
return NULL;
}
} else {
e = errno;
close(pipefds[0]);
close(pipefds[1]);
_unassert(!close(pipefds[0]));
_unassert(!close(pipefds[1]));
errno = e;
return NULL;
}

View file

@ -16,11 +16,14 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/calls/struct/sched_param.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/spawn.h"
#include "libc/stdio/spawna.internal.h"
#include "libc/str/str.h"
@ -47,7 +50,7 @@ int posix_spawn(int *pid, const char *path,
if (setpgid(0, attrp->posix_attr_pgroup)) _Exit(127);
}
if (attrp->posix_attr_flags & POSIX_SPAWN_SETSIGMASK) {
sigprocmask(SIG_SETMASK, &attrp->posix_attr_sigmask, NULL);
sigprocmask(SIG_SETMASK, &attrp->posix_attr_sigmask, 0);
}
if (attrp->posix_attr_flags & POSIX_SPAWN_RESETIDS) {
setuid(getuid());
@ -59,7 +62,7 @@ int posix_spawn(int *pid, const char *path,
sigfillset(&allsigs);
for (s = 0; sigismember(&allsigs, s); s++) {
if (sigismember(&attrp->posix_attr_sigdefault, s)) {
if (sigaction(s, &dfl, NULL) == -1) _Exit(127);
if (sigaction(s, &dfl, 0) == -1) _Exit(127);
}
}
}
@ -96,15 +99,16 @@ int posix_spawn(int *pid, const char *path,
if (attrp->posix_attr_flags & POSIX_SPAWN_SETSCHEDULER) {
if (sched_setscheduler(0, attrp->posix_attr_schedpolicy,
&attrp->posix_attr_schedparam) == -1) {
_Exit(127);
if (errno != ENOSYS) _Exit(127);
}
}
if (attrp->posix_attr_flags & POSIX_SPAWN_SETSCHEDPARAM) {
if (sched_setparam(0, &attrp->posix_attr_schedparam) == -1) {
_Exit(127);
if (errno != ENOSYS) _Exit(127);
}
}
}
if (!envp) envp = environ;
execve(path, argv, envp);
_Exit(127);
} else {

View file

@ -86,6 +86,7 @@ int setvbuf(FILE *, char *, int, size_t);
int pclose(FILE *);
char *ctermid(char *);
void perror(const char *) relegated;
int makedirs(const char *, unsigned);
typedef uint64_t fpos_t;
char *gets(char *) paramsnonnull();

View file

@ -33,14 +33,8 @@
/**
* Launches program with system command interpreter.
*
* This embeds the cocmd.com shell interpreter which supports a limited
* subset of the bourne shell that's significantly faster:
*
* - pipelines
* - single quotes
* - double quotes
* - input redirection, e.g. `<path`
* - output redirection, e.g. `>path`, `>>append`, `2>err.txt, `2>&1`
* This embeds the Cosmopolitan Command Interpreter which provides
* Bourne-like syntax on all platforms including Windows.
*
* @param cmdline is an interpreted Turing-complete command
* @return -1 if child process couldn't be created, otherwise a wait

View file

@ -78,6 +78,7 @@ wint_t towlower_l(wint_t, locale_t);
wint_t towupper_l(wint_t, locale_t);
int strcasecmp_l(const char *, const char *, locale_t);
int strncasecmp_l(const char *, const char *, size_t, locale_t);
ssize_t strfmon_l(char *, size_t, locale_t, const char *, ...);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

@ -151,6 +151,7 @@ wchar_t *wmempcpy(wchar_t *, const wchar_t *, size_t) memcpyesque;
wchar_t *wmemmove(wchar_t *, const wchar_t *, size_t) memcpyesque;
void *tinymemccpy(void *, const void *, int, size_t) memcpyesque;
void *memmem(const void *, size_t, const void *, size_t) libcesque nosideeffect;
ssize_t strfmon(char *, size_t, const char *, ...);
long a64l(const char *);
char *l64a(long);

View file

@ -61,6 +61,7 @@ noasan static inline const char *strchr_sse(const char *s, unsigned char c) {
* @return pointer to first instance of c or NULL if not found
* noting that if c is NUL we return pointer to terminator
* @asyncsignalsafe
* @vforksafe
*/
char *strchr(const char *s, int c) {
const char *r;

View file

@ -60,6 +60,7 @@
* @return dest
* @see stpncpy(), memccpy()
* @asyncsignalsafe
* @vforksafe
*/
char *strncpy(char *dest, const char *src, size_t stride) {
size_t i;

View file

@ -38,24 +38,41 @@ COSMOPOLITAN_C_START_
* _Exit(1);
* EXITS(1);
* }
*
* The above are shorthand for:
*
* TEST(my, test) {
* SPAWN(fork);
* // communicate with parent
* PARENT();
* // communicate with child
* WAIT(exit, 0)
* }
*
* These macros cause a local variable named `child` with the child pid
* to be defined.
*/
#define SPAWN(METHOD) \
{ \
int _pid, _failed = g_testlib_failed; \
ASSERT_NE(-1, (_pid = METHOD())); \
if (!_pid) {
#define SPAWN(METHOD) \
{ \
int child, _failed = g_testlib_failed; \
ASSERT_NE(-1, (child = METHOD())); \
if (!child) {
#define EXITS(rc) \
_Exit(MAX(0, MIN(255, g_testlib_failed - _failed))); \
} \
testlib_waitforexit(__FILE__, __LINE__, #rc, rc, _pid); \
#define EXITS(CODE) \
PARENT() \
WAIT(exit, CODE)
#define TERMS(SIG) \
PARENT() \
WAIT(term, SIG)
#define PARENT() \
_Exit(MAX(0, MIN(255, g_testlib_failed - _failed))); \
}
#define TERMS(sig) \
_Exit(MAX(0, MIN(255, g_testlib_failed - _failed))); \
} \
testlib_waitforterm(__FILE__, __LINE__, #sig, sig, _pid); \
#define WAIT(KIND, CODE) \
testlib_waitfor##KIND(__FILE__, __LINE__, #CODE, CODE, child); \
}
void testlib_waitforexit(const char *, int, const char *, int, int);

View file

@ -52,7 +52,6 @@ void *xload(bool *, void **, const void *, size_t, size_t);
void *xloadzd(bool *, void **, const void *, size_t, size_t, size_t, size_t,
uint32_t);
int rmrf(const char *);
int makedirs(const char *, unsigned);
char *xbasename(const char *) paramsnonnull()
returnspointerwithnoaliases dontthrow nocallback dontdiscard returnsnonnull;
char *xdirname(const char *) paramsnonnull()

View file

@ -26,6 +26,7 @@
#include "libc/calls/struct/stat.h"
#include "libc/calls/struct/timespec.h"
#include "libc/calls/struct/timeval.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/fmt/conv.h"
#include "libc/fmt/itoa.h"
@ -43,7 +44,6 @@
#include "libc/runtime/internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/sysconf.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/sock/struct/sockaddr.h"
@ -71,6 +71,7 @@
#include "net/http/escape.h"
#include "net/http/http.h"
#include "net/http/ip.h"
#include "net/http/tokenbucket.h"
#include "net/http/url.h"
#include "third_party/getopt/getopt.h"
#include "third_party/nsync/counter.h"
@ -88,8 +89,8 @@
*/
#define PORT 8080 // default server listening port
#define CPUS 256 // number of cpus to actually use
#define WORKERS 1000 // size of http client thread pool
#define CPUS 64 // number of cpus to actually use
#define WORKERS 100 // size of http client thread pool
#define SUPERVISE_MS 1000 // how often to stat() asset files
#define KEEPALIVE_MS 60000 // max time to keep idle conn open
#define MELTALIVE_MS 2000 // panic keepalive under heavy load
@ -106,12 +107,17 @@
#define QUEUE_MAX 800 // maximum pending claim items in queue
#define BATCH_MAX 64 // max claims to insert per transaction
#define NICK_MAX 40 // max length of user nickname string
#define TB_INTERVAL 1000 // millis between token replenishes
#define TB_CIDR 22 // token bucket cidr specificity
#define SOCK_MAX 100 // max length of socket queue
#define MSG_BUF 512 // small response lookaside
#define INBUF_SIZE PAGESIZE
#define OUTBUF_SIZE 8192
#define TB_BYTES (1u << TB_CIDR)
#define TB_WORDS (TB_BYTES / 8)
#define GETOPTS "idvp:w:k:"
#define USAGE \
"\
@ -257,6 +263,7 @@ atomic_long g_parsefails;
atomic_long g_iprequests;
atomic_long g_queuefulls;
atomic_long g_htmlclaims;
atomic_long g_ratelimits;
atomic_long g_emptyclaims;
atomic_long g_acceptfails;
atomic_long g_badversions;
@ -270,6 +277,11 @@ atomic_long g_claimsenqueued;
atomic_long g_claimsprocessed;
atomic_long g_statuszrequests;
union TokenBucket {
atomic_schar *b;
atomic_uint_fast64_t *w;
} g_tok;
// http worker objects
struct Worker {
pthread_t th;
@ -599,7 +611,7 @@ void OnlyRunOnCpu(int i) {
int n;
cpu_set_t cpus;
_Static_assert(CPUS > 0, "");
n = GetCpuCount();
n = _getcpucount();
n = MIN(CPUS, n);
i = MIN(i, n - 1);
CPU_ZERO(&cpus);
@ -612,7 +624,7 @@ void DontRunOnFirstCpus(int i) {
int n;
cpu_set_t cpus;
_Static_assert(CPUS > 0, "");
n = GetCpuCount();
n = _getcpucount();
n = MIN(CPUS, n);
i = MIN(i, n - 1);
CPU_ZERO(&cpus);
@ -679,6 +691,7 @@ void ServeStatusz(int client, char *outbuf) {
p = Statusz(p, "iprequests", g_iprequests);
p = Statusz(p, "queuefulls", g_queuefulls);
p = Statusz(p, "htmlclaims", g_htmlclaims);
p = Statusz(p, "ratelimits", g_ratelimits);
p = Statusz(p, "emptyclaims", g_emptyclaims);
p = Statusz(p, "acceptfails", g_acceptfails);
p = Statusz(p, "badversions", g_badversions);
@ -860,6 +873,17 @@ void *HttpWorker(void *arg) {
break;
}
if (!AcquireToken(g_tok.b, ip, TB_CIDR)) {
LOG("%s rate limiting client\n", ipbuf, msg->version);
Write(client.sock, "HTTP/1.1 429 Too Many Requests\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n"
"\r\n"
"429 Too Many Requests\n");
++g_ratelimits;
break;
}
// access log
LOG("%16s %.*s %.*s %.*s %.*s %#.*s\n", ipbuf,
msg->xmethod.b - msg->xmethod.a, inbuf + msg->xmethod.a,
@ -966,6 +990,8 @@ void *HttpWorker(void *arg) {
"Cache-Control: max-age=3600, private\r\n"
"Date: ");
p = FormatDate(p);
p = stpcpy(p, "\r\nX-Token-Count: ");
p = FormatInt32(p, CountTokens(g_tok.b, ip, TB_CIDR));
p = stpcpy(p, "\r\nContent-Length: ");
p = FormatInt32(p, strlen(ipbuf));
p = stpcpy(p, "\r\n\r\n");
@ -1662,6 +1688,25 @@ void *NowWorker(void *arg) {
return 0;
}
// worker for refilling token buckets
void *ReplenishWorker(void *arg) {
BlockSignals();
pthread_setname_np(pthread_self(), "Replenisher");
LOG("%P Replenisher started\n");
UpdateNow();
OnlyRunOnCpu(0);
for (struct timespec ts = _timespec_real();;
ts = _timespec_add(ts, _timespec_frommillis(TB_INTERVAL))) {
if (!nsync_note_wait(g_shutdown[1], ts)) {
ReplenishTokens(g_tok.w, TB_WORDS);
} else {
break;
}
}
LOG("Replenisher exiting\n");
return 0;
}
// we're permissive in allowing http connection keepalive until the
// moment worker resources start becoming scarce. when that happens
// we'll (1) cancel read operations that have not sent us a message
@ -1723,8 +1768,10 @@ int main(int argc, char *argv[]) {
// ShowCrashReports();
// we don't have proper futexes on these platforms
// so choose a smaller number of workers
g_workers = MIN(g_workers, 4);
// we'll be somewhat less aggressive about workers
if (IsXnu() || IsNetbsd()) {
g_workers = MIN(g_workers, _getcpucount());
}
// user interface
GetOpts(argc, argv);
@ -1755,6 +1802,10 @@ int main(int argc, char *argv[]) {
sqlite3_initialize();
CheckDatabase();
// fill token buckets
g_tok.b = malloc(TB_BYTES);
memset(g_tok.b, 127, TB_BYTES);
// server lifecycle locks
g_started = _timespec_real();
for (int i = 0; i < ARRAYLEN(g_shutdown); ++i) {
@ -1789,13 +1840,14 @@ int main(int argc, char *argv[]) {
// make 8 helper threads
g_ready = nsync_counter_new(9);
pthread_t scorer, recenter, claimer, nower;
pthread_t scorer, recenter, claimer, nower, replenisher;
pthread_t scorer_hour, scorer_day, scorer_week, scorer_month;
CHECK_EQ(0, pthread_create(&scorer, 0, ScoreWorker, 0));
CHECK_EQ(0, pthread_create(&scorer_hour, 0, ScoreHourWorker, 0));
CHECK_EQ(0, pthread_create(&scorer_day, 0, ScoreDayWorker, 0));
CHECK_EQ(0, pthread_create(&scorer_week, 0, ScoreWeekWorker, 0));
CHECK_EQ(0, pthread_create(&scorer_month, 0, ScoreMonthWorker, 0));
CHECK_EQ(0, pthread_create(&replenisher, 0, ReplenishWorker, 0));
CHECK_EQ(0, pthread_create(&recenter, 0, RecentWorker, 0));
CHECK_EQ(0, pthread_create(&claimer, 0, ClaimWorker, 0));
CHECK_EQ(0, pthread_create(&nower, 0, NowWorker, 0));
@ -1843,6 +1895,7 @@ int main(int argc, char *argv[]) {
CHECK_EQ(0, pthread_join(scorer_hour, 0));
CHECK_EQ(0, pthread_join(scorer_week, 0));
CHECK_EQ(0, pthread_join(scorer_month, 0));
CHECK_EQ(0, pthread_join(replenisher, 0));
// now that all workers have terminated, the claims queue must be
// empty, therefore, it is now safe to send a cancellation to the
@ -1872,6 +1925,7 @@ int main(int argc, char *argv[]) {
}
nsync_counter_free(g_ready);
free(g_worker);
free(g_tok.b);
LOG("Goodbye\n");
// CheckForMemoryLeaks();

View file

@ -21,9 +21,10 @@
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/mem/gc.internal.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/testlib.h"

View file

@ -19,9 +19,12 @@
#include "libc/calls/calls.h"
#include "libc/calls/struct/cpuset.h"
#include "libc/dce.h"
#include "libc/fmt/conv.h"
#include "libc/intrin/popcnt.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/sysconf.h"
#include "libc/stdio/spawn.h"
#include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/thread.h"
#include "libc/thread/thread2.h"
@ -44,7 +47,7 @@ TEST(sched_getaffinity, firstOnly) {
}
TEST(sched_getaffinity, secondOnly) {
if (GetCpuCount() < 2) return;
if (_getcpucount() < 2) return;
cpu_set_t x, y;
CPU_ZERO(&x);
CPU_SET(1, &x);
@ -55,6 +58,48 @@ TEST(sched_getaffinity, secondOnly) {
EXPECT_TRUE(CPU_ISSET(1, &y));
}
TEST(sched_setaffinity, isInheritedAcrossFork) {
cpu_set_t x, y;
CPU_ZERO(&x);
CPU_SET(0, &x);
ASSERT_SYS(0, 0, sched_setaffinity(0, sizeof(x), &x));
SPAWN(fork);
ASSERT_SYS(0, 0, sched_getaffinity(0, sizeof(y), &y));
EXPECT_EQ(1, CPU_COUNT(&y));
EXPECT_TRUE(CPU_ISSET(0, &y));
EXPECT_FALSE(CPU_ISSET(1, &y));
EXITS(0);
}
__attribute__((__constructor__)) static void init(void) {
cpu_set_t y;
switch (atoi(nulltoempty(getenv("THE_DOGE")))) {
case 42:
ASSERT_SYS(0, 0, sched_getaffinity(0, sizeof(y), &y));
EXPECT_EQ(1, CPU_COUNT(&y));
EXPECT_TRUE(CPU_ISSET(0, &y));
EXPECT_FALSE(CPU_ISSET(1, &y));
exit(42);
default:
break;
}
}
TEST(sched_setaffinity, isInheritedAcrossExecve) {
cpu_set_t x, y;
CPU_ZERO(&x);
CPU_SET(0, &x);
ASSERT_SYS(0, 0, sched_setaffinity(0, sizeof(x), &x));
int rc, ws, pid;
char *prog = GetProgramExecutableName();
char *args[] = {program_invocation_name, NULL};
char *envs[] = {"THE_DOGE=42", NULL};
EXPECT_EQ(0, posix_spawn(&pid, prog, NULL, NULL, args, envs));
EXPECT_NE(-1, waitpid(pid, &ws, 0));
EXPECT_TRUE(WIFEXITED(ws));
EXPECT_EQ(42, WEXITSTATUS(ws));
}
TEST(sched_getaffinity, getpid) {
cpu_set_t x, y;
CPU_ZERO(&x);

View file

@ -22,9 +22,10 @@
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/errno.h"
#include "libc/nt/files.h"
#include "libc/mem/gc.internal.h"
#include "libc/nt/files.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/at.h"
#include "libc/sysv/consts/nr.h"

View file

@ -558,6 +558,20 @@ TEST(strtoul, testoverflow) {
ASSERT_STREQ("", e);
}
TEST(strtol, invalidHex_consistentWithBsd) {
char *c = 0;
long x = strtol("0xz", &c, 16);
ASSERT_EQ(0, x);
ASSERT_STREQ("z", c);
}
TEST(strtol, invalidHex_consistentWithBsd2) {
char *c = 0;
long x = strtol("0xez", &c, 16);
ASSERT_EQ(0xe, x);
ASSERT_STREQ("z", c);
}
BENCH(atoi, bench) {
EZBENCH2("atoi 10⁸", donothing, EXPROPRIATE(atoi(VEIL("r", "100000000"))));
EZBENCH2("strtol 10⁸", donothing,

View file

@ -162,7 +162,7 @@ void *Worker(void *arg) {
}
BENCH(malloc, torture) {
int i, n = GetCpuCount() * 2;
int i, n = _getcpucount() * 2;
pthread_t *t = _gc(malloc(sizeof(pthread_t) * n));
if (!n) return;
printf("\nmalloc torture test w/ %d threads and %d iterations\n", n,

View file

@ -0,0 +1,56 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigset.h"
#include "libc/dce.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/o.h"
#include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h"
#include "libc/time/time.h"
#include "libc/x/x.h"
char testlib_enable_tmp_setup_teardown;
TEST(daemon, test) {
int dirfd;
char buf[512];
SPAWN(fork);
ASSERT_SYS(0, 3, open(".", O_RDONLY | O_DIRECTORY));
ASSERT_SYS(0, 0, daemon(false, false));
ASSERT_SYS(0, 4, openat(3, "ok", O_WRONLY | O_CREAT | O_TRUNC, 0644));
ASSERT_NE(NULL, getcwd(buf, sizeof(buf)));
ASSERT_SYS(0, 0, write(4, buf, strlen(buf)));
ASSERT_SYS(0, 0, close(4));
ASSERT_SYS(0, 0, close(3));
EXITS(0);
for (int i = 0; i < 13; ++i) {
bzero(buf, 512);
open("ok", O_RDONLY);
read(3, buf, 511);
close(3);
if (!strcmp(IsWindows() ? "/C/" : "/", buf)) {
return;
}
usleep(1000L << i);
}
ASSERT_TRUE(false);
}

View file

@ -16,27 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/dce.h"
#include "libc/intrin/asan.internal.h"
#include "libc/intrin/describeflags.internal.h"
#include "libc/intrin/kprintf.h"
#include "libc/macros.internal.h"
#include "libc/calls/calls.h"
#include "libc/runtime/runtime.h"
#include "libc/testlib/subprocess.h"
#include "libc/testlib/testlib.h"
void DescribeIov(const struct iovec *iov, int iovlen, ssize_t rem) {
int i;
if ((!IsAsan() && kisdangerous(iov)) ||
(IsAsan() && !__asan_is_valid(iov, iovlen * sizeof(struct iovec)))) {
kprintf("%p", iov);
return;
}
kprintf("{");
for (i = 0; rem && i < MIN(5, iovlen); ++i) {
kprintf(
"%s{%#.*hhs%s, %'zu}", i ? ", " : "",
MAX(0, MIN(40, MIN(rem, iov[i].iov_len))), iov[i].iov_base,
MAX(0, MIN(40, MIN(rem, iov[i].iov_len))) < iov[i].iov_len ? "..." : "",
iov[i].iov_len);
rem -= iov[i].iov_len;
}
kprintf("%s}", iovlen > 5 ? "..." : "");
TEST(omg, test) {
SPAWN(fork);
EXITS(0);
}

View file

@ -69,10 +69,16 @@ o/$(MODE)/test/libc/sock/sendrecvmsg_test.com.runs \
o/$(MODE)/test/libc/sock/nointernet_test.com.runs: \
private .PLEDGE = stdio rpath wpath cpath fattr proc inet recvfd sendfd
o/$(MODE)/test/libc/sock/socket_test.com.runs: .INTERNET = 1 # todo: ipv6 filtering
o/$(MODE)/test/libc/sock/socket_test.com.runs: \
private .INTERNET = 1 # todo: ipv6 filtering
o/$(MODE)/test/libc/sock/socket_test.com.runs: \
private .PLEDGE = stdio rpath wpath cpath fattr proc inet
o/$(MODE)/test/libc/sock/recvmsg_test.com.runs: \
private .INTERNET = 1 # need to bind to 0.0.0.0
o/$(MODE)/test/libc/sock/recvmsg_test.com.runs: \
private .PLEDGE = stdio rpath wpath cpath fattr proc inet recvfd sendfd
o/$(MODE)/test/libc/sock/shutdown_test.com.runs: \
private .PLEDGE = stdio rpath wpath cpath fattr proc inet

View file

@ -16,12 +16,12 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/mem/mem.h"
#include "libc/mem/gc.internal.h"
#include "libc/mem/mem.h"
#include "libc/stdio/stdio.h"
#include "libc/testlib/testlib.h"
#include "libc/thread/spawn.h"
#include "libc/thread/thread.h"
#include "libc/x/x.h"
#define DIR \
"a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/A/B/C/D/E/F/G/H/I/J/K/" \

View file

@ -0,0 +1,95 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/dce.h"
#include "libc/fmt/fmt.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/sig.h"
#include "libc/testlib/testlib.h"
FILE *f;
char buf[32];
char testlib_enable_tmp_setup_teardown;
TEST(popen, command) {
char foo[6];
testlib_extract("/zip/echo.com", "echo.com", 0755);
ASSERT_NE(NULL, (f = popen("./echo.com hello", "r")));
ASSERT_NE(NULL, fgets(foo, sizeof(foo), f));
ASSERT_STREQ("hello", foo);
ASSERT_EQ(0, pclose(f));
}
TEST(popen, semicolon) {
ASSERT_NE(NULL, (f = popen("echo hello;echo there", "r")));
ASSERT_STREQ("hello\n", fgets(buf, sizeof(buf), f));
ASSERT_STREQ("there\n", fgets(buf, sizeof(buf), f));
ASSERT_EQ(0, pclose(f));
}
TEST(popen, singleQuotes) {
setenv("there", "a b c", true);
ASSERT_NE(NULL, (f = popen("echo -l 'hello $there' yo", "r")));
ASSERT_STREQ("hello $there\n", fgets(buf, sizeof(buf), f));
ASSERT_STREQ("yo\n", fgets(buf, sizeof(buf), f));
ASSERT_EQ(0, pclose(f));
}
TEST(popen, doubleQuotes) {
setenv("hello", "a b c", true);
ASSERT_NE(NULL, (f = popen("echo -l \"$hello there\"", "r")));
ASSERT_STREQ("a b c there\n", fgets(buf, sizeof(buf), f));
ASSERT_EQ(0, pclose(f));
}
TEST(popen, quoteless) {
setenv("there", "a b c", true);
ASSERT_NE(NULL, (f = popen("echo -l hello a$there yo", "r")));
ASSERT_STREQ("hello\n", fgets(buf, sizeof(buf), f));
ASSERT_STREQ("aa b c\n", fgets(buf, sizeof(buf), f)); // mixed feelings
ASSERT_STREQ("yo\n", fgets(buf, sizeof(buf), f));
ASSERT_EQ(0, pclose(f));
}
TEST(popen, pipe) {
setenv("there", "a b c", true);
ASSERT_NE(NULL, (f = popen("echo hello | toupper", "r")));
ASSERT_STREQ("HELLO\n", fgets(buf, sizeof(buf), f));
ASSERT_EQ(0, pclose(f));
}
sig_atomic_t gotsig;
void OnSig(int sig) {
gotsig = 1;
}
TEST(popen, complicated) {
if (IsWindows()) return; // windows treats sigusr1 as terminate
char cmd[64];
signal(SIGUSR1, OnSig);
sprintf(cmd, "read a ; test \"x$a\" = xhello && kill -USR1 %d", getpid());
ASSERT_NE(NULL, (f = popen(cmd, "w")));
ASSERT_GE(fputs("hello", f), 0);
ASSERT_EQ(0, pclose(f));
ASSERT_EQ(1, gotsig);
signal(SIGUSR1, SIG_DFL);
}

View file

@ -29,20 +29,18 @@
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
void SetUpOnce(void) {
__enable_threads();
}
char testlib_enable_tmp_setup_teardown;
__attribute__((__constructor__)) static void init(void) {
if (atoi(nulltoempty(getenv("THE_DOGE"))) == 42) {
exit(42);
switch (atoi(nulltoempty(getenv("THE_DOGE")))) {
case 42:
exit(42);
default:
break;
}
}
TEST(spawn, test) {
if (atoi(nulltoempty(getenv("THE_DOGE"))) == 42) {
exit(42);
}
int rc, ws, pid;
char *prog = GetProgramExecutableName();
char *args[] = {program_invocation_name, NULL};
@ -53,6 +51,25 @@ TEST(spawn, test) {
EXPECT_EQ(42, WEXITSTATUS(ws));
}
TEST(spawn, pipe) {
char buf[10];
int p[2], pid, status;
const char *pn = "./echo.com";
posix_spawn_file_actions_t fa;
testlib_extract("/zip/echo.com", "echo.com", 0755);
ASSERT_SYS(0, 0, pipe(p));
ASSERT_EQ(0, posix_spawn_file_actions_init(&fa));
ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, p[0]));
ASSERT_EQ(0, posix_spawn_file_actions_adddup2(&fa, p[1], 1));
ASSERT_EQ(0, posix_spawn_file_actions_addclose(&fa, p[1]));
ASSERT_EQ(0, posix_spawnp(&pid, pn, &fa, 0, (char *[]){pn, "hello", 0}, 0));
ASSERT_SYS(0, 0, close(p[1]));
ASSERT_SYS(0, pid, waitpid(pid, &status, 0));
ASSERT_SYS(0, 6, read(p[0], buf, sizeof(buf)));
ASSERT_SYS(0, 0, close(p[0]));
ASSERT_EQ(0, posix_spawn_file_actions_destroy(&fa));
}
/*
* BEST LINUX FORK+EXEC+EXIT+WAIT PERFORMANCE
* The fastest it can go with fork() is 40µs

View file

@ -29,27 +29,79 @@
char testlib_enable_tmp_setup_teardown;
TEST(system, testStdoutRedirect) {
int ws;
testlib_extract("/zip/echo.com", "echo.com", 0755);
TEST(system, haveShell) {
ASSERT_TRUE(system(0));
ws = system("./echo.com hello >hello.txt");
ASSERT_TRUE(WIFEXITED(ws));
ASSERT_EQ(0, WEXITSTATUS(ws));
}
TEST(system, echo) {
ASSERT_EQ(0, system("echo hello >\"hello there.txt\""));
EXPECT_STREQ("hello\n", _gc(xslurp("hello there.txt", 0)));
}
TEST(system, exit) {
ASSERT_EQ(123, WEXITSTATUS(system("exit 123")));
}
TEST(system, testStdoutRedirect) {
testlib_extract("/zip/echo.com", "echo.com", 0755);
ASSERT_EQ(0, system("./echo.com hello >hello.txt"));
EXPECT_STREQ("hello\n", _gc(xslurp("hello.txt", 0)));
}
TEST(system, testStdoutRedirect_withSpacesInFilename) {
int ws;
testlib_extract("/zip/echo.com", "echo.com", 0755);
ASSERT_TRUE(system(0));
ws = system("./echo.com hello >\"hello there.txt\"");
ASSERT_TRUE(WIFEXITED(ws));
ASSERT_EQ(0, WEXITSTATUS(ws));
ASSERT_EQ(0, system("./echo.com hello >\"hello there.txt\""));
EXPECT_STREQ("hello\n", _gc(xslurp("hello there.txt", 0)));
}
BENCH(system, bench) {
testlib_extract("/zip/echo.com", "echo.com", 0755);
EZBENCH2("system", donothing, system("./echo.com hi >/dev/null"));
EZBENCH2("system cmd", donothing, system("./echo.com hi >/dev/null"));
EZBENCH2("cocmd echo", donothing, system("echo hi >/dev/null"));
EZBENCH2("cocmd exit", donothing, system("exit"));
}
TEST(system, and) {
ASSERT_EQ(1, WEXITSTATUS(system("false && false")));
ASSERT_EQ(1, WEXITSTATUS(system("true&& false")));
ASSERT_EQ(1, WEXITSTATUS(system("false &&true")));
ASSERT_EQ(0, WEXITSTATUS(system("true&&true")));
}
TEST(system, or) {
ASSERT_EQ(1, WEXITSTATUS(system("false || false")));
ASSERT_EQ(0, WEXITSTATUS(system("true|| false")));
ASSERT_EQ(0, WEXITSTATUS(system("false ||true")));
ASSERT_EQ(0, WEXITSTATUS(system("true||true")));
}
TEST(system, async1) {
ASSERT_EQ(123, WEXITSTATUS(system("exit 123 & "
"wait $!")));
}
TEST(system, async4) {
ASSERT_EQ(0, WEXITSTATUS(system("echo a >a & "
"echo b >b & "
"echo c >c & "
"echo d >d & "
"wait")));
ASSERT_TRUE(fileexists("a"));
ASSERT_TRUE(fileexists("b"));
ASSERT_TRUE(fileexists("c"));
ASSERT_TRUE(fileexists("d"));
}
TEST(system, equals) {
setenv("var", "wand", true);
ASSERT_EQ(0, WEXITSTATUS(system("test a = a")));
ASSERT_EQ(1, WEXITSTATUS(system("test a = b")));
ASSERT_EQ(0, WEXITSTATUS(system("test x$var = xwand")));
ASSERT_EQ(0, WEXITSTATUS(system("[ a = a ]")));
ASSERT_EQ(1, WEXITSTATUS(system("[ a = b ]")));
}
TEST(system, notequals) {
ASSERT_EQ(1, WEXITSTATUS(system("test a != a")));
ASSERT_EQ(0, WEXITSTATUS(system("test a != b")));
}

View file

@ -74,6 +74,26 @@ o/$(MODE)/test/libc/stdio/system_test.com.dbg: \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/stdio/popen_test.com.dbg: \
$(TEST_LIBC_STDIO_DEPS) \
o/$(MODE)/test/libc/stdio/popen_test.o \
o/$(MODE)/test/libc/stdio/stdio.pkg \
o/$(MODE)/tool/build/echo.com.zip.o \
$(LIBC_TESTMAIN) \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
o/$(MODE)/test/libc/stdio/spawn_test.com.dbg: \
$(TEST_LIBC_STDIO_DEPS) \
o/$(MODE)/test/libc/stdio/spawn_test.o \
o/$(MODE)/test/libc/stdio/stdio.pkg \
o/$(MODE)/tool/build/echo.com.zip.o \
$(LIBC_TESTMAIN) \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
$(TEST_LIBC_STDIO_OBJS): private \
DEFAULT_CCFLAGS += \
-fno-builtin

65
third_party/double-conversion/dc.mk vendored Normal file
View file

@ -0,0 +1,65 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += THIRD_PARTY_DOUBLECONVERSION
THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS += THIRD_PARTY_DOUBLECONVERSION_A
THIRD_PARTY_DOUBLECONVERSION = \
$(THIRD_PARTY_DOUBLECONVERSION_A_DEPS) \
$(THIRD_PARTY_DOUBLECONVERSION_A)
THIRD_PARTY_DOUBLECONVERSION_A = \
o/$(MODE)/third_party/double-conversion/libdouble-conversion.a
THIRD_PARTY_DOUBLECONVERSION_A_FILES := $(wildcard third_party/double-conversion/*)
THIRD_PARTY_DOUBLECONVERSION_A_SRCS_C = $(filter %.c,$(THIRD_PARTY_DOUBLECONVERSION_A_FILES))
THIRD_PARTY_DOUBLECONVERSION_A_SRCS_CC = $(filter %.cc,$(THIRD_PARTY_DOUBLECONVERSION_A_FILES))
THIRD_PARTY_DOUBLECONVERSION_A_HDRS = $(filter %.h,$(THIRD_PARTY_DOUBLECONVERSION_A_FILES))
THIRD_PARTY_DOUBLECONVERSION_A_SRCS = \
$(THIRD_PARTY_DOUBLECONVERSION_A_SRCS_C) \
$(THIRD_PARTY_DOUBLECONVERSION_A_SRCS_CC)
THIRD_PARTY_DOUBLECONVERSION_A_OBJS = \
$(THIRD_PARTY_DOUBLECONVERSION_A_SRCS_C:%.c=o/$(MODE)/%.o) \
$(THIRD_PARTY_DOUBLECONVERSION_A_SRCS_CC:%.cc=o/$(MODE)/%.o)
THIRD_PARTY_DOUBLECONVERSION_A_CHECKS = \
$(THIRD_PARTY_DOUBLECONVERSION_A).pkg \
$(THIRD_PARTY_DOUBLECONVERSION_A_HDRS:%=o/$(MODE)/%.okk)
THIRD_PARTY_DOUBLECONVERSION_A_DIRECTDEPS = \
LIBC_INTRIN \
LIBC_NEXGEN32E \
LIBC_STR \
LIBC_TINYMATH
THIRD_PARTY_DOUBLECONVERSION_A_DEPS := \
$(call uniq,$(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_A_DIRECTDEPS),$($(x))))
$(THIRD_PARTY_DOUBLECONVERSION_A): \
third_party/double-conversion/ \
$(THIRD_PARTY_DOUBLECONVERSION_A).pkg \
$(THIRD_PARTY_DOUBLECONVERSION_A_OBJS)
$(THIRD_PARTY_DOUBLECONVERSION_A).pkg: \
$(THIRD_PARTY_DOUBLECONVERSION_A_OBJS) \
$(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_A_DIRECTDEPS),$($(x)_A).pkg)
$(THIRD_PARTY_DOUBLECONVERSION_A_OBJS): private \
OVERRIDE_CXXFLAGS += \
-ffunction-sections \
-fdata-sections
THIRD_PARTY_DOUBLECONVERSION_LIBS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)))
THIRD_PARTY_DOUBLECONVERSION_SRCS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)_SRCS))
THIRD_PARTY_DOUBLECONVERSION_HDRS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)_HDRS))
THIRD_PARTY_DOUBLECONVERSION_CHECKS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)_CHECKS))
THIRD_PARTY_DOUBLECONVERSION_OBJS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)_OBJS))
$(THIRD_PARTY_DOUBLECONVERSION_A_OBJS): third_party/double-conversion/dc.mk
.PHONY: o/$(MODE)/third_party/double-conversion
o/$(MODE)/third_party/double-conversion: \
o/$(MODE)/third_party/double-conversion/test \
$(THIRD_PARTY_DOUBLECONVERSION_CHECKS)

View file

@ -1,25 +1,7 @@
#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐
#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────────────────────┘
PKGS += THIRD_PARTY_DOUBLECONVERSION
THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS += THIRD_PARTY_DOUBLECONVERSION_A
THIRD_PARTY_DOUBLECONVERSION = \
$(THIRD_PARTY_DOUBLECONVERSION_A_DEPS) \
$(THIRD_PARTY_DOUBLECONVERSION_A)
THIRD_PARTY_DOUBLECONVERSION_A = \
o/$(MODE)/third_party/double-conversion/libdouble-conversion.a
THIRD_PARTY_DOUBLECONVERSION_A_FILES := $(wildcard third_party/double-conversion/*)
THIRD_PARTY_DOUBLECONVERSION_A_SRCS_C = $(filter %.c,$(THIRD_PARTY_DOUBLECONVERSION_A_FILES))
THIRD_PARTY_DOUBLECONVERSION_A_SRCS_CC = $(filter %.cc,$(THIRD_PARTY_DOUBLECONVERSION_A_FILES))
THIRD_PARTY_DOUBLECONVERSION_A_HDRS = $(filter %.h,$(THIRD_PARTY_DOUBLECONVERSION_A_FILES))
THIRD_PARTY_DOUBLECONVERSION_A_SRCS = \
$(THIRD_PARTY_DOUBLECONVERSION_A_SRCS_C) \
$(THIRD_PARTY_DOUBLECONVERSION_A_SRCS_CC)
PKGS += THIRD_PARTY_DOUBLECONVERSION_TEST
THIRD_PARTY_DOUBLECONVERSION_TEST_COMS = \
o/$(MODE)/third_party/double-conversion/double-conversion-tester.com
@ -28,9 +10,15 @@ THIRD_PARTY_DOUBLECONVERSION_TEST_BINS = \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_COMS) \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_COMS:%=%.dbg)
THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS += THIRD_PARTY_DOUBLECONVERSION_TEST_A
THIRD_PARTY_DOUBLECONVERSION_TEST_HDRS = \
third_party/double-conversion/test/cctest.h \
third_party/double-conversion/test/checks.h \
third_party/double-conversion/test/gay-fixed.h \
third_party/double-conversion/test/gay-precision.h \
third_party/double-conversion/test/gay-shortest.h \
third_party/double-conversion/test/gay-shortest-single.h
THIRD_PARTY_DOUBLECONVERSION_TEST_A_SRCS_CC = \
THIRD_PARTY_DOUBLECONVERSION_TEST_SRCS = \
third_party/double-conversion/test/cctest.cc \
third_party/double-conversion/test/gay-fixed.cc \
third_party/double-conversion/test/gay-precision.cc \
@ -46,64 +34,26 @@ THIRD_PARTY_DOUBLECONVERSION_TEST_A_SRCS_CC = \
third_party/double-conversion/test/test-ieee.cc \
third_party/double-conversion/test/test-strtod.cc
THIRD_PARTY_DOUBLECONVERSION_TEST_A_HDRS = \
third_party/double-conversion/test/cctest.h \
third_party/double-conversion/test/checks.h \
third_party/double-conversion/test/gay-fixed.h \
third_party/double-conversion/test/gay-precision.h \
third_party/double-conversion/test/gay-shortest.h \
third_party/double-conversion/test/gay-shortest-single.h
THIRD_PARTY_DOUBLECONVERSION_TEST_OBJS = \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_SRCS:%.cc=o/$(MODE)/%.o)
THIRD_PARTY_DOUBLECONVERSION_TEST_A_SRCS = \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_A_SRCS_C) \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_A_SRCS_CC)
THIRD_PARTY_DOUBLECONVERSION_A_OBJS = \
$(THIRD_PARTY_DOUBLECONVERSION_A_SRCS_C:%.c=o/$(MODE)/%.o) \
$(THIRD_PARTY_DOUBLECONVERSION_A_SRCS_CC:%.cc=o/$(MODE)/%.o)
THIRD_PARTY_DOUBLECONVERSION_A_CHECKS = \
$(THIRD_PARTY_DOUBLECONVERSION_A).pkg \
$(THIRD_PARTY_DOUBLECONVERSION_A_HDRS:%=o/$(MODE)/%.okk)
THIRD_PARTY_DOUBLECONVERSION_TEST_A_OBJS = \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_A_SRCS_CC:%.cc=o/$(MODE)/%.o)
THIRD_PARTY_DOUBLECONVERSION_A_DIRECTDEPS = \
THIRD_PARTY_DOUBLECONVERSION_TEST_DIRECTDEPS = \
LIBC_INTRIN \
LIBC_NEXGEN32E \
LIBC_MEM \
LIBC_RUNTIME \
LIBC_STDIO \
LIBC_FMT \
LIBC_SYSV \
LIBC_STR \
LIBC_STUBS \
LIBC_TINYMATH \
THIRD_PARTY_GDTOA \
THIRD_PARTY_LIBCXX
THIRD_PARTY_LIBCXX \
THIRD_PARTY_DOUBLECONVERSION
THIRD_PARTY_DOUBLECONVERSION_A_DEPS := \
$(call uniq,$(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_A_DIRECTDEPS),$($(x))))
$(THIRD_PARTY_DOUBLECONVERSION_A): \
third_party/double-conversion/ \
$(THIRD_PARTY_DOUBLECONVERSION_A).pkg \
$(THIRD_PARTY_DOUBLECONVERSION_A_OBJS)
$(THIRD_PARTY_DOUBLECONVERSION_A).pkg: \
$(THIRD_PARTY_DOUBLECONVERSION_A_OBJS) \
$(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_A_DIRECTDEPS),$($(x)_A).pkg)
$(THIRD_PARTY_DOUBLECONVERSION_A_OBJS): private \
OVERRIDE_CXXFLAGS += \
-ffunction-sections \
-fdata-sections
THIRD_PARTY_DOUBLECONVERSION_TEST_DEPS := \
$(call uniq,$(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_TEST_DIRECTDEPS),$($(x))))
o/$(MODE)/third_party/double-conversion/double-conversion-tester.com.dbg: \
$(THIRD_PARTY_DOUBLECONVERSION_A_DEPS) \
$(THIRD_PARTY_DOUBLECONVERSION_A) \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_A_OBJS) \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_DEPS) \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_OBJS) \
$(CRT) \
$(APE_NO_MODIFY_SELF)
@$(APELINK)
@ -119,7 +69,7 @@ THIRD_PARTY_DOUBLECONVERSION_TEST_NAMES = \
test-bignum-dtoa \
test-bignum
THIRD_PARTY_DOUBLECONVERSION_TEST_RUNS = \
THIRD_PARTY_DOUBLECONVERSION_TEST_CHECKS = \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_NAMES:%=o/$(MODE)/third_party/double-conversion/%.runs)
# TODO(jart): find some way to run these under runitd
@ -160,13 +110,6 @@ o/$(MODE)/third_party/double-conversion/test-bignum.runs: \
o/$(MODE)/third_party/double-conversion/double-conversion-tester.com
@$(COMPILE) -ACHECK -wtT$@ $< test-bignum
THIRD_PARTY_DOUBLECONVERSION_LIBS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)))
THIRD_PARTY_DOUBLECONVERSION_SRCS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)_SRCS))
THIRD_PARTY_DOUBLECONVERSION_HDRS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)_HDRS))
THIRD_PARTY_DOUBLECONVERSION_CHECKS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)_CHECKS))
THIRD_PARTY_DOUBLECONVERSION_OBJS = $(foreach x,$(THIRD_PARTY_DOUBLECONVERSION_ARTIFACTS),$($(x)_OBJS))
.PHONY: o/$(MODE)/third_party/double-conversion
o/$(MODE)/third_party/double-conversion: \
$(THIRD_PARTY_DOUBLECONVERSION_CHECKS) \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_RUNS)
.PHONY: o/$(MODE)/third_party/double-conversion/test
o/$(MODE)/third_party/double-conversion/test: \
$(THIRD_PARTY_DOUBLECONVERSION_TEST_CHECKS)

View file

@ -230,6 +230,7 @@ THIRD_PARTY_LUA_LUA_DIRECTDEPS = \
LIBC_LOG \
LIBC_STR \
LIBC_SYSV \
LIBC_THREAD \
THIRD_PARTY_LINENOISE \
THIRD_PARTY_LUA \
THIRD_PARTY_LUA_UNIX \

134
third_party/musl/strfmon.c vendored Normal file
View file

@ -0,0 +1,134 @@
/*-*- mode:c;indent-tabs-mode:t;c-basic-offset:8;tab-width:8;coding:utf-8 -*-│
vi: set et ft=c ts=8 tw=8 fenc=utf-8 :vi
Musl Libc
Copyright © 2005-2014 Rich Felker, et al.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
#include "libc/str/locale.h"
#include "libc/str/str.h"
#include "libc/thread/tls.h"
#include "third_party/python/Include/object.h"
asm(".ident\t\"\\n\\n\
Musl libc (MIT License)\\n\
Copyright 2005-2014 Rich Felker, et. al.\"");
asm(".include \"libc/disclaimer.inc\"");
// clang-format off
static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_list ap)
{
size_t l;
double x;
int fill, nogrp, negpar, nosym, left, intl;
int lp, rp, w, fw;
char *s0=s;
for (; n && *fmt; ) {
if (*fmt != '%') {
literal:
*s++ = *fmt++;
n--;
continue;
}
fmt++;
if (*fmt == '%') goto literal;
fill = ' ';
nogrp = 0;
negpar = 0;
nosym = 0;
left = 0;
for (; ; fmt++) {
switch (*fmt) {
case '=':
fill = *++fmt;
continue;
case '^':
nogrp = 1;
continue;
case '(':
negpar = 1;
case '+':
continue;
case '!':
nosym = 1;
continue;
case '-':
left = 1;
continue;
}
break;
}
for (fw=0; isdigit(*fmt); fmt++)
fw = 10*fw + (*fmt-'0');
lp = 0;
rp = 2;
if (*fmt=='#') for (lp=0, fmt++; isdigit(*fmt); fmt++)
lp = 10*lp + (*fmt-'0');
if (*fmt=='.') for (rp=0, fmt++; isdigit(*fmt); fmt++)
rp = 10*rp + (*fmt-'0');
intl = *fmt++ == 'i';
w = lp + 1 + rp;
if (!left && fw>w) w = fw;
x = va_arg(ap, double);
l = snprintf(s, n, "%*.*f", w, rp, x);
if (l >= n) {
errno = E2BIG;
return -1;
}
s += l;
n -= l;
}
return s-s0;
}
ssize_t strfmon_l(char *restrict s, size_t n, locale_t loc, const char *restrict fmt, ...)
{
va_list ap;
ssize_t ret;
va_start(ap, fmt);
ret = vstrfmon_l(s, n, loc, fmt, ap);
va_end(ap);
return ret;
}
ssize_t strfmon(char *restrict s, size_t n, const char *restrict fmt, ...)
{
va_list ap;
ssize_t ret;
va_start(ap, fmt);
ret = vstrfmon_l(s, n, (locale_t)__get_tls()->tib_locale, fmt, ap);
va_end(ap);
return ret;
}

View file

@ -10507,7 +10507,7 @@ os_cpu_count_impl(PyObject *module)
/*[clinic end generated code: output=5fc29463c3936a9c input=e7c8f4ba6dbbadd3]*/
{
int ncpu;
ncpu = GetCpuCount();
ncpu = _getcpucount();
if (ncpu >= 1)
return PyLong_FromLong(ncpu);
else

View file

@ -973,7 +973,7 @@ int checkdir(__G__ pathcomp, flag)
/* Function mkdir() */
/********************/
int mkdir(path, mode)
int mkdir_(path, mode)
ZCONST char *path;
int mode; /* ignored */
/*

View file

@ -17,7 +17,6 @@
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/mem/copyfd.internal.h"
#include "libc/calls/copyfile.h"
#include "libc/calls/ioctl.h"
#include "libc/calls/struct/itimerval.h"
@ -42,12 +41,12 @@
#include "libc/macros.internal.h"
#include "libc/math.h"
#include "libc/mem/alg.h"
#include "libc/mem/copyfd.internal.h"
#include "libc/mem/gc.internal.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/kcpuids.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/sysconf.h"
#include "libc/stdio/append.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
@ -301,7 +300,7 @@ void PrintMakeCommand(void) {
appends(&output, "make MODE=");
appends(&output, mode);
appends(&output, " -j");
appendd(&output, buf, FormatUint64(buf, GetCpuCount()) - buf);
appendd(&output, buf, FormatUint64(buf, _getcpucount()) - buf);
appendw(&output, ' ');
appends(&output, target);
}

View file

@ -39,7 +39,6 @@
#include "libc/runtime/ezmap.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/stack.h"
#include "libc/runtime/sysconf.h"
#include "libc/stdio/append.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
@ -426,7 +425,7 @@ int main(int argc, char *argv[]) {
ShowCrashReports();
if (argc == 2 && !strcmp(argv[1], "-n")) exit(0);
GetOpts(argc, argv);
threads = 1; // GetCpuCount();
threads = 1; // _getcpucount();
th = calloc(threads, sizeof(*th));
bouts = calloc(threads, sizeof(*bouts));
LoadRelationships(argc, argv);

View file

@ -44,7 +44,6 @@
#include "libc/mem/mem.h"
#include "libc/nexgen32e/kcpuids.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/sysconf.h"
#include "libc/sock/sock.h"
#include "libc/sock/struct/pollfd.h"
#include "libc/stdio/stdio.h"
@ -162,9 +161,9 @@ static void GetOpts(int argc, char *argv[]) {
g_fszquota = 256 * 1000 * 1000;
if (!sysinfo(&si)) {
g_memquota = si.totalram;
g_proquota = GetCpuCount() + si.procs;
g_proquota = _getcpucount() + si.procs;
} else {
g_proquota = GetCpuCount() * 100;
g_proquota = _getcpucount() * 100;
g_memquota = 4L * 1024 * 1024 * 1024;
}
while ((opt = getopt(argc, argv, "hnqkNVT:p:u:g:c:C:D:P:M:F:O:v:")) != -1) {

View file

@ -1711,6 +1711,7 @@ Keywords={
"LLONG_MAX",
"LONG_LONG_MAX",
"SIZE_MAX",
"SSIZE_MAX",
"INT8_MAX",
"INT16_MAX",
"INT32_MAX",

View file

@ -42,6 +42,7 @@
"LLONG_MAX"
"LONG_LONG_MAX"
"SIZE_MAX"
"SSIZE_MAX"
"INT8_MAX"
"INT16_MAX"
"INT32_MAX"

View file

@ -39,7 +39,6 @@
#include "libc/nexgen32e/rdtsc.h"
#include "libc/nexgen32e/rdtscp.h"
#include "libc/runtime/runtime.h"
#include "libc/runtime/sysconf.h"
#include "libc/sock/sock.h"
#include "libc/stdio/rand.h"
#include "libc/str/str.h"
@ -126,7 +125,7 @@ int LuaGetCpuCore(lua_State *L) {
}
int LuaGetCpuCount(lua_State *L) {
lua_pushinteger(L, GetCpuCount());
lua_pushinteger(L, _getcpucount());
return 1;
}