Skip to content

Instantly share code, notes, and snippets.

@virtuald
Created December 20, 2014 19:49
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save virtuald/54c8657a9ea834fb7fdd to your computer and use it in GitHub Desktop.
Save virtuald/54c8657a9ea834fb7fdd to your computer and use it in GitHub Desktop.
Simple python wrapper to give SSH a password for automation purposes (with output capture)
#!/usr/bin/env python3
import os
import sys
_b = sys.version_info[0] < 3 and (lambda x:x) or (lambda x:x.encode('utf-8'))
def ssh_exec_pass(password, args, capture_output=False):
'''
Wrapper around openssh that allows you to send a password to
ssh/sftp/scp et al similar to sshpass.
Not super robust, but works well enough for most purposes. Typical
usage might be::
ssh_exec_pass('p@ssw0rd', ['ssh', 'root@1.2.3.4', 'echo hi!'])
:param args: A list of args. arg[0] must be the command to run.
:param capture_output: If True, suppresses output to stdout and stores
it in a buffer that is returned
:returns: (retval, output)
*nix only, tested on linux and OSX. Python 2.7 and 3.3+ compatible.
'''
import pty, select
# create pipe for stdout
stdout_fd, w1_fd = os.pipe()
stderr_fd, w2_fd = os.pipe()
pid, pty_fd = pty.fork()
if not pid:
# in child
os.close(stdout_fd)
os.close(stderr_fd)
os.dup2(w1_fd, 1) # replace stdout on child
os.dup2(w2_fd, 2) # replace stderr on child
os.close(w1_fd)
os.close(w2_fd)
os.execvp(args[0], args)
os.close(w1_fd)
os.close(w2_fd)
output = bytearray()
rd_fds = [stdout_fd, stderr_fd, pty_fd]
def _read(fd):
if fd not in rd_ready:
return
try:
data = os.read(fd, 1024)
except (OSError, IOError):
data = None
if not data:
rd_fds.remove(fd) # EOF
return data
# Read data, etc
try:
while rd_fds:
rd_ready, _, _ = select.select(rd_fds, [], [], 0.04)
if rd_ready:
# Deal with prompts from pty
data = _read(pty_fd)
if data is not None:
if b'assword:' in data:
os.write(pty_fd, _b(password + '\n'))
elif b're you sure you want to continue connecting' in data:
os.write(pty_fd, b'yes\n')
# Deal with stdout
data = _read(stdout_fd)
if data is not None:
if capture_output:
output.extend(data)
else:
sys.stdout.write(data.decode('utf-8', 'ignore'))
data = _read(stderr_fd)
if data is not None:
sys.stderr.write(data.decode('utf-8', 'ignore'))
finally:
os.close(pty_fd)
pid, retval = os.waitpid(pid, 0)
return retval, output
if __name__ == '__main__':
retval, _ = ssh_exec_pass(sys.argv[1], sys.argv[2:], False)
exit(retval)
@meirarmon
Copy link

There is a file descriptor leak, you should have os.close(stderr) and os.close(stdout) in the finally clause.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment