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

require  "subroutines.pm";
&st_globals();

####################################################################
# storconfig
#  take snapshots of fcal configurations / state (Golden Snapshot)
#  compare the Golden snapshot to the current snapshot
# 
# Copyright (c) 1998  Sun Microsystems Computer Corporation, Inc.
####################################################################
$PROGNAME = "storconfig";

# Format the date command so that no SCCS expansion takes place
$cmd = "/usr/bin/date +%Y"."%m"."%d"."_"."%H"."%M";
chomp($DATE = `$cmd`);

$GOLDLOG    = "Golden_Snapshot_${DATE}";
$TEMPGOLD   = "Temp_" . $GOLDLOG;
$SNAPSHOT   = "Current_Snapshot";
$ERROR_LOG  = "${LOGDIR}/${PROGNAME}_error_log";

$SIG{INT} = 'int';

sub int {
    print "\nInterrupted\n";
    system("/usr/bin/rm -f $GOLD_PATH/$TEMPGOLD");
    exit;
}

&check_root_access( $PROGNAME );

# Turn on $VERBOSE output if the env variable DEBUG is defined
# and not 0 or not the null string.
if ($ENV{'DEBUG'}) {
	$VERBOSE = "TRUE";
	$DEBUG	 = "TRUE";
}

&proc_cli;

open(MAIL, ">${ERROR_LOG}") or die "Unable to open mail log : $ERROR_LOG\n";
@header_msg = &st_header( "log" );
print MAIL "@header_msg";

# If we are saving a snapshot (golden log)
if ($SAVE) {

    &show_config if ($SHOW_CONF eq "TRUE");
    printf("Creating Configuration Snapshot File\n");
    &open_logfile($TEMPGOLD, $GOLD_PATH);
    &take_snapshot;
    &close_logfile;
    &move_gold_file( "${GOLD_PATH}/${TEMPGOLD}", "${GOLD_PATH}/${GOLDLOG}" );
    print "Golden Snapshot: ${GOLD_PATH}/${GOLDLOG}\n";

# If we are comparing snapshots
} elsif ($CMP) {

    &compare_files;
} elsif ($DISKMAP) {

    &show_disk_map;
}

# If we are sending mail close the file and send it
    close(MAIL);	

if ($MAIL_RECIPIENT && $ERROR_FLAG) {
    if(!mail_message($MAIL_RECIPIENT,  "$PRODUCT:storconfig:$STORAGE_DEVICE: Errors detected!", $ERROR_LOG)) {
		printf("Warining:  Mail message to $MAIL_RECIPIENT cannot be gererated\n");
    }
}

#
# BugTraq #4280945
# Remove the error log, if it exists
# otherwise terminate gracefully
# Added -f option to suppress error message if error log does not exist
#
`/usr/bin/rm -f $ERROR_LOG`;

exit(0);

#####################################################
#
#		 Subroutines
#
####################################################
####################################################
# show configuration prior to saving
####################################################
sub show_config {
    my ($resp);
    my ($yorn);
    $resp = "y";
    while (1) {
       print "Would you like to see a storage summary now?  [<y>, n]: ";
       $resp = &get_response;
       if (!(($resp =~/^n/i) | ($resp =~ /^y/i) | ($resp eq ""))) {
          print "\n '$resp' is an invalid choice.  Please try again.\n\n";
       } else {
         last;
       }
    }

    if ($resp =~/^n/i) {
       exit;
    }elsif (($resp =~ /^y/i) | ($resp eq "")) {
       $rc = 0xffff & system "${BINDIR}/cdmenu -$STORAGE -$HBA -live -do";
       if ($rc == 512) {
          # 
          # cdmenu received interrupt (ctrl-c)
          #
          exit;
       }

       $yorn = "y";
       while (1) {
          print "\n\nIs this configuration correct?  [<y = create snapshot>, n]: ";
          $yorn = &get_response;
          if ($yorn =~ /^n/i) {
             print "Please make the necessary corrections to ensure all loops and storage \n";
             print "are online.  Then Create Configuration Snapshot File. \n";
	     exit(1);
	  } elsif (($yorn =~ /^y/i) | ($yorn eq "")) {
	     # this will enable the user to take advantage of the fact 
	     # that a live config was just taken.  use this file instead
	     # of doing the probe again.
	     $RDMP_FILE = "TRUE";
             # this next line shouldn't be hardcoded
	     $RD_INFILE = $TMP_RAW;
             last;
	  } else {
             print "\n '$yorn' is an invalid choice.  Please try again.\n\n";
             }
       }
    } else {
       print "show_config: Code Error\n";
      }
}

sub compare_files {

    $CHANGES_DETECTED = "FALSE";

    $golden_log = &get_golden_snapshot( $STORAGE );

    if (! ${golden_log}) {
        print "There is no golden snapshot available to compare with!\n";
	print "Please verify the system is configured correctly and \n";
	die   "then run '${PROGNAME} -save' for future comparisons.\n";
    }

    if ( $DEBUG ) {
        print "Golden  Snapshot : ${GOLD_PATH}/${golden_log}\n";
        print "Current Snapshot : ${GOLD_PATH}/${SNAPSHOT}\n";
    }


    # Get the current snapshot
    &open_logfile($SNAPSHOT, $GOLD_PATH);
    &take_snapshot;
    &close_logfile;
    &compare_section_lines($golden_log, $SNAPSHOT, "STORSTAT:");
    &compare_packages($golden_log, $SNAPSHOT, "PKGINFO:");
    &compare_patches ($golden_log, $SNAPSHOT, "PATCHES:");

    if ( ( $HBA eq "S" ) && ( $STORAGE eq "A5" ) ) {
       &smart_compare($golden_log, $SNAPSHOT);
    } elsif ( ( $HBA eq "P" ) && ( $STORAGE eq "A5" ) ) {
       $pci_output = `${BINDIR}/pcicomp -f ${GOLD_PATH}/${golden_log}`;
       if ( $pci_output !~ /No changes detected between system snapshots!/ ) {
           &logit("ERROR", 4013, $pci_output);
           $CHANGES_DETECTED = "TRUE";
           print "*** Changes detected! ***\n" if ($VERBOSE);
       }
    } elsif ( $STORAGE eq "T3" ) {
       &compare_section_lines($golden_log, $SNAPSHOT, "RAWDUMP:");
    }
    &vm_compare($golden_log, $SNAPSHOT);
    if ($CHANGES_DETECTED eq "FALSE") {
        #
        # BugTraq # 4280943
        # Report that no changes were detected
        # if -mail option specified,
        #    then disable notification
        #
        if ( $MAIL_RESULTS ne "TRUE" ){
            print "No changes detected between system snapshots!\n";
        }
    }

}


sub compare_packages {
    my ($golden_log, $current_log, $section_name) = @_;
    print "\n$section_name\n" if ($VERBOSE);

    # Initialize the hash arrays
    undef %current;
    undef %golden;
    my $change_flag = "";
    my ($found_section, $line);

    open(GOLDEN,  "${GOLD_PATH}/$golden_log") or
	die "Unable to open golden logfile : ${GOLD_PATH}/$golden_log\n";
    $found_section = "FALSE";
    while ($line=<GOLDEN>) {
	chomp $line;
	if ($line =~ /^${section_name}/) {
	     $found_section = "TRUE";
	     next;	
	}
        next if ($found_section eq "FALSE");
	next if ($line =~ /^\s*$/);
	next if ($line =~ /^\s*#/);
	last if ($line =~ /^============/);
        ($type,$package,$description) = $line =~ /^(\S+)\s+(\S+)\s+(.*)/;
        if ($found_section eq "TRUE") {
	    $golden{$package} = $description;
	}
    }
    close(GOLDEN);

    open(CURRENT, "${GOLD_PATH}/$current_log") or
	die "Unable to open current logfile : ${GOLD_PATH}/$current_log\n";
    $found_section = "FALSE";
    while ($line = <CURRENT>) {
	chomp $line;
	if ($line =~ /^${section_name}/) {
	     $found_section = "TRUE";
	     next;
	}
        next if ($found_section eq "FALSE");
	next if ($line =~ /^\s*$/);
	next if ($line =~ /^\s*#/);
	last if ($line =~ /^============/);
        ($type,$package,$description) = $line =~ /^(\S+)\s+(\S+)\s+(.*)/;
	if ($found_section eq "TRUE") {
	     $current{$package} = $description;
	}
    }
    close(CURRENT);
    print "Check for packages that have been deleted:\n" if ($VERBOSE);
    foreach $package (sort (keys %golden)) {
	# If the key is not defined in the current list
	# something has changed
	if (! defined $current{$package}) {
	    $change_flag = "TRUE";
	    &logit("WARNING", 3003, "Package deleted since last snapshot : ${package}");
	}
    }
    print "Check for packages that have been installed:\n" if ($VERBOSE);
    foreach $package (sort (keys %current)) {
	if (! defined $golden{$package}) {
	    $change_flag = "TRUE";
	    &logit("WARNING", 3004, "Package installed since the last snapshot : ${package}");
	}
    }
    $CHANGES_DETECTED = "TRUE" if ($change_flag eq "TRUE");
    print "*** Changes detected! ***\n" if ($change_flag && $VERBOSE);
}

sub compare_patches {
    my ($golden_log, $current_log, $section_name) = @_;
    print "\n$section_name\n" if ($VERBOSE);

    # Initialize the hash arrays
    undef %current;
    undef %golden;
    my $change_flag = "";
    my ($found_section, $line);

    open(GOLDEN,  "${GOLD_PATH}/$golden_log") or
	die "Unable to open golden logfile : ${GOLD_PATH}/$golden_log\n";
    $found_section = "FALSE";
    while ($line=<GOLDEN>) {
	chomp $line;
	if ($line =~ /^${section_name}/) {
	     $found_section = "TRUE";
	     next;	
	}
        next if ($found_section eq "FALSE");
	next if ($line !~ /\d{6,6}-\d{2,2}/);
	next if ($line =~ /^\s*#/);
	next if ($line =~ /^\s*$/);
	last if ($line =~ /^============/);
        ($patch,$revision) = split(/-/, $line);
        if ($found_section eq "TRUE") {
	    $golden{$patch} = $revision;
	}
    }
    close(GOLDEN);

    open(CURRENT, "${GOLD_PATH}/$current_log") or
	die "Unable to open current logfile : ${GOLD_PATH}/$current_log\n";
    $found_section = "FALSE";
    while ($line = <CURRENT>) {
	chomp $line;
	if ($line =~ /^${section_name}/) {
	     $found_section = "TRUE";
	     next;
	}
        next if ($found_section eq "FALSE");
	next if ($line !~ /\d{6,6}-\d{2,2}/);
	next if ($line =~ /^\s*#/);
	next if ($line =~ /^\s*$/);
	last if ($line =~ /^============/);
        ($patch,$revision) = split(/-/, $line);
	if ($found_section eq "TRUE") {
	     $current{$patch} = $revision;
	}
    }
    close(CURRENT);
    print "Check for patches that have been deleted:\n" if ($VERBOSE);
    foreach $patch (sort (keys %golden)) {
	# If the key is not defined in the current list
	# something has changed
	if (! defined $current{$patch}) {
	    $change_flag = "TRUE";
	    &logit("WARNING", 3005, "Patch has been deleted since last snapshot : $patch");
	}
    }
    print "Check for patches that have been installed:\n" if ($VERBOSE);
    foreach $patch (sort (keys %current)) {
	if (! defined $golden{$patch}) {
	    $change_flag = "TRUE";
	    &logit("WARNING", 3006, "Patch has been deleted since last snapshot : $patch");
	}
    }
    print "Check for patches that have been upgraded:\n" if ($VERBOSE);
    foreach $patch (sort (keys %current)) {
	# Skip the ones that are not in the current installation
	next if (! defined $current{$patch}); 
	# skip the ones that are not in the original (Golden) installation
	next if (! defined $golden{$patch}); 
	# skip the ones that are the same, we only want differences
	next if ($current{$patch} == $golden{$patch});
	if ($current{$patch} > $golden{$patch}) {
	    $change_flag = "TRUE";
	    &logit("WARNING", 3007, "Patch has been upgraded since last snapshot : $patch");
	}
    }
    print "Check for patches that have been downgraded:\n" if ($VERBOSE);
    foreach $patch (sort (keys %current)) {
	# Skip the ones that are not in the current installation
	next if (! defined $current{$patch}); 
	# skip the ones that are not in the original (Golden) installation
	next if (! defined $golden{$patch}); 
	# skip the ones that are the same, we only want differences
	next if ($current{$patch} == $golden{$patch});
	if ($current{$patch} < $golden{$patch}) {
	    $change_flag = "TRUE";
	    &logit("WARNING", 3008, "Patch has been upgraded since last snapshot : $patch");
	}
    }
    print "*** Changes detected! ***\n" if ($change_flag && $VERBOSE);
    $CHANGES_DETECTED = "TRUE" if ($change_flag eq "TRUE");
}

sub compare_section_lines {

    my ($golden_log, $current_log, $section_name) = @_;
    my (@golden_section, @current_section, @temp);
    my ($line, $gold_counter, $golden_key, $curr_counter, $current_key);
    print "\n$section_name\n" if ($VERBOSE);

    my $change_flag = "";
    my ($found_section);

    open(GOLDEN,  "${GOLD_PATH}/$golden_log") or
	die "Unable to open file : ${GOLD_PATH}/$golden_log\n";
    $found_section = "FALSE";

    while ($line=<GOLDEN>) {
        chomp $line;
        $found_section = "TRUE" if ($line =~ /^${section_name}/);
        next if ($found_section eq "FALSE");
        next if ($line =~ /^\s*$/);
        next if ($line =~ /^\s*#/);
        last if ($line =~ /^============/);
        push @golden_section, $line if ($found_section eq "TRUE");
    }
    close(GOLDEN);

    open(CURRENT, "${GOLD_PATH}/$current_log") or
        die "Unable to open current logfile : ${GOLD_PATH}/$current_log\n";
    $found_section = "FALSE";
    while ($line = <CURRENT>) {
        chomp $line;
        $found_section = "TRUE" if ($line =~ /^${section_name}/);
        next if ($found_section eq "FALSE");
        next if ($line =~ /^\s*$/);
        next if ($line =~ /^\s*#/);
        last if ($line =~ /^============/);
        push @current_section, $line if ($found_section eq "TRUE");
    }
    close(CURRENT);
    #
    # remove all common lines leaving differences
    #
    $gold_counter = 0;
    @temp = (@golden_section);
    foreach $golden_key (@temp) {
        $curr_counter = 0;
        $remove_golden = 0;
        foreach $current_key (@current_section) {
            if ($golden_key eq $current_key) {
                splice(@current_section, $curr_counter,1);
                $remove_golden = 1;
                last;
            } else { 
                $curr_counter ++;
            }
        }
        if ($remove_golden) {
            splice(@golden_section, $gold_counter, 1);
        } else { 
            $gold_counter ++; 
        }
    }

    return if ($#golden_section == -1) and ($#current_section == -1);
    if ($#current_section == -1) {
        ###############################################
        # old messages are gone from current snapshot #
        ###############################################
        my ($info, $lines, $name, @raw_strings);
        if ($section_name =~ /RAWDUMP/) {
            ############################################
            # make rawdump results more human readable #
            ############################################
            foreach $info (@golden_section) {
                chomp $info;
                @raw_strings = split /\s+/, $info;
                if ($raw_strings[7] eq "DD") {
                    if ($raw_strings[0] =~ /T300/) {
                        $lines .= "ERROR : T300 LUN ($raw_strings[3]) offline\n";
                    } else {
                        $lines .= "ERROR : $raw_strings[0] Disk ($raw_strings[3]) offline\n";
                    }
                } elsif ($raw_strings[7] =~ /HA/) {
                    $lines .= "ERROR : HA $raw_strings[8] Loop $raw_strings[9] ($raw_strings[10]) offline\n";
                } elsif ($raw_strings[7] =~ /IB/) {
                    my ($raw_enclosure, $raw_loop) = split /,/, $raw_strings[0];
                    $lines .= "ERROR : Enclosure $raw_enclosure (Loop $raw_loop) offline\n";
                }
            }
        } else {
            foreach $current_key (@golden_section) {
                $lines .= "  $current_key\n";
            }
        }

        $change_flag = "TRUE";
        &logit("ERROR", 4017, "Change detected, hardware offline or missing\n$lines");

    } elsif ($#golden_section == -1) {
        ##############################
        # new messages not in golden #
        ##############################
        $change_flag = "TRUE";
        my ($info, $lines, $name, @raw_strings);
        if ($section_name =~ /RAWDUMP/) {
            ############################################
            # make rawdump results more human readable #
            ############################################
            foreach $info (@current_section) {
                chomp $info;
                @raw_strings = split /\s+/, $info;
                if ($raw_strings[7] eq "DD") {
                    if ($raw_strings[0] =~ /T300/) {
                        $lines .= "WARNING : T300 LUN ($raw_strings[3]) online\n";
                    } else {
                        $lines .= "WARNING : $raw_strings[0] Disk ($raw_strings[3]) online\n";
                    }
                } elsif ($raw_strings[7] =~ /HA/) {
                    $lines .= "WARNING : HA $raw_strings[8] Loop $raw_strings[9] ($raw_strings[10]) online\n";
                } elsif ($raw_strings[7] =~ /IB/) {
                    my ($raw_enclosure, $raw_loop) = split /,/, $raw_strings[0];
                    $lines .= "WARNING : Enclosure $raw_enclosure (Loop $raw_loop) online\n";
                }
            }
        } else {
            foreach $current_key (@current_section) {
                $lines .= "  $current_key\n";
            }
        }
        &logit("WARNING", 3010, "Change detected, new hardware online, creating new snapshot recommended\n$lines");
    } else {
        ###################################
        # some different messages in each #
        ###################################
        my ($info, $lines, $name, @raw_strings);
        $change_flag = "TRUE";
        $lines .= " Current Configuration:\n";
        if ($section_name =~ /RAWDUMP/) {
            ############################################
            # make rawdump results more human readable #
            ############################################
            foreach $info (@current_section) {
                chomp $info;
                @raw_strings = split /\s+/, $info;
                if ($raw_strings[7] eq "DD") {
                    if ($raw_strings[0] =~ /T300/) {
                        $lines .= "WARNING : T300 LUN ($raw_strings[3]) offline\n";
                    } else {
                        $lines .= "WARNING : $raw_strings[0] Disk ($raw_strings[3]) offline\n";
                    }
                } elsif ($raw_strings[7] =~ /HA/) {
                    $lines .= "WARNING : HA $raw_strings[8] Loop $raw_strings[9] ($raw_strings[10]) offline\n";
                } elsif ($raw_strings[7] =~ /IB/) {
                    my ($raw_enclosure, $raw_loop) = split /,/, $raw_strings[0];
                    $lines .= "WARNING : Enclosure $raw_enclosure (Loop $raw_loop) offline\n";
                }
            }
        } else {
            foreach $current_key (@current_section) {
                $lines .= "  $current_key\n";
            }
        }
        $lines .= " Golden Configuration:\n";
        if ($section_name =~ /RAWDUMP/) {
            ############################################
            # make rawdump results more human readable #
            ############################################
            foreach $info (@golden_section) {
                chomp $info;
                @raw_strings = split /\s+/, $info;
                if ($raw_strings[7] eq "DD") {
                    if ($raw_strings[0] =~ /T300/) {
                        $lines .= "WARNING : T300 LUN ($raw_strings[3]) online\n";
                    } else {
                        $lines .= "WARNING : $raw_strings[0] Disk ($raw_strings[3]) online\n";
                    }
                } elsif ($raw_strings[7] =~ /HA/) {
                    $lines .= "WARNING : Host Adaptor Loop ($raw_strings[10]) online\n";
                } elsif ($raw_strings[7] =~ /IB/) {
                    my ($raw_enclosure, $raw_loop) = split /,/, $raw_strings[0];
                    $lines .= "WARNING : Enclosure $raw_enclosure (Loop $raw_loop) online\n";
                }
            }
        } else {
            foreach $current_key (@golden_section) {
                $lines .= "  $current_key\n";
            }
        }
        &logit("WARNING", 3010, "Change detected, analyze the messages\n$lines");
    }
    print "*** Changes detected! ***\n" if ($change_flag && $VERBOSE);
    $CHANGES_DETECTED = "TRUE" if ($change_flag eq "TRUE");
}

sub close_logfile {

    close LOG;

}

sub move_gold_file {
    my ($fileone, $filetwo) = @_;
    system("/usr/bin/mv", $fileone, $filetwo);
}

sub pcidump {
   system "$BINDIR/pcidump";
}

sub take_snapshot {

    &header;
    &storstat;
    &disklist;
    &rawdump;
    if ( $STORAGE ne "T3" ) {
        &port_summary;
    }
    &pkginfo;
    &showrev;
    &volume_manager_summary;
}

sub pkginfo {
    print LOG "PKGINFO:\n\n";
    $pkginfo = `/bin/pkginfo`;
    print LOG $pkginfo;
    &print_field_seperator;
}

sub disklist {
    print LOG "DISKLIST:\n\n";
    $disklist = `/usr/bin/ksh -c '${BINDIR}/disklist 2>&1'`;
    if ($disklist =~ /not\s+found/i) {
	print "ERROR : ${BINDIR}/disklist not found!\n";
    }
    print LOG $disklist;
    &print_field_seperator;
}

sub rawdump {
    @rawdump = ();
    print LOG "RAWDUMP:\n\n";
    if ( $RDMP_FILE eq "TRUE" ) {
        open( RDMP, $RD_INFILE );
        while ( $line = <RDMP> ) {
            print LOG $line;
            @rawdump = ( @rawdump, $line );
        }
        close RDMP;
    } else {
        if ( ( $HBA eq "S" ) || ( $STORAGE eq "T3" ) ) {
           @rawdump = `/usr/bin/ksh -c '${BINDIR}/rawdump -$HBA -$STORAGE 2>&1'`;
           if ($rawdump[0] =~ /not\s+found/i) {
              print "ERROR : ${BINDIR}/rawdump not found!\n";
           }
        } elsif ( ( $HBA eq "P"  ) && ( $STORAGE eq "A5" ) ) {
           @rawdump = `/usr/bin/ksh -c '${BINDIR}/pcidump 2>&1'`;
           # Check for missing program
           if ($rawdump[0] =~ /not\s+found/i) {
              print "ERROR : ${BINDIR}/pcidump not found!\n";
           }
        } 
        print LOG @rawdump;
    }
    &print_field_seperator;
}

sub port_summary {
    my ($max, $wwn, $line, @line, $encname, %sflist, @unique, %seen, $drive, @alldrives, @driveoutput, %drivemap, $mapkey, $curloop, $spacecount, @raw_loop, $rawline, @rawline, @unique_keys);
    my $scount = 0;
    my $pcount = 0;
    my $ecount = 0;
    my $sfcount = 0;
    my $sf = "";
    my $highestsf = 0;
    my $first_ha = "FALSE";

    @temp_loop_list = ();
    @sorted_sf_list = ();
    @unique_keys = ();

    &make_port_list( $HBA );
    if ( $HBA eq "P" ) {
       @rawdump = `/usr/bin/ksh -c '${BINDIR}/rawdump -$HBA -$STORAGE 2>&1'`;
    }
    push @rawdump, " ";
    foreach $rawline (@rawdump) {
        # go thru each section of the rawdump separated by a blank line.
        # each section is a single loop (sf)
        #
        (@rawline) = split(' ', $rawline);
        if (! $rawline[0] ) {
            # blank line encountered
            # process previous loop data
	    $first_ha = "FALSE";

	    # get all of the keys ( IB/enclosure info )
            foreach $line (@raw_loop) {
                (@line) = split(' ', $line);
                # find all the enclosures on a loop
                if ($line =~ /^(\S+),([AB])[01]/) {
                    # found an enclosure line
                    $encname = $1;
                    $curloop = $2;
                    $mapkey = "$sf,$encname,$curloop";
                    if (!grep { /$mapkey/ } @unique_keys) {
                        push @unique_keys, $mapkey;
                    }
                    if (!grep { /$encname/ } @unique) {
                        $ecount += 1;
                        push @unique, $encname;
                    }
                    # a hash of lists (multivalues)
                    $a1 = grep {/$encname/} @{$sflist{$sf}};
                    push(@{$sflist{$sf}}, $encname) if (! $a1);
		}
	    }
	    # end of IB info loop

            # get all the disks drives per IB
            foreach $line (@raw_loop) {
                (@line) = split(' ', $line);
                if ($line[0] =~ /^(\S+,)([rf]\d+)/) {
                   # disk drive line found
                   $drive = "$2";
                   $drive .= $1 if ($line[3] =~ /(c\d+t\d+)/) ;
                   $drive .= ",$line[1]";  # wwn
		   $drive_wwn = "$line[1]";  # wwn
		   # keep a running count of unique drives per loop
                   if (!grep { /$drive_wwn/ } @alldrives) {
                       push @alldrives, $drive_wwn;
                       $ddcount +=1;
                   }
                   foreach $mapkey (@unique_keys) {
                      @mapkey = split(/,/, $mapkey);
                      @enc = split(/,/, $line);
		      if ( $mapkey[1] =~ /$enc[0]/ ) {
                         push(@{$drivemap{$mapkey}}, $drive);
	              }
		   }
                }

	    }
	    # end of drive loop

            @raw_loop = ();
	    @unique_keys = ();

        } elsif ($rawline[0] =~ /\*HA\*/) {
            # new host adaptor line found
	    # save only the first HA found, others may be hanging on the loop
	    if ( $first_ha eq "FALSE" ) {
		$first_ha = "TRUE";
                my $cursfnum;
                if ( $HBA eq "S" ) {
                   $sf = $rawline[9];
                   $cursfnum = substr $sf, 2, 3;
                } elsif ( $HBA eq "P" ) {
                   $sf = $rawline[8];
                   $cursfnum = substr $sf, 3, 4;
                }
                $highestsf = $cursfnum if ($cursfnum > $highestsf);
	    } 
        } elsif (! ($rawline[0] =~ /\*Unknown Type\*/)) {
            # keep line for later processing
            push @raw_loop, $rawline;
        }
    }
    print LOG "LOOP SUMMARY:\n\n";

    @socals = ();
    foreach $line (@LD_A5_PORTS) {
       $a1 = $#{$sflist{$SFNUM{$line}}} + 1;   # number of enclosures on the loop
       if ( ( $a1 == 0 ) || ( $DDCNT{$line} == 0 ) ) {
	  next;
       }
       if (!grep { /$SOCNUM{$line}/ } @socals) {
          push @socals, $SOCNUM{$line};
          $scount++;
       }
       $temp = "$SOCNUM{$line}\tPort $PORTNUM{$line}\t$SFNUM{$line}\t$CNUM{$line}";
       $temp_loop_list[++$#temp_loop_list] = "$temp\t$a1 enclosure(s)\t$DDCNT{$line} ${LUN_DISK}(s)\t$line\n";
    }
    undef %seen;
    @unique = grep { ! $seen{$_} ++ } (values %SOCNUM);
    $pcount = scalar(values %PORTNUM);
    #$sfcount = scalar(values %SFNUM);
    @temp_loop_list = sort (@temp_loop_list);
    print LOG @temp_loop_list;

    print LOG "\nENCLOSURES:\n\n";
    #
    # sort in numerical sequence
    #
    for ($sf = 0; $sf <= $highestsf; $sf ++) {
       if ( $HBA eq "S" ) {
          push @sorted_sf_list, "sf$sf" if exists $sflist{"sf$sf"};
       } elsif ( $HBA eq "P" ) {
          push @sorted_sf_list, "ifp$sf" if exists $sflist{"ifp$sf"};
       }
    }
    foreach $sf (@sorted_sf_list) {
        $sfcount++;
	$first_encname = "TRUE";
        foreach $encname (sort @{$sflist{$sf}}) {
	    if ( $first_encname eq "TRUE" ) {
	       $first_encname = "FALSE";
               print LOG "$sf: ";
	    }
            print LOG "$encname ";
            foreach $curloop ("A", "B") {
                undef @alldrives;
                $mapkey = "$sf,$encname,$curloop";
                if (defined @{$drivemap{$mapkey}}) {
                    #
                    # separate drive list into front and rear lists
                    #
                    @alldrives = @{$drivemap{$mapkey}};
                    @front = ();
                    @rear = ();
                    foreach $line (@alldrives) {
                        $front[$1] = $2 if ($line =~ /^f(\d+)(c\d+t\d+.*)/);
                        $rear[$1] = $2 if ($line =~ /^r(\d+)(c\d+t\d+.*)/);
                    }
                    #
                    # put into 2 columns
                    #
                    if ($#front > $#rear) {
                        $max = $#front + 1;
                    } else {
                        $max = $#rear + 1;
                    }
                    $max = 7 if ($max < 7);
                    undef $alldrives;
                    for ($a1 = 0; $a1 < $max; $a1 ++) {
                        $front[$a1] = "-                         " if (!defined($front[$a1]));
                        $rear[$a1] = "-" if (!defined($rear[$a1]));
                        $spacecount = 26 - length($front[$a1]);
                        $front[$a1] .= pack "A$spacecount", " ";
                        $line = "$front[$a1] $rear[$a1]\n";
                        $alldrives .= $line;
                    }
                }
                push @driveoutput, "\n$sf\t$encname\tLoop $curloop\n$alldrives" if defined(@alldrives);
            }
        }
        print LOG "\n";
    }
    print LOG "\nDRIVE MAP:\n";
    print LOG @driveoutput;

    if ( $HBA eq "S" ) {
      print LOG "\n\nTotals: $scount socal(s), $sfcount sf(s), $ecount enclosure(s), $ddcount ${LUN_DISK}(s)\n\n";
    } elsif ( $HBA eq "P" ) {
      print LOG "\n\nTotals: $scount ifp(s), $ecount enclosure(s), $ddcount ${LUN_DISK}(s)\n\n";
    }
    print LOG "============================================================\n";
}

sub smart_compare {
    my ($file1, $file2) = @_;

    my @goldsum = &read_summary("$GOLD_PATH/$file1");
    my @cursum = &read_summary("$GOLD_PATH/$file2");
    my @cloops = &get_socals(@cursum);
    my @gloops = &get_socals(@goldsum);
    my @gold_enclosures = &get_enclosures(@goldsum);
    my @cur_enclosures = &get_enclosures(@cursum);
    my @gold_encl_map = &get_enclosure_map(@goldsum);
    my @cur_encl_map = &get_enclosure_map(@cursum);
    my ($socal_line, $socal, $dummy, $portnum, $sfnum, $cnum, $encl_count, %cur_sflist, %gold_sflist, $i, $equal);
    my ($socal2, $portnum2, $sfnum2, $cnum2, $encl_count2);
    my ($encl_line, %gold_encl_names, %cur_encl_names, $encl_list, @all_cur_encl, %all_cur_enclosures, @all_gold_encl, %all_gold_encl);
    my $allenclosuresvisible = 0;

    $equal = 1;
    for ($i = 0; $i <= $#goldsum; $i ++) {
        $equal = 0 if (!($goldsum[$i] eq $cursum[$i]));
        last if (!$equal);
    }
    return if $equal;
    $CHANGES_DETECTED = "TRUE";

    # build hash of enclosures per sf
    foreach $encl_line (@gold_enclosures) {
        ($sfnum, $encl_list) = split(/:/, $encl_line);
        push(@all_gold_encl, split(/ /,$encl_list));
        $gold_encl_names{$sfnum} = $encl_list;
    }
    @all_gold_encl = grep { ! $all_gold_enclosures{$_} ++ } (sort @all_gold_encl);

    foreach $encl_line (@cur_enclosures) {
        ($sfnum, $encl_list) = split(/:/, $encl_line); 
        push(@all_cur_encl, split(/ /,$encl_list));
        $cur_encl_names{$sfnum} = $encl_list;
    }
    @all_cur_encl = grep { ! $all_cur_enclosures{$_} ++ } (sort @all_cur_encl);
    $allenclosuresvisible = 1 if (@all_cur_encl eq @all_gold_encl);

    $equal = 1;
    for ($i = 0; $i <= $#gloops; $i ++) {
        $equal = 0 if (!($gloops[$i] eq $cloops[$i]));
        last if (!$equal);
    }
    if (!$equal) {
        # there are differences in the sf's, find out what they are
        # build a hash of sf numbers and begin comparing
        foreach $socal_line (sort @cloops) {
            ($socal, $portnum, $sfnum, $cnum, $encl_count) = split(/\t/, $socal_line);
            $cur_sflist{$sfnum} = $socal_line;
        }
        foreach $socal_line (sort @gloops) {
            ($socal, $portnum, $sfnum, $cnum, $encl_count) = split(/\t/, $socal_line);
            # if found an exact match, remove from hash so only ones left are new or modified loops
            ($encl_count) = split(/ /,$encl_count);
            if (!defined $cur_sflist{$sfnum}) {
                # found an old sf that does not exist anymore in current config, we don't
                # dont validate disks for this loop 
                $msg = "Missing online port: $sfnum on $socal $portnum, $encl_count enclosures";
                &logit("ERROR", 4013, $msg);
            } elsif ($cur_sflist{$sfnum} eq $socal_line) {
                # found matching sf exactly, remove it from list
                delete($cur_sflist{$sfnum});  
            } else {
                # found changed sf save for later breakdown of what was changed
                $gold_sflist{$sfnum} = $socal_line;
            }
        }
        # go through current snapshot again looking for new sf's
        foreach $socal_line (sort values %cur_sflist) {
            ($socal, $portnum, $sfnum, $cnum, $encl_count) = split(/\t/, $socal_line);
            ($encl_count) = split(/ /,$encl_count);
            if (!defined $gold_sflist{$sfnum}) {
                # found a new sf that wasn't in gold snapshot at all, output message and remove
                # from hash, since we don't need it anymore. All that will be left is changes to 
                # existing sf's requiring examination.

                &logit("WARNING", 3012, "New online port detected: $sfnum on $socal $portnum, $encl_count enclosures");
                delete($cur_sflist{$sfnum});
            } else {
                # found an existing but changed sf, figure out what changed
                ($socal2, $portnum2, $sfnum2, $cnum2, $encl_count2) = split(/\t/, $gold_sflist{$sfnum});
                if ($socal ne $socal2) {
                    # sf moved from one socal card to another
                    &logit("WARNING", 3020, "Port has moved: $sfnum now on $socal $portnum ($cnum)");
                } elsif ($portnum ne $portnum2) {
                    # sf moved to different port on same socal card
                    &logit("WARNING", 3020, "Port has moved: $sfnum now on $socal $portnum ($cnum)");
                } 
            }
        }
    }
    
    if (!$allenclosuresvisible) {
        # some enclosures are not available at all, no loop redundancy at this time
        #
        foreach $encl_line (@all_gold_encl) {
          if (!exists $all_cur_enclosures{$encl_line}) {
              &logit("ERROR", 4021, "Enclosure offline: $encl_line");
              delete($all_gold_enclosures{$encl_line}); # no need to check disks if offline
          }
        }
    }
    #
    # check World Wide Numbers for all the drives for the enclosures
    foreach $encl_line (sort keys %all_gold_enclosures) {
        @gold_encl_map = &get_disk_map($encl_line, 'f', @goldsum);
        @cur_encl_map = &get_disk_map($encl_line, 'f', @cursum);
        
        for ($i = 0; $i < 15; $i ++) {
            $gold_encl_map[$i] = '-' if (!defined($gold_encl_map[$i]));
            $cur_encl_map[$i] = '-' if (!defined($cur_encl_map[$i]));
            if ($gold_encl_map[$i] ne $cur_encl_map[$i]) {
                ($cnum2, $wwn2) = split(/,/,$cur_encl_map[$i]);
                ($cnum, $wwn) = split(/,/,$gold_encl_map[$i]);
                if (($cnum2 eq '-') and ($cnum ne '-')) {
                    &logit("ERROR", 4014, "Disk drive offline: $encl_line, front slot $i"); 
                } elsif (($cnum eq '-') and ($cnum2 ne '-')) {
                    &logit("WARNING", 3014, "New disk drive detected: $encl_line, front slot $i");
                } elsif ($wwn2 ne $wwn) {
                    &logit("WARNING", 3014, "Disk drive replaced: $encl_line, front slot $i");
                }
            }
        }
        @gold_encl_map = &get_disk_map($encl_line, 'r', @goldsum);
        @cur_encl_map = &get_disk_map($encl_line, 'r', @cursum);
        for ($i = 0; $i < 15; $i ++) {
            $gold_encl_map[$i] = '-' if (!defined($gold_encl_map[$i]));
            $cur_encl_map[$i] = '-' if (!defined($cur_encl_map[$i]));
            if ($gold_encl_map[$i] ne $cur_encl_map[$i]) {
                ($cnum2, $wwn2) = split(/,/,$cur_encl_map[$i]);
                ($cnum, $wwn) = split(/,/,$gold_encl_map[$i]);
                if (($cnum2 eq '-') and ($cnum ne '-')) {
                    &logit("ERROR", 4014, "Disk drive offline: $encl_line, rear slot $i");
                } elsif (($cnum eq '-') and ($cnum2 ne '-')) {
                    &logit("WARNING", 3014, "New disk drive detected: $encl_line, rear slot $i");
                } elsif ($wwn2 ne $wwn) {
                    &logit("WARNING", 3014, "Disk drive replaced: $encl_line, rear slot $i");
                }
            }
        }
    }
    #print @gold_encl_map;
    #print "\n@cur_encl_map\n";
    print "*** Changes detected! ***\n" if ($CHANGES_DETECTED = "TRUE" && $VERBOSE);

}

sub get_socals {
    my (@goldsum) = @_;
    my $inside_section = 0;
    my ($line, @loops);

    foreach $line (@goldsum) {
        chomp $line;
        last if ($line =~ /^ENCLOSURES/);
        push @loops, $line if (($inside_section) and ($line));
        $inside_section = 1 if ($line =~ /^LOOP SUMMARY:/);
    }
    return @loops;
}

sub get_enclosures {
    my (@goldsum) = @_;
    my $inside_section = 0;
    my ($line, @loops);

    foreach $line (@goldsum) {
        chomp $line;
        last if ($line =~ /^DRIVE MAP:/);
        push @loops, $line if (($inside_section) and ($line));
        $inside_section = 1 if ($line =~ /^ENCLOSURES/);
    }
    return @loops;
}

sub get_enclosure_map {
    my (@goldsum) = @_;
    my $inside_section = 0;
    my ($line, @loops);

    foreach $line (@goldsum) {
        chomp $line;
        last if ($line =~ /^====================/);
        push @loops, $line if (($inside_section) and ($line =~ /^sf/));
        $inside_section = 1 if ($line =~ /^DRIVE MAP/);
    }
    return @loops;
}

sub get_disk_map {
    # retrieve the World Wide Numbers from a specific A5000 and
    # only get the front or rear info
    my ($encl_name, $backplane, @goldsum) = @_;
    my $inside_section = 0;
    my $foundbox = 0;
    my ($line, @loops);

    $backplane = 'f' if (!$backplane =~ /^f$|^r$/);
    foreach $line (@goldsum) {
        chomp $line;
        $line =~ s/\s+/ /g;
        $inside_section = 1 if ($line =~ /^DRIVE MAP/); # Herb moved up
        next if (!$inside_section); # Herb added
        last if ($line =~ /^====================/);
        if (($inside_section) and ($line =~ /^sf\d+\s(.*)\sLoop\s+(.*)/)) {
            last if $foundbox; # already did it
            $foundbox = 1 if ($encl_name eq $1);
            next;
        }
        if ($foundbox) {
            last if ($line eq ''); # Herb added key line here...
            ($front, $rear) = split(/ /, $line);
            push @loops, $front if (($backplane eq 'f') and ($front));
            push @loops, $rear if (($backplane eq 'r') and ($rear));
        }
   }
    return @loops;
}

sub show_disk_map {

    @rawdump = `/usr/bin/ksh -c '${BINDIR}/rawdump -$HBA -$STORAGE 2>&1'`;

    &open_logfile("temp_map");
    &port_summary;
    &close_logfile;
    print &read_summary("$LOGDIR/temp_map");

}

sub read_summary {
    ###########################################################
    # Open the file and read it looking for the Loop Summary
    # section. Pull it out, into an array and return it 
    ###########################################################
    my ($filename) = @_;
    my ($line, @loopsummary);
    open(GOLDEN,  "$filename") or die "Unable to open logfile : $filename\n";

    $found_section = "FALSE";
    while ($line=<GOLDEN>) {
        $found_section = "TRUE" if ($line =~ /^LOOP SUMMARY:/);
        next if ($found_section eq "FALSE");
        last if ($line =~ /^============/);
        # 
        # we are in the middle of our section in the snapshot
        #
        push @loopsummary, $line;
    }
    close(GOLDEN);
    print "\nWarning: LOOP SUMMARY section missing from snapshot. Please run '${PROGNAME} -save'\n" if ($found_section eq 'FALSE');
    return @loopsummary;
}

sub proc_cli {
    #Initialize state of variables prior to command line parsing
    # can't init CMP and SAVE because they are either set or not set
    my $do_something = "FALSE";

    undef $DISKMAP;
    $SHOW_CONF      = "FALSE";
    $RDMP_FILE      = "FALSE";
    $HBA            = "";
    $STORAGE        = "A5";

   # For every command line argument update the usage array
   # SBUS command line options
    $usage[0] = "$PROGNAME help | -save | -cmp | -log | -mail name | -v | -warn | -rd <file> | -sc | -map | -S | -P | -A5 | -T3\n";
    $usage[1]  = "help - displays usage\n";
    $usage[2]  = "save - save the system state\n";
    $usage[3]  = "cmp  - compare the current system state with the saved system state\n";
    $usage[4]  = "log  - log to /var/adm/messages\n";
    $usage[5]  = "mail - mail errors to an admin\n"; 
    $usage[6]  = "v    - verbose\n";
    $usage[7]  = "warn - Mail warnings as well as errors\n";
    $usage[8]  = "rd <file>  - Uses the specified file for the rawdump entry\n";
    $usage[9]  = "sc   - Show the configuration prior to execution \n";
    $usage[10] = "map  - Show disk map details\n";
    $usage[11] = "S    - Select SBUS Host Adapters (default)\n";
    $usage[12] = "P    - Select PCI Host Adapters\n";
    $usage[13] = "A5   - Select A5K Series Storage (default)\n";
    $usage[14] = "T3   - Select T300 Series Storage\n";

    if ($#ARGV<0) {

        print "options:\n";
        foreach $item (@usage) {print $item};
        print "\n";
        die "\n$PROGNAME requires arguments.\n";
    }

    $arg = shift @ARGV;
    while($arg) {
	if ($arg =~ /help/)  {
	    print "If you need help there is a manpage available.\n";
            die @usage if ($arg =~ /help/);
        } elsif ($arg =~ /-cmp/) {
            $CMP          = TRUE;
            $do_something = "TRUE";
        } elsif ($arg =~ /-save/) {
            $SAVE         = TRUE;
            $do_something = "TRUE";
        } elsif ($arg =~ /-log/) {
            $LOGGING      = TRUE;
        } elsif ($arg =~ /-mail/) {
            $MAIL_RESULTS   = TRUE;
	    $MAIL_RECIPIENT = shift @ARGV;
        } elsif ($arg =~ /-v/) {
            $VERBOSE      = TRUE;
        } elsif ($arg =~ /-sc/) {
            $SHOW_CONF = "TRUE";
        } elsif ($arg =~ /-rd/) {
            $RDMP_FILE = "TRUE";
	    $RD_INFILE = shift @ARGV;
        } elsif ($arg =~ /-warn/) {
            $WARN    = TRUE;
        } elsif ($arg =~ /-map/) {
            $DISKMAP = "TRUE";
            $do_something = "TRUE";
        } elsif ($arg =~ /^-S/) {
            $HBA = "S";
            check_bus_option( $HBA );
        } elsif ($arg =~ /^-P/) {
            $HBA = "P";
            check_bus_option( $HBA );
        } elsif ($arg =~ /^-A5/) {
            $STORAGE = "A5";
            $LUN_DISK = "Drive";
        } elsif ($arg =~ /^-T3/) {
            $STORAGE = "T3";
            $LUN_DISK = "LUN";
        }

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

	if ( $STORAGE eq "A5" ) {
		$STORAGE_DEVICE = "A5000";
	} elsif ( $STORAGE eq "T3" ) {
		$STORAGE_DEVICE = "T300";
	}
    $GOLD_PATH  = "${LOGDIR}/${STORAGE}";

    if ($do_something eq "FALSE") {

        print "SBUS options:\n";
        foreach $item (@usage) {print $item};
        print "\n";
        die "\n$PROGNAME requires arguments.\n";
    }

    if ( $HBA ne "P" && $HBA ne "S" ) {
        &get_fcal_hbas();
        &select_fcal_hbas();
        if (! $HBA) {
           die "Verify HBA and driver installation.\nNo FC-AL HBA(s) detected!\n";
        }
    }

    print "HBA: $HBA\n" if ($DEBUG);

#Exit on unsupported variables 
#
    die @usage if ($do_something eq "FALSE");

# check for mail setup
    die @usage if (($MAIL_RESULTS) && (! $MAIL_RECIPIENT));
    if (($WARN) && (! $MAIL_RESULTS)) {
        print "You must specify mail with warn!\n";
        die @usage;
    }
}

sub showrev {
    print LOG "PATCHES:\n\n";
    $showrev = `/usr/bin/showrev -p | /usr/bin/awk '{print \$2}' | /usr/bin/ksh -c '/usr/bin/sort 2>&1'`;
    print LOG $showrev;
    &print_field_seperator;
}

sub print_field_seperator {
    print LOG "\n";
    print LOG "=" x 60;
    print LOG "\n";
}

sub header {
    print LOG "HEADER:\n\n";
    $header = `/usr/bin/uname -a`;
    print LOG $header;
    $date = `/usr/bin/date`;
    print LOG $date;
    &print_field_seperator;
}

sub storstat {
    print LOG "STORSTAT:\n\n";
    $storstat = `${BINDIR}/storstat -$HBA -$STORAGE -warn -all`; 
    print LOG $storstat;
    &print_field_seperator;
}

sub logit {
    # log errors and collect messages to mail to specified user
    #
    ($status, $error_number, $message) = @_;
    $error_number = "xxx" if (! $error_number);
    $status = "ERROR" if (! $status);
    $message = "Error occurred" if (! $message);

    # Define this global variable and send mail later
    if ($status eq "ERROR") {
        $ERROR_FLAG  = "TRUE";
        print MAIL "[$STORAGE_DEVICE:DIAG:$status:$error_number] $message\n";
    }
    if (($status eq "WARNING") && $WARN) {
        $ERROR_FLAG  = "TRUE";
        print MAIL "[$STORAGE_DEVICE:DIAG:$status:$error_number] $message\n";
    }

    print "$status : $message\n";
    &logger($status, $error_number, $message) if $LOGGING;
}

sub volume_manager_summary {
  # Record the volume manager summary for Veritas and/or SDS
  my $vmdata = &get_volume_manager_data;
 
  if ($vmdata) {
      print LOG "VOLUME MANAGER SUMMARY\n";
      print LOG "----------------------\n\n";
      print LOG "$vmdata\n";
      print LOG "=" x 60;
  }
}

sub vm_compare {
  # compare Veritas and/or SDS information for changes

  my ($golden_log, $current_log) = @_;
  my (@current_section, @golden_section, $line, $found_section, $has_veritas, $has_sds);
  my $section_name = "VOLUME MANAGER SUMMARY";
  my $subject = "Volume manager";

    open(GOLDEN,  "${GOLD_PATH}/$golden_log") or die "Unable to open file : ${GOLD_PATH}/$golden_log\n";

    $found_section = "FALSE";
    while ($line=<GOLDEN>) {
        chomp $line;
        $found_section = "TRUE" if ($line =~ /^${section_name}/);
        next if ($found_section eq "FALSE");
        next if ($line =~ /^\s*#/);
        last if ($line =~ /^============/);
        $has_veritas = 1 if ($line =~ /^VERITAS/);
        $has_sds = 1 if ($line =~ /^SOL/);
        push @golden_section, $line if ($found_section eq "TRUE");
    }
    close(GOLDEN);

    open(CURRENT, "${GOLD_PATH}/$current_log") or die "Unable to open current logfile : ${GOLD_PATH}/$current_log\n";

    $found_section = "FALSE";
    while ($line = <CURRENT>) {
        chomp $line;
        $found_section = "TRUE" if ($line =~ /^${section_name}/);
        next if ($found_section eq "FALSE");
        next if ($line =~ /^\s*#/);
        last if ($line =~ /^============/);
        push @current_section, $line if ($found_section eq "TRUE");
    }
    close(CURRENT);
    $subject = "Solstice Disk Suite" if (($has_sds) and (!$has_veritas));
    if ($#golden_section > $#current_section) {
        &logit("WARNING", 3009, "$subject summary has changed");
        $CHANGES_DETECTED = "TRUE";
        return;
    } elsif ($#golden_section < $#current_section) {
        &logit("WARNING", 3010, "$subject summary has changed");
        $CHANGES_DETECTED = "TRUE";
        return;
    }
    for ($i = 0; $i <= $#golden_section; $i ++) {
        if ($golden_section[$i] ne $current_section[$i]) {
            &logit("WARNING", 3009, "$subject summary has changed");
            $CHANGES_DETECTED = "TRUE";
            return;
        }
    }

}

