#!/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 # # # NOTES # # # 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 ($_ = ) { 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 = ) { 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 = ) { 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 ($_ = ) { 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 ($_ = ) { 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 ($_ = ) { 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;