def exec_command(self, cmd, tmp_path, become_user, **kwargs): executable = kwargs.get('executable', '/bin/sh') sudoable = kwargs.get('sudoable', False) if 'su' in kwargs or 'su_user' in kwargs: raise errors.AnsibleError("Internal Error: this module does not support running commands via su") if kwargs.get('in_data'): raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") if sudoable and getattr(self.runner, 'become', False) and self.runner.become_method not in self.become_methods_supported: raise errors.AnsibleError("Internal Error: this module does not support running commands via %s" % self.runner.become_method) become = getattr(self.runner, 'become', False) or getattr(self.runner, 'sudo', False) remote_cmd = [] if not become or not sudoable: if executable: remote_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: remote_cmd.append(cmd) else: if hasattr(self.runner, 'become_exe'): becomecmd, prompt, success_key = utils.make_become_cmd(cmd, become_user, executable, self.runner.become_method, '', self.runner.become_exe) elif hasattr(self.runner, 'sudo_exe'): becomecmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, become_user, executable, cmd) else: becomecmd, prompt, success_key = utils.make_sudo_cmd(become_user, executable, cmd) remote_cmd.append(becomecmd) remote_cmd = ' '.join(remote_cmd) vvv("execnet exec_command %r" % remote_cmd) rc, stdout, stderr = self.rpc.exec_command(remote_cmd) return (rc, '', stdout, stderr)
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' if self.runner.sudo or sudoable and sudo_user: cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnisbleError("Failed to send command to %s" % self.host) response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) return (response.get('rc', None), '', response.get('stdout', ''), response.get('stderr', ''))
def _su_sudo_cmd(self, cmd): if self.runner.su and self.runner.su_user: return utils.make_su_cmd(self.runner.su_user, '/bin/sh', cmd) elif self.runner.sudo: return utils.make_sudo_cmd(self.runner.sudo_user, '/bin/sh', cmd) else: return cmd, None, None
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the local host ''' if not self.runner.sudo or not sudoable: if executable: local_cmd = [executable, '-c', cmd] else: local_cmd = cmd else: local_cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC %s" % (local_cmd), host=self.host) p = subprocess.Popen(local_cmd, shell=isinstance(local_cmd, basestring), cwd=self.runner.basedir, executable=executable or None, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if self.runner.sudo and sudoable and self.runner.sudo_pass: 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 = '' while not sudo_output.endswith(prompt): rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout, p.stderr], self.runner.timeout) if p.stdout in rfd: chunk = p.stdout.read() elif p.stderr in rfd: chunk = p.stderr.read() else: stdout, stderr = p.communicate() raise errors.AnsibleError( 'timeout waiting for sudo password prompt:\n' + sudo_output) if not chunk: stdout, stderr = p.communicate() raise errors.AnsibleError( 'sudo output closed while waiting for password prompt:\n' + sudo_output) sudo_output += chunk p.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) stdout, stderr = p.communicate() return (p.returncode, '', stdout, stderr)
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' if in_data: raise errors.AnsibleError( "Internal Error: this module does not support optimized module pipelining" ) if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.sudo and sudoable and sudo_user: cmd, prompt, success_key = utils.make_sudo_cmd( sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise errors.AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise errors.AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc', None), '', response.get('stdout', ''), response.get('stderr', ''))
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): ''' run a command on the local host ''' # su requires to be run from a terminal, and therefore isn't supported here (yet?) if su or su_user: raise errors.AnsibleError("Internal Error: this module does not support running commands via su") if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") if not self.runner.sudo or not sudoable: if executable: local_cmd = executable.split() + ['-c', cmd] else: local_cmd = cmd else: local_cmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) executable = executable.split()[0] if executable else None vvv("EXEC %s" % (local_cmd), host=self.host) p = subprocess.Popen(local_cmd, shell=isinstance(local_cmd, basestring), cwd=self.runner.basedir, executable=executable, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if self.runner.sudo and sudoable and self.runner.sudo_pass: 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 = '' while not sudo_output.endswith(prompt) and success_key not in sudo_output: rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout, p.stderr], self.runner.timeout) if p.stdout in rfd: chunk = p.stdout.read() elif p.stderr in rfd: chunk = p.stderr.read() else: stdout, stderr = p.communicate() raise errors.AnsibleError('timeout waiting for sudo password prompt:\n' + sudo_output) if not chunk: stdout, stderr = p.communicate() raise errors.AnsibleError('sudo output closed while waiting for password prompt:\n' + sudo_output) sudo_output += chunk if success_key not in sudo_output: p.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) stdout, stderr = p.communicate() return (p.returncode, '', stdout, stderr)
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): ''' run a command on the local host ''' # su requires to be run from a terminal, and therefore isn't supported here (yet?) if su or su_user: raise errors.AnsibleError("Internal Error: this module does not support running commands via su") if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") if not self.runner.sudo or not sudoable: if executable: local_cmd = [executable, '-c', cmd] else: local_cmd = cmd else: local_cmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC %s" % (local_cmd), host=self.host) p = subprocess.Popen(local_cmd, shell=isinstance(local_cmd, basestring), cwd=self.runner.basedir, executable=executable or None, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if self.runner.sudo and sudoable and self.runner.sudo_pass: 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 = '' while not sudo_output.endswith(prompt) and success_key not in sudo_output: rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout, p.stderr], self.runner.timeout) if p.stdout in rfd: chunk = p.stdout.read() elif p.stderr in rfd: chunk = p.stderr.read() else: stdout, stderr = p.communicate() raise errors.AnsibleError('timeout waiting for sudo password prompt:\n' + sudo_output) if not chunk: stdout, stderr = p.communicate() raise errors.AnsibleError('sudo output closed while waiting for password prompt:\n' + sudo_output) sudo_output += chunk if success_key not in sudo_output: p.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) stdout, stderr = p.communicate() return (p.returncode, '', stdout, stderr)
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): ''' run a command on the remote host ''' if su or su_user: raise AnsibleError("Internal Error: this module does not support running commands via su") if in_data: raise AnsibleError("Internal Error: this module does not support optimized module pipelining") if executable == "": executable = constants.DEFAULT_EXECUTABLE if self.runner.sudo and sudoable and sudo_user: cmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC COMMAND %s" % cmd) data = dict( mode='command', cmd=cmd, tmp_path=tmp_path, executable=executable, ) data = utils.jsonify(data) data = utils.encrypt(self.key, data) if self.send_data(data): raise AnsibleError("Failed to send command to %s" % self.host) while True: # we loop here while waiting for the response, because a # long running command may cause us to receive keepalive packets # ({"pong":"true"}) rather than the response we want. response = self.recv_data() if not response: raise AnsibleError("Failed to get a response from %s" % self.host) response = utils.decrypt(self.key, response) response = utils.parse_json(response) if "pong" in response: # it's a keepalive, go back to waiting vvvv("%s: received a keepalive packet" % self.host) continue else: vvvv("%s: received the response" % self.host) break return (response.get('rc',None), '', response.get('stdout',''), response.get('stderr',''))
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the local host ''' if not self.runner.sudo or not sudoable: if executable: local_cmd = [executable, '-c', cmd] else: local_cmd = cmd else: local_cmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC %s" % (local_cmd), host=self.host) p = subprocess.Popen(local_cmd, shell=isinstance(local_cmd, basestring), cwd=self.runner.basedir, executable=executable or None, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if self.runner.sudo and sudoable and self.runner.sudo_pass: 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 = '' while not sudo_output.endswith(prompt): rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout, p.stderr], self.runner.timeout) if p.stdout in rfd: chunk = p.stdout.read() elif p.stderr in rfd: chunk = p.stderr.read() else: stdout, stderr = p.communicate() raise errors.AnsibleError('timeout waiting for sudo password prompt:\n' + sudo_output) if not chunk: stdout, stderr = p.communicate() raise errors.AnsibleError('sudo output closed while waiting for password prompt:\n' + sudo_output) sudo_output += chunk p.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) stdout, stderr = p.communicate() return (p.returncode, '', stdout, stderr)
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): if su or su_user: raise errors.AnsibleError("Internal Error: this module does not support running commands via su") if in_data: raise errors.AnsibleError("Internal Error: this module does not support optimized module pipelining") remote_cmd = [] if not self.runner.sudo or not sudoable: if executable: remote_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: remote_cmd.append(cmd) else: sudocmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) remote_cmd.append(sudocmd) remote_cmd = ' '.join(remote_cmd) vvv("execnet exec_command %r" % remote_cmd) rc, stdout, stderr = self.rpc.exec_command(remote_cmd) return (rc, '', stdout, stderr)
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su_user=None, su=False): ''' run a command on the remote host ''' 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"] else: ssh_cmd += ["-q"] ssh_cmd += self.common_args if self.ipv6: ssh_cmd += ['-6'] ssh_cmd += [self.host] if su and su_user: sudocmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd) ssh_cmd.append(sudocmd) elif not self.runner.sudo or not sudoable: prompt = None if executable: ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ' '.join(ssh_cmd), host=self.host) not_in_host_file = self.not_in_host_file(self.host) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX) fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX) # create process (p, stdin) = self._run(ssh_cmd, in_data) self._send_password() no_prompt_out = '' no_prompt_err = '' if (self.runner.sudo and sudoable and self.runner.sudo_pass) or \ (self.runner.su and su and self.runner.su_pass): (no_prompt_out, no_prompt_err) = self.send_su_sudo_password(p, stdin, success_key, sudoable, prompt) com = self.CommunicateCallbacks(self.runner, in_data, su=su, sudoable=sudoable, prompt=prompt) returncode = self._communicate(p, stdin, callbacks=(com.stdin_cb, com.stdout_cb, com.stderr_cb)) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN) fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN) controlpersisterror = 'Bad configuration option: ControlPersist' in com.stderr or \ 'unknown configuration option: ControlPersist' in com.stderr if C.HOST_KEY_CHECKING: if ssh_cmd[0] == "sshpass" and p.returncode == 6: raise errors.AnsibleError('Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host\'s fingerprint to your known_hosts file to manage this host.') if p.returncode != 0 and controlpersisterror: raise errors.AnsibleError('using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ssh_args in [ssh_connection] section of the config file) before running again') if p.returncode == 255 and (in_data or self.runner.module_name == 'raw'): raise errors.AnsibleError('SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh') return (p.returncode, '', no_prompt_out + com.stdout, no_prompt_err + com.stderr)
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' ssh_cmd = self._password_cmd() ssh_cmd += ["ssh", "-tt", "-q"] + self.common_args if self.ipv6: ssh_cmd += ['-6'] ssh_cmd += [self.host] if not self.runner.sudo or not sudoable: if executable: ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) dounlocking = False if C.HOST_KEY_CHECKING: if self.not_in_host_file(self.host): dounlocking = True # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX) fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX) try: # Make sure stdin is a proper (pseudo) pty to avoid: tcgetattr errors import pty master, slave = pty.openpty() p = subprocess.Popen(ssh_cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = os.fdopen(master, 'w', 0) except: 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: fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) sudo_output = '' while not sudo_output.endswith(prompt): rfd, wfd, efd = select.select([p.stdout], [], [p.stdout], self.runner.timeout) 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: stdout = p.communicate() raise errors.AnsibleError( 'ssh connection error waiting for sudo password prompt' ) stdin.write(self.runner.sudo_pass + '\n') fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) # We can't use p.communicate here because the ControlMaster may have stdout open as well stdout = '' stderr = '' while True: rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout, p.stderr], 1) # fail early if the sudo password is wrong if self.runner.sudo and sudoable and self.runner.sudo_pass: incorrect_password = gettext.dgettext("sudo", "Sorry, try again.") if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)): raise errors.AnsibleError('Incorrect sudo password') if p.stdout in rfd: dat = os.read(p.stdout.fileno(), 9000) stdout += dat if dat == '': p.wait() break elif p.stderr in rfd: dat = os.read(p.stderr.fileno(), 9000) stderr += dat if dat == '': p.wait() break elif p.poll() is not None: break stdin.close( ) # close stdin after we read from stdout (see also issue #848) if dounlocking: fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN) fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN) if p.returncode != 0 and stderr.find( 'Bad configuration option: ControlPersist') != -1: raise errors.AnsibleError( 'using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ansible_ssh_args in the config file) before running again' ) return (p.returncode, '', stdout, stderr)
if not self.runner.sudo or not sudoable: if executable: quoted_command = executable + ' -c ' + pipes.quote(cmd) else: quoted_command = cmd vvv("EXEC %s" % quoted_command, host=self.host) chan.exec_command(quoted_command) else: # sudo usually requires a PTY (cf. requiretty option), therefore # we give it one, and we try to initialise from the calling # environment chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) shcmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC %s" % shcmd, host=self.host) sudo_output = '' try: chan.exec_command(shcmd) if self.runner.sudo_pass: while not sudo_output.endswith(prompt): chunk = chan.recv(bufsize) if not chunk: if 'unknown user' in sudo_output: raise errors.AnsibleError( 'user %s does not exist' % sudo_user) else: raise errors.AnsibleError( 'ssh connection ' + 'closed waiting for password prompt')
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable="/bin/sh", in_data=None): """ run a command on the remote host """ ssh_cmd = self._password_cmd() ssh_cmd += ["ssh", "-C"] if not in_data: ssh_cmd += ["-tt"] if utils.VERBOSITY > 3: ssh_cmd += ["-vvv"] else: ssh_cmd += ["-q"] ssh_cmd += self.common_args if self.ipv6: ssh_cmd += ["-6"] ssh_cmd += [self.host] if not self.runner.sudo or not sudoable: if executable: ssh_cmd.append(executable + " -c " + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) not_in_host_file = self.not_in_host_file(self.host) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX) fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX) # create process if in_data: # do not use pseudo-pty p = subprocess.Popen(ssh_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = p.stdin else: # try to use upseudo-pty try: # Make sure stdin is a proper (pseudo) pty to avoid: tcgetattr errors master, slave = pty.openpty() p = subprocess.Popen(ssh_cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = os.fdopen(master, "w", 0) os.close(slave) except: 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: fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, 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") while not sudo_output.endswith(prompt) and success_key not in sudo_output: rfd, wfd, efd = select.select([p.stdout], [], [p.stdout], self.runner.timeout) 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: 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) # We can't use p.communicate here because the ControlMaster may have stdout open as well stdout = "" stderr = "" rpipes = [p.stdout, p.stderr] if in_data: try: stdin.write(in_data) stdin.close() except: raise errors.AnsibleError( "SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh" ) while True: rfd, wfd, efd = select.select(rpipes, [], rpipes, 1) # fail early if the sudo password is wrong if self.runner.sudo and sudoable and self.runner.sudo_pass: incorrect_password = gettext.dgettext("sudo", "Sorry, try again.") if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)): raise errors.AnsibleError("Incorrect sudo password") if p.stdout in rfd: dat = os.read(p.stdout.fileno(), 9000) stdout += dat if dat == "": rpipes.remove(p.stdout) if p.stderr in rfd: dat = os.read(p.stderr.fileno(), 9000) stderr += dat if dat == "": rpipes.remove(p.stderr) # only break out if we've emptied the pipes, or there is nothing to # read from and the process has finished. if (not rpipes or not rfd) and p.poll() is not None: break # Calling wait while there are still pipes to read can cause a lock elif not rpipes and p.poll() == None: p.wait() stdin.close() # close stdin after we read from stdout (see also issue #848) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN) fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN) controlpersisterror = ( stderr.find("Bad configuration option: ControlPersist") != -1 or stderr.find("unknown configuration option: ControlPersist") != -1 ) if p.returncode != 0 and controlpersisterror: raise errors.AnsibleError( 'using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ansible_ssh_args in the config file) before running again' ) if p.returncode == 255 and in_data: raise errors.AnsibleError( "SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh" ) return (p.returncode, "", stdout, stderr)
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su_user=None, su=False): ''' run a command on the remote host ''' 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"] else: ssh_cmd += ["-q"] ssh_cmd += self.common_args if self.ipv6: ssh_cmd += ['-6'] ssh_cmd += [self.host] if su and su_user: sudocmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd) ssh_cmd.append(sudocmd) elif not self.runner.sudo or not sudoable: if executable: ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) not_in_host_file = self.not_in_host_file(self.host) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX) fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX) # create process if in_data: # do not use pseudo-pty p = subprocess.Popen(ssh_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = p.stdin else: # try to use upseudo-pty try: # Make sure stdin is a proper (pseudo) pty to avoid: tcgetattr errors master, slave = pty.openpty() p = subprocess.Popen(ssh_cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = os.fdopen(master, 'w', 0) os.close(slave) except: 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) or \ (self.runner.su and su and self.runner.su_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 = '' sudo_errput = '' while not sudo_output.endswith(prompt) and success_key not in sudo_output: 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 or su 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 or su password prompt') sudo_output += chunk if not rfd: # timeout. wrap up process communication stdout = p.communicate() raise errors.AnsibleError('ssh connection error waiting for sudo or su password prompt') if success_key not in sudo_output: if sudoable: stdin.write(self.runner.sudo_pass + '\n') elif su: stdin.write(self.runner.su_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 = '' rpipes = [p.stdout, p.stderr] if in_data: try: stdin.write(in_data) stdin.close() except: raise errors.AnsibleError('SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh') while True: rfd, wfd, efd = select.select(rpipes, [], rpipes, 1) # fail early if the sudo/su password is wrong if self.runner.sudo and sudoable and self.runner.sudo_pass: incorrect_password = gettext.dgettext( "sudo", "Sorry, try again.") if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)): raise errors.AnsibleError('Incorrect sudo password') if self.runner.su and su and self.runner.sudo_pass: incorrect_password = gettext.dgettext( "su", "Sorry") if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)): raise errors.AnsibleError('Incorrect su password') if p.stdout in rfd: dat = os.read(p.stdout.fileno(), 9000) stdout += dat if dat == '': rpipes.remove(p.stdout) if p.stderr in rfd: dat = os.read(p.stderr.fileno(), 9000) stderr += dat if dat == '': rpipes.remove(p.stderr) # only break out if we've emptied the pipes, or there is nothing to # read from and the process has finished. if (not rpipes or not rfd) and p.poll() is not None: break # Calling wait while there are still pipes to read can cause a lock elif not rpipes and p.poll() == None: p.wait() # the process has finished and the pipes are empty, # if we loop and do the select it waits all the timeout break stdin.close() # close stdin after we read from stdout (see also issue #848) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN) fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN) controlpersisterror = 'Bad configuration option: ControlPersist' in stderr or \ 'unknown configuration option: ControlPersist' in stderr if C.HOST_KEY_CHECKING: if ssh_cmd[0] == "sshpass" and p.returncode == 6: raise errors.AnsibleError('Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host\'s fingerprint to your known_hosts file to manage this host.') if p.returncode != 0 and controlpersisterror: raise errors.AnsibleError('using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ansible_ssh_args in the config file) before running again') if p.returncode == 255 and in_data: raise errors.AnsibleError('SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh') return (p.returncode, '', stdout, stderr)
else: quoted_command = cmd vvv("EXEC %s" % quoted_command, host=self.host) chan.exec_command(quoted_command) else: # sudo usually requires a PTY (cf. requiretty option), therefore # we give it one by default (pty=True in ansble.cfg), and we try # to initialise from the calling environment if C.PARAMIKO_PTY: chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) if self.runner.sudo or sudoable: shcmd, prompt, success_key = utils.make_sudo_cmd( self.runner.sudo_exe, sudo_user, executable, cmd) elif self.runner.su or su: shcmd, prompt, success_key = utils.make_su_cmd( su_user, executable, cmd) vvv("EXEC %s" % shcmd, host=self.host) sudo_output = '' try: chan.exec_command(shcmd) if self.runner.sudo_pass or self.runner.su_pass: while True:
if not self.runner.sudo or not sudoable: if executable: quoted_command = executable + ' -c ' + pipes.quote(cmd) else: quoted_command = cmd vvv("EXEC %s" % quoted_command, host=self.host) chan.exec_command(quoted_command) else: # sudo usually requires a PTY (cf. requiretty option), therefore # we give it one, and we try to initialise from the calling # environment chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) shcmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC %s" % shcmd, host=self.host) sudo_output = '' try: chan.exec_command(shcmd) if self.runner.sudo_pass: while not sudo_output.endswith(prompt): chunk = chan.recv(bufsize) if not chunk: if 'unknown user' in sudo_output: raise errors.AnsibleError( 'user %s does not exist' % sudo_user) else: raise errors.AnsibleError('ssh connection ' + 'closed waiting for password prompt') sudo_output += chunk
def exec_command(self, cmd, tmp_path, sudo_user,sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' ssh_cmd = self._password_cmd() ssh_cmd += ["ssh", "-tt", "-q"] + self.common_args + [self.host] if not self.runner.sudo or not sudoable: if executable: ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) try: # Make sure stdin is a proper (pseudo) pty to avoid: tcgetattr errors import pty master, slave = pty.openpty() p = subprocess.Popen(ssh_cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = os.fdopen(master, 'w', 0) except: 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: fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) sudo_output = '' while not sudo_output.endswith(prompt): rfd, wfd, efd = select.select([p.stdout], [], [p.stdout], self.runner.timeout) 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: stdout = p.communicate() raise errors.AnsibleError('ssh connection error waiting for sudo password prompt') stdin.write(self.runner.sudo_pass + '\n') fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) # We can't use p.communicate here because the ControlMaster may have stdout open as well stdout = '' stderr = '' while True: rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout, p.stderr], 1) if p.stdout in rfd: dat = os.read(p.stdout.fileno(), 9000) stdout += dat if dat == '': p.wait() break elif p.stderr in rfd: dat = os.read(p.stderr.fileno(), 9000) stderr += dat if dat == '': p.wait() break elif p.poll() is not None: break stdin.close() # close stdin after we read from stdout (see also issue #848) if p.returncode != 0 and stderr.find('Bad configuration option: ControlPersist') != -1: raise errors.AnsibleError('using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ansible_ssh_args in the config file) before running again') return (p.returncode, '', stdout, stderr)
if not (self.runner.sudo and sudoable) and not (self.runner.su and su) or in_data: if executable: quoted_command = executable + ' -c ' + pipes.quote(cmd) else: quoted_command = cmd vvv("EXEC ALT no-tty %s" % quoted_command, host=self.host) chan.exec_command(quoted_command) else: # sudo usually requires a PTY (cf. requiretty option), therefore # we give it one by default (pty=True in ansble.cfg), and we try # to initialise from the calling environment if C.PARAMIKO_PTY: chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) shcmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) vvv("EXEC %s" % shcmd, host=self.host) sudo_output = '' try: chan.exec_command(shcmd) if self.runner.sudo_pass or self.runner.su_pass: while not sudo_output.endswith(prompt) and success_key not in sudo_output: chunk = chan.recv(bufsize) if not chunk: if 'unknown user' in sudo_output: raise errors.AnsibleError( 'user %s does not exist' % sudo_user) else: raise errors.AnsibleError('ssh connection ' + 'closed waiting for password prompt') sudo_output += chunk
def exec_command(self, cmd, tmp_path, sudo_user,sudoable=False, executable='/bin/sh', in_data=None): ''' run a command on the remote host ''' 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"] else: ssh_cmd += ["-q"] ssh_cmd += self.common_args if self.ipv6: ssh_cmd += ['-6'] ssh_cmd += [self.host] if not self.runner.sudo or not sudoable: if executable: ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) not_in_host_file = self.not_in_host_file(self.host) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX) fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX) # create process if in_data: # do not use pseudo-pty p = subprocess.Popen(ssh_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = p.stdin else: # try to use upseudo-pty try: # Make sure stdin is a proper (pseudo) pty to avoid: tcgetattr errors master, slave = pty.openpty() p = subprocess.Popen(ssh_cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = os.fdopen(master, 'w', 0) os.close(slave) except: 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 = '' sudo_errput = '' while not sudo_output.endswith(prompt) and success_key not in sudo_output: 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 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 = '' rpipes = [p.stdout, p.stderr] if in_data: try: stdin.write(in_data) stdin.close() except: raise errors.AnsibleError('SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh') while True: rfd, wfd, efd = select.select(rpipes, [], rpipes, 1) # fail early if the sudo password is wrong if self.runner.sudo and sudoable and self.runner.sudo_pass: incorrect_password = gettext.dgettext( "sudo", "Sorry, try again.") if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)): raise errors.AnsibleError('Incorrect sudo password') if p.stdout in rfd: dat = os.read(p.stdout.fileno(), 9000) stdout += dat if dat == '': rpipes.remove(p.stdout) if p.stderr in rfd: dat = os.read(p.stderr.fileno(), 9000) stderr += dat if dat == '': rpipes.remove(p.stderr) # only break out if we've emptied the pipes, or there is nothing to # read from and the process has finished. if (not rpipes or not rfd) and p.poll() is not None: break # Calling wait while there are still pipes to read can cause a lock elif not rpipes and p.poll() == None: p.wait() stdin.close() # close stdin after we read from stdout (see also issue #848) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN) fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN) controlpersisterror = stderr.find('Bad configuration option: ControlPersist') != -1 or stderr.find('unknown configuration option: ControlPersist') != -1 if p.returncode != 0 and controlpersisterror: raise errors.AnsibleError('using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ansible_ssh_args in the config file) before running again') if p.returncode == 255 and in_data: raise errors.AnsibleError('SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh') return (p.returncode, '', stdout, stderr)
else: quoted_command = cmd vvv("EXEC %s" % quoted_command, host=self.host) chan.exec_command(quoted_command) else: # sudo usually requires a PTY (cf. requiretty option), therefore # we give it one by default (pty=True in ansble.cfg), and we try # to initialise from the calling environment if C.PARAMIKO_PTY: chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) if self.runner.sudo or sudoable: shcmd, prompt, success_key = utils.make_sudo_cmd(sudo_user, executable, cmd) elif self.runner.su or su: shcmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd) prompt_re = re.compile(prompt) vvv("EXEC %s" % shcmd, host=self.host) sudo_output = '' try: chan.exec_command(shcmd) if self.runner.sudo_pass or self.runner.su_pass: while True:
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=False, su_user=None): ''' run a command on the remote host ''' if in_data: raise errors.AnsibleError( "Internal Error: this module does not support optimized module pipelining" ) ssh_cmd = self._password_cmd() ssh_cmd += ["ssh", "-tt"] if utils.VERBOSITY > 3: ssh_cmd += ["-vvv"] else: ssh_cmd += ["-q"] ssh_cmd += self.common_args if self.ipv6: ssh_cmd += ['-6'] ssh_cmd += [self.host] if su and su_user: sudocmd, prompt, success_key = utils.make_su_cmd( su_user, executable, cmd) ssh_cmd.append(sudocmd) elif not self.runner.sudo or not sudoable: if executable: ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt, success_key = utils.make_sudo_cmd( sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) not_in_host_file = self.not_in_host_file(self.host) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX) fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX) try: # Make sure stdin is a proper (pseudo) pty to avoid: tcgetattr errors master, slave = pty.openpty() p = subprocess.Popen(ssh_cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = os.fdopen(master, 'w', 0) os.close(slave) except: 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) or \ (self.runner.su and su and self.runner.su_pass): fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) sudo_output = '' while not sudo_output.endswith( prompt) and success_key not in sudo_output: rfd, wfd, efd = select.select([p.stdout], [], [p.stdout], self.runner.timeout) if p.stdout in rfd: chunk = p.stdout.read() if not chunk: raise errors.AnsibleError( 'ssh connection closed waiting for sudo or su password prompt' ) sudo_output += chunk else: stdout = p.communicate() raise errors.AnsibleError( 'ssh connection error waiting for sudo or su password prompt' ) if success_key not in sudo_output: if sudoable: stdin.write(self.runner.sudo_pass + '\n') elif su: stdin.write(self.runner.su_pass + '\n') fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) # We can't use p.communicate here because the ControlMaster may have stdout open as well stdout = '' stderr = '' rpipes = [p.stdout, p.stderr] while True: rfd, wfd, efd = select.select(rpipes, [], rpipes, 1) # fail early if the sudo/su password is wrong if self.runner.sudo and sudoable and self.runner.sudo_pass: incorrect_password = gettext.dgettext("sudo", "Sorry, try again.") if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)): raise errors.AnsibleError('Incorrect sudo password') if self.runner.su and su and self.runner.su_pass: incorrect_password = gettext.dgettext( "su", "su: Authentication failure") if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)): raise errors.AnsibleError('Incorrect su password') if p.stdout in rfd: dat = os.read(p.stdout.fileno(), 9000) stdout += dat if dat == '': rpipes.remove(p.stdout) if p.stderr in rfd: dat = os.read(p.stderr.fileno(), 9000) stderr += dat if dat == '': rpipes.remove(p.stderr) # only break out if we've emptied the pipes, or there is nothing to # read from and the process has finished. if (not rpipes or not rfd) and p.poll() is not None: break # Calling wait while there are still pipes to read can cause a lock elif not rpipes and p.poll() == None: p.wait() # the process has finished and the pipes are empty, # if we loop and do the select it waits all the timeout break stdin.close( ) # close stdin after we read from stdout (see also issue #848) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN) fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN) if C.HOST_KEY_CHECKING: if ssh_cmd[0] == "sshpass" and p.returncode == 6: raise errors.AnsibleError( 'Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host\'s fingerprint to your known_hosts file to manage this host.' ) controlpersisterror = stderr.find( 'Bad configuration option: ControlPersist') != -1 or stderr.find( 'unknown configuration option: ControlPersist') != -1 if p.returncode != 0 and controlpersisterror: raise errors.AnsibleError( 'using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ansible_ssh_args in the config file) before running again' ) return (p.returncode, '', stdout, stderr)
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su=None, su_user=None): ''' run a command on the remote host ''' if in_data: raise errors.AnsibleError( "Internal Error: this module does not support optimized module pipelining" ) bufsize = 4096 try: self.ssh.get_transport().set_keepalive(5) chan = self.ssh.get_transport().open_session() except Exception as e: msg = "Failed to open session" if len(str(e)) > 0: msg += ": %s" % str(e) raise errors.AnsibleConnectionFailed(msg) no_prompt_out = '' no_prompt_err = '' if not (self.runner.sudo and sudoable) and not (self.runner.su and su): if executable: quoted_command = executable + ' -c ' + pipes.quote(cmd) else: quoted_command = cmd vvv("EXEC %s" % quoted_command, host=self.host) chan.exec_command(quoted_command) else: # sudo usually requires a PTY (cf. requiretty option), therefore # we give it one by default (pty=True in ansble.cfg), and we try # to initialise from the calling environment if C.PARAMIKO_PTY: chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) if self.runner.sudo or sudoable: shcmd, prompt, success_key = utils.make_sudo_cmd( self.runner.sudo_exe, sudo_user, executable, cmd) elif self.runner.su or su: shcmd, prompt, success_key = utils.make_su_cmd( su_user, executable, cmd) vvv("EXEC %s" % shcmd, host=self.host) sudo_output = '' try: chan.exec_command(shcmd) if self.runner.sudo_pass or self.runner.su_pass: while True: if success_key in sudo_output or \ (self.runner.sudo_pass and sudo_output.endswith(prompt)) or \ (self.runner.su_pass and utils.su_prompts.check_su_prompt(sudo_output)): break chunk = chan.recv(bufsize) if not chunk: if 'unknown user' in sudo_output: raise errors.AnsibleError( 'user %s does not exist' % sudo_user) else: raise errors.AnsibleError( 'ssh connection ' + 'closed waiting for password prompt') sudo_output += chunk if success_key not in sudo_output: if sudoable: chan.sendall(self.runner.sudo_pass + '\n') elif su: chan.sendall(self.runner.su_pass + '\n') else: no_prompt_out += sudo_output no_prompt_err += sudo_output except socket.timeout: raise errors.AnsibleError('ssh timed out waiting for sudo.\n' + sudo_output) stdout = ''.join(chan.makefile('rb', bufsize)) stderr = ''.join(chan.makefile_stderr('rb', bufsize)) return (chan.recv_exit_status(), '', no_prompt_out + stdout, no_prompt_out + stderr)
def exec_command(self, cmd, tmp_path, sudo_user, sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' ssh_cmd = self._password_cmd() ssh_cmd += ["ssh", "-tt", "-q"] + self.common_args + [self.host] if not self.runner.sudo or not sudoable: if executable: ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) try: # Make sure stdin is a proper (pseudo) pty to avoid: tcgetattr errors import pty master, slave = pty.openpty() p = subprocess.Popen(ssh_cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = os.fdopen(master, 'w', 0) except: 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: fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) sudo_output = '' while not sudo_output.endswith(prompt): rfd, wfd, efd = select.select([p.stdout], [], [p.stdout], self.runner.timeout) 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: stdout = p.communicate() raise errors.AnsibleError( 'ssh connection error waiting for sudo password prompt' ) stdin.write(self.runner.sudo_pass + '\n') fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) # We can't use p.communicate here because the ControlMaster may have stdout open as well stdout = '' stderr = '' while True: rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout, p.stderr], 1) if p.stdout in rfd: dat = os.read(p.stdout.fileno(), 9000) stdout += dat if dat == '': p.wait() break elif p.stderr in rfd: dat = os.read(p.stderr.fileno(), 9000) stderr += dat if dat == '': p.wait() break elif p.poll() is not None: break stdin.close( ) # close stdin after we read from stdout (see also issue #848) if p.returncode != 0 and stderr.find( 'Bad configuration option: ControlPersist') != -1: raise errors.AnsibleError( 'using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ansible_ssh_args in the config file) before running again' ) return (p.returncode, '', stdout, stderr)
else: quoted_command = cmd vvv("EXEC %s" % quoted_command, host=self.host) chan.exec_command(quoted_command) else: # sudo usually requires a PTY (cf. requiretty option), therefore # we give it one by default (pty=True in ansble.cfg), and we try # to initialise from the calling environment if C.PARAMIKO_PTY: chan.get_pty(term=os.getenv('TERM', 'vt100'), width=int(os.getenv('COLUMNS', 0)), height=int(os.getenv('LINES', 0))) if self.runner.sudo or sudoable: shcmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd) elif self.runner.su or su: shcmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd) vvv("EXEC %s" % shcmd, host=self.host) sudo_output = '' try: chan.exec_command(shcmd) if self.runner.sudo_pass or self.runner.su_pass: while True: if success_key in sudo_output or \
def exec_command(self, cmd, tmp_path, sudo_user=None, sudoable=False, executable='/bin/sh', in_data=None, su_user=None, su=False): ''' run a command on the remote host ''' 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"] else: ssh_cmd += ["-q"] ssh_cmd += self.common_args if self.ipv6: ssh_cmd += ['-6'] #ssh_cmd += [self.host] ssh_cmd += [self.sliver_name] if su and su_user: sudocmd, prompt, success_key = utils.make_su_cmd( su_user, executable, cmd) prompt_re = re.compile(prompt) ssh_cmd.append(sudocmd) elif not self.runner.sudo or not sudoable: prompt = None if executable: ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt, success_key = utils.make_sudo_cmd( sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) not_in_host_file = self.not_in_host_file(self.host) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX) fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX) # create process (p, stdin) = self._run(ssh_cmd, in_data) self._send_password() if (self.runner.sudo and sudoable and self.runner.sudo_pass) or \ (self.runner.su and su and self.runner.su_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 = '' sudo_errput = '' while True: if success_key in sudo_output or \ (self.runner.sudo_pass and sudo_output.endswith(prompt)) or \ (self.runner.su_pass and prompt_re.match(sudo_output)): break 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 or su 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 or su password prompt' ) sudo_output += chunk if not rfd: # timeout. wrap up process communication stdout = p.communicate() raise errors.AnsibleError( 'ssh connection error waiting for sudo or su password prompt' ) if success_key not in sudo_output: if sudoable: stdin.write(self.runner.sudo_pass + '\n') elif su: stdin.write(self.runner.su_pass + '\n') (returncode, stdout, stderr) = self._communicate(p, stdin, in_data, su=su, sudoable=sudoable, prompt=prompt) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN) fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN) controlpersisterror = 'Bad configuration option: ControlPersist' in stderr or \ 'unknown configuration option: ControlPersist' in stderr if C.HOST_KEY_CHECKING: if ssh_cmd[0] == "sshpass" and p.returncode == 6: raise errors.AnsibleError( 'Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host\'s fingerprint to your known_hosts file to manage this host.' ) if p.returncode != 0 and controlpersisterror: raise errors.AnsibleError( 'using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ssh_args in [ssh_connection] section of the config file) before running again' ) if p.returncode == 255 and (in_data or self.runner.module_name == 'raw'): raise errors.AnsibleError( 'SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh' ) return (p.returncode, '', stdout, stderr)
def exec_command( self, cmd, tmp_path, sudo_user=None, sudoable=False, executable="/bin/sh", in_data=None, su_user=None, su=False ): """ run a command on the remote host """ 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"] else: ssh_cmd += ["-q"] ssh_cmd += self.common_args if self.ipv6: ssh_cmd += ["-6"] ssh_cmd += [self.host] if su and su_user: sudocmd, prompt, success_key = utils.make_su_cmd(su_user, executable, cmd) ssh_cmd.append(sudocmd) elif not self.runner.sudo or not sudoable: prompt = None if executable: ssh_cmd.append(executable + " -c " + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt, success_key = utils.make_sudo_cmd(self.runner.sudo_exe, sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % " ".join(ssh_cmd), host=self.host) not_in_host_file = self.not_in_host_file(self.host) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX) fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX) # create process (p, stdin) = self._run(ssh_cmd, in_data) self._send_password() no_prompt_out = "" no_prompt_err = "" if (self.runner.sudo and sudoable and self.runner.sudo_pass) or (self.runner.su and su and self.runner.su_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 = "" sudo_errput = "" while True: if ( success_key in sudo_output or (self.runner.sudo_pass and sudo_output.endswith(prompt)) or (self.runner.su_pass and utils.su_prompts.check_su_prompt(sudo_output)) ): break 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 or su 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 prompt and 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 or su password prompt") sudo_output += chunk if not rfd: # timeout. wrap up process communication stdout = p.communicate() raise errors.AnsibleError("ssh connection error waiting for sudo or su password prompt") if success_key not in sudo_output: if sudoable: stdin.write(self.runner.sudo_pass + "\n") elif su: stdin.write(self.runner.su_pass + "\n") else: no_prompt_out += sudo_output no_prompt_err += sudo_errput (returncode, stdout, stderr) = self._communicate(p, stdin, in_data, su=su, sudoable=sudoable, prompt=prompt) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN) fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN) controlpersisterror = ( "Bad configuration option: ControlPersist" in stderr or "unknown configuration option: ControlPersist" in stderr ) if C.HOST_KEY_CHECKING: if ssh_cmd[0] == "sshpass" and p.returncode == 6: raise errors.AnsibleError( "Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this. Please add this host's fingerprint to your known_hosts file to manage this host." ) if p.returncode != 0 and controlpersisterror: raise errors.AnsibleError( 'using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ssh_args in [ssh_connection] section of the config file) before running again' ) if p.returncode == 255 and (in_data or self.runner.module_name == "raw"): raise errors.AnsibleError( "SSH Error: data could not be sent to the remote host. Make sure this host can be reached over ssh" ) return (p.returncode, "", no_prompt_out + stdout, no_prompt_err + stderr)
def exec_command(self, cmd, tmp_path, sudo_user,sudoable=False, executable='/bin/sh'): ''' run a command on the remote host ''' ssh_cmd = self._password_cmd() ssh_cmd += ["ssh", "-tt"] if utils.VERBOSITY > 3: ssh_cmd += ["-vvv"] else: ssh_cmd += ["-q"] ssh_cmd += self.common_args if self.ipv6: ssh_cmd += ['-6'] ssh_cmd += [self.host] if not self.runner.sudo or not sudoable: if executable: ssh_cmd.append(executable + ' -c ' + pipes.quote(cmd)) else: ssh_cmd.append(cmd) else: sudocmd, prompt = utils.make_sudo_cmd(sudo_user, executable, cmd) ssh_cmd.append(sudocmd) vvv("EXEC %s" % ssh_cmd, host=self.host) not_in_host_file = self.not_in_host_file(self.host) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_EX) fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_EX) try: # Make sure stdin is a proper (pseudo) pty to avoid: tcgetattr errors master, slave = pty.openpty() p = subprocess.Popen(ssh_cmd, stdin=slave, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdin = os.fdopen(master, 'w', 0) os.close(slave) except: 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: fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) | os.O_NONBLOCK) sudo_output = '' while not sudo_output.endswith(prompt): rfd, wfd, efd = select.select([p.stdout], [], [p.stdout], self.runner.timeout) 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: stdout = p.communicate() raise errors.AnsibleError('ssh connection error waiting for sudo password prompt') stdin.write(self.runner.sudo_pass + '\n') fcntl.fcntl(p.stdout, fcntl.F_SETFL, fcntl.fcntl(p.stdout, fcntl.F_GETFL) & ~os.O_NONBLOCK) # We can't use p.communicate here because the ControlMaster may have stdout open as well stdout = '' stderr = '' while True: rfd, wfd, efd = select.select([p.stdout, p.stderr], [], [p.stdout, p.stderr], 1) # fail early if the sudo password is wrong if self.runner.sudo and sudoable and self.runner.sudo_pass: incorrect_password = gettext.dgettext( "sudo", "Sorry, try again.") if stdout.endswith("%s\r\n%s" % (incorrect_password, prompt)): raise errors.AnsibleError('Incorrect sudo password') if p.stdout in rfd: dat = os.read(p.stdout.fileno(), 9000) stdout += dat if dat == '': p.wait() break elif p.stderr in rfd: dat = os.read(p.stderr.fileno(), 9000) stderr += dat if dat == '': p.wait() break elif p.poll() is not None: break stdin.close() # close stdin after we read from stdout (see also issue #848) if C.HOST_KEY_CHECKING and not_in_host_file: # lock around the initial SSH connectivity so the user prompt about whether to add # the host to known hosts is not intermingled with multiprocess output. fcntl.lockf(self.runner.output_lockfile, fcntl.LOCK_UN) fcntl.lockf(self.runner.process_lockfile, fcntl.LOCK_UN) if p.returncode != 0 and stderr.find('Bad configuration option: ControlPersist') != -1: raise errors.AnsibleError('using -c ssh on certain older ssh versions may not support ControlPersist, set ANSIBLE_SSH_ARGS="" (or ansible_ssh_args in the config file) before running again') return (p.returncode, '', stdout, stderr)