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

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

View file

@ -17,17 +17,72 @@
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) return einval();
t->c_cflag &= ~CBAUD;
t->c_cflag |= speed;
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

@ -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/internal.h"
#include "libc/intrin/atomic.h"
#include "libc/macros.internal.h"
#include "libc/str/str.h"
uint32_t cfgetospeed(const struct termios *t) {
if (CBAUD) {
return t->c_cflag & CBAUD;
} else {
return t->c_ospeed;
}
// really want to avoid locking here so close() needn't block signals
void __releasefd(int fd) {
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

@ -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,16 @@
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/struct/sigset.h"
uint32_t cfgetispeed(const struct termios *t) {
if (CBAUD) {
return t->c_cflag & CBAUD;
} else {
return t->c_ispeed;
}
/**
* 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,56 +0,0 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2020 Justine Alexandra Roberts Tunney
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/dce.h"
#include "libc/intrin/weaken.h"
#include "libc/mem/internal.h"
#include "libc/runtime/runtime.h"
#define ToUpper(c) \
(IsWindows() && (c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
/**
* Removes environment variable.
*/
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;
}
}
}
}
return 0;
}