Send initial data before calling select whenever possible

Without this, we could execute «ssh -q ...» and call select(), which
would timeout after the default 10s, and only then send initial data.
(This is a relic of the earlier change where we always ran ssh with
-vvv, so the situation where it would sit quietly never happened in
practice; but this would have been the right thing to do even then.)
This commit is contained in:
Abhijit Menon-Sen 2015-09-23 20:09:50 +05:30
parent c9a004227e
commit 587054db2a

View file

@ -436,6 +436,13 @@ class Connection(ConnectionBase):
for fd in rpipes: for fd in rpipes:
fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK) fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)
# If we can send initial data without waiting for anything, we do so
# before we call select.
if states[state] == 'ready_to_send' and in_data:
self._send_initial_data(stdin, in_data)
state += 1
while True: while True:
rfd, wfd, efd = select.select(rpipes, [], rpipes, timeout) rfd, wfd, efd = select.select(rpipes, [], rpipes, timeout)
@ -518,18 +525,11 @@ class Connection(ConnectionBase):
# Once we're sure that the privilege escalation prompt, if any, has # Once we're sure that the privilege escalation prompt, if any, has
# been dealt with, we can send any initial data and start waiting # been dealt with, we can send any initial data and start waiting
# for output. (Note that we have to close the process's stdin here, # for output.
# otherwise, for example, "sftp -b -" will just hang forever waiting
# for more commands.)
if states[state] == 'ready_to_send': if states[state] == 'ready_to_send':
if in_data: if in_data:
self._display.debug('Sending initial data (%d bytes)' % len(in_data)) self._send_initial_data(stdin, in_data)
try:
stdin.write(in_data)
stdin.close()
except (OSError, IOError):
raise AnsibleConnectionFailure('SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh')
state += 1 state += 1
# Now we just wait for the process to exit. Output is already being # Now we just wait for the process to exit. Output is already being
@ -567,6 +567,21 @@ class Connection(ConnectionBase):
return (p.returncode, stdout, stderr) return (p.returncode, stdout, stderr)
def _send_initial_data(self, fh, in_data):
'''
Writes initial data to the stdin filehandle of the subprocess and closes
it. (The handle must be closed; otherwise, for example, "sftp -b -" will
just hang forever waiting for more commands.)
'''
self._display.debug('Sending initial data (%d bytes)' % len(in_data))
try:
fh.write(in_data)
fh.close()
except (OSError, IOError):
raise AnsibleConnectionFailure('SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh')
# This is a separate method because we need to do the same thing for stdout # This is a separate method because we need to do the same thing for stdout
# and stderr. # and stderr.