--- a/jobs.c +++ b/jobs.c @@ -260,6 +260,7 @@ static int print_job __P((JOB *, int, int, int)); static int process_exit_status __P((WAIT)); static int process_exit_signal __P((WAIT)); static int set_job_status_and_cleanup __P((int)); +static void handle_sigint_caught_during_wait __P((PROCESS *, int)); static WAIT job_signal_status __P((int)); static WAIT raw_job_exit_status __P((int)); @@ -3311,7 +3312,11 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block); } if (job == NO_JOB) - continue; + { + if (PEXITED (child)) + handle_sigint_caught_during_wait (child, NO_JOB); + continue; + } call_set_current += set_job_status_and_cleanup (job); @@ -3371,18 +3376,104 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block); return (children_exited); } -/* Set the status of JOB and perform any necessary cleanup if the job is - marked as JDEAD. +/* Handle SIGINTs received while waiting for a foreground JOB, or CHILD if it + isn't part of a job, to finish. */ +static void handle_sigint_caught_during_wait (child, job) + PROCESS *child; + int job; +{ + SigHandler *temp_handler; + if (job != NO_JOB) + child = jobs[job]->pipe; + int tstatus, job_has_job_control, is_foreground_job; + + job_has_job_control = job == NO_JOB ? job_control != 0 : IS_JOBCONTROL (job); + is_foreground_job = job == NO_JOB ? interactive_shell == 0 : + IS_FOREGROUND (job); + + if (!(wait_sigint_received && is_foreground_job)) + return; + + /* If we're running a shell script and we get a SIGINT with a + SIGINT trap handler, but the foreground job handles it and + does not exit due to SIGINT, run the trap handler but do not + otherwise act as if we got the interrupt. */ + if (interactive_shell == 0 && + child_caught_sigint && signal_is_trapped (SIGINT)) + { + int old_frozen; + wait_sigint_received = 0; + last_command_exit_value = process_exit_status (child->status); + + old_frozen = jobs_list_frozen; + jobs_list_frozen = 1; + tstatus = maybe_call_trap_handler (SIGINT); + jobs_list_frozen = old_frozen; + } + + /* If the foreground job is killed by SIGINT when job control is not + active, we need to perform some special handling. + + The check of wait_sigint_received is a way to determine if the + SIGINT came from the keyboard (in which case the shell has already + seen it, and wait_sigint_received is non-zero, because keyboard + signals are sent to process groups) or via kill(2) to the foreground + process by another process (or itself). If the shell did receive the + SIGINT, it needs to perform normal SIGINT processing. */ + else if (child_caught_sigint == 0 && job_has_job_control == 0) + { + int old_frozen; + + wait_sigint_received = 0; + + /* If SIGINT is trapped, set the exit status so that the trap + handler can see it. */ + if (signal_is_trapped (SIGINT)) + last_command_exit_value = process_exit_status (child->status); + + /* If the signal is trapped, let the trap handler get it no matter + what and simply return if the trap handler returns. + maybe_call_trap_handler() may cause dead jobs to be removed from + the job table because of a call to execute_command. We work + around this by setting JOBS_LIST_FROZEN. */ + old_frozen = jobs_list_frozen; + jobs_list_frozen = 1; + tstatus = maybe_call_trap_handler (SIGINT); + jobs_list_frozen = old_frozen; + if (tstatus == 0 && old_sigint_handler != INVALID_SIGNAL_HANDLER) + { + /* wait_sigint_handler () has already seen SIGINT and + allowed the wait builtin to jump out. We need to + call the original SIGINT handler, if necessary. If + the original handler is SIG_DFL, we need to resend + the signal to ourselves. */ + + temp_handler = old_sigint_handler; + + /* Bogus. If we've reset the signal handler as the result + of a trap caught on SIGINT, then old_sigint_handler + will point to trap_handler, which now knows nothing about + SIGINT (if we reset the sighandler to the default). + In this case, we have to fix things up. What a crock. */ + if (temp_handler == trap_handler && signal_is_trapped (SIGINT) == 0) + temp_handler = trap_to_sighandler (SIGINT); + restore_sigint_handler (); + if (temp_handler == SIG_DFL) + termsig_handler (SIGINT); /* XXX */ + else if (temp_handler != SIG_IGN) + (*temp_handler) (SIGINT); + } + } +} - Currently, the cleanup activity is restricted to handling any SIGINT - received while waiting for a foreground job to finish. */ +/* Set the status of JOB and perform any necessary cleanup if the job is + marked as JDEAD. */ static int set_job_status_and_cleanup (job) int job; { PROCESS *child; - int tstatus, job_state, any_stopped, any_tstped, call_set_current; - SigHandler *temp_handler; + int job_state, any_stopped, any_tstped, call_set_current; child = jobs[job]->pipe; jobs[job]->flags &= ~J_NOTIFIED; @@ -3467,81 +3558,7 @@ set_job_status_and_cleanup (job) */ if (JOBSTATE (job) == JDEAD) - { - /* If we're running a shell script and we get a SIGINT with a - SIGINT trap handler, but the foreground job handles it and - does not exit due to SIGINT, run the trap handler but do not - otherwise act as if we got the interrupt. */ - if (wait_sigint_received && interactive_shell == 0 && - child_caught_sigint && IS_FOREGROUND (job) && - signal_is_trapped (SIGINT)) - { - int old_frozen; - wait_sigint_received = 0; - last_command_exit_value = process_exit_status (child->status); - - old_frozen = jobs_list_frozen; - jobs_list_frozen = 1; - tstatus = maybe_call_trap_handler (SIGINT); - jobs_list_frozen = old_frozen; - } - - /* If the foreground job is killed by SIGINT when job control is not - active, we need to perform some special handling. - - The check of wait_sigint_received is a way to determine if the - SIGINT came from the keyboard (in which case the shell has already - seen it, and wait_sigint_received is non-zero, because keyboard - signals are sent to process groups) or via kill(2) to the foreground - process by another process (or itself). If the shell did receive the - SIGINT, it needs to perform normal SIGINT processing. */ - else if (wait_sigint_received && - child_caught_sigint == 0 && - IS_FOREGROUND (job) && IS_JOBCONTROL (job) == 0) - { - int old_frozen; - - wait_sigint_received = 0; - - /* If SIGINT is trapped, set the exit status so that the trap - handler can see it. */ - if (signal_is_trapped (SIGINT)) - last_command_exit_value = process_exit_status (child->status); - - /* If the signal is trapped, let the trap handler get it no matter - what and simply return if the trap handler returns. - maybe_call_trap_handler() may cause dead jobs to be removed from - the job table because of a call to execute_command. We work - around this by setting JOBS_LIST_FROZEN. */ - old_frozen = jobs_list_frozen; - jobs_list_frozen = 1; - tstatus = maybe_call_trap_handler (SIGINT); - jobs_list_frozen = old_frozen; - if (tstatus == 0 && old_sigint_handler != INVALID_SIGNAL_HANDLER) - { - /* wait_sigint_handler () has already seen SIGINT and - allowed the wait builtin to jump out. We need to - call the original SIGINT handler, if necessary. If - the original handler is SIG_DFL, we need to resend - the signal to ourselves. */ - - temp_handler = old_sigint_handler; - - /* Bogus. If we've reset the signal handler as the result - of a trap caught on SIGINT, then old_sigint_handler - will point to trap_handler, which now knows nothing about - SIGINT (if we reset the sighandler to the default). - In this case, we have to fix things up. What a crock. */ - if (temp_handler == trap_handler && signal_is_trapped (SIGINT) == 0) - temp_handler = trap_to_sighandler (SIGINT); - restore_sigint_handler (); - if (temp_handler == SIG_DFL) - termsig_handler (SIGINT); /* XXX */ - else if (temp_handler != SIG_IGN) - (*temp_handler) (SIGINT); - } - } - } + handle_sigint_caught_during_wait (NULL, job); return call_set_current; }