From b2cd4a62decc43f8960a11c54e02a88bb90c72d6 Mon Sep 17 00:00:00 2001 From: jeromew Date: Tue, 31 Dec 2013 14:29:46 -0500 Subject: [PATCH] fix issue #5372 on ssh_alt: accept -K option even for a user with NOPASSWD --- .../runner/connection_plugins/ssh_alt.py | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/lib/ansible/runner/connection_plugins/ssh_alt.py b/lib/ansible/runner/connection_plugins/ssh_alt.py index 7b40cb7e4c..1f9fb04bd5 100644 --- a/lib/ansible/runner/connection_plugins/ssh_alt.py +++ b/lib/ansible/runner/connection_plugins/ssh_alt.py @@ -151,6 +151,9 @@ class Connection(object): ssh_cmd = self._password_cmd() ssh_cmd += ["ssh", "-C"] if not in_data: + # we can only use tty when we are not pipelining the modules. piping data into /usr/bin/python + # inside a tty automatically invokes the python interactive-mode but the modules are not + # compatible with the interactive-mode ("unexpected indent" mainly because of empty lines) ssh_cmd += ["-tt"] if utils.VERBOSITY > 3: ssh_cmd += ["-vvv"] @@ -200,31 +203,52 @@ class Connection(object): p = subprocess.Popen(ssh_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = p.stdin - self._send_password() if self.runner.sudo and sudoable and self.runner.sudo_pass: + # several cases are handled for sudo privileges with password + # * NOPASSWD (tty & no-tty): detect success_key on stdout + # * without NOPASSWD: + # * detect prompt on stdout (tty) + # * detect prompt on stderr (no-tty) fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) + fcntl.fcntl(p.stderr, fcntl.F_SETFL, + fcntl.fcntl(p.stderr, fcntl.F_GETFL) | os.O_NONBLOCK) sudo_output = '' - if in_data: - # no terminal => no prompt on output. process is waiting for sudo_pass - stdin.write(self.runner.sudo_pass + '\n') + sudo_errput = '' + while not sudo_output.endswith(prompt) and success_key not in sudo_output: - rfd, wfd, efd = select.select([p.stdout], [], + rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout], self.runner.timeout) + if p.stderr in rfd: + chunk = p.stderr.read() + if not chunk: + raise errors.AnsibleError('ssh connection closed waiting for sudo password prompt') + sudo_errput += chunk + incorrect_password = gettext.dgettext( + "sudo", "Sorry, try again.") + if sudo_errput.strip().endswith("%s%s" % (prompt, incorrect_password)): + raise errors.AnsibleError('Incorrect sudo password') + elif sudo_errput.endswith(prompt): + stdin.write(self.runner.sudo_pass + '\n') + if p.stdout in rfd: chunk = p.stdout.read() if not chunk: raise errors.AnsibleError('ssh connection closed waiting for sudo password prompt') sudo_output += chunk - else: + + if not rfd: + # timeout. wrap up process communication stdout = p.communicate() raise errors.AnsibleError('ssh connection error waiting for sudo password prompt') + if success_key not in sudo_output: stdin.write(self.runner.sudo_pass + '\n') fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) + fcntl.fcntl(p.stderr, fcntl.F_SETFL, fcntl.fcntl(p.stderr, fcntl.F_GETFL) & ~os.O_NONBLOCK) # We can't use p.communicate here because the ControlMaster may have stdout open as well stdout = '' stderr = ''