From 5ce8d3b1a3819976aec3dd8aff6e6196bae11b60 Mon Sep 17 00:00:00 2001 From: ff <_ff@tuta.io> Date: Mon, 4 Dec 2017 13:20:47 +0100 Subject: [PATCH] Ignore SIGINT in script mode In script mode, ignore SIGINT, but terminate when a child process terminates with SIGINT. --- Devember/log04.txt | 40 ++++++++++++++++++++++++++++++++ src/minish.c | 67 +++++++++++++++++++----------------------------------- 2 files changed, 64 insertions(+), 43 deletions(-) create mode 100644 Devember/log04.txt diff --git a/Devember/log04.txt b/Devember/log04.txt new file mode 100644 index 0000000..d355005 --- /dev/null +++ b/Devember/log04.txt @@ -0,0 +1,40 @@ +How can a process get the file descriptor of the terminal it is using? +It cannot. But it can cheat. + +I assumed it was STDIN_FILENO, or STDOUT_FILENO, or STDERR_FILENO. +I assumed all three were equally good. +This is not the case, +because stdin, stdout, and stderr can all be redirected, +and then they do not refer to the terminal anymore. + +The current terminal has a filename: “/dev/tty”. +As any other file, it can be opened with open(), +which returns a new file descriptor, not in use before. +This can be used as the file descriptor of the terminal. +And then, when it is no longer needed, it can be closed with close(). + +I said “cheating” because this method does not get THE file descriptor, +but another file descriptor that refer to the same file. + +Yesterday, minish, in script mode, terminated with SIGINT +when it received a SIGINT +and the process it was executing terminated with SIGINT. +bash does it this way, too. +Today, it ignores a received SIGINT +and terminates when the process it is executing terminates with SIGINT. +fish does it this way, too. +It is simpler and I cannot see any downsides. +When pressing Ctrl+C, the behavior is identical, +because the shell receives the SIGINT, too. +When sending SIGINT to a specific process that is part of a script, +for example with the command kill, +if the signal terminates the process, +the first strategy goes on with the script, +but the second aborts it. +But I do not really care: SIGINT is to be sent with Ctrl+C. +If you want to use kill or anything else, send SIGTERM instead. + +Enough for today. + +Suggested reading: +https://gnu.org/software/libc/manual/html_node/Termination-Signals.html diff --git a/src/minish.c b/src/minish.c index 2d1663d..b2a7bd7 100644 --- a/src/minish.c +++ b/src/minish.c @@ -1,9 +1,9 @@ #define _POSIX_SOURCE 200809L -#include // EACCES,ENOMEM,errno +#include // ENOMEM,errno #include // O_RDONLY,open #include // LC_ALL,setlocale -#include // SIGINT,SIGTTOU,sig_atomic_t,sigaction +#include // SIGINT,SIGTTOU,SIG_DFL,SIG_IGN,signal,raise #include /* FILE, * ferror,fopen,fputs,perror,setbuf,stderr,stdin */ @@ -26,13 +26,16 @@ static noreturn void pexit(char const* const filename) { exit(EXIT_FAILURE); } -static void set_sigint_handler(void (* const handler)(int)) { - struct sigaction const action = { - . sa_handler = handler, - }; - if (sigaction(SIGINT, &action, NULL) == -1) { - pexit("sigaction()"); +static FILE* choose_input(char const* const filename) { + if (!filename) { + return stdin; } + FILE* const input = fopen(filename, "r"); + if (!input) { + pexit(filename); + } + signal(SIGINT, SIG_IGN); + return input; } static noreturn void oom(void) { @@ -65,39 +68,12 @@ static void go_to_foreground() { close(terminal); } -static volatile sig_atomic_t received_sigint = 0; - -static void sigint_handler(int const signal) { - (void)signal; - received_sigint = 1; -} - -static FILE* choose_input(char const* const filename) { - if (!filename) { - return stdin; - } - FILE* const input = fopen(filename, "r"); - if (!input) { - pexit(filename); - } - set_sigint_handler(sigint_handler); - return input; -} - -static void wait_child(pid_t const pid) { - int s; - if (waitpid(pid, &s, WUNTRACED) == -1) { +static int wait_child(pid_t const pid) { + int status; + if (waitpid(pid, &status, WUNTRACED) == -1) { pexit("waitpid()"); } - if (received_sigint) { - if (WIFSIGNALED(s) && WTERMSIG(s) == SIGINT) { - set_sigint_handler(SIG_DFL); - raise(SIGINT); // NOTE cause termination - // NOTE raise can fail, but does not set errno - exit(EXIT_FAILURE); - } - received_sigint = 0; - } + return WIFSIGNALED(status) ? WTERMSIG(status) : 0; } int main(int argc, char** argv) { @@ -127,7 +103,9 @@ int main(int argc, char** argv) { switch (pid) { case -1: pexit("fork()"); case 0: - if (!filename) { + if (filename) { + signal(SIGINT, SIG_DFL); + } else { if (setpgid(0, 0) == -1) { pexit("setpgid()"); } @@ -136,9 +114,12 @@ int main(int argc, char** argv) { execv(line, words.data); pexit(line); default: - sleep(1); - wait_child(pid); - if (!filename) { + if (wait_child(pid) == SIGINT && filename) { + signal(SIGINT, SIG_DFL); + raise(SIGINT); // NOTE terminate + // NOTE raise can fail + return EXIT_FAILURE; + } else if (!filename) { go_to_foreground(); } words.len = 0u; -- 2.11.4.GIT