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 }