# $Header: ecmPatchEMD.pl 31-jan-2005.05:29:53 shgangul Exp $
#
# Copyright (c) 2001, 2005, Oracle. All rights reserved.  
#
#    DESCRIPTION
#      ECM script to patch Oracle Enterprise Manager with One-off/Patchset patches
#
#    USAGE
#      ecmPatchEMD.pl <action> <depot_path> <patch_type>
#         [<patch_id> (<patch_size> | <patch_file> <patch_index> <patch_count>)]
#
#    NOTES
#      <other useful comments,qualifications,etc>
#
#    MODIFIED     (MM/DD/YY)
#       shgangul   01/31/05 - bug 3845819: MAC OS port 
#       mbhoopat   03/10/04 - linux port 
#       nsharma    01/19/04 - Correct path of find, id, nawk and unzip on linux
#       xxu        06/25/02 - remove /usr/local/bin/perl
#       mgoodric   12/21/01 - Add script steps to patch job
#       mgoodric   12/07/01 - Add echodo function to display system cmd
#       mgoodric   12/06/01 - Fix PatchDatabase job for Patchset
#       mgoodric   12/04/01 - Fixes for PatchDatabase job
#       mgoodric   11/29/01 - Add PatchEMD job type
#
#

# --- Set up necessary variables for proper running of this environment ---
use strict;
use FileHandle;
use File::Basename;
use File::Path;

my $scriptDir = dirname($0);
require "$scriptDir/ecmCommon.pl";

# ------ Initialize global variables -------------------------------------
my $action          = '';  # [0] = checkTarget | installPatch | showResults
my $depot_path      = '';  # [1] = ecmdepot/patches
my $patch_type      = '';  # [2] = Patch | Patchset
my $patch_id        = '';  # [3] = 1234567
my $patch_size      = '0'; # [4] = 463162 | 142144398
my $patch_file      = '';  # [4] = p1234567.zip
my $patch_index     = '1'; # [5] = 1..n
my $patch_count     = '1'; # [6] = n
my $db_name         = '';
my $patch_lock      = 'PATCH.lck';
my $EMDROOT         = '';
my $JAVA_HOME       = '';
my $OUILOC          = '';
my $debug           = 1; # true if debugging

# --------------------- OS platform-specific ----------------------------
my $chmod           = '/bin/chmod';
my $df              = '/bin/df';
my $echo            = '/bin/echo';
my $egrep           = '/bin/egrep';
my $emctl           = 'emctl';
my $find            = '/bin/find';
my $id              = '/bin/id';
my $jar             = 'jar';
my $ls              = '/bin/ls';
my $nawk            = '/bin/nawk';
my $null            = '/dev/null';
my $ps              = '/bin/ps';
my $rm              = '/bin/rm -f';
my $tar             = '/bin/tar';
my $unzip           = '/bin/unzip';
my $tempfile        = "/tmp/sqlplus_$$.sql";
my $DEF_PATH        = '/bin:/sbin:/usr/bin:/usr/sbin:/etc:/usr/etc:/usr/ccs/bin:/usr/ucb';
my $DEF_LD_LIBRARY_PATH = '/usr/lib';
my $DEF_JAVA_HOME   = '/usr/local/packages/jdk1.3.1';

if ($^O eq 'linux')
{
   $find  = '/usr/bin/find';
   $id    = '/usr/bin/id';
   $nawk  = '/bin/awk';
   $unzip = '/usr/bin/unzip';
}
elsif ($^O eq 'darwin')
{
   $egrep = '/usr/bin/egrep';
   $find  = '/usr/bin/find';
   $id    = '/usr/bin/id';
   $nawk  = '/usr/bin/awk';
   $tar  = '/usr/bin/tar';
   $unzip = '/usr/bin/unzip';
}

# --------------------- Subroutines -------------------------------------
#
# err(<error_number>,<error_text>)
#
# Display a formatted error message
#
sub err($$)
{
  my ($error_number,$error_text) = @_;

  printf "---------- Error Message ----------\n";
  printf "Error: %03d\n",$error_number;
  printf "%s\n",$error_text;
  printf "----------- End Message -----------\n";
}

#
# warn(<warning_number>,<warning_text>)
#
# Display a formatted warning message
#
sub warn($$)
{
  my ($warning_number,$warning_text) = @_;

  printf "--------- Warning Message ---------\n";
  printf "Warning: %03d\n",$warning_number;
  printf "%s\n",$warning_text;
  printf "----------- End Message -----------\n";
}

#
# echodo(<cmd>)
#
# Display the command and execute it
# Return status
#
sub echodo($)
{
  my ($cmd) = @_;
  printf "\n%s\n", $cmd;
  return system($cmd);
}

#
# createLock(<lock_file>,<lock_id>)
#
# Place a marker in the depot to indicate patching state
#
sub createLock($$)
{
  my ($lock_file,$lock_id) = @_;
  my $lock_path = dirname($lock_file);

  mkpath($lock_path,0,0775) if (! -d $lock_path);
  open(OUTPUT,'>',$lock_file);
  print OUTPUT "$lock_id\n";
  close(OUTPUT);
}

#
# isLocked(<lock_file>)
#
# Test a marker in the depot to indicate patching state
#
sub isLocked($)
{
  my ($lock_file) = @_;

  return (-f $lock_file);
}

#
# clearLocks()
#
# Reset all patching state markers
#
sub clearLocks
{
  unlink($patch_lock);
}

#
# checkTarget()
#
# - Verify EMDROOT
# - Create depot directory to store patch
# - Check disk space for available free space
#
sub checkTarget
{
  my $patch_path = "$depot_path/$patch_id";
  my $undo_patch = '';
  my $tempfile = "_test_$$";
  my $freeKB = 0;
  my $needKB = 0;
  my $status = 0;

  $! = 0;
  chdir($EMDROOT)
    or abort($action,$? >>8,"Could not cd to >$EMDROOT<: $!");

  if (! -d $patch_path)
  {
    mkpath($patch_path,0,0775) > 0
      or abort($action,$? >>8,"Could not mkpath >$patch_path<: $!");
  }

  chdir($patch_path)
    or abort($action,$? >>8,"Could not cd to >$patch_path<: $!");

  if ($patch_type eq 'Patch')
  {
    chomp($undo_patch = `$find $patch_path -name 'undo_pre[0-9]*.sh'`);
    $undo_patch eq ''
      or abort($action,1,"$patch_id already applied");
  }

  open(OUTPUT,'>',$tempfile)
    or abort($action,$? >>8,"Could not open file $tempfile to write: $!");

  close(OUTPUT);
  unlink($tempfile);

  chomp($freeKB = `$df -b $patch_path | $nawk '/dev/{print \$2}'`);
  $needKB = int(($needKB + (3 * $patch_size)) / 1024);
  $freeKB > $needKB
    or abort($action,7,"Not enough free disk space: ${freeKB}KB < ${needKB}KB");
}

#
# installPatch()
#
# - Unjar the patch
# - Untar the tar (if Patchset)
# - create the patch script and run in background
#
sub installPatch
{
  my $patch_path = "$depot_path/$patch_id";
  my $orapatch = '';
  my $patch_args = '';
  my $patch_log = "$patch_path/../EMD_${patch_id}.log";
  my $patch_sh = "$patch_path/../EMD_${patch_id}.sh";
  my $patch_home = $patch_path;
  my $patchset_rsp = '';
  my $products_jar = '';
  my $patch_tar = '';
  my $failed = 'skipped';
  my $status = 1;

  $! = 0;
  chdir($patch_path)
    or abort($action,$? >>8,"Could not cd to >$patch_path<: $!");

  unlink($patch_lock) if ($patch_index == 1);

  if (!isLocked($patch_lock))
  {
    createLock($patch_lock,$patch_id) if ($patch_count > 1);

    printf "Unpacking patch file $patch_file...\n";
    echodo("$jar -xf $patch_file") == 0
      or abort($action,$? >>8,"Could not unjar $patch_file");

    open(OUTPUT,'>',$patch_sh);
    print OUTPUT "#!/bin/sh -x\n";

    if ($patch_type eq 'Patch')
    {
      chomp($orapatch = `$find $patch_path -name 'orapatch'`);
      if ($orapatch eq '')
      {
        chomp($orapatch = `$find $patch_path -name 'patch.sh'`);
      }

      $orapatch ne ''
        or abort($action,3,"Could not find patch.sh");

      $patch_home = dirname($orapatch);
      $patch_args = "install $EMDROOT" if (basename($orapatch) eq 'orapatch');

      if ($patch_args eq '')
      {
        print OUTPUT "cd $patch_home || exit 1\n";
        print OUTPUT "$chmod 777 $orapatch; $echo Y | $orapatch\n";
      }
      else
      {
        print OUTPUT "cd $patch_home || exit 1\n";
        print OUTPUT "$chmod 777 $orapatch; $orapatch $patch_args </$null\n";
      }
    }
    else
    {
      $ENV{'DISPLAY'} = ':0.0' if (! defined $ENV{'DISPLAY'});

      chomp($patch_tar = `$find $patch_path -name '*.tar'`);
      $patch_tar ne ''
        or abort($action,3,"Could not find tar file");

      printf "\nUnpacking tar file $patch_tar...\n";
      echodo("$tar xf $patch_tar") == 0
        or abort($action,$? >>8,"Could not untar $patch_tar");

      chomp($patchset_rsp = `$find $patch_path/response -name '*.rsp'`);
      $patchset_rsp ne ''
        or abort($action,3,"Could not find response file");

      chomp($products_jar = `$find $patch_path/stage -name 'products.jar'`);
      $products_jar ne ''
        or abort($action,3,"Could not find products.jar");

      $patch_args = "-silent -responseFile $patchset_rsp session_FROM_LOCATION=\"$products_jar\" session_ORACLE_HOME=\"$EMDROOT\"";
      $orapatch = "$OUILOC/oui/install/runInstaller.sh";

      print OUTPUT "DISPLAY=:0.0; export DISPLAY\n";
      print OUTPUT "cd $EMDROOT\n";
      print OUTPUT "$orapatch $patch_args </$null\n";
    }

    print OUTPUT "status=$?\n";
    print OUTPUT "$rm $patch_sh\n";
    print OUTPUT "exit $status\n";
    close(OUTPUT);

    if (! -x $patch_sh)
    {
      chmod(0777,$patch_sh)
        or abort($action,$? >>8,"Could not chmod >$patch_sh<: $!");
    }

    chdir($EMDROOT)
      or abort($action,$? >>8,"Could not cd to >$EMDROOT<: $!");

    $failed = 'failed';
    printf "\nApplying Patch $patch_id...\n";
    $status = echodo("(sleep 1; $emctl stop; $patch_sh; $emctl start) <$null >$patch_log 2>&1 &");
    unlink($patch_lock) if ($patch_count > 1 && $status == 0);
  }

  if ($patch_index == $patch_count)
  {
    unlink($patch_lock);
  }

  $status == 0
    or abort($action,$status >>8,"Patch $failed");

  if (!$debug)
  {
    unlink($patch_file);
    unlink($patch_tar) if ($patch_tar ne '');
  }
}

#
# showResults()
#
# - Show the log of the actual EMD patch
#
sub showResults
{
  my $patch_path = "$depot_path/$patch_id";
  my $patch_log = "$patch_path/../EMD_$patch_id.log";
  my $status = 1;

  $! = 0;
  chdir($EMDROOT)
    or abort($action,$? >>8,"Could not cd to >$EMDROOT<: $!");

  printf "\nPatch results for $patch_id...\n";
  if (-f $patch_log)
  {
    open(INPUT,'<',$patch_log);
    while(<INPUT>)
    {
      print $_;
    }
    close(INPUT);
  }

  if (!$debug)
  {
    unlink($patch_log);
    rmtree($patch_path,0,1);
  }
}

#
# emdenv()
#
# Set EMD environment
#
sub emdenv
{
  $EMDROOT = $ENV{'EMDROOT'};
  if (! defined $EMDROOT || $EMDROOT eq '')
  {
    ($EMDROOT = $scriptDir) =~ s~/sysman/admin/scripts/osm~~;
  }
  $emctl = "$EMDROOT/bin/$emctl";
  $JAVA_HOME = $ENV{'JAVA_HOME'};
  if (! defined $JAVA_HOME || $JAVA_HOME eq '')
  {
    $JAVA_HOME = $DEF_JAVA_HOME;
  }
  $jar = "$JAVA_HOME/bin/$jar";
  $OUILOC = $ENV{'OUILOC'};
}

# --------------------- Main program -------------------------------------
setOutputAutoflush();
printHeader($0,$#ARGV);
emdenv();

#
# Check for minimum number of args
#
$action = $ARGV[0]; # checkTarget | installPatch | showResults
$#ARGV >= 2
  or abort($action,3,"Missing args: $#ARGV < 2");

$depot_path  = "$EMDROOT/$ARGV[1]"; # ecmdepot/patches
$db_name     = 'EMD';
$patch_lock  = "$depot_path/${db_name}-$patch_lock";

$patch_type = $ARGV[2]; # Patch

#
# Execute the action requested
#
if ($action eq 'checkTarget')
{
  $#ARGV >= 3
    or abort($action,3,"Missing args: $#ARGV < 3");

  $patch_id    = $ARGV[3]; # 1234567
  $patch_size  = $ARGV[4] if ($#ARGV >= 4); # 463162
  checkTarget();
}
elsif ($action eq 'installPatch')
{
  $#ARGV >= 4
    or abort($action,3,"Missing args: $#ARGV < 4");

  $patch_id    = $ARGV[3]; # 1234567
  $patch_file  = $ARGV[4]; # p1234567_9010_SOLARIS.zip
  $patch_index = $ARGV[5] if ($#ARGV >= 5); # 1..n
  $patch_count = $ARGV[6] if ($#ARGV >= 6); # n
  installPatch();
}
elsif ($action eq 'showResults')
{
  $#ARGV >= 3
    or abort($action,3,"Missing args: $#ARGV < 3");

  $patch_id    = $ARGV[3]; # 1234567
  showResults();
}
else
{
  abort($action,3,"Unrecognized action: $action");
}

#
# Indicate a successful action; exit with success status
#
printf "\n$action successful\n";
exit 0;
