#!/usr/bin/perl
# 
# $Header: opatchauto/oms/OpatchUpgrade.pl /st_opatchauto_pt-em13.5mos/3 2024/08/27 23:14:53 smkumbar Exp $
#
# OpatchUpgrade.pl
# 
# Copyright (c) 2023, 2024, Oracle and/or its affiliates. 
#
#    NAME
#      OpatchUpgrade.pl - <one-line expansion of the name>
#
#    DESCRIPTION
#      <short description of component this file declares/defines>
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    smkumbar    09/05/23 - Opatch Upgrade Utility
#    smkumbar    09/05/23 - Creation
# 
use Cwd ;
use File::Spec; 
use File::Path;
use File::Copy;
use File::stat;
use File::Basename;
use Fcntl qw(:flock);
use JSON::PP;
use IO::Compress::Zip;
use File::Spec::Functions qw(splitpath);
use IO::File;
use IO::Uncompress::Unzip qw($UnzipError);
use File::Path qw(mkpath);
local $| =1;

sub unzipToDirectory {
	my ($zipFilename, $destinationDir) = @_;

	unless ($zipFilename and $destinationDir) {
		$! = "zipFilename and destinationDir must be supplied";
		return;
	}

	my $zipFile = IO::Uncompress::Unzip->new($zipFilename);

	unless ($zipFile) {
		$! = "Could not open $zipFilename as zipFile";
		return;
	}


	# Status should always be 1 for each entry in the zip...
	while (my $status = $zipFile->nextStream()) {

		my $zipEntryHeader = $zipFile->getHeaderInfo();
		my $pathAndFilename = $zipEntryHeader->{Name};

		# Returns from splitPath are volume, path, and filename...
		# We don't care about path (and it shouldn't be there, anyway)
			my (undef, $path, $filename) = splitpath($pathAndFilename);

		# Make sure we have a filename...skip directories...
		$filename or next;

		my $fullOutputDir = $destinationDir . "/" . $path;
		mkpath($fullOutputDir);

		my $unzippedFilename = $fullOutputDir . $filename;

		my $outputFh;
		unless (open($outputFh, ">", $unzippedFilename)) {
			$! = "Could not open $unzippedFilename for writing";
			return;
		}

		binmode($outputFh);

		my $buffer;
			while (($status = $zipFile->read($buffer))) {
			print $outputFh $buffer;
			}

			close($outputFh);

		#!print STDERR "Output $unzippedFilename " . -s $unzippedFilename, " bytes\n\n";
	}
		return 1;
}

sub compareVersions {
    my ($version1, $version2) = @_;

    $version1 =~ s/^\s+|\s+$//g;
    $version2 =~ s/^\s+|\s+$//g;
    my @levels1 = split(/\./, $version1);
    my @levels2 = split(/\./, $version2);

    my $length = scalar(@levels1) > scalar(@levels2) ? scalar(@levels1) : scalar(@levels2);
    for (my $i = 0; $i < $length; $i++) {
        my $v1 = $i < scalar(@levels1) ? int($levels1[$i]) : 0;
        my $v2 = $i < scalar(@levels2) ? int($levels2[$i]) : 0;
        my $compare = $v1 <=> $v2;
        if ($compare != 0) {
            return $compare;
        }
    }
    return 0;
}



$platform = $^O;
$platform=lc($platform);

my $oracleHome = @ARGV[0];
my $patchLoc = @ARGV[1];
my $analyzeFlag = @ARGV[2];

$datestring = localtime();
my $logTimeStamp = localtime();
$logTimeStamp =~ s/ /_/g;
$logTimeStamp =~ s/:/_/g;

my $opatchUpdateLog = File::Spec->catfile($oracleHome,"cfgtoollogs","omspatcher","SPB_PATCH","opatch_update_$logTimeStamp.log");
my $fh;
open ($fh, '>', $opatchUpdateLog);
print "\nOPatch update log :: $opatchUpdateLog\n";

my $javaExec = File::Spec->catfile($oracleHome,"oracle_common","jdk","bin","java");
my $destDir = File::Spec->catfile($oracleHome,".omspatcher_storage","opatch_unzip");
my $opatchLoc = File::Spec->catfile($patchLoc,"tools","opatch","generic");
my $opatchZipName;

#Reading the opatch in patch location to get the zip name
opendir ( DIR, $opatchLoc ) || die "Error in opening dir $opatchLoc\n";
while( ($filename = readdir(DIR))) {
    if ($filename eq '.' or $filename eq '..') {
        next;
    }
    $opatchZipName = $filename;
}
closedir(DIR);
print $fh "$datestring :: Opatch zip name is $opatchZipName\n";
my $zipFile = File::Spec->catfile($patchLoc,"tools","opatch","generic",$opatchZipName);

print $fh "$datestring :: Opatch zip loc $zipFile, Unzipped loc $destDir\n";
my $success = unzipToDirectory($zipFile, $destDir);
my $opatchNumber;

#Reading the unzippped opatch to get the patch number
opendir ( DIR, $destDir ) || die "Error in opening dir $destDir\n";
while( ($filename = readdir(DIR))) {
    if ($filename eq '.' or $filename eq '..') {
        next;
    }
    $opatchNumber = $filename;
}
closedir(DIR);
print $fh "$datestring :: Opatch number is $opatchNumber\n";

my $envOpatchPath = File::Spec->catfile($oracleHome,"OPatch","opatch");
print $fh "$datestring :: Opatch version command is :: $envOpatchPath version -oh $oracleHome -silent\n";
my $out = `$envOpatchPath version -oh $oracleHome -silent`;
my $envOpatchVersion = 0;

foreach my $data (split /[\r\n]+/, $out){
	if(index($data, "Version") != -1){
		my @fields = split(':',$data);
		$envOpatchVersion = $fields[1];
		$envOpatchVersion=~ s/^\s+|\s+$//g;
        print "\nThe Env Opatch version :: $envOpatchVersion\n";
		print $fh "$datestring :: The Env Opatch version is $envOpatchVersion\n";
        last;
	}
}
if($envOpatchVersion == 0){
	print $fh "$datestring :: There was an error while getting the Opatch version from oms home. Make sure OPatch folder exists in oms home.\n";	
	print "There was an error while getting the Opatch version from oms home. Make sure OPatch folder exists in oms home.\n";
	die "Opatch update error\n";
}

my $spbOpatchJar = File::Spec->catfile($oracleHome,".omspatcher_storage","opatch_unzip",$opatchNumber,"opatch_generic.jar");
my $opatchUpdateTemp = File::Spec->catfile($oracleHome,"opatch_update_temp");
print $fh "$datestring :: SPB opatch version command is :: $javaExec -jar $spbOpatchJar -version -silent\n";
my $out1 = `$javaExec -jar $spbOpatchJar -version -silent`;
my $spbOpatchVersion == 0;

foreach my $data (split /[\r\n]+/, $out1){
	if(index($data, "OPatch") != -1){
		my @fields = split(' ',$data);
		$spbOpatchVersion = $fields[1];
		$spbOpatchVersion=~ s/^\s+|\s+$//g;
        print "\nThe SPB Opatch version :: $spbOpatchVersion\n";
		print $fh "$datestring :: The SPB Opatch version is $spbOpatchVersion\n";
        last;
	}
}
if($spbOpatchVersion == 0){
	print $fh "$datestring :: Could not find the SPB OPatch Version\n";	
	print "Could not find the SPB OPatch Version\n";
	die "Opatch update error\n";
}

my $compareVal = compareVersions($spbOpatchVersion, $envOpatchVersion);
print $fh "$datestring :: The compare val is $compareVal\n";

#If the spb opatch version is less than or equal to env version, then exit
if($compareVal <= 0){
	print $fh "$datestring :: The Environment Opatch version is as per SPB Patch standards :: OPatch will not be updated\n";	
	print "\nThe Environment Opatch version is as per SPB Patch standards :: OPatch will not be updated\n";
}
else{
	print $fh "$datestring :: The Environment Opatch version is low as per OPatch included in Patch :: OPatch will be updated\n";
        print "\nThe Environment Opatch version is low as per OPatch included in Patch :: OPatch will be updated\n";

	if ($analyzeFlag){
                print $fh "$datestring :: Ananlyze flag is set to true\n";				
        }
        else{
		my $emInstanceFile = File::Spec->catfile($oracleHome,"sysman","config","emInstanceMapping.properties");
	
		#The above file confirms that the oms home is configured home and we can stop oms
		if (-e $emInstanceFile) {
			print $fh "$datestring :: emInstanceMapping.properties Exists..Stopping the oms..\n";
			my $emctlLoc;
			my $stopCmd;
			#print "Stopping the OMS...\n";
			if ($platform =~ /win/){
				$emctlLoc = File::Spec->catfile($oracleHome,"bin","emctl.bat");
				print $fh "$datestring :: $emctlLoc stop oms -all\n";
				$stopCmd = `$emctlLoc stop oms -all`;
			}
			else{
				$emctlLoc = File::Spec->catfile($oracleHome,"bin","emctl");
				print $fh "$datestring :: $emctlLoc stop oms\n";
				$stopCmd = `$emctlLoc stop oms`;
			}
			
			if ($? != 0) {
				print $fh "$datestring :: Could not stop the oms..";
				die "Could not stop the oms..$!\n";
			}
			#print "$stopCmd\n";
			print $fh "$datestring :: $stopCmd\n";		
		}

		print "Updating the Opatch now.......\n";
		print $fh "$datestring :: $javaExec -Djava.io.tmpdir=$opatchUpdateTemp -jar $spbOpatchJar -silent oracle_home=$oracleHome\n";
		my $out2 = `$javaExec -Djava.io.tmpdir=$opatchUpdateTemp -jar $spbOpatchJar -silent oracle_home=$oracleHome`;
		if ($? != 0) {
                	print $fh "$datestring :: Could not upgrade the opatch :: $out2";
                        die "Could not upgrade the opatch..$!\n";
                }
		print $fh "$datestring :: Command output :: $out2";
		print "Updated Opatch Successfully.......\n";
	}
}
close($fh);
