ssh -p 3334 shmem@gateway -L1025:host.example.com:22
####
ssh -p 1025 admin@localhost -L1026:192.168.123.2:975 -R993:localhost:1993
##
##
ssh -p 1026 wrxd15@localhost -L1027:192.168.254.2:22
##
##
ssc shmem@gateway:3334 admin@host.example.com wrxd15@192.168.123.2:975,-R993:localhost:1993 root@192.168.254.2
##
##
#!/usr/bin/perl -w
# $Id: ssc,v 0.4 2006/09/28 20:31:37 shmem Exp $
use strict;
use Getopt::Std;
my (%o,@pids);
getopts('p:',\%o);
$o{'p'} ||= 1025; # default port range start
die "ssh chain - usage: ssc [-p] user\@host1[:sshport][,arg,arg,...] \\\n"
." user\@host1[:sshport][,arg,arg,...] \\\n"
." ...\n"
."where -p is of the forwarded ports range's start (default: $o{p})\n"
unless @ARGV;
$o{'p'}++ while port_busy($o{'p'});
while (@ARGV) {
my ($host,@args) = split/,/,shift;
my ($port,$nexthop,$silent,$nextport);
$port = $nextport = $nexthop = $silent = '';
$host =~ s/:(\d+)// && ($port="-p $1");
if(@ARGV) {
my ($c,@args) = split/,/,$ARGV[0];
my ($u,$host) = split /\@/,$c;
$host =~ s/:(\d+)// && ($nextport=$1);
$nexthop = "-L$o{p}:$host:". ($nextport ? $nextport : 22);
$ARGV[0] = "$u\@localhost:$o{p}";
$ARGV[0] .= ','. join(',',@args);
$o{'p'}++;
$o{'p'}++ while port_busy($o{'p'});
$silent = '-f -N';
}
if(@ARGV) {
# clean up commandline
(my $cmd = "ssh $silent $port $host $nexthop @args")
=~ s/\s+/ /g;
$cmd =~ s/\s+$//;
# maybe I should just 'last' here
# - not die - and kill all shells so far
system $cmd and die "Can't '$cmd': $!\n";
# since ssh - f does a fork, we must get it's pid via ps
# XXX is there a portable way to do this?
open(I,"ps ux |") or die "Can't run ps: $!\n";
while() {
/$cmd/ && /\b(\d+)\b/ && push @pids, $1;
}
} else {
print "ssh $silent $port $host $nexthop @args\n";
system "ssh $silent $port $host $nexthop @args";
}
}
# reap all my shells
kill 15,$_ for reverse @pids;
# XXX is there a portable way to check for busy ports?
sub port_busy { system("lsof -i :$_[0] >/dev/null") ? 0 : 1 }