mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
Add missing ICANON features
This commit is contained in:
parent
dd8544c3bd
commit
03875beadb
22 changed files with 526 additions and 251 deletions
139
examples/ctrlc.c
139
examples/ctrlc.c
|
@ -7,20 +7,43 @@
|
||||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||||
╚─────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────*/
|
||||||
#endif
|
#endif
|
||||||
#include "libc/assert.h"
|
#include <ctype.h>
|
||||||
#include "libc/calls/calls.h"
|
#include <errno.h>
|
||||||
#include "libc/calls/struct/sigaction.h"
|
#include <limits.h>
|
||||||
#include "libc/errno.h"
|
#include <signal.h>
|
||||||
#include "libc/limits.h"
|
#include <stdio.h>
|
||||||
#include "libc/runtime/runtime.h"
|
#include <string.h>
|
||||||
#include "libc/sock/struct/pollfd.h"
|
#include <termios.h>
|
||||||
#include "libc/stdio/stdio.h"
|
#include <unistd.h>
|
||||||
#include "libc/str/str.h"
|
|
||||||
#include "libc/sysv/consts/f.h"
|
// this program is used by jart for manually testing teletype interrupts
|
||||||
#include "libc/sysv/consts/limits.h"
|
// and canonical mode line editing. this file documents the hidden depth
|
||||||
#include "libc/sysv/consts/o.h"
|
// of 1960's era computer usage, that's entrenched in primitive i/o apis
|
||||||
#include "libc/sysv/consts/poll.h"
|
//
|
||||||
#include "libc/sysv/consts/sig.h"
|
// manual testing checklist:
|
||||||
|
//
|
||||||
|
// - "hello" enter echos "got: hello^J"
|
||||||
|
//
|
||||||
|
// - "hello" ctrl-d echos "got: hello"
|
||||||
|
//
|
||||||
|
// - "hello" ctrl-r echos "^R\nhello"
|
||||||
|
//
|
||||||
|
// - "hello" ctrl-u enter echos "got: ^J"
|
||||||
|
//
|
||||||
|
// - ctrl-d during i/o task prints "got eof" and exits
|
||||||
|
//
|
||||||
|
// - ctrl-d during cpu task gets delayed until read() is called
|
||||||
|
//
|
||||||
|
// - ctrl-c during cpu task echos ^C, then calls SignalHandler()
|
||||||
|
// asynchronously, and program exits
|
||||||
|
//
|
||||||
|
// - ctrl-c during i/o task echos ^C, then calls SignalHandler()
|
||||||
|
// asynchronously, read() raises EINTR, and program exits
|
||||||
|
//
|
||||||
|
// - ctrl-v ctrl-c should echo "^\b" then echo "^C" and insert "\3"
|
||||||
|
//
|
||||||
|
// - ctrl-v ctrl-d should echo "^\b" then echo "^D" and insert "\4"
|
||||||
|
//
|
||||||
|
|
||||||
volatile bool gotsig;
|
volatile bool gotsig;
|
||||||
|
|
||||||
|
@ -34,23 +57,41 @@ void SignalHandler(int sig) {
|
||||||
gotsig = true;
|
gotsig = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this is the easiest way to write a string literal to standard output,
|
||||||
|
// without formatting. printf() has an enormous binary footprint so it's
|
||||||
|
// nice to avoid linking that when it is not needed.
|
||||||
|
#define WRITE(sliteral) write(1, sliteral, sizeof(sliteral) - 1)
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
printf("echoing stdin until ctrl+c is pressed\n");
|
WRITE("echoing stdin until ctrl+c is pressed\n");
|
||||||
|
|
||||||
// you need to set your signal handler using sigaction() rather than
|
// when you type ctrl-c, by default it'll kill the process, unless you
|
||||||
// signal(), since the latter uses .sa_flags=SA_RESTART, which means
|
// define a SIGINT handler. there's multiple ways to do it. the common
|
||||||
// read will restart itself after signals, rather than raising EINTR
|
// way is to say signal(SIGINT, func) which is normally defined to put
|
||||||
|
// the signal handler in Berkeley-style SA_RESTART mode. that means if
|
||||||
|
// a signal handler is called while inside a function like read() then
|
||||||
|
// the read operation will keep going afterwards like nothing happened
|
||||||
|
// which can make it difficult to break your event loop. to avoid this
|
||||||
|
// we can use sigaction() without specifying SA_RESTART in sa_flag and
|
||||||
|
// that'll put the signal in system v mode. this means that whenever a
|
||||||
|
// signal handler function in your program is called during an i/o op,
|
||||||
|
// that i/o op will return an EINTR error, so you can churn your loop.
|
||||||
|
// don't take that error too seriously though since SIGINT can also be
|
||||||
|
// delivered asynchronously, during the times you're crunching numbers
|
||||||
|
// rather than performing i/o which means you get no EINTR to warn you
|
||||||
sigaction(SIGINT, &(struct sigaction){.sa_handler = SignalHandler}, 0);
|
sigaction(SIGINT, &(struct sigaction){.sa_handler = SignalHandler}, 0);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
// some programs are blocked on cpu rather than i/o
|
// asynchronous signals are needed to interrupt math, which we shall
|
||||||
// such programs shall rely on asynchronous signals
|
// simulate here. signals can happen any time any place. that's only
|
||||||
printf("doing cpu task...\n");
|
// not the case when you use sigprocmask() to block signals which is
|
||||||
|
// useful for kicking the can down the road.
|
||||||
|
WRITE("doing cpu task...\n");
|
||||||
for (volatile int i = 0; i < INT_MAX / 5; ++i) {
|
for (volatile int i = 0; i < INT_MAX / 5; ++i) {
|
||||||
if (gotsig) {
|
if (gotsig) {
|
||||||
printf("\rgot ctrl+c asynchronously\n");
|
WRITE("\rgot ctrl+c asynchronously\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,14 +112,18 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
// read data from standard input
|
// read data from standard input
|
||||||
//
|
//
|
||||||
// since this is a blocking operation and we're not performing a
|
// assuming you started this program in your terminal standard input
|
||||||
// cpu-bound operation it is almost with absolute certainty that
|
// will be plugged into your termios driver, which cosmpolitan codes
|
||||||
// when the ctrl-c signal gets delivered, it'll happen in read()
|
// in libc/calls/read-nt.c on windows. your read() function includes
|
||||||
//
|
// a primitive version of readline/linenoise called "canonical mode"
|
||||||
// it's possible to be more precise if we were building library
|
// which lets you edit the data that'll be returned by read() before
|
||||||
// code. for example, you can block signals using sigprocmask()
|
// it's actually returned. for example, if you type hello and enter,
|
||||||
// and then use pselect() to do the waiting.
|
// then "hello\n" will be returned. if you type hello and then ^D or
|
||||||
printf("doing read i/o task...\n");
|
// ctrl-d, then "hello" will be returned. the ctrl-d keystroke is in
|
||||||
|
// fact an ascii control code whose special behavior can be bypassed
|
||||||
|
// if you type ctrl-v ctrl-d and then enter, in which case "\3\n" is
|
||||||
|
// returned, also known as ^D^J.
|
||||||
|
WRITE("doing read i/o task...\n");
|
||||||
int got = read(0, buf, sizeof(buf));
|
int got = read(0, buf, sizeof(buf));
|
||||||
|
|
||||||
// check if the read operation failed
|
// check if the read operation failed
|
||||||
|
@ -94,10 +139,10 @@ int main(int argc, char *argv[]) {
|
||||||
// the \r character is needed so when the line is printed
|
// the \r character is needed so when the line is printed
|
||||||
// it'll overwrite the ^C that got echo'd with the ctrl-c
|
// it'll overwrite the ^C that got echo'd with the ctrl-c
|
||||||
if (gotsig) {
|
if (gotsig) {
|
||||||
printf("\rgot ctrl+c via i/o eintr\n");
|
WRITE("\rgot ctrl+c via i/o eintr\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
} else {
|
} else {
|
||||||
printf("\rgot spurious eintr\n");
|
WRITE("\rgot spurious eintr\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -109,16 +154,34 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
// check if the user typed ctrl-d which closes the input handle
|
// check if the user typed ctrl-d which closes the input handle
|
||||||
if (!got) {
|
if (!got) {
|
||||||
printf("got eof\n");
|
WRITE("got eof\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// relay read data to standard output
|
// visualize line data returned by canonical mode to standard output
|
||||||
//
|
//
|
||||||
// it's usually safe to ignore the return code of write. the
|
// it's usually safe to ignore the return code of write; your system
|
||||||
// operating system will send SIGPIPE if there's any problem
|
// will send SIGPIPE if there's any problem, which kills by default.
|
||||||
// which kills the process by default
|
//
|
||||||
|
// it's possible to use keyboard shortcuts to embed control codes in
|
||||||
|
// the line. so we visualize them using the classic tty notation. it
|
||||||
|
// is also possible to type the ascii representation, so we use bold
|
||||||
|
// to visually distinguish ascii codes. see also o//examples/ttyinfo
|
||||||
write(1, "got: ", 5);
|
write(1, "got: ", 5);
|
||||||
write(1, buf, got);
|
for (int i = 0; i < got; ++i) {
|
||||||
|
if (isascii(buf[i])) {
|
||||||
|
if (iscntrl(buf[i])) {
|
||||||
|
char ctl[2];
|
||||||
|
ctl[0] = '^';
|
||||||
|
ctl[1] = buf[i] ^ 0100;
|
||||||
|
WRITE("\033[1m");
|
||||||
|
write(1, ctl, 2);
|
||||||
|
WRITE("\033[0m");
|
||||||
|
} else {
|
||||||
|
write(1, &buf[i], 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WRITE("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
#ifndef COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
|
#ifndef COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
|
||||||
#define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
|
#define COSMOPOLITAN_LIBC_CALLS_INTERNAL_H_
|
||||||
#include "libc/atomic.h"
|
#include "libc/atomic.h"
|
||||||
#include "libc/intrin/fds.h"
|
#include "libc/calls/struct/sigset.h"
|
||||||
#include "libc/calls/struct/sigval.h"
|
#include "libc/calls/struct/sigval.h"
|
||||||
#include "libc/dce.h"
|
#include "libc/dce.h"
|
||||||
|
#include "libc/intrin/fds.h"
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
#include "libc/stdbool.h"
|
#include "libc/stdbool.h"
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ uint32_t sys_getuid_nt(void);
|
||||||
int __ensurefds_unlocked(int);
|
int __ensurefds_unlocked(int);
|
||||||
void __printfds(struct Fd *, size_t);
|
void __printfds(struct Fd *, size_t);
|
||||||
int CountConsoleInputBytes(void);
|
int CountConsoleInputBytes(void);
|
||||||
|
int CountConsoleInputBytesBlocking(uint32_t, sigset_t);
|
||||||
int FlushConsoleInputBytes(void);
|
int FlushConsoleInputBytes(void);
|
||||||
int64_t GetConsoleInputHandle(void);
|
int64_t GetConsoleInputHandle(void);
|
||||||
int64_t GetConsoleOutputHandle(void);
|
int64_t GetConsoleOutputHandle(void);
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
*
|
*
|
||||||
* @param flags can have AT_EMPTY_PATH or AT_SYMLINK_NOFOLLOW
|
* @param flags can have AT_EMPTY_PATH or AT_SYMLINK_NOFOLLOW
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @return 0 on success, or -1 w/ errno
|
||||||
|
* @raise EROFS if either path is under /zip/...
|
||||||
* @asyncsignalsafe
|
* @asyncsignalsafe
|
||||||
*/
|
*/
|
||||||
int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath,
|
int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath,
|
||||||
|
@ -42,7 +43,7 @@ int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath,
|
||||||
if (_weaken(__zipos_notat) &&
|
if (_weaken(__zipos_notat) &&
|
||||||
((rc = __zipos_notat(olddirfd, oldpath)) == -1 ||
|
((rc = __zipos_notat(olddirfd, oldpath)) == -1 ||
|
||||||
(rc = __zipos_notat(newdirfd, newpath)) == -1)) {
|
(rc = __zipos_notat(newdirfd, newpath)) == -1)) {
|
||||||
STRACE("zipos fchownat not supported yet");
|
rc = erofs();
|
||||||
} else if (!IsWindows()) {
|
} else if (!IsWindows()) {
|
||||||
rc = sys_linkat(olddirfd, oldpath, newdirfd, newpath, flags);
|
rc = sys_linkat(olddirfd, oldpath, newdirfd, newpath, flags);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
int mkdirat(int dirfd, const char *path, unsigned mode) {
|
int mkdirat(int dirfd, const char *path, unsigned mode) {
|
||||||
int rc;
|
int rc;
|
||||||
if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
|
if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
|
||||||
STRACE("zipos mkdirat not supported yet");
|
rc = erofs();
|
||||||
} else if (!IsWindows()) {
|
} else if (!IsWindows()) {
|
||||||
rc = sys_mkdirat(dirfd, path, mode);
|
rc = sys_mkdirat(dirfd, path, mode);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -34,9 +34,8 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
||||||
int sig, handler_was_called;
|
int sig, handler_was_called;
|
||||||
if (_check_cancel() == -1)
|
if (_check_cancel() == -1)
|
||||||
return -1;
|
return -1;
|
||||||
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
|
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
|
||||||
goto HandleSignal;
|
goto HandleSignal;
|
||||||
}
|
|
||||||
int expect = 0;
|
int expect = 0;
|
||||||
atomic_int futex = 0;
|
atomic_int futex = 0;
|
||||||
struct PosixThread *pt = _pthread_self();
|
struct PosixThread *pt = _pthread_self();
|
||||||
|
@ -49,9 +48,11 @@ static textwindows int _park_thread(uint32_t msdelay, sigset_t waitmask,
|
||||||
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
||||||
if (_check_cancel() == -1)
|
if (_check_cancel() == -1)
|
||||||
return -1;
|
return -1;
|
||||||
if (!restartable || (handler_was_called & SIG_HANDLED_NO_RESTART)) {
|
if (handler_was_called & SIG_HANDLED_NO_RESTART)
|
||||||
return eintr();
|
return eintr();
|
||||||
}
|
if (handler_was_called & SIG_HANDLED_SA_RESTART)
|
||||||
|
if (!restartable)
|
||||||
|
return eintr();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,15 +79,15 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
bool ok;
|
bool ok;
|
||||||
uint64_t millis;
|
uint64_t millis;
|
||||||
uint32_t cm, avail, waitfor;
|
uint32_t cm, avail, waitfor;
|
||||||
struct sys_pollfd_nt pipefds[8];
|
struct sys_pollfd_nt pipefds[64];
|
||||||
struct sys_pollfd_nt sockfds[64];
|
struct sys_pollfd_nt sockfds[64];
|
||||||
int pipeindices[ARRAYLEN(pipefds)];
|
int pipeindices[ARRAYLEN(pipefds)];
|
||||||
int sockindices[ARRAYLEN(sockfds)];
|
int sockindices[ARRAYLEN(sockfds)];
|
||||||
struct timespec started, deadline, remain, now;
|
struct timespec deadline, remain, now;
|
||||||
int i, rc, sn, pn, gotinvals, gotpipes, gotsocks;
|
int i, rc, sn, pn, gotinvals, gotpipes, gotsocks;
|
||||||
|
|
||||||
started = timespec_real();
|
waitfor = ms ? *ms : -1u;
|
||||||
deadline = timespec_add(started, timespec_frommillis(ms ? *ms : -1u));
|
deadline = timespec_add(timespec_mono(), timespec_frommillis(waitfor));
|
||||||
|
|
||||||
// do the planning
|
// do the planning
|
||||||
// we need to read static variables
|
// we need to read static variables
|
||||||
|
@ -168,16 +168,39 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
pipefds[i].revents |= POLLERR_;
|
pipefds[i].revents |= POLLERR_;
|
||||||
}
|
}
|
||||||
} else if (GetConsoleMode(pipefds[i].handle, &cm)) {
|
} else if (GetConsoleMode(pipefds[i].handle, &cm)) {
|
||||||
switch (CountConsoleInputBytes()) {
|
// some programs like bash like to poll([stdin], 1, -1) so let's
|
||||||
case 0:
|
// avoid busy looping in such cases. we could generalize this to
|
||||||
break;
|
// always avoid busy loops, but we'd need poll to launch threads
|
||||||
case -1:
|
if (pn == 1 && sn == 0 && (pipefds[i].events & POLLRDNORM_)) {
|
||||||
pipefds[i].revents &= ~POLLWRNORM_;
|
int err = errno;
|
||||||
pipefds[i].revents |= POLLHUP_;
|
switch (CountConsoleInputBytesBlocking(waitfor, sigmask)) {
|
||||||
break;
|
case -1:
|
||||||
default:
|
if (errno == EINTR || errno == ECANCELED)
|
||||||
pipefds[i].revents |= POLLRDNORM_;
|
return -1;
|
||||||
break;
|
errno = err;
|
||||||
|
pipefds[i].revents &= ~POLLWRNORM_;
|
||||||
|
pipefds[i].revents |= POLLERR_;
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
pipefds[i].revents &= ~POLLWRNORM_;
|
||||||
|
pipefds[i].revents |= POLLHUP_;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pipefds[i].revents |= POLLRDNORM_;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (CountConsoleInputBytes()) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
pipefds[i].revents &= ~POLLWRNORM_;
|
||||||
|
pipefds[i].revents |= POLLHUP_;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pipefds[i].revents |= POLLRDNORM_;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// we have no way of polling if a non-socket is readable yet
|
// we have no way of polling if a non-socket is readable yet
|
||||||
|
@ -202,7 +225,7 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
// check for pending signals, thread cancelation, etc.
|
// check for pending signals, thread cancelation, etc.
|
||||||
waitfor = 0;
|
waitfor = 0;
|
||||||
if (!gotinvals && !gotsocks && !gotpipes) {
|
if (!gotinvals && !gotsocks && !gotpipes) {
|
||||||
now = timespec_real();
|
now = timespec_mono();
|
||||||
if (timespec_cmp(now, deadline) < 0) {
|
if (timespec_cmp(now, deadline) < 0) {
|
||||||
remain = timespec_sub(deadline, now);
|
remain = timespec_sub(deadline, now);
|
||||||
millis = timespec_tomillis(remain);
|
millis = timespec_tomillis(remain);
|
||||||
|
@ -211,7 +234,7 @@ static textwindows int sys_poll_nt_impl(struct pollfd *fds, uint64_t nfds,
|
||||||
if (waitfor) {
|
if (waitfor) {
|
||||||
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
|
POLLTRACE("poll() sleeping for %'d out of %'lu ms", waitfor,
|
||||||
timespec_tomillis(remain));
|
timespec_tomillis(remain));
|
||||||
if ((rc = _park_norestart(waitfor, sigmask)) == -1)
|
if (_park_norestart(waitfor, sigmask) == -1)
|
||||||
return -1; // eintr, ecanceled, etc.
|
return -1; // eintr, ecanceled, etc.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "libc/calls/state.internal.h"
|
#include "libc/calls/state.internal.h"
|
||||||
#include "libc/calls/struct/iovec.h"
|
#include "libc/calls/struct/iovec.h"
|
||||||
#include "libc/calls/struct/sigset.internal.h"
|
#include "libc/calls/struct/sigset.internal.h"
|
||||||
|
#include "libc/calls/struct/timespec.h"
|
||||||
#include "libc/calls/syscall_support-nt.internal.h"
|
#include "libc/calls/syscall_support-nt.internal.h"
|
||||||
#include "libc/cosmo.h"
|
#include "libc/cosmo.h"
|
||||||
#include "libc/ctype.h"
|
#include "libc/ctype.h"
|
||||||
|
@ -31,7 +32,9 @@
|
||||||
#include "libc/intrin/describeflags.h"
|
#include "libc/intrin/describeflags.h"
|
||||||
#include "libc/intrin/dll.h"
|
#include "libc/intrin/dll.h"
|
||||||
#include "libc/intrin/fds.h"
|
#include "libc/intrin/fds.h"
|
||||||
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/intrin/nomultics.h"
|
#include "libc/intrin/nomultics.h"
|
||||||
|
#include "libc/intrin/safemacros.h"
|
||||||
#include "libc/intrin/strace.h"
|
#include "libc/intrin/strace.h"
|
||||||
#include "libc/intrin/weaken.h"
|
#include "libc/intrin/weaken.h"
|
||||||
#include "libc/macros.h"
|
#include "libc/macros.h"
|
||||||
|
@ -132,6 +135,7 @@ struct Keystrokes {
|
||||||
atomic_uint once;
|
atomic_uint once;
|
||||||
bool end_of_file;
|
bool end_of_file;
|
||||||
bool ohno_decckm;
|
bool ohno_decckm;
|
||||||
|
bool bypass_mode;
|
||||||
uint16_t utf16hs;
|
uint16_t utf16hs;
|
||||||
int16_t freekeys;
|
int16_t freekeys;
|
||||||
int64_t cin, cot;
|
int64_t cin, cot;
|
||||||
|
@ -149,12 +153,12 @@ textwindows void WipeKeystrokes(void) {
|
||||||
bzero(&__keystroke, sizeof(__keystroke));
|
bzero(&__keystroke, sizeof(__keystroke));
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void FreeKeystrokeImpl(struct Dll *key) {
|
textwindows static void FreeKeystrokeImpl(struct Dll *key) {
|
||||||
dll_make_first(&__keystroke.free, key);
|
dll_make_first(&__keystroke.free, key);
|
||||||
++__keystroke.freekeys;
|
++__keystroke.freekeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows struct Keystroke *NewKeystroke(void) {
|
textwindows static struct Keystroke *NewKeystroke(void) {
|
||||||
struct Dll *e = dll_first(__keystroke.free);
|
struct Dll *e = dll_first(__keystroke.free);
|
||||||
if (!e) // See MIN(freekeys) before ReadConsoleInput()
|
if (!e) // See MIN(freekeys) before ReadConsoleInput()
|
||||||
__builtin_trap();
|
__builtin_trap();
|
||||||
|
@ -165,18 +169,18 @@ static textwindows struct Keystroke *NewKeystroke(void) {
|
||||||
return k;
|
return k;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void FreeKeystroke(struct Dll **list, struct Dll *key) {
|
textwindows static void FreeKeystroke(struct Dll **list, struct Dll *key) {
|
||||||
dll_remove(list, key);
|
dll_remove(list, key);
|
||||||
FreeKeystrokeImpl(key);
|
FreeKeystrokeImpl(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void FreeKeystrokes(struct Dll **list) {
|
textwindows static void FreeKeystrokes(struct Dll **list) {
|
||||||
struct Dll *key;
|
struct Dll *key;
|
||||||
while ((key = dll_first(*list)))
|
while ((key = dll_first(*list)))
|
||||||
FreeKeystroke(list, key);
|
FreeKeystroke(list, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void OpenConsole(void) {
|
textwindows static void OpenConsole(void) {
|
||||||
__keystroke.vkt = kVirtualKey;
|
__keystroke.vkt = kVirtualKey;
|
||||||
__keystroke.cin = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite,
|
__keystroke.cin = CreateFile(u"CONIN$", kNtGenericRead | kNtGenericWrite,
|
||||||
kNtFileShareRead, 0, kNtOpenExisting, 0, 0);
|
kNtFileShareRead, 0, kNtOpenExisting, 0, 0);
|
||||||
|
@ -188,21 +192,21 @@ static textwindows void OpenConsole(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows int AddSignal(int sig) {
|
textwindows static int AddSignal(int sig) {
|
||||||
atomic_fetch_or_explicit(&__get_tls()->tib_sigpending, 1ull << (sig - 1),
|
atomic_fetch_or_explicit(&__get_tls()->tib_sigpending, 1ull << (sig - 1),
|
||||||
memory_order_relaxed);
|
memory_order_relaxed);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void InitConsole(void) {
|
textwindows static void InitConsole(void) {
|
||||||
cosmo_once(&__keystroke.once, OpenConsole);
|
cosmo_once(&__keystroke.once, OpenConsole);
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void LockKeystrokes(void) {
|
textwindows static void LockKeystrokes(void) {
|
||||||
pthread_mutex_lock(&__keystroke.lock);
|
pthread_mutex_lock(&__keystroke.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void UnlockKeystrokes(void) {
|
textwindows static void UnlockKeystrokes(void) {
|
||||||
pthread_mutex_unlock(&__keystroke.lock);
|
pthread_mutex_unlock(&__keystroke.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,14 +220,14 @@ textwindows int64_t GetConsoleOutputHandle(void) {
|
||||||
return __keystroke.cot;
|
return __keystroke.cot;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows bool IsMouseModeCommand(int x) {
|
textwindows static bool IsMouseModeCommand(int x) {
|
||||||
return x == 1000 || // SET_VT200_MOUSE
|
return x == 1000 || // SET_VT200_MOUSE
|
||||||
x == 1002 || // SET_BTN_EVENT_MOUSE
|
x == 1002 || // SET_BTN_EVENT_MOUSE
|
||||||
x == 1006 || // SET_SGR_EXT_MODE_MOUSE
|
x == 1006 || // SET_SGR_EXT_MODE_MOUSE
|
||||||
x == 1015; // SET_URXVT_EXT_MODE_MOUSE
|
x == 1015; // SET_URXVT_EXT_MODE_MOUSE
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
textwindows static int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
||||||
for (int i = 0; __keystroke.vkt[i].vk; ++i) {
|
for (int i = 0; __keystroke.vkt[i].vk; ++i) {
|
||||||
if (__keystroke.vkt[i].vk == vk) {
|
if (__keystroke.vkt[i].vk == vk) {
|
||||||
if (shift && ctrl) {
|
if (shift && ctrl) {
|
||||||
|
@ -240,7 +244,7 @@ static textwindows int GetVirtualKey(uint16_t vk, bool shift, bool ctrl) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
textwindows static int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
||||||
|
|
||||||
uint32_t c = r->Event.KeyEvent.uChar.UnicodeChar;
|
uint32_t c = r->Event.KeyEvent.uChar.UnicodeChar;
|
||||||
uint16_t vk = r->Event.KeyEvent.wVirtualKeyCode;
|
uint16_t vk = r->Event.KeyEvent.wVirtualKeyCode;
|
||||||
|
@ -327,7 +331,7 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
||||||
// handle ctrl-\ and ctrl-c
|
// handle ctrl-\ and ctrl-c
|
||||||
// note we define _POSIX_VDISABLE as zero
|
// note we define _POSIX_VDISABLE as zero
|
||||||
// tcsetattr() lets anyone reconfigure these keybindings
|
// tcsetattr() lets anyone reconfigure these keybindings
|
||||||
if (c && !(__ttyconf.magic & kTtyNoIsigs)) {
|
if (c && !(__ttyconf.magic & kTtyNoIsigs) && !__keystroke.bypass_mode) {
|
||||||
if (c == __ttyconf.vintr) {
|
if (c == __ttyconf.vintr) {
|
||||||
return AddSignal(SIGINT);
|
return AddSignal(SIGINT);
|
||||||
} else if (c == __ttyconf.vquit) {
|
} else if (c == __ttyconf.vquit) {
|
||||||
|
@ -337,7 +341,9 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
||||||
|
|
||||||
// handle ctrl-d which generates end-of-file, unless pending line data
|
// handle ctrl-d which generates end-of-file, unless pending line data
|
||||||
// is present, in which case we flush that without the newline instead
|
// is present, in which case we flush that without the newline instead
|
||||||
if (c && c == __ttyconf.veof && !(__ttyconf.magic & kTtyUncanon)) {
|
if (c && c == __ttyconf.veof && //
|
||||||
|
!__keystroke.bypass_mode && //
|
||||||
|
!(__ttyconf.magic & kTtyUncanon)) {
|
||||||
if (dll_is_empty(__keystroke.line)) {
|
if (dll_is_empty(__keystroke.line)) {
|
||||||
__keystroke.end_of_file = true;
|
__keystroke.end_of_file = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -367,7 +373,7 @@ static textwindows int ProcessKeyEvent(const struct NtInputRecord *r, char *p) {
|
||||||
// - write(1, "\e[?1000;1002;1015;1006h") to enable
|
// - write(1, "\e[?1000;1002;1015;1006h") to enable
|
||||||
// - write(1, "\e[?1000;1002;1015;1006l") to disable
|
// - write(1, "\e[?1000;1002;1015;1006l") to disable
|
||||||
// See o//examples/ttyinfo and o//tool/viz/life
|
// See o//examples/ttyinfo and o//tool/viz/life
|
||||||
static textwindows int ProcessMouseEvent(const struct NtInputRecord *r,
|
textwindows static int ProcessMouseEvent(const struct NtInputRecord *r,
|
||||||
char *b) {
|
char *b) {
|
||||||
char *p = b;
|
char *p = b;
|
||||||
unsigned char e = 0;
|
unsigned char e = 0;
|
||||||
|
@ -424,7 +430,7 @@ static textwindows int ProcessMouseEvent(const struct NtInputRecord *r,
|
||||||
return p - b;
|
return p - b;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
|
textwindows static int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
|
||||||
char p[hasatleast 23]) {
|
char p[hasatleast 23]) {
|
||||||
switch (r->EventType) {
|
switch (r->EventType) {
|
||||||
case kNtKeyEvent:
|
case kNtKeyEvent:
|
||||||
|
@ -438,18 +444,19 @@ static textwindows int ConvertConsoleInputToAnsi(const struct NtInputRecord *r,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void WriteTty(const char *p, size_t n) {
|
textwindows static void WriteTty(const char *p, size_t n) {
|
||||||
WriteFile(__keystroke.cot, p, n, 0, 0);
|
WriteFile(__keystroke.cot, p, n, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows bool IsCtl(int c) {
|
textwindows static bool IsCtl(int c, bool escape_harder) {
|
||||||
return isascii(c) && iscntrl(c) && c != '\n' && c != '\t';
|
return isascii(c) && iscntrl(c) &&
|
||||||
|
(escape_harder || (c != '\n' && c != '\t'));
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void WriteCtl(const char *p, size_t n) {
|
textwindows static void WriteCtl(const char *p, size_t n, bool escape_harder) {
|
||||||
size_t i;
|
size_t i;
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
if (IsCtl(p[i])) {
|
if (IsCtl(p[i], escape_harder)) {
|
||||||
char ctl[2];
|
char ctl[2];
|
||||||
ctl[0] = '^';
|
ctl[0] = '^';
|
||||||
ctl[1] = p[i] ^ 0100;
|
ctl[1] = p[i] ^ 0100;
|
||||||
|
@ -460,19 +467,22 @@ static textwindows void WriteCtl(const char *p, size_t n) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void EchoTty(const char *p, size_t n) {
|
textwindows static void EchoTty(const char *p, size_t n, bool escape_harder) {
|
||||||
if (__ttyconf.magic & kTtyEchoRaw) {
|
if (!(__ttyconf.magic & kTtySilence)) {
|
||||||
WriteTty(p, n);
|
if (__ttyconf.magic & kTtyEchoRaw) {
|
||||||
} else {
|
WriteTty(p, n);
|
||||||
WriteCtl(p, n);
|
} else {
|
||||||
|
WriteCtl(p, n, escape_harder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void EraseCharacter(void) {
|
textwindows static void EraseCharacter(bool should_echo) {
|
||||||
WriteTty("\b \b", 3);
|
if (should_echo)
|
||||||
|
WriteTty("\b \b", 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows bool EraseKeystroke(void) {
|
textwindows static bool EraseKeystroke(bool should_echo) {
|
||||||
struct Dll *e;
|
struct Dll *e;
|
||||||
if ((e = dll_last(__keystroke.line))) {
|
if ((e = dll_last(__keystroke.line))) {
|
||||||
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
||||||
|
@ -480,9 +490,9 @@ static textwindows bool EraseKeystroke(void) {
|
||||||
for (int i = k->buflen; i--;) {
|
for (int i = k->buflen; i--;) {
|
||||||
if ((k->buf[i] & 0300) == 0200)
|
if ((k->buf[i] & 0300) == 0200)
|
||||||
continue; // utf-8 cont
|
continue; // utf-8 cont
|
||||||
EraseCharacter();
|
EraseCharacter(should_echo);
|
||||||
if (!(__ttyconf.magic & kTtyEchoRaw) && IsCtl(k->buf[i]))
|
if (!(__ttyconf.magic & kTtyEchoRaw) && IsCtl(k->buf[i], true))
|
||||||
EraseCharacter();
|
EraseCharacter(should_echo);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -490,7 +500,17 @@ static textwindows bool EraseKeystroke(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
|
textwindows static int IsLookingAtSpace(void) {
|
||||||
|
struct Dll *e;
|
||||||
|
if ((e = dll_last(__keystroke.line))) {
|
||||||
|
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
||||||
|
return k->buflen == 1 && isascii(k->buf[0]) && isspace(k->buf[0]);
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
textwindows static void IngestConsoleInputRecord(struct NtInputRecord *r) {
|
||||||
|
|
||||||
// convert win32 console event into ansi
|
// convert win32 console event into ansi
|
||||||
int len;
|
int len;
|
||||||
|
@ -498,19 +518,103 @@ static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
|
||||||
if (!(len = ConvertConsoleInputToAnsi(r, buf)))
|
if (!(len = ConvertConsoleInputToAnsi(r, buf)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// handle ctrl-v in canonical mode
|
||||||
|
// the next keystroke will bypass input processing
|
||||||
|
if (!(__ttyconf.magic & kTtyUncanon) && // ICANON
|
||||||
|
!(__ttyconf.magic & kTtyNoIexten)) { // IEXTEN
|
||||||
|
if (__keystroke.bypass_mode) {
|
||||||
|
struct Keystroke *k = NewKeystroke();
|
||||||
|
memcpy(k->buf, buf, sizeof(k->buf));
|
||||||
|
k->buflen = len;
|
||||||
|
dll_make_last(&__keystroke.line, &k->elem);
|
||||||
|
EchoTty(buf, len, true);
|
||||||
|
if (!__keystroke.freekeys) {
|
||||||
|
dll_make_last(&__keystroke.list, __keystroke.line);
|
||||||
|
__keystroke.line = 0;
|
||||||
|
}
|
||||||
|
__keystroke.bypass_mode = false;
|
||||||
|
return;
|
||||||
|
} else if (len == 1 && buf[0] && //
|
||||||
|
(buf[0] & 255) == __ttyconf.vlnext) {
|
||||||
|
__keystroke.bypass_mode = true;
|
||||||
|
if (!(__ttyconf.magic & kTtySilence) && // ECHO
|
||||||
|
!(__ttyconf.magic & kTtyEchoRaw)) // ECHOCTL
|
||||||
|
WriteTty("^\b", 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// handle backspace in canonical mode
|
// handle backspace in canonical mode
|
||||||
if (len == 1 && buf[0] && //
|
if (len == 1 && buf[0] && //
|
||||||
(buf[0] & 255) == __ttyconf.verase && //
|
(buf[0] & 255) == __ttyconf.verase && //
|
||||||
!(__ttyconf.magic & kTtyUncanon)) {
|
!(__ttyconf.magic & kTtyUncanon) && //
|
||||||
EraseKeystroke();
|
!(__ttyconf.magic & kTtyNoIexten)) {
|
||||||
|
bool should_visually_erase = //
|
||||||
|
!(__ttyconf.magic & kTtySilence) && // ECHO
|
||||||
|
!(__ttyconf.magic & kTtyNoEchoe); // ECHOE
|
||||||
|
EraseKeystroke(should_visually_erase);
|
||||||
|
if (!(__ttyconf.magic & kTtySilence) && // ECHO
|
||||||
|
(__ttyconf.magic & kTtyNoEchoe) && // !ECHOE
|
||||||
|
!(__ttyconf.magic & kTtyEchoRaw)) // ECHOCTL
|
||||||
|
WriteCtl(buf, len, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle ctrl-w in canonical mode
|
||||||
|
// this lets you erase the last word
|
||||||
|
if (len == 1 && buf[0] && //
|
||||||
|
(buf[0] & 255) == __ttyconf.vwerase && //
|
||||||
|
!(__ttyconf.magic & kTtyUncanon) && //
|
||||||
|
!(__ttyconf.magic & kTtyNoIexten)) {
|
||||||
|
bool should_visually_erase = //
|
||||||
|
!(__ttyconf.magic & kTtySilence) && // ECHO
|
||||||
|
!(__ttyconf.magic & kTtyNoEchoe); // ECHOE
|
||||||
|
while (IsLookingAtSpace() == 1)
|
||||||
|
EraseKeystroke(should_visually_erase);
|
||||||
|
while (IsLookingAtSpace() == 0)
|
||||||
|
EraseKeystroke(should_visually_erase);
|
||||||
|
if (!(__ttyconf.magic & kTtySilence) && // ECHO
|
||||||
|
(__ttyconf.magic & kTtyNoEchoe) && // !ECHOE
|
||||||
|
!(__ttyconf.magic & kTtyEchoRaw)) // ECHOCTL
|
||||||
|
WriteCtl(buf, len, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle kill in canonical mode
|
// handle kill in canonical mode
|
||||||
|
// this clears the line you're editing
|
||||||
if (len == 1 && buf[0] && //
|
if (len == 1 && buf[0] && //
|
||||||
(buf[0] & 255) == __ttyconf.vkill && //
|
(buf[0] & 255) == __ttyconf.vkill && //
|
||||||
!(__ttyconf.magic & kTtyUncanon)) {
|
!(__ttyconf.magic & kTtyUncanon) && //
|
||||||
while (EraseKeystroke()) {
|
!(__ttyconf.magic & kTtyNoIexten)) {
|
||||||
|
bool should_visually_kill = //
|
||||||
|
!(__ttyconf.magic & kTtySilence) && // ECHO
|
||||||
|
!(__ttyconf.magic & kTtyNoEchok) && // ECHOK
|
||||||
|
!(__ttyconf.magic & kTtyNoEchoke); // ECHOKE
|
||||||
|
while (EraseKeystroke(should_visually_kill)) {
|
||||||
|
}
|
||||||
|
if (!(__ttyconf.magic & kTtySilence) && // ECHO
|
||||||
|
!(__ttyconf.magic & kTtyNoEchok) && // ECHOK
|
||||||
|
(__ttyconf.magic & kTtyNoEchoke) && // !ECHOKE
|
||||||
|
!(__ttyconf.magic & kTtyEchoRaw)) // ECHOCTL
|
||||||
|
WriteCtl(buf, len, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle ctrl-r in canonical mode
|
||||||
|
// this reprints the line you're editing
|
||||||
|
if (len == 1 && buf[0] && //
|
||||||
|
(buf[0] & 255) == __ttyconf.vreprint && //
|
||||||
|
!(__ttyconf.magic & kTtyUncanon) && // ICANON
|
||||||
|
!(__ttyconf.magic & kTtyNoIexten) && // IEXTEN
|
||||||
|
!(__ttyconf.magic & kTtySilence)) { // ECHO
|
||||||
|
struct Dll *e;
|
||||||
|
if (!(__ttyconf.magic & kTtyEchoRaw))
|
||||||
|
WriteCtl(buf, len, true);
|
||||||
|
WriteTty("\r\n", 2);
|
||||||
|
for (e = dll_first(__keystroke.line); e;
|
||||||
|
e = dll_next(__keystroke.line, e)) {
|
||||||
|
struct Keystroke *k = KEYSTROKE_CONTAINER(e);
|
||||||
|
WriteCtl(k->buf, k->buflen, true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -522,8 +626,7 @@ static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
|
||||||
|
|
||||||
// echo input if it was successfully recorded
|
// echo input if it was successfully recorded
|
||||||
// assuming the win32 console isn't doing it already
|
// assuming the win32 console isn't doing it already
|
||||||
if (!(__ttyconf.magic & kTtySilence))
|
EchoTty(buf, len, false);
|
||||||
EchoTty(buf, len);
|
|
||||||
|
|
||||||
// save keystroke to appropriate list
|
// save keystroke to appropriate list
|
||||||
if (__ttyconf.magic & kTtyUncanon) {
|
if (__ttyconf.magic & kTtyUncanon) {
|
||||||
|
@ -535,14 +638,15 @@ static textwindows void IngestConsoleInputRecord(struct NtInputRecord *r) {
|
||||||
if (!__keystroke.freekeys || (len == 1 && buf[0] &&
|
if (!__keystroke.freekeys || (len == 1 && buf[0] &&
|
||||||
((buf[0] & 255) == '\n' || //
|
((buf[0] & 255) == '\n' || //
|
||||||
(buf[0] & 255) == __ttyconf.veol || //
|
(buf[0] & 255) == __ttyconf.veol || //
|
||||||
(buf[0] & 255) == __ttyconf.veol2))) {
|
((buf[0] & 255) == __ttyconf.veol2 &&
|
||||||
|
!(__ttyconf.magic & kTtyNoIexten))))) {
|
||||||
dll_make_last(&__keystroke.list, __keystroke.line);
|
dll_make_last(&__keystroke.list, __keystroke.line);
|
||||||
__keystroke.line = 0;
|
__keystroke.line = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows void IngestConsoleInput(void) {
|
textwindows static void IngestConsoleInput(void) {
|
||||||
uint32_t i, n;
|
uint32_t i, n;
|
||||||
struct NtInputRecord records[16];
|
struct NtInputRecord records[16];
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
@ -552,7 +656,7 @@ static textwindows void IngestConsoleInput(void) {
|
||||||
return;
|
return;
|
||||||
if (!GetNumberOfConsoleInputEvents(__keystroke.cin, &n))
|
if (!GetNumberOfConsoleInputEvents(__keystroke.cin, &n))
|
||||||
goto UnexpectedEof;
|
goto UnexpectedEof;
|
||||||
if (!n)
|
if (!n || !__keystroke.freekeys)
|
||||||
return;
|
return;
|
||||||
n = MIN(__keystroke.freekeys, MIN(ARRAYLEN(records), n));
|
n = MIN(__keystroke.freekeys, MIN(ARRAYLEN(records), n));
|
||||||
if (!ReadConsoleInput(__keystroke.cin, records, n, &n))
|
if (!ReadConsoleInput(__keystroke.cin, records, n, &n))
|
||||||
|
@ -657,12 +761,11 @@ textwindows void InterceptTerminalCommands(const char *data, size_t size) {
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cm2 != cm) {
|
if (cm2 != cm)
|
||||||
SetConsoleMode(GetConsoleInputHandle(), cm2);
|
SetConsoleMode(GetConsoleInputHandle(), cm2);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows bool DigestConsoleInput(char *data, size_t size, int *rc) {
|
textwindows static bool DigestConsoleInput(char *data, size_t size, int *rc) {
|
||||||
|
|
||||||
// handle eof once available input is consumed
|
// handle eof once available input is consumed
|
||||||
if (dll_is_empty(__keystroke.list) && __keystroke.end_of_file) {
|
if (dll_is_empty(__keystroke.list) && __keystroke.end_of_file) {
|
||||||
|
@ -702,21 +805,42 @@ static textwindows bool DigestConsoleInput(char *data, size_t size, int *rc) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
|
textwindows static uint32_t DisableProcessedInput(void) {
|
||||||
|
// the time has come to ensure that ctrl-v ctrl-c works in icanon mode
|
||||||
|
// we're perfectly capable of generating a SIGINT or SIGQUIT ourselves
|
||||||
|
// while the cosmo termios driver is in control; so we disable windows
|
||||||
|
// console input processing for now; we'll turn it back on when we are
|
||||||
|
// done, since it's useful for ensuring asynchronous signal deliveries
|
||||||
|
uint32_t inmode = 0;
|
||||||
|
if (GetConsoleMode(__keystroke.cin, &inmode))
|
||||||
|
if (inmode & kNtEnableProcessedInput)
|
||||||
|
SetConsoleMode(__keystroke.cin, inmode & ~kNtEnableProcessedInput);
|
||||||
|
return inmode;
|
||||||
|
}
|
||||||
|
|
||||||
|
textwindows static void RestoreProcessedInput(uint32_t inmode) {
|
||||||
|
// re-enable win32 console input processing, if it was enabled when we
|
||||||
|
// started, and no signal handler callbacks changed things in-between.
|
||||||
|
if (inmode & kNtEnableProcessedInput) {
|
||||||
|
uint32_t inmode2;
|
||||||
|
if (GetConsoleMode(__keystroke.cin, &inmode2))
|
||||||
|
if (inmode2 == (inmode & ~kNtEnableProcessedInput))
|
||||||
|
SetConsoleMode(__keystroke.cin, inmode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
textwindows static int CountConsoleInputBytesBlockingImpl(uint32_t ms,
|
||||||
|
sigset_t waitmask,
|
||||||
|
bool restartable) {
|
||||||
int sig;
|
int sig;
|
||||||
int64_t sem;
|
int64_t sem;
|
||||||
uint32_t wi, ms = -1;
|
uint32_t wi;
|
||||||
if (!__ttyconf.vmin) {
|
struct timespec now, deadline;
|
||||||
if (!__ttyconf.vtime) {
|
InitConsole();
|
||||||
return 0; // non-blocking w/o raising eagain
|
deadline = timespec_add(timespec_mono(), timespec_frommillis(ms));
|
||||||
} else {
|
RestartOperation:
|
||||||
ms = __ttyconf.vtime * 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_check_cancel() == -1)
|
if (_check_cancel() == -1)
|
||||||
return -1;
|
return -1;
|
||||||
if (f->flags & _O_NONBLOCK)
|
|
||||||
return eagain();
|
|
||||||
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
|
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
|
||||||
goto DeliverSignal;
|
goto DeliverSignal;
|
||||||
struct PosixThread *pt = _pthread_self();
|
struct PosixThread *pt = _pthread_self();
|
||||||
|
@ -726,12 +850,40 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
|
||||||
wi = WaitForMultipleObjects(2, (int64_t[2]){__keystroke.cin, sem}, 0, ms);
|
wi = WaitForMultipleObjects(2, (int64_t[2]){__keystroke.cin, sem}, 0, ms);
|
||||||
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
atomic_store_explicit(&pt->pt_blocker, 0, memory_order_release);
|
||||||
CloseHandle(sem);
|
CloseHandle(sem);
|
||||||
|
|
||||||
|
// check for wait timeout
|
||||||
if (wi == kNtWaitTimeout)
|
if (wi == kNtWaitTimeout)
|
||||||
return 0; // vtime elapsed
|
return etimedout();
|
||||||
if (wi == 0)
|
|
||||||
return -2; // console data
|
// handle event on console handle. this means we can now read from the
|
||||||
|
// conosle without blocking. so the first thing we do is slurp up your
|
||||||
|
// keystroke data. some of those keystrokes might cause a signal to be
|
||||||
|
// raised. so we need to check for pending signals again and handle it
|
||||||
|
if (wi == 0) {
|
||||||
|
int got = CountConsoleInputBytes();
|
||||||
|
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
|
||||||
|
goto DeliverSignal;
|
||||||
|
if (got == -1)
|
||||||
|
// this is a bona fide eof and console errors are logged to strace
|
||||||
|
return 0;
|
||||||
|
if (got == 0) {
|
||||||
|
// this can happen for multiple reasons. first our driver controls
|
||||||
|
// user interactions in canonical mode. secondly we could lose the
|
||||||
|
// race with another thread that's reading input.
|
||||||
|
now = timespec_mono();
|
||||||
|
if (timespec_cmp(now, deadline) >= 0)
|
||||||
|
return etimedout();
|
||||||
|
ms = min(-1u, timespec_tomillis(timespec_sub(deadline, now)));
|
||||||
|
goto RestartOperation;
|
||||||
|
}
|
||||||
|
return got;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle wait itself failing
|
||||||
if (wi != 1)
|
if (wi != 1)
|
||||||
return __winerr(); // wait failed
|
return __winerr();
|
||||||
|
|
||||||
|
// handle event on throwaway semaphore, it is poked by signal delivery
|
||||||
if (_weaken(__sig_get)) {
|
if (_weaken(__sig_get)) {
|
||||||
if (!(sig = _weaken(__sig_get)(waitmask)))
|
if (!(sig = _weaken(__sig_get)(waitmask)))
|
||||||
return eintr();
|
return eintr();
|
||||||
|
@ -739,24 +891,57 @@ static textwindows int WaitForConsole(struct Fd *f, sigset_t waitmask) {
|
||||||
int handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
int handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
||||||
if (_check_cancel() == -1)
|
if (_check_cancel() == -1)
|
||||||
return -1;
|
return -1;
|
||||||
if (!(handler_was_called & SIG_HANDLED_NO_RESTART))
|
if (handler_was_called & SIG_HANDLED_NO_RESTART)
|
||||||
return -2;
|
return eintr();
|
||||||
|
if (handler_was_called & SIG_HANDLED_SA_RESTART)
|
||||||
|
if (!restartable)
|
||||||
|
return eintr();
|
||||||
}
|
}
|
||||||
return eintr();
|
goto RestartOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows ssize_t ReadFromConsole(struct Fd *f, void *data,
|
textwindows int CountConsoleInputBytesBlocking(uint32_t ms, sigset_t waitmask) {
|
||||||
|
uint32_t inmode = DisableProcessedInput();
|
||||||
|
int rc = CountConsoleInputBytesBlockingImpl(ms, waitmask, false);
|
||||||
|
RestoreProcessedInput(inmode);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
textwindows static int WaitToReadFromConsole(struct Fd *f, sigset_t waitmask) {
|
||||||
|
uint32_t ms = -1;
|
||||||
|
if (!__ttyconf.vmin) {
|
||||||
|
if (!__ttyconf.vtime) {
|
||||||
|
return 0; // non-blocking w/o raising eagain
|
||||||
|
} else {
|
||||||
|
ms = __ttyconf.vtime * 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (f->flags & _O_NONBLOCK)
|
||||||
|
return eagain();
|
||||||
|
int olderr = errno;
|
||||||
|
int rc = CountConsoleInputBytesBlockingImpl(ms, waitmask, true);
|
||||||
|
if (rc == -1 && errno == ETIMEDOUT) {
|
||||||
|
// read() never raises ETIMEDOUT so if vtime elapses we raise an EOF
|
||||||
|
errno = olderr;
|
||||||
|
rc = 0;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
textwindows static ssize_t ReadFromConsole(struct Fd *f, void *data,
|
||||||
size_t size, sigset_t waitmask) {
|
size_t size, sigset_t waitmask) {
|
||||||
int rc;
|
int rc;
|
||||||
InitConsole();
|
InitConsole();
|
||||||
|
uint32_t inmode = DisableProcessedInput();
|
||||||
do {
|
do {
|
||||||
LockKeystrokes();
|
LockKeystrokes();
|
||||||
IngestConsoleInput();
|
IngestConsoleInput();
|
||||||
bool done = DigestConsoleInput(data, size, &rc);
|
bool done = DigestConsoleInput(data, size, &rc);
|
||||||
UnlockKeystrokes();
|
UnlockKeystrokes();
|
||||||
if (done)
|
if (done)
|
||||||
return rc;
|
break;
|
||||||
} while ((rc = WaitForConsole(f, waitmask)) == -2);
|
} while ((rc = WaitToReadFromConsole(f, waitmask)) > 0);
|
||||||
|
RestoreProcessedInput(inmode);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,7 +979,7 @@ textwindows ssize_t ReadBuffer(int fd, void *data, size_t size, int64_t offset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static textwindows ssize_t ReadIovecs(int fd, const struct iovec *iov,
|
textwindows static ssize_t ReadIovecs(int fd, const struct iovec *iov,
|
||||||
size_t iovlen, int64_t opt_offset,
|
size_t iovlen, int64_t opt_offset,
|
||||||
sigset_t waitmask) {
|
sigset_t waitmask) {
|
||||||
ssize_t rc;
|
ssize_t rc;
|
||||||
|
|
|
@ -57,6 +57,7 @@ ssize_t readlinkat(int dirfd, const char *path, char *buf, size_t bufsiz) {
|
||||||
} else if (_weaken(__zipos_notat) &&
|
} else if (_weaken(__zipos_notat) &&
|
||||||
(bytes = __zipos_notat(dirfd, path)) == -1) {
|
(bytes = __zipos_notat(dirfd, path)) == -1) {
|
||||||
STRACE("TODO: zipos support for readlinkat");
|
STRACE("TODO: zipos support for readlinkat");
|
||||||
|
bytes = einval();
|
||||||
} else if (!IsWindows()) {
|
} else if (!IsWindows()) {
|
||||||
bytes = sys_readlinkat(dirfd, path, buf, bufsiz);
|
bytes = sys_readlinkat(dirfd, path, buf, bufsiz);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -86,9 +86,8 @@ RestartOperation:
|
||||||
__cursor_unlock(f->cursor);
|
__cursor_unlock(f->cursor);
|
||||||
return -1; // ECANCELED
|
return -1; // ECANCELED
|
||||||
}
|
}
|
||||||
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
|
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask)))
|
||||||
goto HandleInterrupt;
|
goto HandleInterrupt;
|
||||||
}
|
|
||||||
|
|
||||||
// signals have already been fully blocked by caller
|
// signals have already been fully blocked by caller
|
||||||
// perform i/o operation with atomic signal/cancel checking
|
// perform i/o operation with atomic signal/cancel checking
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
* @param newdirfd is normally AT_FDCWD but if it's an open directory
|
* @param newdirfd is normally AT_FDCWD but if it's an open directory
|
||||||
* and newpath is relative, then newpath become relative to dirfd
|
* and newpath is relative, then newpath become relative to dirfd
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @return 0 on success, or -1 w/ errno
|
||||||
|
* @raise EROFS if either path is under /zip/...
|
||||||
*/
|
*/
|
||||||
int renameat(int olddirfd, const char *oldpath, int newdirfd,
|
int renameat(int olddirfd, const char *oldpath, int newdirfd,
|
||||||
const char *newpath) {
|
const char *newpath) {
|
||||||
|
@ -48,7 +49,7 @@ int renameat(int olddirfd, const char *oldpath, int newdirfd,
|
||||||
if (_weaken(__zipos_notat) &&
|
if (_weaken(__zipos_notat) &&
|
||||||
((rc = __zipos_notat(olddirfd, oldpath)) == -1 ||
|
((rc = __zipos_notat(olddirfd, oldpath)) == -1 ||
|
||||||
(rc = __zipos_notat(newdirfd, newpath)) == -1)) {
|
(rc = __zipos_notat(newdirfd, newpath)) == -1)) {
|
||||||
STRACE("zipos renameat not supported yet");
|
rc = erofs();
|
||||||
} else if (!IsWindows()) {
|
} else if (!IsWindows()) {
|
||||||
rc = sys_renameat(olddirfd, oldpath, newdirfd, newpath);
|
rc = sys_renameat(olddirfd, oldpath, newdirfd, newpath);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -18,9 +18,9 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/intrin/fds.h"
|
|
||||||
#include "libc/calls/struct/termios.h"
|
#include "libc/calls/struct/termios.h"
|
||||||
#include "libc/calls/syscall-nt.internal.h"
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
|
#include "libc/intrin/fds.h"
|
||||||
#include "libc/intrin/nomultics.h"
|
#include "libc/intrin/nomultics.h"
|
||||||
#include "libc/nt/console.h"
|
#include "libc/nt/console.h"
|
||||||
#include "libc/nt/enum/consolemodeflags.h"
|
#include "libc/nt/enum/consolemodeflags.h"
|
||||||
|
@ -58,32 +58,34 @@ textwindows int tcgetattr_nt(int fd, struct termios *tio) {
|
||||||
// kNtEnableLineInput and kNtEnableEchoInput only apply to programs
|
// kNtEnableLineInput and kNtEnableEchoInput only apply to programs
|
||||||
// that call ReadFile() or ReadConsole(). since we do not use them,
|
// that call ReadFile() or ReadConsole(). since we do not use them,
|
||||||
// the flags could serve the purpose of inter-process communication
|
// the flags could serve the purpose of inter-process communication
|
||||||
if ((inmode & kNtEnableLineInput) || !(__ttyconf.magic & kTtyUncanon)) {
|
if ((inmode & kNtEnableLineInput) || !(__ttyconf.magic & kTtyUncanon))
|
||||||
tio->c_lflag |= ICANON;
|
tio->c_lflag |= ICANON;
|
||||||
}
|
|
||||||
// kNtEnableEchoInput only works with kNtEnableLineInput enabled.
|
// kNtEnableEchoInput only works with kNtEnableLineInput enabled.
|
||||||
if ((inmode & kNtEnableEchoInput) || !(__ttyconf.magic & kTtySilence)) {
|
if ((inmode & kNtEnableEchoInput) || !(__ttyconf.magic & kTtySilence))
|
||||||
tio->c_lflag |= ECHO;
|
tio->c_lflag |= ECHO;
|
||||||
}
|
|
||||||
// The Windows console itself always echos control codes as ASCII.
|
// The Windows console itself always echos control codes as ASCII.
|
||||||
if ((inmode & kNtEnableEchoInput) || !(__ttyconf.magic & kTtyEchoRaw)) {
|
if (!(__ttyconf.magic & kTtyEchoRaw))
|
||||||
tio->c_lflag |= ECHOCTL;
|
tio->c_lflag |= ECHOCTL;
|
||||||
}
|
|
||||||
if (!(__ttyconf.magic & kTtyNoCr2Nl)) {
|
if (!(__ttyconf.magic & kTtyNoEchoe))
|
||||||
|
tio->c_lflag |= ECHOE;
|
||||||
|
if (!(__ttyconf.magic & kTtyNoEchok))
|
||||||
|
tio->c_lflag |= ECHOK;
|
||||||
|
if (!(__ttyconf.magic & kTtyNoEchoke))
|
||||||
|
tio->c_lflag |= ECHOKE;
|
||||||
|
|
||||||
|
if (!(__ttyconf.magic & kTtyNoCr2Nl))
|
||||||
tio->c_iflag |= ICRNL;
|
tio->c_iflag |= ICRNL;
|
||||||
}
|
if (!(__ttyconf.magic & kTtyNoIsigs))
|
||||||
if (!(__ttyconf.magic & kTtyNoIsigs)) {
|
|
||||||
tio->c_lflag |= ISIG;
|
tio->c_lflag |= ISIG;
|
||||||
}
|
if (!(__ttyconf.magic & kTtyNoIexten))
|
||||||
if (inmode & kNtEnableProcessedInput) {
|
|
||||||
tio->c_lflag |= IEXTEN;
|
tio->c_lflag |= IEXTEN;
|
||||||
}
|
if (outmode & kNtEnableProcessedOutput)
|
||||||
if (outmode & kNtEnableProcessedOutput) {
|
|
||||||
tio->c_oflag |= OPOST;
|
tio->c_oflag |= OPOST;
|
||||||
}
|
if (!(outmode & kNtDisableNewlineAutoReturn))
|
||||||
if (!(outmode & kNtDisableNewlineAutoReturn)) {
|
|
||||||
tio->c_oflag |= OPOST | ONLCR;
|
tio->c_oflag |= OPOST | ONLCR;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/internal.h"
|
#include "libc/calls/internal.h"
|
||||||
#include "libc/intrin/fds.h"
|
|
||||||
#include "libc/calls/struct/termios.h"
|
#include "libc/calls/struct/termios.h"
|
||||||
#include "libc/calls/syscall-nt.internal.h"
|
#include "libc/calls/syscall-nt.internal.h"
|
||||||
#include "libc/calls/ttydefaults.h"
|
#include "libc/calls/ttydefaults.h"
|
||||||
|
#include "libc/intrin/fds.h"
|
||||||
#include "libc/intrin/nomultics.h"
|
#include "libc/intrin/nomultics.h"
|
||||||
#include "libc/nt/console.h"
|
#include "libc/nt/console.h"
|
||||||
#include "libc/nt/enum/consolemodeflags.h"
|
#include "libc/nt/enum/consolemodeflags.h"
|
||||||
|
@ -61,40 +61,50 @@ textwindows int tcsetattr_nt(int fd, int opt, const struct termios *tio) {
|
||||||
inmode &= ~kNtEnableQuickEditMode;
|
inmode &= ~kNtEnableQuickEditMode;
|
||||||
__ttyconf.magic |= kTtyUncanon;
|
__ttyconf.magic |= kTtyUncanon;
|
||||||
}
|
}
|
||||||
if (!(tio->c_iflag & ICRNL)) {
|
if (!(tio->c_iflag & ICRNL))
|
||||||
__ttyconf.magic |= kTtyNoCr2Nl;
|
__ttyconf.magic |= kTtyNoCr2Nl;
|
||||||
}
|
|
||||||
if (!(tio->c_lflag & ECHOCTL)) {
|
if (!(tio->c_lflag & ECHOE))
|
||||||
|
__ttyconf.magic |= kTtyNoEchoe;
|
||||||
|
if (!(tio->c_lflag & ECHOK))
|
||||||
|
__ttyconf.magic |= kTtyNoEchok;
|
||||||
|
if (!(tio->c_lflag & ECHOKE))
|
||||||
|
__ttyconf.magic |= kTtyNoEchoke;
|
||||||
|
if (!(tio->c_lflag & ECHOCTL))
|
||||||
__ttyconf.magic |= kTtyEchoRaw;
|
__ttyconf.magic |= kTtyEchoRaw;
|
||||||
}
|
|
||||||
if (tio->c_lflag & ECHO) {
|
if (tio->c_lflag & ECHO) {
|
||||||
// "kNtEnableEchoInput can be used only if the
|
// "kNtEnableEchoInput can be used only if the
|
||||||
// kNtEnableLineInput mode is also enabled." -MSDN
|
// kNtEnableLineInput mode is also enabled." -MSDN
|
||||||
if (tio->c_lflag & ICANON) {
|
if (tio->c_lflag & ICANON)
|
||||||
inmode |= kNtEnableEchoInput;
|
inmode |= kNtEnableEchoInput;
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
__ttyconf.magic |= kTtySilence;
|
__ttyconf.magic |= kTtySilence;
|
||||||
}
|
}
|
||||||
if (!(tio->c_lflag & ISIG)) {
|
|
||||||
|
if (!(tio->c_lflag & ISIG))
|
||||||
__ttyconf.magic |= kTtyNoIsigs;
|
__ttyconf.magic |= kTtyNoIsigs;
|
||||||
}
|
|
||||||
|
// IEXTEN enables implementation-defined input processing. This flag,
|
||||||
|
// as well as ICANON must be enabled for the special characters EOL2,
|
||||||
|
// LNEXT, REPRINT, WERASE to be interpreted.
|
||||||
|
if (!(tio->c_lflag & IEXTEN))
|
||||||
|
__ttyconf.magic |= kTtyNoIexten;
|
||||||
|
|
||||||
memcpy(__ttyconf.c_cc, tio->c_cc, NCCS);
|
memcpy(__ttyconf.c_cc, tio->c_cc, NCCS);
|
||||||
if ((tio->c_lflag & ISIG) && //
|
|
||||||
!(tio->c_lflag & ICANON) && //
|
if ((tio->c_lflag & ISIG) && __ttyconf.vintr == CTRL('C'))
|
||||||
__ttyconf.vintr == CTRL('C')) {
|
|
||||||
// allows ctrl-c to be delivered asynchronously via win32
|
// allows ctrl-c to be delivered asynchronously via win32
|
||||||
// we normally don't want win32 doing this 24/7 in the bg
|
// we normally don't want win32 doing this 24/7 in the bg
|
||||||
// because we don't have job control, tcsetpgrp, etc. yet
|
// because we don't have job control, tcsetpgrp, etc. yet
|
||||||
// it's normally much better to let read-nt.c raise a sig
|
// it's normally much better to let read-nt.c raise a sig
|
||||||
// because read-nt only manages your tty whilst it's used
|
// because read-nt only manages your tty while it is used
|
||||||
inmode |= kNtEnableProcessedInput;
|
inmode |= kNtEnableProcessedInput;
|
||||||
}
|
|
||||||
outmode &= ~kNtDisableNewlineAutoReturn;
|
outmode &= ~kNtDisableNewlineAutoReturn;
|
||||||
outmode |= kNtEnableProcessedOutput;
|
outmode |= kNtEnableProcessedOutput;
|
||||||
if (!(tio->c_oflag & ONLCR)) {
|
if (!(tio->c_oflag & ONLCR))
|
||||||
outmode |= kNtDisableNewlineAutoReturn;
|
outmode |= kNtDisableNewlineAutoReturn;
|
||||||
}
|
|
||||||
outmode |= kNtEnableVirtualTerminalProcessing;
|
outmode |= kNtEnableVirtualTerminalProcessing;
|
||||||
|
|
||||||
// tune the win32 configuration
|
// tune the win32 configuration
|
||||||
|
|
|
@ -29,6 +29,6 @@
|
||||||
*/
|
*/
|
||||||
struct timespec timespec_mono(void) {
|
struct timespec timespec_mono(void) {
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
npassert(!clock_gettime(CLOCK_MONOTONIC, &ts));
|
unassert(!clock_gettime(CLOCK_MONOTONIC, &ts));
|
||||||
return ts;
|
return ts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,12 +39,13 @@
|
||||||
* @param path is the thing to delete
|
* @param path is the thing to delete
|
||||||
* @param flags can have AT_REMOVEDIR
|
* @param flags can have AT_REMOVEDIR
|
||||||
* @return 0 on success, or -1 w/ errno
|
* @return 0 on success, or -1 w/ errno
|
||||||
|
* @raise EROFS if either path is under /zip/...
|
||||||
*/
|
*/
|
||||||
int unlinkat(int dirfd, const char *path, int flags) {
|
int unlinkat(int dirfd, const char *path, int flags) {
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
|
if (_weaken(__zipos_notat) && (rc = __zipos_notat(dirfd, path)) == -1) {
|
||||||
STRACE("zipos unlinkat not supported yet");
|
rc = erofs();
|
||||||
} else if (!IsWindows()) {
|
} else if (!IsWindows()) {
|
||||||
rc = sys_unlinkat(dirfd, path, flags);
|
rc = sys_unlinkat(dirfd, path, flags);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,17 +1,21 @@
|
||||||
#ifndef COSMOPOLITAN_NOMULTICS_H_
|
#ifndef COSMOPOLITAN_NOMULTICS_H_
|
||||||
#define COSMOPOLITAN_NOMULTICS_H_
|
#define COSMOPOLITAN_NOMULTICS_H_
|
||||||
|
|
||||||
#define kTtySilence 1 /* do not relay read() into write() */
|
#define kTtySilence 1 /* do not relay read() into write() */
|
||||||
#define kTtyEchoRaw 2 /* don't ^X visualize control codes */
|
#define kTtyEchoRaw 2 /* don't ^X visualize control codes */
|
||||||
#define kTtyUncanon 4 /* enables non-canonical (raw) mode */
|
#define kTtyUncanon 4 /* enables non-canonical (raw) mode */
|
||||||
#define kTtyNoCr2Nl 8 /* don't map \r → \n (a.k.a !ICRNL) */
|
#define kTtyNoCr2Nl 8 /* don't map \r → \n (a.k.a !ICRNL) */
|
||||||
#define kTtyNoIsigs 16 /* don't auto-raise signals on keys */
|
#define kTtyNoIsigs 16 /* don't auto-raise signals on keys */
|
||||||
#define kTtyXtMouse 32 /* enables eXtreme Xterm mouse mode */
|
#define kTtyXtMouse 32 /* enables eXtreme Xterm mouse mode */
|
||||||
|
#define kTtyNoIexten 64 /* disable various canon keystrokes */
|
||||||
|
#define kTtyNoEchoe 128
|
||||||
|
#define kTtyNoEchok 256
|
||||||
|
#define kTtyNoEchoke 512
|
||||||
|
|
||||||
COSMOPOLITAN_C_START_
|
COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
struct TtyConf {
|
struct TtyConf {
|
||||||
unsigned char magic;
|
unsigned magic;
|
||||||
unsigned char mousebs;
|
unsigned char mousebs;
|
||||||
unsigned char replmode;
|
unsigned char replmode;
|
||||||
unsigned char replstderr;
|
unsigned char replstderr;
|
||||||
|
|
|
@ -98,14 +98,14 @@ static int _forker(uint32_t dwCreationFlags) {
|
||||||
struct timespec started;
|
struct timespec started;
|
||||||
int ax, dx, tid, parent;
|
int ax, dx, tid, parent;
|
||||||
parent = __pid;
|
parent = __pid;
|
||||||
started = timespec_real();
|
started = timespec_mono();
|
||||||
_onfork_prepare();
|
_onfork_prepare();
|
||||||
if (!IsWindows()) {
|
if (!IsWindows()) {
|
||||||
ax = sys_fork();
|
ax = sys_fork();
|
||||||
} else {
|
} else {
|
||||||
ax = sys_fork_nt(dwCreationFlags);
|
ax = sys_fork_nt(dwCreationFlags);
|
||||||
}
|
}
|
||||||
micros = timespec_tomicros(timespec_sub(timespec_real(), started));
|
micros = timespec_tomicros(timespec_sub(timespec_mono(), started));
|
||||||
if (!ax) {
|
if (!ax) {
|
||||||
|
|
||||||
// get new process id
|
// get new process id
|
||||||
|
|
|
@ -74,17 +74,14 @@ static textwindows int __proc_wait(int pid, int *wstatus, int options,
|
||||||
|
|
||||||
// check for signals and cancelation
|
// check for signals and cancelation
|
||||||
int sig, handler_was_called;
|
int sig, handler_was_called;
|
||||||
if (_check_cancel() == -1) {
|
if (_check_cancel() == -1)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
|
||||||
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
|
if (_weaken(__sig_get) && (sig = _weaken(__sig_get)(waitmask))) {
|
||||||
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
handler_was_called = _weaken(__sig_relay)(sig, SI_KERNEL, waitmask);
|
||||||
if (_check_cancel() == -1) {
|
if (_check_cancel() == -1)
|
||||||
return -1; // ECANCELED because SIGTHR was just handled
|
return -1; // ECANCELED because SIGTHR was just handled
|
||||||
}
|
if (handler_was_called & SIG_HANDLED_NO_RESTART)
|
||||||
if (handler_was_called & SIG_HANDLED_NO_RESTART) {
|
|
||||||
return eintr(); // a non-SA_RESTART handler was called
|
return eintr(); // a non-SA_RESTART handler was called
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for zombie to harvest
|
// check for zombie to harvest
|
||||||
|
|
|
@ -23,9 +23,8 @@
|
||||||
int __zipos_notat(int dirfd, const char *path) {
|
int __zipos_notat(int dirfd, const char *path) {
|
||||||
struct ZiposUri zipname;
|
struct ZiposUri zipname;
|
||||||
if (!path)
|
if (!path)
|
||||||
return efault();
|
return 0;
|
||||||
if (__isfdkind(dirfd, kFdZip) || __zipos_parseuri(path, &zipname) != -1) {
|
if (__isfdkind(dirfd, kFdZip) || __zipos_parseuri(path, &zipname) != -1)
|
||||||
return einval();
|
return -1;
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,14 @@ COSMOPOLITAN_C_START_
|
||||||
|
|
||||||
#define BENCHMARK(ITERATIONS, WORK_PER_RUN, CODE) \
|
#define BENCHMARK(ITERATIONS, WORK_PER_RUN, CODE) \
|
||||||
do { \
|
do { \
|
||||||
struct timespec start = timespec_real(); \
|
struct timespec start = timespec_mono(); \
|
||||||
for (int __i = 0; __i < ITERATIONS; ++__i) { \
|
for (int __i = 0; __i < ITERATIONS; ++__i) { \
|
||||||
asm volatile("" ::: "memory"); \
|
asm volatile("" ::: "memory"); \
|
||||||
CODE; \
|
CODE; \
|
||||||
} \
|
} \
|
||||||
long long work = ((WORK_PER_RUN) ? (WORK_PER_RUN) : 1) * (ITERATIONS); \
|
long long work = ((WORK_PER_RUN) ? (WORK_PER_RUN) : 1) * (ITERATIONS); \
|
||||||
double nanos = \
|
double nanos = \
|
||||||
(timespec_tonanos(timespec_sub(timespec_real(), start)) + work - 1) / \
|
(timespec_tonanos(timespec_sub(timespec_mono(), start)) + work - 1) / \
|
||||||
(double)work; \
|
(double)work; \
|
||||||
if (nanos < 1000) { \
|
if (nanos < 1000) { \
|
||||||
printf("%10g ns %2dx %s\n", nanos, (ITERATIONS), #CODE); \
|
printf("%10g ns %2dx %s\n", nanos, (ITERATIONS), #CODE); \
|
||||||
|
|
|
@ -161,12 +161,12 @@ void Connect(void) {
|
||||||
CHECK_NE(-1,
|
CHECK_NE(-1,
|
||||||
(g_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)));
|
(g_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)));
|
||||||
expo = INITIAL_CONNECT_TIMEOUT;
|
expo = INITIAL_CONNECT_TIMEOUT;
|
||||||
deadline = timespec_add(timespec_real(),
|
deadline = timespec_add(timespec_mono(),
|
||||||
timespec_fromseconds(MAX_WAIT_CONNECT_SECONDS));
|
timespec_fromseconds(MAX_WAIT_CONNECT_SECONDS));
|
||||||
LOGIFNEG1(sigaction(SIGALRM, &(struct sigaction){.sa_handler = OnAlarm}, 0));
|
LOGIFNEG1(sigaction(SIGALRM, &(struct sigaction){.sa_handler = OnAlarm}, 0));
|
||||||
DEBUGF("connecting to %s (%hhu.%hhu.%hhu.%hhu) to run %s", g_hostname, ip4[0],
|
DEBUGF("connecting to %s (%hhu.%hhu.%hhu.%hhu) to run %s", g_hostname, ip4[0],
|
||||||
ip4[1], ip4[2], ip4[3], g_prog);
|
ip4[1], ip4[2], ip4[3], g_prog);
|
||||||
struct timespec start = timespec_real();
|
struct timespec start = timespec_mono();
|
||||||
TryAgain:
|
TryAgain:
|
||||||
alarmed = false;
|
alarmed = false;
|
||||||
LOGIFNEG1(setitimer(
|
LOGIFNEG1(setitimer(
|
||||||
|
@ -178,7 +178,7 @@ TryAgain:
|
||||||
if (rc == -1) {
|
if (rc == -1) {
|
||||||
if (err == EINTR) {
|
if (err == EINTR) {
|
||||||
expo *= 1.5;
|
expo *= 1.5;
|
||||||
if (timespec_cmp(timespec_real(), deadline) >= 0) {
|
if (timespec_cmp(timespec_mono(), deadline) >= 0) {
|
||||||
FATALF("timeout connecting to %s (%hhu.%hhu.%hhu.%hhu:%d)", g_hostname,
|
FATALF("timeout connecting to %s (%hhu.%hhu.%hhu.%hhu:%d)", g_hostname,
|
||||||
ip4[0], ip4[1], ip4[2], ip4[3],
|
ip4[0], ip4[1], ip4[2], ip4[3],
|
||||||
ntohs(((struct sockaddr_in *)ai->ai_addr)->sin_port));
|
ntohs(((struct sockaddr_in *)ai->ai_addr)->sin_port));
|
||||||
|
@ -193,7 +193,7 @@ TryAgain:
|
||||||
}
|
}
|
||||||
setitimer(ITIMER_REAL, &(const struct itimerval){0}, 0);
|
setitimer(ITIMER_REAL, &(const struct itimerval){0}, 0);
|
||||||
freeaddrinfo(ai);
|
freeaddrinfo(ai);
|
||||||
connect_latency = timespec_tomicros(timespec_sub(timespec_real(), start));
|
connect_latency = timespec_tomicros(timespec_sub(timespec_mono(), start));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Send(int tmpfd, const void *output, size_t outputsize) {
|
bool Send(int tmpfd, const void *output, size_t outputsize) {
|
||||||
|
@ -309,7 +309,7 @@ bool Recv(char *p, int n) {
|
||||||
|
|
||||||
int ReadResponse(void) {
|
int ReadResponse(void) {
|
||||||
int exitcode;
|
int exitcode;
|
||||||
struct timespec start = timespec_real();
|
struct timespec start = timespec_mono();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char msg[5];
|
char msg[5];
|
||||||
if (!Recv(msg, 5)) {
|
if (!Recv(msg, 5)) {
|
||||||
|
@ -354,7 +354,7 @@ int ReadResponse(void) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
execute_latency = timespec_tomicros(timespec_sub(timespec_real(), start));
|
execute_latency = timespec_tomicros(timespec_sub(timespec_mono(), start));
|
||||||
close(g_sock);
|
close(g_sock);
|
||||||
return exitcode;
|
return exitcode;
|
||||||
}
|
}
|
||||||
|
@ -379,9 +379,9 @@ int RunOnHost(char *spec) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
Connect();
|
Connect();
|
||||||
EzFd(g_sock);
|
EzFd(g_sock);
|
||||||
struct timespec start = timespec_real();
|
struct timespec start = timespec_mono();
|
||||||
err = EzHandshake2();
|
err = EzHandshake2();
|
||||||
handshake_latency = timespec_tomicros(timespec_sub(timespec_real(), start));
|
handshake_latency = timespec_tomicros(timespec_sub(timespec_mono(), start));
|
||||||
if (!err)
|
if (!err)
|
||||||
break;
|
break;
|
||||||
WARNF("handshake with %s:%d failed -0x%04x (%s)", //
|
WARNF("handshake with %s:%d failed -0x%04x (%s)", //
|
||||||
|
|
|
@ -453,8 +453,8 @@ void *ClientWorker(void *arg) {
|
||||||
char *addrstr, *origname;
|
char *addrstr, *origname;
|
||||||
unsigned char msg[4 + 1 + 4 + 4 + 4];
|
unsigned char msg[4 + 1 + 4 + 4 + 4];
|
||||||
|
|
||||||
ts0 = timespec_real();
|
ts0 = timespec_mono();
|
||||||
ts1 = timespec_real();
|
ts1 = timespec_mono();
|
||||||
|
|
||||||
SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER, g_psk);
|
SetupPresharedKeySsl(MBEDTLS_SSL_IS_SERVER, g_psk);
|
||||||
defer(FreeClient, client);
|
defer(FreeClient, client);
|
||||||
|
@ -466,14 +466,14 @@ void *ClientWorker(void *arg) {
|
||||||
addrstr = DescribeAddress(&client->addr);
|
addrstr = DescribeAddress(&client->addr);
|
||||||
DEBUF("%s %s %s", DescribeAddress(&g_servaddr), "accepted", addrstr);
|
DEBUF("%s %s %s", DescribeAddress(&g_servaddr), "accepted", addrstr);
|
||||||
DEBUF("it took %'zu us to handshake client",
|
DEBUF("it took %'zu us to handshake client",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts1)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts1)));
|
||||||
|
|
||||||
// get the executable
|
// get the executable
|
||||||
ts1 = timespec_real();
|
ts1 = timespec_mono();
|
||||||
ts2 = timespec_real();
|
ts2 = timespec_mono();
|
||||||
Recv(client, msg, sizeof(msg));
|
Recv(client, msg, sizeof(msg));
|
||||||
DEBUF("it took %'zu us to receive #1",
|
DEBUF("it took %'zu us to receive #1",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts2)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts2)));
|
||||||
if (READ32BE(msg) != RUNITD_MAGIC) {
|
if (READ32BE(msg) != RUNITD_MAGIC) {
|
||||||
WARNF("%s magic mismatch!", addrstr);
|
WARNF("%s magic mismatch!", addrstr);
|
||||||
pthread_exit(0);
|
pthread_exit(0);
|
||||||
|
@ -486,19 +486,19 @@ void *ClientWorker(void *arg) {
|
||||||
filesize = READ32BE(msg + 9);
|
filesize = READ32BE(msg + 9);
|
||||||
crc = READ32BE(msg + 13);
|
crc = READ32BE(msg + 13);
|
||||||
origname = gc(calloc(1, namesize + 1));
|
origname = gc(calloc(1, namesize + 1));
|
||||||
ts2 = timespec_real();
|
ts2 = timespec_mono();
|
||||||
Recv(client, origname, namesize);
|
Recv(client, origname, namesize);
|
||||||
DEBUF("it took %'zu us to receive #2",
|
DEBUF("it took %'zu us to receive #2",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts2)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts2)));
|
||||||
VERBF("%s sent %#s (%'u bytes @ %#s)", addrstr, origname, filesize,
|
VERBF("%s sent %#s (%'u bytes @ %#s)", addrstr, origname, filesize,
|
||||||
client->tmpexepath);
|
client->tmpexepath);
|
||||||
char *exedata = gc(malloc(filesize));
|
char *exedata = gc(malloc(filesize));
|
||||||
ts2 = timespec_real();
|
ts2 = timespec_mono();
|
||||||
Recv(client, exedata, filesize);
|
Recv(client, exedata, filesize);
|
||||||
DEBUF("it took %'zu us to receive #3",
|
DEBUF("it took %'zu us to receive #3",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts2)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts2)));
|
||||||
DEBUF("it took %'zu us to receive executable from network",
|
DEBUF("it took %'zu us to receive executable from network",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts1)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts1)));
|
||||||
if (crc32_z(0, exedata, filesize) != crc) {
|
if (crc32_z(0, exedata, filesize) != crc) {
|
||||||
WARNF("%s crc mismatch! %#s", addrstr, origname);
|
WARNF("%s crc mismatch! %#s", addrstr, origname);
|
||||||
pthread_exit(0);
|
pthread_exit(0);
|
||||||
|
@ -509,7 +509,7 @@ void *ClientWorker(void *arg) {
|
||||||
// condition can happen, where etxtbsy is raised by our execve
|
// condition can happen, where etxtbsy is raised by our execve
|
||||||
// we're using o_cloexec so it's guaranteed to fix itself fast
|
// we're using o_cloexec so it's guaranteed to fix itself fast
|
||||||
// thus we use an optimistic approach to avoid expensive locks
|
// thus we use an optimistic approach to avoid expensive locks
|
||||||
ts1 = timespec_real();
|
ts1 = timespec_mono();
|
||||||
sprintf(client->tmpexepath, "o/%s.XXXXXX",
|
sprintf(client->tmpexepath, "o/%s.XXXXXX",
|
||||||
basename(stripext(gc(strdup(origname)))));
|
basename(stripext(gc(strdup(origname)))));
|
||||||
int exefd = openatemp(AT_FDCWD, client->tmpexepath, 0, O_CLOEXEC, 0700);
|
int exefd = openatemp(AT_FDCWD, client->tmpexepath, 0, O_CLOEXEC, 0700);
|
||||||
|
@ -533,7 +533,7 @@ void *ClientWorker(void *arg) {
|
||||||
pthread_exit(0);
|
pthread_exit(0);
|
||||||
}
|
}
|
||||||
DEBUF("it took %'zu us to write executable to disk",
|
DEBUF("it took %'zu us to write executable to disk",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts1)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts1)));
|
||||||
|
|
||||||
// do the args
|
// do the args
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -574,7 +574,7 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
posix_spawnattr_t spawnattr;
|
posix_spawnattr_t spawnattr;
|
||||||
posix_spawn_file_actions_t spawnfila;
|
posix_spawn_file_actions_t spawnfila;
|
||||||
sigemptyset(&sigmask);
|
sigemptyset(&sigmask);
|
||||||
started = timespec_real();
|
started = timespec_mono();
|
||||||
pipe2(client->pipe, O_CLOEXEC);
|
pipe2(client->pipe, O_CLOEXEC);
|
||||||
posix_spawnattr_init(&spawnattr);
|
posix_spawnattr_init(&spawnattr);
|
||||||
posix_spawnattr_setflags(&spawnattr,
|
posix_spawnattr_setflags(&spawnattr,
|
||||||
|
@ -584,11 +584,11 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
posix_spawn_file_actions_adddup2(&spawnfila, g_bogusfd, 0);
|
posix_spawn_file_actions_adddup2(&spawnfila, g_bogusfd, 0);
|
||||||
posix_spawn_file_actions_adddup2(&spawnfila, client->pipe[1], 1);
|
posix_spawn_file_actions_adddup2(&spawnfila, client->pipe[1], 1);
|
||||||
posix_spawn_file_actions_adddup2(&spawnfila, client->pipe[1], 2);
|
posix_spawn_file_actions_adddup2(&spawnfila, client->pipe[1], 2);
|
||||||
ts1 = timespec_real();
|
ts1 = timespec_mono();
|
||||||
err = posix_spawn(&client->pid, client->tmpexepath, &spawnfila, &spawnattr,
|
err = posix_spawn(&client->pid, client->tmpexepath, &spawnfila, &spawnattr,
|
||||||
args, environ);
|
args, environ);
|
||||||
DEBUF("it took %'zu us to call posix_spawn",
|
DEBUF("it took %'zu us to call posix_spawn",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts1)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts1)));
|
||||||
if (err) {
|
if (err) {
|
||||||
if (err == ETXTBSY) {
|
if (err == ETXTBSY) {
|
||||||
goto RetryOnEtxtbsyRaceCondition;
|
goto RetryOnEtxtbsyRaceCondition;
|
||||||
|
@ -603,7 +603,7 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
|
|
||||||
DEBUF("communicating %s[%d]", origname, client->pid);
|
DEBUF("communicating %s[%d]", origname, client->pid);
|
||||||
struct timespec deadline =
|
struct timespec deadline =
|
||||||
timespec_add(timespec_real(), timespec_fromseconds(DEATH_CLOCK_SECONDS));
|
timespec_add(timespec_mono(), timespec_fromseconds(DEATH_CLOCK_SECONDS));
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (g_interrupted) {
|
if (g_interrupted) {
|
||||||
WARNF("killing %d %s and hanging up %d due to interrupt", client->fd,
|
WARNF("killing %d %s and hanging up %d due to interrupt", client->fd,
|
||||||
|
@ -615,7 +615,7 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
PrintProgramOutput(client);
|
PrintProgramOutput(client);
|
||||||
pthread_exit(0);
|
pthread_exit(0);
|
||||||
}
|
}
|
||||||
struct timespec now = timespec_real();
|
struct timespec now = timespec_mono();
|
||||||
if (timespec_cmp(now, deadline) >= 0) {
|
if (timespec_cmp(now, deadline) >= 0) {
|
||||||
WARNF("killing %s (pid %d) which timed out after %d seconds", origname,
|
WARNF("killing %s (pid %d) which timed out after %d seconds", origname,
|
||||||
client->pid, DEATH_CLOCK_SECONDS);
|
client->pid, DEATH_CLOCK_SECONDS);
|
||||||
|
@ -626,11 +626,11 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
fds[0].events = POLLIN;
|
fds[0].events = POLLIN;
|
||||||
fds[1].fd = client->pipe[0];
|
fds[1].fd = client->pipe[0];
|
||||||
fds[1].events = POLLIN;
|
fds[1].events = POLLIN;
|
||||||
ts1 = timespec_real();
|
ts1 = timespec_mono();
|
||||||
int64_t ms = timespec_tomillis(timespec_sub(deadline, now));
|
int64_t ms = timespec_tomillis(timespec_sub(deadline, now));
|
||||||
events = poll(fds, ARRAYLEN(fds), MIN(ms, -1u));
|
events = poll(fds, ARRAYLEN(fds), MIN(ms, -1u));
|
||||||
DEBUF("it took %'zu us to call poll",
|
DEBUF("it took %'zu us to call poll",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts1)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts1)));
|
||||||
if (events == -1) {
|
if (events == -1) {
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
INFOF("poll interrupted");
|
INFOF("poll interrupted");
|
||||||
|
@ -645,10 +645,10 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
if (fds[0].revents) {
|
if (fds[0].revents) {
|
||||||
int received;
|
int received;
|
||||||
char buf[512];
|
char buf[512];
|
||||||
ts1 = timespec_real();
|
ts1 = timespec_mono();
|
||||||
received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf));
|
received = mbedtls_ssl_read(&ezssl, buf, sizeof(buf));
|
||||||
DEBUF("it took %'zu us to call mbedtls_ssl_read",
|
DEBUF("it took %'zu us to call mbedtls_ssl_read",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts1)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts1)));
|
||||||
if (!received) {
|
if (!received) {
|
||||||
WARNF("%s client disconnected so killing worker %d", origname,
|
WARNF("%s client disconnected so killing worker %d", origname,
|
||||||
client->pid);
|
client->pid);
|
||||||
|
@ -673,10 +673,10 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
}
|
}
|
||||||
if (fds[1].revents) {
|
if (fds[1].revents) {
|
||||||
char buf[512];
|
char buf[512];
|
||||||
ts1 = timespec_real();
|
ts1 = timespec_mono();
|
||||||
ssize_t got = read(client->pipe[0], buf, sizeof(buf));
|
ssize_t got = read(client->pipe[0], buf, sizeof(buf));
|
||||||
DEBUF("it took %'zu us to call read",
|
DEBUF("it took %'zu us to call read",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts1)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts1)));
|
||||||
if (got == -1) {
|
if (got == -1) {
|
||||||
WARNF("got %s reading %s output", strerror(errno), origname);
|
WARNF("got %s reading %s output", strerror(errno), origname);
|
||||||
goto HangupClientAndTerminateJob;
|
goto HangupClientAndTerminateJob;
|
||||||
|
@ -694,10 +694,10 @@ RetryOnEtxtbsyRaceCondition:
|
||||||
WaitAgain:
|
WaitAgain:
|
||||||
DEBUF("waitpid");
|
DEBUF("waitpid");
|
||||||
struct rusage rusage;
|
struct rusage rusage;
|
||||||
ts1 = timespec_real();
|
ts1 = timespec_mono();
|
||||||
int wrc = wait4(client->pid, &wstatus, 0, &rusage);
|
int wrc = wait4(client->pid, &wstatus, 0, &rusage);
|
||||||
DEBUF("it took %'zu us to call wait4",
|
DEBUF("it took %'zu us to call wait4",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts1)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts1)));
|
||||||
if (wrc == -1) {
|
if (wrc == -1) {
|
||||||
if (errno == EINTR) {
|
if (errno == EINTR) {
|
||||||
WARNF("waitpid interrupted; killing %s pid %d", origname, client->pid);
|
WARNF("waitpid interrupted; killing %s pid %d", origname, client->pid);
|
||||||
|
@ -715,7 +715,7 @@ WaitAgain:
|
||||||
}
|
}
|
||||||
client->pid = 0;
|
client->pid = 0;
|
||||||
int exitcode;
|
int exitcode;
|
||||||
struct timespec ended = timespec_real();
|
struct timespec ended = timespec_mono();
|
||||||
int64_t micros = timespec_tomicros(timespec_sub(ended, started));
|
int64_t micros = timespec_tomicros(timespec_sub(ended, started));
|
||||||
if (WIFEXITED(wstatus)) {
|
if (WIFEXITED(wstatus)) {
|
||||||
if (WEXITSTATUS(wstatus)) {
|
if (WEXITSTATUS(wstatus)) {
|
||||||
|
@ -750,18 +750,18 @@ WaitAgain:
|
||||||
AppendResourceReport(&client->output, &rusage, "\n");
|
AppendResourceReport(&client->output, &rusage, "\n");
|
||||||
PrintProgramOutput(client);
|
PrintProgramOutput(client);
|
||||||
}
|
}
|
||||||
ts1 = timespec_real();
|
ts1 = timespec_mono();
|
||||||
SendProgramOutput(client);
|
SendProgramOutput(client);
|
||||||
SendExitMessage(exitcode);
|
SendExitMessage(exitcode);
|
||||||
mbedtls_ssl_close_notify(&ezssl);
|
mbedtls_ssl_close_notify(&ezssl);
|
||||||
DEBUF("it took %'zu us to send result to client",
|
DEBUF("it took %'zu us to send result to client",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts1)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts1)));
|
||||||
if (etxtbsy_tries > 1) {
|
if (etxtbsy_tries > 1) {
|
||||||
WARNF("encountered %d ETXTBSY race conditions spawning %s",
|
WARNF("encountered %d ETXTBSY race conditions spawning %s",
|
||||||
etxtbsy_tries - 1, origname);
|
etxtbsy_tries - 1, origname);
|
||||||
}
|
}
|
||||||
DEBUF("it took %'zu us TO DO EVERYTHING",
|
DEBUF("it took %'zu us TO DO EVERYTHING",
|
||||||
timespec_tomicros(timespec_sub(timespec_real(), ts0)));
|
timespec_tomicros(timespec_sub(timespec_mono(), ts0)));
|
||||||
pthread_exit(0);
|
pthread_exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||||
#include "libc/assert.h"
|
#include "libc/assert.h"
|
||||||
#include "libc/calls/struct/timespec.h"
|
#include "libc/calls/struct/timespec.h"
|
||||||
|
#include "libc/intrin/describeflags.h"
|
||||||
#include "libc/intrin/kprintf.h"
|
#include "libc/intrin/kprintf.h"
|
||||||
#include "libc/runtime/runtime.h"
|
#include "libc/runtime/runtime.h"
|
||||||
#include "libc/stdio/stdio.h"
|
#include "libc/stdio/stdio.h"
|
||||||
|
@ -26,36 +27,19 @@
|
||||||
#define MAXIMUM 1e9
|
#define MAXIMUM 1e9
|
||||||
#define ITERATIONS 10
|
#define ITERATIONS 10
|
||||||
|
|
||||||
void TestSleepRealRelative(void) {
|
void TestSleepRelative(int clock) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("testing: clock_nanosleep(CLOCK_REALTIME) with relative "
|
printf("testing: clock_nanosleep(%s) with relative timeout\n",
|
||||||
"timeout\n");
|
DescribeClockName(clock));
|
||||||
for (long nanos = 1; nanos < (long)MAXIMUM; nanos *= 2) {
|
for (long nanos = 1; nanos < (long)MAXIMUM; nanos *= 2) {
|
||||||
struct timespec t1, t2, wf;
|
struct timespec t1, t2, wf;
|
||||||
wf = timespec_fromnanos(nanos);
|
wf = timespec_fromnanos(nanos);
|
||||||
clock_gettime(CLOCK_REALTIME, &t1);
|
if (clock_gettime(clock, &t1))
|
||||||
|
return;
|
||||||
for (int i = 0; i < ITERATIONS; ++i) {
|
for (int i = 0; i < ITERATIONS; ++i) {
|
||||||
npassert(!clock_nanosleep(CLOCK_REALTIME, 0, &wf, 0));
|
npassert(!clock_nanosleep(clock, 0, &wf, 0));
|
||||||
}
|
}
|
||||||
clock_gettime(CLOCK_REALTIME, &t2);
|
clock_gettime(clock, &t2);
|
||||||
long took = timespec_tonanos(timespec_sub(t2, t1)) / ITERATIONS;
|
|
||||||
printf("%,12ld ns sleep took %,12ld ns delta %,12ld ns\n", nanos, took,
|
|
||||||
took - nanos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestSleepMonoRelative(void) {
|
|
||||||
printf("\n");
|
|
||||||
printf("testing: clock_nanosleep(CLOCK_MONOTONIC) with relative "
|
|
||||||
"timeout\n");
|
|
||||||
for (long nanos = 1; nanos < (long)MAXIMUM; nanos *= 2) {
|
|
||||||
struct timespec t1, t2, wf;
|
|
||||||
wf = timespec_fromnanos(nanos);
|
|
||||||
clock_gettime(CLOCK_REALTIME, &t1);
|
|
||||||
for (int i = 0; i < ITERATIONS; ++i) {
|
|
||||||
npassert(!clock_nanosleep(CLOCK_MONOTONIC, 0, &wf, 0));
|
|
||||||
}
|
|
||||||
clock_gettime(CLOCK_REALTIME, &t2);
|
|
||||||
long took = timespec_tonanos(timespec_sub(t2, t1)) / ITERATIONS;
|
long took = timespec_tonanos(timespec_sub(t2, t1)) / ITERATIONS;
|
||||||
printf("%,12ld ns sleep took %,12ld ns delta %,12ld ns\n", nanos, took,
|
printf("%,12ld ns sleep took %,12ld ns delta %,12ld ns\n", nanos, took,
|
||||||
took - nanos);
|
took - nanos);
|
||||||
|
@ -63,6 +47,8 @@ void TestSleepMonoRelative(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
TestSleepRealRelative();
|
TestSleepRelative(CLOCK_REALTIME);
|
||||||
TestSleepMonoRelative();
|
TestSleepRelative(CLOCK_MONOTONIC);
|
||||||
|
TestSleepRelative(CLOCK_REALTIME_COARSE);
|
||||||
|
TestSleepRelative(CLOCK_MONOTONIC_COARSE);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue