#!/usr/bin/env perl
#-d
##
##----------------------------------------------------------------------------
## History:
## Date      OPR    Who         Reason
## --------  ------ ----------  --------------------------------------------
## 12/03/02  45017  Sherbert    Can't trust user to run script correctly two
##                              times in a row
## 12/10/02    Take pity on poor user and provide help if no params provided
## 02/26/03    Clean up a bit by deleting pscluster lines commented out here
## 03/24/03    Clean up long lines
## 03/24/03    What testing parameters do I really need?
## 03/24/03    Add an abbreviated help for misuses of command
## 03/28/03    Servers are down by time of pstat removal. Do not use
##             pstat_delete: it brings up servers.
## 05/02/03    Commented out "Appending..." and "<csh file> removed"
##             and CommandIt's print of cmd
## 09/08/03    Required to add ".pl" to file names; make less loquacious
## 09/09/03    Added linesize param to pass to pscluster
## 09/10/03    Add -force parameter to alter_server_files.pl
## 08/31/04  49405  Sherbert    Use a TMP facility instead of hardcode /tmp
## 10/04/04  49405  Sherbert    Read opus.env for PSTAT regex; fix message
## 10/05/04  51890  "" Clean up some messaging, point to pscluster.pl
##                     Change name of nodes in Examples
##                     Fail if -kill and (-t p or -path) are specified
## 11/11/04  51890  Sherbert    Clean up PrintShortUsage text
##
##----------------------------------------------------------------------------
##
## create a wrapper script which will create proper pscluster commands
##
## Will pass-through parameters to pscluster unless 
##    a) no parameters are provided or
##    b) -kill is used, in which case "-type a" will be assumed and
##       two commands created:
##       1) the first to kill the processes
##       2) the second to kill the servers
##       This process eliminates the need to run the original script twice.
##
## Usage:  almost same as pscluster.pl only a) help provided if no params are,
##         b) many presumptions are enforced, and
##         c) produces a csh script that
##         runs pscluster.pl instead of performing the tasks itself.
##
## A parameter is for debugging purposes only:
##      -inspection: toggles whether  or not to run the script this 
##      script writes
##
## Need to batten down -kill so no parameters other than node are payed
##    attention to when -kill is specified.
##
## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
## Let us think about this: -type s is the default UNLESS -path is specified.
## However, -type p should still be allowed without -path, to imply all paths.
## I don't think we can make -type p take an argument though that would be nice.
## -type a should also be allowed but implied and not necessary to spell out 
## with -kill so the only provision I made for that is -all, 
## which is incomplete, I think.
##
##----------------------------------------------------------------------------


 # print "\nARGV contains $#ARGV arguments!\n";      ## trace
 if ( $#ARGV < 0 ) {
    PrintShortUsage();
    PrintSwitchUsage();
    PrintExampleUsage();
    PrintMoreUsage();
    exit 1;
 }

 #
 # prepend the Perl include variable using all components of PATH
 # (this allows us to reuse other [OPUS] Perl code)
 #
 $PATH  = $ENV{"PATH"};
 @parts = split /:/, $PATH;
 while (defined($val = pop(@parts))) {
    if (substr($val,0,1) eq "/") { unshift(@INC, $val)}
 }
 #
 # include these modules
 #
 require 'gen_regex_fmfile.pl';

 use IO::File;
 use POSIX qw(tmpnam);

 use Getopt::Long;
 ($opt_nodes, $opt_user, $opt_kill, $opt_type, $opt_path, $opt_linesize, 
  $opt_help, $opt_verbose, $opt_inspection) = (0) x 9;
 GetOptions qw{
    nodes=s
    user=s
    kill
    type=s
    path=s
    linesize=i
    help
    verbose
    inspection
    };
 ## apparently "-nodes string" is same as "-n string" 
 ## whereas   "--nodes string" would be different

 if ( $opt_help ) {
    PrintShortUsage();
    PrintSwitchUsage();
    PrintExampleUsage();
    PrintMoreUsage();
    exit 1;
 }
 if ( $opt_verbose ) {
    $verbose = 1;
    $be_verbose = "-verbose";
 } else {
    $verbose = 0;
    $be_verbose = "";
 }
 if ( $opt_inspection ) {
    $inspect = 1;
 } else {
    $inspect = 0;
 }

 if ( $opt_user && $opt_kill ) {
    print STDERR "\n", "*"x79, "\n";
    print STDERR "*** ERROR: cannot specify kill option AND name a user" ;
    print STDERR "\n", "*"x79, "\n";
    PrintShortUsage();
    exit 1;
 }

 if ( $opt_type && $opt_kill || $opt_path && $opt_kill ) {
    print STDERR "\n", "*"x79, "\n";
    print STDERR "*** ERROR: cannot specify kill option AND a type or path \n" ;
    print STDERR "*** Leave off the -t(ype) or -p(ath) argument OR \n" ;
    print STDERR "*** use pscluster.pl directly instead, please. " ;
    print STDERR "\n", "*"x79, "\n";
    PrintShortUsage();
    exit 1;
 }

 ## check for missing values as far as one is able to do so...
 ## "-nodes -user l" will generate a failure but
 ## "-nodes l -user" will default to current user....will it?
 if ( $opt_nodes    =~ /^-/ ||
     $opt_user     =~ /^-/ ||
     $opt_type     =~ /^-/ ||
     $opt_path     =~ /^-/              ## ||
    ) {
    print STDERR "\n", "*"x79, "\n";
    print STDERR "*** ERROR: parameter name must not begin with'-'. \n";
    print STDERR "***        Is a value missing on the command line? ";
    print STDERR "\n", "*"x79, "\n";
    PrintShortUsage();
    exit 1;
 }

 ##
 ## Set up default values
 ##
  
 ## Determine user name...
 ## Default to current user (found between 1st set of parens)
 $me = `id|cut -d \\( -f 2|cut -d \\) -f 1` ;
 chomp ($me);

 ## Determine name of path in which to search for processes, if any
 if ( ! $opt_path ) {
    $pathname = "" ;
 } else {
    $pathname = $opt_path ;
 }

 ## No longer need Tru64 cluster translations
 ## Setting of parameters for pscluster.pl command
 my ($nodes, $user, $kill, $all, $path, $lsize );
 if ( $opt_nodes ) {
#   ## naughty hard-code 'cuz I'm lazy and this is the way Tru64 should work
#   if ( $opt_nodes =~ /odocluster/ ) {
#       $opt_nodes = "odoalpha1,odoalpha2,odoalpha3,odoalpha4";
#   } elsif ( $opt_nodes =~ /acdsdops/ ) {
#       $opt_nodes = "idaho,illinois,indiana";
#   }
    $nodes = "-n $opt_nodes";

 } else {
    $nodes = "";
 }
 if ( $opt_user ) {
    $user = "-u $opt_user";
 } else {
    $user = "";
 }
 if ( $opt_type ) {
    $type = "-t $opt_type";
 } else {
    ## default to type "server"
    $type = "-t s"; ## 2003 20 Jan get rid of anonymous type
 }
 if ( $opt_kill ) {
    $kill = "-kill";
    $type = "-a";
 } else {
    $kill = "";
 }
 if ( $opt_linesize ) {
    $lsz = "-l $opt_linesize";
 } else {
    $lsz = "";
 }


 ## Make psopus smart.  It is useful just to see if any processes 
 ## are running in any path and that requires -type p without -path p
 ## If "-path p" is specified, "-type p" would and will be assumed and overrides
 ## others.   No "-type t" and no "-path p" and no "-kill" implies "-type s".
 ## "-kill" implies "-type all" and overrides others.  

 $path = "";
 if ( $opt_path ) {
#   if ( !$opt_kill ) {
        $path = "-p $opt_path";
        ## Discourage combining -type s (or -type a) and -p <name>
        if ( $opt_type =~ /^a/i || $opt_type =~ /^s/i ) {
            print STDERR "\n", "*"x79, "\n";
            print STDERR "*"x32, "    WARNING    ", "*"x32 ;
            print STDERR "\n", "*"x79, "\n";
            print STDERR "*** servers (-type $opt_type) are NOT path-specific:";
            print STDERR "\n";
            print STDERR "*** -path $opt_path implies -type p, ",
                         "which excludes servers. \n";
            print STDERR "*** -type $opt_type IGNORED ",
                         "in favor of -path $opt_path";
            print STDERR "\n", "*"x79, "\n";
            print STDERR "\n";
        }   ## end if opt_type is a or s (not p)
        $type = "-t p";
#   } else {
#print STDERR "**Trace4: path is $path and opt_path is $opt_path.  \n";    ##trace
#       ## Discourage combining -type a and -path <name>
#       print STDERR "\n", "*"x79, "\n";
#       print STDERR "*"x32, "    WARNING    ", "*"x32 ;
#       print STDERR "\n", "*"x79, "\n";
#       print STDERR "*** -kill implies -type all:  ";
#       if ( $opt_type ) {
#           print STDERR "servers (-type $opt_type) ";
#       } else {
#           print STDERR "servers (-type a) ";
#       }   ## end if opt_type (set default to servers)
#       print STDERR "are NOT path-specific! \n";
#       print STDERR "*** -path $opt_path is NOT compatible with either ",
#                    "-type s, -type a, nor -kill. \n";
#       print STDERR "*** Input confused.  Please try again. ";
#       print STDERR "\n", "*"x79, "\n";
#       PrintShortUsage();
#       PrintMoreUsage();
#       exit 1;
#   }   ## end if opt_kill
 }  ## end if opt_path  Part of block outmoded now that kill & path disallowed.

 ##
 ## Create temporary file to hold commands
 ##
#my $tag = `date +'%Y%m%d%H%M%S'`;
#chomp $tag;
#my $csh_file = "/tmp/${me}_psopus_${tag}.csh";

 ## Satisfy warn about fh and make me feel better about csh_file:
 my ($csh_file, $fh) ;
 ## Perl Cookbook 1998, S.7.5, pp. 232-234
 ## try new temporary filenames until we get one that didn't already exist
 do { $csh_file = tmpnam() }
    until $fh = IO::File->new($csh_file, O_RDWR|O_CREAT|O_EXCL);

 ## If I install the recommended "at-exit-style handler" so that when we exit or
 ## die, we automatically delete this temporary file", then it gets deleted
 ## whether I want it to be or not.  I do not need the "at-exit-style handler" 
 ## as I am taking care of file removal later.

 ## trace statements
 if ( $verbose ) {
    print STDERR "\n" ;
#   print STDERR "# Nodes: $opt_nodes User: $opt_user Kill: $opt_kill \n";
#   print STDERR "# Type: $opt_type Path: $opt_path \n";
    print STDERR "# csh_file: $csh_file\n";
    print STDERR "\n" ;
 }

 # umask(0022);    ## I want file to be executable
 open (CSH, ">>$csh_file") 
    or die "\n*** ERROR: Cannot open ", $csh_file, "for writing.\n\n";
 print CSH "#!/bin/csh -x \n";

#print STDOUT "Appending the following lines to $csh_file: \n\n\t";
 if ( $opt_kill ) {
    ## If you are going to kill, you will never need a $path value so no need 
    ## to pass it on.  Also, -type a will be assumed, 
    ## so you always need to kill p(rocesses) first, THEN s(ervers)
    print CSH    "pscluster.pl $nodes $user $kill $lsz -t p $be_verbose \n";
    print STDOUT "pscluster.pl $nodes $user $kill $lsz -t p $be_verbose \n";
    print CSH    "pscluster.pl $nodes $user $kill $lsz -t s $be_verbose \n";
    print STDOUT "pscluster.pl $nodes $user $kill $lsz -t s $be_verbose \n";
 } else {
    print CSH    "pscluster.pl $nodes $user $lsz $type $path $be_verbose \n";
    print STDOUT "pscluster.pl $nodes $user $lsz $type $path $be_verbose \n";
 }
 print STDOUT "\n";
 close (CSH);

 if ( $inspect ) {    ## Traces:    Just show the commands
    &CommandIt ("cat $csh_file",$inspect);      ## trace
    &CommandIt ("ls -ld $csh_file",$inspect);   ## trace
 }

 ## Run the csh file just created (or just show it)
 &CommandIt ("/bin/csh < $csh_file", $inspect);

 if ( !$inspect ) {
    ## delete the temporary file
    my @cannot = grep {not unlink} $csh_file;
    die "$0: could not unlink @cannot\n" if @cannot;
    # print STDOUT "\n$csh_file removed.\n\n";
 } else {
    print STDOUT "\n$csh_file NOT removed.\n\n";
    ## This way it can be run from command line if so desired
 }

 ##
 ## "Sterilize" BlackBoard files when processes are killed
 ##
 if ( $kill ) {
    print STDOUT "Sterilizing blackboard files ",
                 "and looking for PSTATs user may want to remove.\n";
#   print STDOUT "If there are pstats, it should be safe to remove them\n",
#                "since we have just killed all the processes and servers\n",
#                "but maybe they haven't died yet...hmmm. \n";  ## loquacious
    # print "\n";

    ## Reset opus_corba_objs, opus_iors, and resource.locks
    &CommandIt ("alter_server_files.pl -force",$inspect);

    ## If just testing psopus, do not sterilize ($inspect = 1)
    my $OPUS_HOME_DIR = $ENV{"OPUS_HOME_DIR"};
    ## Open OPUS_HOME_DIR and find the pstats
    opendir (DIR, $OPUS_HOME_DIR) 
        or die "$0: Cannot open ", $OPUS_HOME_DIR, 
               " directory for reading $!.\n";
   
    my @allfiles = readdir DIR;
    closedir (DIR);
#   my $cmd = "cd $OPUS_HOME_DIR";
##  print "$cmd \n";
#   `$cmd`;
#   if ( $? ) {
#       warn "WARNING: Cannot cd to $OPUS_HOME_DIR. rm commands may fail.";
#   }

    ## Use the template-reading gen_regex_fmfile.pl's GenRegEx routine
    my $regex = GenRegEx ( "opus.env", "PSTAT" );
#   print "Regex for PSTATs is $regex \n";  ## trace
    ## Safeguard against empty returns
    if ( ! length($regex) ) {
        PrintMsg ("E","No regular expression generated for PSTAT (regex is $regex)");
        exit 2 ;
    }

#   print "allfiles is: \n@allfiles\n"; ## trace
    $psfcount = 0;
    foreach my $file ( @allfiles ) {
        ## Delete stranded PSTATS -- or rather, just print a list of files 
        ## matching regex
        if ( $file =~ /$regex/ ) {
#           print STDERR "Found $file\n";   ## trace
            push (@pstats, $file);
#           $cmd = "rm $file";
##          &CommandIt ($cmd, $inspect); ## really do if not $inspect
#           &CommandIt ($cmd, 1 );   ## just print rm statements
            $psfcount++;
        }
    }
    if ( ! $psfcount ) {
        print "No PSTATs found to remove. \n";
    } else {
        print STDERR "Found $psfcount PSTATs in $OPUS_HOME_DIR: \n", 
                      join ("\n",  @pstats),  "\n"   ## trace
    }   ## end if psfcount is zero

 }

 print STDOUT "\n";
 exit;

##------------------------- End of Main Program ------------------------------

 ##############################################################################
 ##
 ## CommandIt
 ##     Allow test of command generated without running it.
 ## 
 ## Inputs: command runable on command line,
 ##         a boolean
 ##              0 - run the command
 ##              1 - print the command being skipped
 ##
 ##############################################################################
 sub CommandIt
 {
    my ($cmd,$just_a_test) = @_;

    ## if NOT just_a_test, run the commands and display the output
    if ( ! $just_a_test ) {
        # print "\n% ", $cmd, "\n";
        # print $cmd, "\n";
        $out = `$cmd`;
        if ( $out ) {
            print $out, "\n";
        }
    } else {
        print qq|Skipping "$cmd". \n|;
    }

 }



 ##############################################################################
 ## Print Usage statements
 ##############################################################################
 ##
 ## 4 subroutines to print different amounts of information
 ##
 ## PrintShortUsage, PrintSwitchUsage, PrintExampleUsage, PrintMoreUsage
 ##
 ## Output: Any combination of 
 ##         short usage statement
 ##         switches (input parameters)
 ##         Examples
 ##         Line saying where to find more
 ## Provide your own 'exit 1;' statement
 ##
 ##############################################################################
 sub PrintShortUsage
 {
    use File::Basename;
    my ($name, $unixpath, $ext) = fileparse($0, '.pl' );
    $name = $name . $ext;
    print STDERR<<" EOF"; 

    $name 
        [-node node1[,node2,...]] 
        [-user user] 
        [-type s|p|a] 
        [-path path] 
        [-kill] 
        [-help]
    Most parameters can be abbreviated to one letter.
    All arguments are optional, but providing 
        none or [-help] gets you MORE usage information.
    [-user user] and [-kill] are mutually exclusive.
    [-type s or -type a] and [-path pathname] are mutually exclusive.
    [-kill] and ([-type server or all] or [-path pathname]) 
        are mutually exclusive.  (See "pscluster.pl -help" instead).

 EOF
 }

 sub PrintSwitchUsage
 {
    print STDERR<<" EOF"; 
    -node node1,...    
        List of nodes (separated by commas, not spaces) on which to look 
        for processes.  Default is current node.  
    -user user 
        Name of user for whose processes to search.
        Defaults to current user.  
    -type a|p|s    
        a(ll)     Implies processes in all paths and the servers.  
                  Automatically used (default/only choice) with -kill.
        p(ath)    Implies OPUS processes running in any path, 
                  unless -path p is used.
        s(ervers) Default.
    -path pathname 
        Look only for processes in specified path. Extension ".path" is 
        unnecessary.  No default.  Implies "-type p".  
        Ignored if used with "-type a" or "-type s", etc.
    -linesize N
        Limit the output of unix ps command to N-characters. 
        N < 80 is not useful.  More characters give more information.
        Defaults to 115 characters.  
    -kill      
        Indicates that OPUS processes and servers should be killed.
        Only works for the current user.  "-type a" (default) is only choice.  
        Also implies a desire to clean up opus_corba_objs and opus_iors and 
        be reminded to remove PSTATs.  
        Please NOTE that ALL processes (including servers) will be killed.  
        If you really think it is safe to kill ONLY processes running in a path,
        but NOT the servers, please see "pscluster.pl -help".

 EOF
 }

 sub PrintExampleUsage 
 {
    use File::Basename;
    my ($name, $unixpath, $ext) = fileparse($0, '.pl' );
    print STDERR<<" EOF"; 
    Examples: 
    $name -kill -nodes idaho,illinois,indiana 
        Kill all OPUS processes (including servers) on  
        nodes idaho, illinois, and indiana for current user 
    $name -n canada,idaho,hollywood -type a 
        List of all OPUS processes on nodes canada, idaho, and hollywood 
        for current user 
    $name -u polk -n canada,idaho,hollywood  
        List of OPUS servers on nodes canada, idaho, and hollywood 
        for user named polk 
    $name -t s   OR $name -u \$USER
        List of OPUS servers on current node for current user
    $name -t s -l 80
        Limit width of list of OPUS server information to 80-characters

 EOF
## I'm thinking we take this one out:
#   $name -n africa -type p
#       List of OPUS processes for current user on cluster africa 
#       (which must be hard-coded in this program to translate to nodes 
#       malawi, mali, mauritania, and madagascar, for example). Useful to 
#       be sure only servers are running. 
 }

 sub PrintMoreUsage
 {
    print STDERR<<" EOF"; 
    More details available in the on-line help file.

 EOF
 }


 ##############################################################################
 ## Print Banner, if ever needed...?
 ##############################################################################
 sub PrintBanner {
    my ( $node, $local ) = @_;

    my $len1 = length $node;
    my $len2 = length $local;
    print STDOUT "\n" . 
                 "-" x 3      . " " .
                 $node.$local . " " .
                 "-" x (80 - 5 - $len1 - $len2 )."\n";
    return;
 }


