[comp.lang.perl] muxrsh.pl: parallel rsh commands

barnett@grymoire.crd.ge.com (Bruce Barnett) (06/07/91)

Here is my first perl script. Be gentle - gang.

You can use this to execute "df -t 4.2" on hundreds of machines.
The script will spawn off several processes (default maximum: 6) and run an
rsh command in each. When each one terminates, a new rsh is executed.
So the rsh processes run in parallel. The name of the machine is 
printed at the beginning of each line.

#!/usr/bin/perl                # -*-Perl-*-
# This is a mux version of rsh 
# it will do several rsh commands in parallel
# just the thing for doing "df -t 4.2" on 400 machines
#
# Bruce Barnett <barnett@crdgw1.ge.com>
# (Thanks to Randal for hard part)
#
# default number of processes is 6, 
# but is resettable using the -m argument
#
# usage:
# muxrsh [-m max] [-v] [-l username] command [args ...] <list_of_machines
#
# example:
#      muxrsh /bin/arch <machines
#      muxrsh -m 10 /bin/arch <machines
#      muxrsh -v -l staff df -t 4.2 <machines
#
# ------------------------------
# subroutine definitions   ....
sub getswitches {
    # parse arguments
    while ($ARGV[0] =~ /^-/) {
	$ARGV[0] =~ /^-v/ && ($verbose++,shift(@ARGV),next);
	$ARGV[0] =~ /^-m/ && (shift(@ARGV),$maxpids = shift(@ARGV), next);
	$ARGV[0] =~ /^-l/ && (shift(@ARGV),$remoteuser = "-l " . shift(@ARGV), next);
	last;
    }
}


sub wait {
    $verbose &&	printf "process %d called wait with %d alive\n", $$, &alive;
	local($somepid);
	while ($somepid = wait) {
		if (defined $pids{$somepid}) {
		    $verbose && print "wait saw $pids{$somepid}\n";
		    return delete $pids{$somepid};
		} else {
		    $verbose && print "wait saw $somepid?\n";
		}
	}
	warn "wait: nobody to wait on: $!";
}

sub alive {
    local(@pids) = keys %pids;
    return scalar(@pids);
}

# Main routine

sub doit {
    while ($machine = <STDIN>) {
	chop $machine;
	$verbose && printf "forking for %s with %d alive\n", $machine, &alive;
	&wait() if &alive > $maxpids;
	$pid = fork;
	die "fork: $!" unless defined $pid;
	if ( $pid) { # parent
	    $pids{$pid} = $machine;
	} else {
	    $results=`rsh -n $remoteuser $machine @ARGV 2>&1 `;
	    # this makes sure "machine: " is before each line returned.
	    @mresults = (split(/\n/, $results));
	    for $mresult (@mresults) {
		print "$machine: $mresult\n";
	    }
	    exit 0;
	}
    }
    &wait() while &alive > 0;
}

#--------------
# main routine
#

$|++;				# flush buffers on a line-by-line basis
$verbose = 0;			# assume verbose is off
$maxpids = 6;			# default maximum number of processes
#$remoteuser="-l staff";	# default remote user
$remoteuser=""; 
%pids = ();			# zero out the pids
&getswitches();			# get any switches/changes to defaults
@ARGV || die "Must specify a command to be executed!\n";
$verbose && $remoteuser && print "remote user is $remoteuser\n";
$verbose && print "Max number of processes is $maxpids\n";
&doit();			# tuit
0;
--
Bruce G. Barnett	barnett@crdgw1.ge.com	uunet!crdgw1!barnett