diff --git a/ape/ape-m1.c b/ape/ape-m1.c index ba07d178d..6db098e53 100644 --- a/ape/ape-m1.c +++ b/ape/ape-m1.c @@ -68,6 +68,16 @@ struct Syslib { int (*pthread_attr_destroy)(pthread_attr_t *); int (*pthread_attr_setstacksize)(pthread_attr_t *, size_t); int (*pthread_attr_setguardsize)(pthread_attr_t *, size_t); + /* v4 (2023-09-19) */ + void (*exit)(int); + long (*close)(int); + long (*munmap)(void *, size_t); + long (*openat)(int, const char *, int, int); + long (*write)(int, const void *, size_t); + long (*read)(int, void *, size_t); + long (*sigaction)(int, const struct sigaction *, struct sigaction *); + long (*pselect)(int, fd_set *, fd_set *, fd_set *, const struct timespec *, const sigset_t *); + long (*mprotect)(void *, size_t, int); }; #define ELFCLASS32 1 @@ -792,14 +802,38 @@ static long sys_fork(void) { return sysret(fork()); } +static long sys_close(int fd) { + return sysret(close(fd)); +} + static long sys_pipe(int pfds[2]) { return sysret(pipe(pfds)); } +static long sys_munmap(void *addr, size_t size) { + return sysret(munmap(addr, size)); +} + +static long sys_read(int fd, void *data, size_t size) { + return sysret(read(fd, data, size)); +} + +static long sys_mprotect(void *data, size_t size, int prot) { + return sysret(mprotect(data, size, prot)); +} + +static long sys_write(int fd, const void *data, size_t size) { + return sysret(write(fd, data, size)); +} + static long sys_clock_gettime(int clock, struct timespec *ts) { return sysret(clock_gettime((clockid_t)clock, ts)); } +static long sys_openat(int fd, const char *path, int flags, int mode) { + return sysret(openat(fd, path, flags, mode)); +} + static long sys_nanosleep(const struct timespec *req, struct timespec *rem) { return sysret(nanosleep(req, rem)); } @@ -809,6 +843,15 @@ static long sys_mmap(void *addr, size_t size, int prot, int flags, int fd, return sysret((long)mmap(addr, size, prot, flags, fd, off)); } +static long sys_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) { + return sysret(sigaction(sig, act, oact)); +} + +static long sys_pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, + const struct timespec *timeout, const sigset_t *sigmask) { + return sysret(pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)); +} + int main(int argc, char **argv, char **envp) { unsigned i; int c, n, fd, rc; @@ -852,6 +895,15 @@ int main(int argc, char **argv, char **envp) { M->lib.pthread_attr_destroy = pthread_attr_destroy; M->lib.pthread_attr_setstacksize = pthread_attr_setstacksize; M->lib.pthread_attr_setguardsize = pthread_attr_setguardsize; + M->lib.exit = exit; + M->lib.close = sys_close; + M->lib.munmap = sys_munmap; + M->lib.openat = sys_openat; + M->lib.write = sys_write; + M->lib.read = sys_read; + M->lib.sigaction = sys_sigaction; + M->lib.pselect = sys_pselect; + M->lib.mprotect = sys_mprotect; /* getenv("_") is close enough to at_execfn */ execfn = argc > 0 ? argv[0] : 0; diff --git a/build/definitions.mk b/build/definitions.mk index 450e98dd0..21552bc23 100644 --- a/build/definitions.mk +++ b/build/definitions.mk @@ -86,7 +86,7 @@ IGNORE := $(shell $(MKDIR) $(TMPDIR)) ifneq ($(findstring aarch64,$(MODE)),) ARCH = aarch64 -HOSTS ?= pi silicon-wifi +HOSTS ?= pi silicon else ARCH = x86_64 HOSTS ?= freebsd rhel7 rhel5 xnu win10 openbsd netbsd diff --git a/build/online.mk b/build/online.mk index ffe5d6b86..ce84893b9 100644 --- a/build/online.mk +++ b/build/online.mk @@ -28,7 +28,7 @@ o/$(MODE)/%.com.ok: \ o/$(MODE)/tool/build/runit.com \ o/$(MODE)/tool/build/runitd.com \ o/$(MODE)/%.com - $(COMPILE) -wATEST -tT$@ $^ $(HOSTS) + @$(COMPILE) -wATEST -tT$@ $^ $(HOSTS) .PHONY: o/tiny/tool/build/runit.com: diff --git a/examples/ttyinfo.c b/examples/ttyinfo.c index 50b7c77e6..8672aecdb 100644 --- a/examples/ttyinfo.c +++ b/examples/ttyinfo.c @@ -8,14 +8,17 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "dsp/tty/tty.h" +#include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/sigset.h" #include "libc/calls/termios.h" #include "libc/errno.h" #include "libc/fmt/fmt.h" +#include "libc/intrin/kprintf.h" #include "libc/log/check.h" #include "libc/log/log.h" -#include "libc/nt/runtime.h" #include "libc/runtime/runtime.h" #include "libc/sock/select.h" #include "libc/stdio/dprintf.h" @@ -36,29 +39,30 @@ #define PROBE_DISPLAY_SIZE "\e7\e[9979;9979H\e[6n\e8" char code[512]; +int infd, outfd; struct winsize wsize; struct termios oldterm; volatile bool resized, killed; -void onresize(void) { +void OnResize(int sig) { resized = true; } -void onkilled(int sig) { +void OnKilled(int sig) { killed = true; } -void restoretty(void) { - WRITE(1, DISABLE_MOUSE_TRACKING); - tcsetattr(1, TCSANOW, &oldterm); +void RestoreTty(void) { + WRITE(outfd, DISABLE_MOUSE_TRACKING); + tcsetattr(outfd, TCSANOW, &oldterm); } -int rawmode(void) { +int EnableRawMode(void) { static bool once; struct termios t; if (!once) { - if (!tcgetattr(1, &oldterm)) { - atexit(restoretty); + if (!tcgetattr(outfd, &oldterm)) { + atexit(RestoreTty); } else { perror("tcgetattr"); } @@ -78,26 +82,26 @@ int rawmode(void) { t.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON | IGNBRK | BRKINT); - t.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL | ISIG); + t.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL /* | ISIG */); t.c_cflag &= ~(CSIZE | PARENB); t.c_oflag |= OPOST | ONLCR; t.c_cflag |= CS8; t.c_iflag |= IUTF8; - if (tcsetattr(1, TCSANOW, &t)) { + if (tcsetattr(outfd, TCSANOW, &t)) { perror("tcsetattr"); } - /* WRITE(1, ENABLE_SAFE_PASTE); */ - /* WRITE(1, ENABLE_MOUSE_TRACKING); */ - /* WRITE(1, PROBE_DISPLAY_SIZE); */ + WRITE(outfd, ENABLE_MOUSE_TRACKING); + /* WRITE(outfd, ENABLE_SAFE_PASTE); */ + /* WRITE(outfd, PROBE_DISPLAY_SIZE); */ return 0; } -void getsize(void) { - if (tcgetwinsize(1, &wsize) != -1) { - printf("termios says terminal size is %hu×%hu\r\n", wsize.ws_col, - wsize.ws_row); +void GetTtySize(void) { + if (tcgetwinsize(outfd, &wsize) != -1) { + dprintf(outfd, "termios says terminal size is %hu×%hu\r\n", wsize.ws_col, + wsize.ws_row); } else { perror("tcgetwinsize"); } @@ -144,50 +148,63 @@ const char *describemouseevent(int e) { // change the code above to enable ISIG if you want to trigger this // then press ctrl-c or ctrl-\ in your pseudoteletypewriter console void OnSignalThatWontEintrRead(int sig) { - dprintf(1, "got %s (read()ing will SA_RESTART)\n", strsignal(sig)); + dprintf(outfd, "got %s (read()ing will SA_RESTART; try CTRL-D to exit)\n", + strsignal(sig)); +} + +void OnSignalThatWillEintrRead(int sig) { + dprintf(outfd, "got %s (read() operation will be aborted)\n", strsignal(sig)); } int main(int argc, char *argv[]) { int e, c, y, x, n, yn, xn; + infd = 0; + outfd = 1; + /* infd = outfd = open("/dev/tty", O_RDWR); */ + signal(SIGTERM, OnKilled); + signal(SIGCONT, OnResize); + signal(SIGWINCH, OnResize); signal(SIGINT, OnSignalThatWontEintrRead); - signal(SIGQUIT, OnSignalThatWontEintrRead); - xsigaction(SIGTERM, onkilled, 0, 0, NULL); - xsigaction(SIGWINCH, onresize, 0, 0, NULL); - xsigaction(SIGCONT, onresize, 0, 0, NULL); - rawmode(); - getsize(); + sigaction(SIGQUIT, + &(struct sigaction){.sa_handler = OnSignalThatWillEintrRead}, 0); + EnableRawMode(); + GetTtySize(); while (!killed) { if (resized) { - printf("SIGWINCH "); - getsize(); + dprintf(outfd, "SIGWINCH "); + GetTtySize(); resized = false; } bzero(code, sizeof(code)); - if ((n = readansi(0, code, sizeof(code))) == -1) { - if (errno == EINTR) continue; + if ((n = read(infd, code, sizeof(code))) == -1) { + if (errno == EINTR) { + dprintf(outfd, "read() was interrupted\n"); + continue; + } perror("read"); exit(1); } if (!n) { - printf("got stdin eof\n"); + dprintf(outfd, "got stdin eof\n"); exit(0); } - printf("%`'.*s (got %d) ", n, code, n); + dprintf(outfd, "%`'.*s (got %d) ", n, code, n); if (iscntrl(code[0]) && !code[1]) { - printf("is CTRL-%c a.k.a. ^%c\r\n", CTRL(code[0]), CTRL(code[0])); + dprintf(outfd, "is CTRL-%c a.k.a. ^%c\r\n", CTRL(code[0]), CTRL(code[0])); if (code[0] == CTRL('C') || code[0] == CTRL('D')) break; } else if (startswith(code, "\e[") && endswith(code, "R")) { yn = 1, xn = 1; sscanf(code, "\e[%d;%dR", &yn, &xn); - printf("inband signalling says terminal size is %d×%d\r\n", xn, yn); + dprintf(outfd, "inband signalling says terminal size is %d×%d\r\n", xn, + yn); } else if (startswith(code, "\e[<") && (endswith(code, "m") || endswith(code, "M"))) { e = 0, y = 1, x = 1; sscanf(code, "\e[<%d;%d;%d%c", &e, &y, &x, &c); - printf("mouse %s at %d×%d\r\n", describemouseevent(e | (c == 'm') << 2), - x, y); + dprintf(outfd, "mouse %s at %d×%d\r\n", + describemouseevent(e | (c == 'm') << 2), x, y); } else { - printf("\r\n"); + dprintf(outfd, "\r\n"); } } return 0; diff --git a/libc/calls/console.internal.h b/libc/calls/console.internal.h deleted file mode 100644 index f4344682f..000000000 --- a/libc/calls/console.internal.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef COSMOPOLITAN_LIBC_CALLS_CONSOLE_INTERNAL_H_ -#define COSMOPOLITAN_LIBC_CALLS_CONSOLE_INTERNAL_H_ -#include "libc/calls/struct/fd.internal.h" -#include "libc/nt/struct/inputrecord.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ - -int CountConsoleInputBytes(int64_t); -int ConvertConsoleInputToAnsi(const struct NtInputRecord *, char[hasatleast 32], - uint16_t *, struct Fd *); - -COSMOPOLITAN_C_END_ -#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#endif /* COSMOPOLITAN_LIBC_CALLS_CONSOLE_INTERNAL_H_ */ diff --git a/libc/calls/fstatfs.c b/libc/calls/fstatfs.c index f29def020..a9cfcb7b2 100644 --- a/libc/calls/fstatfs.c +++ b/libc/calls/fstatfs.c @@ -59,3 +59,5 @@ int fstatfs(int fd, struct statfs *sf) { STRACE("fstatfs(%d, [%s]) → %d% m", fd, DescribeStatfs(rc, sf)); return rc; } + +__weak_reference(fstatfs, fstatfs64); diff --git a/libc/calls/getrlimit.c b/libc/calls/getrlimit.c index 54f66935c..de1c84668 100644 --- a/libc/calls/getrlimit.c +++ b/libc/calls/getrlimit.c @@ -57,3 +57,5 @@ int getrlimit(int resource, struct rlimit *rlim) { DescribeRlimit(rc, rlim), rc); return rc; } + +__weak_reference(getrlimit, getrlimit64); diff --git a/libc/calls/internal.h b/libc/calls/internal.h index 5d9651c46..38f2b7e00 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -25,6 +25,9 @@ int __ensurefds(int); int __ensurefds_unlocked(int); void __printfds(void); uint32_t sys_getuid_nt(void); +int __pause_thread(uint32_t); +int CountConsoleInputBytes(int64_t); +int FlushConsoleInputBytes(int64_t); forceinline int64_t __getfdhandleactual(int fd) { return g_fds.p[fd].handle; diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index ae89b7123..618f7dcee 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -19,6 +19,7 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/errno.h" +#include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" #include "libc/sysv/errfuns.h" #include "libc/thread/posixthread.internal.h" @@ -33,7 +34,9 @@ textwindows int _check_interrupts(int sigops) { goto Interrupted; } if (_weaken(__sig_check) && (status = _weaken(__sig_check)())) { + STRACE("syscall interrupted (status=%d, sigops=%d)", status, sigops); if (status == 2 && (sigops & kSigOpRestartable)) { + STRACE("restarting system call"); return 0; } err = EINTR; diff --git a/libc/calls/ioctl.c b/libc/calls/ioctl.c index cb67b658a..d2dab5cfa 100644 --- a/libc/calls/ioctl.c +++ b/libc/calls/ioctl.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" -#include "libc/calls/console.internal.h" #include "libc/calls/internal.h" #include "libc/calls/struct/fd.internal.h" #include "libc/calls/syscall-sysv.internal.h" @@ -91,10 +90,9 @@ static int ioctl_default(int fd, unsigned long request, void *arg) { } static int ioctl_fionread(int fd, uint32_t *arg) { + int rc; uint32_t cm; int64_t handle; - uint32_t avail; - int rc, e = errno; if (!IsWindows()) { return sys_ioctl(fd, FIONREAD, arg); } else if (__isfdopen(fd)) { @@ -106,19 +104,18 @@ static int ioctl_fionread(int fd, uint32_t *arg) { return _weaken(__winsockerr)(); } } else if (GetFileType(handle) == kNtFileTypePipe) { + uint32_t avail; if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) { *arg = avail; return 0; + } else if (GetLastError() == kNtErrorBrokenPipe) { + return 0; } else { return __winerr(); } } else if (GetConsoleMode(handle, &cm)) { - avail = CountConsoleInputBytes(handle); - if (avail == -1u && errno == ENODATA) { - errno = e; - avail = 0; - } - return avail; + int bytes = CountConsoleInputBytes(handle); + return MAX(0, bytes); } else { return eopnotsupp(); } diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index 40ed5551c..612a04f10 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -144,33 +144,13 @@ static textwindows int sys_open_nt_console(int dirfd, const struct NtMagicPaths *mp, uint32_t flags, int32_t mode, size_t fd) { - uint32_t cm; - int input, output; - if ((__isfdopen((input = STDIN_FILENO)) && - GetConsoleMode(g_fds.p[input].handle, &cm)) && - ((__isfdopen((output = STDOUT_FILENO)) && - GetConsoleMode(g_fds.p[output].handle, &cm)) || - (__isfdopen((output = STDERR_FILENO)) && - GetConsoleMode(g_fds.p[output].handle, &cm)))) { - // this is an ugly hack that works for observed usage patterns - g_fds.p[fd].handle = g_fds.p[input].handle; - g_fds.p[fd].extra = g_fds.p[output].handle; - g_fds.p[fd].dontclose = true; - g_fds.p[input].dontclose = true; - g_fds.p[output].dontclose = true; - } else if ((g_fds.p[fd].handle = sys_open_nt_impl( - dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode, - kNtFileFlagOverlapped)) != -1) { - g_fds.p[fd].extra = - sys_open_nt_impl(dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY, - mode, kNtFileFlagOverlapped); - npassert(g_fds.p[fd].extra != -1); - } else { - return -1; - } g_fds.p[fd].kind = kFdConsole; g_fds.p[fd].flags = flags; g_fds.p[fd].mode = mode; + g_fds.p[fd].handle = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite, + kNtFileShareRead, 0, kNtOpenExisting, 0, 0); + g_fds.p[fd].extra = CreateFile(u"CONOUT$", kNtGenericRead | kNtGenericWrite, + kNtFileShareWrite, 0, kNtOpenExisting, 0, 0); return fd; } diff --git a/libc/calls/overlap.h b/libc/calls/overlap.h new file mode 100755 index 000000000..e69de29bb diff --git a/libc/calls/overlapped.c b/libc/calls/overlapped.c new file mode 100644 index 000000000..be311a243 --- /dev/null +++ b/libc/calls/overlapped.c @@ -0,0 +1,31 @@ +/*-*- 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 2023 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/overlap.h" +#include "libc/calls/overlapped.internal.h" +#include "libc/nt/files.h" +#include "libc/nt/runtime.h" +#include "libc/nt/thread.h" + +void overlapped_cleanup_callback(void *arg) { + uint32_t got; + struct OverlappedCleanup *cleanup = arg; + CancelIoEx(cleanup->handle, cleanup->overlap); + GetOverlappedResult(cleanup->handle, cleanup->overlap, &got, true); + CloseHandle(cleanup->overlap->hEvent); +} diff --git a/libc/calls/overlapped.internal.h b/libc/calls/overlapped.internal.h new file mode 100644 index 000000000..a07ca7e48 --- /dev/null +++ b/libc/calls/overlapped.internal.h @@ -0,0 +1,25 @@ +#ifndef COSMOPOLITAN_LIBC_CALLS_OVERLAPPED_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_CALLS_OVERLAPPED_INTERNAL_H_ +#include "libc/nt/struct/overlapped.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define overlapped_cleanup_push(handle, overlap) \ + { \ + struct OverlappedCleanup overlapped_cleanup = {handle, overlap}; \ + pthread_cleanup_push(overlapped_cleanup_callback, &overlapped_cleanup); + +#define overlapped_cleanup_pop() \ + pthread_cleanup_pop(false); \ + } + +struct OverlappedCleanup { + int64_t handle; + struct NtOverlapped *overlap; +}; + +void overlapped_cleanup_callback(void *); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_CALLS_OVERLAPPED_INTERNAL_H_ */ diff --git a/libc/calls/pause-nt.c b/libc/calls/pause-nt.c index 6db3a9292..5467c81d1 100644 --- a/libc/calls/pause-nt.c +++ b/libc/calls/pause-nt.c @@ -19,22 +19,11 @@ #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/syscall_support-nt.internal.h" -#include "libc/errno.h" -#include "libc/nt/synchronization.h" -#include "libc/thread/posixthread.internal.h" -#include "libc/thread/tls.h" textwindows int sys_pause_nt(void) { int rc; while (!(rc = _check_interrupts(0))) { - struct PosixThread *pt = _pthread_self(); - pt->abort_errno = 0; - pt->pt_flags |= PT_INSEMAPHORE; - WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS); - pt->pt_flags &= ~PT_INSEMAPHORE; - if (pt->abort_errno) { - errno = pt->abort_errno; - rc = -1; + if ((rc = __pause_thread(__SIG_SIG_INTERVAL_MS))) { break; } } diff --git a/libc/calls/pause.c b/libc/calls/pause.c index 406299537..597c4a49f 100644 --- a/libc/calls/pause.c +++ b/libc/calls/pause.c @@ -37,7 +37,9 @@ * * However this has a tinier footprint and better logging. * - * @return -1 w/ errno set to EINTR + * @return -1 w/ errno + * @raise ECANCELED if this thread was canceled in masked mode + * @raise EINTR if interrupted by a signal * @cancellationpoint * @see sigsuspend() * @norestart diff --git a/libc/calls/fionread-nt.c b/libc/calls/pausethread.c similarity index 68% rename from libc/calls/fionread-nt.c rename to libc/calls/pausethread.c index 01bb33e62..673a37118 100644 --- a/libc/calls/fionread-nt.c +++ b/libc/calls/pausethread.c @@ -16,41 +16,21 @@ │ 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/console.internal.h" -#include "libc/calls/struct/fd.internal.h" -#include "libc/calls/syscall_support-nt.internal.h" #include "libc/errno.h" -#include "libc/macros.internal.h" -#include "libc/nt/console.h" -#include "libc/nt/struct/inputrecord.h" -#ifdef __x86_64__ +#include "libc/nt/synchronization.h" +#include "libc/thread/posixthread.internal.h" -int CountConsoleInputBytes(int64_t handle) { - char buf[32]; - int rc, e = errno; - uint16_t utf16hs = 0; - uint32_t i, n, count; - struct NtInputRecord records[64]; - if (PeekConsoleInput(handle, records, ARRAYLEN(records), &n)) { - for (rc = i = 0; i < n; ++i) { - count = ConvertConsoleInputToAnsi(records + i, buf, &utf16hs, 0); - if (count == -1) { - unassert(errno == ENODATA); - if (!rc) { - rc = -1; - } else { - errno = e; - } - break; - } - rc += count; - } - } else { - rc = __winerr(); +textwindows int __pause_thread(uint32_t ms) { + uint32_t status; + struct PosixThread *pt = _pthread_self(); + pt->pt_flags |= PT_INSEMAPHORE; + status = WaitForSingleObject(pt->semaphore, ms); + if (status == -1u) notpossible; + if (!(pt->pt_flags & PT_INSEMAPHORE)) { + errno = pt->abort_errno; + return -1; } - return rc; + pt->pt_flags &= ~PT_INSEMAPHORE; + return 0; } - -#endif /* __x86_64__ */ diff --git a/libc/calls/pledge.c b/libc/calls/pledge.c index f5a19f927..bcfd10f8f 100644 --- a/libc/calls/pledge.c +++ b/libc/calls/pledge.c @@ -35,7 +35,7 @@ * Permits system operations, e.g. * * __pledge_mode = PLEDGE_PENALTY_KILL_PROCESS | PLEDGE_STDERR_LOGGING; - * if (pledge("stdio rfile tty", 0)) { + * if (pledge("stdio rpath tty", 0)) { * perror("pledge"); * exit(1); * } diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index 2e70adc07..563dcc36b 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -16,13 +16,16 @@ │ 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/console.internal.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/struct/sigaction.h" +#include "libc/calls/struct/timespec.h" +#include "libc/dce.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/bits.h" #include "libc/intrin/strace.internal.h" #include "libc/macros.internal.h" @@ -46,38 +49,37 @@ #include "libc/sysv/consts/sig.h" #include "libc/sysv/errfuns.h" #include "libc/thread/posixthread.internal.h" +#include "libc/thread/tls.h" #ifdef __x86_64__ -/* - * Polls on the New Technology. - * - * This function is used to implement poll() and select(). You may poll - * on both sockets and files at the same time. We also poll for signals - * while poll is polling. - */ -textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms, +// Polls on the New Technology. +// +// This function is used to implement poll() and select(). You may poll +// on both sockets and files at the same time. We also poll for signals +// while poll is polling. +textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms, const sigset_t *sigmask) { bool ok; + uint64_t m; + bool interrupted; sigset_t oldmask; - uint32_t avail, cm; + uint32_t cm, avail, waitfor; struct sys_pollfd_nt pipefds[8]; struct sys_pollfd_nt sockfds[64]; int pipeindices[ARRAYLEN(pipefds)]; int sockindices[ARRAYLEN(sockfds)]; - int i, rc, sn, pn, gotinvals, gotpipes, gotsocks, waitfor; + int i, rc, sn, pn, gotinvals, gotpipes, gotsocks; - // check for interrupts early before doing work - if (sigmask) { - __sig_mask(SIG_SETMASK, sigmask, &oldmask); - } - if ((rc = _check_interrupts(0))) { - goto ReturnPath; - } +#if IsModeDbg() + struct timespec noearlier = + timespec_add(timespec_real(), timespec_frommillis(ms ? *ms : -1u)); +#endif // do the planning // we need to read static variables // we might need to spawn threads and open pipes + m = atomic_exchange(&__get_tls()->tib_sigmask, -1); __fds_lock(); for (gotinvals = rc = sn = pn = i = 0; i < nfds; ++i) { if (fds[i].fd < 0) continue; @@ -125,9 +127,10 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms, } } __fds_unlock(); + atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release); if (rc) { // failed to create a polling solution - goto ReturnPath; + goto Finished; } // perform the i/o and sleeping and looping @@ -152,17 +155,8 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms, pipefds[i].revents |= POLLERR; } } else if (GetConsoleMode(pipefds[i].handle, &cm)) { - int e = errno; - avail = CountConsoleInputBytes(pipefds[i].handle); - if (avail > 0) { + if (CountConsoleInputBytes(pipefds[i].handle)) { pipefds[i].revents |= POLLIN; - } else if (avail == -1u) { - if (errno == ENODATA) { - pipefds[i].revents |= POLLIN; - } else { - pipefds[i].revents |= POLLERR; - } - errno = e; } } else { // we have no way of polling if a non-socket is readable yet @@ -176,42 +170,35 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms, } // if we haven't found any good results yet then here we // compute a small time slice we don't mind sleeping for - waitfor = gotinvals || gotpipes ? 0 : MIN(__SIG_POLL_INTERVAL_MS, *ms); if (sn) { -#if _NTTRACE - POLLTRACE("WSAPoll(%p, %u, %'d) out of %'lu", sockfds, sn, waitfor, *ms); -#endif if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1) { - rc = __winsockerr(); - goto ReturnPath; + return __winsockerr(); } } else { gotsocks = 0; } + waitfor = MIN(__SIG_POLL_INTERVAL_MS, *ms); if (!gotinvals && !gotsocks && !gotpipes && waitfor) { - POLLTRACE("sleeping for %'d out of %'lu ms", waitfor, *ms); + POLLTRACE("poll() sleeping for %'d out of %'u ms", waitfor, *ms); struct PosixThread *pt = _pthread_self(); pt->abort_errno = 0; - pt->pt_flags |= PT_INSEMAPHORE; - WaitForSingleObject(pt->semaphore, waitfor); - pt->pt_flags &= ~PT_INSEMAPHORE; - if (pt->abort_errno) { - errno = pt->abort_errno; - rc = -1; - goto ReturnPath; + if (sigmask) __sig_mask(SIG_SETMASK, sigmask, &oldmask); + interrupted = _check_interrupts(0) || __pause_thread(waitfor); + if (sigmask) __sig_mask(SIG_SETMASK, &oldmask, 0); + if (interrupted) return -1; + if (*ms != -1u) { + if (waitfor < *ms) { + *ms -= waitfor; + } else { + *ms = 0; + } } - *ms -= waitfor; // todo: make more resilient } // we gave all the sockets and all the named pipes a shot // if we found anything at all then it's time to end work - if (gotinvals || gotpipes || gotsocks || *ms <= 0) { + if (gotinvals || gotpipes || gotsocks || !*ms) { break; } - // otherwise loop limitlessly for timeout to elapse while - // checking for signal delivery interrupts, along the way - if ((rc = _check_interrupts(0))) { - goto ReturnPath; - } } // the system call is going to succeed @@ -233,10 +220,16 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms, // and finally return rc = gotinvals + gotpipes + gotsocks; -ReturnPath: - if (sigmask) { - __sig_mask(SIG_SETMASK, &oldmask, 0); +Finished: + +#if IsModeDbg() + struct timespec ended = timespec_real(); + if (!rc && timespec_cmp(ended, noearlier) < 0) { + STRACE("poll() ended %'ld ns too soon!", + timespec_tonanos(timespec_sub(noearlier, ended))); } +#endif + return rc; } diff --git a/libc/calls/poll.c b/libc/calls/poll.c index 8007533b4..c84a40aea 100644 --- a/libc/calls/poll.c +++ b/libc/calls/poll.c @@ -68,7 +68,6 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) { int rc; size_t n; - uint64_t millis; BEGIN_CANCELLATION_POINT; if (IsAsan() && @@ -81,9 +80,9 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) { rc = sys_poll_metal(fds, nfds, timeout_ms); } } else { - millis = timeout_ms; BEGIN_BLOCKING_OPERATION; - rc = sys_poll_nt(fds, nfds, &millis, 0); + uint32_t ms = timeout_ms >= 0 ? timeout_ms : -1u; + rc = sys_poll_nt(fds, nfds, &ms, 0); END_BLOCKING_OPERATION; } diff --git a/libc/calls/ppoll.c b/libc/calls/ppoll.c index cd0f93f94..b5cebed28 100644 --- a/libc/calls/ppoll.c +++ b/libc/calls/ppoll.c @@ -64,7 +64,6 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout, const sigset_t *sigmask) { size_t n; int e, rc; - uint64_t millis; sigset_t oldmask; struct timespec ts, *tsp; BEGIN_CANCELLATION_POINT; @@ -84,22 +83,24 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout, } rc = sys_ppoll(fds, nfds, tsp, sigmask, 8); if (rc == -1 && errno == ENOSYS) { + int ms; errno = e; - if (!timeout || - ckd_add(&millis, timeout->tv_sec, timeout->tv_nsec / 1000000)) { - millis = -1; + if (!timeout || ckd_add(&ms, timeout->tv_sec, + (timeout->tv_nsec + 999999) / 1000000)) { + ms = -1; } if (sigmask) sys_sigprocmask(SIG_SETMASK, sigmask, &oldmask); - rc = poll(fds, nfds, millis); + rc = poll(fds, nfds, ms); if (sigmask) sys_sigprocmask(SIG_SETMASK, &oldmask, 0); } } else { + uint32_t ms; if (!timeout || - ckd_add(&millis, timeout->tv_sec, timeout->tv_nsec / 1000000)) { - millis = -1; + ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) { + ms = -1u; } BEGIN_BLOCKING_OPERATION; - rc = sys_poll_nt(fds, nfds, &millis, sigmask); + rc = sys_poll_nt(fds, nfds, &ms, sigmask); END_BLOCKING_OPERATION; } diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index 622d1132c..2efc7fb00 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -16,20 +16,18 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/calls/console.internal.h" #include "libc/calls/internal.h" #include "libc/calls/sig.internal.h" -#include "libc/calls/state.internal.h" #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/iovec.h" -#include "libc/calls/struct/iovec.internal.h" -#include "libc/calls/struct/timespec.h" #include "libc/calls/syscall_support-nt.internal.h" -#include "libc/calls/wincrash.internal.h" +#include "libc/calls/ttydefaults.h" #include "libc/errno.h" #include "libc/fmt/itoa.h" +#include "libc/intrin/atomic.h" +#include "libc/intrin/dll.h" #include "libc/intrin/nomultics.internal.h" +#include "libc/intrin/strace.internal.h" #include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/nt/console.h" @@ -39,95 +37,95 @@ #include "libc/nt/errors.h" #include "libc/nt/events.h" #include "libc/nt/files.h" -#include "libc/nt/ipc.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/inputrecord.h" -#include "libc/nt/struct/overlapped.h" #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" #include "libc/str/str.h" #include "libc/str/utf16.h" #include "libc/sysv/consts/o.h" -#include "libc/sysv/consts/sa.h" -#include "libc/sysv/consts/sicode.h" #include "libc/sysv/consts/sig.h" #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" -#include "libc/thread/posixthread.internal.h" +#include "libc/thread/thread.h" #include "libc/thread/tls.h" - #ifdef __x86_64__ __msabi extern typeof(CloseHandle) *const __imp_CloseHandle; static const struct { - uint16_t vk; - uint32_t normal_str; - uint32_t shift_str; - uint32_t ctrl_str; - uint32_t shift_ctrl_str; + int vk; + int normal_str; + int shift_str; + int ctrl_str; + int shift_ctrl_str; } kVirtualKey[] = { -#define SW(s) W4(s "\0\0") -#define W4(s) (s[3] + 0u) << 24 | s[2] << 16 | s[1] << 8 | s[0] -#define VK(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str) \ - { vk, SW(normal_str), SW(shift_str), SW(ctrl_str), SW(shift_ctrl_str) } - VK(kNtVkInsert, "2~", "2;2~", "2;5~", "2;6~"), - VK(kNtVkEnd, "4~", "4;2~", "4;5~", "4;6~"), - VK(kNtVkDown, "B", "1;2B", "1;5B", "1;6B"), - VK(kNtVkNext, "6~", "6;2~", "6;5~", "6;6~"), - VK(kNtVkLeft, "D", "1;2D", "1;5D", "1;6D"), - VK(kNtVkClear, "G", "1;2G", "1;5G", "1;6G"), - VK(kNtVkRight, "C", "1;2C", "1;5C", "1;6C"), - VK(kNtVkUp, "A", "1;2A", "1;5A", "1;6A"), - VK(kNtVkHome, "1~", "1;2~", "1;5~", "1;6~"), - VK(kNtVkPrior, "5~", "5;2~", "5;5~", "5;6~"), - VK(kNtVkDelete, "3~", "3;2~", "3;5~", "3;6~"), - VK(kNtVkNumpad0, "2~", "2;2~", "2;5~", "2;6~"), - VK(kNtVkNumpad1, "4~", "4;2~", "4;5~", "4;6~"), - VK(kNtVkNumpad2, "B", "1;2B", "1;5B", "1;6B"), - VK(kNtVkNumpad3, "6~", "6;2~", "6;5~", "6;6~"), - VK(kNtVkNumpad4, "D", "1;2D", "1;5D", "1;6D"), - VK(kNtVkNumpad5, "G", "1;2G", "1;5G", "1;6G"), - VK(kNtVkNumpad6, "C", "1;2C", "1;5C", "1;6C"), - VK(kNtVkNumpad7, "A", "1;2A", "1;5A", "1;6A"), - VK(kNtVkNumpad8, "1~", "1;2~", "1;5~", "1;6~"), - VK(kNtVkNumpad9, "5~", "5;2~", "5;5~", "5;6~"), - VK(kNtVkDecimal, "3~", "3;2~", "3;5~", "3;6~"), - VK(kNtVkF1, "[A", "23~", "11^", "23^"), - VK(kNtVkF2, "[B", "24~", "12^", "24^"), - VK(kNtVkF3, "[C", "25~", "13^", "25^"), - VK(kNtVkF4, "[D", "26~", "14^", "26^"), - VK(kNtVkF5, "[E", "28~", "15^", "28^"), - VK(kNtVkF6, "17~", "29~", "17^", "29^"), - VK(kNtVkF7, "18~", "31~", "18^", "31^"), - VK(kNtVkF8, "19~", "32~", "19^", "32^"), - VK(kNtVkF9, "20~", "33~", "20^", "33^"), - VK(kNtVkF10, "21~", "34~", "21^", "34^"), - VK(kNtVkF11, "23~", "23$", "23^", "23@"), - VK(kNtVkF12, "24~", "24$", "24^", "24@"), -#undef VK -#undef W4 -#undef SW +#define S(s) W(s "\0\0") +#define W(s) (s[3] << 24 | s[2] << 16 | s[1] << 8 | s[0]) + {kNtVkUp, S("A"), S("1;2A"), S("1;5A"), S("1;6A")}, + {kNtVkDown, S("B"), S("1;2B"), S("1;5B"), S("1;6B")}, + {kNtVkLeft, S("D"), S("1;2D"), S("1;5D"), S("1;6D")}, + {kNtVkRight, S("C"), S("1;2C"), S("1;5C"), S("1;6C")}, + {kNtVkInsert, S("2~"), S("2;2~"), S("2;5~"), S("2;6~")}, + {kNtVkDelete, S("3~"), S("3;2~"), S("3;5~"), S("3;6~")}, + {kNtVkHome, S("H"), S("1;2H"), S("1;5H"), S("1;6H")}, + {kNtVkEnd, S("F"), S("1;2F"), S("1;5F"), S("1;6F")}, + {kNtVkPrior, S("5~"), S("5;2~"), S("5;5~"), S("5;6~")}, + {kNtVkNext, S("6~"), S("6;2~"), S("6;5~"), S("6;6~")}, + {kNtVkF1, -S("OP"), S("1;2P"), S("11^"), S("1;6P")}, + {kNtVkF2, -S("OQ"), S("1;2Q"), S("12^"), S("1;6Q")}, + {kNtVkF3, -S("OR"), S("1;2R"), S("13^"), S("1;6R")}, + {kNtVkF4, -S("OS"), S("1;2S"), S("14^"), S("1;6S")}, + {kNtVkF5, S("15~"), S("28~"), S("15^"), S("28^")}, + {kNtVkF6, S("17~"), S("29~"), S("17^"), S("29^")}, + {kNtVkF7, S("18~"), S("31~"), S("18^"), S("31^")}, + {kNtVkF8, S("19~"), S("32~"), S("19^"), S("32^")}, + {kNtVkF9, S("20~"), S("33~"), S("20^"), S("33^")}, + {kNtVkF10, S("21~"), S("34~"), S("21^"), S("34^")}, + {kNtVkF11, S("23~"), S("23$"), S("23^"), S("23@")}, + {kNtVkF12, S("24~"), S("24$"), S("24^"), S("24@")}, +#undef W +#undef S }; -static textwindows int ProcessSignal(int sig, struct Fd *f) { - if (f) { - if (_weaken(__sig_raise)) { - pthread_mutex_unlock(&f->lock); - _weaken(__sig_raise)(sig, SI_KERNEL); - pthread_mutex_lock(&f->lock); - if (!(__sighandflags[sig] & SA_RESTART)) { - return eintr(); - } - } else if (sig != SIGWINCH) { - TerminateThisProcess(sig); - } - } - return 0; +#define KEYSTROKE_CONTAINER(e) DLL_CONTAINER(struct Keystroke, elem, e) + +struct Keystroke { + char buf[32]; + unsigned buflen; + struct Dll elem; +}; + +struct Keystrokes { + struct Dll *list; + struct Dll *free; + bool end_of_file; + uint16_t utf16hs; + unsigned allocated; + pthread_mutex_t lock; + struct Keystroke pool[32]; +}; + +static struct Keystrokes __keystroke; + +static textwindows void LockKeystrokes(void) { + pthread_mutex_lock(&__keystroke.lock); } -static textwindows uint32_t GetVirtualKey(uint16_t vk, bool shift, bool ctrl) { +static textwindows void UnlockKeystrokes(void) { + pthread_mutex_unlock(&__keystroke.lock); +} + +static textwindows uint64_t BlockSignals(void) { + return atomic_exchange(&__get_tls()->tib_sigmask, -1); +} + +static textwindows void UnblockSignals(uint64_t mask) { + atomic_store_explicit(&__get_tls()->tib_sigmask, mask, memory_order_release); +} + +static textwindows int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) { for (int i = 0; i < ARRAYLEN(kVirtualKey); ++i) { if (kVirtualKey[i].vk == vk) { if (shift && ctrl) { @@ -144,33 +142,7 @@ static textwindows uint32_t GetVirtualKey(uint16_t vk, bool shift, bool ctrl) { return 0; } -// Manual CMD.EXE echoing for when !ICANON && ECHO is the case. -static textwindows void EchoTerminalInput(struct Fd *f, char *p, size_t n) { - int64_t hOutput; - if (f->kind == kFdConsole) { - hOutput = f->extra; - } else { - hOutput = g_fds.p[1].handle; - } - if (__ttymagic & kFdTtyEchoRaw) { - WriteFile(hOutput, p, n, 0, 0); - } else { - size_t i; - for (i = 0; i < n; ++i) { - if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') { - char ctl[2]; - ctl[0] = '^'; - ctl[1] = p[i] ^ 0100; - WriteFile(hOutput, ctl, 2, 0, 0); - } else { - WriteFile(hOutput, p + i, 1, 0, 0); - } - } - } -} - -static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p, - uint16_t *utf16hs, struct Fd *f) { +static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) { uint16_t c = r->Event.KeyEvent.uChar.UnicodeChar; uint16_t vk = r->Event.KeyEvent.wVirtualKeyCode; @@ -181,23 +153,10 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p, return 0; } - // ignore numpad being used to compose a character - if ((cks & kNtLeftAltPressed) && !(cks & kNtEnhancedKey) && - (vk == kNtVkInsert || vk == kNtVkEnd || vk == kNtVkDown || - vk == kNtVkNext || vk == kNtVkLeft || vk == kNtVkClear || - vk == kNtVkRight || vk == kNtVkHome || vk == kNtVkUp || - vk == kNtVkPrior || vk == kNtVkNumpad0 || vk == kNtVkNumpad1 || - vk == kNtVkNumpad2 || vk == kNtVkNumpad3 || vk == kNtVkNumpad4 || - vk == kNtVkNumpad5 || vk == kNtVkNumpad6 || vk == kNtVkNumpad7 || - vk == kNtVkNumpad8 || vk == kNtVkNumpad9)) { - return 0; - } - - int n = 0; - // process virtual keys + int n = 0; if (!c) { - uint32_t w; + int w; w = GetVirtualKey(vk, !!(cks & kNtShiftPressed), !!(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed))); if (!w) return 0; @@ -205,7 +164,11 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p, if (cks & (kNtLeftAltPressed | kNtRightAltPressed)) { p[n++] = 033; } - p[n++] = '['; + if (w > 0) { + p[n++] = '['; + } else { + w = -w; + } do p[n++] = w; while ((w >>= 8)); return n; @@ -213,11 +176,11 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p, // translate utf-16 into utf-32 if (IsHighSurrogate(c)) { - *utf16hs = c; + __keystroke.utf16hs = c; return 0; } if (IsLowSurrogate(c)) { - c = MergeUtf16(*utf16hs, c); + c = MergeUtf16(__keystroke.utf16hs, c); } // enter sends \r in a raw terminals @@ -240,15 +203,21 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p, // handle ctrl-c and ctrl-\, which tcsetattr() is able to remap if (!(__ttymagic & kFdTtyNoIsigs)) { if (c == __vintr && __vintr != _POSIX_VDISABLE) { - return ProcessSignal(SIGINT, f); + STRACE("encountered CTRL(%#c) c_cc[VINTR] will raise SIGINT", CTRL(c)); + __get_tls()->tib_sigpending |= 1ull << (SIGINT - 1); + return 0; } else if (c == __vquit && __vquit != _POSIX_VDISABLE) { - return ProcessSignal(SIGQUIT, f); + STRACE("encountered CTRL(%#c) c_cc[VQUITR] will raise SIGQUIT", CTRL(c)); + __get_tls()->tib_sigpending |= 1ull << (SIGQUIT - 1); + return 0; } } // handle ctrl-d the end of file keystroke if (c == __veof && __veof != _POSIX_VDISABLE) { - return enodata(); + STRACE("encountered CTRL(%#c) c_cc[VEOF] closing console input", CTRL(c)); + __keystroke.end_of_file = true; + return 0; } // insert esc prefix when alt is held @@ -269,11 +238,11 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p, // - write(1, "\e[?1000;1002;1015;1006h") to enable // - write(1, "\e[?1000;1002;1015;1006l") to disable // See o//examples/ttyinfo.com and o//tool/viz/life.com -static textwindows int ProcessMouseEvent(const struct NtInputRecord *r, char *b, - struct Fd *f) { +static textwindows int ProcessMouseEvent(const struct NtInputRecord *r, + char *b) { int e = 0; char *p = b; - uint32_t currentbs = f ? f->mousebuttons : 0; + uint32_t currentbs = __mousebuttons; uint32_t ev = r->Event.MouseEvent.dwEventFlags; uint32_t bs = r->Event.MouseEvent.dwButtonState; ev &= kNtMouseMoved | kNtMouseWheeled; @@ -320,98 +289,195 @@ static textwindows int ProcessMouseEvent(const struct NtInputRecord *r, char *b, } else { *p++ = 'M'; // down } - if (f) { - f->mousebuttons = bs; - } + __mousebuttons = bs; } return p - b; } -textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r, - char p[hasatleast 32], - uint16_t *utf16hs, struct Fd *f) { +static textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r, + char p[hasatleast 32]) { switch (r->EventType) { case kNtKeyEvent: - return ProcessKeyEvent(r, p, utf16hs, f); + return ProcessKeyEvent(r, p); case kNtMouseEvent: - return ProcessMouseEvent(r, p, f); + return ProcessMouseEvent(r, p); case kNtWindowBufferSizeEvent: - return ProcessSignal(SIGWINCH, f); + STRACE("detected console resize will raise SIGWINCH"); + __get_tls()->tib_sigpending |= 1ull << (SIGWINCH - 1); + return 0; default: return 0; } } -static textwindows ssize_t ReadFromWindowsConsole(struct Fd *f, void *data, - size_t size) { - ssize_t rc; - int e = errno; - uint16_t utf16hs = 0; - pthread_mutex_lock(&f->lock); - for (;;) { - if (f->eoftty) { - rc = 0; - break; - } - uint32_t got = MIN(size, f->buflen); - uint32_t remain = f->buflen - got; - if (got) memcpy(data, f->buf, got); - if (remain) memmove(f->buf, f->buf + got, remain); - f->buflen = remain; - if (got) { - rc = got; - break; - } - uint32_t events_available; - if (!GetNumberOfConsoleInputEvents(f->handle, &events_available)) { - rc = __winerr(); - break; - } - if (events_available) { - uint32_t n; - struct NtInputRecord r; - if (!ReadConsoleInput(f->handle, &r, 1, &n)) { - rc = __winerr(); - break; - } - rc = ConvertConsoleInputToAnsi(&r, f->buf, &utf16hs, f); - if (rc == -1) { - if (errno == ENODATA) { - f->eoftty = true; - errno = e; - rc = 0; - } - break; - } - f->buflen = rc; +static textwindows struct Keystroke *NewKeystroke(void) { + struct Dll *e; + struct Keystroke *k = 0; + int i, n = ARRAYLEN(__keystroke.pool); + if (atomic_load_explicit(&__keystroke.allocated, memory_order_acquire) < n && + (i = atomic_fetch_add(&__keystroke.allocated, 1)) < n) { + k = __keystroke.pool + i; + } else if ((e = dll_first(__keystroke.free))) { + k = KEYSTROKE_CONTAINER(e); + dll_remove(&__keystroke.free, &k->elem); + } else { + return 0; + } + bzero(k, sizeof(*k)); + dll_init(&k->elem); + return k; +} + +static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) { + int len; + struct Keystroke *k; + char buf[sizeof(k->buf)]; + if ((len = ConvertConsoleInputToAnsi(r, buf))) { + if ((k = NewKeystroke())) { + memcpy(k->buf, buf, sizeof(k->buf)); + k->buflen = len; + dll_make_last(&__keystroke.list, &k->elem); } else { - if (f->flags & O_NONBLOCK) { - rc = 0; - break; - } - uint32_t ms = __SIG_POLL_INTERVAL_MS; - if (__ttymagic & kFdTtyNoBlock) { - if (!__vtime) { - rc = 0; - break; - } else { - ms = __vtime * 100; + STRACE("ran out of memory to hold keystroke %#.*s", len, buf); + } + } +} + +static textwindows void IngestConsoleInput(int64_t handle) { + uint32_t i, n; + struct NtInputRecord records[16]; + if (!__keystroke.end_of_file) { + do { + if (GetNumberOfConsoleInputEvents(handle, &n)) { + if (n) { + n = MIN(ARRAYLEN(records), n); + if (ReadConsoleInput(handle, records, n, &n)) { + for (i = 0; i < n && !__keystroke.end_of_file; ++i) { + IngestConsoleInputRecord(records + i); + } + } else { + STRACE("ReadConsoleInput failed w/ %d", GetLastError()); + __keystroke.end_of_file = true; + break; + } } - } - if ((rc = _check_interrupts(kSigOpRestartable))) { + } else { + STRACE("GetNumberOfConsoleInputRecords failed w/ %d", GetLastError()); + __keystroke.end_of_file = true; break; } - struct PosixThread *pt = _pthread_self(); - pt->pt_flags |= PT_INSEMAPHORE; - WaitForSingleObject(pt->semaphore, ms); - pt->pt_flags &= ~PT_INSEMAPHORE; - if (pt->abort_errno == ECANCELED) { - rc = ecanceled(); - break; + } while (n == ARRAYLEN(records)); + } +} + +textwindows int FlushConsoleInputBytes(int64_t handle) { + int rc; + uint64_t m; + m = BlockSignals(); + LockKeystrokes(); + if (FlushConsoleInputBuffer(handle)) { + dll_make_first(&__keystroke.free, __keystroke.list); + __keystroke.list = 0; + rc = 0; + } else { + rc = __winerr(); + } + UnlockKeystrokes(); + UnblockSignals(m); + return rc; +} + +textwindows int CountConsoleInputBytes(int64_t handle) { + int count = 0; + struct Dll *e; + uint64_t m = BlockSignals(); + LockKeystrokes(); + IngestConsoleInput(handle); + for (e = dll_first(__keystroke.list); e; e = dll_next(__keystroke.list, e)) { + count += KEYSTROKE_CONTAINER(e)->buflen; + } + if (!count && __keystroke.end_of_file) { + count = -1; + } + UnlockKeystrokes(); + UnblockSignals(m); + return count; +} + +static textwindows bool DigestConsoleInput(void *data, size_t size, int *rc) { + struct Dll *e; + if ((e = dll_first(__keystroke.list))) { + struct Keystroke *k = KEYSTROKE_CONTAINER(e); + uint32_t got = MIN(size, k->buflen); + uint32_t remain = k->buflen - got; + if (got) memcpy(data, k->buf, got); + if (remain) memmove(k->buf, k->buf + got, remain); + if (!remain) { + dll_remove(&__keystroke.list, e); + dll_make_first(&__keystroke.free, e); + } + k->buflen = remain; + if (got) { + *rc = got; + return true; + } + } else if (__keystroke.end_of_file) { + *rc = 0; + return true; + } + return false; +} + +// Manual CMD.EXE echoing for when !ICANON && ECHO is the case. +static textwindows void EchoTerminalInput(struct Fd *f, char *p, size_t n) { + int64_t hOutput; + if (f->kind == kFdConsole) { + hOutput = f->extra; + } else { + hOutput = g_fds.p[1].handle; + } + if (__ttymagic & kFdTtyEchoRaw) { + WriteFile(hOutput, p, n, 0, 0); + } else { + size_t i; + for (i = 0; i < n; ++i) { + if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') { + char ctl[2]; + ctl[0] = '^'; + ctl[1] = p[i] ^ 0100; + WriteFile(hOutput, ctl, 2, 0, 0); + } else { + WriteFile(hOutput, p + i, 1, 0, 0); } } } - pthread_mutex_unlock(&f->lock); +} + +static textwindows ssize_t ReadFromWindowsConsole(struct Fd *f, void *data, + size_t size) { + int rc = -1; + for (;;) { + bool done = false; + uint64_t m; + m = BlockSignals(); + LockKeystrokes(); + IngestConsoleInput(f->handle); + done = DigestConsoleInput(data, size, &rc); + UnlockKeystrokes(); + UnblockSignals(m); + if (done) break; + if (f->flags & O_NONBLOCK) return eagain(); + uint32_t ms = __SIG_POLL_INTERVAL_MS; + if (__ttymagic & kFdTtyNoBlock) { + if (!__vtime) { + return 0; + } else { + ms = __vtime * 100; + } + } + if (_check_interrupts(kSigOpRestartable)) return -1; + if (__pause_thread(ms)) return -1; + } if (rc > 0 && (__ttymagic & kFdTtyEchoing)) { EchoTerminalInput(f, data, size); } @@ -510,6 +576,12 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size, return got; } + errno_t err; + if (_weaken(pthread_testcancel_np) && + (err = _weaken(pthread_testcancel_np)())) { + return ecanceled(); + } + switch (GetLastError()) { case kNtErrorBrokenPipe: // broken pipe case kNtErrorNoData: // closing named pipe diff --git a/libc/calls/setrlimit.c b/libc/calls/setrlimit.c index e43e05cb9..91886c7f3 100644 --- a/libc/calls/setrlimit.c +++ b/libc/calls/setrlimit.c @@ -97,3 +97,5 @@ int setrlimit(int resource, const struct rlimit *rlim) { DescribeRlimit(0, rlim), rc); return rc; } + +__weak_reference(setrlimit, setrlimit64); diff --git a/libc/calls/sig.c b/libc/calls/sig.c index 48ddf6efc..c4daee116 100644 --- a/libc/calls/sig.c +++ b/libc/calls/sig.c @@ -26,6 +26,7 @@ #include "libc/calls/state.internal.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/siginfo.h" +#include "libc/calls/struct/sigset.internal.h" #include "libc/calls/struct/ucontext.internal.h" #include "libc/calls/ucontext.h" #include "libc/dce.h" @@ -49,6 +50,7 @@ #include "libc/nt/struct/ntexceptionpointers.h" #include "libc/nt/synchronization.h" #include "libc/nt/thread.h" +#include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/sysv/consts/sa.h" #include "libc/sysv/consts/sicode.h" @@ -124,19 +126,21 @@ static textwindows bool __sig_start(struct PosixThread *pt, int sig, return true; } +static textwindows sigaction_f __sig_handler(unsigned rva) { + return (sigaction_f)(__executable_start + rva); +} + static textwindows void __sig_call(int sig, siginfo_t *si, ucontext_t *ctx, unsigned rva, unsigned flags, struct CosmoTib *tib) { - sigaction_f handler; - handler = (sigaction_f)(__executable_start + rva); ++__sig.count; if (__sig_should_use_altstack(flags, tib)) { tib->tib_sigstack_flags |= SS_ONSTACK; - __stack_call(sig, si, ctx, 0, handler, + __stack_call(sig, si, ctx, 0, __sig_handler(rva), tib->tib_sigstack_addr + tib->tib_sigstack_size); tib->tib_sigstack_flags &= ~SS_ONSTACK; } else { - handler(sig, si, ctx); + __sig_handler(rva)(sig, si, ctx); } } @@ -151,21 +155,29 @@ textwindows int __sig_raise(int sig, int sic) { nc.ContextFlags = kNtContextAll; GetThreadContext(GetCurrentThread(), &nc); _ntcontext2linux(&ctx, &nc); - STRACE("raising %G", sig); if (!(flags & SA_NODEFER)) { tib->tib_sigmask |= 1ull << (sig - 1); } + STRACE("entering raise(%G) signal handler %t with mask %s → %s", sig, + __sig_handler(rva), DescribeSigset(0, &ctx.uc_sigmask), + DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}})); __sig_call(sig, &si, &ctx, rva, flags, tib); + STRACE("leaving raise(%G) signal handler %t with mask %s → %s", sig, + __sig_handler(rva), + DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}), + DescribeSigset(0, &ctx.uc_sigmask)); tib->tib_sigmask = ctx.uc_sigmask.__bits[0]; return (flags & SA_RESTART) ? 2 : 1; } -textwindows void __sig_cancel(struct PosixThread *pt) { +textwindows void __sig_cancel(struct PosixThread *pt, unsigned flags) { atomic_int *futex; if (_weaken(WakeByAddressSingle) && (futex = atomic_load_explicit(&pt->pt_futex, memory_order_acquire))) { + STRACE("canceling futex"); _weaken(WakeByAddressSingle)(futex); - } else if (pt->iohandle > 0) { + } else if (!(flags & SA_RESTART) && pt->iohandle > 0) { + STRACE("canceling i/o operation"); if (!CancelIoEx(pt->iohandle, pt->ioverlap)) { int err = GetLastError(); if (err != kNtErrorNotFound) { @@ -173,9 +185,13 @@ textwindows void __sig_cancel(struct PosixThread *pt) { } } } else if (pt->pt_flags & PT_INSEMAPHORE) { + STRACE("canceling semaphore"); + pt->pt_flags &= ~PT_INSEMAPHORE; if (!ReleaseSemaphore(pt->semaphore, 1, 0)) { STRACE("ReleaseSemaphore() failed w/ %d", GetLastError()); } + } else { + STRACE("canceling asynchronously"); } } @@ -194,16 +210,25 @@ static textwindows wontreturn void __sig_panic(const char *msg) { static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) { ucontext_t ctx = {0}; - sigaction_f handler = (sigaction_f)(__executable_start + sf->rva); - STRACE("__sig_tramp(%G, %t)", sf->si.si_signo, handler); + int sig = sf->si.si_signo; _ntcontext2linux(&ctx, sf->nc); - ctx.uc_sigmask.__bits[0] = sf->pt->tib->tib_sigmask; + ctx.uc_sigmask.__bits[0] = + atomic_load_explicit(&sf->pt->tib->tib_sigmask, memory_order_acquire); if (!(sf->flags & SA_NODEFER)) { - sf->pt->tib->tib_sigmask |= 1ull << (sf->si.si_signo - 1); + sf->pt->tib->tib_sigmask |= 1ull << (sig - 1); } ++__sig.count; - handler(sf->si.si_signo, &sf->si, &ctx); - sf->pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0]; + STRACE("entering __sig_tramp(%G, %t) with mask %s → %s", sig, + __sig_handler(sf->rva), DescribeSigset(0, &ctx.uc_sigmask), + DescribeSigset(0, &(sigset_t){{sf->pt->tib->tib_sigmask}})); + __sig_handler(sf->rva)(sig, &sf->si, &ctx); + STRACE("leaving __sig_tramp(%G, %t) with mask %s → %s", sig, + __sig_handler(sf->rva), + DescribeSigset(0, &(sigset_t){{sf->pt->tib->tib_sigmask}}), + DescribeSigset(0, &ctx.uc_sigmask)); + atomic_store_explicit(&sf->pt->tib->tib_sigmask, ctx.uc_sigmask.__bits[0], + memory_order_release); + // TDOO(jart): Do we need to do a __sig_check() here? _ntlinux2context(sf->nc, &ctx); SetThreadContext(GetCurrentThread(), sf->nc); __sig_panic("SetThreadContext(GetCurrentThread)"); @@ -259,7 +284,7 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) { } ResumeThread(th); // doesn't actually resume if doing blocking i/o pt->abort_errno = EINTR; - __sig_cancel(pt); // we can wake it up immediately by canceling it + __sig_cancel(pt, flags); // we can wake it up immediately by canceling it return 0; } @@ -431,7 +456,7 @@ __msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) { static textwindows int __sig_checkem(atomic_ulong *sigs, struct CosmoTib *tib, const char *thing, int id) { - bool handler_was_called = false; + int handler_was_called = 0; uint64_t pending, masked, deliverable; pending = atomic_load_explicit(sigs, memory_order_acquire); masked = atomic_load_explicit(&tib->tib_sigmask, memory_order_acquire); @@ -454,12 +479,12 @@ static textwindows int __sig_checkem(atomic_ulong *sigs, struct CosmoTib *tib, // didn't have the SA_RESTART flag, and `2`, which means SA_RESTART // handlers were called (or `3` if both were the case). textwindows int __sig_check(void) { - bool handler_was_called = false; + int handler_was_called = false; struct CosmoTib *tib = __get_tls(); handler_was_called |= __sig_checkem(&tib->tib_sigpending, tib, "tid", tib->tib_tid); handler_was_called |= __sig_checkem(&__sig.pending, tib, "pid", getpid()); - POLLTRACE("__sig_check() → %hhhd", handler_was_called); + POLLTRACE("__sig_check() → %d", handler_was_called); return handler_was_called; } diff --git a/libc/calls/sig.internal.h b/libc/calls/sig.internal.h index 8b0d8685c..083b4c20d 100644 --- a/libc/calls/sig.internal.h +++ b/libc/calls/sig.internal.h @@ -26,7 +26,7 @@ int __sig_check(void); int __sig_kill(struct PosixThread *, int, int); int __sig_mask(int, const sigset_t *, sigset_t *); int __sig_raise(int, int); -void __sig_cancel(struct PosixThread *); +void __sig_cancel(struct PosixThread *, unsigned); void __sig_generate(int, int); void __sig_init(void); diff --git a/libc/calls/sigsuspend.c b/libc/calls/sigsuspend.c index a3a1e5e69..45f08d7cf 100644 --- a/libc/calls/sigsuspend.c +++ b/libc/calls/sigsuspend.c @@ -70,17 +70,7 @@ int sigsuspend(const sigset_t *ignore) { } else { __sig_mask(SIG_SETMASK, arg, &save); while (!(rc = _check_interrupts(0))) { - struct PosixThread *pt; - pt = _pthread_self(); - pt->abort_errno = 0; - pt->pt_flags |= PT_INSEMAPHORE; - WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS); - pt->pt_flags &= ~PT_INSEMAPHORE; - if (pt->abort_errno) { - errno = pt->abort_errno; - rc = -1; - break; - } + if ((rc = __pause_thread(__SIG_SIG_INTERVAL_MS))) break; } __sig_mask(SIG_SETMASK, &save, 0); } diff --git a/libc/calls/statfs.c b/libc/calls/statfs.c index bd563ee6a..ed6407a43 100644 --- a/libc/calls/statfs.c +++ b/libc/calls/statfs.c @@ -66,3 +66,5 @@ int statfs(const char *path, struct statfs *sf) { STRACE("statfs(%#s, [%s]) → %d% m", path, DescribeStatfs(rc, sf)); return rc; } + +__weak_reference(statfs, statfs64); diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index 27c4ec293..c1a0b3d50 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -31,15 +31,12 @@ struct Fd { char kind; bool eoftty; bool dontclose; - unsigned char buflen; unsigned flags; unsigned mode; int64_t handle; int64_t extra; int64_t pointer; pthread_mutex_t lock; - unsigned char mousebuttons; - char buf[32]; }; struct StdinRelay { diff --git a/libc/calls/tcflush.c b/libc/calls/tcflush.c index 2cea37d70..3111b2eb3 100644 --- a/libc/calls/tcflush.c +++ b/libc/calls/tcflush.c @@ -67,30 +67,7 @@ static dontinline textwindows int sys_tcflush_nt(int fd, int queue) { if (queue == TCOFLUSH) { return 0; // windows console output is never buffered } - FlushConsoleInputBuffer(hConin); - int rc = 0; - int e = errno; - int oldflags = g_fds.p[fd].flags; - g_fds.p[fd].flags |= O_NONBLOCK; - for (;;) { - char buf[512]; - ssize_t got = sys_read_nt_impl(fd, buf, 512, -1); - if (!got) { - break; - } else if (got == -1) { - if (errno == EAGAIN) { - errno = e; - } else if (errno == EINTR) { - errno = e; - continue; - } else { - rc = -1; - } - break; - } - } - g_fds.p[fd].flags = oldflags; - return rc; + return FlushConsoleInputBytes(hConin); } /** diff --git a/libc/calls/tcgetattr-nt.c b/libc/calls/tcgetattr-nt.c index 80234e1f7..fe815e563 100644 --- a/libc/calls/tcgetattr-nt.c +++ b/libc/calls/tcgetattr-nt.c @@ -25,6 +25,7 @@ #include "libc/macros.internal.h" #include "libc/nt/console.h" #include "libc/nt/enum/consolemodeflags.h" +#include "libc/nt/runtime.h" #include "libc/nt/struct/consolescreenbufferinfoex.h" #include "libc/str/str.h" #include "libc/sysv/consts/baud.internal.h" diff --git a/libc/calls/weirdtypes.h b/libc/calls/weirdtypes.h index edcbb701e..072735dcb 100644 --- a/libc/calls/weirdtypes.h +++ b/libc/calls/weirdtypes.h @@ -50,11 +50,40 @@ typedef uint32_t nlink_t; /* uint16_t on xnu */ #define TIME_T_MIN (-TIME_T_MAX - 1) #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) -#define blkcnt64_t blkcnt_t -#define fsblkcnt64_t fsblkcnt_t -#define fsfilcnt64_t fsfilcnt_t -#define ino64_t ino_t -#define off64_t off_t +#define F_GETLK64 F_GETLK +#define F_SETLK64 F_SETLK +#define F_SETLKW64 F_SETLKW +#define RLIM64_INFINITY RLIM_INFINITY +#define RLIM64_SAVED_CUR RLIM_SAVED_CUR +#define RLIM64_SAVED_MAX RLIM_SAVED_MAX +#define alphasort64 alphasort +#define blkcnt64_t blkcnt_t +#define dirent64 dirent +#define flock64 flock +#define fsfilcnt64_t fsfilcnt_t +#define fstat64 fstat +#define fstatat64 fstatat +#define fstatfs64 fstatfs +#define getrlimit64 getrlimit +#define ino64_t ino_t +#define lockf64 lockf +#define lstat64 lstat +#define mmap64 mmap +#define off64_t off_t +#define open64 open +#define openat64 openat +#define posix_fadvise64 posix_fadvise +#define posix_fallocate64 posix_fallocate +#define readdir64 readdir +#define readdir64_r readdir_r +#define rlim64_t rlim_t +#define rlimit64 rlimit +#define scandir64 scandir +#define sendfile64 sendfile +#define setrlimit64 setrlimit +#define stat64 stat +#define statfs64 statfs +#define versionsort64 versionsort #endif #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index 033281a93..83ac291eb 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -86,10 +86,6 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size, if (!pwriting) { offset = 0; } - if (seekable && !pwriting) { - pthread_mutex_lock(&f->lock); - offset = f->pointer; - } // To use the tty mouse events feature: // - write(1, "\e[?1000;1002;1015;1006h") to enable @@ -161,6 +157,11 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size, } } + if (seekable && !pwriting) { + pthread_mutex_lock(&f->lock); + offset = f->pointer; + } + struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0), .Pointer = offset}; ok = WriteFile(handle, data, size, 0, &overlap); @@ -212,6 +213,12 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size, return sent; } + errno_t err; + if (_weaken(pthread_testcancel_np) && + (err = _weaken(pthread_testcancel_np)())) { + return ecanceled(); + } + switch (GetLastError()) { // case kNtErrorInvalidHandle: // return ebadf(); /* handled by consts.sh */ diff --git a/libc/intrin/describefdset.c b/libc/intrin/describefdset.c new file mode 100644 index 000000000..165c7e760 --- /dev/null +++ b/libc/intrin/describefdset.c @@ -0,0 +1,61 @@ +/*-*- 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/dce.h" +#include "libc/intrin/asan.internal.h" +#include "libc/intrin/bsr.h" +#include "libc/intrin/kprintf.h" +#include "libc/sock/select.h" +#include "libc/sock/select.internal.h" + +#define N 100 + +#define append(...) o += ksnprintf(buf + o, N - o, __VA_ARGS__) + +const char *(DescribeFdSet)(char buf[N], ssize_t rc, int nfds, fd_set *fds) { + int o = 0; + + if (!fds) return "NULL"; + if ((!IsAsan() && kisdangerous(fds)) || + (IsAsan() && !__asan_is_valid(fds, sizeof(*fds) * nfds))) { + ksnprintf(buf, N, "%p", fds); + return buf; + } + + append("{"); + + bool gotsome = false; + for (int fd = 0; fd < nfds; fd += 64) { + uint64_t w = fds->fds_bits[fd >> 6]; + while (w) { + unsigned o = _bsr(w); + w &= ~((uint64_t)1 << o); + if (fd + o < nfds) { + if (!gotsome) { + gotsome = true; + append(", "); + append("%d", fd); + } + } + } + } + + append("}"); + + return buf; +} diff --git a/libc/intrin/g_fds.c b/libc/intrin/g_fds.c index e6d09efbe..2d26d3104 100644 --- a/libc/intrin/g_fds.c +++ b/libc/intrin/g_fds.c @@ -19,6 +19,7 @@ #include "libc/calls/state.internal.h" #include "libc/calls/struct/fd.internal.h" #include "libc/calls/ttydefaults.h" +#include "libc/dce.h" #include "libc/intrin/atomic.h" #include "libc/intrin/extend.internal.h" #include "libc/intrin/getenv.internal.h" @@ -26,6 +27,12 @@ #include "libc/intrin/nomultics.internal.h" #include "libc/intrin/pushpop.internal.h" #include "libc/intrin/weaken.h" +#include "libc/nt/console.h" +#include "libc/nt/createfile.h" +#include "libc/nt/enum/accessmask.h" +#include "libc/nt/enum/creationdisposition.h" +#include "libc/nt/enum/fileflagandattributes.h" +#include "libc/nt/enum/filesharemode.h" #include "libc/nt/runtime.h" #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" @@ -116,10 +123,10 @@ textstartup void __init_fds(int argc, char **argv, char **envp) { SetupWinStd(fds, 0, kNtStdInputHandle, sockset); SetupWinStd(fds, 1, kNtStdOutputHandle, sockset); SetupWinStd(fds, 2, kNtStdErrorHandle, sockset); + __veof = CTRL('D'); + __vintr = CTRL('C'); + __vquit = CTRL('\\'); } fds->p[1].flags = O_WRONLY | O_APPEND; fds->p[2].flags = O_WRONLY | O_APPEND; - __veof = CTRL('D'); - __vintr = CTRL('C'); - __vquit = CTRL('\\'); } diff --git a/libc/intrin/nomultics.c b/libc/intrin/nomultics.c index b66eae797..55e497bf9 100644 --- a/libc/intrin/nomultics.c +++ b/libc/intrin/nomultics.c @@ -25,3 +25,4 @@ unsigned char __veof; unsigned char __vintr; unsigned char __vquit; unsigned char __vtime; +unsigned char __mousebuttons; diff --git a/libc/intrin/nomultics.internal.h b/libc/intrin/nomultics.internal.h index 7f3caec3f..248cd3d45 100644 --- a/libc/intrin/nomultics.internal.h +++ b/libc/intrin/nomultics.internal.h @@ -11,6 +11,7 @@ extern unsigned char __veof; extern unsigned char __vintr; extern unsigned char __vquit; extern unsigned char __vtime; +extern unsigned char __mousebuttons; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/intrin/strace.internal.h b/libc/intrin/strace.internal.h index e3c27ce65..1dce6394d 100644 --- a/libc/intrin/strace.internal.h +++ b/libc/intrin/strace.internal.h @@ -16,13 +16,12 @@ COSMOPOLITAN_C_START_ #ifdef SYSDEBUG -#define STRACE(FMT, ...) \ - do { \ - if (UNLIKELY(__strace > 0) && strace_enabled(0) > 0) { \ - ftrace_enabled(-1); \ - __stracef(STRACE_PROLOGUE FMT "\n", ##__VA_ARGS__); \ - ftrace_enabled(+1); \ - } \ +#define STRACE(FMT, ...) \ + do { \ + if (UNLIKELY(strace_enter())) { \ + __stracef(STRACE_PROLOGUE FMT "\n", ##__VA_ARGS__); \ + ftrace_enabled(+1); \ + } \ } while (0) #else #define STRACE(FMT, ...) (void)0 diff --git a/libc/intrin/strace_enter.c b/libc/intrin/strace_enter.c new file mode 100644 index 000000000..64e4c9e59 --- /dev/null +++ b/libc/intrin/strace_enter.c @@ -0,0 +1,31 @@ +/*-*- 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 2023 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/runtime/runtime.h" +#ifdef SYSDEBUG + +dontinstrument bool strace_enter(void) { + if (strace_enabled(0) > 0) { + ftrace_enabled(-1); + return true; + } else { + return false; + } +} + +#endif /* SYSDEBUG */ diff --git a/libc/limits.h b/libc/limits.h index 636f0ceaf..34d863152 100644 --- a/libc/limits.h +++ b/libc/limits.h @@ -29,7 +29,7 @@ #define INT16_MAX __INT16_MAX__ #define INT32_MAX __INT32_MAX__ #define INT64_MAX __INT64_MAX__ -#define WINT_MAX __WCHAR_MAX__ +#define WINT_MAX __WINT_MAX__ #define WCHAR_MAX __WCHAR_MAX__ #define INTPTR_MAX __INTPTR_MAX__ #define PTRDIFF_MAX __PTRDIFF_MAX__ @@ -55,7 +55,7 @@ #define INT64_MIN (-INT64_MAX - 1) #define INTMAX_MIN (-INTMAX_MAX - 1) #define INTPTR_MIN (-INTPTR_MAX - 1) -#define WINT_MIN (-WINT_MAX - 1) +#define WINT_MIN __WINT_MIN__ #define WCHAR_MIN (-WCHAR_MAX - 1) #define PTRDIFF_MIN (-PTRDIFF_MAX - 1) diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c index e7b030dcc..29be8f882 100644 --- a/libc/log/checkfail.c +++ b/libc/log/checkfail.c @@ -30,6 +30,7 @@ #include "libc/runtime/memtrack.internal.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" +#include "libc/str/str.h" __static_yoink("strerror_wr"); @@ -69,11 +70,10 @@ relegated void __check_fail(const char *suffix, // va_end(va); kprintf("\n"); } - kprintf("\t%m\n\t%s%s", SUBTLE, program_invocation_name); + kprintf("\t%s\n\t%s%s", strerror(errno), SUBTLE, program_invocation_name); for (i = 1; i < __argc; ++i) { kprintf(" %s", __argv[i]); } kprintf("%s\n", RESET); __die(); - __builtin_unreachable(); } diff --git a/libc/log/die.c b/libc/log/die.c index e59e2072a..a39b427a9 100644 --- a/libc/log/die.c +++ b/libc/log/die.c @@ -47,7 +47,7 @@ relegated wontreturn void __die(void) { strcpy(host, "unknown"); gethostname(host, sizeof(host)); kprintf("%serror: %s on %s pid %d tid %d has perished%s\n" - " cosmoaddr2line %s%s %s\n", + "cosmoaddr2line %s%s %s\n", __nocolor ? "" : "\e[1;31m", program_invocation_short_name, host, getpid(), gettid(), __nocolor ? "" : "\e[0m", __argv[0], endswith(__argv[0], ".com") ? ".dbg" : "", diff --git a/libc/mem/internal.h b/libc/mem/internal.h index 076649f54..9309c5c52 100644 --- a/libc/mem/internal.h +++ b/libc/mem/internal.h @@ -9,7 +9,7 @@ struct CritbitNode { unsigned otherbits; }; -int PutEnvImpl(char *, bool); +int __putenv(char *, bool); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/intrin/putenv.c b/libc/mem/putenv.c similarity index 85% rename from libc/intrin/putenv.c rename to libc/mem/putenv.c index fb9655676..e8c22414c 100644 --- a/libc/intrin/putenv.c +++ b/libc/mem/putenv.c @@ -19,31 +19,28 @@ #include "libc/intrin/getenv.internal.h" #include "libc/intrin/leaky.internal.h" #include "libc/intrin/strace.internal.h" -#include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/mem/internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" #include "libc/sysv/errfuns.h" -#define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) - static char **expected; static size_t capacity; -static size_t GetEnvironLen(char **env) { +static size_t __lenenv(char **env) { char **p = env; while (*p) ++p; return p - env; } -static char **GrowEnviron(char **a) { +static char **__growenv(char **a) { size_t n, c; char **b, **p; if (!a) a = environ; - n = a ? GetEnvironLen(a) : 0; - c = MAX(16ul, n) << 1; - if (_weaken(malloc) && (b = _weaken(malloc)(c * sizeof(char *)))) { + n = a ? __lenenv(a) : 0; + c = MAX(8ul, n) << 1; + if ((b = malloc(c * sizeof(char *)))) { if (a) { for (p = b; *a;) { *p++ = *a++; @@ -56,18 +53,17 @@ static char **GrowEnviron(char **a) { capacity = c; return b; } else { - enomem(); return 0; } } -IGNORE_LEAKS(GrowEnviron) +IGNORE_LEAKS(__growenv) -int PutEnvImpl(char *s, bool overwrite) { +int __putenv(char *s, bool overwrite) { char **p; struct Env e; if (!(p = environ)) { - if (!(p = GrowEnviron(0))) { + if (!(p = __growenv(0))) { return -1; } } @@ -83,7 +79,7 @@ int PutEnvImpl(char *s, bool overwrite) { capacity = e.i; } if (e.i + 1 >= capacity) { - if (!(p = GrowEnviron(p))) { + if (!(p = __growenv(p))) { return -1; } } @@ -99,13 +95,13 @@ int PutEnvImpl(char *s, bool overwrite) { * 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 + * @raise ENOMEM if out of memory * @see setenv(), getenv() * @threadunsafe */ int putenv(char *s) { int rc; - rc = PutEnvImpl(s, true); + rc = __putenv(s, true); STRACE("putenv(%#s) → %d% m", s, rc); return rc; } diff --git a/libc/intrin/setenv.c b/libc/mem/setenv.c similarity index 82% rename from libc/intrin/setenv.c rename to libc/mem/setenv.c index 19e91c195..6f723995b 100644 --- a/libc/intrin/setenv.c +++ b/libc/mem/setenv.c @@ -18,7 +18,6 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/leaky.internal.h" #include "libc/intrin/strace.internal.h" -#include "libc/intrin/weaken.h" #include "libc/mem/internal.h" #include "libc/mem/mem.h" #include "libc/runtime/runtime.h" @@ -29,8 +28,8 @@ * Copies variable to environment. * * @return 0 on success, or -1 w/ errno and environment is unchanged - * @raise ENOMEM if out of memory or malloc() wasn't linked * @raise EINVAL if `name` is empty or contains `'='` + * @raise ENOMEM if out of memory * @see putenv(), getenv() * @threadunsafe */ @@ -38,19 +37,14 @@ int setenv(const char *name, const char *value, int overwrite) { int rc; char *s; size_t n, m; - const char *t; - if (!name || !*name || !value) return einval(); - for (t = name; *t; ++t) { - if (*t == '=') return einval(); + if (!name || !*name || !value || strchr(name, '=')) return einval(); + if ((s = malloc((n = strlen(name)) + 1 + (m = strlen(value)) + 1))) { + memcpy(mempcpy(mempcpy(s, name, n), "=", 1), value, m + 1); + rc = __putenv(s, overwrite); + } else { + rc = -1; } - n = strlen(name); - m = strlen(value); - if (!_weaken(malloc) || !(s = _weaken(malloc)(n + 1 + m + 1))) { - return enomem(); - } - 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); + STRACE("setenv(%#s, %#s, %hhhd) → %d% m", name, value, overwrite, rc); return rc; } diff --git a/libc/mem/wcsdup.c b/libc/mem/wcsdup.c index a2c3c3dad..b7170cdd4 100644 --- a/libc/mem/wcsdup.c +++ b/libc/mem/wcsdup.c @@ -25,6 +25,6 @@ */ wchar_t *wcsdup(const wchar_t *s) { size_t len = wcslen(s); - char *s2 = malloc((len + 1) * sizeof(wchar_t)); - return s2 ? memcpy(s2, s, (len + 1) * sizeof(wchar_t)) : NULL; + wchar_t *s2 = malloc((len + 1) * sizeof(wchar_t)); + return s2 ? wmemcpy(s2, s, len + 1) : NULL; } diff --git a/libc/nt/enum/vk.h b/libc/nt/enum/vk.h index b52355785..3861eac20 100644 --- a/libc/nt/enum/vk.h +++ b/libc/nt/enum/vk.h @@ -28,8 +28,8 @@ #define kNtVkAccept 0x1E #define kNtVkModechange 0x1F #define kNtVkSpace 0x20 -#define kNtVkPrior 0x21 -#define kNtVkNext 0x22 +#define kNtVkPrior 0x21 /* page up */ +#define kNtVkNext 0x22 /* page down */ #define kNtVkEnd 0x23 #define kNtVkHome 0x24 #define kNtVkLeft 0x25 diff --git a/libc/proc/execve-nt.greg.c b/libc/proc/execve-nt.greg.c index b5ca7b231..5b7837685 100644 --- a/libc/proc/execve-nt.greg.c +++ b/libc/proc/execve-nt.greg.c @@ -180,9 +180,6 @@ static keywords void sys_execve_nt_relay(intptr_t h, long b, long c, long d) { // close more handles __imp_SetConsoleCtrlHandler((void *)sys_execve_nt_event, 1); - PurgeThread(g_fds.stdin.thread); - PurgeHandle(g_fds.stdin.reader); - PurgeHandle(g_fds.stdin.writer); for (i = 0; i < g_fds.n; ++i) { if (g_fds.p[i].kind != kFdEmpty) { PurgeHandle(g_fds.p[i].handle); diff --git a/libc/proc/execve-sysv.c b/libc/proc/execve-sysv.c index 6120edec8..3bfdcda2d 100644 --- a/libc/proc/execve-sysv.c +++ b/libc/proc/execve-sysv.c @@ -58,8 +58,7 @@ static bool IsApeFile(const char *path) { return true; } else { bool res = false; - BLOCK_CANCELLATIONS; - BEGIN_CANCELLATION_POINT; + BLOCK_SIGNALS; int fd; char buf[8]; int flags = O_RDONLY | O_NOCTTY | O_NONBLOCK | O_CLOEXEC; @@ -67,8 +66,7 @@ static bool IsApeFile(const char *path) { res = sys_pread(fd, buf, 8, 0, 0) == 8 && IsApeLoadable(buf); sys_close(fd); } - END_CANCELLATION_POINT; - ALLOW_CANCELLATIONS; + ALLOW_SIGNALS; return res; } } diff --git a/libc/calls/fexecve.c b/libc/proc/fexecve.c similarity index 100% rename from libc/calls/fexecve.c rename to libc/proc/fexecve.c diff --git a/libc/proc/fork-nt.c b/libc/proc/fork-nt.c index 4d51c4a8b..afb77dc39 100644 --- a/libc/proc/fork-nt.c +++ b/libc/proc/fork-nt.c @@ -199,10 +199,7 @@ textwindows void WinMainForked(void) { char16_t fvar[21 + 1 + 21 + 1]; uint32_t i, varlen, oldprot, savepid; long mapcount, mapcapacity, specialz; - - struct StdinRelay stdin; struct Fds *fds = __veil("r", &g_fds); - stdin = fds->stdin; // check to see if the process was actually forked // this variable should have the pipe handle numba @@ -282,7 +279,6 @@ textwindows void WinMainForked(void) { // rewrap the stdin named pipe hack // since the handles closed on fork - fds->stdin = stdin; fds->p[0].handle = GetStdHandle(kNtStdInputHandle); fds->p[1].handle = GetStdHandle(kNtStdOutputHandle); fds->p[2].handle = GetStdHandle(kNtStdErrorHandle); diff --git a/libc/proc/kill-nt.c b/libc/proc/kill-nt.c index df9d73e84..d99ab28ba 100644 --- a/libc/proc/kill-nt.c +++ b/libc/proc/kill-nt.c @@ -18,11 +18,14 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" #include "libc/errno.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/dll.h" #include "libc/nt/errors.h" #include "libc/nt/runtime.h" #include "libc/proc/proc.internal.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/thread.h" +#include "libc/thread/tls.h" #ifdef __x86_64__ static textwindows int sys_kill_nt_impl(int pid, int sig) { @@ -50,7 +53,6 @@ static textwindows int sys_kill_nt_impl(int pid, int sig) { } textwindows int sys_kill_nt(int pid, int sig) { - int rc; if (!(0 <= sig && sig <= 64)) return einval(); // XXX: NT doesn't really have process groups. For instance the @@ -66,9 +68,14 @@ textwindows int sys_kill_nt(int pid, int sig) { return raise(sig); } + int rc; + uint64_t m; + m = atomic_exchange(&__get_tls()->tib_sigmask, -1); __proc_lock(); + pthread_cleanup_push((void *)__proc_unlock, 0); rc = sys_kill_nt_impl(pid, sig); - __proc_unlock(); + pthread_cleanup_pop(true); + atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release); return rc; } diff --git a/libc/calls/mkntcmdline.c b/libc/proc/mkntcmdline.c similarity index 100% rename from libc/calls/mkntcmdline.c rename to libc/proc/mkntcmdline.c diff --git a/libc/calls/mkntenvblock.c b/libc/proc/mkntenvblock.c similarity index 100% rename from libc/calls/mkntenvblock.c rename to libc/proc/mkntenvblock.c diff --git a/libc/proc/proc.c b/libc/proc/proc.c index 4565e8b90..9a4308297 100644 --- a/libc/proc/proc.c +++ b/libc/proc/proc.c @@ -144,7 +144,7 @@ textwindows struct Proc *__proc_new(void) { struct Dll *e; struct Proc *proc = 0; int i, n = ARRAYLEN(__proc.pool); - if (atomic_load_explicit(&__proc.allocated, memory_order_relaxed) < n && + if (atomic_load_explicit(&__proc.allocated, memory_order_acquire) < n && (i = atomic_fetch_add(&__proc.allocated, 1)) < n) { proc = __proc.pool + i; } else { diff --git a/libc/proc/wait4-nt.c b/libc/proc/wait4-nt.c index 47a7e688b..6a738f734 100644 --- a/libc/proc/wait4-nt.c +++ b/libc/proc/wait4-nt.c @@ -26,6 +26,7 @@ #include "libc/cosmo.h" #include "libc/errno.h" #include "libc/fmt/conv.h" +#include "libc/intrin/atomic.h" #include "libc/intrin/dll.h" #include "libc/intrin/strace.internal.h" #include "libc/macros.internal.h" @@ -38,6 +39,7 @@ #include "libc/str/str.h" #include "libc/sysv/consts/w.h" #include "libc/sysv/errfuns.h" +#include "libc/thread/thread.h" #include "libc/thread/tls.h" #ifdef __x86_64__ @@ -98,7 +100,7 @@ static textwindows int CheckZombies(int pid, int *wstatus, } static textwindows int WaitForProcess(int pid, int *wstatus, int options, - struct rusage *opt_out_rusage) { + struct rusage *rusage, uint64_t *m) { int rc, *wv; nsync_cv *cv; struct Dll *e; @@ -106,7 +108,7 @@ static textwindows int WaitForProcess(int pid, int *wstatus, int options, struct timespec deadline = timespec_zero; // check list of processes that've already exited - if ((rc = CheckZombies(pid, wstatus, opt_out_rusage))) { + if ((rc = CheckZombies(pid, wstatus, rusage))) { return rc; } @@ -139,16 +141,16 @@ CheckForInterrupt: if (_check_interrupts(kSigOpRestartable) == -1) return -1; deadline = GetNextDeadline(deadline); SpuriousWakeup: - BEGIN_BLOCKING_OPERATION; ++*wv; + atomic_store_explicit(&__get_tls()->tib_sigmask, *m, memory_order_release); rc = nsync_cv_wait_with_deadline(cv, &__proc.lock, deadline, 0); + *m = atomic_exchange(&__get_tls()->tib_sigmask, -1); --*wv; - END_BLOCKING_OPERATION; if (rc == ECANCELED) return ecanceled(); - if (pr && pr->iszombie) return ReapZombie(pr, wstatus, opt_out_rusage); + if (pr && pr->iszombie) return ReapZombie(pr, wstatus, rusage); if (rc == ETIMEDOUT) goto CheckForInterrupt; unassert(!rc); - if (!pr && (rc = CheckZombies(pid, wstatus, opt_out_rusage))) return rc; + if (!pr && (rc = CheckZombies(pid, wstatus, rusage))) return rc; goto SpuriousWakeup; } @@ -163,9 +165,12 @@ textwindows int sys_wait4_nt(int pid, int *opt_out_wstatus, int options, // just does an "ignore ctrl-c" internally. if (pid == 0) pid = -1; if (pid < -1) pid = -pid; + uint64_t m = atomic_exchange(&__get_tls()->tib_sigmask, -1); __proc_lock(); - rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage); - __proc_unlock(); + pthread_cleanup_push((void *)__proc_unlock, 0); + rc = WaitForProcess(pid, opt_out_wstatus, options, opt_out_rusage, &m); + pthread_cleanup_pop(true); + atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release); return rc; } diff --git a/libc/runtime/enable_tls.c b/libc/runtime/enable_tls.c index a82569643..77f1bef2f 100644 --- a/libc/runtime/enable_tls.c +++ b/libc/runtime/enable_tls.c @@ -17,7 +17,6 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "ape/sections.internal.h" -#include "libc/assert.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" @@ -120,8 +119,8 @@ textstartup void __enable_tls(void) { // _tbss_start + _tbss_size: // _tdata_start + _tls_size: // - unassert(_tbss_start == _tdata_start + I(_tbss_offset)); - unassert(_tbss_start + I(_tbss_size) == _tdata_start + I(_tls_size)); + // unassert(_tbss_start == _tdata_start + I(_tbss_offset)); + // unassert(_tbss_start + I(_tbss_size) == _tdata_start + I(_tls_size)); #ifdef __x86_64__ @@ -136,10 +135,7 @@ textstartup void __enable_tls(void) { // malloc() being linked, which links _mapanon(). otherwise // if you exceed this, you need to __static_yoink("_mapanon"). // please note that it's probably too early to call calloc() - npassert(_weaken(_mapanon)); - siz = ROUNDUP(siz, FRAMESIZE); mem = _weaken(_mapanon)(siz); - npassert(mem); } if (IsAsan()) { @@ -158,10 +154,7 @@ textstartup void __enable_tls(void) { if (siz <= sizeof(__static_tls)) { mem = __static_tls; } else { - npassert(_weaken(_mapanon)); - siz = ROUNDUP(siz, FRAMESIZE); mem = _weaken(_mapanon)(siz); - npassert(mem); } if (IsAsan()) { @@ -221,7 +214,9 @@ textstartup void __enable_tls(void) { _pthread_list = &_pthread_static.list; atomic_store_explicit(&_pthread_static.ptid, tid, memory_order_relaxed); if (IsWindows()) { - npassert((_pthread_static.semaphore = CreateSemaphore(0, 0, 1, 0))); + if (!(_pthread_static.semaphore = CreateSemaphore(0, 0, 1, 0))) { + notpossible; + } } // copy in initialized data section diff --git a/libc/runtime/ftraceinit.greg.c b/libc/runtime/ftraceinit.greg.c index 74717a415..96c9e33bc 100644 --- a/libc/runtime/ftraceinit.greg.c +++ b/libc/runtime/ftraceinit.greg.c @@ -18,6 +18,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" +#include "libc/runtime/symbols.internal.h" #include "libc/str/str.h" #include "libc/thread/tls.h" @@ -36,6 +37,9 @@ __static_yoink("zipos"); * @see libc/runtime/_init.S for documentation */ textstartup int ftrace_init(void) { + if (strace_enabled(0) > 0) { + GetSymbolTable(); + } if (__intercept_flag(&__argc, __argv, "--ftrace")) { ftrace_install(); ftrace_enabled(+1); diff --git a/libc/runtime/getdosargv.c b/libc/runtime/getdosargv.c index 97f58911e..7f1313cf8 100644 --- a/libc/runtime/getdosargv.c +++ b/libc/runtime/getdosargv.c @@ -87,8 +87,8 @@ static textwindows int Count(int c, struct DosArgv *st) { // @see test/libc/dosarg_test.c // @see libc/runtime/ntspawn.c // @note kudos to Simon Tatham for figuring out quoting behavior -textwindows int GetDosArgv(const char16_t *cmdline, char *buf, - size_t size, char **argv, size_t max) { +textwindows int GetDosArgv(const char16_t *cmdline, char *buf, size_t size, + char **argv, size_t max) { bool inquote; int i, argc, slashes, quotes, ignore; static struct DosArgv st_; @@ -107,32 +107,6 @@ textwindows int GetDosArgv(const char16_t *cmdline, char *buf, if (!st->wc) break; if (++argc < max) { argv[argc - 1] = st->p < st->pe ? st->p : NULL; - if (argc == 1) { - // windows lets you run "foo.com" without saying "./foo.com" - // which caused emacs to crash after searching for itself :( - char16_t cmd[256]; - uint32_t i, j, attr; - i = j = 0; - cmd[j++] = st->wc; - for (; st->s[i]; ++i) { - if (i == 255 || st->s[i] == '/' || st->s[i] == '\\') { - goto GiveUpAddingDotSlash; - } - if (st->s[i] == ' ' || st->s[i] == '\t') { - break; - } - cmd[j++] = st->s[i]; - } - cmd[j] = 0; - if (IsWindows() && // - (attr = __imp_GetFileAttributesW(cmd)) != -1u && // - !(attr & kNtFileAttributeDirectory)) { - AppendDosArgv('.', st); - AppendDosArgv('\\', st); - } - GiveUpAddingDotSlash: - donothing; - } } inquote = false; while (st->wc) { diff --git a/libc/runtime/mmap.c b/libc/runtime/mmap.c index e7afe843c..ea026ef25 100644 --- a/libc/runtime/mmap.c +++ b/libc/runtime/mmap.c @@ -157,9 +157,9 @@ static bool __auto_map(int count, int align, int *res) { *res + count <= FRAME(kAutomapStart + (kAutomapSize - 1)); } -static void *__finish_memory(void *addr, size_t size, int prot, - int flags, int fd, int64_t off, int f, - int x, int n, struct DirectMap dm) { +static void *__finish_memory(void *addr, size_t size, int prot, int flags, + int fd, int64_t off, int f, int x, int n, + struct DirectMap dm) { if (!IsWindows() && (flags & MAP_FIXED)) { if (__untrack_memories(addr, size)) { __mmap_die("FIXED UNTRACK FAILED"); @@ -178,8 +178,8 @@ static void *__finish_memory(void *addr, size_t size, int prot, return addr; } -static void *__map_memory(void *addr, size_t size, int prot, int flags, - int fd, int64_t off, int f, int x, int n) { +static void *__map_memory(void *addr, size_t size, int prot, int flags, int fd, + int64_t off, int f, int x, int n) { struct DirectMap dm; dm = sys_mmap(addr, size, prot, f, fd, off); if (VERY_UNLIKELY(dm.addr == MAP_FAILED)) { @@ -200,9 +200,10 @@ static void *__map_memory(void *addr, size_t size, int prot, int flags, * This is useful on Windows since it allows us to partially unmap or * punch holes into existing mappings. */ -static textwindows dontinline void *__map_memories( - char *addr, size_t size, int prot, int flags, int fd, int64_t off, int f, - int x, int n) { +static textwindows dontinline void *__map_memories(char *addr, size_t size, + int prot, int flags, int fd, + int64_t off, int f, int x, + int n) { size_t i, m; int64_t oi, sz; struct DirectMap dm; @@ -238,8 +239,8 @@ static textwindows dontinline void *__map_memories( return addr; } -inline void *__mmap_unlocked(void *addr, size_t size, int prot, - int flags, int fd, int64_t off) { +inline void *__mmap_unlocked(void *addr, size_t size, int prot, int flags, + int fd, int64_t off) { char *p = addr; struct DirectMap dm; size_t requested_size; @@ -480,3 +481,5 @@ void *mmap(void *addr, size_t size, int prot, int flags, int fd, int64_t off) { toto); return res; } + +__strong_reference(mmap, mmap64); diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index edfb47eb9..963b2a89b 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -93,6 +93,7 @@ void __printargs(const char *); int ftrace_install(void); int ftrace_enabled(int); int strace_enabled(int); +bool strace_enter(void); void _bt(const char *, ...); void __print_maps(void); long _GetMaxFd(void); @@ -141,6 +142,15 @@ long __get_minsigstksz(void) pureconst; void __get_main_stack(void **, size_t *, int *); long __get_safe_size(long, long); char *__get_tmpdir(void); +__funline int __trace_disabled(int x) { + return 0; +} +#ifndef FTRACE +#define ftrace_enabled __trace_disabled +#endif +#ifndef SYSDEBUG +#define strace_enabled __trace_disabled +#endif #endif /* _COSMO_SOURCE */ COSMOPOLITAN_C_END_ diff --git a/libc/runtime/set_tls.c b/libc/runtime/set_tls.c index 3cabed830..9faebba57 100644 --- a/libc/runtime/set_tls.c +++ b/libc/runtime/set_tls.c @@ -20,7 +20,6 @@ #include "libc/calls/calls.h" #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" -#include "libc/errno.h" #include "libc/nexgen32e/msr.internal.h" #include "libc/nt/thread.h" #include "libc/sysv/consts/arch.h" @@ -49,9 +48,7 @@ textstartup void __set_tls(struct CosmoTib *tib) { sys_set_tls(tib); } else if (IsXnu()) { // thread_fast_set_cthread_self has a weird ABI - int e = errno; sys_set_tls((intptr_t)tib - 0x30); - errno = e; } else { uint64_t val = (uint64_t)tib; asm volatile("wrmsr" diff --git a/libc/runtime/syslib.internal.h b/libc/runtime/syslib.internal.h index 7942ad6ce..de3cc8a4e 100644 --- a/libc/runtime/syslib.internal.h +++ b/libc/runtime/syslib.internal.h @@ -12,7 +12,7 @@ COSMOPOLITAN_C_START_ */ #define SYSLIB_MAGIC ('s' | 'l' << 8 | 'i' << 16 | 'b' << 24) -#define SYSLIB_VERSION 3 +#define SYSLIB_VERSION 4 typedef uint64_t dispatch_time_t; typedef uint64_t dispatch_semaphore_t; @@ -49,6 +49,16 @@ struct Syslib { int (*__pthread_attr_destroy)(void *); int (*__pthread_attr_setstacksize)(void *, size_t); int (*__pthread_attr_setguardsize)(void *, size_t); + /* v4 (2023-09-19) */ + void (*__exit)(int); + long (*__close)(int); + long (*__munmap)(void *, size_t); + long (*__openat)(int, const void *, int, int); + long (*__write)(int, const void *, size_t); + long (*__read)(int, void *, size_t); + long (*__sigaction)(int, const void *, void *); + long (*__pselect)(int, void *, void *, void *, const void *, const void *); + long (*__mprotect)(void *, size_t, int); }; extern struct Syslib *__syslib; diff --git a/libc/runtime/winargs.internal.h b/libc/runtime/winargs.internal.h index 065d04bc8..b854fba61 100644 --- a/libc/runtime/winargs.internal.h +++ b/libc/runtime/winargs.internal.h @@ -6,10 +6,11 @@ COSMOPOLITAN_C_START_ struct WinArgs { char *argv[4096]; - char *envp[4092]; + char *envp[4060]; intptr_t auxv[2][2]; char argblock[ARG_MAX / 2]; char envblock[ARG_MAX / 2]; + char argv0buf[256]; }; COSMOPOLITAN_C_END_ diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index c33199965..e06a6f000 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -19,6 +19,7 @@ #include "libc/assert.h" #include "libc/calls/internal.h" #include "libc/calls/syscall_support-nt.internal.h" +#include "libc/intrin/bits.h" #include "libc/intrin/weaken.h" #include "libc/limits.h" #include "libc/log/libfatal.internal.h" @@ -145,6 +146,12 @@ static abi wontreturn void WinInit(const char16_t *cmdline) { } } + // avoid programs like emacs nagging the user to define this + char16_t var[8]; + if (!__imp_GetEnvironmentVariableW(u"TERM", var, 8)) { + __imp_SetEnvironmentVariableW(u"TERM", u"xterm-256color"); + } + // allocate memory for stack and argument block _Static_assert(sizeof(struct WinArgs) % FRAMESIZE == 0, ""); _mmi.p = _mmi.s; @@ -173,6 +180,21 @@ static abi wontreturn void WinInit(const char16_t *cmdline) { int count = GetDosArgv(cmdline, wa->argblock, ARRAYLEN(wa->argblock), wa->argv, ARRAYLEN(wa->argv)); + // normalize executable path + if (wa->argv[0] && !WinFileExists(wa->argv[0])) { + unsigned i, n = 0; + while (wa->argv[0][n]) ++n; + if (n + 4 < sizeof(wa->argv0buf)) { + for (i = 0; i < n; ++i) { + wa->argv0buf[i] = wa->argv[0][i]; + } + WRITE32LE(wa->argv0buf + i, READ32LE(".com")); + if (WinFileExists(wa->argv0buf)) { + wa->argv[0] = wa->argv0buf; + } + } + } + // munge argv so dos paths become cosmo paths for (int i = 0; wa->argv[i]; ++i) { if (wa->argv[i][0] == '\\' && // diff --git a/libc/sock/pselect.c b/libc/sock/pselect.c index 538a3ddc7..2f04ca9d1 100644 --- a/libc/sock/pselect.c +++ b/libc/sock/pselect.c @@ -28,6 +28,7 @@ #include "libc/intrin/strace.internal.h" #include "libc/sock/internal.h" #include "libc/sock/select.h" +#include "libc/sock/select.internal.h" #include "libc/sysv/consts/nrlinux.h" #include "libc/sysv/errfuns.h" @@ -68,6 +69,15 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, } ss; BEGIN_CANCELLATION_POINT; +#ifdef SYSDEBUG + fd_set old_readfds; + fd_set *old_readfds_ptr = 0; + fd_set old_writefds; + fd_set *old_writefds_ptr = 0; + fd_set old_exceptfds; + fd_set *old_exceptfds_ptr = 0; +#endif + if (nfds < 0) { rc = einval(); } else if (IsAsan() && @@ -77,33 +87,55 @@ int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, (timeout && !__asan_is_valid(timeout, sizeof(*timeout))) || (sigmask && !__asan_is_valid(sigmask, sizeof(*sigmask))))) { rc = efault(); - } else if (IsLinux()) { - if (timeout) { - ts = *timeout; - tsp = &ts; - } else { - tsp = 0; - } - ss.s = sigmask; - ss.n = 8; - rc = sys_pselect(nfds, readfds, writefds, exceptfds, tsp, &ss); - } else if (!IsWindows()) { - rc = sys_pselect(nfds, readfds, writefds, exceptfds, - (struct timespec *)timeout, sigmask); } else { - if (timeout) { - tv.tv_sec = timeout->tv_sec; - tv.tv_usec = timeout->tv_nsec / 1000; - tvp = &tv; - } else { - tvp = 0; +#ifdef SYSDEBUG + if (readfds) { + old_readfds = *readfds; + old_readfds_ptr = &old_readfds; + } + if (writefds) { + old_writefds = *writefds; + old_writefds_ptr = &old_writefds; + } + if (exceptfds) { + old_exceptfds = *exceptfds; + old_exceptfds_ptr = &old_exceptfds; + } +#endif + if (IsLinux()) { + if (timeout) { + ts = *timeout; + tsp = &ts; + } else { + tsp = 0; + } + ss.s = sigmask; + ss.n = 8; + rc = sys_pselect(nfds, readfds, writefds, exceptfds, tsp, &ss); + } else if (!IsWindows()) { + rc = sys_pselect(nfds, readfds, writefds, exceptfds, + (struct timespec *)timeout, sigmask); + } else { + if (timeout) { + tv.tv_sec = timeout->tv_sec; + tv.tv_usec = timeout->tv_nsec / 1000; + tvp = &tv; + } else { + tvp = 0; + } + rc = sys_select_nt(nfds, readfds, writefds, exceptfds, tvp, sigmask); } - rc = sys_select_nt(nfds, readfds, writefds, exceptfds, tvp, sigmask); } END_CANCELLATION_POINT; - POLLTRACE("pselect(%d, %p, %p, %p, %s, %s) → %d% m", nfds, readfds, writefds, - exceptfds, DescribeTimespec(0, timeout), DescribeSigset(0, sigmask), - rc); + STRACE("pselect(%d, %s → [%s], %s → [%s], %s → [%s], %s, %s) → %d% m", nfds, + DescribeFdSet(rc, nfds, old_readfds_ptr), + DescribeFdSet(rc, nfds, readfds), + DescribeFdSet(rc, nfds, old_writefds_ptr), + DescribeFdSet(rc, nfds, writefds), + DescribeFdSet(rc, nfds, old_exceptfds_ptr), + DescribeFdSet(rc, nfds, exceptfds), // + DescribeTimespec(0, timeout), // + DescribeSigset(0, sigmask), rc); return rc; } diff --git a/libc/sock/select-nt.c b/libc/sock/select-nt.c index 72bf4f100..276dfb646 100644 --- a/libc/sock/select-nt.c +++ b/libc/sock/select-nt.c @@ -16,10 +16,12 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/bo.internal.h" #include "libc/calls/internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/struct/timeval.h" +#include "libc/limits.h" #include "libc/macros.internal.h" #include "libc/sock/select.h" #include "libc/sock/sock.h" @@ -34,11 +36,10 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout, const sigset_t *sigmask) { - uint64_t millis; int i, pfds, events, fdcount; - struct pollfd fds[64]; // convert bitsets to pollfd + struct pollfd fds[64]; for (pfds = i = 0; i < nfds; ++i) { events = 0; if (readfds && FD_ISSET(i, readfds)) events |= POLLIN; @@ -57,26 +58,49 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds, } // convert the wait time to a word + uint32_t millis; if (!timeout) { millis = -1; } else { - millis = timeval_tomillis(*timeout); + int64_t ms = timeval_tomillis(*timeout); + if (ms < 0 || ms > UINT32_MAX) { + millis = -1u; + } else { + millis = ms; + } } // call our nt poll implementation BEGIN_BLOCKING_OPERATION; fdcount = sys_poll_nt(fds, pfds, &millis, sigmask); + unassert(fdcount < 64); END_BLOCKING_OPERATION; - if (fdcount == -1) return -1; + if (fdcount < 0) return -1; // convert pollfd back to bitsets if (readfds) FD_ZERO(readfds); if (writefds) FD_ZERO(writefds); if (exceptfds) FD_ZERO(exceptfds); - for (i = 0; i < fdcount; ++i) { - if (fds[i].revents & POLLIN) FD_SET(fds[i].fd, readfds); - if (fds[i].revents & POLLOUT) FD_SET(fds[i].fd, writefds); - if (fds[i].revents & (POLLERR | POLLNVAL)) FD_SET(fds[i].fd, exceptfds); + int bits = 0; + for (i = 0; i < pfds; ++i) { + if (fds[i].revents & POLLIN) { + if (readfds) { + FD_SET(fds[i].fd, readfds); + ++bits; + } + } + if (fds[i].revents & POLLOUT) { + if (writefds) { + FD_SET(fds[i].fd, writefds); + ++bits; + } + } + if (fds[i].revents & (POLLERR | POLLNVAL)) { + if (exceptfds) { + FD_SET(fds[i].fd, exceptfds); + ++bits; + } + } } // store remaining time back in caller's timeval @@ -84,7 +108,7 @@ int sys_select_nt(int nfds, fd_set *readfds, fd_set *writefds, *timeout = timeval_frommillis(millis); } - return fdcount; + return bits; } #endif /* __x86_64__ */ diff --git a/libc/sock/select.c b/libc/sock/select.c index fab5f6b89..6c2ba0f0d 100644 --- a/libc/sock/select.c +++ b/libc/sock/select.c @@ -27,6 +27,8 @@ #include "libc/intrin/describeflags.internal.h" #include "libc/intrin/strace.internal.h" #include "libc/sock/internal.h" +#include "libc/sock/select.h" +#include "libc/sock/select.internal.h" #include "libc/sysv/errfuns.h" /** @@ -88,7 +90,8 @@ int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, } END_CANCELLATION_POINT; - POLLTRACE("select(%d, %p, %p, %p, [%s]) → %d% m", nfds, readfds, writefds, - exceptfds, DescribeTimeval(rc, tvp), rc); + STRACE("select(%d, [%s], [%s], [%s], [%s]) → %d% m", nfds, + DescribeFdSet(rc, nfds, readfds), DescribeFdSet(rc, nfds, writefds), + DescribeFdSet(rc, nfds, exceptfds), DescribeTimeval(rc, tvp), rc); return rc; } diff --git a/libc/sock/select.internal.h b/libc/sock/select.internal.h new file mode 100644 index 000000000..a1d767372 --- /dev/null +++ b/libc/sock/select.internal.h @@ -0,0 +1,13 @@ +#ifndef COSMOPOLITAN_LIBC_SOCK_SELECT_INTERNAL_H_ +#define COSMOPOLITAN_LIBC_SOCK_SELECT_INTERNAL_H_ +#include "libc/mem/alloca.h" +#include "libc/sock/select.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +const char *DescribeFdSet(char[100], ssize_t, int, fd_set *); +#define DescribeFdSet(x, y, z) DescribeFdSet(alloca(100), x, y, z) + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_SOCK_SELECT_INTERNAL_H_ */ diff --git a/libc/sock/sendfile.c b/libc/sock/sendfile.c index 06ea84ec4..0991ece9c 100644 --- a/libc/sock/sendfile.c +++ b/libc/sock/sendfile.c @@ -219,3 +219,5 @@ ssize_t sendfile(int outfd, int infd, int64_t *opt_in_out_inoffset, DescribeInOutInt64(rc, opt_in_out_inoffset), uptobytes, rc); return rc; } + +__weak_reference(sendfile, sendfile64); diff --git a/libc/sock/struct/pollfd.internal.h b/libc/sock/struct/pollfd.internal.h index 673a3a7a4..adaec48cb 100644 --- a/libc/sock/struct/pollfd.internal.h +++ b/libc/sock/struct/pollfd.internal.h @@ -12,7 +12,7 @@ int32_t __sys_poll(struct pollfd *, uint64_t, signed); int sys_ppoll(struct pollfd *, size_t, const struct timespec *, const sigset_t *, size_t); int sys_poll_metal(struct pollfd *, size_t, unsigned); -int sys_poll_nt(struct pollfd *, uint64_t, uint64_t *, const sigset_t *); +int sys_poll_nt(struct pollfd *, uint64_t, uint32_t *, const sigset_t *); const char *DescribePollFds(char[300], ssize_t, struct pollfd *, size_t); #define DescribePollFds(x, y, z) DescribePollFds(alloca(300), x, y, z) diff --git a/libc/sock/wsablock.c b/libc/sock/wsablock.c index 361eb7369..24b9831ed 100644 --- a/libc/sock/wsablock.c +++ b/libc/sock/wsablock.c @@ -22,6 +22,7 @@ #include "libc/calls/sig.internal.h" #include "libc/calls/struct/timespec.h" #include "libc/errno.h" +#include "libc/intrin/weaken.h" #include "libc/nt/enum/wait.h" #include "libc/nt/enum/wsa.h" #include "libc/nt/errors.h" @@ -113,6 +114,10 @@ BlockingOperation: if (WSAGetOverlappedResult(f->handle, overlapped, &got, nonblock, flags)) { rc = got; } else { + if (_weaken(pthread_testcancel_np) && + (err = _weaken(pthread_testcancel_np)())) { + return ecanceled(); + } rc = -1; err = WSAGetLastError(); if (err == kNtErrorOperationAborted) { diff --git a/libc/stdio/dirstream.c b/libc/stdio/dirstream.c index c696481c3..bead6c70e 100644 --- a/libc/stdio/dirstream.c +++ b/libc/stdio/dirstream.c @@ -677,3 +677,4 @@ void seekdir(DIR *dir, long tell) { } __weak_reference(readdir, readdir64); +__weak_reference(readdir_r, readdir_r64); diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index 342071111..cb66ccd64 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -396,9 +396,6 @@ syscon fcntl F_OFD_GETLK 36 36 -1 -1 -1 -1 -1 -1 # listed in syscon fcntl F_RDLCK 0 0 1 1 1 1 1 0 # polyfilled nt; bsd consensus syscon fcntl F_WRLCK 1 1 3 3 3 3 3 1 # polyfilled nt; bsd consensus syscon fcntl F_UNLCK 2 2 2 2 2 2 2 2 # polyfilled nt; unix consensus -syscon compat F_SETLK64 6 6 8 8 12 8 8 6 # polyfilled nt -syscon compat F_SETLKW64 7 7 9 9 13 9 9 7 -syscon compat F_GETLK64 5 5 7 7 11 7 7 5 # polyfilled nt syscon fcntl F_SETSIG 10 10 -1 -1 -1 -1 -1 -1 syscon fcntl F_GETSIG 11 11 -1 -1 -1 -1 -1 -1 diff --git a/libc/sysv/consts/F_GETLK64.S b/libc/sysv/consts/F_GETLK64.S deleted file mode 100644 index 05f78ee95..000000000 --- a/libc/sysv/consts/F_GETLK64.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon compat,F_GETLK64,5,5,7,7,11,7,7,5 diff --git a/libc/sysv/consts/F_SETLK64.S b/libc/sysv/consts/F_SETLK64.S deleted file mode 100644 index 8ac643e3d..000000000 --- a/libc/sysv/consts/F_SETLK64.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon compat,F_SETLK64,6,6,8,8,12,8,8,6 diff --git a/libc/sysv/consts/F_SETLKW64.S b/libc/sysv/consts/F_SETLKW64.S deleted file mode 100644 index 3981eba5d..000000000 --- a/libc/sysv/consts/F_SETLKW64.S +++ /dev/null @@ -1,2 +0,0 @@ -#include "libc/sysv/consts/syscon.internal.h" -.syscon compat,F_SETLKW64,7,7,9,9,13,9,9,7 diff --git a/libc/sysv/consts/f.h b/libc/sysv/consts/f.h index 7a466096b..8bf1d1b7f 100644 --- a/libc/sysv/consts/f.h +++ b/libc/sysv/consts/f.h @@ -15,14 +15,12 @@ extern const int F_FULLFSYNC; extern const int F_GETFD; extern const int F_GETFL; extern const int F_GETLEASE; -extern const int F_GETLK64; extern const int F_GETLK; extern const int F_GETNOSIGPIPE; extern const int F_GETOWN; extern const int F_GETPATH; extern const int F_GETPIPE_SZ; extern const int F_GETSIG; -extern const int F_LOCK; extern const int F_MAXFD; extern const int F_NOCACHE; extern const int F_NOTIFY; @@ -33,9 +31,7 @@ extern const int F_RDLCK; extern const int F_SETFD; extern const int F_SETFL; extern const int F_SETLEASE; -extern const int F_SETLK64; extern const int F_SETLK; -extern const int F_SETLKW64; extern const int F_SETLKW; extern const int F_SETNOSIGPIPE; extern const int F_SETOWN; @@ -61,14 +57,11 @@ COSMOPOLITAN_C_END_ * posix advisory locks * polyfilled poorly on windows */ -#define F_SETLK F_SETLK -#define F_SETLK64 F_SETLK64 -#define F_SETLKW F_SETLKW -#define F_SETLKW64 F_SETLKW64 -#define F_GETLK F_GETLK -#define F_GETLK64 F_GETLK64 -#define F_RDLCK F_RDLCK -#define F_UNLCK F_UNLCK -#define F_WRLCK F_WRLCK +#define F_SETLK F_SETLK +#define F_SETLKW F_SETLKW +#define F_GETLK F_GETLK +#define F_RDLCK F_RDLCK +#define F_UNLCK F_UNLCK +#define F_WRLCK F_WRLCK #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_F_H_ */ diff --git a/libc/thread/pthread_cancel.c b/libc/thread/pthread_cancel.c index 14c54d8a0..66ab9c7d7 100644 --- a/libc/thread/pthread_cancel.c +++ b/libc/thread/pthread_cancel.c @@ -92,11 +92,7 @@ static void _pthread_cancel_sig(int sig, siginfo_t *si, void *arg) { // punts cancellation to start of next cancellation point // we ensure sigthr is a pending signal in case unblocked - if (IsXnuSilicon()) { - __syslib->__pthread_kill(_pthread_syshand(pt), sig); - } else { - sys_tkill(_pthread_tid(pt), sig, __get_tls()); - } + raise(sig); } static void _pthread_cancel_listen(void) { @@ -111,10 +107,7 @@ static void _pthread_cancel_listen(void) { static void pthread_cancel_nt(struct PosixThread *pt, intptr_t hThread) { uint32_t old_suspend_count; - if (!(pt->pt_flags & PT_NOCANCEL) && - (pt->pt_flags & (PT_ASYNC | PT_MASKED))) { - pt->pt_flags |= PT_NOCANCEL; - pt->abort_errno = ECANCELED; + if (!(pt->pt_flags & PT_NOCANCEL)) { if ((pt->pt_flags & PT_ASYNC) && (old_suspend_count = SuspendThread(hThread)) != -1u) { if (!old_suspend_count) { @@ -125,13 +118,13 @@ static void pthread_cancel_nt(struct PosixThread *pt, intptr_t hThread) { cpu.Rdi = (uintptr_t)PTHREAD_CANCELED; cpu.Rsp &= -16; *(uintptr_t *)(cpu.Rsp -= sizeof(uintptr_t)) = cpu.Rip; - pt->abort_errno = ECANCELED; unassert(SetThreadContext(hThread, &cpu)); } } ResumeThread(hThread); } - __sig_cancel(pt); + pt->abort_errno = ECANCELED; + __sig_cancel(pt, 0); } } diff --git a/libc/thread/pthread_create.c b/libc/thread/pthread_create.c index 2efd84fc7..275dfa5e8 100644 --- a/libc/thread/pthread_create.c +++ b/libc/thread/pthread_create.c @@ -39,6 +39,7 @@ #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" #include "libc/nt/runtime.h" +#include "libc/nt/synchronization.h" #include "libc/runtime/runtime.h" #include "libc/runtime/stack.h" #include "libc/runtime/syslib.internal.h" @@ -149,6 +150,11 @@ static errno_t pthread_create_impl(pthread_t *thread, } pt->start = start_routine; pt->arg = arg; + if (IsWindows()) { + if (!(pt->semaphore = CreateSemaphore(0, 0, 1, 0))) { + notpossible; + } + } // create thread local storage memory if (!(pt->tls = _mktls(&pt->tib))) { diff --git a/test/libc/calls/read_test.c b/test/libc/calls/read_test.c index e0f67d551..a23255263 100644 --- a/test/libc/calls/read_test.c +++ b/test/libc/calls/read_test.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/atomic.h" #include "libc/calls/calls.h" #include "libc/calls/cp.internal.h" #include "libc/calls/internal.h" @@ -26,12 +27,14 @@ #include "libc/calls/syscall-sysv.internal.h" #include "libc/dce.h" #include "libc/errno.h" +#include "libc/runtime/runtime.h" #include "libc/sock/internal.h" #include "libc/sysv/consts/nr.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sig.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" +#include "libc/thread/thread.h" void SetUpOnce(void) { testlib_enable_tmp_setup_teardown(); @@ -82,6 +85,57 @@ TEST(read_directory, eisdir) { ASSERT_SYS(0, 0, close(3)); } +int fds[2]; +jmp_buf jb; +pthread_t th; +atomic_bool isdone; + +void *GenerateSignals(void *arg) { + while (!isdone) { + usleep(123); + pthread_kill(th, SIGINT); + } + return 0; +} + +void *GenerateData(void *arg) { + for (;;) { + usleep(223); + int rc = write(fds[1], "hi", 2); + if (rc == -1 && errno == EPIPE) break; + ASSERT_EQ(2, rc); + } + return 0; +} + +void OnSig(int sig) { + char buf[8]; + ASSERT_SYS(0, 2, read(fds[0], buf, 8)); + longjmp(jb, 1); +} + +TEST(read, whatEmacsDoes) { + pthread_t sigth; + sighandler_t sh1 = signal(SIGINT, SIG_IGN); + sighandler_t sh2 = signal(SIGPIPE, SIG_IGN); + ASSERT_SYS(0, 0, pipe(fds)); + ASSERT_EQ(0, pthread_create(&th, 0, GenerateData, 0)); + ASSERT_EQ(0, pthread_create(&sigth, 0, GenerateSignals, 0)); + for (int i = 0; i < 100; ++i) { + if (!setjmp(jb)) { + char buf[8]; + ASSERT_GE(read(fds[0], buf, 8), 2); + } + } + isdone = true; + ASSERT_SYS(0, 0, close(fds[0])); + ASSERT_EQ(0, pthread_join(sigth, 0)); + ASSERT_EQ(0, pthread_join(th, 0)); + ASSERT_SYS(0, 0, close(fds[1])); + signal(SIGPIPE, sh2); + signal(SIGINT, sh1); +} + //////////////////////////////////////////////////////////////////////////////// BENCH(read, bench) { diff --git a/test/libc/runtime/getdosargv_test.c b/test/libc/runtime/getdosargv_test.c index fb98edef9..b0b5c4572 100644 --- a/test/libc/runtime/getdosargv_test.c +++ b/test/libc/runtime/getdosargv_test.c @@ -25,11 +25,6 @@ #include "libc/testlib/testlib.h" void SetUpOnce(void) { - if (!IsWindows()) { - // TODO(jart): mock out that win32 i/o call - tinyprint(2, program_invocation_name, ": skipping on non-windows\n", NULL); - exit(0); - } testlib_enable_tmp_setup_teardown(); } @@ -197,16 +192,3 @@ TEST(GetDosArgv, cmdToil) { free(argv); free(buf); } - -TEST(GetDosArgv, canonicalizesCurrentDirectoryCommandPath) { - size_t max = 4; - size_t size = ARG_MAX / 2; - char *buf = malloc(size * sizeof(char)); - char **argv = malloc(max * sizeof(char *)); - ASSERT_SYS(0, 0, touch("emacs.com", 0755)); - EXPECT_EQ(1, GetDosArgv(u"emacs.com", buf, size, argv, max)); - EXPECT_STREQ(".\\emacs.com", argv[0]); - EXPECT_EQ(NULL, argv[1]); - free(argv); - free(buf); -} diff --git a/test/libc/thread/pthread_cancel_test.c b/test/libc/thread/pthread_cancel_test.c index 11e8fa011..a3dbdaeaa 100644 --- a/test/libc/thread/pthread_cancel_test.c +++ b/test/libc/thread/pthread_cancel_test.c @@ -32,6 +32,7 @@ #include "libc/thread/thread2.h" int pfds[2]; +atomic_bool ready; pthread_cond_t cv; pthread_mutex_t mu; atomic_int gotcleanup; @@ -72,6 +73,7 @@ TEST(pthread_cancel, self_deferred_waitsForCancellationPoint) { void *Worker(void *arg) { char buf[8]; + ready = true; pthread_cleanup_push(OnCleanup, 0); read(pfds[0], buf, sizeof(buf)); pthread_cleanup_pop(0); @@ -91,11 +93,12 @@ TEST(pthread_cancel, synchronous) { ASSERT_SYS(0, 0, close(pfds[0])); } -TEST(pthread_cancel, synchronous_delayed) { +TEST(pthread_cancel, synchronous_deferred) { void *rc; pthread_t th; ASSERT_SYS(0, 0, pipe(pfds)); ASSERT_EQ(0, pthread_create(&th, 0, Worker, 0)); + while (!ready) pthread_yield(); ASSERT_SYS(0, 0, usleep(10)); EXPECT_EQ(0, pthread_cancel(th)); EXPECT_EQ(0, pthread_join(th, &rc)); diff --git a/third_party/musl/lockf.c b/third_party/musl/lockf.c index 8d729554e..6ee23fe1d 100644 --- a/third_party/musl/lockf.c +++ b/third_party/musl/lockf.c @@ -25,13 +25,13 @@ │ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. │ │ │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "third_party/musl/lockf.h" #include "libc/calls/calls.h" #include "libc/calls/struct/flock.h" #include "libc/calls/weirdtypes.h" #include "libc/errno.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/errfuns.h" -#include "third_party/musl/lockf.h" asm(".ident\t\"\\n\\n\ Musl libc (MIT License)\\n\ @@ -68,3 +68,5 @@ int lockf(int fd, int op, off_t size) } return einval(); } + +__weak_reference(lockf, lockf64); diff --git a/tool/build/runit.c b/tool/build/runit.c index e75e03823..6fcd2c3c3 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -122,6 +122,9 @@ long g_backoff; char *g_runitd; jmp_buf g_jmpbuf; uint16_t g_sshport; +int connect_latency; +int execute_latency; +int handshake_latency; char g_hostname[128]; uint16_t g_runitdport; volatile bool alarmed; @@ -174,6 +177,7 @@ void Connect(void) { LOGIFNEG1(sigaction(SIGALRM, &(struct sigaction){.sa_handler = OnAlarm}, 0)); DEBUGF("connecting to %s (%hhu.%hhu.%hhu.%hhu) to run %s", g_hostname, ip4[0], ip4[1], ip4[2], ip4[3], g_prog); + struct timespec start = timespec_real(); TryAgain: alarmed = false; LOGIFNEG1(setitimer( @@ -199,6 +203,7 @@ TryAgain: } setitimer(ITIMER_REAL, &(const struct itimerval){0}, 0); freeaddrinfo(ai); + connect_latency = timespec_tomicros(timespec_sub(timespec_real(), start)); } bool Send(int tmpfd, const void *output, size_t outputsize) { @@ -309,6 +314,7 @@ bool Recv(char *p, int n) { int ReadResponse(void) { int exitcode; + struct timespec start = timespec_real(); for (;;) { char msg[5]; if (!Recv(msg, 5)) { @@ -351,6 +357,7 @@ int ReadResponse(void) { break; } } + execute_latency = timespec_tomicros(timespec_sub(timespec_real(), start)); close(g_sock); return exitcode; } @@ -373,7 +380,9 @@ int RunOnHost(char *spec) { for (;;) { Connect(); EzFd(g_sock); + struct timespec start = timespec_real(); err = EzHandshake2(); + handshake_latency = timespec_tomicros(timespec_sub(timespec_real(), start)); if (!err) break; WARNF("handshake with %s:%d failed -0x%04x (%s)", // g_hostname, g_runitdport, err, GetTlsError(err)); @@ -381,7 +390,10 @@ int RunOnHost(char *spec) { return 1; } RelayRequest(); - return ReadResponse(); + int rc = ReadResponse(); + kprintf("%s on %-16s %'11d µs %'8ld µs %'8ld µs\n", basename(g_prog), + g_hostname, execute_latency, connect_latency, handshake_latency); + return rc; } bool IsParallelBuild(void) { diff --git a/tool/build/runitd.c b/tool/build/runitd.c index da37e5b4e..e1a677d25 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -40,6 +40,7 @@ #include "libc/mem/mem.h" #include "libc/nexgen32e/crc32.h" #include "libc/proc/posix_spawn.h" +#include "libc/runtime/internal.h" #include "libc/runtime/runtime.h" #include "libc/runtime/syslib.internal.h" #include "libc/sock/sock.h" @@ -488,7 +489,8 @@ void *ClientWorker(void *arg) { // condition can happen, where etxtbsy is raised by our execve // we're using o_cloexec so it's guaranteed to fix itself fast // thus we use an optimistic approach to avoid expensive locks - sprintf(client->tmpexepath, "o/%s.XXXXXX.com", basename(origname)); + sprintf(client->tmpexepath, "o/%s.XXXXXX.com", + basename(stripext(gc(strdup(origname))))); int exefd = openatemp(AT_FDCWD, client->tmpexepath, 4, O_CLOEXEC, 0700); if (exefd == -1) { WARNF("%s failed to open temporary file %#s due to %m", addrstr,