|
|
|
@ -98,6 +98,28 @@ class Connection(object):
|
|
|
|
|
|
|
|
|
|
return self
|
|
|
|
|
|
|
|
|
|
def _run(self, cmd, indata):
|
|
|
|
|
if indata:
|
|
|
|
|
# do not use pseudo-pty
|
|
|
|
|
p = subprocess.Popen(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(cmd, stdin=slave,
|
|
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
|
stdin = os.fdopen(master, 'w', 0)
|
|
|
|
|
os.close(slave)
|
|
|
|
|
except:
|
|
|
|
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
|
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
|
stdin = p.stdin
|
|
|
|
|
|
|
|
|
|
return (p, stdin)
|
|
|
|
|
|
|
|
|
|
def _password_cmd(self):
|
|
|
|
|
if self.password:
|
|
|
|
|
try:
|
|
|
|
@ -116,6 +138,58 @@ class Connection(object):
|
|
|
|
|
os.write(self.wfd, "%s\n" % self.password)
|
|
|
|
|
os.close(self.wfd)
|
|
|
|
|
|
|
|
|
|
def _communicate(self, p, stdin, indata):
|
|
|
|
|
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 indata:
|
|
|
|
|
try:
|
|
|
|
|
stdin.write(indata)
|
|
|
|
|
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)
|
|
|
|
|
return (p.returncode, stdout, stderr)
|
|
|
|
|
|
|
|
|
|
def not_in_host_file(self, host):
|
|
|
|
|
if 'USER' in os.environ:
|
|
|
|
|
user_host_file = os.path.expandvars("~${USER}/.ssh/known_hosts")
|
|
|
|
@ -203,24 +277,7 @@ class Connection(object):
|
|
|
|
|
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
|
|
|
|
|
(p, stdin) = self._run(ssh_cmd, in_data)
|
|
|
|
|
|
|
|
|
|
self._send_password()
|
|
|
|
|
|
|
|
|
@ -269,56 +326,9 @@ class Connection(object):
|
|
|
|
|
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')
|
|
|
|
|
(returncode, stdout, stderr) = self._communicate(p, stdin, in_data)
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
@ -357,12 +367,13 @@ class Connection(object):
|
|
|
|
|
cmd += ["sftp"] + self.common_args + [host]
|
|
|
|
|
indata = "put %s %s\n" % (pipes.quote(in_path), pipes.quote(out_path))
|
|
|
|
|
|
|
|
|
|
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
|
|
|
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
|
(p, stdin) = self._run(cmd, indata)
|
|
|
|
|
|
|
|
|
|
self._send_password()
|
|
|
|
|
stdout, stderr = p.communicate(indata)
|
|
|
|
|
|
|
|
|
|
if p.returncode != 0:
|
|
|
|
|
(returncode, stdout, stderr) = self._communicate(p, stdin, indata)
|
|
|
|
|
|
|
|
|
|
if returncode != 0:
|
|
|
|
|
raise errors.AnsibleError("failed to transfer file to %s:\n%s\n%s" % (out_path, stdout, stderr))
|
|
|
|
|
|
|
|
|
|
def fetch_file(self, in_path, out_path):
|
|
|
|
|