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

####################################################################
#
# PROGRAM NAME : storstat
#
# This program checks the patch and firmware levels for the A5000.
# It uses the a5k-config-matrix included with the STORtools
# package to check patch and firmware levels.
#
# Joe Harman's "Official SSA & A5000 Software/Firmware Configuration 
# Matrix" is the basis for the patch and firmware check.
# Checking the patch and firmware levels for the A5000 is a very
# complex process and includes checks for OS, System Type, System
# architecture, system bus, and disk types. Always retrieve the
# latest version of STORtools when checking out an installation
# so that you can take advantage of the new features included
# in storstat to support new versions of the patch matrix.
# This program supports checks for
#	REV 1.14, 1.15, 1.16, 1.19
# The default behavior is always to check against the latest
# REV of the patch matrix.
#
# This program written by David Halliwell SQE Network Storage
# 
# Copyright (c) 1999  Sun Microsystems Computer Corporation, Inc.
####################################################################

# Program style guidelines
#   1) Upper case variables are intended to be globals.
#          (This is the default in PERL)
#      my or local variables should be lower case
#   2) This program uses 4 space indentation for tabs
#   3) Run perl -cw storstat.pl before checking in changes
#      and resolve any problems.

$PKGDIR     = "/var/opt/STORtools";
$LOGDIR     = "${PKGDIR}/logs";
$BINDIR     = "${PKGDIR}/bin";

# The patch and firmware configuration file included
# with stortools. This can also be specified on the 
# command line.
$CONFIGURATION_MATRIX = "${BINDIR}/a5k-config-matrix";

# Be sure to avoid subroutine name duplication between
# any files defined in these require statements and
# any subroutines defined in this program.
require  "$BINDIR/subroutines.pm";
require  "$BINDIR/pcisubroutines.pl";

# storstat sets the PASS/FAIL criteria in ss_logger
# Individual sections maintain their own PASS/FAIL
# criteria and when messages are sent to
# /var/adm/messages any ERROR messages will
# set the global program result for storstat.

# Global variables use upper case letters

$PROGNAME = "storstat";

$SIG{INT} = 'int';

&check_root_access( $PROGNAME );

# Check for DEBUG flag
$DEBUG = "TRUE" if ($ENV{'DEBUG'}); 

# Define a log for ERROR messages
$ERROR_LOG  = "${LOGDIR}/error_log";

# Some system information
# uname -i platform => SUNW,Ultra-1
chomp($PLATFORM = `/usr/bin/uname -i`);

# uname -r release  => 5.6
chop($RELEASE  = `/bin/uname -r`);

# get the Veritas release
&get_veritas_version;

# get the Soltice release
&get_solstice_version;

# Default case is PASS
$ERROR_FLAG = "PASS";

# $LUXADM_PROBE contains the output from luxadm probe

# Store the output from luxadm display by enclosure name
# in a hash array
# $LUXADM_DISPLAY{$enclosure_name} contains the output for each enclosure

# Store enclosure names and wwn
# $ENCLOSURE{$name} = $enc_wwn

# Save disk inquiry data in
# @INQUIRY

# Flush buffers for each print
$| = 1;

# Main program

# Get command line arguments sets numerous globals (Upper case)
# used in conditional execution
&get_command_line_arguments;

# We are using our own version of this matrix and this
# routine must be updated every time the matrix gets upgraded
&get_configuration_matrix;

# Open the Log file
open(MAIL, ">${ERROR_LOG}") or die "Unable to open mail log : $ERROR_LOG\n";

# All tests or FAN Check or Power supply check 
if ($ALL or $FAN or $FW or $MIN or $PS or $PC or $DISK or $FRU) {
    print "\nSYSTEM QUERY:\n" if ($VERBOSE);
    &luxadm_probe;
    &get_enclosure_names; 		# From LUXADM_PROBE set in &luxadm_probe
    &luxadm_display_enclosures;
} 

# All tests or patch check
if (($ALL or $PC) and ($PATCH_CHECK_REQUIRED{$RELEASE})) {
    &patch_check;
    &veritas_patch_check;
    &solstice_patch_check;
}
# All tests or fan check
if ($ALL or $FAN or $FRU) {
    &check_fans;
}

# All tests or Power supply check
if ($ALL or $MIN or $FRU) {
    # This sets DISK_COUNT used in the IB fw check
    &check_min_configuration;
}

# All tests or firmware tests
if (($ALL or $FW) and ($PATCH_CHECK_REQUIRED{$RELEASE})) {
    &inquiry;
    &check_disk_firmware;
    &check_ib_firmware if ($ENCLOSURES_FOUND);
    if ($HBA eq "S") {
	&check_socal_driver;
        &check_socal_fcode;
    } elsif ( $HBA eq "P" ) {
	&check_ifp_driver;
        #this functionality is currently broken - fix in March release.
        #&check_pci_fcode;
    } else {
	print "Undefined HBA type in $PLATFORM\n";
    }
    &check_103346;
}
# All tests or Power supply check
if ($ALL or $PS or $FRU) {
    &check_power_supplies;
}
# All tests or Power supply check or FRU check
if ($ALL or $DISK or $FRU) {
    &check_disk_status;
}

# Determine if an error was encountered and if so remind them.
if ($VERBOSE and ($ERROR_FLAG eq "FAIL")) {
    print "\nErrors were detected during the program execution!\n";
    print "Please review the program output and perform any \n";
    print "required upgrades and replace any defective components.\n";
}

if ($VERBOSE) {
    print "\nThis check was done against the \"Official SSA & A5000 Software\n";
    print "Firmware Configuration Matrix\" Rev ${PATCH_MATRIX_REVISION}\n";
    print "Always run with the latest version of this program!\n";
}

# If no patch matrix is defined for this release
if (! $PATCH_CHECK_REQUIRED{$RELEASE}) {
    print "No patch matrix defined for this release : $RELEASE\n";
    print "No patch or firmware checking possible!\n";
}

print "Program result (Rev. ${PATCH_MATRIX_REVISION}) : $ERROR_FLAG\n";

# If we are sending mail close the file and send it
close(MAIL);
if ($MAIL_RECIPIENT) {
    system("/usr/bin/mailx -s \"A5000:DIAG: Errors or Warnings detected!\" $MAIL_RECIPIENT < $ERROR_LOG");
}

if ($ERROR_FLAG) {
    exit 1;
} else {
    exit 0;
}

##############################################
#
#		Functions
#
##############################################

sub get_command_line_arguments {

    # For every command line argument update the usage array
    my @usage = (
    "$PROGNAME -all | -disk | -fan | -fru | -fw | -min | -pc | -ps \n",
    "          [-log] [-mail name] [-mf matrix_file] [-P] [-S] [-v] [-warn] [-dots] \n",
    "all  - Perform all checks\n",
    "disk - Perform disk status checks\n",
    "dots - show dots during enclosure queries\n",
    "fan  - Check Fans\n",
    "fru  - Check FRU's disks, fans, power supplies\n",
    "fw   - Firmware check\n",
    "help - prints out the usage\n",
    "log  - Turn on logging to /var/adm/messages\n",
    "mail - mail errors to admin\n",
    "mf   - patch matrix file",
    "min  - Check for recommended disk placement\n",
    "P    - Select PCI Host Adapters\n",
    "pc   - Patch check\n",
    "ps   - Check power supply status\n",
    "S    - Select SBUS Host Adapters (default)\n",
    "v    - Verbose mode\n",
    "warn - Mail warnings as well as errors\n",
    );

    if ($#ARGV<0) {
	print "No arguments!\n";
	die @usage;
    }
    my $do_something = "FALSE";

    $arg = shift @ARGV;
    while($arg) {
	# For every command line argument have an entry
	# It's more readable to keep these items in order
	# Set a unique global variable for each argument
	if ($arg =~ /help/)  {
	    die @usage if ($arg =~ /help/); 
	} elsif ($arg =~ /-all/) {
	    $ALL          = TRUE;
	    $do_something = "TRUE";
        } elsif ($arg =~ /-disk/) {
	    $DISK         = TRUE;
	    $do_something = "TRUE";
        } elsif ($arg =~ /-fan/) {
	    $FAN          = TRUE;
	    $do_something = "TRUE";
        } elsif ($arg =~ /-fru/) {
	    $FRU          = TRUE;
	    $do_something = "TRUE";
        } elsif ($arg =~ /-fw/) {
	    $FW           = TRUE;
	    $do_something = "TRUE";
	} elsif ($arg =~ /-log/) {
	    $LOGGING      = TRUE;
	} elsif ($arg =~ /-mail/) {
	    $MAIL           = TRUE;
            $MAIL_RECIPIENT = shift @ARGV;
	} elsif ($arg =~ /-mf/i) {
	    $CONFIGURATION_MATRIX = shift @ARGV;
	} elsif ($arg =~ /-min/i) {
	    $MIN          = TRUE;
	    $do_something = "TRUE";
	} elsif ($arg =~ /-pc/) {
	    $PC           = TRUE;
	    $do_something = "TRUE";
	} elsif ($arg =~ /-ps/) {
	    $PS           = TRUE;
	    $do_something = "TRUE";
	} elsif ($arg =~ /-v/) {
	    $VERBOSE      = TRUE;
	} elsif ($arg =~ /-dots/) {
	    $SHOWDOTS	  = "TRUE";
	} elsif ($arg =~ /-warn/) {
	    $WARN         = TRUE;
        } elsif ($arg =~ /^-S/) {
            $HBA = "S";
            check_bus_option( $HBA );
        } elsif ($arg =~ /^-P/) {
            $HBA = "P";
            check_bus_option( $HBA );
	} else {
	    die @usage;	
	}

	# Get the next value. 
	# The last command in the loop
        $arg = shift @ARGV;
    }

    # Check to see that the configuration matrix exists
    if ( ! -e $CONFIGURATION_MATRIX) {
        print "The configuration matrix does not exist : ";
        print "$CONFIGURATION_MATRIX\n";
        die   "@usage";
    }
    if ( ! -r $CONFIGURATION_MATRIX) {
        print "The configuration matrix is not readable : ";
        print "$CONFIGURATION_MATRIX\n";
        die   "@usage";
    }
    if ( ! -f $CONFIGURATION_MATRIX) {
        print "The configuration matrix is not a regular file : ";
        print "$CONFIGURATION_MATRIX\n";
        die   "@usage";
    }
    if ( ! -T $CONFIGURATION_MATRIX) {
        print "The configuration matrix is not a text file : ";
        print "$CONFIGURATION_MATRIX\n";
        die   "@usage";
    }

    # Did we set a required flag?
    if ($do_something eq "FALSE") {
	print "No required parameters specified!\n";
        die @usage;
    }

    # The mail recipient must be specified if we want to send mail
    die @usage if ($MAIL and (! $MAIL_RECIPIENT));

    # If the environment variable VERBOSE is set enable verbose mode
    $VERBOSE = TRUE if ($ENV{'VERBOSE'});	

    if ( ( $HBA ne "P" ) and ( $HBA ne "S" ) ) {
        &get_A5000_hbas;
        &select_A5000_hbas;
    }
    print "HBA: $HBA\n" if ($DEBUG);
}

sub luxadm_probe {

    print "\tluxadm probe\n" if ($VERBOSE);
 
    # Probe once and store in a global variable
    print STDERR "." if $SHOWDOTS;
    $last_command = "/usr/sbin/luxadm probe";
    $LUXADM_PROBE = `/usr/bin/ksh -c '/usr/sbin/luxadm probe 2>&1'`;
    print "$LUXADM_PROBE" if ($DEBUG);
    if ($LUXADM_PROBE =~ /No Network Array enclosures found/) {
	print "\n\t${LUXADM_PROBE}\n" if ($VERBOSE);
    } elsif ($LUXADM_PROBE =~ /Error:/) {
	# This covers : I/O errors : Connection timeout Errors : ...
	print "\t",$LUXADM_PROBE;
        &ss_logger("ERROR", 4019, "luxadm parse error : luxadm probe");
	print "luxadm probe must complete without errors to complete the patch/firmware/FRU check.\n";
	print "The usual cause of this problem is a heavy workload causing\n";
	print "loop problems associated with the loop initialization primitive (LIP). \n";
	die   "Please resolve this issue and run the program again.\n";
    }
    $last_command = "";
}

sub get_enclosure_names {

    my @data = split(/\n/, $LUXADM_PROBE);

    my ($line, $name, $enc_wwn);

    foreach $line (@data) {
        chomp $line;
        next if ($line !~ /WWN/);
	# Example line SENA may change
        # SENA               Name:z   Node WWN:50800200000001a0   
        ($name, $enc_wwn) = $line =~ /Name:(\S+)\s.*WWN:(\w+)/;
	$ENCLOSURE{$name} = $enc_wwn;
	$ENCLOSURES_FOUND = "TRUE";
    }
    if ($DEBUG) {
	print "DEBUG : ENCLOSURES:\n";
	foreach $key (keys %ENCLOSURE) {
	    print "$key = $ENCLOSURE{$key}\n";
        }
    }
}

sub luxadm_display_enclosures {
    
    # Get all the data for all the enclosures. In the interest of saving
    # time, issue a single command rather than individual commands for
    # each enclosure.

    my $key;

    @enclosurenames = sort (keys %ENCLOSURE);
    foreach $key (@enclosurenames) {
        $allenclosures .= " $key";
    }

    my $enclosure_count = $#enclosurenames;	
    $enclosure_count++;
    if ($enclosure_count > 1) {
        print "\tluxadm display on $enclosure_count enclosures\n" if $VERBOSE;
    } else {
        print "\tluxadm display on $enclosure_count enclosure\n" if $VERBOSE;
    }

    $keyptr = 0;
    $curkey = "";
    $lux_out = `/usr/bin/ksh -c '/usr/sbin/luxadm display $allenclosures 2>&1'`;
    @lux_out = split(/\n/, $lux_out);
    foreach $curline (@lux_out) {
    	if ($curline =~ /luxadm version:/) {
	#
	# Trigger to capture output for next enclosure
	#
        	$curkey = $enclosurenames[$keyptr];
		print STDERR "." if $SHOWDOTS;
		$keyptr += 1;
        }
        if ($curkey) {
        	$LUXADM_DISPLAY{$curkey} .= "$curline\n";
        }
    }
    print STDERR "\n" if $SHOWDOTS;

    # Check that luxadm probe completed
    my $luxadm_display_parse_error = "";
    foreach $key (keys %LUXADM_DISPLAY) {
	if ($LUXADM_DISPLAY{$key} !~ /Language/) {
	    &ss_logger("ERROR", 4019, "luxadm parse error : luxadm display $key");
	    $luxadm_display_parse_error = "TRUE";
	}
    }
    if ($luxadm_display_parse_error) {
        print "${PROGNAME} has dependencies on luxadm display output!\n";
	die "Please resolve the luxadm display issues before running the program again.\n";
    }

    # Debug code added to check data structure integrity
    if ($DEBUG) {
	print "DEBUG : LUXADM DISPLAY\n";
	foreach $key (keys %LUXADM_DISPLAY) {
		print "########## Enclosure name = ${key} #############\n";
		print "$LUXADM_DISPLAY{$key}";
	}
    }
}

sub check_power_supplies {

    my (@luxadm, $line, $luxadm_display, $index, $enclosure, $status);
    print "\nPOWER SUPPLIES\n" if ($VERBOSE);
    foreach $enclosure (keys (%LUXADM_DISPLAY)) {

        $PS_COUNT = 0;
	$power_supply_string_found = "";

	print "\n\tChecking enclosure $enclosure\n" if ($VERBOSE);

	@luxadm = split(/\n/, $LUXADM_DISPLAY{$enclosure});
        foreach $index (0 .. $#luxadm ) {

	    if ($line =~ /Error: Invalid path name/) {
	        print "Inconsistency detected between luxadm probe and display\n";
	        print "for enclosure $key\n";
		last;
	    } 

            next if ($luxadm[$index] !~ /^Power Supplies/);
	    $power_supply_string_found = "TRUE";

	    if ($VERBOSE) {
	        print "\t$luxadm[$index]\n";
	        print "\t$luxadm[$index+1]\n";
	    }
            ($ps0_stat, $ps1_stat, $ps2_stat) = $luxadm[$index+1] =~
		        /\s+0\s+(.+)\s+1\s+(.+)\s+2\s+(.+)/;
	
            $status = &check_ps_stat(0, $ps0_stat, $enclosure);

            $status = &check_ps_stat(1, $ps1_stat, $enclosure);

            $status = &check_ps_stat(2, $ps2_stat, $enclosure);

	    last;
	}
	if (! $power_supply_string_found) {
	    print "Program error: power supply information not found while\n";
	    print "parsing the luxadm display $enclosure output.\n";
	    &ss_logger("ERROR", 4019, "luxadm parse error");
	} else {
	    print "\tCount of O.K. power supplies = $PS_COUNT\n" if $VERBOSE;
	    if ($PS_COUNT >= 2) {
	        print "\tPASS\n" if ($VERBOSE);
            } elsif ($PS_COUNT == 1) {
	        &ss_logger("ERROR", 4008, "Need two or more power supplies");
	        print "\tFAIL\n" if ($VERBOSE);
	    }
        }
    }
}

sub check_ps_stat {

    my ($ps_num, $ps_stat, $enclosure) = @_;

    my $status = "PASS";

    print "\tPower supply ${ps_num} status = $ps_stat\n"
        if ($VERBOSE);

    if ($ps_stat =~ /^O\.K\./) {
        $PS_COUNT++;
    } else {
	&ss_logger("WARNING", 3000, "Enclosure $enclosure : Power Supply $ps_num $ps_stat");
	$status = "FAIL";
    }
    return $status;
}

sub check_fans {

    my (@luxadm, $line, $luxadm_display, $index, $enclosure, $status);
    print "\nFAN CHECK:\n" if ($VERBOSE);
    foreach $enclosure (keys (%LUXADM_DISPLAY)) {

        $FAN_COUNT = 0;

	print "\n\tChecking enclosure $enclosure\n" if ($VERBOSE);

	@luxadm = split(/\n/, $LUXADM_DISPLAY{$enclosure});
        foreach $index (0 .. $#luxadm ) {

	    if ($luxadm[$index] =~ /Error: Invalid path name/) {
	        print "Inconsistency detected between luxadm probe and display\n";
	        print "for enclosure $key\n";
		last;
	    } 

            next if ($luxadm[$index] !~ /^Fans/);

	    if ($VERBOSE) {
	        print "\t$luxadm[$index]\n";
	        print "\t$luxadm[$index+1]\n";
	    }
            ($fan0_stat, $fan1_stat) = $luxadm[$index+1] =~
		        /^\s+0\s+(\S+)\s+1\s+(\S+)/;
	
            $status = &check_fan_stat(0, $fan0_stat, $enclosure, 
			   $luxadm[$index], $luxadm[$index+1]);

            $status = &check_fan_stat(1, $fan1_stat, $enclosure, 
			   $luxadm[$index], $luxadm[$index+1]);

	    last;

	}
	print "\tCount of O.K. fans = $FAN_COUNT\n" if $VERBOSE;
        if ($FAN_COUNT == 2) {
	    print "\tPASS\n" if ($VERBOSE);
  	} else {
            &ss_logger("ERROR", 4018, "Fan Failure in enclosure : $enclosure");
	    print "\tFAIL\n" if ($VERBOSE);
        }
    }
}

sub check_fan_stat {

    my ($fan_num, $fan_stat, $enclosure, $line1, $line2) = @_;

    my $status = "PASS";

    print "\tFan ${fan_num} status = $fan_stat\n"
        if ($VERBOSE);

    if ($fan_stat =~ /^O\.K\./) {
        $FAN_COUNT++;
    } else {
        print "\t$line1\n"               if ($VERBOSE);
        print "\t$line2\n"               if ($VERBOSE);
	$status = "FAIL";
    }
    return $status;
}

sub patch_check {

    my $patch_error_flag = "PASS";
    my $result           = "PASS";

    print "\nPATCH CHECK: OS $RELEASE\n" if ($VERBOSE);

    if ($RELEASE eq "5.5.1") {
	foreach $patch (sort (keys %PATCH_MATRIX_251)) {
	    $result = &check_patch($patch, $PATCH_MATRIX_251{$patch});
	    if ($result eq "FAIL") {
		$patch_error_flag = "FAIL";
	    }
	}
    } elsif ($RELEASE eq "5.6") {
	foreach $patch (sort (keys %PATCH_MATRIX_26)) {
            $result = &check_patch($patch, $PATCH_MATRIX_26{$patch});
            if ($result eq "FAIL") {
                $patch_error_flag = "FAIL";
            }
        }
    # Release string is changing, check if the release contains 7
    } elsif ($RELEASE =~ /7/) {
	foreach $patch (sort (keys %PATCH_MATRIX_7)) {
            $result = &check_patch($patch, $PATCH_MATRIX_7{$patch});
            if ($result eq "FAIL") {
                $patch_error_flag = "FAIL";
            }
        }
    } else {
	&ss_logger("ERROR", 4000, "Unsupported version of the OS $RELEASE");
	$patch_error_flag = "FAIL";
    }	
    if ($patch_error_flag eq "FAIL" ) {
	print "\n" if ($VERBOSE);
#        if ($VERBOSE) {
#	    print "Please obtain the latest version of the patch list from:\n";
#	    print "    http://storageweb.eng/tm/arrays/matrix.ps\n";
#	    print "The patch installation is complex and should follow the guidelines in:\n";
#           print "    http://storageweb.eng/techmark_site/photon/main/seq.txt";
#	    print "Patch sites:\n";
#	    print "    /net/oss.corp/patchdb/patchdb\n";
#	}
    }	
    print "\n\t$patch_error_flag\n" if ($VERBOSE);
}

sub inquiry {

    # Set the global array INQUIRY
    # so we only have to do this once
    $last_command = "${BINDIR}/disk_inquiry";
    $inquiry = `/usr/bin/ksh -c '${BINDIR}/disk_inquiry 2>&1'`;
    $last_command = "";
    @INQUIRY = split(/\n/, $inquiry);
}

sub check_disk_firmware {

    my $line;
    # This variable is used to print any messages related
    # to an error associated with the SEAGATE drives
    my $disk_status = "PASS";
    my $drive_found = "FALSE";	
    my $req_106129  = "FALSE";
    print "\nDISK FW CHECK : \n" if $VERBOSE;
    foreach $line (@INQUIRY) {
	# I would like to split the whole line but the field seperator is not consistent
	# and there can be six or seven fields caused by blanks in the label
	($current_rev) = $line =~ /\s+(\S+)\s+\S+\s*$/;
	($device)      = $line =~ /\s(c\d+t\d+d\d+)\s/;
	# 14 Drive photons
 	if (($line =~ /SEAGATE/) and ($line =~ /ST19171FC/)) {
	    $drive_found = "TRUE";
	    if ($current_rev !~ /^$ST19171FC/) {
		# Set the local status
		$disk_status      = "FAIL";
	        $req_106129       = "TRUE";
		&ss_logger("ERROR", 4003, "Incorrect disk fw : $device : ST19171FC : has $current_rev : needs $ST19171FC");
	    } else {
		print "\t$line\n" if $VERBOSE;
	    }
        }
	# 22 Drive photon - Not yet in the matrix but we need to check this
 	if (($line =~ /SEAGATE/) and ($line =~ /ST39102F/)) {
	    $drive_found = "TRUE";
	    if ($current_rev !~ /^$ST39102F/) {
		# Set the local status
		$disk_status = "FAIL";
		&ss_logger("ERROR", 4003, "Incorrect disk fw : $device : ST39102F : has $current_rev : needs $ST39102F");
	    } else {
		print "\t$line\n" if $VERBOSE;
	    }
        }
	# Seagate 18G
 	if (($line =~ /SEAGATE/) and ($line =~ /ST118273/)) {
	    $drive_found = "TRUE";
	    if ($current_rev !~ /^$ST118273/) {
		# Set the local status
		$disk_status = "FAIL";
		&ss_logger("ERROR", 4003, "Incorrect disk fw : $device : ST118273 : has $current_rev : needs $ST118273");
	    } else {
		print "\t$line\n" if $VERBOSE;
	    }
        }
    }
    if ($drive_found =~ /FALSE/) {
	$disk_status = "NORESULT";
        print "\tNo drives matched the patch matrix!\n" if $VERBOSE;
    }
    # Tell them which patch to install
    if ($req_106129 eq "TRUE") {
        print "    Patch 106129 will upgrade disk firmware for Seagate ST19171FC drives.\n"
    }
    print "\t$disk_status\n" if $VERBOSE;
}	

sub check_ib_firmware {
    my $line;
    my $ib_status   = "PASS";
    my $expected    = "";

    print "\nIB FW CHECK : \n" if $VERBOSE;
    $ib_status       = "PASS";
    $found_display   = "FALSE";
    $ib_section      = "";	
    # foreach enclosure
    foreach $enclosure (sort (keys (%LUXADM_DISPLAY))) {
	$found_display  = "TRUE";
        undef @luxadm_display;
	@luxadm_display = split(/\n/, $LUXADM_DISPLAY{$enclosure});
        foreach $line (@luxadm_display) {

	    # Check to see if any IB boards are missing
	    $ib_section = "TRUE" if ($line =~ /^ESI/);
	    if ($ib_section eq "TRUE") {
		if ($line =~ /^\s+(\S): Not Installed/) {
			&ss_logger("WARNING", 3021, "Enclosure $enclosure : Board $1 : IB Board missing"); 
		}
	    }
	    $ib_section = "FALSE" if ($line =~ /^DISK/);

	    # Check IB firmware
            if ($line =~ /^[\w\s]+:\s*(\S+)\s*.*WWN/i) {

		$current_ib_fw = $1;

		if ($DISK_COUNT{$enclosure} eq "14") {
		    $expected = $IB_5000;
		    # If the spec has | we should use regular expressions
		    if ($IB_5000 =~ /\|/) {
		        if ($current_ib_fw !~ /$IB_5000/) {
			    $ib_status     = "FAIL";
		            &ss_logger("ERROR", 4004, 
				"Enclosure $enclosure : Downrev IB FW : Expected $IB_5000 found $current_ib_fw");
		        }
	            } else {
		        #  Do a numeric comparison 
		        if ($current_ib_fw < $IB_5000) {
		            $ib_status     = "FAIL";
		            &ss_logger("ERROR", 4004, 
				"Enclosure $enclosure : Downrev IB FW : Expected $IB_5000 found $current_ib_fw");
			}
		    }

		} elsif ($DISK_COUNT{$enclosure} eq "22")  {
		    $expected = $IB_5200;
		    # If the spec has | we should use regular expressions
		    if ($IB_5200 =~ /\|/) {
		        if ($current_ib_fw !~ /$IB_5200/) {
			    $ib_status     = "FAIL";
		            &ss_logger("ERROR", 4004, 
				"Enclosure $enclosure : Downrev IB FW : Expected $IB_5200 found $current_ib_fw");
		        }
	            } else {
		        #  Do a numeric comparison 
		        if ($current_ib_fw < $IB_5200) {
		            $ib_status     = "FAIL";
		            &ss_logger("ERROR", 4004, 
				"Enclosure $enclosure : Downrev IB FW : Expected $IB_5200 found $current_ib_fw");
			}
		    }
	        }
		    
		if ($ib_status eq "FAIL") {
		    &ss_logger("INFO", 1001, "$line");
		}
	    }
        }
        print "\tEnclosure $enclosure : expected $expected : found $current_ib_fw\n" if ($VERBOSE);
    }
    if ($found_display eq "FALSE") {
        print "No A5000 enclosures detected!\n";
	$ERROR_FLAG = "NORESULT";
        if ($HBA eq "P") {
            print "Please verify correct hardware connectivity, functionality and\n";
            print "minimum software/firmware requirements. You may need to attach\n";
            print "a SCI system to use luxadm if the IB firmware is downreved.\n";
        }

    }	
    if ($VERBOSE and ($ib_status eq "FAIL")) {
        if ($RELEASE =~ /5.5.1/) {           
            print "\tInstall patch 105310 and follow the special instructions.\n";
        } elsif ($RELEASE =~ /5.6/) {          
            print "\tInstall patch 105375 and follow the special instructions.\n";              
        }  elsif ($RELEASE =~ /7/) {
	    print "\tPatch not available for S7 to upgrade your firmware.\n";
	}
    }
    print "\t$ib_status\n" if ($VERBOSE);
}      


sub check_prom_revs {

#  103346-13
#Current System Board PROM Revisions:
#------------------------------------
#Board  0: CPU/Memory OBP   3.2.10 1997/09/08 13:47 POST  3.7.2 1997/09/04 09:51
#Board  2: CPU/Memory OBP   3.2.10 1997/09/08 13:47 POST  3.7.2 1997/09/04 09:51
#Board  1: I/O Type 1 FCODE 1.8.1  1996/12/12 18:23 iPOST 3.4.2 1997/01/10 13:34
#Board  5: I/O Type 1 FCODE 1.8.1  1996/12/12 18:23 iPOST 3.4.2 1997/01/10 13:34
#Board  7: I/O Type 1 FCODE 1.8.1  1996/12/12 18:23 iPOST 3.4.2 1997/01/10 13:34
#
#
#Available 'Update' Revisions:
#-----------------------------
#          CPU/Memory OBP   3.2.12 1998/01/13 19:51 POST  3.8.4 1998/01/21 17:09
#          I/O Type 1 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.4 1997/08/26 17:37
#          I/O Type 2 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.4 1997/08/26 17:37
#          I/O Type 3 FCODE 1.8.7  1997/05/09 11:18 iPOST 3.0.2 1997/05/01 10:56
#          I/O Type 4 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.4 1997/08/26 17:37
#          I/O Type 5 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.4 1997/08/26 17:37
#
#Verifying Checksums: Okay

# 103346-19
# Current System Board PROM Revisions:
# ------------------------------------
#Board  0: CPU/Memory OBP   3.2.16 1998/06/08 16:58 POST  3.9.4 1998/06/09 16:25
#Board  2: CPU/Memory OBP   3.2.16 1998/06/08 16:58 POST  3.9.4 1998/06/09 16:25
#Board  4: CPU/Memory OBP   3.2.16 1998/06/08 16:58 POST  3.9.4 1998/06/09 16:25
#Board  1: I/O Type 1 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.6 1998/04/16 14:23
#Board  3: I/O Type 4 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.6 1998/04/16 14:23
#Board  5: I/O Type 1 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.6 1998/04/16 14:23
#Board  7: I/O Type 4 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.6 1998/04/16 14:23
# 
# 
# Available 'Update' Revisions:
# -----------------------------
#         CPU/Memory OBP   3.2.19 1998/10/20 18:13 POST  3.9.8 1998/11/09 15:09
#         I/O Type 1 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.8 1998/10/27 12:24
#         I/O Type 2 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.8 1998/10/27 12:24
#         I/O Type 3 FCODE 1.8.7  1997/05/09 11:18 iPOST 3.0.2 1997/05/01 10:56
#         I/O Type 4 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.8 1998/10/27 12:24
#         I/O Type 5 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.8 1998/10/27 12:24

    local ($result_proms) = "PASS";

    foreach $line (@FLASH) {
        print "$line\n" if ($VERBOSE);

#Board  2: CPU/Memory OBP   3.2.10 1997/09/08 13:57 POST  3.7.2 1997/09/04 09:51
#Board  0: CPU/Memory OBP   3.2.14 1998/03/24 13:34 POST  3.9.2 1998/03/04 11:32

	if ($line =~ /^Board\s+(\d+):\s+CPU\/Memory\s+OBP\s+(\S+).*POST\s+(\S+)/) {
	    $board_number = $1;
	    $current_obp  = $2;
	    $current_post = $3;
	    # Check the OBP
            print "\n\tBoard $board_number : CPU/Memory OBP $current_obp expected $CPU_OBP\n"
		if $VERBOSE;
            if (&version_ok($CPU_OBP, $current_obp)) {
                print "\tPASS\n" if ($VERBOSE);
            } else {
                print "\tFAIL\n" if ($VERBOSE);
		$result_proms = "FAIL";
	        &ss_logger("ERROR", 4005, "103346 not installed : Board $board_number : CPU/Memory OBP $current_obp expected $CPU_OBP");
            }
            # Check the POST
            print "\n\tBoard $board_number : CPU/Memory POST $current_post expected $CPU_POST\n"
		if $VERBOSE;
            if (&version_ok($CPU_POST, $current_post)) {
                print "\tPASS\n" if ($VERBOSE);
            } else {
                print "\tFAIL\n" if ($VERBOSE);
		$result_proms = "FAIL";
	        &ss_logger("ERROR", 4005, "103346 not installed : Board $board_number : CPU/Memory POST $current_post expected $CPU_POST");
            }
	}

#Board  1: I/O Type 1 FCODE 1.8.1  1996/12/12 18:23 iPOST 3.4.2 1997/01/10 13:34
        if ($line =~ /^Board\s+(\d+):\s+I\/O\s+Type\s+(\d+)\s+FCODE\s+(\S+).*iPOST\s+(\S+)/) {
  	    $board_number  = $1;
	    $board_type    = $2;
	    $current_fcode = $3;
	    $current_ipost = $4;
	    if ($VERBOSE) {
                print "\n\tBoard $board_number : I/O Type $board_type ";
                print ": FCODE found $current_fcode expected $IO_FCODE[$board_type] \n";
	    }
            if (&version_ok($IO_FCODE[$board_type], $current_fcode)) {
                print "\tPASS\n" if ($VERBOSE);
            } else {
                print "\tFAIL\n" if ($VERBOSE);
                &ss_logger("ERROR", 4006, "103346 not installed : Board $board_number : I/O Type $board_type : FCODE found $current_fcode expected $IO_FCODE[$board_type]")
            } 
	    if ($VERBOSE) {
                print "\n\tBoard $board_number : I/O Type $board_type ";
                print ": iPOST found $current_ipost expected $IO_IPOST[$board_type] \n";
	    }
            if (&version_ok($IO_IPOST[$board_type], $current_ipost)) {
                print "\tPASS\n" if ($VERBOSE);
            } else {
                print "\tFAIL\n" if ($VERBOSE);
                &ss_logger("ERROR", 4006, "103346 not installed : Board $board_number : I/O Type $board_type : iPOST found $current_ipost expected $IO_IPOST[$board_type]")
            } 
	}
    } 
}

sub run_flash_update {

#  103346-13
# -----------------------------
#           CPU/Memory OBP   3.2.12 1998/01/13 19:51 POST  3.8.4 1998/01/21 17:09
#           I/O Type 1 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.4 1997/08/26 17:37
#           I/O Type 2 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.4 1997/08/26 17:37
#           I/O Type 3 FCODE 1.8.7  1997/05/09 11:18 iPOST 3.0.2 1997/05/01 10:56
#           I/O Type 4 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.4 1997/08/26 17:37
#           I/O Type 5 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.4 1997/08/26 17:37
 
#  103346-19
# Current System Board PROM Revisions:
# ------------------------------------
# Board  0: CPU/Memory OBP   3.2.16 1998/06/08 16:58 POST  3.9.4 1998/06/09 16:25
# Board  2: CPU/Memory OBP   3.2.16 1998/06/08 16:58 POST  3.9.4 1998/06/09 16:25
# Board  4: CPU/Memory OBP   3.2.16 1998/06/08 16:58 POST  3.9.4 1998/06/09 16:25
# Board  1: I/O Type 1 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.6 1998/04/16 14:23
# Board  3: I/O Type 4 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.6 1998/04/16 14:23
# Board  5: I/O Type 1 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.6 1998/04/16 14:23
# Board  7: I/O Type 4 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.6 1998/04/16 14:23
# 
# 
# Available 'Update' Revisions:
# -----------------------------
#           CPU/Memory OBP   3.2.19 1998/10/20 18:13 POST  3.9.8 1998/11/09 15:09
#           I/O Type 1 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.8 1998/10/27 12:24
#           I/O Type 2 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.8 1998/10/27 12:24
#           I/O Type 3 FCODE 1.8.7  1997/05/09 11:18 iPOST 3.0.2 1997/05/01 10:56
#           I/O Type 4 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.8 1998/10/27 12:24
#           I/O Type 5 FCODE 1.8.7  1997/12/08 15:39 iPOST 3.4.8 1998/10/27 12:24
    # Check to see if the reconfigure file exists. This must be a global
    # so that the interupt routine can also check to see if the query
    # created the file.
    $RECONFIGURE = "FALSE";
    $RECONFIGURE = "TRUE" if (-e "/reconfigure");

    if ($VERBOSE) {
        print "\n\tflashprom messages if present are generated\n";
        print "\tby the flash-update program which appears to be \n";
        print "\tblocking Unix file redirection. They can be ignored.\n";
    }
    # Here shell scripts to get the flashupdate data
    # Not well defined in the patch matrix
    my $flashupdate;
    if ($PATCH_MATRIX_REVISION =~ /1.14/) {
	# Patch matrix version 1.14 used 103346-13 
        $last_command   = "${BINDIR}/flash-update-here-13";
        $flashupdate = `/usr/bin/ksh -c '${BINDIR}/flash-update-here-13 2>&1'`;
        $last_command   = "";
    } elsif ($PATCH_MATRIX_REVISION =~ /1.15|1.16/) {
	# Patch matrix version 1.15 uses 103346-19 
	$last_command   = "${BINDIR}/flash-update-here-19";
        $flashupdate = `/usr/bin/ksh -c '${BINDIR}/flash-update-here-19 2>&1'`;
        $last_command   = "";
    } elsif ($PATCH_MATRIX_REVISION =~ /1.19/) {
        # Patch matrix version 1.19 uses 103346-22
        $last_command   = "${BINDIR}/flash-update-here-22";
        $flashupdate = `/usr/bin/ksh -c '${BINDIR}/flash-update-here-22 2>&1'`;
        $last_command   = "";
    } else {
	if ($VERBOSE) {
	    print "The flash-update program to use for this revision ";
	    print "($PATCH_MATRIX_REVISION) is not defined.\n";
	    print "The default is to use 103346-22 for PROM checks\n";
	}
	$last_command   = "${BINDIR}/flash-update-here-22";
        $flashupdate = `/usr/bin/ksh -c '${BINDIR}/flash-update-here-22 2>&1'`;
        $last_command   = "";
    }

    # Global variable that contains flash update date 103346
    @FLASH = split(/\n/, $flashupdate);
    my $found_avalable;
    foreach $line (@FLASH) {

        $FOUND_PROM_REVS = "TRUE" if ($line =~ /^Available/); 

        if ($FOUND_PROM_REVS) {

#          CPU/Memory OBP   3.2.12 1998/01/13 19:51 POST  3.8.4 1998/01/21 17:09
           if ($line =~ /\s+CPU.*OBP\s+(\S+).*POST\s+(\S+)/) {
	        $CPU_OBP  = $1;
		$CPU_POST = $2;

#          I/O Type 1 FCODE 1.8.3  1997/11/14 12:41 iPOST 3.4.4 1997/08/26 17:37
            } elsif ($line =~ /\s+I\/O\s+Type\s+(\d+)\s+FCODE\s+(\S+).*iPOST\s+(\S+)/) {
	        $IO_FCODE[$1] = $2;
	        $IO_IPOST[$1] = $3;
	    }
        } 
    }
    if ((-e "/reconfigure") and ($RECONFIGURE eq "FALSE")) {
	system("rm /reconfigure");
    }
}

sub check_socal_fcode {

    my $fcode_result = "PASS";

    # Assumes fcode defined in the matrix 
    if (! $FCODE) {
	$fcode_result = "NORESULT";
	print "FCODE not defined for Solaris $RELEASE. Check patch matrix!\n";
        print "\t$fcode_result\n" if ($VERBOSE);
	return;
    } 
    my $count        = 0;
    print "\nCHECK FCODE : $FCODE\n" if ($VERBOSE);
    $last_command = "/usr/sbin/prtconf -pv | /bin/egrep 'device_type|version'";
    $prtconf_output  = `/usr/bin/ksh -c '/usr/sbin/prtconf -pv 2>&1' | /bin/egrep "device_type|version"`;
    $last_command = "";
    @prtconf_output  = split(/\n/, $prtconf_output);
    for($i=0;$i<=$#prtconf_output;$i++) {
        if ($prtconf_output[$i] =~ /^\s+device_type:.*socal/) {
            if ($prtconf_output[$i+1] =~ /^\s+version:.*FCode\s+(\S+)/) {
                $count++;
                $current_fcode = $1;
                if (! &version_ok($FCODE, $current_fcode)) {
                    &ss_logger("ERROR", 4006, "fcode downreved : expected $FCODE and found $current_fcode");
                    $fcode_result = "FAIL";
                } elsif ($VERBOSE) {
                    print "$prtconf_output[$i+1]\n";
                }
            }
	}
    }
    if ($count == 0) {
        &ss_logger("ERROR", 4006, "Unable to check the fcode with prtconf (failed to detect socal cards)");
	$fcode_result = "ERROR";
    }
    print "\t$fcode_result\n" if ($VERBOSE);
    if ($fcode_result =~ /FAIL|NORESULT/) {
	if ($RELEASE =~ /5.5.1/) {
	     print "\tInstall patch 105310 and follow the special instructions in the README.\n"
		if $VERBOSE;
	} elsif ($RELEASE =~ /5.6/) {
	     print "\tInstall patch 105375 and follow the special instructions in the README.\n"
		if $VERBOSE;
	} elsif ($RELEASE =~ /7/) {
	     print "\tInstall patch XXXXXX and follow the special instructions in the README.\n"
		if $VERBOSE;
	}
	print "\tluxadm fcal_s_download -f /usr/lib/firmware/fc_s/fcal_s_fcode\n";
	print "\tUse the luxadm fcal_s_download command in single user mode only!\n";
    } 
}

sub version_ok {

    my ($spec, $installed) = @_;

    die "version_ok :  no specified value!\n" if (! $spec);
    die "version_ok :  no installed value!\n" if (! $installed);

    # In perl 0 or the null string are considered false
    # anything else is TRUE. Initialize this variable
    # as false so this function can be called from an
    # if statement.

    my $return_value = 0;

    my ($spec_major_num, $spec_minor_num, $spec_increment) = $spec      
	=~ /(\d+)\D*(\d*)\D*(\d*)/;
    my ($installed_major_num, $installed_minor_num, $installed_increment) 
        = $installed =~ /(\d+)\D*(\d*)\D*(\d*)/;

    # Assign some default values if the minor number
    # or increment didn't match in the regular expression
    $spec_minor_num = 0 if (! $spec_minor_num);
    $spec_increment = 0 if (! $spec_increment);
    $installed_minor_num = 0 if (! $installed_minor_num);
    $installed_increment = 0 if (! $installed_increment);

#    print "$spec =  $spec_major_num, $spec_minor_num, $spec_increment\n";
#    print "$installed = $installed_major_num, $installed_minor_num, $installed_increment\n";

    if ($installed_major_num > $spec_major_num) {
	return 1;
    } elsif ($installed_major_num < $spec_major_num) {
	return 0;
    } else { # Check minor number
	if ($installed_minor_num > $spec_minor_num) {
	    return 1;
	} elsif ($installed_minor_num < $spec_minor_num) {
	    return 0;
	} else { #Check increment
            if ($installed_increment < $spec_increment) {
		return 0;
	    } else {
                return 1;
	    }
	}
    }
    print "${PROGNAME} : Logic error in version_ok subroutine!\n";
}

sub get_configuration_matrix {

    # We are using our own version of this matrix and this
    # routine must be updated every time the matrix gets upgraded.

    # We have a request from engineering to maintain support
    # for previous versions of the patch matrix. They are
    # interested in a tool that will tell them what level the
    # system is currently passing and what they need to do to
    # get the system at the current patch level. That functionality
    # is best implemented by writing another program which takes
    # advantage of the command line option for the patch matrix
    # available in storstat.

    # Parse the configuration matrix supplied with the package.

    my ($parse_251_patches, $parse_26_patches, $parse_7_patches)  = "";
    my ($parse_firmware, $parse_disk_fw) 			  = "";
    my ($parse_ib_fw, $parse_fcode) 				  = "";
    my ($patch, $patch_description) 				  = "";
    my ($f1, $f2, $f3)						  = "";

    print "\tData File : $CONFIGURATION_MATRIX\n" if $VERBOSE;
    open(MATRIX, "$CONFIGURATION_MATRIX") or die "Unable to open : $CONFIGURATION_MATRIX\n";
    while ($line=<MATRIX>) {

	# Skip blank lines
	next if ($line =~ /^\s*$/);

	# Clear flags if we reach an end of data section 
	if ($line =~ /========================/) {
	   $parse_251_patches           = "FALSE";
	   $parse_26_patches            = "FALSE";
	   $parse_7_patches             = "FALSE";
	   $parse_vx24_patches          = "FALSE"; 
	   $parse_vx25_patches          = "FALSE"; 
           $parse_disk_fw   		= "FALSE";
           $parse_ib_fw   		= "FALSE";
           $parse_fcode   		= "FALSE";
           $parse_new_sds               = "FALSE";
	   next;
	}

        # Look for the patch matrix rev imbedded in the comments
        if ($line =~ /REV\s+(.*)$/) {
	    $PATCH_MATRIX_REVISION = $1;
	    next;
        }
	# Skip Comments
	next if ($line =~ /^\s*#/);

	# Set flags if we detect a new section identifier

	# first section sets flags for parsing data
	if ($line =~ /^251_PATCH_LIST/) {
	   $parse_251_patches = "TRUE";
	   next;
	}
	if ($line =~ /^26_PATCH_LIST/) {
	   $parse_26_patches  = "TRUE";
	   next;
	}
	if ($line =~ /7_PATCH_LIST/) {
	   $parse_7_patches  = "TRUE";
	   next;
	}
	if ($line =~ /^VERITAS 2.4/) {
	   $parse_vx24_patches = "TRUE"; 
	   next;
	}
	if ($line =~ /^VERITAS 2.5/) {
	   $parse_vx25_patches  = "TRUE"; 
	   next;
	}
	if ($line =~ /^SOLSTICE 4.0/) {
           $parse_sds40 = "TRUE";
           next;
        }
	if ($line =~ /^SOLSTICE 4.1/) {
           $parse_sds41 = "TRUE";
           next;
        }
	if ($line =~ /^SOLSTICE 4.2/) {
           $parse_sds42   = "TRUE";
           next;
        }
	if ($line =~ /^DISK FIRMWARE/) {
           $parse_disk_fw  = "TRUE";
           next;
        }
	if ($line =~ /^IB FIRMWARE/) {
           $parse_ib_fw  = "TRUE";
           next;
        }
	if ($line =~ /^FCODE/) {
           $parse_fcode  = "TRUE";
           next;
        }
        if ($line =~ /^SDS:/) {
           $parse_new_sds = "TRUE";
           next;
        }

	# this section extracts data based on the flags

	if ($parse_251_patches eq "TRUE") {

	    if ($line =~ /^\s+(\d\S+)\s+:\s+(.*)/) {
	        $patch 	           = $1;
	        $patch_description = $2;

		# Found a 2.5.1 patch, we can do a patch check
		if ($RELEASE =~ /5.5.1/) {
		    $PATCH_CHECK_REQUIRED{$RELEASE} = "TRUE";
		}

		# Some of the patches are platform specific
		if ($patch =~ /103346/) { 
    		    if ($PLATFORM eq "SUNW,Ultra-Enterprise") {
	        	$PATCH_MATRIX_251{$patch} = $patch_description;
		    }
		# 105298 has been obsoleted by 105029
	        } elsif ($patch =~ /105298|105029/) {
		    if ($PLATFORM eq "SUNW,Ultra-Enterprise-10000") {
                        $last_command = "/bin/pkginfo SUNWapr 2> /dev/null | /bin/wc -l";
		        $aprcount = `/usr/bin/ksh -c '/bin/pkginfo SUNWapr 2> /dev/null' | /bin/wc -l`;
                        $last_command = "/bin/pkginfo SUNWapu 2> /dev/null | /bin/wc -l";
		        $apucount = `/usr/bin/ksh -c '/bin/pkginfo SUNWapu 2> /dev/null' | /bin/wc -l`;	
                        $last_command = "";
		        if (($aprcount !~ /0/) and ($apucount !~ /0/)) {
			    $PATCH_MATRIX_251{$patch} = $patch_description;
		        }
		    }
		} else {
        	    $PATCH_MATRIX_251{$patch} = $patch_description;
		}
	    }

	} elsif ($parse_26_patches eq "TRUE") {

	    if ($line =~ /^\s+(\d\S+)\s+:\s+(.*)/) {

	        $patch 	           = $1;
	        $patch_description = $2;

		# Found a 2.6 patch, we can do a patch check
		if ($RELEASE =~ /5.6/) {
		    $PATCH_CHECK_REQUIRED{$RELEASE} = "TRUE";
		}
		if ($patch =~ /103346/) { 
    		    if ($PLATFORM eq "SUNW,Ultra-Enterprise") {
	        	$PATCH_MATRIX_26{$patch} = $patch_description;
		    }
		} else {
	        	$PATCH_MATRIX_26{$patch} = $patch_description;
		}
	    }
	} elsif ($parse_7_patches eq "TRUE") {

	    if ($line =~ /^\s+(\d\S+)\s+:\s+(.*)/) {
	        $patch 	           = $1;
	        $patch_description = $2;

		# Found a 7 patch, we can do a patch check
    		# Release string is changing, check if the release contains 7
		if ($RELEASE =~ /7/) {
		    $PATCH_CHECK_REQUIRED{$RELEASE} = "TRUE";
		}
		if ($patch =~ /103346/) {
		    if ($PLATFORM eq "SUNW,Ultra-Enterprise") {
	        	$PATCH_MATRIX_7{$patch} = $patch_description;
		    }
		} else {
	        	$PATCH_MATRIX_7{$patch} = $patch_description;
		}
	    }
	} elsif ($parse_vx24_patches eq "TRUE") {
	    if ($line =~ /^\s+(\d\S+)\s+:\s+(.*)/) {
                $patch         = $1;
                $patch_description = $2;
		$VERITAS_24{$patch} = $patch_description;
	    }
	} elsif ($parse_vx25_patches eq "TRUE") {
	    if ($line =~ /^\s+(\d\S+)\s+:\s+(.*)/) {
                $patch         = $1;
                $patch_description = $2;
	    }
	   # Introducing some logic checks based on the README's
           if (($PLATFORM eq "SUNW,Ultra-4") and ($patch =~ /106191/)) {
		$VERITAS_25{$patch} = $patch_description;
	   } elsif (($PLATFORM ne "SUNW,Ultra-4") and ($patch =~ /105463/)) {
		$VERITAS_25{$patch} = $patch_description;
	   } else {
		$VERITAS_25{$patch} = $patch_description;
	   }
        } elsif ($parse_new_sds eq "TRUE") {
           my ($temp_os, $temp_sds, $temp_patch, $temp_comment) = split /:/, $line;
           $temp_os =~ s/\s+//g;
           $temp_sds =~ s/\s+//g;
           $temp_patch =~ s/^\s+(.*)\s+$/$1/;
           $temp_comment =~ s/^\s+(.*)\s+$/$1/;
           # if $temp_patch is null then SDS Release is supported but no patch required
           if (($RELEASE eq $temp_os) and ($SDS_VERSION =~ /^$temp_sds/)) {
               $SDS_VERSION_IS_SUPPORTED = 1;
               $NEW_SDS{$temp_patch} = $temp_comment if $temp_patch;
           }
        } elsif ($parse_disk_fw eq "TRUE") {
	    # ST19171FC, ST39102F, ST118273 are disk model numbers
	    if ($line =~ /ST19171FC/) {
		# Version 1.15 required different disk fw for PCI
		if ($HBA eq "S") {
		    if ($line =~ /SBUS/) {
			($f1, $ST19171FC, $f3) =  split(' ',$line);
		    }	
		} elsif ($HBA eq "P") {
		    if ($line =~ /PCI/) {
			($f1, $ST19171FC, $f3) =  split(' ',$line);
		    }	
		}
	    } elsif ($line =~ /ST39102F/) {
		($f1, $ST39102F) = split(' ',$line);
	    } elsif ($line =~ /ST118273/) {
		($f1, $ST118273) = split(' ',$line);
	    }
        } elsif ($parse_ib_fw eq "TRUE") {
	    # 14 drive A5000
	    if ($line =~ /5000\s+(\S+)/) {
		$IB_5000 = $1;
	    # 22 drive A5200
	    } elsif ($line =~ /5200\s+(\S+)/) {
                $IB_5200 = $1;
            }	

        } elsif ($parse_fcode eq "TRUE") {
	    if ($line =~ /^\s+FCODE\s+(\S+)/) {
		$FCODE = $1;
	    }
	}
    }

    die "The patch matrix doesn't contain the expected version string! (i.e # REV 1.15)\n"
	if (! $PATCH_MATRIX_REVISION);

    # Print the patch requirements

    # If we haven't parsed any data there is nothing to print (4186926)
    return if (! $PATCH_CHECK_REQUIRED{$RELEASE});

    if ($RELEASE eq "5.5.1") {
	print "\n\t2.5.1 Patch Matrix\n" if $VERBOSE;
	foreach $key (sort (keys %PATCH_MATRIX_251)) {
	    printf("\t%-10.10s : %-s\n", $key , $PATCH_MATRIX_251{$key}) if $VERBOSE;
	}
    } elsif ($RELEASE eq "5.6") {
        print "\n\t2.6 Patch Matrix\n" if $VERBOSE;
        foreach $key (sort (keys %PATCH_MATRIX_26)) {
	    printf("\t%-10.10s : %-s\n", $key , $PATCH_MATRIX_26{$key}) if $VERBOSE;
        }
    # Release string is changing, check if the release contains 7
    } elsif ($RELEASE =~ /7/) {
        print "\n\t7 Patch Matrix\n" if $VERBOSE;
        foreach $key (sort (keys %PATCH_MATRIX_7)) {
	    printf("\t%-10.10s : %-s\n", $key , $PATCH_MATRIX_7{$key}) if $VERBOSE;
        }
    }
    if ($VERITAS_VERSION =~ /^2.4/) {
	print "\n\tVeritas $VERITAS_VERSION Patch Matrix:\n" if $VERBOSE;
	foreach $key (sort (keys %VERITAS_24)) {
            printf("\t%-10.10s : %-s\n", $key , $VERITAS_24{$key}) if $VERBOSE;
        }
    } elsif ($VERITAS_VERSION =~ /^2.5/) {
	print "\n\tVeritas $VERITAS_VERSION Patch Matrix:\n" if $VERBOSE;
	foreach $key (sort (keys %VERITAS_25)) {
            printf("\t%-10.10s : %-s\n", $key , $VERITAS_25{$key}) if $VERBOSE;
        }
    }
    print "\n\tFirmware Requirements:\n" if $VERBOSE;
    printf("\t%-10.10s : %-s\n", "5000 IB FW",   $IB_5000)    if (($IB_5000)   and ($VERBOSE));
    printf("\t%-10.10s : %-s\n", "5200 IB FW",   $IB_5200)    if (($IB_5200)   and ($VERBOSE));
    printf("\t%-10.10s : %-s\n", "HA FCODE",       $FCODE)    if  		   ($VERBOSE);
    printf("\t%-10.10s : %-s\n", "ST19171FC", "$ST19171FC")   if (($ST19171FC) and ($VERBOSE));
    printf("\t%-10.10s : %-s\n", "ST39102F", "$ST39102F")     if (($ST39102F)  and ($VERBOSE));
    printf("\t%-10.10s : %-s\n", "ST118273", "$ST118273")     if (($ST118273)  and ($VERBOSE));
    print "\n\tSoltice Disk Suite $SDS_VERSION Patch Matrix: $RELEASE\n" if $VERBOSE;
    foreach $key (sort (keys %NEW_SDS)) {
        printf("\t%-10.10s : %-s\n", $key , $NEW_SDS{$key}) if $VERBOSE;
    }

}

sub check_103346 {

    # Needs to be an Ultra Enterprise 3x00/4x00/5x00/6x00
    if ($PLATFORM eq "SUNW,Ultra-Enterprise") {

	print "\nCHECK PROM : SUNW,Ultra-Enterprise : 103346\n" if $VERBOSE;

        &run_flash_update;

        if ($FOUND_PROM_REVS) {
            &check_prom_revs;
        } else {
	    print "\n\tUnable to determine the required prom revs!\n";
	    print "\tPlease install the latest version of 103346\n";
	    print "\tand run the flash-update program to insure that\n";
	    print "\tany SOC+I/O Boards are at the correct level.\n";
            &ss_logger("WARNING", 3001, "Unable to run flash-update");
	}
    }
}

sub check_patch {

    # Checks the patch
    # Sets the FW flag which introduces the dependency
    # that the patch checks should be run before the
    # firmware checks.

    my ($patch, $patch_description) = @_;

    if ( ( $patch_description =~ /ifp/ ) && ( $HBA eq "S" ) ) {
        return;
    }
    if ( ( $patch_description =~ /socal/ ) && ( $HBA eq "P" ) ) {
        return;
    }

    # default is FAIL. Only PASS if we find the patch
    my $patch_status = "FAIL";
 
    if ($patch =~ /-/) {
        ($base, $req_rev) = split (/-/, $patch);
    } else {
        $base = $patch;
        $req_rev = "";
    }
    print "\n\tChecking $patch : $patch_description\n" if ($VERBOSE);

    # Eliminate firmware patches that don't show up with showrev -p
    if ($patch =~ /103346|106129/) {
	$FW = TRUE;
	return "NORESULT";
    }

    # Look at patches that show up with showrev
    $last_command = "/bin/showrev -p | awk '{print \$2}' | /bin/egrep -c $patch";
    chop($count =   `/bin/showrev -p | awk '{print \$2}' | /bin/egrep -c $patch`);
    $last_command = "";
    if ($count == 0) {
        $last_command =                    "/bin/showrev -p       | awk '{print \$2}' | /bin/egrep $base | /bin/sort ";
        $other_versions = `/usr/bin/ksh -c '/bin/showrev -p 2>&1' | awk '{print \$2}' | /bin/egrep $base | /bin/sort`;
        $last_command = "";
        (@patch_list) = split(/\n/, $other_versions);
        foreach $installed_patch (@patch_list) {
            print "\t\t$installed_patch installed\n" if $VERBOSE;
            ($installed_rev) = $installed_patch =~ /-(\d\d)$/;
            if (! $req_rev ) {
                print "\t\tPatch revision not specified.\n" if $VERBOSE;
                $patch_status = "PASS";
            } elsif ($installed_rev == $req_rev) {
                print "\t\tCorrect revision installed.\n" if $VERBOSE;
                $patch_status = "PASS";
            } elsif ($installed_rev > $req_rev) {
                print "\t\tNewer revision installed.\n" if $VERBOSE;
                $patch_status = "PASS";
            } elsif ($installed_rev < $req_rev) {
                print "\t\tOlder revision installed.\n" if $VERBOSE;
                $patch_status = "FAIL";
            } else {
                print "\t\t$PROGNAME Logic Flaw in check_patch\n";
            }
        }
    } elsif ($count == 1) {
        print "\t\tInstalled\n" if $VERBOSE;
        $patch_status = "PASS";
    } else {
        print "\t\t$PROGNAME Logic Flaw in check_patch\n";
	print "\t\tdetected $count instances of $patch\n";
    }
    if ($patch_status eq "FAIL") {
	&ss_logger("ERROR", 4007, "$patch or newer not installed");
    }
    return $patch_status;
}

sub ss_logger {

    # Log messages using mail.crit for ERROR, mail.warn for WARN,
    # and mail.info for other.

    ($status, $error_number, $message) = @_;
    die "You must define an error_number!" if (! $error_number);
    die "You must define a status!"        if (! $status);
    die "You must define a message!"       if (! $message);

    die "Incorrect status : $status\n"     if ($status =~ /[^A-Z]/);
    die "Incorrect error number : $error_number\n"     
					   if ($error_number =~ /\D/);

    # Define this global variable and send mail later
    if (($status eq "ERROR") and $MAIL) {
	$MAIL_FLAG  = "TRUE";
        print MAIL "[A5000:DIAG:$status:$error_number] $message\n";
    }
    if (($status =~ /WARN/) and $WARN) {
	$MAIL_FLAG    = "TRUE";
        print MAIL "[A5000:DIAG:$status:$error_number] $message\n";
    }
    $ERROR_FLAG = "FAIL" if ($status eq "ERROR");
    
    if ($status eq "ERROR") {
        print "$status : $message\n";
    } elsif (($status =~ /WARNING/) and $WARN) {
        print "$status : $message\n";
    }
    if ($LOGGING) {
	if ($status eq "ERROR") {
   	    $exec_str = 
	    "/usr/bin/logger -p mail.crit -t \"[A5000:DIAG:$status:$error_number]\" \"$message\"";
        } elsif ($status eq "WARN") {
   	    $exec_str = 
	    "/usr/bin/logger -p mail.warn -t \"[A5000:DIAG:$status:$error_number]\" \"$message\"";
	} else {
   	    $exec_str = 
	    "/usr/bin/logger -p mail.info -t \"[A5000:DIAG:$status:$error_number]\" \"$message\"";
        }   
        my $return_value = `/usr/bin/ksh -c '$exec_str 2>&1'`;
    }
}

sub veritas_patch_check { 
    my ($line, $veritas_version, $status); 
    my ($patch);
    $last_command = "/bin/pkginfo -l SUNWvxvm";
    $pkginfo = `/usr/bin/ksh -c '/bin/pkginfo -l SUNWvxvm 2>&1'`; 
    $last_command = "";
    @pkginfo = split(/\n/, $pkginfo); 
    my $veritas_installed = "TRUE";
    print "\nPATCH CHECK VERITAS : " if $VERBOSE;
    foreach $line (@pkginfo) {
	if ($line =~ /ERROR/) {
	    $veritas_installed = "FALSE";
	    last;
	} elsif ($line =~ /^\s+VERSION:\s+(\S+)/) {
	    $veritas_version = $1;
	} elsif ($line =~ /^\s+STATUS:\s+(.*)/) {
	    $status = $1;
	    if ($status !~ /completely installed/) {
	        &ss_logger("ERROR", 4008, "SUNWvxvm not completely installed");
	        $veritas_installed = "FALSE";
		last;
	    }
        }
    }
   
    # This section of the code checks that the correct OS is associated with
    # each version of Veritas according to the patch matrix.
    if ($veritas_installed eq "TRUE") {
	# Added to header print line above PATCH CHECK STATUS
        print " $veritas_version : $status\n" if $VERBOSE;

        my $patch_error_flag = "PASS";

        if (($VERITAS_VERSION =~ /2.3/) and ($RELEASE eq "5.5.1")) {
            foreach $patch (sort (keys %VERITAS_23)) { 
	        $result = &check_patch($patch, $VERITAS_23{$patch}); 
	        if ($result eq "FAIL") {
                    $patch_error_flag = "FAIL";
                }
            }
        } elsif (($VERITAS_VERSION =~ /^2.4/) and (($RELEASE eq "5.5.1") or $RELEASE eq "5.6")) {
            foreach $patch (sort (keys %VERITAS_24)) { 
	        $result = &check_patch($patch, $VERITAS_24{$patch}); 
	        if ($result eq "FAIL") {
                    $patch_error_flag = "FAIL";
                }
            }
        } elsif (($VERITAS_VERSION =~ /^2.5/) and (($RELEASE eq "5.5.1") or ($RELEASE eq "5.6"))) {
            foreach $patch (sort (keys %VERITAS_25)) {
                $result = &check_patch($patch, $VERITAS_25{$patch});
                if ($result eq "FAIL") {
                    $patch_error_flag = "FAIL";
                }
            }
	} elsif ($VERITAS_VERSION =~ /^2.6/) {
	    if ($RELEASE !~ /5.5.1|5.6/) {
                &ss_logger("ERROR", 4009, "Veritas $VERITAS_VERSION not supported with Solaris $RELEASE");
                $patch_error_flag = "FAIL";
            }
	} elsif ($VERITAS_VERSION =~ /^3.0/) {
	    if ($RELEASE !~ /7/) {
	        &ss_logger("ERROR", 4009, "Veritas $VERITAS_VERSION not supported with Solaris $RELEASE");
		$patch_error_flag = "FAIL";
	    }
        } else {
	   &ss_logger("ERROR", 4009, "Veritas $VERITAS_VERSION not supported with Solaris $RELEASE");
        }
        print "\t$patch_error_flag\n" if $VERBOSE;
    } else {
	print "\tnot installed\n" if $VERBOSE;
    }
}

sub get_veritas_version {
    $last_command = "pkginfo SUNWvxvm 2>&1"; 
    chop($VERITAS_VERSION = `/usr/bin/ksh -c 'pkginfo SUNWvxvm 2>&1'`);
    $last_command = "";
    if ($VERITAS_VERSION =~ /ERROR/i) {
        $VERITAS_VERSION = "";
    } else {
        $last_command = "/bin/pkginfo -l SUNWvxvm 2> /dev/null | /bin/egrep VERSION | /bin/awk '{print \$2}'";
        chop($VERITAS_VERSION = 
	    `/usr/bin/ksh -c '/bin/pkginfo -l SUNWvxvm 2> /dev/null' | /bin/egrep VERSION | /bin/awk '{print \$2}'`);
        $last_command = "";
    }
    print "VERITAS_VERSION = $VERITAS_VERSION\n" if ($DEBUG);
}

sub solstice_patch_check { 
    my ($line, $solstice_version, $status); 
    my ($patch);
    $last_command = "/bin/pkginfo -l SUNWmd";
    $pkginfo = `/usr/bin/ksh -c '/bin/pkginfo -l SUNWmd 2>&1'`; 
    $last_command = "";
    @pkginfo = split(/\n/, $pkginfo); 
    my $solstice_installed = "TRUE";
    print "\nPATCH CHECK SOLSTICE DISK SUITE : " if $VERBOSE;
    foreach $line (@pkginfo) {
	if ($line =~ /ERROR/) {
	    $solstice_installed = "FALSE";
	    last;
	} elsif ($line =~ /^\s+VERSION:\s+(\S+)/) {
	    $solstice_version = $1;
	} elsif ($line =~ /^\s+STATUS:\s+(.*)/) {
	    $status = $1;
	    if ($status !~ /completely installed/) {
	        &ss_logger("ERROR", 4010, "SUNWmd not completely installed");
	        $solstice_installed = "FALSE";
		last;
	    }
        }
    }

    # This section of the code is where we make the association between
    # versions of SDS and their supported OS
    if ($solstice_installed eq "TRUE") {

        print " $solstice_version : $status\n" if $VERBOSE;
        my $patch_error_flag = "PASS";
        if ($SDS_VERSION_IS_SUPPORTED) {
            foreach $patch (sort (keys %NEW_SDS)) {
                $result = &check_patch($patch, $NEW_SDS{$patch});
                $patch_error_flag = "FAIL" if ($result eq "FAIL");
            }
        } else {
	   &ss_logger("ERROR", 4011, "Solstice Disk Suite Version $SDS_VERSION not supported with Solaris $RELEASE");
        }
        print "\t$patch_error_flag\n" if $VERBOSE;
    } else {
	print "not installed\n" if $VERBOSE;
    }
}

sub get_solstice_version {
    $last_command = "/bin/pkginfo SUNWmd 2>&1";
    chop($SDS_VERSION = `/usr/bin/ksh -c '/bin/pkginfo SUNWmd 2>&1'`);
    $last_command = "";
    if ($SDS_VERSION =~ /ERROR/) {
        $SDS_VERSION = "";
    } else {
        $last_command = "/bin/pkginfo -l SUNWmd 2> /dev/null | /bin/egrep VERSION | /bin/awk '{print \$2}'";
        chop($SDS_VERSION = 
	    `/usr/bin/ksh -c '/bin/pkginfo -l SUNWmd 2> /dev/null' | /bin/egrep VERSION | /bin/awk '{print \$2}'`);
        $last_command = "";
    }
}

sub check_disk_status {

    print "\nCHECK DISK STATUS : " if $VERBOSE;

    my $result = "PASS";

# These strings are changing under us. Reservation conflict can be
# "Rsrv cnflt" or "Reserve cnflt"

# Dexter guarantees the position won't change, but won't guarantee
# the format of the string.

# for luxadm rev 1.36
#SLOT   FRONT DISKS       (Node WWN)          REAR DISKS        (Node WWN)
#print "0000000000111111111122222222223333333333444444444455555555556666666666\n";
#print "0123456789012345678901234567890123456789012345678901234567890123456789\n";
# 	4      On (Rsrv cnflt:B) 20000020370e06cf    On (O.K.)         20000020370df120

#                         (luxadm version: 1.29 98/03/18)
#                                   SENA            
#                                 DISK STATUS 
#SLOT   FRONT DISKS       (Node WWN)          REAR DISKS        (Node WWN)
# 4      On (Reserve cnflt)2000002037070c53    On (O.K.)         2000002037041397


    foreach $enclosure (keys (%LUXADM_DISPLAY)) {

        print "\n\tChecking enclosure $enclosure\n" if ($VERBOSE);
        @luxadm = split(/\n/, $LUXADM_DISPLAY{$enclosure});
        foreach $index (0 .. $#luxadm ) {

	    next if ($luxadm[$index] !~ /^[S0-9]/);
            next if ($luxadm[$index] =~ /Retrying/);

	    print "\t$luxadm[$index]\n" if ($VERBOSE);

            if ($luxadm[$index] =~ /^SLOT/) {
                $p0_start = index($luxadm[$index],  "SLOT");
                $p1_start = index($luxadm[$index],  "FRONT DISKS");
                $p2_start = index($luxadm[$index],  "(Node WWN)");
                $p3_start = index($luxadm[$index],  "REAR DISKS");
                $p4_start = rindex($luxadm[$index], "(Node WWN)");

	        $p0_length  = $p1_start - $p0_start;
	        $p1_length  = $p2_start - $p1_start;
	        $p2_length  = $p3_start - $p2_start;
	        $p3_length  = $p4_start - $p3_start;
	        $p4_length  = length ($luxadm[$index]) - $p4_start;
		next;
	    }
	
            $slot = substr($luxadm[$index], $p0_start, $p0_length);

	    # Front disk check
            $fd_status = substr($luxadm[$index], $p1_start, $p1_length);
            $fd_wwn    = substr($luxadm[$index], $p2_start, $p2_length);
	    if (! &disk_status_accepted($enclosure, $fd_wwn, $fd_status) ) {
		$result = "FAIL";
	    }
	    # Rear disk check
            $rd_status = substr($luxadm[$index], $p3_start, $p3_length);
            $rd_wwn = substr($luxadm[$index], $p4_start, $p4_length);
	    if (! &disk_status_accepted($enclosure, $rd_wwn, $rd_status) ) {
		$result = "FAIL";
	    }
	}
    }
    print "\t$result\n" if ($VERBOSE);
}

sub disk_status_accepted {

    # Accept disks that are O.K. or Not Installed or Reservation Conflicts
    # or Bypassed or Loop not accessible

    # Warning for Bypassed disks

    my ($enclosure, $wwn, $status) = @_;
    $wwn =~ s/\s+/ /;	
    if ($status =~ /Bypassed/i) {
	&ss_logger("WARNING", 3019, "Disk Bypassed : $wwn : $status"); 
    }
    # Loop not acc = (Loop not accessible)
    if ($status =~ /O\.K\.|Not\s+Installed|R\S+\s+cnflt|Bypassed|Loop not acc/i) {
	return 1;
    } else {
	&ss_logger("ERROR", 4019, "Luxadm disk status error : $enclosure : $wwn : $status");
	return 0;
    }
}		

sub check_min_configuration {

    # According to Brian Yunker (A5000) the system works best
    # with the min drives installed in specified slots.
    # There may be scenerios where the disks could be balanced
    # in a more evenly distributed manner and the signal noise
    # is not compromised though it is easiest just to specify
    # that the min configuration slots be occupied always.
    # we KNOW this meets our min requirements.

    print "\nCHECK MIN DISK CONFIGS : " if $VERBOSE;

    my $result = "PASS";

    # for luxadm rev 1.36
    #SLOT   FRONT DISKS       (Node WWN)          REAR DISKS        (Node WWN)
    #print "0000000000111111111122222222223333333333444444444455555555556666666666\n";
    #print "0123456789012345678901234567890123456789012345678901234567890123456789\n";

    foreach $enclosure (keys (%LUXADM_DISPLAY)) {

        print "\n\tChecking enclosure $enclosure\n" if ($VERBOSE);

        # Two pass: 1st to dtermine how many slots
        @luxadm = split(/\n/, $LUXADM_DISPLAY{$enclosure});
        foreach $index (0 .. $#luxadm ) {
            next if ($luxadm[$index] !~ /^[0-9]/);
            ($last_slot) = $luxadm[$index] =~ /^(\d+)/;
        }

        @luxadm = split(/\n/, $LUXADM_DISPLAY{$enclosure});
        foreach $index (0 .. $#luxadm ) {

            next if ($luxadm[$index] !~ /^[S0-9]/);
            print "\t$luxadm[$index]\n" if ($VERBOSE);

            if ($luxadm[$index] =~ /^SLOT/) {
                $p0_start = index($luxadm[$index],  "SLOT");
                $p1_start = index($luxadm[$index],  "FRONT DISKS");
                $p2_start = index($luxadm[$index],  "(Node WWN)");
                $p3_start = index($luxadm[$index],  "REAR DISKS");
                $p4_start = rindex($luxadm[$index], "(Node WWN)");

                $p0_length  = $p1_start - $p0_start;
                $p1_length  = $p2_start - $p1_start;
                $p2_length  = $p3_start - $p2_start;
                $p3_length  = $p4_start - $p3_start;
                $p4_length  = length ($luxadm[$index]) - $p4_start;
                next;
            }

            $slot = substr($luxadm[$index], $p0_start, $p0_length);

            $fd_status = substr($luxadm[$index], $p1_start, $p1_length);
            $fd_wwn    = substr($luxadm[$index], $p2_start, $p2_length);
            if ($fd_status =~ /Not Installed/) {
                $fd[$slot] = "0";
            } else {
                $fd[$slot] = "OK";
            }

            $rd_status = substr($luxadm[$index], $p3_start, $p3_length);
            $rd_wwn = substr($luxadm[$index], $p4_start, $p4_length);
            if ($rd_status =~ /Not Installed/) {
                $rd[$slot] = "0";
            } else {
                $rd[$slot] = "OK";
            }
        }

        # Bryan Yunker Spec
        # 22 drive back plane
        if ($last_slot == 10) {
	    $DISK_COUNT{$enclosure} = "22";
	    if (! $fd[0]) {
	  	$result = "FAIL";
		&ss_logger("ERROR", 4020, "Required Disk Placement : $enclosure : drive missing in front slot 0");
	    }
	    if (! $fd[5]) {
	  	$result = "FAIL";
		&ss_logger("ERROR", 4020, "Required Disk Placement : $enclosure : drive missing in front slot 5");
	    }
	    if (! $fd[10]) {
	  	$result = "FAIL";
		&ss_logger("ERROR", 4020, "Required Disk Placement : $enclosure : drive missing in front slot 10");
	    }
	    # Check the rear disk
	    if (! $rd[10]) {
	  	$result = "FAIL";
		&ss_logger("ERROR", 4020, "Required Disk Placement : $enclosure : drive missing in rear slot 10");
	    }
        # 14 drive backplane
        } elsif ($last_slot == 6) {
	    $DISK_COUNT{$enclosure} = "14";
	    # Check the front disk requirements for the 14 drive	
	    if (! $fd[3]) {
	  	$result = "FAIL";
		&ss_logger("ERROR", 4020, "Required Disk Placement : $enclosure : drive missing in front slot 3");
	    }
	    if (! $fd[6]) {
	  	$result = "FAIL";
		&ss_logger("ERROR", 4020, "Required Disk Placement : $enclosure : drive missing in front slot 6");
	    }
	}
	# These rear disk requirements are the same for 14 drive and 22 drive
	if (! $rd[0]) {
	    $result = "FAIL";
	    &ss_logger("ERROR", 4020, "Required Disk Placement : $enclosure : drive missing in rear slot 0");
	}
	if (! $rd[3]) {
	    $result = "FAIL";
	    &ss_logger("ERROR", 4020, "Required Disk Placement : $enclosure : drive missing in rear slot 3");
	}
	if (! $rd[6]) {
	    $result = "FAIL";
	    &ss_logger("ERROR", 4020, "Required Disk Placement : $enclosure : drive missing in rear slot 6");
	}
    }
    if ($VERBOSE and ($result eq "FAIL")) {
        print "\tRecommended Disk Placement:\n";
        print "\t\t22 drive :\n";
        print "\t\t\tFront:     0, 5, 10\n";
        print "\t\t\tRear :     0, 3, 6, 10\n";
        print "\t\t14 drive :\n";
        print "\t\t\tFront:     3, 6\n";
        print "\t\t\tRear :     0, 3, 6\n";
        print "\tSee Sun Microsystems FIN#I0400.\n";
    }
    print "\t$result\n" if ($VERBOSE);
}

sub int {
    # Remove the /reconfigure file if it was caused
    # by running this program.
    if ((-e "/reconfigure") and ($RECONFIGURE eq "FALSE")) {
	system("rm /reconfigure");
    }
    print "Interrupted $last_command\n";
    exit;
}

sub check_ifp_driver {

    # Add this check because ifp is not bundled
    # and we need this driver to talk with the enclosures.
    # pci is the bus architecture. ifp is the driver.

    # HBA is defined in the supporting routines
    # HBA = P	is PCI
    # HBA = S   is SBUS

    # This check is only meaningful on PCI systems
    return if ($HBA ne "P");

    if (! -e "/kernel/drv/ifp") {
	&ss_logger("ERROR", 4023, "ifp driver not installed : SUNWifp : /kernel/drv/ifp");
    }
}

sub check_socal_driver {

    # Insure the socal driver is installed. We had some
    # versions of the OS where the socal driver was
    # not installed with the base distribution, but
    # showed up when a full install was performed.
    
    # HBA is defined in the supporting routines
    # HBA = P   is PCI
    # HBA = S   is SBUS

    # This check is only meaningful on SBUS systems
    return if ($HBA ne "S");

    if (! -e "/kernel/drv/socal") {
        &ss_logger("ERROR", 4022, "Socal driver not installed : SUNWluxal : /kernel/drv/socal");
    }
}
