From 46a311165e57a1292970938c66f3c65247fd5c25 Mon Sep 17 00:00:00 2001 From: David Wilson Date: Wed, 18 Apr 2018 21:58:31 +0100 Subject: [PATCH] issue #148: parent: prevent race in iter_read() There is no guarantee on the ordering select() returns file descriptors. So if, e.g. in the case of sudo_nonexistent.yml, sudo prints an error to a single FD before exitting, there was previously no gurantee iter_read() would read off the error before failing due to detecting disconnect on any FD. Now instead we keep reading while any non-disconnected FD exists. --- .travis/ansible_tests.sh | 2 +- mitogen/parent.py | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/.travis/ansible_tests.sh b/.travis/ansible_tests.sh index 0d6df4ae..12cad13c 100755 --- a/.travis/ansible_tests.sh +++ b/.travis/ansible_tests.sh @@ -51,7 +51,7 @@ echo \ # Build the binaries. make -C ${TRAVIS_BUILD_DIR}/tests/ansible -sudo apt install -y sshpass +[ ! "$(type -p sshpass)" ] && sudo apt install -y sshpass echo travis_fold:end:job_setup diff --git a/mitogen/parent.py b/mitogen/parent.py index 515a9e26..56dbc31f 100644 --- a/mitogen/parent.py +++ b/mitogen/parent.py @@ -379,10 +379,11 @@ def write_all(fd, s, deadline=None): def iter_read(fds, deadline=None): + fds = list(fds) bits = [] timeout = None - while True: + while fds: if deadline is not None: timeout = max(0, deadline - time.time()) if timeout == 0: @@ -394,15 +395,19 @@ def iter_read(fds, deadline=None): for fd in rfds: s, disconnected = mitogen.core.io_op(os.read, fd, 4096) - IOLOG.debug('iter_read(%r) -> %r', fd, s) if disconnected or not s: - raise mitogen.core.StreamError( - 'EOF on stream; last 300 bytes received: %r' % - (''.join(bits)[-300:],) - ) - - bits.append(s) - yield s + IOLOG.debug('iter_read(%r) -> disconnected', fd) + fds.remove(fd) + else: + IOLOG.debug('iter_read(%r) -> %r', fd, s) + bits.append(s) + yield s + + if not fds: + raise mitogen.core.StreamError( + 'EOF on stream; last 300 bytes received: %r' % + (''.join(bits)[-300:],) + ) raise mitogen.core.TimeoutError('read timed out')