#! /usr/bin/perl
use strict;
use warnings;
use Getopt::Long; # used in getting unix-style options
use Win32::OLE qw(in with); # use base OLE
use Win32::OLE::Const 'Microsoft Excel'; # use OLE/Excel
   $Win32::OLE::Warn = 3; # die on errors...
use Win32::OLE::Enum;
use Win32::OLE::Variant;

################################################################################
# Purpose:
#    This script catalogues the psinfo information on systems, including patches
# and software. This is sent to an excel spread sheet for easy comparison. This
# is done to keep on top of what software/patches do not have negative impacts
# upon SeaChange(tm) systems
#
# This should be kept as both a script and an executable. The executable should
# be made by the last person to revise the script. The Active State Perl PDK has
# been used.
#
# Created: 03/02/2006
# Original Author: Josh Perlmutter
# Maintainers:
# Last Revised: 03/10/2006
# Copyright SeaChange International (tm)
################################################################################

my(@comps);
my($comp, $date);
# standard options/revision information variables
# verbose line is 79 chars long w/2 \t
# base to use to add options:        -<option>\t\t<use>
my($help, $usage, $verb, $qrev, $rev, $revdat, $modules, $log, $err);
$modules="\tnone as of now\n\n"; $help=0; $qrev=0; $err=0;
$rev="0.0.5"; $revdat="03/10/2006"; $verb=0; $log='';
$usage = <<EOD;
\tFile: $0\tVersion: $rev\tReleased: $revdat\n
This is the $0 file.\n
The options for this file are:
        -help           This help screen only (overrides all other options)
        -log            File to log to (appends if the file exists)
        -version        List Version and exit (overrides everything but -help)
        -verbose        Turn on verbose (useful for debugging errors; call twice
                         for extra output)

        usage ([] denotes optional argument {} denotes default value
               {} denotes option set of which your choice of one is required):
$0 [-help] [-version] [-verbose]
to debug try: $0 -verb -verb > debug_log.txt\n
EOD

######
# this section deals with options. it looks for unix-style options passed. that
# is, <script> -option [value]
GetOptions ('help'=>\$help, 'verbose+'=>\$verb, 'version'=>\$qrev,
	    'log:s'=>\$log, 'computers:s' => \$comp);

###########################
# arguments ARE required
unless($help || $qrev ||
       ($log)){
  my $necessary = <<EOD;
        You must supply at least 1 parameter(s) to run this program. It needs:
-log     log file to log actions to (appends if existing)
Turning on help
EOD
  print $necessary;
  $help++;
}

# show usage and exit if asked for
die $usage if $help;

# report version and exit
die "\tFile: $0\tVersion: $rev\tReleased: $revdat\n" if $qrev;

####################################################
## this is a standardized reporting subroutine #####
## it takes a line and a flag for verbose and  #####
## reports to a universal file handle (and screen) #
####################################################
sub rep{
  # this assumes two passed values,
  # first a message
  # second a flag for verbose (screen printing)
  my $time=localtime().' | ';
  print LOG "$time $_[0]\n";
  if($_[1]){ print STDOUT "$time $_[0]\n"; }
}

sub getinfo(){
  #############################################################################
  ## this grabs the system information for reporting at the begining of logs ##
  #############################################################################

  my $nname=`hostname`; chomp($nname);
  my $nos=`ver`; $nos =~ s/\W*(\w.*\])\s.*/$1/;
  my $nip=`ipconfig`; $nip =~ s/\D+([\d\.]{7,15})[\D\d]*/$1/;

  my $info= <<EOD;
Program Information:
Script: $0
Last Updated: $revdat          Version $rev

System Information:
       Node Name: $nname
       Node OS: $nos
       Node IP: $nip

EOD
  return $info;
}

sub dbg{
  # this is a debugging subroutine that prints things to screen if the
  # verbosity level is high enough
  my $item=$_[1];
  my $dbgpt=$_[0];

  while(1){
    print "Debug Point: $dbgpt\nItem value:\n$item
\n\t\tShall I continue? (y or n)\n\n";
    if(<STDIN> =~ m/^n$/i){ die "\t\t\tyou chose to leave at $dbgpt\n\n"; }
    elsif(<STDIN> =~ m/^y$/i){ print "Continuing to next point\n\n"; last; }
    else{ print "response not understood\n"; }
  }
}

# start the log
open LOG, ">>$log" or die "cannot open log file";
&rep(&getinfo,$verb);
############################ Below is non-templeted ############################
# make the computer list an array
@comps=split ',', $comp;

# get the date
my @rdate=split ' ', localtime();
$date=$rdate[4].'-'.$rdate[1].'-'.$rdate[2];
if($verb>1){ &rep("$date\n",$verb); }

# set up OLE interaction
# get an active Excel or create a new one
my $Excel = Win32::OLE -> GetActiveObject('Excel.Application')
  || Win32::OLE -> new('Excel.Application', 'Quit');
$Excel -> {'Visible'} = 1;

my $labrep=`cd`;
chomp($labrep);
if (substr($labrep, -1) ne '\\'){ $labrep.='/'; }
$labrep.="Lab_Report_$date.xls";

# does the file exit?
if(-e "$labrep"){
  #we are just adding to it, so open it
  my $report = $Excel->Workbooks->Open("$labrep");
  my $ws = $report->Worksheets(1);

  # start row counter
  my $row=2;
  # set row counter to continue at the end of what's there
  my $ncell = $ws -> Range("A$row") -> {'Value'};
  while($ncell =~ m/\w+/i){
    $row++; # increment row & V- grab next row's data -V
    $ncell = $ws -> Range("A$row") -> {'Value'};
  }

  # we need to get the information & parse it into the excel file for each node
  foreach my $node (@comps){
    # for each node we check the information returned
    my $ldate = localtime();
    &rep("Examining information gathered from node $node @ $ldate\n", $verb);

    # a sub routine defined later in the script is used for simplicity
    &populate($node,$row,$ws);

    # end the loop increasing the row number
    $row++;
  }
}else{
  # we have to create it, including make the first row
  $Excel -> { 'SheetsInNewWorkBook' } = 1;
  my $workbook = $Excel -> Workbooks -> Add();
  my $ws = $workbook -> Worksheets(1);
  $ws -> { 'Name' } = "Lab Report $date";

  # set first row titles
  $ws -> Cells(1, "A") -> {'Value'} = "Node";
  $ws -> Cells(1, "B") -> {'Value'} = "NAV";
  $ws -> Cells(1, "C") -> {'Value'} = "Alarms";
  $ws -> Cells(1, "D") -> {'Value'} = "SNMP";
  $ws -> Cells(1, "E") -> {'Value'} = "Uptime";
  $ws -> Cells(1, "F") -> {'Value'} = "Kernel Version";
  $ws -> Cells(1, "G") -> {'Value'} = "Product Type";
  $ws -> Cells(1, "H") -> {'Value'} = "Product Version";
  $ws -> Cells(1, "I") -> {'Value'} = "Service Pack";
  $ws -> Cells(1, "J") -> {'Value'} = "Kernel Build Number";
  $ws -> Cells(1, "K") -> {'Value'} = "Registered Organization";
  $ws -> Cells(1, "L") -> {'Value'} = "Registered Owner";
  $ws -> Cells(1, "M") -> {'Value'} = "Install Date";
  $ws -> Cells(1, "N") -> {'Value'} = "Activation Status";
  $ws -> Cells(1, "O") -> {'Value'} = "IE Version";
  $ws -> Cells(1, "P") -> {'Value'} = "System Root";
  $ws -> Cells(1, "Q") -> {'Value'} = "Processors";
  $ws -> Cells(1, "R") -> {'Value'} = "Processor Speed";
  $ws -> Cells(1, "S") -> {'Value'} = "Processor Type";
  $ws -> Cells(1, "T") -> {'Value'} = "Physical Memory";
  $ws -> Cells(1, "U") -> {'Value'} = "Installed OS Hotfixes";
  $ws -> Cells(1, "V") -> {'Value'} = "Other Applications";

  # start row counter
  my $row=2;

  foreach my $node (@comps){
    # for each node we check the information returned
    my $ldate = localtime();
    &rep("Examining information gathered from node $node @ $ldate\n", $verb);

    # a sub routine defined later in the script is used for simplicity
    &populate($node,$row,$ws);

    # end the loop increasing the row number
    $row++;
  }

  $workbook -> SaveAs($labrep); # save active sheet
}

# save and exit
$Excel -> Workbooks -> Save(); # save file
$Excel -> Workbooks -> Quit(); # leave excel
my $et=locatime();
&rep("program completed at $et.",$verb); # wrap up log
close LOG; # close log

sub populate{
  # this is a subroutine to populate cells in a microsoft excel spreadsheet
  # this particular one is made for the compinfo.pl script/executable but should
  # be easily modified for other scripts, this routine does ONE row at a time

  my $node=$_[0]; # node who's info sheet needs to be picked apart
  my $row=$_[1]; # row to add info too
  my $ws=$_[2]; # worksheet link
  my $err=0; # error marker

  # this expects the following row set up:
  # A -> Node; B -> NAV; C -> Alarms; D -> SNMP; E -> Uptime;
  # F -> Kernel Version; G -> Product Type; H -> Product Version;
  # I -> Service Pack; J -> Kernel Build Number; K -> Registered Organization;
  # L -> Registered Owner; M -> Install Date; N -> Activation Status;
  # O -> IE Version; P -> System Root; Q -> Processors; R -> Processor Speed;
  # S -> Processor Type; T -> Physical Memory; U -> Installed OS Hotfixes;
  # V -> Other Applications;

  # set the first cell since that's always going to be the same
  $ws -> Cells($row, "A") -> {'Value'} = "$node";

  my $cmd = "psinfo -h -s \\\\$node";
  my @examine = split "\n", `$cmd`;

  # noting which couldnt be connected to...
  if($examine[6] =~ m/The network path was not found./i ){
    $ws -> Cells($row, "B") -> {'Value'} = "The network path was not found.";

  }else{
    # we need to run through the information to put into the rows
    # drop what's before what we need
    while (!($examine[0] =~ m/Uptime.*/i)){ shift(@examine); }

    while (!($examine[0] =~ m/OS Hot Fix.*/i)){
      # now go through a series of if/elsif sections for the rest
      # hot fixes and programs will be handled slightly differently though

      if($examine[0] =~ m/Uptime:\s+(\w.*)/i){
	$ws -> Cells($row, "E") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Kernel version:\s+(\w.*)/i){
	$ws -> Cells($row, "F") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Product type:\s+(\w.*)/i){
	$ws -> Cells($row, "G") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Product version:\s+(\w.*)/i){
	$ws -> Cells($row, "H") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Service pack:\s+(\w.*)/i){
	$ws -> Cells($row, "I") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Kernel build number:\s+(\w.*)/i){
	$ws -> Cells($row, "J") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Registered organization:\s+(\w.*)/i){
	$ws -> Cells($row, "K") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Registered owner:\s+(\w.*)/i){
	$ws -> Cells($row, "L") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Install date:\s+(\w.*)/i){
	$ws -> Cells($row, "M") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Activation status:\s+(\w.*)/i){
	$ws -> Cells($row, "N") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/IE version:\s+(\w.*)/i){
	$ws -> Cells($row, "O") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/System root:\s+(\w.*)/i){
	$ws -> Cells($row, "P") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Processors:\s+(\w.*)/i){
	$ws -> Cells($row, "Q") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Processor speed:\s+(\w.*)/i){
	$ws -> Cells($row, "R") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Processor type:\s+(\w.*)/i){
	$ws -> Cells($row, "S") -> {'Value'} = "$1";
      }elsif($examine[0] =~ m/Physical memory:\s+(\w.*)/i){
	$ws -> Cells($row, "T") -> {'Value'} = "$1";
      }
    }

    #  remove the line starting "OS Hot Fix..."
    shift(@examine);
    # create a variable for holding the hotfixes
    my $hotfixes="\"";

    while (!($examine[0] =~ m/Applications.*/i)){
      # while hot fixes need to be added. skip blank lines
      if($examine[0] =~ m/\w/){
	chomp($examine[0]);
	$hotfixes .="$examine[0]\n";
      }
    }

    # add the hotfixes
    $ws -> Cells($row, "U") -> {'Value'} = "$hotfixes";

    #  remove the line starting "Applications..."
    shift(@examine);

    my $apps='';
    foreach my $app (@examine){
      # the rest should all be applications to add so we're using a foreach to
      # put them in; only add lines that are not blank; there are 3 special
      # lines: NAV, Alarms, and SNMP
      if($app =~ m/Symantec AntiVirus (\w+)/i){
	$ws -> Cells($row, "B") -> {'Value'} = "ver: $1";
      }elsif($app =~ m/Alarm/i){
	$ws -> Cells($row, "C") -> {'Value'} = "Installed";
      }elsif($app =~ m/SNMP Informant Agent \(([^\)]+)\) (\w.*)/i){
	$ws -> Cells($row, "D") -> {'Value'} = "$1 : $2";
      }elsif($app =~ m/\s+(\w.*)/i){
	$apps .= "$1\n";
      }
    }

    # add the other applications
    $ws -> Cells($row, "V") -> {'Value'} = "$apps";
  }
}
################################################################################
__END__
