#if 0 /*─────────────────────────────────────────────────────────────────╗ │ To the extent possible under law, Justine Tunney has waived │ │ all copyright and related or neighboring rights to this file, │ │ as it is written in the following disclaimers: │ │ • http://unlicense.org/ │ │ • http://creativecommons.org/publicdomain/zero/1.0/ │ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/calls/struct/sigaction.h" #include "libc/errno.h" #include "libc/limits.h" #include "libc/runtime/runtime.h" #include "libc/sock/struct/pollfd.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/f.h" #include "libc/sysv/consts/limits.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/sig.h" volatile bool gotsig; void SignalHandler(int sig) { // we don't need to do anything in our signal handler since the signal // delivery itself causes a visible state change, saying what happened const char *s = "SignalHandler() called\n"; write(1, s, strlen(s)); // notice both functions are @asyncsignalsafe // however this will help if delivered asynchronously in cpubound code // it's also necessary to discern spurious interrupts from real signal gotsig = true; } int main(int argc, char *argv[]) { printf("echoing stdin until ctrl+c is pressed\n"); // you need to set your signal handler using sigaction() rather than // signal(), since the latter uses .sa_flags=SA_RESTART, which means // read will restart itself after signals, rather than raising EINTR sigaction(SIGINT, &(struct sigaction){.sa_handler = SignalHandler}, 0); for (;;) { // some programs are blocked on cpu rather than i/o // such programs shall rely on asynchronous signals printf("doing cpu task...\n"); for (volatile int i = 0; i < INT_MAX / 5; ++i) { if (gotsig) { printf("\rgot ctrl+c asynchronously\n"); exit(0); } } // posix guarantees atomic i/o if you use pipe_buf sized buffers // that way we don't need to worry about things like looping and // we can also be assured that multiple actors wont have tearing // 512 is the minimum permissible value for PIPE_BUF for all the // platforms. when stdin is a terminal there are more guarantees // about exactly how many bytes will be returned. in ICANON mode // which is the default you can count on it returning one single // line, including its \n (or VEOL, or VEOL2) per read. if using // non-canonical raw mode, then a single logical keystroke shall // be returned per read, so long as has VMIN characters or more, // and the default VMIN is 1. you can also set VMIN w/ tcsetattr // to 0 for a special kind of non-blocking mode. char buf[512]; // read data from standard input // // since this is a blocking operation and we're not performing a // cpu-bound operation it is almost with absolute certainty that // when the ctrl-c signal gets delivered, it'll happen in read() // // it's possible to be more precise if we were building library // code. for example, you can block signals using sigprocmask() // and then use pselect() to do the waiting. printf("doing read i/o task...\n"); int got = read(0, buf, sizeof(buf)); // check if the read operation failed // negative one is the *only* return value to indicate errors if (got == -1) { if (errno == EINTR) { // a signal handler was invoked during the read operation // since we have only one such signal handler it's sigint // if we didn't have any signal handlers in our app, then // we wouldn't need to check this errno. using SA_RESTART // is another great way to avoid having to worry about it // however EINTR is very useful, when we choose to use it // the \r character is needed so when the line is printed // it'll overwrite the ^C that got echo'd with the ctrl-c if (gotsig) { printf("\rgot ctrl+c via i/o eintr\n"); exit(0); } else { printf("\rgot spurious eintr\n"); continue; } } else { // log it in the unlikely event something else went wrong perror(""); exit(1); } } // check if the user typed ctrl-d which closes the input handle if (!got) { printf("got eof\n"); exit(0); } // relay read data to standard output // // it's usually safe to ignore the return code of write. the // operating system will send SIGPIPE if there's any problem // which kills the process by default write(1, "got: ", 5); write(1, buf, got); } }