source: gpfs_3.1_ker2.6.20/lpp/mmfs/bin/mmdsh @ 223

Last change on this file since 223 was 16, checked in by rock, 17 years ago
  • Property svn:executable set to *
File size: 57.5 KB
Line 
1#!/usr/bin/perl
2# IBM_PROLOG_BEGIN_TAG
3# This is an automatically generated prolog.
4
5
6
7# Licensed Materials - Property of IBM
8
9# (C) COPYRIGHT International Business Machines Corp. 2000,2006
10# All Rights Reserved
11
12# US Government Users Restricted Rights - Use, duplication or
13# disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
14
15# IBM_PROLOG_END_TAG
16# @(#)24  1.50  src/avs/fs/mmfs/ts/admin/mmdsh, mmfs, avs_rgpfs24, rgpfs240610b 2/12/06 12:34:29
17#############################################################################
18#
19# Module:  mmdsh
20#
21#CPRY
22# 5765-296 (C) Copyright IBM Corporation 1994, 2000
23# Licensed Materials - Property of IBM
24# All rights reserved.
25# US Government Users Restricted Rights -
26# Use, duplication or disclosure restricted by
27# GSA ADP Schedule Contract with IBM Corp.
28#CPRY
29#
30#############################################################################
31#
32# Description:  command for running commands on multiple nodes or
33#               network connected hosts in parallel
34#
35#   mmdsh [-isv] [-f num] [-F hostlistfile] [-L listofhosts] [-I file] [-k]
36#         [-N nodespecification] [-R reportfile] [-r] [command]
37#
38# Flags/parms:
39#
40# [-f num]    Specifies a fanout value used for concurrent execution.
41#
42# [-F hostlistfile]
43#             Runs the command on the hosts in the specified hostlistfile.
44#             The hostlistfile argument is the fully qualified name
45#             of a file that lists the names of the desired hosts,
46#             one host per line.
47#
48# [-i]        Displays the set of nodes on which the command will run
49#             before command execution.
50#
51# [-I file]   Name of the file to be copied on the remote node prior
52#             to executing the command.  The file must be in /var/mmfs/tmp.
53#             This option can be specified twice.
54#
55# [-k]        Remove files specified with the -I flag before exiting.
56#
57# [-L listofhosts]
58#             Runs the command on the specified list of hosts.
59#             The listofhosts argument is a comma-separated list of hosts.
60#
61# [-N nodespecification]
62#             Runs the command on the nodes in the given node specification.
63#             The nodespecification argument can be a comma-separated list of
64#             nodes, a node file, or a node class.  The nodes in the list or
65#             the file can be specified as long or short admin or daemon
66#             node names, node numbers, node number ranges, or IP addresses.
67#
68# [-R reportfile]
69#             Reports the list of hosts removed from the working collective
70#             when host verification (host ping) fails.  The report is written
71#             to the specified file with one host per line.  The report is
72#             generated only when combined with the '-v' flag.
73#
74# [-r]        Reports the list of hosts removed from the working collective
75#             when host verification (host ping) fails.  The report is written
76#             to standard error.  The report is generated only when combined
77#             with the '-v' flag.
78#
79# [-s]        Suppresses the prepending of the hostname string to each line
80#             of output generated by running the command on the remote host.
81#
82# [-v]        Verify that each host is reachable before adding it to the
83#             set of nodes on which the command will run.
84#
85# [command]   command to be run on the remote hosts.
86#             If the command is the reserved word "_SELECT_FROM_FILE_",
87#             then the commands to be run on the different hosts are
88#             expected to be in the file pointed to by the environment
89#             variable mmdshCommandsFile.  Each line of this file consists
90#             of hostname followed by a command string.
91#
92# Outputs:
93#   The command is executed on the specified host(s).
94#
95# Examples:
96#   mmdsh -F /nodes uname -a
97#   mmdsh -L host1,host2,host3 ls
98#   mmdsh -N quorumnodes date
99#
100#############################################################################
101
102# Obtain valid "waitpid" flags.
103use POSIX ":sys_wait_h";
104
105$MMCOMMON="/usr/lpp/mmfs/bin/mmcommon";
106$REL_HOSTNAME_Field=8;
107
108
109#----------------------------------------------------------------------------
110#
111# Function:  Correct the return value from system() and etc.
112#
113#----------------------------------------------------------------------------
114sub normalizeRC
115{
116  local ($rc);
117
118  $rc = $_[0] + 0;
119  if ($rc >= 256) { $rc = $rc / 256; }
120
121  return $rc;
122
123}  #---- end of function normalizeRC -----------------------------------
124
125
126#----------------------------------------------------------------------------
127#
128# Function:  Print a message using the message catalog facilities.
129#
130# Input:     $_[0] - message number in catalog
131#            $_[1] - message text formatting string
132#            ...   - optional message parameters (up to 5)
133#
134#----------------------------------------------------------------------------
135
136sub prtMsg
137{
138  local ($msgNum) = shift(@_);
139  local ($p1)     = shift(@_);
140  local ($p2)     = shift(@_);
141  local ($p3)     = shift(@_);
142  local ($p4)     = shift(@_);
143  local ($p5)     = shift(@_);
144
145  local ($msgTxt);
146  local ($prog);
147
148  chop($prog = `/bin/basename $0`);
149 
150  for ($msgNum) {
151
152    if    (/^13$/)
153    {
154      $msgTxt="%s:  Incorrect option: %s";
155    }
156    elsif (/^36$/)
157    {
158      $msgTxt="%s:  %s flag specified twice.";
159    }
160    elsif (/^38$/)
161    {
162      $msgTxt="%s:  Invalid extra argument: %s";
163    }
164    elsif (/^40$/)
165    {
166      $msgTxt="%s:  Invalid integer for %s: %s";
167    }
168    elsif (/^153$/)
169    {
170      $msgTxt="%s:  Invalid value for %s flag";
171    }
172    elsif (/^168$/)
173    {
174      $msgTxt="%s:  The device name %s starts with a slash, but not /dev/.";
175    }
176    elsif (/^171$/)
177    {
178      $msgTxt="%s:  Unexpected error from %s. Return code: %s";
179    }
180    elsif (/^204$/)
181    {
182      $msgTxt="%s:  Missing argument after %s flag";
183    }
184    elsif (/^206$/)
185    {
186      $msgTxt="Command %s failed with return code %s.";
187    }
188    elsif (/^362$/)
189    {
190      $msgTxt="%s:  Nodes on which the command will be run:";
191    }
192    elsif (/^363$/)
193    {
194      $msgTxt="%s:  WCOLL (working collective) environment variable not set.";
195    }
196    elsif (/^364$/)
197    {
198      $msgTxt="%s:  Cannot open file %s.  Error string was:  %s";
199    }
200    elsif (/^365$/)
201    {
202      $msgTxt="%s:  %s remote shell process had return code %s.";
203    }
204    elsif (/^366$/)
205    {
206      $msgTxt="%s:  Caught SIG %s - terminating the child processes.";
207    }
208    elsif (/^367$/)
209    {
210      $msgTxt="%s:  There are no available nodes on which to run the command.";
211    }
212    elsif (/^368$/)
213    {
214      $msgTxt="%s:  Unable to pipe.  Error string was:  %s";
215    }
216    elsif (/^369$/)
217    {
218      $msgTxt="%s:  Unable to redirect %s.  Error string was:  %s";
219    }
220    elsif (/^422$/)
221    {
222      $msgTxt="%s:  Invalid or missing remote shell command:  %s";
223    }
224    elsif (/^423$/)
225    {
226      $msgTxt="%s:  Invalid or missing remote file copy command:  %s";
227    }
228    elsif (/^573$/)
229    {
230      $msgTxt="%s:  Node %s is not available to run the command.";
231    }
232    else            # should never happen
233    {
234      $msgTxt="\n%s:  An unknown error number %s was passed to prtMsg.";
235      printf STDERR  "$msgTxt\n\n", $prog, $msgNum;
236      return;
237    }
238  }
239
240  if ($osName eq "AIX")
241  {
242    $msg=`$DSPMSG $msgNum \"$msgTxt\" \"$prog\" \"$p1\" \"$p2\" \"$p3\" \"$p4\" \"$p5\"`;
243    printf STDERR  "$msg\n";
244  }
245  else
246  {
247    printf STDERR  "$msgTxt\n", $prog, $p1, $p2, $p3, $p4, $p5;
248  }
249
250}  #---- end of function prtMsg ----------------------------------------
251
252
253#----------------------------------------------------------------------------
254#
255# read_pipes_and_wait
256#
257# Read STDOUT and STDERR on specified child rsh process pipes.
258# Display to parent's STDOUT or STDERR.  If the '-s' flag is not
259# specified, then host output is preceded by "hostname:  ".
260#
261# Output from STDERR is first written to an array, since there is
262# a limit of 32k from STDERR before it ends up hanging.
263#
264# Reap completed children to allow additional child processes to be
265# spawned for fanout maintenance.
266#
267# Parameters:
268#   final_read -- true to "read/wait" on all specified children, false
269#                 to only "read" currently available data for specified
270#                 children and reap any completed children for fanout
271#                 maintenance.
272#   hosts      -- Array of hostnames with STDOUT/STDERR pipes to query.
273#
274#----------------------------------------------------------------------------
275
276sub read_pipes_and_wait {
277
278  my $final_read = shift;
279
280  local(@hosts) = @_;
281
282  local($fno,$fh,$rin,$rout,$num_fh,$host,$err_ctr,$array_updated,$offset);
283
284  # Initialize the pipes read select vector.
285  $rin = '';
286  $num_fh = 0;
287  foreach $host (@hosts) {
288    if (($fno = fileno($r_out{$host})) ne '') {
289      vec($rin, $fno, 1) = 1;
290      $num_fh++;
291    }
292    if (($fno = fileno($r_err{$host})) ne '') {
293      vec($rin, $fno, 1) = 1;
294      $num_fh++;
295    }
296  }
297
298  $err_ctr = 0;
299  $array_updated = 0;
300
301  READWAIT: {
302
303    if ($num_fh > 0) {
304      # Wait for outstanding I/O on read pipes.
305      $nready = select($rout = $rin, undef, undef, undef);
306      if ($nready > 0) {
307        # At least one pipe has available data.
308        foreach $host (@hosts) {
309
310          # Process STDOUT I/O
311          $fh = $r_out{$host};
312          if (vec($rout, fileno($fh), 1)) {
313            $_ = <$fh>;
314            if (length($_)) {
315              if ($sflag) {
316               print STDOUT "$_";
317              }
318              else {
319               print STDOUT "$host:  $_";
320              }
321            }
322            else {
323              # EOF has been reached
324              vec($rin, fileno($fh), 1) = 0;
325              $num_fh--;
326              close($fh);
327            }
328          }
329
330          # Process STDERR I/O.
331          $fh = $r_err{$host};
332          if (vec($rout, fileno($fh), 1)) {
333            $_ = <$fh>;
334            if (length($_)) {
335              if ($sflag) {
336                $stderr_array[$err_ctr] = $_;
337              }
338              else {
339                $stderr_array[$err_ctr] = "$host:  $_";
340              }
341              $err_ctr++;
342              $array_updated = 1;
343            }
344            else {
345              # EOF has been reached
346              vec($rin, fileno($fh), 1) = 0;
347              $num_fh--;
348              close($fh);
349            }
350          }
351        }
352      }
353
354      # Reap any completed child process, in order to maintain fanout.
355      if (($child_pid = waitpid(-1, &WNOHANG)) > 0) {
356
357        # Obtain the return code and hostname for the child process.
358        $child_rc = $? >> 8;
359        foreach (keys %pid) {
360          if ($pid{$_} == $child_pid) {
361            $child_host = $_;
362            last;
363          }
364        }
365
366        # Process any outstanding STDOUT I/O for the deceased child.
367        $fh = $r_out{$child_host};
368        if (fileno($fh) != undef) {
369          do {
370            $_ = <$fh>;
371            if (length($_))
372            {
373              if ($sflag) {
374                print STDOUT "$_";
375              }
376              else {
377                print STDOUT "$child_host:  $_";
378              }
379            }
380          } while (length($_));
381
382          # EOF has been reached
383          vec($rin, fileno($fh), 1) = 0;
384          $num_fh--;
385          close($fh);
386        }
387
388        # Process any outstanding STDERR I/O for the deceased child.
389        $fh = $r_err{$child_host};
390        if (fileno($fh) != undef) {
391          do {
392            $_ = <$fh>;
393            if (length($_)) {
394              if ($sflag) {
395                $stderr_array[$err_ctr] = $_;
396              }
397              else {
398                $stderr_array[$err_ctr] = "$child_host:  $_";
399              }
400              $err_ctr++;
401              $array_updated = 1;
402            }
403          } while (length($_));
404
405          # EOF has been reached
406          vec($rin, fileno($fh), 1) = 0;
407          $num_fh--;
408          close($fh);
409        }
410
411        if ($child_rc != 0) {
412          if ($hostCount == 1) {
413            $rcode = $child_rc;   # Pass back child's rc to mmdsh caller
414          }
415          else {
416            $rcode++;
417            #print STDERR "mmdsh: $child_host rsh had exit code $child_rc\n";
418            &prtMsg(365, $child_host, $child_rc);
419          }
420          push(@goners,$child_host);
421        }
422
423        # Remove the deceased child from @hs so we don't continually try to add
424        # it to the select vector as problems may occur with sufficiently large
425        # numbers of nodes.
426        $offset = 0;
427        foreach $host (@hs) {
428          if ($host eq $child_host) {
429            splice(@hs, $offset, 1);
430            last;
431          }
432          $offset++;
433        }
434
435        # Child terminated ==> decrement fanout
436        $cur_fanout--;
437      }
438
439      if (($final_read eq true) && ($num_fh > 0) ||
440          ($cur_fanout >= $fanout)) {
441        # Final pipes data retrieval ==> Wait/retrieve until all pipes closed
442        redo READWAIT;
443      }
444    }
445  }
446
447  # Print out accumulated STDERR output.
448  if ($array_updated) {
449    for ($prt_ctr=0; $prt_ctr < $err_ctr; $prt_ctr++) {
450      print STDERR "$stderr_array[$prt_ctr]";
451    }
452  }
453
454}  #---------- end of read_pipes_and_wait routine ---------------------------
455
456
457#----------------------------------------------------------------------------
458#
459# get_command
460#
461# Return the command from the command line or stdin.
462# Return 0 if no more to be read from command line or stdin.
463# If the command is in double quotes, add a double quote to the beginning.
464# If the command starts with '!', execute here and read in the next command.
465#
466#----------------------------------------------------------------------------
467
468sub get_command {
469
470  local($command);
471  if ($done) {
472    return(0);
473  }
474  if (@ARGV) {
475    $done = 1;
476    @dsh_ARGV = @ARGV;
477    shift(@ARGV);
478    #
479    # Save the arguments in a scalar.  The return value
480    # of this routine is assigned to a scalar variable.
481    #
482    $dsh_return = join(' ',@dsh_ARGV);
483    return($dsh_return);
484  }
485  GET_COMMAND: {
486    -t && print STDERR "mmdsh> ";
487    $command = <STDIN>;
488    if (!defined($command)  ||
489      $command =~ /^\s*$/ || $command =~ /^\s*exit\s*$/) {
490      return(0);
491    } else {
492      chop $command;
493      # Add a double-quote to the beginning of the command if the user
494      # entered the command with double-quotes.
495      if ($command =~ /^\s*"(.*)/) {
496        $command = '"' . $command;
497      }
498      if ($command =~ /^\s*!(.*)/) {
499        &do_system($1);
500        redo GET_COMMAND;
501      } else {
502        return($command) || redo GET_COMMAND;
503      }
504    }
505  }
506
507}  #---------- end of get_command routine ------------------------------
508
509
510#----------------------------------------------------------------------------
511#
512# do_system
513#
514# Issue system() call with default signal handling.
515# Return the executed command's exit code.
516#
517#----------------------------------------------------------------------------
518
519sub do_system {
520
521  local($command) = @_;
522  local(%save_sig,$rc);
523  %save_sig = %SIG;
524  grep($_ = 'DEFAULT', %SIG);
525  $rc = system("$command 2>&1") >> 8;
526  %SIG = %save_sig;
527  return($rc);
528
529}  #---------- end of do_system routine ---------------------------------
530
531
532#----------------------------------------------------------------------------
533#
534# display_help
535#
536# Display help information
537#
538#----------------------------------------------------------------------------
539
540sub display_help {
541
542  $usageMsg="
543Usage:
544
545  mmdsh [flags/parms] command
546
547Flags/parms:
548
549  -f number     fanout number to use for concurrent execution
550  -F filename   nodelist file listing nodes on which to execute command
551  -I filename   file to be staged on the remote node prior to executing
552                  command; the file must be in /var/mmfs/tmp
553  -k            remove the file specified with the -I flag before exiting
554  -L n1,n2,n3   comma-separated list of nodes on which to execute command
555  -N nodespec   node specificaton that consists of a node list, a node file,
556                  or a node class on which to execute the command
557  -s            suppress the prepending of the hostname string to each line
558                  of output generated by running the command on the remote node
559  -v            verify that nodes are reachable before adding them to the
560                  set of nodes on which to run the command
561  -i            display the set of nodes on which the command will run
562                  before command execution
563";
564  print STDERR "$usageMsg\n";
565
566}  #---------- end of display_help routine -----------------------------
567
568
569#----------------------------------------------------------------------------
570#
571# add_wc
572#
573# Add a host to the working collective.
574#
575# Input parameter is the hostname to be added.
576#
577# Don't add a hostname if it is already in the working collective.
578#
579#----------------------------------------------------------------------------
580
581sub add_wc {
582
583   local($host) = @_;
584   local($hostname);
585
586   $host =~ s/\s//g;
587
588   # If the host is already in the working collective, return to the caller
589   #  without adding it to the collective again (avoid redundancies).
590   foreach $hostname (@wc) {
591      return if $hostname eq $host;
592   }
593
594   # If we made it this far, add the host to the working collective.
595   push(@wc,$host);
596
597}    #---------- end of add_wc routine ------------------------------------
598
599
600#----------------------------------------------------------------------------
601#
602# add_wc_parallel
603#
604# Add hosts to the working collective in parallel via fork.
605#
606# Input parameter is the opened working collective file handle.
607#
608# If -v was specified, only add a host to the working collective
609# if it is reachable via ping, or if the user said to add it
610# even though it is not reachable.
611#
612# Don't add a hostname if it is already in the working collective.
613#
614#----------------------------------------------------------------------------
615
616sub add_wc_parallel {
617
618  local($filehandle) = @_;
619  local(@hosts_seen, $child_host);
620
621  # Unlink the temporary file here.  Otherwise, it may be
622  # left behind if we run into a problem and exit early.
623  if ($hlfile) {
624    `/bin/rm -f $hlfile`;
625  }
626
627  if ($fanout) {      # set fanval from user value, environment, or default
628    $fanval = $fanout;
629  } else {
630    unless ($fanval = $ENV{'FANOUT'}) {
631      $fanval = $fanout_default;
632    }
633  }
634
635  HOST: while ($host = <$filehandle>) {
636
637    $host =~ /^\s*#/ && next;
638    $host =~ /^\s*$/ && next;
639    $host =~ /;/ && next;
640    $host =~ /\S+\s+\S+/ && next;
641    chop($host);
642
643    # Remove any leading or trailing whitespace.
644    ($host, $remainder) = split(" ", $host);
645
646    # Prevent duplicates in the working collective.  Duplicates are culled
647    # here to prevent the key collisions in the %pid hashtable, which can
648    # cause unexpected behavior.  For example, defined, but empty hostnames
649    # being added to the working collective, leading to "Unknown host"
650    # messages from the remote shell during execution attempts.
651    foreach (@hosts_seen) {
652      if ($_ eq $host) {
653        next HOST;
654      }
655    }
656    push(@hosts_seen, $host);
657
658    # Create filehandles for pipe ends.
659    $Rout{$host} = "READ_STDOUT__" . $host;
660    $Wout{$host} = "WRITE_STDOUT__" . $host;
661    $Rerr{$host} = "READ_STDERR__" . $host;
662    $Werr{$host} = "WRITE_STDERR__" . $host;
663
664    # Open pipes for this host's stdout and stderr from ping.
665    if (pipe($Rout{$host}, $Wout{$host}) < 0) {
666      #die "mmdsh: Couldn't pipe: $!\n";
667      &prtMsg(368, "\'$!\'");
668      exit(-1);
669    }
670    if (pipe($Rerr{$host}, $Werr{$host}) < 0) {
671      #die "mmdsh: Couldn't pipe: $!\n";
672      &prtMsg(368, "\'$!\'");
673      exit(-1);
674    }
675
676    # Fork a child to execute the ping to the current host to be added.
677    ADDFORK: {
678
679      if ($pid{$host} = fork) {
680
681        # parent code -
682        # close unneeded ends of pipes
683        # parent will wait for child processes if fanout limit reached
684
685        close($Wout{$host});
686        close($Werr{$host});
687
688        if (++$cur_fanout >= $fanval) {
689          &wait_for_kids_and_add_hosts(false);
690        }
691
692      } elsif (defined $pid{$host}) {
693
694        # child code -
695        # close unneeded ends of pipes
696        # redirect stdout and stderr to output pipes
697        # exec the ping command
698        # stdout/stderr will go to pipes to be read by parent
699
700        close($Rout{$host});
701        close($Rerr{$host});
702
703        # If the -v flag was specified, check whether the host can be ping'd.
704        # If the 1st ping fails, try a few more times to avoid false node down.
705        if ($verify) {
706          $pingCount = 0;
707          $pingRc = 1;
708          while ($pingRc != 0 && ++$pingCount <= $maxPingCount) {
709            `$ping -w $pingTimeout -c 1 $host 2>/dev/null`;
710            $pingRc = $? >> 8;
711            if ($pingRc > 0 && $iflag) {
712              printf STDERR "ping %s failed (%s); pingRc=%s\n", $host, $pingCount, $pingRc;
713            }
714            ++$pingTimeout;
715          }
716        }
717
718        unless (open(STDOUT, ">&$Wout{$host}")) {
719          #die "mmdsh: Cannot redirect STDOUT: $!\n";
720          &prtMsg(369, 'STDOUT', "\'$!\'");
721          exit(-1);
722        }
723        unless (open(STDERR, ">&$Werr{$host}")) {
724          #die "mmdsh: Cannot redirect STDERR: $!\n";
725          &prtMsg(369, 'STDERR', "\'$!\'");
726          exit(-1);
727        }
728
729        select(STDOUT); $| = 1;
730        select(STDERR); $| = 1;
731
732        exit($pingRc);
733
734      } else {
735
736        # Try again.  The fork must have failed due to a resource problem.
737
738        sleep 5;
739        redo ADDFORK;
740      }
741    }
742  }
743
744  # Parent continues here after forking all the children for this command.
745  # Get the results of any remaining ping's (the number of hosts in the
746  # working collective may not be a multiple of the fanout value).
747  # Get rid of any hosts that have failed, reporting all removed hosts as
748  # directed by the [-R reportfile] and [-r] flags.
749  &wait_for_kids_and_add_hosts(true);
750  if ($reportFile && ($#goners >= 0)) {
751    # Write removed hosts to specified file.  The file is not created if
752    # the goners array is empty (i.e., $#goners = -1).
753    open(REPORTFILE, ">$reportFile");
754    foreach $child_host (@goners) {
755      print REPORTFILE "$child_host\n";
756    }
757    close(REPORTFILE);
758  }
759  if ($rflag) {
760    # Write removed hosts to standard error.
761    foreach $child_host (@goners) {
762      print STDERR "$child_host\n";
763    }
764  }
765  &delete_hosts;
766  unless ($done) {
767    $cur_fanout = 0;
768    &check_wc;
769  }
770
771}  #---------- end of add_wc_parallel routine ----------------------------
772
773
774#----------------------------------------------------------------------------
775#
776# parse
777#
778# Parse the command line.
779#
780#----------------------------------------------------------------------------
781
782sub parse {
783
784   local(@indices,@temp,$Findex,$findex,$Iindex,$Lindex,$Nindex);
785   local($fn,$wfile,$host,@hostlist,$ht,$hl,$Narg,$nodesToUse);
786
787   if ($ARGV[0] eq "-\?") {
788      &display_help;
789      exit;
790   }
791
792   while ($ARGV[0] =~ /^-/) {    # while current parm starts with "-"
793
794      if ($ARGV[0] =~ /[FfILNR](\S+)/) { # if flag is one of F, f, I, L, N, or R
795                                         # and there is no whitespace
796                                         # immediately after the flag
797         $Findex = index($ARGV[0],"F");
798         $findex = index($ARGV[0],"f");
799         $Iindex = index($ARGV[0],"I");
800         $Lindex = index($ARGV[0],"L");
801         $Nindex = index($ARGV[0],"N");
802         $Rindex = index($ARGV[0],"R");
803         @indices = ($Findex, $findex, $Iindex, $Lindex, $Nindex, $Rindex);
804
805         @indices = sort @indices;
806         @temp = @indices;
807         foreach (@temp) {            # loop moves parm pos value to indices[0]
808            $_ == -1 && shift(@indices);
809         }
810
811         if ($indices[0] == $Findex) {  # if the flag was "F"
812            if (!$fn) {
813               $fn = $1;
814               unless ($wfile = $fn) {
815                  #die "mmdsh: Missing argument\n";
816                  &prtMsg(204, "-F");
817                  &display_help;
818                  exit(-1);
819               }
820            } else {
821               #die "mmdsh:  F flag specified twice.\n";
822               &prtMsg(36, "F");
823               &display_help;
824               exit(-1);
825            }
826
827         } elsif ($indices[0] == $findex) {  # if the flag was "f"
828            if (!$fanout) {
829               $fanout = $1;
830               if ($fanout =~ /\D/) {
831                  #die "mmdsh: Incorrect argument - $fanout\n";
832                  &prtMsg(40, "-f", $fanout);
833                  &display_help;
834                  exit(-1);
835               }
836            } else {
837               #die "mmdsh:  f flag specified twice.\n";
838               &prtMsg(36, "f");
839               &display_help;
840               exit(-1);
841            }
842
843         } elsif ($indices[0] == $Iindex) {  # if the flag was "I"
844            if (!$stageFile) {
845               $stageFile = $1;
846               unless ($fileToCopy = $stageFile) {
847                  #die "mmdsh: Missing argument\n";
848                  &prtMsg(204, "-I");
849                  &display_help;
850                  exit(-1);
851               }
852            } elsif (!$stageFile2) {
853               $stageFile2 = $1;
854               unless ($fileToCopy2 = $stageFile2) {
855                  #die "mmdsh: Missing argument\n";
856                  &prtMsg(204, "-I");
857                  &display_help;
858                  exit(-1);
859               }
860            } else {
861               #die "mmdsh:  I flag specified twice.\n";
862               &prtMsg(36, "I");
863               &display_help;
864               exit(-1);
865            }
866
867         } elsif ($indices[0] == $Lindex) {  # if the flag was "L"
868            if (!$hl) {
869               $hl = $1;
870               if ($hl =~ /^,|,,|,$/) {
871                  #die "Incorrect argument - $hl\n";
872                  &prtMsg(153, "-L");
873                  &display_help;
874                  exit(-1);
875               }
876               if ($hl eq "-") {
877                  # Get names from stdin
878                  while (<STDIN>) {
879                     /^\s*#/ && next;
880                     /^\s*$/ && next;
881                     /;/ && next;
882                     /\S+ \S+/ && next;
883                     s/ //g;
884                     chop;
885                     push(@hostlist,$_);
886                  }
887               } else {
888                  @hostlist = split(/,/,$hl);
889               }
890            } else {
891               #die "mmdsh:  L flag specified twice.\n";
892               &prtMsg(36, "L");
893               &display_help;
894               exit(-1);
895            }
896
897         } elsif ($indices[0] == $Nindex) {  # if the flag was "N"
898            if (!$Narg) {
899               $Narg = $1;
900               unless ($nodesToUse = $Narg) {
901                  #die "mmdsh: Missing argument\n";
902                  &prtMsg(204, "-N");
903                  &display_help;
904                  exit(-1);
905               }
906            } else {
907               #die "mmdsh:  N flag specified twice.\n";
908               &prtMsg(36, "N");
909               &display_help;
910               exit(-1);
911            }
912
913         } elsif ($indices[0] == $Rindex) {  # if the flag was "R"
914            if (!$reportFile) {
915               unless ($reportFile = $1) {
916                  #die "mmdsh: Missing argument\n";
917                  &prtMsg(204, "-R");
918                  &display_help;
919                  exit(-1);
920               }
921            } else {
922               #die "mmdsh:  R flag specified twice.\n";
923               &prtMsg(36, "R");
924               &display_help;
925               exit(-1);
926            }
927         }
928         $ARGV[0] = substr($ARGV[0], 0, $indices[0] + 1);
929
930      } elsif ($ARGV[0] =~ /F$/) {  # otherwise, if the flag is an "F"
931                                    #  followed by whitespace
932         if (!$fn) {
933            $fn = $ARGV[1];
934            unless ($wfile = $fn) {
935               #die "mmdsh: Missing argument\n";
936               &prtMsg(204, "-F");
937               &display_help;
938               exit(-1);
939            }
940            $shiftflag++;
941         } else {
942            #die "mmdsh:  F flag specified twice.\n";
943            &prtMsg(36, "F");
944            &display_help;
945            exit(-1);
946         }
947
948      } elsif ($ARGV[0] =~ /f$/) {  # otherwise, if the flag is an "f"
949                                    #  followed by whitespace
950         if (!$fanout) {
951            $fanout = $ARGV[1];
952            unless ($fanout) {
953               #die "mmdsh: Missing argument\n";
954               &prtMsg(204, "-f");
955               &display_help;
956               exit(-1);
957            }
958            if ($fanout =~ /\D/) {
959               #die "mmdsh: Incorrect argument - $fanout\n";
960               &prtMsg(40, "-f", $fanout);
961               &display_help;
962               exit(-1);
963            }
964            $shiftflag++;
965         } else {
966            #die "mmdsh:  f flag specified twice.\n";
967            &prtMsg(36, "f");
968            &display_help;
969            exit(-1);
970         }
971
972      } elsif ($ARGV[0] =~ /I$/) {  # otherwise, if the flag is an "I"
973                                    #  followed by whitespace
974         if (!$stageFile) {
975            $stageFile = $ARGV[1];
976            unless ($fileToCopy = $stageFile) {
977               #die "mmdsh: Missing argument\n";
978               &prtMsg(204, "-I");
979               &display_help;
980               exit(-1);
981            }
982            $shiftflag++;
983         } elsif (!$stageFile2) {
984            $stageFile2 = $ARGV[1];
985            unless ($fileToCopy2 = $stageFile2) {
986               #die "mmdsh: Missing argument\n";
987               &prtMsg(204, "-I");
988               &display_help;
989               exit(-1);
990            }
991            $shiftflag++;
992         } else {
993            #die "mmdsh:  I flag specified twice.\n";
994            &prtMsg(36, "I");
995            &display_help;
996            exit(-1);
997         }
998
999      } elsif ($ARGV[0] =~ /L$/) {  # otherwise, if the flag is an "L"
1000                                    #  followed by whitespace
1001         if (!$hl) {
1002            $hl = $ARGV[1];
1003            unless ($hl) {
1004              #die "mmdsh: Missing argument\n";
1005               &prtMsg(204, "-L");
1006               &display_help;
1007               exit(-1);
1008            }
1009            if ($hl =~ /^,|,,|,$/) {
1010               #die "Incorrect argument - $hl\n";
1011               &prtMsg(153, "-L");
1012               &display_help;
1013               exit(-1);
1014            }
1015            if ($hl eq "-") {
1016               while (<STDIN>) {
1017                  /^\s*#/ && next;
1018                  /^\s*$/ && next;
1019                  /;/ && next;
1020                  /\S+ \S+/ && next;
1021                  s/ //g;
1022                  chop;
1023                  push(@hostlist,$_);
1024               }
1025            } else {
1026               @hostlist = split(/,/,$hl);
1027            }
1028            $shiftflag++;
1029         } else {
1030            #die "mmdsh:  L flag specified twice.\n";
1031            &prtMsg(36, "L");
1032            &display_help;
1033            exit(-1);
1034         }
1035
1036      } elsif ($ARGV[0] =~ /N$/) {  # otherwise, if the flag is an "N"
1037                                    #  followed by whitespace
1038         if (!$Narg) {
1039            $Narg = $ARGV[1];
1040            unless ($nodesToUse = $Narg) {
1041               #die "mmdsh: Missing argument\n";
1042               &prtMsg(204, "-N");
1043               &display_help;
1044               exit(-1);
1045            }
1046            $shiftflag++;
1047         } else {
1048            #die "mmdsh:  N flag specified twice.\n";
1049            &prtMsg(36, "N");
1050            &display_help;
1051            exit(-1);
1052         }
1053
1054      } elsif ($ARGV[0] =~ /R$/) {  # otherwise, if the flag is an "R"
1055                                    #  followed by whitespace
1056         if (!$reportFile) {
1057            unless ($reportFile = $ARGV[1]) {
1058              #die "mmdsh: Missing argument\n";
1059              &prtMsg(204, "-R");
1060              &display_help;
1061              exit(-1);
1062            }
1063            $shiftflag++;
1064         } else {
1065            #die "mmdsh:  R flag specified twice.\n";
1066            &prtMsg(36, "R");
1067            &display_help;
1068            exit(-1);
1069         }
1070      }  # end of if ($ARGV[0] =~ /[FfILNR](\S+)/)
1071
1072      if (index($ARGV[0], 'i') >= $[) {   # if there is an "i" flag present
1073         if (rindex($ARGV[0], 'i') == index($ARGV[0], 'i')) {
1074            if ($iflag++) {
1075               #die "mmdsh:  i flag specified twice.\n";
1076               &prtMsg(36, "i");
1077               &display_help;
1078               exit(-1);
1079            }
1080         } else {
1081            #die "mmdsh:  i flag specified twice.\n";
1082            &prtMsg(36, "i");
1083            &display_help;
1084            exit(-1);
1085         }
1086      }
1087
1088      if (index($ARGV[0], 'k') >= $[) {   # if there is a "k" flag present
1089         if (rindex($ARGV[0], 'k') == index($ARGV[0], 'k')) {
1090            if ($kflag++) {
1091               #die "mmdsh:  k flag specified twice.\n";
1092               &prtMsg(36, "k");
1093               &display_help;
1094               exit(-1);
1095            }
1096         } else {
1097            #die "mmdsh:  k flag specified twice.\n";
1098            &prtMsg(36, "k");
1099            &display_help;
1100            exit(-1);
1101         }
1102      }
1103
1104      if (index($ARGV[0], 'r') >= $[) {   # if there is an "r" flag present
1105         if (rindex($ARGV[0], 'r') == index($ARGV[0], 'r')) {
1106            if ($rflag++) {
1107               #die "mmdsh:  r flag specified twice.\n";
1108               &prtMsg(36, "r");
1109               &display_help;
1110               exit(-1);
1111            }
1112         } else {
1113            #die "mmdsh:  r flag specified twice.\n";
1114            &prtMsg(36, "r");
1115            &display_help;
1116            exit(-1);
1117         }
1118      }
1119
1120      if (index($ARGV[0], 's') >= $[) {   # if there is an "s" flag present
1121         if (rindex($ARGV[0], 's') == index($ARGV[0], 's')) {
1122            if ($sflag++) {
1123               #die "mmdsh:  s flag specified twice.\n";
1124               &prtMsg(36, "s");
1125               &display_help;
1126               exit(-1);
1127            }
1128         } else {
1129            #die "mmdsh:  s flag specified twice.\n";
1130            &prtMsg(36, "s");
1131            &display_help;
1132            exit(-1);
1133         }
1134      }
1135
1136      if (index($ARGV[0], 'v') >= $[) {   # if there is a "v" flag present
1137         if (rindex($ARGV[0], 'v') == index($ARGV[0], 'v')) {
1138            if ($verify++) {
1139               #die "mmdsh:  v flag specified twice.\n";
1140               &prtMsg(36, "v");
1141               &display_help;
1142               exit(-1);
1143            }
1144         } else {
1145            #die "mmdsh:  v flag specified twice.\n";
1146            &prtMsg(36, "v");
1147            &display_help;
1148            exit(-1);
1149         }
1150      }
1151
1152      if ($ARGV[0] =~ /^-.*([^fFiIkLNRrsv]).*/) {  # fail if any unknown flags
1153         #die "mmdsh: Incorrect option - $1\n";
1154         &prtMsg(13, $1);
1155         &display_help;
1156         exit(-1);
1157      }
1158
1159      if ($ARGV[0] =~ /^-$/) {                   # fail if no flag after "-"
1160         #die "mmdsh: Missing option\n";
1161         &prtMsg(168);
1162         &display_help;
1163         exit(-1);
1164      }
1165
1166      shift(@ARGV);      # shift to next parameter
1167      if ($shiftflag) {
1168         shift(@ARGV);   # shift again if parameter pertains to previous flag
1169         $shiftflag--;   # reset the shift flag
1170      }
1171   }
1172
1173   # Process any hosts that were specified via the -L flag.
1174   if (@hostlist) {
1175      $wfound++;
1176      # If the -v flag was specified, call routine to
1177      # only add pingable hosts; otherwise, add them all.
1178      if ($verify) {
1179         # Put the hosts specified by the "L" flag into a temporary file.
1180         $hlfile = "/var/mmfs/tmp/hostlistFile.mmdsh.$$";
1181         open(HLFILE, ">$hlfile");
1182         foreach $ht (@hostlist) {
1183            print HLFILE "$ht\n";
1184            $hostCount++;
1185            $targetHost = $ht;
1186         }
1187         close(HLFILE);
1188
1189         # Pass the created file to routine that only adds pingable hosts.
1190         # The temp file is cleaned up inside the add_wc_parallel routine.
1191         unless (open(HLFILE, "$hlfile")) {
1192            #die "mmdsh: Cannot open hostlist file $hlfile: $!\n";
1193            &prtMsg(364, $hlfile, "\'$!\'");
1194            `/bin/rm -f $hlfile`;
1195            exit(-1);
1196         }
1197         &add_wc_parallel(HLFILE);
1198         close(HLFILE);
1199         `/bin/rm -f $hlfile`;
1200
1201      } else {
1202         foreach $ht (@hostlist) {  # add any hosts specified by the "L" flag
1203            &add_wc($ht);
1204            $hostCount++;
1205            $targetHost = $ht;
1206         }
1207      }
1208   }
1209
1210   # Process any hosts that were specified via the -F flag.
1211   if ($wfile) {
1212      $wfound++;
1213      unless (open(WCFILE, $wfile)) {
1214         #die "mmdsh: Cannot open working collective file $wfile: $!\n";
1215         &prtMsg(364, $wfile, "\'$!\'");
1216         exit(-1);
1217      }
1218      # If the -v flag was specified, call routine to
1219      # only add pingable hosts; otherwise, add them all.
1220      if ($verify) {
1221         &add_wc_parallel(WCFILE);
1222      } else {
1223         while ($new_host = <WCFILE>) {
1224            $new_host =~ /^\s*#/ && next;
1225            $new_host =~ /^\s*$/ && next;
1226            $new_host =~ /;/ && next;
1227            $new_host =~ /\S+\s+\S+/ && next;
1228            chop($new_host);
1229            &add_wc($new_host);
1230         }
1231      }
1232      close(WCFILE);
1233   }
1234
1235   # Process any nodes that were specified via the -N flag.
1236   if ($nodesToUse) {
1237      # Convert the node specification into a verified file of nodes.
1238      $hlfile = "/var/mmfs/tmp/hostlistFile.mmdsh.$$";
1239      $cmd = "$MMCOMMON run createVerifiedNodefile $nodesToUse $REL_HOSTNAME_Field no $hlfile";
1240      if ($rc = system($cmd))
1241      {
1242         $rc = &normalizeRC($rc);
1243         &prtMsg(206, $rc);
1244         `/bin/rm -f $hlfile`;
1245         exit(-1);
1246      }
1247
1248      # Process the nodes in the file that was created.
1249      if ($hlfile) {
1250         $wfound++;
1251         unless (open(HLFILE, $hlfile)) {
1252            #die "mmdsh: Cannot open node file $hlfile: $!\n";
1253            &prtMsg(364, $hlfile, "\'$!\'");
1254            exit(-1);
1255         }
1256         # If the -v flag was specified, call routine to
1257         # only add pingable hosts; otherwise, add them all.
1258         if ($verify) {
1259            &add_wc_parallel(HLFILE);
1260         } else {
1261            while ($new_host = <HLFILE>) {
1262               $new_host =~ /^\s*#/ && next;
1263               $new_host =~ /^\s*$/ && next;
1264               $new_host =~ /;/ && next;
1265               $new_host =~ /\S+\s+\S+/ && next;
1266               chop($new_host);
1267               &add_wc($new_host);
1268            }
1269         }
1270         close(HLFILE);
1271         `/bin/rm -f $hlfile`;
1272      }
1273   }
1274
1275   # Verify the files to be staged exist and are in a subdir of /var/mmfs.
1276   if ($stageFile) {
1277     unless ( $fileToCopy =~ /^\/var\/mmfs\//) {
1278       die "mmdsh: Files may only be copied to /var/mmfs.\n";
1279     }
1280     unless ( -f $fileToCopy ) {
1281       #die "mmdsh: File $fileToCopy not found\n";
1282       &prtMsg(364, $fileToCopy, "\'$!\'");
1283       exit(-1);
1284     }
1285   }
1286
1287   if ($stageFile2) {
1288     unless ( $fileToCopy2 =~ /^\/var\/mmfs\//) {
1289       die "mmdsh: Files may only be copied to /var/mmfs.\n";
1290     }
1291     unless ( -f $fileToCopy2 ) {
1292       #die "mmdsh: File $fileToCopy2 not found\n";
1293       &prtMsg(364, $fileToCopy2, "\'$!\'");
1294       exit(-1);
1295     }
1296   }
1297
1298}  #---------- end of parse routine ------------------------------------
1299
1300
1301#----------------------------------------------------------------------------
1302#
1303# set_defaults
1304#
1305# Set default values for those values that have not been specified.
1306# If fanout was not specified, it is set to the default fanout value.
1307# If login name was not specified, it is set to that of the current user.
1308#
1309#----------------------------------------------------------------------------
1310
1311sub set_defaults {
1312
1313  unless ($fanout) {
1314    unless ($fanout = $ENV{'FANOUT'}) {
1315      $fanout = $fanout_default;
1316    }
1317  }
1318  unless ($login) {
1319    $login = (getpwuid($<))[0];
1320  }
1321
1322}  #---------- end of set_defaults routine -----------------------------
1323
1324
1325#----------------------------------------------------------------------------
1326#
1327# readem
1328#
1329# Read the stdout and stderr pipes for all hosts in the current fanout.
1330#
1331#----------------------------------------------------------------------------
1332
1333sub readem {
1334
1335  local(@h) = @_;
1336  local($host);
1337  foreach $host (@h) {
1338    &read_pipes_and_wait(true, $host);
1339  }
1340  @h = ();
1341
1342}  #---------- end of readem routine ---------------------------------
1343
1344
1345#----------------------------------------------------------------------------
1346#
1347# display_wc
1348#
1349# If requested by means of the -i option, display the set of nodes
1350# on which the command will be run.
1351#
1352#----------------------------------------------------------------------------
1353
1354sub display_wc {
1355
1356  local($i);
1357  if ($iflag) {
1358    #print STDOUT "Nodes on which the command will be run:\n";
1359    &prtMsg(362);
1360    $i = 0;
1361    while ($i <= $#wc) {
1362      printf STDERR "%-19.18s", $wc[$i];
1363      printf STDERR "%-19.18s", $wc[$i+1];
1364      printf STDERR "%-19.18s", $wc[$i+2];
1365      printf STDERR "%-19.18s\n", $wc[$i+3];
1366      $i = $i + 4;
1367    }
1368  }
1369
1370}  #---------- end of display_wc routine -------------------------------
1371
1372
1373#----------------------------------------------------------------------------
1374#
1375# get_wc
1376#
1377# Determine the working collective, if not already obtained from command line.
1378# Look for filename in $WCOLL containing the hostnames, one per line.
1379#
1380#----------------------------------------------------------------------------
1381
1382sub get_wc {
1383
1384  local($wfile,$new_host);
1385  if (!@wc && !$wfound) {
1386    unless ($wfile = $ENV{'WCOLL'}) {
1387      #die "mmdsh: Working collective environment variable not set\n";
1388      &prtMsg(363);
1389      exit(-1);
1390    }
1391    unless (open(WCFILE, $wfile)) {
1392      #die "mmdsh: Cannot open working collective file $wfile: $!\n";
1393      &prtMsg(364, $wfile, "\'$!\'");
1394      exit(-1);
1395    }
1396    if ($verify) {
1397      &add_wc_parallel(WCFILE);
1398    } else {
1399      while ($new_host = <WCFILE>) {
1400        $new_host =~ /^\s*#/ && next;
1401        $new_host =~ /^\s*$/ && next;
1402        $new_host =~ /;/ && next;
1403        $new_host =~ /\S+\s+\S+/ && next;
1404        chop($new_host);
1405        &add_wc($new_host);
1406      }
1407    }
1408    close(WCFILE);
1409  }
1410
1411}  #---------- end of get_wc routine -----------------------------------
1412
1413
1414#----------------------------------------------------------------------------
1415#
1416# set_signals
1417#
1418# HUP is ignored in the mmdsh parent and its exec'ed rsh children.
1419#
1420# STOP, CONT, and TSTP are defaulted - this means that they work on the
1421# parent, but are ignored (not propagated to) the exec'ed rsh children
1422# or the remote processes.
1423#
1424# Set the signal handler for all other signals.
1425# The signals will be propagated to the exec'd children and then
1426# the default action will be taken in the parent.
1427#
1428# rsh will propagate TERM, QUIT, and INT to the remote processes.
1429#
1430#----------------------------------------------------------------------------
1431
1432sub set_signals {
1433
1434  # Default STOP, CONT, TSTP signal handling
1435  $SIG{'STOP'} = 'DEFAULT';
1436  $SIG{'CONT'} = 'DEFAULT';
1437  $SIG{'TSTP'} = 'DEFAULT';
1438
1439  # Propagate signals to forked kids.
1440  $SIG{'TERM'} = 'infanticide';
1441  $SIG{'QUIT'} = 'infanticide';
1442  $SIG{'INT'}  = 'infanticide';
1443  $SIG{'ABRT'} = 'infanticide';
1444  $SIG{'ALRM'} = 'infanticide';
1445  $SIG{'FPE'}  = 'infanticide';
1446  $SIG{'ILL'}  = 'infanticide';
1447  $SIG{'PIPE'} = 'infanticide';
1448  $SIG{'SEGV'} = 'infanticide';
1449  $SIG{'USR1'} = 'infanticide';
1450  $SIG{'USR2'} = 'infanticide';
1451  $SIG{'TTIN'} = 'infanticide';
1452  $SIG{'TTOU'} = 'infanticide';
1453  $SIG{'BUS'}  = 'infanticide';
1454
1455}  #---------- end of set_signals routine -----------------------------
1456
1457
1458#----------------------------------------------------------------------------
1459#
1460# wait_for_kids_and_add_hosts
1461#
1462# When a child dies, it must be an exit after the end of his ping.
1463# If a negative return code, the ping failed.  Check whether we
1464# should give up on this host and eliminate him from the collective.
1465# Add the kid's host to the working collective if the ping succeeded
1466# or if we were told to add him anyway.
1467#
1468# Parameters:
1469#   wait_all -- true to "wait" on all active children, false to only "wait"
1470#               for completed children (fanout maintenance).
1471#
1472#----------------------------------------------------------------------------
1473
1474sub wait_for_kids_and_add_hosts {
1475
1476  my $wait_all = shift;
1477
1478  local($child_pid, $child_rc, $child_host, $host_found, $wait_opt);
1479
1480  if ($wait_all eq true) {
1481    # Wait for all children ==> Blocking waitpid
1482    $wait_opt = 0;
1483  }
1484  else {
1485    # Wait for completed children only ==> Non-blocking waitpid
1486    $wait_opt = &WNOHANG;
1487  }
1488
1489  # Wait for any children and process accordingly.  We will wait for at
1490  # least one child for both "wait some" and "wait all" cases as the design
1491  # point is to only call this routine when the fanout limit has been reached.
1492  if (($child_pid = wait) != -1) {
1493
1494    PINGWAIT: {
1495
1496      # Obtain the return code and hostname for the child process.
1497      $child_rc = $? >> 8;
1498      foreach (keys %pid) {
1499        if ($pid{$_} == $child_pid) {
1500          $child_host = $_;
1501          last;
1502        }
1503      }
1504
1505      # Close the pipes used for collecting data from the ping.
1506      close($Rout{$child_host});
1507      close($Rerr{$child_host});
1508
1509      # If the ping failed, assume the host is not reachable;
1510      #  don't add it to the collective unless told to include it anyway.
1511      if ($child_rc != 0) {
1512        push(@goners,$child_host);
1513      }
1514      else {
1515        # Host reachable --> Add to collective (duplicates previously culled).
1516        push(@wc,$child_host);
1517      }
1518
1519      # Child harvested ==> Decrement cur_fanout
1520      $cur_fanout--;
1521
1522      # Waitpid for any child.  For "wait some" we will perform a non-blocking
1523      # child wait, terminating processing if none completed.  For "wait all"
1524      # we will perform a blocking wait, terminating processing if no children
1525      # remain.
1526
1527      $child_pid = waitpid(-1, $wait_opt);
1528      if ((($wait_all eq false) && ($child_pid > 0)) ||
1529          (($wait_all eq true) && ($child_pid != -1)))
1530      {
1531        redo PINGWAIT;
1532      }
1533    }
1534  }
1535
1536}  #---------- end of wait_for_kids_and_add_hosts routine --------------
1537
1538
1539#----------------------------------------------------------------------------
1540#
1541# delete_hosts
1542#
1543# Called if any hosts don't respond.
1544# Remove them from the working collective unless the -c flag was set.
1545# Input is the hostnames to remove from the working collective.
1546#
1547#----------------------------------------------------------------------------
1548
1549sub delete_hosts {
1550
1551  local($child_host,$h,$host_count);
1552
1553  foreach $child_host (@goners) {
1554    $host_count = 0;
1555    foreach $h (@wc) {
1556      if ($h eq $child_host) {
1557        splice(@wc, $host_count, 1);
1558      }
1559      $host_count++;
1560    }
1561  }
1562
1563}  #---------- end of delete_hosts routine -----------------------------
1564
1565
1566#----------------------------------------------------------------------------
1567#
1568# infanticide
1569#
1570# User has signaled the mmdsh parent - propagate TERM, INT, or QUIT to children.
1571# (Note - TERM, INT, and QUIT will be propagated to remote processes by rsh).
1572# Signal any children with SIGTERM if signal is not one of the above.
1573# Wait for children to manage output and prevent zombies.
1574# Signal self after setting default signal-handling for self.
1575# If still alive, exit.
1576# Input is the signal type.
1577#
1578#----------------------------------------------------------------------------
1579
1580sub infanticide {
1581
1582  local($sig) = @_;
1583  local($kid_sig);
1584  #print STDERR "mmdsh: Caught SIG$sig - terminating the kids\n";
1585  &prtMsg(366, $sig);
1586  if ($sig ne 'QUIT' && $sig ne 'INT' && $sig ne 'TERM') {
1587    $kid_sig = 'TERM';
1588    $SIG{'TERM'} = 'IGNORE';
1589  } else {
1590    $kid_sig = $sig;
1591  }
1592  $SIG{$sig} = 'DEFAULT';
1593  kill $kid_sig, (values %pid);
1594  &read_pipes_and_wait(true, @hs);
1595  kill $sig, $$;
1596  exit($rcode);
1597
1598}       #---------- end of infanticide routine ----------------------------
1599
1600
1601#----------------------------------------------------------------------------
1602#
1603# check_wc
1604#
1605# Check to ensure that there are hosts in our working collective.
1606#
1607#----------------------------------------------------------------------------
1608
1609sub check_wc {
1610
1611  if (!@wc) {
1612    if ($hostCount == 1) {
1613      #print STDERR "mmdsh:  Node $targetHost is not available to run the command.";
1614      &prtMsg(573, $targetHost);
1615      exit(80);   # MM_HostDown
1616    }
1617    else {
1618      #print STDERR "There are no available nodes on which to run the command."
1619      &prtMsg(367);
1620      exit(++$rcode);
1621    }
1622  }
1623
1624}     #---------- end of check_wc routine ---------------------------------
1625
1626
1627#----------------------------------------------------------------------------
1628#
1629# Mainline program
1630#
1631# The program continues while there are commands to distribute to the working
1632# collective via rsh.  Children are forked for each host, up to the fanout
1633# limit.  Stdout and stderr are piped back from the children to the parent
1634# who will display the results of the execed rsh's to the parent's stdout
1635# and stderr.
1636#
1637#----------------------------------------------------------------------------
1638
1639# Determine the execution environment and initialize variables.
1640chop($osName=`/bin/uname -s`);
1641if ($osName eq "AIX") {
1642   $grep="/usr/bin/grep";
1643   $ping="/usr/sbin/ping";
1644   $DSPMSG="/usr/bin/dspmsg -s 32 mmfs.cat";
1645}
1646elsif ($osName eq "Linux") {
1647   $grep="/bin/grep";
1648   $ping="/bin/ping";
1649}
1650else {
1651   print STDERR  "mmdsh:  Unknown execution environment $osName\n";
1652   exit(-1);
1653}
1654$pingTimeout  = 2;       # initial single ping timeout period
1655$maxPingCount = 3;       # number of pings before declaring a node dead
1656$fanout_default = 64;    # default fanout value
1657
1658# Flush output filesystem buffers after each write.
1659select(STDOUT); $| = 1;
1660select(STDERR); $| = 1;
1661
1662# Get the set environment locales.
1663use locale;
1664$lang=$ENV{'LANG'};
1665$lcall=$ENV{'LC_ALL'};
1666$lccol=$ENV{'LC_COLLATE'};
1667$lctyp=$ENV{'LC_TYPE'};
1668$lcmon=$ENV{'LC_MONETARY'};
1669$lcnum=$ENV{'LC_NUMERIC'};
1670$lctim=$ENV{'LC_TIME'};
1671$lcmsg=$ENV{'LC_MESSAGES'};
1672
1673$mmmode=$ENV{'MMMODE'};
1674$environmentType=$ENV{'environmentType'};
1675$rshPath=$ENV{'GPFS_rshPath'};
1676$rcpPath=$ENV{'GPFS_rcpPath'};
1677$mmTrace=$ENV{'mmScriptTrace'};
1678
1679if ($rshPath eq "" || $rshPath eq "_DEFAULT_") {
1680   $rshPath="/usr/bin/rsh";
1681}
1682elsif ( ! -x $rshPath) {
1683#esjlrm - fix the above to strip any options
1684   #die "mmdsh: Invalid or missing remote shell command: $rshPath\n";
1685   &prtMsg(422, $rshPath);
1686   exit(-1);
1687}
1688
1689if ($rcpPath eq "" || $rcpPath eq "_DEFAULT_") {
1690   $rcpPath="/usr/bin/rcp";
1691}
1692elsif ( ! -x $rcpPath) {
1693#esjlrm - fix the above to strip any options
1694   #die "mmdsh: Invalid or missing remote file copy command: $rcpPath\n";
1695   &prtMsg(423, $rcpPath);
1696   exit(-1);
1697}
1698
1699$exportcmd = "export LANG=$lang; export LC_ALL=$lcall;  export LC_COLLATE=$lccol; export LC_TYPE=$lctyp; export LC_MONETARY=$lcmon; export LC_NUMERIC=$lcnum; export LC_TIME=$lctim; export LC_MESSAGES=$lcmsg; export MMMODE=$mmmode; export environmentType=$environmentType;  export GPFS_rshPath=$rshPath; export GPFS_rcpPath=$rcpPath; export mmScriptTrace=$mmTrace; ";
1700
1701
1702# Set signal handling for forked (child) processes,
1703#  parse the command line, and determine our working collective.
1704&set_signals;
1705&parse;
1706&get_wc;
1707&display_wc;
1708&set_defaults;
1709&check_wc;
1710
1711# Perform each command on the nodes in our working collective.
1712while ($command = &get_command) {
1713
1714   if ($iflag) {
1715     print STDERR "mmdsh: Command to run: $command\n\n";
1716   }
1717
1718   # If the special command _SELECT_FROM_FILE_ is specified,
1719   # the command to be executed by each of the nodes is given
1720   # in the file specified in the mmdshCommandsFile variable.
1721   if ($command eq "_SELECT_FROM_FILE_") {
1722     $selectCommandFromFile++;
1723     unless ($mmdshCommandsFile = $ENV{'mmdshCommandsFile'}) {
1724       die "mmdsh: mmdshCommandsFile environment variable not set\n";
1725     }
1726     unless ( -f $mmdshCommandsFile ) {
1727       #die "mmdsh: File $mmdshCommandsFile not found\n";
1728       &prtMsg(364, $mmdshCommandsFile, "\'$!\'");
1729       exit(-1);
1730     }
1731   }
1732
1733   # Clear out the host array and pid hash (also used in host verification)
1734   undef @hs;
1735   undef %pid;
1736
1737   # The working collective, @wc, at this point is guaranteed to contain only
1738   # unique entries.  This prevents key collisions on the %pid hash table.
1739   foreach $host (@wc) {
1740
1741      # If the special command _SELECT_FROM_FILE_ is specified,
1742      # retrieve the command string to be executed by the node.
1743
1744      if ($selectCommandFromFile) {
1745        $command = `$grep -w ^$host $mmdshCommandsFile`;
1746        chop($command);
1747
1748        # If there is no command for this host, move on.
1749        unless ($command) {
1750          if ($iflag) {
1751            print STDERR "mmdsh: $host: No command found.\n";
1752          }
1753          next;
1754        }
1755
1756        # Remove the hostname from the command string.
1757        @dsh_ARGV = split(' ',$command);
1758        shift(@dsh_ARGV);
1759        $command = join(' ',@dsh_ARGV);
1760
1761        if ($iflag) {
1762          print STDERR "mmdsh: $host: $command \n";
1763        }
1764      }
1765
1766
1767      # Create filehandles for pipe ends.
1768      $r_out{$host} = "READ_STDOUT_" . $host;
1769      $w_out{$host} = "WRITE_STDOUT_" . $host;
1770      $r_err{$host} = "READ_STDERR_" . $host;
1771      $w_err{$host} = "WRITE_STDERR_" . $host;
1772
1773      # Open pipes for this host's stdout and stderr from rsh.
1774      if (pipe($r_out{$host}, $w_out{$host}) < 0) {
1775         #die "mmdsh: Couldn't pipe: $!\n";
1776         &prtMsg(368, "\'$!\'");
1777         exit(-1);
1778      }
1779      if (pipe($r_err{$host}, $w_err{$host}) < 0) {
1780         #die "mmdsh: Couldn't pipe: $!\n";
1781         &prtMsg(368, "\'$!\'");
1782         exit(-1);
1783      }
1784
1785      # Fork a child to exec the rsh.
1786      MAINFORK: {
1787
1788         if ($pid{$host} = fork) {
1789
1790            # Parent code -
1791            # Close unneeded ends of pipes.
1792            # Parent will wait for child processes if fanout limit reached.
1793            close($w_out{$host});
1794            close($w_err{$host});
1795
1796            push(@hs,$host);
1797
1798            if (++$cur_fanout >= $fanout) {
1799               &read_pipes_and_wait(false, @hs);
1800            }
1801
1802         } elsif (defined $pid{$host}) {
1803
1804            # Child code -
1805            # Close unneeded ends of pipes.
1806            # Redirect stdout and stderr to output pipes.
1807            # Exec rsh.
1808            # stdout/stderr will go to pipes to be read by parent.
1809            close($r_out{$host});
1810            close($r_err{$host});
1811
1812            unless (open(STDOUT, ">&$w_out{$host}")) {
1813               #die "mmdsh: Cannot redirect STDOUT: $!\n";
1814               &prtMsg(369, 'STDOUT', "\'$!\'");
1815               exit(-1);
1816            }
1817            unless (open(STDERR, ">&$w_err{$host}")) {
1818               #die "mmdsh: Cannot redirect STDERR: $!\n";
1819               &prtMsg(369, 'STDERR', "\'$!\'");
1820               exit(-1);
1821            }
1822
1823            select(STDOUT); $| = 1;
1824            select(STDERR); $| = 1;
1825
1826            # If the -I option is specified, copy the specified files
1827            # to the remote host prior to invoking the actual command.
1828            if ($stageFile) {
1829              $rcpCommandString = "$rcpPath -p $fileToCopy $host:$fileToCopy";
1830              if ($iflag) {
1831                print STDERR "mmdsh: $rcpCommandString \n";
1832              }
1833              $rcpOutput = `LC_ALL=C $rcpCommandString 2>&1`;
1834              $rcpRc = $? >> 8;
1835
1836              # If the rcopy fails, move to the next host.
1837              if ($rcpRc > 0 && $rcpOutput !~ /refer to the same file/) {
1838                if ($rcpOutput) {
1839                  printf STDERR "%s", $rcpOutput;
1840                }
1841                &prtMsg(171, $rcpCommandString, $rcpRc);
1842                die "mmdsh: $command will not be executed on $host.\n";
1843              }
1844            }
1845
1846            if ($stageFile2) {
1847              $rcpCommandString = "$rcpPath -p $fileToCopy2 $host:$fileToCopy2";
1848              if ($iflag) {
1849                print STDERR "mmdsh: $rcpCommandString \n";
1850              }
1851              $rcpOutput = `LC_ALL=C $rcpCommandString 2>&1`;
1852              $rcpRc = $? >> 8;
1853
1854              # If the rcopy fails, move to the next host.
1855              if ($rcpRc > 0 && $rcpOutput !~ /refer to the same file/) {
1856                if ($rcpOutput) {
1857                  printf STDERR "%s", $rcpOutput;
1858                }
1859                &prtMsg(171, $rcpCommandString, $rcpRc);
1860                die "mmdsh: $command will not be executed on $host.\n";
1861              }
1862            }
1863
1864            # Execute the command on the remote host.
1865            if (!@dsh_ARGV) {
1866               # mmdsh was invoked without specifying a command.
1867               # The command to execute comes from the mmdsh prompt.
1868         if ($ENV{'DSHPATH'}) {
1869                  exec ($rshPath,
1870                        $host, '-n', '-l', $login,
1871                        "export PATH=$ENV{'DSHPATH'};",
1872                        $exportcmd, $command)                 ||
1873                  die "mmdsh: rsh: $host $command: $!\n";
1874         } else {
1875                  exec ($rshPath,
1876                        $host, '-n', '-l', $login,
1877                        $exportcmd, $command)                 ||
1878                  die "mmdsh: rsh: $host $command: $!\n";
1879               }
1880            } else {
1881               # The command to execute comes from the mmdsh command line.
1882               # This is the path taken by the mm commands.
1883         if ($ENV{'DSHPATH'}) {
1884                  exec ($rshPath,
1885                        $host, '-n', '-l', $login,
1886                        "export PATH=$ENV{'DSHPATH'};",
1887                        $exportcmd, @dsh_ARGV)                ||
1888                  die "mmdsh: rsh: $host @dsh_ARGV: $!\n";
1889         } else {
1890                  exec ($rshPath,
1891                        $host, '-n', '-l', $login,
1892                        $exportcmd, @dsh_ARGV)                ||
1893                  die "mmdsh: rsh: $host @dsh_ARGV: $!\n";
1894               }
1895      }
1896
1897         } else {
1898
1899           # Try again.  The fork must have failed due to a resource problem.
1900           if ($iflag) {
1901             print STDERR "mmdsh: fork() failed; will retry in 5 seconds.\n";
1902           }
1903           sleep 5;
1904           redo MAINFORK;
1905         }
1906      }
1907   }
1908
1909   # Parent continues here after forking all the children for this command.
1910   # Get the results of any remaining rsh's (the number of hosts in our
1911   #  working collective may not be a multiple of the fanout value).
1912   # Get rid of any hosts that have failed.
1913   # Display the working collective and command.
1914   &read_pipes_and_wait(true, @hs);
1915   &delete_hosts;
1916   unless ($done) {
1917      $cur_fanout = 0;
1918      &check_wc;
1919      &display_wc;
1920   }
1921}
1922
1923# If requested, delete the -I files.
1924if ($stageFile && $kflag) {
1925  `/bin/rm -f $fileToCopy $fileToCopy2 >/dev/null 2>&1`;
1926}
1927
1928# exit
1929
1930exit($rcode);       #---------- end of mainline program ----------------
1931
Note: See TracBrowser for help on using the repository browser.