home *** CD-ROM | disk | FTP | other *** search
- From: merlyn@iwarp.intel.com (Randal Schwartz)
- Newsgroups: comp.lang.perl,alt.sources
- Subject: multiple host command launcher (gsh) in Perl
- Message-ID: <1990Mar7.190633.3801@iwarp.intel.com>
- Date: 7 Mar 90 19:06:33 GMT
-
- Here's the 'gsh' I've been using for a while (industrial strength by
- now). The coding style is not pretty, but it has been roadtested.
-
- Yes, this stuff was inspired by the 'gsh' in the Perl distribution,
- although I've taken it about three steps further. Mine has parallel
- launching and waiting, a built-in (but overridable/extensible)
- hostlist, and a timeout for those rsh's that launch but "never" come
- back. You'll want to edit the builtin hostlist, unless you just
- *happen* to have a bunch of systems named 'iwarpa', 'iwarpb', etc.
- etc. :-)
-
- Enjoy.
-
- ================================================== snip here
- #!/local/merlyn/bin/perl
- ## Copyright (C) 1989, 1990, by Randal L. Schwartz. All Rights Reserved.
- ## usage: gsh [options] hostspec [command [arg]...]
- ## Runs command and args on hosts according to hostspec. Results are
- ## sent to STDOUT, with hostname prefix. A missing command means to just
- ## echo the computed hostnames on STDOUT. 'hostspec' is one of:
- ## hostname, hostattribute, hostspec+hostspec, hostspec-hostspec
- ## Default hostlist is defined in @HOSTLIST later on.
- ##
- ## options:
- ## -d: don't run any commands on other hosts... but fork anyway.
- ## -h hostlist: extend the hostlist with the contents of the named file.
- ## -H hostlist: replace the hostlist with the contents of the named file.
- ## -i: give STDIN to the processes as STDIN
- ## -o place: send the outputs to "place$host" instead of STDOUT
- ## -n procs: run this many processes at a time (default 5).
- ## (remember that each rsh is two processes on this host!)
- ## -v: be noisy about starting and finishing processes.
- ## -z sec: zap processes after sec seconds (default 300).
-
- ## requires 3.0 beta or better
- @HOSTLIST = split(/\n/, <<'ENDHOSTLIST'); # comments allowed in here...
- all=vax+sun
- sun=sun3+sun4+sun386
- sun3=sun3server+sun3client
- sun4=sun4server+sun4client
- sunserver=sun3server+sun4server
- sunclient=sun3client+sun4client
- sun3server=sun3/160s+sun3/260s+sun3/280s
- sun3client=sun3/50c+sun3/60c+sun3/75c+sun3/140c
- sun4server=sun4/280s
- sun4client=sun4/110c
- sun386=sun386i
- iwarpa iwa a vax ultrix2
- iwarpb iwb b vax ultrix2
- iwarpc iwc c vax ultrix2
- iwarpd iwd d vax ultrix2
- iwarpe iwe e vax ultrix2
- iwarpf iwf f vax ultrix2
- iwarpg iwg g vax ultrix2
- iwarph iwh h vax ultrix2
- iwarpi iwi i vax ultrix2
- iwarpj iwj j sun3/160s sunos4 diskserver
- iwarpj0 iwj0 j0 sun3/75c sunos4 diskclient
- iwarpj1 iwj1 j1 sun3/75c sunos4 diskclient
- iwarpj2 iwj2 j2 sun3/75c sunos4 diskclient
- iwarpj3 iwj3 j3 sun3/75c sunos4 diskclient
- iwarpk iwk k sun3/260s sunos4 diskserver
- iwarpk0 iwk0 k0 sun3/75c sunos4 diskclient
- iwarpk1 iwk1 k1 sun3/75c sunos4 diskclient
- iwarpk2 iwk2 k2 sun3/75c sunos4 diskclient
- iwarpk3 iwk3 k3 sun3/75c sunos4 diskclient
- iwarpl iwl l sun3/260s sunos4 diskserver
- iwarpl0 iwl0 l0 sun3/75c sunos4 diskclient
- iwarpl1 iwl1 l1 sun3/75c sunos4 diskclient
- iwarpl2 iwl2 l2 sun3/75c sunos4 diskclient
- iwarpl3 iwl3 l3 sun3/75c sunos4 diskclient
- iwarpm iwm m sun3/260s sunos4 diskserver
- iwarpm0 iwm0 m0 sun3/140c sunos4 diskclient
- iwarpm1 iwm1 m1 sun3/140c sunos4 diskclient
- iwarpm2 iwm2 m2 sun3/140c sunos4 diskclient
- iwarpm3 iwm3 m3 sun3/140c sunos4 diskclient
- iwarpn iwn n sun3/260s sunos4 diskserver
- iwarpn0 iwn0 n0 sun3/140c sunos4 diskclient
- iwarpn1 iwn1 n1 sun3/140c sunos4 diskclient
- iwarpn2 iwn2 n2 sun3/140c sunos4 diskclient
- ## iwarpn3 iwn3 n3 sun3/140c sunos4 diskclient
- iwarpo iwo o sun3/260s sunos4 diskserver
- iwarpo0 iwo0 o0 sun3/140c sunos4 diskclient
- iwarpo1 iwo1 o1 sun3/140c sunos4 diskclient
- iwarpo2 iwo2 o2 sun3/140c sunos4 diskclient
- iwarpo3 iwo3 o3 sun3/140c sunos4 diskclient
- iwarpp iwp p sun3/280s sunos4
- iwarpp0 iwp0 p0 sun386i sunos4
- iwarpp1 iwp1 p1 sun386i sunos4
- iwarpp2 iwp2 p2 sun386i sunos4
- iwarpp3 iwp3 p3 sun386i sunos4
- iwarpp4 iwp4 p4 sun386i sunos4
- iwarpp5 iwp5 p5 sun386i sunos4
- iwarpq iwq q sun4/280s sunos4 diskserver
- ## iwarpq0 iwq0 q0 sun4/110c sunos4 diskclient
- ## iwarpq1 iwq1 q1 sun4/110c sunos4 diskclient
- iwarpr iwr r sun3/280s sunos4 diskserver
- iwarpr0 iwr0 r0 sun3/60c sunos4 diskclient
- iwarpr1 iwr1 r1 sun3/60c sunos4 diskclient
- iwarpr2 iwr2 r2 sun3/60c sunos4 diskclient
- iwarpr3 iwr3 r3 sun3/60c sunos4 diskclient
- iwarpr4 iwr4 r4 sun3/60c sunos4 diskclient
- ## iwarps iws s sun3/160s sunos4
- iwarpv iwv v vax ultrix2
- iwarpw iww w vax ultrix2
- iwarpx iwx x vax ultrix2
- iwarpy iwy y vax ultrix2
- iwarpz iwz z sun3/260s sunos4 diskserver
- iwarpz0 iwz0 z0 sun3/60c sunos4 diskclient
- iwarpz1 iwz1 z1 sun3/60c sunos4 diskclient
- iwarpz2 iwz2 z2 sun3/60c sunos4 diskclient
- iwarpz3 iwz3 z3 sun3/60c sunos4 diskclient
- ENDHOSTLIST
-
- $| = 1; # don't buffer STDOUT
-
- $the_task_filename = "/tmp/$$.thetask";
-
- $tasks = 0;
- $taskmax = 5;
- $zapsecs = 300;
-
- sub start {
- local($host) = @_;
-
- print "starting '$host'...\n" if $verbose;
-
- while ($tasks > 0 && $tasks >= $taskmax) {
- &finish();
- };
- unless ($pid = fork) { # child
- open(STDIN, "<$the_task_filename") ||
- die "Cannot open $the_task_filename as STDIN ($!)";
- open(STDOUT, ">$place$host") ||
- die "Cannot open $place$host ($!)";
- open(STDERR, ">&STDOUT");
- exec 'cat' if $debug;
- $parent = $$;
- if (fork) { # still the child
- exec 'rsh', $host, '/bin/sh';
- die "Cannot exec rsh ($!)";
- }
- # child child
- $zaptime = time + $zapsecs;
- while (time < $zaptime) {
- sleep 5;
- exit 0 if getppid == 1;
- }
- kill 9, $parent;
- print "\nTIMED OUT AFTER $zapsecs SECONDS\n";
- exit 0;
- }
- $tasklist{$pid} = $host;
- $tasks++;
- }
-
- sub finish {
- return unless $tasks > 0;
- print "waiting on '", join(" ", sort values(tasklist)), "'...\n"
- if $verbose;
- do {
- die "Nothing to wait for??? ($!)" unless ($pid = wait) > 0;
- } until $tasklist{$pid};
- print "finished task on '", delete $tasklist{$pid}, "'.\n"
- if $verbose;
- $tasks--;
- }
-
- sub finishall {
- while ($tasks > 0) {
- &finish();
- }
- }
-
- sub gethostlist {
- local($f,$replace) = @_;
- open(GETHOSTLIST, "<$f") || die "Cannot open '$f' ($!)";
- @HOSTLIST = () if $replace;
- unshift(@HOSTLIST, <GETHOSTLIST>); # put it at the beginning
- close(GETHOSTLIST);
- }
-
- # end initialization... begin code...
-
- while ($ARGV[0] =~ /^-/) {
- $_ = shift;
- $debug++, $verbose++, next if /^-d/;
- $verbose++, next if /^-v/;
- $taskmax = $1, next if /^-n(.+)/;
- $taskmax = shift, next if /^-n/;
- &gethostlist($1, 1), next if /^-H(.+)/;
- &gethostlist(shift, 1), next if /^-H/;
- &gethostlist($1), next if /^-h(.+)/;
- &gethostlist(shift), next if /^-h/;
- $do_stdin++, next if /^-i/;
- $place = $1, next if /^-o(.+)/;
- $place = shift, next if /^-o/;
- $zapsecs = $1, next if /^-z(.+)/;
- $zapsecs = shift, next if /^-z/;
- die "unknown flag $_";
- }
-
- $place = "/tmp/$$.", $do_stdout++ unless $place;
-
- unshift(@HOSTLIST,"TARGET=" . shift);
-
- $the_task .= join(" ", @ARGV);
- if ($do_stdin) {
- $_ = join("",<STDIN>);
- chop if /\n$/;
- $the_task = "($the_task ;) <<'FoObAr'\n$_\nFoObAr\n";
- # if I got tricky, I could skip the extra shell, but, hey... it works
- }
-
- @TARGETS = ();
-
- $attr{'TARGET'} = 1; # this is what I want.
-
- for $_ (@HOSTLIST) {
- s/\s*\n?$//; # toss trailing white
- s/^\s*//; # toss leading white
- next if /^(#.*)?$/; # skip comment lines and blank lines
- if (/^([^-+=]+)=(.*)/) {
- ($name,$repl) = ($1,"+$2");
- next unless $yes = $attr{$name}; # +1 if wanted, -1 if not
- while ($repl =~ s/^([+-])([^-+]+)//) {
- next if $attr{$2};
- $attr{$2} = ($1 eq '-') ? - $yes : $yes;
- print "assigning $attr{$2} to $2\n" if $debug;
- }
- } else { # must be a terminal node:
- @attr = split;
- $host = $attr[0];
- $wanted = 0;
- for $attr (@attr) {
- $wanted++, next if $attr{$attr} > 0;
- $wanted=-1, last if $attr{$attr} < 0;
- }
- push(TARGETS, $host) if $wanted > 0;
- }
- }
-
- if ($the_task =~ /^\s*$/) { # no command? just list the hosts
- print join("\n", @TARGETS), "\n";
- exit 0;
- }
-
- open(THE_TASK, ">$the_task_filename") || die "Cannot open THE_TASK ($!)";
- print THE_TASK $the_task;
- close(THE_TASK);
-
- for $host (@TARGETS) { # launch'em all, $taskmax at a time
- &start($host);
- }
-
- &finishall(); # and hang out while the last $taskmax finish
-
- unlink $the_task_filename; # no need for this anymore
-
- exit 0 unless $do_stdout;
-
- for $host (@TARGETS) { # show what they said
- open(F,"<$place$host") || die "missing output for $host ($!)";
- if ($_ = join("$host:\t", <F>)) {
- print "$host:\t$_";
- print "\n" unless /\n$/;
- }
- close(F);
- unlink "$place$host";
- }
- exit 0;
- ================================================== snip here
-
- Just another Perl hacker,
- --
- /=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
- | on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III |
- | merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
- \=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/
-