#!/usr/bin/perl -I/usr/local/lib/perl5 --  # -*-Perl-*-
#
# t4_rnid_cfg.pl -- script to configure T4 RNID parameters
#
# Copyright 2002, 2003 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# ident	"@(#)t4_rnid_cfg	1.10	04/04/26 SMI
#

require 5.005;

use File::Basename;
use Fcntl ':flock';
use Getopt::Long;
use MIME::Base64;

use Net::Telnet;
use Net::Ping;

use strict;

################################################################
#
# local variables
#
################################################################

my ($version) = "1.10";
my ($prog,$path,$suffix);
my ($packageName) = "SUNWsespfw";
my ($filePath) = "/opt/" . ${packageName} . "/";
my ($rep_name) = "repository";
my ($repos_block) = "fw_access_block";
my ($repos_blocked) = "no";
my ($wanIPaddr, $hostid);
my ($rnidCmd) = "rnid -i ";
my (@buffer, @arrays);
my ($idx) = 0;
my ($port_no) = 6789;
my (%in_vars);
my ($returnCode) = 0;

#
# log message controls
#
my ($logger) = "/usr/bin/logger";
my ($logFacility) = "local7";
my ($logLevel) = "warning";
my ($logMsg) = "Default t4_rnid_cfg log message\n";

#
# T4 variables
#
my ($T4_login) = 'root';
my ($T4_passwd);
my ($T4_speclog) = 'sun';
my ($T4_specpwd) = 'arrayservice';
my ($t4Prompt) = '/:<\d+>/';

#
# telnet "object"
#
my ($tnet) = undef;

#
# ping/Telnet loop controls
#
my ($telnetAttempts) = 6;
my ($tryCtr) = 0;
my ($pingTries) = 0;
my ($pingAttempts) = 8;

# hash to store repository information
my (%curr_repository);

################################################################
#
# subroutines
#
################################################################

#
# &sigHandler(sigName) -- handle signals
# "sigName" must be the name of a valid signal
#
sub sigHandler {
    my ($sigNo) = shift;

	$tnet->close;

	if ( $repos_blocked eq "yes" ) {
		&unblock_repos;
	}
    exit 150;
}

#
# &write_lock(filehandle) -- set an exclusive write lock on filehandle
#
sub write_lock {
    my ($filehandle) = shift;
    flock ($filehandle, LOCK_EX) or
        exit 103;
}

#
# &read_lock(filehandle) -- set an advisory read lock on filehandle
#
sub read_lock {
    my ($filehandle) = shift;
    flock ($filehandle, LOCK_SH | LOCK_NB) or
        exit 103;
}

#
# &unlock(filehandle) -- unlock previously-locked filehandle
sub unlock {
    my ($filehandle) = shift;
    flock ($filehandle, LOCK_UN) or
        exit 103;
}

#
# &block_repos() -- block access to the firewall repository
# This routine creates the file "fw_access_block" if it does not
# exist, and sets an exclusive write lock on the file when preparing
# to access the file.  This means that if the agent is called
# subsequently, it will block trying to set another lock on the
# file.
#
sub block_repos
{
	open BLOCKER, ">" . $filePath . $repos_block;
    &write_lock(\*BLOCKER);
    $repos_blocked = "yes";
}

#
# &unblock_repos() -- restore access to the firewall
#
sub unblock_repos
{
    if ( $repos_blocked eq "yes") {
        &unlock(\*BLOCKER);
        $repos_blocked = "no";
        close BLOCKER;
    }
}

#
# &read_options() -- parse command-line options
#
sub read_options {
   my ($ret_code, $key);

   $ret_code = GetOptions( \%in_vars,
      'array=s', );

   if ( $ret_code eq '' ) {
	   if ( $repos_blocked eq "yes" ) {
		   &unblock_repos;
	   }
      exit 110;
   }
}

#
# &verify_connect(hostname_or_ip) -- ping a host
# This routine will act on the parameter as a hostname or IP address.
# If the parameter is a hostname, the SP must be able to resolve it.
#
sub verify_connect {

    my ($pingHost) = shift;
    my ($status, $ping);

    $ping = Net::Ping->new('icmp',20);
	$status = $ping->ping($pingHost);
    $ping->close;

    return($status);
}

#
# &read_repository() -- read the repository file into the
# "curr_repository" hash
#
sub read_repository {
    my ($key,$value);
    my $rep_file = $filePath . $rep_name;

    if ( -e $rep_file ) {

      # open for reading
      $rep_file = "<" . $rep_file;
	  open REPOSITORY, $rep_file or do {
		  if ( $repos_blocked eq "yes" ) {
			  &unblock_repos;
		  };
		  exit 102;
	  };

      # lock the repository while we read it
      &read_lock(\*REPOSITORY);

      # read the repository file, load "curr_repository" hash
      while (<REPOSITORY>) {
          ($key,$value) = split /:/;
          chomp $value;
          $curr_repository{$key} = $value;
      }

      # unlock the repository file
      &unlock(\*REPOSITORY);
      close REPOSITORY;

    } else {
		if ( $repos_blocked eq "yes" ) {
			&unblock_repos;
		}
        exit 102;
    }

  return;
}

#
# &new_telnet(ip.addr, prompt) -- set up a new Telnet connection
#
sub new_telnet {
	my ($target) = shift;
	my ($prompt) = shift;
	my ($timeoutVal) = 30;
	my ($onErr) = 'return';
	my ($retCode) = "";

    # create a telnet object
    $retCode =  130 unless ($tnet = new Net::Telnet);

	if ( ! $retCode ) {
		# set up configuration options:
		# "return" on error
		# default command timeout
		# match pattern for main menu
		$tnet->errmode($onErr);
		$tnet->timeout($timeoutVal);
		$tnet->prompt($prompt);
	}

	if ( ! $retCode ) {
		# try to connect
		$retCode =  130 unless
			($tnet->open(Host => $target));
	}

	if ( ! $retCode ) {
		$retCode =  134 unless $tnet->login($T4_login, $T4_passwd);
	}

	return $retCode;
}

#
# &close_telnet() -- close the telnet connection
#
sub close_telnet {
    $tnet->close;
}

#
# &get_t4_pwd() -- decode and return a the T4 administrative password
#
sub get_t4_pwd {
   my ($pwd_enc);
   my ($pwd_dec) = '';
   my ($pwd_file) = "/opt/SUNWstade/System/passwords/array";

   if ( -e $pwd_file ) {
	   open PWD_FILE, "<" . $pwd_file or do {
		 if ( $repos_blocked eq "yes" ) {
			 &unblock_repos;
		 }
         exit 104;
   };

     $pwd_enc = <PWD_FILE>;

     close PWD_FILE;

     chomp $pwd_enc;

     if ( $pwd_enc eq 'place Holder' ) {
		if ( $repos_blocked eq "yes" ) {
			&unblock_repos;
		}
        exit 105;
     } else {
        $pwd_dec = &decode_base64($pwd_enc);
     }
   }

   return ($pwd_dec);
}

#
# &get_hostid() -- get system hostid
#
sub get_hostid {
   my ($cmd) = "/usr/bin/hostid |";
   my ($h_id);

   # retrieve the hostid
   open HOSTID, $cmd or do {
	   if ( $repos_blocked eq "yes" ) {
		   &unblock_repos;
	   };
      exit 101;
   };

   $h_id = <HOSTID>;

   close HOSTID;

   # remove trailing newline
   chomp $h_id;

   return ($h_id);
}

#
# &find_arrays - see if we can reach all the arrays in the inventory
#
sub find_arrays {
	my ($currArray);
	my ($idx) = 0;
	my ($pingReturn);

	open ARRAYS, "/usr/local/bin/build_array_list |" or do {
		if ( $repos_blocked eq "yes" ) {
			&unblock_repos;
		}
		exit 101;
	};

	while ( <ARRAYS> ) {
		$currArray = $_;
		chomp $currArray;

		while ( $pingTries < $pingAttempts ) {
			$pingReturn = "";
			$pingReturn = &verify_connect($currArray);
			if ( $pingReturn == 1 ) {
				$arrays[$idx] = $currArray;
				$idx++;
				$pingTries = 0;
				last;
			} else {
				$pingTries++;
			}

		}

		if ( $pingReturn != 1 ) {
			# log the fact that we couldn't reach an array
			$logMsg = "Could not reach array at ${currArray}\n";
			system "${logger} -p ${logFacility}.${logLevel} -t ${prog} ${logMsg}";
		}
	}

	# check error return of build_array_list.
	$? && do {
		&unblock_repos;
		die;
	};
	
	close ARRAYS ;
}

################################################################
#
# main -- start of processing
#
################################################################

#
# get our name (uses Basename)
#
($prog,$path,$suffix) = &fileparse($0, ".pl");

#
# handle these signals
#
$SIG{'INT'} = 'sigHandler';
$SIG{'QUIT'} = 'sigHandler';

#
# get our command-line options
#
&read_options;

#
# block access to the repository
#
&block_repos;

#
# read stored repository, create working copy
#
&read_repository;

$hostid = &get_hostid;
$wanIPaddr = $curr_repository{WanIPAddr};

# build RNID command
$rnidCmd = $rnidCmd . $wanIPaddr . " -p " . $port_no . " -n " . $hostid;

# determine what our password should be
$T4_passwd = &get_t4_pwd;

# if we were given an array name on the command line, we only
# want to set that one.  Otherwise, find them all
if ( exists $in_vars{array} ) {
   @arrays = $in_vars{array};
} else {
   	&find_arrays;
}

if ( $#arrays >= 0) {
	while ($idx <= $#arrays) {
		# reset the return code so we don't inadvertently skip a good array
		$returnCode = "";

		while ( $tryCtr < $telnetAttempts ) {
   		# establish a Telnet connection to the T4 and login
   		$returnCode = &new_telnet($arrays[$idx], $t4Prompt);
			if ( $returnCode ) {
				$tryCtr++;
				sleep 30;
			} else {
				$tryCtr = 0;
				last;
			}
		}

   	if ( ! $returnCode ) {
			# access the submenu that allows us to set the RNID parameters
			$tnet->print($T4_speclog);
			$tnet->waitfor(/Password: /);
			$tnet->cmd($T4_specpwd);

			# update the necessary RNID information
			$tnet->cmd($rnidCmd);

			# exit and close the Telnet connection
			$tnet->cmd('exit');
			&close_telnet;
  		} else {
	  		# log the error
			$logMsg = "Error ${returnCode} logging into array at ${arrays[$idx]}\n";
			system "${logger} -p ${logFacility}.${logLevel} -t ${prog} ${logMsg}";
		}

		# increment the index
		$idx++;
	}
}

#
# restore access to the repository
#
&unblock_repos;

#
# time to go
#
exit $returnCode;
