mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-07 06:53:33 +00:00
Improve Windows Console I/O
- Blocking read operations on the Windows Console can now EINTR - Blocking read operations on Windows pipes now EINTR more reliably - setitimer() will no longer be inherited across fork() on Windows - It's now possible to use ECHO when the console is in raw mode - The ECHOCTL flag now works correctly on the Windows Console - The ICRNL flag now works correctly on the Windows Console - pread() and pwrite() will now raise ESPIPE on Windows - Opening /dev/tty on Windows is improved (untested) - Overlapped I/O is now implemented in a better way
This commit is contained in:
parent
decf216655
commit
33d280c8ba
34 changed files with 580 additions and 376 deletions
|
@ -17,28 +17,24 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/itimerval.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
/**
|
||||
* Asks for single-shot SIGALRM to be raise()'d after interval.
|
||||
*
|
||||
* @param seconds until we get signal, or 0 to reset previous alarm()
|
||||
* @return seconds previous alarm() had remaining, or -1u w/ errno
|
||||
* @see setitimer()
|
||||
* @param seconds is how long to wait before raising SIGALRM (which will
|
||||
* only happen once) or zero to clear any previously scheduled alarm
|
||||
* @return seconds that were remaining on the previously scheduled
|
||||
* alarm, or zero if there wasn't one (failure isn't possible)
|
||||
* @see setitimer() for a more powerful api
|
||||
* @asyncsignalsafe
|
||||
*/
|
||||
unsigned alarm(unsigned seconds) {
|
||||
struct itimerval it;
|
||||
bzero(&it, sizeof(it));
|
||||
it.it_value.tv_sec = seconds;
|
||||
npassert(!setitimer(ITIMER_REAL, &it, &it));
|
||||
if (!it.it_value.tv_sec && !it.it_value.tv_usec) {
|
||||
return 0;
|
||||
} else {
|
||||
return MIN(1, it.it_value.tv_sec + (it.it_value.tv_usec > 5000000));
|
||||
}
|
||||
struct itimerval it, old;
|
||||
it.it_value = timeval_fromseconds(seconds);
|
||||
it.it_interval = timeval_zero;
|
||||
npassert(!setitimer(ITIMER_REAL, &it, &old));
|
||||
return timeval_toseconds(old.it_value);
|
||||
}
|
||||
|
|
|
@ -41,9 +41,15 @@ textwindows int _check_interrupts(int sigops, struct Fd *fd) {
|
|||
errno = rc;
|
||||
return -1;
|
||||
}
|
||||
if (__tls_enabled) {
|
||||
__get_tls()->tib_flags |= TIB_FLAG_TIME_CRITICAL;
|
||||
}
|
||||
if (_weaken(_check_sigalrm)) {
|
||||
_weaken(_check_sigalrm)();
|
||||
}
|
||||
if (__tls_enabled) {
|
||||
__get_tls()->tib_flags &= ~TIB_FLAG_TIME_CRITICAL;
|
||||
}
|
||||
if (!__tls_enabled || !(__get_tls()->tib_flags & TIB_FLAG_TIME_CRITICAL)) {
|
||||
if (!(sigops & kSigOpNochld) && _weaken(_check_sigchld)) {
|
||||
_weaken(_check_sigchld)();
|
||||
|
|
|
@ -23,21 +23,28 @@
|
|||
#include "libc/calls/syscall-nt.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/fileno.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
|
||||
static textwindows int sys_open_nt_impl(int dirfd, const char *path,
|
||||
uint32_t flags, int32_t mode) {
|
||||
static textwindows int64_t sys_open_nt_impl(int dirfd, const char *path,
|
||||
uint32_t flags, int32_t mode,
|
||||
uint32_t extra_attr) {
|
||||
char16_t path16[PATH_MAX];
|
||||
uint32_t perm, share, disp, attr;
|
||||
if (__mkntpathat(dirfd, path, flags, path16) == -1) return -1;
|
||||
if (GetNtOpenFlags(flags, mode, &perm, &share, &disp, &attr) == -1) return -1;
|
||||
return __fix_enotdir(
|
||||
CreateFile(path16, perm, share, &kNtIsInheritable, disp, attr, 0),
|
||||
path16);
|
||||
if (__mkntpathat(dirfd, path, flags, path16) == -1) {
|
||||
return kNtInvalidHandleValue;
|
||||
}
|
||||
if (GetNtOpenFlags(flags, mode, &perm, &share, &disp, &attr) == -1) {
|
||||
return kNtInvalidHandleValue;
|
||||
}
|
||||
return __fix_enotdir(CreateFile(path16, perm, share, &kNtIsInheritable, disp,
|
||||
attr | extra_attr, 0),
|
||||
path16);
|
||||
}
|
||||
|
||||
static textwindows int sys_open_nt_console(int dirfd,
|
||||
|
@ -49,10 +56,10 @@ static textwindows int sys_open_nt_console(int dirfd,
|
|||
g_fds.p[fd].handle = g_fds.p[STDIN_FILENO].handle;
|
||||
g_fds.p[fd].extra = g_fds.p[STDOUT_FILENO].handle;
|
||||
} else if ((g_fds.p[fd].handle = sys_open_nt_impl(
|
||||
dirfd, mp->conin, (flags & ~O_ACCMODE) | O_RDONLY, mode)) !=
|
||||
-1) {
|
||||
g_fds.p[fd].extra = sys_open_nt_impl(dirfd, mp->conout,
|
||||
(flags & ~O_ACCMODE) | O_WRONLY, mode);
|
||||
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, 0);
|
||||
npassert(g_fds.p[fd].extra != -1);
|
||||
} else {
|
||||
return -1;
|
||||
|
@ -66,7 +73,8 @@ static textwindows int sys_open_nt_console(int dirfd,
|
|||
static textwindows int sys_open_nt_file(int dirfd, const char *file,
|
||||
uint32_t flags, int32_t mode,
|
||||
size_t fd) {
|
||||
if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode)) != -1) {
|
||||
if ((g_fds.p[fd].handle = sys_open_nt_impl(dirfd, file, flags, mode, 0)) !=
|
||||
-1) {
|
||||
g_fds.p[fd].kind = kFdFile;
|
||||
g_fds.p[fd].flags = flags;
|
||||
g_fds.p[fd].mode = mode;
|
||||
|
|
|
@ -25,51 +25,126 @@
|
|||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/calls/syscall_support-nt.internal.h"
|
||||
#include "libc/calls/wincrash.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/enum/filetype.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#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/overlapped.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static textwindows void sys_read_nt_abort(int64_t handle,
|
||||
struct NtOverlapped *overlapped) {
|
||||
unassert(CancelIoEx(handle, overlapped) ||
|
||||
GetLastError() == kNtErrorNotFound);
|
||||
}
|
||||
|
||||
static textwindows void MungeTerminalInput(struct Fd *fd, char *p, size_t n) {
|
||||
if (!(fd->ttymagic & kFdTtyNoCr2Nl)) {
|
||||
size_t i;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (p[i] == '\r') {
|
||||
p[i] = '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Manual CMD.EXE echoing for when !ICANON && ECHO is the case.
|
||||
static textwindows void EchoTerminalInput(struct Fd *fd, char *p, size_t n) {
|
||||
int64_t hOutput;
|
||||
if (fd->kind == kFdConsole) {
|
||||
hOutput = fd->extra;
|
||||
} else {
|
||||
hOutput = g_fds.p[1].handle;
|
||||
}
|
||||
if (fd->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 ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
|
||||
size_t size, int64_t offset) {
|
||||
|
||||
// try to poll rather than block
|
||||
uint32_t avail;
|
||||
if (GetFileType(fd->handle) == kNtFileTypePipe) {
|
||||
for (;;) {
|
||||
if (!PeekNamedPipe(fd->handle, 0, 0, 0, &avail, 0)) break;
|
||||
if (avail) break;
|
||||
POLLTRACE("sys_read_nt polling");
|
||||
if (SleepEx(__SIG_POLLING_INTERVAL_MS, true) == kNtWaitIoCompletion) {
|
||||
POLLTRACE("IOCP EINTR");
|
||||
}
|
||||
if (fd->flags & O_NONBLOCK) {
|
||||
return eagain();
|
||||
}
|
||||
if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
|
||||
POLLTRACE("sys_read_nt interrupted");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
POLLTRACE("sys_read_nt ready to read");
|
||||
}
|
||||
|
||||
// perform the read i/o operation
|
||||
bool32 ok;
|
||||
uint32_t got;
|
||||
int filetype;
|
||||
int abort_errno = EAGAIN;
|
||||
size = MIN(size, 0x7ffff000);
|
||||
if (offset == -1) {
|
||||
// perform simple blocking read
|
||||
filetype = GetFileType(fd->handle);
|
||||
if (filetype == kNtFileTypeChar || filetype == kNtFileTypePipe) {
|
||||
struct NtOverlapped overlap = {0};
|
||||
if (offset != -1) {
|
||||
// pread() and pwrite() should not be called on a pipe or tty
|
||||
return espipe();
|
||||
}
|
||||
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);
|
||||
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);
|
||||
} else if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
|
||||
Interrupted:
|
||||
abort_errno = errno;
|
||||
sys_read_nt_abort(fd->handle, &overlap);
|
||||
} else {
|
||||
for (;;) {
|
||||
uint32_t i;
|
||||
i = WaitForSingleObject(overlap.hEvent, __SIG_POLLING_INTERVAL_MS);
|
||||
if (i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(kSigOpRestartable, g_fds.p)) {
|
||||
goto Interrupted;
|
||||
}
|
||||
} else {
|
||||
npassert(!i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
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);
|
||||
}
|
||||
CloseHandle(overlap.hEvent);
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else if (offset == -1) {
|
||||
// perform simple blocking file read
|
||||
ok = ReadFile(fd->handle, data, size, &got, 0);
|
||||
} else {
|
||||
// perform pread()-style read at particular file offset
|
||||
// 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)) {
|
||||
|
@ -84,6 +159,12 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
|
|||
unassert(SetFilePointerEx(fd->handle, position, 0, SEEK_SET));
|
||||
}
|
||||
if (ok) {
|
||||
if (fd->ttymagic & kFdTtyMunging) {
|
||||
MungeTerminalInput(fd, data, got);
|
||||
}
|
||||
if (fd->ttymagic & kFdTtyEchoing) {
|
||||
EchoTerminalInput(fd, data, got);
|
||||
}
|
||||
return got;
|
||||
}
|
||||
|
||||
|
@ -94,6 +175,9 @@ static textwindows ssize_t sys_read_nt_impl(struct Fd *fd, void *data,
|
|||
return 0; //
|
||||
case kNtErrorAccessDenied: // read doesn't return EACCESS
|
||||
return ebadf(); //
|
||||
case kNtErrorOperationAborted:
|
||||
errno = abort_errno;
|
||||
return -1;
|
||||
default:
|
||||
return __winerr();
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/struct/itimerval.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
|
@ -43,21 +43,33 @@ textwindows void _check_sigalrm(void) {
|
|||
__sig_add(0, SIGALRM, SI_TIMER);
|
||||
}
|
||||
|
||||
textwindows void sys_setitimer_nt_reset(void) {
|
||||
// this function is called by fork(), because
|
||||
// timers aren't inherited by forked subprocesses
|
||||
bzero(&g_setitimer, sizeof(g_setitimer));
|
||||
}
|
||||
|
||||
textwindows int sys_setitimer_nt(int which, const struct itimerval *neu,
|
||||
struct itimerval *old) {
|
||||
struct itimerval config;
|
||||
if (which != ITIMER_REAL || (neu && (!timeval_isvalid(neu->it_value) ||
|
||||
!timeval_isvalid(neu->it_interval)))) {
|
||||
return einval();
|
||||
}
|
||||
if (neu) {
|
||||
// POSIX defines setitimer() with the restrict keyword but let's
|
||||
// accommodate the usage setitimer(ITIMER_REAL, &it, &it) anyway
|
||||
config = *neu;
|
||||
}
|
||||
if (old) {
|
||||
old->it_interval = g_setitimer.it_interval;
|
||||
old->it_value = timeval_subz(g_setitimer.it_value, timeval_real());
|
||||
}
|
||||
if (neu) {
|
||||
g_setitimer.it_interval = neu->it_interval;
|
||||
g_setitimer.it_value = timeval_iszero(neu->it_value)
|
||||
? timeval_zero
|
||||
: timeval_add(timeval_real(), neu->it_value);
|
||||
if (!timeval_iszero(config.it_value)) {
|
||||
config.it_value = timeval_add(config.it_value, timeval_real());
|
||||
}
|
||||
g_setitimer = config;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -31,17 +31,17 @@
|
|||
* Raise SIGALRM every 1.5s:
|
||||
*
|
||||
* sigaction(SIGALRM,
|
||||
* &(struct sigaction){.sa_sigaction = _missingno},
|
||||
* &(struct sigaction){.sa_handler = OnSigalrm},
|
||||
* NULL);
|
||||
* setitimer(ITIMER_REAL,
|
||||
* &(const struct itimerval){{1, 500000},
|
||||
* {1, 500000}},
|
||||
* NULL);
|
||||
*
|
||||
* Set single-shot 50ms timer callback to interrupt laggy connect():
|
||||
* Single-shot alarm to interrupt connect() after 50ms:
|
||||
*
|
||||
* sigaction(SIGALRM,
|
||||
* &(struct sigaction){.sa_sigaction = _missingno,
|
||||
* &(struct sigaction){.sa_handler = OnSigalrm,
|
||||
* .sa_flags = SA_RESETHAND},
|
||||
* NULL);
|
||||
* setitimer(ITIMER_REAL,
|
||||
|
@ -49,11 +49,14 @@
|
|||
* NULL);
|
||||
* if (connect(...) == -1 && errno == EINTR) { ... }
|
||||
*
|
||||
* Disarm timer:
|
||||
* Disarm existing timer:
|
||||
*
|
||||
* setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL);
|
||||
*
|
||||
* Be sure to check for EINTR on your i/o calls, for best low latency.
|
||||
* If the goal is to use alarms to interrupt blocking i/o routines, e.g.
|
||||
* read(), connect(), etc. then it's important to install the signal
|
||||
* handler using sigaction() rather than signal(), because the latter
|
||||
* sets the `SA_RESTART` flag.
|
||||
*
|
||||
* Timers are not inherited across fork.
|
||||
*
|
||||
|
|
|
@ -13,13 +13,19 @@ COSMOPOLITAN_C_START_
|
|||
#define kFdEpoll 7
|
||||
#define kFdReserved 8
|
||||
|
||||
#define kFdTtyEchoing 1 /* read()→write() (ECHO && !ICANON) */
|
||||
#define kFdTtyEchoRaw 2 /* don't ^X visualize control codes */
|
||||
#define kFdTtyMunging 4 /* enable input / output remappings */
|
||||
#define kFdTtyNoCr2Nl 8 /* don't map \r → \n (a.k.a !ICRNL) */
|
||||
|
||||
struct Fd {
|
||||
int kind;
|
||||
char kind;
|
||||
bool zombie;
|
||||
char ttymagic;
|
||||
unsigned flags;
|
||||
unsigned mode;
|
||||
int64_t handle;
|
||||
int64_t extra;
|
||||
bool zombie;
|
||||
};
|
||||
|
||||
struct Fds {
|
||||
|
|
|
@ -29,7 +29,11 @@ struct timeval timeval_frommillis(int64_t) pureconst;
|
|||
struct timeval timeval_add(struct timeval, struct timeval) pureconst;
|
||||
struct timeval timeval_sub(struct timeval, struct timeval) pureconst;
|
||||
struct timeval timeval_subz(struct timeval, struct timeval) pureconst;
|
||||
int64_t timeval_toseconds(struct timeval);
|
||||
struct timeval timespec_totimeval(struct timespec) pureconst;
|
||||
static inline struct timeval timeval_fromseconds(int64_t __x) {
|
||||
return (struct timeval){__x};
|
||||
}
|
||||
static inline struct timespec timeval_totimespec(struct timeval __tv) {
|
||||
return (struct timespec){__tv.tv_sec, __tv.tv_usec * 1000};
|
||||
}
|
||||
|
|
|
@ -18,17 +18,20 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/fd.internal.h"
|
||||
#include "libc/calls/struct/termios.h"
|
||||
#include "libc/calls/ttydefaults.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/enum/consolemodeflags.h"
|
||||
#include "libc/nt/struct/consolescreenbufferinfoex.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/baud.internal.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int tcgetattr_nt(int ignored, struct termios *tio) {
|
||||
int ttymagic;
|
||||
int64_t in, out;
|
||||
bool32 inok, outok;
|
||||
uint32_t inmode, outmode;
|
||||
|
@ -37,34 +40,43 @@ textwindows int tcgetattr_nt(int ignored, struct termios *tio) {
|
|||
if (inok | outok) {
|
||||
bzero(tio, sizeof(*tio));
|
||||
|
||||
tio->c_cflag |= CS8;
|
||||
|
||||
tio->c_cc[VMIN] = 1;
|
||||
tio->c_cc[VINTR] = CTRL('C');
|
||||
tio->c_cc[VQUIT] = CTRL('\\');
|
||||
tio->c_cc[VERASE] = CTRL('?');
|
||||
tio->c_cc[VWERASE] = CTRL('?'); // windows swaps these :'(
|
||||
tio->c_cc[VERASE] = CTRL('H'); // windows swaps these :'(
|
||||
tio->c_cc[VKILL] = CTRL('U');
|
||||
tio->c_cc[VEOF] = CTRL('D');
|
||||
tio->c_cc[VMIN] = CTRL('A');
|
||||
tio->c_cc[VSTART] = CTRL('Q');
|
||||
tio->c_cc[VSTOP] = CTRL('S');
|
||||
tio->c_cc[VSUSP] = CTRL('Z');
|
||||
tio->c_cc[VSTART] = _POSIX_VDISABLE;
|
||||
tio->c_cc[VSTOP] = _POSIX_VDISABLE;
|
||||
tio->c_cc[VSUSP] = _POSIX_VDISABLE;
|
||||
tio->c_cc[VREPRINT] = CTRL('R');
|
||||
tio->c_cc[VDISCARD] = CTRL('O');
|
||||
tio->c_cc[VWERASE] = CTRL('W');
|
||||
tio->c_cc[VLNEXT] = CTRL('V');
|
||||
|
||||
tio->c_iflag = IUTF8;
|
||||
tio->c_lflag = ECHOE;
|
||||
tio->c_cflag = CS8;
|
||||
tio->_c_ispeed = B38400;
|
||||
tio->_c_ospeed = B38400;
|
||||
|
||||
if (inok) {
|
||||
ttymagic = g_fds.p[0].ttymagic;
|
||||
if (inmode & kNtEnableLineInput) {
|
||||
tio->c_lflag |= ICANON;
|
||||
}
|
||||
if (inmode & kNtEnableEchoInput) {
|
||||
if ((inmode & kNtEnableEchoInput) || (ttymagic & kFdTtyEchoing)) {
|
||||
tio->c_lflag |= ECHO;
|
||||
}
|
||||
if (!(ttymagic & kFdTtyEchoRaw)) {
|
||||
tio->c_lflag |= ECHOCTL;
|
||||
}
|
||||
if (!(ttymagic & kFdTtyNoCr2Nl)) {
|
||||
tio->c_iflag |= ICRNL;
|
||||
}
|
||||
if (inmode & kNtEnableProcessedInput) {
|
||||
tio->c_lflag |= IEXTEN | ISIG;
|
||||
if (tio->c_lflag | ECHO) {
|
||||
tio->c_lflag |= ECHOE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,57 +29,86 @@
|
|||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
textwindows int tcsetattr_nt(int ignored, int opt, const struct termios *tio) {
|
||||
int64_t in, out;
|
||||
bool32 ok, inok, outok;
|
||||
textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
||||
bool32 ok;
|
||||
int ttymagic;
|
||||
int64_t hInput, hOutput;
|
||||
uint32_t inmode, outmode;
|
||||
inok = GetConsoleMode((in = __getfdhandleactual(0)), &inmode);
|
||||
outok = GetConsoleMode((out = __getfdhandleactual(1)), &outmode);
|
||||
if (inok | outok) {
|
||||
if (__isfdkind(fd, kFdConsole)) {
|
||||
// program manually opened /dev/tty in O_RDWR mode for cmd.exe
|
||||
hInput = g_fds.p[fd].handle;
|
||||
hOutput = g_fds.p[fd].extra;
|
||||
} else if (fd == 0 || fd == 1) {
|
||||
// otherwise just assume cmd.exe console stdio
|
||||
// there's no serial port support yet
|
||||
hInput = g_fds.p[0].handle;
|
||||
hOutput = g_fds.p[1].handle;
|
||||
fd = 0;
|
||||
} else {
|
||||
STRACE("tcsetattr(fd) must be 0, 1, or open'd /dev/tty");
|
||||
return enotty();
|
||||
}
|
||||
if (GetConsoleMode(hInput, &inmode) && GetConsoleMode(hOutput, &outmode)) {
|
||||
|
||||
if (inok) {
|
||||
if (opt == TCSAFLUSH) {
|
||||
FlushConsoleInputBuffer(in);
|
||||
if (opt == TCSAFLUSH) {
|
||||
FlushConsoleInputBuffer(hInput);
|
||||
}
|
||||
inmode &=
|
||||
~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput);
|
||||
inmode |= kNtEnableWindowInput;
|
||||
ttymagic = 0;
|
||||
if (tio->c_lflag & ICANON) {
|
||||
inmode |= kNtEnableLineInput;
|
||||
} else {
|
||||
ttymagic |= kFdTtyMunging;
|
||||
if (tio->c_cc[VMIN] != 1) {
|
||||
STRACE("tcsetattr c_cc[VMIN] must be 1 on Windows");
|
||||
return einval();
|
||||
}
|
||||
inmode &=
|
||||
~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput);
|
||||
inmode |= kNtEnableWindowInput;
|
||||
}
|
||||
if (!(tio->c_iflag & ICRNL)) {
|
||||
ttymagic |= kFdTtyNoCr2Nl;
|
||||
}
|
||||
if (!(tio->c_lflag & ECHOCTL)) {
|
||||
ttymagic |= kFdTtyEchoRaw;
|
||||
}
|
||||
if (tio->c_lflag & ECHO) {
|
||||
// "kNtEnableEchoInput can be used only if the
|
||||
// kNtEnableLineInput mode is also enabled." -MSDN
|
||||
if (tio->c_lflag & ICANON) {
|
||||
inmode |= kNtEnableLineInput;
|
||||
inmode |= kNtEnableEchoInput;
|
||||
} else {
|
||||
// If ECHO is enabled in raw mode, then read(0) needs to
|
||||
// magically write(1) to simulate echoing. This normally
|
||||
// visualizes control codes, e.g. \r → ^M unless ECHOCTL
|
||||
// hasn't been specified.
|
||||
ttymagic |= kFdTtyEchoing;
|
||||
}
|
||||
if (tio->c_lflag & ECHO) {
|
||||
/*
|
||||
* kNtEnableEchoInput can be used only if the ENABLE_LINE_INPUT mode
|
||||
* is also enabled. --Quoth MSDN
|
||||
*/
|
||||
inmode |= kNtEnableEchoInput | kNtEnableLineInput;
|
||||
}
|
||||
if (tio->c_lflag & (IEXTEN | ISIG)) {
|
||||
inmode |= kNtEnableProcessedInput;
|
||||
}
|
||||
if (IsAtLeastWindows10()) {
|
||||
inmode |= kNtEnableVirtualTerminalInput;
|
||||
}
|
||||
ok = SetConsoleMode(in, inmode);
|
||||
(void)ok;
|
||||
NTTRACE("SetConsoleMode(%p, %s) → %hhhd", in,
|
||||
DescribeNtConsoleInFlags(inmode), ok);
|
||||
}
|
||||
if (tio->c_lflag & (IEXTEN | ISIG)) {
|
||||
inmode |= kNtEnableProcessedInput;
|
||||
}
|
||||
if (IsAtLeastWindows10()) {
|
||||
inmode |= kNtEnableVirtualTerminalInput;
|
||||
}
|
||||
g_fds.p[fd].ttymagic = ttymagic;
|
||||
ok = SetConsoleMode(hInput, inmode);
|
||||
(void)ok;
|
||||
NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hInput,
|
||||
DescribeNtConsoleInFlags(inmode), ok);
|
||||
|
||||
if (outok) {
|
||||
outmode &= ~(kNtDisableNewlineAutoReturn);
|
||||
outmode |= kNtEnableProcessedOutput;
|
||||
if (!(tio->c_oflag & ONLCR)) {
|
||||
outmode |= kNtDisableNewlineAutoReturn;
|
||||
}
|
||||
if (IsAtLeastWindows10()) {
|
||||
outmode |= kNtEnableVirtualTerminalProcessing;
|
||||
}
|
||||
ok = SetConsoleMode(out, outmode);
|
||||
(void)ok;
|
||||
NTTRACE("SetConsoleMode(%p, %s) → %hhhd", out,
|
||||
DescribeNtConsoleOutFlags(outmode), ok);
|
||||
outmode &= ~kNtDisableNewlineAutoReturn;
|
||||
outmode |= kNtEnableProcessedOutput;
|
||||
if (!(tio->c_oflag & ONLCR)) {
|
||||
outmode |= kNtDisableNewlineAutoReturn;
|
||||
}
|
||||
if (IsAtLeastWindows10()) {
|
||||
outmode |= kNtEnableVirtualTerminalProcessing;
|
||||
}
|
||||
ok = SetConsoleMode(hOutput, outmode);
|
||||
(void)ok;
|
||||
NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hOutput,
|
||||
DescribeNtConsoleOutFlags(outmode), ok);
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ 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 │
|
||||
|
@ -16,14 +16,21 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/dce.h"
|
||||
#include "libc/nt/version.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/limits.h"
|
||||
|
||||
/**
|
||||
* Returns true if we're running at least Windows 10.
|
||||
* Converts timeval to seconds.
|
||||
*
|
||||
* This function may only be called if IsWindows() is true.
|
||||
* This function uses ceil rounding, so 1µs becomes 1s. The addition
|
||||
* operation is saturating so timeval_toseconds(timeval_max) returns
|
||||
* INT64_MAX.
|
||||
*/
|
||||
privileged bool(IsAtLeastWindows10)(void) {
|
||||
return IsAtLeastWindows10();
|
||||
int64_t timeval_toseconds(struct timeval tv) {
|
||||
int64_t secs;
|
||||
secs = tv.tv_sec;
|
||||
if (tv.tv_usec && secs < INT64_MAX) {
|
||||
++secs;
|
||||
}
|
||||
return secs;
|
||||
}
|
|
@ -42,7 +42,11 @@ static textwindows ssize_t sys_write_nt_impl(int fd, void *data, size_t size,
|
|||
bool32 ok;
|
||||
uint32_t sent;
|
||||
int64_t handle;
|
||||
handle = g_fds.p[fd].handle;
|
||||
if (g_fds.p[fd].kind == kFdConsole) {
|
||||
handle = g_fds.p[fd].extra; // get write end of console
|
||||
} else {
|
||||
handle = g_fds.p[fd].handle;
|
||||
}
|
||||
size = MIN(size, 0x7ffff000);
|
||||
if (offset == -1) {
|
||||
// perform simple blocking write
|
||||
|
|
|
@ -1,38 +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 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/syscall_support-nt.internal.h"
|
||||
#include "libc/intrin/describentoverlapped.internal.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/struct/overlapped.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
|
||||
__msabi extern typeof(CancelIoEx) *const __imp_CancelIoEx;
|
||||
|
||||
/**
|
||||
* Cancels Windows i/o operation.
|
||||
*/
|
||||
bool32 CancelIoEx(int64_t hFile, struct NtOverlapped *opt_lpOverlapped) {
|
||||
bool32 ok;
|
||||
ok = __imp_CancelIoEx(hFile, opt_lpOverlapped);
|
||||
if (!ok) __winerr();
|
||||
NTTRACE("CancelIoEx(%ld, %s) → %hhhd% m", hFile,
|
||||
DescribeNtOverlapped(opt_lpOverlapped), ok);
|
||||
return ok;
|
||||
}
|
|
@ -146,7 +146,6 @@ o/$(MODE)/libc/intrin/exit1.greg.o \
|
|||
o/$(MODE)/libc/intrin/wsarecv.o \
|
||||
o/$(MODE)/libc/intrin/wsarecvfrom.o \
|
||||
o/$(MODE)/libc/intrin/createfile.o \
|
||||
o/$(MODE)/libc/intrin/cancelioex.o \
|
||||
o/$(MODE)/libc/intrin/reopenfile.o \
|
||||
o/$(MODE)/libc/intrin/deletefile.o \
|
||||
o/$(MODE)/libc/intrin/createpipe.o \
|
||||
|
|
|
@ -29,10 +29,10 @@ __msabi extern typeof(GetStdHandle) *const __imp_GetStdHandle;
|
|||
|
||||
extern uint32_t __pid_exec;
|
||||
|
||||
const signed char kConsoleHandles[3] = {
|
||||
kNtStdInputHandle,
|
||||
kNtStdOutputHandle,
|
||||
kNtStdErrorHandle,
|
||||
const signed char kNtConsoleHandles[3] = {
|
||||
(signed char)kNtStdInputHandle,
|
||||
(signed char)kNtStdOutputHandle,
|
||||
(signed char)kNtStdErrorHandle,
|
||||
};
|
||||
|
||||
// Puts cmd.exe gui back the way it was.
|
||||
|
@ -41,7 +41,7 @@ void _restorewintty(void) {
|
|||
if (!IsWindows()) return;
|
||||
if (__imp_GetCurrentProcessId() != __pid_exec) return;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
__imp_SetConsoleMode(__imp_GetStdHandle(kConsoleHandles[i]),
|
||||
__imp_SetConsoleMode(__imp_GetStdHandle(kNtConsoleHandles[i]),
|
||||
__ntconsolemode[i]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_NT_ENUM_WAIT_H_
|
||||
#define COSMOPOLITAN_LIBC_NT_ENUM_WAIT_H_
|
||||
|
||||
#define kNtWaitFailed 0xffffffffu
|
||||
#define kNtWaitTimeout 0x00000102u
|
||||
#define kNtWaitFailed 0xffffffffu
|
||||
#define kNtWaitTimeout 0x00000102u
|
||||
#define kNtWaitAbandoned 0x00000080u
|
||||
|
||||
#endif /* COSMOPOLITAN_LIBC_NT_ENUM_WAIT_H_ */
|
||||
|
|
|
@ -55,13 +55,17 @@ int64_t RegisterEventSource(const char16_t *lpUNCServerName,
|
|||
const char16_t *lpSourceName);
|
||||
int32_t DeregisterEventSource(uint64_t handle);
|
||||
|
||||
int64_t CreateEvent(struct NtSecurityAttributes *lpEventAttributes,
|
||||
int64_t CreateEvent(struct NtSecurityAttributes *opt_lpEventAttributes,
|
||||
bool32 bManualReset, bool32 bInitialState,
|
||||
const char16_t *lpName);
|
||||
const char16_t *opt_lpName);
|
||||
int64_t CreateEventEx(struct NtSecurityAttributes *lpEventAttributes,
|
||||
const char16_t *lpName, uint32_t dwFlags,
|
||||
uint32_t dwDesiredAccess);
|
||||
|
||||
int32_t SetEvent(int64_t hEvent);
|
||||
int32_t ResetEvent(int64_t hEvent);
|
||||
int32_t PulseEvent(int64_t hEvent);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_NT_EVENTS_H_ */
|
||||
|
|
|
@ -1,2 +1,18 @@
|
|||
#include "libc/nt/codegen.h"
|
||||
.imp kernel32,__imp_CancelIoEx,CancelIoEx
|
||||
|
||||
.text.windows
|
||||
.ftrace1
|
||||
CancelIoEx:
|
||||
.ftrace2
|
||||
#ifdef __x86_64__
|
||||
push %rbp
|
||||
mov %rsp,%rbp
|
||||
mov __imp_CancelIoEx(%rip),%rax
|
||||
jmp __sysv2nt
|
||||
#elif defined(__aarch64__)
|
||||
mov x0,#0
|
||||
ret
|
||||
#endif
|
||||
.endfn CancelIoEx,globl
|
||||
.previous
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
# KERNEL32.DLL
|
||||
#
|
||||
# Name Actual DLL Arity
|
||||
imp '' CancelIoEx kernel32 2
|
||||
imp '' CloseHandle kernel32 1
|
||||
imp '' CreateDirectoryW kernel32 2
|
||||
imp '' CreateFileMappingNumaW kernel32 7
|
||||
|
@ -53,6 +52,7 @@ imp 'AttachConsole' AttachConsole kernel32 1
|
|||
imp 'CallNamedPipe' CallNamedPipeW kernel32 7
|
||||
imp 'CallNamedPipeA' CallNamedPipeA kernel32 7
|
||||
imp 'CancelIo' CancelIo kernel32 1
|
||||
imp 'CancelIoEx' CancelIoEx kernel32 2
|
||||
imp 'CancelSynchronousIo' CancelSynchronousIo kernel32 1
|
||||
imp 'CheckRemoteDebuggerPresent' CheckRemoteDebuggerPresent kernel32 2
|
||||
imp 'ClearCommBreak' ClearCommBreak kernel32 1
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
|
||||
#define kNtCpUtf8 65001
|
||||
#define kNtInvalidHandleValue -1L
|
||||
#define kNtStdInputHandle -10L
|
||||
#define kNtStdOutputHandle -11L
|
||||
#define kNtStdErrorHandle -12L
|
||||
#define kNtStdInputHandle -10u
|
||||
#define kNtStdOutputHandle -11u
|
||||
#define kNtStdErrorHandle -12u
|
||||
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
@ -36,8 +36,8 @@ int64_t GetCurrentProcess(void) pureconst;
|
|||
void ExitProcess(uint32_t uExitCode) wontreturn;
|
||||
uint32_t GetLastError(void) nosideeffect;
|
||||
bool32 CloseHandle(int64_t hObject) dontthrow nocallback;
|
||||
intptr_t GetStdHandle(int64_t nStdHandle) nosideeffect;
|
||||
bool32 SetStdHandle(int64_t nStdHandle, int64_t hHandle);
|
||||
intptr_t GetStdHandle(uint32_t nStdHandle) nosideeffect;
|
||||
bool32 SetStdHandle(uint32_t nStdHandle, int64_t hHandle);
|
||||
bool32 SetDefaultDllDirectories(unsigned dirflags);
|
||||
bool32 RtlGenRandom(void *RandomBuffer, uint32_t RandomBufferLength);
|
||||
uint32_t GetModuleFileName(int64_t hModule, char16_t *lpFilename,
|
||||
|
|
|
@ -85,10 +85,6 @@ int64_t CreateSemaphore(struct NtSecurityAttributes *opt_lpSemaphoreAttributes,
|
|||
uint32_t lInitialCount, uint32_t lMaximumCount,
|
||||
const char16_t *opt_lpName);
|
||||
|
||||
int32_t SetEvent(int64_t hEvent);
|
||||
int32_t ResetEvent(int64_t hEvent);
|
||||
int32_t PulseEvent(int64_t hEvent);
|
||||
|
||||
int32_t ReleaseMutex(int64_t hMutex);
|
||||
int32_t ReleaseSemaphore(int64_t hSemaphore, int32_t lReleaseCount,
|
||||
int *lpPreviousCount);
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
bool IsAtLeastWindows10(void) pureconst;
|
||||
bool32 GetVersionEx(struct NtOsVersionInfo *lpVersionInformation);
|
||||
|
||||
#if defined(__GNUC__) && !defined(__STRICT_ANSI__) && defined(__x86_64__)
|
||||
|
|
|
@ -68,6 +68,7 @@ __static_yoink("_check_sigchld");
|
|||
|
||||
extern int64_t __wincrashearly;
|
||||
bool32 __onntconsoleevent_nt(uint32_t);
|
||||
void sys_setitimer_nt_reset(void);
|
||||
void kmalloc_unlock(void);
|
||||
|
||||
static textwindows wontreturn void AbortFork(const char *func) {
|
||||
|
@ -396,6 +397,10 @@ textwindows int sys_fork_nt(uint32_t dwCreationFlags) {
|
|||
if (ftrace_stackdigs) {
|
||||
_weaken(__hook)(_weaken(ftrace_hook), _weaken(GetSymbolTable)());
|
||||
}
|
||||
// reset alarms
|
||||
if (_weaken(sys_setitimer_nt_reset)) {
|
||||
_weaken(sys_setitimer_nt_reset)();
|
||||
}
|
||||
}
|
||||
if (untrackpid != -1) {
|
||||
__releasefd(untrackpid);
|
||||
|
|
|
@ -95,6 +95,16 @@ static dontasan void PrintDependencies(const char *prologue) {
|
|||
static dontasan void Print(const char *prologue) {
|
||||
}
|
||||
|
||||
static dontasan const char *ConvertCcToStr(int cc) {
|
||||
if (cc == _POSIX_VDISABLE) {
|
||||
return "_POSIX_VDISABLE";
|
||||
} else {
|
||||
static char buf[8] = "CTRL-";
|
||||
buf[5] = CTRL(cc);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints lots of information about this process, e.g.
|
||||
*
|
||||
|
@ -558,67 +568,6 @@ dontasan textstartup void __printargs(const char *prologue) {
|
|||
} else if ((termios.c_cflag & CSIZE) == CS8) {
|
||||
kprintf(" CS8");
|
||||
}
|
||||
|
||||
b = cfgetospeed(&termios);
|
||||
if (b == B0) {
|
||||
kprintf(" B0");
|
||||
} else if (b == B50) {
|
||||
kprintf(" B50");
|
||||
} else if (b == B75) {
|
||||
kprintf(" B75");
|
||||
} else if (b == B110) {
|
||||
kprintf(" B110");
|
||||
} else if (b == B134) {
|
||||
kprintf(" B134");
|
||||
} else if (b == B150) {
|
||||
kprintf(" B150");
|
||||
} else if (b == B200) {
|
||||
kprintf(" B200");
|
||||
} else if (b == B300) {
|
||||
kprintf(" B300");
|
||||
} else if (b == B600) {
|
||||
kprintf(" B600");
|
||||
} else if (b == B1200) {
|
||||
kprintf(" B1200");
|
||||
} else if (b == B1800) {
|
||||
kprintf(" B1800");
|
||||
} else if (b == B2400) {
|
||||
kprintf(" B2400");
|
||||
} else if (b == B4800) {
|
||||
kprintf(" B4800");
|
||||
} else if (b == B9600) {
|
||||
kprintf(" B9600");
|
||||
} else if (b == B19200) {
|
||||
kprintf(" B19200");
|
||||
} else if (b == B38400) {
|
||||
kprintf(" B38400");
|
||||
} else if (b == B57600) {
|
||||
kprintf(" B57600");
|
||||
} else if (b == B115200) {
|
||||
kprintf(" B115200");
|
||||
} else if (b == B230400) {
|
||||
kprintf(" B230400");
|
||||
} else if (b == B500000) {
|
||||
kprintf(" B500000");
|
||||
} else if (b == B576000) {
|
||||
kprintf(" B576000");
|
||||
} else if (b == B1000000) {
|
||||
kprintf(" B1000000");
|
||||
} else if (b == B1152000) {
|
||||
kprintf(" B1152000");
|
||||
} else if (b == B1500000) {
|
||||
kprintf(" B1500000");
|
||||
} else if (b == B2000000) {
|
||||
kprintf(" B2000000");
|
||||
} else if (b == B2500000) {
|
||||
kprintf(" B2500000");
|
||||
} else if (b == B3000000) {
|
||||
kprintf(" B3000000");
|
||||
} else if (b == B3500000) {
|
||||
kprintf(" B3500000");
|
||||
} else if (b == B4000000) {
|
||||
kprintf(" B4000000");
|
||||
}
|
||||
kprintf("\n");
|
||||
kprintf(prologue);
|
||||
kprintf(" c_lflag =");
|
||||
|
@ -642,21 +591,21 @@ dontasan textstartup void __printargs(const char *prologue) {
|
|||
PRINT(" cfgetospeed() = %u", cfgetospeed(&termios));
|
||||
PRINT(" c_cc[VMIN] = %d", termios.c_cc[VMIN]);
|
||||
PRINT(" c_cc[VTIME] = %d", termios.c_cc[VTIME]);
|
||||
PRINT(" c_cc[VINTR] = CTRL-%c", CTRL(termios.c_cc[VINTR]));
|
||||
PRINT(" c_cc[VQUIT] = CTRL-%c", CTRL(termios.c_cc[VQUIT]));
|
||||
PRINT(" c_cc[VERASE] = CTRL-%c", CTRL(termios.c_cc[VERASE]));
|
||||
PRINT(" c_cc[VKILL] = CTRL-%c", CTRL(termios.c_cc[VKILL]));
|
||||
PRINT(" c_cc[VEOF] = CTRL-%c", CTRL(termios.c_cc[VEOF]));
|
||||
PRINT(" c_cc[VSTART] = CTRL-%c", CTRL(termios.c_cc[VSTART]));
|
||||
PRINT(" c_cc[VSTOP] = CTRL-%c", CTRL(termios.c_cc[VSTOP]));
|
||||
PRINT(" c_cc[VSUSP] = CTRL-%c", CTRL(termios.c_cc[VSUSP]));
|
||||
PRINT(" c_cc[VEOL] = CTRL-%c", CTRL(termios.c_cc[VEOL]));
|
||||
PRINT(" c_cc[VSWTC] = CTRL-%c", CTRL(termios.c_cc[VSWTC]));
|
||||
PRINT(" c_cc[VREPRINT] = CTRL-%c", CTRL(termios.c_cc[VREPRINT]));
|
||||
PRINT(" c_cc[VDISCARD] = CTRL-%c", CTRL(termios.c_cc[VDISCARD]));
|
||||
PRINT(" c_cc[VWERASE] = CTRL-%c", CTRL(termios.c_cc[VWERASE]));
|
||||
PRINT(" c_cc[VLNEXT] = CTRL-%c", CTRL(termios.c_cc[VLNEXT]));
|
||||
PRINT(" c_cc[VEOL2] = CTRL-%c", CTRL(termios.c_cc[VEOL2]));
|
||||
PRINT(" c_cc[VINTR] = %s", ConvertCcToStr(termios.c_cc[VINTR]));
|
||||
PRINT(" c_cc[VQUIT] = %s", ConvertCcToStr(termios.c_cc[VQUIT]));
|
||||
PRINT(" c_cc[VERASE] = %s", ConvertCcToStr(termios.c_cc[VERASE]));
|
||||
PRINT(" c_cc[VKILL] = %s", ConvertCcToStr(termios.c_cc[VKILL]));
|
||||
PRINT(" c_cc[VEOF] = %s", ConvertCcToStr(termios.c_cc[VEOF]));
|
||||
PRINT(" c_cc[VSTART] = %s", ConvertCcToStr(termios.c_cc[VSTART]));
|
||||
PRINT(" c_cc[VSTOP] = %s", ConvertCcToStr(termios.c_cc[VSTOP]));
|
||||
PRINT(" c_cc[VSUSP] = %s", ConvertCcToStr(termios.c_cc[VSUSP]));
|
||||
PRINT(" c_cc[VSWTC] = %s", ConvertCcToStr(termios.c_cc[VSWTC]));
|
||||
PRINT(" c_cc[VREPRINT] = %s", ConvertCcToStr(termios.c_cc[VREPRINT]));
|
||||
PRINT(" c_cc[VDISCARD] = %s", ConvertCcToStr(termios.c_cc[VDISCARD]));
|
||||
PRINT(" c_cc[VWERASE] = %s", ConvertCcToStr(termios.c_cc[VWERASE]));
|
||||
PRINT(" c_cc[VLNEXT] = %s", ConvertCcToStr(termios.c_cc[VLNEXT]));
|
||||
PRINT(" c_cc[VEOL] = %s", ConvertCcToStr(termios.c_cc[VEOL]));
|
||||
PRINT(" c_cc[VEOL2] = %s", ConvertCcToStr(termios.c_cc[VEOL2]));
|
||||
} else {
|
||||
PRINT(" - tcgetattr(%d) failed %m", i);
|
||||
}
|
||||
|
|
|
@ -27,8 +27,13 @@
|
|||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/rdtsc.h"
|
||||
#include "libc/nt/console.h"
|
||||
#include "libc/nt/createfile.h"
|
||||
#include "libc/nt/enum/accessmask.h"
|
||||
#include "libc/nt/enum/consolemodeflags.h"
|
||||
#include "libc/nt/enum/creationdisposition.h"
|
||||
#include "libc/nt/enum/fileflagandattributes.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/nt/enum/filesharemode.h"
|
||||
#include "libc/nt/enum/pageflags.h"
|
||||
#include "libc/nt/files.h"
|
||||
#include "libc/nt/memory.h"
|
||||
|
@ -38,29 +43,37 @@
|
|||
#include "libc/nt/signals.h"
|
||||
#include "libc/nt/struct/ntexceptionpointers.h"
|
||||
#include "libc/nt/struct/teb.h"
|
||||
#include "libc/nt/thunk/msabi.h"
|
||||
#include "libc/runtime/internal.h"
|
||||
#include "libc/runtime/memtrack.internal.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/runtime/winargs.internal.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sysv/consts/prot.h"
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#if IsTiny()
|
||||
// clang-format off
|
||||
__msabi extern typeof(AddVectoredExceptionHandler) *const __imp_AddVectoredExceptionHandler;
|
||||
__msabi extern typeof(CloseHandle) *const __imp_CloseHandle;
|
||||
__msabi extern typeof(CreateFile) *const __imp_CreateFileW;
|
||||
__msabi extern typeof(CreateFileMapping) *const __imp_CreateFileMappingW;
|
||||
__msabi extern typeof(DuplicateHandle) *const __imp_DuplicateHandle;
|
||||
__msabi extern typeof(ExitProcess) *const __imp_ExitProcess;
|
||||
__msabi extern typeof(FreeEnvironmentStrings) *const __imp_FreeEnvironmentStringsW;
|
||||
__msabi extern typeof(GetConsoleMode) *const __imp_GetConsoleMode;
|
||||
__msabi extern typeof(GetCurrentProcess) *const __imp_GetCurrentProcess;
|
||||
__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(SetConsoleCP) *const __imp_SetConsoleCP;
|
||||
__msabi extern typeof(SetConsoleMode) *const __imp_SetConsoleMode;
|
||||
__msabi extern typeof(SetConsoleOutputCP) *const __imp_SetConsoleOutputCP;
|
||||
__msabi extern typeof(SetStdHandle) *const __imp_SetStdHandle;
|
||||
__msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
|
||||
#define CreateFileMapping __imp_CreateFileMappingW
|
||||
#define MapViewOfFileEx __imp_MapViewOfFileEx
|
||||
#define VirtualProtect __imp_VirtualProtect
|
||||
#endif
|
||||
|
||||
#define AT_EXECFN 31L
|
||||
#define MAP_ANONYMOUS 32
|
||||
#define MAP_PRIVATE 2
|
||||
#define PROT_EXEC 4
|
||||
#define PROT_READ 1
|
||||
#define PROT_WRITE 2
|
||||
__msabi extern typeof(WriteFile) *const __imp_WriteFile;
|
||||
// clang-format on
|
||||
|
||||
/*
|
||||
* TODO: Why can't we allocate addresses above 4GB on Windows 7 x64?
|
||||
|
@ -68,7 +81,7 @@ __msabi extern typeof(VirtualProtect) *const __imp_VirtualProtect;
|
|||
*/
|
||||
|
||||
extern int64_t __wincrashearly;
|
||||
extern const signed char kConsoleHandles[3];
|
||||
extern const signed char kNtConsoleHandles[3];
|
||||
extern void cosmo(int, char **, char **, long (*)[2]) wontreturn;
|
||||
|
||||
static const short kConsoleModes[3] = {
|
||||
|
@ -83,7 +96,7 @@ static const short kConsoleModes[3] = {
|
|||
};
|
||||
|
||||
// https://nullprogram.com/blog/2022/02/18/
|
||||
static inline char16_t *MyCommandLine(void) {
|
||||
__msabi static inline char16_t *MyCommandLine(void) {
|
||||
void *cmd;
|
||||
asm("mov\t%%gs:(0x60),%0\n"
|
||||
"mov\t0x20(%0),%0\n"
|
||||
|
@ -92,7 +105,7 @@ static inline char16_t *MyCommandLine(void) {
|
|||
return cmd;
|
||||
}
|
||||
|
||||
static inline size_t StrLen16(const char16_t *s) {
|
||||
__msabi static inline size_t StrLen16(const char16_t *s) {
|
||||
size_t n;
|
||||
for (n = 0;; ++n) {
|
||||
if (!s[n]) {
|
||||
|
@ -121,19 +134,45 @@ __msabi static textwindows int OnEarlyWinCrash(struct NtExceptionPointers *ep) {
|
|||
p = __fixcpy(p, ep->ContextRecord ? ep->ContextRecord->Rip : -1, 32);
|
||||
*p++ = '\r';
|
||||
*p++ = '\n';
|
||||
WriteFile(GetStdHandle(kNtStdErrorHandle), buf, p - buf, &wrote, 0);
|
||||
ExitProcess(11);
|
||||
__imp_WriteFile(__imp_GetStdHandle(kNtStdErrorHandle), buf, p - buf, &wrote,
|
||||
0);
|
||||
__imp_ExitProcess(11);
|
||||
__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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this ensures close(1) won't accidentally close(2) for example
|
||||
__msabi static textwindows void DeduplicateStdioHandles(void) {
|
||||
int64_t proc, outhand, errhand, newhand;
|
||||
outhand = GetStdHandle(kNtStdOutputHandle);
|
||||
errhand = GetStdHandle(kNtStdErrorHandle);
|
||||
if (outhand == errhand) {
|
||||
proc = GetCurrentProcess();
|
||||
DuplicateHandle(proc, errhand, proc, &newhand, 0, true,
|
||||
kNtDuplicateSameAccess);
|
||||
SetStdHandle(kNtStdErrorHandle, newhand);
|
||||
long i, j;
|
||||
int64_t h1, h2, h3, proc;
|
||||
for (i = 0; i < 3; ++i) {
|
||||
h1 = __imp_GetStdHandle(kNtConsoleHandles[i]);
|
||||
for (j = i + 1; j < 3; ++j) {
|
||||
h3 = h2 = __imp_GetStdHandle(kNtConsoleHandles[j]);
|
||||
if (h1 == h2) {
|
||||
proc = __imp_GetCurrentProcess();
|
||||
__imp_DuplicateHandle(proc, h2, proc, &h3, 0, true,
|
||||
kNtDuplicateSameAccess);
|
||||
__imp_SetStdHandle(kNtConsoleHandles[j], h3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,20 +189,21 @@ __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 = SetConsoleCP(kNtCpUtf8);
|
||||
rc = __imp_SetConsoleCP(kNtCpUtf8);
|
||||
NTTRACE("SetConsoleCP(kNtCpUtf8) → %hhhd", rc);
|
||||
rc = SetConsoleOutputCP(kNtCpUtf8);
|
||||
rc = __imp_SetConsoleOutputCP(kNtCpUtf8);
|
||||
NTTRACE("SetConsoleOutputCP(kNtCpUtf8) → %hhhd", rc);
|
||||
for (i = 0; i < 3; ++i) {
|
||||
hand = GetStdHandle(kConsoleHandles[i]);
|
||||
rc = GetConsoleMode(hand, __ntconsolemode + i);
|
||||
hand = __imp_GetStdHandle(kNtConsoleHandles[i]);
|
||||
rc = __imp_GetConsoleMode(hand, __ntconsolemode + i);
|
||||
NTTRACE("GetConsoleMode(%p, [%s]) → %hhhd", hand,
|
||||
i ? (DescribeNtConsoleOutFlags)(outflagsbuf, __ntconsolemode[i])
|
||||
: (DescribeNtConsoleInFlags)(inflagsbuf, __ntconsolemode[i]),
|
||||
rc);
|
||||
rc = SetConsoleMode(hand, kConsoleModes[i]);
|
||||
rc = __imp_SetConsoleMode(hand, kConsoleModes[i]);
|
||||
NTTRACE("SetConsoleMode(%p, %s) → %hhhd", hand,
|
||||
i ? (DescribeNtConsoleOutFlags)(outflagsbuf, kConsoleModes[i])
|
||||
: (DescribeNtConsoleInFlags)(inflagsbuf, kConsoleModes[i]),
|
||||
|
@ -179,14 +219,15 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) {
|
|||
allocaddr = stackaddr;
|
||||
allocsize = stacksize + sizeof(struct WinArgs);
|
||||
NTTRACE("WinMainNew() mapping %'zu byte stack at %p", allocsize, allocaddr);
|
||||
MapViewOfFileEx(
|
||||
(_mmi.p[0].h =
|
||||
CreateFileMapping(-1, &kNtIsInheritable, kNtPageExecuteReadwrite,
|
||||
__imp_MapViewOfFileEx((_mmi.p[0].h = __imp_CreateFileMappingW(
|
||||
-1, &kNtIsInheritable, kNtPageExecuteReadwrite,
|
||||
allocsize >> 32, allocsize, NULL)),
|
||||
kNtFileMapWrite | kNtFileMapExecute, 0, 0, allocsize, (void *)allocaddr);
|
||||
kNtFileMapWrite | kNtFileMapExecute, 0, 0, allocsize,
|
||||
(void *)allocaddr);
|
||||
prot = (intptr_t)ape_stack_prot;
|
||||
if (~prot & PROT_EXEC) {
|
||||
VirtualProtect((void *)allocaddr, allocsize, kNtPageReadwrite, &oldprot);
|
||||
__imp_VirtualProtect((void *)allocaddr, allocsize, kNtPageReadwrite,
|
||||
&oldprot);
|
||||
}
|
||||
_mmi.p[0].x = allocaddr >> 16;
|
||||
_mmi.p[0].y = (allocaddr >> 16) + ((allocsize - 1) >> 16);
|
||||
|
@ -203,57 +244,26 @@ __msabi static textwindows wontreturn void WinMainNew(const char16_t *cmdline) {
|
|||
wa->argv[0][i] = '/';
|
||||
}
|
||||
}
|
||||
env16 = GetEnvironmentStrings();
|
||||
env16 = __imp_GetEnvironmentStringsW();
|
||||
NTTRACE("WinMainNew() loading environment");
|
||||
GetDosEnviron(env16, wa->envblock, ARRAYLEN(wa->envblock) - 8, wa->envp,
|
||||
ARRAYLEN(wa->envp) - 1);
|
||||
FreeEnvironmentStrings(env16);
|
||||
__imp_FreeEnvironmentStringsW(env16);
|
||||
NTTRACE("WinMainNew() switching stacks");
|
||||
_jmpstack((char *)(stackaddr + stacksize - (intptr_t)ape_stack_align), cosmo,
|
||||
count, wa->argv, wa->envp, wa->auxv);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function on Windows NT.
|
||||
*
|
||||
* The Cosmopolitan Runtime provides the following services, which aim
|
||||
* to bring Windows NT behavior closer in harmony with System Five:
|
||||
*
|
||||
* 1. We configure CMD.EXE for UTF-8 and enable ANSI colors on Win10.
|
||||
*
|
||||
* 2. Command line arguments are passed as a blob of UTF-16 text. We
|
||||
* chop them up into an char *argv[] UTF-8 data structure, in
|
||||
* accordance with the DOS conventions for argument quoting.
|
||||
*
|
||||
* 3. Environment variables are passed to us as a sorted UTF-16 double
|
||||
* NUL terminated list. We translate this to char ** using UTF-8.
|
||||
*
|
||||
* 4. Allocates new stack at a high address. NT likes to choose a
|
||||
* stack address that's beneath the program image. We want to be
|
||||
* able to assume that stack addresses are located at higher
|
||||
* addresses than heap and program memory.
|
||||
*
|
||||
* 5. Reconfigure x87 FPU so long double is actually long (80 bits).
|
||||
*
|
||||
* 6. Finally, we need fork. Since disagreeing with fork is axiomatic to
|
||||
* Microsoft's engineering culture, we need to go to great lengths to
|
||||
* have it anyway without breaking Microsoft's rules: using the WIN32
|
||||
* API (i.e. not NTDLL) to copy MAP_PRIVATE pages via a pipe. It'd go
|
||||
* faster if the COW pages CreateFileMappingNuma claims to have turns
|
||||
* out to be true. Until then we have a "PC Scale" and entirely legal
|
||||
* workaround that they hopefully won't block using Windows Defender.
|
||||
*
|
||||
* @param hInstance call GetModuleHandle(NULL) from main if you need it
|
||||
*/
|
||||
__msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
|
||||
const char *lpCmdLine, int64_t nCmdShow) {
|
||||
const char16_t *cmdline;
|
||||
extern char os asm("__hostos");
|
||||
os = _HOSTWINDOWS; /* madness https://news.ycombinator.com/item?id=21019722 */
|
||||
kStartTsc = rdtsc();
|
||||
__pid = GetCurrentProcessId();
|
||||
__pid = __imp_GetCurrentProcessId();
|
||||
#if !IsTiny()
|
||||
__wincrashearly = AddVectoredExceptionHandler(1, (void *)OnEarlyWinCrash);
|
||||
__wincrashearly =
|
||||
__imp_AddVectoredExceptionHandler(1, (void *)OnEarlyWinCrash);
|
||||
#endif
|
||||
cmdline = MyCommandLine();
|
||||
#ifdef SYSDEBUG
|
||||
|
@ -261,8 +271,12 @@ __msabi textwindows int64_t WinMain(int64_t hInstance, int64_t hPrevInstance,
|
|||
if (__strstr16(cmdline, u"--strace")) ++__strace;
|
||||
#endif
|
||||
NTTRACE("WinMain()");
|
||||
if (_weaken(WinSockInit)) _weaken(WinSockInit)();
|
||||
if (_weaken(WinMainForked)) _weaken(WinMainForked)();
|
||||
if (_weaken(WinSockInit)) {
|
||||
_weaken(WinSockInit)();
|
||||
}
|
||||
if (_weaken(WinMainForked)) {
|
||||
_weaken(WinMainForked)();
|
||||
}
|
||||
WinMainNew(cmdline);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,11 @@
|
|||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/strace.internal.h"
|
||||
#include "libc/nt/enum/wait.h"
|
||||
#include "libc/nt/enum/wsa.h"
|
||||
#include "libc/nt/errors.h"
|
||||
#include "libc/nt/runtime.h"
|
||||
#include "libc/nt/synchronization.h"
|
||||
#include "libc/nt/thread.h"
|
||||
#include "libc/nt/winsock.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
|
@ -36,50 +36,61 @@
|
|||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
static textwindows void __wsablock_abort(int64_t handle,
|
||||
struct NtOverlapped *overlapped) {
|
||||
unassert(CancelIoEx(handle, overlapped) ||
|
||||
GetLastError() == kNtErrorNotFound);
|
||||
}
|
||||
|
||||
textwindows int __wsablock(struct Fd *fd, struct NtOverlapped *overlapped,
|
||||
uint32_t *flags, int sigops, uint32_t timeout) {
|
||||
int e, rc;
|
||||
uint32_t i, got;
|
||||
int rc, abort_errno;
|
||||
if (WSAGetLastError() != kNtErrorIoPending) {
|
||||
NTTRACE("sock i/o failed %s", strerror(errno));
|
||||
// our i/o operation never happened because it failed
|
||||
return __winsockerr();
|
||||
}
|
||||
// our i/o operation is in flight and it needs to block
|
||||
abort_errno = EAGAIN;
|
||||
if (fd->flags & O_NONBLOCK) {
|
||||
e = errno;
|
||||
unassert(CancelIoEx(fd->handle, overlapped) ||
|
||||
WSAGetLastError() == kNtErrorNotFound);
|
||||
errno = e;
|
||||
__wsablock_abort(fd->handle, overlapped);
|
||||
} else if (_check_interrupts(sigops, g_fds.p)) {
|
||||
Interrupted:
|
||||
abort_errno = errno; // EINTR or ECANCELED
|
||||
__wsablock_abort(fd->handle, overlapped);
|
||||
} else {
|
||||
if (_check_interrupts(sigops, g_fds.p)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
for (;;) {
|
||||
i = WSAWaitForMultipleEvents(1, &overlapped->hEvent, true,
|
||||
__SIG_POLLING_INTERVAL_MS, true);
|
||||
if (i == kNtWaitFailed) {
|
||||
NTTRACE("WSAWaitForMultipleEvents failed %lm");
|
||||
return __winsockerr();
|
||||
} else if (i == kNtWaitTimeout || i == kNtWaitIoCompletion) {
|
||||
if (_check_interrupts(sigops, g_fds.p)) {
|
||||
return -1;
|
||||
}
|
||||
if (timeout) {
|
||||
if (timeout <= __SIG_POLLING_INTERVAL_MS) {
|
||||
NTTRACE("__wsablock timeout elapsed");
|
||||
return eagain();
|
||||
for (;;) {
|
||||
i = WSAWaitForMultipleEvents(1, &overlapped->hEvent, true,
|
||||
__SIG_POLLING_INTERVAL_MS, true);
|
||||
if (i == kNtWaitFailed || i == kNtWaitTimeout) {
|
||||
if (_check_interrupts(sigops, g_fds.p)) {
|
||||
goto Interrupted;
|
||||
}
|
||||
timeout -= __SIG_POLLING_INTERVAL_MS;
|
||||
if (i == kNtWaitFailed) {
|
||||
// Failure should be an impossible condition, but MSDN lists
|
||||
// WSAENETDOWN and WSA_NOT_ENOUGH_MEMORY as possible errors,
|
||||
// which we're going to hope are ephemeral.
|
||||
SleepEx(__SIG_POLLING_INTERVAL_MS, false);
|
||||
}
|
||||
if (timeout) {
|
||||
if (timeout <= __SIG_POLLING_INTERVAL_MS) {
|
||||
__wsablock_abort(fd->handle, overlapped);
|
||||
break;
|
||||
}
|
||||
timeout -= __SIG_POLLING_INTERVAL_MS;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (WSAGetOverlappedResult(fd->handle, overlapped, &got, false, flags)) {
|
||||
// overlapped is allocated on stack by caller, so it's important that
|
||||
// we wait for win32 to acknowledge that it's done using that memory.
|
||||
if (WSAGetOverlappedResult(fd->handle, overlapped, &got, true, flags)) {
|
||||
return got;
|
||||
} else if ((fd->flags & O_NONBLOCK) &&
|
||||
WSAGetLastError() == kNtErrorOperationAborted) {
|
||||
return eagain();
|
||||
} else if (WSAGetLastError() == kNtErrorOperationAborted) {
|
||||
errno = abort_errno;
|
||||
return -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,9 @@
|
|||
* The returned memory is owned by the stream. It'll be reused when
|
||||
* fgetln() is called again. It's free()'d upon fclose() / fflush()
|
||||
*
|
||||
* When reading from the console on Windows in `ICANON` mode, the
|
||||
* returned line will end with `\r\n` rather than `\n`.
|
||||
*
|
||||
* @param stream specifies non-null open input stream
|
||||
* @param len optionally receives byte length of line
|
||||
* @return nul-terminated line string, including the `\n` character
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
* exceeding size. The line ending marker is included and may be removed
|
||||
* using _chomp().
|
||||
*
|
||||
* When reading from the console on Windows in `ICANON` mode, the
|
||||
* returned line will end with `\r\n` rather than `\n`.
|
||||
*
|
||||
* @param s is output buffer
|
||||
* @param size is capacity of s
|
||||
* @param f is non-null file object stream pointer
|
||||
|
|
|
@ -33,13 +33,16 @@
|
|||
* documentation. Concerning lines, please note the \n or \r\n are
|
||||
* included in results, and can be removed with _chomp().
|
||||
*
|
||||
* @param line is the caller's buffer (in/out) which is extended
|
||||
* automatically. *line may be NULL but only if *n is 0;
|
||||
* When reading from the console on Windows in `ICANON` mode, the
|
||||
* returned line will end with `\r\n` rather than `\n`.
|
||||
*
|
||||
* @param linebuf is the caller's buffer (in/out) which is extended
|
||||
* automatically. *line may be NULL but only if *capacity is 0;
|
||||
* NUL-termination is guaranteed FTMP
|
||||
* @return number of bytes read, including delim, excluding NUL, or -1
|
||||
* w/ errno on EOF or error; see ferror() and feof()
|
||||
* @see fgetln(), xgetline(), getdelim(), gettok_r()
|
||||
*/
|
||||
ssize_t getline(char **line, size_t *n, FILE *f) {
|
||||
return getdelim(line, n, '\n', f);
|
||||
ssize_t getline(char **linebuf, size_t *capacity, FILE *f) {
|
||||
return getdelim(linebuf, capacity, '\n', f);
|
||||
}
|
||||
|
|
|
@ -18,14 +18,18 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/assert.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/cp.internal.h"
|
||||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/struct/iovec.h"
|
||||
#include "libc/calls/struct/iovec.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/syscall-sysv.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sysv/consts/nr.h"
|
||||
#include "libc/sysv/consts/o.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/ezbench.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
|
||||
|
@ -43,10 +47,35 @@ TEST(read, eof) {
|
|||
ASSERT_SYS(0, 0, close(3));
|
||||
}
|
||||
|
||||
volatile bool got_sigalrm;
|
||||
|
||||
void OnSigalrm(int sig) {
|
||||
got_sigalrm = true;
|
||||
}
|
||||
|
||||
TEST(read_pipe, canBeInterruptedByAlarm) {
|
||||
int fds[2];
|
||||
char buf[1];
|
||||
struct sigaction sa;
|
||||
alarm(1);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = OnSigalrm;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGALRM, &sa, 0);
|
||||
ASSERT_SYS(0, 0, pipe(fds));
|
||||
ASSERT_SYS(ESPIPE, -1, pread(fds[0], buf, 1, 777));
|
||||
ASSERT_SYS(EINTR, -1, read(fds[0], buf, 1));
|
||||
ASSERT_TRUE(got_sigalrm);
|
||||
signal(SIGALRM, SIG_DFL);
|
||||
close(fds[1]);
|
||||
close(fds[0]);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BENCH(read, bench) {
|
||||
char buf[16];
|
||||
BEGIN_CANCELLATION_POINT;
|
||||
ASSERT_SYS(0, 3, open("/dev/zero", O_RDONLY));
|
||||
EZBENCH2("read", donothing, read(3, buf, 5));
|
||||
EZBENCH2("pread", donothing, pread(3, buf, 5, 0));
|
||||
|
@ -59,4 +88,5 @@ BENCH(read, bench) {
|
|||
EZBENCH2("sys_read", donothing, sys_read(3, buf, 5));
|
||||
EZBENCH2("sys_readv", donothing, sys_readv(3, &(struct iovec){buf, 5}, 1));
|
||||
ASSERT_SYS(0, 0, close(3));
|
||||
END_CANCELLATION_POINT;
|
||||
}
|
||||
|
|
|
@ -21,18 +21,21 @@
|
|||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/siginfo.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/calls/ucontext.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/itimer.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/sysv/consts/sicode.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/testlib/subprocess.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/time/time.h"
|
||||
|
||||
bool gotsig;
|
||||
|
||||
void SetUpOnce(void) {
|
||||
ASSERT_SYS(0, 0, pledge("stdio", 0));
|
||||
ASSERT_SYS(0, 0, pledge("stdio proc", 0));
|
||||
}
|
||||
|
||||
void OnSigAlrm(int sig, siginfo_t *si, void *ctx) {
|
||||
|
@ -59,3 +62,15 @@ TEST(setitimer, testSingleShot) {
|
|||
EXPECT_EQ(0, sigaction(SIGUSR1, &oldalrm, 0));
|
||||
EXPECT_EQ(true, gotsig);
|
||||
}
|
||||
|
||||
TEST(setitimer, notInheritedAcrossFork) {
|
||||
struct itimerval disarm = {0};
|
||||
struct itimerval singleshot = {{0}, {100}};
|
||||
ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &singleshot, 0));
|
||||
SPAWN(fork);
|
||||
struct itimerval it;
|
||||
ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, 0, &it));
|
||||
ASSERT_TRUE(timeval_iszero(it.it_value));
|
||||
EXITS(0);
|
||||
ASSERT_SYS(0, 0, setitimer(ITIMER_REAL, &disarm, 0));
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/struct/timespec.h"
|
||||
#include "libc/calls/struct/timeval.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/stdio/rand.h"
|
||||
|
@ -84,6 +85,15 @@ TEST(timespec_tonanos, test) {
|
|||
EXPECT_EQ(INT64_MIN, timespec_tonanos((struct timespec){INT64_MIN, 0}));
|
||||
}
|
||||
|
||||
TEST(timeval_toseconds, test) {
|
||||
ASSERT_EQ(0, timeval_toseconds((struct timeval){0, 0}));
|
||||
ASSERT_EQ(1, timeval_toseconds((struct timeval){0, 1}));
|
||||
ASSERT_EQ(1, timeval_toseconds((struct timeval){0, 2}));
|
||||
ASSERT_EQ(1, timeval_toseconds((struct timeval){1, 0}));
|
||||
ASSERT_EQ(2, timeval_toseconds((struct timeval){1, 1}));
|
||||
ASSERT_EQ(INT64_MAX, timeval_toseconds(timeval_max));
|
||||
}
|
||||
|
||||
static long mod(long x, long y) {
|
||||
if (y == -1) return 0;
|
||||
return x - y * (x / y - (x % y && (x ^ y) < 0));
|
||||
|
|
|
@ -448,7 +448,7 @@ int SpawnSubprocesses(int argc, char *argv[]) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
CHECK_NE(-1, unlink(tpath));
|
||||
unlink(tpath);
|
||||
sigprocmask(SIG_SETMASK, &savemask, 0);
|
||||
sigaction(SIGQUIT, &savequit, 0);
|
||||
sigaction(SIGINT, &saveint, 0);
|
||||
|
|
|
@ -474,12 +474,25 @@ void HandleClient(void) {
|
|||
CHECK_NE(-1, events); // EINTR shouldn't be possible
|
||||
if (events) {
|
||||
if (fds[0].revents) {
|
||||
if (!(fds[0].revents & POLLHUP)) {
|
||||
WARNF("%s got unexpected input event from client %#x", exename,
|
||||
fds[0].revents);
|
||||
int received;
|
||||
char buf[512];
|
||||
INFOF("mbedtls_ssl_read");
|
||||
received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf));
|
||||
if (!received) {
|
||||
WARNF("%s client disconnected so killing worker %d", exename, child);
|
||||
goto TerminateJob;
|
||||
}
|
||||
WARNF("%s client disconnected so killing worker %d", exename, child);
|
||||
goto TerminateJob;
|
||||
if (received > 0) {
|
||||
WARNF("%s client sent %d unexpected bytes so killing job", exename,
|
||||
received);
|
||||
goto TerminateJob;
|
||||
}
|
||||
if (received != MBEDTLS_ERR_SSL_WANT_READ) {
|
||||
WARNF("%s client ssl read failed with -0x%04x so killing job",
|
||||
exename, -received);
|
||||
goto TerminateJob;
|
||||
}
|
||||
INFOF("got spurious ssl data");
|
||||
}
|
||||
if (fds[1].revents) {
|
||||
INFOF("read");
|
||||
|
|
Loading…
Reference in a new issue