Документ взят из кэша поисковой машины. Адрес оригинального документа : http://hea-www.harvard.edu/~fine/Tech/multi-rsh
Дата изменения: Fri Aug 1 23:27:55 2008
Дата индексирования: Mon Oct 1 23:19:17 2012
Кодировка:
#!/usr/bin/perl

#
# multi-rsh version 1.2
# Copyright © 2004,2005 Thomas A. Fine
# Freely redistribute for any purpose, in whole or part, provided this
# copyright/license statement is preserved.
#
# online docs at http://hea-www.harvard.edu/~fine/Tech/multi-rsh.html
#
# email to my last name at head.cfa.harvard.edu with questions and
# comments.
#
#Version 1.0 - 4/26/04 - Initial Version
#Version 1.1 - 5/26/05 - Bug Fix: non-existent host translated to localhost
#Version 1.2 - 12/29/06 - Bug Fix: typo at line 90, wrong var in chop
#

if ($> != 0) {
print "I have to run as root!\n";
exit(1);
}

$|=1;

use Socket;

@killlist=(HUP,INT,TERM,KILL);
$SIG{'INT'}=report;

if ($#ARGV < 1) {
&usage;
exit(1);
}

$numconns=20;
$timeout=15;
$user=(getpwuid($>))[0];

while ($ARGV[0] =~ /^-/) {
$opt=shift(@ARGV);
if ($opt eq "-p") {
$numconns=shift(@ARGV);
} elsif ($opt eq "-t") {
$timeout=shift(@ARGV);
} elsif ($opt eq "-u") {
#only allow this if the real user is root.
if ($< == 0) {
$user=shift(@ARGV);
} else {
print "the -u option requires root priveleges\n";
exit(1);
}
} elsif ($opt eq "-d") {
$debug=1;
} elsif ($opt eq "-l") {
$inputlist=shift(@ARGV);
} else {
print "ERROR: unknown option \"$opt\"\n";
&usage;
exit(1);
}
}

if (length($inputlist)) {
open(LIST,$inputlist);
while() {
chop;
push(@hostlist,$_);
}
close(LIST);
if ($#hostlist == -1) {
print "ERROR: empty host list\n";
exit(1);
}
$command=join(" ",@ARGV);
} else {
$command=shift(@ARGV);
@hostlist=@ARGV;
#expand a single-arg list into a real list
if ($#hostlist == 0) {
@tmp=split(' ',$hostlist[0]);
if ($#tmp>0) {
@hostlist=@tmp;
}
}
}

$myhost=`hostname`;
chop($myhost);

# make initial connections
for ($contact=0, $i=0; $i<$numconns && $i<=$#hostlist; ++$i, ++$contact) {
($handle[$i],$pid[$i])=&myrshinit($hostlist[$contact],$user,$command);
$hostslot[$i]=$hostlist[$contact];
$when[$i]=time;
$killnum[$i]=0;
++$numhits;
}

# select loop
while ($numhits) {
$rin="";
#set up mask bits
$num=0;
for ($i=0; $i<$numconns; ++$i) {
next if (!defined($handle[$i]));
vec($rin,fileno($handle[$i]),1)=1;
++$num;
}
#print "$num filehandles currently open!\n";

($nfound,$tym)=select($rout=$rin, undef, undef, 1);

#check each bit in mask
for ($i=0; $i<$numconns; ++$i) {
next if (!defined($handle[$i]));
if (vec($rout,fileno($handle[$i]),1)) {
if (eof($handle[$i])) {
close($handle[$i]);
--$numhits;
if ($contact <= $#hostlist) {
($handle[$i],$pid[$i])=&myrshinit($hostlist[$contact],$user,$command);
$hostslot[$i]=$hostlist[$contact];
$when[$i]=time;
$killnum[$i]=0;
++$contact;
++$numhits;
#print "reopened $i\n";
} else {
$handle[$i]=undef;
#print "finshed $i\n";
}
} else {
#$junk=<$handle[$i]>;
#$input{$hostslot[$i]}.=$junk;
#print "$hostslot[$i]: $junk";

if (! read($handle[$i],$buf,8192)) {
print "read ERROR: $!\n";
}
$buf =~ s/\n/\n$hostslot[$i]: /g;
$buf =~ s/$hostslot[$i]: $//g;
print "$hostslot[$i]: $buf";
#don't timeout if it's already answered
$when[$i]=0;
}
} elsif ($when[$i] && (time-$when[$i] > $timeout)) {
if ($debug) { print STDERR "timing out $hostslot[$i]\n"; }
if (! kill(0,$pid[$i])) {
print "$hostslot[$i]: dead\n";
} elsif ($killnum[$i]>$#killlist) {
print "$hostslot[$i]: multi-rsh timeout exceeded, no response to kills\n";
} else {
print "$hostslot[$i]: multi-rsh timeout exceeded, attempting kill $killlist[$killnum[$i]] $pid[$i]\n";
kill($killlist[$killnum[$i]],$pid[$i]);
++$killnum[$i];
}
}
}
}

#
# END OF MAIN
#

sub rshinit {
local($host,$user,$command)=($_[0],$_[1],$_[2]);
local *FH;

# DON'T use -n as it causes rsh sockets to exit non-cleanly and temporarily
# blocks that port -- this is bad because this script can conceivably tie
# up all free secure ports.
#$procid=open(FH,"rsh -l $user $host $command 2>&1 |");
if (! ($procid=open(FH,"-|")) ) {
setpgrp(0,0);
close(STDERR);
open(STDERR,">&STDOUT");
exec("/usr/ucb/rsh", "-l", $user, $host, $command);
}
if ($debug) { print "$host contacted\n"; }
return (*FH,$procid);
}

sub myrshinit {
local($host,$user,$command)=($_[0],$_[1],$_[2]);
local($proto,$iaddr,$there);
local *FH;

if (! ($procid=open(FH, "-|")) ) {
$rshport=514;
setpgrp(0,0);
#close(STDERR);
#open(STDERR, ">&STDOUT");
select(STDOUT); $|=1;

$myaddr = inet_aton($myhost);
$iaddr = inet_aton($host);
if (length($iaddr) == 0) {
print "ERROR: Host not found\n";
exit(1);
}
$there = sockaddr_in($rshport, $iaddr);
$proto = getprotobyname("tcp");
if (!socket(S,PF_INET,SOCK_STREAM,$proto)) {
print STDERR "socket: $!\n"; exit(1);
}
# we don't want this - leads to addr in use errors on connect
# if local and remote host/ports are all the same!
# if (!setsockopt(S, SOL_SOCKET, SO_REUSEADDR, pack("l", 1))) {
# print STDERR "setsockopt: $!\n"; exit(1);
# }
$done=0;
$myport=1023;
while(!$done) {
$here = sockaddr_in($myport, $myaddr);
if (bind(S, $here)) {
#print "using local port $myport\n";
$done=1;
}
if (--$myport == 0) {
print "$host- ERROR: Can't get a port\n";
exit(1);
}
}
if (!connect(S,$there)) {
print STDERR "$host- connect: $!\n";
exit(1);
}
select(S); $|=1; select(STDOUT);
#send error port (zero means send stderr on same channel as stdout)
#print S "$errport";
print S "0\0$user\0$user\0$command\0";
##source user
#print S "$user\0";
##destination user
#print S "$user\0";
##command
#print S "$command\0";

#rsh verifies the complete connection by returning a null character
#But we don't want to eat that here; we want to let the main process
#handle it, so that it can have separate timeouts for the connection
#and the program. Well, yeah, but none of that is implemented yet.
read(S,$buf,1);
while(read(S,$buf,8192)) {
print $buf;
}
exit(0);
}
if ($debug) { print "$host contacted\n"; }
return (*FH,$procid);
}

sub usage {
print "Usage: multi-rsh [-p n] [-t n] [-u user] command host [host ...]\n";
print " multi-rsh [-p n] [-t n] [-u user] -l inputlist command\n";
print " In the first form, command must be a single argument\n";
print " In the second form, command can be multiple args\n";
print " The second form reads a list of hosts from inputfile\n";
print " (- may be used to indicate standard input)\n";
print " -p sets number of parallel connections (default 20)\n";
print " -t sets timeout in seconds before giving up (default 15)\n";
print " -u sets the remote user (defaults to effective uid)\n";
}

sub report {
local($i);
for ($i=0; $i<$numconns; ++$i) {
printf("%2d: %20s %d\n", $i, $hostslot[$i], $when[$i]?time-$when[$i]:-1);
}
}