This option disables the standards-mandated resetting of SIGINT (and QUIT) to SIG_IGN in asynchronous children.
This resetting makes it very hard to write a bash script which invokes a number of subprocesses in parallel and collects the output. The problem is that if you ^C the script, the subprocesses carry on running (doing needless work and perhaps causing lossage). Working around this in a race-free way with additional code in the script is very hard indeed. However, we can provide an option to do the right thing in modern programs. (The reason for SIGINT being ignored is purely historical: back in the dawn of time, there was no job control. If you interactively spawned a background process with &, you wouldn't want your attempts to ^C your foreground process to kill it. This SIGINT-ignoring also applied to noninteractive shells and of course came to be relied on. So it is too late to change the default.) Signed-off-by: Ian Jackson <ijack...@chiark.greenend.org.uk> Signed-off-by: Ian Jackson <ian.jack...@eu.citrix.com> CC: Ian Campbell <ian.campb...@citrix.com> --- MANIFEST | 3 +++ builtins/shopt.def | 2 ++ doc/bash.1 | 10 ++++++++++ doc/bashref.texi | 13 ++++++++++++- execute_cmd.c | 5 ++++- tests/asyncsigint.right | 6 ++++++ tests/asyncsigint.tests | 27 +++++++++++++++++++++++++++ tests/run-asyncsigint | 2 ++ tests/shopt.right | 3 +++ 9 files changed, 69 insertions(+), 2 deletions(-) create mode 100644 tests/asyncsigint.right create mode 100644 tests/asyncsigint.tests create mode 100644 tests/run-asyncsigint diff --git a/MANIFEST b/MANIFEST index 55c8d1c..1c3106c 100644 --- a/MANIFEST +++ b/MANIFEST @@ -841,6 +841,8 @@ tests/assoc4.sub f tests/assoc5.sub f tests/assoc6.sub f tests/assoc7.sub f +tests/asyncsigint.tests f +tests/asyncsigint.right f tests/braces.tests f tests/braces.right f tests/builtins.tests f @@ -1107,6 +1109,7 @@ tests/run-arith f tests/run-array f tests/run-array2 f tests/run-assoc f +tests/run-asyncsigint f tests/run-braces f tests/run-builtins f tests/run-case f diff --git a/builtins/shopt.def b/builtins/shopt.def index 6050a14..89a9aae 100644 --- a/builtins/shopt.def +++ b/builtins/shopt.def @@ -90,6 +90,7 @@ extern int autocd; extern int glob_star; extern int glob_asciirange; extern int lastpipe_opt; +extern int no_async_sig_ignore_opt; #if defined (EXTENDED_GLOB) extern int extended_glob; @@ -199,6 +200,7 @@ static struct { #endif { "login_shell", &shopt_login_shell, set_login_shell }, { "mailwarn", &mail_warning, (shopt_set_func_t *)NULL }, + { "no_async_sig_ignore", &no_async_sig_ignore_opt, (shopt_set_func_t *)NULL }, #if defined (READLINE) { "no_empty_cmd_completion", &no_empty_command_completion, (shopt_set_func_t *)NULL }, #endif diff --git a/doc/bash.1 b/doc/bash.1 index 2b1ca15b..f318ab1 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -9535,6 +9535,16 @@ If set, and a file that \fBbash\fP is checking for mail has been accessed since the last time it was checked, the message ``The mail in \fImailfile\fP has been read'' is displayed. .TP 8 +.B no_async_sig_ignore +If set in a noninteractive shell, asynchronous commands will +not have +.B SIGINT +and +.BR SIGQUIT +ignored; +instead, those signals' handlers will be reset to those +inherited by the shell from its parent, as for other signals. +.TP 8 .B no_empty_cmd_completion If set, and .B readline diff --git a/doc/bashref.texi b/doc/bashref.texi index 597274b..2556b60 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -3060,7 +3060,10 @@ Non-builtin commands started by Bash have signal handlers set to the values inherited by the shell from its parent. When job control is not in effect, asynchronous commands ignore @code{SIGINT} and @code{SIGQUIT} in addition to these inherited -handlers. +handlers, +unless the shell option @code{no_async_sig_ignore} +(see the description of @code{shopt} in @ref{The Shopt Builtin}) +is enabled. Commands run as a result of command substitution ignore the keyboard-generated job control signals @code{SIGTTIN}, @code{SIGTTOU}, and @code{SIGTSTP}. @@ -5146,6 +5149,14 @@ If set, and a file that Bash is checking for mail has been accessed since the last time it was checked, the message @code{"The mail in @var{mailfile} has been read"} is displayed. +@item no_async_sig_ignore +If set in a noninteractive shell, asynchronous commands will not +have +@code{SIGINT} and @code{SIGQUIT} +ignored; +instead, those signals' handlers will be reset to those +inherited by the shell from its parent, as for other signals. + @item no_empty_cmd_completion If set, and Readline is being used, Bash will not attempt to search the @env{PATH} for possible completions when completion is attempted diff --git a/execute_cmd.c b/execute_cmd.c index 9cebaef..c3b30b6 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -287,6 +287,7 @@ int funcnest = 0; int funcnest_max = 0; /* bash-4.2 */ int lastpipe_opt = 0; +int no_async_sig_ignore_opt = 0; struct fd_bitmap *current_fds_to_close = (struct fd_bitmap *)NULL; @@ -4827,7 +4828,9 @@ setup_async_signals () #endif #if defined (JOB_CONTROL) - if (job_control == 0) + if (no_async_sig_ignore_opt == 0 && job_control == 0) +#else + if (no_async_sig_ignore_opt == 0) #endif { /* Make sure we get the original signal dispositions now so we don't diff --git a/tests/asyncsigint.right b/tests/asyncsigint.right new file mode 100644 index 0000000..0113480 --- /dev/null +++ b/tests/asyncsigint.right @@ -0,0 +1,6 @@ +killing + 0 0 0 +0 +killing +130 +trapped-sigint diff --git a/tests/asyncsigint.tests b/tests/asyncsigint.tests new file mode 100644 index 0000000..ac0ee79 --- /dev/null +++ b/tests/asyncsigint.tests @@ -0,0 +1,27 @@ +: ${TMPDIR:=/tmp} + +rc=16 +trap 'echo trapped-sigint; exit $rc' INT + +p=$TMPDIR/asyncsigint-rendezvous + +runtest () { + rm -f $p + mkfifo -m 600 $p + wc <$p & + exec 3>$p + echo killing + kill -INT $! + exec 3>&- + wait $! + echo $? +} + +runtest + +shopt -s no_async_sig_ignore + +runtest + +rc=0 +kill -INT $$ diff --git a/tests/run-asyncsigint b/tests/run-asyncsigint new file mode 100644 index 0000000..a2baf9a --- /dev/null +++ b/tests/run-asyncsigint @@ -0,0 +1,2 @@ +${THIS_SH} ./asyncsigint.tests > /tmp/xx 2>&1 +diff /tmp/xx asyncsigint.right && rm -f /tmp/xx diff --git a/tests/shopt.right b/tests/shopt.right index 0a7edfb..ba54b73 100644 --- a/tests/shopt.right +++ b/tests/shopt.right @@ -37,6 +37,7 @@ shopt -u lastpipe shopt -u lithist shopt -u login_shell shopt -u mailwarn +shopt -u no_async_sig_ignore shopt -u no_empty_cmd_completion shopt -u nocaseglob shopt -u nocasematch @@ -92,6 +93,7 @@ shopt -u lastpipe shopt -u lithist shopt -u login_shell shopt -u mailwarn +shopt -u no_async_sig_ignore shopt -u no_empty_cmd_completion shopt -u nocaseglob shopt -u nocasematch @@ -128,6 +130,7 @@ lastpipe off lithist off login_shell off mailwarn off +no_async_sig_ignore off no_empty_cmd_completion off nocaseglob off nocasematch off -- 2.1.4