zsh-workers
 help / color / mirror / code / Atom feed
* A tail of two situations
@ 2013-05-11 16:03 Jeremy Mates
  2013-05-11 22:40 ` Bart Schaefer
  0 siblings, 1 reply; 2+ messages in thread
From: Jeremy Mates @ 2013-05-11 16:03 UTC (permalink / raw)
  To: zsh-workers

If MONITOR is set in ZSH, and control+c issued, the subsequent command
*is not* run:

  % tail -n0 -f /etc/passwd; echo foo
  ^C
  %

However! If tail is modified to handle SIGINT, and quits via errx(3)
or the like:

  void polonius_polka(int sig)
  {
    errx(1, "oh I am slain"); // 128+sig does not replicate "err by sig"
  }

the subsequent command *is* run:

  % tail -n0 -f /etc/passwd; echo foo
  ^Ctail: oh I am slain
  foo
  %

This seems inconsistent, as the behavior deep within ZSH (with MONITOR
set) depends on how(if) the program handles the ^C. Consistency in ZSH
demands either `unsetopt MONITOR` (subsequent code never run), or using
a subshell and trapping INT via a "function trap" (subsequent code run):

  % ( TRAPINT(){}; tail -n0 -f /etc/passwd ); echo foo
  ^Cfoo
  % 

(But not the "list trap" `trap '' INT`, as that causes ZSH to ignore the
SIGINT, and pass that ignore down to tail, unless disabling control+c
is for some reason desired.)

Spelunking Src/jobs.c and Src/signals.c reveals that in either case
(`tail` having a signal handler or not), ZSH receives a SIGCHLD, and
that the only apparent difference is the contents of the "status" int
populated by the wait(2) call, which eventually reaches the

    /* When MONITOR is set, the foreground process runs in a different *
     * process group from the shell, so the shell will not receive     *
     * terminal signals, therefore we pretend that the shell got       *
     * the signal too.                                                 */
    if (inforeground == 2 && isset(MONITOR) && WIFSIGNALED(status)) {
        int sig = WTERMSIG(status);

block of code in Src/jobs.c, and then for the tail-with-no-signal-
handler case the code sets `breaks = loops; errflag = 1`, wanders into
check_cursh_sig(), which does nothing in this case, and then the code
returns to who knows where. Something in that subsequent code then does
not run the `; echo foo` code. However, I do not know where that code
is, and do not know whether changing it would cause any other
ramifications. It might be nice to have consistent behavior, though
there is a workaround of either unsetting MONITOR, or using a
subshell/TRAPINT function.


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: A tail of two situations
  2013-05-11 16:03 A tail of two situations Jeremy Mates
@ 2013-05-11 22:40 ` Bart Schaefer
  0 siblings, 0 replies; 2+ messages in thread
From: Bart Schaefer @ 2013-05-11 22:40 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 2964 bytes --]

On Saturday, May 11, 2013, Jeremy Mates wrote:
>
> This seems inconsistent, as the behavior deep within ZSH (with MONITOR
> set) depends on how(if) the program handles the ^C. Consistency in ZSH
> demands either `unsetopt MONITOR` (subsequent code never run), or using
> a subshell and trapping INT via a "function trap"


Although this may seem inconsistent, the definition of signal delivery in
Unix-like operating systems makes it difficult to do anything else.  A
signal may be delivered either to a single process, or to all processes in
a single process group.  As explained in the comment you quoted:


>     /* When MONITOR is set, the foreground process runs in a different *
>      * process group from the shell, so the shell will not receive     *
>      * terminal signals, therefore we pretend that the shell got       *
>      * the signal too.                                                 */
>

For process groups, you can think of it as if the signal were percolating
toward the root of the process tree from the leaf children.  If the
"leader" (closest to the root) of the signalled process group handles the
signal, then that's the end of it; the only way its parent can determine
what happened is by examining the exit status, which the child can set to
anything it likes.  (In your example there is only one process in the
group, so the leaf is the leader.)

For comparison, consider what you'd want to have happen if "tail" handled
the signal and then proceeded without exiting, until it eventually exited
with status zero somewhere down the line.

To be "consistent" zsh could, when monitor is off, discard the signal that
it recieves as the group leader and always run the next command, thus
"pretending" that every foreground process is its own group leader; but
that leads to things like shell scripts it is impossible to interrupt.
 (It's true there are other things that also lead to that, but they are
less common.)

The other option, when monitor is on, would be to create an extra zsh
process whose sole job is to be the leader of every foreground process
group, to be certain of receiving all signals and alerting the parent
shell.  This means doubling the number of forks on every command and having
an otherwise useless extra copy of the shell hanging around, even though *most*
processes are well-behaved and reveal the signal state in their exit status.

In fact the reason you need the trap in the subshell is because zsh instead
chooses to minimize the number of forks or extra copies of itself, and so
exec's the subshell out of existence if it has no traps and only a single
child to wait for.

Originally (before the writing of the code that goes along with the comment
you quoted), zsh "consistently" ignored whether the exit status indicated a
signal and only responded to signals that it received directly.  Users
complained that this did not match expectation in the "well-behaved child"
situation, and here we are.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2013-05-11 22:40 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-05-11 16:03 A tail of two situations Jeremy Mates
2013-05-11 22:40 ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).