#!/usr/bin/perl
# Very quick and _very_ dirty PERL script to gather some necessary debug
# stuff from a SpinServer and package it up nicely for us

use warnings;
use strict;
use Getopt::Long;
use Data::Dumper;

# Get the P4 changelist number
my $p4version = '$Change: 367805 $';

my $client="/sbin/nconfigclient";
my $auth="/sbin/passthruauth";
my $mqtest="/sbin/mqtest";
my $spinver="/sbin/spinver";
my $cat="/bin/cat";
my $netstat="/bin/netstat";
my $ifconfig="/sbin/ifconfig";
my $rdb_dump="/usr/sbin/rdb_dump";
my $lspci="/sbin/lspci";
my $ls="/bin/ls";
my $file="/usr/bin/file";
my $hamgr="/sbin/hamgr";
my $ups_rpc="/sbin/ups_rpc";
my $rpcinfo="/usr/sbin/rpcinfo";
my $ps="/bin/ps";
my $df="/bin/df";
my $tar="/bin/tar";
my $rm="/bin/rm";
my $lsmod="/sbin/lsmod";
my $fcdRpcTest="/sbin/fcdRpcTest";
my $test="/usr/bin/test";
my $rpm="/bin/rpm";
my $sum="/usr/bin/sum";
my $find="/usr/bin/find";
my $vldb_dump="/usr/sbin/vldb_dump";
my $lpinfo="/sbin/lpInfo";
my $spinlocate="/sbin/spinlocate.pl";
my $SpinLogReader="/usr/spinmgmt/bin/SpinLogReader";
my $virusRpcTest="/sbin/virusRpcTest";
my $cp="/bin/cp";
my $compress=0;
our $directory;

use constant Dir => 0;
use constant File => 1;

$p4version =~ s/^\s*\$[C][h][a]n[g][e]:\s*(\d+)\s*\$\s*$/$1/;
print "$0 version $p4version\n";

my $majorversion = GetMajorVersion();

my %options=(
	     username       => "Administrator",
	     password       => "",
	     cifsserver     => "",
	     bypass         => "",
	     compress       =>  0,
	     skipclean      =>  0,
	     showonly       =>  0,
	     messages_limit => -1,
	     );
my @bypass;

sub CmpRev {
    my ($rev1, $rev2) = @_;

#    print "Comparing $rev1 and $rev2 returns ";
    my @rev1 = split(/\./, $rev1);
    my @rev2 = split(/\./, $rev2);
    my $retval = 0;
    for (my $i = 0; $i < ((@rev1 > @rev2) ? @rev2 : @rev1); $i++){
	$retval = ($rev1[$i] <=> $rev2[$i]);
	last if $retval;
    }
#    print "$retval\n";
    $retval;
}

sub handleProcDir {

    foreach my $dirname (@_){
	if (grep(/^$dirname$/, @bypass)){
	    print "Retrieval of $dirname bypassed\n";
	    return;
	}
	my @files;
	my @postprocess;
	my $type = 0;
	if (-d $dirname){
	    opendir(DH, $dirname);
	    @files = readdir(DH);
	    closedir(DH);
	} else {
	    push @files, $dirname;
	    $type = File;
	}

	foreach my $filename (@files){
	    next if ($filename =~ /^\.+$/);   # Skip all files of the form .+
	    my $fullpath;
	    if ($type == File){
		$fullpath = $filename;
	    } else {
		$fullpath = "$dirname/$filename";
	    }
	    my $shortpath = $fullpath;
	    $shortpath =~ s/^\///g;
#	print "File is $fullpath:$shortpath\n";
	    if (-d $fullpath){
		push(@postprocess, $fullpath);
	    } else {
		if (CmpRev($majorversion, "2.1.5") < 0){
		    if (($shortpath =~ /cifs_users/)
			|| ($shortpath =~ /cifs_commands2/)
			|| ($fullpath =~ /stream.d/)
			|| ($shortpath =~ /locks/)){

			print "Skipping $fullpath\n";
			next;
		    }
		}
		getInfo(File => $shortpath, Cmd => "$cat $fullpath");
	    }
	}
	foreach my $subdir (@postprocess){
	    handleProcDir($subdir);
	}
    }
}

sub checkBin {
    my ($name, $file) = @_;

    if (! -f $file){
	warn "Unable to locate the $name binary.  Gathering as much information as possible.";
	return 0;
    }
    return 1;
}

sub GetMajorVersion {
    my $retval = undef;
    if (open(IF, "$spinver |")){
	# Skip header lines
	<IF>;
	<IF>;
	my %revs;
	while (<IF>){
	    my ($module, $version, $build) = split;
	    $revs{$version}++;
	}
	close(IF);
	my $max = 0;
	foreach my $key (keys %revs){
	    if ($revs{$key} > $max){
		$max = $revs{$key};
		$retval = $key;
	    }
	}
    }
    $retval;
}
Getopt::Long::GetOptions(\%options, 'username=s', 'password=s', 'cifsserver=s', 'compress', 'skipclean', 'showonly', 'proc', 'sums', 'bypass=s', 'messages_limit=i');
@bypass = split(/,/, $options{bypass});
my $docifs= 0;
if ($options{username} && $options{password} && $options{cifsserver}){
    $docifs = 1;
}

sub makeParentDir {
    my ($fullpath) = @_;

    my @dirs = split(/\//, $fullpath);
    if ($fullpath =~ /^\//){
	shift @dirs;
    }
    pop @dirs;
    my $currpath = "";
    foreach my $dir (@dirs){
	$currpath .= "/$dir";
#	print "Would mkdir $currpath\n";
	if (!-d $currpath){
	    mkdir ($currpath, 0777) || warn "Could not make directory $currpath";
	}
    }
}

sub getInfo {
    my %params;
    while (@_){
	my $key = ucfirst lc shift;
	my $value = shift;
	if ($key =~ /^Cmd$/){
	    push(@{$params{Cmd}}, $value);
	} else {
	    $params{$key} = $value;
	}
    }

    if (exists($params{Minrev}) && (CmpRev($majorversion, $params{Minrev}) < 0)){
	print "Not gathering information for $params{File}.\n  Version required $params{Minrev}.  Running version $majorversion\n";
	return;
    }
    
    if (exists($params{Maxrev}) && (CmpRev($majorversion, $params{Maxrev}) > 0 )){
	print "Not gathering information for $params{File}.\n  Running version $majorversion higher than Max version $params{Maxrev}\n";
	return;
    }
    
    if (grep(/^$params{File}$/, @bypass)){
	print "Check for $params{File} bypassed\n";
	return;
    }
    if ($params{File} =~ /\//){
	makeParentDir("$directory/$params{File}");
    }
    if ($params{File} ne "/dev/null"){
	$params{File} = "$directory/$params{File}";
	while (-e $params{File}){
	    if (!($params{File} =~ /\.\d+$/)){
		$params{File} .= ".1";
	    } else {
		$params{File}++;
	    }
	}
    }
    if (!open(LOG_FILE, ">$params{File}")){
	warn "Could not execute commands (" . join("#", @{$params{Cmd}}) . "). Error opening $params{File}";
    }
    foreach my $cmd (@{$params{Cmd}}){
	print "====> Executing: $cmd -> ";
	if (!$options{showonly}){
	    print LOG_FILE `$cmd 2>&1`;
	    if ($?){
		print "Error $?\n";
	    } else {
		print "OK\n";
	    }
	} else {
	    print "No exec\n";
	}
    }
    close(LOG_FILE) || die "Could not close LOG_FILE: $!";
}

my @date = localtime;
my $date = sprintf("%02d%02d%02d_%02d%02d%02d", $date[5]+1900, $date[4]+1, $date[3], $date[2], $date[1], $date[0]);
$directory = "/space1/support_$date";

if (-d $directory){
    `rm -rf $directory`;
    if ($?){
	die "Could not remove $directory.  Error was $?.  Exiting";
    }
}
mkdir($directory, 0777) || die "Could not make directory $directory";
chdir($directory) || die "Could not chdir to $directory";

if (open(OF, ">gsi_version")){
    print OF "Data generated by $0 version $p4version\n";
    close OF;
} else {
    warn "Couldn't open gsi_version to write version information: $p4version";
}
# Check on the n-blade
getInfo(File => "root_history", Cmd => "$cat /root/.bash_history");
# Check CIFS config
getInfo(File => "smbconfig_info", Cmd => "$client -l", Cmd => "$client -l -INTERFACE", Cmd => "$client -l -SHARE", Cmd => "$client -l -SID") if checkBin("nconfigclient", $client);
# attempt a pass thru auth of the user we were given on the virtual server
# we were given
if ($docifs && checkBin("passthruauth", $auth)){
    print LOG_FILE "+++++++ PASSTHRUAUTH AUTH ATTEMPT +++++++++\n";
    print LOG_FILE "USERNAME => $options{username}\n";
    print LOG_FILE "CIFSSERVER => $options{cifsserver}\n";
    print LOG_FILE "PASSWORD => ".($options{password} ? "yes" : "no");
    print LOG_FILE `$auth -username\=$options{username} -password\=$options{password} -nbtname\=$options{cifsserver}\n`;
}
# Extract info from /proc.  Need this processing since tar doesn't handle /proc correctly.....
handleProcDir("/proc/spin","/proc/net/nicinfo", "/proc/spinstats") unless $options{proc};
# Check for nospin
getInfo(File => "auth_stats", Cmd => "$auth -STATS -get");
# Check for nospin
getInfo(File => "nospin", Cmd => "if $test -a /etc/nospin; then echo \"Exists\"; else echo \"Doesn\'t exist\"; fi");
# Check for /dev/ups
getInfo(File => "ups", Cmd => "if $test -a /dev/ups; then echo \"Exists\"; else echo \"Doesn\'t exist\"; fi");
# Get the module list
getInfo(File => "module_list", Cmd => "$lsmod");
# Get rpm info
getInfo(File => "rpm_info", Cmd => "$rpm -qa | egrep spin");
# Get the module list
getInfo(File => "checksums", Cmd => "$sum `$rpm -ql \\`cat $directory/rpm_info\\``") if $options{sums};
# Get module address info
getInfo(File => "spinlocate", Cmd => "$spinlocate");
# gather filesystem stats
getInfo(File => "df_info", Cmd => "$df --local");
# gather current memory stats
getInfo(File => "mem_info", Cmd => "$cat /proc/meminfo");
# get the current spinver info
getInfo(File => "software_vers", Cmd => "$spinver");
# Check for vmdump files
getInfo(File => "vmdumps", Cmd => "$ls -lh /space1/vmdump");
# what processes are running
getInfo(File => "ps_info", Cmd => "$ps -efw");
# what rpc servers are registered
getInfo(File => "rpc_info", Cmd => "$rpcinfo -p");
# what is the status of the FCD disks
getInfo(File => "fcd_drive_info", Cmd => "$fcdRpcTest localhost devlist");
# what is the status of the local raids
getInfo(File => "scsi_raid_info", Cmd => "$cat /proc/mdstat");
# get any DBLADE debug information laying around the kernel
getInfo(File => "mqtest_result", Cmd => "$mqtest 102 0");
sleep 2;

# grab all of the messages files, limited by the command line parameter passed
my $message_dir = "/var/log/";
my @files = glob($message_dir . "messages*");

while ($options{messages_limit}-- && (my $filename = shift @files)){
    $filename =~ s/$message_dir//g;
    getInfo(File => "/dev/null", Cmd => "$cp $message_dir$filename $directory/$filename");
}

# Check the state of HA
getInfo(File => "hamgr_state", Cmd => "$hamgr -status");
# Check the ups
getInfo(File => "ups_state", Cmd => "$ups_rpc");
# Check for a core from HA
getInfo(File => "hamgr_core", Cmd => "if $test -a /core; then echo \"Exists\"; else echo \"Doesn\'t exist\"; fi", "$ls -l /core", "$file /core");
# grab the portmap file
getInfo(File => "portmap.cfg", Cmd => "$cat /etc/sysconfig/portmap.cfg");
# grab PCI information
getInfo(File => "pci_info", Cmd => "$lspci");
# Get the device map
getInfo(File => "dev_map", Cmd => "$cat /proc/net/dev_map");
# Check RDB state
getInfo(File => "rdb_dump", Cmd => "$rdb_dump localhost");
# Get interface information
getInfo(File => "interface_config", Cmd => "$ifconfig -a");
# Check Network ports
getInfo(File => "network_ports", Cmd => "$netstat -anp");
# Check Network routes
getInfo(File => "network_routes", Cmd => "$netstat -anr");
# Check Network statistics
getInfo(File => "network_stats", Cmd => "$netstat -s");
# Check the local VLDB state
getInfo(File => "vldb_dump", Cmd => "$vldb_dump localhost");
# Check the local LP state
getInfo(File => "lpInfo", Cmd => "$lpinfo");
# Get the rdb databases
getInfo(File => "spinmgmt_tar", Cmd => "(cd /var/spinmgmt; $tar cf - rdb log) | (cd $directory; $tar xfBp -)");
# Translate the spinmgmt log into a readable format
getInfo(File => "spinevents", Cmd => "$SpinLogReader /var/spinmgmt/log/messages");
getInfo(File => "spinevents.0", Cmd => "$SpinLogReader /var/spinmgmt/log/messages.0");
# Get the antivirus stats
getInfo(MinRev => "2.5", File => "virusd_stats", Cmd => "$virusRpcTest localhost getStatistics");
# Check lost+found directories for contents
getInfo(File => "lost_found", Cmd => "for fs in `grep ext /etc/mtab | awk '{print \$2}'`; do echo \"Fs is \$fs\"; ls -l \$fs/lost+found; done");


# Extract info from /proc again, just so we can see if anything changed.
handleProcDir("/proc/spin","/proc/net/nicinfo", "/proc/spinstats") unless $options{proc};


# XXX not implemented yet
# check to see if there are any core files in some well known directories,
# if this was a "real" debug gather and packagin things directory into a tar
# file it would just tar them up with the rest and send them along, but just
# alert someone they are there, and do a file on them to tell where they
# came from.

my $tarfile="$directory.tgz";
`$tar cvfz $tarfile $directory > /dev/null 2>&1`;
if ($?){
    print "Error creating tar file $tarfile";
} else {
    print "Tar file is $tarfile\n";
    `$rm -rf $directory` unless $options{skipclean};
}
