#!/usr/bin/perl # @(#)55 1.1 src/43haes/usr/sbin/cluster/cspoc/dsh, powerha, 61aha_r721 10/27/16 08:31:51 # @(#)55 1.1 src/43haes/usr/sbin/cluster/cspoc/dsh, powerha, 61aha_r721 10/27/16 08:31:51 ##################################################################### # # # Module: dsh # # # #CPRY # # 5765-296 (C) Copyright IBM Corporation 1994 # # Licensed Materials - Property of IBM # # All rights reserved. # # US Government Users Restricted Rights - # # Use, duplication or disclosure restricted by # # GSA ADP Schedule Contract with IBM Corp. # #CPRY # # # #-------------------------------------------------------------------# # # # Description: Provides method for running commands on multiple # # nodes or network connected hosts in parallel. # # # # Inputs: # # -v : verify hosts before adding to collective # # -i : inform of working collective before command execution # # -q : list the working collective for command execution # # -l : specify login name to execute remote commands # # -h : display dsh command syntax # # -c : specify that commands will continue to be sent to # # hosts for which previous commands have failed # # -a : add all members of current SysPar to working collective # # -G : extend scope of -a to all members of SP System # # -w : add list of hostnames to working collective # # -f : fanout for concurrent execution # # # # Ouputs: # # Commands executed on hosts in working collective # # # # Syntax (example): # # dsh -w host1,host2 -vi ls # # # # External Ref: # # SDRGetObjects # # # # Internal Ref: None # # # ##################################################################### #----------------------------------------------------------------------------- # # do_msg # # Issue message # #----------------------------------------------------------------------------- sub do_msg { local($num,$arg1,$arg2,$arg3,$prog) = @_; local($MSGCAT,$SPMSG); $MSGCAT = "$msgdir/\?/cl_smsysman.cat"; $SPMSG = "dspmsg"; chop($prog = `/bin/basename $0`); if ($num == 1) { system "$SPMSG -s 2 $MSGCAT 10 'dsh: 5025-501 Msg EMSG501 not found. No alphanumeric characters, command not issued.\n' $prog"; } elsif ($num == 2) { system "$SPMSG -s 2 $MSGCAT 1 'Msg INFO501 not found. Usage: \t-v : verify hosts before adding to collective \t-i : verify collective before doing commands \t-l login_name : remote login name \t-f number : number to fanout for concurrent execution \t-q : list the working collective for command execution \t-h : display DSH command syntax \t-c : commands continue to be sent to hosts for which commands failed \t-a : add all members of current System Partition to working collective \t-G : extend scope of -a to all members of SP System \t-w host,host : add list of hostnames to working collective\n'"; } elsif ($num == 3) { system "$SPMSG -s 2 $MSGCAT 2 'Msg INFO502 not found. %1\$s not responding. Enter y if add to collective\n' $arg1 1>&2"; } elsif ($num == 4) { system "$SPMSG -s 2 $MSGCAT 3 'Msg INFO503 not found. Working collective file %1\$s:\n' $arg1"; } elsif ($num == 5) { system "$SPMSG -s 2 $MSGCAT 11 'dsh: 5025-502 Msg EMSG502 not found. Extraneous option - %1\$s\n' $arg1 $prog 1>&2"; } elsif ($num == 6) { system "$SPMSG -s 2 $MSGCAT 12 'dsh: 5025-503 Msg EMSG503 not found. Invalid argument - %1\$s\n' $arg1 $prog 1>&2"; } elsif ($num == 7) { system "$SPMSG -s 2 $MSGCAT 13 'dsh: 5025-504 Msg EMSG504 not found. Missing argument\n' $prog 1>&2"; } elsif ($num == 8) { system "$SPMSG -s 2 $MSGCAT 14 'dsh: 5025-505 Msg EMSG505 not found. Invalid option - %1\$s\n' $arg1 $prog 1>&2"; } elsif ($num == 9) { system "$SPMSG -s 2 $MSGCAT 15 'dsh: 5025-506 Msg EMSG506 not found. Missing option\n' $prog 1>&2"; } elsif ($num == 10) { system "$SPMSG -s 2 $MSGCAT 4 'Msg INFO504 not found. Working collective:\n' 1>&2"; } elsif ($num == 11) { system "$SPMSG -s 2 $MSGCAT 16 'dsh: 5025-507 Msg EMSG507 not found. Working collective environment variable not set\n' $prog 1>&2"; } elsif ($num == 12) { system "$SPMSG -s 2 $MSGCAT 17 'dsh: 5025-508 Msg EMSG508 not found. Cannot open working collective file %1\$s: %2\$s\n' $arg1 $arg2 $prog 1>&2"; } elsif ($num == 13) { system "dspmsg scripts.cat 9682 '%1\$s: cl_rsh had exit code = %2\$s, see cspoc.log and/or clcomd.log for more information\n' $arg1 $arg2 1>&2"; } elsif ($num == 14) { system "$SPMSG -s 2 $MSGCAT 19 'dsh: 5025-510 Msg EMSG510 not found. Caught SIG%1\$s - terminating the child processes\n' $arg1 $prog 1>&2"; } elsif ($num == 15) { system "$SPMSG -s 2 $MSGCAT 20 'dsh: 5025-511 Msg EMSG511 not found. No hosts in working collective.\n' $prog 1>&2"; } elsif ($num == 17) { system "$SPMSG -s 2 $MSGCAT 21 'dsh: 5025-512 Msg EMSG512 not found. Could not pipe\n' $prog 1>&2"; } elsif ($num == 18) { system "$SPMSG -s 2 $MSGCAT 22 'dsh: 5025-513 Msg EMSG513 not found. Cannot redirect %1\$s\n' $arg1 $prog 1>&2"; } elsif ($num == 19) { system "$SPMSG -s 2 $MSGCAT 6 'Msg INFO506 not found. Verifying host %1\$s...\n' $arg1 1>&2"; } elsif ($num == 20) { system "$SPMSG -s 2 $MSGCAT 7 'Msg INFO507 not found. Fanout: %1\$s\n' $arg1 1>&2"; } elsif ($num == 21) { system "$SPMSG -s 2 $MSGCAT 23 'dsh: 5025-514 Msg EMSG514 not found. System data repository down\n' $prog 1>&2"; } elsif ($num == 22) { system "$SPMSG -s 2 $MSGCAT 24 'dsh: 5025-515 Msg EMSG515 not found. Hostname %1\$s unresolvable\n' $arg1 $prog 1>&2"; } elsif ($num == 23) { system "$SPMSG -s 2 $MSGCAT 8 'Msg INFO508 not found. Fanout: 64\n' 1>&2"; } } #----------------------------------------------------------------------------- # # read_pipes # # Read stdout and stderr from exiting child rsh process pipes. # Display to parent's stdout or stderr preceded by "hostname: ". # # Input is name of host for which rsh child is exiting. # #----------------------------------------------------------------------------- sub read_pipes { local($hostn) = @_; local($ofh,$efh); $ofh = $r_out{$hostn}; while (<$ofh>) { print STDOUT "$hostn: $_"; } close($ofh); $efh = $r_err{$hostn}; while (<$efh>) { print STDERR "$hostn: $_"; } close($efh); } #----------------------------------------------------------------------------- # # get_command # # Return the command from the command line or stdin. # Return 0 if no more to be read from command line or stdin. # If the command starts with '!', execute here and read in the next command. # #----------------------------------------------------------------------------- sub get_command { local($command); if ($done) { return(0); } if (@ARGV) { $done = 1; @dsh_ARGV = @ARGV; shift(@ARGV); return(@dsh_ARGV); } GET_COMMAND: { -t && print STDERR "dsh> "; $command = ; if (!defined($command) || $command =~ /^\s*$/ || $command =~ /^\s*exit\s*$/) { return(0); } else { chop $command; if ($command =~ /^\s*!(.*)/) { &do_system($1); redo GET_COMMAND; } else { return($command) || redo GET_COMMAND; } } } } #----------------------------------------------------------------------------- # # do_system # # Issue system() call with default signal handling. # Return the executed command's exit code. # #----------------------------------------------------------------------------- sub do_system { local($command) = @_; local(%save_sig,$rc); %save_sig = %SIG; grep($_ = 'DEFAULT', %SIG); $rc = system("$command 2>&1") >> 8; %SIG = %save_sig; return($rc); } #----------------------------------------------------------------------------- # # d_syntax # # Display help info # #----------------------------------------------------------------------------- sub d_syntax { &do_msg(2,'','',''); } #----------------------------------------------------------------------------- # # check_SDR # # Return 1 if the SDR is up, 0 if it is down. If already checked, return # previous result (only want to check it once per invocation). # #----------------------------------------------------------------------------- sub check_SDR { if (!$checked_SDR) { $checked_SDR++; if (&do_system("$bindir/SDRGetObjects SP > /dev/null 2>&1")) { $prev_result = 0; } else { $prev_result = 1; } } return($prev_result); } #----------------------------------------------------------------------------- # # add_wc # # Add a host to the working collective. If the -v flag is set, check if the # host is responding first. Try the SDR, if not there, try ping. # Don't add a hostname if it is already there. # Input is the hostname to add. # #----------------------------------------------------------------------------- sub add_wc { local($host) = @_; local($hostname,@hostnames); local($name,$aliases,$addtype,$length,@addrs); $host =~ s/\s//g; if ($verify) { #print STDERR "Verifying $host...\n"; $iflag && &do_msg(19,$host,'',''); %is_responding || &build_resp_array; ($name,$aliases,$addtype,$length,@addrs) = gethostbyname($host); unless ($name) { #print STDERR "Hostname $host unresolvable\n"; &do_msg(22,$host,'',''); return; } if ($is_responding{$name} eq "0") { &include_anyway($host) || return; } elsif ($is_responding{$name} ne "1") { if (&do_system("/etc/ping -c 1 $host > /dev/null 2>&1")) { &include_anyway($host) || return; } } } foreach $hostname (@wc) { return if $hostname eq $host; } push(@wc,$host); } #----------------------------------------------------------------------------- # # include_anyway # # Inform of not responding node and prompt if to include in collective. # If include it, return 1, else 0. # #----------------------------------------------------------------------------- sub include_anyway { local($host) = @_; local($ans); $iflag || return (0); #print STDERR $host," not responding. Enter y if add to collective\n"; &do_msg(3,$host,'',''); open(TERM,"/dev/tty"); chop($ans = ); return ($ans =~ /^\s*[Yy]\s*$/); } #----------------------------------------------------------------------------- # # build_resp_array # # Build an array indicating whether hosts are responding, based on the SDR. # #----------------------------------------------------------------------------- sub build_resp_array { local(@nodelines,@resplines,%r_hostname,%responding,$r,$noden,$rhn); &check_SDR || return; @nodelines = `$bindir/SDRGetObjects -G Node node_number initial_hostname`; shift @nodelines; chop @nodelines; chop @nodelines; foreach (@nodelines) { ($discard,$noden,$rhn) = split(/\s+/,$_); $rhn =~ s/ //g; $r_hostname{$noden} = $rhn; } @resplines = `$bindir/SDRGetObjects -G host_responds`; shift @resplines; chop @resplines; chop @resplines; foreach (@resplines) { ($discard,$noden,$r) = split(/\s+/,$_); $r =~ s/ //g; $responding{$noden} = $r; } foreach (keys %r_hostname) { $is_responding{$r_hostname{$_}} = $responding{$_}; } } #----------------------------------------------------------------------------- # # add_cluster_wc # # Get node objects currently in SDR for current System Partition with # hostname attributes and add to the working collective. # #----------------------------------------------------------------------------- sub add_cluster_wc { local($hostname,@hostnames); unless (&check_SDR) { #die "System data repository down\"; &do_msg(21,'','',''); exit(-1); } @hostnames = `$bindir/SDRGetObjects $global_opt Node initial_hostname`; shift @hostnames; chop @hostnames; chop @hostnames; foreach $hostname (@hostnames) { &add_wc($hostname); } } #----------------------------------------------------------------------------- # # parse # # Parse the command line # #----------------------------------------------------------------------------- sub parse { local(@indices,@temp,$rest,$findex,$windex,$lindex,$wcf,$wcoll_file,$host,@hostlist,$ht,$hl); if ($ARGV[0] =~ /^-[hq](\S+)/) { #die "dsh: Extraneous option - $1\n"; &do_msg(5,$1,'',''); &d_syntax; exit(-1); } if ($ARGV[0] =~ /^-q$/) { if (!defined $ARGV[1]) { &get_wc; #print STDOUT "Working collective file $ENV{'WCOLL'}:\n"; &do_msg(4,$ENV{'WCOLL'},'',''); print STDERR join("\n",@wc),"\n"; if (!($fanout = $ENV{'FANOUT'})) { #print STDOUT "Fanout: 64\n"; &do_msg(23,'','',''); exit(0); } #print STDOUT "Fanout: $fanout\n"; &do_msg(20,$fanout,'',''); exit(0); } else { #die "dsh: Extraneous option - $ARGV[1]\n"; &do_msg(5,$ARGV[1],'',''); &d_syntax; exit(-1); } } if ($ARGV[0] =~ /^-h$/) { if (!defined $ARGV[1]) { &d_syntax; exit(0); } else { #die "dsh: Extraneous option - $ARGV[1]\n"; &do_msg(5,$ARGV[1],'',''); &d_syntax; exit(-1); } } while ($ARGV[0] =~ /^-/) { if ($ARGV[0] =~ /[wfl](\S+)/) { $findex = index($ARGV[0],"f"); $windex = index($ARGV[0],"w"); $lindex = index($ARGV[0],"l"); @indices = ($findex, $windex, $lindex); @indices = sort @indices; @temp = @indices; foreach (@temp) { $_ == -1 && shift(@indices); } if ($indices[0] == $findex) { if (!$fanout) { $fanout = $1; if ($fanout =~ /\D/) { #die "dsh: Invalid argument - $fanout\n"; &do_msg(6,$fanout,'',''); &d_syntax; exit(-1); } } else { #die "dsh: Extraneous option - f\n"; &do_msg(5,"f",'',''); &d_syntax; exit(-1); } } elsif ($indices[0] == $windex) { if (!$hl) { $hl = $1; if ($hl =~ /^,|,,|,$/) { #die "Invalid argument - $hl\n"; &do_msg(6,$hl,'',''); &d_syntax; exit(-1); } if ($hl eq "-") { while () { /^\s*#/ && next; /^\s*$/ && next; /;/ && next; /\S+ \S+/ && next; s/ //g; chop; push(@hostlist,$_); } } else { @hostlist = split(/,/,$hl); } } else { #die "dsh: Extraneous option - w\n"; &do_msg(5,"w",'',''); &d_syntax; exit(-1); } } elsif ($indices[0] == $lindex) { if (!$login) { $login = $1; } else { #die "dsh: Extraneous option - l\n"; &do_msg(5,"l",'',''); &d_syntax; exit(-1); } } $ARGV[0] = substr($ARGV[0], 0, $indices[0] + 1); } elsif ($ARGV[0] =~ /f$/) { if (!$fanout) { $fanout = $ARGV[1]; unless ($fanout) { #die "dsh: Missing argument\n"; &do_msg(7,'','',''); &d_syntax; exit(-1); } if ($fanout =~ /\D/) { #die "dsh: Invalid argument - $fanout\n"; &do_msg(6,$fanout,'',''); &d_syntax; exit(-1); } $shiftflag++; } else { #die "dsh: Extraneous option - f\n"; &do_msg(5,"f",'',''); &d_syntax; exit(-1); } } elsif ($ARGV[0] =~ /l$/) { if (!$login) { $login = $ARGV[1]; unless ($login) { #die "dsh: Missing argument\n"; &do_msg(7,'','',''); &d_syntax; exit(-1); } $shiftflag++; } else { #die "dsh: Extraneous option - l\n"; &do_msg(5,"l",'',''); &d_syntax; exit(-1); } } elsif ($ARGV[0] =~ /w$/) { if (!$hl) { $hl = $ARGV[1]; unless ($hl) { #die "dsh: Missing argument\n"; &do_msg(7,'','',''); &d_syntax; exit(-1); } if ($hl =~ /^,|,,|,$/) { #die "Invalid argument - $hl\n"; &do_msg(6,$hl,'',''); &d_syntax; exit(-1); } if ($hl eq "-") { while () { /^\s*#/ && next; /^\s*$/ && next; /;/ && next; /\S+ \S+/ && next; s/ //g; chop; push(@hostlist,$_); } } else { @hostlist = split(/,/,$hl); } $shiftflag++; } else { #die "dsh: Extraneous option - w\n"; &do_msg(5,"w",'',''); &d_syntax; exit(-1); } } if ($ARGV[0] =~ /^-.*([^iaGcvwfl]).*/) { #die "dsh: Invalid option - $1\n"; &do_msg(8,$1,'',''); &d_syntax; exit(-1); } if ($ARGV[0] =~ /^-$/) { #die "dsh: Missing option\n"; &do_msg(9,'','',''); &d_syntax; exit(-1); } if (index($ARGV[0], 'i') >= $[) { if (rindex($ARGV[0], 'i') == index($ARGV[0], 'i')) { if ($iflag++) { #die "dsh: Extraneous option - i\n"; &do_msg(5,"i",'',''); &d_syntax; exit(-1); } } else { #die "dsh: Extraneous option - i\n"; &do_msg(5,"i",'',''); &d_syntax; exit(-1); } } if (index($ARGV[0], 'v') >= $[) { if (rindex($ARGV[0], 'v') == index($ARGV[0], 'v')) { if ($verify++) { #die "dsh: Extraneous option - v\n"; &do_msg(5,"v",'',''); &d_syntax; exit(-1); } } else { #die "dsh: Extraneous option - v\n"; &do_msg(5,"v",'',''); &d_syntax; exit(-1); } } if (index($ARGV[0], 'G') >= $[) { if (rindex($ARGV[0], 'G') == index($ARGV[0], 'G')) { if ($Gflag++) { #die "dsh: Extraneous option - G\n"; &do_msg(5,"G",'',''); &d_syntax; exit(-1); } $global_opt = "-G"; } else { #die "dsh: Extraneous option - G\n"; &do_msg(5,"G",'',''); &d_syntax; exit(-1); } } if (index($ARGV[0], 'a') >= $[) { if (rindex($ARGV[0], 'a') == index($ARGV[0], 'a')) { if ($aflag++) { #die "dsh: Extraneous option - a\n"; &do_msg(5,"a",'',''); &d_syntax; exit(-1); } $wfound++; } else { #die "dsh: Extraneous option - a\n"; &do_msg(5,"a",'',''); &d_syntax; exit(-1); } } if (index($ARGV[0], 'c') >= $[) { if (rindex($ARGV[0], 'c') == index($ARGV[0], 'c')) { if ($continue++) { #die "dsh: Extraneous option - c\n"; &do_msg(5,"c",'',''); &d_syntax; exit(-1); } } else { #die "dsh: Extraneous option - c\n"; &do_msg(5,"c",'',''); &d_syntax; exit(-1); } } shift(@ARGV); if ($shiftflag) { shift(@ARGV); $shiftflag--; } } $aflag && &add_cluster_wc; if (@hostlist) { $wfound++; foreach $ht (@hostlist) { &add_wc($ht); } } } #----------------------------------------------------------------------------- # # set_defaults # # Set default values, if any. 64 is default fanout. # #----------------------------------------------------------------------------- sub set_defaults { unless ($fanout) { unless ($fanout = $ENV{'FANOUT'}) { $fanout = 64; } } unless ($login) { $login = (getpwuid($<))[0]; } } #----------------------------------------------------------------------------- # # readem # # Read the stdout and stderr pipes for all hosts in current fanout. # #----------------------------------------------------------------------------- sub readem { local(@h) = @_; local($host); local($host_reader); foreach $host (@h) { $host_reader=$host . "_reader"; if($pid{$host_reader} = fork) { next; } else { &read_pipes($host); exit(0); } } @h = (); } #----------------------------------------------------------------------------- # # display_wc # # Display the working collective, if requested with the -i option. # #----------------------------------------------------------------------------- sub display_wc { local($i,$ans); if ($iflag) { #print STDOUT "Working collective:\n"; &do_msg(10,'','',''); $i = 0; while ($i <= $#wc) { printf STDERR "%-19.18s", $wc[$i]; printf STDERR "%-19.18s", $wc[$i+1]; printf STDERR "%-19.18s", $wc[$i+2]; printf STDERR "%-19.18s\n", $wc[$i+3]; $i = $i + 4; } } } #----------------------------------------------------------------------------- # # get_wc # # Determine the working collective, if not already obtained from command line. # Look for filename in $WCOLL containing the hostnames, one per line. # #----------------------------------------------------------------------------- sub get_wc { local($wfile,$new_host); if (!@wc && !$wfound) { unless ($wfile = $ENV{'WCOLL'}) { #die "dsh: Working collective environment variable not set\n"; &do_msg(11,'','',''); exit(-1); } unless (open(WCFILE, $wfile)) { #die "dsh: Cannot open working collective file $wfile: $!\n"; &do_msg(12,$wfile,"\'$!\'",''); exit(-1); } while ($new_host = ) { $new_host =~ /^\s*#/ && next; $new_host =~ /^\s*$/ && next; $new_host =~ /;/ && next; $new_host =~ /\S+\s+\S+/ && next; chop($new_host); &add_wc($new_host); } close(WCFILE); } } #----------------------------------------------------------------------------- # # set_signals # # HUP is ignored in the dsh parent and exec'ed rsh children. # # STOP, CONT, and TSTP are defaulted - this means that they work on the # parent, but are ignored (not propagated to) the exec'ed rsh children # or the remote processes. # # Set the signal handler for all other signals. # The signals will be propagated to the execed children and then the # default action will be taken in the parent. # # Rsh will propagate TERM, QUIT, and INT to the remote processes. # #----------------------------------------------------------------------------- sub set_signals { # Default STOP, CONT, TSTP signal handling $SIG{'STOP'} = 'DEFAULT'; $SIG{'CONT'} = 'DEFAULT'; $SIG{'TSTP'} = 'DEFAULT'; # Propagate signals to forked kids $SIG{'TERM'} = 'infanticide'; $SIG{'QUIT'} = 'infanticide'; $SIG{'INT'} = 'infanticide'; $SIG{'ABRT'} = 'infanticide'; $SIG{'ALRM'} = 'infanticide'; $SIG{'FPE'} = 'infanticide'; $SIG{'ILL'} = 'infanticide'; $SIG{'PIPE'} = 'infanticide'; $SIG{'SEGV'} = 'infanticide'; $SIG{'USR1'} = 'infanticide'; $SIG{'USR2'} = 'infanticide'; $SIG{'TTIN'} = 'infanticide'; $SIG{'TTOU'} = 'infanticide'; $SIG{'BUS'} = 'infanticide'; } #----------------------------------------------------------------------------- # # wait_for_kids # # When a child dies, it must be an exit after the end of his rsh. # If a negative return code, the rsh itself failed. Check to see if we # should give up on this host and eliminate him from the collective. # Display the contents of his stdout/stderr pipes and save the rsh exit code # in an environment variable. # #----------------------------------------------------------------------------- sub wait_for_kids { local($child_pid,$child_rc,$child_host); while (($child_pid = wait) != -1) { $child_rc = $? >> 8; foreach (keys %pid) { if ($pid{$_} == $child_pid) { $child_host = $_; last; } } if ($child_rc != 0) { $rcode++; #print STDERR "dsh: $child_host rsh had exit code $child_rc\n"; &do_msg(13,$child_host,$child_rc,''); push(@goners,$child_host); } # &read_pipes($child_host); } } #----------------------------------------------------------------------------- # # delete_hosts # # Called if any hosts don't respond. Remove them from the working collective # unless the -c flag was set. # Input is the hostnames to remove from the working collective. # #----------------------------------------------------------------------------- sub delete_hosts { local($child_host,$h,$host_count); unless ($continue) { foreach $child_host (@goners) { $host_count = 0; foreach $h (@wc) { if ($h eq $child_host) { splice(@wc, $host_count, 1); } $host_count++; } } } } #----------------------------------------------------------------------------- # # infanticide # # User has signaled the dsh parent - propagate TERM, INT, or QUIT to children. # (Note - TERM, INT, and QUIT will be propagated to remote processes by rsh). # Signal any children with SIGTERM if signal is not one of the above. # Wait for children to manage output and prevent zombies. # Signal self after setting default signal-handling for self. # If still alive, exit. # Input is the signal type. # #----------------------------------------------------------------------------- sub infanticide { local($sig) = @_; local($kid_sig); #print STDERR "dsh: Caught SIG$sig - terminating the kids\n"; &do_msg(14,$sig,'',''); if ($sig ne 'QUIT' && $sig ne 'INT' && $sig ne 'TERM') { $kid_sig = 'TERM'; $SIG{'TERM'} = 'IGNORE'; } else { $kid_sig = $sig; } $SIG{$sig} = 'DEFAULT'; kill $kid_sig, %pid; &readem(@hs); &wait_for_kids; kill $sig, $$; exit($rcode); } #------------------------------------------------------------------------------ # # check_wc # # Check to ensure that there are hosts in the collective # #------------------------------------------------------------------------------ sub check_wc { if (!@wc) { #print STDERR "dsh: No hosts in working collective.\n"; &do_msg(15,'','',''); exit($rcode++); } } #------------------------------------------------------------------------------ # # Main line program # # Program continues while there are commands to distribute to working # collective via rsh. Children are forked for each host (up to the fanout # limit). Stdout and stderr are piped back from the children to the parent # who will display the results of the execed rsh's to the parent's stdout # and stderr. # #------------------------------------------------------------------------------ # Path for our executables $msgdir = "/usr/lib/nls/msg"; $bindir = "/usr/es/sbin/cluster/cspoc"; $global_opt = ""; # Supplied to SDRGetObjects # Flush output filesystem buffers after each write select(STDOUT); $| = 1; select(STDERR); $| = 1; # Set signal handling, parse the command line, and determine the working # collective &parse; &get_wc; &display_wc; &set_defaults; &set_signals; &check_wc; # Perform each command on the working collective while ($command = &get_command) { $beenhere++ || &check_wc; if ($iflag) { if (@dsh_ARGV) { print STDERR (">>> ",join(' ',@dsh_ARGV),"\n"); } else { $print_command = `echo $command`; print STDERR ">>> $print_command"; } } foreach $host (@wc) { # Create filehandles for pipe ends $r_out{$host} = "READ_STDOUT_" . $host; $w_out{$host} = "WRITE_STDOUT_" . $host; $r_err{$host} = "READ_STDERR_" . $host; $w_err{$host} = "WRITE_STDERR_" . $host; # Open pipes for this host's stdout and stderr from rsh if (pipe($r_out{$host}, $w_out{$host}) < 0) { #die "dsh: Couldn't pipe: $!\n"; &do_msg(17,"\'$!\'",'',''); exit(-1); } if (pipe($r_err{$host}, $w_err{$host}) < 0) { #die "dsh: Couldn't pipe: $!\n"; &do_msg(17,"\'$!\'",'',''); exit(-1); } # Fork a child to exec the rsh FORK: { if ($pid{$host} = fork) { # parent code - # close unneeded ends of pipes # parent will wait for child processes if fanout limit reached close($w_out{$host}); close($w_err{$host}); push(@hs,$host); if (++$cur_fanout >= $fanout) { $cur_fanout = 0; &readem(@hs); &wait_for_kids; } } elsif (defined $pid{$host}) { # child code - # close unneeded ends of pipes # redirect stdout and stderr to output pipes # exec rsh # stdout/stderr will go to pipes to be read by parent close($r_out{$host}); close($r_err{$host}); unless (open(STDOUT, ">&$w_out{$host}")) { #die "dsh: Cannot redirect STDOUT: $!\n"; &do_msg(18,'STDOUT',"\'$!\'",''); exit(-1); } unless (open(STDERR, ">&$w_err{$host}")) { #die "dsh: Cannot redirect STDERR: $!\n"; &do_msg(18,'STDERR',"\'$!\'",''); exit(-1); } select(STDOUT); $| = 1; select(STDERR); $| = 1; if (!@dsh_ARGV) { exec "/usr/es/sbin/cluster/utilities/cl_rsh $host $command" || die "dsh:rsh: $host $command: $!\n"; } else { exec ('/usr/es/sbin/cluster/utilities/cl_rsh',$host,@dsh_ARGV) || die "dsh:rsh: $host $command: $!\n"; } } else { # try again fork must have failed due to resource problem sleep 5; redo FORK; } } } # parent continues here after forking all children for this command # get the results of any remaining rsh's, if any # (number of hosts in working collective may not be multiple of fanout) # get rid of any hosts that have failed # display working collective and command &readem(@hs); &wait_for_kids; &delete_hosts; unless ($done) { $cur_fanout = 0; &check_wc; &display_wc; } } # Exit exit($rcode);