mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Make improvements
- Improved async signal safety of read() particularly for longjmp() - Started adding cancel cleanup handlers for locks / etc on Windows - Make /dev/tty work better particularly for uses like `foo | less` - Eagerly read console input into a linked list, so poll can signal - Fix some libc definitional bugs, which configure scripts detected
This commit is contained in:
parent
d6c2830850
commit
0c5dd7b342
85 changed files with 1062 additions and 671 deletions
52
ape/ape-m1.c
52
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_ */
|
|
@ -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);
|
||||
|
|
|
@ -57,3 +57,5 @@ int getrlimit(int resource, struct rlimit *rlim) {
|
|||
DescribeRlimit(rc, rlim), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
__weak_reference(getrlimit, getrlimit64);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
0
libc/calls/overlap.h
Executable file
0
libc/calls/overlap.h
Executable file
31
libc/calls/overlapped.c
Normal file
31
libc/calls/overlapped.c
Normal file
|
@ -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);
|
||||
}
|
25
libc/calls/overlapped.internal.h
Normal file
25
libc/calls/overlapped.internal.h
Normal file
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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__ */
|
|
@ -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);
|
||||
* }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -97,3 +97,5 @@ int setrlimit(int resource, const struct rlimit *rlim) {
|
|||
DescribeRlimit(0, rlim), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
__weak_reference(setrlimit, setrlimit64);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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 */
|
||||
|
|
61
libc/intrin/describefdset.c
Normal file
61
libc/intrin/describefdset.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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('\\');
|
||||
}
|
||||
|
|
|
@ -25,3 +25,4 @@ unsigned char __veof;
|
|||
unsigned char __vintr;
|
||||
unsigned char __vquit;
|
||||
unsigned char __vtime;
|
||||
unsigned char __mousebuttons;
|
||||
|
|
|
@ -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) */
|
||||
|
|
|
@ -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
|
||||
|
|
31
libc/intrin/strace_enter.c
Normal file
31
libc/intrin/strace_enter.c
Normal file
|
@ -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 */
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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" : "",
|
||||
|
|
|
@ -9,7 +9,7 @@ struct CritbitNode {
|
|||
unsigned otherbits;
|
||||
};
|
||||
|
||||
int PutEnvImpl(char *, bool);
|
||||
int __putenv(char *, bool);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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] == '\\' && //
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
13
libc/sock/select.internal.h
Normal file
13
libc/sock/select.internal.h
Normal file
|
@ -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_ */
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -677,3 +677,4 @@ void seekdir(DIR *dir, long tell) {
|
|||
}
|
||||
|
||||
__weak_reference(readdir, readdir64);
|
||||
__weak_reference(readdir_r, readdir_r64);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon compat,F_GETLK64,5,5,7,7,11,7,7,5
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon compat,F_SETLK64,6,6,8,8,12,8,8,6
|
|
@ -1,2 +0,0 @@
|
|||
#include "libc/sysv/consts/syscon.internal.h"
|
||||
.syscon compat,F_SETLKW64,7,7,9,9,13,9,9,7
|
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
4
third_party/musl/lockf.c
vendored
4
third_party/musl/lockf.c
vendored
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue