Subject: bug#15781: timeout: Child gets SIGTTOU when run from a shell script

From: Pádraig Brady <[email-masked]>
Date: Fri, 01 Nov 2013 11:53:44 +0000

On 11/01/2013 08:10 AM, Richard W.M. Jones wrote:
> Grab the tiny 'run' and 'test.c' attachments from here:
> 
> https://bugzilla.redhat.com/show_bug.cgi?id=1025269#c5
> 
> $ cd /tmp
> $ gcc -Wall test.c -o test
> $ ./test                             # this is ok
> $ timeout 4h ./test                  # this is ok
> 
> $ cat ./run
> #!/bin/bash -
> timeout 4h ./test
> 
> $ ./run                              # this HANGS
> 
> The run script simply runs the same timeout command as before, from a
> shell script, but this time the test child process gets SIGTTOU and
> suspends itself, resulting in a hang.
> 
> I was able to fix this by *removing* the following lines in
> src/timeout.c:
> 
>        /* exec doesn't reset SIG_IGN -> SIG_DFL.  */
>        signal (SIGTTIN, SIG_DFL);
>        signal (SIGTTOU, SIG_DFL);
> 
> In other words, leave SIGTTOU as SIG_IGN in the child process (which
> seems to make sense -- since the child is a backgrounded process, but
> we want it to be able to print things, we don't want it to get SIGTTOU
> signals when it outputs to the terminal).
> 
> Anyway, I don't understand why this only happens when run from a shell
> script, and not from a terminal.  Presumably bash does something
> strange with SIGTTOU.

Probably something to do with job control
If you `set -m` first in the script,
then the test binary doesn't hang.

> Also I don't understand from looking at the kernel code why it's
> sending SIGTTOU in one case but not the other.  The relevant code is
> in drivers/tty/tty_io.c:tty_check_change but AFAICT it should send the
> signal in both cases.
> 
> Rich.
> 

Ugh these signals are really hairy to think about,
there are all sorts of edge cases here.

I did a version of timeout once that put the child in it's own group,
and noted in that version that I needed to leave SIGTTOU as IGN
as tcsetpgrp(0, getpid()) caused SIGTTOU to be sent and I wasn't sure why.

Maybe we should be resetting SIGTT{OU,IN} to what it was
previously set to, rather than SIG_DFL?

Pádraig.