Subject: bug#8938: make timeout and CTRL-C

From: shay shimony <[email-masked]>
Date: Tue, 28 Jun 2011 21:33:39 +0300

Thanks for taking care for this! Interesting stuff as well.

2011/6/28 Pádraig Brady <[email-masked]>

> On 27/06/11 21:12, Alan Curry wrote:
> > =?UTF-8?Q?P=C3=A1draig?= Brady writes:
> >>
> >> On 26/06/11 20:20, shay shimony wrote:
> >>> all:
> >>>         timeout 12 sleep 10
> >>>
> >>> Note there is a tab before "timeout 12 sleep 10".
> >>> Then run at same directory where the file is located "make" and try to
> press
> >>> CTRL-C.
> >>>
> >>> Notes:
> >>> CTRL-Z works.
> >>> When executing timeout without make CTRL-C works.
> >>> When executing make without timeout CTRL-C works.
> >>
> >> Drats,
> >>
> >> That because SIGINT is sent by the terminal to the foreground group.
> >> The issue is that `make` and `timeout` use much the same method
> >> to control their jobs. I.E. they create their own process group
> >> so they can terminate all sub-processes.
> >
> > Are you sure? I see no evidence of that. When I run make with the above
> > makefile, the processes look like this:
> >
> >  PPID   PID  PGID   SID TTY TPGID  STAT  UID   TIME COMMAND
> >     1  1451  1451  1451   6 16407  S    1000   0:06 -zsh
> >  1451 16407 16407  1451   6 16407  S    1000   0:00 make
> > 16407 16408 16408  1451   6 16407  S    1000   0:00 timeout 60 sleep 30
> > 16408 16409 16408  1451   6 16407  S    1000   0:00 sleep 30
> >
> > The first PGID is the login shell. The second PGID is make, which was put
> > into its own process group by the shell because the shell has job control
> > enabled. The last PGID is timeout, which put itself into a process group.
> > make never noticed any of them.
> >
> > In the source for GNU make 3.82 there are no calls to setpgrp or setpgid
> > (unless obfuscated from grep). There is the following comment:
> >
> >   /* A termination signal won't be sent to the entire
> >      process group, but it means we want to kill the children.  */
> >
> > That's above the handling of SIGTERM, which iterates over child processes
> and
> > passes along the SIGTERM to them.
> >
> > After that is the handling of SIGINT, which doesn't kill child processes
> > (unless they're "remote", which is... news to me that make does remote
> > things) but just waits for them.
> >
> > What seems to be happening is that make *doesn't* create a process group,
> > therefore assumes that when it gets a SIGINT, its children have already
> > gotten it too, and it just waits for them to die. A child that puts
> itself
> > into a new process group screws this up (as would kill -2 `pidof make`).
>
> Thanks for the analysis Alan.
> Yes you're right I think.
> In any case the important point is that timeout sets itself as group
> leader,
> and is not the foreground group.
>
> >
> > I think the answer is that timeout should put itself into the foreground.
> > That way it would get the SIGINT. make wouldn't get it, but wouldn't need
> to.
> > timeout would exit quickly after SIGINT and make would proceed or abort
> > according to the exit code.
>
> I've a version locally here actually that calls tcsetpgrp() but I
> discounted
> that as it's not timeout's place to call that I think.
> timeout sets itself as group leader so that it can kill everything it
> starts,
> but it shouldn't need to grab the foreground group as the shell (or make)
> may be starting it in the background etc.
>
> So really `make` should not assume children stay in the same group,
> and propagate signals down (like it does for TERM).
> It might be appropriate for `make` to call tcsetpgrp() before exec..()
> but given remote & parallel jobs etc. signal propagation might be best.
>
> I've fixed up `timeout` itself not assume children stay in
> the same group, and pass signals on, in the attached patch.
>
> cheers,
> Pádraig.
>