#!/usr/bin/perl -w
# usage: mscp [options] 'scp args template' hosts
#   type password
# options:
#   -t specify timeout (in seconds)
#   -C connect_timeout - specify a connection timeout in seconds after which it will give up (default is 20sec)
#   -p specify prefix for log files
#   -d debug mode, prints the expect script
#   -u specify user (uses 'dev' by default)
#   -F n - max number of children to fork (default 40) - actually creates more
#	than one process for each of these (expect, scp)
# the 'scp args template' is a single argument (i.e. in quotes) which contains
# space separated arguments to pass to scp, with two special characters:
#	: means "user@host:"
#	@ means "host"
# e.g.
#	mscp ':/etc/sina/sina.conf sina.conf.@' adm2.snc adm2.vic
# is the same as
#	scp adm2.snc:/etc/sina/sina.conf sina.conf.adm2.snc
#	scp adm2.vic:/etc/sina/sina.conf sina.conf.adm2.vic

use strict;
use Getopt::Std;
use IO::File;
use vars qw/$opt_t $opt_p $opt_d $opt_u $opt_F $opt_C/;
getopts('t:p:du:F:C');

$opt_F ||= 40;

my $user = $opt_u || 'dev';
my $timeout = defined $opt_t ? $opt_t : -1;
my $connect_timeout = defined $opt_C ? $opt_C : 20;
my $prefix = defined $opt_p ? "$opt_p" : "";
my($srcdest) = shift;
my @hosts = @ARGV; @ARGV = ();
-t STDIN && system "stty -echo" and die;
print STDERR "$user password: ";
chomp(my $passwd1 = <STDIN>);
$passwd1 =~ s/\$/\\\$/g;
-t STDIN && system "stty echo" and die;

while (@hosts) {
	do_batch(splice @hosts, 0, $opt_F);
}

print STDERR "complete.\n";

exit 0;

sub do_batch {
my @hosts = @_;
my @children;
for my $host (@hosts) {
	(my $srcdest_real = $srcdest) =~ s/@/$host/g;
	$srcdest_real =~ s/:/$user\@$host:/g;
	$srcdest_real =~ s/([\[\]])/\\$1/g;
	my $expect = <<End;
set timeout $connect_timeout
spawn scp $srcdest_real
expect {
	timeout { exit 1 }
	-re "\\\\?.*" {
		send "yes\\r"
		expect {
			timeout { exit 1 }
			"password: "
		}
	}
	"password: "
}
send "$passwd1\\r";
expect {
	timeout { exit 1 }
	"password: " { exit 1 }
	-re "|.*"
}
set timeout $timeout
expect eof
End
print $expect if $opt_d;
print STDERR "$host";
my $fh = IO::File->new("|expect > $prefix$host");
$fh->autoflush;
print STDERR ".";
push @children, $fh;
print $fh $expect;
print STDERR ".\n";
}
for my $fh (@children) {
	close $fh;
}
}
