#
# Copyright 2001-2003 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
#ident	"@(#)RunCommand.pm 1.13	03/03/30 SMI"
#
# Execute a command and cache the results, if possible
#

package Cluster::RunCommand;
use strict;
use Sun::Solaris::Utils qw(gettext);

##############################################################################
#
# Class constructor
#
##############################################################################

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self  = {};
    bless ($self, $class);
    return $self;
}

##############################################################################
#
# Set locale information
#
##############################################################################$
my $lang    = "en";
my $locale  = "C";	# or e.g. en_US
my $country = "US";

$ENV{"LANG"} = "C";
$ENV{"LC_ALL"} = "C";
$ENV{"LC_MESSAGES"} = "C";

##############################################################################
#
# Class Variables
#
##############################################################################

# Set directory paths
my $BIN 		   = '/usr/bin';
my $SBIN		   = '/usr/sbin';
my $CLUSTERBIN		   = '/usr/cluster/bin';

# Set paths to Solaris commands
my $PFEXEC		   = "$BIN/pfexec";
my $PS			   = "$PFEXEC $BIN/ps";
my $SUM			   = "$PFEXEC $BIN/sum";
my $CAT			   = "$PFEXEC $BIN/cat";
my $DATE		   = "$PFEXEC $BIN/date";
my $GREP		   = "$PFEXEC $BIN/grep";
my $AUTHS                  = "$PFEXEC $BIN/auths";
my $ROLES                  = "$PFEXEC $BIN/roles";
my $UNAME		   = "$PFEXEC $BIN/uname";
my $DIDADM		   = "$PFEXEC $SBIN/didadm";

# Set paths to cluster commands
my $CLINFO                 = "$PFEXEC $SBIN/clinfo";
my $SCDPM                  = "$PFEXEC $CLUSTERBIN/scdpm";
my $SCCONF                 = "$PFEXEC $CLUSTERBIN/scconf";
my $SCSTAT                 = "$PFEXEC $CLUSTERBIN/scstat";
my $PNMSTAT                = "$PFEXEC $CLUSTERBIN/pnmstat";
my $SCRGADM                = "$PFEXEC $CLUSTERBIN/scrgadm";
my $SCDIDADM               = "$PFEXEC $CLUSTERBIN/scdidadm";
my $SCSWITCH               = "$PFEXEC $CLUSTERBIN/scswitch";
my $SCHA_CLUSTER_GET       = "$PFEXEC $CLUSTERBIN/scha_cluster_get";
my $SCHA_RESOURCE_GET      = "$PFEXEC $CLUSTERBIN/scha_resource_get";
my $SCHA_RESOURCEGROUP_GET = "$PFEXEC $CLUSTERBIN/scha_resourcegroup_get";

# Set paths to cluster configuration files
my $CCR			   = '/etc/cluster/ccr';
my $DID_INSTANCES	   = "$CCR/did_instances";
my $INFRASTRUCTURE	   = "$CCR/infrastructure";

# Set boolean constants
use constant TRUE          => 0;
use constant FALSE         => 1;

# Counting variable
my $REF_COUNT = 0;

# Global variables for command output caching
my @DIDADML_OUT = ();
my @SCCONFP_OUT = ();
my $DIDADML_CHECKSUM = "";
my $SCCONFP_CHECKSUM = "";
my $SCCONFP_TIMESTAMP = "";

##############################################################################
#
# Generic cluster commands -- all of these functions return a status
#
##############################################################################

#
# Run clinfo
#

sub clinfo($$) {
	my ($self, $cmd) = @_;
	my $output = `$CLINFO $cmd`;
	my $ret = ($? / 256);
	return ($ret, $output);
}

#
# Run scha_cluster_get
#

sub scha_cluster_get($$) {
	my ($self, $command) = @_;
	my @output = `$SCHA_CLUSTER_GET $command`;
	for (my $i=0; $i<=$#output; $i++){
		chomp $output[$i];
	}
	return @output;
}

#
# Run scrgadm
#

sub scrgadm {
	my ($self, $command) = @_;
	my @output = `$SCRGADM $command 2>&1`;
	my $ret = ($? / 256);
	for (my $i=0; $i<=$#output; $i++){
		chomp $output[$i];
	}
	return ($ret, @output);
}

#
# Run scswitch
#

sub scswitch {
	my ($self, $command) = @_;
	my (@output) = `$SCSWITCH $command`;
	my $ret = $?;
	for (my $i=0; $i<=$#output; $i++){
		chomp $output[$i];
	}
	return ($ret, @output);
}

#
# Run scha_resource_get
#

sub scha_resource_get {
	my ($self, $command) = @_;
	my @output = `$SCHA_RESOURCE_GET $command`;
	for (my $i=0; $i<=$#output; $i++) {
		chomp $output[$i];
	}
	return @output;
}

#
# Run scha_resourcegroup_get
#

sub scha_resourcegroup_get {
	my ($self, $command) = @_;
	my @output = `$SCHA_RESOURCEGROUP_GET $command`;
	for (my $i=0; $i<=$#output; $i++){
		chomp $output[$i];
	}	
	return @output;
}

#
# Run scconf
#

sub scconf {
	my ($self, $command) = @_;
	my (@output);

	open(SCCONF, "$SCCONF $command |");
	while (my $line = <SCCONF>) {
		chomp $line;
		push @output, $line;
	}
	close(SCCONF);
	return @output;
}

#
# Run scstat
#

sub scstat  {
	my ($self, $command) = @_;
	my (@output);

	open(SCSTAT, "$SCSTAT $command |");
	while (my $line = <SCSTAT>) {
		chomp $line;
		push @output, $line;
	}
	close(SCSTAT);
	return @output;
}

#
# Run didadm
#

sub didadm  {
	my ($self, $command) = @_;
	my (@output);

	open(DIDADM, "$DIDADM $command |");
	while (my $line = <DIDADM>) {
		chomp $line;
		push @output, $line;
	}
	close(DIDADM);
	return @output;
}

#
# Run pnmstat
#

sub pnmstat {
	my ($self, $command) = @_;
	my @output = `$PNMSTAT $command`;
	my $ret = $?;
	for (my $i=0; $i<=$#output; $i++){
		chomp $output[$i];
	}
	return ($ret, @output);
}

#
# Run scdpm
#

sub scdpm {
	my ($self, $command) = @_;
	my @output = `$SCDPM $command`;
	my $ret = $?;
	for (my $i=0; $i<=$#output; $i++){
		chomp $output[$i];
	}
	return ($ret, @output);
}


##############################################################################
#
# Generic Solaris commands
#
##############################################################################

sub date($) {
	my ($self) = @_;
	my $date = `$DATE`;
	chomp $date;
	return $date;
}

sub uname($$) {
	my ($self, $command) = @_;
	my $output = `$UNAME $command`;
	chomp $output;
	return $output;
}

sub ps($$) {
	my ($self, $command) = @_;
	my @output = `$PS $command`;
	for (my $i=0; $i<=$#output; $i++){
		chomp $output[$i];
	}	
	return @output;
}

sub grep($$$) {
	my ($self, $pattern, $file) = @_;
	my $cmd = "$GREP $pattern $file";
	my @output = `$cmd`;
	for (my $i=0; $i<=$#output; $i++){
		chomp $output[$i];
	}	
	return @output;
}

sub auths($$) {
	my ($self, $user) = @_;
	my ($output) = `$AUTHS $user`;
	chomp $output;
	my @auths = split /,/, $output;
	return @auths;
}

sub roles($$) {
	my ($self, $user) = @_;

	# To workaround the Solaris 8 roles bug, call /usr/bin/roles
	# with the same user twice, and take the second output
	# line. This is unnecessary on Solaris 9, but we'll use the
	# same code path for consistency.
	my @output = `$ROLES $user $user`;
	chomp $output[1];
	my @parts = split / : /, $output[1];
	my @roles = split /,/, $parts[1];
	if ($roles[0] =~ /No roles/) {
		@roles = ();
	}
	return @roles;
}

###############################################################################
#
# Cached output of cluster commands
#
###############################################################################

# help function to read the ccr_checksum from a given ccr table
sub get_ccrchecksum($$)
{
	my ($self, $ccrtable) = @_;
	open(CCRTABLE, $ccrtable);

	my $checksum="";
	while (my $line = <CCRTABLE>) {
		if ($line =~ /ccr_checksum/) {
			$checksum = $line;
			last;
		}
	}
	close(CCRTABLE);
	return $checksum;
}

#
# Update the array that stores the scconf -p output if is necessary
# (via checksum) and return both the array and true or false to
# indicate whether the array has just been updated
#

sub scconfp_cache($) {
	my ($self) = @_;

	# Read the infrastructure checksum
	my $checksum = $self->get_ccrchecksum($INFRASTRUCTURE);
	# Check for modifications to the CCR directory
	my $timestamp = -M $CCR;

	# Check whether we need to re-read from "scconf -p"
	if (($SCCONFP_CHECKSUM ne $checksum) ||
	    $SCCONFP_TIMESTAMP ne $timestamp ||
	    ($#SCCONFP_OUT == -1)) {
		$SCCONFP_CHECKSUM = $checksum;
		$SCCONFP_TIMESTAMP = $timestamp;
		@SCCONFP_OUT = $self->scconf("-p"); 
		return (TRUE, @SCCONFP_OUT);

	} else {

		# Fix! There's a small problem here... the first time
		# this function is called, there may already be
		# transport data, because of caching. However, the
		# junction and cable lists may not have been created
		# yet. Solution? Create the new lists every time?

		# We can fix the above problem by forcing the client
		# to re-read the lists whenever it retruns TRUE or the
		# lists are empty.  But it creates new problem --
		# Since the client doesn't keep a copy of checksum, if
		# the client makes some change in the configuration
		# and reloads the menu before reloading itself, the
		# checksum gets updated in the shared memory before
		# the client gets chance to re-read its lists. Later
		# on, if the client invokes this function, it will be
		# informed that the checksum has not been changed even
		# though it was acutally changed. As the result, the
		# client's lists may never get updated!  On the other
		# hand, re-reading the lists happens in memory, which
		# really doesn't affect the performance that much.  I
		# did some comparison and the difference is hard to
		# tell. It looks ok to ask the client to re-read the
		# new lists every time for now.
		return (TRUE, @SCCONFP_OUT); # FALSE
	}
}

sub get_scconfpout
{
	return @SCCONFP_OUT;
}

#
# Cache didadm -l output
#

sub didadml_cache($) {
	my ($self) = @_;

	# Read the infrastructure checksum
	my $checksum = $self->get_ccrchecksum($DID_INSTANCES);

	# Check whether we need to re-read from "didadm -l"
	if (($DIDADML_CHECKSUM ne $checksum) ||
	    ($#DIDADML_OUT == -1)) {
		$DIDADML_CHECKSUM = $checksum;
		@DIDADML_OUT = $self->didadm("-L"); 
		return (TRUE, @DIDADML_OUT);
	} else {
		return (FALSE, @DIDADML_OUT);
	}
}

# Return true
1;
