diff --git a/libc/calls/createpipename.c b/libc/calls/createpipename.c index 48eb48b72..a2bf78bc8 100644 --- a/libc/calls/createpipename.c +++ b/libc/calls/createpipename.c @@ -16,10 +16,11 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/calls/calls.h" -#include "libc/nt/process.h" +#include "libc/atomic.h" +#include "libc/intrin/atomic.h" +#include "libc/runtime/internal.h" -static textwindows char16_t *UintToChar16Array(char16_t p[21], uint64_t x) { +static dontasan textwindows char16_t *itoa16(char16_t p[21], uint64_t x) { char t; size_t a, b, i = 0; do { @@ -36,14 +37,15 @@ static textwindows char16_t *UintToChar16Array(char16_t p[21], uint64_t x) { return p + i; } -textwindows char16_t *CreatePipeName(char16_t *a) { - static long x; +// This function is called very early by WinMain(). +dontasan textwindows char16_t *__create_pipe_name(char16_t *a) { char16_t *p = a; const char *q = "\\\\?\\pipe\\cosmo\\"; + static atomic_uint x; while (*q) *p++ = *q++; - p = UintToChar16Array(p, GetCurrentProcessId()); + p = itoa16(p, __pid); *p++ = '-'; - p = UintToChar16Array(p, (x += 1)); + p = itoa16(p, atomic_fetch_add(&x, 1)); *p = 0; return a; } diff --git a/libc/calls/ioctl.c b/libc/calls/ioctl.c index 82550f954..af92700eb 100644 --- a/libc/calls/ioctl.c +++ b/libc/calls/ioctl.c @@ -18,7 +18,9 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" #include "libc/calls/internal.h" +#include "libc/calls/struct/fd.internal.h" #include "libc/calls/syscall-sysv.internal.h" +#include "libc/calls/syscall_support-nt.internal.h" #include "libc/calls/termios.h" #include "libc/dce.h" #include "libc/errno.h" @@ -29,7 +31,10 @@ #include "libc/macros.internal.h" #include "libc/mem/alloca.h" #include "libc/mem/mem.h" +#include "libc/nt/enum/filetype.h" #include "libc/nt/errors.h" +#include "libc/nt/files.h" +#include "libc/nt/ipc.h" #include "libc/nt/iphlpapi.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/ipadapteraddresses.h" @@ -48,6 +53,10 @@ #include "libc/sysv/consts/termios.h" #include "libc/sysv/errfuns.h" +#ifdef __x86_64__ +__static_yoink("WinMainStdin"); +#endif + /* Maximum number of unicast addresses handled for each interface */ #define MAX_UNICAST_ADDR 32 #define MAX_NAME_CLASH ((int)('z' - 'a')) /* Allow a..z */ @@ -61,14 +70,10 @@ static struct HostAdapterInfoNode { short flags; } * __hostInfo; -static int ioctl_default(int fd, unsigned long request, ...) { +static int ioctl_default(int fd, unsigned long request, void *arg) { int rc; - void *arg; va_list va; int64_t handle; - va_start(va, request); - arg = va_arg(va, void *); - va_end(va); if (!IsWindows()) { return sys_ioctl(fd, request, arg); } else if (__isfdopen(fd)) { @@ -87,6 +92,36 @@ static int ioctl_default(int fd, unsigned long request, ...) { } } +static int ioctl_fionread(int fd, uint32_t *arg) { + int rc; + va_list va; + int64_t handle; + uint32_t avail; + if (!IsWindows()) { + return sys_ioctl(fd, FIONREAD, arg); + } else if (__isfdopen(fd)) { + handle = __resolve_stdin_handle(g_fds.p[fd].handle); + if (g_fds.p[fd].kind == kFdSocket) { + if ((rc = _weaken(__sys_ioctlsocket_nt)(handle, FIONREAD, arg)) != -1) { + return rc; + } else { + return _weaken(__winsockerr)(); + } + } else if (GetFileType(handle) == kNtFileTypePipe) { + if (PeekNamedPipe(handle, 0, 0, 0, &avail, 0)) { + *arg = avail; + return 0; + } else { + return __winerr(); + } + } else { + return eopnotsupp(); + } + } else { + return ebadf(); + } +} + /* Frees all the nodes of the _hostInfo */ static textwindows void freeHostInfo(void) { struct HostAdapterInfoNode *next, *node = __hostInfo; @@ -564,8 +599,7 @@ static int ioctl_siocgifflags(int fd, void *arg) { * @param request can be any of: * * - `FIONREAD` takes an `int *` and returns how many bytes of input - * are available on a terminal or socket, waiting to be read. On - * Windows this currently won't work for console file descriptors. + * are available on a terminal or socket, waiting to be read. * * - `TIOCGWINSZ` populates `struct winsize *` with the dimensions * of your teletypewriter. It's an alias for tcgetwinsize(). @@ -635,8 +669,8 @@ int ioctl(int fd, unsigned long request, ...) { va_start(va, request); arg = va_arg(va, void *); va_end(va); - if (request == FIONBIO) { - rc = ioctl_default(fd, request, arg); + if (request == FIONREAD) { + rc = ioctl_fionread(fd, arg); } else if (request == TIOCGWINSZ) { rc = tcgetwinsize(fd, arg); } else if (request == TIOCSWINSZ) { diff --git a/libc/calls/pipe-nt.c b/libc/calls/pipe-nt.c index 1affe6793..7698dbbc1 100644 --- a/libc/calls/pipe-nt.c +++ b/libc/calls/pipe-nt.c @@ -34,7 +34,7 @@ textwindows int sys_pipe_nt(int pipefd[2], unsigned flags) { int64_t hin, hout; int reader, writer; char16_t pipename[64]; - CreatePipeName(pipename); + __create_pipe_name(pipename); __fds_lock(); if ((reader = __reservefd_unlocked(-1)) == -1) { __fds_unlock(); diff --git a/libc/calls/poll-nt.c b/libc/calls/poll-nt.c index 06a8dd0a1..7c610907f 100644 --- a/libc/calls/poll-nt.c +++ b/libc/calls/poll-nt.c @@ -32,6 +32,8 @@ #include "libc/nt/runtime.h" #include "libc/nt/struct/pollfd.h" #include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" #include "libc/nt/winsock.h" #include "libc/sock/internal.h" #include "libc/sock/struct/pollfd.h" @@ -43,6 +45,8 @@ #ifdef __x86_64__ +__static_yoink("WinMainStdin"); + /* * Polls on the New Technology. * @@ -92,7 +96,7 @@ textwindows int sys_poll_nt(struct pollfd *fds, uint64_t nfds, uint64_t *ms, } } else if (pn < ARRAYLEN(pipefds)) { pipeindices[pn] = i; - pipefds[pn].handle = g_fds.p[fds[i].fd].handle; + pipefds[pn].handle = __resolve_stdin_handle(g_fds.p[fds[i].fd].handle); pipefds[pn].events = 0; pipefds[pn].revents = 0; switch (g_fds.p[fds[i].fd].flags & O_ACCMODE) { diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index 3e4200a15..e3fdcbb4d 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -91,9 +91,11 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, bool32 ok; uint32_t got; int filetype; + int64_t handle; int abort_errno = EAGAIN; size = MIN(size, 0x7ffff000); - filetype = GetFileType(fd->handle); + handle = __resolve_stdin_handle(fd->handle); + filetype = GetFileType(handle); if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) { struct NtOverlapped overlap = {0}; if (offset != -1) { @@ -103,18 +105,18 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, if ((overlap.hEvent = CreateEvent(0, 0, 0, 0))) { // the win32 manual says it's important to *not* put &got here // since for overlapped i/o, we always use GetOverlappedResult - ok = ReadFile(fd->handle, data, size, 0, &overlap); + ok = ReadFile(handle, data, size, 0, &overlap); if (!ok && GetLastError() == kNtErrorIoPending) { // i/o operation is in flight; blocking is unavoidable // if we're in non-blocking mode, then immediately abort // if an interrupt is pending, then abort before waiting // otherwise wait for i/o periodically checking interrupts if (fd->flags & O_NONBLOCK) { - sys_read_nt_abort(fd->handle, &overlap); + sys_read_nt_abort(handle, &overlap); } else if (_check_interrupts(kSigOpRestartable, g_fds.p)) { Interrupted: abort_errno = errno; - sys_read_nt_abort(fd->handle, &overlap); + sys_read_nt_abort(handle, &overlap); } else { for (;;) { uint32_t i; @@ -134,7 +136,7 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, if (ok) { // overlapped is allocated on stack, so it's important we wait // for windows to acknowledge that it's done using that memory - ok = GetOverlappedResult(fd->handle, &overlap, &got, true); + ok = GetOverlappedResult(handle, &overlap, &got, true); } CloseHandle(overlap.hEvent); } else { @@ -142,21 +144,21 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data, } } else if (offset == -1) { // perform simple blocking file read - ok = ReadFile(fd->handle, data, size, &got, 0); + ok = ReadFile(handle, data, size, &got, 0); } else { // perform pread()-style file read at particular file offset int64_t position; // save file pointer which windows clobbers, even for overlapped i/o - if (!SetFilePointerEx(fd->handle, 0, &position, SEEK_CUR)) { + if (!SetFilePointerEx(handle, 0, &position, SEEK_CUR)) { return __winerr(); // fd probably isn't seekable? } struct NtOverlapped overlap = {0}; overlap.Pointer = (void *)(uintptr_t)offset; - ok = ReadFile(fd->handle, data, size, 0, &overlap); + ok = ReadFile(handle, data, size, 0, &overlap); if (!ok && GetLastError() == kNtErrorIoPending) ok = true; - if (ok) ok = GetOverlappedResult(fd->handle, &overlap, &got, true); + if (ok) ok = GetOverlappedResult(handle, &overlap, &got, true); // restore file pointer which windows clobbers, even on error - unassert(SetFilePointerEx(fd->handle, position, 0, SEEK_SET)); + unassert(SetFilePointerEx(handle, position, 0, SEEK_SET)); } if (ok) { if (fd->ttymagic & kFdTtyMunging) { diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index 5070ec240..126787c33 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -28,12 +28,24 @@ struct Fd { int64_t extra; }; +struct StdinRelay { + _Atomic(uint32_t) once; + int64_t inisem; /* semaphore to delay 1st read */ + int64_t handle; /* should == g_fds.p[0].handle */ + int64_t reader; /* ReadFile() use this instead */ + int64_t writer; /* only used by WinStdinThread */ +}; + struct Fds { _Atomic(int) f; /* lowest free slot */ size_t n; struct Fd *p, *e; + struct StdinRelay stdin; }; +int64_t __resolve_stdin_handle(int64_t); +void WinMainStdin(void); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_CALLS_STRUCT_FD_INTERNAL_H_ */ diff --git a/libc/calls/syscall_support-nt.internal.h b/libc/calls/syscall_support-nt.internal.h index 5c2e5554e..470fdfff0 100644 --- a/libc/calls/syscall_support-nt.internal.h +++ b/libc/calls/syscall_support-nt.internal.h @@ -7,7 +7,7 @@ bool isdirectory_nt(const char *); bool isregularfile_nt(const char *); bool issymlink_nt(const char *); bool32 ntsetprivilege(int64_t, const char16_t *, uint32_t); -char16_t *CreatePipeName(char16_t *); +char16_t *__create_pipe_name(char16_t *); int __mkntpath(const char *, char16_t[hasatleast PATH_MAX]); int __mkntpath2(const char *, char16_t[hasatleast PATH_MAX], int); int __mkntpathat(int, const char *, int, char16_t[hasatleast PATH_MAX]); diff --git a/libc/calls/winstdin1.c b/libc/calls/winstdin1.c new file mode 100644 index 000000000..bd82a32cc --- /dev/null +++ b/libc/calls/winstdin1.c @@ -0,0 +1,121 @@ +/*-*- 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/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/syscall_support-nt.internal.h" +#include "libc/intrin/strace.internal.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/ipc.h" +#include "libc/nt/runtime.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" +#include "libc/nt/thunk/msabi.h" + +__msabi extern typeof(CloseHandle) *const __imp_CloseHandle; +__msabi extern typeof(CreateFile) *const __imp_CreateFileW; +__msabi extern typeof(CreateNamedPipe) *const __imp_CreateNamedPipeW; +__msabi extern typeof(CreateSemaphore) *const __imp_CreateSemaphoreW; +__msabi extern typeof(CreateThread) *const __imp_CreateThread; +__msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; +__msabi extern typeof(ReadFile) *const __imp_ReadFile; +__msabi extern typeof(WaitForSingleObject) *const __imp_WaitForSingleObject; +__msabi extern typeof(WriteFile) *const __imp_WriteFile; + +__msabi static dontasan dontubsan dontinstrument textwindows uint32_t +WinStdinThread(void *lpParameter) { + char buf[4096]; + uint32_t i, got, wrote; + + // wait forever for semaphore to exceed 0 + // + // this semaphore is unlocked the first time read or poll happens. we + // need it so the prog has time to call functions like SetConsoleMode + // before we begin performing i/o. + __imp_WaitForSingleObject(g_fds.stdin.inisem, -1u); + __imp_CloseHandle(g_fds.stdin.inisem); + + // relay stdin to process + NTTRACE(" activated"); + for (;;) { + if (!__imp_ReadFile(g_fds.stdin.handle, buf, sizeof(buf), &got, 0)) { + NTTRACE(" read failed"); + goto Finish; + } + if (!got) { + NTTRACE(" end of file"); + goto Finish; + } + for (i = 0; i < got; i += wrote) { + if (!__imp_WriteFile(g_fds.stdin.writer, buf + i, got - i, &wrote, 0)) { + NTTRACE(" failed to write to pipe"); + goto Finish; + } + } + } + +Finish: + __imp_CloseHandle(g_fds.stdin.writer); + return 0; +} + +// this makes it possible for our read() implementation to periodically +// poll for signals while performing a blocking overlapped io operation +dontasan dontubsan dontinstrument textwindows void WinMainStdin(void) { + uint32_t mode; + char16_t pipename[64]; + int64_t hStdin, hWriter, hReader, hThread, hSemaphore; + hStdin = __imp_GetStdHandle(kNtStdInputHandle); + if (hStdin == kNtInvalidHandleValue) { + NTTRACE(" GetStdHandle failed"); + return; + } + // create non-inherited semaphore with initial value of 0 + hSemaphore = __imp_CreateSemaphoreW(0, 0, 1, 0); + if (!hSemaphore) { + NTTRACE(" CreateSemaphore failed"); + return; + } + __create_pipe_name(pipename); + hReader = __imp_CreateNamedPipeW( + pipename, kNtPipeAccessInbound | kNtFileFlagOverlapped, + kNtPipeTypeByte | kNtPipeReadmodeByte, 1, 4096, 4096, 0, 0); + if (hReader == kNtInvalidHandleValue) { + NTTRACE(" CreateNamedPipe failed"); + return; + } + hWriter = __imp_CreateFileW(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting, + kNtFileFlagOverlapped, 0); + if (hWriter == kNtInvalidHandleValue) { + NTTRACE(" CreateFile failed"); + return; + } + hThread = __imp_CreateThread(0, 65536, WinStdinThread, 0, 0, 0); + if (!hThread) { + NTTRACE(" CreateThread failed"); + return; + } + __imp_CloseHandle(hThread); + g_fds.stdin.handle = hStdin; + g_fds.stdin.reader = hReader; + g_fds.stdin.writer = hWriter; + g_fds.stdin.inisem = hSemaphore; +} diff --git a/libc/calls/winstdin2.c b/libc/calls/winstdin2.c new file mode 100644 index 000000000..cfe091acc --- /dev/null +++ b/libc/calls/winstdin2.c @@ -0,0 +1,32 @@ +/*-*- 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/calls.h" +#include "libc/calls/internal.h" +#include "libc/intrin/atomic.h" +#include "libc/nt/synchronization.h" + +textwindows int64_t __resolve_stdin_handle(int64_t handle) { + if (handle == g_fds.stdin.handle) { + if (!atomic_exchange(&g_fds.stdin.once, 1)) { + ReleaseSemaphore(g_fds.stdin.inisem, 1, 0); + } + handle = g_fds.stdin.reader; + } + return handle; +} diff --git a/libc/intrin/createthread.c b/libc/intrin/createthread.c index 5cd8774f2..f2ec9bd6a 100644 --- a/libc/intrin/createthread.c +++ b/libc/intrin/createthread.c @@ -31,10 +31,10 @@ __msabi extern typeof(CreateThread) *const __imp_CreateThread; * @return thread handle, or 0 on failure * @note this wrapper takes care of ABI, STRACE() */ -textwindows int64_t CreateThread( - struct NtSecurityAttributes *lpThreadAttributes, size_t dwStackSize, - NtThreadStartRoutine lpStartAddress, void *lpParameter, - uint32_t dwCreationFlags, uint32_t *opt_lpThreadId) { +textwindows int64_t +CreateThread(struct NtSecurityAttributes *lpThreadAttributes, + size_t dwStackSize, void *lpStartAddress, void *lpParameter, + uint32_t dwCreationFlags, uint32_t *opt_lpThreadId) { int64_t hHandle; hHandle = __imp_CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, opt_lpThreadId); diff --git a/libc/nt/thread.h b/libc/nt/thread.h index 72f836b2c..dc107e433 100644 --- a/libc/nt/thread.h +++ b/libc/nt/thread.h @@ -30,10 +30,8 @@ COSMOPOLITAN_C_START_ │ cosmopolitan § new technology » threads ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -typedef uint32_t (*NtThreadStartRoutine)(void *lpParameter); - int64_t CreateThread(struct NtSecurityAttributes *lpThreadAttributes, - size_t dwStackSize, NtThreadStartRoutine lpStartAddress, + size_t dwStackSize, void *lpStartAddress, void *lpParameter, uint32_t dwCreationFlags, uint32_t *opt_lpThreadId); diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 226f82362..c24a1b61b 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -183,6 +183,10 @@ textwindows void WinMainForked(void) { 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 varlen = GetEnvironmentVariable(u"_FORK", fvar, ARRAYLEN(fvar)); @@ -261,7 +265,7 @@ textwindows void WinMainForked(void) { // rewrap the stdin named pipe hack // since the handles closed on fork - struct Fds *fds = __veil("r", &g_fds); + fds->stdin = stdin; fds->p[0].handle = GetStdHandle(kNtStdInputHandle); fds->p[1].handle = GetStdHandle(kNtStdOutputHandle); fds->p[2].handle = GetStdHandle(kNtStdErrorHandle); @@ -310,7 +314,7 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) { tib = __tls_enabled ? __get_tls() : 0; if (!setjmp(jb)) { pid = untrackpid = __reservefd_unlocked(-1); - reader = CreateNamedPipe(CreatePipeName(pipename), kNtPipeAccessInbound, + reader = CreateNamedPipe(__create_pipe_name(pipename), kNtPipeAccessInbound, kNtPipeTypeByte | kNtPipeReadmodeByte, 1, PIPE_BUF, PIPE_BUF, 0, &kNtIsInheritable); writer = CreateFile(pipename, kNtGenericWrite, 0, 0, kNtOpenExisting, 0, 0); diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 1f141a5e1..8c5db241e 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -17,6 +17,7 @@ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/assert.h" +#include "libc/calls/internal.h" #include "libc/calls/state.internal.h" #include "libc/calls/syscall_support-nt.internal.h" #include "libc/dce.h" @@ -37,6 +38,7 @@ #include "libc/nt/enum/filesharemode.h" #include "libc/nt/enum/pageflags.h" #include "libc/nt/files.h" +#include "libc/nt/ipc.h" #include "libc/nt/memory.h" #include "libc/nt/pedef.internal.h" #include "libc/nt/process.h" @@ -44,6 +46,8 @@ #include "libc/nt/signals.h" #include "libc/nt/struct/ntexceptionpointers.h" #include "libc/nt/struct/teb.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" #include "libc/nt/thunk/msabi.h" #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.internal.h" @@ -54,6 +58,26 @@ #ifdef __x86_64__ +/** + * @fileoverview makes windows stdin handle capable of being poll'd + * + * 1. On Windows, there's no way to check how many bytes of input are + * available from the cmd.exe console. The only thing you can do is a + * blocking read that can't be interrupted. + * + * 2. On Windows, it's up to the parent process whether or not the + * handles it passes us are capable of non-blocking overlapped i/o + * reads (which we need for busy polling to check for interrupts). + * + * So we solve this by creating a thread which just does naive reads on + * standard input, and then relays the data to the process via a named + * pipe, which we explicitly creaete with overlapped i/o enabled. + * + * This code runs very early during process initialization, at the + * beginning of WinMain(). This module is only activated if the app + * links any one of: poll(), select(), or ioctl(FIONREAD). + */ + // clang-format off __msabi extern typeof(AddVectoredExceptionHandler) *const __imp_AddVectoredExceptionHandler; __msabi extern typeof(CloseHandle) *const __imp_CloseHandle; @@ -68,6 +92,7 @@ __msabi extern typeof(GetCurrentProcessId) *const __imp_GetCurrentProcessId; __msabi extern typeof(GetEnvironmentStrings) *const __imp_GetEnvironmentStringsW; __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle; __msabi extern typeof(MapViewOfFileEx) *const __imp_MapViewOfFileEx; +__msabi extern typeof(ReadFile) *const __imp_ReadFile; __msabi extern typeof(SetConsoleCP) *const __imp_SetConsoleCP; __msabi extern typeof(SetConsoleMode) *const __imp_SetConsoleMode; __msabi extern typeof(SetConsoleOutputCP) *const __imp_SetConsoleOutputCP; @@ -147,22 +172,8 @@ __msabi static textwindows int OnEarlyWinCrash(struct NtExceptionPointers *ep) { __builtin_unreachable(); } -// this makes it possible for our read() implementation to periodically -// poll for signals while performing a blocking overlapped io operation -__msabi static textwindows void ReopenConsoleForOverlappedIo(void) { - uint32_t mode; - int64_t hOld, hNew; - hOld = __imp_GetStdHandle(kNtStdInputHandle); - if (__imp_GetConsoleMode(hOld, &mode)) { - hNew = __imp_CreateFileW(u"CONIN$", kNtGenericRead | kNtGenericWrite, - kNtFileShareRead | kNtFileShareWrite, - &kNtIsInheritable, kNtOpenExisting, - kNtFileFlagOverlapped, 0); - if (hNew != kNtInvalidHandleValue) { - __imp_SetStdHandle(kNtStdInputHandle, hNew); - __imp_CloseHandle(hOld); - } - } +__msabi static textwindows bool ProxyStdin(void) { + return true; } // this ensures close(1) won't accidentally close(2) for example @@ -196,8 +207,6 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) { intptr_t stackaddr, allocaddr; version = NtGetPeb()->OSMajorVersion; __oldstack = (intptr_t)__builtin_frame_address(0); - ReopenConsoleForOverlappedIo(); - DeduplicateStdioHandles(); if ((intptr_t)v_ntsubsystem == kNtImageSubsystemWindowsCui && version >= 10) { rc = __imp_SetConsoleCP(kNtCpUtf8); NTTRACE("SetConsoleCP(kNtCpUtf8) → %hhhd", rc); @@ -268,6 +277,10 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance, os = _HOSTWINDOWS; /* madness https://news.ycombinator.com/item?id=21019722 */ kStartTsc = rdtsc(); __pid = __imp_GetCurrentProcessId(); + DeduplicateStdioHandles(); + if (_weaken(WinMainStdin)) { + _weaken(WinMainStdin)(); + } #if !IsTiny() __wincrashearly = __imp_AddVectoredExceptionHandler(1, (void *)OnEarlyWinCrash); diff --git a/libc/sock/socketpair-nt.c b/libc/sock/socketpair-nt.c index 8ec12a741..1800fc2d3 100644 --- a/libc/sock/socketpair-nt.c +++ b/libc/sock/socketpair-nt.c @@ -54,7 +54,7 @@ textwindows int sys_socketpair_nt(int family, int type, int proto, int sv[2]) { return eopnotsupp(); } - CreatePipeName(pipename); + __create_pipe_name(pipename); __fds_lock(); reader = __reservefd_unlocked(-1); writer = __reservefd_unlocked(-1); diff --git a/tool/build/apelink.c b/tool/build/apelink.c index 8e3b03aea..f178d0d05 100644 --- a/tool/build/apelink.c +++ b/tool/build/apelink.c @@ -1625,7 +1625,7 @@ static char *FinishGeneratingDosHeader(char *p) { p = WRITE16LE(p, 0); // 38 // terminate the shell quote started earlier in the ape magic. the big - // concern with shell script quoting is that binary content mimght get + // concern with shell script quoting, is that binary content might get // generated in the dos stub which has an ascii value that is the same // as the end of quote. using a longer terminator reduces it to a very // low order of probability. tacking on an unpredictable deterministic