#!/usr/local/bin/perl
# 
# $Header: emdb/sysman/webapps/em/WEB-INF/perl/db/rman/rman_o.pl /st_emdbsa_11.2/6 2010/03/11 00:53:47 mmootha Exp $
#
# rman.pl
# 
# Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. 
#
#    NAME
#      10gR2
#      rman_o.pl - Perl script for backup and recovery jobs and remote operations.
#      rman.pl - Perl script for backup and recovery jobs and remote operations.
#
#    DESCRIPTION
#      <short description of component this file declares/defines>
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    mmootha     03/04/10 - Bug fix - 9435782
#    nzhao       04/02/09 - Fix bug 8355600 to shut down db before open
#                           resetlog for rac db.
#    hasriniv    01/29/09 - Code Sync
#    pbantis     01/14/09 - Bug 6697519 unset JAVA_HOME for rac db.
#    pbantis     12/23/08 - Bug 6697519 Change env for 9.2 RAC.
#    rgiroux     11/15/08 - fix for bug 7524930; report ORA 9925 errors to the
#                           user
#    spanchum    10/21/08 - fix createPasswordfile
#    mmootha     10/10/08 - Preserve changes to db_password.
#    mmootha     10/01/08 - 10205 code slap fix
#    ngade       09/19/08 - rename find_escape
#    ngade       07/22/08 - 11gc to 10205GC code slap
#    rgiroux     06/13/08 - fix for bug 6934515; return actual db open status
#                           from br_open_db
#    ngade       05/12/08 - backport bug 6998238
#    vgoli       03/18/08 - adding JDBC OCI wrappers
#    ngade       06/12/08 - 
#    rrawat      03/05/08 - Backport rrawat_bug-6815341 from main
#    rrawat      02/11/08 - Bug-6604837 
#    sksantha    02/04/08 - 6736728 bug fix to sync this file with the one
#                           under emdb/sbrm/impl/scripts
#    pbantis     10/11/07 - Support NOMOUNT for restore controlfile work.
#    hasriniv    09/06/07 - add support for duplicate
#    nzhao       08/27/07 - Add shutdown option
#    pbantis     06/21/07 - Backport pbantis_bug-4703149 from main
#    sjconnol    02/13/07 - Fix compilation warnings
#    rrawat      12/06/06 - Bug-5584039
#    pbantis     07/12/06 - Add br_add_passwords(). 
#    pbantis     06/23/06 - Encryption in br_rman. 
#    sksantha    07/06/06 - Rman longterm backups 
#    pbantis     11/16/05 - Encryption support. 
#    pfgavin     09/29/05 - increase multi-char byte size to 8. 
#    sjconnol    10/10/05 - Fix deprecation warning
#    hying       09/21/05 - dump_dest for 10.1 db 
#    pfgavin     09/20/05 - fix 4557455 
#    pbantis     09/07/05 - Status code 5 is now an error.
#    sjconnol    08/30/05 - Bug 4440066
#    hying       08/26/05 - fix br_start_db_to_mount for default spfile 
#    hying       08/15/05 - create dump dirs for startup 
#    pbantis     07/28/05 - Handle new rman status codes for 10.2. 
#    vkapur      07/26/05 - startup_db/shutdown_db call issue 
#    hying       07/19/05 - br_create_init_ora_file
#    hying       06/16/05 - trace rman output 
#    hying       05/18/05 - 4283642
#    kramarat    05/04/05 - Turn on use strict 
#    hying       03/21/05 - remove rman_script_file 
#    hying       03/10/05 - 4193722 and 4005320 
#    rrawat      01/10/05 - 3984336
#    hying       01/30/05 - obfuscation of createPasswordFile 
#    gallison    01/28/05 - Fix previous edit 
#    rrawat      01/10/05 - 3984336
#    vmaddali    12/20/04 - 4079135
#    hying       12/08/04 - rac cutover 
#    pbantis     11/04/04 - pbantis_br_omsperl_041104
#    hying       09/14/04 - Fix bug 3879682, set command_id 
#    pbantis     07/19/04 - Run RMAN out of Agent OH. Skip target connect.
#    hying       05/18/04 - createPasswordFile
#    pbantis     05/18/04 - Remove setting of dbstate.pl global variable. 
#    pbantis     05/11/04 - Use dbstate.pl for single-instance 
#                           shutdown/startup. 
#    ngade       04/28/04 - tts support 
#    hying       04/07/04 - Fix set echo on 
#    hying       03/19/04 - set dbid before connect, 3448192 
#    hying       03/16/04 - 3334212, shutdown RAC for db recovery 
#    vkapur      03/04/04 - NT bug 3486880: double quotes around inst list
#    xuliu       02/27/04 - workaround 3440918 
#    hying       12/19/03 - Limit rman output for DB control offline backup
#    hying       12/01/03 - 3259146, reopen rac instances if srvctl stop 
#                           database fails 
#    hying       11/25/03 - Set OH and SID in set_srvctl_env 
#    xuliu       11/12/03 - fix hang of sysread on Solaris - bug 3145925 
#    hying       11/05/03 - Handle srvctl output 
#    pbantis     10/22/03 - Bug 3190159 - allow noarchivelog, online backups 
#                           for recovery files 
#    pbantis     10/19/03 - Bug 3096949 - generate unique tag names 
#    pbantis     10/17/03 - Better handling for open errors 
#    pbantis     10/16/03 - Fix bug 3183372 - DBI connect errors 
#    pbantis     10/14/03 - Tracing of results in run_rman() 
#    pbantis     10/10/03 - db_role case insensitive 
#    pbantis     09/29/03 - Fix bug 3130705 - handle shutdown and startup in 
#                             two different sql sessions
#    xuliu       09/16/03 - fix sysread in run_rman() 
#    xuliu       09/08/03 - fix 3098007 
#    pbantis     09/03/03 - Add more tracing 
#    xuliu       09/02/03 - fix 3122214 
#    xuliu       08/20/03 - catch error from rman process in run_rman() 
#                           && change error code to 8 for rman() as fix for Backup Mgmt
#    hying       08/08/03 - Fix bug 3091039 
#    hying       08/07/03 - Fix return value when sqlplus is not found 
#    hying       07/25/03 - Fix bug 3069729
#    hying       07/10/03 - Fix bug 3014546
#    hying       07/03/03 - Fix bug 2936745
#    hying       06/20/03 - tempfile cleanup
#    hying       06/11/03 - Check rman error code for core dump
#    hying       06/06/03 - Reduce parameters for rman()
#    hying       05/28/03 - 
#    hying       05/27/03 - OSDBA
#    hying       05/20/03 - pass in db_name
#    hying       05/02/03 - Fix bug 2937784, offline backup for rac instance
#    hying       04/25/03 - debug db_connect_string
#    hying       04/24/03 - Use db_connect_string for rman()
#    hying       04/22/03 - Debug use_rcvcat
#    hying       04/20/03 - start_db
#    hying       04/09/03 - Use db_connect_string for rman
#    hying       03/21/03 - Temp workaround for perl core dump
#    hying       02/20/03 - set command id
#    hying       02/10/03 - 
#    hying       01/31/03 - bounce db to state
#    hying       12/13/02 - Fix backup result
#    hying       11/27/02 - Weekly vs. daily
#    hying       11/13/02 - RAC cold backup
#    hying       11/07/02 - Recovery
#    hying       11/04/02 - Use TNS descriptor for VOB DB
#    hying       09/20/02 - Add dry_run
#    hying       09/16/02 - Add recovery catalog
#    hying       08/06/02 - 
#    hying       08/05/02 - Suggested Backup
#    hying       07/25/02 - Error catch
#    hying       07/23/02 - hying_bw4
#    hying       07/23/02 - Creation
# 
use FileHandle;
use IPC::Open2;
use DBI;
use POSIX "sys_wait_h";
use vars qw/ $OS $NT $S $TEMP $CP $MV $PS $DF $DELIMITER/;
if ( !defined $ENV{'EMHA_SKIP_LOCAL_IMPORTS'} )
{
 require "$ENV{EMDROOT}/sysman/admin/scripts/db/db_common.pl";
}
#use strict; # avoid error in setting escape
#use warnings;

use vars qw/ $target_home $target_sid $db_10_or_higher
             $rman_script $daily_backup_script $weekly_backup_script $weekly_backup_day
             $device_type $to_state $db_state $run_state $backup_strategy $rman_command_id $shutdown_option
             $use_rcvcat $rcvcat_username $rcvcat_password $rcvcat_connect_string
             $blackout_target_type $is_cold_backup $dbid $do_target_connect
             $db_username $db_password $db_role $job_name_blen $job_name $curr_date
             $skip_noarchivelog_check $use_agent_env $pid $rman_result $rman_result2
             $target_home $target_sid $target_version $db_10_or_higher
             $ERROR_CODE $SHUTDOWN_IMMEDIATE $STARTUP_OPEN $MAX_OUT_SIZE $UTMOST_MAX_SIZE
             $AGENT_ORACLE_HOME $AGENT_LD_LIBRARY_PATH $AGENT_SHLIB_PATH $AGENT_LIBPATH
             $AGENT_PATH $AGENT_JAVA_HOME $AGENT_ORA_NLS $AGENT_ORA_NLS32 $AGENT_ORA_NLS33
             $db_name $dbName $dbPassword $db_connect_string
	     $auxdb_username $auxdb_password
             $rin $rout $buf $fullBuf $sysret $rmanExit $is_repos_database
             $open_db_option @SQLCMDS $encrypt_passwords $use_duplicate
           /;

my $rac_insts = '';

$ERROR_CODE = 8;

sub br_save_agent_env()
{
  EMD_PERL_DEBUG("rman_o.br_save_agent_env()");
  $AGENT_ORACLE_HOME = $ENV{ORACLE_HOME};
  $AGENT_LD_LIBRARY_PATH = $ENV{LD_LIBRARY_PATH};
  $AGENT_SHLIB_PATH = $ENV{SHLIB_PATH};
  $AGENT_LIBPATH = $ENV{LIBPATH};
  $AGENT_PATH = $ENV{PATH};
  $AGENT_JAVA_HOME = $ENV{JAVA_HOME};
  $AGENT_ORA_NLS = $ENV{ORA_NLS};
  $AGENT_ORA_NLS32 = $ENV{ORA_NLS32};
  $AGENT_ORA_NLS33 = $ENV{ORA_NLS33};
}

sub br_set_agent_env()
{
  EMD_PERL_DEBUG("rman_o.br_set_agent_env()");
  $ENV{ORACLE_HOME} = $AGENT_ORACLE_HOME;
  $ENV{LD_LIBRARY_PATH} = $AGENT_LD_LIBRARY_PATH;
  $ENV{SHLIB_PATH} = $AGENT_SHLIB_PATH;
  $ENV{LIBPATH} = $AGENT_LIBPATH;
  $ENV{PATH} = $AGENT_PATH;
  $ENV{JAVA_HOME} = $AGENT_JAVA_HOME;
  $ENV{ORA_NLS} = $AGENT_ORA_NLS;
  $ENV{ORA_NLS32} = $AGENT_ORA_NLS32;
  $ENV{ORA_NLS33} = $AGENT_ORA_NLS33;

  EMD_PERL_DEBUG("rman_o.br_set_agent_env() ORACLE_HOME = $ENV{ORACLE_HOME}");
  EMD_PERL_DEBUG("rman_o.br_set_agent_env() LD_LIBRARY_PATH = $ENV{LD_LIBRARY_PATH}");
  EMD_PERL_DEBUG("rman_o.br_set_agent_env() SHLIB_PATH = $ENV{SHLIB_PATH}");
  EMD_PERL_DEBUG("rman_o.br_set_agent_env() LIBPATH = $ENV{LIBPATH}");
  EMD_PERL_DEBUG("rman_o.br_set_agent_env() PATH = $ENV{PATH}");
  EMD_PERL_DEBUG("rman_o.br_set_agent_env() JAVA_HOME = $ENV{JAVA_HOME}");
  EMD_PERL_DEBUG("rman_o.br_set_agent_env() ORA_NLS = $ENV{ORA_NLS}");
  EMD_PERL_DEBUG("rman_o.br_set_agent_env() ORA_NLS32 = $ENV{ORA_NLS32}");
  EMD_PERL_DEBUG("rman_o.br_set_agent_env() ORA_NLS33 = $ENV{ORA_NLS33}");
}

sub br_set_target_env()
{
  EMD_PERL_DEBUG("rman_o.br_set_target_env()");
  if ('YES' eq $db_10_or_higher)
  {
    set_env_var($target_home, $target_sid, "TRUE");
  }
  else
  {
    set_env_var($target_home, $target_sid, "FALSE");
  }

  # 10.2 RMAN exit codes
  $ENV{ORA_RMAN_NEW_RETCODE} = "1";
  EMD_PERL_DEBUG("rman_o.br_set_target_env() ORA_RMAN_NEW_RETCODE = $ENV{ORA_RMAN_NEW_RETCODE}");

  # Trace the following items even though they are not handled by this subroutine.
  EMD_PERL_DEBUG("rman_o.br_set_target_env() JAVA_HOME = $ENV{JAVA_HOME}");
}

sub br_set_srvctl_env()
{
  EMD_PERL_DEBUG("rman_o.br_set_srvctl_env()");
  $ENV{ORACLE_HOME} = $target_home;
  $ENV{ORACLE_SID} = $target_sid;
  $ENV{LD_LIBRARY_PATH} = $AGENT_LD_LIBRARY_PATH;
  $ENV{SHLIB_PATH} = $AGENT_SHLIB_PATH;
  $ENV{LIBPATH} = $AGENT_LIBPATH;
  $ENV{PATH} = $AGENT_PATH;
  $ENV{JAVA_HOME} = '';

  EMD_PERL_DEBUG("rman_o.br_set_srvctl_env() LD_LIBRARY_PATH: $ENV{LD_LIBRARY_PATH}");
  EMD_PERL_DEBUG("rman_o.br_set_srvctl_env() SHLIB_PATH: $ENV{SHLIB_PATH}");
  EMD_PERL_DEBUG("rman_o.br_set_srvctl_env() LIBPATH: $ENV{LIBPATH}");
  EMD_PERL_DEBUG("rman_o.br_set_srvctl_env() PATH: $ENV{PATH}");
  EMD_PERL_DEBUG("rman_o.br_set_srvctl_env() JAVA_HOME: $ENV{JAVA_HOME}");

  # Trace the following items even though they are not handled by this subroutine.
  EMD_PERL_DEBUG("rman_o.br_set_srvctl_env() ORACLE_HOME: $ENV{ORACLE_HOME}");
  EMD_PERL_DEBUG("rman_o.br_set_srvctl_env() ORACLE_SID: $ENV{ORACLE_SID}");
  EMD_PERL_DEBUG("rman_o.br_set_srvctl_env() DB_NAME: $db_name");
}

sub br_createPasswordFile
{
  EMD_PERL_DEBUG("rman_o.br_createPasswordFile(): *** START ***");
  my ($obfuscated, $pwdfile) = @_;
  my $cmd;
  if ($pwdfile eq "")
  {
    $pwdfile = "$ENV{ORACLE_HOME}/dbs/orapw$dbName";
  }
  if ($obfuscated =~ /YES/i)
  {
    $cmd = "$ENV{ORACLE_HOME}/bin/orapwd file=$pwdfile password=$dbPassword entries=30 -ob"
  }
  else
  {
    $cmd = "$ENV{ORACLE_HOME}/bin/orapwd file=$pwdfile password=$dbPassword entries=30"
  }

  !system $cmd
    or ((print "Cannot create password file!") && (return -1));
                                                                                          
  EMD_PERL_DEBUG("rman_o.br_createPasswordFile(): Password file $ENV{ORACLE_HOME}/dbs/orapw.$dbName has been created.");
  EMD_PERL_DEBUG("rman_o.br_createPasswordFile(): *** END ***");
  return 0;
}

sub br_get_db_status()
{
  EMD_PERL_DEBUG("rman_o.br_get_db_status()");
  
  my $status_filename = br_get_temp_file_name();
    
  open(SQL_WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$status_filename")
    || ((print "Unable to open the SQLPLUS process in br_get_db_status().\n") && (return "ERROR"));
  EMD_PERL_DEBUG("rman_o.br_get_db_status() db_username: {$db_username}");
  ## escape characters before passing into shell
  $db_password =~ s/(['"])/\\$1/g;
  if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
  {
    print SQL_WRITER "connect / as SYSDBA\n";
  }
  else
  {
    my $db_password_copy = $db_password;
    if ($db_password_copy =~ m/&/)
    {
      my $e = &find_escape_password($db_password_copy);
      print SQL_WRITER "set escape $e\n";
      $db_password_copy =~ s/&/$e&/g;
    }
    print SQL_WRITER "connect ${db_username}/\"${db_password_copy}\" as SYSDBA\n";
  }
  print SQL_WRITER "select status from v\$instance;\n";
  print SQL_WRITER "exit;\n";
  close SQL_WRITER;

  # Command did not get executed
  my $fileSize = getFileSize($status_filename);
  if ($fileSize == 0 || $fileSize == -1)
  {
    unlink($status_filename);
    return 'ERROR';
  }
  
  open (DB_STATUS, "$status_filename")
    || ((print "Unable to open the temporary file in br_get_db_status()\n") && (return "ERROR"));
  while ($_ = <DB_STATUS>)
  {
    if (/OPEN/)
    {
      close DB_STATUS;
      unlink($status_filename);
      return 'OPEN';
    }
    if (/MOUNTED/)
    {
      close DB_STATUS;
      unlink($status_filename);
      return 'MOUNTED';
    }
    if (/STARTED/)
    {
      close DB_STATUS;
      unlink($status_filename);
      return 'STARTED';
    }
  }

  close DB_STATUS;
  unlink($status_filename);
  return 'CLOSED';
}

sub br_bounce_db_to_nomount()
{
  EMD_PERL_DEBUG("rman_o.br_bounce_db_to_nomount()");
  $to_state = "nomount";
  return br_bounce_db();
}

sub br_bounce_db_to_mount()
{
  EMD_PERL_DEBUG("rman_o.br_bounce_db_to_mount()");
  $to_state = "mount";
  return br_bounce_db();
}

sub br_get_temp_file_name()
{
  my ($fh, $filename);

  if($NT)
  {
    my $TEMP;
    #if ($ENV{TEMP} ne "")
    #{
    #    $TEMP = $ENV{TEMP};
    #}
	#elsif ($ENV{TMP} ne "")
	#{
	#    $TEMP = $ENV{TMP};    
	#}
	#else
	#{
	    $TEMP = "\\temp";
	#}    

    &mkDir($TEMP);
	
	my $mytime = time();
	my $sid = ($target_sid ne "")? $target_sid : $ENV{ORACLE_SID};
	
    $filename = "$TEMP\\rman${sid}.$mytime";
  }
  else
  {
    my $dir = tempdir(CLEANUP => 1);
    ($fh, $filename) = tempfile( DIR => $dir );  
  }  
  
  $filename;
}

#
# Xun: add one argument $controlfile to the method.
# If $controlfile is specified, the method will update the spfile such that the
# instance will use the new control to mount
#

sub br_bounce_db
{
  my $controlfile = $_[0];
  my $presql = $_[1];
  
  EMD_PERL_DEBUG("rman_o.br_bounce_db(@_)");

  EMD_PERL_DEBUG("rman_o.br_bounce_db() db_username: {$db_username}");
  EMD_PERL_DEBUG("rman_o.br_bounce_db() CAT db_name: {$db_name}");
  EMD_PERL_DEBUG("rman_o.br_bounce_db() to_state: {$to_state}");
  my ($tns, $instanceList, @preShutdownSql, @postStartupSql, $sqlRunState) = "";
  my ($restoreDBState, $bounceAfterPostSQL, $isDB10i) = 0;

  if ($controlfile ne "")
  {
    push(@preShutdownSql, "alter system set control_files = $controlfile scope=SPFILE");        
  }
  if ($presql ne "")
  {
    push(@preShutdownSql, $presql);        
    #EMD_PERL_DEBUG("rman_o.br_bounce_db: adding presql: $presql");
  }
  if ($db_10_or_higher =~ /YES/i)
  {
    $isDB10i = 1;
  }
  $restoreDBState = 1;
  
  my $restart_result = 0;
  my $shutdownOption = $SHUTDOWN_IMMEDIATE;
  if (defined($shutdown_option) && $shutdown_option ne "")
  {
    $shutdownOption = $shutdown_option;
  }

  
  # Shutdown the database, then start it up to the state specified.
  if ($blackout_target_type eq "oracle_database")
  {
     $restart_result = &restart_db($blackout_target_type, $target_home, $target_sid, $db_name, 
      $db_username, $db_password, $db_role, $tns, $instanceList, $isDB10i, 
      $shutdownOption, \@preShutdownSql, $to_state, \@postStartupSql, $sqlRunState, 
      $restoreDBState, $bounceAfterPostSQL);
  }
  else
  {
    my $bounce_errCode = 0;
    my $bounce_initState = "";
    if ($restoreDBState)
    {
      # get current state, for later use in case of failure
      # The database should be returned to this state in case of failure during
      # shutdown OR startup

      # use oracle_database targetType for both single-inst and RAC to get status 
      # for local instance in case of RAC
      ($bounce_errCode, $bounce_initState) = &get_db_state("oracle_database", $target_home, $target_sid, $db_name, $db_username, $db_password, $db_role, $tns, $instanceList, $isDB10i);
    }
    EMD_PERL_DEBUG("rman_o:bounce_db get_db_state errCode returned=$bounce_errCode");
    EMD_PERL_DEBUG("rman_o:bounce_db get_db_state initState returned=$bounce_initState");

    # Bug 6697519 - unset JAVA_HOME environment variable for rac database
    delete $ENV{JAVA_HOME};
    EMD_PERL_DEBUG("rman_o.br_bounce_db() (deleted) JAVA_HOME = $ENV{JAVA_HOME}");

    # Dereference preShutdownSql and postStartupSql variables.
    $restart_result = &shutdown_db($blackout_target_type, $target_home, $target_sid, $db_name, $db_username, $db_password, $db_role, $tns, $instanceList, $isDB10i, $shutdownOption, $restoreDBState, $bounce_initState, @preShutdownSql);

    EMD_PERL_DEBUG("rman_o:bounce_db after shutdown_db restoreDBState =$restoreDBState");
    EMD_PERL_DEBUG("rman_o:bounce_db after shutdown_db bounce_initState =$bounce_initState");
    EMD_PERL_DEBUG("rman_o:bounce_db after shutdown_db to_state =$to_state");
    # only do startup if shutdown did not receive errors
    if ($restart_result == $main::DBSTATE_SUCCESS_CODE)
    {
      $restart_result = &startup_db("oracle_database", $target_home, $target_sid, $db_name, $db_username, $db_password, $db_role, $tns, $instanceList, $isDB10i, $to_state, $sqlRunState, $restoreDBState, $bounce_initState, $bounceAfterPostSQL, @postStartupSql);
    }

  }
  return($restart_result);    

}

# Change database from mounted to open state
sub br_open_db
{
  EMD_PERL_DEBUG("rman_o.br_open_db()");
  EMD_PERL_DEBUG("rman_o.br_open_db() db_name: {$db_name}");
  my $open_option = $_[0];

  my ($tns, $instanceList, @preShutdownSql, @postStartupSql, $sqlRunState, $initState_p) = "";
  my ($restoreDBState, $bounceAfterPostSQL, $isDB10i) = 0;
  if ($db_10_or_higher =~ /YES/i)
  {
    $isDB10i = 1;
  }
  # $restoreDBState = 1;

  # Start up the database to OPEN.
  if ($open_option eq "")
  {
    $open_option = $STARTUP_OPEN;
  }

  my $startup_result = 0;
  if ($open_option ne "open resetlogs")
  {
    $startup_result = startup_db("oracle_database", $target_home, $target_sid, $db_name, 
      $db_username, $db_password, $db_role, $tns, $instanceList, $isDB10i, 
      $open_option, $sqlRunState, $restoreDBState, $initState_p, $bounceAfterPostSQL, @postStartupSql);
  }
  else
  {
    $rman_script = "alter database open resetlogs;\n";
    $startup_result = br_run_rman();
    EMD_PERL_DEBUG("rman_o.br_open_db() br_run_rman returns : $startup_result");
    if ($startup_result != 0)
    {
      br_fix_online_logs();
      $startup_result = br_run_rman();
    }
  }

  if ($blackout_target_type eq "rac_database")
  {
    if ($open_option eq "open resetlogs")
    {
      $open_option = $STARTUP_OPEN;
 
      # Bug 8355600 - shut down the db first before start db after open db with resetlog option
      my $shutdownOption = $SHUTDOWN_IMMEDIATE;
      EMD_PERL_DEBUG("rman_o.br_open_db() Shut down db immediately first after open db with resetlogs option if the db is a rac db");
      $restart_result = &shutdown_db($blackout_target_type, $target_home, $target_sid, $db_name, $db_username, $db_password, $db_role, $tns, $instanceList, $isDB10i, $shutdownOption, $restoreDBState, $bounce_initState, @preShutdownSql);

      EMD_PERL_DEBUG("rman_o.br_open_db() shutdown_db return result : $restart_result");
    }

    # Bug 6697519 - unset JAVA_HOME environment variable for rac database
    delete $ENV{JAVA_HOME};
    EMD_PERL_DEBUG("rman_o.br_open_db() (deleted) JAVA_HOME = $ENV{JAVA_HOME}");

    $startup_result = startup_db($blackout_target_type, $target_home, $target_sid, $db_name,
      $db_username, $db_password, $db_role, $tns, $instanceList, $isDB10i,
      $open_option, $sqlRunState, $restoreDBState, $initState_p, $bounceAfterPostSQL, @postStartupSql);
  }
  
  return $startup_result;
}


# Xun:
# return -1 (255) if failed
# return 1 if database is in nomount state
# return 0 if succeed
#
sub br_start_db_to_mount()
{
  my($init_ora_dir, $spfile_name) = @_;
  EMD_PERL_DEBUG("rman_o.br_start_db_to_mount() ORACLE_HOME: $ENV{ORACLE_HOME}");
  EMD_PERL_DEBUG("rman_o.br_start_db_to_mount() ORACLE_SID: $ENV{ORACLE_SID}");
  EMD_PERL_DEBUG("rman_o.br_start_db_to_mount() init_ora_dir: $init_ora_dir");
  EMD_PERL_DEBUG("rman_o.br_start_db_to_mount() spfile_name: $spfile_name");

  my $retry = 1;
  my ($pfile, $adump, $bdump, $udump, $orig_spfile) = "";
  my $reset_fra = 0;
  my $open_result = -1;
  while ($retry)
  {
    $retry = 0;
    my $startdb_filename = br_get_temp_file_name();  
  
    open(SQL_WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$startdb_filename")
      || ((print "Unable to open the SQLPLUS process in br_start_db_to_mount().\n") && (return -1));
    ## escape characters before passing into shell
    $db_password =~ s/(['"])/\\$1/g;
    if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
    {
      print SQL_WRITER "connect / as SYSDBA\n";
    }
    else
    {
      my $db_password_copy = $db_password;
      if ($db_password_copy =~ m/&/)
      {
        my $e = &find_escape_password($db_password_copy);
        print SQL_WRITER "set escape $e\n";
        $db_password_copy =~ s/&/$e&/g;
        print SQL_WRITER "connect ${db_username}/\"${db_password_copy}\" as SYSDBA\n";
      }
      else
      {
        print SQL_WRITER "connect ${db_username}/${db_password} as SYSDBA\n";
      }
    }
    if ($pfile eq "")
    {
      print SQL_WRITER "startup mount;\n";
    }
    else
    {
      print SQL_WRITER "startup pfile=\'$pfile\' mount;\n";
    }
  
    #Xun: we check which mode, mounted or started, is the db in after 'startup mount'
    print SQL_WRITER "select status from v\$instance;\n";
    print SQL_WRITER "exit;\n";
    close SQL_WRITER;

    # Command did not get executed
    my $fileSize = getFileSize($startdb_filename);
    if ($fileSize == 0 || $fileSize == -1)
    {
      unlink($startdb_filename);
      print "Unable to start the database mounted.\n";
      return -1;
    }

    open (STARTDB, "$startdb_filename")
      || ((print "Unable to open the temporary file in br_start_db_to_mount()\n") && (return -1));

    if ($pfile eq "")
    {
      $pfile = $init_ora_dir.'init'.$target_sid.'.tmp';
      if (open(PFILE, ">$pfile"))
      {
        print PFILE "SPFILE=\'$spfile_name\'\n";
        close PFILE;
      }
    }
    while ($_ = <STARTDB>)
    {
      print $_;
      if (/ORA-09925/)
      {
        # fix for bug 7524930; db versions 10.2.0.4 and greater hang on ORA-09925, so
        # go ahead and report the situation to the user and let them deal with it
        if (defined($target_version))
        {
          my @version_tokens = split(/\./, $target_version);
          my $size = scalar @version_tokens;
          if (($version_tokens[0] > 10) ||
              (($version_tokens[0] == 10) && ($size > 3) && ($version_tokens[1] == 2) && ($version_tokens[3] >= 4)))
          {
            # special return code so we can tell the user what's going on
            return -2;
          }
        }

        print "\nOverriding init parameter audit_file_dest.\n";
        open(PFILE, ">>$pfile") || ((print "Unable to open $pfile in br_start_db_to_mount()\n") && (return -1));
        if($NT)
        {
          print PFILE "\*\.audit_file_dest=\'$target_home\\admin\\$target_sid\\adump\'\n";
          $adump = "$target_home\\admin\\$target_sid\\adump";
        }
        else
        {
          print PFILE "\*\.audit_file_dest=\'$target_home\/admin\/$target_sid\/adump\'\n";
          $adump = "$target_home\/admin\/$target_sid\/adump";
        }
        EMD_PERL_DEBUG("rman_o.br_start_db_to_mount() Overriding init parameter audit_file_dest to: $adump");
        close PFILE;
        create_dump_dir($adump) || ((print "Unable to create audit_file_dest at $adump.\n") && (return -1));
        $retry = 1;
      }
      elsif (/ORA-07446/)
      {
        print "\nOverriding init parameter user_dump_dest.\n";
        open(PFILE, ">>$pfile") || ((print "Unable to open $pfile in br_start_db_to_mount()\n") && (return -1));
        if($NT)
        {
          print PFILE "\*\.user_dump_dest=\'$target_home\\admin\\$target_sid\\udump\'\n";
          $udump = "$target_home\\admin\\$target_sid\\udump";
        }
        else
        {
          print PFILE "\*\.user_dump_dest=\'$target_home\/admin\/$target_sid\/udump\'\n";
          $udump = "$target_home\/admin\/$target_sid\/udump";
        }
        EMD_PERL_DEBUG("rman_o.br_start_db_to_mount() Overriding init parameter user_dump_dest to: $udump");
        create_dump_dir($udump) || ((print "Unable to create user_dump_dest at $udump.\n") && (return -1));

        print "\nOverriding init parameter background_dump_dest.\n";
        if($NT)
        {
          print PFILE "\*\.background_dump_dest=\'$target_home\\admin\\$target_sid\\bdump\'\n";
          $bdump = "$target_home\\admin\\$target_sid\\bdump";
        }
        else
        {
          print PFILE "\*\.background_dump_dest=\'$target_home\/admin\/$target_sid\/bdump\'\n";
          $bdump = "$target_home\/admin\/$target_sid\/bdump";
        }
        EMD_PERL_DEBUG("rman_o.br_start_db_to_mount() Overriding init parameter background_dump_dest to: $bdump");
        close PFILE;
        create_dump_dir($bdump) || ((print "Unable to create background_dump_dest at $bdump.\n") && (return -1));
        $retry = 1;
      }
      elsif (/ORA-01261/)
      {
        open(PFILE, ">>$pfile") || ((print "Unable to open $pfile in br_start_db_to_mount()\n") && (return -1));
        print PFILE "\*\.db_recovery_file_dest=\'\'\n";
        close PFILE;
        $reset_fra = 1;
        $retry = 1;
      }

      if (/STARTED/)
      {
        $open_result = 1;
      }
      elsif (/MOUNTED/)
      {
        $open_result = 0;
      }
    }

    close STARTDB;
    unlink($startdb_filename);
  }

  ## Database instance has been started, but some init parameters have been modified.
  if (($open_result == 0 || $open_result == 1)
      && ($reset_fra == 1 || $adump ne "" || $bdump ne "" || $udump ne ""))
  {
    my $alter_db_filename = br_get_temp_file_name();  
  
    open(SQL_WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$alter_db_filename")
      || ((print "Unable to open the SQLPLUS process in br_start_db_to_mount().\n") && (return -1));
    ## escape characters before passing into shell
    $db_password =~ s/(['"])/\\$1/g;
    if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
    {
      print SQL_WRITER "connect / as SYSDBA\n";
    }
    else
    {
      my $db_password_copy = $db_password;
      if ($db_password_copy =~ m/&/)
      {
        my $e = &find_escape_password($db_password_copy);
        print SQL_WRITER "set escape $e\n";
        $db_password_copy =~ s/&/$e&/g;
      }
      print SQL_WRITER "connect ${db_username}/\"${db_password_copy}\" as SYSDBA\n";
    }
  
    #Xun: we check which mode, mounted or started, is the db in after 'startup mount'
    if ($reset_fra)
    {
      print SQL_WRITER "alter system set db_recovery_file_dest=\'\' scope=spfile;\n";
      print "\nReset db_recovery_file_dest in SPFILE due to inaccessible db_recovery_file_dest.\n";
    }
    if ($adump ne "")
    {
      print SQL_WRITER "alter system set audit_file_dest=\'$adump\' scope=spfile;\n";
      print "\nSet audit_file_dest to $adump in SPFILE\n";
    }
    if ($bdump ne "")
    {
      print SQL_WRITER "alter system set background_dump_dest=\'$bdump\' scope=spfile;\n";
      print "\nSet background_dump_dest to $bdump in SPFILE\n";
    }
    if ($udump ne "")
    {
      print SQL_WRITER "alter system set user_dump_dest=\'$udump\' scope=spfile;\n";
      print "\nSet user_dump_dest to $udump in SPFILE\n";
    }
    print SQL_WRITER "shutdown immediate;\n";
    print SQL_WRITER "startup mount;\n";

    print SQL_WRITER "exit;\n";
    close SQL_WRITER;
  }
  unlink($pfile);
  return $open_result;
}

# returns 1 on success
# returns 0 on failure
sub create_dump_dir()
{
  my ($dump_dir) = @_;
  my $dir = $target_home.'/admin';
  if (! -e $dir)
  {
    print "Creating directory $dir\n";
    if (!mkdir($dir, 0755))
    {
      return 0;
    }
  }
  if (! -d $dir)
  {
    return 0;
  }
  $dir = $dir.'/'.$target_sid;
  if (! -e $dir)
  {
    print "Creating directory $dir\n";
    if (!mkdir($dir, 0755))
    {
      return 0;
    }
  }
  if (! -d $dir)
  {
    return 0;
  }

  if (! -e $dump_dir)
  {
    print "Creating directory $dump_dir\n";
    return mkdir($dump_dir, 0755);
  }
  if (! -d $dump_dir)
  {
    print "$dump_dir is not a directory. Unable to create $dump_dir as a directory.\n";
    return 0;
  }
  return 1;
}

sub br_validate_syntax()
{
  EMD_PERL_DEBUG("rman_o.br_validate_syntax()");
  
  my $rman_filename = br_get_temp_file_name();    
  
  EMD_PERL_DEBUG("rman_o.br_validate_syntax() rman_script:");
  EMD_PERL_DEBUG("$rman_script");
  open(RMAN_WRITER, "|$ENV{ORACLE_HOME}/bin/rman log=$rman_filename")
    || die "Can not open pipe for RMAN at $ENV{ORACLE_HOME}";
  print RMAN_WRITER $rman_script;
  print RMAN_WRITER "exit;\n";
  close RMAN_WRITER;

  open (OUT, "$rman_filename") || die "Unable to open tmp file\n";
  my $result = 1;
  while ($_ = <OUT>)
  {
    EMD_PERL_DEBUG("$_");
    if (/RMAN-06171/)
    {
      $result = 0;
    }
    elsif (/RMAN-01005/)
    {
      $result = -1;
    }
  }

  close OUT;
  unlink($rman_filename);
  
  exit($result);
}


# Return codes:
#   0 'success'
#   -1 'failure'
# If global variable target_version is defined and it's 10.2 or higher, then
# other possible return codes are:
#   4 'success with warnings'
#   5 'completed with errors' (failure)
#
# Xun: fix 3145925
# In order to fix the hang of sysread, we use Singal Trap, Non-Blocking File Handle, & select(2).
# 
# However, these features are not supported on NT. So the fix is just done for UNIX. 
#
sub br_run_rman()
{
  EMD_PERL_DEBUG("rman_o.br_run_rman()");
  if (defined($use_agent_env) && $use_agent_env eq "YES")
  {
    br_set_agent_env();
  }
  else
  {
    br_set_target_env();
  }

  if(defined($ENV{NLS_LANG})){
  EMD_PERL_DEBUG("rman_o.br_run_rman() env NLS_LANG: $ENV{NLS_LANG}");
  }
  
  $? = 0;

  # Remove password for encryption before tracing.
  my $rman_script_copy = &br_remove_passwords($rman_script);
  EMD_PERL_DEBUG("rman_o.br_run_rman() rman_script:\n$rman_script_copy");
  EMD_PERL_DEBUG("rman_o.br_run_rman() db_username: {$db_username}");
  EMD_PERL_DEBUG("rman_o.br_run_rman() db_connect_string: {$db_connect_string}");
  EMD_PERL_DEBUG("rman_o.br_run_rman() use_rcvcat: {$use_rcvcat}");
  EMD_PERL_DEBUG("rman_o.br_run_rman() rcvcat_username: {$rcvcat_username}");
  EMD_PERL_DEBUG("rman_o.br_run_rman() rcvcat_connect_string: {$rcvcat_connect_string}");

  local $SIG{PIPE};
  local $SIG{CHLD};
  if (!$NT)
  {
      # Ignore PIPE signal which might be invoked by "print RMAN_WRITER .. "
      # If we don't catch it the signal will terminate the script
      $SIG{PIPE} = sub { 
         EMD_PERL_DEBUG("PIPE signal received and ignored"); 
      };
      
      $rmanExit = 0;
        
      # Reaper to collect rman exit status
      $SIG{CHLD} = sub { 
        if (waitpid($pid, WNOHANG))
        {           
           $rman_result = ($? >> 8);
           $rmanExit = 1;
           EMD_PERL_DEBUG("rman_o.br_reaper: rman exited with $rman_result\n");
        }
      };  
  }

  if (! -e "$ENV{ORACLE_HOME}/bin/rman" && ! -e "$ENV{ORACLE_HOME}/bin/rman.exe")
  {
      print "No rman found in $ENV{ORACLE_HOME}/bin\n";
      return -1;
  }
  
  if ($use_rcvcat eq "YES")
  {
    EMD_PERL_DEBUG("rman_o.br_run_rman() Recovery Catalog TNS Descriptor: $rcvcat_connect_string");
    $pid = open2(\*RDRFH, \*RMAN_WRITER, "$ENV{ORACLE_HOME}/bin/rman")
      || ((print "Unable to open the RMAN process in br_run_rman().\n") && (return -1));
  }
  else
  {
    $pid = open2(\*RDRFH, \*RMAN_WRITER, "$ENV{ORACLE_HOME}/bin/rman nocatalog")
      || ((print "Unable to open the RMAN process in br_run_rman().\n") && (return -1));
  }
  
  # Turn on autoflush for pipe output
  my $old_fh = select(RDRFH);
  $| = 1;
  select($old_fh);

  if (!$NT)
  {  
      # set RDRFH non-blocking
      my $flags = fcntl(RDRFH, &F_GETFL, 0)
          or die "Couldn't get flags for RDRFH : $!\n";
      fcntl(RDRFH, &F_SETFL, $flags | &O_NONBLOCK)
          or die "Couldn't set flags for RDRFH: $!\n";
  }  

  # Turn on autoflush for standard output
  $old_fh = select(STDOUT);
  $| = 1;
  select($old_fh);

  if ($dbid > 0)
  {
    print RMAN_WRITER "set echo on;\n";
    print RMAN_WRITER "set dbid $dbid;\n";
    print RMAN_WRITER "set echo off;\n";
  }

  # Connection for duplicate
  if (defined($use_duplicate) && ($use_duplicate eq "YES"))
  {
    print RMAN_WRITER "connect target ${db_username}/${db_password}". "@". "${db_connect_string} ;\n";
    if (defined($auxdb_username) && defined($auxdb_password) )
    {
      EMD_PERL_DEBUG("Auxiliary database username: $auxdb_username");
      print RMAN_WRITER "connect auxiliary ${auxdb_username}/${auxdb_password} ;\n";
    }
    else
    {
      print RMAN_WRITER "connect auxiliary /;\n";
    }    
  }
  # Target connection
  elsif (!defined($do_target_connect) || (defined($do_target_connect) && $do_target_connect eq "YES"))
  {
    ## escape characters before passing into shell
    $db_password =~ s/(['"])/\\$1/g;
    if (!($db_role =~ /SYSDBA/i))
    {
      print RMAN_WRITER "connect target /;\n";
    }
    else
    {
      print RMAN_WRITER "connect target ${db_username}/${db_password}\n";
    }
  }
  
  # Recovery catalog connection
  if ($use_rcvcat eq "YES")
  {
    print RMAN_WRITER "connect rcvcat $rcvcat_username/$rcvcat_password"."@"."$rcvcat_connect_string\n";
  }
  print RMAN_WRITER "set echo on;\n";
  if (($db_10_or_higher eq "YES") && ($rman_command_id ne ""))
  {
    EMD_PERL_DEBUG("rman_o.br_run_rman() rman_command_id: $rman_command_id");
    print RMAN_WRITER "set command id to '$rman_command_id';\n";
  }
  my $rman_script_file = "";
  if (length($rman_script) > 2048)
  {
    $rman_script_file = br_get_temp_file_name();  
    $rman_script_file = $rman_script_file.'.rman';
    if (!open(CMD_FILE, "> $rman_script_file"))
    {
      print RMAN_WRITER $rman_script;
    }
    else
    {
      print CMD_FILE $rman_script;
      close CMD_FILE;
      print RMAN_WRITER "\@$rman_script_file\n";
    }
  }
  else
  {
    print RMAN_WRITER $rman_script;
  }
  
  print RMAN_WRITER "exit;\n";
  close RMAN_WRITER;
  
  
  $MAX_OUT_SIZE = 1024; #1K
  $main::UTMOST_OUT_SIZE = 2048; #2k

  my $cur_out_size = 0;
  my $print_out_size = 0;
  my $dbconsole_coldbackup = ($ENV{CONSOLE_CFG} eq "dbconsole" || $is_repos_database eq "TRUE") && ($is_cold_backup eq "YES");
  
  if (!$NT)
  {
      # Construct the data structure for select call
      vec($rin, fileno(RDRFH), 1) = 1;
    
      my $bufSize = 100;
      $fullBuf ="";
      
      while (1)
      {
          # wait for reading event on RDRFH, or timeout after 5 seconds
          $a = select($rout=$rin, undef, undef, 5);
        
          if ($a > 0 && vec($rout,fileno(RDRFH),1))
          {
              # There are something in RDRFH for read
            
              $sysret = sysread RDRFH, $buf, $bufSize;
            
              if (defined($sysret))
              {
                  if ($sysret == 0)
                  {
                      # RDRFH is closed by rman
                      last;
                  }
                  else
                  {
                      $fullBuf .= $buf;
                      
                      $cur_out_size += length($buf);
                      if (!$dbconsole_coldbackup || $cur_out_size <= $MAX_OUT_SIZE)
                      {
                          print "$buf";
                          $print_out_size += length($buf);
                      }
                  }
              }
          }
          else
          {
             # select() times out or detects an error
             if ($rmanExit)
             {
                # rman has exited as detected by the reaper
              
                # we do a final non-blocking reading in case there are something 
                # in the pipe left by rman
                while ($sysret = sysread RDRFH, $buf, $bufSize)
                {
                    $fullBuf .= $buf;

                    $cur_out_size += length($buf);
                    if (!$dbconsole_coldbackup || $cur_out_size <= $MAX_OUT_SIZE)
                    {
                        print "$buf";
                        $print_out_size += length($buf);
                    }
                }    
    
                last;
             } 
          } 
      }
  }
  else
  {
    # NT case
    $fullBuf ="";
    do {
        $sysret = sysread RDRFH, $buf, 100;

        if (defined($sysret))
        {
            $cur_out_size += length($buf);
            if (!$dbconsole_coldbackup || $cur_out_size <= $MAX_OUT_SIZE)
            {
                print "$buf";
                $print_out_size += length($buf);
            }
    
            $fullBuf=$fullBuf.$buf;
        }    
        else
        {
            print "An error ocurred when reading from rman: $? $!\n";
            $rman_result = -1;
        }
    } while (defined($sysret) && $sysret != 0);
  
  }  
  
  if ($dbconsole_coldbackup && $cur_out_size > $MAX_OUT_SIZE)
  {
      # Print the last $MAX_OUT_SIZE of data
      if ($cur_out_size > $main::UTMOST_OUT_SIZE)
      {
          EMD_PERL_DEBUG("rman_o.br_run_rman() rman output exceeded, printing first and last $MAX_OUT_SIZE.");
          # We'll print "..." if output is ommited
          print "...\n...\n...\n";
          $buf = substr($fullBuf, $cur_out_size - $MAX_OUT_SIZE, $MAX_OUT_SIZE);
      }
      # Print the remainder of the data
      else
      {
          $buf = substr($fullBuf, $print_out_size, $cur_out_size);
      }
      print "$buf";
  }  
    
  # xun: close RDRFH after reading is done
  close RDRFH;

  
  if ($NT)
  {
     # On NT, there is no reaper to collect the exit status of rman. We'll do it here.
     
     # xun: we need to do a wait in order to get the correct $? from the child rman process
     my $wpid = waitpid $pid, 0;
     
     if ($wpid != -1)
     {
        $rman_result = ($? >> 8);
     }   

     # if waitpid returns -1 (in which case it's a bug for perl), 
     # we'll have to parse the rman output to determine whether 
     # the operation is successful or not

     EMD_PERL_DEBUG("rman_o.br_run_rman() waitpid=$wpid, rman_result1=$rman_result");
  }

  # Determine if the target version is 10.2 or higher
  my $db_102_or_higher = 0;
  if (defined($target_version))
  {
    EMD_PERL_DEBUG("rman_o.br_run_rman() target_version=$target_version");
    my @version_tokens = split(/\./, $target_version);
    # Look at the major and minor version numbers.
    if (($version_tokens[0] > 10) ||
        (($version_tokens[0] == 10) && ($version_tokens[1] >= 2)))
    {
      $db_102_or_higher = 1;
    }
  }
  EMD_PERL_DEBUG("rman_o.br_run_rman() db_102_or_higher=$db_102_or_higher");

  # RMAN return codes
  # Pre-10.2 return codes: 0 'success', 1 'failure', 3 'fatal error' (RMAN-00600)
  # 10.2 return codes: 4 'success with warnings', 5 'completed with errors',
  # in addition to the pre-10.2 return codes
  if (defined($rman_result) && ($db_102_or_higher))
  {
    if (($rman_result != 0) && ($rman_result != 4) && ($rman_result != 5))
    {
      # Failure
      $rman_result2 = -1;
    }
    else
    {
      $rman_result2 = $rman_result;
    }
  }
  else
  {
    if (defined($rman_result) && ($rman_result != 0))
    {
      if ($db_102_or_higher)
      {
        if (($rman_result != 4) && ($rman_result != 5))
        {
          # Failure
          $rman_result2 = -1;
        }
        else
        {
          $rman_result2 = $rman_result;
        }
      }
      else
      {
        # Failure
        $rman_result2 = -1;
      }
    }
    else
    {
      ## Catch rman error (RMAN-00569) and warning (RMAN-) upon error stack
      if (index($fullBuf, "RMAN-") == -1)
      {
        # Success
        $rman_result2 = 0;
      }
      elsif (index($fullBuf, "RMAN-00569") == -1)
      {
        # Success with warnings
        $rman_result2 = 4;
      }
      else
      {
        # Completed with errors
        $rman_result2 = 5;
      }
    }
  }
  
  EMD_PERL_DEBUG("rman_o.br_run_rman() rman_result2=$rman_result2");
  if ($rman_result2 == 0)
  {
    EMD_PERL_DEBUG("rman_o.br_run_rman() rman output: $fullBuf");
  }
  else
  {
    EMD_PERL_ERROR("rman_o.br_run_rman() rman output: $fullBuf");
  }

  if ($rman_script_file ne "")
  {
    unlink $rman_script_file;  
  }
  return($rman_result2);
}

sub br_rman()
{
  ($use_rcvcat, $db_connect_string) = @_;
  EMD_PERL_DEBUG("rman_o.br_rman()");

  # Edit the RMAN script for password encryption.
  &br_add_passwords();
  
  my $result = br_run_rman();

  #Xun: the exit value should be in [0 - 255] range. -1 is actually 255. 
  #I'm changing the exit value to 8 as error code if -1 is returned
  if (($result < 0) || ($result == 5))
  {
    $result = $ERROR_CODE;
  }  
  EMD_PERL_DEBUG("rman_o.br_rman() result=$result");
  $result;
}

sub br_backup()
{
  EMD_PERL_DEBUG("rman_o.br_backup()");
  # Edit the RMAN script so that the tag name is unique.
  if ($rman_script =~ /'%TAG'/)
  {
    my $tag_name = "TAG";
    if (defined($job_name) && $job_name ne "" && 
      defined($curr_date) && $curr_date ne "")
    {
      my $job_name_length = length($job_name);
      my $tag_name_length = $job_name_length + length($curr_date) + 1;
      # Determine if the job name contains multi-byte characters.
      EMD_PERL_DEBUG("rman_o.br_backup(): job_name_length: $job_name_length");

      if (defined($job_name_blen) && ($job_name_blen > $job_name_length))
        {
        # Ok, we have a multi-byte job_name, chop it down to 4 characters. This will make
        # the maximum byte size of 12.
        EMD_PERL_DEBUG("rman_o.br_backup(): job_name_blen: $job_name_blen ");
        if ($job_name_length > 8)
          {
          my $new_job_name = trunc_byte($job_name, 8);
          $tag_name = $new_job_name.'_'.$curr_date;
          EMD_PERL_DEBUG("rman_o.br_backup(): short tag_name: $tag_name ");
          }
        else
          {
          $tag_name = $job_name.'_'.$curr_date;
          EMD_PERL_DEBUG("rman_o.br_backup(): mb tag_name: $tag_name ");
          }
        }
      else
        {
        if ($tag_name_length > 31)
          {
          my $chop_length = $tag_name_length - 31;
          $job_name = substr($job_name, 0, $job_name_length - $chop_length);
          }
        $tag_name = $job_name.'_'.$curr_date;
        }
    }
    $rman_command_id = $tag_name;
    EMD_PERL_DEBUG("rman_o.br_backup() tag_name=$tag_name");
    $rman_script =~ s/'%TAG'/'$tag_name'/g;
  }
   # Edit the RMAN script so that the restore point name is unique.
   # Rman longterm backup -start
  if ($rman_script =~ /'%RESTORE_POINT'/)
  {
    my $rp_name = "RESTORE_POINT";
    if (defined($job_name) && $job_name ne "" && 
      defined($curr_date) && $curr_date ne "")
    {
      my $job_name_length = length($job_name);
      my $rp_name_length = $job_name_length + length($curr_date) + 1;
      # Determine if the job name contains multi-byte characters.
      EMD_PERL_DEBUG("rman_o.br_backup(): job_name_length: $job_name_length");

      if (defined($job_name_blen) && ($job_name_blen > $job_name_length))
        {
        # Ok, we have a multi-byte job_name, chop it down to 4 characters. This will make
        # the maximum byte size of 12.
        EMD_PERL_DEBUG("rman_o.br_backup(): job_name_blen: $job_name_blen ");
        if ($job_name_length > 8)
          {
          my $new_job_name = trunc_byte($job_name, 8);
          $rp_name = $new_job_name.'_'.$curr_date;
          EMD_PERL_DEBUG("rman_o.br_backup(): short rp_name: $rp_name ");
          }
        else
          {
          $rp_name = $job_name.'_'.$curr_date;
          EMD_PERL_DEBUG("rman_o.br_backup(): mb rp_name: $rp_name ");
          }
        }
      else
        {
        if ($rp_name_length > 31)
          {
          my $chop_length = $rp_name_length - 31;
          $job_name = substr($job_name, 0, $job_name_length - $chop_length);
          }
        $rp_name = $job_name.'_'.$curr_date;
        }
    }
    $rman_command_id = $rp_name;
    EMD_PERL_DEBUG("rman_o.br_backup() rp_name=$rp_name");
    $rman_script =~ s/'%RESTORE_POINT'/'$rp_name'/g;
  }	
  # Rman longterm backup -end
  # Edit the RMAN script for password encryption.
  &br_add_passwords();

  EMD_PERL_DEBUG("rman_o.br_backup() Database is $db_state");
  my $result = 0;
  ## Online (hot) backup.
  if ($is_cold_backup eq "NO")
  {
    EMD_PERL_DEBUG("rman_o.br_backup() online backup");
    $result = br_run_rman();
    return $result;
  }
  ## Offline (cold) backup.
  elsif ($db_state eq "MOUNTED" && $blackout_target_type eq "oracle_database") 
  {
    EMD_PERL_DEBUG("rman_o.br_backup() offline backup, database is mounted");
    $result = br_run_rman();
    return $result;
  }
  else 
  {
    EMD_PERL_DEBUG("rman_o.br_backup() offline backup");

    my(@broker_sql);
    ## Determine if DG broker is running
    if(isDGBrokerConfig($db_username, $db_password, "", $target_home, $target_sid)){
      @broker_sql = getBrokerRestartSQL($db_username, $db_password, "", $target_home, $target_sid);
    }
      
    $to_state = "mount";
    $result = br_bounce_db("", $broker_sql[2]);
    EMD_PERL_DEBUG("rman_o.br_backup() br_bounce_db returned: $result");
    if ($result == -1)
    {
      print "Unable to perform the backup because the database could not be shut down and restarted.\n";
      EMD_PERL_ERROR("rman_o.br_backup() Unable to perform the backup because the database could not be shut down and restarted.");
      return $result;
    }

    my $rman_result = br_run_rman();
    EMD_PERL_DEBUG("rman_o.br_backup() br_run_rman returned: $rman_result");

    if(@broker_sql){
      EMD_PERL_DEBUG("rman_o.br_backup() running DG broker restart sql");
      @SQLCMDS = ($broker_sql[3]);
      my $connStr = &constructConnectStr($db_username, $db_password, "", $db_role);
      $result = &executeSQLPlus($connStr);
      ## Broker will open db if necessary; no need to continue if successful
      if(!$result){
        EMD_PERL_DEBUG("rman_o.br_backup() DG broker restart succeeded; returning");
        return $rman_result;
      }
      EMD_PERL_DEBUG("rman_o.br_backup() DG broker restart failed; continuing");
    }
      
    if ($rman_result == -1)
    {
      # change database from mounted to open state
      br_open_db();
      return $rman_result;
    }

    # change database from mounted to open state
    $result = br_open_db();
    EMD_PERL_DEBUG("rman_o.br_backup() br_open_db returned: $result");
    if ($result == -1)
    {
      print "Unable to open the database.\n";
      EMD_PERL_ERROR("rman_o.br_backup() Unable to open the database.");
      return $result;
    }
    # Return the result from the RMAN operation.
    return $rman_result;
  }
}

# truncate the string in given length in byte.
# no partial multibyte character is returned.
#
# $result = trunc_byte($src, $byte);

sub trunc_byte {
  my ($src, $trunc_len) = @_;
  my $tmp_len = 0;
  my $ret = "";
  while (length($src)) {
    my $tmp_char = substr($src, 0, 1);
    {use bytes; $tmp_len += length $tmp_char;}
    last if ($tmp_len > $trunc_len);
    $src = substr($src, 1);
    $ret .= $tmp_char;
  }
  return $ret;
}


sub br_getLDA()
{
  EMD_PERL_DEBUG("rman_o.br_getLDA() db_username: $db_username");
#  EMD_PERL_DEBUG("rman_o.br_getLDA() db_password: $db_password");
  EMD_PERL_DEBUG("rman_o.br_getLDA() db_role: $db_role");
  my $lda;
  my $mode = 0;
  if ($db_role =~ /SYSDBA/i)
  {
    $mode = 2;
  }
  elsif ($db_role =~ /SYSOPER/i)
  {
    $mode = 4;
  }

  $lda = DBI->connect('dbi:Oracle:', "$db_username@".$db_connect_string, "$db_password",
                        {ora_session_mode => $mode,
                         PrintError => 1,
                         RaiseError => 0}) || return 0;
  return $lda;
}

sub br_prebackup()
{
 ($db_connect_string, $is_cold_backup, $use_rcvcat, $db_10_or_higher, $backup_strategy, $is_repos_database) = @_;
 
  EMD_PERL_DEBUG("rman_o.br_prebackup()");
  EMD_PERL_DEBUG("rman_o.br_prebackup() is RepositoryDatabase: $is_repos_database");
  br_set_target_env();
  $db_state = br_get_db_status();
  EMD_PERL_DEBUG("rman_o.br_prebackup() Database Status: $db_state");
  if ($db_state eq "ERROR")
  {
    print "Unable to perform the backup because the database status cannot be determined.\n";
    EMD_PERL_ERROR("rman_o.br_prebackup() Unable to perform the backup because the database status cannot be determined.");
    exit(-1);
  }
  elsif ($db_state eq "CLOSED")
  {
    print "Unable to perform the backup because the database is closed.\n";
    EMD_PERL_ERROR("rman_o.br_prebackup() Unable to perform the backup because the database is closed.");
    exit(-1);
  }
  elsif ($db_state eq "STARTED")
  {
    print "Unable to perform the backup because the database is in the STARTED (NOMOUNT) state.\n";
    EMD_PERL_ERROR("rman_o.br_prebackup() Unable to perform the backup because the database is in the STARTED (NOMOUNT) state.");
    exit(-1);
  }

  br_set_agent_env();

  if ($backup_strategy eq "basic")
  {
    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
    if ($wday eq $weekly_backup_day && $weekly_backup_script ne '')
    {
      EMD_PERL_DEBUG("rman_o.br_prebackup() suggested_backup: Today is the weekly backup day.");
      $rman_script = $weekly_backup_script;
    }
    else
    {
      EMD_PERL_DEBUG("rman_o.br_prebackup() suggested_backup: Today is the daily backup day.");
      $rman_script = $daily_backup_script;
    }
  }
  
  my $sql;
  my $dbcur;
  my @row;

  my $lda = br_getLDA();
  if ($lda == 0)
  {
    print "Skipping prebackup checks...\n";
    EMD_PERL_ERROR("rman_o.br_prebackup() Skipping prebackup checks - unable to connect to the database using Perl DBI.");
    return;
  }

  # Bug 3190159 - allow noarchivelog, online backups for recovery files
  if ($is_cold_backup eq "NO" && $db_state eq "OPEN" && !defined($skip_noarchivelog_check))
  {
    $sql = "select log_mode from v\$database";
    $dbcur = $lda->prepare($sql);
    $dbcur->execute;
    @row = $dbcur->fetchrow_array();
    if ($row[0] eq 'NOARCHIVELOG')
    {
      print "The database is OPEN and in NOARCHIVELOG mode. Online backup is supported only in ARCHIVELOG mode.\n";
      EMD_PERL_ERROR("rman_o.br_prebackup() The database is OPEN and in NOARCHIVELOG mode. Online backup is supported only in ARCHIVELOG mode.");
      $dbcur->finish;
      $lda->disconnect;
      exit(-1);
    }
    $dbcur->finish;
  }
    
  if ($backup_strategy eq "basic")
  {
    if ($db_10_or_higher eq "YES" && $device_type ne "sbt")
    {
      $sql = "select value from v\$parameter where name = 'db_recovery_file_dest'";
      $dbcur = $lda->prepare($sql);
      $dbcur->execute;
      @row = $dbcur->fetchrow_array();
      if ($row[0] eq '')
      {
        print "Fast recovery area setting is required for the Oracle suggested backup strategy";
        EMD_PERL_ERROR("rman_o.br_prebacup(): Fast recovery area setting is required for the Oracle suggested backup strategy");
        $dbcur->finish;
        $lda->disconnect;
        exit(-1);
      }
      $dbcur->finish;
    }
    $lda->disconnect;
  }
  else
  {
    $lda->disconnect;
  }
}

sub br_recovery()
{
  EMD_PERL_DEBUG("rman_o.br_recovery()");
  my $result = 0;

  # Edit the RMAN script for password encryption.
  &br_add_passwords();

  if ($run_state eq "MOUNTED")
  {
    $result = br_bounce_db_to_mount();
    if ($result != 0)
    {
      EMD_PERL_DEBUG("rman_o.br_recovery() br_bounce_db_to_mount failed");
      return -1;
    }
  }
  elsif ($run_state eq "NOMOUNT")
  {
    $result = br_bounce_db_to_nomount();
    if ($result != 0)
    {
      EMD_PERL_DEBUG("rman_o.br_recovery() br_bounce_db_to_nomount failed");
      return -1;
    }
  }
  my $rman_result = br_run_rman();
  EMD_PERL_DEBUG("rman_o.br_recovery() br_run_rman returned: $rman_result");
  if (($run_state eq "NOMOUNT" || $run_state eq "MOUNTED") && $open_db_option ne "")
  {
    if (($rman_result == -1) || ($rman_result == 5))
    {
      br_open_db($open_db_option);
      return $rman_result;
    }
    else
    {
      $result = br_open_db($open_db_option);
      if ($result == -1)
      {
        EMD_PERL_DEBUG("rman_o.br_recovery() br_open_db returned: $result");
        return $result;
      }
    }
  }
  # Return the result from the RMAN operation.
  EMD_PERL_DEBUG("rman_o.br_recovery() rman_result=$rman_result");
  return $rman_result;
}

sub br_rename_init_ora_file()
{
  my ($init_ora_file_name) = @_;
  EMD_PERL_DEBUG("rman_o.br_rename_init_ora_file() init_ora_file_name: $init_ora_file_name");
  if (-e $init_ora_file_name)
  {
    EMD_PERL_DEBUG("$init_ora_file_name exists");
    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
    my $postfix = sprintf ".%04d-%02d-%02d_%02d-%02d-%02d", $year + 1900, $mon + 1, $mday, $hour, $min, $sec;

    my $newname = $init_ora_file_name.$postfix;
    rename($init_ora_file_name, $newname);
    EMD_PERL_DEBUG("$init_ora_file_name has been renamed to $newname");
  }
}

sub br_create_init_ora_file()
{
  my ($init_ora_file_name, $spfile_name) = @_;
  EMD_PERL_DEBUG("rman_o.br_create_init_ora_file() init_ora_file_name: $init_ora_file_name");
  EMD_PERL_DEBUG("rman_o.br_create_init_ora_file() spfile_name: $spfile_name");
  if (open(INITORA, ">$init_ora_file_name"))
  {
    print INITORA "SPFILE=\'$spfile_name\'";
    close INITORA;
  }
}

sub br_query_one_column()
{
  my ($sql, $marker) = @_;
  EMD_PERL_DEBUG("rman_o.br_query_one_column() sql: $sql");
  EMD_PERL_DEBUG("rman_o.br_query_one_column() marker: $marker");
  
  my $sql_filename = br_get_temp_file_name();
    
  my @rs;
  open(SQL_WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$sql_filename")
    || ((print "Unable to open the SQLPLUS process in br_query_one_column().\n") && (return @rs));
  EMD_PERL_DEBUG("rman_o.br_query_one_column() db_username: {$db_username}");
  ## escape characters before passing into shell
  $db_password =~ s/(['"])/\\$1/g;
  if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
  {
    print SQL_WRITER "connect / as SYSDBA\n";
  }
  else
  {
    my $db_password_copy = $db_password;
      if ($db_password_copy =~ m/&/)
      {
        my $e = &find_escape_password($db_password_copy);
        print SQL_WRITER "set escape $e\n";
        $db_password_copy =~ s/&/$e&/g;
      }
      print SQL_WRITER "connect ${db_username}/\"${db_password_copy}\" as SYSDBA\n";
  }
  print SQL_WRITER "$sql;\n";
  print SQL_WRITER "exit;\n";
  close SQL_WRITER;

  # Command did not get executed
  my $fileSize = getFileSize($sql_filename);
  if ($fileSize == 0 || $fileSize == -1)
  {
    unlink($sql_filename);
    return @rs;
  }
  
  open (RS, "$sql_filename")
    || ((print "Unable to open the temporary file in br_query_one_column()\n") && (return @rs));
  my (@line);
  while ($_ = <RS>)
  {
    if (/$marker/)
    {
      @line = split(/$marker/);
      push(@rs, $line[0]);
      EMD_PERL_DEBUG("rman_o.br_query_one_column() member: $line[0]");
    }
  }
  close RS;
  unlink $sql_filename;
  return @rs;
}

sub br_fix_online_logs()
{
  EMD_PERL_DEBUG("rman_o.br_fix_online_logs()");
  my ($name, $dir, $ch, $need_fix);
  my $sql = "select member || '====' member from v\$logfile";
  my $marker = "====";
  my @log_names = &br_query_one_column($sql, $marker);
  foreach $name(@log_names)
  {
    $ch = substr($name, 0, 1);
    if ($ch eq "+")
    {
      next;  # Do not handle ASM for 10.2 release
    }
    if (-e $name)
    {
      next;  # online redo log file exists, fine.
    }
    $dir = getDirname($name);
    if (-e $dir)
    {
      next; # dir exist, open resetlog will re-create the online logs
    }
    elsif (mkdir($dir, 0755))
    {
      next;
    }
    else
    {
      EMD_PERL_DEBUG("rman_o.br_fix_online_logs() unable to create directory: $dir");
      $need_fix = 1; 
    }
  }

  if ($need_fix)
  {
    $sql = "select name || '====' name from v\$datafile where file# = 1";
    my @dfnames = &br_query_one_column($sql, $marker);
    if (@dfnames == 0)
    {
      EMD_PERL_DEBUG("rman_o.br_fix_online_logs() unable to obtain datafile 1 location. Abort.");
      return;
    }
    my ($name, $new_dir, $suffix) = fileparse($dfnames[0]);
    EMD_PERL_DEBUG("rman_o.br_fix_online_logs() datafile 1 location: $new_dir");
    if (! -e $new_dir)
    {
      EMD_PERL_DEBUG("rman_o.br_fix_online_logs() datafile 1 location does not exist. Unable to proceed to create online redo logs in this location.");
      return;
    }
    EMD_PERL_DEBUG("rman_o.br_fix_online_logs() renaming online redo logs to $new_dir");
    my ($log, $fullname,$rename_sql);
    @SQLCMDS = ("");
    foreach $name(@log_names)
    {
      ($log, $dir, $suffix) = fileparse($name);
      $fullname = $new_dir.$log.$suffix;
      EMD_PERL_DEBUG("rman_o.br_fix_online_logs() online redo log will be renamed from $name to $fullname");
      print "Warning: online redo log will be renamed from $name to $fullname.\n";
      $rename_sql = "alter database rename file '".$name."' to '".$fullname."'";
      push(@SQLCMDS, $rename_sql);
    }
    my ($tns) = "";
    my $sql_filename = br_get_temp_file_name();
    executeSQLPlusSYSDBA($db_username, $db_password, $tns, $sql_filename); 
  }
  return;
}

## For EM TTS Support
sub br_rman_tts()
{
  ($rman_script, $db_username, $db_password, $db_role, $target_home, $target_sid, $db_10_or_higher) =
@_;
  return(&br_rman());
}

# 
# Add encryption passwords to the script.
#
sub br_add_passwords()
{
  EMD_PERL_DEBUG("rman_o.br_add_passwords()");

  # Edit the RMAN script for password encryption.
  if (defined($encrypt_passwords) && $encrypt_passwords ne "" && 
      $encrypt_passwords ne "NULL")
  {
    if ($rman_script =~ /'%PASSWORD'/)
    {
      # encrypt_passwords will contain the appropriate quotes
      $rman_script =~ s/'%PASSWORD'/$encrypt_passwords/g;
    }
  }
}

# 
# Remove encryption passwords from the script.
#
# Parameters:
#   rman_script_copy - RMAN script to edit.
#
# Return values: 
#   The edited RMAN script.
#
sub br_remove_passwords()
{
  my ($rman_script_copy) = @_;
  if (defined($encrypt_passwords) && $encrypt_passwords ne "" && 
      $encrypt_passwords ne "NULL")
  {
    # Handle:
    # "set encryption on for all tablespaces algorithm '<algorithm>' identified by '<password>' only;"
    if ($rman_script_copy =~ /identified by (.*)only;/i)
    {
      $rman_script_copy =~ s/identified by (.*)only;/identified by * only;/ig;
    }
    # Handle:
    # "set encryption on for all tablespaces algorithm '<algorithm>' identified by '<password>';"
    # "set decryption identified by '<password>';"
    elsif ($rman_script_copy =~ /identified by (.*);/i)
    {
      $rman_script_copy =~ s/identified by (.*);/identified by *;/ig;
    }
  }  
  return($rman_script_copy);
}

# Get a special char that is not included in the password as escape char
# Called in three places: db_instance.pl, ha_dbstate_o.pl and rman_o,pl
# Should be put in a common file like db_common.pl
sub find_escape_password()
{
  my ($password_str) = @_;
  my @specialchars = ('!', '~', '#', '_', '`');
  my $escape = q/!/;
  for my $char (@specialchars) 
  {
    $escape = $char;
    if ( $password_str !~ m/$char/)
    {
      last; 
    }
  }
  return $escape;
}

1;

