From 587054db2a3b419b98e0b696ce3d3948eb15fbcf Mon Sep 17 00:00:00 2001 From: Abhijit Menon-Sen Date: Wed, 23 Sep 2015 20:09:50 +0530 Subject: [PATCH] Send initial data before calling select whenever possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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.) --- lib/ansible/plugins/connection/ssh.py | 33 +++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/lib/ansible/plugins/connection/ssh.py b/lib/ansible/plugins/connection/ssh.py index c485ba84fe7..c383ac133ea 100644 --- a/lib/ansible/plugins/connection/ssh.py +++ b/lib/ansible/plugins/connection/ssh.py @@ -436,6 +436,13 @@ class Connection(ConnectionBase): for fd in rpipes: 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: 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 # 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, - # otherwise, for example, "sftp -b -" will just hang forever waiting - # for more commands.) + # for output. if states[state] == 'ready_to_send': if in_data: - self._display.debug('Sending initial data (%d bytes)' % len(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') + self._send_initial_data(stdin, in_data) state += 1 # 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) + 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 # and stderr.