mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-25 03:50:29 +00:00
Make improvements
- Improved async signal safety of read() particularly for longjmp() - Started adding cancel cleanup handlers for locks / etc on Windows - Make /dev/tty work better particularly for uses like `foo | less` - Eagerly read console input into a linked list, so poll can signal - Fix some libc definitional bugs, which configure scripts detected
This commit is contained in:
parent
d6c2830850
commit
0c5dd7b342
85 changed files with 1062 additions and 671 deletions
|
@ -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_ */
|
|
@ -1,260 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2022 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/blockcancel.internal.h"
|
||||
#include "libc/calls/blocksigs.internal.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/proc/execve.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/stat.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/safemacros.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/fd.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
#include "libc/sysv/consts/mfd.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
#include "libc/sysv/consts/shm.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static bool IsAPEFd(const int fd) {
|
||||
char buf[8];
|
||||
return (sys_pread(fd, buf, 8, 0, 0) == 8) && IsApeLoadable(buf);
|
||||
}
|
||||
|
||||
static int fexecve_impl(const int fd, char *const argv[], char *const envp[]) {
|
||||
int rc;
|
||||
if (IsLinux()) {
|
||||
char path[14 + 12];
|
||||
FormatInt32(stpcpy(path, "/proc/self/fd/"), fd);
|
||||
rc = __sys_execve(path, argv, envp);
|
||||
} else if (IsFreebsd()) {
|
||||
rc = sys_fexecve(fd, argv, envp);
|
||||
} else {
|
||||
rc = enosys();
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
PTF_NUM = 1 << 0,
|
||||
PTF_NUM2 = 1 << 1,
|
||||
PTF_NUM3 = 1 << 2,
|
||||
PTF_ANY = 1 << 3
|
||||
} PTF_PARSE;
|
||||
|
||||
static bool ape_to_elf(void *ape, const size_t apesize) {
|
||||
static const char printftok[] = "printf '";
|
||||
static const size_t printftoklen = sizeof(printftok) - 1;
|
||||
const char *tok = memmem(ape, apesize, printftok, printftoklen);
|
||||
if (tok) {
|
||||
tok += printftoklen;
|
||||
uint8_t *dest = ape;
|
||||
PTF_PARSE state = PTF_ANY;
|
||||
uint8_t value = 0;
|
||||
for (; tok < (const char *)(dest + apesize); tok++) {
|
||||
if ((state & (PTF_NUM | PTF_NUM2 | PTF_NUM3)) &&
|
||||
(*tok >= '0' && *tok <= '7')) {
|
||||
value = (value << 3) | (*tok - '0');
|
||||
state <<= 1;
|
||||
if (state & PTF_ANY) {
|
||||
*dest++ = value;
|
||||
}
|
||||
} else if (state & PTF_NUM) {
|
||||
break;
|
||||
} else {
|
||||
if (state & (PTF_NUM2 | PTF_NUM3)) {
|
||||
*dest++ = value;
|
||||
}
|
||||
if (*tok == '\\') {
|
||||
state = PTF_NUM;
|
||||
value = 0;
|
||||
} else if (*tok == '\'') {
|
||||
return true;
|
||||
} else {
|
||||
*dest++ = *tok;
|
||||
state = PTF_ANY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
errno = ENOEXEC;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a memfd and copies fd to it.
|
||||
*
|
||||
* This does an inplace conversion of APE to ELF when detected!!!!
|
||||
*/
|
||||
static int fd_to_mem_fd(const int infd, char *path) {
|
||||
if ((!IsLinux() && !IsFreebsd()) || !_weaken(mmap) || !_weaken(munmap)) {
|
||||
return enosys();
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(infd, &st) == -1) {
|
||||
return -1;
|
||||
}
|
||||
int fd;
|
||||
if (IsLinux()) {
|
||||
fd = sys_memfd_create(__func__, MFD_CLOEXEC);
|
||||
} else if (IsFreebsd()) {
|
||||
fd = sys_shm_open(SHM_ANON, O_CREAT | O_RDWR, 0);
|
||||
} else {
|
||||
return enosys();
|
||||
}
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
}
|
||||
void *space;
|
||||
if ((sys_ftruncate(fd, st.st_size, st.st_size) != -1) &&
|
||||
((space = _weaken(mmap)(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
fd, 0)) != MAP_FAILED)) {
|
||||
ssize_t readRc;
|
||||
readRc = pread(infd, space, st.st_size, 0);
|
||||
bool success = readRc != -1;
|
||||
if (success && (st.st_size > 8) && IsApeLoadable(space)) {
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if ((success = (flags != -1) &&
|
||||
(fcntl(fd, F_SETFD, flags & (~FD_CLOEXEC)) != -1) &&
|
||||
ape_to_elf(space, st.st_size))) {
|
||||
const int newfd = fcntl(fd, F_DUPFD, 9001);
|
||||
if (newfd != -1) {
|
||||
close(fd);
|
||||
fd = newfd;
|
||||
}
|
||||
}
|
||||
}
|
||||
const int e = errno;
|
||||
if ((_weaken(munmap)(space, st.st_size) != -1) && success) {
|
||||
if (path) {
|
||||
FormatInt32(stpcpy(path, "COSMOPOLITAN_INIT_ZIPOS="), fd);
|
||||
}
|
||||
unassert(readRc == st.st_size);
|
||||
return fd;
|
||||
} else if (!success) {
|
||||
errno = e;
|
||||
}
|
||||
}
|
||||
const int e = errno;
|
||||
close(fd);
|
||||
errno = e;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes binary executable at file descriptor.
|
||||
*
|
||||
* This is only supported on Linux and FreeBSD. APE binaries are
|
||||
* supported. Zipos is supported. Zipos fds or FD_CLOEXEC APE fds or
|
||||
* fds that fail fexecve with ENOEXEC are copied to a new memfd (with
|
||||
* in-memory APE to ELF conversion) and fexecve is (re)attempted.
|
||||
*
|
||||
* @param fd is opened executable and current file position is ignored
|
||||
* @return doesn't return on success, otherwise -1 w/ errno
|
||||
* @raise ENOEXEC if file at `fd` isn't an assimilated ELF executable
|
||||
* @raise ENOSYS on Windows, XNU, OpenBSD, NetBSD, and Metal
|
||||
*/
|
||||
int fexecve(int fd, char *const argv[], char *const envp[]) {
|
||||
int rc = 0;
|
||||
if (!argv || !envp ||
|
||||
(IsAsan() &&
|
||||
(!__asan_is_valid_strlist(argv) || !__asan_is_valid_strlist(envp)))) {
|
||||
rc = efault();
|
||||
} else {
|
||||
STRACE("fexecve(%d, %s, %s) → ...", fd, DescribeStringList(argv),
|
||||
DescribeStringList(envp));
|
||||
int savedErr = 0;
|
||||
do {
|
||||
if (!IsLinux() && !IsFreebsd()) {
|
||||
rc = enosys();
|
||||
break;
|
||||
}
|
||||
if (!__isfdkind(fd, kFdZip)) {
|
||||
bool memfdReq;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
strace_enabled(-1);
|
||||
memfdReq = ((rc = fcntl(fd, F_GETFD)) != -1) && (rc & FD_CLOEXEC) &&
|
||||
IsAPEFd(fd);
|
||||
strace_enabled(+1);
|
||||
ALLOW_SIGNALS;
|
||||
END_CANCELLATION_POINT;
|
||||
if (rc == -1) {
|
||||
break;
|
||||
} else if (!memfdReq) {
|
||||
fexecve_impl(fd, argv, envp);
|
||||
if (errno != ENOEXEC) {
|
||||
break;
|
||||
}
|
||||
savedErr = ENOEXEC;
|
||||
}
|
||||
}
|
||||
int newfd;
|
||||
char *path = alloca(PATH_MAX);
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
strace_enabled(-1);
|
||||
newfd = fd_to_mem_fd(fd, path);
|
||||
strace_enabled(+1);
|
||||
ALLOW_SIGNALS;
|
||||
END_CANCELLATION_POINT;
|
||||
if (newfd == -1) {
|
||||
break;
|
||||
}
|
||||
size_t numenvs;
|
||||
for (numenvs = 0; envp[numenvs];) ++numenvs;
|
||||
// const size_t desenvs = min(500, max(numenvs + 1, 2));
|
||||
static _Thread_local char *envs[500];
|
||||
memcpy(envs, envp, numenvs * sizeof(char *));
|
||||
envs[numenvs] = path;
|
||||
envs[numenvs + 1] = NULL;
|
||||
fexecve_impl(newfd, argv, envs);
|
||||
if (!savedErr) {
|
||||
savedErr = errno;
|
||||
}
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
BLOCK_SIGNALS;
|
||||
strace_enabled(-1);
|
||||
close(newfd);
|
||||
strace_enabled(+1);
|
||||
ALLOW_SIGNALS;
|
||||
END_CANCELLATION_POINT;
|
||||
} while (0);
|
||||
if (savedErr) {
|
||||
errno = savedErr;
|
||||
}
|
||||
rc = -1;
|
||||
}
|
||||
STRACE("fexecve(%d) failed %d% m", fd, rc);
|
||||
return rc;
|
||||
}
|
|
@ -59,3 +59,5 @@ int fstatfs(int fd, struct statfs *sf) {
|
|||
STRACE("fstatfs(%d, [%s]) → %d% m", fd, DescribeStatfs(rc, sf));
|
||||
return rc;
|
||||
}
|
||||
|
||||
__weak_reference(fstatfs, fstatfs64);
|
||||
|
|
|
@ -57,3 +57,5 @@ int getrlimit(int resource, struct rlimit *rlim) {
|
|||
DescribeRlimit(rc, rlim), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
__weak_reference(getrlimit, getrlimit64);
|
||||
|
|
|
@ -25,6 +25,9 @@ int __ensurefds(int);
|
|||
int __ensurefds_unlocked(int);
|
||||
void __printfds(void);
|
||||
uint32_t sys_getuid_nt(void);
|
||||
int __pause_thread(uint32_t);
|
||||
int CountConsoleInputBytes(int64_t);
|
||||
int FlushConsoleInputBytes(int64_t);
|
||||
|
||||
forceinline int64_t __getfdhandleactual(int fd) {
|
||||
return g_fds.p[fd].handle;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
@ -33,7 +34,9 @@ textwindows int _check_interrupts(int sigops) {
|
|||
goto Interrupted;
|
||||
}
|
||||
if (_weaken(__sig_check) && (status = _weaken(__sig_check)())) {
|
||||
STRACE("syscall interrupted (status=%d, sigops=%d)", status, sigops);
|
||||
if (status == 2 && (sigops & kSigOpRestartable)) {
|
||||
STRACE("restarting system call");
|
||||
return 0;
|
||||
}
|
||||
err = EINTR;
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/console.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
|
@ -91,10 +90,9 @@ static int ioctl_default(int fd, unsigned long request, void *arg) {
|
|||
}
|
||||
|
||||
static int ioctl_fionread(int fd, uint32_t *arg) {
|
||||
int rc;
|
||||
uint32_t cm;
|
||||
int64_t handle;
|
||||
uint32_t avail;
|
||||
int rc, e = errno;
|
||||
if (!IsWindows()) {
|
||||
return sys_ioctl(fd, FIONREAD, arg);
|
||||
} else if (__isfdopen(fd)) {
|
||||
|
@ -106,19 +104,18 @@ static int ioctl_fionread(int fd, uint32_t *arg) {
|
|||
return _weaken(__winsockerr)();
|
||||
}
|
||||
} else if (GetFileType(handle) == kNtFileTypePipe) {
|
||||
uint32_t avail;
|
||||
if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) {
|
||||
*arg = avail;
|
||||
return 0;
|
||||
} else if (GetLastError() == kNtErrorBrokenPipe) {
|
||||
return 0;
|
||||
} else {
|
||||
return __winerr();
|
||||
}
|
||||
} else if (GetConsoleMode(handle, &cm)) {
|
||||
avail = CountConsoleInputBytes(handle);
|
||||
if (avail == -1u && errno == ENODATA) {
|
||||
errno = e;
|
||||
avail = 0;
|
||||
}
|
||||
return avail;
|
||||
int bytes = CountConsoleInputBytes(handle);
|
||||
return MAX(0, bytes);
|
||||
} else {
|
||||
return eopnotsupp();
|
||||
}
|
||||
|
|
|
@ -1,146 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/thompike.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define APPEND(c) \
|
||||
do { \
|
||||
cmdline[k++] = c; \
|
||||
if (k == ARG_MAX / 2) { \
|
||||
return e2big(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static bool NeedsQuotes(const char *s) {
|
||||
if (!*s) return true;
|
||||
do {
|
||||
switch (*s) {
|
||||
case '"':
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\n':
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (*s++);
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
// Converts System V argv to Windows-style command line.
|
||||
//
|
||||
// Escaping is performed and it's designed to round-trip with
|
||||
// GetDosArgv() or GetDosArgv(). This function does NOT escape
|
||||
// command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd).
|
||||
//
|
||||
// TODO(jart): this needs fuzzing and security review
|
||||
//
|
||||
// @param cmdline is output buffer
|
||||
// @param argv is an a NULL-terminated array of UTF-8 strings
|
||||
// @return 0 on success, or -1 w/ errno
|
||||
// @raise E2BIG if everything is too huge
|
||||
// @see "Everyone quotes command line arguments the wrong way" MSDN
|
||||
// @see libc/runtime/getdosargv.c
|
||||
textwindows int mkntcmdline(char16_t cmdline[ARG_MAX / 2], char *const argv[]) {
|
||||
uint64_t w;
|
||||
wint_t x, y;
|
||||
int slashes, n;
|
||||
bool needsquote;
|
||||
char *ansiargv[2];
|
||||
size_t i, j, k, s;
|
||||
if (!argv[0]) {
|
||||
bzero(ansiargv, sizeof(ansiargv));
|
||||
argv = ansiargv;
|
||||
}
|
||||
for (k = i = 0; argv[i]; ++i) {
|
||||
if (i) APPEND(u' ');
|
||||
if ((needsquote = NeedsQuotes(argv[i]))) APPEND(u'"');
|
||||
for (slashes = j = 0;;) {
|
||||
x = argv[i][j++] & 255;
|
||||
if (x >= 0300) {
|
||||
n = ThomPikeLen(x);
|
||||
x = ThomPikeByte(x);
|
||||
while (--n) {
|
||||
if ((y = argv[i][j++] & 255)) {
|
||||
x = ThomPikeMerge(x, y);
|
||||
} else {
|
||||
x = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!x) break;
|
||||
if (x == '/' || x == '\\') {
|
||||
if (!i) {
|
||||
// turn / into \ for first argv[i]
|
||||
x = '\\';
|
||||
// turn \c\... into c:\ for first argv[i]
|
||||
if (k == 2 && IsAlpha(cmdline[1]) && cmdline[0] == '\\') {
|
||||
cmdline[0] = cmdline[1];
|
||||
cmdline[1] = ':';
|
||||
}
|
||||
} else {
|
||||
// turn stuff like `less /c/...`
|
||||
// into `less c:/...`
|
||||
// turn stuff like `more <"/c/..."`
|
||||
// into `more <"c:/..."`
|
||||
if (k > 3 && IsAlpha(cmdline[k - 1]) &&
|
||||
(cmdline[k - 2] == '/' || cmdline[k - 2] == '\\') &&
|
||||
(cmdline[k - 3] == '"' || cmdline[k - 3] == ' ')) {
|
||||
cmdline[k - 2] = cmdline[k - 1];
|
||||
cmdline[k - 1] = ':';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (x == '\\') {
|
||||
++slashes;
|
||||
} else if (x == '"') {
|
||||
APPEND(u'"');
|
||||
APPEND(u'"');
|
||||
APPEND(u'"');
|
||||
} else {
|
||||
for (s = 0; s < slashes; ++s) {
|
||||
APPEND(u'\\');
|
||||
}
|
||||
slashes = 0;
|
||||
w = EncodeUtf16(x);
|
||||
do {
|
||||
APPEND(w);
|
||||
} while ((w >>= 16));
|
||||
}
|
||||
}
|
||||
for (s = 0; s < (slashes << needsquote); ++s) {
|
||||
APPEND(u'\\');
|
||||
}
|
||||
if (needsquote) {
|
||||
APPEND(u'"');
|
||||
}
|
||||
}
|
||||
cmdline[k] = u'\0';
|
||||
return 0;
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/proc/ntspawn.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/getenv.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/alloca.h"
|
||||
#include "libc/mem/arraylist2.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/thompike.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
#define ToUpper(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c))
|
||||
|
||||
static inline int IsAlpha(int c) {
|
||||
return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z');
|
||||
}
|
||||
|
||||
static inline char *StrChr(const char *s, int c) {
|
||||
for (;; ++s) {
|
||||
if ((*s & 255) == (c & 255)) return (char *)s;
|
||||
if (!*s) return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows inline int CompareStrings(const char *l, const char *r) {
|
||||
int a, b;
|
||||
size_t i = 0;
|
||||
while ((a = ToUpper(l[i] & 255)) == (b = ToUpper(r[i] & 255)) && r[i]) ++i;
|
||||
return a - b;
|
||||
}
|
||||
|
||||
static textwindows void FixPath(char *path) {
|
||||
char *p;
|
||||
|
||||
// skip over variable name
|
||||
while (*path++) {
|
||||
if (path[-1] == '=') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// turn colon into semicolon
|
||||
// unless it already looks like a dos path
|
||||
for (p = path; *p; ++p) {
|
||||
if (p[0] == ':' && p[1] != '\\') {
|
||||
p[0] = ';';
|
||||
}
|
||||
}
|
||||
|
||||
// turn \c\... into c:\...
|
||||
p = path;
|
||||
if ((p[0] == '/' || p[0] == '\\') && IsAlpha(p[1]) &&
|
||||
(p[2] == '/' || p[2] == '\\')) {
|
||||
p[0] = p[1];
|
||||
p[1] = ':';
|
||||
}
|
||||
for (; *p; ++p) {
|
||||
if (p[0] == ';' && (p[1] == '/' || p[1] == '\\') && IsAlpha(p[2]) &&
|
||||
(p[3] == '/' || p[3] == '\\')) {
|
||||
p[1] = p[2];
|
||||
p[2] = ':';
|
||||
}
|
||||
}
|
||||
|
||||
// turn slash into backslash
|
||||
for (p = path; *p; ++p) {
|
||||
if (*p == '/') {
|
||||
*p = '\\';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows void InsertString(char **a, size_t i, const char *s,
|
||||
char buf[ARG_MAX], size_t *bufi,
|
||||
bool *have_systemroot) {
|
||||
char *v;
|
||||
size_t j, k;
|
||||
|
||||
v = StrChr(s, '=');
|
||||
|
||||
// apply fixups to var=/c/...
|
||||
if (v && v[1] == '/' && IsAlpha(v[2]) && v[3] == '/') {
|
||||
v = buf + *bufi;
|
||||
for (k = 0; s[k]; ++k) {
|
||||
if (*bufi + 1 < ARG_MAX) {
|
||||
buf[(*bufi)++] = s[k];
|
||||
}
|
||||
}
|
||||
if (*bufi < ARG_MAX) {
|
||||
buf[(*bufi)++] = 0;
|
||||
FixPath(v);
|
||||
s = v;
|
||||
}
|
||||
}
|
||||
|
||||
// append to sorted list
|
||||
for (j = i; j > 0 && CompareStrings(s, a[j - 1]) < 0; --j) {
|
||||
a[j] = a[j - 1];
|
||||
}
|
||||
a[j] = (char *)s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies sorted environment variable block for Windows.
|
||||
*
|
||||
* This is designed to meet the requirements of CreateProcess().
|
||||
*
|
||||
* @param envvars receives sorted double-NUL terminated string list
|
||||
* @param envp is an a NULL-terminated array of UTF-8 strings
|
||||
* @param extravar is a VAR=val string we consider part of envp or NULL
|
||||
* @return 0 on success, or -1 w/ errno
|
||||
* @error E2BIG if total number of shorts exceeded ARG_MAX/2 (32767)
|
||||
*/
|
||||
textwindows int mkntenvblock(char16_t envvars[ARG_MAX / 2], char *const envp[],
|
||||
const char *extravar, char buf[ARG_MAX]) {
|
||||
bool v;
|
||||
uint64_t w;
|
||||
char **vars;
|
||||
wint_t x, y;
|
||||
bool have_systemroot = false;
|
||||
size_t i, j, k, n, m, bufi = 0;
|
||||
for (n = 0; envp[n];) n++;
|
||||
#pragma GCC push_options
|
||||
#pragma GCC diagnostic ignored "-Walloca-larger-than="
|
||||
int nbytes = (n + 1) * sizeof(char *);
|
||||
vars = alloca(nbytes);
|
||||
CheckLargeStackAllocation(vars, nbytes);
|
||||
#pragma GCC pop_options
|
||||
for (i = 0; i < n; ++i) {
|
||||
InsertString(vars, i, envp[i], buf, &bufi, &have_systemroot);
|
||||
}
|
||||
if (extravar) {
|
||||
InsertString(vars, n++, extravar, buf, &bufi, &have_systemroot);
|
||||
}
|
||||
if (!have_systemroot && environ) {
|
||||
// https://jpassing.com/2009/12/28/the-hidden-danger-of-forgetting-to-specify-systemroot-in-a-custom-environment-block/
|
||||
struct Env systemroot;
|
||||
systemroot = __getenv(environ, "SYSTEMROOT");
|
||||
if (systemroot.s) {
|
||||
InsertString(vars, n++, environ[systemroot.i], buf, &bufi,
|
||||
&have_systemroot);
|
||||
}
|
||||
}
|
||||
for (k = i = 0; i < n; ++i) {
|
||||
j = 0;
|
||||
v = false;
|
||||
do {
|
||||
x = vars[i][j++] & 0xff;
|
||||
if (x >= 0200) {
|
||||
if (x < 0300) continue;
|
||||
m = ThomPikeLen(x);
|
||||
x = ThomPikeByte(x);
|
||||
while (--m) {
|
||||
if ((y = vars[i][j++] & 0xff)) {
|
||||
x = ThomPikeMerge(x, y);
|
||||
} else {
|
||||
x = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!v) {
|
||||
if (x != '=') {
|
||||
x = ToUpper(x);
|
||||
} else {
|
||||
v = true;
|
||||
}
|
||||
}
|
||||
w = EncodeUtf16(x);
|
||||
do {
|
||||
envvars[k++] = w & 0xffff;
|
||||
if (k == ARG_MAX / 2) {
|
||||
return e2big();
|
||||
}
|
||||
} while ((w >>= 16));
|
||||
} while (x);
|
||||
}
|
||||
envvars[k] = u'\0';
|
||||
return 0;
|
||||
}
|
|
@ -144,33 +144,13 @@ static textwindows int sys_open_nt_console(int dirfd,
|
|||
const struct NtMagicPaths *mp,
|
||||
uint32_t flags, int32_t mode,
|
||||
size_t fd) {
|
||||
uint32_t cm;
|
||||
int input, output;
|
||||
if ((__isfdopen((input = STDIN_FILENO)) &&
|
||||
GetConsoleMode(g_fds.p[input].handle, &cm)) &&
|
||||
((__isfdopen((output = STDOUT_FILENO)) &&
|
||||
GetConsoleMode(g_fds.p[output].handle, &cm)) ||
|
||||
(__isfdopen((output = STDERR_FILENO)) &&
|
||||
GetConsoleMode(g_fds.p[output].handle, &cm)))) {
|
||||
// this is an ugly hack that works for observed usage patterns
|
||||
g_fds.p[fd].handle = g_fds.p[input].handle;
|
||||
g_fds.p[fd].extra = g_fds.p[output].handle;
|
||||
g_fds.p[fd].dontclose = true;
|
||||
g_fds.p[input].dontclose = true;
|
||||
g_fds.p[output].dontclose = true;
|
||||
} else if ((g_fds.p[fd].handle = sys_open_nt_impl(
|
||||
dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode,
|
||||
kNtFileFlagOverlapped)) != -1) {
|
||||
g_fds.p[fd].extra =
|
||||
sys_open_nt_impl(dirfd, mp->conout, (flags & ~O_ACCMODE) | O_WRONLY,
|
||||
mode, kNtFileFlagOverlapped);
|
||||
npassert(g_fds.p[fd].extra != -1);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
g_fds.p[fd].kind = kFdConsole;
|
||||
g_fds.p[fd].flags = flags;
|
||||
g_fds.p[fd].mode = mode;
|
||||
g_fds.p[fd].handle = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite,
|
||||
kNtFileShareRead, 0, kNtOpenExisting, 0, 0);
|
||||
g_fds.p[fd].extra = CreateFile(u"CONOUT$", kNtGenericRead | kNtGenericWrite,
|
||||
kNtFileShareWrite, 0, kNtOpenExisting, 0, 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
|
0
libc/calls/overlap.h
Executable file
0
libc/calls/overlap.h
Executable file
31
libc/calls/overlapped.c
Normal file
31
libc/calls/overlapped.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2023 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/overlap.h"
|
||||
#include "libc/calls/overlapped.internal.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/thread.h"
|
||||
|
||||
void overlapped_cleanup_callback(void *arg) {
|
||||
uint32_t got;
|
||||
struct OverlappedCleanup *cleanup = arg;
|
||||
CancelIoEx(cleanup->handle, cleanup->overlap);
|
||||
GetOverlappedResult(cleanup->handle, cleanup->overlap, &got, true);
|
||||
CloseHandle(cleanup->overlap->hEvent);
|
||||
}
|
25
libc/calls/overlapped.internal.h
Normal file
25
libc/calls/overlapped.internal.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_CALLS_OVERLAPPED_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_CALLS_OVERLAPPED_INTERNAL_H_
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
#define overlapped_cleanup_push(handle, overlap) \
|
||||
{ \
|
||||
struct OverlappedCleanup overlapped_cleanup = {handle, overlap}; \
|
||||
pthread_cleanup_push(overlapped_cleanup_callback, &overlapped_cleanup);
|
||||
|
||||
#define overlapped_cleanup_pop() \
|
||||
pthread_cleanup_pop(false); \
|
||||
}
|
||||
|
||||
struct OverlappedCleanup {
|
||||
int64_t handle;
|
||||
struct NtOverlapped *overlap;
|
||||
};
|
||||
|
||||
void overlapped_cleanup_callback(void *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_CALLS_OVERLAPPED_INTERNAL_H_ */
|
|
@ -19,22 +19,11 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
textwindows int sys_pause_nt(void) {
|
||||
int rc;
|
||||
while (!(rc = _check_interrupts(0))) {
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->abort_errno = 0;
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS);
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno) {
|
||||
errno = pt->abort_errno;
|
||||
rc = -1;
|
||||
if ((rc = __pause_thread(__SIG_SIG_INTERVAL_MS))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,9 @@
|
|||
*
|
||||
* However this has a tinier footprint and better logging.
|
||||
*
|
||||
* @return -1 w/ errno set to EINTR
|
||||
* @return -1 w/ errno
|
||||
* @raise ECANCELED if this thread was canceled in masked mode
|
||||
* @raise EINTR if interrupted by a signal
|
||||
* @cancellationpoint
|
||||
* @see sigsuspend()
|
||||
* @norestart
|
||||
|
|
|
@ -16,41 +16,21 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/console.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/struct/inputrecord.h"
|
||||
#ifdef __x86_64__
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
|
||||
int CountConsoleInputBytes(int64_t handle) {
|
||||
char buf[32];
|
||||
int rc, e = errno;
|
||||
uint16_t utf16hs = 0;
|
||||
uint32_t i, n, count;
|
||||
struct NtInputRecord records[64];
|
||||
if (PeekConsoleInput(handle, records, ARRAYLEN(records), &n)) {
|
||||
for (rc = i = 0; i < n; ++i) {
|
||||
count = ConvertConsoleInputToAnsi(records + i, buf, &utf16hs, 0);
|
||||
if (count == -1) {
|
||||
unassert(errno == ENODATA);
|
||||
if (!rc) {
|
||||
rc = -1;
|
||||
} else {
|
||||
errno = e;
|
||||
}
|
||||
break;
|
||||
}
|
||||
rc += count;
|
||||
}
|
||||
} else {
|
||||
rc = __winerr();
|
||||
textwindows int __pause_thread(uint32_t ms) {
|
||||
uint32_t status;
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
status = WaitForSingleObject(pt->semaphore, ms);
|
||||
if (status == -1u) notpossible;
|
||||
if (!(pt->pt_flags & PT_INSEMAPHORE)) {
|
||||
errno = pt->abort_errno;
|
||||
return -1;
|
||||
}
|
||||
return rc;
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* __x86_64__ */
|
|
@ -35,7 +35,7 @@
|
|||
* Permits system operations, e.g.
|
||||
*
|
||||
* __pledge_mode = PLEDGE_PENALTY_KILL_PROCESS | PLEDGE_STDERR_LOGGING;
|
||||
* if (pledge("stdio rfile tty", 0)) {
|
||||
* if (pledge("stdio rpath tty", 0)) {
|
||||
* perror("pledge");
|
||||
* exit(1);
|
||||
* }
|
||||
|
|
|
@ -16,13 +16,16 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/console.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/bits.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -46,38 +49,37 @@
|
|||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
/*
|
||||
* Polls on the New Technology.
|
||||
*
|
||||
* This function is used to implement poll() and select(). You may poll
|
||||
* on both sockets and files at the same time. We also poll for signals
|
||||
* while poll is polling.
|
||||
*/
|
||||
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
||||
// Polls on the New Technology.
|
||||
//
|
||||
// This function is used to implement poll() and select(). You may poll
|
||||
// on both sockets and files at the same time. We also poll for signals
|
||||
// while poll is polling.
|
||||
textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint32_t *ms,
|
||||
const sigset_t *sigmask) {
|
||||
bool ok;
|
||||
uint64_t m;
|
||||
bool interrupted;
|
||||
sigset_t oldmask;
|
||||
uint32_t avail, cm;
|
||||
uint32_t cm, avail, waitfor;
|
||||
struct sys_pollfd_nt pipefds[8];
|
||||
struct sys_pollfd_nt sockfds[64];
|
||||
int pipeindices[ARRAYLEN(pipefds)];
|
||||
int sockindices[ARRAYLEN(sockfds)];
|
||||
int i, rc, sn, pn, gotinvals, gotpipes, gotsocks, waitfor;
|
||||
int i, rc, sn, pn, gotinvals, gotpipes, gotsocks;
|
||||
|
||||
// check for interrupts early before doing work
|
||||
if (sigmask) {
|
||||
__sig_mask(SIG_SETMASK, sigmask, &oldmask);
|
||||
}
|
||||
if ((rc = _check_interrupts(0))) {
|
||||
goto ReturnPath;
|
||||
}
|
||||
#if IsModeDbg()
|
||||
struct timespec noearlier =
|
||||
timespec_add(timespec_real(), timespec_frommillis(ms ? *ms : -1u));
|
||||
#endif
|
||||
|
||||
// do the planning
|
||||
// we need to read static variables
|
||||
// we might need to spawn threads and open pipes
|
||||
m = atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
__fds_lock();
|
||||
for (gotinvals = rc = sn = pn = i = 0; i < nfds; ++i) {
|
||||
if (fds[i].fd < 0) continue;
|
||||
|
@ -125,9 +127,10 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
|||
}
|
||||
}
|
||||
__fds_unlock();
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, m, memory_order_release);
|
||||
if (rc) {
|
||||
// failed to create a polling solution
|
||||
goto ReturnPath;
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
// perform the i/o and sleeping and looping
|
||||
|
@ -152,17 +155,8 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
|||
pipefds[i].revents |= POLLERR;
|
||||
}
|
||||
} else if (GetConsoleMode(pipefds[i].handle, &cm)) {
|
||||
int e = errno;
|
||||
avail = CountConsoleInputBytes(pipefds[i].handle);
|
||||
if (avail > 0) {
|
||||
if (CountConsoleInputBytes(pipefds[i].handle)) {
|
||||
pipefds[i].revents |= POLLIN;
|
||||
} else if (avail == -1u) {
|
||||
if (errno == ENODATA) {
|
||||
pipefds[i].revents |= POLLIN;
|
||||
} else {
|
||||
pipefds[i].revents |= POLLERR;
|
||||
}
|
||||
errno = e;
|
||||
}
|
||||
} else {
|
||||
// we have no way of polling if a non-socket is readable yet
|
||||
|
@ -176,42 +170,35 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
|||
}
|
||||
// if we haven't found any good results yet then here we
|
||||
// compute a small time slice we don't mind sleeping for
|
||||
waitfor = gotinvals || gotpipes ? 0 : MIN(__SIG_POLL_INTERVAL_MS, *ms);
|
||||
if (sn) {
|
||||
#if _NTTRACE
|
||||
POLLTRACE("WSAPoll(%p, %u, %'d) out of %'lu", sockfds, sn, waitfor, *ms);
|
||||
#endif
|
||||
if ((gotsocks = WSAPoll(sockfds, sn, 0)) == -1) {
|
||||
rc = __winsockerr();
|
||||
goto ReturnPath;
|
||||
return __winsockerr();
|
||||
}
|
||||
} else {
|
||||
gotsocks = 0;
|
||||
}
|
||||
waitfor = MIN(__SIG_POLL_INTERVAL_MS, *ms);
|
||||
if (!gotinvals && !gotsocks && !gotpipes && waitfor) {
|
||||
POLLTRACE("sleeping for %'d out of %'lu ms", waitfor, *ms);
|
||||
POLLTRACE("poll() sleeping for %'d out of %'u ms", waitfor, *ms);
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->abort_errno = 0;
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore, waitfor);
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno) {
|
||||
errno = pt->abort_errno;
|
||||
rc = -1;
|
||||
goto ReturnPath;
|
||||
if (sigmask) __sig_mask(SIG_SETMASK, sigmask, &oldmask);
|
||||
interrupted = _check_interrupts(0) || __pause_thread(waitfor);
|
||||
if (sigmask) __sig_mask(SIG_SETMASK, &oldmask, 0);
|
||||
if (interrupted) return -1;
|
||||
if (*ms != -1u) {
|
||||
if (waitfor < *ms) {
|
||||
*ms -= waitfor;
|
||||
} else {
|
||||
*ms = 0;
|
||||
}
|
||||
}
|
||||
*ms -= waitfor; // todo: make more resilient
|
||||
}
|
||||
// we gave all the sockets and all the named pipes a shot
|
||||
// if we found anything at all then it's time to end work
|
||||
if (gotinvals || gotpipes || gotsocks || *ms <= 0) {
|
||||
if (gotinvals || gotpipes || gotsocks || !*ms) {
|
||||
break;
|
||||
}
|
||||
// otherwise loop limitlessly for timeout to elapse while
|
||||
// checking for signal delivery interrupts, along the way
|
||||
if ((rc = _check_interrupts(0))) {
|
||||
goto ReturnPath;
|
||||
}
|
||||
}
|
||||
|
||||
// the system call is going to succeed
|
||||
|
@ -233,10 +220,16 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms,
|
|||
// and finally return
|
||||
rc = gotinvals + gotpipes + gotsocks;
|
||||
|
||||
ReturnPath:
|
||||
if (sigmask) {
|
||||
__sig_mask(SIG_SETMASK, &oldmask, 0);
|
||||
Finished:
|
||||
|
||||
#if IsModeDbg()
|
||||
struct timespec ended = timespec_real();
|
||||
if (!rc && timespec_cmp(ended, noearlier) < 0) {
|
||||
STRACE("poll() ended %'ld ns too soon!",
|
||||
timespec_tonanos(timespec_sub(noearlier, ended)));
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,6 @@
|
|||
int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
|
||||
int rc;
|
||||
size_t n;
|
||||
uint64_t millis;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
|
||||
if (IsAsan() &&
|
||||
|
@ -81,9 +80,9 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
|
|||
rc = sys_poll_metal(fds, nfds, timeout_ms);
|
||||
}
|
||||
} else {
|
||||
millis = timeout_ms;
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
rc = sys_poll_nt(fds, nfds, &millis, 0);
|
||||
uint32_t ms = timeout_ms >= 0 ? timeout_ms : -1u;
|
||||
rc = sys_poll_nt(fds, nfds, &ms, 0);
|
||||
END_BLOCKING_OPERATION;
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,6 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
|||
const sigset_t *sigmask) {
|
||||
size_t n;
|
||||
int e, rc;
|
||||
uint64_t millis;
|
||||
sigset_t oldmask;
|
||||
struct timespec ts, *tsp;
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
|
@ -84,22 +83,24 @@ int ppoll(struct pollfd *fds, size_t nfds, const struct timespec *timeout,
|
|||
}
|
||||
rc = sys_ppoll(fds, nfds, tsp, sigmask, 8);
|
||||
if (rc == -1 && errno == ENOSYS) {
|
||||
int ms;
|
||||
errno = e;
|
||||
if (!timeout ||
|
||||
ckd_add(&millis, timeout->tv_sec, timeout->tv_nsec / 1000000)) {
|
||||
millis = -1;
|
||||
if (!timeout || ckd_add(&ms, timeout->tv_sec,
|
||||
(timeout->tv_nsec + 999999) / 1000000)) {
|
||||
ms = -1;
|
||||
}
|
||||
if (sigmask) sys_sigprocmask(SIG_SETMASK, sigmask, &oldmask);
|
||||
rc = poll(fds, nfds, millis);
|
||||
rc = poll(fds, nfds, ms);
|
||||
if (sigmask) sys_sigprocmask(SIG_SETMASK, &oldmask, 0);
|
||||
}
|
||||
} else {
|
||||
uint32_t ms;
|
||||
if (!timeout ||
|
||||
ckd_add(&millis, timeout->tv_sec, timeout->tv_nsec / 1000000)) {
|
||||
millis = -1;
|
||||
ckd_add(&ms, timeout->tv_sec, (timeout->tv_nsec + 999999) / 1000000)) {
|
||||
ms = -1u;
|
||||
}
|
||||
BEGIN_BLOCKING_OPERATION;
|
||||
rc = sys_poll_nt(fds, nfds, &millis, sigmask);
|
||||
rc = sys_poll_nt(fds, nfds, &ms, sigmask);
|
||||
END_BLOCKING_OPERATION;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,20 +16,18 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/console.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/wincrash.internal.h"
|
||||
#include "libc/calls/ttydefaults.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/atomic.h"
|
||||
#include "libc/intrin/dll.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/intrin/weaken.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
|
@ -39,95 +37,95 @@
|
|||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/events.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/ipc.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/inputrecord.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/str/utf16.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/thread/posixthread.internal.h"
|
||||
#include "libc/thread/thread.h"
|
||||
#include "libc/thread/tls.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
|
||||
static const struct {
|
||||
uint16_t vk;
|
||||
uint32_t normal_str;
|
||||
uint32_t shift_str;
|
||||
uint32_t ctrl_str;
|
||||
uint32_t shift_ctrl_str;
|
||||
int vk;
|
||||
int normal_str;
|
||||
int shift_str;
|
||||
int ctrl_str;
|
||||
int shift_ctrl_str;
|
||||
} kVirtualKey[] = {
|
||||
#define SW(s) W4(s "\0\0")
|
||||
#define W4(s) (s[3] + 0u) << 24 | s[2] << 16 | s[1] << 8 | s[0]
|
||||
#define VK(vk, normal_str, shift_str, ctrl_str, shift_ctrl_str) \
|
||||
{ vk, SW(normal_str), SW(shift_str), SW(ctrl_str), SW(shift_ctrl_str) }
|
||||
VK(kNtVkInsert, "2~", "2;2~", "2;5~", "2;6~"),
|
||||
VK(kNtVkEnd, "4~", "4;2~", "4;5~", "4;6~"),
|
||||
VK(kNtVkDown, "B", "1;2B", "1;5B", "1;6B"),
|
||||
VK(kNtVkNext, "6~", "6;2~", "6;5~", "6;6~"),
|
||||
VK(kNtVkLeft, "D", "1;2D", "1;5D", "1;6D"),
|
||||
VK(kNtVkClear, "G", "1;2G", "1;5G", "1;6G"),
|
||||
VK(kNtVkRight, "C", "1;2C", "1;5C", "1;6C"),
|
||||
VK(kNtVkUp, "A", "1;2A", "1;5A", "1;6A"),
|
||||
VK(kNtVkHome, "1~", "1;2~", "1;5~", "1;6~"),
|
||||
VK(kNtVkPrior, "5~", "5;2~", "5;5~", "5;6~"),
|
||||
VK(kNtVkDelete, "3~", "3;2~", "3;5~", "3;6~"),
|
||||
VK(kNtVkNumpad0, "2~", "2;2~", "2;5~", "2;6~"),
|
||||
VK(kNtVkNumpad1, "4~", "4;2~", "4;5~", "4;6~"),
|
||||
VK(kNtVkNumpad2, "B", "1;2B", "1;5B", "1;6B"),
|
||||
VK(kNtVkNumpad3, "6~", "6;2~", "6;5~", "6;6~"),
|
||||
VK(kNtVkNumpad4, "D", "1;2D", "1;5D", "1;6D"),
|
||||
VK(kNtVkNumpad5, "G", "1;2G", "1;5G", "1;6G"),
|
||||
VK(kNtVkNumpad6, "C", "1;2C", "1;5C", "1;6C"),
|
||||
VK(kNtVkNumpad7, "A", "1;2A", "1;5A", "1;6A"),
|
||||
VK(kNtVkNumpad8, "1~", "1;2~", "1;5~", "1;6~"),
|
||||
VK(kNtVkNumpad9, "5~", "5;2~", "5;5~", "5;6~"),
|
||||
VK(kNtVkDecimal, "3~", "3;2~", "3;5~", "3;6~"),
|
||||
VK(kNtVkF1, "[A", "23~", "11^", "23^"),
|
||||
VK(kNtVkF2, "[B", "24~", "12^", "24^"),
|
||||
VK(kNtVkF3, "[C", "25~", "13^", "25^"),
|
||||
VK(kNtVkF4, "[D", "26~", "14^", "26^"),
|
||||
VK(kNtVkF5, "[E", "28~", "15^", "28^"),
|
||||
VK(kNtVkF6, "17~", "29~", "17^", "29^"),
|
||||
VK(kNtVkF7, "18~", "31~", "18^", "31^"),
|
||||
VK(kNtVkF8, "19~", "32~", "19^", "32^"),
|
||||
VK(kNtVkF9, "20~", "33~", "20^", "33^"),
|
||||
VK(kNtVkF10, "21~", "34~", "21^", "34^"),
|
||||
VK(kNtVkF11, "23~", "23$", "23^", "23@"),
|
||||
VK(kNtVkF12, "24~", "24$", "24^", "24@"),
|
||||
#undef VK
|
||||
#undef W4
|
||||
#undef SW
|
||||
#define S(s) W(s "\0\0")
|
||||
#define W(s) (s[3] << 24 | s[2] << 16 | s[1] << 8 | s[0])
|
||||
{kNtVkUp, S("A"), S("1;2A"), S("1;5A"), S("1;6A")},
|
||||
{kNtVkDown, S("B"), S("1;2B"), S("1;5B"), S("1;6B")},
|
||||
{kNtVkLeft, S("D"), S("1;2D"), S("1;5D"), S("1;6D")},
|
||||
{kNtVkRight, S("C"), S("1;2C"), S("1;5C"), S("1;6C")},
|
||||
{kNtVkInsert, S("2~"), S("2;2~"), S("2;5~"), S("2;6~")},
|
||||
{kNtVkDelete, S("3~"), S("3;2~"), S("3;5~"), S("3;6~")},
|
||||
{kNtVkHome, S("H"), S("1;2H"), S("1;5H"), S("1;6H")},
|
||||
{kNtVkEnd, S("F"), S("1;2F"), S("1;5F"), S("1;6F")},
|
||||
{kNtVkPrior, S("5~"), S("5;2~"), S("5;5~"), S("5;6~")},
|
||||
{kNtVkNext, S("6~"), S("6;2~"), S("6;5~"), S("6;6~")},
|
||||
{kNtVkF1, -S("OP"), S("1;2P"), S("11^"), S("1;6P")},
|
||||
{kNtVkF2, -S("OQ"), S("1;2Q"), S("12^"), S("1;6Q")},
|
||||
{kNtVkF3, -S("OR"), S("1;2R"), S("13^"), S("1;6R")},
|
||||
{kNtVkF4, -S("OS"), S("1;2S"), S("14^"), S("1;6S")},
|
||||
{kNtVkF5, S("15~"), S("28~"), S("15^"), S("28^")},
|
||||
{kNtVkF6, S("17~"), S("29~"), S("17^"), S("29^")},
|
||||
{kNtVkF7, S("18~"), S("31~"), S("18^"), S("31^")},
|
||||
{kNtVkF8, S("19~"), S("32~"), S("19^"), S("32^")},
|
||||
{kNtVkF9, S("20~"), S("33~"), S("20^"), S("33^")},
|
||||
{kNtVkF10, S("21~"), S("34~"), S("21^"), S("34^")},
|
||||
{kNtVkF11, S("23~"), S("23$"), S("23^"), S("23@")},
|
||||
{kNtVkF12, S("24~"), S("24$"), S("24^"), S("24@")},
|
||||
#undef W
|
||||
#undef S
|
||||
};
|
||||
|
||||
static textwindows int ProcessSignal(int sig, struct Fd *f) {
|
||||
if (f) {
|
||||
if (_weaken(__sig_raise)) {
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
_weaken(__sig_raise)(sig, SI_KERNEL);
|
||||
pthread_mutex_lock(&f->lock);
|
||||
if (!(__sighandflags[sig] & SA_RESTART)) {
|
||||
return eintr();
|
||||
}
|
||||
} else if (sig != SIGWINCH) {
|
||||
TerminateThisProcess(sig);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#define KEYSTROKE_CONTAINER(e) DLL_CONTAINER(struct Keystroke, elem, e)
|
||||
|
||||
struct Keystroke {
|
||||
char buf[32];
|
||||
unsigned buflen;
|
||||
struct Dll elem;
|
||||
};
|
||||
|
||||
struct Keystrokes {
|
||||
struct Dll *list;
|
||||
struct Dll *free;
|
||||
bool end_of_file;
|
||||
uint16_t utf16hs;
|
||||
unsigned allocated;
|
||||
pthread_mutex_t lock;
|
||||
struct Keystroke pool[32];
|
||||
};
|
||||
|
||||
static struct Keystrokes __keystroke;
|
||||
|
||||
static textwindows void LockKeystrokes(void) {
|
||||
pthread_mutex_lock(&__keystroke.lock);
|
||||
}
|
||||
|
||||
static textwindows uint32_t GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
||||
static textwindows void UnlockKeystrokes(void) {
|
||||
pthread_mutex_unlock(&__keystroke.lock);
|
||||
}
|
||||
|
||||
static textwindows uint64_t BlockSignals(void) {
|
||||
return atomic_exchange(&__get_tls()->tib_sigmask, -1);
|
||||
}
|
||||
|
||||
static textwindows void UnblockSignals(uint64_t mask) {
|
||||
atomic_store_explicit(&__get_tls()->tib_sigmask, mask, memory_order_release);
|
||||
}
|
||||
|
||||
static textwindows int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
||||
for (int i = 0; i < ARRAYLEN(kVirtualKey); ++i) {
|
||||
if (kVirtualKey[i].vk == vk) {
|
||||
if (shift && ctrl) {
|
||||
|
@ -144,33 +142,7 @@ static textwindows uint32_t GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
|
||||
static textwindows void EchoTerminalInput(struct Fd *f, char *p, size_t n) {
|
||||
int64_t hOutput;
|
||||
if (f->kind == kFdConsole) {
|
||||
hOutput = f->extra;
|
||||
} else {
|
||||
hOutput = g_fds.p[1].handle;
|
||||
}
|
||||
if (__ttymagic & kFdTtyEchoRaw) {
|
||||
WriteFile(hOutput, p, n, 0, 0);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
|
||||
char ctl[2];
|
||||
ctl[0] = '^';
|
||||
ctl[1] = p[i] ^ 0100;
|
||||
WriteFile(hOutput, ctl, 2, 0, 0);
|
||||
} else {
|
||||
WriteFile(hOutput, p + i, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p,
|
||||
uint16_t *utf16hs, struct Fd *f) {
|
||||
static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
||||
|
||||
uint16_t c = r->Event.KeyEvent.uChar.UnicodeChar;
|
||||
uint16_t vk = r->Event.KeyEvent.wVirtualKeyCode;
|
||||
|
@ -181,23 +153,10 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p,
|
|||
return 0;
|
||||
}
|
||||
|
||||
// ignore numpad being used to compose a character
|
||||
if ((cks & kNtLeftAltPressed) && !(cks & kNtEnhancedKey) &&
|
||||
(vk == kNtVkInsert || vk == kNtVkEnd || vk == kNtVkDown ||
|
||||
vk == kNtVkNext || vk == kNtVkLeft || vk == kNtVkClear ||
|
||||
vk == kNtVkRight || vk == kNtVkHome || vk == kNtVkUp ||
|
||||
vk == kNtVkPrior || vk == kNtVkNumpad0 || vk == kNtVkNumpad1 ||
|
||||
vk == kNtVkNumpad2 || vk == kNtVkNumpad3 || vk == kNtVkNumpad4 ||
|
||||
vk == kNtVkNumpad5 || vk == kNtVkNumpad6 || vk == kNtVkNumpad7 ||
|
||||
vk == kNtVkNumpad8 || vk == kNtVkNumpad9)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int n = 0;
|
||||
|
||||
// process virtual keys
|
||||
int n = 0;
|
||||
if (!c) {
|
||||
uint32_t w;
|
||||
int w;
|
||||
w = GetVirtualKey(vk, !!(cks & kNtShiftPressed),
|
||||
!!(cks & (kNtLeftCtrlPressed | kNtRightCtrlPressed)));
|
||||
if (!w) return 0;
|
||||
|
@ -205,7 +164,11 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p,
|
|||
if (cks & (kNtLeftAltPressed | kNtRightAltPressed)) {
|
||||
p[n++] = 033;
|
||||
}
|
||||
p[n++] = '[';
|
||||
if (w > 0) {
|
||||
p[n++] = '[';
|
||||
} else {
|
||||
w = -w;
|
||||
}
|
||||
do p[n++] = w;
|
||||
while ((w >>= 8));
|
||||
return n;
|
||||
|
@ -213,11 +176,11 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p,
|
|||
|
||||
// translate utf-16 into utf-32
|
||||
if (IsHighSurrogate(c)) {
|
||||
*utf16hs = c;
|
||||
__keystroke.utf16hs = c;
|
||||
return 0;
|
||||
}
|
||||
if (IsLowSurrogate(c)) {
|
||||
c = MergeUtf16(*utf16hs, c);
|
||||
c = MergeUtf16(__keystroke.utf16hs, c);
|
||||
}
|
||||
|
||||
// enter sends \r in a raw terminals
|
||||
|
@ -240,15 +203,21 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p,
|
|||
// handle ctrl-c and ctrl-\, which tcsetattr() is able to remap
|
||||
if (!(__ttymagic & kFdTtyNoIsigs)) {
|
||||
if (c == __vintr && __vintr != _POSIX_VDISABLE) {
|
||||
return ProcessSignal(SIGINT, f);
|
||||
STRACE("encountered CTRL(%#c) c_cc[VINTR] will raise SIGINT", CTRL(c));
|
||||
__get_tls()->tib_sigpending |= 1ull << (SIGINT - 1);
|
||||
return 0;
|
||||
} else if (c == __vquit && __vquit != _POSIX_VDISABLE) {
|
||||
return ProcessSignal(SIGQUIT, f);
|
||||
STRACE("encountered CTRL(%#c) c_cc[VQUITR] will raise SIGQUIT", CTRL(c));
|
||||
__get_tls()->tib_sigpending |= 1ull << (SIGQUIT - 1);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// handle ctrl-d the end of file keystroke
|
||||
if (c == __veof && __veof != _POSIX_VDISABLE) {
|
||||
return enodata();
|
||||
STRACE("encountered CTRL(%#c) c_cc[VEOF] closing console input", CTRL(c));
|
||||
__keystroke.end_of_file = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// insert esc prefix when alt is held
|
||||
|
@ -269,11 +238,11 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p,
|
|||
// - write(1, "\e[?1000;1002;1015;1006h") to enable
|
||||
// - write(1, "\e[?1000;1002;1015;1006l") to disable
|
||||
// See o//examples/ttyinfo.com and o//tool/viz/life.com
|
||||
static textwindows int ProcessMouseEvent(const struct NtInputRecord *r, char *b,
|
||||
struct Fd *f) {
|
||||
static textwindows int ProcessMouseEvent(const struct NtInputRecord *r,
|
||||
char *b) {
|
||||
int e = 0;
|
||||
char *p = b;
|
||||
uint32_t currentbs = f ? f->mousebuttons : 0;
|
||||
uint32_t currentbs = __mousebuttons;
|
||||
uint32_t ev = r->Event.MouseEvent.dwEventFlags;
|
||||
uint32_t bs = r->Event.MouseEvent.dwButtonState;
|
||||
ev &= kNtMouseMoved | kNtMouseWheeled;
|
||||
|
@ -320,98 +289,195 @@ static textwindows int ProcessMouseEvent(const struct NtInputRecord *r, char *b,
|
|||
} else {
|
||||
*p++ = 'M'; // down
|
||||
}
|
||||
if (f) {
|
||||
f->mousebuttons = bs;
|
||||
}
|
||||
__mousebuttons = bs;
|
||||
}
|
||||
return p - b;
|
||||
}
|
||||
|
||||
textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
|
||||
char p[hasatleast 32],
|
||||
uint16_t *utf16hs, struct Fd *f) {
|
||||
static textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
|
||||
char p[hasatleast 32]) {
|
||||
switch (r->EventType) {
|
||||
case kNtKeyEvent:
|
||||
return ProcessKeyEvent(r, p, utf16hs, f);
|
||||
return ProcessKeyEvent(r, p);
|
||||
case kNtMouseEvent:
|
||||
return ProcessMouseEvent(r, p, f);
|
||||
return ProcessMouseEvent(r, p);
|
||||
case kNtWindowBufferSizeEvent:
|
||||
return ProcessSignal(SIGWINCH, f);
|
||||
STRACE("detected console resize will raise SIGWINCH");
|
||||
__get_tls()->tib_sigpending |= 1ull << (SIGWINCH - 1);
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows ssize_t ReadFromWindowsConsole(struct Fd *f, void *data,
|
||||
size_t size) {
|
||||
ssize_t rc;
|
||||
int e = errno;
|
||||
uint16_t utf16hs = 0;
|
||||
pthread_mutex_lock(&f->lock);
|
||||
for (;;) {
|
||||
if (f->eoftty) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
uint32_t got = MIN(size, f->buflen);
|
||||
uint32_t remain = f->buflen - got;
|
||||
if (got) memcpy(data, f->buf, got);
|
||||
if (remain) memmove(f->buf, f->buf + got, remain);
|
||||
f->buflen = remain;
|
||||
if (got) {
|
||||
rc = got;
|
||||
break;
|
||||
}
|
||||
uint32_t events_available;
|
||||
if (!GetNumberOfConsoleInputEvents(f->handle, &events_available)) {
|
||||
rc = __winerr();
|
||||
break;
|
||||
}
|
||||
if (events_available) {
|
||||
uint32_t n;
|
||||
struct NtInputRecord r;
|
||||
if (!ReadConsoleInput(f->handle, &r, 1, &n)) {
|
||||
rc = __winerr();
|
||||
break;
|
||||
}
|
||||
rc = ConvertConsoleInputToAnsi(&r, f->buf, &utf16hs, f);
|
||||
if (rc == -1) {
|
||||
if (errno == ENODATA) {
|
||||
f->eoftty = true;
|
||||
errno = e;
|
||||
rc = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
f->buflen = rc;
|
||||
static textwindows struct Keystroke *NewKeystroke(void) {
|
||||
struct Dll *e;
|
||||
struct Keystroke *k = 0;
|
||||
int i, n = ARRAYLEN(__keystroke.pool);
|
||||
if (atomic_load_explicit(&__keystroke.allocated, memory_order_acquire) < n &&
|
||||
(i = atomic_fetch_add(&__keystroke.allocated, 1)) < n) {
|
||||
k = __keystroke.pool + i;
|
||||
} else if ((e = dll_first(__keystroke.free))) {
|
||||
k = KEYSTROKE_CONTAINER(e);
|
||||
dll_remove(&__keystroke.free, &k->elem);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
bzero(k, sizeof(*k));
|
||||
dll_init(&k->elem);
|
||||
return k;
|
||||
}
|
||||
|
||||
static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
|
||||
int len;
|
||||
struct Keystroke *k;
|
||||
char buf[sizeof(k->buf)];
|
||||
if ((len = ConvertConsoleInputToAnsi(r, buf))) {
|
||||
if ((k = NewKeystroke())) {
|
||||
memcpy(k->buf, buf, sizeof(k->buf));
|
||||
k->buflen = len;
|
||||
dll_make_last(&__keystroke.list, &k->elem);
|
||||
} else {
|
||||
if (f->flags & O_NONBLOCK) {
|
||||
rc = 0;
|
||||
break;
|
||||
}
|
||||
uint32_t ms = __SIG_POLL_INTERVAL_MS;
|
||||
if (__ttymagic & kFdTtyNoBlock) {
|
||||
if (!__vtime) {
|
||||
rc = 0;
|
||||
break;
|
||||
} else {
|
||||
ms = __vtime * 100;
|
||||
STRACE("ran out of memory to hold keystroke %#.*s", len, buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static textwindows void IngestConsoleInput(int64_t handle) {
|
||||
uint32_t i, n;
|
||||
struct NtInputRecord records[16];
|
||||
if (!__keystroke.end_of_file) {
|
||||
do {
|
||||
if (GetNumberOfConsoleInputEvents(handle, &n)) {
|
||||
if (n) {
|
||||
n = MIN(ARRAYLEN(records), n);
|
||||
if (ReadConsoleInput(handle, records, n, &n)) {
|
||||
for (i = 0; i < n && !__keystroke.end_of_file; ++i) {
|
||||
IngestConsoleInputRecord(records + i);
|
||||
}
|
||||
} else {
|
||||
STRACE("ReadConsoleInput failed w/ %d", GetLastError());
|
||||
__keystroke.end_of_file = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((rc = _check_interrupts(kSigOpRestartable))) {
|
||||
} else {
|
||||
STRACE("GetNumberOfConsoleInputRecords failed w/ %d", GetLastError());
|
||||
__keystroke.end_of_file = true;
|
||||
break;
|
||||
}
|
||||
struct PosixThread *pt = _pthread_self();
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore, ms);
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno == ECANCELED) {
|
||||
rc = ecanceled();
|
||||
break;
|
||||
} while (n == ARRAYLEN(records));
|
||||
}
|
||||
}
|
||||
|
||||
textwindows int FlushConsoleInputBytes(int64_t handle) {
|
||||
int rc;
|
||||
uint64_t m;
|
||||
m = BlockSignals();
|
||||
LockKeystrokes();
|
||||
if (FlushConsoleInputBuffer(handle)) {
|
||||
dll_make_first(&__keystroke.free, __keystroke.list);
|
||||
__keystroke.list = 0;
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = __winerr();
|
||||
}
|
||||
UnlockKeystrokes();
|
||||
UnblockSignals(m);
|
||||
return rc;
|
||||
}
|
||||
|
||||
textwindows int CountConsoleInputBytes(int64_t handle) {
|
||||
int count = 0;
|
||||
struct Dll *e;
|
||||
uint64_t m = BlockSignals();
|
||||
LockKeystrokes();
|
||||
IngestConsoleInput(handle);
|
||||
for (e = dll_first(__keystroke.list); e; e = dll_next(__keystroke.list, e)) {
|
||||
count += KEYSTROKE_CONTAINER(e)->buflen;
|
||||
}
|
||||
if (!count && __keystroke.end_of_file) {
|
||||
count = -1;
|
||||
}
|
||||
UnlockKeystrokes();
|
||||
UnblockSignals(m);
|
||||
return count;
|
||||
}
|
||||
|
||||
static textwindows bool DigestConsoleInput(void *data, size_t size, int *rc) {
|
||||
struct Dll *e;
|
||||
if ((e = dll_first(__keystroke.list))) {
|
||||
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
||||
uint32_t got = MIN(size, k->buflen);
|
||||
uint32_t remain = k->buflen - got;
|
||||
if (got) memcpy(data, k->buf, got);
|
||||
if (remain) memmove(k->buf, k->buf + got, remain);
|
||||
if (!remain) {
|
||||
dll_remove(&__keystroke.list, e);
|
||||
dll_make_first(&__keystroke.free, e);
|
||||
}
|
||||
k->buflen = remain;
|
||||
if (got) {
|
||||
*rc = got;
|
||||
return true;
|
||||
}
|
||||
} else if (__keystroke.end_of_file) {
|
||||
*rc = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
|
||||
static textwindows void EchoTerminalInput(struct Fd *f, char *p, size_t n) {
|
||||
int64_t hOutput;
|
||||
if (f->kind == kFdConsole) {
|
||||
hOutput = f->extra;
|
||||
} else {
|
||||
hOutput = g_fds.p[1].handle;
|
||||
}
|
||||
if (__ttymagic & kFdTtyEchoRaw) {
|
||||
WriteFile(hOutput, p, n, 0, 0);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (isascii(p[i]) && iscntrl(p[i]) && p[i] != '\n' && p[i] != '\t') {
|
||||
char ctl[2];
|
||||
ctl[0] = '^';
|
||||
ctl[1] = p[i] ^ 0100;
|
||||
WriteFile(hOutput, ctl, 2, 0, 0);
|
||||
} else {
|
||||
WriteFile(hOutput, p + i, 1, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
}
|
||||
|
||||
static textwindows ssize_t ReadFromWindowsConsole(struct Fd *f, void *data,
|
||||
size_t size) {
|
||||
int rc = -1;
|
||||
for (;;) {
|
||||
bool done = false;
|
||||
uint64_t m;
|
||||
m = BlockSignals();
|
||||
LockKeystrokes();
|
||||
IngestConsoleInput(f->handle);
|
||||
done = DigestConsoleInput(data, size, &rc);
|
||||
UnlockKeystrokes();
|
||||
UnblockSignals(m);
|
||||
if (done) break;
|
||||
if (f->flags & O_NONBLOCK) return eagain();
|
||||
uint32_t ms = __SIG_POLL_INTERVAL_MS;
|
||||
if (__ttymagic & kFdTtyNoBlock) {
|
||||
if (!__vtime) {
|
||||
return 0;
|
||||
} else {
|
||||
ms = __vtime * 100;
|
||||
}
|
||||
}
|
||||
if (_check_interrupts(kSigOpRestartable)) return -1;
|
||||
if (__pause_thread(ms)) return -1;
|
||||
}
|
||||
if (rc > 0 && (__ttymagic & kFdTtyEchoing)) {
|
||||
EchoTerminalInput(f, data, size);
|
||||
}
|
||||
|
@ -510,6 +576,12 @@ textwindows ssize_t sys_read_nt_impl(int fd, void *data, size_t size,
|
|||
return got;
|
||||
}
|
||||
|
||||
errno_t err;
|
||||
if (_weaken(pthread_testcancel_np) &&
|
||||
(err = _weaken(pthread_testcancel_np)())) {
|
||||
return ecanceled();
|
||||
}
|
||||
|
||||
switch (GetLastError()) {
|
||||
case kNtErrorBrokenPipe: // broken pipe
|
||||
case kNtErrorNoData: // closing named pipe
|
||||
|
|
|
@ -97,3 +97,5 @@ int setrlimit(int resource, const struct rlimit *rlim) {
|
|||
DescribeRlimit(0, rlim), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
__weak_reference(setrlimit, setrlimit64);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "libc/calls/state.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/sigset.internal.h"
|
||||
#include "libc/calls/struct/ucontext.internal.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/dce.h"
|
||||
|
@ -49,6 +50,7 @@
|
|||
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
|
@ -124,19 +126,21 @@ static textwindows bool __sig_start(struct PosixThread *pt, int sig,
|
|||
return true;
|
||||
}
|
||||
|
||||
static textwindows sigaction_f __sig_handler(unsigned rva) {
|
||||
return (sigaction_f)(__executable_start + rva);
|
||||
}
|
||||
|
||||
static textwindows void __sig_call(int sig, siginfo_t *si, ucontext_t *ctx,
|
||||
unsigned rva, unsigned flags,
|
||||
struct CosmoTib *tib) {
|
||||
sigaction_f handler;
|
||||
handler = (sigaction_f)(__executable_start + rva);
|
||||
++__sig.count;
|
||||
if (__sig_should_use_altstack(flags, tib)) {
|
||||
tib->tib_sigstack_flags |= SS_ONSTACK;
|
||||
__stack_call(sig, si, ctx, 0, handler,
|
||||
__stack_call(sig, si, ctx, 0, __sig_handler(rva),
|
||||
tib->tib_sigstack_addr + tib->tib_sigstack_size);
|
||||
tib->tib_sigstack_flags &= ~SS_ONSTACK;
|
||||
} else {
|
||||
handler(sig, si, ctx);
|
||||
__sig_handler(rva)(sig, si, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,21 +155,29 @@ textwindows int __sig_raise(int sig, int sic) {
|
|||
nc.ContextFlags = kNtContextAll;
|
||||
GetThreadContext(GetCurrentThread(), &nc);
|
||||
_ntcontext2linux(&ctx, &nc);
|
||||
STRACE("raising %G", sig);
|
||||
if (!(flags & SA_NODEFER)) {
|
||||
tib->tib_sigmask |= 1ull << (sig - 1);
|
||||
}
|
||||
STRACE("entering raise(%G) signal handler %t with mask %s → %s", sig,
|
||||
__sig_handler(rva), DescribeSigset(0, &ctx.uc_sigmask),
|
||||
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}));
|
||||
__sig_call(sig, &si, &ctx, rva, flags, tib);
|
||||
STRACE("leaving raise(%G) signal handler %t with mask %s → %s", sig,
|
||||
__sig_handler(rva),
|
||||
DescribeSigset(0, &(sigset_t){{pt->tib->tib_sigmask}}),
|
||||
DescribeSigset(0, &ctx.uc_sigmask));
|
||||
tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||
return (flags & SA_RESTART) ? 2 : 1;
|
||||
}
|
||||
|
||||
textwindows void __sig_cancel(struct PosixThread *pt) {
|
||||
textwindows void __sig_cancel(struct PosixThread *pt, unsigned flags) {
|
||||
atomic_int *futex;
|
||||
if (_weaken(WakeByAddressSingle) &&
|
||||
(futex = atomic_load_explicit(&pt->pt_futex, memory_order_acquire))) {
|
||||
STRACE("canceling futex");
|
||||
_weaken(WakeByAddressSingle)(futex);
|
||||
} else if (pt->iohandle > 0) {
|
||||
} else if (!(flags & SA_RESTART) && pt->iohandle > 0) {
|
||||
STRACE("canceling i/o operation");
|
||||
if (!CancelIoEx(pt->iohandle, pt->ioverlap)) {
|
||||
int err = GetLastError();
|
||||
if (err != kNtErrorNotFound) {
|
||||
|
@ -173,9 +185,13 @@ textwindows void __sig_cancel(struct PosixThread *pt) {
|
|||
}
|
||||
}
|
||||
} else if (pt->pt_flags & PT_INSEMAPHORE) {
|
||||
STRACE("canceling semaphore");
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (!ReleaseSemaphore(pt->semaphore, 1, 0)) {
|
||||
STRACE("ReleaseSemaphore() failed w/ %d", GetLastError());
|
||||
}
|
||||
} else {
|
||||
STRACE("canceling asynchronously");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,16 +210,25 @@ static textwindows wontreturn void __sig_panic(const char *msg) {
|
|||
|
||||
static textwindows wontreturn void __sig_tramp(struct SignalFrame *sf) {
|
||||
ucontext_t ctx = {0};
|
||||
sigaction_f handler = (sigaction_f)(__executable_start + sf->rva);
|
||||
STRACE("__sig_tramp(%G, %t)", sf->si.si_signo, handler);
|
||||
int sig = sf->si.si_signo;
|
||||
_ntcontext2linux(&ctx, sf->nc);
|
||||
ctx.uc_sigmask.__bits[0] = sf->pt->tib->tib_sigmask;
|
||||
ctx.uc_sigmask.__bits[0] =
|
||||
atomic_load_explicit(&sf->pt->tib->tib_sigmask, memory_order_acquire);
|
||||
if (!(sf->flags & SA_NODEFER)) {
|
||||
sf->pt->tib->tib_sigmask |= 1ull << (sf->si.si_signo - 1);
|
||||
sf->pt->tib->tib_sigmask |= 1ull << (sig - 1);
|
||||
}
|
||||
++__sig.count;
|
||||
handler(sf->si.si_signo, &sf->si, &ctx);
|
||||
sf->pt->tib->tib_sigmask = ctx.uc_sigmask.__bits[0];
|
||||
STRACE("entering __sig_tramp(%G, %t) with mask %s → %s", sig,
|
||||
__sig_handler(sf->rva), DescribeSigset(0, &ctx.uc_sigmask),
|
||||
DescribeSigset(0, &(sigset_t){{sf->pt->tib->tib_sigmask}}));
|
||||
__sig_handler(sf->rva)(sig, &sf->si, &ctx);
|
||||
STRACE("leaving __sig_tramp(%G, %t) with mask %s → %s", sig,
|
||||
__sig_handler(sf->rva),
|
||||
DescribeSigset(0, &(sigset_t){{sf->pt->tib->tib_sigmask}}),
|
||||
DescribeSigset(0, &ctx.uc_sigmask));
|
||||
atomic_store_explicit(&sf->pt->tib->tib_sigmask, ctx.uc_sigmask.__bits[0],
|
||||
memory_order_release);
|
||||
// TDOO(jart): Do we need to do a __sig_check() here?
|
||||
_ntlinux2context(sf->nc, &ctx);
|
||||
SetThreadContext(GetCurrentThread(), sf->nc);
|
||||
__sig_panic("SetThreadContext(GetCurrentThread)");
|
||||
|
@ -259,7 +284,7 @@ static textwindows int __sig_killer(struct PosixThread *pt, int sig, int sic) {
|
|||
}
|
||||
ResumeThread(th); // doesn't actually resume if doing blocking i/o
|
||||
pt->abort_errno = EINTR;
|
||||
__sig_cancel(pt); // we can wake it up immediately by canceling it
|
||||
__sig_cancel(pt, flags); // we can wake it up immediately by canceling it
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -431,7 +456,7 @@ __msabi textwindows dontinstrument bool32 __sig_console(uint32_t dwCtrlType) {
|
|||
|
||||
static textwindows int __sig_checkem(atomic_ulong *sigs, struct CosmoTib *tib,
|
||||
const char *thing, int id) {
|
||||
bool handler_was_called = false;
|
||||
int handler_was_called = 0;
|
||||
uint64_t pending, masked, deliverable;
|
||||
pending = atomic_load_explicit(sigs, memory_order_acquire);
|
||||
masked = atomic_load_explicit(&tib->tib_sigmask, memory_order_acquire);
|
||||
|
@ -454,12 +479,12 @@ static textwindows int __sig_checkem(atomic_ulong *sigs, struct CosmoTib *tib,
|
|||
// didn't have the SA_RESTART flag, and `2`, which means SA_RESTART
|
||||
// handlers were called (or `3` if both were the case).
|
||||
textwindows int __sig_check(void) {
|
||||
bool handler_was_called = false;
|
||||
int handler_was_called = false;
|
||||
struct CosmoTib *tib = __get_tls();
|
||||
handler_was_called |=
|
||||
__sig_checkem(&tib->tib_sigpending, tib, "tid", tib->tib_tid);
|
||||
handler_was_called |= __sig_checkem(&__sig.pending, tib, "pid", getpid());
|
||||
POLLTRACE("__sig_check() → %hhhd", handler_was_called);
|
||||
POLLTRACE("__sig_check() → %d", handler_was_called);
|
||||
return handler_was_called;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ int __sig_check(void);
|
|||
int __sig_kill(struct PosixThread *, int, int);
|
||||
int __sig_mask(int, const sigset_t *, sigset_t *);
|
||||
int __sig_raise(int, int);
|
||||
void __sig_cancel(struct PosixThread *);
|
||||
void __sig_cancel(struct PosixThread *, unsigned);
|
||||
void __sig_generate(int, int);
|
||||
void __sig_init(void);
|
||||
|
||||
|
|
|
@ -70,17 +70,7 @@ int sigsuspend(const sigset_t *ignore) {
|
|||
} else {
|
||||
__sig_mask(SIG_SETMASK, arg, &save);
|
||||
while (!(rc = _check_interrupts(0))) {
|
||||
struct PosixThread *pt;
|
||||
pt = _pthread_self();
|
||||
pt->abort_errno = 0;
|
||||
pt->pt_flags |= PT_INSEMAPHORE;
|
||||
WaitForSingleObject(pt->semaphore, __SIG_SIG_INTERVAL_MS);
|
||||
pt->pt_flags &= ~PT_INSEMAPHORE;
|
||||
if (pt->abort_errno) {
|
||||
errno = pt->abort_errno;
|
||||
rc = -1;
|
||||
break;
|
||||
}
|
||||
if ((rc = __pause_thread(__SIG_SIG_INTERVAL_MS))) break;
|
||||
}
|
||||
__sig_mask(SIG_SETMASK, &save, 0);
|
||||
}
|
||||
|
|
|
@ -66,3 +66,5 @@ int statfs(const char *path, struct statfs *sf) {
|
|||
STRACE("statfs(%#s, [%s]) → %d% m", path, DescribeStatfs(rc, sf));
|
||||
return rc;
|
||||
}
|
||||
|
||||
__weak_reference(statfs, statfs64);
|
||||
|
|
|
@ -31,15 +31,12 @@ struct Fd {
|
|||
char kind;
|
||||
bool eoftty;
|
||||
bool dontclose;
|
||||
unsigned char buflen;
|
||||
unsigned flags;
|
||||
unsigned mode;
|
||||
int64_t handle;
|
||||
int64_t extra;
|
||||
int64_t pointer;
|
||||
pthread_mutex_t lock;
|
||||
unsigned char mousebuttons;
|
||||
char buf[32];
|
||||
};
|
||||
|
||||
struct StdinRelay {
|
||||
|
|
|
@ -67,30 +67,7 @@ static dontinline textwindows int sys_tcflush_nt(int fd, int queue) {
|
|||
if (queue == TCOFLUSH) {
|
||||
return 0; // windows console output is never buffered
|
||||
}
|
||||
FlushConsoleInputBuffer(hConin);
|
||||
int rc = 0;
|
||||
int e = errno;
|
||||
int oldflags = g_fds.p[fd].flags;
|
||||
g_fds.p[fd].flags |= O_NONBLOCK;
|
||||
for (;;) {
|
||||
char buf[512];
|
||||
ssize_t got = sys_read_nt_impl(fd, buf, 512, -1);
|
||||
if (!got) {
|
||||
break;
|
||||
} else if (got == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
errno = e;
|
||||
} else if (errno == EINTR) {
|
||||
errno = e;
|
||||
continue;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_fds.p[fd].flags = oldflags;
|
||||
return rc;
|
||||
return FlushConsoleInputBytes(hConin);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/consolemodeflags.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/struct/consolescreenbufferinfoex.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/baud.internal.h"
|
||||
|
|
|
@ -50,11 +50,40 @@ typedef uint32_t nlink_t; /* uint16_t on xnu */
|
|||
#define TIME_T_MIN (-TIME_T_MAX - 1)
|
||||
|
||||
#if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
|
||||
#define blkcnt64_t blkcnt_t
|
||||
#define fsblkcnt64_t fsblkcnt_t
|
||||
#define fsfilcnt64_t fsfilcnt_t
|
||||
#define ino64_t ino_t
|
||||
#define off64_t off_t
|
||||
#define F_GETLK64 F_GETLK
|
||||
#define F_SETLK64 F_SETLK
|
||||
#define F_SETLKW64 F_SETLKW
|
||||
#define RLIM64_INFINITY RLIM_INFINITY
|
||||
#define RLIM64_SAVED_CUR RLIM_SAVED_CUR
|
||||
#define RLIM64_SAVED_MAX RLIM_SAVED_MAX
|
||||
#define alphasort64 alphasort
|
||||
#define blkcnt64_t blkcnt_t
|
||||
#define dirent64 dirent
|
||||
#define flock64 flock
|
||||
#define fsfilcnt64_t fsfilcnt_t
|
||||
#define fstat64 fstat
|
||||
#define fstatat64 fstatat
|
||||
#define fstatfs64 fstatfs
|
||||
#define getrlimit64 getrlimit
|
||||
#define ino64_t ino_t
|
||||
#define lockf64 lockf
|
||||
#define lstat64 lstat
|
||||
#define mmap64 mmap
|
||||
#define off64_t off_t
|
||||
#define open64 open
|
||||
#define openat64 openat
|
||||
#define posix_fadvise64 posix_fadvise
|
||||
#define posix_fallocate64 posix_fallocate
|
||||
#define readdir64 readdir
|
||||
#define readdir64_r readdir_r
|
||||
#define rlim64_t rlim_t
|
||||
#define rlimit64 rlimit
|
||||
#define scandir64 scandir
|
||||
#define sendfile64 sendfile
|
||||
#define setrlimit64 setrlimit
|
||||
#define stat64 stat
|
||||
#define statfs64 statfs
|
||||
#define versionsort64 versionsort
|
||||
#endif
|
||||
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
|
|
|
@ -86,10 +86,6 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
|||
if (!pwriting) {
|
||||
offset = 0;
|
||||
}
|
||||
if (seekable && !pwriting) {
|
||||
pthread_mutex_lock(&f->lock);
|
||||
offset = f->pointer;
|
||||
}
|
||||
|
||||
// To use the tty mouse events feature:
|
||||
// - write(1, "\e[?1000;1002;1015;1006h") to enable
|
||||
|
@ -161,6 +157,11 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
|||
}
|
||||
}
|
||||
|
||||
if (seekable && !pwriting) {
|
||||
pthread_mutex_lock(&f->lock);
|
||||
offset = f->pointer;
|
||||
}
|
||||
|
||||
struct NtOverlapped overlap = {.hEvent = CreateEvent(0, 0, 0, 0),
|
||||
.Pointer = offset};
|
||||
ok = WriteFile(handle, data, size, 0, &overlap);
|
||||
|
@ -212,6 +213,12 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
|||
return sent;
|
||||
}
|
||||
|
||||
errno_t err;
|
||||
if (_weaken(pthread_testcancel_np) &&
|
||||
(err = _weaken(pthread_testcancel_np)())) {
|
||||
return ecanceled();
|
||||
}
|
||||
|
||||
switch (GetLastError()) {
|
||||
// case kNtErrorInvalidHandle:
|
||||
// return ebadf(); /* handled by consts.sh */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue