diff --git a/libc/calls/alarm.c b/libc/calls/alarm.c index d76257986..e610a9c92 100644 --- a/libc/calls/alarm.c +++ b/libc/calls/alarm.c @@ -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); } diff --git a/libc/calls/interrupts-nt.c b/libc/calls/interrupts-nt.c index 4341d37bc..37d91122b 100644 --- a/libc/calls/interrupts-nt.c +++ b/libc/calls/interrupts-nt.c @@ -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)(); diff --git a/libc/calls/open-nt.c b/libc/calls/open-nt.c index e8b41e580..b7d0fb26c 100644 --- a/libc/calls/open-nt.c +++ b/libc/calls/open-nt.c @@ -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; diff --git a/libc/calls/read-nt.c b/libc/calls/read-nt.c index ba0ecdd3d..3e4200a15 100644 --- a/libc/calls/read-nt.c +++ b/libc/calls/read-nt.c @@ -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(); } diff --git a/libc/calls/setitimer-nt.c b/libc/calls/setitimer-nt.c index 597033585..474d16211 100644 --- a/libc/calls/setitimer-nt.c +++ b/libc/calls/setitimer-nt.c @@ -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; } diff --git a/libc/calls/setitimer.c b/libc/calls/setitimer.c index e7decb065..91faca204 100644 --- a/libc/calls/setitimer.c +++ b/libc/calls/setitimer.c @@ -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. * diff --git a/libc/calls/struct/fd.internal.h b/libc/calls/struct/fd.internal.h index cc3e79a00..5070ec240 100644 --- a/libc/calls/struct/fd.internal.h +++ b/libc/calls/struct/fd.internal.h @@ -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 { diff --git a/libc/calls/struct/timeval.h b/libc/calls/struct/timeval.h index fcc39d0f3..31087dbba 100644 --- a/libc/calls/struct/timeval.h +++ b/libc/calls/struct/timeval.h @@ -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}; } diff --git a/libc/calls/tcgetattr-nt.c b/libc/calls/tcgetattr-nt.c index a64e55f88..18464cfd1 100644 --- a/libc/calls/tcgetattr-nt.c +++ b/libc/calls/tcgetattr-nt.c @@ -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; - } } } diff --git a/libc/calls/tcsetattr-nt.c b/libc/calls/tcsetattr-nt.c index a1bd813d7..fc40cae63 100644 --- a/libc/calls/tcsetattr-nt.c +++ b/libc/calls/tcsetattr-nt.c @@ -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 { diff --git a/libc/intrin/isatleastwindows10.greg.c b/libc/calls/timeval_toseconds.c similarity index 79% rename from libc/intrin/isatleastwindows10.greg.c rename to libc/calls/timeval_toseconds.c index b8e4c33ed..b9d601d53 100644 --- a/libc/intrin/isatleastwindows10.greg.c +++ b/libc/calls/timeval_toseconds.c @@ -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; } diff --git a/libc/calls/write-nt.c b/libc/calls/write-nt.c index c5f4c8ad2..41d97dc53 100644 --- a/libc/calls/write-nt.c +++ b/libc/calls/write-nt.c @@ -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 diff --git a/libc/intrin/cancelioex.c b/libc/intrin/cancelioex.c deleted file mode 100644 index 93d8c8b0f..000000000 --- a/libc/intrin/cancelioex.c +++ /dev/null @@ -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; -} diff --git a/libc/intrin/intrin.mk b/libc/intrin/intrin.mk index da4f3b7d5..373d6fd13 100644 --- a/libc/intrin/intrin.mk +++ b/libc/intrin/intrin.mk @@ -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 \ diff --git a/libc/intrin/restorewintty.c b/libc/intrin/restorewintty.c index e77070891..044593100 100644 --- a/libc/intrin/restorewintty.c +++ b/libc/intrin/restorewintty.c @@ -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]); } } diff --git a/libc/nt/enum/wait.h b/libc/nt/enum/wait.h index b634edf97..549015cbc 100644 --- a/libc/nt/enum/wait.h +++ b/libc/nt/enum/wait.h @@ -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_ */ diff --git a/libc/nt/events.h b/libc/nt/events.h index d9bcce6cf..9cb1d45cc 100644 --- a/libc/nt/events.h +++ b/libc/nt/events.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_ */ diff --git a/libc/nt/kernel32/CancelIoEx.S b/libc/nt/kernel32/CancelIoEx.S index 2154ae9bc..2ef5fc09d 100644 --- a/libc/nt/kernel32/CancelIoEx.S +++ b/libc/nt/kernel32/CancelIoEx.S @@ -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 diff --git a/libc/nt/master.sh b/libc/nt/master.sh index af27f24d7..04ff3578b 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -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 diff --git a/libc/nt/runtime.h b/libc/nt/runtime.h index 39bedab34..303f3cbd7 100644 --- a/libc/nt/runtime.h +++ b/libc/nt/runtime.h @@ -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, diff --git a/libc/nt/synchronization.h b/libc/nt/synchronization.h index 1d710243f..68bf3cdc3 100644 --- a/libc/nt/synchronization.h +++ b/libc/nt/synchronization.h @@ -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); diff --git a/libc/nt/version.h b/libc/nt/version.h index 5523a11f4..2140c7c14 100644 --- a/libc/nt/version.h +++ b/libc/nt/version.h @@ -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__) diff --git a/libc/runtime/fork-nt.c b/libc/runtime/fork-nt.c index 5d2c1cab2..226f82362 100644 --- a/libc/runtime/fork-nt.c +++ b/libc/runtime/fork-nt.c @@ -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); diff --git a/libc/runtime/printargs.c b/libc/runtime/printargs.c index 666704a9c..92c6349bd 100644 --- a/libc/runtime/printargs.c +++ b/libc/runtime/printargs.c @@ -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); } diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 973bae99d..3d543fe39 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -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); } diff --git a/libc/sock/wsablock.c b/libc/sock/wsablock.c index 61a29110f..7639f2c4d 100644 --- a/libc/sock/wsablock.c +++ b/libc/sock/wsablock.c @@ -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; } diff --git a/libc/stdio/fgetln.c b/libc/stdio/fgetln.c index 04f8edfca..483786136 100644 --- a/libc/stdio/fgetln.c +++ b/libc/stdio/fgetln.c @@ -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 diff --git a/libc/stdio/fgets.c b/libc/stdio/fgets.c index 121260ee9..fdd50cd3f 100644 --- a/libc/stdio/fgets.c +++ b/libc/stdio/fgets.c @@ -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 diff --git a/libc/stdio/getline.c b/libc/stdio/getline.c index 88f17836e..48647ac60 100644 --- a/libc/stdio/getline.c +++ b/libc/stdio/getline.c @@ -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); } diff --git a/test/libc/calls/read_test.c b/test/libc/calls/read_test.c index 997d5e719..006002b0f 100644 --- a/test/libc/calls/read_test.c +++ b/test/libc/calls/read_test.c @@ -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; } diff --git a/test/libc/calls/setitimer_test.c b/test/libc/calls/setitimer_test.c index 2f6a5e66d..62a47f182 100644 --- a/test/libc/calls/setitimer_test.c +++ b/test/libc/calls/setitimer_test.c @@ -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)); +} diff --git a/test/libc/calls/timespec_test.c b/test/libc/calls/timespec_test.c index 7869594bf..e6caabb0a 100644 --- a/test/libc/calls/timespec_test.c +++ b/test/libc/calls/timespec_test.c @@ -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)); diff --git a/tool/build/runit.c b/tool/build/runit.c index f90456e97..5db9ea6ae 100644 --- a/tool/build/runit.c +++ b/tool/build/runit.c @@ -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); diff --git a/tool/build/runitd.c b/tool/build/runitd.c index c56342209..b5b596532 100644 --- a/tool/build/runitd.c +++ b/tool/build/runitd.c @@ -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");