#!/usr/local/bin/perl # # $Header: emdb/sysman/webapps/em/WEB-INF/perl/db/rman/rman_o.pl /st_emdbsa_11.2/4 2009/04/06 10:05:02 nzhao Exp $ # # rman.pl # # Copyright (c) 2002, 2009, 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 # # # NOTES # # # MODIFIED (MM/DD/YY) # 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 $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 ($_ = ) { 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 ($_ = ) { 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 ($_ = ) { 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"; 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 "Flash recovery area setting is required for the Oracle suggested backup strategy"; EMD_PERL_ERROR("rman_o.br_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 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 ($_ = ) { 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 '' identified by '' 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 '' identified by '';" # "set decryption identified by '';" 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;