diff --git a/Makefile b/Makefile index 9f277030c..09f242c0c 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/examples/examples.mk b/examples/examples.mk index 751227759..f0ad7b420 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -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 \ diff --git a/examples/getcpucount.c b/examples/getcpucount.c index 7d757cc3c..66184743a 100644 --- a/examples/getcpucount.c +++ b/examples/getcpucount.c @@ -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()); } diff --git a/examples/greenbean.c b/examples/greenbean.c index 4cfe8d871..148203a72 100644 --- a/examples/greenbean.c +++ b/examples/greenbean.c @@ -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); diff --git a/libc/calls/execve-nt.greg.c b/libc/calls/execve-nt.greg.c index 1d41fbc28..61553a921 100644 --- a/libc/calls/execve-nt.greg.c +++ b/libc/calls/execve-nt.greg.c @@ -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); diff --git a/libc/calls/getcpucount.c b/libc/calls/getcpucount.c index 3f0640322..500ce3c76 100644 --- a/libc/calls/getcpucount.c +++ b/libc/calls/getcpucount.c @@ -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; } diff --git a/libc/calls/getloadavg-nt.c b/libc/calls/getloadavg-nt.c index 6a82d5bbc..d8325576a 100644 --- a/libc/calls/getloadavg-nt.c +++ b/libc/calls/getloadavg-nt.c @@ -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); } diff --git a/libc/calls/ntspawn.c b/libc/calls/ntspawn.c index 4590dfd1b..91bf1d1ee 100644 --- a/libc/calls/ntspawn.c +++ b/libc/calls/ntspawn.c @@ -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; diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index 2d85c355d..f005312b9 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -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; } diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index afa626d0f..f06417b6a 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -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; } diff --git a/libc/calls/readv.c b/libc/calls/readv.c index 6d412f83d..f7c4d7e4b 100644 --- a/libc/calls/readv.c +++ b/libc/calls/readv.c @@ -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; } diff --git a/libc/calls/sched_setaffinity.c b/libc/calls/sched_setaffinity.c index 722326826..3b4d99050 100644 --- a/libc/calls/sched_setaffinity.c +++ b/libc/calls/sched_setaffinity.c @@ -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 diff --git a/libc/calls/sched_setparam.c b/libc/calls/sched_setparam.c index 565c071ba..ac8e5475b 100644 --- a/libc/calls/sched_setparam.c +++ b/libc/calls/sched_setparam.c @@ -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; diff --git a/libc/calls/sched_setscheduler.c b/libc/calls/sched_setscheduler.c index 64e1fd2b4..f8054fba6 100644 --- a/libc/calls/sched_setscheduler.c +++ b/libc/calls/sched_setscheduler.c @@ -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; diff --git a/libc/calls/struct/iovec.h b/libc/calls/struct/iovec.h index 97d37fae6..de85f13f6 100644 --- a/libc/calls/struct/iovec.h +++ b/libc/calls/struct/iovec.h @@ -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) */ diff --git a/libc/calls/struct/iovec.internal.h b/libc/calls/struct/iovec.internal.h index 3996d3c4d..2f4c4c4ef 100644 --- a/libc/calls/struct/iovec.internal.h +++ b/libc/calls/struct/iovec.internal.h @@ -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_ */ diff --git a/libc/calls/syscall-nt.internal.h b/libc/calls/syscall-nt.internal.h index 4d011f596..f20f54ade 100644 --- a/libc/calls/syscall-nt.internal.h +++ b/libc/calls/syscall-nt.internal.h @@ -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; diff --git a/libc/calls/syscall_support-sysv.internal.h b/libc/calls/syscall_support-sysv.internal.h index b75389757..d50f79b2b 100644 --- a/libc/calls/syscall_support-sysv.internal.h +++ b/libc/calls/syscall_support-sysv.internal.h @@ -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) */ diff --git a/libc/calls/writev.c b/libc/calls/writev.c index da2a0791c..292570c79 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -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; } diff --git a/libc/fmt/strtol.c b/libc/fmt/strtol.c index 4094f6da2..b37720662 100644 --- a/libc/fmt/strtol.c +++ b/libc/fmt/strtol.c @@ -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; diff --git a/libc/fmt/strtol.internal.h b/libc/fmt/strtol.internal.h index 968a159f7..3be9d6cce 100644 --- a/libc/fmt/strtol.internal.h +++ b/libc/fmt/strtol.internal.h @@ -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; \ diff --git a/libc/fmt/wcstoul.c b/libc/fmt/wcstoul.c index 6f2ddeef2..192bc28c3 100644 --- a/libc/fmt/wcstoul.c +++ b/libc/fmt/wcstoul.c @@ -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; diff --git a/libc/intrin/createprocess.c b/libc/intrin/createprocess.c index 5244cda19..449fcd16a 100644 --- a/libc/intrin/createprocess.c +++ b/libc/intrin/createprocess.c @@ -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; diff --git a/libc/intrin/describeiovec.c b/libc/intrin/describeiovec.c new file mode 100644 index 000000000..072579e58 --- /dev/null +++ b/libc/intrin/describeiovec.c @@ -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; +} diff --git a/libc/isystem/monetary.h b/libc/isystem/monetary.h new file mode 100644 index 000000000..5fee6c283 --- /dev/null +++ b/libc/isystem/monetary.h @@ -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_ */ diff --git a/libc/isystem/sys/socket.h b/libc/isystem/sys/socket.h index bb311e8e1..4c51ed938 100644 --- a/libc/isystem/sys/socket.h +++ b/libc/isystem/sys/socket.h @@ -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" diff --git a/libc/mem/setenv.c b/libc/mem/setenv.c index b0de20855..21af18abb 100644 --- a/libc/mem/setenv.c +++ b/libc/mem/setenv.c @@ -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; } diff --git a/libc/runtime/daemon.c b/libc/runtime/daemon.c index f70011623..91bd6aa59 100644 --- a/libc/runtime/daemon.c +++ b/libc/runtime/daemon.c @@ -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)); } } diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 490b44e73..1a2eb6699 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -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)) && diff --git a/libc/runtime/fork.c b/libc/runtime/fork.c index 34a0bf470..bc18b67ab 100644 --- a/libc/runtime/fork.c +++ b/libc/runtime/fork.c @@ -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); +} diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 40953f2aa..91f3a4ada 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -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 *); diff --git a/libc/runtime/sysconf.c b/libc/runtime/sysconf.c index a17085baa..a3f835d9b 100644 --- a/libc/runtime/sysconf.c +++ b/libc/runtime/sysconf.c @@ -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; diff --git a/libc/runtime/sysconf.h b/libc/runtime/sysconf.h index 317158ad9..6c2e05cf9 100644 --- a/libc/runtime/sysconf.h +++ b/libc/runtime/sysconf.h @@ -147,7 +147,6 @@ COSMOPOLITAN_C_START_ long sysconf(int); -unsigned GetCpuCount(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/runtime/vfork.S b/libc/runtime/vfork.S index a1bb44b53..7b4e4ba89 100644 --- a/libc/runtime/vfork.S +++ b/libc/runtime/vfork.S @@ -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 diff --git a/libc/sock/recvfrom.c b/libc/sock/recvfrom.c index 447bb12b1..d34bcb1c5 100644 --- a/libc/sock/recvfrom.c +++ b/libc/sock/recvfrom.c @@ -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; diff --git a/libc/sock/recvmsg.c b/libc/sock/recvmsg.c index a2da9a986..8ad9ff089 100644 --- a/libc/sock/recvmsg.c +++ b/libc/sock/recvmsg.c @@ -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; } diff --git a/libc/sock/sendmsg.c b/libc/sock/sendmsg.c index dc700b9c2..195e04709 100644 --- a/libc/sock/sendmsg.c +++ b/libc/sock/sendmsg.c @@ -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); } diff --git a/libc/sock/sendto.c b/libc/sock/sendto.c index 82aa74ab7..b7bf87e2d 100644 --- a/libc/sock/sendto.c +++ b/libc/sock/sendto.c @@ -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; diff --git a/libc/sock/struct/cmsghdr.h b/libc/sock/struct/cmsghdr.h new file mode 100644 index 000000000..bdd1d1274 --- /dev/null +++ b/libc/sock/struct/cmsghdr.h @@ -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_ */ diff --git a/libc/sock/struct/cmsghdr.internal.h b/libc/sock/struct/cmsghdr.internal.h new file mode 100644 index 000000000..291f79ef4 --- /dev/null +++ b/libc/sock/struct/cmsghdr.internal.h @@ -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_ */ diff --git a/libc/sock/struct/msghdr.h b/libc/sock/struct/msghdr.h index d3730de29..a673ba9da 100644 --- a/libc/sock/struct/msghdr.h +++ b/libc/sock/struct/msghdr.h @@ -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 */ }; diff --git a/libc/sock/struct/sockaddr.h b/libc/sock/struct/sockaddr.h index 3fcd71ce1..b9477668b 100644 --- a/libc/sock/struct/sockaddr.h +++ b/libc/sock/struct/sockaddr.h @@ -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_ diff --git a/libc/stdio/cocmd.c b/libc/stdio/cocmd.c index aa0f0cb44..1f198e246 100644 --- a/libc/stdio/cocmd.c +++ b/libc/stdio/cocmd.c @@ -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(); } diff --git a/libc/x/makedirs.c b/libc/stdio/makedirs.c similarity index 92% rename from libc/x/makedirs.c rename to libc/stdio/makedirs.c index 3794a7c0f..ee2ccc2cd 100644 --- a/libc/x/makedirs.c +++ b/libc/stdio/makedirs.c @@ -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 { diff --git a/libc/stdio/popen.c b/libc/stdio/popen.c index 8a385912d..2a3e62a64 100644 --- a/libc/stdio/popen.c +++ b/libc/stdio/popen.c @@ -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`, `>>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; } diff --git a/libc/stdio/spawn.c b/libc/stdio/spawn.c index 59270ae6d..904a750a5 100644 --- a/libc/stdio/spawn.c +++ b/libc/stdio/spawn.c @@ -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 { diff --git a/libc/stdio/stdio.h b/libc/stdio/stdio.h index 22e4d5966..ceaf58274 100644 --- a/libc/stdio/stdio.h +++ b/libc/stdio/stdio.h @@ -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(); diff --git a/libc/stdio/system.c b/libc/stdio/system.c index 8fbf8c0c8..d4abac217 100644 --- a/libc/stdio/system.c +++ b/libc/stdio/system.c @@ -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`, `>>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 diff --git a/libc/str/locale.h b/libc/str/locale.h index e1382fa59..d9868fa2f 100644 --- a/libc/str/locale.h +++ b/libc/str/locale.h @@ -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) */ diff --git a/libc/str/str.h b/libc/str/str.h index b0eb7fbb2..571dc03b4 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -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); diff --git a/libc/str/strchr.c b/libc/str/strchr.c index 99cd2f4eb..a52a58803 100644 --- a/libc/str/strchr.c +++ b/libc/str/strchr.c @@ -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; diff --git a/libc/str/strncpy.c b/libc/str/strncpy.c index 5b920c960..ae317629c 100644 --- a/libc/str/strncpy.c +++ b/libc/str/strncpy.c @@ -60,6 +60,7 @@ * @return dest * @see stpncpy(), memccpy() * @asyncsignalsafe + * @vforksafe */ char *strncpy(char *dest, const char *src, size_t stride) { size_t i; diff --git a/libc/testlib/subprocess.h b/libc/testlib/subprocess.h index 5e1611448..43f865ade 100644 --- a/libc/testlib/subprocess.h +++ b/libc/testlib/subprocess.h @@ -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); diff --git a/libc/x/x.h b/libc/x/x.h index 4f5ff9301..297273298 100644 --- a/libc/x/x.h +++ b/libc/x/x.h @@ -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() diff --git a/net/turfwar/turfwar.c b/net/turfwar/turfwar.c index b22d7f353..7eb85efe0 100644 --- a/net/turfwar/turfwar.c +++ b/net/turfwar/turfwar.c @@ -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(); diff --git a/test/libc/calls/mkdir_test.c b/test/libc/calls/mkdir_test.c index 783dea847..6f9b4015a 100644 --- a/test/libc/calls/mkdir_test.c +++ b/test/libc/calls/mkdir_test.c @@ -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" diff --git a/test/libc/calls/sched_getaffinity_test.c b/test/libc/calls/sched_getaffinity_test.c index 286009b42..055d91fe4 100644 --- a/test/libc/calls/sched_getaffinity_test.c +++ b/test/libc/calls/sched_getaffinity_test.c @@ -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); diff --git a/test/libc/calls/stat_test.c b/test/libc/calls/stat_test.c index cc1754054..4420d86b7 100644 --- a/test/libc/calls/stat_test.c +++ b/test/libc/calls/stat_test.c @@ -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" diff --git a/test/libc/fmt/atoi_test.c b/test/libc/fmt/atoi_test.c index 0f032a2ef..7afed03f2 100644 --- a/test/libc/fmt/atoi_test.c +++ b/test/libc/fmt/atoi_test.c @@ -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, diff --git a/test/libc/mem/malloc_test.c b/test/libc/mem/malloc_test.c index 680c60d42..fb8c72509 100644 --- a/test/libc/mem/malloc_test.c +++ b/test/libc/mem/malloc_test.c @@ -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, diff --git a/test/libc/runtime/daemon_test.c b/test/libc/runtime/daemon_test.c new file mode 100644 index 000000000..b06ca4fc3 --- /dev/null +++ b/test/libc/runtime/daemon_test.c @@ -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); +} diff --git a/libc/intrin/describeiov.c b/test/libc/runtime/omg_test.c similarity index 70% rename from libc/intrin/describeiov.c rename to test/libc/runtime/omg_test.c index 3f8e32828..83f05e78f 100644 --- a/libc/intrin/describeiov.c +++ b/test/libc/runtime/omg_test.c @@ -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); } diff --git a/test/libc/sock/test.mk b/test/libc/sock/test.mk index 4d8f630c6..1cdc64d55 100644 --- a/test/libc/sock/test.mk +++ b/test/libc/sock/test.mk @@ -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 diff --git a/test/libc/x/makedirs_test.c b/test/libc/stdio/makedirs_test.c similarity index 98% rename from test/libc/x/makedirs_test.c rename to test/libc/stdio/makedirs_test.c index eb1ae9419..6c6117abd 100644 --- a/test/libc/x/makedirs_test.c +++ b/test/libc/stdio/makedirs_test.c @@ -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/" \ diff --git a/test/libc/stdio/popen_test.c b/test/libc/stdio/popen_test.c new file mode 100644 index 000000000..c4dabde5a --- /dev/null +++ b/test/libc/stdio/popen_test.c @@ -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); +} diff --git a/test/libc/stdio/spawn_test.c b/test/libc/stdio/spawn_test.c index 6defbd214..95fabe42a 100644 --- a/test/libc/stdio/spawn_test.c +++ b/test/libc/stdio/spawn_test.c @@ -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 diff --git a/test/libc/stdio/system_test.c b/test/libc/stdio/system_test.c index 6e67abcd4..970c10aaf 100644 --- a/test/libc/stdio/system_test.c +++ b/test/libc/stdio/system_test.c @@ -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"))); } diff --git a/test/libc/stdio/test.mk b/test/libc/stdio/test.mk index 58141822d..3e001f3ab 100644 --- a/test/libc/stdio/test.mk +++ b/test/libc/stdio/test.mk @@ -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 diff --git a/third_party/double-conversion/dc.mk b/third_party/double-conversion/dc.mk new file mode 100644 index 000000000..34591b39c --- /dev/null +++ b/third_party/double-conversion/dc.mk @@ -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) diff --git a/third_party/double-conversion/double-conversion.mk b/third_party/double-conversion/test/test.mk similarity index 54% rename from third_party/double-conversion/double-conversion.mk rename to third_party/double-conversion/test/test.mk index abaff4aa8..49c095eec 100644 --- a/third_party/double-conversion/double-conversion.mk +++ b/third_party/double-conversion/test/test.mk @@ -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) diff --git a/third_party/lua/lua.mk b/third_party/lua/lua.mk index 45694a40c..a157cbcff 100644 --- a/third_party/lua/lua.mk +++ b/third_party/lua/lua.mk @@ -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 \ diff --git a/third_party/musl/strfmon.c b/third_party/musl/strfmon.c new file mode 100644 index 000000000..87610ab9b --- /dev/null +++ b/third_party/musl/strfmon.c @@ -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; +} diff --git a/third_party/python/Modules/posixmodule.c b/third_party/python/Modules/posixmodule.c index 52f1919ac..26c40f0f8 100644 --- a/third_party/python/Modules/posixmodule.c +++ b/third_party/python/Modules/posixmodule.c @@ -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 diff --git a/third_party/unzip/unix.c b/third_party/unzip/unix.c index 5e3f90328..4d41e9b96 100644 --- a/third_party/unzip/unix.c +++ b/third_party/unzip/unix.c @@ -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 */ /* diff --git a/tool/build/compile.c b/tool/build/compile.c index 9a96c7e70..5f09c5699 100644 --- a/tool/build/compile.c +++ b/tool/build/compile.c @@ -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); } diff --git a/tool/build/mkdeps.c b/tool/build/mkdeps.c index 0d4d7a257..705a94c58 100644 --- a/tool/build/mkdeps.c +++ b/tool/build/mkdeps.c @@ -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); diff --git a/tool/build/pledge.c b/tool/build/pledge.c index 518f6d03f..7149965ea 100644 --- a/tool/build/pledge.c +++ b/tool/build/pledge.c @@ -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) { diff --git a/tool/emacs/c.lang b/tool/emacs/c.lang index c316300c3..3f25223d9 100644 --- a/tool/emacs/c.lang +++ b/tool/emacs/c.lang @@ -1711,6 +1711,7 @@ Keywords={ "LLONG_MAX", "LONG_LONG_MAX", "SIZE_MAX", +"SSIZE_MAX", "INT8_MAX", "INT16_MAX", "INT32_MAX", diff --git a/tool/emacs/cosmo-c-constants.el b/tool/emacs/cosmo-c-constants.el index c351f747c..9164a86f7 100644 --- a/tool/emacs/cosmo-c-constants.el +++ b/tool/emacs/cosmo-c-constants.el @@ -42,6 +42,7 @@ "LLONG_MAX" "LONG_LONG_MAX" "SIZE_MAX" + "SSIZE_MAX" "INT8_MAX" "INT16_MAX" "INT32_MAX" diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c index a4f3b6e0d..373400317 100644 --- a/tool/net/lfuncs.c +++ b/tool/net/lfuncs.c @@ -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; }