Restore SIGPIPE default action before exec(3)

Python chooses to ignore SIGPIPE, however most unix processes expect
to terminate on the signal.  Therefore failing to restore the default
action results in surprising behavior.  For example, we expect
`dd if=/dev/zero | head -c1` to return immediately.  However, prior to
this commit, that pipeline would hang forever.  Insidious forms of
data corruption or loss were also possible.

See:

  http://www.chiark.greenend.org.uk/ucgi/~cjwatson/blosxom/2009-07-02-python-sigpipe.html
  http://blog.nelhage.com/2010/02/a-very-subtle-bug/
This commit is contained in:
Travis Cross 2013-06-28 01:48:41 +00:00 committed by Avery Pennarun
commit cb713bdace

View file

@ -1,4 +1,4 @@
import sys, os, errno, stat import sys, os, errno, stat, signal
import vars, jwack, state import vars, jwack, state
from helpers import unlink, close_on_exec, join from helpers import unlink, close_on_exec, join
from log import log, log_, debug, debug2, err, warn from log import log, log_, debug, debug2, err, warn
@ -190,6 +190,7 @@ class BuildJob:
def run(): def run():
os.chdir(vars.BASE) os.chdir(vars.BASE)
os.environ['REDO_DEPTH'] = vars.DEPTH + ' ' os.environ['REDO_DEPTH'] = vars.DEPTH + ' '
signal.signal(signal.SIGPIPE, signal.SIG_DFL) # python ignores SIGPIPE
os.execvp(argv[0], argv) os.execvp(argv[0], argv)
assert(0) assert(0)
# returns only if there's an exception # returns only if there's an exception
@ -214,6 +215,7 @@ class BuildJob:
os.dup2(self.f.fileno(), 1) os.dup2(self.f.fileno(), 1)
os.close(self.f.fileno()) os.close(self.f.fileno())
close_on_exec(1, False) close_on_exec(1, False)
signal.signal(signal.SIGPIPE, signal.SIG_DFL) # python ignores SIGPIPE
if vars.VERBOSE or vars.XTRACE: log_('* %s\n' % ' '.join(self.argv)) if vars.VERBOSE or vars.XTRACE: log_('* %s\n' % ' '.join(self.argv))
os.execvp(self.argv[0], self.argv) os.execvp(self.argv[0], self.argv)
assert(0) assert(0)