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:
Justine Tunney 2023-09-21 07:30:39 -07:00
parent d6c2830850
commit 0c5dd7b342
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
85 changed files with 1062 additions and 671 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -57,3 +57,5 @@ int getrlimit(int resource, struct rlimit *rlim) {
DescribeRlimit(rc, rlim), rc);
return rc;
}
__weak_reference(getrlimit, getrlimit64);

View file

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

View file

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

View file

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

View file

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

31
libc/calls/overlapped.c Normal file
View 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);
}

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -97,3 +97,5 @@ int setrlimit(int resource, const struct rlimit *rlim) {
DescribeRlimit(0, rlim), rc);
return rc;
}
__weak_reference(setrlimit, setrlimit64);

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View file

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

View file

@ -25,3 +25,4 @@ unsigned char __veof;
unsigned char __vintr;
unsigned char __vquit;
unsigned char __vtime;
unsigned char __mousebuttons;

View file

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

View file

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

View 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 */

View file

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

View file

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

View file

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

View file

@ -9,7 +9,7 @@ struct CritbitNode {
unsigned otherbits;
};
int PutEnvImpl(char *, bool);
int __putenv(char *, bool);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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] == '\\' && //

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

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

View file

@ -677,3 +677,4 @@ void seekdir(DIR *dir, long tell) {
}
__weak_reference(readdir, readdir64);
__weak_reference(readdir_r, readdir_r64);

View file

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

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,F_GETLK64,5,5,7,7,11,7,7,5

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,F_SETLK64,6,6,8,8,12,8,8,6

View file

@ -1,2 +0,0 @@
#include "libc/sysv/consts/syscon.internal.h"
.syscon compat,F_SETLKW64,7,7,9,9,13,9,9,7

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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