(Get this article & the current version of oraback.sh in one tar file.)
Trying to back up today's databases is like trying to catch a charging rhino with a butterfly net. Real world databases have become screaming behemoths that are very difficult to capture (back up), and standard UNIX utilities are like butterfly nets. If you try to use these utilities for the job, not only will you not capture the data -- you'll break your arm! This article is my attempt to build a better rhino trap. It uses many advanced shell scripting features, so you may find it useful even if you do not have Oracle.
One of the first decisions a database administrator must make is whether to perform "cold" or "hot" backups. A cold backup involves:
Here are the steps to perform a hot backup. (Note that a hot backup requires much more knowledge of Oracle's internal structure, so a basic definition of helpful terms is given in the sidebar "Definitions.") First, you must determine the name of each tablespace in the instance and the files that are within it. Then, for each tablespace, you:
Once all tablespaces are done:
It is much easier to perform cold backups, so why do hot backups at all? Simply stated, they increase your backup window, or the time in which you are allowed to perform backups. Because cold backups require a shutdown of the database, your users will probably allow only a very small backup window. Hot backups allow you to back up without shutting down, remaining virtually invisible to the user, and thereby significantly enlarging your window. I have even performed hot backups of production data during primetime hours. Try that with a cold backup!
If you are going to use hot backups, though, you must follow certain precautions. Failure to do so could mean you would only be able to restore up to your last cold backup -- if you made one.
During a cold backup, all data files have been synchronized. A restore simply requires copying all files into place and restarting the database. However, during a hot backup, each file is backed up at a different time; thus each has a different time stamp. You must have some method of syncing all of those files to the same point during a restore. That is what Oracle's Redo Logs, (also known as archiving, or Archive Logs) are for. Archiving continually records all database changes in what is called the On-line Redo log. Once that log is full, it is copied to an Archived Redo log. (For more information, see the sidebar "Should I Use Archiving?")
When restoring from a hot backup, you first replace the damaged database files with their respective backup copies. (You only need to replace the damaged files.) You then "play" the redo logs, effectively "redoing" all the transactions that affected those files. All files are then synchronized, and the database can be brought on line.
With all the logs in place, this restore can be done with one command. However, if just one redolog is lost, your restore could range from extremely difficult to impossible. If you do not have the set of redo logs that span the time of your backup, you will not recover at all.
This is why you must treat your redo logs as if they were gold. Some of the methods of protecting them are:
It is not uncommon, or unwise, to use all of the methods listed above to protect your redo logs.
To automate Oracle backups, you must know the location of tablespaces, raw partitions, data files, control files, redolog files, and whether archiving is running. To make things more difficult, each installation will have different answers to these questions. Oraback.sh (Listing 1) addresses all of the problems above by obtaining most of its information from Oracle. The script will do the following for each instance:
Oraback.sh supports these features:
The script assumes that all instances want hot backups, which require redologs. Oraback.sh will warn you of any instances that do not have logs. It will continue to warn you until you correct the problem or customize your setup for that instance. Customization is done with oraback.conf (Listing 2).
Now that I've explained the overall purpose of oraback.sh, here is a description of each step within the script. Oraback.sh begins by checking oraback.conf to see whether the user running the backup is Oracle, or a user listed in field 7. It then looks to see whether the word "skip" is in field 2. If so, it skips the backup once, and removes the word from oraback.conf. The script then does one of the following, depending on which arguments, if any, it received:
Oraback.sh first checks the $HOST.master line in oraback.conf to find out if: (a) there is a tape device in field 6. If so, it labels the tape, making sure the no-rewind device is being used; and (b) there is a number in field 8. If so, it will perform that many simultaneous copies of database files.
Next, oraback.sh asks Oracle questions that will determine how (or if) the SID will be backed up. It asks:
If the instance is online, logs are on, and the instance is not excluded from hot backups, the script will put each tablespace into backup mode by using the sqldba command begin backup. The script then copies that tablespace's files to the backup device. When the copying is done, it takes the tablespace out of backup mode with the sqldba command end backup.
These three steps are the core of the hot backup. While the tablespace is in backup mode, the files will not change. Any changes made to the tablespace while it is in backup mode are sent to the redo logs, and are applied once it is taken out of backup mode. This is all done automatically by Oracle.
The script supports simultaneous copying of individual data files to dramatically increase the speed of the backup. Depending on how many files there are per tablespace, there may be one or more tablespaces in backup mode at once. This will be done in order to perform the number of concurrent file copies (using dd) that are specified in field 8 of oraback.conf, if you are using this feature.
Next, the script forces a checkpoint and archive log switch, which causes the online redo log to be sent to an archived redo log. It then backs up the control files to disk using both sqldba and a manual copy. Finally, the script makes copies of the essential redo logs and compresses them. (Essential logs are those that span the time of your backup.) If the backup device is a tape, it then backs up all files that were sent to disk to the tape device.
If the instance is off-line, oraback.sh only needs to copy the datafiles to the backup device. The script then backs up the control files and redo logs the same way the hot backup does.
If the instance is online, but archiving is off, it checks oraback.conf for a line that reads:
$HOST:$SID:NOARCHIVELOG
If that line is not found, oraback.sh complains and sends you mail. If the line is found, it looks for the word "offline" on that same line. If "offline" is there, oraback.sh will shut down the instance, perform a cold backup, and then restart the instance. (This is one of the ways you can customize oraback.sh for different instances.)
If the instance is online, oraback.sh also looks for a line in oraback.conf that reads
$HOST:$SID:::nohotbackup
If that word is found, the script will skip this instance when performing a hot backup.
In summary, oraback.sh can back up from one to all of the instances in oratab. To back up all instances, run it with no arguments. To back up one or more, run it with those SID's as arguments. To schedule a backup for the time in oraback.conf, run it with the at argument.
Check the following values in the site-specific section at the top of
the script. They must be changed to fit your site.
a. BINDIR must be set to where you install oraback.sh.
b. ORATAB must be set to the name and location of Oracle's oratab
file. (It must be located in a directory that Oracle can write to.)
c. ORACONF must be set to the name and location of your oraback.conf
file.
You should also review and confirm all other preference variables in the site-specific section, including ORADIR, TMP, LOG, and PATH.
You must also verify that:
a. You know which instances are using archiving
b. There is a valid archive_dest in each instances's config.ora
(not the default /?/dbs/arch)
c. Oracle can create directories in the filesystem that you are
using for backups
Oraback.conf is the main configuration file for oraback.sh. If you want to use the at argument, you must have the following line in that file:
$HOST.master::Sun:1200:1730:::5 :/dmp::Mail -s Error_oracle:
These and other fields are described below:
There are four predefined functions, unused by default, which are listed in the site-specific section of oraback.sh. They are listed below, with their respective usage times:
Preback: Runs before entire backup starts
Preshut: Runs before shutdown of each instance
Poststart: Runs after startup of each instance
Postback: Runs after entire backup finishes
If you wanted to restart orasrv, for instance, after an instance is restarted (with a script called rc.orasrv.sh,) you would change the Poststart function to read:
Poststart(){
rc.orasrv.sh
}
If you have an instance that will not run archiving or hot backups, you need a line in oraback.conf that starts with:
$HOST:$SID::::
For testing, select a small instance and run the following command as Oracle:
$ /usr/local/bin/oraback.sh <instance>
Be aware that if the word "offline," is in oraback.conf, or if the day that you specified for cold backups is today, the instance will be shut down!
If this test functions properly, you can put an entry in Oracle's cron. Call this command:
/usr/local/bin/oraback.sh at
at around 1200. It will check the oraback.conf and schedule an at job that will perform the backup.
Never accept that a new backup strategy works until you have fully tested it under all circumstances! Make sure you test all of the following until you are comfortable:
Once your restore test is successful, you will have caught and tamed the rhino! This script represents more than three years of work, and its development is a continual process. I hope you find it useful.
![]()
In one word, yes! Having logs available during a recovery is a tremendous help. There are several things that you can do with logs that simply are not possible without them:
There are two common arguments for not using archiving. The first is that it would decrease performance. The performance loss is relatively minor, unless you have some parameters set incorrectly. It is important to know that redo logs are always being created in every Oracle instance. Enabling archiving simply copies the closed redo log to another directory for recovery purposes. Before you make any decision about archiving, you should benchmark your instance with and without archiving. (A recent benchmark I performed showed a 2.5 percent performance loss during continuous loads.)
The second argument is that archiving is superfluous. Some databases are read-only and are updated only occasionally. Users might argue that archiving is not needed. They often tell you to backup the database manually when it changes, after a load or import. This defeats one of the main purposes of automating: cron never forgets! If the database does not need archiving because there is little activity, then enabling it won't hurt. The CPU usage is negligible, and archiving will allow you to do hot backups and use all the features listed above.
There is one type of database that I think would be significantly hampered by archiving: the test database. I still believe that logs should be on whenever possible, but when a DBA is testing a new database, he/she may be loading and unloading tables all day long. This generates a huge amount of archived redo logs that have no value. It was for this type of database that I added the ability to do a nightly cold backup of an instance that does not have archiving.
In summary, every production and development database should use archiving,
unless continuous imports make it unrealistic. ![]()
PLEASE NOTE! This script is always changing, trying to keep up with the latest versions of Oracle and requests from users. To make sure you are running the most current version, contact me. I am in the process of adding some new features to it, so the version that you see here may not be fully functional! Once again, contact me!
#!/bin/sh
#
# Rcs_ID" = '$Id: oraback.sh,v 1.3 1997/06/20 15:26:59 Pcurtis Exp Pcurtis $'
#
# @(#)oraback.sh - (Hot backups for Oracle databases)
# Copyright (C) 1996 Curtis Preston - curtis@pencom.com
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 2 of the License, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# For a copy of the license, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139
#
# Many thanks to Sam Binder, for bringing me gracefully into SQL land...
#
#######################################################
##Site-specific section (change as appopriate)
COMPRESS=/usr/local/bin/gzip
BINDIR=/usr/local/bin #Where $0 is located
ORACLE=oracle
ORATAB=/var/opt/oracle/oratab #Oracle's configfile
ORADIR=`grep -v '^#' $ORATAB|cut -d: -f2|tail -1`
ORACONF=$ORACLE_HOME/oraback.conf
TMP=/var/tmp
PATH=$PATH:/usr/bin:/usr/sbin:/sbin:/usr/5bin:/bin:$ORACLE_HOME/bin
ORIG_PATH=$PATH
Preshut() { #Run prior to shutting down instance
echo
}
Poststart() { #Run after starting up instance
echo
}
Preback() { #Run prior to backup
echo
}
Postback() { #Run after entire backup finishes
echo
}
export BINDIR ORATAB ORADIR ORACONF TMP PATH ORIG_PATH
##End site-specific configuration section
#######################################################
Usage() {
echo "Usage: $0 [at|\$ORACLE_SID(s)]"
echo "(Must be run as one of the following users:"
echo "${USERS}."
exit 1
}
Log_error() { #Send errors to various places
if [ "$1" -gt 0 ] ; then
TIME="`date '+%b %e %T'` `uname -n`"
MSG=" `basename $0` $SID: WARNING:"
ERR=`echo $2|sed "s/^/$TIME $MSG /"`
echo "$ERR" |$TLOG
echo "$ERR" >>$TMP/$X.Mail
[ "$1" = 99 ] && touch $TMP/$X.Fatal
fi
}
Error_chk() { #Check sqldba output files for errors
ERROR=`egrep -i 'dba\-|ora\-|error' $1`
[ -n "$ERROR" ] && Log_error 1 "$ERROR"
[ ! -s $1 ] && Log_error 99 "Bus error on sqdldba"
}
Backup_offline() { #backup database offline
echo " Backing up $ORACLE_SID OFFLINE." |$TLOG
Backup_instance cold
}
Backup_instance() { #All tablespaces in an instance
cut -d: -f1 $CWD/$TBLSPACE_LIST | sort -u |\
while read TBS ; do
if [ -n "$SIMULT" ] ; then #If doing simultaneous
Ps_check #backups, then do
export TBS #them in the
Backup_one_tablespace $1 & #background
else
export TBS
Backup_one_tablespace $1
fi
done #done with all tablespaces
while [ -n "`ls $TMP/$X.$SID.*.* 2>/dev/null`" ] ; do
sleep 10 #Wait for all TBS's to finish
done
}
Backup_one_tablespace() { #All files in one tbs
touch $TMP/$X.$SID.${TBS}all.allfiles
if [ $1 = "hot" ] ; then #If hot b/up, then put in
$SQLDBA<<EOF_BOT >$TMP/$X.$TBS.tablebu 2>&1 #b/up mode
connect internal;
alter tablespace $TBS begin backup;
exit;
EOF_BOT
Error_chk $TMP/$X.$TBS.tablebu
fi
grep "^$TBS:" $CWD/$TBLSPACE_LIST | cut -d: -f2 | \
while read FILE ; do
if [ -n "$SIMULT" ] ; then #More simult. copying
export FILE
Ps_check
Copy_one_database_file &
else
CT=`cat $TMP/$X.CT`
echo `expr $CT + 1` > $TMP/$X.CT
export FILE CT
Copy_one_database_file
fi
done #Done with all files in TBS
while [ -n "`ls $TMP/$X.*.$TBS.* 2>/dev/null`" ] ; do
sleep 10 #Wait for all files to copy
done
if [ $1 = "hot" ] ; then #If hot, take out of b/u mode
$SQLDBA << EOF_TBS > $TMP/$X.$TBS.tablebu 2>&1
connect internal;
alter tablespace $TBS end backup;
exit;
EOF_TBS
Error_chk $TMP/$X.$TBS.tablebu
fi
rm $TMP/$X.$SID.${TBS}all.allfiles
}
Copy_one_database_file() {
DBF_BASENAME=`basename $FILE`
touch $TMP/$X.$SID.$TBS.$DBF_BASENAME
[ -n "$TAPE" ] && DEVICE=$TAPE || DEVICE=$CWD/$DBF_BASENAME
NEW=`find $FILE -newer $CWD/$DBF_BASENAME.Z -print 2>/dev/null`
#If datafile newer than b/u,or if no b/u file,copy it
if [ $? -gt 0 -o "$NEW" = "$FILE" ] ; then
echo " ${CT}- Backing up $FILE." |$TLOG
if [ "$COMP" = Y ] ; then
dd if=$FILE bs=64k 2>/dev/null| $COMPRESS -c >$DEVICE.Z
else
dd if=$FILE of=$DEVICE bs=64k 2>/dev/null
fi
Log_error $? "Error Copying $FILE to $DEVICE"
else
echo " NOT Backing up $FILE. (NO CHANGE)" |$TLOG
fi
rm $TMP/$X.$SID.$TBS.$DBF_BASENAME
}
Switch_logfile_n_backup_controlfile() {
echo " Switch logfile and backup controlfile"|$TLOG
#This section needs to be changed to use the 'alter system logfile current'
#syntax, while providing backwards compatibility to this old way for v.6
$SQLDBA << EOF_CONTROL >$TMP/$X.control 2>&1
connect internal;
alter system checkpoint;
alter system switch logfile;
alter database backup controlfile to '$CWD/cntrlfile';
exit;
EOF_CONTROL
Error_chk $TMP/$X.control
$COMPRESS -f $CWD/cntrlfile >/dev/null 2>&1
Log_error $? "Error compressing $CWD/cntrlfile"
#If it is a valid archive log dir, and there are logs in it
if [ -d $ARCH_LOG_DIR -a -f ${LOGDEST}* ] ; then
NEWER=`find $ARCH_LOG_DIR -type f -name "$ARCH_LOG_FILE*" -newer $OLD -print`
NEWER=`echo $NEWER`
NEWEST=`ls -t $NEWER|head -1`
OLD_SIZE=`du -s $NEWEST|awk '{print $1}'` ; SLEEP=Y
#This section sleeps if last archive log file is still being copied
#(sometimes this takes a few minutes)
#This is a 'hacky' way to do it, but was the best way to do in v.6
while test $SLEEP = Y ; do
sleep 60
NEW_SIZE=`du -s $NEWEST|awk '{print $1}'`
[ $NEW_SIZE -eq $OLD_SIZE ] && SLEEP=N || OLD_SIZE=$NEW_SIZE
done
else
[ ! -d $ARCH_LOG_DIR ] && Log_error 1 "Log dest. (${ARCH_LOG_DIR}) not found."
[ ! -f ${LOGDEST}* ] && Log_error 1 "NO archive logs found in $ARCH_LOG_DIR."
fi
}
Copy_control_and_essential_redolog_files() {
if [ -f $CWD/$CONTROLFILE_LIST ] ; then
echo " Manually copying one controlfile." |$TLOG
CONTROLFILE=`grep -v '^$' $CWD/$CONTROLFILE_LIST`
CONTROLFILE_BASENAME=`basename $CONTROLFILE`
$COMPRESS -c $CONTROLFILE > $CWD/$CONTROLFILE_BASENAME.Z
Log_error $? "Error copying $FILE to $CWD"
else
Log_error 1 "$CWD/$CONTROLFILE_LIST: File not found"
fi
if [ -f $CWD/$CONTROLFILE_LIST $CWD/$LOGFILE_LIST ] ; then
if [ -z "$NOLOG" ] ; then
#If Instance is up, remove old logs, otherwise leave them
if [ "$DB_UP" = 2 ] ; then
REMOVE=`ls $CWD/BacKuP.CoPy.${ARCH_LOG_FILE}* 2>/dev/null|grep -v $BOLD`
REMOVE=`echo $REMOVE`
if [ -n "$REMOVE" ] ; then
echo " Removing old archive logs: $REMOVE" |$TLOG
rm -f $REMOVE
else
echo " NO old archive logs found." |$TLOG
fi
fi
echo " Copying redo logs created since $0 started:" |$TLOG
NEWER=`find $ARCH_LOG_DIR -type f -name "$ARCH_LOG_FILE*" -newer $OLD -print`
NEWER=`echo $NEWER`
if [ -n "$NEWER" ] ; then
echo " New archive logs found: $NEWER" |$TLOG
#Files are stable, copy them
for ARCHIVED_LOG_FILE in $NEWER ; do
ARCHIVED_LOG_FILE_BASENAME=`basename $ARCHIVED_LOG_FILE`
BACKUP_COPY_NAME="BacKuP.CoPy.$ARCHIVED_LOG_FILE_BASENAME"
cp -p $ARCHIVED_LOG_FILE $CWD/$BACKUP_COPY_NAME
[ $? -ne 0 ] && Log_error 1 "Error copying $ARCHIVED_LOG_FILE to $CWD"
done
$COMPRESS -f $CWD/BacKuP.CoPy.${ARCH_LOG_FILE}*
[ $? -ne 0 ] && Log_error 1 "Error compressing $CWD/${ARCH_LOG_FILE}\*"
else
echo " NO archive logs made since this backup started." |$TLOG
fi
else
echo " No logs: $SID set to NOARCHIVELOG." |$TLOG
fi
else
Log_error 1 "$CWD/$LOGFILE_LIST: File not found"
fi
if [ -n "$TAPE" ] ; then #Send disk b/u's to tape
CT=`cat $TMP/$X.CT`
echo `expr $CT + 1` > $TMP/$X.CT
echo " ${CT}- Tar file of $CWD." |$TLOG
cd $CWD
tar cvf $TAPE ./*
mt -t $TAPE offl
fi
}
Copy_online_redologs() {
echo " Copying online redo logs." |$TLOG
cat $CWD/$REDOLOGS_LIST | \
while read REDOLOG
do
LOGBASE=`basename $REDOLOG`
$COMPRESS -c $REDOLOG >$CWD/$LOGBASE.Z
[ $? -ne 0 ] && Log_error 1 "Error compressing $REDOLOG to $CWD/LOGBASE.Z"
done
}
Shutdown_instance() {
echo " (Shutting down $ORACLE_SID `date` )" |$TLOG
Preshut #Pre-Shutdown function
$SQLDBA<<EOF_SID >$TMP/$SID.shut.out 2>&1
connect internal;
shutdown immediate;
host sleep 2
startup restrict;
host sleep 2
shutdown;
exit;
EOF_SID
Error_chk $TMP/$SID.shut.out
}
Startup_instance() {
echo " (Starting up $ORACLE_SID `date` )" |$TLOG
$SQLDBA<<EOF_STARTUP >$TMP/$SID.start.out 2>&1
connect internal;
startup;
exit;
EOF_STARTUP
Error_chk $TMP/$SID.start.out
Poststart #Post-startup function
}
Ps_check() {
while [ `$PS|grep "$WHO.*dd" | grep -v grep | wc -l \
|sed 's/ *//'` -ge $SIMULT ] ; do
sleep 10 #Sleep while more than $SIMULT processes
done
}
#Setup log and global variables
LOG=$TMP/oraback.log
touch $LOG
chown $ORACLE $LOG
TLOG="tee -a $LOG"
LOGFILE_LIST=logfile.list
CONTROLFILE_LIST=controlfile.list
REDOLOGS_LIST=redologs.list
X=$$
TBLSPACE_LIST=tablespace.list
export LOGFILE_LIST CONTROLFILE_LIST TBLSPACE_LIST REDOLOGS_LIST
ps -ef >/dev/null && PS='ps -ef' || PS='ps -aux'
HOST=`uname -n|cut -d. -f1`
SKIP=`grep "^$HOST.master:" $ORACONF | cut -d: -f2`
COLD=`grep "^$HOST.master:" $ORACONF | cut -d: -f3`
[ "$COLD" -gt 0 ] 2>/dev/null && DAY=`date +%d` || DAY=`date +%a`
[ "$COLD" = '*' ] && COLD=$DAY
if [ "$DAY" = "$COLD" ] ; then
TYPE=COLD ; NUM=4
else
TYPE=HOT ; NUM=5
fi
TIME=`grep "^$HOST.master:" $ORACONF | cut -d: -f$NUM`
TAPE=`grep "^$HOST.master:" $ORACONF | cut -d: -f6`
USERS=`grep "^$HOST.master:" $ORACONF | cut -d: -f7`
[ -z "$USERS" ] && USERS=$ORACLE
SIMULT=`grep "^$HOST.master:" $ORACONF | cut -d: -f8`
BACKUPDIR=`grep "^$HOST.master:" $ORACONF |cut -d: -f9`
if [ -n "$TAPE" -a -n "$SIMULT" ] ; then
echo "Simultaneous backups not supported for" |$TLOG
echo "tape devices. (Will do serially.) " |$TLOG
SIMULT='' ; export SIMULT
fi
COMP=`grep "^$HOST.master:" $ORACONF | cut -d: -f10`
MAIL_DBA=`grep "^$HOST.master:" $ORACONF |cut -d: -f11`
WHO=`id | cut -d'(' -f2 | cut -d')' -f1`
if [ -z "$BACKUPDIR" -o -z "$MAIL_DBA" ] ; then
echo "Field 9 or 12 in $ORACONF is empty!" |$TLOG
echo "(9=BACKUPDIR, 12=complete Mail command)" |$TLOG
exit 1
fi
#Build list of SID's
if [ $# -ne 0 -a "$1" != "at" ] ; then
GREP=`echo $* | sed 's/ /:|^/g' | sed 's/$/:/'`
SID_LIST=`egrep "^$GREP" $ORATAB`
ARG=backup
else
SID_LIST=`grep -v '^\#' $ORATAB`
[ $# -eq 0 ] && ARG=backup || ARG=at
fi
if [ -z "`echo $WHO | egrep \"$USERS\"`" ] ; then
echo "WRONG LOGIN!\n"
Usage
fi
if [ ! -s $ORATAB ]; then
Log_error 1 "NO $ORATAB - ABORTING BACKUP"
exit 1
fi
touch $ORACONF ${ORACONF}.2
Log_error $? "Unable to create $ORACONF or $ORACONF.2 (Needed 4 SKIP feature.)"
#Skip b/u if find "skip" in ORACONF
if [ "$SKIP" = "skip" ]; then
sed "s/$HOST.*:skip:/$HOST.master::/" $ORACONF >${ORACONF}.2
mv -f ${ORACONF}.2 $ORACONF
echo "SKIPPING $0 ON $HOST TONIGHT ONLY DUE" |$TLOG
echo "TO THE WORD \"skip\" IN $ORACONF!!" |$TLOG
exit 0
fi
case ${ARG} in
at) #Check $ORACONF for backup and schedule it
if [ -z "$TIME" ]; then
Log_error 1 "No backup time found in $ORACONF"
exit 1
fi
#Apparently, HP-UX doesn't like the -s flag for 'at'
[ `uname -s` = "HP-UX" ] && _S='' || _S='-s'
at $_S $TIME<<EOF_AT
#Start an at job w/Bourne sh
$BINDIR/oraback.sh > $TMP/oraback.out 2>&1
EOF_AT
;;
backup)
#Actually run backup
echo "in backup"
echo "\n====================================" |$TLOG
echo "Began Oracle backup: `date '+%X %D'`" |$TLOG
echo "1" > $TMP/$X.CT
Preback #Pre-backup function
if [ -n "$TAPE" ]; then #If using tape, label
LABEL="$* $X.`date +%D`"
mt -t $TAPE rew
if [ $? -eq 0 ]; then
sleep 2
echo $LABEL > $TAPE
else
Log_error 1 "$TAPE failed to rewind!"
exit 1
fi
if [ "`dd if=$TAPE count=5 2>/dev/null`" = "$LABEL" ]
then
Log_error 1 "$TAPE NOT the non-rewind device"
exit 1
else
mt -t $TAPE rew
mt -t $TAPE fsf 1
echo "Label the tape in $TAPE: \"$LABEL\"." |$TLOG
fi
fi
#For each SID we are backing up
for LINE in $SID_LIST
do
ORACLE_SID=`echo $LINE | cut -d: -f1`
SID=$ORACLE_SID
CWD=$BACKUPDIR/$SID
ORACLE_HOME=`echo $LINE | cut -d: -f2`
[ -f $ORACLE_HOME/bin/sqldba ] && SQLDBA=sqldba || SQLDBA=svrmgrl
export CWD ORACLE_SID SID ORACLE_HOME SQLDBA
mkdir $CWD 2>/dev/null
chmod 775 $CWD ; chgrp dba $CWD
PATH=$ORIG_PATH:$ORACLE_HOME/bin ; export PATH
DBNUM=`$PS|egrep -c "ora_...._$SID"`
if [ $DBNUM -ge 4 ] ; then #Database is UP
DB_UP=2
$SQLDBA <<EOF_XTBS >$TMP/$X.tbs #Make list of tbls
connect internal;
select tablespace_name, file_name \
from sys.dba_data_files;
exit;
EOF_XTBS
nawk 'NF == 2 && $2 ~ "/" {print $1":"$2}' $TMP/$X.tbs > $CWD/$TBLSPACE_LIST
Error_chk $TMP/$X.tbs
$SQLDBA <<EOF_LOGFILE > $TMP/$X.rdl
connect internal;
select * from v\$logfile ;
exit;
EOF_LOGFILE
nawk 'NF == 2 && $2 ~ "/" {print $2}' $TMP/$X.rdl > $CWD/$REDOLOGS_LIST
Error_chk $TMP/$X.rdl
$SQLDBA <<EOF_ARCHIVE >$TMP/$X.lf #List archiving info
connect internal;
archive log list;
exit;
EOF_ARCHIVE
awk '/^Ar/ {print $3}' $TMP/$X.lf > $CWD/logfile.list
Error_chk $TMP/$X.lf
LOGMODE=`grep 'og mode.* ARCHIVELO' $TMP/$X.lf`
[ -n "$LOGMODE" ] && LOGS_UP=20 || LOGS_UP=10
#
#Yet another section that needs to be coded for v.6
# Original version of the "get control files" (which works for v.6)
#
# $SLQDBA<<EOF_SHOW >$TMP/$X.cf #Get name of controlfile
#connect internal;
#show parameters;
#exit;
#EOF_SHOW
# grep '^control_' $TMP/$X.cf | awk '{print $3}' \
# |sed 's/,//' >$CWD/$CONTROLFILE_LIST
# Error_chk $TMP/$X.cf
$SQLDBA <<EOF >$TMP/$X.cf #Get name of controlfile
connect internal;
select name from v_\$controlfile;
exit;
EOF
grep "^/" $TMP/$X.cf > $CWD/$CONTROLFILE_LIST
Error_chk $TMP/$X.cf
else #else Database is DOWN
DB_UP=1 ; LOGS_UP=10
fi
LOGDEST=`grep -v '^$' $CWD/$LOGFILE_LIST`
ARCH_LOG_FILE=`basename $LOGDEST`
ARCH_LOG_DIR=`echo $LOGDEST | sed 's-/[^/]*$--'`
OLD=`ls -t $ARCH_LOG_DIR/${ARCH_LOG_FILE}* | head -3 | tail -1`
BOLD=`basename $OLD`
NOLOG=`grep "^$HOST:$SID:NOARCHIVELOG" $ORACONF`
NOHOTBACKUP=`grep -i "$HOST:$SID:.*nohotbackup" $ORACONF`
[ -n "$NOHOTBACKUP" ] && HOTBACKUP=100 || HOTBACKUP=200
DB_STAT=`expr $HOTBACKUP + $LOGS_UP + $DB_UP`
[ -f $TMP/$X.Fatal ] && DB_STAT=221
if [ "$TYPE" = COLD ] ; then
#IF instance is not already down, and its cold backup
#time, then shut her down, else set a variable to not
#start it when we are done
if [ $DB_STAT -ne 111 o $DB_STAT -ne 211 ] ; then
Switch_logfile_n_backup_controlfile #Function
Shutdown_instance
DB_UP=1 ; LOGS_UP=10
DB_STAT=`expr $HOTBACKUP + $LOGS_UP + $DB_UP`
[ -f $TMP/$X.Fatal ] && DB_STAT=221
else
DONT_START=DONT
fi
fi
echo " -----------------------------" |$TLOG
echo " Backup of $SID Started `date +%T`" |$TLOG
case $DB_STAT in
222) #DB=up, LOGS=on, Backup gets green light
Backup_instance hot #Function
Switch_logfile_n_backup_controlfile #Function
Copy_online_redologs
Copy_control_and_essential_redolog_files ;;
211|111) #Database is completely down
if [ $DB_STAT = 211 -a "$TYPE" = "HOT" ] ; then
Log_error 1 "!!$SID IS DOWN During a Hot Backup!!"
fi
Backup_offline
Copy_online_redologs
Copy_control_and_essential_redolog_files ;;
212) #Database is up, but is set to NOARCHIVELOG
if [ -n "$NOLOG" ] ; then #Logs should be off
if [ -n "`echo "$NOLOG"|grep ':offline'`" ] ; then
Shutdown_instance #Configured for offline
Backup_offline #backup, so do it
Copy_online_redologs
Copy_control_and_essential_redolog_files
Startup_instance
Switch_logfile_n_backup_controlfile
Error_chk $TMP/$X.control
else #else just skip instance
echo " $SID skipped! (NOARCHIVELOG)" |$TLOG
fi
else #This SID has no logs and is not customized
Log_error 1 "BACKUP FAILED (NOARCHIVELOG)!"
fi ;;
112|122) #Word 'nohotbackup' is in ORACONF
echo " Skipping $SID. Found NOHOTBACKUP" |$TLOG
echo " variable in $ORATAB." |$TLOG ;;
221) #Fatal error in early sqldba command
Log_error 1 "No backup performed"
rm $TMP/$X.Fatal ;;
esac
#If instance was not already down before the /bu started,
#then start it up
if [ "$DONT_START" != DONT ] ; then
[ "$TYPE" = COLD ] && Startup_instance
fi
done #done with all $SIDS
Postback #Run Post-backup script if there is one
echo "Ended Oracle backup: `date '+%X %D'`" |$TLOG
echo "====================================" |$TLOG
#If there were errors, mail them to the dba
[ -s $TMP/$X.Mail ] && $MAIL_DBA < $TMP/$X.Mail
rm -f $TMP/$X.* ;; #Remove temporary logs
esac
#End of oraback.sh
#(Back-slashes used for display only. Real config #lines must be continuous) #Host.master:skip:Sun:2330:1730:/dev/rmt/1cn \ # :oracle|curtis:3:/backup:Y:/usr/ucb/Mail -s Backup_Error # oracle@Host,sa@HisPC #Host:instance:NOARCHIVELOG:offline:nohotbackup: saturn.master::Sat:2330:1830:::5:/back:Y:/usr/ucb/Mail -s Backup_Error oracle@saturn,sa@HisPC saturn:OraSatDbA:NOARCHIVELOG:offline:: saturn:OraSatDbB:::nohotbackup: #'.Master' line says that all db's on saturn will get a #cold b/u on Sat at 2330, a hot b/u all other nights at #1830, doing 5 simultaneous copies, backing up to /back #with compression, and will send mail to id's oracle&sa #OraSatDbA has no redologs-will do cold b/u every night #OraSatDbB will be skipped on hot b/u nights, and will #only be backed up by the cold b/u on Saturday apollo.master::*:2330::/dev/rmt/1cn:root::/dump:Y:/usr/ucb/Mail -s \ Backup_Error root@apollo #This host will do a cold b/u every night, and will #use the tape /dev/rmt/1cn. Only root is allowed to #run oraback.sh. (Simult. backups do not work to tape) # End of File