Skip to content
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.pyc
*.egg-info
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#Parallel SSH
Parallel SSH
============

This is a fork of the wonderful project at http://code.google.com/p/parallel-ssh/

A goal of this fork is to keep up to date with the original repository. Ideally we would set up a trigger so updates from the main project will populate to this repo automatically.

Another goal is to submit patches to the original project so we can contribute back.

###Changes
* Adding `padb`. This is a parallel implementation of the Android Device Bridge command to allow for easier management of multiple Android devices via USB or adb over tcp.

Changes
=======

* Adding `padb`. This is a parallel implementation of the Android Device Bridge command to allow for easier management of multiple Android devices via USB or adb over tcp.
8 changes: 7 additions & 1 deletion bin/pssh
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ def option_parser():
help='read from standard input and send as input to ssh')
parser.add_option('-P', '--print', dest='print_out', action='store_true',
help='print output as we get it')
parser.add_option('--no-annotate-each-line',
dest='no_annotate_lines', action='store_true',
help="Don't prefix each line of the `-P`rinted output with the host info")
parser.add_option('--no-buffer-each-line',
dest='no_buffer_lines', action='store_true',
help="Don't use line-buffering i.e. allow breaking the lines")

return parser

Expand Down Expand Up @@ -114,5 +120,5 @@ if __name__ == "__main__":
sys.exit(1)
if opts.host_strings:
for s in opts.host_strings:
hosts.extend(psshutil.parse_host_string(s, default_user=opts.user))
hosts.extend(psshutil.parse_host_string(s, default_user=opts.user, all_addresses=opts.all_addresses))
do_pssh(hosts, cmdline, opts)
2 changes: 2 additions & 0 deletions psshlib/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ def common_parser():
parser.add_option('-H', '--host', dest='host_strings', action='append',
metavar='HOST_STRING',
help='additional host entries ("[user@]host[:port]")')
parser.add_option('-a', '--all-addresses', dest='all_addresses', action='store_true',
help='Unpack all addresses in the specified hostnames as hosts')
parser.add_option('-l', '--user', dest='user',
help='username (OPTIONAL)')
parser.add_option('-p', '--par', dest='par', type='int',
Expand Down
2 changes: 2 additions & 0 deletions psshlib/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from errno import EINTR
import os
import fcntl
import select
import signal
import sys
Expand Down Expand Up @@ -209,6 +210,7 @@ def __init__(self):

# Setup the wakeup file descriptor to avoid hanging on lost signals.
wakeup_readfd, wakeup_writefd = os.pipe()
fcntl.fcntl(wakeup_writefd, fcntl.F_SETFL, os.O_NONBLOCK)
self.register_read(wakeup_readfd, self.wakeup_handler)
# TODO: remove test when we stop supporting Python <2.5
if hasattr(signal, 'set_wakeup_fd'):
Expand Down
31 changes: 28 additions & 3 deletions psshlib/psshutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Copyright (c) 2003-2008, Brent N. Chun

import fcntl
import socket
import string
import sys

Expand Down Expand Up @@ -72,15 +73,22 @@ def parse_host_entry(line, default_user, default_port):
return host, port, user


def parse_host_string(host_string, default_user=None, default_port=None):
"""Parses a whitespace-delimited string of "[user@]host[:port]" entries.
def parse_host_string(host_string, default_user=None, default_port=None, all_addresses=False):
"""
Parses a whitespace-delimited string of "[user@]host[:port]" entries.

Returns a list of (host, port, user) triples.
"""
hosts = []
entries = host_string.split()
for entry in entries:
hosts.append(parse_host(entry, default_user, default_port))
if all_addresses:
hosts_full = []
for host, port, user in hosts:
for host_address in get_host_addresses(host, port=port):
hosts_full.append((host_address, port, user))
hosts = hosts_full
return hosts


Expand All @@ -99,8 +107,25 @@ def parse_host(host, default_user=None, default_port=None):
return (host, port, user)


def get_host_addresses(host, port=0, **kwargs):
results = socket.getaddrinfo(
host,
port,
# family=
socket.AF_UNSPEC,
# type=
socket.SOCK_STREAM,
# proto=
socket.IPPROTO_TCP,
# flags=
socket.AI_ADDRCONFIG,
)
return [item[4][0] for item in results]


def set_cloexec(filelike):
"""Sets the underlying filedescriptor to automatically close on exec.
"""
Sets the underlying filedescriptor to automatically close on exec.

If set_cloexec is called for all open files, then subprocess.Popen does
not require the close_fds option.
Expand Down
Loading