merlyn@iwarp.intel.com (Randal Schwartz) (12/10/90)
In article <THOTH.90Dec9144127@reef.cis.ufl.edu>, thoth@reef (Gilligan) writes: | Whenever my fileserver goes down I lose mail, so I hacked up a perl | script to execute commands in parallel and hand me the output in | sequence. Sounds like what I did. And solving your exact problems too. Here's a script that I use hourly (at least... it's fired up from a cron job for a background status check as one of its *many* uses aroundhere). Shamelessly derived from 'gsh' in the Perl distribution, and even has the same name... You will need to change the stuff after <<'ENDHOSTLIST' unless by some miracle you have exactly the same-named hosts as iWarp. :-) Some example commands: Set all the clocks to match up with r: gsh -v all-r 'rdate r' Reboot the microvaxen: gsh -n15 -v -z15 vax 'reboot' Invoke rdist to all the hosts from here: gsh -r -v all-`hostname` 'rdist -m $H' See what the nfs params on a particular disk is: gsh sun grep iwarpq:/q /etc/fstab Set a new root password: gsh -v -i all perl -pi.BAK - /etc/passwd <<EOF s#^root:[^:]+:#root:FoBS0d0PI8QRM:#; EOF Look at the df's for the diskful machines: gsh all-diskclient df See... many uses. #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: gsh # Wrapped by merlyn@iwarpse on Sun Dec 9 12:49:09 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'gsh' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'gsh'\" else echo shar: Extracting \"'gsh'\" \(8198 characters\) sed "s/^X//" >'gsh' <<'END_OF_FILE' X#!/local/usr/bin/perl X## Copyright (C) 1989, 1990, by Randal L. Schwartz. All Rights Reserved. X## usage: gsh [options] hostspec [command [arg]...] X## Runs command and args on hosts according to hostspec. Results are X## sent to STDOUT, with hostname prefix. A missing command means to just X## echo the computed hostnames on STDOUT. 'hostspec' is one of: X## hostname, hostattribute, hostspec+hostspec, hostspec-hostspec X## Default hostlist is defined in @HOSTLIST later on. X## X## options: X## -d: don't run any commands on other hosts... but fork anyway. X## -h hostlist: extend the hostlist with the contents of the named file. X## -H hostlist: replace the hostlist with the contents of the named file. X## -i: give STDIN to the processes as their STDIN X## -o place: send the outputs to "place$host" instead of STDOUT X## -n procs: run this many processes at a time (default 5). X## (remember that each rsh is two processes on this host!) X## -v: be noisy about starting and finishing processes. X## -z sec: zap processes after sec seconds (default 300). X## -r: don't run rsh, just invoke /bin/sh with $HOSTNAME and $H set to host X## "gsh -r -v all 'rdist -m $HOSTNAME'", for example X X@HOSTLIST = split(/\n/, <<'ENDHOSTLIST'); # comments allowed in here... Xall=vax+sun Xdiskful=vax+sun3server+sun4server+sun386 Xvax=microvax2 Xsun=sun3+sun4+sun386 Xsun3=sun3server+sun3client Xsun4=sun4server+sun4client Xsunserver=sun3server+sun4server Xsunclient=sun3client+sun4client Xsun3server=sun3/160s+sun3/260s+sun3/280s Xsun3client=sun3/50c+sun3/60c+sun3/75c+sun3/140c Xsun4server=sun4/280s+sun4/370s+sun4/390s+sun4/490s Xsun4client=sun4/110c Xsun386=sun386i Xiwarpa iwa a microvax2 ultrix2 Xiwarpb iwb b microvax2 ultrix2 Xiwarpc iwc c microvax2 ultrix2 Xiwarpd iwd d microvax2 ultrix2 Xiwarpe iwe e microvax2 ultrix2 Xiwarpf iwf f microvax2 ultrix2 Xiwarpg iwg g microvax2 ultrix2 Xiwarph iwh h microvax2 ultrix2 Xiwarpi iwi i microvax2 ultrix2 Xiwarpj iwj j sun3/160s sunos4 diskserver exabyte Xiwarpj0 iwj0 j0 sun3/75c sunos4 diskclient Xiwarpj1 iwj1 j1 sun3/75c sunos4 diskclient Xiwarpj2 iwj2 j2 sun3/75c sunos4 diskclient Xiwarpj3 iwj3 j3 sun3/75c sunos4 diskclient Xiwarpk iwk k sun3/260s sunos4 diskserver exabyte Xiwarpk0 iwk0 k0 sun3/75c sunos4 diskclient Xiwarpk1 iwk1 k1 sun3/75c sunos4 diskclient Xiwarpk2 iwk2 k2 sun3/75c sunos4 diskclient Xiwarpk3 iwk3 k3 sun3/75c sunos4 diskclient Xiwarpl iwl l sun3/260s sunos4 diskserver exabyte Xiwarpl0 iwl0 l0 sun3/75c sunos4 diskclient Xiwarpl1 iwl1 l1 sun3/75c sunos4 diskclient Xiwarpl2 iwl2 l2 sun3/75c sunos4 diskclient Xiwarpl3 iwl3 l3 sun3/75c sunos4 diskclient Xiwarpm iwm m sun3/260s sunos4 diskserver exabyte Xiwarpm0 iwm0 m0 sun3/140c sunos4 diskclient Xiwarpm1 iwm1 m1 sun3/140c sunos4 diskclient Xiwarpm2 iwm2 m2 sun3/140c sunos4 diskclient Xiwarpm3 iwm3 m3 sun3/140c sunos4 diskclient Xiwarpn iwn n sun3/260s sunos4 diskserver exabyte Xiwarpn0 iwn0 n0 sun3/140c sunos4 diskclient Xiwarpn1 iwn1 n1 sun3/140c sunos4 diskclient Xiwarpn2 iwn2 n2 sun3/140c sunos4 diskclient Xiwarpn3 iwn3 n3 sun3/140c sunos4 diskclient Xiwarpo iwo o sun3/260s sunos4 diskserver exabyte Xiwarpo0 iwo0 o0 sun3/140c sunos4 diskclient Xiwarpo1 iwo1 o1 sun3/140c sunos4 diskclient Xiwarpo2 iwo2 o2 sun3/140c sunos4 diskclient Xiwarpo3 iwo3 o3 sun3/140c sunos4 diskclient Xiwarpp iwp p sun3/280s sunos4 exabyte Xiwarpp0 iwp0 p0 sun386i sunos4 Xiwarpp1 iwp1 p1 sun386i sunos4 Xiwarpp2 iwp2 p2 sun386i sunos4 Xiwarpp3 iwp3 p3 sun386i sunos4 Xiwarpp4 iwp4 p4 sun386i sunos4 Xiwarpp5 iwp5 p5 sun386i sunos4 Xiwarpq iwq q sun4/280s sunos4 diskserver exabyte Xiwarpr iwr r sun3/280s sunos4 diskserver exabyte Xiwarpr0 iwr0 r0 sun3/60c sunos4 diskclient Xiwarpr1 iwr1 r1 sun3/60c sunos4 diskclient Xiwarpr2 iwr2 r2 sun3/60c sunos4 diskclient Xiwarpr3 iwr3 r3 sun3/60c sunos4 diskclient Xiwarpr4 iwr4 r4 sun3/60c sunos4 diskclient Xiwarps iws s sun3/160s sunos4 X# iwarpsa iwsa sa sun4/390s sunos4 exabyte Xiwarpsc iwsc sc sun4/390s sunos4 exabyte Xiwarpsd iwsd sd sun4/390s sunos4 exabyte Xiwarpse iwse se sun4/490s sunos4 exabyte Xiwarpv iwv v microvax2 ultrix2 Xiwarpw iww w microvax2 ultrix2 Xiwarpx iwx x microvax2 ultrix2 Xiwarpy iwy y microvax2 ultrix2 Xiwarpz iwz z sun3/260s sunos4 diskserver exabyte Xiwarpz0 iwz0 z0 sun3/60c sunos4 diskclient Xiwarpz1 iwz1 z1 sun3/60c sunos4 diskclient Xiwarpz2 iwz2 z2 sun3/60c sunos4 diskclient Xiwarpz3 iwz3 z3 sun3/60c sunos4 diskclient XENDHOSTLIST X X$| = 1; # don't buffer STDOUT X X$the_task_filename = "/tmp/$$.thetask"; X X$tasks = 0; X$taskmax = 5; X$zapsecs = 300; X Xsub start { X local($host) = @_; X X print "starting '$host'...\n" if $verbose; X X while ($tasks > 0 && $tasks >= $taskmax) { X &finish(); X }; X unless ($pid = fork) { # child X open(STDIN, "<$the_task_filename") || X die "Cannot open $the_task_filename as STDIN ($!)"; X open(STDOUT, ">$place$host") || X die "Cannot open $place$host ($!)"; X open(STDERR, ">&STDOUT"); X exec 'cat' if $debug; X $parent = $$; X if (fork) { # still the child X if ($dont_rsh) { X $ENV{'H'} = $host; X $ENV{'HOSTNAME'} = $host; X exec '/bin/sh'; X die "Cannot exec /bin/sh ($!)"; X } else { X exec 'rsh', $host, '/bin/sh'; X die "Cannot exec rsh ($!)"; X } X } X # child child X $zaptime = time + $zapsecs; X while (time < $zaptime) { X sleep 5; X exit 0 if getppid == 1; X } X kill 9, $parent; X print "\nTIMED OUT AFTER $zapsecs SECONDS\n"; X exit 0; X } X $tasklist{$pid} = $host; X $tasks++; X} X Xsub finish { X return unless $tasks > 0; X print "waiting on '", join(" ", sort values(tasklist)), "'...\n" X if $verbose; X do { X die "Nothing to wait for??? ($!)" unless ($pid = wait) > 0; X } until $tasklist{$pid}; X print "finished task on '", delete $tasklist{$pid}, "'.\n" X if $verbose; X $tasks--; X} X Xsub finishall { X while ($tasks > 0) { X &finish(); X } X} X Xsub gethostlist { X local($f,$replace) = @_; X open(GETHOSTLIST, "<$f") || die "Cannot open '$f' ($!)"; X @HOSTLIST = () if $replace; X unshift(@HOSTLIST, <GETHOSTLIST>); # put it at the beginning X close(GETHOSTLIST); X} X X# end initialization... begin code... X Xwhile ($ARGV[0] =~ /^-/) { X $_ = shift; X $debug++, $verbose++, next if /^-d/; X $verbose++, next if /^-v/; X $taskmax = (length($_) ? $_ : shift), next if s/^-n//; X &gethostlist(length($_) ? $_ : shift, 1), next if s/^-H//; X &gethostlist(length($_) ? $_ : shift, 0), next if s/^-h//; X $do_stdin++, next if /^-i/; X $place = (length($_) ? $_ : shift), next if s/^-o//; X $zapsecs = (length($_) ? $_ : shift), next if s/^-z//; X $dont_rsh++, next if /^-r/; X die "unknown flag $_"; X} X X$place = "/tmp/$$.", $do_stdout++ unless $place; X Xunshift(@HOSTLIST,"TARGET=" . shift); X X$the_task .= join(" ", @ARGV); Xif ($do_stdin) { X $_ = join("",<STDIN>); X chop if /\n$/; X $the_task = "($the_task ;) <<'FoObAr'\n$_\nFoObAr\n"; X # if I got tricky, I could skip the extra shell, but, hey... it works X} X X@TARGETS = (); X X$attr{'TARGET'} = 1; # this is what I want. X Xfor $_ (@HOSTLIST) { X s/\s*\n?$//; # toss trailing white X s/^\s*//; # toss leading white X next if /^(#.*)?$/; # skip comment lines and blank lines X if (/^([^-+=]+)=(.*)/) { X ($name,$repl) = ($1,"+$2"); X next unless $yes = $attr{$name}; # +1 if wanted, -1 if not X while ($repl =~ s/^([+-])([^-+]+)//) { X next if $attr{$2}; X $attr{$2} = ($1 eq '-') ? - $yes : $yes; X print "assigning $attr{$2} to $2\n" if $debug; X } X } else { # must be a terminal node: X @attr = split; X $host = $attr[0]; X $wanted = 0; X for $attr (@attr) { X $wanted++, next if $attr{$attr} > 0; X $wanted=-1, last if $attr{$attr} < 0; X } X push(TARGETS, $host) if $wanted > 0; X } X} X Xif ($the_task =~ /^\s*$/) { # no command? just list the hosts X print join("\n", @TARGETS), "\n"; X exit 0; X} X Xopen(THE_TASK, ">$the_task_filename") || die "Cannot open THE_TASK ($!)"; Xprint THE_TASK $the_task; Xclose(THE_TASK); X Xfor $host (@TARGETS) { # launch'em all, $taskmax at a time X &start($host); X} X X&finishall(); # and hang out while the last $taskmax finish X Xunlink $the_task_filename; # no need for this anymore X Xexit 0 unless $do_stdout; X Xfor $host (@TARGETS) { # show what they said X (warn "missing output for $host ($!)"), next X unless open(F,"<$place$host"); X if ($_ = join("$host:\t", <F>)) { X print "$host:\t$_"; X print "\n" unless /\n$/; X } X close(F); X unlink "$place$host"; X} Xexit 0; END_OF_FILE if test 8198 -ne `wc -c <'gsh'`; then echo shar: \"'gsh'\" unpacked with wrong size! fi chmod +x 'gsh' # end of 'gsh' fi echo shar: End of shell archive. exit 0 print "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: "Intel: putting the 'backward' in 'backward compatible'..."====/