core: rearrange stdio setup to cope with buffering; closes #422
This commit is contained in:
parent
704e6c0b2c
commit
01c4f3fee1
|
@ -245,6 +245,10 @@ Core Library
|
|||
execution of its :keyword:`finally` block was delayed on Python 3. Now
|
||||
callers explicitly close the generator when finished.
|
||||
|
||||
* `#421 <https://github.com/dw/mitogen/issues/421>`_: the fork method could
|
||||
fail to start if :data:`sys.stdout` was opened in block buffered mode, and
|
||||
buffered data was pending in the parent prior to fork.
|
||||
|
||||
* `16ca111e <https://github.com/dw/mitogen/commit/16ca111e>`_: handle OpenSSH
|
||||
7.5 permission denied prompts when ``~/.ssh/config`` rewrites are present.
|
||||
|
||||
|
|
|
@ -2808,28 +2808,32 @@ class ExternalContext(object):
|
|||
mitogen.parent_ids = self.config['parent_ids'][:]
|
||||
mitogen.parent_id = mitogen.parent_ids[0]
|
||||
|
||||
def _setup_stdio(self):
|
||||
# We must open this prior to closing stdout, otherwise it will recycle
|
||||
# a standard handle, the dup2() will not error, and on closing it, we
|
||||
# lose a standrd handle, causing later code to again recycle a standard
|
||||
# handle.
|
||||
fp = open('/dev/null')
|
||||
|
||||
# When sys.stdout was opened by the runtime, overwriting it will not
|
||||
# cause close to be called. However when forking from a child that
|
||||
# previously used fdopen, overwriting it /will/ cause close to be
|
||||
# called. So we must explicitly close it before IoLogger overwrites the
|
||||
# file descriptor, otherwise the assignment below will cause stdout to
|
||||
# be closed.
|
||||
sys.stdout.close()
|
||||
sys.stdout = None
|
||||
|
||||
def _nullify_stdio(self):
|
||||
"""
|
||||
Open /dev/null to replace stdin, and stdout/stderr temporarily. In case
|
||||
of odd startup, assume we may be allocated a standard handle.
|
||||
"""
|
||||
fd = os.open('/dev/null', os.O_RDWR)
|
||||
try:
|
||||
os.dup2(fp.fileno(), 0)
|
||||
os.dup2(fp.fileno(), 1)
|
||||
os.dup2(fp.fileno(), 2)
|
||||
for stdfd in (0, 1, 2):
|
||||
if fd != stdfd:
|
||||
os.dup2(fd, stdfd)
|
||||
finally:
|
||||
fp.close()
|
||||
if fd not in (0, 1, 2):
|
||||
os.close(fd)
|
||||
|
||||
def _setup_stdio(self):
|
||||
# When sys.stdout was opened by the runtime, overwriting it will not
|
||||
# close FD 1. However when forking from a child that previously used
|
||||
# fdopen(), overwriting it /will/ close FD 1. So we must swallow the
|
||||
# close before IoLogger overwrites FD 1, otherwise its new FD 1 will be
|
||||
# clobbered. Additionally, stdout must be replaced with /dev/null prior
|
||||
# to stdout.close(), since if block buffering was active in the parent,
|
||||
# any pre-fork buffered data will be flushed on close(), corrupting the
|
||||
# connection to the parent.
|
||||
self._nullify_stdio()
|
||||
sys.stdout.close()
|
||||
self._nullify_stdio()
|
||||
|
||||
self.stdout_log = IoLogger(self.broker, 'stdout', 1)
|
||||
self.stderr_log = IoLogger(self.broker, 'stderr', 2)
|
||||
|
|
Loading…
Reference in New Issue