mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Fix SIGINT handling for external commands run from scripts
Reproducer: $ ksh -c 'bash -c '\''kill -s INT $$'\''; echo "$?, continuing"' Expected result: output "258, continuing"; exit status 0. Actual result: no output; exit status 258. The child process sent SIGINT only to itself and not to the process group, so the parent script was wrongly interrupted. Every shell except ksh93 produces the expected result. ksh93 also gave the expected result before version 2008-01-31 93s+, which introduced the code below. Analysis: The problem is in these lines of code in xec.c, sh_exec(), TFORK case, parent branch of fork: 1649: if(!sh_isstate(SH_MONITOR)) 1650: { 1651: if(!(sh.sigflag[SIGINT]&(SH_SIGFAULT|SH_SIGOFF))) 1652: sh_sigtrap(SIGINT); 1653: sh.trapnote |= SH_SIGIGNORE; 1654: } [...pipe and I/O handling, wait for command to finish...] 1667: if(!sh_isstate(SH_MONITOR)) 1668: { 1669: sh.trapnote &= ~SH_SIGIGNORE; 1700: if(sh.exitval == (SH_EXITSIG|SIGINT)) 1701: kill(sh.current_pid,SIGINT); 1702: } When a user presses Ctrl+C, SIGINT is sent to the entire process group. If job control is fully off (i.e., !sh_isstate(SH_MONITOR)), then the process group includes the parent script. Therefore, in a script such as $ ksh -c 'bash -c '\''read x'\''; echo "$?, continuing"' when the user presses Ctrl+C while bash waits for 'read x' input, the parent ksh script should be interrupted as well. Now, the code above ignores SIGINT while bash is running. (This is done using special-casing in sh_fault() to handle that SH_SIGIGNORE flag for SIGINT.) So, when Ctrl+C interrupts the process group, the parent script is not getting interrupted as it should. To compensate for that, the code then detects, using sh.exitval (the child process' exit status), whether the child process was killed by SIGINT. If so, it simply assumes that the signal was meant for the process group including the parent script, so it reissues SIGINT to itself after unignoring it. But, as we can see from the broken reproducer above, that assumption is not valid. Scripts are perfectly free to send SIGINT to themselves only, and that must work as expected. src/cmd/ksh93/sh/xec.c: sh_exec(): TFORK: parent branch: - Instead of ignoring SIGINT, sigblock() it, which delays handling the signal until sigrelease(). (Note that these are macros defined in src/cmd/ksh93/features/sigfeatures according to OS capabilities.) - This makes reissuing SIGINT redundant, so delete that, which fixes the bug. src/cmd/ksh93/sh/fault.c: - Nothing now sets the SH_SIGIGNORE flag in sh.trapnote, so remove special-casing added in 2008-01-31 93s+.
This commit is contained in:
parent
9a5af738ef
commit
d650c73e55
4 changed files with 25 additions and 12 deletions
|
@ -113,8 +113,6 @@ void sh_fault(register int sig)
|
|||
flag = sh.sigflag[sig]&~SH_SIGOFF;
|
||||
if(!trap)
|
||||
{
|
||||
if(sig==SIGINT && (sh.trapnote&SH_SIGIGNORE))
|
||||
return;
|
||||
if(flag&SH_SIGIGNORE)
|
||||
{
|
||||
if(sh.subshell)
|
||||
|
@ -386,7 +384,7 @@ void sh_chktrap(void)
|
|||
{
|
||||
register int sig=sh.st.trapmax;
|
||||
register char *trap;
|
||||
if(!(sh.trapnote&~SH_SIGIGNORE))
|
||||
if(!sh.trapnote)
|
||||
sig=0;
|
||||
sh.trapnote &= ~SH_SIGTRAP;
|
||||
/* execute errexit trap first */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue