Subject: Re: bug#17981: [PATCH] maint.mk: less syntax-check noise when SIGPIPE is ignored

From: Eric Blake <[email-masked]>
Date: Fri, 11 Jul 2014 15:10:21 -0600

On 07/11/2014 02:58 PM, Paul Eggert wrote:
> On 07/08/2014 12:42 PM, Eric Blake wrote:
>> It is unclear
>> at this point whether POSIX would recommend that filter
>> applications should_always_  exit with 0 status on pipe failure,
>> or only do this for EPIPE write failures when SIGPIPE is ignored,
>> or whether it should be optional behavior that must be explicitly
>> enabled via a command-line option and/or system-wide environment
>> variable.
> 
> None of these options sound appealing, I'm afraid.   The first two would
> be an incompatible change to longstanding standard behavior.  A
> system-wide environment variable would be problematic for all the usual
> reaosns.  A command-line option would be a pain to use (what? I have to
> modify all my shell scripts?).

Not all your scripts, only those scripts where you plan to use 'set -o
pipefail'.

> 
> Instead, how about this idea?  Change the behavior of the shell so that
> SIGPIPE is not ignored in a pipeline (except in the pipeline's last
> member of course), even if it is ignored in the parent.  This is also a
> change to POSIX, but it's a relatively minor one.  Or, if we want to be
> conservative about it, we could make the new behavior depend on a new
> shell option.  Either way, this would solve the problem without having
> to change grep, sed, etc.

That's not quite right.  Remember, the choice is between:

sigpipe normal:
foo | bar

if foo dies from SIGPIPE, but 'set -o pipefail' is in effect, then the
whole pipeline fails with status 141 (SIGPIPE killed a member of the
pipeline).

vs. sigpipe ignored:

foo | bar

foo will NOT get SIGPIPE, but instead gets EPIPE.  If it treats the
write error as fatal, and exits non-zero, then 'set -o pipefail' fails
the whole pipeline.  But if it treats the write error as the request to
do an early non-fatal exit with status 0, then the whole pipeline can
also have status 0.

The idea is that if you are going to write code with 'set -o pipefail',
you would do it like:

set -o pipefail
(trap '' PIPE; foo) | bar
set +o pipefail

where you explicitly ignore SIGPIPE (and force EPIPE write errors) on
any element of the pipeline where you expect the right side of the pipe
may exit early.

> 
> We might also want to have a way to reenable traps in the shell when
> they're disabled; that's been a longstanding problem even aside from
> this SIGPIPE business.

Yes, that would be nice to have.  But it goes in the opposite direction
of 'set -o pipefail', because re-enabling default SIGPIPE behavior
causes processes to die with status 141.

Or are you arguing that any shell that provides 'set -o pipefail' should
ALSO provide a knob to explicitly treat death due to SIGPIPE as not
impacting pipefail?  At which point, then you DO want to re-enable
rather than ignore SIGPIPE, and don't have to worry about what child
processes do on EPIPE, but only what they do on SIGPIPE, where death by
SIGPIPE is not fatal to the pipeline.

Probably worth adding some of these thoughts to the Austin Group bug
proposal.

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org