#! /usr/bin/perl -w

# Copyright 2008-2014 Cumulus Systems Incorporated.
# All Rights Reserved.

###############################################################################################
# What the program does?
#
# Given a list of hosts (names or IP addresses), this scripts runs on a central host and installs
# our unix probe software on specified target nodes with the help of doInstall.sh script present
# on specified target node.
#
# Following are the command line options:
#
# -k <file>                  : SSH Public Key file for password less login.
# -c <Host IP>               : Probe appliance IP.
# -n                         : Add SSH Options.
# -l <User name>             : User name of target machine.
# -p <Password>              : Password of target machine.
# -d <Destination Directory> : Destination install directory.
# -i                         : Installation mode.
# -r                         : Check prerequisites mode.
# -a                         : Start data collection mode.
# -s                         : Clean target machine's' installed directory mode.
# -g <site name>             : Site name.
# -q <probe id>              : Probe ID.
# -o <os type>               : OS Type.
# -z 			             : download data.
# -t 			             : test connection by ignoring empty install directory check.
# -v 			             : allow password less authentication.
################################################################################################

# Switch on warnings. It is used so that we can switch-off warning in Getopts() call.
$^W = 1;

# Flush output after every write. Unbuffered output is usually desired.
$| = 1;

# It is used for strict compilation.
use strict;

# Used for running commands on target machine.
use Net::OpenSSH;

# It is used to get base file name from the complete file name.
use File::Basename;

# These are used to get the current date and time.
use Time::HiRes qw(gettimeofday);
use POSIX "strftime";

# This is used to create folder recursively.
use File::Path qw(mkpath rmtree);

use File::Copy::Recursive qw(fcopy rcopy dircopy fmove rmove dirmove);

use IO::File;

use POSIX qw(tmpnam);

# Used to read script arguments.
use Getopt::Std;

# It is used to indicate the path where common module is placed.
use lib "/usr/local/megha/lib/common/perl";

# It is required for logging information.
use commonLogModule;

# ----------------------------------------------------------------------------------------------------------------------------------------------------
# GLOBAL Variables
# ----------------------------------------------------------------------------------------------------------------------------------------------------

# This variable stores the log type.
my $LOG_SOURCE = "installOnRemoteHost";

# This variable stores the probe type.
my $probeType = "Unix";

# This variable stores commonLogModule instance.
my $logObj = "";

# SSH object for remote communication.
my $ssh = undef;

# This variable is used to store the return status of function calls.
my $status = 0;

# Installation directory.
my $installDir = undef;

# SSH object for remote communication.
my $rootDIR = "probe";

# Temporary directory during installation.
my $installStaging = undef;

# Install script name on remote host.
my $ISCRIPT = undef;

# MARS Unix TAR file name.
my $TAR_NAME = $rootDIR.".tar";

# Host IP from where probes are deployed.
$::opt_c = undef;

# Add SSH Options.
$::opt_n = undef;

# User name.
$::opt_l = undef;

# Password.
$::opt_p = undef;

# Destination install directory.
$::opt_d = undef;

# Install mode.
$::opt_i = undef;

# Un-install mode.
$::opt_u = undef;

# Check prerequisites mode.
$::opt_r = undef;

# Start data collection mode.
$::opt_a = undef;

# Stop data collection mode.
$::opt_b = undef;

# Clean target machine's' installed directory mode.
$::opt_s = undef;

# Probe ID.
$::opt_q = undef;

# OS Type.
$::opt_o = undef;

# start data download
$::opt_z = undef;

# test connection after edit target details like username and password
$::opt_t = undef;

# public key for password less login.
$::opt_k = undef;

# allow password less authentication.
$::opt_v = undef;

# target host IP where we want to install unix probe.
my $targetIP = undef;

# ----------------------------------------------------------------------------------------------------------------------------------------------------
# Main function
# ----------------------------------------------------------------------------------------------------------------------------------------------------

# Initialize the environment.
$status = init();
if (0 != $status)
{
    $logObj->error("Call to init() failed.");
    goto EXIT;
}

# Configure all target machines.
$status = configureClient();
if (0 != $status)
{
    $logObj->error("Call to configureClient() failed.");
    goto EXIT;
}

# Call main functionality.
$status = callTarget();
if (0 != $status)
{
    $logObj->error("Call to callTarget() failed.");
    goto EXIT;
}

EXIT:

exit $status;

# This function is used to initialize the environment to run the Perl script correctly.
#
# @affected global variables :
#    $::opt_l
#    $installDir
#    $installStaging
#    $ISCRIPT
#    @clients
#
# @return :
#   0 if Success
#   1 if Error
#
sub init
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    # This variable is used to store the base folder path on the MARS probe.
    my $baseFolder = "/usr/local/megha";
    
    # This variable stores Log folder path.
    my $logFolder = $baseFolder."/logs";

    # If log folder does not exist then create it.
    if (! (-e $logFolder))
    {
        mkpath($logFolder);
    }

    # Get commonLogModule instance.
    $logObj = commonLogModule->getInstance($logFolder, $probeType, "", $LOG_SOURCE);
    
    # If first argument is not an option then it is an error.
    if (@ARGV && $ARGV[0] !~ "^-.+")
    {
        $logObj->error("First argument is not an option.");
        $retVal = 1;

        goto EXIT;
    }
    else
    {
        # Suppress annoying undef warnings.
        local($^W) = 0;

        # Get options.
        &getopts("nk:c:sl:iurtabvp:g:q:d:o:z:k:");
    }

    if (!defined($::opt_l) || !defined($::opt_d) ||  !defined($::opt_c) ||
       !(defined($::opt_i) || defined($::opt_u) || defined($::opt_o) || defined($::opt_z) || defined($::opt_p) || defined($::opt_r) || defined($::opt_t) || defined($::opt_v) || defined($::opt_k) || defined($::opt_a) || defined($::opt_b) || defined($::opt_s)))
    {
        print("Usage: perl installOnRemoteHost.pl <nk:tc:f:m:sl:iurabpv:j:y:g:q:d:>");

        print("-k <file>                  : SSH Public Key file for password less login.\n");
        print("-c <Host IP>               : Probe appliance IP.\n");
        print("-n                         : Add SSH Options.\n");
        print("-l <User name>             : User name of target machine.\n");
        print("-p <Password>              : Password of target machine.\n");
        print("-d <Destination Directory> : Destination install directory.\n");
        print("-i                         : Installation mode.\n");
        print("-r                         : Check prerequisites mode.\n");
        print("-a                         : Start data collection mode.\n");
        print("-s                         : Clean target machine's' installed directory mode.\n");
        print("-g                         : Site name.\n");
        print("-q                         : Probe ID.\n");
		print("-o                         : OS Type.\n");
		print("-z                         : Download data\n");
		print("-t                         : Test connection by ignoring empty install dir check.\n");
		print("-v                         : Allow password less authentication.\n");

        die $logObj->error("Missing command line options.");
    }

    # If central-host is 'eth0', get our own eth0 IP address.
    if ($::opt_c eq 'eth0')
    {
        $::opt_c = &getIp('eth0');
        $::opt_c =~ s/"//g;
        $::opt_c =~ s/'//g;
    }
    
    if (defined($::opt_q))
    {
        # Update the final log message information with populated Unix probe id.
        $logObj->setProbeId($::opt_q);
    }
    
    # Remove " or ' from user name.
    $::opt_l  =~ s/"//g;
    $::opt_l  =~ s/'//g;

    $installDir = $::opt_d;

    $installStaging = $::opt_d."/".$rootDIR."/installer";

    $ISCRIPT = "doInstall.sh";

    # Set IP of remote host.
    $targetIP = $ARGV[0];

EXIT:

    return $retVal;
}

# This function does configuration of target machine like copying key on target machine.
#
# @affected global variables:
#   None
#
# @param:
#   None
#
# @return:
#   0 if Success
#   1 if Error
sub configureClient
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    # Do operation for target host.
    # Get an SSH object to do operation on target machine.

	if ($::opt_v) {
		$ssh = Net::OpenSSH->new($targetIP, (user => $::opt_l, master_opts => [-o => "StrictHostKeyChecking=yes"]));
	} else {
		$ssh = Net::OpenSSH->new($targetIP, (user => $::opt_l, passwd => $::opt_p, master_opts => [-o => "StrictHostKeyChecking=no"]));
	}
    
    if ($ssh->error)
    {
        # If error contains "bad password" that means password or user name is invalid.
        if (index($ssh->error, "bad password") != -1) 
        {
			print STDERR "Unable to connect.\n";
        }
		
        die $logObj->error("Error connecting to [$targetIP], error: [".$ssh->error."].");
		
	}

    # If -k was specified then copy public key on target machine.
    if (! $::opt_k)
    {
		$logObj->info("Not setting SSH keys on target machine as -k option not provided.");
        goto EXIT;
    }

    if (! -r $::opt_k)
    {
		die $logObj->error("Can't read ssh public key file [".$::opt_k."], error: [$!].");
    }

    $logObj->info("Getting user home directory for target [$targetIP].");

    # Get current home directory.
    my $homeDir = $ssh->capture({}, 'echo $HOME');

    if ($ssh->error)
    {
		die $logObj->error("Failed connect to [$targetIP], error: [".$ssh->error."].");
    }

    chomp($homeDir);

    # Set home directory if not already set.
    if (!defined $homeDir || $homeDir eq "")
    {
		if ($::opt_l eq "root")
        {
            $logObj->info("Setting home directory as /root because no home directory specified.");
            $homeDir = "/root";
        }
        else
        {
            $logObj->info("Setting home directory to: [/home/".$::opt_l."].");
            $homeDir = "/home/".$::opt_l;
        }
    }

    # Create SSH folder on target machine.
    $logObj->info("Creating SSH folder on target: [$targetIP] at location: [$homeDir].");
	
    my $cmd = "mkdir -p $homeDir/.ssh";

    $logObj->info("Command to execute for target [$targetIP] - [$cmd].");
    if (! defined($ssh->system({}, $cmd)))
    {
		die $logObj->error("Failed to create .ssh folder on target machine [$targetIP], error: [".$ssh->error."]");
    }
	
	$cmd = "chmod og-w $homeDir/.ssh";

    $logObj->info("Command to execute for target [$targetIP] - [$cmd].");
    if (! defined($ssh->system({}, $cmd)))
    {
		die $logObj->error("Failed to change ownership of .ssh folder on target machine [$targetIP], error: [".$ssh->error."]");
    }

    # Create key file and get the pipe handle.
    my ($rin, $pid) = $ssh->pipe_in("cat >>$homeDir/.ssh/authorized_keys") or $logObj->error("pipe_in method failed for target [$targetIP], error: [".$ssh->error."]");

    # Copy data from local file to remote file.
    print $rin `cat $::opt_k`;

    close($rin);

EXIT:

    return $retVal;
}

# This function call core functionality depending upon command line options.
#
# @param:
#     None
#
# @return :
#     0 if Success
#     1 if Error
sub callTarget
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    # Install operation.
    if ($::opt_i)
    {
        $retVal = install();
        if (0 != $status)
        {
            $logObj->error("Call to install() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }
    # Check pre-requisites.
    elsif ($::opt_r)
    {
        $retVal = checkPrerequisites();
        if (0 != $status)
        {
            $logObj->error("Call to checkPrerequisites() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }
    # Start data collection.
    elsif ($::opt_a)
    {
        $retVal = start();
        if (0 != $status)
        {
            $logObj->error("Call to start() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }
    
    # Clean installation directory on target machine.
    elsif ($::opt_s)
    {
        $retVal = cleanUpTarget();
        if (0 != $status)
        {
            $logObj->error("Call to cleanUpTarget() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }

	# Download data from target machine.
    elsif ($::opt_z)
    {
        $status = downloadData();
        if (0 != $status)
        {
            $logObj->error("Call to downloadData() failed.");
            $retVal = 1;

            goto EXIT;
        }
    }
	
EXIT:

    return $retVal;
}

# This function calls installer scripts method to start data collection on target machine.
#
# @param:
#    None
#
# @return:
#    0 if Success
#    1 if Error
sub start
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    my $cmd = "$installStaging/$ISCRIPT -a $installDir";

    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute [$cmd], error: [$err].");
		
		$status = downloadLogs();
        
		if (0 != $status)
		{
			$logObj->error("Call to downloadLogs() failed.");
		}  
		
        $retVal = 1;
    }

    return $retVal;
}

# This function will download data from target host and delete downloaded files from target host
#
# @param:
#    None
#
# @return:
#    0 if Success
#    1 if Error
sub downloadData
{	
	my $retVal = 0;
	
	my $downloadFrom = $::opt_d."/data";
	my $downloadTo = $::opt_z;

	if (! $ssh->scp_get({recursive => 1}, $downloadFrom, $downloadTo))
    {
        die $logObj->error("Failed while downloading data from  remote host, error: [".$ssh->error."].");
		return 1;
    }
	
	#delete downloaded file from remote machine
	my $cmd = "rm -rf ".$downloadFrom."/*";

    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute [$cmd], error: [$err].");
        $retVal = 1;
    }
	
	$status = downloadLogs();
        
	if (0 != $status)
    {
		$logObj->error("Call to downloadLogs() failed.");
        $retVal = 1;
    }
	
	return $retVal;
}

# This function calls installer scripts method to clean installation on target machine after deletion of probe.
#
# @param:
#    None.
#
# @return :
#    0 if Success
#    1 if Error
sub cleanUpTarget
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

	$logObj->info("Uninstalling probe from target host");
	
    my $cmd = "rm -rf $installDir/*";

    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to uninstall probe from  target host due to failure in execute [$cmd], error: [$err].");
        $retVal = 1;
    }

    return $retVal;
}

# This function will download logs from target host and delete downloaded logs file from target host
#
# @param:
#    None
#
# @return:
#    0 if Success
#    1 if Error
sub downloadLogs
{	
	my $retVal = 0;
	
	#download logs of probe running on target machine
	my $downloadFrom = $::opt_d."/logs";
	my $downloadTo = $::opt_z;

	if (! $ssh->scp_get({recursive => 1}, $downloadFrom, $downloadTo))
    {
        die $logObj->error("Failed while downloading logs from  remote host, error: [".$ssh->error."].");
		return 1;
    }
	
	#delete downloaded log file from remote machine
	my $cmd = "rm -rf ".$downloadFrom."/*";

    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute [$cmd], error: [$err].");
        $retVal = 1;
    }
	
	return $retVal;
}

# This function copy installation script on target machine.
#
# @affected global variables:
#    None
#
# @param:
#    $_[0]: [IN} SSH object.
#
# @return:
#   0 if Success
#   1 if Error
#
sub install
{
    # This variable is used to store the return value of function calls.
    my $retVal = 0;

    my $sourceHome = "/usr/local/megha/lib/unix";

    my $sourceDir = $sourceHome."/".$rootDIR;

    # Modify runtime.cf first.
    my $configFile = $sourceDir."/conf/runtime.cf";

    # If Probe id is not provided then error.
    if (! defined($::opt_q))
    {
        die $logObj->error("Probe Id not provided.");
    }

	#Open existing config file and a new config file.
    open(CF, '<', $configFile) || die $logObj->error("Can not read [$configFile], error: [$!].");
    open(CFNEW, '>', "$configFile.new") || die $logObj->error("Can not write to [$configFile.new], error: [$!].");

    $logObj->info("Updating runtime.cf.new with user provided values.");

    while (<CF>)
    {
        # If line is DESTHOST="%DESTHOST%" then replace %DESTHOST% with $::opt_c (probe appliance).
        if (/^\s*DESTHOST\s*=\s*(\S+)/)
        {
            s/%DESTHOST%/$::opt_c/g;
        }
        # If line is PROBEID=%PROBEID% then replace %PROBEID% with $::opt_q (probe id).
        elsif (/^\s*PROBEID\s*=\s*(\S+)/)
        {
            s/%PROBEID%/$::opt_q/g;
        }
		# If line is TARGETHOST="%TARGETHOST%" then replace %TARGETHOST% with $host (target host).
        elsif (/^\s*TARGETHOST\s*=\s*(\S+)/)
        {
            s/%TARGETHOST%/$targetIP/g;
        }
		# If line is OSTYPE="%OSTYPE%" then replace %OSTYPE% with os type installed on target host (target host).
        elsif (/^\s*OSTYPE\s*=\s*(\S+)/)
        {
            s/%OSTYPE%/$::opt_o/g;
        }
		
        else
        {
            # Skip the line.
        }
		
        # Printing modified line in new runtime.
        print CFNEW $_;
    }

    if (! close(CFNEW))
    {
        die $logObj->error("Failed to close [$configFile.new], error: [$!].");
    }

    $logObj->info("File [runtime.cf.new] updated successfully.");


    # Create TAR file name for local and remote machine.

    my $cmd = "";

    my $tarLocal = $sourceHome."/".$TAR_NAME;
    my $tarRemote = $installDir."/".$TAR_NAME;

    # Delete existing tar file on local machine.
    if (-e $tarLocal)
    {
        unlink($tarLocal);
    }
	
	#create temporary directory to copy files from probe
	my $tempDir = $sourceHome."/temp";
	
	if(! -e $tempDir) {
		mkdir $tempDir or die $logObj->error("Unable to create [$tempDir]");
    }
	
	#copy files from probe to temporary directory
	my $fromDir = $sourceHome."/".$rootDIR."/";
		
    rcopy($fromDir, $tempDir) 
        or die $logObj->error("Could not perform rcopy from [$fromDir] to [$tempDir]");
	
	#move library files directory for input os type from tempDir/lib to tempDir
	my $moveFrom = $tempDir."/lib/".$::opt_o;
	my $moveTo = $tempDir."/".$::opt_o;
	
	rmove($moveFrom,$moveTo)
		or die $logObj->error("Could not perform rmove from [$moveFrom] to [$moveTo]");
		
	#delete all files inside tempDir/lib
	my $dirToClean = $tempDir."/lib/";
	
	rmtree($dirToClean)
		or die $logObj->error("Unable to delete files from [$dirToClean]");
	
	#copy files from tempDir/osType/ to tempDir/lib/
	my $copyFrom = $tempDir."/".$::opt_o."/";
	my $copyTo = $tempDir."/lib/";
	
	rcopy($copyFrom, $copyTo) 
        or die $logObj->error("Could not perform rcopy from [$copyFrom] to [$copyTo]");
	
	#delete tempDir/osType directory
	my $dirToDelete = $tempDir."/".$::opt_o;
	
	rmtree($dirToDelete)
		or die $logObj->error("Unable to delete directory [$dirToDelete]");
	
	chdir($sourceHome);
	
	#move tempDir to probe/
	rmove($tempDir,$rootDIR."/".$rootDIR)
		or die $logObj->error("Could not perform rmove from [$tempDir] to [$rootDIR."/".$rootDIR]");
	
	
	chdir($sourceHome."/".$rootDIR);
	
    # Create installation tar.
    $cmd = "tar cf $tarLocal $rootDIR/";

    $logObj->info("Executing command [$cmd].");

    my $exitCode = system($cmd);
    if (0 != $exitCode)
    {
        $logObj->error("Can not create installation tar.");
        $retVal = 1;

        goto EXIT;
    }
	
	#delete temporary directory
	rmtree($rootDIR)
		or die $logObj->error("Unable to delete directory [$tempDir]");
	
	
	chdir($sourceHome);
	
    # Cross check whether we are able to see local tar file.
    if (! -e $tarLocal)
    {
        $logObj->error("Can not find ['$tarLocal'], error: [$!].");
        $retVal = 1;

        goto EXIT;
    }

    # Copy tar file on remote machine.
    # NOTE: scp may prompt user for password or key.
    if (! $ssh->scp_put({}, $tarLocal, $installDir))
    {
        die $logObj->error("Failed while uploading software on remote host, error: [".$ssh->error."].");
    }

    # Un-tar tar file on remote machine.
    $cmd = "cd $installDir; tar xf $tarRemote";

    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->system({}, $cmd);

    if ($err)
    {
		$logObj->error("Can not extract [$tarRemote] on remote machine, error: [$err], STDOUT: [$out].");
		
		$status = cleanUpTarget();
		
		if (0 != $status)
		{
			$logObj->error("Call to cleanUpTarget() failed.");
			$logObj->error("Can not revert back changes after unsuccessfull install");
		}

        $retVal = 1;

        goto EXIT;
    }

    # Update permission of installation script.
    $cmd = "chmod a+x $installStaging/$ISCRIPT";

    $logObj->info("Executing command [$cmd].");

    ($out,$err) = $ssh->system({}, $cmd);

    if ($err)
    {
		$logObj->error("Can not change permission of install script on remote machine, error: [$err]. STDOUT: [$out].");
	
		$status = cleanUpTarget();
		
		if (0 != $status)
		{
			$logObj->error("Call to cleanUpTarget() failed.");
			$logObj->error("Can not revert back changes after unsuccessfull install");
		}
		
        $retVal = 1;

        goto EXIT;
    }

    # Delete tar file from remote machine.
    $cmd = "rm -f $tarRemote";
	
    $logObj->info("Executing command [$cmd].");

    ($out,$err) = $ssh->system({}, $cmd);

    if ($err)
    {
        $logObj->error("Can not remove [$tarRemote], error: [$err], STDOUT [$out].");
		
		$status = cleanUpTarget();
		
		if (0 != $status)
		{
			$logObj->error("Call to cleanUpTarget() failed.");
			$logObj->error("Can not revert back changes after unsuccessfull install");
		}

        $retVal = 1;

        goto EXIT;
    }
	
	# Delete tar file from local machine.
	$cmd = "rm -f $tarLocal";

    $logObj->info("Executing command [$cmd].");
    $exitCode = system($cmd);
	
    if (0 != $exitCode)
    {
        $logObj->error("Could not delete local tar file [$tarLocal].");
		
		$status = cleanUpTarget();
		
		if (0 != $status)
		{
			$logObj->error("Call to cleanUpTarget() failed.");
			$logObj->error("Can not revert back changes after unsuccessfull install");
		}
		
        $retVal = 1;

        goto EXIT;
    }

    # Command to install software on remote machine.
    $cmd = "$installStaging/$ISCRIPT -i $installDir";

    $logObj->info("Executing command [$cmd].");

    # Install software on remote machine.
    ($out, $err) = $ssh->capture2({}, "$cmd");

    # If there is an error while uploading software then uninstall.
    if ($err)
    {
        $logObj->error("Uninstalling setup as found error installing Unix probe on remote machine, error: [$err], STDOUT: [$out].");
		
		$status = downloadLogs();
        
		if (0 != $status)
		{
			$logObj->error("Call to downloadLogs() failed.");
		}  		

		$status = cleanUpTarget();
		
		if (0 != $status)
		{
			$logObj->error("Call to cleanUpTarget() failed.");
			$logObj->error("Can not revert back changes after unsuccessfull install");
		}
		
		$retVal = 1;
			
		goto EXIT;
    }

EXIT:

    return $retVal;
}

# This function check following on target machine.
# 1). Is installDir is present or not.
# 2). Is installation directory is empty or not. If installDir is not empty then it will quit.
# 3). Is there sufficient permission for that directory available or not.
#
# @param:
#    None.
#
# @return:
#    0 if Success
#    1 if Error
sub checkPrerequisites
{
    # Store return value of this function.
    my $retVal = 0;

	#Check target host OS type match with user selected OS type or not
	my $cmd = "uname";
    $logObj->info("Executing command [$cmd].");

    my ($out, $err) = $ssh->capture2({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute command [$cmd]. Error: [$err].");
        print STDERR "Unable to get target host OS Type.\n";

        $retVal = 1;
        goto EXIT;
    }

    $out = trim($out);
	
	if ($out ne $::opt_o)
	{
		#specific condition for Solaris
		if (($out eq "SunOS") && ($::opt_o eq "Solaris"))
		{
			#nothing to do
		} 		
		else
		{
			$logObj->error("Target host OS type [$out] does not matched with user selected OS tpye[$::opt_o].");
			print STDERR "Incorrect OS Type found.\n";

			$retVal = 1;
			goto EXIT;
		}
	}
	
	$logObj->info("Target host OS type [$out] matched with user selected OS type [$::opt_o].");
	
    # Check whether install directory is exist or not.
    $cmd = "ls $installDir".' | wc -l';
    $logObj->info("Executing command [$cmd].");

    ($out, $err) = $ssh->capture2({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute command [$cmd]. Error: [$err].");
        print STDERR "Listing failed.\n";

        $retVal = 1;
        goto EXIT;
    }

    $out = trim($out);

	if (! defined($::opt_t))
    {
        # If install directory is not empty then return.
		if ($out ne "0")
		{
			$logObj->error("Installation directory [$installDir] is not empty.");
			print STDERR "Directory is not empty.\n";

			$retVal = 1;
			goto EXIT;
		}
    }

    # Check installation directory permission.
    $cmd = "ls -ld $installDir".' | awk \'{print $1}\'';
    $logObj->info("Executing command [$cmd].");

    ($out, $err) = $ssh->capture2({}, $cmd);

    if ($err)
    {
        $logObj->error("Failed to execute command [$cmd], error: [$err].");
        print STDERR "$err\n";

        $retVal = 1;
        goto EXIT;
    }

    $out = trim($out);

    # If folder permission doesn't start with drwx then in-sufficient permission.
    if ($out !~ m!^drwx!)
    {
        $logObj->error("Installation directory [$installDir] does not have sufficient permissions.");
        print STDERR "Permissions issue.\n";

        $retVal = 1;
        goto EXIT;
    }

EXIT:

    return $retVal;
}

# Remove white space character from beginning and end of a string.
#
# @param:
#    $_[0]: [IN] Input string.
#
# @return:
#    String without white space character at beginning and end.
sub trim
{
    my $str = shift;

    $str =~ s!^\s+!!g;
    $str =~ s!\s+$!!g;

    return $str;
}

# Get IP address of specified interface. If not found then return any IP address which is not loopback IP.
#
# @param:
#    $_[0]: [IN] Interface name.
#
# @return:
#    IP address in success case. "no-ip-found-for-eth0" string in failure case.
sub getIp
{
    my $interfaceName = @_;

    # Try to get IP address associated with a network interface.
    my $ip = `/sbin/ifconfig $interfaceName | sed 1d`;

    # If we get IP address for specified interface then return.
    if ($ip =~ /addr:(\d+\.\d+\.\d+\.\d+)/)
    {
        return $1;
    }
    # If we are not able to find IP address for given interface then find any IP address which is not loopback IP.
    else
    {
        $ip = getNonLoopBackIp();
        chomp($ip);

        return $ip;
    }

    return 'no-ip-found-for-eth0';
}

# This function is used to get the Loopback IP address
#
# @param:
#     None
#
# @return:
#     Loopback IP address list.
sub getLoopBackIps
{
    # Get ifconfig output.
    my $ifconfig = `/sbin/ifconfig -a`;

    # Used to decide the line which contain IP address.
    my $readNextLine = 0;

    # This list is used to store complete line having IP address.
    my @outArray = ();

    # Output list which contain Loopback IP address.
    my @outIpList = ();

    # Split output for each line.
    for (split(/\n/, $ifconfig))
    {
        # If current line is empty then go to next line.
        if (/^$/)
        {
            next;
        }

        # If current line doesn't have "Loopback" string or exact "inet" string (no "inet6") then go to next line.
        if ($_ !~ /Loopback|\binet\b/)
        {
            next;
        }

        # This line has IP address.
        if ($readNextLine == 1)
        {
            push(@outArray, $_);
        }

        # If current line has "Loopback" string then means that next line will have IP address.
        if (~/Loopback$/)
        {
            $readNextLine = 1;
        }
        # If current line has no "Loopback" string then means that we already found IP address.
        else
        {
            $readNextLine = 0;
        }
    }

    # Use only those lines where "inet" is at the start of line.
    my @ipArray = grep {/^\s*inet/} @outArray;

    foreach (@ipArray)
    {
        # Find IP address and push in output list.
        if (m/addr:(.*)\s+M/)
        {
            push(@outIpList, $1);
        }
    }

    return @outIpList;
}

# This function is used to return an IP address which is not a loopback IP.
#
# @param
#    None
#
# @return
#    IP address which is not loopback IP.
sub getNonLoopBackIp
{
    # Get IP address list as a single string.
    my $ifconf = `/sbin/ifconfig | sed -n '/.*inet .*dr:/{;s/.*dr://;s/ .*//;p;}'`;

    # Get IP addresses in a list.
    my @ips = split(/\n/, $ifconf);

    # Get the list of loopback IPs.
    my @loopBackIps = getLoopBackIps();

    # Loop for all IPs.
    for (my $i = 0; $i <= $#ips; $i++)
    {
        # Loop for all loopback IPs.
        foreach my $loopBackaddr (@loopBackIps)
        {
            # If we found any IP which is not a loopback IP then return that IP.
            if ($ips[$i] ne $loopBackaddr)
            {
                return $ips[$i];
            }
        }
    }

    return undef;
}