#!/usr/local/bin/perl
# 
# $Header: rman.pl 14-apr-2004.12:38:50 hying Exp $
#
# rman.pl
# 
# Copyright (c) 2002, 2004, Oracle Corporation.  All rights reserved.  
#
#    NAME
#      rman.pl - Perl script for backup and recovery jobs and remote operations.#
#      This is the EMDW_10.1.0.3.0_XXX_RELEASE version 
#      (rman.pl@@/main/st_emdw_10.1/3) of the file plus the addition of
#      rman_tts() procedure for ngade.
#
#      Perl script changes should NOT be made in this file. Instead,
#      they should be made in the OMS-side version of the file
#      ($ORACLE_HOME/emdb/sysman/webapps/em/WEB-INF/perl/db/rman/rman_o.pl).
#
#    DESCRIPTION
#      <short description of component this file declares/defines>
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
#    MODIFIED   (MM/DD/YY)
#    hying       04/14/04 - Fix bug 3543299 
#    vkapur      03/04/04 - NT bug 3486880: double quotes around inst list
#    xuliu       02/25/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 vars qw/ $OS $NT $S $TEMP $CP $MV $PS $DF $DELIMITER/;

require "$ENV{EMDROOT}/sysman/admin/scripts/db/db_common.pl";

my $rac_insts = '';

$ERROR_CODE = 8;

sub save_agent_env()
{
  EMD_PERL_DEBUG("rman.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 set_agent_env()
{
  EMD_PERL_DEBUG("rman.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.set_agent_env() ORACLE_HOME = $ENV{ORACLE_HOME}");
  EMD_PERL_DEBUG("rman.set_agent_env() LD_LIBRARY_PATH = $ENV{LD_LIBRARY_PATH}");
  EMD_PERL_DEBUG("rman.set_agent_env() SHLIB_PATH = $ENV{SHLIB_PATH}");
  EMD_PERL_DEBUG("rman.set_agent_env() LIBPATH = $ENV{LIBPATH}");
  EMD_PERL_DEBUG("rman.set_agent_env() PATH = $ENV{PATH}");
  EMD_PERL_DEBUG("rman.set_agent_env() JAVA_HOME = $ENV{JAVA_HOME}");
  EMD_PERL_DEBUG("rman.set_agent_env() ORA_NLS = $ENV{ORA_NLS}");
  EMD_PERL_DEBUG("rman.set_agent_env() ORA_NLS32 = $ENV{ORA_NLS32}");
  EMD_PERL_DEBUG("rman.set_agent_env() ORA_NLS33 = $ENV{ORA_NLS33}");
}

sub set_target_env()
{
  EMD_PERL_DEBUG("rman.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");
  }
  # Trace the following items even though they are not handled by this subroutine.
  EMD_PERL_DEBUG("rman.set_target_env() JAVA_HOME = $ENV{JAVA_HOME}");
}

sub set_srvctl_env()
{
  EMD_PERL_DEBUG("rman.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.set_srvctl_env() LD_LIBRARY_PATH: $ENV{LD_LIBRARY_PATH}");
  EMD_PERL_DEBUG("rman.set_srvctl_env() SHLIB_PATH: $ENV{SHLIB_PATH}");
  EMD_PERL_DEBUG("rman.set_srvctl_env() LIBPATH: $ENV{LIBPATH}");
  EMD_PERL_DEBUG("rman.set_srvctl_env() PATH: $ENV{PATH}");
  EMD_PERL_DEBUG("rman.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.set_srvctl_env() ORACLE_HOME: $ENV{ORACLE_HOME}");
  EMD_PERL_DEBUG("rman.set_srvctl_env() ORACLE_SID: $ENV{ORACLE_SID}");
  EMD_PERL_DEBUG("rman.set_srvctl_env() DB_NAME: $db_name");
}

sub get_db_status()
{
  EMD_PERL_DEBUG("rman.get_db_status()");
  
  my $status_filename = get_temp_file_name();
    
  open(SQL_WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$status_filename")
    || ((print "Unable to open the SQLPLUS process in get_db_status().\n") && (return "ERROR"));
  EMD_PERL_DEBUG("rman.get_db_status() db_username: {$db_username}");
  if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
  {
    print SQL_WRITER "connect / as SYSDBA\n";
  }
  else
  {
    print SQL_WRITER "connect ${db_username}/${db_password} 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
  $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 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 bounce_db_to_nomount()
{
  EMD_PERL_DEBUG("rman.bounce_db_to_nomount()");
  $to_state = "nomount";
  return bounce_db();
}

sub bounce_db_to_mount()
{
  EMD_PERL_DEBUG("rman.bounce_db_to_mount()");
  $to_state = "mount";
  return bounce_db();
}

sub 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 bounce_db()
{
  my $controlfile = $_[0];
  
  EMD_PERL_DEBUG("rman.bounce_db(@_)");

  my $bounce_filename = get_temp_file_name();
  EMD_PERL_DEBUG("rman.bounce_db() db_username: {$db_username}");
  EMD_PERL_DEBUG("rman.bounce_db() db_connect_string: {$db_connect_string}");
  if ($blackout_target_type eq "oracle_database")
  {
    set_target_env();
    open(WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$bounce_filename")
      || ((print "Unable to open the SQLPLUS process in bounce_db().\n") && (return -1));
    if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
    {
      print WRITER "connect / as SYSDBA\n";
    }
    else
    {
      print WRITER "connect ${db_username}/${db_password} as SYSDBA\n";
    }

    if ($controlfile ne "")
    {
        print WRITER "alter system set control_files = $controlfile scope=SPFILE;\n";        
    }    
    print WRITER "shutdown immediate;\n";
    print WRITER "exit;\n";
    close WRITER;

    open(WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >>$bounce_filename")
      || ((print "Unable to open the SQLPLUS process in bounce_db().\n") && (return -1));
    if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
    {
      print WRITER "connect / as SYSDBA\n";
    }
    else
    {
      print WRITER "connect ${db_username}/${db_password} as SYSDBA\n";
    }

    EMD_PERL_DEBUG("rman.bounce_db() to_state: $to_state");
    print WRITER "startup $to_state;\n";
    print WRITER "exit;\n";
    close WRITER;

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

    open (BOUNCE, "$bounce_filename")
      || ((print "Unable to open the temporary file in bounce_db()\n") && (return -1));
    my $ln;

    while ($ln = <BOUNCE>)
    {
      print $ln;
      if ($ln =~ /ORA-/)
      {
        if ($ln =~ /ORA-01109/ || $ln =~ /ORA-01507/ )
        {
            #ignore ORA-01109: database not open
            #   and ORA-01507: database not mounted
            #shutdown immediate may shed these warnings 
        }
        else
        {    
            close BOUNCE;
            unlink($bounce_filename);
            return -1;
        }    
      }
    }
    close BOUNCE;
    unlink($bounce_filename);
    return 0;
  }
  else
  {
    set_agent_env();
    my $lda = getLDA();
    if ($lda eq null)
    {
      print "Skipping selection of RAC instances and database name...\n";
      EMD_PERL_ERROR("rman.bounce_db() Skipping selection of RAC instances and database name - unable to connect to the database using Perl DBI.");
    }
    else
    {
      my $sql = "select instance_name from gv\$instance where status = 'OPEN'";
      my $dbcur = $lda->prepare($sql);
      $dbcur->execute;
      my @row = $dbcur->fetchrow_array();
      $rac_insts = $row[0];
      while (@row = $dbcur->fetchrow_array())
      {
        $rac_insts .= ",$row[0]";
      }
      ## Query for db_name
      $sql = "select name from v\$database";
      $dbcur = $lda->prepare($sql);
      $dbcur->execute;
      @row = $dbcur->fetchrow_array();
      $db_name = $row[0];
      $dbcur->finish;
      $lda->disconnect;
    }

    set_srvctl_env();

    ## Unable to use rac_srvctl::shutdown_rac due to conflicting get_db_status routine
    $cmd = "$ENV{ORACLE_HOME}/bin/srvctl stop database -d $db_name -o immediate\n";
    $result = `$cmd`;
    print $result;

    if ($? != 0)
    {
      $cmd = "$ENV{ORACLE_HOME}/bin/srvctl start instance -d $db_name -i \"$rac_insts\"\n";
      $result = `$cmd`;
      print $result;
      return -1;
    }
    set_target_env();

    if ($controlfile ne "")
    {
        open(WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$bounce_filename")
          || ((print "Unable to open the SQLPLUS process in bounce_db().\n") && (return -1));

        if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
        {
          print WRITER "connect / as SYSDBA\n";
        }
        else
        {
          print WRITER "connect ${db_username}/${db_password} as SYSDBA\n";
        }
        print WRITER "startup nomount;\n";
        print WRITER "alter system set control_files = $controlfile scope=SPFILE;\n";
        print WRITER "shutdown immediate;\n";                
        print WRITER "exit;\n";
        close WRITER;
    }    

    open(WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >>$bounce_filename")
      || ((print "Unable to open the SQLPLUS process in bounce_db().\n") && (return -1));

    if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
    {
      print WRITER "connect / as SYSDBA\n";
    }
    else
    {
      print WRITER "connect ${db_username}/${db_password} as SYSDBA\n";
    }
    print WRITER "startup $to_state;\n";
    print WRITER "exit;\n";
    close WRITER;

    # Command did not get executed
    $fileSize = getFileSize($bounce_filename);
    if ($fileSize == 0 || $fileSize == -1)
    {
      unlink($bounce_filename);
      print "Unable to shut down and start up the rac database.\n";
      return -1;
    }

    open (BOUNCE, "$bounce_filename")
      || ((print "Unable to open the temporary file in bounce_db()\n") && (return -1));
    my $ln;
    while ($ln = <BOUNCE>)
    {
      print $ln;
      if ($ln =~ /ORA-/)
      {
        if ($ln =~ /ORA-01109/ || $ln =~ /ORA-01507/ )
        {
            #ignore ORA-01109: database not open
            #   and ORA-01507: database not mounted
            #shutdown immediate may shed these warnings 
        }
        else
        {
            close BOUNCE;
            unlink($bounce_filename);
            return -1;
        }    
      }
    }
    
    close BOUNCE;
    unlink($bounce_filename);
    return 0;
  }
}

sub open_db()
{
  EMD_PERL_DEBUG("rman.open_db()");
  my $opendb_filename = get_temp_file_name();  
  
  open(SQL_WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$opendb_filename")
    || ((print "Unable to open the SQLPLUS process in open_db().\n") && (return -1));
  if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
  {
    print SQL_WRITER "connect / as SYSDBA\n";
  }
  else
  {
    print SQL_WRITER "connect ${db_username}/${db_password} as SYSDBA\n";
  }
  print SQL_WRITER "alter database open;\n";
  print SQL_WRITER "exit;\n";
  close SQL_WRITER;

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

  open (OPENDB, "$opendb_filename")
    || ((print "Unable to open the temporary file in open_db()\n") && (return -1));
  while ($_ = <OPENDB>)
  {
    print $_;
    if (/ORA-/)
    {
      close OPENDB;
      unlink($opendb_filename);
      return -1;
    }
  }
  
  close OPENDB;
  unlink($opendb_filename);

  ## Restore original rac instance status
  if ($blackout_target_type ne "oracle_database" && $rac_insts ne '')
  {
    set_srvctl_env();
    $cmd = "$ENV{ORACLE_HOME}/bin/srvctl start instance -d $db_name -i \"$rac_insts\"\n";
    my $result = `$cmd`;
    print $result;
  }
  return 0;
}


# Xun:
# return -1 (255) if failed
# return 1 if database is in nomount state
# return 0 if succeed
#
sub start_db_to_mount()
{
  EMD_PERL_DEBUG("rman.start_db_to_mount() ORACLE_HOME: $ENV{ORACLE_HOME}");
  EMD_PERL_DEBUG("rman start_db_to_mount() ORACLE_SID: $ENV{ORACLE_SID}");

  my $startdb_filename = get_temp_file_name();  
  
  open(SQL_WRITER, "|$ENV{ORACLE_HOME}/bin/sqlplus /nolog >$startdb_filename")
    || ((print "Unable to open the SQLPLUS process in start_db_to_mount().\n") && (return -1));
  if (!($db_role =~ /SYSDBA/i)) ## OS Authentication
  {
    print SQL_WRITER "connect / as SYSDBA\n";
  }
  else
  {
    print SQL_WRITER "connect ${db_username}/${db_password} as SYSDBA\n";
  }
  print SQL_WRITER "startup 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
  $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 start_db_to_mount()\n") && (return -1));
  my $result = 0;
  while ($_ = <STARTDB>)
  {
    if (/ORA-/)
    {
      if ($result != 1)
      {
         # ORA error shouldn't override nomount status
         $result = -1;
      }  
    }
    if (/STARTED/)
    {
       $result = 1;
    }
  }

  close STARTDB;
  unlink($startdb_filename);
  
  return $result;
}

sub validate_syntax()
{
  EMD_PERL_DEBUG("rman.validate_syntax()");
  
  my $rman_filename = get_temp_file_name();    
  
  EMD_PERL_DEBUG("rman.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";
  $result = 1;
  while ($_ = <OUT>)
  {
    EMD_PERL_DEBUG("$_");
    if (/RMAN-06171/)
    {
      $result = 0;
    }
    elsif (/RMAN-01005/)
    {
      $result = -1;
    }
  }

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


# 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 run_rman()
{
  EMD_PERL_DEBUG("rman.run_rman()");
  set_target_env();
  EMD_PERL_DEBUG("rman.run_rman() env NLS_LANG: $ENV{NLS_LANG}");
  
  $? = 0;
  EMD_PERL_DEBUG("rman.run_rman() rman_script:\n$rman_script");
  EMD_PERL_DEBUG("rman.run_rman() db_username: {$db_username}");
  EMD_PERL_DEBUG("rman.run_rman() db_connect_string: {$db_connect_string}");
  EMD_PERL_DEBUG("rman.run_rman() use_rcvcat: {$use_rcvcat}");
  EMD_PERL_DEBUG("rman.run_rman() rcvcat_username: {$rcvcat_username}");
  EMD_PERL_DEBUG("rman.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 = $?;
           $rmanExit = 1;
           EMD_PERL_DEBUG("rman 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.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 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 run_rman().\n") && (return -1));
  }
  
  # Turn on autoflush for pipe output
  $old_fh = select(RDRFH);
  $| = 1;
  select($old_fh);

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

  # Turn on autoflush for standard output
  $old_fh = select(STDOUT);
  $| = 1;
  select($old_fh);
  if (!($db_role =~ /SYSDBA/i))
  {
    print RMAN_WRITER "connect target /;\n";
  }
  else
  {
    print RMAN_WRITER "connect target ${db_username}/${db_password}\n";
  }
  if ($use_rcvcat eq "YES")
  {
    print RMAN_WRITER "connect rcvcat $rcvcat_username/$rcvcat_password"."@"."$rcvcat_connect_string\n";
  }
  if (($db_10_or_higher eq "YES") && ($rman_command_id ne ""))
  {
    $rman_command_id = $rman_command_id."_".localtime(time);
    EMD_PERL_DEBUG("rman.run_rman() rman_command_id: $rman_command_id");
    print RMAN_WRITER "set command id to '$rman_command_id';\n";
  }
  print RMAN_WRITER $rman_script;
  print RMAN_WRITER "exit;\n";
  close RMAN_WRITER;
  
  
  $MAX_OUT_SIZE = 7168; #7K
  $cur_out_size = 0;
  $dbconsole_coldbackup = ($ENV{CONSOLE_CFG} eq "dbconsole") && ($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";
                      }
                  }
              }
          }
          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";
                    }
                }    
    
                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";
            }
    
            $fullBuf=$fullBuf.$buf;
        }    
        else
        {
            print "An error ocurred when reading from rman: $? $!\n";
            $rman_result = -1;
        }
    } while (defined($sysret) && $sysret != 0);
  
  }  
  
  # We'll print "... ..." if output is ommited
  if ($dbconsole_coldbackup && $cur_out_size > $MAX_OUT_SIZE)
  {
      EMD_PERL_DEBUG("rman.rman() rman output exceeded $MAX_OUT_SIZE, truncated the rest.");
      print "...\n... ...\n... ... ...\n\n\n";
  }  
    
  # 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 = $?;
     }   

     # 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.run_rman() waitpid=$wpid, rman_result1=$rman_result");
  }

  if ($rman_result != 0)
  {
    $rman_result = -1;
  }
  else
  {
    ## Catch rman error (RMAN-00569) and warning (RMAN-) upon error stack
    if (index($fullBuf, "RMAN-") == -1)
    {
      $rman_result = 0;
    }
    elsif (index($fullBuf, "RMAN-00569") == -1)
    {
      $rman_result = 1;
    }
    else
    {
      $rman_result = -1;
    }
  }
  EMD_PERL_DEBUG("rman.run_rman() rman_result2=$rman_result");
  return($rman_result);
}

sub rman()
{
  ($use_rcvcat, $db_connect_string) = @_;
  EMD_PERL_DEBUG("rman.rman()");
  
  $result = 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 = $ERROR_CODE;
  }  
  EMD_PERL_DEBUG("rman.rman() result=$result");
  $result;
}

sub backup()
{
  EMD_PERL_DEBUG("rman.backup()");

  # Edit the RMAN script so that the tag name is unique.
  if ($rman_script =~ /'%TAG'/)
  {
    if (defined($job_name) && $job_name ne "" && 
      defined($curr_date) && $curr_date ne "")
    {
      $job_name_length = length($job_name);
      $tag_name_length = $job_name_length + length($curr_date) + 1;
      if ($tag_name_length > 31)
      {
        $chop_length = $tag_name_length - 31;
        $job_name = substr($job_name, 0, $job_name_length - $chop_length);
      }
      $tag_name = $job_name.'_'.$curr_date;
    }
    else
    {
      $tag_name = "TAG";
    }
    EMD_PERL_DEBUG("rman.backup() tag_name=$tag_name");
    $rman_script =~ s/'%TAG'/'$tag_name'/g;
  }

  EMD_PERL_DEBUG("rman.backup() Database is $db_state");
  if ($db_state eq "MOUNTED")
  {
    $result = run_rman();
    return $result;
  }
  ## Database is OPEN, check if is_cold_backup
  elsif ($is_cold_backup eq "YES")
  {
    EMD_PERL_DEBUG("rman.backup() offline backup");
    $to_state = "mount";
    $result = bounce_db();
    EMD_PERL_DEBUG("rman.backup() 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.backup() Unable to perform the backup because the database could not be shut down and restarted.");
      return $result;
    }

    $result = run_rman();
    EMD_PERL_DEBUG("rman.backup() run_rman returned: $result");
    if ($result == -1)
    {
      open_db();
      return $result;
    }

    $result = open_db();
    EMD_PERL_DEBUG("rman.backup() open_db returned: $result");
    if ($result == -1)
    {
      print "Unable to open the database.\n";
      EMD_PERL_ERROR("rman.backup() Unable to open the database.");
    }
    return $result;
  }
  ## Online (hot) backup.
  else
  {
    EMD_PERL_DEBUG("rman.backup() online backup");
    $result = run_rman();
    return $result;
  }
}

sub getLDA()
{
  EMD_PERL_DEBUG("rman.getLDA() db_username: $db_username");
#  EMD_PERL_DEBUG("rman.getLDA() db_password: $db_password");
  EMD_PERL_DEBUG("rman.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 null;
  return $lda;
}

sub prebackup()
{
  ($db_connect_string, $is_cold_backup, $use_rcvcat, $db_10_or_higher, $backup_strategy) = @_;
  
  EMD_PERL_DEBUG("rman.prebackup()");
  set_target_env();
  $db_state = get_db_status();
  EMD_PERL_DEBUG("rman.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.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.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.prebackup() Unable to perform the backup because the database is in the STARTED (NOMOUNT) state.");
    exit(-1);
  }

  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.prebackup() suggested_backup: Today is the weekly backup day.");
      $rman_script = $weekly_backup_script;
    }
    else
    {
      EMD_PERL_DEBUG("rman.prebackup() suggested_backup: Today is the daily backup day.");
      $rman_script = $daily_backup_script;
    }
  }

  my $sql;
  my $dbcur;
  my @row;

  $lda = getLDA();
  if ($lda eq null)
  {
    print "Skipping prebackup checks...\n";
    EMD_PERL_ERROR("rman.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.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 "Flash recovery area setting is required for the Oracle suggested backup strategy";
        EMD_PERL_ERROR("Rman.prebacup(): Flash 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 recovery()
{
  ($db_connect_string, $use_rcvcat) = @_;
  EMD_PERL_DEBUG("rman.recovery()");
  $result = run_rman();
  exit($result);
}

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

