#!/bin/bash
#< BCP data out of specified Sybase tables
# Description:
# Script to bcp data out of specified tables, appending
# to already existing dump files
#
# Usage:
# See usage() function below
#
# Requirements:
# Needs configuration file (specified by ${CONFIG_FILE})
# Format of configuration file
# dbname.owner.tablename
# Inline, whole-line comments, and whitespace, allowed
#
# Exit codes:
# 0 - Success
# 1 - Invalid synatax
# 2 - Not user "sybase"
# 3 - Configuration file does not exist
# 4 - No configuration options to process
# 5 - Gzip operation failed
# 6 - Filesystem capacity check failed
# 7 - DB filesystem not mounted
#
# History:
# 26/04/06 - KWALDRON - Initial Version - 0.1
# 27/04/06 - KWALDRON - Added GZIP code - 0.2
# Added Disk Space Check
# 01/05/06 - KWALDRON - Check that - 0.3
# filesystem is mounted
# 10/05/06 - KWALDRON - Added action to - 0.4
# unix2dos files
# Command initialisation
AWK="/bin/awk"
BASENAME="/bin/basename"
BCP="/opt/sybase/OCS-12_5/bin/bcp"
CAT="/bin/cat"
DATE="/bin/date"
DF="/bin/df"
ECHO="/bin/echo"
GREP="/bin/grep"
GZIP="/bin/gzip"
ID="/usr/bin/id"
MOUNT="/bin/mount"
RM="/bin/rm"
SED="/bin/sed"
SEQ="/usr/bin/seq"
TR="/usr/bin/tr"
UNIX2DOS="/usr/bin/unix2dos"
# Variable initialisation
AUTHOR="Kevin Waldron"
CONFIG_FILE="/usr/local/etc/dump_tables.conf"
MAJOR_VER="0"
MAX_THRESHOLD="40" # If ${OUTPUT_DIR} FS greater than ${MAX_THRESHOLD} % used, do not proceed.
MINOR_VER="4"
MOUNTPOINT="/foo"
OUTPUT_DELIM="|"
OUTPUT_DIR="/backup"
OUTPUT_RS="\\n"
SYBASE_DB="FOODB"
SYBASE_USER="sa"
SYBASE_PASS="passwd"
TEMP_DIR="/var/tmp"
TEMP_FILENAME="${TEMP_DIR}/bcp.$$"
THIS_PROG=$( ${BASENAME} $0 )
VERBOSE=0
VERSION="${MAJOR_VER}.${MINOR_VER}"
YEAR_WRITTEN="2006"
# Function definitions
# Function: echo_stderr
# Arguments: Strings to output
# Returns: nothing
# Purpose: For use when VERBOSE flag set, also to output error messages to, erm, STDERR :-)
echo_stderr() {
${ECHO} "$@" >&2
}
# Function: usage
# Arguments: none
# Returns: nothing
# Purpose: Display brief usage message
usage() {
echo_stderr "${THIS_PROG} [-v|-h]
-v Verbose
-h Print this help message"
}
# Function: check_config_file_exists
# Arguments: none - expects CONFIG_FILE variable to be set
# Returns: 0 - if file exists
# 1 - if file does not exist or cannot be read
# Purpose: Check that configuration file exists and is readable
check_config_file_exists() {
if [ ! -e "${CONFIG_FILE}" ]; then
return 1
else
return 0
fi
}
# Function: check_uid
# Arguments: none
# Returns: 0 - if we are user "sybase"
# 1 - if we're not
# Purpose: Check that we are running the script as user "sybase"
check_uid() {
MY_USERNAME=$( ${GREP} "^[^:]*:[^:]*:$( ${ID} -u ):.*$" /etc/passwd | ${AWK} -vFS=':' '{print $1}' )
if [ "${MY_USERNAME}" != "sybase" ]; then
return 1
else
return 0
fi
}
# 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: check_disk_space
# Arguments: 1 - the filesystem to check
# Returns: 1 on failure (i.e. usage greater than ${MAX_THRESHOLD}
# 0 on success
# Purpose: Check that disk usage does not exceed specified threshold on dump filesystem
check_disk_space() {
FILESYSTEM="$1"
CAPACITY_USED=$( ${DF} ${FILESYSTEM} | ${SED} -n '$p' | ${AWK} '{print $5}' | ${TR} -d '%' )
(( VERBOSE )) && echo_stderr "<-- Capacity on ${FILESYSTEM} [${CAPACITY_USED}%]"
if [ "${CAPACITY_USED}" -gt "${MAX_THRESHOLD}" ]; then
return 1
else
return 0
fi
}
# Function: print_timestamp
# Arguments: none
# Returns: nothing
# Purpose: Print simple timestamp to STDERR
print_timestamp() {
echo_stderr "<-- $( ${DATE} )"
}
# Function: parse_input_file
# Arguments: none
# Returns: nothing
# Purpose: Parses input file. Removes comments and blank lines. Checks for correct
# number of properly delimited fields. Assigns verified lines to array.
parse_input_file() {
COUNTER=0
oldIFS="${IFS}"
IFS='
'
for line in $(< ${CONFIG_FILE} ); do
# ignore comments
[[ $( ${ECHO} "${line}" | ${GREP} "^#" ) ]] && continue
# ignore blank lines
[[ $( ${ECHO} "${line}" | ${GREP} "^[ ]*$" ) ]] && continue
# remove trailing comments
line=$( ${ECHO} "${line}" | ${SED} 's/^\([^#]*\)#.*$/\1/g' )
# remove whitespace
line=$( ${ECHO} "${line}" | ${SED} 's/[ ]//g' )
# check for correct syntax of remaining lines
if [ $( ${ECHO} "${line}" | ${AWK} -vFS='.' '{print NF}' ) -ne "3" ]; then
(( VERBOSE )) && {
echo_stderr "--> Incorrect number of fields in line:"
echo_stderr "<-- ${line}"
echo_stderr "--> Ignoring line above!"
}
continue
fi
CONFIG_ENTRIES[${COUNTER}]="${line}"
(( COUNTER = COUNTER + 1 ))
done
IFS="${oldIFS}"
}
# Function: parse_input_file
# Arguments: none
# Returns: nothing
# Purpose: Process the CONFIG_ENTRIES array, performing the actual bcp operation
# where appropriate. Append onto monthly archive file
# ToDo: Perform archiving/gzipping - need to run a few times first to estimate
# capacity requirements, etc
process_entries() {
(( VERBOSE )) && {
echo_stderr "--> Temporary output file is"
echo_stderr "<-- ${TEMP_FILENAME}"
}
if [ "$(( ${#CONFIG_ENTRIES[@]} ))" -eq "0" ]; then
echo_stderr "--> Error: No configuration entries to process!"
echo_stderr "--> ${TEMP_FILENAME} not created. No action taken. Exiting..."
print_timestamp
exit 4
fi
for i in $( seq 0 $(( ${#CONFIG_ENTRIES[@]} - 1 )) ); do
(( VERBOSE )) && {
echo_stderr "--> Processing entry:"
echo_stderr "<-- CONFIG_ENTRIES[${i}] is ${CONFIG_ENTRIES[${i}]}"
}
RESULT=$( ${BCP} ${CONFIG_ENTRIES[${i}]} out ${TEMP_FILENAME} -U${SYBASE_USER} -P${SYBASE_PASS} -S${SYBASE_DB} -c -t"${OUTPUT_DELIM}" -r ${OUTPUT_RS} )
(( VERBOSE )) && {
echo_stderr "--> BCP RESULT:"
echo_stderr "${RESULT}"
}
${ECHO} "${RESULT}" | ${GREP} -i "unexpected" >/dev/null 2>&1
if [ "$?" -eq "0" ]; then
echo_stderr "--> Warning: Unexpected output from BCP command. Check configuration file for entry"
echo_stderr "<-- ${CONFIG_ENTRIES[${i}]}"
(( VERBOSE )) && {
echo_stderr "--> Are database, owner, and tablename specified correctly?"
}
fi
${ECHO} "${RESULT}" | ${GREP} -i "rows copied" >/dev/null 2>&1
if [ "$?" -eq "0" ]; then
echo_stderr "--> BCP successful for entry"
echo_stderr "<-- ${CONFIG_ENTRIES[${i}]}"
oldIFS="${IFS}"
IFS="."
set -- ${CONFIG_ENTRIES[${i}]}
DBNAME=$1
TABLENAME=$3
IFS="${oldIFS}"
DAY=$( ${DATE} +%d )
MONTH=$( ${DATE} +%b )
YEAR=$( ${DATE} +%Y )
APPEND_FILENAME="${OUTPUT_DIR}/${DBNAME}-${TABLENAME}-${DAY}-${MONTH}-${YEAR}.bcp"
if [ -e "${APPEND_FILENAME}.gz" ]; then
${GZIP} -d ${APPEND_FILENAME}.gz
fi
(( VERBOSE )) && echo_stderr "--> Appending to ${APPEND_FILENAME}"
${CAT} ${TEMP_FILENAME} >> ${APPEND_FILENAME}
(( VERBOSE )) && echo_stderr "--> Removing ${TEMP_FILENAME}"
${RM} ${TEMP_FILENAME}
(( VERBOSE )) && echo_stderr "--> Converting ${APPEND_FILENAME}"
${UNIX2DOS} ${APPEND_FILENAME}
(( VERBOSE )) && echo_stderr "--> Gzipping ${APPEND_FILENAME}"
${GZIP} ${APPEND_FILENAME} >/dev/null 2>&1
if [ "$?" -eq "0" ]; then
(( VERBOSE )) && echo_stderr "--> Gzipped to ${APPEND_FILENAME}.gz"
else
echo_stderr "--> Error: Gzip operation failed!"
exit 5
fi
fi
done
}
#
# First, check arguments
#
if [ "$#" -gt "1" ]; then
usage
exit 1
elif [ "$#" -eq "1" ]; then
case $1 in
-v) VERBOSE=1
;;
-h) usage
exit 1
;;
*) usage
exit 1
;;
esac
fi
(( VERBOSE )) && {
echo_stderr "${THIS_PROG} - Version: ${VERSION}"
echo_stderr "Copyright ${YEAR_WRITTEN} ${AUTHOR}"
}
check_uid
if [ "$?" -ne "0" ]; then
echo_stderr "<-- You must be sybase to run this script"
exit 2
fi
check_config_file_exists
if [ "$?" -ne "0" ]; then
echo_stderr "<-- Configuration file ${CONFIG_FILE} does not exist or is not readable"
exit 3
fi
echo_stderr "--> Checking mountpoint ${MOUNTPOINT}"
print_timestamp
check_mountpoint "${MOUNTPOINT}"
[[ "$?" -eq "0" ]] && {
echo_stderr "--> Mountpoint exists and is mounted"
print_timestamp
} || {
echo_stderr "--> Error: No filesystem mounted on ${MOUNTPOINT}!"
print_timestamp
exit 7
}
echo_stderr "--> Checking capacity on ${OUTPUT_DIR}"
print_timestamp
check_disk_space "${OUTPUT_DIR}"
if [ "$?" -ne "0" ]; then
echo_stderr "<-- Capacity check failed on filesystem housing ${OUTPUT_DIR}"
exit 6
fi
echo_stderr "--> Capacity check complete"
print_timestamp
echo_stderr "--> Input file parsing commenced at:"
print_timestamp
parse_input_file
echo_stderr "--> Input file parsing finished at:"
print_timestamp
echo_stderr "--> Entry processing commenced at:"
print_timestamp
process_entries
echo_stderr "--> Entry proecessing finished at:"
print_timestamp
# All good
exit 0