#!/bin/bash
### Backup specified PostgreSQL Databases to file
# Original script:	MySQL Backup Script
#			VER. 2.5 - http://sourceforge.net/projects/automysqlbackup/
#			Copyright (c) 2002-2003 wipe_out@lycos.co.uk
# -----------
# CHANGELOG
# -----------
# 090106 - Hacked by PHS to do PostgreSQL Databases instead of MySQL
# 090819 - Minor fixes, added configuration option for CONNECT_DB
# 090914 - Removed use of $PATH in favour of using explicitly set path variables within options.conf
# 091210 - Added support for using a password for the database user. See the DB_PASS config option
#
# 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.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#

# Version Number
VER=0.9.7

# Path to options file
OPTIONS_FILE='/etc/pgsql-backup/options.conf'

#=====================================================================
#=====================================================================
#=====================================================================
#
# YOU SHOULD NOT NEED TO MODIFY ANYTHING BELOW HERE!!
#
#=====================================================================
#=====================================================================
#=====================================================================
#
# Make sure our config file exists
if [ -e "$OPTIONS_FILE" ] ; then
	source $OPTIONS_FILE
else
	echo "Failed to find the options file: $OPTIONS_FILE"
	exit 1
fi
#
# Make all config from options.conf READ-ONLY
declare -r USERNAME
declare -r DBHOST
declare -r BACKUPDIR
declare -r MAILCONTENT
declare -r MAXATTSIZE
declare -r MAILADDR
declare -r DBEXCLUDE
declare -r CREATE_DATABASE
declare -r DOWEEKLY
declare -r COMP
declare -r LATEST
declare -r PG_DUMP
declare -r PSQL
declare -r GZIP
declare -r BZIP2
declare -r RM
declare -r MKDIR
declare -r DATE
declare -r LN
declare -r SED
declare -r DU
declare -r GREP
declare -r CAT
declare -r MAILX

# Make sure our binaries are good
MISSING_BIN=''
[ -x "$PG_DUMP" ]	|| MISSING_BIN="$MISSING_BIN pgdump not found: $PG_DUMP\n"
[ -x "$PSQL" ]		|| MISSING_BIN="$MISSING_BIN psql not found: $PSQL\n"
[ -x "$RM" ]		|| MISSING_BIN="$MISSING_BIN rm not found: $RM\n"
[ -x "$MKDIR" ]		|| MISSING_BIN="$MISSING_BIN mkdir not found: $MKDIR\n"
[ -x "$DATE" ]		|| MISSING_BIN="$MISSING_BIN date not found: $DATE\n"
[ -x "$LN" ]		|| MISSING_BIN="$MISSING_BIN ln not found: $LN\n"
[ -x "$SED" ]		|| MISSING_BIN="$MISSING_BIN sed not found: $SED\n"
[ -x "$DU" ]		|| MISSING_BIN="$MISSING_BIN du not found: $DU\n"
[ -x "$GREP" ]		|| MISSING_BIN="$MISSING_BIN grep not found: $GREP\n"
[ -x "$CAT" ]		|| MISSING_BIN="$MISSING_BIN cat not found: $CAT\n"
[ -x "$MAILX" ]		|| MISSING_BIN="$MISSING_BIN mail not found: $MAILX\n"
[ ! -x "$GZIP" -a "$COMP" = 'gzip' ]	&& MISSING_BIN="$MISSING_BIN gzip not found: $GZIP\n"
[ ! -x "$BZIP2" -a "$COMP" = 'bzip2' ]	&& MISSING_BIN="$MISSING_BIN bzip2 not found: $BZIP2\n"
if [ -n "$MISSING_BIN" ] ; then
	echo "Some required programs were not found. Please check $OPTIONS_FILE to ensure correct paths are set."
	echo "The missing files are:"
	echo -e $MISSING_BIN
fi

#PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/postgres/bin 
FULLDATE=`$DATE +%Y-%m-%d_%Hh%Mm`			# Datestamp e.g 2002-09-21
DOW=`$DATE +%A`						# Day of the week e.g. Monday
DNOW=`$DATE +%u`					# Day number of the week 1 to 7 where 1 represents Monday
DOM=`$DATE +%d`						# Date of the Month e.g. 27
M=`$DATE +%B`						# Month e.g January
W=`$DATE +%V`						# Week Number e.g 37
LOGFILE=$BACKUPDIR/$DBHOST-`$DATE +%N`.log		# Logfile Name
LOGERR=$BACKUPDIR/ERRORS_$DBHOST-`$DATE +%N`.log	# Logfile Name
BACKUPFILES=""
OPT="--blobs --format=${DUMPFORMAT}"			# OPT string for use with pg_dump

# Create required directories
if [ ! -e "$BACKUPDIR" ] ; then		# Check Backup Directory exists.
	$MKDIR -p "$BACKUPDIR"
fi

if [ ! -e "$BACKUPDIR/daily" ] ; then	# Check Daily Directory exists.
	$MKDIR -p "$BACKUPDIR/daily"
fi

if [ ! -e "$BACKUPDIR/weekly" ]	; then	# Check Weekly Directory exists.
	$MKDIR -p "$BACKUPDIR/weekly"
fi

if [ ! -e "$BACKUPDIR/monthly" ] ; then	# Check Monthly Directory exists.
	$MKDIR -p "$BACKUPDIR/monthly"
fi

if [ "$LATEST" = "yes" ] ; then
if [ ! -e "$BACKUPDIR/latest" ]	; then # Check Latest Directory exists.
		$MKDIR -p "$BACKUPDIR/latest"
	fi
	eval $RM -f "$BACKUPDIR/latest/*"
fi

# IO redirection for logging.
touch $LOGFILE
exec 6>&1           # Link file descriptor #6 with stdout.
                    # Saves stdout.
exec > $LOGFILE     # stdout replaced with file $LOGFILE.
touch $LOGERR
exec 7>&2           # Link file descriptor #7 with stderr.
                    # Saves stderr.
exec 2> $LOGERR     # stderr replaced with file $LOGERR.

# Output Extension (depends on the output format)
if [ "$DUMPFORMAT" = 'tar' ] ; then
	OUTEXT='tar'
elif [ "$DUMPFORMAT" = 'plain' ] ; then
	OUTEXT='sql'
else
	echo "Invalid output format configured. Defaulting to 'plain'"
	DUMPFORMAT='plain'
	OUTEXT='sql'
fi
OPT="$OPT --format=${DUMPFORMAT}"

#########################
# Functions

# Database dump function
dbdump () {
	export PGPASSWORD="$DB_PASS"
	$PG_DUMP --username=$USERNAME --host=$DBHOST $OPT $1 > $2
	return $?
}

# Compression function plus latest copy
SUFFIX=""
compression () {
	if [ "$COMP" = "gzip" ]; then
		$GZIP -f "$1"
		echo
		echo Backup Information for "$1"
		$GZIP -l "$1.gz"
		SUFFIX=".gz"
	elif [ "$COMP" = "bzip2" ]; then
		echo Compression information for "$1.bz2"
		$BZIP2 -f -v $1 2>&1
		SUFFIX=".bz2"
	else
		echo "No compression option set, check advanced settings"
	fi
	if [ "$LATEST" = "yes" ]; then
		#cp $1$SUFFIX "$BACKUPDIR/latest/"
		$LN $1$SUFFIX "$BACKUPDIR/latest/"
	fi	
	return 0
}

#########################################

# Run command before we begin
if [ "$PREBACKUP" ]
	then
	echo ======================================================================
	echo "Prebackup command output."
	echo
	eval $PREBACKUP
	echo
	echo ======================================================================
	echo
fi


if [ "$SEPDIR" = "yes" ]; then # Check if CREATE DATABSE should be included in Dump
	if [ "$CREATE_DATABASE" = "no" ]; then
		OPT="$OPT --no-create"
	else
		OPT="$OPT --create"
	fi
fi

# Hostname for LOG information
if [ "$DBHOST" = "localhost" ]; then
	HOST=`hostname`
	if [ "$SOCKET" ]; then
		OPT="$OPT --host=$SOCKET"
	fi
else
	HOST=$DBHOST
fi

# If backing up all DBs on the server
if [ "$DBNAMES" = "all" ]; then
	export PGPASSWORD="$DB_PASS"
	DBNAMES=`$PSQL --username=$USERNAME --dbname=$CONNECT_DB -P format=Unaligned -tqc 'SELECT datname FROM pg_database;' | $SED 's/ /%/g'`

	# If DBs are excluded
	for exclude in $DBEXCLUDE ; do
		DBNAMES=`echo $DBNAMES | $SED "s/\b$exclude\b//g"`
	done

        MDBNAMES=$DBNAMES
fi

echo ======================================================================
echo AutoPostgreSQLBackup VER $VER
echo   Based on: http://sourceforge.net/projects/automysqlbackup/
echo 
echo Backup of PostgreSQL Database Server - $HOST
echo ======================================================================

echo Backup Start Time `$DATE`
echo ======================================================================
# Monthly Full Backup of all Databases
if [ $DOM = "01" ]; then
	for MDB in $MDBNAMES ; do
		# Prepare $DB for using
	        MDB="`echo $MDB | $SED 's/%/ /g'`"

		if [ ! -e "$BACKUPDIR/monthly/$MDB" ] ; then		# Check Monthly DB Directory exists.
			$MKDIR -p "$BACKUPDIR/monthly/$MDB"
		fi
		echo Monthly Backup of $MDB...
		dbdump "${MDB}" "${BACKUPDIR}/monthly/${MDB}/${MDB}_${FULLDATE}.${M}.${MDB}.${OUTEXT}"
		compression "${BACKUPDIR}/monthly/${MDB}/${MDB}_${FULLDATE}.${M}.${MDB}.${OUTEXT}"
		BACKUPFILES="${BACKUPFILES} ${BACKUPDIR}/monthly/${MDB}/${MDB}_${FULLDATE}.${M}.${MDB}.${OUTEXT}${SUFFIX}"
		echo ----------------------------------------------------------------------
	done
fi

for DB in $DBNAMES ; do
	# Prepare $DB for using
	DB="`echo $DB | $SED 's/%/ /g'`"
	
	# Create Seperate directory for each DB
	if [ ! -e "$BACKUPDIR/daily/$DB" ] ; then		# Check Daily DB Directory exists.
		$MKDIR -p "$BACKUPDIR/daily/$DB"
	fi
	
	if [ ! -e "$BACKUPDIR/weekly/$DB" ] ; then		# Check Weekly DB Directory exists.
		$MKDIR -p "$BACKUPDIR/weekly/$DB"
	fi
	
	# Weekly Backup
	if [ $DNOW = $DOWEEKLY ]; then
		echo Weekly Backup of Database \( $DB \)
		echo Rotating 5 weeks Backups...
			if [ "$W" -le 05 ];then
				REMW=`expr 48 + $W`
			elif [ "$W" -lt 15 ];then
				REMW=0`expr $W - 5`
			else
				REMW=`expr $W - 5`
			fi
		eval $RM -f "$BACKUPDIR/weekly/$DB/${DB}_week.$REMW.*" 
		echo
		dbdump "$DB" "$BACKUPDIR/weekly/$DB/${DB}_week.$W.$FULLDATE.${OUTEXT}"
		compression "$BACKUPDIR/weekly/$DB/${DB}_week.$W.$FULLDATE.${OUTEXT}"
		BACKUPFILES="$BACKUPFILES $BACKUPDIR/weekly/$DB/${DB}_week.$W.$FULLDATE.${OUTEXT}$SUFFIX"
		echo ----------------------------------------------------------------------
	
	# Daily Backup
	else
		echo Daily Backup of Database \( $DB \)
		echo Rotating last weeks Backup...
		eval $RM -f "$BACKUPDIR/daily/$DB/*.$DOW.*" 
		echo
		dbdump "$DB" "$BACKUPDIR/daily/$DB/${DB}_$FULLDATE.$DOW.${OUTEXT}"
		compression "$BACKUPDIR/daily/$DB/${DB}_$FULLDATE.$DOW.${OUTEXT}"
		BACKUPFILES="$BACKUPFILES $BACKUPDIR/daily/$DB/${DB}_$FULLDATE.$DOW.${OUTEXT}$SUFFIX"
		echo ----------------------------------------------------------------------
	fi
done

echo Backup End `$DATE`
echo ======================================================================

echo Total disk space used for backup storage..
echo Size - Location
echo `$DU -hs "$BACKUPDIR"`
echo
echo ======================================================================

# Run command when we're done
if [ "$POSTBACKUP" ]
	then
	echo ======================================================================
	echo "Postbackup command output."
	echo
	eval $POSTBACKUP
	echo
	echo ======================================================================
fi

#Clean up IO redirection
exec 1>&6 6>&-      # Restore stdout and close file descriptor #6.
exec 1>&7 7>&-      # Restore stdout and close file descriptor #7.

if [ "$MAILCONTENT" = "files" ] ; then
	if [ -s "$LOGERR" ] ; then
		# Include error log if is larger than zero.
		BACKUPFILES="$BACKUPFILES $LOGERR"
		ERRORNOTE="WARNING: Error Reported - "
	fi
	# Get backup size
	ATTSIZE=`$DU -c $BACKUPFILES | $GREP "[[:digit:][:space:]]total$" | $SED s/\s*total//`
	if [ $MAXATTSIZE -lt $ATTSIZE ] ; then
		$CAT "$LOGFILE" | $MAILX -s "WARNING! - PostgreSQL Backup exceeds set maximum attachment size on $HOST - $FULLDATE" $MAILADDR
	fi
elif [ "$MAILCONTENT" = "log" ] ; then
	$CAT "$LOGFILE" | $MAILX -s "PostgreSQL Backup Log for $HOST - $FULLDATE" $MAILADDR
	if [ -s "$LOGERR" ] ; then
		$CAT "$LOGERR" | $MAILX -s "ERRORS REPORTED: PostgreSQL Backup error Log for $HOST - $FULLDATE" $MAILADDR
	fi	
elif [ "$MAILCONTENT" = "quiet" ] ; then
	if [ -s "$LOGERR" ] ; then
		$CAT "$LOGERR" | $MAILX -s "ERRORS REPORTED: PostgreSQL Backup error Log for $HOST - $FULLDATE" $MAILADDR
		$CAT "$LOGFILE" | $MAILX -s "PostgreSQL Backup Log for $HOST - $FULLDATE" $MAILADDR
	fi
else
	if [ -s "$LOGERR" ] ; then
		$CAT "$LOGFILE"
		echo
		echo "!!!!!! WARNING !!!!!!"
		echo "Errors reported during AutoPostgreSQLBackup execution.. Backup failed"
		echo "Error log below.."
		$CAT "$LOGERR"
	else
		$CAT "$LOGFILE"
	fi	
fi

if [ -s "$LOGERR" ] ; then
	STATUS=1
else
	STATUS=0
fi

# Clean up Logfile
eval $RM -f "$LOGFILE"
eval $RM -f "$LOGERR"

exit $STATUS
