# $Header: empshutdowndatabase.pl 02-sep-2007.22:45:30 shnavane Exp $
#
# Copyright (c) 2002, 2007, Oracle. All rights reserved.  
#
#    DESCRIPTION
#      ECM script to shutdown a list of databases and all related 
#      processes 
#
#      For each database
#       -   Shutdown the Database targets passed in
#       -   Shutdown the Listeners
#       -   Shutdown the Dbconsole
#       -   (only for RAC) Shutdown GSD (9i)/ ONS (10g))
#
#    USAGE
#      perl empshutdowndatabase.pl <options>
#
#    options
#          -oh <ORACLE_HOME>
#          -os <List of SIDs(separated by ampersand(&)) running out 
#               the ORACLE_HOME>
#          -l List of listeners running out of the OH (Optional)
#          -shared (If it is a shared database) (Optional)
#          -rac    (If it is a RAC database) (Optional)
#
#    NOTES
#      <other useful comments,qualifications,etc>
#
#    MODIFIED     (MM/DD/YY)
#       shnavane   05/18/07 - Fix bug #6024894
#       shnavane   09/02/07 - Backport shnavane_bug-6024894 from main
#       summukhe   05/13/07 - 
#       rsah       03/09/06 - Fix for bug 5064490. 
#       milshah    10/17/05 - Deleting unecessary Env variables 
#       milshah    09/28/05 - Windows stop DB service 
#       shgangul   09/21/05 - Remove warnings 
#       shgangul   08/26/05 - Add isqlplus startup shutdown 
#       shgangul   07/14/05 - support extra unknown parameters, -asm <asm 
#                             instances> 
#       mningomb   04/13/05 - mningomb_dbpatch
#       shgangul   03/23/05 - Creation
#

# --- Set up necessary variables for proper running of this environment ---
use strict;
use FileHandle;
use File::Basename();
use File::Path();
use File::Find();
use File::Spec();
use File::stat;
use User::pwent;

my $scriptName   = File::Basename::basename($0);
my $osmScriptDir = File::Basename::dirname($0);
my $ecmCommon    = File::Spec->catfile($osmScriptDir, 'ecmCommon.pl');
#my $db_instance = File::Spec->catfile($ENV{EMDROOT}, "sysman", "admin", "scripts", "db", "db_instance.pl");
my $db_state = File::Spec->catfile($ENV{EMDROOT}, "sysman", "admin", "scripts", "db", "dbstate.pl");

require "$ecmCommon";
require "$db_state";
#require "$db_instance";

# ------ Initialize global variables -------------------------------------
use constant COPYRIGHT => "Copyright \251 2002, 2006, Oracle. All rights reserved.";
use constant VERSION   => '10.2.0.0.0';
use constant PTYPE_PATCH        => 'patch';
use constant PTYPE_PATCHSET     => 'patchset';
use constant TTYPE_HOST         => 'host';
use constant TTYPE_DB           => 'oracle_database';
use constant TTYPE_EMD          => 'oracle_emd';
use constant TTYPE_IAS          => 'oracle_ias';
use constant DEPOTROOT          => 'EMStagedPatches';
use constant S_DELAY     => '-delay';
use constant S_FORCE     => '-force';
use constant S_HELP      => '-help';
use constant S_JRE       => '-jre';
use constant S_JDK       => '-jdk';
use constant S_LOCAL     => '-local';
use constant S_MINIMIZEDOWNTIME => '-minimize_downtime';
use constant S_NOBUGSUPERSET    => '-no_bug_superset';
use constant S_NOINVENTORY      => '-no_inventory';
use constant S_OH               => '-oh';
use constant S_PATCHID          => '-patchid';
use constant S_RETRY            => '-retry';
use constant S_SILENT           => '-silent';
use constant S_VERSION          => '-version';
use constant S_VERBOSE          => '-verbose';
use constant S_INVPTRLOC        => '-invPtrLoc';
use constant S_ASM              => '-asm';
use constant S_EMPTY            => '';
use constant S_SPACE            => ' ';
use constant S_QUOTE            => '"';
use constant S_APOSTROPHE       => "'";
use constant KB                 => 1024;
use constant B_TRUE             => 1;
use constant B_FALSE            => 0;
use constant E_SUCCESS          => 0;
use constant E_FAIL             => 1 * (1 << 8);
use constant E_NO_ENV           => 2 * (1 << 8);
use constant E_NO_OPATCHPL      => 3 * (1 << 8);
use constant E_NO_INVENTORY     => 4 * (1 << 8);
use constant E_NO_COMMAND       => 5 * (1 << 8);
use constant E_INV_COMMAND      => 6 * (1 << 8);
use constant E_INV_ARG          => 7 * (1 << 8);
use constant E_TOO_MANY         => 8 * (1 << 8);
use constant E_MISSING_ARG      => 9 * (1 << 8);
use constant E_NOT_ENOUGH_SPACE => 10 * (1 << 8);
my $action          = S_EMPTY;     # ARGV[0]
my $ORACLE_HOME     = S_EMPTY;     # -oh /private/OraHome1
my $ORACLE_SID      = S_EMPTY;     # 
my @ORACLE_SIDs     = ();          # -os mjgdb817,msdfsjk,sdnms23
my @ORACLE_ASMs     = ();          # -os mjgdb817,msdfsjk,sdnms23
my $tns_admin       = S_EMPTY;     # Location of listener config file
my $listener_list   = S_EMPTY;     # List of comma separated listeners
my @db_listeners    = ();          # List of listeners
my $shared          = 0;           # Whether shared or not
my $rac             = 0;           # Whether RAC setup or not
my $shutdown_type   = '15';        # -st 1=instance,2=listener,4=agent,8=startup
my $shutdown_sids   = S_EMPTY;     # -ss mjgdb817
my $shutdown_lsnrs  = S_EMPTY;     # -sl LISTENER
my $patch_id        = S_EMPTY;     #  -i 1390304
my $shutdowndbsids  = B_FALSE;     # whether to shutdown db sids
my $shutdownlisteners = B_FALSE;   # whether to shutdown listeners
my $shutdowndbcontrol = B_FALSE;   # whether to shutdown db control
my $patch_size      = '0';         #  -s 5237
my $patch_file      = S_EMPTY;     #  -f p1390304.zip
my $patch_index     = '1';         #  -x 1..n
my $patch_count     = '1';         #  -c n
my $patch_lock      = 'PATCH.lck';
my $dbsnmp_lock     = 'DBSNMP.lck';
my $lsnr_lock       = 'LSNR.lck';
my $db_lock         = 'DB.lck';
my $db_name         = S_EMPTY;
my @db_instances    = ();
my $OS              = $^O;         # OS type (solaris|linux)
my $PERL            = $^X;         # Perl executable
my $PERL5LIB        = S_EMPTY;
my $EMDROOT         = S_EMPTY;
my $JAVA_HOME       = S_EMPTY;
my $OUILOC          = S_EMPTY;
my $isRAC           = B_FALSE;     # true if RAC patch
my $db_shutdown     = B_FALSE;     # true if database shutdown required
my $lsnr_shutdown   = B_FALSE;     # true if listener shutdown required
my $dbsnmp_shutdown = B_FALSE;     # true if Oracle agent shutdown required
my $isProduct       = B_FALSE;     # true if RTM release
my $isDebug         = B_FALSE;     # true if debugging
my $isHelp          = B_FALSE;     # true if help request
my $isLogging       = B_FALSE;     # true if logging output
my $isHeader        = B_FALSE;     # true if header printed
my $local_opt       = S_EMPTY;     # if -local specified
my $inv_loc         = S_EMPTY;     # invPtrLoc option for apply
my $isShared        = 0;           # if it is a shared home
my $Shared          = S_EMPTY;     # sharedMove string
my $INVPTRLOCFILE   = 'oraInst.loc';
my $INVPTRLOCFILEPATH = S_EMPTY;

# --------------------- Command-line arguments --------------------------
use constant CTYPE_HELP     => 'help';
use constant CTYPE_APPLY    => 'applyPatch';
use constant CTYPE_CHECK    => 'checkTarget';
use constant CTYPE_EXPAND   => 'expandPatch';
use constant CTYPE_SHOW     => 'showResults';
use constant CTYPE_SHUTDOWN => 'shutdown';
use constant CTYPE_STARTUP  => 'startup';
my $CT     = '-c';                 #  -c count
my $CF     = '-cf';                # -cf
my $DP     = '-d';                 #  -d directory
my $PF     = '-f';                 #  -f patchfile
my $DB     = '-g';                 #  -g debug
my $HP     = '-h';                 #  -h
my $ID     = '-i';                 #  -i patchid
my $OH     = '-oh';                # -oh ORACLE_HOME
my $SID    = '-os';                # -os ORACLE_SID
my $PT     = '-p';                 #  -p patchtype
my $QT     = '-q';                 #  -q
my $SI     = '-s';                 #  -s size
my $SL     = '-sl';                # -sl shutownlisteners
my $SS     = '-ss';                # -ss shutdownsids
my $ST     = '-st';                # -st shutdownttype
my $TT     = '-t';                 #  -t targettype
my $PI     = '-x';                 #  -x index
my $VE     = '-v';                 #  -v
my $SHOME  = '-sharedMove';        #  -sharedMove, an unary operator
my %OPTV   = ();
my %UNARYS = ();
my %DEFS   = ();
my %ARGS   = ();
my %CMDS   = ();

# --------------------- OSD platform-specific ---------------------------
my $DF           = '/usr/bin/df';
my $dfOpt        = '-k';
my $ECHO         = '/usr/bin/echo';             # obsolete
my $EGREP        = '/usr/bin/egrep';            # obsolete
my $EMUNZIP      = 'emunzip';
my $EMZIP        = 'emzip';
my $LS           = '/usr/bin/ls';
my $AWK          = '/usr/bin/awk';              # obsolete
my $NULL_DEVICE  = '/dev/null';
my $PS           = '/usr/bin/ps';               # obsolete
my $LSNRCTL      = 'lsnrctl';                   # obsolete
my $SHELL        = '/bin/sh';
my $SQLPLUS      = 'sqlplus';                   # obsolete
my $SRVCTL       = 'srvctl';                    # obsolete
my $TAR          = '/usr/bin/tar';
my $tarOpt       = 'xvf';
my $UNZIP        = '/usr/bin/unzip';
my $unzipOpt     = '-o';
my $ZIP          = '/usr/bin/zip';
my $zipOpt       = S_EMPTY;
my $oratab       = '/var/opt/oracle/oratab';    # obsolete
my $BAT_SUFFIX   = S_EMPTY;
my $EXE_SUFFIX   = S_EMPTY;
my $CLASSPATHSEP = ':';
my $PATHSEP      = ':';
my $FILESEP      = '/';
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';

# --------------------- Subroutines -------------------------------------

# setupOSD()
#
# Setup OSD commands
#
sub setupOSD
{
    if (onWindows())
    {
        $ECHO                = 'echo';
        $LS                  = 'dir';
        $NULL_DEVICE         = 'NUL';
        $SHELL               = 'cmd.exe /c';
        $TAR                 = 'tar.exe';
        $UNZIP               = 'unzip.exe';
        $ZIP                 = 'zip.exe';
        $BAT_SUFFIX          = '.bat';
        $EXE_SUFFIX          = '.exe';
        $CLASSPATHSEP        = ';';
        $PATHSEP             = ';';
        $FILESEP             = '\\';
        $DEF_PATH            = $ENV{'PATH'};
        $DEF_LD_LIBRARY_PATH = $ENV{'PATH'};
        $DEF_JAVA_HOME       = $ENV{'JAVA_HOME'};
        $ENV{'DIRCMD'}       = '';
        $ENV{'COPYCMD'}      = '';
    }
    else
    {
        if (!equalsIgnoreCase('solaris', $OS)
            && !equalsIgnoreCase('linux', $OS))
        {
            $dfOpt = '-Pk';
        }
        if (!equalsIgnoreCase('solaris', $OS))
        {
            $oratab = '/etc/oratab';
        }
        if (equalsIgnoreCase('hpux', $OS))
        {
           $DF =  '/usr/bin/bdf';
           $dfOpt = '';
        }
        if (equalsIgnoreCase('linux', $OS))
        {
            $DF    = '/bin/df';
            $ECHO  = '/bin/echo';
            $EGREP = '/bin/egrep';
            $LS    = '/bin/ls';
            $AWK   = '/bin/awk';
            $PS    = '/bin/ps';
            $TAR   = '/bin/tar';
        }
        elsif (equalsIgnoreCase('darwin', $OS))
        {
            $DF    = '/bin/df';
            $ECHO  = '/bin/echo';
            $LS    = '/bin/ls';
            $PS    = '/bin/ps';
        }
    }
}

#
# logf(<message>)
#
# Display the message with timestamp
#
#
sub logf($) 
{
    my ($msg) = @_;

    printf("\n%s - %s\n", scalar(localtime()), $msg);
}


# isRunning(<proc_match>)
#
# Return true if process is running
#
sub isRunning($)
{
    my ($proc_match) = @_;

    return (system("$PS -e -o args | $EGREP -s \'$proc_match\'") == E_SUCCESS);
}

# initOptions()
#
# Setup known options for parsing
#
sub initOptions
{
    $CMDS{lc(CTYPE_HELP)}   = '1';
    $CMDS{lc(CTYPE_APPLY)}  = '1';
    $CMDS{lc(CTYPE_CHECK)}  = '1';
    $CMDS{lc(CTYPE_EXPAND)} = '1';
    $CMDS{lc(CTYPE_SHOW)}   = '1';

    #$CMDS{lc(CTYPE_SHUTDOWN)} = '1';
    #$CMDS{lc(CTYPE_STARTUP)}  = '1';

    $OPTV{$CT}   = 'count';
    $DEFS{$CT}   = '1';              # -c
    $OPTV{$DP}   = 'directory';
    $DEFS{$DP}   = DEPOTROOT;        # -d
    $OPTV{$PF}   = 'patchfile';      # -f
    $OPTV{$DB}   = 'debug';
    $DEFS{$DB}   = '0';              # -g
    $OPTV{$HP}   = 'help';
    $UNARYS{$HP} = '1';              # -h
    $OPTV{$ID}   = 'patchid';        # -i
    $OPTV{$OH}   = 'ORACLE_HOME';    # -oh
    $OPTV{$SID}  = 'ORACLE_SID';
    $DEFS{$SID}  = 'none';           # -os
    $OPTV{$PT}   = 'patchtype';
    $DEFS{$PT}   = PTYPE_PATCH;      # -p
    $OPTV{$SI}   = 'size';
    $DEFS{$SI}   = '0';              # -s
    $OPTV{$SL}   = 'listeners';
    $DEFS{$SL}   = ' ';              # -sl
    $OPTV{$SS}   = 'ids';
    $DEFS{$SS}   = ' ';              # -ss
    $OPTV{$ST}   = 'shutdowntype';
    $DEFS{$ST}   = '15';             # -st
    $OPTV{$TT}   = 'targettype';
    $DEFS{$TT}   = TTYPE_DB;         # -t
    $OPTV{$PI}   = 'index';
    $DEFS{$PI}   = '1';              # -x
    $OPTV{$VE}   = 'version';
    $UNARYS{$VE} = '1';              # -v
    $OPTV{$CF}   = 'checkfree';
    $UNARYS{$CF} = '1';              # -cf
    $OPTV{$QT}   = 'quiet';
    $UNARYS{$QT} = '1';              # -q
    $OPTV{$SHOME} = 'sharedMove';    # -sharedMove
    $UNARYS{$SHOME} = '1';           # -sharedMove
    $DEFS{$SHOME} = '0';             # default is not-shared
}

# parseArgs()
#
# Store all the arguments in hashed table
#
sub parseArgs
{

    # Initialize the parameters;

    my $opt      = S_EMPTY;
    my $argcount = scalar(@ARGV);

    logf("Arguments: @ARGV");

    my $i = 0;
    while ($i < $argcount)
    {
        $opt = $ARGV[$i];
        if ($opt eq "-oh")
        {
            $i ++;
            $ORACLE_HOME = $ARGV[$i];
        }
        elsif ($opt eq "-os")
        {
            $i ++;
            # Parse the SID list and store it in SID array
            my $sidlist = $ARGV[$i];
            chomp $sidlist;
            @ORACLE_SIDs = split(/&/, $sidlist);

            # Get a list of uniq SIDs
            my @tempsids = ();
            for(my $sidi = 0; $sidi < scalar(@ORACLE_SIDs); $sidi++)
            {
                my $uniq = 1;
                for(my $sidj = 0; $sidj < scalar(@tempsids); $sidj++)
                {
                    if ($ORACLE_SIDs[$sidi] eq $tempsids[$sidj])
                    {
                        $uniq = 0;
                        last;
                    }
                }

                if ($uniq == 1)
                {
                    push(@tempsids, $ORACLE_SIDs[$sidi]);
                }
            }
            @ORACLE_SIDs = @tempsids;
        }
        elsif ($opt eq "-l")
        {
            $i ++;
            # Parse the listener list and store it in listeners array
            my $listener_list = $ARGV[$i];
            chomp $listener_list;
            if (substr($listener_list, 0, 1) eq "-")
            {
                $listener_list = "";
                @db_listeners = ();
                $i --;
            }
            else
            {
                @db_listeners = split(/&/, $listener_list);
            }
        }
        elsif ($opt eq "-tns_admin")
        {
            $i ++;
            $tns_admin = $ARGV[$i];
        }
        elsif ($opt eq "-shutdowndbsids")
        {
            $shutdowndbsids = B_TRUE;
        }
        elsif ($opt eq "-shutdownlisteners")
        {
            $shutdownlisteners = B_TRUE;
        }
        elsif ($opt eq "-shutdowndbcontrol")
        {
            $shutdowndbcontrol = B_TRUE;
        }
        elsif ($opt eq "-shared")
        {
            $i ++;
            $shared = $ARGV[$i];
        }
        elsif ($opt eq "-rac")
        {
            $i ++;
            $rac = $ARGV[$i];
        }
        elsif ($opt eq S_ASM)
        {
            $i ++;
            # Parse the ASM list and store it in ASM array
            my $asmlist = $ARGV[$i];
            chomp $asmlist;
            @ORACLE_ASMs = split(/&/, $asmlist);

            # Get a list of uniq ASMs
            my @tempasms = ();
            for(my $asmi = 0; $asmi < scalar(@ORACLE_ASMs); $asmi++)
            {
                my $uniq = 1;
                for(my $asmj = 0; $asmj < scalar(@tempasms); $asmj++)
                {
                    if ($ORACLE_ASMs[$asmi] eq $tempasms[$asmj])
                    {
                        $uniq = 0;
                        last;
                    }
                }

                if ($uniq == 1)
                {
                    push(@tempasms, $ORACLE_ASMs[$asmi]);
                }
            }
            @ORACLE_ASMs = @tempasms;
        }
        else
        {
            printHeader($0, $#ARGV) if (!$isHeader);
            #abort($action, statusf(E_INV_ARG), "Invalid argument: $opt");
            logf("Following argument is not supported: $opt");
        }
        
        $i ++; # increment the count
    }

    if ($ORACLE_HOME eq S_EMPTY)
    {
        abort($action, statusf(E_INV_ARG), "Oracle Home not set");
    }

    if ($tns_admin eq S_EMPTY)
    {
        $tns_admin = File::Spec->catfile($ORACLE_HOME, "network", "admin");
    }
}

# isDBRunning(<sid>)
#
# Return true if db is running
#
sub isDBRunning($)
{
    my ($sid) = @_;

    my $old_sid   = $ENV{'ORACLE_SID'};
    my $isRunning = B_FALSE;

    $ENV{'ORACLE_SID'} = $sid;
    my ($errCode, $initState) = get_db_state('oracle_database', $ORACLE_HOME, $sid, "", "", "", "sysdba", "", "", "");
    logf ("Database [$ORACLE_HOME (OH), $sid (SID)] in state $initState, ErrorCode=$errCode");
    if (($errCode == 0) && ($initState ne "SHUTDOWN"))
    {
        $isRunning = B_TRUE;
    }
    $ENV{'ORACLE_SID'} = $old_sid;
    return $isRunning;
}

# isDBStarted(<sid>)
#
# Return true if db is started
#
sub isDBStarted($)
{
    my ($sid) = @_;

    my $old_sid   = $ENV{'ORACLE_SID'};
    my $isStarted = B_FALSE;

    $ENV{'ORACLE_SID'} = $sid;
    my ($errCode, $initState) = get_db_state('oracle_database', $ORACLE_HOME, $sid, "", "", "", "sysdba", "", "", "");
    logf ("Database [$ORACLE_HOME (OH), $sid (SID)] in state $initState, ErrorCode=$errCode");
    if (($errCode == 0) && ($initState eq "STARTED"))
    {
        $isStarted = B_TRUE;
    }
    $ENV{'ORACLE_SID'} = $old_sid;
    return $isStarted;
}

# 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 = File::Basename::dirname($lock_file);

    File::Path::mkpath($lock_path, 0, 0775) if (!-d "$lock_path");
    open(OUTPUT, "> $lock_file")
      or abort($action, statusf($?),
               "Could not open lockfile $lock_file to write: $!");
    printf(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);
}

# findLocks(<path>,<mask>)
#
# Return list of lock files
#
sub findLocks($$)
{
    my ($path, $mask) = @_;

    my @locks   = ();
    my $pattern = File::Spec->catfile($path, $mask);

    if (onWindows())
    {
        $pattern =~ s~/~\\~g;
        chomp(@locks = `DIR /B $pattern 2> $NULL_DEVICE`);
        for (my $i = 0 ; $i <= $#locks ; $i++)
        {
            $locks[$i] = File::Spec->catfile($path, $locks[$i]);
        }
    }
    else
    {
        chomp(@locks = `$LS -1 $pattern 2> $NULL_DEVICE`);
    }

    return @locks;
}

# getDB_NAME(<lock_file>)
#
# Find db_name for use in shutting down RAC instances
#
sub getDB_NAME($)
{
    my ($lock_file) = @_;

    my @lines = ();

    if (!isEmpty($lock_file))
    {
        open(INPUT, "< $lock_file");
        while (<INPUT>)
        {
            chomp;
            $db_name = $_ if (!isEmpty($_));
        }
        close(INPUT);

        return;
    }

    chomp(@lines =
          `$ECHO "SET pagesize 0\\nSET tab off\\nSELECT SYS_CONTEXT(\'USERENV\',\'DB_NAME\') FROM sys.dual;\\n" | $SQLPLUS -S '/ as sysdba'`
          );

    if ($#lines >= 0)
    {
        $lines[0] =~ s/ //g;
        $db_name = $lines[0];
    }
}

# getDB_LISTENERS()
#
# Populate db_listeners for use in shutting down all Oracle listeners
#
sub getDB_LISTENERS()
{

    my $lsnr  = S_EMPTY;
    my @lines = ();

    # Listener config file
    my $list_config_file = File::Spec->catfile($tns_admin, "listener.ora");
    logf("Geting the listenres  file=$list_config_file\n");
    if (! -e $list_config_file )
    {
        return;
    }

    # Parse the listener.ora file to get the list of listeners
    local *LISTDATA;
    open(LISTDATA, "<$list_config_file")
      or abort($action, statusf($?),
               "Could not open listerner config file \'$list_config_file\'");
    my @ldata = <LISTDATA>;
    close(LISTDATA);

    foreach my $line (@ldata)
    {
        chomp $line;
        logf("Reading line=$line\n");
        if ($line =~ m#^LISTENER(.*)=(\s*)$#)
        {
        logf("Special Reading line=$line\n");
            my @linewords = split(/ /, $line);
            if ($linewords[0] eq "") {next;}
            if ($linewords[0] =~ m#^\(#) {next;}
            if ($linewords[0] =~ m#^SID_LIST_#) {next;}
            push (@db_listeners, $linewords[0]);
        }
    }

    logf ("List of listeners detected: @db_listeners");
}


# createInitOra(<sid>,<initora>)
#
# Create an init$ORACLE_SID.ora file for use in startup
#
sub createInitOra($$)
{
    my ($sid, $initora) = @_;

    my $name  = S_EMPTY;
    my $value = S_EMPTY;
    my @lines = ();

    open(OUTPUT, "> $initora")
      or abort($action, statusf($?),
               "Could not open initfile $initora to write: $!");

    printf(OUTPUT "# $initora generated on %s\n", scalar(localtime()));

    chomp(@lines =
          `$ECHO "SET pagesize 0\\nSET tab off\\nSET linesize 512\\nSELECT CONCAT(CONCAT(name,'='),value) FROM V\\\$PARAMETER WHERE isdefault='FALSE' ORDER BY name;\\n" | $SQLPLUS -S '/ as sysdba'`
          );

    for (my $i = 0 ; $i <= $#lines ; $i++)
    {
        $lines[$i] =~ s/ //g;
        next if (!$lines[$i] =~ m/=/);

        ($name, $value) = split (/=/, $lines[$i], 2);
        next if (!defined $value);

        if ($value =~ m/,/)
        {
            $value =~ s/,/","/g;
            $value = '"' . $value . '"';
        }
        elsif ($value =~ m/=/)
        {
            $value = '"' . $value . '"';
        }
        if ($name =~ m/control_files/i)
        {
            $value = '(' . $value . ')';
        }
        else
        {
            $value = '""' if (isEmpty($value));
        }

        printf(OUTPUT "%s=%s\n", $name, $value);
    }
    printf(OUTPUT "# end of generated file\n");
    close(OUTPUT);
}

# shutdownLSNR(<name>)
#
# Shutdown database listener <name> if running
# Create shutdown marker to indicate what we did
#
sub shutdownLSNR($)
{
    my ($name) = @_;

    my $status = E_SUCCESS;

    $! = E_SUCCESS;
    logf("Stopping Oracle database listener ${name}...");
    $status = echodo("$LSNRCTL stop $name");
    $status = statusf($status);
    if ($status != E_SUCCESS)
    {
      logf("Could not shutdown database listener. Return Code: $status, Error Message: $!");
    }
}

# shutdownDBSNMP()
#
# Shutdown Oracle Agent if running
# Create shutdown marker to indicate what we did
#
sub shutdownDBSNMP
{
    my $DBSNMP   = 'dbsnmp';
    my $AGENTCTL = "$LSNRCTL dbsnmp_stop";
    my $status   = E_SUCCESS;

    if (-f File::Spec->catfile($ORACLE_HOME, 'bin', 'agentctl'))
    {
        $DBSNMP = File::Spec->catfile($ORACLE_HOME, 'bin', $DBSNMP);
        $AGENTCTL =
          File::Spec->catfile($ORACLE_HOME, 'bin', 'agentctl') . ' stop';
    }

    if (isRunning("^${DBSNMP}\$"))
    {
        $! = E_SUCCESS;
        logf("Stopping Oracle agent...\n");
        $status = echodo("$AGENTCTL");
        $status = statusf($status);
        $status == E_SUCCESS
          or abort($action, $status,
                   "Could not shutdown Oracle agent: $!");

        createLock($dbsnmp_lock, 'AGENT');
    }
}

# shutdownSID(<sid>)
#
# Shutdown database instance if running
# Create shutdown marker to indicate what we did
#
sub shutdownSID($)
{
    my ($sid) = @_;

    my $connStr = constructConnectStr("", "", "", "sysdba");
    change_db_state('oracle_database', $connStr, $sid, "SHUTDOWN", $sid);
}

# shutdownDBControl(<sid>)
#
# Shutdown database console if running
# Create shutdown marker to indicate what we did
#
sub shutdownDBControl($)
{
    my ($sid) = @_;

    my $status = E_SUCCESS;

    $! = E_SUCCESS;
    logf("Stopping Oracle DB Control ${sid}...");

    my $emctl = File::Spec->catfile($ENV{ORACLE_HOME}, "bin", "emctl");
    $status = echodo("$emctl stop dbconsole");
    $status = statusf($status);
    if ($status != E_SUCCESS)
    {
      logf("Could not stop DB Control. Return Code: $status, Error Message: $!");
    }
}

# setShutdownTypes()
#
# Set up requested shutdown/startup types
#
sub setShutdownTypes
{
    my $sid   = S_EMPTY;
    my $lsnr  = S_EMPTY;
    my $count = 0;

    if (equalsIgnoreCase(CTYPE_SHUTDOWN, $action))
    {
        $db_shutdown     = B_TRUE if ($shutdown_type & 1);
        $lsnr_shutdown   = B_TRUE if (($shutdown_type & 2) && !$isRAC);
        $dbsnmp_shutdown = B_TRUE if ($shutdown_type & 4);
    }
    elsif (equalsIgnoreCase(CTYPE_STARTUP, $action) && ($shutdown_type & 8))
    {
        $db_shutdown     = B_TRUE if ($shutdown_type & 1);
        $lsnr_shutdown   = B_TRUE if (($shutdown_type & 2) && !$isRAC);
        $dbsnmp_shutdown = B_TRUE if ($shutdown_type & 4);
    }

    if ($db_shutdown && !isEmpty($shutdown_sids))
    {
        $count = $#db_instances;
        $shutdown_sids =~ s/ //g;
        LOOP: foreach $sid (split (/,/, $shutdown_sids))
        {
            next LOOP if (isEmpty($sid));
            for (my $i = 0 ; $i <= $count ; $i++)
            {
                next LOOP if (isEqual($sid, $db_instances[$i]));
            }
            $count += 1;
            $db_instances[$count] = $sid;
        }
    }

    if ($lsnr_shutdown && !isEmpty($shutdown_lsnrs))
    {
        $count = $#db_listeners;
        $shutdown_lsnrs =~ s/ //g;
        LOOP: foreach $lsnr (split (/,/, $shutdown_lsnrs))
        {
            next LOOP if (isEmpty($lsnr));
            for (my $i = 0 ; $i <= $count ; $i++)
            {
                next LOOP if (isEqual($lsnr, $db_listeners[$i]));
            }
            $count += 1;
            $db_listeners[$count] = $lsnr;
        }
    }
}

# startupDBSNMP()
#
# Start Oracle agent if it was running
# Remove shutdown marker to indicate the current state
#
sub startupDBSNMP
{
    my $DBSNMP   = 'dbsnmp';
    my $AGENTCTL = "$LSNRCTL dbsnmp_start";
    my $status   = E_SUCCESS;

    if (-f File::Spec->catfile($ORACLE_HOME, 'bin', 'agentctl'))
    {
        $DBSNMP = File::Spec->catfile($ORACLE_HOME, 'bin', $DBSNMP);
        $AGENTCTL =
          File::Spec->catfile($ORACLE_HOME, 'bin', 'agentctl') . ' start';
    }

    if (isLocked($dbsnmp_lock))
    {
        if (!isRunning("^${DBSNMP}\$"))
        {
            $! = E_SUCCESS;
            logf("Starting Oracle agent...\n");
            $status = echodo("$AGENTCTL");
            $status = statusf($status);
            $status == E_SUCCESS
              or abort($action, $status,
                       "Could not startup Oracle agent: $!");
        }
        unlink($dbsnmp_lock);
    }
}

# addPATH(<new_path>)
#
# Add the directory at the beginning of PATH
#
sub addPATH($)
{
    my ($new_path) = @_;

    my $PATH = $ENV{'PATH'};

    if (onWindows())
    {
        $new_path =~ s~/~\\~g;
    }

    if (isEmpty($PATH))
    {
        $PATH = $DEF_PATH;
    }
    else
    {
        if (onWindows())
        {
            $new_path =~ s~\\~\\\\~g;
        }
        $PATH =~ s~(^|${PATHSEP})${new_path}(${PATHSEP}|$)~${PATHSEP}~g;
        $PATH =~ s~(^${PATHSEP}|${PATHSEP}$)~~g;
        if (onWindows())
        {
            $new_path =~ s~\\\\~\\~g;
        }
    }
    $ENV{'PATH'} = "${new_path}${PATHSEP}${PATH}";
}

# addLDPATH(<NEW_LDPATH>)
#
# Add the directory at the beginning of LD_LIBRARY_PATH
#
sub addLDPATH($)
{
    my ($new_ldpath) = @_;

    my $LD_LIBRARY_PATH = $ENV{'LD_LIBRARY_PATH'};

    if (onWindows())
    {
        addPATH($new_ldpath);
        return;
    }

    if (isEmpty($LD_LIBRARY_PATH))
    {
        $LD_LIBRARY_PATH = $DEF_LD_LIBRARY_PATH;
    }
    else
    {
        $LD_LIBRARY_PATH =~
          s~(^|${PATHSEP})${new_ldpath}(${PATHSEP}|$)~${PATHSEP}~g;
        $LD_LIBRARY_PATH =~ s~(^${PATHSEP}|${PATHSEP}$)~~g;
    }
    $ENV{'LD_LIBRARY_PATH'} = "${new_ldpath}${PATHSEP}${LD_LIBRARY_PATH}";
}

# oraenv(<oracle_home>,<oracle_sid>)
#
# Set ORACLE_HOME environment
#
sub oraenv($$)
{
    ($ORACLE_HOME, $ORACLE_SID) = @_;

    $ORACLE_HOME = trim($ORACLE_HOME);
    $ORACLE_SID  = trim($ORACLE_SID);
    $db_name = $ORACLE_SID;
    $LSNRCTL = "lsnrctl";
    $LSNRCTL = File::Spec->catfile($ORACLE_HOME, 'bin', $LSNRCTL);
    $SQLPLUS = "sqlplus";
    $SQLPLUS = File::Spec->catfile($ORACLE_HOME, 'bin', "$SQLPLUS$EXE_SUFFIX");
    $SRVCTL = "srvctl";
    $SRVCTL  = File::Spec->catfile($ORACLE_HOME, 'bin', $SRVCTL);
    if (-f $SRVCTL)
    {
        $isRAC = B_TRUE;
    }
    my %oldenv = %ENV;
    if (!onWindows())
    {
        %ENV = ();

        #   my ($login,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = getpwuid($>);
        my $pw = getpw($>);
        $ENV{'HOME'}    = $pw->dir;
        $ENV{'LOGNAME'} = $pw->name;
        $ENV{'USER'}    = $pw->name;
        $ENV{'LC_ALL'}  = 'C';
    }
    $ENV{'EMDROOT'}     = $EMDROOT;
    $ENV{'PERL5LIB'}    = $PERL5LIB;
    $ENV{'JAVA_HOME'}   = $JAVA_HOME;
    $ENV{'PWD'}         = $oldenv{'PWD'} if (defined $oldenv{'PWD'});
    $ENV{'SHELL'}       = $SHELL;
    $ENV{'TZ'}          = $oldenv{'TZ'} if (defined $oldenv{'TZ'});
    $ENV{'ORACLE_HOME'} = $ORACLE_HOME;
    $ENV{'ORACLE_SID'}  = $ORACLE_SID;
    $ENV{'TNS_ADMIN'}   = File::Spec->catfile($ORACLE_HOME, 'network', 'admin');

    $ENV{'PATH'} = S_EMPTY;
    addPATH(File::Spec->catfile($JAVA_HOME,   'bin'));
    addPATH(File::Spec->catfile($ORACLE_HOME, 'bin'));

    $ENV{'LD_LIBRARY_PATH'} = S_EMPTY;
    addLDPATH(File::Spec->catfile($ORACLE_HOME, 'network', 'lib'));
    addLDPATH(File::Spec->catfile($ORACLE_HOME, 'lib'));

    #printf(STDOUT "PATH = %s\nLD_LIBRARY_PATH = %s\n",$ENV{'PATH'},$ENV{'LD_LIBRARY_PATH'});
}

# emdenv()
#
# Set EMD environment
#
sub emdenv
{
    $EMDROOT = $ENV{'EMDROOT'};
    if (isEmpty($EMDROOT))
    {
        abort($action, statusf(E_INV_ARG), "EMDROOT not set");
        # ($EMDROOT = $osmScriptDir) =~ s/.sysman.admin.scripts.osm$//;
    }
    $JAVA_HOME = $ENV{'JAVA_HOME'};
    if (isEmpty($JAVA_HOME))
    {
        $JAVA_HOME = $DEF_JAVA_HOME;
    }
    $UNZIP = File::Spec->catfile($EMDROOT, 'bin', "unzip$EXE_SUFFIX");
    if (!-x $UNZIP)
    {
        $UNZIP = File::Spec->catfile($EMDROOT, 'bin', "$EMUNZIP$BAT_SUFFIX");
    }
    if (!-x $UNZIP)
    {
        $UNZIP = 'unzip';
    }
    $ZIP = File::Spec->catfile($EMDROOT, 'bin', "zip$EXE_SUFFIX");
    if (!-x $ZIP)
    {
        $ZIP = File::Spec->catfile($EMDROOT, 'bin', "$EMZIP$BAT_SUFFIX");
    }
    if (!-x $ZIP)
    {
        $ZIP = 'zip';
    }
    $OUILOC   = $ENV{'OUILOC'};
    $PERL5LIB = getPERL5LIB();
}

# usage()
#
# Display a usage help page
#
sub usage
{
    if (defined $ARGS{$VE})
    {
        printf(STDOUT "\n%s Version %s\n%s\n\n", $scriptName, VERSION, COPYRIGHT);
        cleanAndExit(0);
    }

    my ($checkTarget, $expandPatch, $applyPatch, $showResults) =
      (CTYPE_CHECK, CTYPE_EXPAND, CTYPE_APPLY, CTYPE_SHOW);

    # Start of "here doc".
    print STDOUT <<_EOM_

Usage: perl $scriptName <command> [<options>]

  command
      $checkTarget | $expandPatch | $applyPatch | $showResults
  options
      $CT <$OPTV{$CT}>\t: Number of patches (defaults to $DEFS{$CT})
      $CF \t\t: Check freespace ($checkTarget only)
      $DP <$OPTV{$DP}>\t: Location of patches (defaults to "$DEFS{$DP}")
      $PF <$OPTV{$PF}>\t: Name of patch file (defaults to "p<patchid>.zip")
      $HP \t\t: Display this help
      $ID <$OPTV{$ID}>\t: Patch number (required)
      $OH <$OPTV{$OH}>\t: Location of Oracle Home (required)
      $SID <$OPTV{$SID}>\t: Name of database instance (defaults to "$DEFS{$SID}")
      $QT \t\t: Omit header/trailer output
      $PT <$OPTV{$PT}>\t: Type of patch (defaults to "$DEFS{$PT}")
      $SI <$OPTV{$SI}>\t\t: Size of patch file (defaults to $DEFS{$SI})
      $TT <$OPTV{$TT}>\t: Type of target (defaults to "$DEFS{$TT}")
      $PI <$OPTV{$PI}>\t: Index of current patch (defaults to $DEFS{$PI})
      $SHOME \t: The Agent home is a shared home

Examples:

  $checkTarget -i 1390304 -oh /private/OraHome1 -s 5192
  $expandPatch -i 1390304 -oh /private/OraHome1 -f p1390304.zip
  $applyPatch  -i 1390304 -oh /private/OraHome1 -t oracle_emd
  $applyPatch  -i 1390304 -oh /private/OraHome1 -t oracle_emd -sharedMove
  $showResults -i 1390304 -oh /private/OraHome1

_EOM_
      ;

}

# statusf(<status>)
#
# Returns the termination status of command
#
sub statusf($)
{
    my ($status) = @_;

    $status = E_FAIL if (!defined $status);
    $status = ($status >> 8);
    #$status = ($status >> 8) if ($status >= E_FAIL);
    return $status;
}

# cleanAndExit(<status>)
#
# Clean up and terminate with status
#
sub cleanAndExit($)
{
    my ($status) = @_;

    exit $status;
}

# --------------------- Main program ----------------------------------
setOutputAutoflush();

parseArgs();

#
# Check for help requested
#
if ($isHelp)
{
    usage();
    cleanAndExit(0);
}

#
# Set the EMDROOT environment
#
setupOSD();
emdenv();

#
# Cleaning up unecessary env variables which are creating error only in case of windows
#
if (onWindows())
{
    $ENV{'EMSTATE'} = S_EMPTY;
    $ENV{'DBCONSOLE_SERVICE_NAME'} = S_EMPTY;
}


#
# Set ORACLE_HOME, ORACLE_SID environment and shutdown database;
#
if ($shutdowndbsids == B_TRUE)
{
    for(my $i=0; $i < scalar(@ORACLE_SIDs); $i++)
    {
        my $oracle_sid = $ORACLE_SIDs[$i];
        oraenv($ORACLE_HOME, $oracle_sid);
        if (onWindows())
        {
            logf ("Shutting down database [$ORACLE_HOME (OH), $oracle_sid (SID)] on Windows.");
            my $status = E_SUCCESS;
            $! = E_SUCCESS;

            #Stop the DB service on Windows
            my $serviceName = "OracleService" . $oracle_sid; 
            $status = echodo("net stop $serviceName");
            $status = statusf($status);
            if ($status != E_SUCCESS)
            {
               logf("Could not shutdown OracleService for $ORACLE_HOME (OH) on windows. Return Code: $status, Error Message: $!");
            }
        }
        # Not on Windows and hence sqlplus, shutdown immediate is enough
        elsif (isDBRunning($oracle_sid))
        {
            logf ("Shutting down database [$ORACLE_HOME (OH), $oracle_sid (SID)]");
            shutdownSID($oracle_sid);
        }
        else # Database not running, nothing to do
        {
            logf ("Database [$ORACLE_HOME (OH), $oracle_sid (SID)] is already DOWN");
        }
    }
}

# Shutdown listeners
# Shutdown one at a time
if ($shutdownlisteners == B_TRUE)
{
    for(my $i=0; $i < scalar(@db_listeners); $i++)
    {
        my $shut_listener_name = $db_listeners[$i];
        logf ("Shutting down listener [$ORACLE_HOME (OH), $shut_listener_name (NAME)]");
        shutdownLSNR($db_listeners[$i]);
    }
}

# Shutdown DB Control
if ($shutdowndbcontrol == B_TRUE)
{
    for(my $i=0; $i < scalar(@ORACLE_SIDs); $i++)
    {
        my $oracle_sid = $ORACLE_SIDs[$i];
        logf ("Shutting down DB Control [$ORACLE_HOME (OH), $oracle_sid (SID)]");
        oraenv($ORACLE_HOME, $oracle_sid);
        shutdownDBControl($oracle_sid);
    }
}

# Shutdown isqlplus. isqlplus is per Oracle Home and not per database
# Shutdown isqlplus only in UNIx. In Windows let the services be stopped in the following catch -all loop
# The reason this is done is because isqlplusctl stops the service a little late and if the next loop doesnt recognize that the service is down, 
# it also tries to stop it at the same time causing memory corruption issues.
if (!onWindows())
{
    my $status = E_SUCCESS;
    $! = E_SUCCESS;

    logf ("Shutting down iSQL*Plus for $ORACLE_HOME (OH)");
    my $ISQLPLUSCTL = "isqlplusctl";
    my $ISQLPLUS = File::Spec->catfile($ORACLE_HOME, 'bin', "$ISQLPLUSCTL");
    $ENV{'ORACLE_HOME'} = $ORACLE_HOME;
    $status = echodo("$ISQLPLUS stop");
    $status = statusf($status);
    if ($status != E_SUCCESS)
    {
        logf("Could not shutdown iSQL*Plus for $ORACLE_HOME (OH). Return Code: $status, Error Message: $!");
    }
}

#Finally if Windows, see if any services are running then stop them.
if (onWindows())
{
    # Run oradim.exe to figure out the services running out of an Oracle Home.
    my @op = `$ORACLE_HOME\\bin\\oradim -ex services enum with image | findstr /I RUNNING | findstr /IL $ORACLE_HOME`;
    my $line = S_EMPTY;
    my $service = S_EMPTY;     

    # Loop through the services, figure the names and shut them down.
    foreach $line (@op)
    {
       my $status = E_SUCCESS;
       $! = E_SUCCESS;

       chomp($line);
 
       # Figure out the service name.
       $service = (split(/\s+/,$line))[1];
       
       # Stop the services
       logf("Manually stopping $service ...");
       $status = system("net stop $service");
       $status = statusf($status);
       if ($status != E_SUCCESS)
       {
           logf("Could not shutdown $service for $ORACLE_HOME (OH). Return Code: $status, Error Message: $!");
       }

    } 
}

#
# Finally if Database is still up then abort
#
if ($shutdowndbsids == B_TRUE)
{
    for(my $i=0; $i < scalar(@ORACLE_SIDs); $i++)
    {
        my $oracle_sid = $ORACLE_SIDs[$i];
        oraenv($ORACLE_HOME, $oracle_sid);
        if (isDBStarted($oracle_sid))
        {
            abort($action, statusf(E_FAIL), "Error shutting down database [$ORACLE_HOME (OH), $oracle_sid (SID)]");
        }
        logf("Database has been shutdown successfully.");
    }
}


cleanAndExit(0);
