merlyn@iwarp.intel.com (Randal Schwartz) (03/08/90)
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!"=/