A running background job can be stopped by SIGTTOU or SIGTTIN, just as the fg command issues tcsetprgp() to give the job the controlling terminal. This causes the fg command to appear to have caused the job to stop.
Prevent this from happening by always issuing SIGCONT after the fg command calls tcsetpgrp(). The following script reproduces the issue with the fg command returning status 149 (SIGTTIN 21): + sleep 0.0008305541 + sleep 0.00062593 + sleep 0.004898882 + jobs [1]- Running ( sleep ${1}1; read X ) & [2]+ Running ( sleep ${3}2; kill $! ) & + RC=0 + read X + fg %1 ( sleep ${1}1; read X ) + RC=149 + '[' 149 = 143 -o 149 = 1 -o 149 = 2 ']' + jobs [1]+ Stopped ( sleep ${1}1; read X ) [2]- Running ( sleep ${3}2; kill $! ) & + exit 1 #!/bin/sh set -mbux rand() { set -- $(( $( od -An -N1 -t u1 /dev/random ) % 5 + 6 )) printf "%0$1u" $(( $(od -An -N4 -t u4 /dev/random) % 1000000 )) } race() { set -- $1 $2 $(rand) set -- $1 $2 0.$3 rm -f READ ( sleep ${1}1 ; read X ) & ( sleep ${3}2 ; kill $! ) & sleep ${2}3 jobs RC=0 fg %1 || RC=$? # # bash fg returns 1, and dash fg can return 2, if # the job has already terminated. # [ $RC = 143 -o $RC = 1 -o $RC = 2 ] || { #ps wwwaxjfh jobs exit 1 } wait %1 wait %2 wait %3 } main() { set -- ${1-0} ${2-0} while : ; do set -- ${1%.*} ${2%.*} set -- "$@" $(rand) set -- "$@" $(rand) set -- $1.$3 $2.$4 race "$@" done } main "$@" Signed-off-by: Earl Chew <earl_c...@yahoo.com> --- jobs.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/jobs.c b/jobs.c index 6b986ed7..e11b1a9f 100644 --- a/jobs.c +++ b/jobs.c @@ -3543,6 +3543,7 @@ start_job (job, foreground) { register PROCESS *p; int already_running; + int jump_start; sigset_t set, oset; char *wd, *s; static TTYSTRUCT save_stty; @@ -3564,6 +3565,7 @@ start_job (job, foreground) } already_running = RUNNING (job); + jump_start = !already_running; if (foreground == 0 && already_running) { @@ -3616,7 +3618,10 @@ start_job (job, foreground) /* Run the job. */ if (already_running == 0) - set_job_running (job); + { + set_job_running (job); + jobs[job]->flags |= J_NOTIFIED; + } /* Save the tty settings before we start the job in the foreground. */ if (foreground) @@ -3625,17 +3630,18 @@ start_job (job, foreground) save_stty = shell_tty_info; /* Give the terminal to this job. */ if (IS_JOBCONTROL (job)) - give_terminal_to (jobs[job]->pgrp, 0); + { + give_terminal_to (jobs[job]->pgrp, 0); + jump_start = 1; + } } else jobs[job]->flags &= ~J_FOREGROUND; - /* If the job is already running, then don't bother jump-starting it. */ - if (already_running == 0) - { - jobs[job]->flags |= J_NOTIFIED; - killpg (jobs[job]->pgrp, SIGCONT); - } + /* Jump start if not running. Also jump start if running as foreground + via job control to avoid racing with SIGTTOU and SIGTTIN signals. */ + if (jump_start) + killpg (jobs[job]->pgrp, SIGCONT); if (foreground) { -- 2.39.1