#!/bin/bash
#< Sybase Backup Script
# Description:
# Backup Script to DUMP Sybase Devices
# Usage:
# Read usage() function below
#
# Exit codes:
# 0 - Success
# 1 - Output file already exists
# 2 - Usage error
# 3 - Output directory does not exist
# 4 - Threshold check failed
# 5 - Errors found after DUMP
# 6 - DUMP status could not be ascertained
# 7 - Should NOT be reached!
# 8 - Database device not found
# 9 - DB filesystem not mounted
#
# History:
# 26/04/06 - KWALDRON - Initial Version - 0.1
# 26/04/06 - KWALDRON - Exit code 8 if DB_NAME.dat not found - 0.2
# 01/05/06 - KWALDRON - Added check_mountpoint() function - 0.3
# Variable Initialisation
AUTHOR="Kevin Waldron"
YEAR_WRITTEN="2006"
VERBOSE="0"
MAJOR_VER="0"
MINOR_VER="2"
MOUNTPOINT="/foo"
VERSION="${MAJOR_VER}.${MINOR_VER}"
SYBASE_DB="FOODB"
SYBASE_DEVICE_PATH="/foo/sybase/devices"
SYBASE_SA="sa"
SYBASE_SA_PASS="passwd"
SYBASE_USER="sybase"
SYBASE_WIDTH="200"
# HTTPD_CONF="/etc/httpd/conf/httpd.conf"
# Command Initialisation
AWK="/bin/awk"
BASENAME="/bin/basename"
BC="/usr/bin/bc"
CAT="/bin/cat"
CHOWN="/bin/chown"
DATE="/bin/date"
DF="/bin/df"
ECHO="/bin/echo"
GREP="/bin/grep"
HOSTNAME="/bin/hostname"
ISQL_BINARY="/opt/sybase/OCS-12_5/bin/isql"
ISQL_OPTIONS="-U${SYBASE_SA} -P${SYBASE_SA_PASS} -S${SYBASE_DB} -w${SYBASE_WIDTH}"
ISQL="${ISQL_BINARY} ${ISQL_OPTIONS}"
LS="/bin/ls"
MOUNT="/bin/mount"
SED="/bin/sed"
SU_BINARY="/bin/su"
SU_OPTIONS="- ${SYBASE_USER} -c"
SU="${SU_BINARY} ${SU_OPTIONS}"
TAIL="/usr/bin/tail"
TRUE="/bin/true"
# Global Variables Used in Functions
# DOCUMENT_ROOT=$( ${GREP} "^DocumentRoot" ${HTTPD_CONF} | ${SED} 's/^DocumentRoot[^"]*"\([^"]*\)"$/\1/' )
THIS_PROG=$( ${BASENAME} $0 )
DAY_OF_WEEK=$( date +"%a" )
OUTPUT_DIR="/backup"
# Function Definitions
# Function: echo_v
# Arguments: Strings to output
# Returns: nothing
# Purpose: For use when VERBOSE flag set - outputs to STDERR - also can be used
# to output error messages
echo_v() {
${ECHO} "$@" >&2
}
# Function: check_mountpoint
# Arguments: 1 - the mountpoint to check
# Returns: 1 on failure (filesystem not mounted)
# 0 on success
# Purpose: Check that the filesystem holding the database devices
check_mountpoint() {
MNTPNT="${1}"
${MOUNT} | ${GREP} ${MNTPNT} >/dev/null 2>&1
[[ "$?" -eq "0" ]] && {
return 0
} || {
return 1
}
}
# Function: create_output_filename
# Arguments: none
# Returns: nothing
# Purpose: Create the filename to be used for the output file
create_output_filename() {
# First, check if the output directory exists, if not, exit
if [ ! -d "${OUTPUT_DIR}" ]; then
echo_v "--> Error: Output directory ${OUTPUT_DIR} does not exist!"
exit 3
else
# always do this - just in case
${CHOWN} -R sybase:sybase ${OUTPUT_DIR}
:
fi
DUMP_FILE="${OUTPUT_DIR}/${DUMP_DB}-${DAY_OF_WEEK}.dmp"
(( VERBOSE )) && {
echo_v "--> Output Dump Filename Formulated:"
echo_v "<-- ${DUMP_FILE}"
}
}
# Function: check_output_file
# Arguments: none
# Returns: nothing
# Purpose: Check if output file already exists or not
check_output_file() {
if [ ! -e "${DUMP_FILE}" ]; then
(( VERBOSE )) && {
echo_v "--> Output file does not exist - ok"
}
else
# output the error whether we're verbose or not
echo_v "--> Error: Output file (${DUMP_FILE}) already exists!"
if [ ! "${FORCE}" ]; then
echo_v "--> Use -f to ignore this message"
echo_v "--> Exiting..."
exit 1
fi
fi
}
# Function: check_disk_usage
# Arguments: none
# Returns: nothing
# Purpose: Check available disk space on the backup volume, and
# handle accordingly, exiting on error unless -f specified
check_disk_usage() {
AVAIL=$( ${DF} -k ${OUTPUT_DIR} | ${SED} -n '$p' | ${AWK} '{print $4}' )
(( VERBOSE )) && {
echo_v "--> Current available space on filesystem containing ${OUTPUT_DIR}"
echo_v "<-- ${AVAIL}Kb"
}
DB_DEV="${SYBASE_DEVICE_PATH}/${DUMP_DB}.dat"
${LS} -l ${DB_DEV} >/dev/null 2>&1
if [ "$?" -ne "0" ]; then
echo_v "--> Error: Cannot find Database Device for ${DUMP_DB}.dat"
exit 8
else
(( VERBOSE )) && {
echo_v "--> Database Device ${DB_DEV} Found OK"
echo_v "<-- $( ${LS} -l ${DB_DEV} )"
}
# The following calculates the current DB .dat file size in Kb
DB_DEV_SIZE=$( ${ECHO} "`${LS} -l ${DB_DEV} | ${AWK} '{print $5}'` / 1024" | ${BC} )
(( VERBOSE )) && {
echo_v "--> Database Device Size in kilobytes"
echo_v "<-- ${DB_DEV_SIZE}Kb"
}
# Check that at least the size of the device is available, although with
# compression, and the fact that the entire device will not likely be used up
# this should be fine
MULTIPLIER="1"
THRESHOLD_SIZE=$( ${ECHO} "${DB_DEV_SIZE} * ${MULTIPLIER}" | ${BC} )
(( VERBOSE )) && {
echo_v "--> Threshold for Available Space"
echo_v "<-- ${THRESHOLD_SIZE}Kb"
}
if [ "${THRESHOLD_SIZE}" -gt "${AVAIL}" ]; then
echo_v "--> Error: Not enough space available on Dump Device for backup!"
if [ ! "${FORCE}" ]; then
echo_v "--> Use -f to ignore this message"
echo_v "--> Exiting..."
exit 4
fi
fi
fi
}
# Function: dump_database
# Arguments: none
# Returns: nothing
# Purpose: DUMP DATABASE to our specified ${DUMP_FILE}
dump_database() {
COMPRESSION_LEVEL="9"
(( VERBOSE )) && {
echo_v "--> Dumping database ${DUMP_DB} to ${DUMP_FILE}"
echo_v "<-- $(date)"
${SU} "
${ISQL} <<EndOfSQL
USE master
GO
DUMP DATABASE ${DUMP_DB} TO \"compress::${COMPRESSION_LEVEL}::${DUMP_FILE}\"
GO
QUIT
EndOfSQL"
} || {
echo_v "--> Dumping database ${DUMP_DB} to ${DUMP_FILE}"
echo_v "<-- $(date)"
${SU} "
${ISQL} <<EndOfSQL >/dev/null 2>&1
USE master
GO
DUMP DATABASE ${DUMP_DB} TO \"compress::${COMPRESSION_LEVEL}::${DUMP_FILE}\"
GO
QUIT
EndOfSQL"
}
# We'll want to output these messages whether verbose or not
${TAIL} -n 3 ${SYBASE_DEVICE_PATH}/backupserver.log | ${GREP} -i "error" >/dev/null 2>&1
if [ "$?" -eq "0" ]; then
echo_v "--> Found Errors in Backup Log - DUMP may have failed!"
echo_v "--> Check ${SYBASE_DEVICE_PATH}/backupserver.log"
echo_v "<-- $(date)"
exit 5
else
${TAIL} -n 3 ${SYBASE_DEVICE_PATH}/backupserver.log | ${GREP} -i "DUMP is complete" >/dev/null 2>&1
if [ "$?" -eq "0" ]; then
echo_v "--> Database DUMP of ${DUMP_DB} completed successfully!"
echo_v "<-- $(date)"
exit 0
else
echo_v "--> DUMP status could not be determined!"
echo_v "--> Check ${SYBASE_DEVICE_PATH}/backupserver.log"
echo_v "<-- $(date)"
exit 6
fi
fi
}
# Function: usage
# Arguments: none
# Returns: nothing
# Purpose: Display brief usage message to STDERR
usage() {
${ECHO} "Usage: ${THIS_PROG} [-v][-f][-h] dump_db
-f Force
-v Verbose Mode
-h Display This Usage Message
dump_db Sybase DB to Dump" >&2
}
# Argument Processing - This is not as thorough as it could be,
# but as this script is not intended to be called by anything
# other than cron, simple argument checking will suffice
if [ "$#" -lt "1" ]; then # 1 argument, dump_db, mandatory
usage
exit 2
fi
while [ ${TRUE} ]; do
case $1 in
-f) FORCE="1"
shift
;;
-v) VERBOSE="1"
shift
;;
-h) usage
exit 0
;;
*) break
;;
esac
done
# OK, now, if there is only one argument, proceed
if [ "$#" -ne "1" ]; then
usage
exit 2
else
DUMP_DB="$1"
fi
(( VERBOSE )) && {
echo_v "${THIS_PROG} - Version: ${VERSION}"
echo_v "Copyright ${YEAR_WRITTEN} ${AUTHOR}"
}
(( VERBOSE )) && {
echo_v "--> Verbose mode set"
}
(( FORCE && VERBOSE )) && {
echo_v "--> Force mode set"
}
(( VERBOSE )) && {
echo_v "--> We will be dumping database:"
echo_v "<-- ${DUMP_DB}"
}
# Now we need to check if the output file already exists - if it does, we will
# not overwrite it - this may be changed at a later date. We will perform a 7
# day dump rotation.
create_output_filename
check_output_file
check_mountpoint "${MOUNTPOINT}"
echo_v "--> Checking mountpoint ${MOUNTPOINT}"
check_mountpoint "${MOUNTPOINT}"
[[ "$?" -eq "0" ]] && {
echo_v "--> Mountpoint exists and is mounted"
} || {
echo_v "--> Error: No filesystem mounted on ${MOUNTPOINT}!"
exit 9
}
# Perform a check of the available disk capacity on /backup
check_disk_usage
# Time to dump
dump_database
# Not reached!
exit 7