Make fixes and improvements

- Invent iso8601us() for faster timestamps
- Improve --strace descriptions of sigset_t
- Rebuild the Landlock Make bootstrap binary
- Introduce MODE=sysv for non-Windows builds
- Permit OFD fcntl() locks under pledge(flock)
- redbean can now protect your kernel from ddos
- Have vfork() fallback to sys_fork() not fork()
- Change kmalloc() to not die when out of memory
- Improve documentation for some termios functions
- Rewrite putenv() and friends to conform to POSIX
- Fix linenoise + strace verbosity issue on Windows
- Fix regressions in our ability to show backtraces
- Change redbean SetHeader() to no-op if value is nil
- Improve fcntl() so SQLite locks work in non-WAL mode
- Remove some unnecessary work during fork() on Windows
- Create redbean-based SSL reverse proxy for IPv4 TurfWar
- Fix ape/apeinstall.sh warning when using non-bash shells
- Add ProgramTrustedIp(), and IsTrustedIp() APIs to redbean
- Support $PWD, $UID, $GID, and $EUID in command interpreter
- Introduce experimental JTqFpD APE prefix for non-Windows builds
- Invent blackhole daemon for firewalling IP addresses via UNIX named socket
- Add ProgramTokenBucket(), AcquireToken(), and CountTokens() APIs to redbean
This commit is contained in:
Justine Tunney 2022-10-17 11:02:04 -07:00
parent 648bf6555c
commit f7ff77d865
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
209 changed files with 3818 additions and 998 deletions

View file

@ -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

View file

@ -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

View file

@ -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

Binary file not shown.

View file

@ -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 \

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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\

42
examples/symtab.c Normal file
View file

@ -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);
}

View file

@ -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"

View file

@ -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_ */

View file

@ -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_

88
libc/calls/cfspeed.c Normal file
View file

@ -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;
}
}

View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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),

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/assert.h"
#include "libc/calls/calls.h"
#include "libc/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;
}

View file

@ -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

View file

@ -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);

View file

@ -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) {

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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));
}

View file

@ -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;

View file

@ -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;

View file

@ -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));
}

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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;

View file

@ -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;

View file

@ -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"

View file

@ -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;
}

View file

@ -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;
};

View file

@ -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_

View file

@ -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) */

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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};
}

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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("}");

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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))

View file

@ -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;
}
}

View file

@ -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) */

View file

@ -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;
}

View file

@ -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;

View file

@ -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 {

View file

@ -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);
}

View file

@ -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));

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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"
;
}

View file

@ -0,0 +1,3 @@
M(SIGKILL)
M(SIGABRT)
M(SIGSTOP)

View file

@ -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]) {

View file

@ -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;

View file

@ -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());
}

View file

@ -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;

View file

@ -42,6 +42,7 @@ LIBC_LOG_A_DIRECTDEPS = \
LIBC_SYSV_CALLS \
LIBC_TIME \
LIBC_TINYMATH \
LIBC_ZIPOS \
THIRD_PARTY_DLMALLOC \
THIRD_PARTY_GDTOA

View file

@ -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

View file

@ -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;
}

View file

@ -10,7 +10,6 @@ struct CritbitNode {
};
int PutEnvImpl(char *, bool) hidden;
void __freeenv(void *) hidden;
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

93
libc/mem/sortedints.c Normal file
View file

@ -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;
}

View file

@ -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_ */

View file

@ -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

View file

@ -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

36
libc/nt/comms.h Normal file
View file

@ -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_ */

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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()");
}

12
libc/runtime/morph.h Normal file
View file

@ -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_ */

View file

@ -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);

View file

@ -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);
}

View file

@ -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__ */

View file

@ -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;
}

Some files were not shown because too many files have changed in this diff Show more