diff --git a/ape/ape.S b/ape/ape.S index 5313039db..2521ac30c 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -112,7 +112,15 @@ cstr: .endobj cstr,globl,hidden # ←for gdb readability // // @param dl is drive number // @noreturn -ape_mz: .asciz "MZqFpD='\n" # Mark 'Zibo' Joseph Zbikowski +ape_mz: +#if SupportsWindows() || SupportsMetal() + .asciz "MZqFpD='\n" # Mark 'Zibo' Joseph Zbikowski +#else +// Avoid virus scanner reputation damage when targeting System Five. +// WARNING: This prefix is experimental; it may be removed sometime. +// TODO(jart): Find another prefix that will work with BIOS loading. + .asciz "JTqFpD='\n" # Mark 'Zibo' Joseph Zbikowski +#endif .short 0x1000 # MZ: lowers upper bound load / 16 .short 0xf800 # MZ: roll greed on bss .short 0 # MZ: lower bound on stack segment diff --git a/ape/apeinstall.sh b/ape/apeinstall.sh index 4f1c50948..c97d8fff7 100755 --- a/ape/apeinstall.sh +++ b/ape/apeinstall.sh @@ -1,6 +1,6 @@ #!/bin/sh -if [ $UID -eq 0 ]; then +if [ "$(id -u)" -eq 0 ]; then SUDO= else SUDO=sudo @@ -97,6 +97,8 @@ if [ x"$(uname -s)" = xLinux ]; then echo you may need to edit configs to persist across reboot >&2 echo '$SUDO sh -c "echo '"'"':APE:M::MZqFpD::/usr/bin/ape:'"'"' >/proc/sys/fs/binfmt_misc/register"' >&2 $SUDO sh -c "echo ':APE:M::MZqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" || exit + echo '$SUDO sh -c "echo '"'"':APE-sysv:M::JTqFpD::/usr/bin/ape:'"'"' >/proc/sys/fs/binfmt_misc/register"' >&2 + $SUDO sh -c "echo ':APE-sysv:M::JTqFpD::/usr/bin/ape:' >/proc/sys/fs/binfmt_misc/register" || exit echo done >&2 if [ x"$(cat /proc/sys/fs/binfmt_misc/status)" = xdisabled ]; then diff --git a/ape/apeuninstall.sh b/ape/apeuninstall.sh index e4abe5406..69cd41031 100755 --- a/ape/apeuninstall.sh +++ b/ape/apeuninstall.sh @@ -11,6 +11,7 @@ fi echo "APE Uninstaller intends to run" echo echo " $SUDO sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/APE'" + echo " $SUDO sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/APE-sysv'" echo " $SUDO rm -f /usr/bin/ape ~/.ape o/tmp/.ape /tmp/.ape" echo echo "You may then use ape/apeinstall.sh to reinstall it" @@ -21,4 +22,7 @@ set -ex if [ -f /proc/sys/fs/binfmt_misc/APE ]; then $SUDO sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/APE' || exit fi +if [ -f /proc/sys/fs/binfmt_misc/APE-sysv ]; then + $SUDO sh -c 'echo -1 >/proc/sys/fs/binfmt_misc/APE-sysv' || exit +fi $SUDO rm -f /usr/bin/ape ~/.ape o/tmp/.ape o/tmp/ape /tmp/.ape /tmp/ape || exit diff --git a/build/bootstrap/make.com b/build/bootstrap/make.com index a9ded9870..04ddfe983 100755 Binary files a/build/bootstrap/make.com and b/build/bootstrap/make.com differ diff --git a/build/config.mk b/build/config.mk index d37681ec8..3cf604303 100644 --- a/build/config.mk +++ b/build/config.mk @@ -29,7 +29,7 @@ endif # ifeq ($(MODE),fastbuild) CONFIG_CCFLAGS += $(BACKTRACES) $(FTRACE) -O -CONFIG_CPPFLAGS += -DSYSDEBUG +CONFIG_CPPFLAGS += -DSYSDEBUG -DDWARFLESS CONFIG_OFLAGS += -g0 CONFIG_LDFLAGS += -S TARGET_ARCH ?= -msse3 @@ -125,6 +125,22 @@ TARGET_ARCH ?= -msse3 OVERRIDE_CCFLAGS += -fno-pie endif +# System Five Mode +# +# - `make MODE=sysv` +# - Optimized +# - Backtraces +# - Debuggable +# - Syscall tracing +# - Function tracing +# - No Windows bloat! +# +ifeq ($(MODE), sysv) +CONFIG_CCFLAGS += $(BACKTRACES) $(FTRACE) -O2 +CONFIG_CPPFLAGS += -DSYSDEBUG -DSUPPORT_VECTOR=121 +TARGET_ARCH ?= -msse3 +endif + # Tiny Mode # # - `make MODE=tiny` @@ -151,7 +167,8 @@ CONFIG_CCFLAGS += \ -fschedule-insns2 \ -fomit-frame-pointer \ -momit-leaf-frame-pointer \ - -foptimize-sibling-calls + -foptimize-sibling-calls \ + -DDWARFLESS CONFIG_OFLAGS += \ -g0 CONFIG_LDFLAGS += \ @@ -181,7 +198,8 @@ CONFIG_CPPFLAGS += \ -DTINY \ -DNDEBUG \ -DTRUSTWORTHY \ - -DSUPPORT_VECTOR=1 + -DSUPPORT_VECTOR=1 \ + -DDWARFLESS DEFAULT_COPTS += \ -mred-zone CONFIG_OFLAGS += \ @@ -217,7 +235,8 @@ CONFIG_CPPFLAGS += \ -DTINY \ -DNDEBUG \ -DTRUSTWORTHY \ - -DSUPPORT_VECTOR=113 + -DSUPPORT_VECTOR=113 \ + -DDWARFLESS DEFAULT_COPTS += \ -mred-zone CONFIG_OFLAGS += \ @@ -252,7 +271,8 @@ CONFIG_CPPFLAGS += \ -DTINY \ -DNDEBUG \ -DTRUSTWORTHY \ - -DSUPPORT_VECTOR=121 + -DSUPPORT_VECTOR=121 \ + -DDWARFLESS DEFAULT_COPTS += \ -mred-zone CONFIG_CCFLAGS += \ @@ -287,7 +307,8 @@ CONFIG_CPPFLAGS += \ -DTINY \ -DNDEBUG \ -DTRUSTWORTHY \ - -DSUPPORT_VECTOR=251 + -DSUPPORT_VECTOR=251 \ + -DDWARFLESS CONFIG_CCFLAGS += \ -Os \ -fno-align-functions \ diff --git a/examples/auto-memory-safety-crash2.c b/examples/auto-memory-safety-crash2.c index 3365afd0b..5916e0b40 100644 --- a/examples/auto-memory-safety-crash2.c +++ b/examples/auto-memory-safety-crash2.c @@ -7,12 +7,12 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif -#include "libc/intrin/bits.h" #include "libc/dce.h" +#include "libc/intrin/bits.h" +#include "libc/intrin/kprintf.h" #include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" #include "libc/str/str.h" /** @@ -59,9 +59,15 @@ int main(int argc, char *argv[]) { if (!IsAsan()) { - printf("this example is intended for MODE=asan or MODE=dbg\n"); + kprintf("this example is intended for MODE=asan or MODE=dbg\n"); exit(1); } + + kprintf("----------------\n"); + kprintf(" THIS IS A TEST \n"); + kprintf("SIMULATING CRASH\n"); + kprintf("----------------\n"); + char *buffer; ShowCrashReports(); /* not needed but yoinks appropriate symbols */ buffer = malloc(13); diff --git a/examples/crashreport.c b/examples/crashreport.c index 558b6a28a..326356f74 100644 --- a/examples/crashreport.c +++ b/examples/crashreport.c @@ -7,6 +7,7 @@ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif +#include "libc/intrin/kprintf.h" #include "libc/log/log.h" #include "libc/runtime/symbols.internal.h" @@ -24,6 +25,12 @@ */ noubsan int main(int argc, char *argv[]) { + + kprintf("----------------\n"); + kprintf(" THIS IS A TEST \n"); + kprintf("SIMULATING CRASH\n"); + kprintf("----------------\n"); + volatile int64_t x; ShowCrashReports(); return 1 / (x = 0); diff --git a/examples/examples.mk b/examples/examples.mk index 944c677a4..26317f1e8 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -146,6 +146,25 @@ o/$(MODE)/examples/nesemu1.com.dbg: \ $(EXAMPLES_BOOTLOADER) @$(APELINK) +# # force symtab.com to be a zip file, by pulling a zip asset into linkage +# # we wouldn't need to do this if we depended on functions like localtime +# o/$(MODE)/examples/symtab.com.dbg: \ +# $(EXAMPLES_DEPS) \ +# o/$(MODE)/examples/symtab.o \ +# o/$(MODE)/examples/symtab.c.zip.o \ +# o/$(MODE)/examples/examples.pkg \ +# $(EXAMPLES_BOOTLOADER) +# @$(APELINK) + +# modify .com so it can read the symbol table without needing the .com.dbg file +o/$(MODE)/examples/symtab.com: \ + o/$(MODE)/examples/symtab.com.dbg \ + o/$(MODE)/third_party/zip/zip.com \ + o/$(MODE)/tool/build/symtab.com + @$(MAKE_OBJCOPY) + @$(MAKE_SYMTAB_CREATE) + @$(MAKE_SYMTAB_ZIP) + o/$(MODE)/examples/picol.o: private \ OVERRIDE_CPPFLAGS += \ -DSTACK_FRAME_UNLIMITED diff --git a/examples/nesemu1.cc b/examples/nesemu1.cc index 2e648dbea..099fbe79d 100644 --- a/examples/nesemu1.cc +++ b/examples/nesemu1.cc @@ -47,7 +47,7 @@ #include "third_party/libcxx/vector" #include "tool/viz/lib/knobs.h" -STATIC_YOINK("zip_uri_support"); +STATIC_YOINK("zipos"); #define USAGE \ " [ROM] [FMV]\n\ diff --git a/examples/symtab.c b/examples/symtab.c new file mode 100644 index 000000000..2c11eb964 --- /dev/null +++ b/examples/symtab.c @@ -0,0 +1,42 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/intrin/kprintf.h" +#include "libc/log/log.h" + +/** + * @fileoverview example of how to embed symbol table in .com file + * + * # build our binary + * make -j16 o//examples/symtab.com + * + * # move binary somewhere else + * # so it can't find the .com.dbg binary + * cp o//examples/symtab.com /tmp + * + * # run program + * # notice that it has a symbolic backtrace + * /tmp/symtab.com + * + * @see examples/examples.mk + */ + +int main(int argc, char *argv[]) { + + // this links all the debugging and zip functionality + ShowCrashReports(); + + kprintf("----------------\n"); + kprintf(" THIS IS A TEST \n"); + kprintf("SIMULATING CRASH\n"); + kprintf("----------------\n"); + + volatile int64_t x; + return 1 / (x = 0); +} diff --git a/libc/calls/__sig_mask.c b/libc/calls/__sig_mask.c index 2cb92a38f..1fc839bd5 100644 --- a/libc/calls/__sig_mask.c +++ b/libc/calls/__sig_mask.c @@ -16,7 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/sig.internal.h" +#include "libc/calls/struct/sigset.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/thread/tls.h" diff --git a/libc/calls/blocksigs.internal.h b/libc/calls/blocksigs.internal.h new file mode 100644 index 000000000..1886f02c1 --- /dev/null +++ b/libc/calls/blocksigs.internal.h @@ -0,0 +1,19 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_INTERNAL_H_ +#include "libc/calls/struct/sigset.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define BLOCK_SIGNALS \ + do { \ + sigset_t _SigMask; \ + _SigMask = _sigblockall() + +#define ALLOW_SIGNALS \ + _sigsetmask(_SigMask); \ + } \ + while (0) + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_BLOCKSIGS_INTERNAL_H_ */ diff --git a/libc/calls/calls.h b/libc/calls/calls.h index df2bceef7..7ee4d2083 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -65,13 +65,13 @@ char *ttyname(int); int access(const char *, int) dontthrow; int arch_prctl(); int chdir(const char *); -int chmod(const char *, uint32_t); -int chown(const char *, uint32_t, uint32_t); +int chmod(const char *, unsigned); +int chown(const char *, unsigned, unsigned); int chroot(const char *); int close(int); int close_range(unsigned, unsigned, unsigned); int closefrom(int); -int creat(const char *, uint32_t); +int creat(const char *, unsigned); int dup(int); int dup2(int, int); int dup3(int, int, int); @@ -84,25 +84,22 @@ int execv(const char *, char *const[]); int execve(const char *, char *const[], char *const[]); int execvp(const char *, char *const[]); int execvpe(const char *, char *const[], char *const[]); -int fexecve(int, char *const[], char *const[]); int faccessat(int, const char *, int, int); int fadvise(int, uint64_t, uint64_t, int); int fchdir(int); -int fchmod(int, uint32_t) dontthrow; -int fchmodat(int, const char *, uint32_t, int); -int fchown(int, uint32_t, uint32_t); -int fchownat(int, const char *, uint32_t, uint32_t, int); +int fchmod(int, unsigned) dontthrow; +int fchmodat(int, const char *, unsigned, int); +int fchown(int, unsigned, unsigned); +int fchownat(int, const char *, unsigned, unsigned, int); int fcntl(int, int, ...); int fdatasync(int); +int fexecve(int, char *const[], char *const[]); int flock(int, int); int fork(void); int fsync(int); int ftruncate(int, int64_t); int getdomainname(char *, size_t); -uint32_t getegid(void) nosideeffect; -uint32_t geteuid(void) nosideeffect; -uint32_t getgid(void) nosideeffect; -int getgroups(int size, uint32_t list[]); +int getgroups(int, unsigned[]); int gethostname(char *, size_t); int getloadavg(double *, int); int getpgid(int) libcesque; @@ -110,36 +107,29 @@ int getpgrp(void) nosideeffect; int getpid(void) nosideeffect libcesque; int getppid(void); int getpriority(int, unsigned); -int getresgid(uint32_t *, uint32_t *, uint32_t *); -int getresuid(uint32_t *, uint32_t *, uint32_t *); +int getresgid(unsigned *, unsigned *, unsigned *); +int getresuid(unsigned *, unsigned *, unsigned *); int getsid(int) nosideeffect libcesque; int gettid(void) libcesque; -uint32_t getuid(void) libcesque; -int sys_iopl(int); int ioprio_get(int, int); int ioprio_set(int, int, int); int issetugid(void); int kill(int, int); int killpg(int, int); -int lchmod(const char *, uint32_t); -int lchown(const char *, uint32_t, uint32_t); +int lchmod(const char *, unsigned); +int lchown(const char *, unsigned, unsigned); int link(const char *, const char *) dontthrow; int linkat(int, const char *, int, const char *, int); int madvise(void *, uint64_t, int); +int makedirs(const char *, unsigned); int memfd_create(const char *, unsigned int); int mincore(void *, size_t, unsigned char *); int mkdir(const char *, unsigned); int mkdirat(int, const char *, unsigned); -int makedirs(const char *, unsigned); -int mkfifo(const char *, uint32_t); -int mkfifoat(int, const char *, uint32_t); -int mknod(const char *, uint32_t, uint64_t); -int mknodat(int, const char *, int32_t, uint64_t); -int sys_mlock(const void *, size_t); -int sys_mlock2(const void *, size_t, int); -int sys_mlockall(int); -int sys_munlock(const void *, size_t); -int sys_munlockall(void); +int mkfifo(const char *, unsigned); +int mkfifoat(int, const char *, unsigned); +int mknod(const char *, unsigned, uint64_t); +int mknodat(int, const char *, int, uint64_t); int nice(int); int open(const char *, int, ...); int openat(int, const char *, int, ...); @@ -161,19 +151,19 @@ int renameat2(long, const char *, long, const char *, int); int rmdir(const char *); int sched_yield(void); int seccomp(unsigned, unsigned, void *); -int setegid(uint32_t); -int seteuid(uint32_t); +int setegid(unsigned); +int seteuid(unsigned); int setfsgid(unsigned); int setfsuid(unsigned); int setgid(unsigned); -int setgroups(size_t, const uint32_t[]); +int setgroups(size_t, const unsigned[]); int setpgid(int, int); int setpgrp(void); int setpriority(int, unsigned, int); -int setregid(uint32_t, uint32_t); -int setresgid(uint32_t, uint32_t, uint32_t); -int setresuid(uint32_t, uint32_t, uint32_t); -int setreuid(uint32_t, uint32_t); +int setregid(unsigned, unsigned); +int setresgid(unsigned, unsigned, unsigned); +int setresuid(unsigned, unsigned, unsigned); +int setreuid(unsigned, unsigned); int setsid(void); int setuid(unsigned); int sigignore(int); @@ -181,16 +171,22 @@ int siginterrupt(int, int); int symlink(const char *, const char *); int symlinkat(const char *, int, const char *); int sync_file_range(int, int64_t, int64_t, unsigned); +int sys_iopl(int); +int sys_mlock(const void *, size_t); +int sys_mlock2(const void *, size_t, int); +int sys_mlockall(int); +int sys_munlock(const void *, size_t); +int sys_munlockall(void); int sys_ptrace(int, ...); int sys_sysctl(const int *, unsigned, void *, size_t *, void *, size_t); -int tcsetpgrp(int, int32_t); +int tcgetpgrp(int); +int tcsetpgrp(int, int); int tgkill(int, int, int); int tkill(int, int); int tmpfd(void); -int touch(const char *, uint32_t); +int touch(const char *, unsigned); int truncate(const char *, int64_t); int ttyname_r(int, char *, size_t); -unsigned umask(unsigned); int unlink(const char *); int unlink_s(const char **); int unlinkat(int, const char *, int); @@ -199,11 +195,10 @@ int usleep(unsigned); int vfork(void) returnstwice; int wait(int *); int waitpid(int, int *, int); -int32_t tcgetpgrp(int); intptr_t syscall(int, ...); long ptrace(int, ...); -ssize_t copy_file_range(int, long *, int, long *, size_t, uint32_t); -ssize_t copyfd(int, int64_t *, int, int64_t *, size_t, uint32_t); +ssize_t copy_file_range(int, long *, int, long *, size_t, unsigned); +ssize_t copyfd(int, int64_t *, int, int64_t *, size_t, unsigned); ssize_t getfiledescriptorsize(int); ssize_t lseek(int, int64_t, int); ssize_t pread(int, void *, size_t, int64_t); @@ -212,8 +207,13 @@ ssize_t read(int, void *, size_t); ssize_t readansi(int, char *, size_t); ssize_t readlink(const char *, char *, size_t); ssize_t readlinkat(int, const char *, char *, size_t); -ssize_t splice(int, int64_t *, int, int64_t *, size_t, uint32_t); +ssize_t splice(int, int64_t *, int, int64_t *, size_t, unsigned); ssize_t write(int, const void *, size_t); +unsigned getegid(void) nosideeffect; +unsigned geteuid(void) nosideeffect; +unsigned getgid(void) nosideeffect; +unsigned getuid(void) libcesque; +unsigned umask(unsigned); void sync(void); COSMOPOLITAN_C_END_ diff --git a/libc/calls/cfspeed.c b/libc/calls/cfspeed.c new file mode 100644 index 000000000..e6a388a1d --- /dev/null +++ b/libc/calls/cfspeed.c @@ -0,0 +1,88 @@ +/*-*- 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/termios.h" +#include "libc/sysv/consts/termios.h" +#include "libc/sysv/errfuns.h" + +/** + * Returns input baud rate. + * @asyncsignalsafe + */ +uint32_t cfgetispeed(const struct termios *t) { + if (CBAUD) { + return t->c_cflag & CBAUD; + } else { + return t->c_ispeed; + } +} + +/** + * Returns output baud rate. + * @asyncsignalsafe + */ +uint32_t cfgetospeed(const struct termios *t) { + if (CBAUD) { + return t->c_cflag & CBAUD; + } else { + return t->c_ospeed; + } +} + +/** + * Sets input baud rate. + * + * @return 0 on success, or -1 w/ errno + * @asyncsignalsafe + */ +int cfsetispeed(struct termios *t, unsigned speed) { + if (speed) { + if (CBAUD) { + if (!(speed & ~CBAUD)) { + t->c_cflag &= ~CBAUD; + t->c_cflag |= speed; + } else { + return einval(); + } + } else { + t->c_ispeed = speed; + } + } + return 0; +} + +/** + * Sets output baud rate. + * + * @return 0 on success, or -1 w/ errno + * @asyncsignalsafe + */ +int cfsetospeed(struct termios *t, unsigned speed) { + if (CBAUD) { + if (!(speed & ~CBAUD)) { + t->c_cflag &= ~CBAUD; + t->c_cflag |= speed; + return 0; + } else { + return einval(); + } + } else { + t->c_ospeed = speed; + return 0; + } +} diff --git a/libc/calls/close-nt.c b/libc/calls/close-nt.c index 0db63759d..af398658d 100644 --- a/libc/calls/close-nt.c +++ b/libc/calls/close-nt.c @@ -18,15 +18,22 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/fd.internal.h" #include "libc/errno.h" +#include "libc/intrin/weaken.h" #include "libc/nt/enum/filetype.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/sysv/consts/o.h" -textwindows int sys_close_nt(struct Fd *fd) { +void sys_fcntl_nt_lock_cleanup(int) hidden; + +textwindows int sys_close_nt(struct Fd *fd, int fildes) { int e; bool ok = true; + if (_weaken(sys_fcntl_nt_lock_cleanup)) { + _weaken(sys_fcntl_nt_lock_cleanup)(fildes); + } + if (fd->kind == kFdFile && ((fd->flags & O_ACCMODE) != O_RDONLY && GetFileType(fd->handle) == kNtFileTypeDisk)) { // Like Linux, closing a file on Windows doesn't guarantee it's diff --git a/libc/calls/close.c b/libc/calls/close.c index 4aa2dbca8..d3d30fc3a 100644 --- a/libc/calls/close.c +++ b/libc/calls/close.c @@ -16,10 +16,10 @@ │ 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/internal.h" #include "libc/calls/state.internal.h" +#include "libc/calls/struct/fd.internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" @@ -81,7 +81,7 @@ int close(int fd) { } else if (__isfdkind(fd, kFdFile) || // __isfdkind(fd, kFdConsole) || // __isfdkind(fd, kFdProcess)) { // - rc = sys_close_nt(g_fds.p + fd); + rc = sys_close_nt(g_fds.p + fd, fd); } else { rc = eio(); } diff --git a/libc/calls/closefrom.c b/libc/calls/closefrom.c index 566040ade..759a26351 100644 --- a/libc/calls/closefrom.c +++ b/libc/calls/closefrom.c @@ -27,12 +27,9 @@ /** * Closes extra file descriptors, e.g. * - * // close all non-stdio file descriptors - * if (closefrom(3) == -1) { - * for (int i = 3; i < 256; ++i) { + * if (closefrom(3)) + * for (int i = 3; i < 256; ++i) * close(i); - * } - * } * * @return 0 on success, or -1 w/ errno * @raise EBADF if `first` is negative diff --git a/libc/calls/dup-nt.c b/libc/calls/dup-nt.c index 40b094385..d33ea0312 100644 --- a/libc/calls/dup-nt.c +++ b/libc/calls/dup-nt.c @@ -45,27 +45,18 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) { } // allocate a new file descriptor - for (;;) { - if (newfd == -1) { - if ((newfd = __reservefd_unlocked(start)) == -1) { - __fds_unlock(); - return -1; - } - break; - } else { - if (__ensurefds_unlocked(newfd) == -1) { - __fds_unlock(); - return -1; - } - if (g_fds.p[newfd].kind) { - __fds_unlock(); - close(newfd); - __fds_lock(); - } - if (!g_fds.p[newfd].kind) { - g_fds.p[newfd].kind = kFdReserved; - break; - } + if (newfd == -1) { + if ((newfd = __reservefd_unlocked(start)) == -1) { + __fds_unlock(); + return -1; + } + } else { + if (__ensurefds_unlocked(newfd) == -1) { + __fds_unlock(); + return -1; + } + if (g_fds.p[newfd].kind) { + sys_close_nt(g_fds.p + newfd, newfd); } } @@ -86,7 +77,7 @@ textwindows int sys_dup_nt(int oldfd, int newfd, int flags, int start) { } rc = newfd; } else { - __releasefd_unlocked(newfd); + __releasefd(newfd); rc = __winerr(); } diff --git a/libc/calls/execve-sysv.c b/libc/calls/execve-sysv.c index 38add6742..cf275a93c 100644 --- a/libc/calls/execve-sysv.c +++ b/libc/calls/execve-sysv.c @@ -16,6 +16,7 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" @@ -40,7 +41,9 @@ static bool IsApeBinary(const char *path) { char buf[8]; bool res = false; if ((fd = sys_open(path, O_RDONLY, 0)) != -1) { - if (sys_read(fd, buf, 8) == 8 && READ64LE(buf) == READ64LE("MZqFpD='")) { + if (sys_read(fd, buf, 8) == 8 && // + (READ64LE(buf) == READ64LE("MZqFpD='") || + READ64LE(buf) == READ64LE("JTqFpD='"))) { res = true; } sys_close(fd); @@ -61,35 +64,41 @@ static const char *Join(const char *a, const char *b, char buf[PATH_MAX]) { } int sys_execve(const char *prog, char *const argv[], char *const envp[]) { - int e; size_t i; + int e, rc; char *buf; char **shargs; const char *ape; e = errno; __sys_execve(prog, argv, envp); - if (errno != ENOEXEC) return -1; - for (i = 0; argv[i];) ++i; - buf = alloca(PATH_MAX); - shargs = alloca((i + 4) * sizeof(char *)); - if (IsApeBinary(prog) && - (CanExecute((ape = "/usr/bin/ape")) || - CanExecute((ape = Join(firstnonnull(getenv("TMPDIR"), - firstnonnull(getenv("HOME"), ".")), - ".ape", buf))) || - CanExecute( - (ape = Join(firstnonnull(getenv("HOME"), "."), ".ape", buf))))) { - shargs[0] = ape; - shargs[1] = "-"; - shargs[2] = prog; - memcpy(shargs + 3, argv, (i + 1) * sizeof(char *)); - } else if (CanExecute(prog)) { - shargs[0] = _PATH_BSHELL; - shargs[1] = prog; - memcpy(shargs + 2, argv + 1, i * sizeof(char *)); + if (errno == ENOEXEC) { + for (i = 0; argv[i];) ++i; + buf = alloca(PATH_MAX); + shargs = alloca((i + 4) * sizeof(char *)); + if (IsApeBinary(prog) && + (CanExecute((ape = "/usr/bin/ape")) || + CanExecute((ape = Join(firstnonnull(getenv("TMPDIR"), + firstnonnull(getenv("HOME"), ".")), + ".ape", buf))) || + CanExecute( + (ape = Join(firstnonnull(getenv("HOME"), "."), ".ape", buf))))) { + shargs[0] = ape; + shargs[1] = "-"; + shargs[2] = prog; + memcpy(shargs + 3, argv, (i + 1) * sizeof(char *)); + errno = e; + rc = __sys_execve(shargs[0], shargs, envp); + } else if (CanExecute(prog)) { + shargs[0] = _PATH_BSHELL; + shargs[1] = prog; + memcpy(shargs + 2, argv + 1, i * sizeof(char *)); + errno = e; + rc = __sys_execve(shargs[0], shargs, envp); + } else { + rc = enoexec(); + } } else { - return enoexec(); + rc = -1; } - errno = e; - return __sys_execve(shargs[0], shargs, envp); + return rc; } diff --git a/libc/calls/execve.c b/libc/calls/execve.c index 4af61ccbd..1e0395da5 100644 --- a/libc/calls/execve.c +++ b/libc/calls/execve.c @@ -54,9 +54,9 @@ int execve(const char *prog, char *const argv[], char *const envp[]) { int rc; size_t i; if (!prog || !argv || !envp || - (IsAsan() && - (!__asan_is_valid(prog, 1) || !__asan_is_valid_strlist(argv) || - !__asan_is_valid_strlist(envp)))) { + (IsAsan() && (!__asan_is_valid(prog, 1) || // + !__asan_is_valid_strlist(argv) || // + !__asan_is_valid_strlist(envp)))) { rc = efault(); } else { STRACE("execve(%#s, %s, %s) → ...", prog, DescribeStringList(argv), diff --git a/libc/calls/fcntl-nt.c b/libc/calls/fcntl-nt.c index 491388499..f5c11ab89 100644 --- a/libc/calls/fcntl-nt.c +++ b/libc/calls/fcntl-nt.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/calls/internal.h" #include "libc/calls/struct/fd.internal.h" @@ -24,63 +25,131 @@ #include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/wincrash.internal.h" #include "libc/errno.h" +#include "libc/intrin/kmalloc.h" #include "libc/intrin/weaken.h" +#include "libc/limits.h" #include "libc/log/backtrace.internal.h" +#include "libc/macros.internal.h" #include "libc/nt/enum/filelockflags.h" #include "libc/nt/errors.h" #include "libc/nt/files.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/byhandlefileinformation.h" #include "libc/nt/struct/overlapped.h" +#include "libc/str/str.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/fd.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/thread.h" -static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) { - if (start < 0) return einval(); - return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start); +struct FileLock { + struct FileLock *next; + int64_t off; + int64_t len; + int fd; + bool exc; +}; + +struct FileLocks { + pthread_mutex_t mu; + struct FileLock *list; + struct FileLock *free; +}; + +static struct FileLocks g_locks; + +static textwindows struct FileLock *NewFileLock(void) { + struct FileLock *fl; + if (g_locks.free) { + fl = g_locks.free; + g_locks.free = fl->next; + } else { + fl = kmalloc(sizeof(*fl)); + } + bzero(fl, sizeof(*fl)); + fl->next = g_locks.list; + g_locks.list = fl; + return fl; } -static textwindows int sys_fcntl_nt_lock(struct Fd *f, int cmd, uintptr_t arg) { +static textwindows void FreeFileLock(struct FileLock *fl) { + fl->next = g_locks.free; + g_locks.free = fl; +} + +static textwindows bool OverlapsFileLock(struct FileLock *fl, int64_t off, + int64_t len) { + uint64_t BegA, EndA, BegB, EndB; + BegA = off; + EndA = off + (len - 1); + BegB = fl->off; + EndB = fl->off + (fl->len - 1); + return MAX(BegA, BegB) < MIN(EndA, EndB); +} + +static textwindows bool EncompassesFileLock(struct FileLock *fl, int64_t off, + int64_t len) { + return off <= fl->off && fl->off + fl->len <= off + len; +} + +static textwindows bool EqualsFileLock(struct FileLock *fl, int64_t off, + int64_t len) { + return fl->off == off && off + len == fl->off + fl->len; +} + +hidden textwindows void sys_fcntl_nt_lock_cleanup(int fd) { + struct FileLock *fl, *ft, **flp; + pthread_mutex_lock(&g_locks.mu); + for (flp = &g_locks.list, fl = *flp; fl;) { + if (fl->fd == fd) { + *flp = fl->next; + ft = fl->next; + FreeFileLock(fl); + fl = ft; + } else { + flp = &fl->next; + fl = *flp; + } + } + pthread_mutex_unlock(&g_locks.mu); +} + +static textwindows int sys_fcntl_nt_lock(struct Fd *f, int fd, int cmd, + uintptr_t arg) { int e; struct flock *l; uint32_t flags, err; - int64_t pos, off, len, size; - struct NtByHandleFileInformation info; - - if (!GetFileInformationByHandle(f->handle, &info)) { - return __winerr(); - } - - pos = 0; - if (!SetFilePointerEx(f->handle, 0, &pos, SEEK_CUR)) { - return __winerr(); - } + struct FileLock *fl, *ft, **flp; + int64_t pos, off, len, end, size; l = (struct flock *)arg; len = l->l_len; off = l->l_start; - size = (uint64_t)info.nFileSizeHigh << 32 | info.nFileSizeLow; switch (l->l_whence) { case SEEK_SET: break; case SEEK_CUR: - off = pos + off; + pos = 0; + if (SetFilePointerEx(f->handle, 0, &pos, SEEK_CUR)) { + off = pos + off; + } else { + return __winerr(); + } break; case SEEK_END: - off = size - off; + off = INT64_MAX - off; break; default: return einval(); } if (!len) { - len = size - off; + len = INT64_MAX - off; } - if (off < 0 || len < 0) { + if (off < 0 || len < 0 || __builtin_add_overflow(off, len, &end)) { return einval(); } @@ -89,8 +158,57 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int cmd, uintptr_t arg) { .Pointer = (void *)(uintptr_t)off}; if (l->l_type == F_RDLCK || l->l_type == F_WRLCK) { + + if (cmd == F_SETLK || cmd == F_SETLKW) { + // make it possible to transition read locks to write locks + for (flp = &g_locks.list, fl = *flp; fl;) { + if (fl->fd == fd) { + if (EqualsFileLock(fl, off, len)) { + if (fl->exc == l->l_type == F_WRLCK) { + // we already have this lock + return 0; + } else { + // unlock our read lock and acquire write lock below + if (UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) { + *flp = fl->next; + ft = fl->next; + FreeFileLock(fl); + fl = ft; + continue; + } else { + return -1; + } + } + break; + } else if (OverlapsFileLock(fl, off, len)) { + return enotsup(); + } + } + flp = &fl->next; + fl = *flp; + } + } + + // return better information on conflicting locks if possible + if (cmd == F_GETLK) { + for (fl = g_locks.list; fl; fl = fl->next) { + if (fl->fd == fd && // + OverlapsFileLock(fl, off, len) && + (l->l_type == F_WRLCK || !fl->exc)) { + l->l_whence = SEEK_SET; + l->l_start = fl->off; + l->l_len = fl->len; + l->l_type == fl->exc ? F_WRLCK : F_RDLCK; + l->l_pid = getpid(); + return 0; + } + } + } + flags = 0; if (cmd != F_SETLKW) { + // TODO(jart): we should use expo backoff in wrapper function + // should not matter since sqlite doesn't need it flags |= kNtLockfileFailImmediately; } if (l->l_type == F_WRLCK) { @@ -101,68 +219,133 @@ static textwindows int sys_fcntl_nt_lock(struct Fd *f, int cmd, uintptr_t arg) { if (ok) { l->l_type = F_UNLCK; if (!UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) { - notpossible; + return -1; } } else { l->l_pid = -1; ok = true; } + } else if (ok) { + fl = NewFileLock(); + fl->off = off; + fl->len = len; + fl->exc = l->l_type == F_WRLCK; + fl->fd = fd; } return ok ? 0 : -1; } if (l->l_type == F_UNLCK) { if (cmd == F_GETLK) return einval(); - e = errno; - if (UnlockFileEx(f->handle, 0, len, len >> 32, &ov)) { - return 0; - } else if (errno == ENOLCK) { - errno = e; - return 0; - } else { - return 0; + + // allow a big range to unlock many small ranges + for (flp = &g_locks.list, fl = *flp; fl;) { + if (fl->fd == fd && EncompassesFileLock(fl, off, len)) { + struct NtOverlapped ov = {.hEvent = f->handle, + .Pointer = (void *)(uintptr_t)fl->off}; + if (UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) { + *flp = fl->next; + ft = fl->next; + FreeFileLock(fl); + fl = ft; + } else { + return -1; + } + } else { + flp = &fl->next; + fl = *flp; + } } + + // win32 won't let us carve up existing locks + int overlap_count = 0; + for (fl = g_locks.list; fl; fl = fl->next) { + if (fl->fd == fd && // + OverlapsFileLock(fl, off, len)) { + ++overlap_count; + } + } + + // try to handle the carving cases needed by sqlite + if (overlap_count == 1) { + for (fl = g_locks.list; fl; fl = fl->next) { + if (fl->fd == fd && // + off <= fl->off && // + off + len >= fl->off && // + off + len < fl->off + fl->len) { + // cleave left side of lock + struct NtOverlapped ov = {.hEvent = f->handle, + .Pointer = (void *)(uintptr_t)fl->off}; + if (!UnlockFileEx(f->handle, 0, fl->len, fl->len >> 32, &ov)) { + return -1; + } + fl->len = (fl->off + fl->len) - (off + len); + fl->off = off + len; + ov.Pointer = (void *)(uintptr_t)fl->off; + if (!LockFileEx(f->handle, kNtLockfileExclusiveLock, 0, fl->len, + fl->len >> 32, &ov)) { + return -1; + } + return 0; + } + } + } + + if (overlap_count) { + return enotsup(); + } + + return 0; } return einval(); } +static textwindows int sys_fcntl_nt_dupfd(int fd, int cmd, int start) { + if (start < 0) return einval(); + return sys_dup_nt(fd, -1, (cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0), start); +} + textwindows int sys_fcntl_nt(int fd, int cmd, uintptr_t arg) { + int rc; uint32_t flags; if (__isfdkind(fd, kFdFile) || __isfdkind(fd, kFdSocket)) { if (cmd == F_GETFL) { - return g_fds.p[fd].flags & - (O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | - O_NONBLOCK | O_RANDOM | O_SEQUENTIAL); + rc = g_fds.p[fd].flags & + (O_ACCMODE | O_APPEND | O_ASYNC | O_DIRECT | O_NOATIME | O_NONBLOCK | + O_RANDOM | O_SEQUENTIAL); } else if (cmd == F_SETFL) { // O_APPEND doesn't appear to be tunable at cursory glance // O_NONBLOCK might require we start doing all i/o in threads // O_DSYNC / O_RSYNC / O_SYNC maybe if we fsync() everything // O_DIRECT | O_RANDOM | O_SEQUENTIAL | O_NDELAY possible but // not worth it. - return einval(); + rc = einval(); } else if (cmd == F_GETFD) { if (g_fds.p[fd].flags & O_CLOEXEC) { - return FD_CLOEXEC; + rc = FD_CLOEXEC; } else { - return 0; + rc = 0; } } else if (cmd == F_SETFD) { if (arg & FD_CLOEXEC) { g_fds.p[fd].flags |= O_CLOEXEC; - return FD_CLOEXEC; + rc = FD_CLOEXEC; } else { g_fds.p[fd].flags &= ~O_CLOEXEC; - return 0; + rc = 0; } } else if (cmd == F_SETLK || cmd == F_SETLKW || cmd == F_GETLK) { - return sys_fcntl_nt_lock(g_fds.p + fd, cmd, arg); + pthread_mutex_lock(&g_locks.mu); + rc = sys_fcntl_nt_lock(g_fds.p + fd, fd, cmd, arg); + pthread_mutex_unlock(&g_locks.mu); } else if (cmd == F_DUPFD || cmd == F_DUPFD_CLOEXEC) { - return sys_fcntl_nt_dupfd(fd, cmd, arg); + rc = sys_fcntl_nt_dupfd(fd, cmd, arg); } else { - return einval(); + rc = einval(); } } else { - return ebadf(); + rc = ebadf(); } + return rc; } diff --git a/libc/calls/fcntl.c b/libc/calls/fcntl.c index be68a3f9c..06369e9ba 100644 --- a/libc/calls/fcntl.c +++ b/libc/calls/fcntl.c @@ -31,7 +31,7 @@ #include "libc/zipos/zipos.internal.h" /** - * Does things with file descriptor, via re-imagined hourglass api, e.g. + * Does things with file descriptor, e.g. * * CHECK_NE(-1, fcntl(fd, F_SETFD, FD_CLOEXEC)); * @@ -41,12 +41,14 @@ * CHECK_GE((newfd = fcntl(oldfd, F_DUPFD, 3)), 3); * CHECK_GE((newfd = fcntl(oldfd, F_DUPFD_CLOEXEC, 3)), 3); * - * This function implements POSIX Advisory Locks, which let independent - * processes (and on Windows, threads too!) read/write lock byte ranges - * of files. See `test/libc/calls/lock_test.c` for an example. + * This function implements file record locking, which lets independent + * processes (and on Linux 3.15+, threads too!) lock arbitrary ranges + * associated with a file. See `test/libc/calls/lock_test.c` and other + * locking related tests in that folder. * - * Please be warned that locks currently do nothing on Windows since - * figuring out how to polyfill them correctly is a work in progress. + * On Windows, the Cosmopolitan Libc polyfill for POSIX advisory locks + * only implements enough of its nuances to support SQLite's needs. Some + * possibilities, e.g. punching holes in lock, will raise `ENOTSUP`. * * @param fd is the file descriptor * @param cmd can be one of: @@ -56,12 +58,12 @@ * - `F_SETFL` sets file descriptor status flags * - `F_DUPFD` is like dup() but `arg` is a minimum result, e.g. 3 * - `F_DUPFD_CLOEXEC` ditto but sets `O_CLOEXEC` on returned fd - * - `F_SETLK` for record locking where `arg` is `struct flock` - * - `F_SETLKW` ditto but waits (i.e. blocks) for lock + * - `F_SETLK` for record locking where `arg` is `struct flock *` + * - `F_SETLKW` ditto but waits for lock (SQLite avoids this) * - `F_GETLK` to retrieve information about a record lock - * - `F_OFD_SETLK` for better locks on Linux and XNU - * - `F_OFD_SETLKW` for better locks on Linux and XNU - * - `F_OFD_GETLK` for better locks on Linux and XNU + * - `F_OFD_SETLK` for better non-blocking lock (Linux 3.15+ only) + * - `F_OFD_SETLKW` for better blocking lock (Linux 3.15+ only) + * - `F_OFD_GETLK` for better lock querying (Linux 3.15+ only) * - `F_FULLFSYNC` on MacOS for fsync() with release barrier * - `F_BARRIERFSYNC` on MacOS for fsync() with even more barriers * - `F_SETNOSIGPIPE` on MacOS and NetBSD to control `SIGPIPE` @@ -71,7 +73,7 @@ * - `F_NOCACHE` on MacOS to toggle data caching * - `F_GETPIPE_SZ` on Linux to get pipe size * - `F_SETPIPE_SZ` on Linux to set pipe size - * - `F_NOTIFY` raise `SIGIO` upon `fd` events in `arg` on Linux + * - `F_NOTIFY` raise `SIGIO` upon `fd` events in `arg` (Linux only) * - `DN_ACCESS` for file access * - `DN_MODIFY` for file modifications * - `DN_CREATE` for file creations @@ -88,6 +90,7 @@ * @raise ENOLCK if `F_SETLKW` would have exceeded `RLIMIT_LOCKS` * @raise EPERM if `cmd` is `F_SETOWN` and we weren't authorized * @raise ESRCH if `cmd` is `F_SETOWN` and process group not found + * @raise ENOTSUP on Windows if locking operation isn't supported yet * @raise EDEADLK if `cmd` was `F_SETLKW` and waiting would deadlock * @raise EMFILE if `cmd` is `F_DUPFD` or `F_DUPFD_CLOEXEC` and * `RLIMIT_NOFILE` would be exceeded diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 498b94e5d..68123098d 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -18,7 +18,6 @@ hidden extern const struct Fd kEmptyFd; int __reservefd(int) hidden; int __reservefd_unlocked(int) hidden; void __releasefd(int) hidden; -void __releasefd_unlocked(int) hidden; int __ensurefds(int) hidden; int __ensurefds_unlocked(int) hidden; void __printfds(void) hidden; @@ -43,7 +42,7 @@ forceinline size_t _clampio(size_t size) { } } -int sys_close_nt(struct Fd *) hidden; +int sys_close_nt(struct Fd *, int) hidden; bool _check_interrupts(bool, struct Fd *) hidden; int sys_openat_metal(int, const char *, int, unsigned); diff --git a/libc/calls/kill.c b/libc/calls/kill.c index fd8f459de..03ecfe4fb 100644 --- a/libc/calls/kill.c +++ b/libc/calls/kill.c @@ -17,10 +17,10 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" -#include "libc/intrin/strace.internal.h" #include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" +#include "libc/intrin/strace.internal.h" #include "libc/str/str.h" /** @@ -38,6 +38,10 @@ * >0 can be SIGINT, SIGTERM, SIGKILL, SIGUSR1, etc. * =0 checks both if pid exists and we can signal it * @return 0 if something was accomplished, or -1 w/ errno + * @raise ESRCH if `pid` couldn't be found + * @raise EPERM if lacked permission to signal process + * @raise EPERM if pledge() is in play without `proc` promised + * @raise EINVAL if the provided `sig` is invalid or unsupported * @asyncsignalsafe */ int kill(int pid, int sig) { diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index d1242f63c..bcc7c032d 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -88,7 +88,7 @@ textwindows int sys_open_nt(int dirfd, const char *file, uint32_t flags, rc = sys_open_nt_file(dirfd, file, flags, mode, fd); } if (rc == -1) { - __releasefd_unlocked(fd); + __releasefd(fd); } __fds_unlock(); } diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index 5bede1c12..1affe6793 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -41,7 +41,7 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { return -1; } if ((writer = __reservefd_unlocked(-1)) == -1) { - __releasefd_unlocked(reader); + __releasefd(reader); __fds_unlock(); return -1; } @@ -73,8 +73,8 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { CloseHandle(hin); } } - __releasefd_unlocked(writer); - __releasefd_unlocked(reader); + __releasefd(writer); + __releasefd(reader); __fds_unlock(); return -1; } diff --git a/libc/calls/pledge-linux.c b/libc/calls/pledge-linux.c index 0e49c94fa..7e29bd67a 100644 --- a/libc/calls/pledge-linux.c +++ b/libc/calls/pledge-linux.c @@ -1606,19 +1606,26 @@ static privileged void AllowFcntlStdio(struct Filter *f) { // The second argument of fcntl() must be one of: // -// - F_GETLK (5) -// - F_SETLK (6) -// - F_SETLKW (7) +// - F_GETLK (0x05) +// - F_SETLK (0x06) +// - F_SETLKW (0x07) +// - F_OFD_GETLK (0x24) +// - F_OFD_SETLK (0x25) +// - F_OFD_SETLKW (0x26) // static privileged void AllowFcntlLock(struct Filter *f) { static const struct sock_filter fragment[] = { - /*L0*/ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_fcntl, 0, 6 - 1), - /*L1*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), - /*L2*/ BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 5, 0, 5 - 3), - /*L3*/ BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, 8, 5 - 4, 0), - /*L4*/ BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), - /*L5*/ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), - /*L6*/ /* next filter */ + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, __NR_linux_fcntl, 0, 9), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(args[1])), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x05, 5, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x06, 4, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x07, 3, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x24, 2, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x25, 1, 0), + BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x26, 0, 1), + BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW), + BPF_STMT(BPF_LD | BPF_W | BPF_ABS, OFF(nr)), + /* next filter */ }; AppendFilter(f, PLEDGE(fragment)); } diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index f005312b9..0657395f1 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -16,12 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/iovec.internal.h" -#include "libc/calls/struct/sigset.h" -#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" @@ -31,16 +28,13 @@ #include "libc/intrin/likely.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" -#include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) { int e, i; size_t got; - bool masked; ssize_t rc, toto; - sigset_t mask, oldmask; if (fd < 0) { return ebadf(); @@ -94,26 +88,17 @@ static ssize_t Preadv(int fd, struct iovec *iov, int iovlen, int64_t off) { if (rc == -1) { if (!toto) { toto = -1; + } else if (errno != EINTR) { + notpossible; } break; } - got = rc; toto += got; off += got; if (got != iov[i].iov_len) { break; } - - if (!masked) { - sigfillset(&mask); - _npassert(!sys_sigprocmask(SIG_SETMASK, &mask, &oldmask)); - masked = true; - } - } - - if (masked) { - _npassert(!sys_sigprocmask(SIG_SETMASK, &oldmask, 0)); } return toto; diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index f06417b6a..3197ca6c9 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -16,12 +16,9 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/iovec.internal.h" -#include "libc/calls/struct/sigset.h" -#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/dce.h" @@ -31,17 +28,14 @@ #include "libc/intrin/likely.h" #include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" -#include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/zipos/zipos.internal.h" static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen, int64_t off) { int i, e; - bool masked; size_t sent; ssize_t rc, toto; - sigset_t mask, oldmask; if (fd < 0) { return ebadf(); @@ -95,26 +89,17 @@ static ssize_t Pwritev(int fd, const struct iovec *iov, int iovlen, if (rc == -1) { if (!toto) { toto = -1; + } else if (errno != EINTR) { + notpossible; } break; } - sent = rc; toto += sent; off += sent; if (sent != iov[i].iov_len) { break; } - - if (!masked) { - sigfillset(&mask); - _npassert(!sys_sigprocmask(SIG_SETMASK, &mask, &oldmask)); - masked = true; - } - } - - if (masked) { - _npassert(!sys_sigprocmask(SIG_SETMASK, &oldmask, 0)); } return toto; diff --git a/libc/intrin/releasefd.c b/libc/calls/releasefd.c similarity index 81% rename from libc/intrin/releasefd.c rename to libc/calls/releasefd.c index 505fd9eb5..2efcf13c7 100644 --- a/libc/intrin/releasefd.c +++ b/libc/calls/releasefd.c @@ -17,11 +17,18 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/internal.h" -#include "libc/calls/state.internal.h" +#include "libc/intrin/atomic.h" #include "libc/macros.internal.h" +#include "libc/str/str.h" +// really want to avoid locking here so close() needn't block signals void __releasefd(int fd) { - __fds_lock(); - __releasefd_unlocked(fd); - __fds_unlock(); + int f1, f2; + if (!(0 <= fd && fd < g_fds.n)) return; + bzero(g_fds.p + fd, sizeof(*g_fds.p)); + f1 = atomic_load_explicit(&g_fds.f, memory_order_relaxed); + do { + f2 = MIN(fd, f1); + } while (!atomic_compare_exchange_weak_explicit( + &g_fds.f, &f1, f2, memory_order_release, memory_order_relaxed)); } diff --git a/libc/calls/reservefd.c b/libc/calls/reservefd.c index 28df3135e..4a13960d2 100644 --- a/libc/calls/reservefd.c +++ b/libc/calls/reservefd.c @@ -22,6 +22,7 @@ #include "libc/calls/state.internal.h" #include "libc/calls/struct/sigset.h" #include "libc/dce.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/cmpxchg.h" #include "libc/intrin/extend.internal.h" #include "libc/intrin/strace.internal.h" @@ -36,6 +37,8 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" +// TODO(jart): make more of this code lockless + static volatile size_t mapsize; /** @@ -71,9 +74,10 @@ int __ensurefds(int fd) { * @asyncsignalsafe */ int __reservefd_unlocked(int start) { - int fd; + int fd, f1, f2; for (;;) { - for (fd = MAX(start, g_fds.f); fd < g_fds.n; ++fd) { + f1 = atomic_load_explicit(&g_fds.f, memory_order_acquire); + for (fd = MAX(start, f1); fd < g_fds.n; ++fd) { if (!g_fds.p[fd].kind) { break; } @@ -81,7 +85,11 @@ int __reservefd_unlocked(int start) { fd = __ensurefds_unlocked(fd); bzero(g_fds.p + fd, sizeof(*g_fds.p)); if (_cmpxchg(&g_fds.p[fd].kind, kFdEmpty, kFdReserved)) { - _cmpxchg(&g_fds.f, fd, fd + 1); + // g_fds.f isn't guarded by our mutex + do { + f2 = MAX(fd + 1, f1); + } while (!atomic_compare_exchange_weak_explicit( + &g_fds.f, &f1, f2, memory_order_release, memory_order_relaxed)); return fd; } } diff --git a/libc/calls/sigaction.c b/libc/calls/sigaction.c index 4aab5d495..bea1a434b 100644 --- a/libc/calls/sigaction.c +++ b/libc/calls/sigaction.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" @@ -29,6 +30,7 @@ #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/sigaction.internal.h" #include "libc/calls/struct/siginfo.internal.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/syscall_support-sysv.internal.h" #include "libc/calls/ucontext.h" @@ -474,9 +476,11 @@ int sigaction(int sig, const struct sigaction *act, struct sigaction *oldact) { if (sig == SIGKILL || sig == SIGSTOP) { rc = einval(); } else { + BLOCK_SIGNALS; __sig_lock(); rc = __sigaction(sig, act, oldact); __sig_unlock(); + ALLOW_SIGNALS; } STRACE("sigaction(%G, %s, [%s]) → %d% m", sig, DescribeSigaction(0, act), DescribeSigaction(rc, oldact), rc); diff --git a/libc/intrin/releasefd_unlocked.c b/libc/calls/sigblockall.c similarity index 87% rename from libc/intrin/releasefd_unlocked.c rename to libc/calls/sigblockall.c index 08d9d8a08..9ca00277c 100644 --- a/libc/intrin/releasefd_unlocked.c +++ b/libc/calls/sigblockall.c @@ -16,13 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/internal.h" -#include "libc/macros.internal.h" -#include "libc/str/str.h" +#include "libc/calls/struct/sigset.h" -void __releasefd_unlocked(int fd) { - if (0 <= fd && fd < g_fds.n) { - bzero(g_fds.p + fd, sizeof(*g_fds.p)); - g_fds.f = MIN(fd, g_fds.f); - } +/** + * Blocks all signals without strace logging. + * + * @param neu is new signal mask for process + * @return old signal mask + */ +sigset_t _sigblockall(void) { + sigset_t ss; + sigfillset(&ss); + return _sigsetmask(ss); } diff --git a/libc/calls/sigenter-openbsd.c b/libc/calls/sigenter-openbsd.c index 74a9f3e5d..f9b277ee8 100644 --- a/libc/calls/sigenter-openbsd.c +++ b/libc/calls/sigenter-openbsd.c @@ -49,8 +49,8 @@ privileged void __sigenter_openbsd(int sig, struct siginfo_openbsd *openbsdinfo, __repstosb(&g.uc, 0, sizeof(g.uc)); __siginfo2cosmo(&g.si, (void *)openbsdinfo); g.uc.uc_mcontext.fpregs = &g.uc.__fpustate; - __repmovsb(&g.uc.uc_sigmask, &ctx->sc_mask, - MIN(sizeof(g.uc.uc_sigmask), sizeof(ctx->sc_mask))); + g.uc.uc_sigmask.__bits[0] = ctx->sc_mask; + g.uc.uc_sigmask.__bits[1] = 0; g.uc.uc_mcontext.rdi = ctx->sc_rdi; g.uc.uc_mcontext.rsi = ctx->sc_rsi; g.uc.uc_mcontext.rdx = ctx->sc_rdx; diff --git a/libc/calls/sigenter-xnu.c b/libc/calls/sigenter-xnu.c index 14c2d7f2d..89e9381e8 100644 --- a/libc/calls/sigenter-xnu.c +++ b/libc/calls/sigenter-xnu.c @@ -473,6 +473,7 @@ privileged void __sigenter_xnu(void *fn, int infostyle, int sig, if (xnuctx) { g.uc.uc_flags = xnuctx->uc_onstack ? SA_ONSTACK : 0; g.uc.uc_sigmask.__bits[0] = xnuctx->uc_sigmask; + g.uc.uc_sigmask.__bits[1] = 0; g.uc.uc_stack.ss_sp = xnuctx->uc_stack.ss_sp; g.uc.uc_stack.ss_flags = xnuctx->uc_stack.ss_flags; g.uc.uc_stack.ss_size = xnuctx->uc_stack.ss_size; diff --git a/libc/calls/sigprocmask-sysv.greg.c b/libc/calls/sigprocmask-sysv.c similarity index 100% rename from libc/calls/sigprocmask-sysv.greg.c rename to libc/calls/sigprocmask-sysv.c diff --git a/libc/calls/sigprocmask.c b/libc/calls/sigprocmask.c index c59a64ce9..caab7e275 100644 --- a/libc/calls/sigprocmask.c +++ b/libc/calls/sigprocmask.c @@ -26,7 +26,6 @@ #include "libc/intrin/asan.internal.h" #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" -#include "libc/log/log.h" #include "libc/str/str.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" diff --git a/libc/calls/cfsetospeed.c b/libc/calls/sigsetmask.c similarity index 75% rename from libc/calls/cfsetospeed.c rename to libc/calls/sigsetmask.c index a6ea81adc..5cebef6f5 100644 --- a/libc/calls/cfsetospeed.c +++ b/libc/calls/sigsetmask.c @@ -1,7 +1,7 @@ /*-*- 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 │ +│ 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 │ @@ -16,21 +16,25 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/termios.h" -#include "libc/sysv/consts/termios.h" -#include "libc/sysv/errfuns.h" +#include "libc/assert.h" +#include "libc/calls/calls.h" +#include "libc/calls/sig.internal.h" +#include "libc/calls/struct/sigset.internal.h" +#include "libc/dce.h" +#include "libc/sysv/consts/sig.h" -int cfsetospeed(struct termios *t, unsigned speed) { - if (CBAUD) { - if (!(speed & ~CBAUD)) { - t->c_cflag &= ~CBAUD; - t->c_cflag |= speed; - return 0; - } else { - return einval(); - } +/** + * Sets signal mask without strace logging. + * + * @param neu is new signal mask for process + * @return old signal mask + */ +sigset_t _sigsetmask(sigset_t neu) { + sigset_t res; + if (IsMetal() || IsWindows()) { + __sig_mask(SIG_SETMASK, &neu, &res); } else { - t->c_ospeed = speed; - return 0; + _npassert(!sys_sigprocmask(SIG_SETMASK, &neu, &res)); } + return res; } diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index fe117b253..a607d7402 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -25,7 +25,7 @@ struct Fd { }; struct Fds { - int f; /* lowest free slot */ + _Atomic(int) f; /* lowest free slot */ size_t n; struct Fd *p, *e; }; diff --git a/libc/calls/struct/flock.h b/libc/calls/struct/flock.h index eb671cf15..ac897f6db 100644 --- a/libc/calls/struct/flock.h +++ b/libc/calls/struct/flock.h @@ -9,7 +9,7 @@ struct flock { /* cosmopolitan abi */ int64_t l_start; /* starting offset */ int64_t l_len; /* no. bytes (0 means to end of file) */ int32_t l_pid; /* lock owner */ - int32_t l_sysid; /* remote system id or zero for local */ + int32_t l_sysid; /* remote system id or zero for local (freebsd) */ }; COSMOPOLITAN_C_END_ diff --git a/libc/calls/struct/sigset.h b/libc/calls/struct/sigset.h index 97d36bda6..afcace1b2 100644 --- a/libc/calls/struct/sigset.h +++ b/libc/calls/struct/sigset.h @@ -21,6 +21,8 @@ int sigprocmask(int, const sigset_t *, sigset_t *); int sigsuspend(const sigset_t *); int sigpending(sigset_t *); int pthread_sigmask(int, const sigset_t *, sigset_t *); +sigset_t _sigsetmask(sigset_t); +sigset_t _sigblockall(void); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/syscall-sysv.internal.h b/libc/calls/syscall-sysv.internal.h index 7264cd0f0..cd7f36ee4 100644 --- a/libc/calls/syscall-sysv.internal.h +++ b/libc/calls/syscall-sysv.internal.h @@ -10,8 +10,8 @@ COSMOPOLITAN_C_START_ │ cosmopolitan § syscalls » system five » structless synthetic jump slots ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ +axdx_t __sys_fork(void) hidden; axdx_t __sys_pipe(i32[hasatleast 2], i32) hidden; -axdx_t sys_fork(void) hidden; axdx_t sys_getpid(void) hidden; char *sys_getcwd(char *, u64) hidden; char *sys_getcwd_xnu(char *, u64) hidden; @@ -46,6 +46,7 @@ i32 sys_fchownat(i32, const char *, u32, u32, u32) hidden; i32 sys_fcntl(i32, i32, u64) hidden; i32 sys_fdatasync(i32) hidden; i32 sys_flock(i32, i32) hidden; +i32 sys_fork(void) hidden; i32 sys_fsync(i32) hidden; i32 sys_ftruncate(i32, i64, i64) hidden; i32 sys_getcontext(void *) hidden; diff --git a/libc/calls/tcdrain.c b/libc/calls/tcdrain.c index c7ca74c3a..5c6d2a6e3 100644 --- a/libc/calls/tcdrain.c +++ b/libc/calls/tcdrain.c @@ -16,8 +16,41 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/termios.h" +#include "libc/dce.h" +#include "libc/intrin/strace.internal.h" +#include "libc/nt/files.h" +#include "libc/sysv/errfuns.h" -int tcdrain(int fd) { - return ioctl(fd, TCSBRK, (void *)(intptr_t)1); +static textwindows int sys_tcdrain_nt(int fd) { + if (!__isfdopen(fd)) return ebadf(); + if (!FlushFileBuffers(g_fds.p[fd].handle)) return __winerr(); + return 0; +} + +/** + * Waits until all written output is transmitted. + * + * @param fd is file descriptor of tty + * @raise EBADF if `fd` isn't an open file descriptor + * @raise ENOTTY if `fd` is open but not a teletypewriter + * @raise EIO if process group of writer is orphoned, calling thread is + * not blocking `SIGTTOU`, and process isn't ignoring `SIGTTOU` + * @raise ENOSYS on bare metal + * @asyncsignalsafe + */ +int tcdrain(int fd) { + int rc; + if (IsMetal()) { + rc = enosys(); + } else if (!IsWindows()) { + rc = sys_ioctl(fd, TCSBRK, (void *)(intptr_t)1); + } else { + rc = sys_tcdrain_nt(fd); + } + STRACE("tcdrain(%d) → %d% m", fd, rc); + return rc; } diff --git a/libc/calls/tcflow.c b/libc/calls/tcflow.c index ec1152d90..8015c7abe 100644 --- a/libc/calls/tcflow.c +++ b/libc/calls/tcflow.c @@ -16,31 +16,91 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" #include "libc/calls/struct/termios.h" #include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/termios.h" #include "libc/dce.h" +#include "libc/fmt/itoa.h" +#include "libc/intrin/strace.internal.h" +#include "libc/mem/alloca.h" +#include "libc/nt/comms.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" +#define kNtPurgeTxabort 1 +#define kNtPurgeRxabort 2 + +static const char *DescribeFlow(char buf[12], int action) { + if (action == TCOOFF) return "TCOOFF"; + if (action == TCOON) return "TCOON"; + if (action == TCIOFF) return "TCIOFF"; + if (action == TCION) return "TCION"; + FormatInt32(buf, action); + return buf; +} + +static int sys_tcflow_bsd(int fd, int action) { + int rc; + uint8_t c; + struct termios t; + if (action == TCOOFF) return sys_ioctl(fd, TIOCSTOP, 0); + if (action == TCOON) return sys_ioctl(fd, TIOCSTART, 0); + if (action != TCIOFF && action != TCION) return einval(); + if (sys_ioctl(fd, TCGETS, &t) == -1) return -1; + c = t.c_cc[action == TCIOFF ? VSTOP : VSTART]; + if (c == 255) return 0; // code is disabled + if (sys_write(fd, &c, 1) == -1) return -1; + return 0; +} + +static dontinline textwindows int sys_tcflow_nt(int fd, int action) { + bool32 ok; + int64_t h; + if (!__isfdopen(fd)) return ebadf(); + h = g_fds.p[fd].handle; + if (action == TCOOFF) { + ok = PurgeComm(h, kNtPurgeTxabort); + } else if (action == TCIOFF) { + ok = PurgeComm(h, kNtPurgeRxabort); + } else if (action == TCOON || action == TCION) { + ok = ClearCommBreak(h); + } else { + return einval(); + } + return ok ? 0 : __winerr(); +} + /** * Changes flow of teletypewriter data. * - * - `TCOOFF` suspends output - * - `TCOON` resumes output - * - `TCIOFF` transmits a STOP character - * - `TCION` transmits a START character + * @param fd is file descriptor of tty + * @param action may be one of: + * - `TCOOFF` to suspend output + * - `TCOON` to resume output + * - `TCIOFF` to transmit a `STOP` character + * - `TCION` to transmit a `START` character + * @return 0 on success, or -1 w/ errno + * @raise EINVAL if `action` is invalid + * @raise ENOSYS on Windows and Bare Metal + * @raise EBADF if `fd` isn't an open file descriptor + * @raise ENOTTY if `fd` is open but not a teletypewriter + * @raise EIO if process group of writer is orphoned, calling thread is + * not blocking `SIGTTOU`, and process isn't ignoring `SIGTTOU` + * @asyncsignalsafe */ int tcflow(int fd, int action) { - uint8_t c; - struct termios t; - if (!IsBsd()) return sys_ioctl(fd, TCXONC, action); - if (action == TCOOFF) return sys_ioctl(fd, TIOCSTOP, 0); - if (action == TCOON) return sys_ioctl(fd, TIOCSTART, 0); - if (action != TCIOFF && action != TCION) return einval(); - if (tcgetattr(fd, &t) == -1) return -1; - if ((c = t.c_cc[action == TCIOFF ? VSTOP : VSTART]) != 255) { - if (sys_write(fd, &c, 1) == -1) return -1; + int rc; + if (IsMetal()) { + rc = enosys(); + } else if (IsBsd()) { + rc = sys_ioctl(fd, TCXONC, action); + } else if (!IsWindows()) { + rc = sys_ioctl(fd, TCXONC, action); + } else { + rc = sys_tcflow_nt(fd, action); } - return 0; + STRACE("tcflow(%d, %s) → %d% m", fd, DescribeFlow(alloca(12), action), rc); + return rc; } diff --git a/libc/calls/tcflush.c b/libc/calls/tcflush.c index 6229b6c68..d23033985 100644 --- a/libc/calls/tcflush.c +++ b/libc/calls/tcflush.c @@ -16,17 +16,70 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/syscall-nt.internal.h" #include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/termios.h" +#include "libc/dce.h" +#include "libc/fmt/itoa.h" +#include "libc/intrin/strace.internal.h" +#include "libc/mem/alloca.h" +#include "libc/nt/comms.h" +#include "libc/sysv/consts/termios.h" +#include "libc/sysv/errfuns.h" + +#define kNtPurgeTxclear 4 +#define kNtPurgeRxclear 8 + +static const char *DescribeFlush(char buf[12], int action) { + if (action == TCIFLUSH) return "TCIFLUSH"; + if (action == TCOFLUSH) return "TCOFLUSH"; + if (action == TCIOFLUSH) return "TCIOFLUSH"; + FormatInt32(buf, action); + return buf; +} + +static dontinline textwindows int sys_tcflush_nt(int fd, int queue) { + bool32 ok; + int64_t h; + if (!__isfdopen(fd)) return ebadf(); + ok = true; + h = g_fds.p[fd].handle; + if (queue == TCIFLUSH || queue == TCIOFLUSH) { + ok &= !!PurgeComm(h, kNtPurgeRxclear); + } + if (queue == TCOFLUSH || queue == TCIOFLUSH) { + ok &= !!PurgeComm(h, kNtPurgeTxclear); + } + return ok ? 0 : __winerr(); +} /** - * Flushes teletypewriter data. + * Discards queued data on teletypewriter. * - * - `TCIFLUSH` flushes data received but not read - * - `TCOFLUSH` flushes data written but not transmitted - * - `TCIOFLUSH` does both `TCOFLUSH` and `TCIFLUSH` + * @param queue may be one of: + * - `TCIFLUSH` flushes data received but not read + * - `TCOFLUSH` flushes data written but not transmitted + * - `TCIOFLUSH` does both `TCOFLUSH` and `TCIFLUSH` + * @return 0 on success, or -1 w/ errno + * @raise EINVAL if `action` is invalid + * @raise EBADF if `fd` isn't an open file descriptor + * @raise ENOTTY if `fd` is open but not a teletypewriter + * @raise EIO if process group of writer is orphoned, calling thread is + * not blocking `SIGTTOU`, and process isn't ignoring `SIGTTOU` + * @raise ENOSYS on bare metal + * @asyncsignalsafe */ int tcflush(int fd, int queue) { - /* TODO(jart): Windows? */ - return sys_ioctl(fd, TCFLSH, queue); + int rc; + if (IsMetal()) { + rc = enosys(); + } else if (!IsWindows()) { + rc = sys_ioctl(fd, TCFLSH, queue); + } else { + rc = sys_tcflush_nt(fd, queue); + } + STRACE("tcflush(%d, %s) → %d% m", fd, DescribeFlush(alloca(12), queue), rc); + return rc; } diff --git a/libc/calls/tcgetpgrp.c b/libc/calls/tcgetpgrp.c index 292105e28..3ccf7e446 100644 --- a/libc/calls/tcgetpgrp.c +++ b/libc/calls/tcgetpgrp.c @@ -16,16 +16,29 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" +#include "libc/dce.h" +#include "libc/intrin/strace.internal.h" #include "libc/sysv/consts/termios.h" +#include "libc/sysv/errfuns.h" /** * Returns which process group controls terminal. + * + * @return process group id on success, or -1 w/ errno + * @raise ENOTTY if `fd` is isn't controlling teletypewriter + * @raise EBADF if `fd` isn't an open file descriptor + * @raise ENOSYS on Windows and Bare Metal * @asyncsignalsafe */ -int32_t tcgetpgrp(int fd) { - int pgrp; - if (ioctl(fd, TIOCGPGRP, &pgrp) < 0) return -1; - return pgrp; +int tcgetpgrp(int fd) { + int rc, pgrp; + if (IsWindows() || IsMetal()) { + rc = enosys(); + } else { + rc = sys_ioctl(fd, TIOCGPGRP, &pgrp); + } + STRACE("tcgetpgrp(%d) → %d% m", fd, rc == -1 ? rc : pgrp); + return rc == -1 ? rc : pgrp; } diff --git a/libc/calls/tcsendbreak.c b/libc/calls/tcsendbreak.c index 60fbf39d6..f4c4000c0 100644 --- a/libc/calls/tcsendbreak.c +++ b/libc/calls/tcsendbreak.c @@ -17,18 +17,53 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/termios.h" #include "libc/dce.h" +#include "libc/intrin/strace.internal.h" +#include "libc/nt/comms.h" #include "libc/sysv/consts/termios.h" +#include "libc/sysv/errfuns.h" -int tcsendbreak(int fd, int len) { - if (!IsBsd()) { - return sys_ioctl(fd, TCSBRK, 0); - } else { - if (sys_ioctl(fd, TIOCSBRK, 0) == -1) return -1; - usleep(400000); - if (sys_ioctl(fd, TIOCCBRK, 0) == -1) return -1; - return 0; - } +static int sys_tcsendbreak_bsd(int fd) { + if (sys_ioctl(fd, TIOCSBRK, 0) == -1) return -1; + usleep(400000); + if (sys_ioctl(fd, TIOCCBRK, 0) == -1) return -1; + return 0; +} + +static textwindows int sys_tcsendbreak_nt(int fd) { + if (!__isfdopen(fd)) return ebadf(); + if (!TransmitCommChar(g_fds.p[fd].handle, '\0')) return __winerr(); + return 0; +} + +/** + * Sends break. + * + * @param fd is file descriptor of tty + * @param duration of 0 sends a break for 0.25-0.5 seconds, and other + * durations are treated the same by this implementation + * @raise EBADF if `fd` isn't an open file descriptor + * @raise ENOTTY if `fd` is open but not a teletypewriter + * @raise EIO if process group of writer is orphoned, calling thread is + * not blocking `SIGTTOU`, and process isn't ignoring `SIGTTOU` + * @raise ENOSYS on bare metal + * @asyncsignalsafe + */ +int tcsendbreak(int fd, int duration) { + int rc; + if (IsMetal()) { + rc = enosys(); + } else if (IsBsd()) { + rc = sys_tcsendbreak_bsd(fd); + } else if (!IsWindows()) { + rc = sys_ioctl(fd, TCSBRK, 0); + } else { + rc = sys_tcsendbreak_nt(fd); + } + STRACE("tcsendbreak(%d, %u) → %d% m", fd, duration, rc); + return rc; } diff --git a/libc/calls/tcsetpgrp.c b/libc/calls/tcsetpgrp.c index db4bad481..39ee96e13 100644 --- a/libc/calls/tcsetpgrp.c +++ b/libc/calls/tcsetpgrp.c @@ -17,14 +17,33 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/calls/syscall-sysv.internal.h" #include "libc/calls/termios.h" +#include "libc/dce.h" +#include "libc/intrin/strace.internal.h" #include "libc/sysv/consts/termios.h" +#include "libc/sysv/errfuns.h" /** - * Puts process group in control of terminal. + * Sets foreground process group id. + * + * @return 0 on success, or -1 w/ errno + * @raise EINVAL if `pgrp` is invalid + * @raise ENOSYS on Windows and Bare Metal + * @raise EBADF if `fd` isn't an open file descriptor + * @raise EPERM if `pgrp` didn't match process in our group + * @raise ENOTTY if `fd` is isn't controlling teletypewriter + * @raise EIO if process group of writer is orphoned, calling thread is + * not blocking `SIGTTOU`, and process isn't ignoring `SIGTTOU` * @asyncsignalsafe */ -int tcsetpgrp(int fd, int32_t pgrp) { - int pgrp_int = pgrp; - return ioctl(fd, TIOCSPGRP, &pgrp_int); +int tcsetpgrp(int fd, int pgrp) { + int rc; + if (IsWindows() || IsMetal()) { + rc = enosys(); + } else { + rc = sys_ioctl(fd, TIOCSPGRP, &pgrp); + } + STRACE("tcsetpgrp(%d, %d) → %d% m", fd, pgrp, rc); + return rc; } diff --git a/libc/calls/cfsetispeed.c b/libc/intrin/_getenv.c similarity index 74% rename from libc/calls/cfsetispeed.c rename to libc/intrin/_getenv.c index 9662e3922..24ce1bc34 100644 --- a/libc/calls/cfsetispeed.c +++ b/libc/intrin/_getenv.c @@ -1,7 +1,7 @@ /*-*- 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 │ +│ 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 │ @@ -16,18 +16,26 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/termios.h" -#include "libc/sysv/errfuns.h" +#include "libc/dce.h" +#include "libc/intrin/_getenv.internal.h" -int cfsetispeed(struct termios *t, unsigned speed) { - if (speed) { - if (CBAUD) { - if (speed & ~CBAUD) return einval(); - t->c_cflag &= ~CBAUD; - t->c_cflag |= speed; - } else { - t->c_ispeed = speed; +#define ToUpper(c) \ + (IsWindows() && (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) + +struct Env _getenv(char **p, const char *k) { + char *t; + int i, j; + for (i = 0; (t = p[i]); ++i) { + for (j = 0;; ++j) { + if (!k[j] || k[j] == '=') { + if (!t[j]) return (struct Env){t + j, i}; + if (t[j] == '=') return (struct Env){t + j + 1, i}; + break; + } + if (ToUpper(k[j] & 255) != ToUpper(t[j] & 255)) { + break; + } } } - return 0; + return (struct Env){0, i}; } diff --git a/libc/intrin/_getenv.internal.h b/libc/intrin/_getenv.internal.h new file mode 100644 index 000000000..229665677 --- /dev/null +++ b/libc/intrin/_getenv.internal.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_INTRIN__GETENV_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_INTRIN__GETENV_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct Env { + char *s; + int i; +}; + +struct Env _getenv(char **, const char *) hidden; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_INTRIN__GETENV_INTERNAL_H_ */ diff --git a/libc/intrin/bt.c b/libc/intrin/bt.c index 90cb5f239..a1f0c22eb 100644 --- a/libc/intrin/bt.c +++ b/libc/intrin/bt.c @@ -17,10 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/errno.h" -#include "libc/intrin/intrin.h" #include "libc/intrin/kprintf.h" #include "libc/intrin/weaken.h" #include "libc/log/backtrace.internal.h" +#include "libc/runtime/symbols.internal.h" + +static _Thread_local bool noreentry; /** * Shows backtrace if crash reporting facilities are linked. @@ -28,12 +30,20 @@ void _bt(const char *fmt, ...) { int e; va_list va; + + if (!noreentry) { + noreentry = true; + } else { + return; + } + if (fmt) { va_start(va, fmt); kvprintf(fmt, va); va_end(va); } - if (_weaken(ShowBacktrace)) { + + if (_weaken(ShowBacktrace) && _weaken(GetSymbolTable)) { e = errno; _weaken(ShowBacktrace)(2, __builtin_frame_address(0)); errno = e; @@ -41,5 +51,18 @@ void _bt(const char *fmt, ...) { kprintf("_bt() can't show backtrace because you need:\n" "\tSTATIC_YOINK(\"ShowBacktrace\");\n" "to be linked.\n"); + if (_weaken(PrintBacktraceUsingSymbols) && _weaken(GetSymbolTable)) { + e = errno; + _weaken(PrintBacktraceUsingSymbols)(2, __builtin_frame_address(0), + _weaken(GetSymbolTable)()); + errno = e; + } else { + kprintf("_bt() can't show backtrace because you need:\n" + "\tSTATIC_YOINK(\"PrintBacktraceUsingSymbols\");\n" + "\tSTATIC_YOINK(\"GetSymbolTable\");\n" + "to be linked.\n"); + } } + + noreentry = false; } diff --git a/libc/runtime/clearenv.c b/libc/intrin/clearenv.c similarity index 96% rename from libc/runtime/clearenv.c rename to libc/intrin/clearenv.c index 59eedfbc5..fe15c9fd4 100644 --- a/libc/runtime/clearenv.c +++ b/libc/intrin/clearenv.c @@ -21,9 +21,11 @@ /** * Removes all environment variables. + * + * @return 0 on success, or nonzero on error */ int clearenv(void) { + environ = 0; STRACE("clearenv() → 0"); - environ = NULL; return 0; } diff --git a/libc/intrin/describeflock.c b/libc/intrin/describeflock.c index 80364479a..950a68b0b 100644 --- a/libc/intrin/describeflock.c +++ b/libc/intrin/describeflock.c @@ -56,7 +56,7 @@ const char *(DescribeFlock)(char buf[N], int cmd, const struct flock *l) { append(", .l_pid=%d", l->l_pid); } - if (l->l_sysid) { + if (IsFreebsd() && l->l_sysid) { append(", .l_sysid=%d", l->l_sysid); } diff --git a/libc/intrin/describesigaction.c b/libc/intrin/describesigaction.c index 06d316e9a..d5ca35f19 100644 --- a/libc/intrin/describesigaction.c +++ b/libc/intrin/describesigaction.c @@ -16,23 +16,68 @@ │ 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/calls/struct/sigset.h" #include "libc/calls/struct/sigset.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/mem/alloca.h" +#include "libc/sysv/consts/sa.h" -const char *(DescribeSigaction)(char buf[256], int rc, +static const char *DescribeSigHandler(char buf[64], void f(int)) { + if (f == SIG_ERR) return "SIG_ERR"; + if (f == SIG_DFL) return "SIG_DFL"; + if (f == SIG_IGN) return "SIG_IGN"; + ksnprintf(buf, 64, "%t", f); + return buf; +} + +static const char *DescribeSigFlags(char buf[64], int x) { + const struct DescribeFlags kSigFlags[] = { + {SA_NOCLDSTOP, "NOCLDSTOP"}, // + {SA_NOCLDWAIT, "NOCLDWAIT"}, // + {SA_SIGINFO, "SIGINFO"}, // + {SA_ONSTACK, "ONSTACK"}, // + {SA_RESTART, "RESTART"}, // + {SA_NODEFER, "NODEFER"}, // + {SA_RESETHAND, "RESETHAND"}, // + {SA_NOMASK, "NOMASK"}, // + {SA_ONESHOT, "ONESHOT"}, // + }; + return DescribeFlags(buf, 64, kSigFlags, ARRAYLEN(kSigFlags), "SA_", x); +} + +#define N 256 + +#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__) + +const char *(DescribeSigaction)(char buf[N], int rc, const struct sigaction *sa) { + int o = 0; + char b64[64]; + if (rc == -1) return "n/a"; if (!sa) return "NULL"; if ((!IsAsan() && kisdangerous(sa)) || (IsAsan() && !__asan_is_valid(sa, sizeof(*sa)))) { - ksnprintf(buf, 256, "%p", sa); - } else { - ksnprintf(buf, 256, "{.sa_handler=%t, .sa_flags=%#lx, .sa_mask=%s}", - sa->sa_handler, sa->sa_flags, DescribeSigset(rc, &sa->sa_mask)); + ksnprintf(buf, N, "%p", sa); + return buf; } + + append("{.sa_handler=%s", DescribeSigHandler(b64, sa->sa_handler)); + + if (sa->sa_flags) { + append(", .sa_flags=%s", DescribeSigFlags(b64, sa->sa_flags)); + } + + if (!sigisemptyset(&sa->sa_mask)) { + append(", .sa_mask=%s", DescribeSigset(rc, &sa->sa_mask)); + } + + append("}"); + return buf; } diff --git a/libc/intrin/describesigset.c b/libc/intrin/describesigset.c index 35a0041fd..87e0ea04f 100644 --- a/libc/intrin/describesigset.c +++ b/libc/intrin/describesigset.c @@ -23,6 +23,7 @@ #include "libc/intrin/kprintf.h" #include "libc/intrin/popcnt.h" #include "libc/str/str.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/sig.h" #define N 128 @@ -31,6 +32,7 @@ const char *(DescribeSigset)(char buf[N], int rc, const sigset_t *ss) { bool gotsome; + const char *s; int sig, o = 0; sigset_t sigset; @@ -45,9 +47,9 @@ const char *(DescribeSigset)(char buf[N], int rc, const sigset_t *ss) { if (sigcountset(ss) > 16) { append("~"); sigemptyset(&sigset); - for (sig = 1; sig <= NSIG; ++sig) { + for (sig = 1; sig <= _NSIG; ++sig) { if (!sigismember(ss, sig)) { - sigaddset(&sigset, sig); + sigset.__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); } } } else { @@ -56,14 +58,18 @@ const char *(DescribeSigset)(char buf[N], int rc, const sigset_t *ss) { append("{"); gotsome = false; - for (sig = 1; sig <= NSIG; ++sig) { + for (sig = 1; sig <= _NSIG; ++sig) { if (sigismember(&sigset, sig) > 0) { if (gotsome) { append(","); } else { gotsome = true; } - append("%s", strsignal(sig) + 3); + s = strsignal(sig); + if (s[0] == 'S' && s[1] == 'I' && s[2] == 'G') { + s += 3; + } + append("%s", s); } } append("}"); diff --git a/libc/intrin/extend.c b/libc/intrin/extend.c index 392446e1c..50840c28e 100644 --- a/libc/intrin/extend.c +++ b/libc/intrin/extend.c @@ -19,6 +19,7 @@ #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/dce.h" +#include "libc/errno.h" #include "libc/intrin/asan.internal.h" #include "libc/intrin/asancodes.h" #include "libc/macros.internal.h" @@ -29,17 +30,26 @@ #define G FRAMESIZE -static void _mapframe(void *p, int f) { - int prot, flags; +static void *_mapframe(void *p, int f) { + int rc, prot, flags; struct DirectMap dm; prot = PROT_READ | PROT_WRITE; flags = f | MAP_ANONYMOUS | MAP_FIXED; - _npassert((dm = sys_mmap(p, G, prot, flags, -1, 0)).addr == p); - __mmi_lock(); - _npassert(!TrackMemoryInterval(&_mmi, (uintptr_t)p >> 16, (uintptr_t)p >> 16, - dm.maphandle, prot, flags, false, false, 0, - G)); - __mmi_unlock(); + if ((dm = sys_mmap(p, G, prot, flags, -1, 0)).addr == p) { + __mmi_lock(); + rc = TrackMemoryInterval(&_mmi, (uintptr_t)p >> 16, (uintptr_t)p >> 16, + dm.maphandle, prot, flags, false, false, 0, G); + __mmi_unlock(); + if (!rc) { + return p; + } else { + _unassert(errno == ENOMEM); + return 0; + } + } else { + _unassert(errno == ENOMEM); + return 0; + } } /** @@ -58,7 +68,8 @@ static void _mapframe(void *p, int f) { * @param e points to end of memory that's allocated * @param h is highest address to which `e` may grow * @param f should be `MAP_PRIVATE` or `MAP_SHARED` - * @return new value for `e` + * @return new value for `e` or null w/ errno + * @raise ENOMEM if we require more vespene gas */ noasan void *_extend(void *p, size_t n, void *e, int f, intptr_t h) { char *q; @@ -67,10 +78,10 @@ noasan void *_extend(void *p, size_t n, void *e, int f, intptr_t h) { for (q = e; q < ((char *)p + n); q += 8) { if (!((uintptr_t)q & (G - 1))) { _unassert(q + G <= (char *)h); - _mapframe(q, f); + if (!_mapframe(q, f)) return 0; if (IsAsan()) { if (!((uintptr_t)SHADOW(q) & (G - 1))) { - _mapframe(SHADOW(q), f); + if (!_mapframe(SHADOW(q), f)) return 0; __asan_poison(q, G << kAsanScale, kAsanProtected); } } diff --git a/test/libc/mem/putenv_test.c b/libc/intrin/getenv.c similarity index 72% rename from test/libc/mem/putenv_test.c rename to libc/intrin/getenv.c index d3253ed3e..c5b856e2f 100644 --- a/test/libc/mem/putenv_test.c +++ b/libc/intrin/getenv.c @@ -1,7 +1,7 @@ /*-*- 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 Gavin Arthur Hayes │ +│ Copyright 2020 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 │ @@ -16,18 +16,28 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/mem.h" +#include "libc/intrin/_getenv.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/runtime/runtime.h" -#include "libc/testlib/testlib.h" -TEST(putenv, test) { - EXPECT_EQ(0, clearenv()); - EXPECT_EQ(0, putenv("hi=there")); - EXPECT_STREQ("there", getenv("hi")); - EXPECT_EQ(0, clearenv()); - EXPECT_EQ(0, putenv("hi=theretwo")); - EXPECT_STREQ("theretwo", getenv("hi")); - EXPECT_EQ(0, clearenv()); - EXPECT_EQ(0, setenv("hi", "therethree", 0)); - EXPECT_STREQ("therethree", getenv("hi")); +/** + * Returns value of environment variable, or NULL if not found. + * + * Environment variables can store empty string on Unix but not Windows. + * + * @return pointer to value of `environ` entry, or null if not found + */ +char *getenv(const char *s) { + char **p; + struct Env e; + if (!s) return 0; + if (!(p = environ)) return 0; + e = _getenv(p, s); +#if SYSDEBUG + if (!(s[0] == 'T' && s[1] == 'Z' && !s[2])) { + // TODO(jart): memoize TZ or something + STRACE("getenv(%#s) → %#s", s, e.s); + } +#endif + return e.s; } diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index ddc5615e2..9cd87f500 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -7,6 +7,7 @@ LIBC_INTRIN_ARTIFACTS += LIBC_INTRIN_A LIBC_INTRIN = $(LIBC_INTRIN_A_DEPS) $(LIBC_INTRIN_A) LIBC_INTRIN_A = o/$(MODE)/libc/intrin/intrin.a LIBC_INTRIN_A_HDRS = $(filter %.h,$(LIBC_INTRIN_A_FILES)) +LIBC_INTRIN_A_INCS = $(filter %.inc,$(LIBC_INTRIN_A_FILES)) LIBC_INTRIN_A_SRCS_S = $(filter %.S,$(LIBC_INTRIN_A_FILES)) LIBC_INTRIN_A_SRCS_C = $(filter %.c,$(LIBC_INTRIN_A_FILES)) LIBC_INTRIN_A_SRCS = $(LIBC_INTRIN_A_SRCS_S) $(LIBC_INTRIN_A_SRCS_C) @@ -121,7 +122,6 @@ o/$(MODE)/libc/intrin/describeprotflags.o: private \ -fno-sanitize=address o/$(MODE)/libc/intrin/exit1.greg.o \ -o/$(MODE)/libc/intrin/getenv.greg.o \ o/$(MODE)/libc/intrin/wsarecv.o \ o/$(MODE)/libc/intrin/wsarecvfrom.o \ o/$(MODE)/libc/intrin/createfile.o \ @@ -182,6 +182,7 @@ o/$(MODE)/libc/intrin/memmove.o: private \ LIBC_INTRIN_LIBS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x))) LIBC_INTRIN_HDRS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_HDRS)) +LIBC_INTRIN_INCS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_INCS)) LIBC_INTRIN_SRCS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_SRCS)) LIBC_INTRIN_CHECKS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_CHECKS)) LIBC_INTRIN_OBJS = $(foreach x,$(LIBC_INTRIN_ARTIFACTS),$($(x)_OBJS)) diff --git a/libc/intrin/kmalloc.c b/libc/intrin/kmalloc.c index 0e9190f9d..2f2872dea 100644 --- a/libc/intrin/kmalloc.c +++ b/libc/intrin/kmalloc.c @@ -29,7 +29,7 @@ #include "libc/thread/thread.h" #include "libc/thread/tls.h" -#define KMALLOC_ALIGN __BIGGEST_ALIGNMENT__ +#define KMALLOC_ALIGN sizeof(intptr_t) static struct { char *endptr; @@ -55,21 +55,37 @@ __attribute__((__constructor__)) static void kmalloc_init(void) { * The code malloc() depends upon uses this function to allocate memory. * The returned memory can't be freed, and leak detection is impossible. * This function panics when memory isn't available. + * + * Memory returned by this function is aligned on the word size, and as + * such, kmalloc() shouldn't be used for vector operations. + * + * @return zero-initialized memory on success, or null w/ errno + * @raise ENOMEM if we require more vespene gas */ void *kmalloc(size_t size) { - char *start; - size_t i, n; + char *p, *e; + size_t i, n, t; n = ROUNDUP(size + (IsAsan() * 8), KMALLOC_ALIGN); kmalloc_lock(); - i = g_kmalloc.total; - g_kmalloc.total += n; - start = (char *)kMemtrackKmallocStart; - if (!g_kmalloc.endptr) g_kmalloc.endptr = start; - g_kmalloc.endptr = - _extend(start, g_kmalloc.total, g_kmalloc.endptr, MAP_PRIVATE, - kMemtrackKmallocStart + kMemtrackKmallocSize); + t = g_kmalloc.total; + e = g_kmalloc.endptr; + i = t; + t += n; + p = (char *)kMemtrackKmallocStart; + if (!e) e = p; + if ((e = _extend(p, t, e, MAP_PRIVATE, + kMemtrackKmallocStart + kMemtrackKmallocSize))) { + g_kmalloc.endptr = e; + g_kmalloc.total = t; + } else { + p = 0; + } kmalloc_unlock(); - _unassert(!((intptr_t)(start + i) & (KMALLOC_ALIGN - 1))); - if (IsAsan()) __asan_poison(start + i + size, n - size, kAsanHeapOverrun); - return start + i; + if (p) { + _unassert(!((intptr_t)(p + i) & (KMALLOC_ALIGN - 1))); + if (IsAsan()) __asan_poison(p + i + size, n - size, kAsanHeapOverrun); + return p + i; + } else { + return 0; + } } diff --git a/libc/intrin/kmalloc.h b/libc/intrin/kmalloc.h index b26bba057..a2a01a330 100644 --- a/libc/intrin/kmalloc.h +++ b/libc/intrin/kmalloc.h @@ -3,7 +3,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -void *kmalloc(size_t) attributeallocsize((1)) mallocesque returnsnonnull; +void *kmalloc(size_t) mallocesque attributeallocsize((1)) returnsaligned((8)); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/mem/putenv.c b/libc/intrin/putenv.c similarity index 60% rename from libc/mem/putenv.c rename to libc/intrin/putenv.c index 561adae93..364536347 100644 --- a/libc/mem/putenv.c +++ b/libc/intrin/putenv.c @@ -16,19 +16,16 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/intrin/_getenv.internal.h" +#include "libc/intrin/kmalloc.h" #include "libc/intrin/strace.internal.h" -#include "libc/dce.h" #include "libc/macros.internal.h" -#include "libc/mem/alg.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" #define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) -static bool once; +static char **expected; static size_t capacity; static size_t GetEnvironLen(char **env) { @@ -37,98 +34,69 @@ static size_t GetEnvironLen(char **env) { return p - env; } -static void RestoreOriginalEnvironment(char **envp) { - environ = envp; -} - -static void PutEnvImplAtExit(void *p) { - free(p); -} - -static void FreeEnviron(char **env) { - char **a; - for (a = env; *a; ++a) { - free(*a); - } - free(env); -} - -static void GrowEnviron(void) { +static char **GrowEnviron(char **a) { size_t n, c; - char **a, **b, **p; - a = environ; + char **b, **p; + if (!a) a = environ; n = a ? GetEnvironLen(a) : 0; c = MAX(16ul, n) << 1; - b = calloc(c, sizeof(char *)); - if (a) { - for (p = b; *a;) { - *p++ = strdup(*a++); + if ((b = kmalloc(c * sizeof(char *)))) { + if (a) { + for (p = b; *a;) { + *p++ = *a++; + } } + environ = b; + expected = b; + capacity = c; + return b; + } else { + return 0; } - __cxa_atexit(FreeEnviron, b, 0); - environ = b; - capacity = c; } int PutEnvImpl(char *s, bool overwrite) { - char *p; - unsigned i, namelen; - if (!once) { - __cxa_atexit(RestoreOriginalEnvironment, environ, 0); - GrowEnviron(); - once = true; - } else if (!environ) { - GrowEnviron(); - } - for (p = s; *p && *p != '='; ++p) { - if (IsWindows()) { - *p = ToUpper(*p); + char **p; + struct Env e; + if (!(p = environ)) { + if (!(p = GrowEnviron(0))) { + return -1; } } - if (*p != '=') goto Fail; - namelen = p + 1 - s; - for (i = 0; environ[i]; ++i) { - if (!strncmp(environ[i], s, namelen)) { - if (!overwrite) { - free(s); - return 0; - } - goto Replace; + e = _getenv(p, s); + if (e.s && !overwrite) { + return 0; + } + if (e.s) { + p[e.i] = s; + return 0; + } + if (p != expected) { + capacity = e.i; + } + if (e.i + 1 >= capacity) { + if (!(p = GrowEnviron(p))) { + return -1; } } - if (i + 1 >= capacity) { - GrowEnviron(); - } - environ[i + 1] = 0; -Replace: - __cxa_atexit(PutEnvImplAtExit, environ[i], 0); - environ[i] = s; + p[e.i + 1] = 0; + p[e.i] = s; return 0; -Fail: - free(s); - return einval(); -} - -static void UnsetenvFree(void *p) { - free(p); -} - -/* weakly called by unsetenv() when removing a pointer */ -void __freeenv(void *p) { - if (once) { - __cxa_atexit(UnsetenvFree, p, 0); - } } /** * Emplaces environment key=value. * - * @return 0 on success or non-zero on error + * @param s should be a string that looks like `"name=value"` and it'll + * become part of the environment; changes to its memory will change + * the environment too + * @return 0 on success, or non-zero w/ errno on error + * @raise ENOMEM if we require more vespene gas * @see setenv(), getenv() */ int putenv(char *s) { int rc; - rc = PutEnvImpl(strdup(s), true); - STRACE("putenv(%#s) → %d", s, rc); + rc = PutEnvImpl(s, true); + STRACE("putenv(%#s) → %d% m", s, rc); return rc; } diff --git a/libc/mem/setenv.c b/libc/intrin/setenv.c similarity index 85% rename from libc/mem/setenv.c rename to libc/intrin/setenv.c index 21af18abb..7879d1bfb 100644 --- a/libc/mem/setenv.c +++ b/libc/intrin/setenv.c @@ -16,10 +16,9 @@ │ 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/kmalloc.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" @@ -27,7 +26,7 @@ /** * Copies variable to environment. * - * @return 0 on success, or -1 w/ errno + * @return 0 on success, or -1 w/ errno and environment is unchanged * @raise EINVAL if `name` is empty or contains `'='` * @raise ENOMEM if we require more vespene gas * @see putenv(), getenv() @@ -35,12 +34,16 @@ 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); - if (!(s = malloc(namelen + valuelen + 2))) return -1; - memcpy(mempcpy(mempcpy(s, name, namelen), "=", 1), value, valuelen + 1); + size_t n, m; + const char *t; + if (!name || !*name || !value) return einval(); + for (t = name; *t; ++t) { + if (*t == '=') return einval(); + } + n = strlen(name); + m = strlen(value); + if (!(s = kmalloc(n + 1 + m + 1))) return -1; + memcpy(mempcpy(mempcpy(s, name, n), "=", 1), value, m + 1); rc = PutEnvImpl(s, overwrite); STRACE("setenv(%#s, %#s, %d) → %d% m", name, value, overwrite, rc); return rc; diff --git a/libc/intrin/sigaddset.c b/libc/intrin/sigaddset.c index a513ad536..b19ac04cf 100644 --- a/libc/intrin/sigaddset.c +++ b/libc/intrin/sigaddset.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/sigset.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" @@ -28,10 +29,16 @@ * @asyncsignalsafe */ int sigaddset(sigset_t *set, int sig) { + _Static_assert(NSIG == sizeof(set->__bits) * CHAR_BIT, ""); _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); if (1 <= sig && sig <= NSIG) { - if (!sigisprecious(sig)) { - set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); + if (1 <= sig && sig <= _NSIG) { + if ( +#define M(x) sig != x && +#include "libc/intrin/sigisprecious.inc" + 1) { + set->__bits[(sig - 1) >> 6] |= 1ull << ((sig - 1) & 63); + } } return 0; } else { diff --git a/libc/intrin/sigcountset.c b/libc/intrin/sigcountset.c index 46d3e932c..c1eabf01c 100644 --- a/libc/intrin/sigcountset.c +++ b/libc/intrin/sigcountset.c @@ -19,6 +19,7 @@ #include "libc/calls/struct/sigset.h" #include "libc/intrin/popcnt.h" #include "libc/macros.internal.h" +#include "libc/sysv/consts/limits.h" /** * Returns population count of signal set. @@ -27,9 +28,22 @@ * @asyncsignalsafe */ int sigcountset(const sigset_t *set) { - int r, i; - for (r = i = 0; i < ARRAYLEN(set->__bits); ++i) { - r += popcnt(set->__bits[i]); + int r, i, x, y; + switch (_NSIG) { + case 32: + x = (uint32_t)set->__bits[0]; + y = 0; + break; + case 64: + x = set->__bits[0]; + y = 0; + break; + case 128: + x = set->__bits[0]; + y = set->__bits[1]; + break; + default: + notpossible; } - return r; + return popcnt(x) + popcnt(y); } diff --git a/libc/intrin/sigdelset.c b/libc/intrin/sigdelset.c index 1ab470193..1ddfc5bd6 100644 --- a/libc/intrin/sigdelset.c +++ b/libc/intrin/sigdelset.c @@ -27,6 +27,7 @@ * @asyncsignalsafe */ int sigdelset(sigset_t *set, int sig) { + _Static_assert(NSIG == sizeof(set->__bits) * CHAR_BIT, ""); _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); if (1 <= sig && sig <= NSIG) { set->__bits[(sig - 1) >> 6] &= ~(1ull << ((sig - 1) & 63)); diff --git a/libc/intrin/sigfillset.c b/libc/intrin/sigfillset.c index 596c6fb19..b2d61f804 100644 --- a/libc/intrin/sigfillset.c +++ b/libc/intrin/sigfillset.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/sigset.h" #include "libc/str/str.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/sig.h" /** @@ -29,7 +30,17 @@ */ int sigfillset(sigset_t *set) { memset(set->__bits, -1, sizeof(set->__bits)); - sigdelset(set, SIGKILL); - sigdelset(set, SIGSTOP); +#define M(x) set->__bits[(x - 1) >> 6] &= ~(1ull << ((x - 1) & 63)); +#include "libc/intrin/sigisprecious.inc" + switch (_NSIG) { + case 32: + set->__bits[0] &= 0xffffffff; + break; + case 64: + set->__bits[1] = 0; + break; + default: + break; + } return 0; } diff --git a/libc/intrin/sigismember.c b/libc/intrin/sigismember.c index 279abedbb..ed62cf767 100644 --- a/libc/intrin/sigismember.c +++ b/libc/intrin/sigismember.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/sigset.h" +#include "libc/sysv/consts/limits.h" #include "libc/sysv/errfuns.h" /** @@ -30,7 +31,11 @@ int sigismember(const sigset_t *set, int sig) { _Static_assert(sizeof(set->__bits[0]) * CHAR_BIT == 64, ""); if (1 <= sig && sig <= NSIG) { - return !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63))); + if (1 <= sig && sig <= _NSIG) { + return !!(set->__bits[(sig - 1) >> 6] & (1ull << ((sig - 1) & 63))); + } else { + return 0; + } } else { return einval(); } diff --git a/libc/intrin/sigisprecious.c b/libc/intrin/sigisprecious.c index 37bfcb434..1d3bf80c1 100644 --- a/libc/intrin/sigisprecious.c +++ b/libc/intrin/sigisprecious.c @@ -23,6 +23,8 @@ * Returns true if you're not authorized to block this signal. */ int sigisprecious(int sig) { - return sig == SIGKILL || // - sig == SIGSTOP; + return 0 +#define M(x) || sig == x +#include "libc/intrin/sigisprecious.inc" + ; } diff --git a/libc/intrin/sigisprecious.inc b/libc/intrin/sigisprecious.inc new file mode 100644 index 000000000..5c08fd00b --- /dev/null +++ b/libc/intrin/sigisprecious.inc @@ -0,0 +1,3 @@ +M(SIGKILL) +M(SIGABRT) +M(SIGSTOP) diff --git a/libc/intrin/strsignal_r.c b/libc/intrin/strsignal_r.c index 35d27280c..b030f9b80 100644 --- a/libc/intrin/strsignal_r.c +++ b/libc/intrin/strsignal_r.c @@ -33,6 +33,7 @@ * @param buf may be used to store output having at least 15 bytes * @return pointer to .rodata string, or to `buf` after mutating * @see sigaction() + * @asyncsignalsafe * @threadsafe */ char *strsignal_r(int sig, char buf[hasatleast 15]) { diff --git a/libc/calls/unsetenv.c b/libc/intrin/unsetenv.c similarity index 75% rename from libc/calls/unsetenv.c rename to libc/intrin/unsetenv.c index b70bb87a5..440bbc560 100644 --- a/libc/calls/unsetenv.c +++ b/libc/intrin/unsetenv.c @@ -17,39 +17,29 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/dce.h" -#include "libc/intrin/weaken.h" -#include "libc/mem/internal.h" +#include "libc/intrin/_getenv.internal.h" #include "libc/runtime/runtime.h" - -#define ToUpper(c) \ - (IsWindows() && (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) +#include "libc/sysv/errfuns.h" /** * Removes environment variable. + * + * @param s is non-empty environment key which can't contain `'='` + * @return 0 on success, or -1 w/ errno and environment is unchanged + * @raise EINVAL if `s` is an empty string or has a `'='` character */ int unsetenv(const char *s) { - char **p; - size_t i, j, k; - if (s && (p = environ)) { - for (i = 0; p[i]; ++i) { - for (j = 0;; ++j) { - if (!s[j]) { - if (p[i][j] == '=') { - if (_weaken(__freeenv)) { - _weaken(__freeenv)(p[i]); - } - k = i + 1; - do { - p[k - 1] = p[k]; - } while (p[k++]); - return 0; - } - break; - } - if (ToUpper(s[j]) != ToUpper(p[i][j])) { - break; - } - } + char **p, *t; + struct Env e; + if (!s || !*s) return einval(); + for (t = s; *t; ++t) { + if (*t == '=') return einval(); + } + if ((p = environ)) { + e = _getenv(p, s); + while (p[e.i]) { + p[e.i] = p[e.i + 1]; + ++e.i; } } return 0; diff --git a/libc/log/backtrace2.c b/libc/log/backtrace2.c index f64659e73..aee07c0f3 100644 --- a/libc/log/backtrace2.c +++ b/libc/log/backtrace2.c @@ -125,7 +125,7 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { if (sys_pipe2(pipefds, O_CLOEXEC) == -1) { return -1; } - if ((pid = fork()) == -1) { + if ((pid = __sys_fork().ax) == -1) { sys_close(pipefds[0]); sys_close(pipefds[1]); return -1; @@ -149,19 +149,12 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { } p1 = buf; p3 = p1 + got; - /* - * remove racist output from gnu tooling, that can't be disabled - * otherwise, since it breaks other tools like emacs that aren't - * equipped to ignore it, and what's most problematic is that - * addr2line somehow manages to put the racism onto the one line - * in the backtrace we actually care about. - */ for (got = p3 - buf, p1 = buf; got;) { if ((p2 = memmem(p1, got, " (discriminator ", strlen(" (discriminator ") - 1)) && (p3 = memchr(p2, '\n', got - (p2 - p1)))) { if (p3 > p2 && p3[-1] == '\r') --p3; - write(2, p1, p2 - p1); + sys_write(2, p1, p2 - p1); got -= p3 - p1; p1 += p3 - p1; } else { @@ -183,11 +176,13 @@ static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { } static int PrintBacktrace(int fd, const struct StackFrame *bp) { +#if !defined(DWARFLESS) if (!IsTiny() && !__isworker) { if (PrintBacktraceUsingAddr2line(fd, bp) != -1) { return 0; } } +#endif return PrintBacktraceUsingSymbols(fd, bp, GetSymbolTable()); } diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c index 1ff5522f5..b1193fb2d 100644 --- a/libc/log/backtrace3.c +++ b/libc/log/backtrace3.c @@ -83,7 +83,7 @@ noinstrument noasan int PrintBacktraceUsingSymbols(int fd, } else { addend = 0; } - kprintf("%012lx %012lx %s%+d\n", frame, addr, __get_symbol_name(st, symbol), + kprintf("%012lx %lx %s%+d\n", frame, addr, __get_symbol_name(st, symbol), addend); } return 0; diff --git a/libc/log/log.mk b/libc/log/log.mk index 438a19291..e765c25a9 100644 --- a/libc/log/log.mk +++ b/libc/log/log.mk @@ -42,6 +42,7 @@ LIBC_LOG_A_DIRECTDEPS = \ LIBC_SYSV_CALLS \ LIBC_TIME \ LIBC_TINYMATH \ + LIBC_ZIPOS \ THIRD_PARTY_DLMALLOC \ THIRD_PARTY_GDTOA diff --git a/libc/log/showcrashreports.c b/libc/log/showcrashreports.c index 1723dbb80..24dfa7201 100644 --- a/libc/log/showcrashreports.c +++ b/libc/log/showcrashreports.c @@ -32,6 +32,7 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/ss.h" +STATIC_YOINK("zipos"); // for symtab STATIC_YOINK("__die"); // for backtracing STATIC_YOINK("ShowBacktrace"); // for backtracing STATIC_YOINK("GetSymbolTable"); // for backtracing diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index f2763ae93..83ffa1717 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/calls/dprintf.h" -#include "libc/intrin/strace.internal.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/timeval.h" #include "libc/dce.h" @@ -27,6 +26,7 @@ #include "libc/fmt/fmt.h" #include "libc/intrin/bits.h" #include "libc/intrin/safemacros.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/log/internal.h" #include "libc/log/log.h" #include "libc/math.h" @@ -131,7 +131,9 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, __start_fatal(file, line); strcpy(buf32, "unknown"); gethostname(buf32, sizeof(buf32)); - (dprintf)(STDERR_FILENO, "fatality %s pid %d\n", buf32, getpid()); + (dprintf)(STDERR_FILENO, + "exiting due to aforementioned error (host %s pid %d tid %d)\n", + buf32, getpid(), gettid()); __die(); unreachable; } diff --git a/libc/mem/internal.h b/libc/mem/internal.h index d3517779d..250a60607 100644 --- a/libc/mem/internal.h +++ b/libc/mem/internal.h @@ -10,7 +10,6 @@ struct CritbitNode { }; int PutEnvImpl(char *, bool) hidden; -void __freeenv(void *) hidden; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/mem/sortedints.c b/libc/mem/sortedints.c new file mode 100644 index 000000000..7301d1d15 --- /dev/null +++ b/libc/mem/sortedints.c @@ -0,0 +1,93 @@ +/*-*- 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/assert.h" +#include "libc/dce.h" +#include "libc/intrin/midpoint.h" +#include "libc/mem/mem.h" +#include "libc/mem/sortedints.internal.h" +#include "libc/str/str.h" + +bool ContainsInt(const struct SortedInts *t, int k) { + int l, m, r; + l = 0; + r = t->n - 1; + while (l <= r) { + m = _midpoint(l, r); + if (t->p[m] < k) { + l = m + 1; + } else if (t->p[m] > k) { + r = m - 1; + } else { + return true; + } + } + return false; +} + +int LeftmostInt(const struct SortedInts *t, int k) { + int l, m, r; + l = 0; + r = t->n; + while (l < r) { + m = _midpoint(l, r); + if (t->p[m] < k) { + l = m + 1; + } else { + r = m; + } + } + _unassert(l == 0 || k >= t->p[l - 1]); + _unassert(l == t->n || k <= t->p[l]); + return l; +} + +int CountInt(const struct SortedInts *t, int k) { + int i, c; + for (c = 0, i = LeftmostInt(t, k); i < t->n; ++i) { + if (t->p[i] == k) { + ++c; + } else { + break; + } + } + return c; +} + +bool InsertInt(struct SortedInts *t, int k, bool u) { + int l; + _unassert(t->n >= 0); + _unassert(t->n <= t->c); + if (t->n == t->c) { + ++t->c; + if (!IsModeDbg()) { + t->c += t->c >> 1; + } + t->p = realloc(t->p, t->c * sizeof(*t->p)); + } + l = LeftmostInt(t, k); + if (l < t->n) { + if (u && t->p[l] == k) { + return false; + } + memmove(t->p + l + 1, t->p + l, (t->n - l) * sizeof(*t->p)); + } + t->p[l] = k; + t->n++; + return true; +} diff --git a/libc/mem/sortedints.internal.h b/libc/mem/sortedints.internal.h new file mode 100644 index 000000000..a5ce6f6a3 --- /dev/null +++ b/libc/mem/sortedints.internal.h @@ -0,0 +1,19 @@ +#ifndef COSMOPOLITAN_LIBC_MEM_SORTEDINTS_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_MEM_SORTEDINTS_INTERNAL_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct SortedInts { + int n; + int c; + int *p; +}; + +bool ContainsInt(const struct SortedInts *, int); +bool InsertInt(struct SortedInts *, int, bool); +int CountInt(const struct SortedInts *, int); +int LeftmostInt(const struct SortedInts *, int); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_MEM_SORTEDINTS_INTERNAL_H_ */ diff --git a/libc/nexgen32e/longjmp.S b/libc/nexgen32e/longjmp.S index eb96b02fd..4e9de7751 100644 --- a/libc/nexgen32e/longjmp.S +++ b/libc/nexgen32e/longjmp.S @@ -23,9 +23,8 @@ // @param rdi points to the jmp_buf // @param esi is returned by setjmp() invocation (coerced nonzero) // @noreturn -// @assume system five nexgen32e abi conformant -// @note code built w/ microsoft abi compiler can't call this -// @see _gclongjmp() unwinds _gc() destructors +// @see _gclongjmp() +// @see siglongjmp() longjmp: mov %esi,%eax test %eax,%eax @@ -41,4 +40,3 @@ longjmp: jmp *56(%rdi) .endfn longjmp,globl .alias longjmp,_longjmp - .alias longjmp,siglongjmp diff --git a/libc/calls/cfgetospeed.c b/libc/nexgen32e/siglongjmp.S similarity index 77% rename from libc/calls/cfgetospeed.c rename to libc/nexgen32e/siglongjmp.S index f0e57fc16..b1b9e8dff 100644 --- a/libc/calls/cfgetospeed.c +++ b/libc/nexgen32e/siglongjmp.S @@ -1,7 +1,7 @@ -/*-*- 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│ +/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ +│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2021 Justine Alexandra Roberts Tunney │ +│ 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 │ @@ -16,13 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/termios.h" -#include "libc/sysv/consts/termios.h" +#include "libc/macros.internal.h" -uint32_t cfgetospeed(const struct termios *t) { - if (CBAUD) { - return t->c_cflag & CBAUD; - } else { - return t->c_ospeed; - } -} +// Loads previously saved processor state. +// +// @param rdi points to the jmp_buf +// @param esi is returned by setjmp() invocation (coerced nonzero) +// @noreturn +// @asyncsignalsafe +siglongjmp: + jmp longjmp + .endfn siglongjmp,globl diff --git a/libc/nt/comms.h b/libc/nt/comms.h new file mode 100644 index 000000000..02b702f08 --- /dev/null +++ b/libc/nt/comms.h @@ -0,0 +1,36 @@ +#ifndef COSMOPOLITAN_LIBC_NT_COMMS_H_ +#define COSMOPOLITAN_LIBC_NT_COMMS_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ +/* ░░░░ + ▒▒▒░░░▒▒▒▒▒▒▒▓▓▓░ + ▒▒▒▒░░░▒▒▒▒▒▒▓▓▓▓▓▓░ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓░ + ▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▒ ▒▒▒▓▓█ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ░▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ █▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓░ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▒ + ▒▒▒▒▓▓ ▓▒▒▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▓ ▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓ + ░░░░░░░░░░░▒▒▒▒ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▒░░░░░░░░░░▒▒▒▒▒▓▓▓ ▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ░▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓░ ░▓███▓ + ▒▒░░░░░░░░░░▒▒▒▒▒▓▓░ ▒▓▓▓▒▒▒ ░▒▒▒▓ ████████████ + ▒▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▒▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒░ ░███ + ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ███ + ▒▒░░░░░░░░░░▒▒▒▒▒▒▓▓ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ▓██ + ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▓ ▓██ + ▒▒░░░▒▒▒░░░▒▒░▒▒▒▓▓▒ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ███ + ░▒▓ ░▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ▓██ +╔────────────────────────────────────────────────────────────────▀▀▀─────────│─╗ +│ cosmopolitan § new technology » communications ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +bool32 PurgeComm(int64_t hFile, uint32_t dwFlags); +bool32 TransmitCommChar(int64_t hFile, char cChar); +bool32 ClearCommBreak(int64_t hFile); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_COMMS_H_ */ diff --git a/libc/nt/kernel32/ClearCommBreak.s b/libc/nt/kernel32/ClearCommBreak.s index e9dbac01f..af9012f5e 100644 --- a/libc/nt/kernel32/ClearCommBreak.s +++ b/libc/nt/kernel32/ClearCommBreak.s @@ -1,2 +1,15 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_ClearCommBreak,ClearCommBreak,0 + + .text.windows +ClearCommBreak: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_ClearCommBreak(%rip) + leave + ret + .endfn ClearCommBreak,globl + .previous diff --git a/libc/nt/kernel32/PurgeComm.s b/libc/nt/kernel32/PurgeComm.s index fa7496653..d1d3a441d 100644 --- a/libc/nt/kernel32/PurgeComm.s +++ b/libc/nt/kernel32/PurgeComm.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_PurgeComm,PurgeComm,0 + + .text.windows +PurgeComm: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_PurgeComm(%rip),%rax + jmp __sysv2nt + .endfn PurgeComm,globl + .previous diff --git a/libc/nt/kernel32/TransmitCommChar.s b/libc/nt/kernel32/TransmitCommChar.s index 4c594a188..45756320d 100644 --- a/libc/nt/kernel32/TransmitCommChar.s +++ b/libc/nt/kernel32/TransmitCommChar.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_TransmitCommChar,TransmitCommChar,0 + + .text.windows +TransmitCommChar: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_TransmitCommChar(%rip),%rax + jmp __sysv2nt + .endfn TransmitCommChar,globl + .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index ac463d4b5..439ef2ae1 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -112,7 +112,7 @@ imp 'CheckNameLegalDOS8Dot3' CheckNameLegalDOS8Dot3W kernel32 128 imp 'CheckRemoteDebuggerPresent' CheckRemoteDebuggerPresent kernel32 0 2 imp 'CheckTokenCapability' CheckTokenCapability kernel32 0 imp 'CheckTokenMembershipEx' CheckTokenMembershipEx kernel32 0 -imp 'ClearCommBreak' ClearCommBreak kernel32 0 +imp 'ClearCommBreak' ClearCommBreak kernel32 0 1 imp 'ClearCommError' ClearCommError kernel32 0 imp 'CloseConsoleHandle' CloseConsoleHandle kernel32 134 imp 'ClosePackageInfo' ClosePackageInfo kernel32 0 @@ -764,7 +764,7 @@ imp 'PssWalkMarkerSetPosition' PssWalkMarkerSetPosition kernel32 0 imp 'PssWalkMarkerTell' PssWalkMarkerTell kernel32 1080 imp 'PssWalkSnapshot' PssWalkSnapshot kernel32 0 imp 'PulseEvent' PulseEvent kernel32 0 1 -imp 'PurgeComm' PurgeComm kernel32 0 +imp 'PurgeComm' PurgeComm kernel32 0 2 imp 'QueryActCtx' QueryActCtxW kernel32 0 imp 'QueryActCtxSettings' QueryActCtxSettingsW kernel32 0 imp 'QueryActCtxSettingsWWorker' QueryActCtxSettingsWWorker kernel32 1085 @@ -1011,7 +1011,7 @@ imp 'TlsGetValue' TlsGetValue kernel32 0 1 imp 'TlsSetValue' TlsSetValue kernel32 0 2 imp 'Toolhelp32ReadProcessMemory' Toolhelp32ReadProcessMemory kernel32 1449 imp 'TransactNamedPipe' TransactNamedPipe kernel32 0 7 -imp 'TransmitCommChar' TransmitCommChar kernel32 0 +imp 'TransmitCommChar' TransmitCommChar kernel32 0 2 imp 'TryAcquireSRWLockExclusive' TryAcquireSRWLockExclusive kernel32 0 1 imp 'TryAcquireSRWLockShared' TryAcquireSRWLockShared kernel32 0 1 imp 'TryEnterCriticalSection' TryEnterCriticalSection kernel32 0 1 diff --git a/libc/runtime/cocmd.c b/libc/runtime/cocmd.c index 6c63a53af..3100fff88 100644 --- a/libc/runtime/cocmd.c +++ b/libc/runtime/cocmd.c @@ -25,6 +25,7 @@ #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/fmt/magnumstrs.internal.h" +#include "libc/intrin/_getenv.internal.h" #include "libc/intrin/bits.h" #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" @@ -55,11 +56,6 @@ #define TOMBSTONE ((char *)-1) #define READ24(s) READ32LE(s "\0") -struct Env { - char *s; - int i; -}; - static char *p; static char *q; static char *r; @@ -71,7 +67,7 @@ static char *assign; static char var[32]; static int lastchild; static int exitstatus; -static char *envs[3000]; +static char *envs[500]; static char *args[3000]; static const char *prog; static char errbuf[512]; @@ -148,27 +144,9 @@ static int GetSignalByName(const char *s) { return 0; } -static struct Env GetEnv(char **p, const char *k) { - int i, j; - for (i = 0; p[i]; ++i) { - for (j = 0;; ++j) { - if (!k[j] || k[j] == '=') { - if (p[i][j] == '=') { - return (struct Env){p[i] + j + 1, i}; - } - break; - } - if (toupper(k[j] & 255) != toupper(p[i][j] & 255)) { - break; - } - } - } - return (struct Env){0, i}; -} - static void PutEnv(char **p, const char *kv) { struct Env e; - e = GetEnv(p, kv); + e = _getenv(p, kv); p[e.i] = kv; if (!e.s) p[e.i + 1] = 0; } @@ -271,7 +249,7 @@ static int Read(void) { static int Cd(void) { const char *s; - if ((s = n > 1 ? args[1] : GetEnv(envs, "HOME").s)) { + if ((s = n > 1 ? args[1] : _getenv(envs, "HOME").s)) { if (!chdir(s)) { return 0; } else { @@ -489,14 +467,27 @@ static const char *IntToStr(int x) { } static const char *GetVar(const char *key) { + static char vbuf[PATH_MAX]; 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 if (!strcmp(key, "PWD")) { + _npassert(getcwd(vbuf, sizeof(vbuf))); + return vbuf; + } else if (!strcmp(key, "UID")) { + FormatInt32(vbuf, getuid()); + return vbuf; + } else if (!strcmp(key, "GID")) { + FormatInt32(vbuf, getgid()); + return vbuf; + } else if (!strcmp(key, "EUID")) { + FormatInt32(vbuf, geteuid()); + return vbuf; } else { - return GetEnv(envs, key).s; + return _getenv(envs, key).s; } } diff --git a/libc/runtime/enable_threads.c b/libc/runtime/enable_threads.c index 21f0176d6..72211596b 100644 --- a/libc/runtime/enable_threads.c +++ b/libc/runtime/enable_threads.c @@ -19,8 +19,10 @@ #include "ape/sections.internal.h" #include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/intrin/strace.internal.h" +#include "libc/runtime/morph.h" #include "libc/runtime/runtime.h" #include "libc/thread/tls.h" @@ -30,7 +32,8 @@ extern int __threadcalls_start[]; #pragma weak __threadcalls_end static privileged dontinline void FixupLockNops(void) { - __morph_begin(); + sigset_t mask; + __morph_begin(&mask); /* * _NOPL("__threadcalls", func) * @@ -54,7 +57,7 @@ static privileged dontinline void FixupLockNops(void) { _base[*p + 1] = 0x67; _base[*p + 2] = 0xe8; } - __morph_end(); + __morph_end(&mask); } void __enable_threads(void) { diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index 2a5117234..419b6bd8c 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -19,6 +19,7 @@ #include "ape/sections.internal.h" #include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -32,6 +33,7 @@ #include "libc/nexgen32e/msr.h" #include "libc/nt/thread.h" #include "libc/runtime/internal.h" +#include "libc/runtime/morph.h" #include "libc/runtime/runtime.h" #include "libc/stdalign.internal.h" #include "libc/str/str.h" @@ -196,9 +198,10 @@ privileged void __enable_tls(void) { if ((intptr_t)_tls_content && (IsWindows() || IsXnu())) { int n; uint64_t w; + sigset_t mask; unsigned m, dis; unsigned char *p; - __morph_begin(); + __morph_begin(&mask); if (IsXnu()) { // Apple is quite straightforward to patch. We basically @@ -262,7 +265,7 @@ privileged void __enable_tls(void) { } } - __morph_end(); + __morph_end(&mask); } // we are now allowed to use tls diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 0f59649ab..ffc24b366 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -224,9 +224,6 @@ textwindows void WinMainForked(void) { AbortFork("CloseHandle"); } - // turn tls back on - __enable_tls(); - // rewrap the stdin named pipe hack // since the handles closed on fork struct Fds *fds = VEIL("r", &g_fds); @@ -267,11 +264,14 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { uint32_t oldprot; char **args, **args2; char16_t pipename[64]; + bool needtls, threaded; int64_t reader, writer; struct NtStartupInfo startinfo; int i, n, pid, untrackpid, rc = -1; char *p, forkvar[6 + 21 + 1 + 21 + 1]; struct NtProcessInformation procinfo; + threaded = __threaded; + needtls = __tls_enabled; if (!setjmp(jb)) { pid = untrackpid = __reservefd_unlocked(-1); reader = CreateNamedPipe(CreatePipeName(pipename), @@ -345,9 +345,15 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { } } else { rc = 0; + if (needtls) { + __enable_tls(); + } + if (threaded && !__threaded && _weaken(__enable_threads)) { + _weaken(__enable_threads)(); + } } if (untrackpid != -1) { - __releasefd_unlocked(untrackpid); + __releasefd(untrackpid); } return rc; } diff --git a/libc/calls/cfgetispeed.c b/libc/runtime/fork-sysv.c similarity index 82% rename from libc/calls/cfgetispeed.c rename to libc/runtime/fork-sysv.c index 51fefc120..9a7aba589 100644 --- a/libc/calls/cfgetispeed.c +++ b/libc/runtime/fork-sysv.c @@ -1,7 +1,7 @@ /*-*- 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 │ +│ 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 │ @@ -16,13 +16,19 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/termios.h" -#include "libc/sysv/consts/termios.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" -uint32_t cfgetispeed(const struct termios *t) { - if (CBAUD) { - return t->c_cflag & CBAUD; - } else { - return t->c_ispeed; +int sys_fork(void) { + axdx_t ad; + int ax, dx; + ad = __sys_fork(); + ax = ad.ax; + dx = ad.dx; + if (IsXnu() && ax != -1) { + // eax always returned with childs pid + // edx is 0 for parent and 1 for child + ax &= dx - 1; } + return ax; } diff --git a/libc/runtime/fork.c b/libc/runtime/fork.c index 4c5fec362..48da63f7a 100644 --- a/libc/runtime/fork.c +++ b/libc/runtime/fork.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.internal.h" @@ -33,30 +34,15 @@ #include "libc/thread/tls.h" int _fork(uint32_t dwCreationFlags) { - axdx_t ad; - bool threaded; - sigset_t old, all; int ax, dx, parent; - sigfillset(&all); - _unassert(!sigprocmask(SIG_BLOCK, &all, &old)); + BLOCK_SIGNALS; if (__threaded && _weaken(_pthread_onfork_prepare)) { _weaken(_pthread_onfork_prepare)(); } if (!IsWindows()) { - ad = sys_fork(); - ax = ad.ax; - dx = ad.dx; - if (IsXnu() && ax != -1) { - // eax always returned with childs pid - // edx is 0 for parent and 1 for child - ax &= dx - 1; - } + ax = sys_fork(); } else { - threaded = __threaded; ax = sys_fork_nt(dwCreationFlags); - if (threaded && !__threaded && _weaken(__enable_threads)) { - _weaken(__enable_threads)(); - } } if (!ax) { if (!IsWindows()) { @@ -81,7 +67,7 @@ int _fork(uint32_t dwCreationFlags) { } STRACE("fork() → %d% m", ax); } - _unassert(!sigprocmask(SIG_SETMASK, &old, 0)); + ALLOW_SIGNALS; return ax; } @@ -92,6 +78,7 @@ int _fork(uint32_t dwCreationFlags) { * @raise EAGAIN if `RLIMIT_NPROC` was exceeded or system lacked resources * @raise ENOMEM if we require more vespene gas * @asyncsignalsafe + * @threadsafe */ int fork(void) { return _fork(0); diff --git a/libc/runtime/hook.greg.c b/libc/runtime/hook.greg.c index 7f8ac9456..04145bfbb 100644 --- a/libc/runtime/hook.greg.c +++ b/libc/runtime/hook.greg.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/sections.internal.h" #include "libc/calls/struct/sigset.h" +#include "libc/runtime/morph.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.internal.h" @@ -45,13 +46,13 @@ privileged noinstrument noasan int __hook(void *ifunc, size_t i; char *p, *pe; intptr_t addr; + sigset_t mask; uint64_t code, mcode; - sigset_t mask, oldmask; intptr_t kMcount = (intptr_t)&mcount; intptr_t kProgramCodeStart = (intptr_t)_ereal; intptr_t kPrivilegedStart = (intptr_t)__privileged_addr; if (!symbols) return -1; - __morph_begin(); + __morph_begin(&mask); for (i = 0; i < symbols->count; ++i) { if (symbols->addr_base + symbols->symbols[i].x < kProgramCodeStart) { continue; @@ -112,6 +113,6 @@ privileged noinstrument noasan int __hook(void *ifunc, } } } - __morph_end(); + __morph_end(&mask); return 0; } diff --git a/libc/runtime/memtrack.internal.h b/libc/runtime/memtrack.internal.h index 80454f27f..1235e36a3 100644 --- a/libc/runtime/memtrack.internal.h +++ b/libc/runtime/memtrack.internal.h @@ -135,39 +135,39 @@ forceinline pureconst bool IsFixedFrame(int x) { } forceinline pureconst bool OverlapsImageSpace(const void *p, size_t n) { - const unsigned char *start, *ender; + const unsigned char *BegA, *EndA, *BegB, *EndB; if (n) { - start = p; - ender = start + (n - 1); - return ((_base <= start && start < _end) || - (_base <= ender && ender < _end) || - (start < _base && _end <= ender)); + BegA = p; + EndA = BegA + (n - 1); + BegB = _base; + EndB = _end - 1; + return MAX(BegA, BegB) < MIN(EndA, EndB); } else { return 0; } } forceinline pureconst bool OverlapsArenaSpace(const void *p, size_t n) { - intptr_t x, y; + intptr_t BegA, EndA, BegB, EndB; if (n) { - x = (intptr_t)p; - y = x + (n - 1); - return ((0x50000000 <= x && x <= 0x7ffdffff) || - (0x50000000 <= y && y <= 0x7ffdffff) || - (x < 0x50000000 && 0x7ffdffff < y)); + BegA = (intptr_t)p; + EndA = BegA + (n - 1); + BegB = 0x50000000; + EndB = 0x7ffdffff; + return MAX(BegA, BegB) < MIN(EndA, EndB); } else { return 0; } } forceinline pureconst bool OverlapsShadowSpace(const void *p, size_t n) { - intptr_t x, y; + intptr_t BegA, EndA, BegB, EndB; if (n) { - x = (intptr_t)p; - y = x + (n - 1); - return ((0x7fff0000 <= x && x <= 0x10007fffffff) || - (0x7fff0000 <= y && y <= 0x10007fffffff) || - (x < 0x7fff0000 && 0x10007fffffff < y)); + BegA = (intptr_t)p; + EndA = BegA + (n - 1); + BegB = 0x7fff0000; + EndB = 0x10007fffffff; + return MAX(BegA, BegB) < MIN(EndA, EndB); } else { return 0; } diff --git a/libc/runtime/morph.greg.c b/libc/runtime/morph.greg.c index 27fcf7930..5d86e4e55 100644 --- a/libc/runtime/morph.greg.c +++ b/libc/runtime/morph.greg.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #define ShouldUseMsabiAttribute() 1 #include "ape/sections.internal.h" +#include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/struct/sigset.h" #include "libc/dce.h" @@ -37,9 +38,6 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect; -static int64_t vector; -static sigset_t oldss; - static privileged void __morph_mprotect(void *addr, size_t size, int prot, int ntprot) { bool cf; @@ -58,7 +56,7 @@ static privileged void __morph_mprotect(void *addr, size_t size, int prot, _Exit(26); } #endif - if (ax) notpossible; + _npassert(!ax); } else { __imp_VirtualProtect(addr, size, ntprot, &op); } @@ -69,29 +67,26 @@ static privileged void __morph_mprotect(void *addr, size_t size, int prot, * * @return 0 on success, or -1 w/ errno */ -privileged void __morph_begin(void) { +privileged void __morph_begin(sigset_t *save) { int ax; bool cf; intptr_t dx; sigset_t ss = {{-1, -1}}; STRACE("__morph_begin()"); - if (!IsWindows()) { - if (!IsOpenbsd()) { - asm volatile("mov\t$8,%%r10d\n\t" - "syscall" - : "=a"(ax), "=d"(dx) - : "0"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(&ss), - "1"(&oldss) - : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); - if (ax) notpossible; - } else { - asm volatile(CFLAG_ASM("syscall") - : CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx) - : "1"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(-1u) - : "rcx", "r8", "r9", "r10", "r11", "memory"); - oldss.__bits[0] = ax & 0xffffffff; - if (cf) notpossible; - } + if (IsOpenbsd()) { + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx) + : "1"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(-1u) + : "rcx", "r8", "r9", "r10", "r11", "memory"); + save->__bits[0] = ax & 0xffffffff; + _npassert(!cf); + } else if (!IsWindows() && !IsMetal()) { + asm volatile("mov\t$8,%%r10d\n\t" + "syscall" + : "=a"(ax), "=d"(dx) + : "0"(__NR_sigprocmask), "D"(SIG_BLOCK), "S"(&ss), "1"(save) + : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); + _npassert(!ax); } __morph_mprotect(_base, __privileged_addr - _base, PROT_READ | PROT_WRITE, kNtPageWritecopy); @@ -100,29 +95,25 @@ privileged void __morph_begin(void) { /** * Begins code morphing executable. */ -privileged void __morph_end(void) { +privileged void __morph_end(sigset_t *save) { int ax; long dx; bool cf; __morph_mprotect(_base, __privileged_addr - _base, PROT_READ | PROT_EXEC, kNtPageExecuteRead); - if (!IsWindows()) { - if (!IsOpenbsd()) { - asm volatile("mov\t$8,%%r10d\n\t" - "syscall" - : "=a"(ax), "=d"(dx) - : "0"(__NR_sigprocmask), "D"(SIG_SETMASK), "S"(&oldss), - "1"(0) - : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); - if (ax) notpossible; - } else { - asm volatile(CFLAG_ASM("syscall") - : CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx) - : "1"(__NR_sigprocmask), "D"(SIG_SETMASK), - "S"(oldss.__bits[0]) - : "rcx", "r8", "r9", "r10", "r11", "memory"); - if (cf) notpossible; - } + if (IsOpenbsd()) { + asm volatile(CFLAG_ASM("syscall") + : CFLAG_CONSTRAINT(cf), "=a"(ax), "=d"(dx) + : "1"(__NR_sigprocmask), "D"(SIG_SETMASK), "S"(save->__bits[0]) + : "rcx", "r8", "r9", "r10", "r11", "memory"); + _npassert(!cf); + } else if (!IsWindows() && !IsMetal()) { + asm volatile("mov\t$8,%%r10d\n\t" + "syscall" + : "=a"(ax), "=d"(dx) + : "0"(__NR_sigprocmask), "D"(SIG_SETMASK), "S"(save), "1"(0) + : "rcx", "r8", "r9", "r10", "r11", "memory", "cc"); + _npassert(!ax); } STRACE("__morph_end()"); } diff --git a/libc/runtime/morph.h b/libc/runtime/morph.h new file mode 100644 index 000000000..813140317 --- /dev/null +++ b/libc/runtime/morph.h @@ -0,0 +1,12 @@ +#ifndef COSMOPOLITAN_LIBC_RUNTIME_MORPH_H_ +#define COSMOPOLITAN_LIBC_RUNTIME_MORPH_H_ +#include "libc/calls/struct/sigset.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +void __morph_begin(sigset_t *); +void __morph_end(sigset_t *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_RUNTIME_MORPH_H_ */ diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index 954556c1b..b6e59d0a5 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -57,7 +57,7 @@ int atfork(void *, void *) libcesque; int atexit(void (*)(void)) libcesque; char *getenv(const char *) nosideeffect libcesque; int putenv(char *) paramsnonnull(); -int setenv(const char *, const char *, int) paramsnonnull(); +int setenv(const char *, const char *, int); int unsetenv(const char *); int clearenv(void); void fpreset(void); @@ -100,8 +100,6 @@ char *GetInterpreterExecutableName(char *, size_t); void __printargs(const char *); void __paginate(int, const char *); int __arg_max(void); -void __morph_begin(void); -void __morph_end(void); void __print_maps(void); void __warn_if_powersave(void); const char *__describe_os(void); diff --git a/libc/runtime/straceinit.greg.c b/libc/runtime/straceinit.greg.c index e47bede51..8eeff8ef3 100644 --- a/libc/runtime/straceinit.greg.c +++ b/libc/runtime/straceinit.greg.c @@ -16,6 +16,8 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/safemacros.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/log/libfatal.internal.h" @@ -29,7 +31,7 @@ textstartup int __strace_init(int argc, char **argv, char **envp, long *auxv) { /* asan isn't initialized yet at runlevel 300 */ if (__intercept_flag(&argc, argv, "--strace") || __atoul(nulltoempty(__getenv(envp, "STRACE")))) { - ++__strace; + atomic_store_explicit(&__strace, 1, memory_order_relaxed); } return (__argc = argc); } diff --git a/libc/runtime/vfork.S b/libc/runtime/vfork.S index 7bb00df25..c6febc9a1 100644 --- a/libc/runtime/vfork.S +++ b/libc/runtime/vfork.S @@ -20,7 +20,6 @@ #include "libc/intrin/strace.internal.h" #include "libc/thread/tls.h" #include "libc/macros.internal.h" -.privileged // Forks process without copying page tables. // @@ -32,37 +31,55 @@ // do anything in a vfork()'d child process. TLS memory must not // be disabled (it's enabled by default) since vfork() needs it. // +// What makes vfork() dangerous is that any changes to memory in +// the child process can happen in the parent too. The exception +// to this rule is `errno` which is saved/restored in a register +// by this implementation. However, despite its dangers, vfork's +// performance is irresistible and wonderous to behold. If safer +// code is desired, consider posix_spawn() which uses vfork(). +// // Do not make the assumption that the parent is suspended until -// the child terminates since this impl calls fork() on Windows, -// OpenBSD, and MacOS. +// the child terminates since this uses the raw fork system call +// on Windows, OpenBSD, and MacOS. In that case the process will +// proceed without blocking the parent; however, the `__vforked` +// variable is still set to true in the child, so lock functions +// won't do anything, and other functions shall change behavior. +// This ensures that, even if the operating system does not give +// us the performance of vfork(), we'll still be able to cut out +// the libc overhead, e.g. pthread_atfork(). // // @return pid of child process or 0 if forked process // @returnstwice // @threadsafe // @vforksafe -vfork: call __require_tls - xor %edi,%edi # dwCreationFlags -#ifdef __SANITIZE_ADDRESS__ - jmp fork # TODO: asan and vfork don't mix? - .endfn vfork,globl -#else -#if SupportsWindows() - testb IsWindows() - jnz sys_fork_nt # and we're lucky to have that -#endif -#if SupportsXnu() - testb IsXnu() - jnz fork -#endif -#if SupportsOpenbsd() - testb IsOpenbsd() - jnz fork # fake vfork plus msyscall issues -#endif +vfork: +#if !IsTiny() + push %rbp + mov %rsp,%rbp + .profilable + call __require_tls #ifdef SYSDEBUG ezlea .Llog,di call __stracef -#endif /* SYSDEBUG */ +#endif + pop %rbp +#endif mov %fs:0,%r9 # get thread information block +#if SupportsWindows() + testb IsWindows() + jnz 6f # and we're lucky to have that +#endif +#ifdef __SANITIZE_ADDRESS__ + jmp 5f # TODO: asan and vfork don't mix? +#endif +#if SupportsXnu() + testb IsXnu() + jnz 5f +#endif +#if SupportsOpenbsd() + testb IsOpenbsd() + jnz 5f # fake vfork plus msyscall issues +#endif mov 0x3c(%r9),%r8d # avoid question of @vforksafe errno pop %rsi # saves return address in a register mov __NR_vfork(%rip),%eax @@ -79,12 +96,35 @@ vfork: call __require_tls cmp $-4095,%eax jae systemfive_error mov %r8d,0x3c(%r9) # restore errno - test %eax,%eax - jnz .Lprnt -.Lchld: orb $TIB_FLAG_VFORKED,0x40(%r9) +1: test %eax,%eax + jnz .Lpar +.Lchi: orb $TIB_FLAG_VFORKED,0x40(%r9) ret -.Lprnt: andb $~TIB_FLAG_VFORKED,0x40(%r9) +.Lpar: andb $~TIB_FLAG_VFORKED,0x40(%r9) ret +#if SupportsXnu() || SupportsOpenbsd() || defined(__SANITIZE_ADDRESS__) +5: push %rbp + mov %rsp,%rbp + push %r9 + push %r9 + call sys_fork + pop %r9 + pop %r9 + pop %rbp + jmp 1b +#endif +#if SupportsWindows() +6: push %rbp + mov %rsp,%rbp + push %r9 + push %r9 + xor %edi,%edi # dwCreationFlags + call sys_fork_nt + pop %r9 + pop %r9 + pop %rbp + jmp 1b +#endif .endfn vfork,globl #ifdef SYSDEBUG @@ -93,5 +133,3 @@ vfork: call __require_tls .asciz "vfork()\n" .previous #endif /* DEBUGSYS */ - -#endif /* __SANITIZE_ADDRESS__ */ diff --git a/libc/sock/socketpair-nt.c b/libc/sock/socketpair-nt.c index e4ad00122..8ec12a741 100644 --- a/libc/sock/socketpair-nt.c +++ b/libc/sock/socketpair-nt.c @@ -95,8 +95,8 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) { rc = 0; } else { CloseHandle(hpipe); - __releasefd_unlocked(writer); - __releasefd_unlocked(reader); + __releasefd(writer); + __releasefd(reader); rc = -1; } diff --git a/libc/stdio/getrandom.c b/libc/stdio/getrandom.c index 7f1a8e6a6..9df21814a 100644 --- a/libc/stdio/getrandom.c +++ b/libc/stdio/getrandom.c @@ -129,8 +129,9 @@ ssize_t getrandom(void *p, size_t n, unsigned f) { static textstartup void getrandom_init(void) { int e, rc; - e = errno; struct sigaction sa, oldsa; + if (IsWindows()) return; + e = errno; if (IsBsd()) { sa.sa_flags = 0; sa.sa_handler = SIG_IGN; diff --git a/libc/str/str.h b/libc/str/str.h index 0ee028e81..ef1e03b14 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -3,6 +3,9 @@ #define INVALID_CODEPOINT 0xfffd +#define _tolower(u) (0040 | (u)) +#define _toupper(u) (0137 & (u)) + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ diff --git a/libc/sysv/calls/__sys_fork.s b/libc/sysv/calls/__sys_fork.s new file mode 100644 index 000000000..3a39a32f5 --- /dev/null +++ b/libc/sysv/calls/__sys_fork.s @@ -0,0 +1,2 @@ +.include "o/libc/sysv/macros.internal.inc" +.scall __sys_fork,0x0020020022002039,globl,hidden diff --git a/libc/sysv/calls/sys_fork.s b/libc/sysv/calls/sys_fork.s deleted file mode 100644 index 17643ee98..000000000 --- a/libc/sysv/calls/sys_fork.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/macros.internal.inc" -.scall sys_fork,0x0020020022002039,globl,hidden diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 68ce27681..813784629 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -385,9 +385,9 @@ syscon fcntl FWRITE 0 2 2 2 2 0 # wut is it syscon fcntl F_SETLK 6 8 12 8 8 6 # polyfilled nt syscon fcntl F_SETLKW 7 9 13 9 9 7 # polyfilled nt syscon fcntl F_GETLK 5 7 11 7 7 5 # polyfilled nt -syscon fcntl F_OFD_SETLK 37 90 -1 -1 -1 6 -syscon fcntl F_OFD_SETLKW 38 91 -1 -1 -1 7 -syscon fcntl F_OFD_GETLK 36 92 -1 -1 -1 5 +syscon fcntl F_OFD_SETLK 37 -1 -1 -1 -1 -1 # listed in xnu source code but marked private +syscon fcntl F_OFD_SETLKW 38 -1 -1 -1 -1 -1 # listed in xnu source code but marked private +syscon fcntl F_OFD_GETLK 36 -1 -1 -1 -1 -1 # listed in xnu source code but marked private syscon fcntl F_RDLCK 0 1 1 1 1 0 # polyfilled nt; bsd consensus syscon fcntl F_WRLCK 1 3 3 3 3 1 # polyfilled nt; bsd consensus syscon fcntl F_UNLCK 2 2 2 2 2 2 # polyfilled nt; unix consensus @@ -1340,8 +1340,8 @@ syscon compat TIOCSETAF 0x5404 0x80487416 0x802c7416 0x802c7416 0x802c74 syscon termios TIOCGWINSZ 0x5413 1074295912 1074295912 1074295912 1074295912 0x5413 # ioctl(tty, TIOCGWINSZ, struct winsize *argp); polyfilled NT syscon termios TIOCSWINSZ 0x5414 0x80087467 0x80087467 0x80087467 0x80087467 0x5414 # ioctl(tty, TIOCSWINSZ, const struct winsize *argp) (faked NT) syscon termios TIOCOUTQ 0x5411 0x40047473 0x40047473 0x40047473 0x40047473 0 # get # bytes queued in TTY's output buffer ioctl(tty, TIOCSWINSZ, const struct winsize *argp) -syscon termios TIOCGPGRP 0x540f 0x40047477 0x40047477 0x40047477 0x40047477 0 # get pgrp of tty -syscon termios TIOCSPGRP 0x5410 0x80047476 0x80047476 0x80047476 0x80047476 0 # set pgrp of tty +syscon termios TIOCGPGRP 0x540f 0x40047477 0x40047477 0x40047477 0x40047477 0 # tcgetpgrp(): get pgrp of tty +syscon termios TIOCSPGRP 0x5410 0x80047476 0x80047476 0x80047476 0x80047476 0 # tcsetpgrp(): set pgrp of tty syscon termios TIOCSBRK 0x5427 0x2000747b 0x2000747b 0x2000747b 0x2000747b 0 # set break bit syscon termios TIOCCBRK 0x5428 0x2000747a 0x2000747a 0x2000747a 0x2000747a 0 # boop syscon termios TIOCCONS 0x541d 0x80047462 0x80047462 0x80047462 0x80047462 0 # boop @@ -1356,8 +1356,7 @@ syscon termios TIOCGSID 0x5429 0x40047463 0x40047463 0x40047463 0x400474 syscon termios TABLDISC 0 0x3 0 0x3 0x3 0 # boop syscon termios SLIPDISC 0 0x4 0x4 0x4 0x4 0 # boop syscon termios PPPDISC 0 0x5 0x5 0x5 0x5 0 # boop -syscon termios TCSBRK 0x5409 0x2000745e 0x2000745e 0x2000745e 0x2000745e 0 # TIOCDRAIN on BSD -syscon termios TIOCDRAIN 0x5409 0x2000745e 0x2000745e 0x2000745e 0x2000745e 0 # TCSBRK on Linux +syscon termios TCSBRK 0x5409 0x2000745e 0x2000745e 0x2000745e 0x2000745e 0 # TIOCDRAIN on BSD; TIOCDRAIN on BSD syscon termios TIOCSTAT 0 0x20007465 0x20007465 0x20007465 0x20007465 0 # boop syscon termios TIOCSTART 0 0x2000746e 0x2000746e 0x2000746e 0x2000746e 0 # boop syscon termios TIOCCDTR 0 0x20007478 0x20007478 0x20007478 0x20007478 0 # clear data terminal ready @@ -2308,35 +2307,38 @@ syscon misc AIO_ALLDONE 2 1 3 0 0 0 syscon misc AIO_NOTCANCELED 1 4 2 0 0 0 syscon misc AIO_CANCELED 0 2 1 0 0 0 -syscon baud B0 0 0 0 0 0 0 # consensus -syscon baud B50 1 50 50 50 50 0 # bsd consensus -syscon baud B75 2 75 75 75 75 0 # bsd consensus -syscon baud B110 3 110 110 110 110 0 # bsd consensus -syscon baud B134 4 134 134 134 134 0 # bsd consensus -syscon baud B150 5 150 150 150 150 0 # bsd consensus -syscon baud B200 6 200 200 200 200 0 # bsd consensus -syscon baud B300 7 300 300 300 300 0 # bsd consensus -syscon baud B600 8 600 600 600 600 0 # bsd consensus -syscon baud B1200 9 0x04b0 0x04b0 0x04b0 0x04b0 0 # bsd consensus -syscon baud B1800 10 0x0708 0x0708 0x0708 0x0708 0 # bsd consensus -syscon baud B2400 11 0x0960 0x0960 0x0960 0x0960 0 # bsd consensus -syscon baud B4800 12 0x12c0 0x12c0 0x12c0 0x12c0 0 # bsd consensus -syscon baud B9600 13 0x2580 0x2580 0x2580 0x2580 0 # bsd consensus -syscon baud B19200 14 0x4b00 0x4b00 0x4b00 0x4b00 0 # bsd consensus -syscon baud B38400 15 0x9600 0x9600 0x9600 0x9600 0 # bsd consensus -syscon baud B57600 0x1001 0xe100 0xe100 0xe100 0xe100 0 # bsd consensus -syscon baud B115200 0x1002 0x01c200 0x01c200 0x01c200 0x01c200 0 # bsd consensus -syscon baud B230400 0x1003 0x038400 0x038400 0x038400 0x038400 0 # bsd consensus -syscon baud B500000 0x1005 0 0 0 0 0 -syscon baud B576000 0x1006 0 0 0 0 0 -syscon baud B1000000 0x1008 0 0 0 0 0 -syscon baud B1152000 0x1009 0 0 0 0 0 -syscon baud B1500000 0x100a 0 0 0 0 0 -syscon baud B2000000 0x100b 0 0 0 0 0 -syscon baud B2500000 0x100c 0 0 0 0 0 -syscon baud B3000000 0x100d 0 0 0 0 0 -syscon baud B3500000 0x100e 0 0 0 0 0 -syscon baud B4000000 0x100f 0 0 0 0 0 +# baud rates +# +# group name GNU/Systemd XNU's Not UNIX! FreeBSD OpenBSD NetBSD The New Technology Commentary +syscon baud B0 0 0 0 0 0 0 +syscon baud B50 1 50 50 50 50 50 +syscon baud B75 2 75 75 75 75 75 +syscon baud B110 3 110 110 110 110 110 +syscon baud B134 4 134 134 134 134 134 +syscon baud B150 5 150 150 150 150 150 +syscon baud B200 6 200 200 200 200 200 +syscon baud B300 7 300 300 300 300 300 +syscon baud B600 8 600 600 600 600 600 +syscon baud B1200 9 1200 1200 1200 1200 1200 +syscon baud B1800 10 1800 1800 1800 1800 1800 +syscon baud B2400 11 2400 2400 2400 2400 2400 +syscon baud B4800 12 4800 4800 4800 4800 4800 +syscon baud B9600 13 9600 9600 9600 9600 9600 +syscon baud B19200 14 19200 19200 19200 19200 19200 +syscon baud B38400 15 38400 38400 38400 38400 38400 +syscon baud B57600 0x1001 57600 57600 57600 57600 57600 +syscon baud B115200 0x1002 115200 115200 115200 115200 115200 +syscon baud B230400 0x1003 230400 230400 230400 230400 230400 +syscon baud B500000 0x1005 500000 500000 500000 500000 500000 +syscon baud B576000 0x1006 576000 576000 576000 576000 576000 +syscon baud B1000000 0x1008 1000000 1000000 1000000 1000000 1000000 +syscon baud B1152000 0x1009 1152000 1152000 1152000 1152000 1152000 +syscon baud B1500000 0x100a 1500000 1500000 1500000 1500000 1500000 +syscon baud B2000000 0x100b 2000000 2000000 2000000 2000000 2000000 +syscon baud B2500000 0x100c 2500000 2500000 2500000 2500000 2500000 +syscon baud B3000000 0x100d 3000000 3000000 3000000 3000000 3000000 +syscon baud B3500000 0x100e 3500000 3500000 3500000 3500000 3500000 +syscon baud B4000000 0x100f 4000000 4000000 4000000 4000000 4000000 syscon misc WEOF 0xffffffff -1 -1 -1 -1 -1 # bsd consensus (win fake) syscon misc _LINUX_QUOTA_VERSION 2 0 0 0 0 0 diff --git a/libc/sysv/consts/B1000000.s b/libc/sysv/consts/B1000000.s index 6474b8cf8..c6c8ae9ac 100644 --- a/libc/sysv/consts/B1000000.s +++ b/libc/sysv/consts/B1000000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B1000000,0x1008,0,0,0,0,0 +.syscon baud,B1000000,0x1008,1000000,1000000,1000000,1000000,1000000 diff --git a/libc/sysv/consts/B110.s b/libc/sysv/consts/B110.s index 9f5767b2c..a49b1ede3 100644 --- a/libc/sysv/consts/B110.s +++ b/libc/sysv/consts/B110.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B110,3,110,110,110,110,0 +.syscon baud,B110,3,110,110,110,110,110 diff --git a/libc/sysv/consts/B115200.s b/libc/sysv/consts/B115200.s index 3eec71805..1cd70186d 100644 --- a/libc/sysv/consts/B115200.s +++ b/libc/sysv/consts/B115200.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B115200,0x1002,0x01c200,0x01c200,0x01c200,0x01c200,0 +.syscon baud,B115200,0x1002,115200,115200,115200,115200,115200 diff --git a/libc/sysv/consts/B1152000.s b/libc/sysv/consts/B1152000.s index e55931ba0..21b17045d 100644 --- a/libc/sysv/consts/B1152000.s +++ b/libc/sysv/consts/B1152000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B1152000,0x1009,0,0,0,0,0 +.syscon baud,B1152000,0x1009,1152000,1152000,1152000,1152000,1152000 diff --git a/libc/sysv/consts/B1200.s b/libc/sysv/consts/B1200.s index e8cc5bc4a..c4ae5e9db 100644 --- a/libc/sysv/consts/B1200.s +++ b/libc/sysv/consts/B1200.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B1200,9,0x04b0,0x04b0,0x04b0,0x04b0,0 +.syscon baud,B1200,9,1200,1200,1200,1200,1200 diff --git a/libc/sysv/consts/B134.s b/libc/sysv/consts/B134.s index dd93e5564..3150b1fc3 100644 --- a/libc/sysv/consts/B134.s +++ b/libc/sysv/consts/B134.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B134,4,134,134,134,134,0 +.syscon baud,B134,4,134,134,134,134,134 diff --git a/libc/sysv/consts/B150.s b/libc/sysv/consts/B150.s index 88b9db955..5468176ab 100644 --- a/libc/sysv/consts/B150.s +++ b/libc/sysv/consts/B150.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B150,5,150,150,150,150,0 +.syscon baud,B150,5,150,150,150,150,150 diff --git a/libc/sysv/consts/B1500000.s b/libc/sysv/consts/B1500000.s index 462d2321f..9bb71dc25 100644 --- a/libc/sysv/consts/B1500000.s +++ b/libc/sysv/consts/B1500000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B1500000,0x100a,0,0,0,0,0 +.syscon baud,B1500000,0x100a,1500000,1500000,1500000,1500000,1500000 diff --git a/libc/sysv/consts/B1800.s b/libc/sysv/consts/B1800.s index 297dfdb3f..6b17873f6 100644 --- a/libc/sysv/consts/B1800.s +++ b/libc/sysv/consts/B1800.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B1800,10,0x0708,0x0708,0x0708,0x0708,0 +.syscon baud,B1800,10,1800,1800,1800,1800,1800 diff --git a/libc/sysv/consts/B19200.s b/libc/sysv/consts/B19200.s index 3768ccb53..2be3aa9ea 100644 --- a/libc/sysv/consts/B19200.s +++ b/libc/sysv/consts/B19200.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B19200,14,0x4b00,0x4b00,0x4b00,0x4b00,0 +.syscon baud,B19200,14,19200,19200,19200,19200,19200 diff --git a/libc/sysv/consts/B200.s b/libc/sysv/consts/B200.s index 6254fac46..942b23f81 100644 --- a/libc/sysv/consts/B200.s +++ b/libc/sysv/consts/B200.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B200,6,200,200,200,200,0 +.syscon baud,B200,6,200,200,200,200,200 diff --git a/libc/sysv/consts/B2000000.s b/libc/sysv/consts/B2000000.s index 67a3bd0a8..9a2266176 100644 --- a/libc/sysv/consts/B2000000.s +++ b/libc/sysv/consts/B2000000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B2000000,0x100b,0,0,0,0,0 +.syscon baud,B2000000,0x100b,2000000,2000000,2000000,2000000,2000000 diff --git a/libc/sysv/consts/B230400.s b/libc/sysv/consts/B230400.s index 5a78ac7dd..e1d0a7756 100644 --- a/libc/sysv/consts/B230400.s +++ b/libc/sysv/consts/B230400.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B230400,0x1003,0x038400,0x038400,0x038400,0x038400,0 +.syscon baud,B230400,0x1003,230400,230400,230400,230400,230400 diff --git a/libc/sysv/consts/B2400.s b/libc/sysv/consts/B2400.s index a426d7adf..b5e704ddc 100644 --- a/libc/sysv/consts/B2400.s +++ b/libc/sysv/consts/B2400.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B2400,11,0x0960,0x0960,0x0960,0x0960,0 +.syscon baud,B2400,11,2400,2400,2400,2400,2400 diff --git a/libc/sysv/consts/B2500000.s b/libc/sysv/consts/B2500000.s index 95adf3939..1acb797b4 100644 --- a/libc/sysv/consts/B2500000.s +++ b/libc/sysv/consts/B2500000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B2500000,0x100c,0,0,0,0,0 +.syscon baud,B2500000,0x100c,2500000,2500000,2500000,2500000,2500000 diff --git a/libc/sysv/consts/B300.s b/libc/sysv/consts/B300.s index 99e550c2b..296d10fe4 100644 --- a/libc/sysv/consts/B300.s +++ b/libc/sysv/consts/B300.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B300,7,300,300,300,300,0 +.syscon baud,B300,7,300,300,300,300,300 diff --git a/libc/sysv/consts/B3000000.s b/libc/sysv/consts/B3000000.s index 47cb70c5f..6d4f71572 100644 --- a/libc/sysv/consts/B3000000.s +++ b/libc/sysv/consts/B3000000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B3000000,0x100d,0,0,0,0,0 +.syscon baud,B3000000,0x100d,3000000,3000000,3000000,3000000,3000000 diff --git a/libc/sysv/consts/B3500000.s b/libc/sysv/consts/B3500000.s index 4a8dc2cb4..a86415186 100644 --- a/libc/sysv/consts/B3500000.s +++ b/libc/sysv/consts/B3500000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B3500000,0x100e,0,0,0,0,0 +.syscon baud,B3500000,0x100e,3500000,3500000,3500000,3500000,3500000 diff --git a/libc/sysv/consts/B38400.s b/libc/sysv/consts/B38400.s index 739187e9e..0d10b17ea 100644 --- a/libc/sysv/consts/B38400.s +++ b/libc/sysv/consts/B38400.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B38400,15,0x9600,0x9600,0x9600,0x9600,0 +.syscon baud,B38400,15,38400,38400,38400,38400,38400 diff --git a/libc/sysv/consts/B4000000.s b/libc/sysv/consts/B4000000.s index 009a89244..f83baaba0 100644 --- a/libc/sysv/consts/B4000000.s +++ b/libc/sysv/consts/B4000000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B4000000,0x100f,0,0,0,0,0 +.syscon baud,B4000000,0x100f,4000000,4000000,4000000,4000000,4000000 diff --git a/libc/sysv/consts/B4800.s b/libc/sysv/consts/B4800.s index 495c51443..99134b6b1 100644 --- a/libc/sysv/consts/B4800.s +++ b/libc/sysv/consts/B4800.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B4800,12,0x12c0,0x12c0,0x12c0,0x12c0,0 +.syscon baud,B4800,12,4800,4800,4800,4800,4800 diff --git a/libc/sysv/consts/B50.s b/libc/sysv/consts/B50.s index d0d3f6cd2..c010ae819 100644 --- a/libc/sysv/consts/B50.s +++ b/libc/sysv/consts/B50.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B50,1,50,50,50,50,0 +.syscon baud,B50,1,50,50,50,50,50 diff --git a/libc/sysv/consts/B500000.s b/libc/sysv/consts/B500000.s index 21a399ebe..d5c0dba10 100644 --- a/libc/sysv/consts/B500000.s +++ b/libc/sysv/consts/B500000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B500000,0x1005,0,0,0,0,0 +.syscon baud,B500000,0x1005,500000,500000,500000,500000,500000 diff --git a/libc/sysv/consts/B57600.s b/libc/sysv/consts/B57600.s index e72b66f6b..77306126a 100644 --- a/libc/sysv/consts/B57600.s +++ b/libc/sysv/consts/B57600.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B57600,0x1001,0xe100,0xe100,0xe100,0xe100,0 +.syscon baud,B57600,0x1001,57600,57600,57600,57600,57600 diff --git a/libc/sysv/consts/B576000.s b/libc/sysv/consts/B576000.s index 125379cf8..5b46b3c65 100644 --- a/libc/sysv/consts/B576000.s +++ b/libc/sysv/consts/B576000.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B576000,0x1006,0,0,0,0,0 +.syscon baud,B576000,0x1006,576000,576000,576000,576000,576000 diff --git a/libc/sysv/consts/B600.s b/libc/sysv/consts/B600.s index e7ba226d7..9443aeb53 100644 --- a/libc/sysv/consts/B600.s +++ b/libc/sysv/consts/B600.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B600,8,600,600,600,600,0 +.syscon baud,B600,8,600,600,600,600,600 diff --git a/libc/sysv/consts/B75.s b/libc/sysv/consts/B75.s index c5c2a1f0e..61f34b2cf 100644 --- a/libc/sysv/consts/B75.s +++ b/libc/sysv/consts/B75.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B75,2,75,75,75,75,0 +.syscon baud,B75,2,75,75,75,75,75 diff --git a/libc/sysv/consts/B9600.s b/libc/sysv/consts/B9600.s index c981da0bc..adacf6629 100644 --- a/libc/sysv/consts/B9600.s +++ b/libc/sysv/consts/B9600.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon baud,B9600,13,0x2580,0x2580,0x2580,0x2580,0 +.syscon baud,B9600,13,9600,9600,9600,9600,9600 diff --git a/libc/sysv/consts/F_OFD_GETLK.s b/libc/sysv/consts/F_OFD_GETLK.s index 61ac76389..592a7b9e1 100644 --- a/libc/sysv/consts/F_OFD_GETLK.s +++ b/libc/sysv/consts/F_OFD_GETLK.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon fcntl,F_OFD_GETLK,36,92,-1,-1,-1,5 +.syscon fcntl,F_OFD_GETLK,36,-1,-1,-1,-1,-1 diff --git a/libc/sysv/consts/F_OFD_SETLK.s b/libc/sysv/consts/F_OFD_SETLK.s index 8330b673d..d9b807b95 100644 --- a/libc/sysv/consts/F_OFD_SETLK.s +++ b/libc/sysv/consts/F_OFD_SETLK.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon fcntl,F_OFD_SETLK,37,90,-1,-1,-1,6 +.syscon fcntl,F_OFD_SETLK,37,-1,-1,-1,-1,-1 diff --git a/libc/sysv/consts/F_OFD_SETLKW.s b/libc/sysv/consts/F_OFD_SETLKW.s index c59ff381e..c4a6d7a70 100644 --- a/libc/sysv/consts/F_OFD_SETLKW.s +++ b/libc/sysv/consts/F_OFD_SETLKW.s @@ -1,2 +1,2 @@ .include "o/libc/sysv/consts/syscon.internal.inc" -.syscon fcntl,F_OFD_SETLKW,38,91,-1,-1,-1,7 +.syscon fcntl,F_OFD_SETLKW,38,-1,-1,-1,-1,-1 diff --git a/libc/sysv/consts/TIOCDRAIN.s b/libc/sysv/consts/TIOCDRAIN.s deleted file mode 100644 index 21f9cd93c..000000000 --- a/libc/sysv/consts/TIOCDRAIN.s +++ /dev/null @@ -1,2 +0,0 @@ -.include "o/libc/sysv/consts/syscon.internal.inc" -.syscon termios,TIOCDRAIN,0x5409,0x2000745e,0x2000745e,0x2000745e,0x2000745e,0 diff --git a/libc/sysv/consts/clone.h b/libc/sysv/consts/clone.h index f88c712ff..d70e15e0e 100644 --- a/libc/sysv/consts/clone.h +++ b/libc/sysv/consts/clone.h @@ -1,10 +1,12 @@ #ifndef COSMOPOLITAN_LIBC_SYSV_CONSTS_CLONE_H_ #define COSMOPOLITAN_LIBC_SYSV_CONSTS_CLONE_H_ +#define CSIGNAL 0x000000ff #define CLONE_VM 0x00000100 #define CLONE_FS 0x00000200 #define CLONE_FILES 0x00000400 #define CLONE_SIGHAND 0x00000800 +#define CLONE_PIDFD 0x00001000 #define CLONE_PTRACE 0x00002000 #define CLONE_VFORK 0x00004000 #define CLONE_PARENT 0x00008000 @@ -17,6 +19,12 @@ #define CLONE_DETACHED 0x00400000 #define CLONE_UNTRACED 0x00800000 #define CLONE_CHILD_SETTID 0x01000000 -#define CLONE_STOPPED 0x02000000 +#define CLONE_NEWCGROUP 0x02000000 +#define CLONE_NEWUTS 0x04000000 +#define CLONE_NEWIPC 0x08000000 +#define CLONE_NEWUSER 0x10000000 +#define CLONE_NEWPID 0x20000000 +#define CLONE_NEWNET 0x40000000 +#define CLONE_IO 0x80000000 #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_CLONE_H_ */ diff --git a/libc/sysv/consts/f.h b/libc/sysv/consts/f.h index f5a48e19b..53ea7bddf 100644 --- a/libc/sysv/consts/f.h +++ b/libc/sysv/consts/f.h @@ -76,7 +76,7 @@ COSMOPOLITAN_C_END_ #define F_UNLCK SYMBOLIC(F_UNLCK) #define F_WRLCK SYMBOLIC(F_WRLCK) -/* avoid leading #ifdef astray */ +/* avoid leading #ifdef configurations astray */ /* #define F_FULLFSYNC SYMBOLIC(F_FULLFSYNC) */ /* #define F_BARRIERFSYNC SYMBOLIC(F_BARRIERFSYNC) */ /* #define F_OFD_GETLK SYMBOLIC(F_OFD_GETLK) */ diff --git a/libc/sysv/consts/termios.h b/libc/sysv/consts/termios.h index 7d3fe9d5d..b7cbd2080 100644 --- a/libc/sysv/consts/termios.h +++ b/libc/sysv/consts/termios.h @@ -111,7 +111,6 @@ extern const uint64_t TIOCCBRK; extern const uint64_t TIOCCDTR; extern const uint64_t TIOCCHKVERAUTH; extern const uint64_t TIOCCONS; -extern const uint64_t TIOCDRAIN; extern const uint64_t TIOCEXT; extern const uint64_t TIOCFLAG_CLOCAL; extern const uint64_t TIOCFLAG_MDMBUF; @@ -300,7 +299,6 @@ COSMOPOLITAN_C_END_ #define TIOCCDTR SYMBOLIC(TIOCCDTR) #define TIOCCHKVERAUTH SYMBOLIC(TIOCCHKVERAUTH) #define TIOCCONS SYMBOLIC(TIOCCONS) -#define TIOCDRAIN SYMBOLIC(TIOCDRAIN) #define TIOCEXT SYMBOLIC(TIOCEXT) #define TIOCFLAG_CLOCAL SYMBOLIC(TIOCFLAG_CLOCAL) #define TIOCFLAG_MDMBUF SYMBOLIC(TIOCFLAG_MDMBUF) diff --git a/libc/sysv/syscalls.sh b/libc/sysv/syscalls.sh index 39ae8d635..5db4784ca 100755 --- a/libc/sysv/syscalls.sh +++ b/libc/sysv/syscalls.sh @@ -92,7 +92,7 @@ scall __sys_getpeername 0x01f01f08d201f034 globl hidden scall __sys_socketpair 0x0870870872087035 globl hidden scall sys_setsockopt 0x0690690692069036 globl hidden scall sys_getsockopt 0x0760760762076037 globl hidden -scall sys_fork 0x0020020022002039 globl hidden # xnu needs eax&=~-edx bc eax always holds pid and edx is 0 for parent and 1 for child +scall __sys_fork 0x0020020022002039 globl hidden # xnu needs eax&=~-edx bc eax always holds pid and edx is 0 for parent and 1 for child #scall vfork 0x042042042204203a globl # this syscall is from the moon so we implement it by hand in libc/runtime/vfork.S; probably removed from XNU in 12.5 scall sys_posix_spawn 0x1daffffff20f4fff globl hidden # good luck figuring out how xnu defines this scall __sys_execve 0x03b03b03b203b03b globl hidden diff --git a/libc/thread/freebsd.internal.h b/libc/thread/freebsd.internal.h index 53addff10..5134a108d 100644 --- a/libc/thread/freebsd.internal.h +++ b/libc/thread/freebsd.internal.h @@ -11,6 +11,9 @@ * maximum legal range is PID_MAX + 2 (100001) through INT_MAX */ +#define THR_SUSPENDED 1 +#define THR_SYSTEM_SCOPE 2 + #define UMTX_OP_WAIT 2 #define UMTX_OP_WAIT_UINT 11 #define UMTX_OP_WAIT_UINT_PRIVATE 15 diff --git a/libc/thread/posixthread.internal.h b/libc/thread/posixthread.internal.h index 95ed0400d..e4129c819 100644 --- a/libc/thread/posixthread.internal.h +++ b/libc/thread/posixthread.internal.h @@ -16,8 +16,6 @@ COSMOPOLITAN_C_START_ * @fileoverview Cosmopolitan POSIX Thread Internals */ -typedef void (*atfork_f)(void); - // LEGAL TRANSITIONS ┌──> TERMINATED // pthread_create ─┬─> JOINABLE ─┴┬─> DETACHED ──> ZOMBIE // └──────────────┘ @@ -80,6 +78,8 @@ struct PosixThread { struct _pthread_cleanup_buffer *cleanup; }; +typedef void (*atfork_f)(void); + extern struct PosixThread _pthread_main; hidden extern pthread_spinlock_t _pthread_keys_lock; hidden extern uint64_t _pthread_key_usage[(PTHREAD_KEYS_MAX + 63) / 64]; diff --git a/libc/thread/pthread_attr_getscope.c b/libc/thread/pthread_attr_getscope.c index ed264edf6..2556481fe 100644 --- a/libc/thread/pthread_attr_getscope.c +++ b/libc/thread/pthread_attr_getscope.c @@ -18,7 +18,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/thread/thread.h" -int pthread_attr_getscope(const pthread_attr_t *a, int *x) { - *x = a->__scope; +/** + * Gets contention scope attribute. + * + * @return 0 on success, or errno on error + */ +int pthread_attr_getscope(const pthread_attr_t *attr, int *contentionscope) { + *contentionscope = attr->__contentionscope; return 0; } diff --git a/libc/thread/pthread_attr_setscope.c b/libc/thread/pthread_attr_setscope.c index e4aaaf856..648198523 100644 --- a/libc/thread/pthread_attr_setscope.c +++ b/libc/thread/pthread_attr_setscope.c @@ -16,9 +16,34 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/dce.h" +#include "libc/errno.h" #include "libc/thread/thread.h" -int pthread_attr_setscope(pthread_attr_t *a, int x) { - a->__scope = x; - return 0; +/** + * Sets contention scope attribute. + * + * @param contentionscope may be one of: + * - `PTHREAD_SCOPE_SYSTEM` to fight the system for resources + * - `PTHREAD_SCOPE_PROCESS` to fight familiar threads for resources + * @return 0 on success, or errno on error + * @raise ENOTSUP if `contentionscope` isn't supported on host OS + * @raise EINVAL if `contentionscope` was invalid + */ +int pthread_attr_setscope(pthread_attr_t *attr, int contentionscope) { + switch (contentionscope) { + case PTHREAD_SCOPE_SYSTEM: + attr->__contentionscope = contentionscope; + return 0; + case PTHREAD_SCOPE_PROCESS: + // Linux almost certainly doesn't support thread scoping + // FreeBSD has THR_SYSTEM_SCOPE but it's not implemented + // OpenBSD pthreads claims support but really ignores it + // NetBSD pthreads claims support, but really ignores it + // XNU sources appear to make no mention of thread scope + // WIN32 documentation says nothing about thread scoping + return ENOTSUP; + default: + return EINVAL; + } } diff --git a/libc/thread/pthread_cleanup_pop.c b/libc/thread/pthread_cleanup_pop.c index b9efb902e..f8bb41e0f 100644 --- a/libc/thread/pthread_cleanup_pop.c +++ b/libc/thread/pthread_cleanup_pop.c @@ -19,10 +19,11 @@ #include "libc/assert.h" #include "libc/thread/posixthread.internal.h" #include "libc/thread/thread.h" +#include "libc/thread/tls.h" void _pthread_cleanup_pop(struct _pthread_cleanup_buffer *cb, int execute) { - struct PosixThread *pt = (struct PosixThread *)pthread_self(); - _npassert(cb == pt->cleanup); + struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread; + _unassert(cb == pt->cleanup); pt->cleanup = cb->__prev; if (execute) { cb->__routine(cb->__arg); diff --git a/libc/thread/pthread_cleanup_push.c b/libc/thread/pthread_cleanup_push.c index 125d30c18..dd5428968 100644 --- a/libc/thread/pthread_cleanup_push.c +++ b/libc/thread/pthread_cleanup_push.c @@ -21,7 +21,7 @@ void _pthread_cleanup_push(struct _pthread_cleanup_buffer *cb, void (*routine)(void *), void *arg) { - struct PosixThread *pt = (struct PosixThread *)pthread_self(); + struct PosixThread *pt = (struct PosixThread *)__get_tls()->tib_pthread; cb->__routine = routine; cb->__arg = arg; cb->__prev = pt->cleanup; diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index 27429b8f9..84df4dde6 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/sched-sysv.internal.h" #include "libc/calls/state.internal.h" @@ -97,7 +98,7 @@ static int PosixThread(void *arg, int tid) { // set long jump handler so pthread_exit can bring control back here if (!setjmp(pt->exiter)) { __get_tls()->tib_pthread = (pthread_t)pt; - sigprocmask(SIG_SETMASK, &pt->sigmask, 0); + _sigsetmask(pt->sigmask); pt->rc = pt->start_routine(pt->arg); // ensure pthread_cleanup_pop(), and pthread_exit() popped cleanup _npassert(!pt->cleanup); @@ -325,12 +326,10 @@ static errno_t pthread_create_impl(pthread_t *thread, errno_t pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) { errno_t rc; - sigset_t blocksigs, oldsigs; __require_tls(); _pthread_zombies_decimate(); - sigfillset(&blocksigs); - _npassert(!sigprocmask(SIG_SETMASK, &blocksigs, &oldsigs)); - rc = pthread_create_impl(thread, attr, start_routine, arg, oldsigs); - _npassert(!sigprocmask(SIG_SETMASK, &oldsigs, 0)); + BLOCK_SIGNALS; + rc = pthread_create_impl(thread, attr, start_routine, arg, _SigMask); + ALLOW_SIGNALS; return rc; } diff --git a/libc/thread/pthread_kill.c b/libc/thread/pthread_kill.c index e6f0ca18a..61e39716d 100644 --- a/libc/thread/pthread_kill.c +++ b/libc/thread/pthread_kill.c @@ -29,6 +29,7 @@ * @raise EAGAIN if `RLIMIT_SIGPENDING` was exceeded * @raise EINVAL if `tid` or `sig` was invalid * @raise EPERM if permission was denied + * @asyncsignalsafe */ int pthread_kill(pthread_t thread, int sig) { int rc, e = errno; diff --git a/libc/thread/pthread_mutexattr_gettype.c b/libc/thread/pthread_mutexattr_gettype.c index 6138568e9..5d1e9a8d7 100644 --- a/libc/thread/pthread_mutexattr_gettype.c +++ b/libc/thread/pthread_mutexattr_gettype.c @@ -23,7 +23,6 @@ * * @param type will be set to one of these on success * - `PTHREAD_MUTEX_NORMAL` - * - `PTHREAD_MUTEX_DEFAULT` * - `PTHREAD_MUTEX_RECURSIVE` * - `PTHREAD_MUTEX_ERRORCHECK` * @return 0 on success, or error on failure diff --git a/libc/thread/pthread_self.c b/libc/thread/pthread_self.c index 2152a3482..250cc0391 100644 --- a/libc/thread/pthread_self.c +++ b/libc/thread/pthread_self.c @@ -22,6 +22,7 @@ /** * Returns current POSIX thread. + * @asyncsignalsafe */ pthread_t pthread_self(void) { pthread_t t; diff --git a/libc/thread/pthread_sigmask.c b/libc/thread/pthread_sigmask.c index 78a1dd185..03093c8dc 100644 --- a/libc/thread/pthread_sigmask.c +++ b/libc/thread/pthread_sigmask.c @@ -23,6 +23,7 @@ * Examines and/or changes blocked signals on current thread. * * @return 0 on success, or errno on error + * @asyncsignalsafe */ int pthread_sigmask(int how, const sigset_t *set, sigset_t *old) { int rc, e = errno; diff --git a/libc/thread/thread.h b/libc/thread/thread.h index 6f2d5eda9..b38171bbc 100644 --- a/libc/thread/thread.h +++ b/libc/thread/thread.h @@ -31,6 +31,9 @@ #define PTHREAD_CANCEL_DEFERRED 0 #define PTHREAD_CANCEL_ASYNCHRONOUS 1 +#define PTHREAD_SCOPE_SYSTEM 0 +#define PTHREAD_SCOPE_PROCESS 1 + #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -89,7 +92,7 @@ typedef struct pthread_attr_s { char __inheritsched; int __schedparam; int __schedpolicy; - int __scope; + int __contentionscope; unsigned __guardsize; unsigned __stacksize; char *__stackaddr; diff --git a/libc/time/iso8601.c b/libc/time/iso8601.c index 8a9b40d60..ad1113a95 100644 --- a/libc/time/iso8601.c +++ b/libc/time/iso8601.c @@ -16,37 +16,78 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" /** * Converts timestamp to ISO-8601 formatted string. + * + * For example: + * + * char *GetZuluTime(void) { + * char *p; + * struct tm tm; + * struct timespec ts; + * static _Thread_local int64_t last; + * static _Thread_local char str[21]; + * clock_gettime(0, &ts); + * if (ts.tv_sec != last) { + * gmtime_r(&ts.tv_sec, &tm); + * last = ts.tv_sec; + * stpcpy(iso8601(str, &tm), "Z"); + * } + * return str; + * } + * + * Will return timestamps that look like: + * + * 2020-01-01T13:14:15Z + * + * The generated timestamp is always exactly 19 characters long. It is + * also always nul terminated too. + * + * This function defines no failure conditions. The results of passing + * timestamp values outside of the appropriate intervals is undefined. + * + * @param p is buffer with at least 20 bytes + * @param tm has valid gmtime_r() or localtime_r() output + * @return pointer to nul terminator within `p`, cf. stpcpy() + * @see iso8601us() for microsecond timestamps + * @asyncsignalsafe + * @threadsafe */ char *iso8601(char p[hasatleast 20], struct tm *tm) { int x; x = tm->tm_year + 1900; + _unassert(0 <= x && x <= 9999); *p++ = '0' + x / 1000; *p++ = '0' + x / 100 % 10; *p++ = '0' + x / 10 % 10; *p++ = '0' + x % 10; *p++ = '-'; x = tm->tm_mon + 1; + _unassert(1 <= x && x <= 12); *p++ = '0' + x / 10; *p++ = '0' + x % 10; *p++ = '-'; x = tm->tm_mday; + _unassert(1 <= x && x <= 31); *p++ = '0' + x / 10; *p++ = '0' + x % 10; *p++ = 'T'; x = tm->tm_hour; + _unassert(0 <= x && x <= 23); *p++ = '0' + x / 10; *p++ = '0' + x % 10; *p++ = ':'; x = tm->tm_min; + _unassert(0 <= x && x <= 59); *p++ = '0' + x / 10; *p++ = '0' + x % 10; *p++ = ':'; x = tm->tm_sec; + _unassert(0 <= x && x <= 60); *p++ = '0' + x / 10; *p++ = '0' + x % 10; *p = 0; diff --git a/libc/time/iso8601us.c b/libc/time/iso8601us.c new file mode 100644 index 000000000..e993be55b --- /dev/null +++ b/libc/time/iso8601us.c @@ -0,0 +1,80 @@ +/*-*- 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/assert.h" +#include "libc/time/struct/tm.h" +#include "libc/time/time.h" + +/** + * Converts timestamp to ISO-8601 formatted string. + * + * For example: + * + * char *GetTimestamp(void) { + * struct timespec ts; + * static _Thread_local struct tm tm; + * static _Thread_local int64_t last; + * static _Thread_local char str[27]; + * clock_gettime(0, &ts); + * if (ts.tv_sec != last) { + * localtime_r(&ts.tv_sec, &tm); + * last = ts.tv_sec; + * } + * iso8601us(str, &tm, ts.tv_nsec); + * return str; + * } + * + * Will return timestamps that look like: + * + * 2020-01-01T13:14:15.123456 + * + * The generated timestamp is always exactly 26 characters long. It is + * also always nul terminated too. + * + * This function defines no failure conditions. The results of passing + * timestamp, or nanosecond values outside their appropriate intervals + * is undefined. + * + * This goes 19x faster than strftime(). + * + * iso8601 l: 21c 7ns + * iso8601us l: 39c 13ns + * strftime l: 779c 252ns + * + * @param p is buffer with at least 20 bytes + * @param tm has valid gmtime_r() or localtime_r() output + * @param ns is nanosecond value associated with timestamp + * @return pointer to nul terminator within `p`, cf. stpcpy() + * @see iso8601() if microsecond resolution isn't desirable + * @asyncsignalsafe + * @threadsafe + */ +char *iso8601us(char p[hasatleast 27], struct tm *tm, long ns) { + int x; + p = iso8601(p, tm); + _unassert(0 <= ns && ns < 1000000000); + *p++ = '.'; + *p++ = '0' + ns / 100000000; + *p++ = '0' + ns / 10000000 % 10; + *p++ = '0' + ns / 1000000 % 10; + *p++ = '0' + ns / 100000 % 10; + *p++ = '0' + ns / 10000 % 10; + *p++ = '0' + ns / 1000 % 10; + *p = 0; + return p; +} diff --git a/libc/time/struct/tm.h b/libc/time/struct/tm.h index b8e411132..c682aec01 100644 --- a/libc/time/struct/tm.h +++ b/libc/time/struct/tm.h @@ -20,6 +20,7 @@ struct tm { char *asctime(const struct tm *); char *asctime_r(const struct tm *, char[hasatleast 26]); char *iso8601(char[hasatleast 20], struct tm *); +char *iso8601us(char[hasatleast 27], struct tm *, long); char *strptime(const char *, const char *, struct tm *); int64_t mktime(struct tm *); int64_t timegm(struct tm *); diff --git a/libc/time/time.mk b/libc/time/time.mk index 840d3a4ca..dec94b996 100644 --- a/libc/time/time.mk +++ b/libc/time/time.mk @@ -62,7 +62,10 @@ o/$(MODE)/libc/time/localtime.o: private \ -fdata-sections \ -ffunction-sections -o/$(MODE)/libc/time/now.o: private \ +# we need -O3 because: +# we're dividing by constants +o/$(MODE)/libc/time/iso8601.o \ +o/$(MODE)/libc/time/iso8601us.o: private \ OVERRIDE_CFLAGS += \ -O3 diff --git a/libc/time/xiso8601.c b/libc/time/xiso8601.c index 3b748de9b..f8b05d14f 100644 --- a/libc/time/xiso8601.c +++ b/libc/time/xiso8601.c @@ -27,9 +27,7 @@ #include "libc/time/time.h" #include "libc/x/x.h" -/** - * @fileoverview Timestamps in One True Format w/o toil. - */ +// TODO(jart): DELETE static char *xiso8601_impl(struct timespec *opt_ts, int sswidth) { char *p; diff --git a/libc/intrin/intrin.h b/libc/zipos/.cosmo old mode 100755 new mode 100644 similarity index 100% rename from libc/intrin/intrin.h rename to libc/zipos/.cosmo diff --git a/libc/zipos/open.c b/libc/zipos/open.c index 333fe9814..943fda568 100644 --- a/libc/zipos/open.c +++ b/libc/zipos/open.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/calls/blocksigs.internal.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" @@ -188,13 +189,14 @@ static int __zipos_load(struct Zipos *zipos, size_t cf, unsigned flags, * Loads compressed file from αcτµαlly pδrταblε εxεcµταblε object store. * * @param uri is obtained via __zipos_parseuri() - * @asyncsignalsafe (todo) + * @asyncsignalsafe * @threadsafe */ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) { int rc; ssize_t cf; struct Zipos *zipos; + BLOCK_SIGNALS; if ((flags & O_ACCMODE) == O_RDONLY) { if ((zipos = __zipos_get())) { if ((cf = __zipos_find(zipos, name)) != -1) { @@ -208,5 +210,6 @@ int __zipos_open(const struct ZiposUri *name, unsigned flags, int mode) { } else { rc = einval(); } + ALLOW_SIGNALS; return rc; } diff --git a/libc/zipos/zipos.S b/libc/zipos/zipos.S index acb603e11..71cb51978 100644 --- a/libc/zipos/zipos.S +++ b/libc/zipos/zipos.S @@ -19,8 +19,8 @@ #include "libc/macros.internal.h" // static_yoink this symbol for open(/zip/...) support. - zip_uri_support = 0 - .globl zip_uri_support + zipos = 0 + .globl zipos .yoink __zip_end .yoink __zip_start @@ -34,3 +34,10 @@ .yoink __zipos_read .yoink __zipos_stat .yoink __zipos_notat + +// TODO(jart): why does corruption happen when zip has no assets? + .yoink .cosmo + +// deprecated: use STATIC_YOINK("zipos") + zip_uri_support = 0 + .globl zip_uri_support diff --git a/libc/zipos/zipos.mk b/libc/zipos/zipos.mk index a7d234281..0632b4a0d 100644 --- a/libc/zipos/zipos.mk +++ b/libc/zipos/zipos.mk @@ -20,7 +20,8 @@ LIBC_ZIPOS_A_SRCS = \ LIBC_ZIPOS_A_OBJS = \ $(LIBC_ZIPOS_A_SRCS_S:%.S=o/$(MODE)/%.o) \ - $(LIBC_ZIPOS_A_SRCS_C:%.c=o/$(MODE)/%.o) + $(LIBC_ZIPOS_A_SRCS_C:%.c=o/$(MODE)/%.o) \ + o/$(MODE)/libc/zipos/.cosmo.zip.o LIBC_ZIPOS_A_CHECKS = \ $(LIBC_ZIPOS_A).pkg \ @@ -51,6 +52,10 @@ $(LIBC_ZIPOS_A).pkg: \ $(LIBC_ZIPOS_A_OBJS) \ $(foreach zipos,$(LIBC_ZIPOS_A_DIRECTDEPS),$($(zipos)_A).pkg) +o/$(MODE)/libc/zipos/.cosmo.zip.o: private \ + ZIPOBJ_FLAGS += \ + -B + LIBC_ZIPOS_LIBS = $(foreach zipos,$(LIBC_ZIPOS_ARTIFACTS),$($(zipos))) LIBC_ZIPOS_SRCS = $(foreach zipos,$(LIBC_ZIPOS_ARTIFACTS),$($(zipos)_SRCS)) LIBC_ZIPOS_HDRS = $(foreach zipos,$(LIBC_ZIPOS_ARTIFACTS),$($(zipos)_HDRS)) diff --git a/net/http/http.h b/net/http/http.h index eb5e8d9d0..de3ab7e09 100644 --- a/net/http/http.h +++ b/net/http/http.h @@ -214,6 +214,7 @@ int ParseForwarded(const char *, size_t, uint32_t *, uint16_t *); bool IsMimeType(const char *, size_t, const char *); ssize_t Unchunk(struct HttpUnchunker *, char *, size_t, size_t *); const char *FindContentType(const char *, size_t); +bool IsNoCompressExt(const char *, size_t); char *FoldHeader(struct HttpMessage *, char *, int, size_t *); COSMOPOLITAN_C_END_ diff --git a/net/http/http.mk b/net/http/http.mk index 470883106..fbe09425c 100644 --- a/net/http/http.mk +++ b/net/http/http.mk @@ -57,6 +57,8 @@ o/$(MODE)/net/http/istestnetip.o: private \ OVERRIDE_CFLAGS += \ -Os +# we need -O3 because: +# we're dividing by constants o/$(MODE)/net/http/formathttpdatetime.o: private\ OVERRIDE_CFLAGS += \ -O3 diff --git a/tool/build/lib/isnocompressext.c b/net/http/isnocompressext.c similarity index 94% rename from tool/build/lib/isnocompressext.c rename to net/http/isnocompressext.c index 3f1b60be8..12d49b19d 100644 --- a/tool/build/lib/isnocompressext.c +++ b/net/http/isnocompressext.c @@ -1,7 +1,7 @@ /*-*- 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 │ +│ 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 │ @@ -21,7 +21,7 @@ #include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/str/tab.internal.h" -#include "tool/build/lib/isnocompressext.h" +#include "net/http/http.h" static const char kNoCompressExts[][8] = { "bz2", // @@ -56,13 +56,14 @@ static bool BisectNoCompressExts(uint64_t ext) { } bool IsNoCompressExt(const char *p, size_t n) { - int c; + int c, i; uint64_t w; if (n == -1) n = p ? strlen(p) : 0; if (n) { - for (w = 0; n--;) { + for (i = w = 0; n--;) { c = p[n] & 255; if (c == '.') break; + if (++i > 8) return false; w <<= 8; w |= kToLower[c]; } diff --git a/net/turfwar/.init.lua b/net/turfwar/.init.lua new file mode 100644 index 000000000..deef6b15a --- /dev/null +++ b/net/turfwar/.init.lua @@ -0,0 +1,46 @@ +-- reverse proxy for turfwar + +ProgramPort(443) +ProgramTokenBucket() + +RELAY_HEADERS_TO_CLIENT = { + 'Access-Control-Allow-Origin', + 'Cache-Control', + 'Connection', + 'Content-Encoding', + 'Content-Type', + 'Last-Modified', + 'Referrer-Policy', + 'Vary', +} + +function OnWorkerStart() + assert(unix.unveil(nil, nil)) + assert(unix.pledge("stdio inet", nil, unix.PLEDGE_PENALTY_RETURN_EPERM)) +end + +function OnHttpRequest() + local status, headers, body = + Fetch('http://127.0.0.1' .. EscapePath(GetPath()), + {method = GetMethod(), + headers = { + ['Accept'] = GetHeader('Accept'), + ['Accept-Encoding'] = GetHeader('Accept-Encoding'), + ['CF-IPCountry'] = GetHeader('CF-IPCountry'), + ['If-Modified-Since'] = GetHeader('If-Modified-Since'), + ['Referer'] = GetHeader('Referer'), + ['Sec-CH-UA-Platform'] = GetHeader('Sec-CH-UA-Platform'), + ['User-Agent'] = GetHeader('User-Agent'), + ['X-Forwarded-For'] = FormatIp(GetClientAddr())}}) + if status then + SetStatus(status) + for k,v in pairs(RELAY_HEADERS_TO_CLIENT) do + SetHeader(v, headers[v]) + end + Write(body) + else + err = headers + Log(kLogError, "proxy failed %s" % {err}) + ServeError(503) + end +end diff --git a/libc/intrin/getenv.greg.c b/net/turfwar/blackhole.c similarity index 59% rename from libc/intrin/getenv.greg.c rename to net/turfwar/blackhole.c index b552280c4..9f38ca0d1 100644 --- a/libc/intrin/getenv.greg.c +++ b/net/turfwar/blackhole.c @@ -1,7 +1,7 @@ /*-*- 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 2020 Justine Alexandra Roberts Tunney │ +│ 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 │ @@ -16,62 +16,55 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/intrin/strace.internal.h" -#include "libc/dce.h" +#include "libc/calls/calls.h" #include "libc/errno.h" -#include "libc/log/libfatal.internal.h" +#include "libc/intrin/bits.h" +#include "libc/intrin/kprintf.h" #include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" +#include "libc/sock/struct/sockaddr.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/ex.h" +#include "libc/sysv/consts/sock.h" +#include "net/http/http.h" -forceinline int Identity(int c) { - return c; -} +int main(int argc, char *argv[]) { -forceinline int ToUpper(int c) { - return 'a' <= c && c <= 'z' ? c - ('a' - 'A') : c; -} + if (argc < 2) { + kprintf("usage: blackhole IP...\n"); + return EX_USAGE; + } -forceinline char *GetEnv(const char *s, int xlat(int)) { - char **p; - size_t i, j; - if ((p = environ)) { - for (i = 0; p[i]; ++i) { - for (j = 0;; ++j) { - if (!s[j]) { - if (p[i][j] == '=') { - return p[i] + j + 1; - } - break; - } - if (xlat(s[j]) != xlat(p[i][j])) { - break; - } + int fd; + struct sockaddr_un addr = { + AF_UNIX, + "/var/run/blackhole.sock", + }; + if ((fd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) { + kprintf("error: socket(AF_UNIX) failed: %s\n", strerror(errno)); + return 3; + } + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + kprintf("error: connect(%#s) failed: %s\n", addr.sun_path, strerror(errno)); + return 4; + } + + int rc = 0; + for (int i = 1; i < argc; ++i) { + int64_t ip; + char buf[4]; + if ((ip = ParseIp(argv[i], -1)) != -1) { + WRITE32BE(buf, ip); + if (write(fd, buf, 4) == -1) { + kprintf("error: write() failed: %s\n", strerror(errno)); + rc |= 2; } + } else { + kprintf("error: bad ipv4 address: %s\n", argv[i]); + rc |= 1; } } - return 0; -} -/** - * Returns value of environment variable, or NULL if not found. - * - * Environment variables can store empty string on Unix but not Windows. - * - * @note should not be used after __cxa_finalize() is called - */ -char *getenv(const char *s) { - char *r; - if (!s) return 0; - if (!IsWindows()) { - r = GetEnv(s, Identity); - } else { - r = GetEnv(s, ToUpper); - } -#if SYSDEBUG - if (!(s[0] == 'T' && s[1] == 'Z' && !s[2])) { - // TODO(jart): memoize TZ or something - STRACE("getenv(%#s) → %#s", s, r); - } -#endif - return r; + return rc; } diff --git a/net/turfwar/blackholed.c b/net/turfwar/blackholed.c new file mode 100644 index 000000000..7a3e09fda --- /dev/null +++ b/net/turfwar/blackholed.c @@ -0,0 +1,448 @@ +/*-*- 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/assert.h" +#include "libc/calls/blocksigs.internal.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" +#include "libc/calls/struct/timespec.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/fmt/conv.h" +#include "libc/fmt/itoa.h" +#include "libc/intrin/bits.h" +#include "libc/intrin/kprintf.h" +#include "libc/intrin/midpoint.h" +#include "libc/intrin/safemacros.internal.h" +#include "libc/mem/mem.h" +#include "libc/mem/sortedints.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" +#include "libc/sock/struct/sockaddr.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/clock.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/ok.h" +#include "libc/sysv/consts/sa.h" +#include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/sock.h" +#include "libc/sysv/consts/timer.h" +#include "libc/time/struct/tm.h" +#include "third_party/getopt/getopt.h" +#include "third_party/musl/passwd.h" + +#define LOG(FMT, ...) \ + kprintf("%s %s:%d] " FMT "\n", GetTimestamp(), __FILE__, __LINE__, \ + ##__VA_ARGS__) + +#define DEFAULT_LOGNAME "/var/log/blackhole.log" +#define DEFAULT_PIDNAME "/var/run/blackhole.pid" +#define DEFAULT_SOCKNAME "/var/run/blackhole.sock" +#define GETOPTS "L:S:P:M:G:dh" +#define USAGE \ + "\ +Usage: blackholed [-hdLPSMG]\n\ + -h help\n\ + -d daemonize\n\ + -L PATH log file name (default: " DEFAULT_LOGNAME ")\n\ + -P PATH pid file name (default: " DEFAULT_PIDNAME ")\n\ + -S PATH socket file name (default: " DEFAULT_SOCKNAME ")\n\ + -M MODE socket mode bits (default: 0777)\n\ + -G GROUP socket group name or gid (default: n/a)\n\ + --assimilate change executable header to native format\n\ + --ftrace function call tracing\n\ + --strace system call tracing\n\ +\n\ +Usage:\n\ + sudo blackholed -d # run daemon\n\ + blackhole 1.2.3.4 # anyone can securely ban ips\n\ +\n\ +Protocol:\n\ + Send a 4 byte datagram to the unix socket file containing\n\ + the IPv4 address you want banned encoded using big endian\n\ + a.k.a. network byte order. We ignore these ips: 0.0.0.0/8\n\ + and 127.0.0.0/8 so sending 0 to the socket is a good test\n" + +#define LINUX_DOCS \ + "\n\ +Linux Requirements:\n\ + sudo modprobe ip_tables\n\ + sudo echo ip_tables >>/etc/modules\n\ +\n\ +Administration Notes:\n\ + This program inserts IP bans into iptables raw prerouting, so\n\ + the kernel won't track the TCP connections of threat actors.\n\ + If you restart this program, then you should run\n\ + sudo iptables -t raw -F\n\ + to clear the IP blocks. It's a good idea to have a cron job\n\ + restart this daemon and clear the raw table daily. Use the\n\ + sudo iptables -t raw -L -vn\n\ + command to list the IP addresses that have been blocked.\n\ +\n" + +#define BSD_DOCS \ + "\n\ +BSD Requirements:\n\ + kldload pf\n\ + echo 'table <badhosts> persist' >>/etc/pf.conf\n\ + echo 'block on em0 from <badhosts> to any' >>/etc/pf.conf\n\ + echo 'pf_enable=\"YES\"' >>/etc/rc.conf\n\ + echo 'pf_rules=\"/etc/pf.conf\"' >>/etc/rc.conf\n\ + /etc/rc.d/pf start\n\ + pfctl -t badhosts -T add 1.2.3.4\n\ + pfctl -t badhosts -T show\n\ +\n\ +Administration Notes:\n\ + If you restart this program, then you should run\n\ + pfctl -t badhosts -T flush\n\ + to clear the IP blocks. It's a good idea to have a cron job\n\ + restart this daemon and clear the raw table daily. Use the\n\ + pfctl -t badhosts -T show\n\ + command to list the IP addresses that have been blocked.\n\ +\n\ +" + +int g_logfd; +int g_sockmode; +bool g_daemonize; +uint32_t *g_myips; +const char *g_group; +const char *g_pfctl; +const char *g_logname; +const char *g_pidname; +const char *g_sockname; +const char *g_iptables; +sig_atomic_t g_shutdown; +struct SortedInts g_blocked; + +static wontreturn void ShowUsage(int fd, int rc) { + write(fd, USAGE, sizeof(USAGE) - 1); + if (IsLinux()) write(fd, LINUX_DOCS, sizeof(LINUX_DOCS) - 1); + if (IsBsd()) write(fd, BSD_DOCS, sizeof(BSD_DOCS) - 1); + _Exit(rc); +} + +static void GetOpts(int argc, char *argv[]) { + int opt; + g_sockmode = 0777; + g_pidname = DEFAULT_PIDNAME; + g_logname = DEFAULT_LOGNAME; + g_sockname = DEFAULT_SOCKNAME; + while ((opt = getopt(argc, argv, GETOPTS)) != -1) { + switch (opt) { + case 'd': + g_daemonize = true; + break; + case 'S': + g_sockname = optarg; + break; + case 'L': + g_logname = emptytonull(optarg); + break; + case 'P': + g_pidname = emptytonull(optarg); + break; + case 'G': + g_group = emptytonull(optarg); + break; + case 'M': + g_sockmode = strtol(optarg, 0, 8) & 0777; + break; + case 'h': + ShowUsage(1, 0); + default: + ShowUsage(2, 64); + } + } +} + +char *GetTimestamp(void) { + struct timespec ts; + static struct tm tm; + static int64_t last; + static char str[27]; + clock_gettime(0, &ts); + if (ts.tv_sec != last) { + localtime_r(&ts.tv_sec, &tm); + last = ts.tv_sec; + } + iso8601us(str, &tm, ts.tv_nsec); + return str; +} + +void OnTerm(int sig) { + char tmp[15]; + LOG("got %s", strsignal_r(sig, tmp)); + g_shutdown = sig; +} + +char *FormatIp(uint32_t ip) { + static char ipbuf[16]; + ksnprintf(ipbuf, sizeof(ipbuf), "%hhu.%hhu.%hhu.%hhu", ip >> 24, ip >> 16, + ip >> 8, ip); + return ipbuf; +} + +void BlockIp(uint32_t ip) { + if (!vfork()) { + if (g_iptables) { + execve(g_iptables, + (char *const[]){ + "iptables", // + "-t", "raw", // + "-I", "PREROUTING", // + "-s", FormatIp(ip), // + "-j", "DROP", // + 0, // + }, + (char *const[]){0}); + } else if (g_pfctl) { + execve(g_pfctl, + (char *const[]){ + "pfctl", // + "-t", "badhosts", // + "-T", "add", // + FormatIp(ip), // + 0, // + }, + (char *const[]){0}); + } + _Exit(127); + } +} + +void RequireRoot(void) { + if (geteuid()) { + kprintf("error: need root privileges\n"); + ShowUsage(2, 2); + } +} + +void ListenForTerm(void) { + struct sigaction sa = {.sa_handler = OnTerm}; + _npassert(!sigaction(SIGTERM, &sa, 0)); + _npassert(!sigaction(SIGHUP, &sa, 0)); + _npassert(!sigaction(SIGINT, &sa, 0)); +} + +void AutomaticallyHarvestZombies(void) { + struct sigaction sa = {.sa_handler = SIG_IGN, .sa_flags = SA_NOCLDWAIT}; + _npassert(!sigaction(SIGCHLD, &sa, 0)); +} + +void FindFirewall(void) { + if (!access("/sbin/iptables", X_OK)) { + g_iptables = "/sbin/iptables"; + } else if (!access("/usr/sbin/iptables", X_OK)) { + g_iptables = "/usr/sbin/iptables"; + } else if (!access("/sbin/pfctl", X_OK)) { + g_pfctl = "/sbin/pfctl"; + } else { + kprintf("error: could not find `iptables` or `pfctl` command\n"); + ShowUsage(2, 3); + } + errno = 0; +} + +void OpenLog(void) { + if (!g_logname) return; + if (!g_daemonize) return; + if ((g_logfd = open(g_logname, O_WRONLY | O_APPEND | O_CREAT, 0644)) == -1) { + kprintf("error: open(%#s) failed: %s\n", g_logname, strerror(errno)); + ShowUsage(2, 5); + } +} + +void Daemonize(void) { + if (g_daemonize && daemon(false, false)) { + kprintf("error: daemon() failed: %s\n", strerror(errno)); + ShowUsage(2, 4); + } +} + +void UseLog(void) { + _npassert(dup2(g_logfd, 2) == 2); + if (g_logfd != 2) { + _npassert(!close(g_logfd)); + } +} + +void UninterruptibleSleep(int ms) { + struct timespec ts = + _timespec_add(_timespec_real(), _timespec_frommillis(ms)); + while (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, 0)) errno = 0; +} + +void Unlink(const char *path) { + if (!path) return; + if (!unlink(path)) { + LOG("deleted %s", path); + } else { + if (errno != ENOENT) { + LOG("error: unlink(%#s) failed: %s", path, strerror(errno)); + } + errno = 0; + } +} + +void WritePid(void) { + ssize_t rc; + int fd, pid; + char buf[12] = {0}; + if (!g_pidname) return; + if ((fd = open(g_pidname, O_RDWR | O_CREAT, 0644)) == -1) { + LOG("error: open(%#s) failed: %s", g_pidname, strerror(errno)); + _Exit(4); + } + _npassert((rc = pread(fd, buf, 11, 0)) != -1); + if (rc) { + pid = atoi(buf); + LOG("killing old blackholed process %d", pid); + if (!kill(pid, SIGTERM)) { + UninterruptibleSleep(100); + if (kill(pid, SIGKILL)) { + if (errno != ESRCH) { + LOG("kill -KILL %s failed: %s", pid, strerror(errno)); + } + errno = 0; + } + } else { + if (errno != ESRCH) { + LOG("kill -TERM %d failed: %s", pid, strerror(errno)); + } + errno = 0; + } + } + FormatInt32(buf, getpid()); + _npassert(!ftruncate(fd, 0)); + _npassert((rc = pwrite(fd, buf, strlen(buf), 0)) == strlen(buf)); + _npassert(!close(fd)); +} + +bool IsMyIp(uint32_t ip) { + uint32_t *p; + for (p = g_myips; *p; ++p) { + if (ip == *p) { + return true; + } + } + return false; +} + +int main(int argc, char *argv[]) { + + if (closefrom(3)) + for (int i = 3; i < 256; ++i) // + close(i); + + GetOpts(argc, argv); + RequireRoot(); + FindFirewall(); + OpenLog(); + Daemonize(); + UseLog(); + WritePid(); + Unlink(g_sockname); + + if (!(g_myips = GetHostIps())) { + LOG("failed to get host network interface addresses: %s", strerror(errno)); + } + + int server; + struct sockaddr_un addr = {AF_UNIX}; + strlcpy(addr.sun_path, g_sockname, sizeof(addr.sun_path)); + if ((server = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) { + LOG("error: socket(AF_UNIX) failed: %s", strerror(errno)); + _Exit(3); + } + if (bind(server, (struct sockaddr *)&addr, sizeof(addr))) { + LOG("error: bind(%s) failed: %s", g_sockname, strerror(errno)); + _Exit(4); + } + if (chmod(g_sockname, g_sockmode)) { + LOG("error: chmod(%s, %o) failed: %s", g_sockname, g_sockmode, + strerror(errno)); + _Exit(5); + } + if (g_group) { + int gid; + struct group *g; + if (isdigit(*g_group)) { + gid = atoi(g_group); + } else if ((g = getgrnam(g_group))) { + gid = g->gr_gid; + } else { + LOG("error: group %s not found: %s", g_group, strerror(errno)); + _Exit(6); + } + if (chown(g_sockname, -1, gid)) { + LOG("error: chmod(%s, -1, %o) failed: %s", g_sockname, g_sockmode, + strerror(errno)); + _Exit(7); + } + } + + AutomaticallyHarvestZombies(); + ListenForTerm(); + + while (!g_shutdown) { + ssize_t rc; + uint32_t ip; + char msg[16]; + + if (!(rc = read(server, msg, sizeof(msg)))) { + LOG("error: impossible eof", strerror(errno)); + _Exit(6); + } else if (rc == -1) { + if (errno == EINTR) { + errno = 0; + continue; + } + LOG("error: read failed: %s", strerror(errno)); + continue; + } else if (rc != 4) { + LOG("error: read unexpected size of %ld: %s", rc, strerror(errno)); + continue; + } + + BLOCK_SIGNALS; + + if ((ip = READ32BE(msg))) { + if (IsMyIp(ip) || // nics + (ip & 0xff000000) == 0x00000000 || // 0.0.0.0/8 + (ip & 0xff000000) == 0x7f000000) { // 127.0.0.0/8 + LOG("won't block %s", FormatIp(ip)); + } else if (InsertInt(&g_blocked, ip, true)) { + BlockIp(ip); + LOG("blocked %s", FormatIp(ip)); + } else { + LOG("already blocked %s", FormatIp(ip)); + } + } + + ALLOW_SIGNALS; + } + + if (g_shutdown == SIGINT || // + g_shutdown == SIGHUP) { + Unlink(g_sockname); + Unlink(g_pidname); + } +} diff --git a/net/turfwar/blackholed.sh b/net/turfwar/blackholed.sh new file mode 100755 index 000000000..053defc1a --- /dev/null +++ b/net/turfwar/blackholed.sh @@ -0,0 +1,5 @@ +#!/bin/sh +make -j16 o//net/turfwar/blackholed.elf && +sudo chown root o//net/turfwar/blackholed.elf && +sudo chmod 06755 o//net/turfwar/blackholed.elf && +exec o//net/turfwar/blackholed.elf diff --git a/net/turfwar/turfwar.c b/net/turfwar/turfwar.c index 70f87c5fa..43a71df8c 100644 --- a/net/turfwar/turfwar.c +++ b/net/turfwar/turfwar.c @@ -90,7 +90,7 @@ #define PORT 8080 // default server listening port #define CPUS 64 // number of cpus to actually use -#define WORKERS 100 // size of http client thread pool +#define WORKERS 500 // 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 @@ -108,7 +108,7 @@ #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 TB_CIDR 24 // token bucket cidr specificity #define SOCK_MAX 100 // max length of socket queue #define MSG_BUF 512 // small response lookaside @@ -233,6 +233,14 @@ struct Asset { char lastmodified[32]; }; +struct Blackhole { + struct sockaddr_un addr; + int fd; +} g_blackhole = {{ + AF_UNIX, + "/var/run/blackhole.sock", +}}; + // cli flags bool g_integrity; bool g_daemonize; @@ -248,6 +256,7 @@ atomic_int g_connections; nsync_note g_shutdown[3]; // whitebox metrics +atomic_long g_banned; atomic_long g_accepts; atomic_long g_dbfails; atomic_long g_proxied; @@ -401,6 +410,19 @@ int DbPrepare(sqlite3 *db, sqlite3_stmt **stmt, const char *sql) { return sqlite3_prepare_v2(db, sql, -1, stmt, 0); } +bool Blackhole(uint32_t ip) { + char buf[4]; + WRITE32BE(buf, ip); + if (sendto(g_blackhole.fd, buf, 4, 0, (struct sockaddr *)&g_blackhole.addr, + sizeof(g_blackhole.addr)) == 4) { + return true; + } else { + kprintf("error: sendto(/var/run/blackhole.sock) failed: %s\n", + strerror(errno)); + return false; + } +} + // validates name registration validity bool IsValidNick(const char *s, size_t n) { size_t i; @@ -674,10 +696,11 @@ void ServeStatusz(int client, char *outbuf) { g_messages / MAX(1, _timespec_sub(now, g_started).tv_sec)); p = Statusz(p, "started", g_started.tv_sec); p = Statusz(p, "now", now.tv_sec); + p = Statusz(p, "messages", g_messages); p = Statusz(p, "connections", g_connections); + p = Statusz(p, "banned", g_banned); p = Statusz(p, "workers", g_workers); p = Statusz(p, "accepts", g_accepts); - p = Statusz(p, "messages", g_messages); p = Statusz(p, "dbfails", g_dbfails); p = Statusz(p, "proxied", g_proxied); p = Statusz(p, "memfails", g_memfails); @@ -759,8 +782,8 @@ void *ListenWorker(void *arg) { } if (!AddClient(&g_clients, &client, WaitFor(ACCEPT_DEADLINE_MS))) { ++g_rejected; - LOG("502 Accept Queue Full\n"); - Write(client.sock, "HTTP/1.1 502 Accept Queue Full\r\n" + LOG("503 Accept Queue Full\n"); + Write(client.sock, "HTTP/1.1 503 Accept Queue Full\r\n" "Content-Type: text/plain\r\n" "Connection: close\r\n" "\r\n" @@ -834,6 +857,9 @@ void *HttpWorker(void *arg) { ++g_messages; ++g_worker[id].msgcount; + ipv6 = false; + ip = clientip; + // get client address from frontend if (HasHeader(kHttpXForwardedFor)) { if (!IsLoopbackIp(clientip) && // @@ -861,17 +887,21 @@ void *HttpWorker(void *arg) { ip = clientip; ++g_unproxied; } + ksnprintf(ipbuf, sizeof(ipbuf), "%hhu.%hhu.%hhu.%hhu", ip >> 24, ip >> 16, ip >> 8, ip); - if ((tok = AcquireToken(g_tok.b, ip, TB_CIDR)) < 64) { - if (tok > 8) { + if (!ipv6 && (tok = AcquireToken(g_tok.b, ip, TB_CIDR)) < 32) { + if (tok > 4) { 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"); + } else { + Blackhole(ip); + ++g_banned; } ++g_ratelimits; break; @@ -1113,8 +1143,8 @@ void *HttpWorker(void *arg) { sent = write(client.sock, outbuf, p - outbuf); break; } else { - LOG("%s: 502 Claims Queue Full\n", ipbuf); - Write(client.sock, "HTTP/1.1 502 Claims Queue Full\r\n" + LOG("%s: 503 Claims Queue Full\n", ipbuf); + Write(client.sock, "HTTP/1.1 503 Claims Queue Full\r\n" "Content-Type: text/plain\r\n" "Connection: close\r\n" "\r\n" @@ -1793,6 +1823,15 @@ int main(int argc, char *argv[]) { CHECK_EQ(0, chdir("/opt/turfwar")); putenv("TMPDIR=/opt/turfwar/tmp"); + if ((g_blackhole.fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) { + kprintf("error: socket(AF_UNIX) failed: %s\n", strerror(errno)); + _Exit(3); + } + if (!Blackhole(0)) { + kprintf("redbean isn't able to protect your kernel from level 4 ddos\n"); + kprintf("please run the blackholed program, see https://justine.lol/\n"); + } + // the power to serve if (g_daemonize) { if (fork() > 0) _Exit(0); diff --git a/net/turfwar/turfwar.mk b/net/turfwar/turfwar.mk index 30aa0130b..b6e6a6e71 100644 --- a/net/turfwar/turfwar.mk +++ b/net/turfwar/turfwar.mk @@ -9,7 +9,8 @@ NET_TURFWAR_OBJS = \ $(NET_TURFWAR_SRCS:%.c=o/$(MODE)/%.o) NET_TURFWAR_COMS = \ - $(NET_TURFWAR_SRCS:%.c=o/$(MODE)/%.com) + $(NET_TURFWAR_SRCS:%.c=o/$(MODE)/%.com) \ + o/$(MODE)/net/turfwar/turfbean.com NET_TURFWAR_BINS = \ $(NET_TURFWAR_COMS) \ @@ -33,6 +34,7 @@ NET_TURFWAR_DIRECTDEPS = \ LIBC_X \ NET_HTTP \ THIRD_PARTY_GETOPT \ + THIRD_PARTY_MUSL \ THIRD_PARTY_NSYNC \ THIRD_PARTY_NSYNC_MEM \ THIRD_PARTY_SQLITE3 \ @@ -53,6 +55,30 @@ o/$(MODE)/net/turfwar/%.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) +o/$(MODE)/net/turfwar/turfbean.com.dbg: \ + $(TOOL_NET_DEPS) \ + o/$(MODE)/tool/net/redbean.o \ + $(TOOL_NET_REDBEAN_LUA_MODULES) \ + o/$(MODE)/tool/net/net.pkg \ + o/$(MODE)/net/turfwar/.init.lua.zip.o \ + o/$(MODE)/tool/net/redbean.png.zip.o \ + o/$(MODE)/tool/net/favicon.ico.zip.o \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/net/turfwar/turfbean.com: \ + o/$(MODE)/net/turfwar/turfbean.com.dbg \ + o/$(MODE)/third_party/zip/zip.com \ + o/$(MODE)/tool/build/symtab.com + @$(MAKE_OBJCOPY) + @$(MAKE_SYMTAB_CREATE) + @$(MAKE_SYMTAB_ZIP) + +o/$(MODE)/net/turfwar/.init.lua.zip.o: private \ + ZIPOBJ_FLAGS += \ + -B + $(NET_TURFWAR_OBJS): \ $(BUILD_FILES) \ net/turfwar/turfwar.mk diff --git a/test/libc/calls/access_test.c b/test/libc/calls/access_test.c index be795f213..fe110c374 100644 --- a/test/libc/calls/access_test.c +++ b/test/libc/calls/access_test.c @@ -36,7 +36,7 @@ void SetUpOnce(void) { TEST(access, efault) { ASSERT_SYS(EFAULT, -1, access(0, F_OK)); - if (IsWindows() && !IsAsan()) return; // not possible + if (IsWindows() || !IsAsan()) return; // not possible ASSERT_SYS(EFAULT, -1, access((void *)77, F_OK)); } diff --git a/test/libc/calls/chdir_test.c b/test/libc/calls/chdir_test.c index 9fb7f5043..d02f73bec 100644 --- a/test/libc/calls/chdir_test.c +++ b/test/libc/calls/chdir_test.c @@ -30,7 +30,7 @@ void SetUpOnce(void) { TEST(chdir, efault) { ASSERT_SYS(EFAULT, -1, chdir(0)); - if (IsWindows() && !IsAsan()) return; // not possible + if (IsWindows() || !IsAsan()) return; // not possible ASSERT_SYS(EFAULT, -1, chdir((void *)77)); } diff --git a/test/libc/calls/lock2_test.c b/test/libc/calls/lock2_test.c new file mode 100644 index 000000000..a472513d3 --- /dev/null +++ b/test/libc/calls/lock2_test.c @@ -0,0 +1,168 @@ +/*-*- 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/flock.h" +#include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" +#include "libc/errno.h" +#include "libc/sysv/consts/at.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sig.h" +#include "libc/testlib/subprocess.h" +#include "libc/testlib/testlib.h" + +char testlib_enable_tmp_setup_teardown; + +TEST(lock, wholeFile) { + char buf[512]; + ASSERT_SYS(0, 3, open("db", O_RDWR | O_CREAT | O_EXCL, 0644)); + ASSERT_SYS(0, 0, fcntl(3, F_SETLK, &(struct flock){.l_type = F_RDLCK})); + ASSERT_SYS(0, 0, fcntl(3, F_SETLK, &(struct flock){.l_type = F_UNLCK})); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(lock, testUpgradeFromReadToWriteLock) { + char buf[512]; + ASSERT_SYS(0, 3, open("db", O_RDWR | O_CREAT | O_EXCL, 0644)); + ASSERT_SYS(0, 0, + fcntl(3, F_SETLK, + &(struct flock){ + .l_type = F_RDLCK, + .l_start = 0x40000000, + .l_len = 1, + })); + ASSERT_SYS(0, 0, + fcntl(3, F_SETLK, + &(struct flock){ + .l_type = F_WRLCK, + .l_start = 0x40000000, + .l_len = 1, + })); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(lock, testUpgradeWriteToWriteLock) { + char buf[512]; + ASSERT_SYS(0, 3, open("db", O_RDWR | O_CREAT | O_EXCL, 0644)); + ASSERT_SYS(0, 0, + fcntl(3, F_SETLK, + &(struct flock){ + .l_type = F_WRLCK, + .l_start = 0x40000000, + .l_len = 1, + })); + ASSERT_SYS(0, 0, + fcntl(3, F_SETLK, + &(struct flock){ + .l_type = F_WRLCK, + .l_start = 0x40000000, + .l_len = 1, + })); + ASSERT_SYS(0, 0, close(3)); +} + +TEST(lock, unlockEverything_unlocksSmallerRanges) { + int fd, pi[2]; + char buf[8] = {0}; + ASSERT_SYS(0, 3, creat("db", 0644)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, pipe(pi)); + SPAWN(fork); + ASSERT_SYS(0, 5, open("db", O_RDWR)); + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 8, read(3, buf, 8)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, + fcntl(5, F_SETLK, + &(struct flock){ + .l_type = F_WRLCK, + .l_start = 0x40000000, + .l_len = 1, + })); + ASSERT_SYS(0, 0, + fcntl(5, F_SETLK, + &(struct flock){ + .l_type = F_WRLCK, + .l_start = 0x40000005, + .l_len = 1, + })); + ASSERT_SYS(0, 0, close(5)); + PARENT(); + ASSERT_SYS(0, 0, close(3)); + ASSERT_NE(-1, (fd = open("db", O_RDWR))); + ASSERT_SYS(0, 0, + fcntl(fd, F_SETLK, + &(struct flock){ + .l_type = F_WRLCK, + .l_start = 0x40000000, + .l_len = 1, + })); + ASSERT_SYS(0, 0, + fcntl(fd, F_SETLK, + &(struct flock){ + .l_type = F_WRLCK, + .l_start = 0x40000005, + .l_len = 1, + })); + ASSERT_SYS(0, 0, + fcntl(fd, F_SETLK, + &(struct flock){ + .l_type = F_UNLCK, + })); + ASSERT_SYS(0, 8, write(4, buf, 8)); + ASSERT_SYS(0, 0, close(4)); + WAIT(exit, 0); + ASSERT_SYS(0, 0, close(fd)); +} + +TEST(lock, close_releasesLocks) { + int fd, pi[2]; + char buf[8] = {0}; + ASSERT_SYS(0, 3, creat("db", 0644)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, pipe(pi)); + SPAWN(fork); + ASSERT_SYS(0, 5, open("db", O_RDWR)); + ASSERT_SYS(0, 0, close(4)); + ASSERT_SYS(0, 8, read(3, buf, 8)); + ASSERT_SYS(0, 0, close(3)); + ASSERT_SYS(0, 0, + fcntl(5, F_SETLK, + &(struct flock){ + .l_type = F_WRLCK, + .l_start = 0x40000000, + .l_len = 1, + })); + ASSERT_SYS(0, 0, close(5)); + PARENT(); + ASSERT_SYS(0, 0, close(3)); + ASSERT_NE(-1, (fd = open("db", O_RDWR))); + ASSERT_SYS(0, 0, + fcntl(fd, F_SETLK, + &(struct flock){ + .l_type = F_WRLCK, + .l_start = 0x40000000, + .l_len = 1, + })); + ASSERT_SYS(0, 0, close(fd)); + ASSERT_SYS(0, 8, write(4, buf, 8)); + ASSERT_SYS(0, 0, close(4)); + WAIT(exit, 0); +} diff --git a/test/libc/calls/lock_ofd_test.c b/test/libc/calls/lock_ofd_test.c new file mode 100644 index 000000000..05eda094b --- /dev/null +++ b/test/libc/calls/lock_ofd_test.c @@ -0,0 +1,207 @@ +/*-*- 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/flock.h" +#include "libc/calls/syscall-sysv.internal.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/intrin/kprintf.h" +#include "libc/macros.internal.h" +#include "libc/runtime/runtime.h" +#include "libc/sysv/consts/f.h" +#include "libc/sysv/consts/o.h" +#include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" + +/** + * @fileoverview Better Advisory Locks Test + */ + +#define PROCESSES 8 +#define THREADS 8 // <-- THIS +#define RATIO 3 +#define ITERATIONS 10 + +char testlib_enable_tmp_setup_teardown; + +_Thread_local const char *kind; + +bool SupportsOfdLocks(void) { + int e; + bool r; + if (!IsLinux()) return false; + // F_OFD_* was introduced in linux 3.15 + // getrandom() was introduced in linux 3.17 + // testing for getrandom() should be a sure thing w/o creating an fd + e = errno; + r = !sys_getrandom(0, 0, 0); + errno = e; + return r; +} + +void SetUp(void) { + if (!SupportsOfdLocks()) { + kprintf("ofd locks not supported on this system\n"); + exit(0); + } +} + +void Log(const char *fmt, ...) { +#if 0 + va_list va; + char b[512]; + int i, n = sizeof(b); + va_start(va, fmt); + i = ksnprintf(b, n, "%s pid=%d tid=%d ", kind, getpid(), gettid()); + i += kvsnprintf(b + i, MAX(0, n - i), fmt, va); + i += ksnprintf(b + i, MAX(0, n - i), "\n"); + write(2, b, MIN(i, n)); + va_end(va); +#endif +} + +void Lock(int fd, int type, long start, long len) { + int e; + struct flock lock = { + .l_type = type, + .l_whence = SEEK_SET, + .l_start = start, + .l_len = len, + }; + e = errno; + while (fcntl(fd, F_OFD_SETLK, &lock)) { + ASSERT_TRUE(errno == EAGAIN || errno == EACCES); + errno = e; + Log("flock spinning on %d", fd); + } +} + +void WriteLock(int fd, long start, long len) { + Lock(fd, F_WRLCK, start, len); + Log("acquired write lock on %d", fd); +} + +void ReadLock(int fd, long start, long len) { + Lock(fd, F_RDLCK, start, len); + Log("acquired read lock on %d", fd); +} + +void Unlock(int fd, long start, long len) { + Lock(fd, F_UNLCK, start, len); + Log("released lock on %d", fd); +} + +void *Reader(void *arg) { + int i, j, fd; + char buf[10]; + kind = arg; + ASSERT_NE(-1, (fd = open("db", O_RDONLY))); + for (j = 0; j < ITERATIONS; ++j) { + ReadLock(fd, 10, 10); + for (i = 0; i < 10; ++i) { + ASSERT_SYS(0, 1, pread(fd, buf + i, 1, 10 + i)); + ASSERT_EQ(buf[0], buf[i]); + } + Unlock(fd, 10, 10); + sched_yield(); + } + ASSERT_SYS(0, 0, close(fd)); + return 0; +} + +void *Writer(void *arg) { + int i, j, fd; + char buf[10]; + kind = arg; + ASSERT_NE(-1, (fd = open("db", O_RDWR))); + for (j = 0; j < ITERATIONS; ++j) { + WriteLock(fd, 10, 10); + for (i = 0; i < 10; ++i) { + ASSERT_SYS(0, 1, pread(fd, buf + i, 1, 10 + i)); + ASSERT_EQ(buf[0], buf[i]); + } + for (i = 0; i < 10; ++i) { + buf[i]++; + } + for (i = 0; i < 10; ++i) { + ASSERT_SYS(0, 1, pwrite(fd, buf + i, 1, 10 + i)); + } + Unlock(fd, 10, 10); + sched_yield(); + } + ASSERT_SYS(0, 0, close(fd)); + return 0; +} + +TEST(posixAdvisoryLocks, threadsAndProcessesFightingForLock) { + int i, rc, pid, fd, ws; + pthread_t th[THREADS + 1]; + + ASSERT_SYS(0, 3, creat("db", 0644)); + ASSERT_SYS(0, 0, ftruncate(3, 30)); + ASSERT_SYS(0, 0, close(3)); + + for (i = 0; i < THREADS; ++i) { + if (i % RATIO == 0) { + ASSERT_EQ(0, pthread_create(th + i, 0, Reader, "reader thread")); + } else { + ASSERT_EQ(0, pthread_create(th + i, 0, Writer, "writer thread")); + } + } + + for (i = 0; i < PROCESSES; ++i) { + ASSERT_NE(-1, (rc = fork())); + if (!rc) { + if (i % RATIO == 0) { + Writer("writer process"); + } else { + Reader("reader process"); + } + _Exit(0); + } + } + + ASSERT_NE(-1, (fd = open("db", O_RDWR))); + Lock(fd, F_WRLCK, 0, 10); + Lock(fd, F_WRLCK, 20, 10); + + for (i = 0; i < THREADS; ++i) { + ASSERT_EQ(0, pthread_join(th[i], 0)); + } + + kind = "main process"; + for (;;) { + int e = errno; + if ((pid = waitpid(0, &ws, 0)) != -1) { + if (WIFSIGNALED(ws)) { + Log("process %d terminated with %G", pid, WTERMSIG(ws)); + testlib_incrementfailed(); + } else if (WEXITSTATUS(ws)) { + Log("process %d exited with %d", pid, WEXITSTATUS(ws)); + testlib_incrementfailed(); + } + } else { + ASSERT_EQ(ECHILD, errno); + errno = e; + break; + } + } + + ASSERT_SYS(0, 0, close(fd)); +} diff --git a/test/libc/calls/lock_test.c b/test/libc/calls/lock_test.c index c9f454a82..6b59c2f0e 100644 --- a/test/libc/calls/lock_test.c +++ b/test/libc/calls/lock_test.c @@ -33,7 +33,7 @@ */ #define PROCESSES 8 -#define THREADS (IsWindows() ? 8 : 0) +#define THREADS 0 // <-- consider F_OFD_* locks #define RATIO 3 #define ITERATIONS 10 diff --git a/test/libc/calls/open_test.c b/test/libc/calls/open_test.c index b04c54f3c..30e49a227 100644 --- a/test/libc/calls/open_test.c +++ b/test/libc/calls/open_test.c @@ -35,7 +35,7 @@ void SetUpOnce(void) { TEST(open, efault) { ASSERT_SYS(EFAULT, -1, open(0, O_RDONLY)); - if (IsWindows() && !IsAsan()) return; // not possible + if (IsWindows() || !IsAsan()) return; // not possible ASSERT_SYS(EFAULT, -1, open((void *)77, O_RDONLY)); } diff --git a/test/libc/calls/sigaction_test.c b/test/libc/calls/sigaction_test.c index b5d4f79db..6a78cc085 100644 --- a/test/libc/calls/sigaction_test.c +++ b/test/libc/calls/sigaction_test.c @@ -20,6 +20,7 @@ #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" #include "libc/errno.h" @@ -175,3 +176,27 @@ TEST(sigaction, ignoringSignalDiscardsSignal) { ASSERT_EQ(0, sigprocmask(SIG_UNBLOCK, &blocked, NULL)); EXPECT_EQ(0, OnSignalCnt); } + +TEST(sigaction, autoZombieSlayer) { + if (IsWindows()) return; + int pid; + struct sigaction sa; + // make sure we're starting in expected state + ASSERT_SYS(0, 0, sigaction(SIGCHLD, 0, &sa)); + ASSERT_EQ(SIG_DFL, sa.sa_handler); + // verify child becomes zombie + ASSERT_NE(-1, (pid = fork())); + if (!pid) _Exit(0); + ASSERT_SYS(0, pid, wait(0)); + // enable automatic zombie slayer + sa.sa_handler = SIG_IGN; + sa.sa_flags = SA_NOCLDWAIT; // seems to be optional + sigemptyset(&sa.sa_mask); + ASSERT_SYS(0, 0, sigaction(SIGCHLD, &sa, &sa)); + // verify it works + ASSERT_NE(-1, (pid = fork())); + if (!pid) _Exit(0); + ASSERT_SYS(ECHILD, -1, wait(0)); + // clean up + ASSERT_SYS(0, 0, sigaction(SIGCHLD, &sa, 0)); +} diff --git a/test/libc/calls/test.mk b/test/libc/calls/test.mk index 47cfc25f8..61ea7438d 100644 --- a/test/libc/calls/test.mk +++ b/test/libc/calls/test.mk @@ -117,6 +117,12 @@ o/$(MODE)/test/libc/calls/fcntl_test.com.runs: \ o/$(MODE)/test/libc/calls/lock_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc flock +o/$(MODE)/test/libc/calls/lock2_test.com.runs: \ + private .PLEDGE = stdio rpath wpath cpath fattr proc flock + +o/$(MODE)/test/libc/calls/lock_ofd_test.com.runs: \ + private .PLEDGE = stdio rpath wpath cpath fattr proc flock + o/$(MODE)/test/libc/calls/openbsd_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc unveil diff --git a/test/libc/calls/unlinkat_test.c b/test/libc/calls/unlinkat_test.c index d19facdaf..f194b3a05 100644 --- a/test/libc/calls/unlinkat_test.c +++ b/test/libc/calls/unlinkat_test.c @@ -30,7 +30,7 @@ void SetUpOnce(void) { TEST(unlink, efault) { ASSERT_SYS(EFAULT, -1, unlink(0)); - if (IsWindows() && !IsAsan()) return; // not possible + if (IsWindows() || !IsAsan()) return; // not possible ASSERT_SYS(EFAULT, -1, unlink((void *)77)); } diff --git a/test/libc/intrin/describesigset_test.c b/test/libc/intrin/describesigset_test.c index 47f1e377c..66c35a302 100644 --- a/test/libc/intrin/describesigset_test.c +++ b/test/libc/intrin/describesigset_test.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/struct/sigset.h" #include "libc/calls/struct/sigset.internal.h" +#include "libc/dce.h" #include "libc/intrin/describeflags.internal.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/testlib.h" @@ -25,7 +26,7 @@ TEST(DescribeSigset, full) { sigset_t ss; sigfillset(&ss); - EXPECT_STREQ("~{}", DescribeSigset(0, &ss)); + EXPECT_STREQ("~{ABRT,KILL,STOP}", DescribeSigset(0, &ss)); } TEST(DescribeSigset, present) { @@ -41,5 +42,9 @@ TEST(DescribeSigset, absent) { sigfillset(&ss); sigdelset(&ss, SIGINT); sigdelset(&ss, SIGUSR1); - EXPECT_STREQ("~{INT,USR1}", DescribeSigset(0, &ss)); + if (IsBsd()) { + EXPECT_STREQ("~{INT,ABRT,KILL,STOP,USR1}", DescribeSigset(0, &ss)); + } else { + EXPECT_STREQ("~{INT,ABRT,KILL,USR1,STOP}", DescribeSigset(0, &ss)); + } } diff --git a/test/libc/intrin/lockipc_test.c b/test/libc/intrin/lockipc_test.c index 78402dc0d..daac676ff 100644 --- a/test/libc/intrin/lockipc_test.c +++ b/test/libc/intrin/lockipc_test.c @@ -19,7 +19,6 @@ #include "libc/calls/calls.h" #include "libc/errno.h" #include "libc/intrin/bits.h" -#include "libc/intrin/intrin.h" #include "libc/intrin/kprintf.h" #include "libc/macros.internal.h" #include "libc/runtime/runtime.h" diff --git a/test/libc/intrin/lockscale_test.c b/test/libc/intrin/lockscale_test.c index e082ad2d1..dbb465c81 100644 --- a/test/libc/intrin/lockscale_test.c +++ b/test/libc/intrin/lockscale_test.c @@ -19,7 +19,6 @@ #include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/struct/timespec.h" -#include "libc/intrin/intrin.h" #include "libc/mem/gc.h" #include "libc/mem/mem.h" #include "libc/stdio/stdio.h" diff --git a/test/libc/intrin/pthread_atfork_test.c b/test/libc/intrin/pthread_atfork_test.c index d15292c5e..0d7da5e2e 100644 --- a/test/libc/intrin/pthread_atfork_test.c +++ b/test/libc/intrin/pthread_atfork_test.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" -#include "libc/intrin/intrin.h" #include "libc/mem/gc.h" #include "libc/mem/mem.h" #include "libc/runtime/internal.h" diff --git a/test/libc/intrin/putenv_test.c b/test/libc/intrin/putenv_test.c new file mode 100644 index 000000000..f2a7c7d34 --- /dev/null +++ b/test/libc/intrin/putenv_test.c @@ -0,0 +1,69 @@ +/*-*- 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 Gavin Arthur Hayes │ +│ │ +│ 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/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/str/str.h" +#include "libc/testlib/testlib.h" + +TEST(putenv, test) { + EXPECT_EQ(0, clearenv()); + EXPECT_EQ(0, putenv("hi=there")); + EXPECT_STREQ("there", getenv("hi")); + EXPECT_EQ(0, clearenv()); + EXPECT_EQ(0, putenv("hi=theretwo")); + EXPECT_STREQ("theretwo", getenv("hi")); + EXPECT_EQ(0, clearenv()); + EXPECT_EQ(0, setenv("hi", "therethree", 0)); + EXPECT_STREQ("therethree", getenv("hi")); +} + +TEST(putenv, usesProvidedMemory) { + char kv[32] = "hi=hello"; + EXPECT_EQ(0, putenv(kv)); + EXPECT_STREQ("hello", getenv("hi")); + strcpy(kv, "hi=there"); + EXPECT_STREQ("there", getenv("hi")); + EXPECT_EQ(0, unsetenv("hi")); + EXPECT_EQ(0, unsetenv("hi")); + EXPECT_EQ(0, getenv("hi")); + EXPECT_EQ(0, clearenv()); +} + +TEST(putenv, keyonly) { + EXPECT_EQ(0, clearenv()); + EXPECT_EQ(0, putenv("hi")); + EXPECT_STREQ("", getenv("hi")); + EXPECT_STREQ("hi", environ[0]); + EXPECT_EQ(0, environ[1]); + EXPECT_EQ(0, unsetenv("hi")); + EXPECT_EQ(0, getenv("hi")); + EXPECT_EQ(0, environ[0]); + EXPECT_EQ(0, environ[1]); +} + +TEST(putenv, environ) { + char *s = strdup("hi=there"); + EXPECT_EQ(0, clearenv()); + EXPECT_EQ(0, putenv(s)); + EXPECT_EQ(0, putenv(s)); + EXPECT_EQ(s, environ[0]); + EXPECT_EQ(0, environ[1]); + EXPECT_EQ(0, clearenv()); + free(s); +} diff --git a/test/libc/log/backtrace_test.c b/test/libc/log/backtrace_test.c index d2a3de7a3..aa46bfe62 100644 --- a/test/libc/log/backtrace_test.c +++ b/test/libc/log/backtrace_test.c @@ -22,6 +22,7 @@ #include "libc/errno.h" #include "libc/fmt/conv.h" #include "libc/intrin/asan.internal.h" +#include "libc/intrin/kprintf.h" #include "libc/limits.h" #include "libc/log/libfatal.internal.h" #include "libc/log/log.h" diff --git a/test/libc/mem/sortedints_test.c b/test/libc/mem/sortedints_test.c new file mode 100644 index 000000000..f77f89543 --- /dev/null +++ b/test/libc/mem/sortedints_test.c @@ -0,0 +1,126 @@ +/*-*- 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/limits.h" +#include "libc/mem/mem.h" +#include "libc/mem/sortedints.internal.h" +#include "libc/stdio/rand.h" +#include "libc/str/str.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" + +struct SortedInts T; + +void TearDown(void) { + free(T.p); + bzero(&T, sizeof(T)); +} + +TEST(sortedints, test) { + EXPECT_TRUE(InsertInt(&T, 3, false)); + EXPECT_TRUE(InsertInt(&T, 1, false)); + EXPECT_TRUE(InsertInt(&T, -1, false)); + EXPECT_TRUE(InsertInt(&T, 2, false)); + EXPECT_EQ(4, T.n); + EXPECT_EQ(-1, T.p[0]); + EXPECT_EQ(+1, T.p[1]); + EXPECT_EQ(+2, T.p[2]); + EXPECT_EQ(+3, T.p[3]); + EXPECT_FALSE(ContainsInt(&T, -2)); + EXPECT_TRUE(ContainsInt(&T, -1)); + EXPECT_FALSE(ContainsInt(&T, 0)); + EXPECT_TRUE(ContainsInt(&T, 1)); + EXPECT_TRUE(ContainsInt(&T, 2)); + EXPECT_TRUE(ContainsInt(&T, 3)); + EXPECT_FALSE(ContainsInt(&T, 4)); +} + +TEST(sortedints, unique) { + EXPECT_TRUE(InsertInt(&T, INT_MAX, true)); + EXPECT_TRUE(InsertInt(&T, 1, true)); + EXPECT_FALSE(InsertInt(&T, INT_MAX, true)); + EXPECT_TRUE(InsertInt(&T, INT_MIN, true)); + EXPECT_FALSE(InsertInt(&T, 1, true)); + EXPECT_TRUE(InsertInt(&T, 2, true)); + EXPECT_EQ(4, T.n); + EXPECT_EQ(INT_MIN, T.p[0]); + EXPECT_EQ(+1, T.p[1]); + EXPECT_EQ(+2, T.p[2]); + EXPECT_EQ(INT_MAX, T.p[3]); + EXPECT_FALSE(ContainsInt(&T, -2)); + EXPECT_TRUE(ContainsInt(&T, INT_MIN)); + EXPECT_FALSE(ContainsInt(&T, 0)); + EXPECT_TRUE(ContainsInt(&T, 1)); + EXPECT_TRUE(ContainsInt(&T, 2)); + EXPECT_TRUE(ContainsInt(&T, INT_MAX)); + EXPECT_FALSE(ContainsInt(&T, 4)); + EXPECT_EQ(1, CountInt(&T, 1)); + EXPECT_EQ(0, CountInt(&T, -5)); +} + +TEST(sortedints, bag) { + EXPECT_TRUE(InsertInt(&T, INT_MAX, false)); + EXPECT_TRUE(InsertInt(&T, 1, false)); + EXPECT_TRUE(InsertInt(&T, INT_MAX, false)); + EXPECT_TRUE(InsertInt(&T, INT_MIN, false)); + EXPECT_TRUE(InsertInt(&T, 1, false)); + EXPECT_TRUE(InsertInt(&T, 2, false)); + EXPECT_EQ(6, T.n); + EXPECT_EQ(INT_MIN, T.p[0]); + EXPECT_EQ(1, T.p[1]); + EXPECT_EQ(1, T.p[2]); + EXPECT_EQ(2, T.p[3]); + EXPECT_EQ(INT_MAX, T.p[4]); + EXPECT_EQ(INT_MAX, T.p[5]); + EXPECT_FALSE(ContainsInt(&T, -2)); + EXPECT_TRUE(ContainsInt(&T, INT_MIN)); + EXPECT_FALSE(ContainsInt(&T, 0)); + EXPECT_TRUE(ContainsInt(&T, 1)); + EXPECT_TRUE(ContainsInt(&T, 2)); + EXPECT_TRUE(ContainsInt(&T, INT_MAX)); + EXPECT_FALSE(ContainsInt(&T, 4)); + EXPECT_EQ(1, CountInt(&T, INT_MIN)); + EXPECT_EQ(2, CountInt(&T, 1)); + EXPECT_EQ(0, CountInt(&T, -5)); +} + +TEST(sortedints, fuzz) { + for (int i = 0; i < 10000; ++i) { + volatile bool b; + volatile int y, x = lemur64(); + InsertInt(&T, x, x & 1); + b = ContainsInt(&T, x); + b = ContainsInt(&T, -x); + y = CountInt(&T, x); + } + for (int i = 1; i < T.n; ++i) { + ASSERT_GE(T.p[i], T.p[i - 1]); + } +} + +BENCH(sortedints, bench) { + volatile int x; + EZBENCH2("overhead", donothing, x = lemur64()); + EZBENCH2("insert small", donothing, InsertInt(&T, lemur64(), true)); + EZBENCH2("contains small", donothing, ContainsInt(&T, lemur64())); + for (int i = 0; i < 20000; ++i) { + InsertInt(&T, lemur64(), true); + } + EZBENCH2("insert big", donothing, InsertInt(&T, lemur64(), true)); + EZBENCH2("contains big", donothing, ContainsInt(&T, lemur64())); +} diff --git a/test/libc/stdio/dirstream_test.c b/test/libc/stdio/dirstream_test.c index bc0dfca18..5b6afc501 100644 --- a/test/libc/stdio/dirstream_test.c +++ b/test/libc/stdio/dirstream_test.c @@ -61,6 +61,8 @@ TEST(opendir, enotdir) { TEST(opendir, zipTest_fake) { ASSERT_NE(NULL, (dir = opendir("/zip"))); EXPECT_NE(NULL, (ent = readdir(dir))); + EXPECT_STREQ(".cosmo", ent->d_name); + EXPECT_NE(NULL, (ent = readdir(dir))); EXPECT_STREQ("echo", ent->d_name); EXPECT_NE(NULL, (ent = readdir(dir))); EXPECT_STREQ("usr", ent->d_name); @@ -68,6 +70,8 @@ TEST(opendir, zipTest_fake) { EXPECT_EQ(0, closedir(dir)); ASSERT_NE(NULL, (dir = opendir("/zip/"))); EXPECT_NE(NULL, (ent = readdir(dir))); + EXPECT_STREQ(".cosmo", ent->d_name); + EXPECT_NE(NULL, (ent = readdir(dir))); EXPECT_STREQ("echo", ent->d_name); EXPECT_NE(NULL, (ent = readdir(dir))); EXPECT_STREQ("usr", ent->d_name); diff --git a/test/libc/thread/pthread_cond_signal_test.c b/test/libc/thread/pthread_cond_signal_test.c index f72e77abd..c2aca7b4b 100644 --- a/test/libc/thread/pthread_cond_signal_test.c +++ b/test/libc/thread/pthread_cond_signal_test.c @@ -22,6 +22,8 @@ #include "libc/thread/thread.h" #include "libc/thread/thread2.h" +// TODO(jart): Can we make this test go faster on NetBSD? + int pos; int count; int limit; @@ -77,7 +79,7 @@ long Get(struct timespec *abs_deadline) { return v; } -#define N 10000 +#define N 1000 void *Producer(void *arg) { for (int i = 0; i < N; i++) { diff --git a/test/libc/thread/pthread_rwlock_rdlock_test.c b/test/libc/thread/pthread_rwlock_rdlock_test.c index b2e259168..c29dbcfec 100644 --- a/test/libc/thread/pthread_rwlock_rdlock_test.c +++ b/test/libc/thread/pthread_rwlock_rdlock_test.c @@ -17,8 +17,8 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/atomic.h" -#include "libc/mem/mem.h" #include "libc/mem/gc.h" +#include "libc/mem/mem.h" #include "libc/testlib/testlib.h" #include "libc/thread/spawn.h" #include "libc/thread/thread.h" diff --git a/test/libc/time/iso8601_test.c b/test/libc/time/iso8601_test.c new file mode 100644 index 000000000..5c420fa8e --- /dev/null +++ b/test/libc/time/iso8601_test.c @@ -0,0 +1,51 @@ +/*-*- 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/testlib/ezbench.h" +#include "libc/testlib/testlib.h" +#include "libc/time/struct/tm.h" +#include "libc/time/time.h" + +TEST(iso8601, test) { + char p[20]; + struct tm tm; + int64_t t = 0x62820bcd; + gmtime_r(&t, &tm); + EXPECT_EQ(p + 19, iso8601(p, &tm)); + EXPECT_STREQ("2022-05-16T08:31:09", p); +} + +TEST(iso8601us, test) { + char p[27]; + struct tm tm; + int64_t t = 0x62820bcd; + gmtime_r(&t, &tm); + EXPECT_EQ(p + 26, iso8601us(p, &tm, 1234000)); + EXPECT_STREQ("2022-05-16T08:31:09.001234", p); +} + +BENCH(iso8601, bench) { + char p[27]; + struct tm tm; + int64_t t = 0x62820bcd; + gmtime_r(&t, &tm); + EZBENCH2("iso8601", donothing, iso8601(p, &tm)); + EZBENCH2("iso8601us", donothing, iso8601us(p, &tm, 123456)); + EZBENCH2("strftime", donothing, + strftime(p, sizeof(p), "%Y-%m-%dT%H:%M:%S", &tm)); +} diff --git a/test/tool/build/lib/isnocompressext_test.c b/test/net/http/isnocompressext_test.c similarity index 93% rename from test/tool/build/lib/isnocompressext_test.c rename to test/net/http/isnocompressext_test.c index 7c47741cc..22766c7a9 100644 --- a/test/tool/build/lib/isnocompressext_test.c +++ b/test/net/http/isnocompressext_test.c @@ -1,7 +1,7 @@ /*-*- 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 │ +│ 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 │ @@ -17,11 +17,12 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/testlib/testlib.h" -#include "tool/build/lib/isnocompressext.h" +#include "net/http/http.h" TEST(IsNoCompressExt, test) { EXPECT_TRUE(IsNoCompressExt("MP4", -1)); EXPECT_TRUE(IsNoCompressExt("mp4", -1)); EXPECT_FALSE(IsNoCompressExt("dog", -1)); EXPECT_TRUE(IsNoCompressExt("dog.mp4", -1)); + EXPECT_FALSE(IsNoCompressExt("dog.mp4mp4mp4", -1)); } diff --git a/test/tool/net/redbean_test.c b/test/tool/net/redbean_test.c index b5c8c4b97..aa68c07b4 100644 --- a/test/tool/net/redbean_test.c +++ b/test/tool/net/redbean_test.c @@ -41,7 +41,7 @@ #include "third_party/regex/regex.h" STATIC_YOINK("zip_uri_support"); -STATIC_YOINK("o/" MODE "/tool/net/redbean.com"); +STATIC_YOINK("o/" MODE "/test/tool/net/redbean-tester.com"); char testlib_enable_tmp_setup_teardown_once; int port; @@ -52,9 +52,9 @@ void SetUpOnce(void) { char buf[1024]; int fdin, fdout; ASSERT_NE(-1, mkdir("bin", 0755)); - ASSERT_NE(-1, - (fdin = open("/zip/o/" MODE "/tool/net/redbean.com", O_RDONLY))); - ASSERT_NE(-1, (fdout = creat("bin/redbean.com", 0755))); + ASSERT_NE(-1, (fdin = open("/zip/o/" MODE "/test/tool/net/redbean-tester.com", + O_RDONLY))); + ASSERT_NE(-1, (fdout = creat("bin/redbean-tester.com", 0755))); for (;;) { ASSERT_NE(-1, (n = read(fdin, buf, sizeof(buf)))); if (!n) break; @@ -93,6 +93,7 @@ char *SendHttpRequest(const char *s) { bool Matches(const char *regex, const char *str) { bool r; regex_t re; + printf("%s\n", str); EXPECT_EQ(REG_OK, regcomp(&re, regex, 0)); r = regexec(&re, str, 0, 0, 0) == REG_OK; regfree(&re); @@ -113,14 +114,12 @@ TEST(redbean, testOptions) { close(0); open("/dev/null", O_RDWR); close(1); - dup(0); - close(2); - open("log", O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644); close(pipefds[0]); dup2(pipefds[1], 1); sigprocmask(SIG_SETMASK, &savemask, NULL); - execv("bin/redbean.com", - (char *const[]){"bin/redbean.com", "-vvszp0", "-l127.0.0.1", 0}); + execv("bin/redbean-tester.com", + (char *const[]){"bin/redbean-tester.com", "-vvszXp0", "-l127.0.0.1", + __strace > 0 ? "--strace" : 0, 0}); _exit(127); } EXPECT_NE(-1, close(pipefds[1])); @@ -139,7 +138,6 @@ TEST(redbean, testOptions) { EXPECT_NE(-1, kill(pid, SIGTERM)); EXPECT_NE(-1, wait(0)); EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0)); - if (g_testlib_failed) fputs(gc(xslurp("log", 0)), stderr); } TEST(redbean, testPipeline) { @@ -155,13 +153,12 @@ TEST(redbean, testPipeline) { setpgrp(); close(0); open("/dev/null", O_RDWR); - close(2); - open("log", O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644); close(pipefds[0]); dup2(pipefds[1], 1); sigprocmask(SIG_SETMASK, &savemask, NULL); - execv("bin/redbean.com", - (char *const[]){"bin/redbean.com", "-vvszp0", "-l127.0.0.1", 0}); + execv("bin/redbean-tester.com", + (char *const[]){"bin/redbean-tester.com", "-vvszXp0", "-l127.0.0.1", + __strace > 0 ? "--strace" : 0, 0}); _exit(127); } EXPECT_NE(-1, close(pipefds[1])); @@ -189,5 +186,85 @@ TEST(redbean, testPipeline) { EXPECT_NE(-1, kill(pid, SIGTERM)); EXPECT_NE(-1, wait(0)); EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0)); - if (g_testlib_failed) fputs(gc(xslurp("log", 0)), stderr); +} + +TEST(redbean, testContentRange) { + if (IsWindows()) return; + char portbuf[16]; + int pid, pipefds[2]; + sigset_t chldmask, savemask; + sigaddset(&chldmask, SIGCHLD); + EXPECT_NE(-1, sigprocmask(SIG_BLOCK, &chldmask, &savemask)); + ASSERT_NE(-1, pipe(pipefds)); + ASSERT_NE(-1, (pid = fork())); + if (!pid) { + setpgrp(); + close(0); + open("/dev/null", O_RDWR); + close(pipefds[0]); + dup2(pipefds[1], 1); + sigprocmask(SIG_SETMASK, &savemask, NULL); + execv("bin/redbean-tester.com", + (char *const[]){"bin/redbean-tester.com", "-vvszXp0", "-l127.0.0.1", + __strace > 0 ? "--strace" : 0, 0}); + _exit(127); + } + EXPECT_NE(-1, close(pipefds[1])); + EXPECT_NE(-1, read(pipefds[0], portbuf, sizeof(portbuf))); + port = atoi(portbuf); + + EXPECT_TRUE(Matches("\ +HTTP/1.1 206 Partial Content\r\n\ +Content-Range: bytes 18-21/52\r\n\ +Content-Type: text/plain; charset=utf-8\r\n\ +Vary: Accept-Encoding\r\n\ +Last-Modified: .*\r\n\ +Accept-Ranges: bytes\r\n\ +X-Content-Type-Options: nosniff\r\n\ +Date: .*\r\n\ +Server: redbean/.*\r\n\ +Content-Length: 4\r\n\ +\r\n\ +J\n\ +K\n", + gc(SendHttpRequest("GET /seekable.txt HTTP/1.1\r\n" + "Range: bytes=18-21\r\n" + "\r\n")))); + + EXPECT_TRUE(Matches("\ +HTTP/1.1 416 Range Not Satisfiable\r\n\ +Content-Range: bytes \\*/52\r\n\ +Content-Type: text/plain; charset=utf-8\r\n\ +Vary: Accept-Encoding\r\n\ +Last-Modified: .*\r\n\ +Accept-Ranges: bytes\r\n\ +X-Content-Type-Options: nosniff\r\n\ +Date: .*\r\n\ +Server: redbean/.*\r\n\ +Content-Length: 0\r\n\ +\r\n", + gc(SendHttpRequest("GET /seekable.txt HTTP/1.1\r\n" + "Range: bytes=-18-21\r\n" + "\r\n")))); + + EXPECT_TRUE(Matches("\ +HTTP/1.1 416 Range Not Satisfiable\r\n\ +Content-Range: bytes \\*/52\r\n\ +Content-Type: text/plain; charset=utf-8\r\n\ +Vary: Accept-Encoding\r\n\ +Last-Modified: .*\r\n\ +Accept-Ranges: bytes\r\n\ +X-Content-Type-Options: nosniff\r\n\ +Date: .*\r\n\ +Server: redbean/.*\r\n\ +Content-Length: 0\r\n\ +\r\n", + gc(SendHttpRequest("GET /seekable.txt HTTP/1.1\r\n" + "Range: bytes=18-60\r\n" + "\r\n")))); + + EXPECT_EQ(0, close(pipefds[0])); + EXPECT_NE(-1, kill(pid, SIGTERM)); + EXPECT_NE(-1, wait(0)); + EXPECT_NE(-1, sigprocmask(SIG_SETMASK, &savemask, 0)); } diff --git a/test/tool/net/sqlite_test.c b/test/tool/net/sqlite_test.c index 70c5f7255..3a827532c 100644 --- a/test/tool/net/sqlite_test.c +++ b/test/tool/net/sqlite_test.c @@ -31,7 +31,6 @@ char testlib_enable_tmp_setup_teardown; void SetUpOnce(void) { - if (IsWindows()) exit(0); sqlite3_initialize(); } diff --git a/test/tool/net/test.mk b/test/tool/net/test.mk index 67c430b9a..c4ebc5cfe 100644 --- a/test/tool/net/test.mk +++ b/test/tool/net/test.mk @@ -14,7 +14,7 @@ TEST_TOOL_NET_COMS = $(TEST_TOOL_NET_SRCS:%.c=o/$(MODE)/%.com) TEST_TOOL_NET_OBJS = \ $(TEST_TOOL_NET_SRCS:%.c=o/$(MODE)/%.o) \ - o/$(MODE)/tool/net/redbean.com.zip.o + o/$(MODE)/test/tool/net/redbean-tester.com.zip.o TEST_TOOL_NET_BINS = \ $(TEST_TOOL_NET_COMS) \ @@ -71,6 +71,26 @@ o/$(MODE)/test/tool/net/%.com.dbg: \ $(APE_NO_MODIFY_SELF) @$(APELINK) +o/$(MODE)/test/tool/net/redbean-tester.com.dbg: \ + $(TOOL_NET_DEPS) \ + o/$(MODE)/tool/net/redbean.o \ + $(TOOL_NET_REDBEAN_LUA_MODULES) \ + o/$(MODE)/tool/net/demo/seekable.txt.zip.o \ + o/$(MODE)/tool/net/net.pkg \ + $(CRT) \ + $(APE_NO_MODIFY_SELF) + @$(APELINK) + +o/$(MODE)/test/tool/net/redbean-tester.com: \ + o/$(MODE)/test/tool/net/redbean-tester.com.dbg \ + o/$(MODE)/third_party/zip/zip.com \ + o/$(MODE)/tool/build/symtab.com \ + $(TOOL_NET_REDBEAN_STANDARD_ASSETS) + @$(MAKE_OBJCOPY) + @$(MAKE_SYMTAB_CREATE) + @$(MAKE_SYMTAB_ZIP) + @$(TOOL_NET_REDBEAN_STANDARD_ASSETS_ZIP) + o/$(MODE)/test/tool/net/redbean_test.com.runs: \ private .PLEDGE = stdio rpath wpath cpath fattr proc inet diff --git a/third_party/python/Python/sysmodule.c b/third_party/python/Python/sysmodule.c index 9a1355244..625139b99 100644 --- a/third_party/python/Python/sysmodule.c +++ b/third_party/python/Python/sysmodule.c @@ -6,10 +6,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/dce.h" +#include "libc/mem/gc.internal.h" #include "libc/mem/mem.h" #include "libc/nt/dll.h" #include "libc/nt/version.h" -#include "libc/mem/gc.internal.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/locale.h" diff --git a/third_party/sqlite3/README.cosmo b/third_party/sqlite3/README.cosmo index 7f39d179e..df74597f3 100644 --- a/third_party/sqlite3/README.cosmo +++ b/third_party/sqlite3/README.cosmo @@ -13,4 +13,8 @@ LICENSE LOCAL CHANGES - - Changed the fsync() code to be configured at runtime. + - Added `/zip/.args` file support to SQLite shell + - Added `--strace` system call tracing flag to SQLite shell + - Added `--strace` function call logging flag to SQLite shell + - Configured fsync() using runtime magnums rather than ifdefs + - Save and restore errno in some places to avoid log pollution diff --git a/third_party/sqlite3/main.shell.c b/third_party/sqlite3/main.shell.c index 9c64591ad..681aa77a2 100644 --- a/third_party/sqlite3/main.shell.c +++ b/third_party/sqlite3/main.shell.c @@ -1 +1,2 @@ +STATIC_YOINK("zipos"); #include "third_party/sqlite3/main.c" diff --git a/third_party/sqlite3/shell.c b/third_party/sqlite3/shell.c index 0500f35c3..374009089 100644 --- a/third_party/sqlite3/shell.c +++ b/third_party/sqlite3/shell.c @@ -60,6 +60,9 @@ #include "libc/time/time.h" #include "libc/runtime/runtime.h" #include "libc/errno.h" +#include "libc/log/log.h" +#include "libc/runtime/symbols.internal.h" + #if SQLITE_USER_AUTHENTICATION #include "third_party/sqlite3/sqlite3userauth.inc" #endif @@ -10728,6 +10731,8 @@ static char *cmdline_option_value(int argc, char **argv, int i){ # endif #endif +STATIC_YOINK("zipos"); // for symtab + int SQLITE_CDECL main(int argc, char **argv){ char *zErrMsg = 0; ShellState data; @@ -10740,7 +10745,16 @@ int SQLITE_CDECL main(int argc, char **argv){ char **azCmd = 0; const char *zVfs = 0; /* Value of -vfs command-line option */ + // [jart] ensure %t symbols in strace log are symbolic + if (__strace > 0) { + GetSymbolTable(); + } + + // ShowCrashReports(); + + // [jart] support /zip/.args file for white labeling LoadZipArgs(&argc, &argv); + setBinaryMode(stdin, 0); setvbuf(stderr, 0, _IONBF, 0); /* Make sure stderr is unbuffered */ setvbuf(stdin, (char *)NULL, _IONBF, BUFSIZ); diff --git a/third_party/sqlite3/sqlite3.mk b/third_party/sqlite3/sqlite3.mk index 4d596ca2a..a1c984505 100644 --- a/third_party/sqlite3/sqlite3.mk +++ b/third_party/sqlite3/sqlite3.mk @@ -57,6 +57,7 @@ THIRD_PARTY_SQLITE3_A_DIRECTDEPS = \ LIBC_THREAD \ LIBC_TIME \ LIBC_TINYMATH \ + LIBC_ZIPOS \ THIRD_PARTY_GDTOA \ THIRD_PARTY_LINENOISE \ THIRD_PARTY_MUSL \ diff --git a/third_party/zip/fileio.c b/third_party/zip/fileio.c index 371511eaf..e350503c1 100644 --- a/third_party/zip/fileio.c +++ b/third_party/zip/fileio.c @@ -1934,88 +1934,6 @@ int bfcopy(n) return ZE_OK; } - - -#ifdef NO_RENAME -int rename(from, to) -ZCONST char *from; -ZCONST char *to; -{ - unlink(to); - if (link(from, to) == -1) - return -1; - if (unlink(from) == -1) - return -1; - return 0; -} - -#endif /* NO_RENAME */ - - -#ifdef ZMEM - -/************************/ -/* Function memset() */ -/************************/ - -/* - * memset - for systems without it - * bill davidsen - March 1990 - */ - -char * -memset(buf, init, len) -register char *buf; /* buffer loc */ -register int init; /* initializer */ -register unsigned int len; /* length of the buffer */ -{ - char *start; - - start = buf; - while (len--) *(buf++) = init; - return(start); -} - - -/************************/ -/* Function memcpy() */ -/************************/ - -char * -memcpy(dst,src,len) /* v2.0f */ -register char *dst, *src; -register unsigned int len; -{ - char *start; - - start = dst; - while (len--) - *dst++ = *src++; - return(start); -} - - -/************************/ -/* Function memcmp() */ -/************************/ - -int -memcmp(b1,b2,len) /* jpd@usl.edu -- 11/16/90 */ -register char *b1, *b2; -register unsigned int len; -{ - - if (len) do { /* examine each byte (if any) */ - if (*b1++ != *b2++) - return (*((uch *)b1-1) - *((uch *)b2-1)); /* exit when miscompare */ - } while (--len); - - return(0); /* no miscompares, yield 0 result */ -} - -#endif /* ZMEM */ - - /*------------------------------------------------------------------ * Split archives */ diff --git a/tool/build/lib/buildlib.mk b/tool/build/lib/buildlib.mk index 4cdf39fe7..edf860840 100644 --- a/tool/build/lib/buildlib.mk +++ b/tool/build/lib/buildlib.mk @@ -49,6 +49,7 @@ TOOL_BUILD_LIB_A_DIRECTDEPS = \ LIBC_TIME \ LIBC_TINYMATH \ LIBC_X \ + NET_HTTP \ NET_HTTPS \ THIRD_PARTY_COMPILER_RT \ THIRD_PARTY_MBEDTLS \ diff --git a/tool/build/lib/elfwriter_zip.c b/tool/build/lib/elfwriter_zip.c index d86c4ac62..ee9ae1d97 100644 --- a/tool/build/lib/elfwriter_zip.c +++ b/tool/build/lib/elfwriter_zip.c @@ -20,9 +20,9 @@ #include "libc/fmt/conv.h" #include "libc/limits.h" #include "libc/log/check.h" +#include "libc/mem/gc.h" #include "libc/nexgen32e/crc32.h" #include "libc/nt/enum/fileflagandattributes.h" -#include "libc/mem/gc.h" #include "libc/stdio/rand.h" #include "libc/str/str.h" #include "libc/sysv/consts/s.h" @@ -30,9 +30,9 @@ #include "libc/x/x.h" #include "libc/x/xasprintf.h" #include "libc/zip.h" +#include "net/http/http.h" #include "third_party/zlib/zlib.h" #include "tool/build/lib/elfwriter.h" -#include "tool/build/lib/isnocompressext.h" #define ZIP_LOCALFILE_SECTION ".zip.2." #define ZIP_DIRECTORY_SECTION ".zip.4." diff --git a/tool/build/lib/isnocompressext.h b/tool/build/lib/isnocompressext.h deleted file mode 100644 index be7bb4b7f..000000000 --- a/tool/build/lib/isnocompressext.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_ISNOCOMPRESSEXT_H_ -#define COSMOPOLITAN_TOOL_BUILD_LIB_ISNOCOMPRESSEXT_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -bool IsNoCompressExt(const char *, size_t); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_ISNOCOMPRESSEXT_H_ */ diff --git a/tool/net/counters.inc b/tool/net/counters.inc index 4f55e94f8..f0088857d 100644 --- a/tool/net/counters.inc +++ b/tool/net/counters.inc @@ -2,10 +2,12 @@ C(accepterrors) C(acceptflakes) C(acceptinterrupts) C(acceptresets) +C(accepts) C(badlengths) C(badmessages) C(badmethods) C(badranges) +C(bans) C(closeerrors) C(compressedresponses) C(connectionshandled) @@ -35,6 +37,7 @@ C(http11) C(http12) C(hugepayloads) C(identityresponses) +C(ignores) C(inflates) C(listingrequests) C(loops) @@ -57,6 +60,7 @@ C(readresets) C(readtimeouts) C(redirects) C(reindexes) +C(rejects) C(reloads) C(rewrites) C(serveroptions) diff --git a/tool/net/demo/.init.lua b/tool/net/demo/.init.lua index b6dbf1658..1f0c08855 100644 --- a/tool/net/demo/.init.lua +++ b/tool/net/demo/.init.lua @@ -1,6 +1,9 @@ mymodule = require "mymodule" sqlite3 = require "lsqlite3" +-- ddos protection +ProgramTokenBucket() + -- /.init.lua is loaded at startup in redbean's main process HidePath('/usr/share/zoneinfo/') HidePath('/usr/share/ssl/') diff --git a/tool/net/fetch.inc b/tool/net/fetch.inc index cff031720..64b5f55e8 100644 --- a/tool/net/fetch.inc +++ b/tool/net/fetch.inc @@ -26,8 +26,8 @@ static int LuaFetch(lua_State *L) { char *key, *val, *hdr; size_t keylen, vallen; size_t urlarglen, requestlen, paylen, bodylen; - size_t g, i, n, hdrsize; - int numredirects = 0, maxredirects = 5; + size_t g, n, hdrsize; + int imethod, numredirects = 0, maxredirects = 5; bool followredirect = true; struct addrinfo hints = {.ai_family = AF_INET, .ai_socktype = SOCK_STREAM, @@ -44,7 +44,12 @@ static int LuaFetch(lua_State *L) { body = luaL_optlstring(L, -1, "", &bodylen); lua_getfield(L, 2, "method"); // use GET by default if no method is provided - method = strtoupper(luaL_optstring(L, -1, kHttpMethod[kHttpGet])); + method = luaL_optstring(L, -1, kHttpMethod[kHttpGet]); + if ((imethod = GetHttpMethod(method, -1))) { + method = kHttpMethod[imethod]; + } else { + return LuaNilError(L, "bad method"); + } lua_getfield(L, 2, "followredirect"); if (lua_isboolean(L, -1)) followredirect = lua_toboolean(L, -1); lua_getfield(L, 2, "maxredirects"); diff --git a/tool/net/help.txt b/tool/net/help.txt index 8f2899fe3..d6e578096 100644 --- a/tool/net/help.txt +++ b/tool/net/help.txt @@ -1878,6 +1878,160 @@ FUNCTIONS string of unspecified format describing the error. Calls to this function may be wrapped in assert() if an exception is desired. + IsTrustedIp(ip:int) + └─→ bool + + Returns true if IP address is trustworthy. + + If the ProgramTrustedIp() function has NOT been called then redbean + will consider the networks 127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, + and 192.168.0.0/16 to be trustworthy too. If ProgramTrustedIp() HAS + been called at some point earlier in your redbean's lifecycle, then + it'll trust the IPs and network subnets you specify instead. + + The network interface addresses used by the host machine are always + considered trustworthy, e.g. 127.0.0.1. This may change soon, if we + decide to export a GetHostIps() API which queries your NIC devices. + + ProgramTrustedIp(ip:int[, cidr:int]) + + Trusts an IP address or network. + + This function may be used to configure the IsTrustedIp() function + which is how redbean determines if a client is allowed to send us + headers like X-Forwarded-For (cf GetRemoteAddr vs. GetClientAddr) + without them being ignored. Trusted IPs is also how redbean turns + off token bucket rate limiting selectively, so be careful. Here's + an example of how you could trust all of Cloudflare's IPs: + + ProgramTrustedIp(ParseIp("103.21.244.0"), 22); + ProgramTrustedIp(ParseIp("103.22.200.0"), 22); + ProgramTrustedIp(ParseIp("103.31.4.0"), 22); + ProgramTrustedIp(ParseIp("104.16.0.0"), 13); + ProgramTrustedIp(ParseIp("104.24.0.0"), 14); + ProgramTrustedIp(ParseIp("108.162.192.0"), 18); + ProgramTrustedIp(ParseIp("131.0.72.0"), 22); + ProgramTrustedIp(ParseIp("141.101.64.0"), 18); + ProgramTrustedIp(ParseIp("162.158.0.0"), 15); + ProgramTrustedIp(ParseIp("172.64.0.0"), 13); + ProgramTrustedIp(ParseIp("173.245.48.0"), 20); + ProgramTrustedIp(ParseIp("188.114.96.0"), 20); + ProgramTrustedIp(ParseIp("190.93.240.0"), 20); + ProgramTrustedIp(ParseIp("197.234.240.0"), 22); + ProgramTrustedIp(ParseIp("198.41.128.0"), 17); + + Although you might want consider trusting redbean's open source + freedom embracing solution to DDOS protection instead! + + ProgramTokenBucket([replenish:num, cidr:int, reject:int, ignore:int, ban:int]) + + Enables DDOS protection. + + Imagine you have 2**32 buckets, one for each IP address. Each bucket + can hold about 127 tokens. Every second a background worker puts one + token in each bucket. When a TCP client socket is opened, it takes a + token from its bucket, and then proceeds. If the bucket holds only a + third of its original tokens, then redbean sends them a 429 warning. + If the client ignores this warning and keeps sending requests, until + there's no tokens left, then the banhammer finally comes down. + + This model of network rate limiting generously lets people "burst" a + tiny bit. For example someone might get a strong craving for content + and smash the reload button in Chrome 64 times in a fow seconds. But + since the client only get 1 new token per second, they'd better cool + their heels for a few minutes after doing that. This amount of burst + can be altered by choosing the `reject` / `ignore` / `ban` threshold + arguments. For example, if the `reject` parameter is set to 126 then + no bursting is allowed, which probably isn't a good idea. + + redbean is programmed to acquire a token immediately after accept() + is called from the main server process, which is well before fork() + or read() or any Lua code happens. redbean then takes action, based + on the token count, which can be accept / reject / ignore / ban. If + redbean determines a ban is warrented, then 4-byte datagram is sent + to the unix domain socket `/var/run/blackhole.sock` which should be + operated using the blackholed program we distribute separately. + + The trick redbean uses on Linux for example is insert rules in your + raw prerouting table. redbean is very fast at the application layer + so the biggest issue we've encountered in production is are kernels + themselves, and programming the raw prerouting table dynamically is + how we solved that. + + `replenish` is the number of times per second a token should be + added to each bucket. The default value is 1 which means one token + is granted per second to all buckets. The minimum value is 1/3600 + which means once per hour. The maximum value for this setting is + 1e6, which means once every microsecond. + + `cidr` is the specificity of judgement. Since creating 2^32 buckets + would need 4GB of RAM, redbean defaults this value to 24 which means + filtering applies to class c network blocks (i.e. x.x.x.*), and your + token buckets only take up 2^24 bytes of RAM (16MB). This can be set + to any number on the inclusive interval [8,32], where having a lower + number means you use less ram/cpu, but splash damage applies more to + your clients; whereas higher numbers means more ram/cpu usage, while + ensuring rate limiting only applies to specific compromised actors. + + `reject` is the token count or treshold at which redbean should send + 429 Too Many Request warnings to the client. Permitted values can be + anywhere between -1 and 126 inclusively. The default value is 30 and + -1 means disable to disable (assuming AcquireToken() will be used). + + `ignore` is the token count or treshold, at which redbean should try + simply ignoring clients and close the connection without logging any + kind of warning, and without sending any response. The default value + for this setting is `MIN(reject / 2, 15)`. This must be less than or + equal to the `reject` setting. Allowed values are [-1,126] where you + can use -1 as a means of disabling `ignore`. + + `ban` is the token count at which redbean should report IP addresses + to the blackhole daemon via a unix-domain socket datagram so they'll + get banned in the kernel routing tables. redbean's default value for + this setting is `MIN(ignore / 10, 1)`. Permitted values are [-1,126] + where -1 may be used as a means of disabling the `ban` feature. + + This function throws an exception if the constraints described above + are not the case. Warnings are logged should redbean fail to connect + to the blackhole daemon, assuming it hasn't been disabled. It's safe + to use load balancing tools when banning is enabled, since you can't + accidentally ban your own network interface addresses, loopback ips, + or ProgramTrustedIp() addresses where these rate limits don't apply. + + It's assumed will be called from the .init.lua global scope although + it could be used in interpreter mode, or from a forked child process + in which case the only processes that'll have ability to use it will + be that same process, and any descendent processes. This function is + only able to be called once. + + This feature is not available in unsecure mode. + + AcquireToken([ip:uint32]) + └─→ int8 + + Atomically acquires token. + + This routine atomically acquires a single token for an `ip` address. + The return value is the token count before the subtraction happened. + No action is taken based on the count, since the caller will decide. + + `ip` should be an IPv4 address and this defaults to GetClientAddr(), + although other interpretations of its meaning are possible. + + Your token buckets are stored in shared memory so this can be called + from multiple forked processes. which operate on the same values. + + CountTokens([ip:uint32]) + └─→ int8 + + Counts number of tokens in bucket. + + This function is the same as AcquireToken() except no subtraction is + performed, i.e. no token is taken. + + `ip` should be an IPv4 address and this defaults to GetClientAddr(), + although other interpretations of its meaning are possible. + ──────────────────────────────────────────────────────────────────────────────── CONSTANTS diff --git a/tool/net/redbean.c b/tool/net/redbean.c index 2978cf151..1f5ca6b41 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.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/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/ioctl.h" @@ -25,8 +26,10 @@ #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/rusage.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/termios.h" +#include "libc/calls/struct/timespec.h" #include "libc/dce.h" #include "libc/dns/dns.h" #include "libc/dns/hoststxt.h" @@ -35,8 +38,8 @@ #include "libc/fmt/conv.h" #include "libc/fmt/itoa.h" #include "libc/intrin/atomic.h" +#include "libc/intrin/bits.h" #include "libc/intrin/bsr.h" -#include "libc/intrin/kprintf.h" #include "libc/intrin/likely.h" #include "libc/intrin/nomultics.internal.h" #include "libc/intrin/safemacros.internal.h" @@ -62,13 +65,16 @@ #include "libc/sock/goodsocket.internal.h" #include "libc/sock/sock.h" #include "libc/sock/struct/pollfd.h" +#include "libc/sock/struct/sockaddr.h" #include "libc/stdio/append.h" #include "libc/stdio/hex.internal.h" #include "libc/stdio/rand.h" #include "libc/stdio/stdio.h" #include "libc/str/slice.h" +#include "libc/str/str.h" #include "libc/str/strwidth.h" #include "libc/sysv/consts/af.h" +#include "libc/sysv/consts/clock.h" #include "libc/sysv/consts/clone.h" #include "libc/sysv/consts/dt.h" #include "libc/sysv/consts/ex.h" @@ -88,6 +94,7 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/termios.h" +#include "libc/sysv/consts/timer.h" #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" #include "libc/thread/spawn.h" @@ -99,6 +106,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 "net/https/https.h" #include "third_party/getopt/getopt.h" @@ -334,13 +342,30 @@ static struct Assets { } * p; } assets; -static struct ProxyIps { +static struct TrustedIps { size_t n; - struct ProxyIp { + struct TrustedIp { uint32_t ip; uint32_t mask; } * p; -} proxyips; +} trustedips; + +struct TokenBucket { + signed char cidr; + signed char reject; + signed char ignore; + signed char ban; + struct timespec replenish; + union { + atomic_schar *b; + atomic_uint_fast64_t *w; + }; +} tokenbucket; + +struct Blackhole { + struct sockaddr_un addr; + int fd; +} blackhole; static struct Shared { int workers; @@ -467,6 +492,7 @@ static char gzip_footer[8]; static long maxpayloadsize; static const char *pidpath; static const char *logpath; +static uint32_t *interfaces; static const char *histpath; static struct pollfd *polls; static size_t payloadlength; @@ -858,19 +884,27 @@ static void ProgramRedirectArg(int code, const char *s) { ProgramRedirect(code, s, p - s, p + 1, n - (p - s + 1)); } -static void TrustProxy(uint32_t ip, int cidr) { +static void ProgramTrustedIp(uint32_t ip, int cidr) { uint32_t mask; mask = 0xffffffffu << (32 - cidr); - proxyips.p = xrealloc(proxyips.p, ++proxyips.n * sizeof(*proxyips.p)); - proxyips.p[proxyips.n - 1].ip = ip; - proxyips.p[proxyips.n - 1].mask = mask; + trustedips.p = xrealloc(trustedips.p, ++trustedips.n * sizeof(*trustedips.p)); + trustedips.p[trustedips.n - 1].ip = ip; + trustedips.p[trustedips.n - 1].mask = mask; } -static bool IsTrustedProxy(uint32_t ip) { +static bool IsTrustedIp(uint32_t ip) { int i; - if (proxyips.n) { - for (i = 0; i < proxyips.n; ++i) { - if ((ip & proxyips.p[i].mask) == proxyips.p[i].ip) { + uint32_t *p; + if (interfaces) { + for (p = interfaces; *p; ++p) { + if (ip == *p) { + return true; + } + } + } + if (trustedips.n) { + for (i = 0; i < trustedips.n; ++i) { + if ((ip & trustedips.p[i].mask) == trustedips.p[i].ip) { return true; } } @@ -909,7 +943,7 @@ static inline int GetRemoteAddr(uint32_t *ip, uint16_t *port) { char str[40]; GetClientAddr(ip, port); if (HasHeader(kHttpXForwardedFor)) { - if (IsTrustedProxy(*ip)) { + if (IsTrustedIp(*ip)) { if (ParseForwarded(HeaderData(kHttpXForwardedFor), HeaderLength(kHttpXForwardedFor), ip, port) == -1) { VERBOSEF("could not parse x-forwarded-for %`'.*s len=%ld", @@ -934,7 +968,7 @@ static char *DescribeClient(void) { uint32_t client; static char description[128]; GetClientAddr(&client, &port); - if (HasHeader(kHttpXForwardedFor) && IsTrustedProxy(client)) { + if (HasHeader(kHttpXForwardedFor) && IsTrustedIp(client)) { DescribeAddress(str, client, port); snprintf(description, sizeof(description), "%'.*s via %s", HeaderLength(kHttpXForwardedFor), HeaderData(kHttpXForwardedFor), @@ -1065,9 +1099,17 @@ static void ProgramHeader(const char *s) { } static void ProgramLogPath(const char *s) { + int fd; logpath = strdup(s); - close(2); - open(logpath, O_APPEND | O_WRONLY | O_CREAT, 0640); + fd = open(logpath, O_APPEND | O_WRONLY | O_CREAT, 0640); + if (fd == -1) { + WARNF("(srvr) open(%`'s) failed: %m", logpath); + return; + } + if (fd != 2) { + dup2(fd, 2); + close(fd); + } } static void ProgramPidPath(const char *s) { @@ -3394,7 +3436,7 @@ static int LuaRoute(lua_State *L) { return 1; } -static int LuaTrustProxy(lua_State *L) { +static int LuaProgramTrustedIp(lua_State *L) { lua_Integer ip, cidr; uint32_t ip32, imask; ip = luaL_checkinteger(L, 1); @@ -3415,18 +3457,18 @@ static int LuaTrustProxy(lua_State *L) { "it has bits masked by the cidr"); unreachable; } - TrustProxy(ip, cidr); + ProgramTrustedIp(ip, cidr); return 0; } -static int LuaIsTrustedProxy(lua_State *L) { +static int LuaIsTrusted(lua_State *L) { lua_Integer ip; ip = luaL_checkinteger(L, 1); if (!(0 <= ip && ip <= 0xffffffff)) { luaL_argerror(L, 1, "ip out of range"); unreachable; } - lua_pushboolean(L, IsTrustedProxy(ip)); + lua_pushboolean(L, IsTrustedIp(ip)); return 1; } @@ -4141,7 +4183,8 @@ static int LuaSetHeader(lua_State *L) { size_t i, keylen, vallen, evallen; OnlyCallDuringRequest(L, "SetHeader"); key = luaL_checklstring(L, 1, &keylen); - val = luaL_checklstring(L, 2, &vallen); + val = luaL_optlstring(L, 2, 0, &vallen); + if (!val) return 0; if ((h = GetHttpHeader(key, keylen)) == -1) { if (!IsValidHttpToken(key, keylen)) { luaL_argerror(L, 1, "invalid"); @@ -4161,11 +4204,7 @@ static int LuaSetHeader(lua_State *L) { } switch (h) { case kHttpConnection: - if (!SlicesEqualCase(eval, evallen, "close", 5)) { - luaL_argerror(L, 2, "unsupported"); - unreachable; - } - connectionclose = true; + connectionclose = SlicesEqualCase(eval, evallen, "close", 5); break; case kHttpContentType: p = AppendContentType(p, eval); @@ -4708,6 +4747,145 @@ static int LuaIsAssetCompressed(lua_State *L) { return 1; } +static void Blackhole(uint32_t ip) { + char buf[4]; + if (blackhole.fd <= 0) return; + WRITE32BE(buf, ip); + if (write(blackhole.fd, buf, 4) == -1) { + WARNF("error: write(%s) failed: %m\n", blackhole.addr.sun_path); + } +} + +wontreturn static void Replenisher(void) { + struct timespec ts; + VERBOSEF("token replenish worker started"); + signal(SIGINT, OnTerm); + signal(SIGHUP, OnTerm); + signal(SIGTERM, OnTerm); + signal(SIGUSR1, SIG_IGN); // make sure reload won't kill this + signal(SIGUSR2, SIG_IGN); // make sure meltdown won't kill this + ts = _timespec_real(); + while (!terminated) { + if (clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &ts, 0)) { + errno = 0; + continue; + } + ReplenishTokens(tokenbucket.w, (1ul << tokenbucket.cidr) / 8); + ts = _timespec_add(ts, tokenbucket.replenish); + } + VERBOSEF("token replenish worker exiting"); + _Exit(0); +} + +static int LuaAcquireToken(lua_State *L) { + uint32_t ip; + if (!tokenbucket.cidr) { + luaL_error(L, "ProgramTokenBucket() needs to be called first"); + unreachable; + } + GetClientAddr(&ip, 0); + lua_pushinteger(L, AcquireToken(tokenbucket.b, luaL_optinteger(L, 1, ip), + tokenbucket.cidr)); + return 1; +} + +static int LuaCountTokens(lua_State *L) { + uint32_t ip; + if (!tokenbucket.cidr) { + luaL_error(L, "ProgramTokenBucket() needs to be called first"); + unreachable; + } + GetClientAddr(&ip, 0); + lua_pushinteger(L, CountTokens(tokenbucket.b, luaL_optinteger(L, 1, ip), + tokenbucket.cidr)); + return 1; +} + +static int LuaProgramTokenBucket(lua_State *L) { + if (tokenbucket.cidr) { + luaL_error(L, "ProgramTokenBucket() can only be called once"); + unreachable; + } + lua_Number replenish = luaL_optnumber(L, 1, 1); // per second + lua_Integer cidr = luaL_optinteger(L, 2, 24); + lua_Integer reject = luaL_optinteger(L, 3, 30); + lua_Integer ignore = luaL_optinteger(L, 4, MIN(reject / 2, 15)); + lua_Integer ban = luaL_optinteger(L, 5, MIN(ignore / 10, 1)); + if (!(1 / 3600. <= replenish && replenish <= 1e6)) { + luaL_argerror(L, 1, "require 1/3600 <= replenish <= 1e6"); + unreachable; + } + if (!(8 <= cidr && cidr <= 32)) { + luaL_argerror(L, 2, "require 8 <= cidr <= 32"); + unreachable; + } + if (!(-1 <= reject && reject <= 126)) { + luaL_argerror(L, 3, "require -1 <= reject <= 126"); + unreachable; + } + if (!(-1 <= ignore && ignore <= 126)) { + luaL_argerror(L, 4, "require -1 <= ignore <= 126"); + unreachable; + } + if (!(-1 <= ban && ban <= 126)) { + luaL_argerror(L, 5, "require -1 <= ban <= 126"); + unreachable; + } + if (!(ignore <= reject)) { + luaL_argerror(L, 4, "require ignore <= reject"); + unreachable; + } + if (!(ban <= ignore)) { + luaL_argerror(L, 5, "require ban <= ignore"); + unreachable; + } + INFOF("deploying %,ld buckets " + "(one for every %ld ips) " + "each holding 127 tokens which " + "replenish %g times per second, " + "reject at %d tokens, " + "ignore at %d tokens, and " + "ban at %d tokens", + 1L << cidr, // + 4294967296 / (1L << cidr), // + replenish, // + reject, // + ignore, // + ban); + if (ignore == -1) ignore = -128; + if (ban == -1) ban = -128; + if (ban >= 0 && (IsLinux() || IsBsd())) { + struct Blackhole bh; + bh.addr.sun_family = AF_UNIX; + strlcpy(bh.addr.sun_path, "/var/run/blackhole.sock", + sizeof(bh.addr.sun_path)); + if ((bh.fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0)) == -1) { + WARNF("error: socket(AF_UNIX) failed: %m"); + ban = -1; + } else if (connect(bh.fd, (struct sockaddr *)&bh.addr, sizeof(bh.addr)) == + -1) { + WARNF("error: connect(%`'s) failed: %m", bh.addr.sun_path); + WARNF("redbean isn't able to protect your kernel from level 4 ddos"); + WARNF("please run the blackholed program, see https://justine.lol/"); + close(bh.fd); + ban = -1; + } else { + blackhole = bh; + } + } + tokenbucket.b = _mapshared(ROUNDUP(1ul << cidr, FRAMESIZE)); + memset(tokenbucket.b, 127, 1ul << cidr); + tokenbucket.cidr = cidr; + tokenbucket.reject = reject; + tokenbucket.ignore = ignore; + tokenbucket.ban = ban; + tokenbucket.replenish = _timespec_fromnanos(1 / replenish * 1e9); + int pid = fork(); + _npassert(pid != -1); + if (!pid) Replenisher(); + return 0; +} + static const char *GetContentTypeExt(const char *path, size_t n) { char *r, *e; int top; @@ -4961,7 +5139,7 @@ static const luaL_Reg kLuaFuncs[] = { {"IsPrivateIp", LuaIsPrivateIp}, // {"IsPublicIp", LuaIsPublicIp}, // {"IsReasonablePath", LuaIsReasonablePath}, // - {"IsTrustedProxy", LuaIsTrustedProxy}, // undocumented + {"IsTrustedIp", LuaIsTrusted}, // undocumented {"IsValidHttpToken", LuaIsValidHttpToken}, // {"LaunchBrowser", LuaLaunchBrowser}, // {"Lemur64", LuaLemur64}, // @@ -4992,6 +5170,7 @@ static const luaL_Reg kLuaFuncs[] = { {"ProgramPort", LuaProgramPort}, // {"ProgramRedirect", LuaProgramRedirect}, // {"ProgramTimeout", LuaProgramTimeout}, // + {"ProgramTrustedIp", LuaProgramTrustedIp}, // undocumented {"ProgramUid", LuaProgramUid}, // {"ProgramUniprocess", LuaProgramUniprocess}, // {"Rand64", LuaRand64}, // @@ -5020,7 +5199,6 @@ static const luaL_Reg kLuaFuncs[] = { {"Sleep", LuaSleep}, // {"Slurp", LuaSlurp}, // {"StoreAsset", LuaStoreAsset}, // - {"TrustProxy", LuaTrustProxy}, // undocumented {"Uncompress", LuaUncompress}, // {"Underlong", LuaUnderlong}, // {"VisualizeControlCodes", LuaVisualizeControlCodes}, // @@ -5029,6 +5207,8 @@ static const luaL_Reg kLuaFuncs[] = { {"hex", LuaHex}, // {"oct", LuaOct}, // #ifndef UNSECURE + {"AcquireToken", LuaAcquireToken}, // + {"CountTokens", LuaCountTokens}, // {"EvadeDragnetSurveillance", LuaEvadeDragnetSurveillance}, // {"GetSslIdentity", LuaGetSslIdentity}, // {"ProgramCertificate", LuaProgramCertificate}, // @@ -5040,6 +5220,7 @@ static const luaL_Reg kLuaFuncs[] = { {"ProgramSslPresharedKey", LuaProgramSslPresharedKey}, // {"ProgramSslRequired", LuaProgramSslRequired}, // {"ProgramSslTicketLifetime", LuaProgramSslTicketLifetime}, // + {"ProgramTokenBucket", LuaProgramTokenBucket}, // #endif // deprecated {"GetPayload", LuaGetBody}, // @@ -5267,6 +5448,8 @@ static void MemDestroy(void) { FreeStrings(&hidepaths); Free(&launchbrowser); Free(&serverheader); + Free(&trustedips.p); + Free(&interfaces); Free(&extrahdrs); Free(&pidpath); Free(&logpath); @@ -5368,6 +5551,16 @@ Content-Length: 0\r\n\ \r\n"); } +static ssize_t SendTooManyRequests(void) { + return SendString("\ +HTTP/1.1 429 Too Many Requests\r\n\ +Connection: close\r\n\ +Content-Type: text/plain\r\n\ +Content-Length: 22\r\n\ +\r\n\ +429 Too Many Requests\n"); +} + static void EnterMeltdownMode(void) { if (shared->lastmeltdown.tv_sec && !_timespec_gte(_timespec_sub(_timespec_real(), shared->lastmeltdown), @@ -5669,7 +5862,7 @@ static void ParseRequestParameters(void) { &url, kUrlPlus | kUrlLatin1)); if (!url.host.p) { if (HasHeader(kHttpXForwardedHost) && // - !GetRemoteAddr(&ip, 0) && IsTrustedProxy(ip)) { + !GetRemoteAddr(&ip, 0) && IsTrustedIp(ip)) { FreeLater(ParseHost(HeaderData(kHttpXForwardedHost), HeaderLength(kHttpXForwardedHost), &url)); } else if (HasHeader(kHttpHost)) { @@ -5925,12 +6118,17 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) { } else { return ServeError(500, "Internal Server Error"); } +#if 0 // TODO(jart): re-enable me } else if (!IsTiny() && cpm.msg.method != kHttpHead && !IsSslCompressed() && ClientAcceptsGzip() && + !(a->file && + IsNoCompressExt(a->file->path.s, a->file->path.n)) && ((cpm.contentlength >= 100 && _startswithi(ct, "text/")) || (cpm.contentlength >= 1000 && MeasureEntropy(cpm.content, 1000) < 7))) { + WARNF("serving compressed asset"); p = ServeAssetCompressed(a); +#endif } else { p = ServeAssetIdentity(a, ct); } @@ -6490,10 +6688,41 @@ static void MonitorMemory(void) { } static int HandleConnection(size_t i) { - int pid, rc = 0; + uint32_t ip; + int pid, tok, rc = 0; clientaddrsize = sizeof(clientaddr); if ((client = accept4(servers.p[i].fd, (struct sockaddr *)&clientaddr, &clientaddrsize, SOCK_CLOEXEC)) != -1) { + LockInc(&shared->c.accepts); + GetClientAddr(&ip, 0); + if (tokenbucket.cidr && tokenbucket.reject >= 0) { + if (!IsLoopbackIp(ip) && !IsTrustedIp(ip)) { + tok = AcquireToken(tokenbucket.b, ip, tokenbucket.cidr); + if (tok <= tokenbucket.ban && tokenbucket.ban >= 0) { + WARNF("(srvr) banning %hhu.%hhu.%hhu.%hhu who only has %d tokens", + ip >> 24, ip >> 16, ip >> 8, ip, tok); + LockInc(&shared->c.bans); + Blackhole(ip); + close(client); + return 0; + } else if (tok <= tokenbucket.ignore && tokenbucket.ignore >= 0) { + LockInc(&shared->c.ignores); + close(client); + return 0; + } else if (tok < tokenbucket.reject) { + WARNF("(srvr) rejecting %hhu.%hhu.%hhu.%hhu who only has %d tokens", + ip >> 24, ip >> 16, ip >> 8, ip, tok); + LockInc(&shared->c.rejects); + SendTooManyRequests(); + close(client); + return 0; + } + } else { + DEBUGF( + "(srvr) won't acquire token for whitelisted ip %hhu.%hhu.%hhu.%hhu", + ip >> 24, ip >> 16, ip >> 8, ip); + } + } startconnection = _timespec_real(); if (UNLIKELY(maxworkers) && shared->workers >= maxworkers) { EnterMeltdownMode(); @@ -6741,14 +6970,14 @@ static int HandlePoll(int ms) { static void Listen(void) { char ipbuf[16]; size_t i, j, n; - uint32_t ip, port, addrsize, *ifs, *ifp; + uint32_t ip, port, addrsize, *ifp; bool hasonserverlisten = IsHookDefined("OnServerListen"); if (!ports.n) { ProgramPort(8080); } if (!ips.n) { - if ((ifs = GetHostIps()) && *ifs) { - for (ifp = ifs; *ifp; ++ifp) { + if (interfaces && *interfaces) { + for (ifp = interfaces; *ifp; ++ifp) { sprintf(ipbuf, "%hhu.%hhu.%hhu.%hhu", *ifp >> 24, *ifp >> 16, *ifp >> 8, *ifp); ProgramAddr(ipbuf); @@ -6756,7 +6985,6 @@ static void Listen(void) { } else { ProgramAddr("0.0.0.0"); } - free(ifs); } servers.p = malloc(ips.n * ports.n * sizeof(*servers.p)); for (n = i = 0; i < ips.n; ++i) { @@ -7108,6 +7336,10 @@ void RedBean(int argc, char *argv[]) { SetDefaults(); LuaStart(); GetOpts(argc, argv); + // this can fail with EPERM if we're running under pledge() + if (!interpretermode && !(interfaces = GetHostIps())) { + WARNF("(srvr) failed to query system network interface addresses: %m"); + } #ifndef STATIC if (selfmodifiable) { MakeExecutableModifiable();