#!/var/opt/STORtools/bin/perl

require "subroutines.pm";
use POSIX "sys_wait_h";
use POSIX "unistd_h";
use POSIX "fcntl_h";

&st_globals();

$SUM_NAME = "$LOGDIAGS/test_summary";

#$DEBUG = "TRUE";

####################################################################
#
# seqrlbf
# sequential read dex32 with lbf diagnostic
# 
# INPUTS:	none
# OUTPUTS:	0 = normal completion
#		1 = no online loops
#		2 = interrupt 
#
# 9/2/98 aku create program
# 10/7/98 aku fix usage of sbus path (fix for sun4d)
# 10/15/98 aku rewrite code to test multiple loops in background
# 		to enable parallel testing of loops
# 10/19/98 aku do not use plbf with multiple threads
# 11/04/98 herb forced LOGDIR to /var/opt/STORtools/logs/diags 
# 11/21/98 change from pp2 to dex
# 2/5/99  mds took out unused code. 
#
#
# Copyright (c) 1998  Sun Microsystems Computer Corporation, Inc.
####################################################################

$PROGNAME = "seqrlbf";

&check_root_access( $PROGNAME );
&proc_cli();

$RETCODE = 0;
$MENU_FIRST = 1;
$| = 1;					# turn off print buffering
$SIG{CHLD} = sub { wait };              # prevents zombie orphans
$SIG{"INT"} = 'int_handler';
$default_dex_options = "-v -S1 8g 0 720m -x 128k -r -e 0";
$dex_options = $default_dex_options;
$dex_to_file_options = "-v -MI1 10m 0 720m -x 32k -wr -e0";
$dex_filename = '';

print SUMFILE "\nStarting: " . `/bin/date` if (open SUMFILE, ">$SUM_NAME");

print "\n";
&make_port_list;
$DUAL_PORT = $PRIMARY if $T300_PRESENT;

#$READ = "FALSE" if ($TEST{$path} !~ /dex/);
#$READ = "FALSE" if ($TEST{$path} !~ /con/);

$is_child = 0;
$DEFAULT_LBF_PASSES = 1000;
$DEFAULT_LBF_XFR_SZ = 64;
$DEFAULT_LBF_COMPARE_F = "OFF";
init_default();

    $NUM_LOOPS = $#LD_DD_PORTS + 1;

if ( $READ eq "TRUE" ) {
    $RUN_DEX32 = "TRUE";
    print "\nWARNING: This test has heavy system performance impact.\n";
    print "It should not be run on storage arrays currently in use. Continue? [y, <n>]: ";
    $usr_choice = "";
    while ($usr_choice eq "") {
        $usr_choice = &get_response;
        if ($usr_choice =~ /^y/i) {
            $RUN_DEX32 = "TRUE";
            last;
        } elsif (($usr_choice =~ /^n/i) | ($usr_choice eq "")) {
    	    $RUN_DEX32 = "FALSE";
            $usr_choice = "n";
            exit;
        } else {
            print "Please choose [<y>, n]:";
            $usr_choice = "";
        }
    }
} else {
    $RUN_DEX32 = "FALSE";
}
####################################################################
# menu 1
# choose an online loop
####################################################################
undef @TEST_LP_LIST;

while (1) {
    # get user input
    $MENU = 1; # ctrl-C exits program
    $res = display_menu();
    $MENU = 0; # ctrl-C returns to menu

    # Run the test
    if($res eq "run") {
        #Run Test
        &run_test();

        # Make sure that user can read results
        printf("\nHit any key to continue ...");
        &get_response;

    # Leave the program
    } elsif ($res eq "exit") {
        exit($RETCODE);
    }

}

# allow user to specify a pattern file

sub sel_pattern() {

    my $regex;
    foreach $e (@TEST_LP_LIST) {
        $regex .= "t_seqrl*$CNUM{$e}|";
    }
    chop $regex;
    $PATTERN_CHOICE = &choose_patterns($regex);
    if ($PATTERN_CHOICE eq '') {
        return($RETCODE);
    } elsif ($PATTERN_CHOICE eq 'all') {
        # user does not want to specify a pattern file
        # use all patterns
        $PATTERN_CHOICE = '';
        $RUN_WITH_DEF_PATTERNS = "All Patterns";
    } else {
        $RUN_WITH_DEF_PATTERNS = "User Defined";
    }
}

####################################################################
# start looking for control c
####################################################################


sub run_test() {

   foreach $d (@TEST_LP_LIST) 
   {
      if (($d =~ /qlc/ ))
      {
         # this is a qlc port
         print "\nWARNING: This test will offline this port and I/O path:\n($CNUM{$d}) $d\n";
         print "It should not be run on storage arrays currently in use. Continue? [y, <n>]: ";
         $usr_choice = "";
         while ($usr_choice eq "") 
	 {
            $usr_choice = &get_response;
            if ($usr_choice =~ /^y/i) 
	    {
               # continue in for loop
            } 
	    elsif (($usr_choice =~ /^n/i) | ($usr_choice eq "")) 
	    {  
	       print "\nUser decided not to run test with current selected ports.\n";
               return;
            } 
	    else 
	    {
               print "Please choose [<y>, n]:";
               $usr_choice = "";
            }
	 }
      }

   }
    print "\nRunning the following tests: \n";
    &st_sync();



    if ($RUN_DEX32 eq "TRUE") {
        foreach $d (@TEST_LP_LIST) {		# run dex32 with lbf
            my $drv_count = &get_ddcnt( $d );
            if (($TEST{$d} =~ /lbf/) and ($TEST{$d} =~ /dex/) and ($TEST{$d} =~ /con/)) {
                print "($CNUM{$d}) $d \t lbf + dex32 ($drv_count drives)\n";
                if ($drv_count == 0) {
                    print "Error: Cannot run dex with zero drives\n";
                    return;
                }
            } else {
                if ($TEST{$d} =~ /lbf/) {
                    print "($CNUM{$d}) $d \t lbf only, concurrent testing with dex32 not supported\n";
                } else {
                    print "($CNUM{$d}) $d \t skipping lbf test, not supported\n";
                }
            }
        }
    } else {
        foreach $d (@TEST_LP_LIST) {
            if ($TEST{$d} =~ /lbf/) {
                print "($CNUM{$d}) $d \t lbf only \n";
            } else {
                print "($CNUM{$d}) $d \t skipping lbf test, not supported \n";
            }
        }
    }

    print "\n";

    while (1) {

        if ($RUN_DEX32 eq "TRUE") {
            foreach $d (@TEST_LP_LIST) {		# run dex32 with lbf
                if ($TEST{$d} =~ /lbf/) {
                    &logger("INFO", 2010, "Start of dex32 and mpat_lbf from ${PROGNAME}");
                    &fork_lptest($d, $CNUM{$d}, $CNUM{$d}, $PATTERN_CHOICE, $LBF_COMPARE_F);
                }
            }
        } else {  # run lbf
            foreach $d (@TEST_LP_LIST) {
                if ($TEST{$d} =~ /lbf/) {
                    &logger("INFO", 2010, "Start of mpat_lbf from ${PROGNAME}");
                    &fork_lptest($d, 0, $CNUM{$d}, $PATTERN_CHOICE, $LBF_COMPARE_F);
                }
            }
        }
        last;
    }


####################################################################
# wait for all jobs to complete (via the process table)
#  could use waitpid in non-blocking mode here
####################################################################

    if ($is_child) {
        print "Child exiting\n";
        exit;
    }
    $wait_on_child = 1;

    while ($wait_on_child) {
        $wait_on_child = 0;
        foreach $i (@MY_CHILDREN) {
            if (wait2comp($i)) {
                $wait_on_child += 1;
            }
        }
        sleep 5;
    }

    if ($RUN_DEX32 eq "TRUE") {
        &logger("INFO", 2010, "end of dex32 and mpat_lbf from ${PROGNAME}");
    } else {
        &logger("INFO", 2010, "end of mpat_lbf from ${PROGNAME}");
    }

    print SUMFILE "Finished: " . `/bin/date` if (open SUMFILE, ">>$SUM_NAME");
    
    &killdex32;
    &sum_test;

    return;
}

####################################################################
# SUBROUTINES
####################################################################


####################################################################
# control c interrupt trap
####################################################################
sub int_handler {
    &clean_children;
    &killdex32;
    #kill_pid_files($TMPDIR, "k", "TRUE");
    print SUMFILE "Interrupted: " . `/bin/date` if (open SUMFILE, ">>$SUM_NAME");
    # sum_test();
    exit 2 if $MENU;
    exit 2 if $is_child;
    $wait_on_child = 0;
}
####################################################################
# report pass or fail
####################################################################
sub sum_test {
    my @summary;
    my ($lp, $grlp, $mark);
    my $failstring = "FAIL";

    @summary = ("\n\n",
                "\tLoop Test Summary\n",
                "\t==================\n\n");
    $RETCODE = 1; # initialize
    foreach $lp (@TEST_LP_LIST) {
        $mark = "$$.$CNUM{$lp}";
        foreach $file_name (split /\n/, `/bin/ksh -c "/usr/bin/ls -1 ${LOGDIAGS}/t_seqrl.$mark.* 2> /dev/null"`) {
            if ($file_name =~ /t_seqrl\.\d+\.c\d+\.(\d+)/) {
                $child_pid = "$1";
                `/usr/bin/ksh -c "/usr/bin/kill $child_pid 2> /dev/null"`;
            }
        }
        $grlp = `/bin/ksh -c "/bin/egrep -i $failstring ${LOGDIAGS}/t_seqrl.$mark.* 2>&1"`;
        if ($grlp eq "") {
            $RC{$lp} = "PASS";
	    if ($RETCODE != -1)
	    {
	       $RETCODE = 0;
	    }
        } elsif ($grlp =~ /can\'t open/) {
            $RC{$lp} = "SKIPPED";
	    if ($RETCODE == 1)
	    {
	      $RETCODE = -2;
	    }
        } else {
            $RC{$lp} = "++++ FAIL ++++";
            $RETCODE = -1;
        }
        @summary = (@summary, "LOOP: ($CNUM{$lp}) ($SFNUM{$lp}) $lp \t $RC{$lp} \n");
    }

    @summary = (@summary, "\n\n");
    if ($RETCODE == 0) 
    {
        @summary = (@summary, `${BINDIR}/testpass`);
    } 
    elsif ($RETCODE == -2)
    {
       @summary = (@summary, " ****** Test Skipped ******\n ****** Test Skipped ******\n ****** Test Skipped ******\n ");
    }
    else
    {
        @summary = (@summary, "\n\n");
        @summary = (@summary, `${BINDIR}/testfail`);
    }
    print "\n";
    print @summary;
    open SUMFILE, ">>$SUM_NAME" or return;
    print SUMFILE @summary;
    close SUMFILE;
}

####################################################################
# print disks menu
####################################################################
# future enhancement:
#   add to this list a list of disks on a single backplane.
# 
####################################################################
sub print_disks {
    print "\n\n";
    $dd_cnt = 1;
    $DD_LIST_CNT = $#DD_LIST + 1;
    foreach $disk (@DD_LIST) {
        ($dd) = $disk =~ /(c\d+t\d+d\d+)/;
        ($CNUM_TEST) = $disk =~ /(c\d+)t\d+d\d+/;
        print " [$dd_cnt]\t$dd \n";
        $dd_cnt++;
    }
    print " [a]\t$CNUM_TEST \n";
    print "\n";
    print " [q] = Quit \n";
    print "\n\n";
    print "Please select a disk or disks to test [<0 - $DD_LIST_CNT>, a, list, q ]: ";
}


####################################################################
# start a process to test a loop
# 	INPUT:	host adapter id (port -n format)
#		controller number (ie - c1)
#		id mark (for log file naming)
####################################################################
sub fork_lptest {
    my ($haid, $cnum, $idmark, $patternfile, $cmpflag) = @_;
    my $lp_test = "-m $DUAL_PORT";
    my $mark = "$$.$idmark";
    my @lp_cmd;

    ###############################################################
    # if this port does not support the test desired, just return #
    ###############################################################
    if ($RUN_DEX32 eq "TRUE") {
        ###############################
        # no lbf and dex concurrently #
        ###############################
        return if ($TEST{$d} !~ /dex/);
    } else {
        ########################################
        # no lbf on this host bus adapter card #
        ########################################
        return if ($TEST{$d} !~ /lbf/);
    }
    if ($cmpflag eq "ON") {
        @lp_cmd[0] = "$lp_test -l $mark -s $haid -c $cnum -n $LBF_PASSES -k $LBF_XFR_SZ -d";
    } else {
        @lp_cmd[0] = "$lp_test -l $mark -s $haid -c $cnum -n $LBF_PASSES -k $LBF_XFR_SZ";
        }
    $lp_cmd[0] .= " -f $patternfile" if $patternfile;
LPT_FORK: {
    if ($pid = fork) {
        # parent here
        # child process pid is available in $pid
        system("/bin/echo $pid >> $TMPDIR/$CNUM{$haid}.pids");
        push @MY_CHILDREN, $pid;
    } elsif (defined $pid) {
        # child here
        # parent process pid is available with getppid
        # start loop test
        $is_child = 1;
        print "@lp_cmd \n" if ($DEBUG);
        my $rc = &t_seqrl(@lp_cmd[0]);
        exit($rc);
    } elsif ($! =~ /No more process/) {
        # EAGAIN, supposedly recoverable fork error
        sleep 5;
        redo LPT_FORK;
    } else {
        # weird fork error
        die "Can't fork: $! \n";
    }
  }
}


####################################################################
# kill all dex32 processes 
####################################################################
sub killdex32 {
    delete $ENV{'DEX_ARGS'};
    delete $ENV{'DEX_FILENAME'};
    &kill_pid_files($TMPDIR, "k", "TRUE");
    &logger("INFO", 2010, "end of dex32 from ${PROGNAME}") if ($RUN_DEX32 eq "TR
UE");
}
##########################################################################
# Display Menu
##########################################################################
sub display_menu {

    system_clear();
    &st_header();
    if ( $READ eq "TRUE" ) {
        print "Disk Read Loop Test (heavy load)\n";
        print "--------------------------------";
    } else {
        print "Loop Integrity Test (light load)\n";
        print "--------------------------------";
    }

    if ( $MENU_FIRST ) {
        $RUN_WITH_DEF_PATTERNS = "All Patterns";
        $RUN_WITH_DEF_PARAMS = "Default Parameters";
        $RUN_WITH_DEX_PARAMS = "Default Parameters";
        $RUN_WITH_DEX_TOFILE = "Off";
        $LUT = "All";

        @TEST_LP_LIST = @LD_DD_PORTS;

        if ( @LD_MB_PORTS ) {
            push @TEST_LP_LIST, @LD_MB_PORTS;
            $NUM_LOOPS += $#LD_MB_PORTS + 1;
        }
        if ( $READ ne "TRUE" ) {
            if ( @LD_STE_PORTS ) {
                push @TEST_LP_LIST, @LD_STE_PORTS;
                $NUM_LOOPS += $#LD_STE_PORTS + 1;
            }
            if ( @LD_ND_PORTS ) {
                push @TEST_LP_LIST, @LD_ND_PORTS;
                $NUM_LOOPS += $#LD_ND_PORTS + 1;
            }
        }
        $MENU_FIRST = 0;
    }

    print ("\n\n");
    print ("Options                                Value\n");
    print ("======================                 ====================\n");
     if ( $READ eq "TRUE" ) {
        print "[m] Dual Port Mode                     $DUAL_PORT\n" if $T300_PRESENT;
    }
    print "[l] Loop(s) under test                 ";
    foreach $e (@TEST_LP_LIST) {
        print "$CNUM{$e} ";
    }
    print "\n";
    print  "[b] Select lbf Parameters              $RUN_WITH_DEF_PARAMS\n";
    print  "[p] Select lbf Patterns                $RUN_WITH_DEF_PATTERNS\n";

    printf ("\n[r] Run\n");
    printf ("\n[q] Quit\n\n");

    printf("Enter Selection: ");
    $choice = &get_response;				# user response

    # quit
    if($choice =~ /^q/i ) { 		# Q or q : quit 
        delete $ENV{'DEX_ARGS'};
        delete $ENV{'DEX_FILENAME'};
        return "exit";
    }

     if ( ( $choice =~ /^m/i ) && ( $READ eq "TRUE" ) ) {
        $DUAL_PORT = &select_dual_port_mode();
        next;
    }

if ( $ALLOW_DEX_PARAMETERS ) {
 
    if ($choice =~ /^f/i) {
        # choose dex to a file
        print "\nEnter Filename To Use: ";
        $dex_filename = &get_response;

        if ($dex_filename ne '') {
            ###################################################
            # User chose a filename, switch to other defaults #
            # for dex command line args                       #
            ###################################################
            $ENV{'DEX_FILENAME'} = $dex_filename;
            $dex_options = $dex_to_file_options if ($dex_options !~ /-M/);
            if ($dex_options ne $dex_to_file_options) {
                $RUN_WITH_DEX_PARAMS = "User Defined";
            } else {
                $RUN_WITH_DEX_PARAMS = "Default Parameters";
            }

        } else {
            #####################################################
            # User chose null filename, meaning dex to raw disk #
            # so, switch back to original defaults for dex cli  #
            #####################################################
            $dex_options = $default_dex_options if ($dex_options !~ /-S/);
            if ($dex_options ne $default_dex_options) {
                $RUN_WITH_DEX_PARAMS = "User Defined";
            } else {
                $RUN_WITH_DEX_PARAMS = "Default Parameters";
            }
            delete $ENV{'DEX_FILENAME'};
        }
        if ($dex_options ne $dex_to_file_options) {
            $RUN_WITH_DEX_PARAMS = "User Defined";
        } else {
            $RUN_WITH_DEX_PARAMS = "Default Parameters";
        }
        $ENV{'DEX_ARGS'} = $dex_options;
        next;
    }

    if ($choice eq "s") {
        print "Default DEX Options: $default_dex_options\n";
        print "Current DEX Options: $dex_options\n";
	next;
    }


}

    # list loops
    if ( $choice =~ /^l/i ) {      
        if ( $READ eq "TRUE" ) {
            &get_test_list( "dex" );
        } else {
            &get_test_list( "lbf" );
        }
        next;
    }

    # run test
    if($choice =~ /^r/i ) {
        return "run";
    }
    # Modify patterns
    if ($choice =~ /^p/i) {
       &sel_pattern;
       next;
    } 

    if ($choice =~ /^b/i) {
        &mod_lbfparms;
        &compare_lbf_params;
	next;
     }

     #if here, didn't match input
     print "\n$choice is an invalid option!\n\n";
     print "Press Return to continue";
     $choice = &get_response;
     $choice="";



}

sub init_default {
    &init_def_parms;
    &init_def_patterns;
}

sub init_def_parms {
    $LBF_PASSES = $DEFAULT_LBF_PASSES;
    $LBF_XFR_SZ = $DEFAULT_LBF_XFR_SZ;
    $LBF_COMPARE_F = $DEFAULT_LBF_COMPARE_F;
    $LBF_PATTERN_F = "USER";
    $PLBF_PROCS = 4;
    $PLBF_LOC = `/bin/which plbf`;
    chomp $PLBF_LOC;
    $WHICH_LBF = "lbf";  # not currently releasing plbf


}

sub compare_lbf_params {
   if (($LBF_PASSES != $DEFAULT_LBF_PASSES) ||
       ($LBF_XFR_SZ != $DEFAULT_LBF_XFR_SZ) ||
       ($LBF_COMPARE_F ne $DEFAULT_LBF_COMPARE_F)) {
      $RUN_WITH_DEF_PARAMS = "User Defined";
   } else {
      $RUN_WITH_DEF_PARAMS = "Default Parameters";
   }
}

sub init_def_patterns {
    $USR_PATTERN[0] = "4a4a4a4a";
    $USR_PATTERN[1] = "7e7e7e7e";
    $USR_PATTERN[2] = "aa55aa55"; 
    $USR_PATTERN[3] = "55aa55aa";
    $USR_PATTERN[4] = "a5a5a5a5";
    $USR_PATTERN[5] = "0f0f0f0f";
    $USR_PATTERN[6] = "b5b5b5b5";
    $USR_PATTERN[7] = "abababab";
    $USR_PATTERN[8] = "ffffffff";
    $USR_PAT_IDX = 9;
}

sub proc_cli {

    # print the usage
    $usage[0] = "$PROGNAME | [-read]\n";
    $usage[5] = "read - Disk Read Loop Test\n";

    if ($ARGV < 0) {
        die @usage;
    }

    $DUAL_PORT = $NONE;
    $DUAL_PORT = $PRIMARY if $T300_PRESENT;
    $LUN_DISK = "Drive";
    $READ = "FALSE";

    $arg = shift @ARGV;
    while($arg) {
    if ($arg =~ /^-read/i) {
        #&check_lite( $PROGNAME );
        $READ = "TRUE";
    } else {
        die "\n$arg is an invalid option!\n\n @usage";
    }
        $arg = shift @ARGV;
    }
}

sub t_seqrl {
  @cli = split / /, @_[0];
  #$SIG{INT} = 'int_handler';

  $mlbfPids = 0; 
  $dex32Pids = 0; 

####################################################################
#
# run mpat lbf to the given loop
#  if the -c input is a valid controller number, start i/o
#  put output of mpat lbf into a log file with the -l mark
# 
# 
# INPUTS:   see usage
# OUTPUTS:  0 = normal completion
#       1 = error completion
#       2 = interrupted
#       
#       log file created in /var/opt/STORtools/logs/diags
#
# 10/20/98 aku create program
# Copyright (c) 1998  Sun Microsystems Computer Corporation, Inc.
####################################################################
  $| = 1;                 # turn off print buffering

  $LBF_XFR_SZ = 64;
  $LBF_PASSES = 1000;
  &t_proc_cli(@cli);
  $LOGFILE = "$LOGDIAGS/t_seqrl.${LOG_MARK}.$$";

  `/bin/rm $LOGDIAGS/t_seqrl.${LOG_MARK}.* 2> /dev/null`;
  
  ####################################################################
  # check if the log file already exists and erase it if it does
  ####################################################################

  if (-e $LOGFILE) {
      `/bin/rm $LOGFILE`;
  }

  print " Printing to logfile : $LOGFILE \n";
  print " Monitor /var/adm/messages for any disk errors.\n";


  $mlbfArray[$mlbfPids++] = fork_mlbf();

  if ($TEST{$d} =~ /con/)
  {
     if ($CONUM) 
     {
        $dex32Array[$dex32Pids++] = fork_dex32();
     }
  }
  ####################################################################
  # wait for mpat-lbf to finish
  # do a tail -f on the log file, search for FAIL messages
  ####################################################################

  sleep 5;
  my $dotnow = 0;
  if (open TAIL, "<$LOGFILE") {
    while (1) {
      #########################################
      # process log file from current pointer #
      # Collect fail lines, clear line, print #
      #########################################
      my $buffer = "";
      while ($line = <TAIL>) {
          $buffer .= $line if ($line =~ /FAIL/i);
      }
      if ($buffer ne "") {
          print "\n$buffer";
          $buffer = "";
      }

      ###########################################
      # EOF hit, check if process still running #
      ###########################################
      $retpid = POSIX::waitpid($LBF_CHILD_PID, &POSIX::WNOHANG);
      last if (($retpid == $LBF_CHILD_PID ) or ($retpid == -1));

      sleep 2; # print dot every 4 seconds
      if ($dotnow) {
          print ".";
          $dotnow = 0;
      } else {
          $dotnow = 1;
      }
      #########################################
      # still running, see if more of the log #
      # was written by resetting EOF flag     #
      #########################################
      seek TAIL,0,1; # reset EOF flag
    }
    close(TAIL);
  }
  clean_children();
  &get_retcode;
  return($RETCODE);
}
####################################################################
#           SUBROUTINES
#           SUBROUTINES
#           SUBROUTINES
####################################################################

####################################################################
# get retcode
####################################################################
sub get_retcode {
    my $grc = `/bin/ksh -c "/bin/egrep -i FAIL $LOGFILE 2> /dev/null"`;
    if ($grc ne "") {
        $RETCODE = 1;
    } else {
        $RETCODE = 0;  # did not find fail, good completion
    }
}

####################################################################
# fork mpat-lbf job
####################################################################

sub fork_mlbf {

    my $lbf_test = "${BINDIR}/mpat_lbf -v -p $HA_ID -i 1 -k $LBF_XFR_SZ -n $LBF_PASSES";
    $lbf_test .= " -f $PATTERNFILE" if $PATTERNFILE;
    $lbf_test .= " -c" if $COMPARE_F;
    my @lbf_cmd = "$lbf_test";
    print "t_seqrl cmd = $lbf_test\n" if ($DEBUG);

    if ($pid = fork) {
        # parent here
        # child process pid is available in $pid
        $LBF_CHILD_PID = $pid;
        return($pid);
    } elsif (defined $pid) {
        # child here
        # parent process pid is available with getppid
        $is_child = 1;
        if (! (open LF, ">$LOGFILE")) {
            print "Cannot write to $LOGFILE\n";
            exit 1;
        }
        my $oldfh = select LF; 
        $| = 1; 

        select $oldfh;
        print LF "$lbf_test $LOGFILE\n";
        print LF "\t====================================================\n";
        close(LF);
        POSIX::close(1);  #do this in a more appropriate UNIX fashion.
        $fd = POSIX::open("$LOGFILE", O_CREAT, 0777);
        POSIX::close($fd);
        $fd = POSIX::open("$LOGFILE", O_RDWR);

        $rc = 0xffff & exec($lbf_test);
        exit($rc);
    } elsif ($! =~ /No more process/) {
        printf("Fork error:  No more processes\n");
        printf("Please check your system for rogue processes\n");
        exit(1);
    } else {
        # weird fork error
        die "Can't fork: $! \n";
    }
}

####################################################################
# fork dex32 jobs
####################################################################
sub fork_dex32 {
    my $dex32_test = "${BINDIR}/dex32";
    my $dex32_seqr = "-v -S1 8g 0 720m -x 128k -r";
    my ($devices, $tested_drives, $i, $dex32_cmd);
    $dex32_seqr = $ENV{'DEX_ARGS'} if ($ENV{'DEX_ARGS'});
    ################################################
    # All drives on this loop targeted for testing.#
    # If T300 drives are on this loop, then remove #
    # alternate path if in primary mode.           #
    ################################################
    if ($T300_DRIVES{$HA_ID} ne "") {
        ########################################
        # T300 drives are on this loop. Remove #
        # alternate path if in primary mode.   #
        ########################################
        $SELECTED_DRIVES = $T300_DRIVES{$HA_ID};
        $SELECTED_DRIVES .= " $A5000_DRIVES{$HA_ID}" if ($A5000_DRIVES{$HA_ID} ne "");
        $SELECTED_DRIVES .= " $OTHER_DRIVES{$HA_ID}" if ($OTHER_DRIVES{$HA_ID} ne "");
    } else {
        $SELECTED_DRIVES .= " $A5000_DRIVES{$HA_ID}" if ($A5000_DRIVES{$HA_ID} ne "");
        $SELECTED_DRIVES .= " $OTHER_DRIVES{$HA_ID}" if ($OTHER_DRIVES{$HA_ID} ne "");
    }
    @drives = split / /, $SELECTED_DRIVES;
    $SELECTED_DRIVES = "";
    foreach $drive ( @drives ) {
         next if ($drive eq "");
         if (($T300{$drive} eq "p") && ($DUAL_PORT eq $PRIMARY)) {
             ############################################
             # Primary drive and we are in primary mode #
             # so allow testing to it.                  #
             ############################################
             $SELECTED_DRIVES .= "/dev/rdsk/${drive}s2 ";
         } elsif (($T300{$drive} eq "a") && ($DUAL_PORT eq $ALTERNATE)) {
             ################################################
             # Alternate drive and we are in alternate mode #
             # so allow testing to it.                      #
             ################################################
             $SELECTED_DRIVES .= "/dev/rdsk/${drive}s2 ";
         } elsif ($T300{$drive} eq "") {
             ############################################
             # Not a T300 drive, so allow testing to it #
             ############################################
             $SELECTED_DRIVES .= "/dev/rdsk/${drive}s2 ";
         } else {
             print "Skipping drive $drive";
             print " (primary T300 LUN)" if (($T300{$drive} eq "p") && ($DUAL_PORT eq $ALTERNATE));
             print " (alternate T300 LUN)" if (($T300{$drive} eq "a") && ($DUAL_PORT eq $PRIMARY));
             print "\n";
         }
    }
    chop $SELECTED_DRIVES if ($SELECTED_DRIVES =~ / $/); # remove trailing space

    @drives = split / /, $SELECTED_DRIVES;
    $tested_drives = "";
    foreach $drive ( @drives ) {
        #############################
        # check for unlabeled LUN's #
        #############################
        $cmd = "/usr/bin/ksh -c \'/usr/sbin/devinfo -i $drive 2>&1\'";
        $results = `$cmd`;
        chomp $results;
        if ($results =~ /Invalid argument|I\/O error|No such file/) {
            print "WARNING: Possible unmounted or unlabeled disk/LUN, not testing $drive\n";
        } else {
            $tested_drives .= "$drive ";
        }
    }

    if ($tested_drives eq "") {
        print "WARNING: Disk exerciser not started on $CONUM, no drives available\n";
        return 0
    }

    $dex32_cmd = "$dex32_test $dex32_seqr $tested_drives";

    if ($pid = fork) {
        # parent here
        # child process pid is available in $pid
        return($pid);
    } elsif (!$pid) {
        # child here
        # parent process pid is available with getppid
        $is_child = 1;
        print "$dex32_cmd \n" if ($DEBUG);
        
        $status = POSIX::close(1);
        if ( $status ) {
            print "POSIX::close(1) failed, status: $status\n";
        }
        $error =  exec($dex32_cmd);
        exit($error);
    } elsif ($! =~ /No more process/) {
        # EAGAIN, supposedly recoverable fork error
        # print "\nDEBUG:  no more processes \n";
        printf("Fork error:  No more processes\n");
        printf("Please check your system for rogue processes\n");
        exit(1);
    } else {
        die "Can't fork: $! \n";
    }
}

####################################################################
# clean up all child procs
####################################################################
sub clean_children {

    foreach $i (@dex32Array) {
        `/usr/bin/ksh -c '/bin/kill $i 2>/dev/null'` if $i;
    }
    foreach $i (@mlbfArray) {
        `/usr/bin/ksh -c '/bin/kill $i 2>/dev/null'` if $i;
    }

}

####################################################################
# process command line interrupts
####################################################################
sub t_proc_cli {
    my (@cli) = @_;
    if ($#cli < 5) {
        die "t_seqrl requires some arguments\n";
    }

    $arg = shift @cli;

    while ($arg) {
        if ($arg =~ /^-l/i) {
            $LOG_MARK = shift @cli;
            if ($LOG_MARK eq "") {
                die @usage;
            }
        } elsif ($arg =~ /^-s/i) {
            $HA_ID = shift @cli;
            if ($HA_ID eq "") {
                # check that HA exists
                die @usage;
            }
        } elsif ($arg =~ /^-c/i) {
            $CONUM = shift @cli;
            if ($CONUM eq "") {
                # check that controller number exists
                die @usage;
            }
        } elsif ($arg =~ /^-f/i) {
            $PATTERNFILE = shift @cli;
            die "$PATTERNFILE not found\n" if (! -e $PATTERNFILE);
            if (open PATTERNS, "<$PATTERNFILE") {
                my $p_count = 0;
                my ($i, $j);
                while ($i = <PATTERNS>) {
                    $p_count ++;
                    chomp $i;
		    if($i eq '')
		    {
		       print "\nError: Detected blank line on line $p_count.\n";
		       die "\n";

		    }

                    $j = &input_pattern($i); # verify valid pattern
                    if ($j eq '') {
                        # found an illegal pattern, reject file
                        die "\nIllegal pattern \'$i\' detected on line $p_count.\n";
                    }
                }
                close PATTERNS;
            } else {
                die "\nCould not open $PATTERNFILE\n";
            }
        } elsif ($arg =~ /^-k/i) {
            $LBF_XFR_SZ = shift @cli;

        } elsif ($arg =~ /^-n/i) {
            $LBF_PASSES = shift @cli;

        } elsif ($arg =~ /^-d/i) {
            $COMPARE_F = "ON";
    
        } elsif ($arg =~ /^-m/i) {
            $DUAL_PORT = shift @cli;
            if ( ( $DUAL_PORT ne $PRIMARY ) &&
                 ( $DUAL_PORT ne $ALTERNATE ) &&
                 ( $DUAL_PORT ne $BOTH_AP ) &&
                 ( $DUAL_PORT ne $NONE ) ) {
                die @usage;
            }

        } else {
            die @usage;
        }

        $arg = shift @cli;
    }
}

