2015-09-20 17:12:45 +0100, Stephane Chazelas: [...] > I thought the termsig_handler was being invoked upon SIGINT as > the SIGINT handler, but it is being called explicitely by > set_job_status_and_cleanup so the problem is elsewhere. > > child_caught_sigint is 0 while if I understand correctly it > should be 1 for a cmd that calls exit() upon SIGINT. So that's > probably probably where we should be looking. [...]
I had another look. If we're to beleive gdb, child_caught_sigint is 0 because waitpid() returns without EINTR even though wait_sigint_received is 1. The only reasonable explanation I can think of is that the child handles its SIGINT first, exits which updates its state and causes bash the parent to be scheduled, and waitpid() returns (without EINT) and after that bash's SIGINT handler kicks in too late. Anyway, this patch makes the problem go away for me (and addresses my problem #2 about exit code 130 not being treated as an interrupted child). It might break things though if there was a real reason for bash to check for waitpid()'s EINTR. With that patch applied, ./bash -c 'sh -c "trap exit INT; sleep 120; :"; echo hi' ./bash -c 'mksh -c "sleep 120; :"; echo hi' Does *not* output "hi" (as mksh or sh do a exit(130) which is regarded as them being "interrupted by that SIGINT", or at least reporting that the child they want to report the status of (sleep) has been killed by a SIGINT). And ./bash -c 'sh -c "trap exit\ 0 INT; sleep 120; :"; echo hi' *consistently* outputs "hi" (the zero exit status cancels the aborting of bash). --- jobs.c~ 2015-09-20 20:03:14.692119372 +0100 +++ jobs.c 2015-09-20 20:37:01.510892045 +0100 @@ -3257,21 +3257,15 @@ itrace("waitchld: waitpid returns %d blo CHECK_TERMSIG; CHECK_WAIT_INTR; - /* If waitpid returns -1/EINTR and the shell saw a SIGINT, then we - assume the child has blocked or handled SIGINT. In that case, we - require the child to actually die due to SIGINT to act on the - SIGINT we received; otherwise we assume the child handled it and - let it go. */ - if (pid < 0 && errno == EINTR && wait_sigint_received) - child_caught_sigint = 1; - if (pid <= 0) continue; /* jumps right to the test */ - /* If the child process did die due to SIGINT, forget our assumption - that it caught or otherwise handled it. */ - if (WIFSIGNALED (status) && WTERMSIG (status) == SIGINT) - child_caught_sigint = 0; + /* If we received a SIGINT, but the child did not die of a SIGINT and + did not report a 128+SIGINT exit status, we assume the child handled + it and let it go. */ + child_caught_sigint = wait_sigint_received && + ! ((WIFSIGNALED (status) && WTERMSIG (status) == SIGINT) || + (WIFEXITED (status) && WEXITSTATUS (status) == 128 + SIGINT)); /* children_exited is used to run traps on SIGCHLD. We don't want to run the trap if a process is just being continued. */ -- Stephane