#!/bin/sh
set -e
#
# Script postgresql-dump
#
# This script is used to dump and destroy a database when PostgreSQL is updated
# to an incompatible database format.

function syntax() {
	echo Usage: `basename $0` [-c] [-d] [-i] [-l] [-p directory] -t target [-v] [-x]
	echo "       where target is a file or tape device."
	echo "      -c        list the ASCII dump to screen for the user to check"
	echo "      -d        destroy the old database framework"
	echo "      -i        create a new database framework with initdb"
	echo "      -l        load the ASCII dump into the new database"
	echo "      -p dir    preserve the old database file under directory <dir>"
	echo "      -t target create the ASCII dump on file or device <target>"
	echo "      -v        verbose output"
	echo "      -x        do not dump the old database\; forces -c"
	exit 1
}

function add_option() {
	if [ -n "$OPTIONS" ]
	then
		OPTIONS=$OPTIONS\ $1
	else
		OPTIONS=$1
	fi
	shift

	while [ -n "$1" ]
	do
		OPTIONS=$OPTIONS\ $1
		shift
	done
}

function checkpath() {
	dir=$1
	while [ -n "`echo $dir | awk -F/ '{print $2}'`" ]
	do
		if [ $dir = $PGDATA ]
		then
			echo $1 is or is under PGDATA. It will be destroyed when PGDATA is cleared. >&2
			echo Specify a different path.>&2
			exit 11
		fi
		dir=`dirname $dir`
	done
}

SHELL=/bin/sh
current=6.3          # Current PostgreSQL database version
check=
destroy=
create=
load=
preserve=
verbose=
exclude_dump=

# Read command line
while [ $OPTIND -le $# ]
do
	getopts cdilp:t:vx arg
	case "$arg"
		in
		c)
			check=TRUE;;
		d)
			destroy=TRUE;;
		i)
			create=TRUE;;
		l)
			load=TRUE;;
		p)
			preserve=$OPTARG;;
		t)
			target=$OPTARG;;
		v)
			verbose=TRUE;;
		x)
			exclude_dump=TRUE;;
		*)
			syntax;;
	esac
done

if [ -z "${exclude_dump}" ]
then
	check=TRUE
fi

if [ -z "${destroy}" ]
then
	preserve=
fi

if [ -z "${target}" ]
then
	echo No target file or device specified >&2
	syntax
else
	checkpath ${target}
fi

if [ -f /etc/postgresql/postmaster.init ]
then
	. /etc/postgresql/postmaster.init
fi

PGDATA=${POSTGRES_DATA:=/var/postgres/data}
PGLIB=/usr/lib/postgresql
export PGLIB PGDATA

# Since this script has to dump the entire database, it must be run by the
# PostgreSQL superuser
if [ `id | cut -d\( -f2 | cut -d\) -f1` != postgres ]
then
	echo $0: this script must be run by the user postgres. >&2
	exit 2
fi

# Make sure either the ${PGDATA} directory or the dump target exists
dumped=
if [ ! -d ${PGDATA:=/var/postgres/data}/base ]
then
	if [ ! -f ${target} ]
	then
		echo The PostgreSQL data directory, ${PGDATA}, and the dump file are missing. >&2
		echo >&2
		echo Please check the settings in /etc/postgresql/postmaster.init >&2
		exit 3
	else
		dumped=TRUE
		destroy=""
		preserve=""
	fi
fi
if [ -n "${preserve}" ]
then
	checkpath ${preserve}
fi


load_only=
if [ -z "${dumped}" ]
then
	# Find out what the current database format version is
	installed=`cat ${PGDATA}/PG_VERSION`
	must_dump=

	if [ -z "${installed}" ]
	then
		echo "There is no existing installation of Postgresql" >&2
		if [ -d ${PGDATA}/template1 ]
		then
			echo However, there is a template1 directory in ${PGDATA}, which >&2
			echo is unexpected. Please analyse and repair the directory by >&2
			echo hand. This script is unable to handle it in its present >&2
			echo state. >&2
			echo >&2
			exit 4
		fi
	else
		# check the current and installed version numbers
		majorc=`echo ${current} | cut -d. -f1`
		minorc=`echo ${current} | cut -d. -f2`
		majori=`echo ${installed} | cut -d. -f1`
		minori=`echo ${installed} | cut -d. -f2`

		if [ ${majorc} -eq ${majori} -a ${minorc} -eq ${minori} ]
		then
			echo The PostgreSQL database format has not changed\; there is >&2
			echo no need to do a dump and restore with this script. >&2
			load_only=TRUE
		else
			must_dump=TRUE
			echo The PostgreSQL database format has changed, and it is >&2
			echo necessary to dump your data before you can use the new >&2
			echo release of PostgreSQL. >&2
			echo >&2
		fi
	fi

	if [ -n "${must_dump}" -a -z "${exclude_dump}" ]
	then
		# Look for existing postgresql binaries, for the previous release
		export BINDIR=/usr/lib/postgresql/dumpall/${installed}

		if [ ! -d ${BINDIR} ]
		then
			echo There are no binaries for dumping database format ${installed} >&2
			exit 5
		fi

		if [ -n "${verbose}" ]
		then
			echo "Stopping and restarting the postmaster" >&2
		fi
		pm_pid=`ps ax | grep -s postmaster | grep -v grep | awk '{print $1}'`
		kill $pm_pid

		OPTIONS=
		
		# American or European date format
		if [ "${PGDATESTYLE}" != American ]
		then
		    OPTIONS="-o -e"
		fi

		POSTMASTER=${BINDIR}/postmaster
		POSTGRES=${BINDIR}/postgres
		PORT="-p 5431"    # change the port to stop normal users connecting

		${POSTMASTER} -S -D ${PGDATA} ${AUTH} ${PORT} ${OPTIONS}

		new_pm_pid=`ps ax | grep -s postmaster | grep -v grep | awk '{print $1}'`
		if [ A${new_pm_pid} = A ]
		then
			echo "Failed to start the postmaster" 2>&1
			exit 7
		fi
		if [ A${new_pm_pid} = A${pm_pid} ]
		then
			echo "Failed to stop the running postmaster" 2>&1
			exit 6
		fi

		if [ -n "${verbose}" ]
		then
			echo "Dumping the database to ${target}" >&2
		fi
		/usr/lib/postgresql/dumpall/pg_dumpall >$target

		if [ -n "${verbose}" ]
		then
			echo "Killing the postmaster" >&2
		fi
		kill $new_pm_pid

	fi
fi

if [ -n "${check}" ]
then
	if echo ${target} | grep -q '^/dev/'
	then
		mt -f ${target} rewind
	else
		if [ ! -f ${target} ]
		then
			echo ASCII dump file ${target} does not exist.
			exit 13
		fi
	fi
	(echo This is the ASCII output of the dump for you to check:
	 echo
	 cat ${target}) | ${PAGER:-more}
	if [ -n "${destroy}" ]
	then
		echo -n On the basis of this dump, is it OK to delete the old database? [y/n]\ 
		read x
		case $x
			in
			y|Y|Yes|yes|YES)
				echo Destroying old database...
				;;
			*)
				exit 11;;
		esac
	else
		echo -n Is this dump suitable for loading into the new database? [y/n]\ 
		read x
		case $x
			in
			y|Y|Yes|yes|YES)
				;;
			*)
				exit 11;;
		esac
	fi
fi

if [ -n "${destroy}" ]
then
	if [ -n "${preserve}" ]
	then
		# Copy the database tree to ${preserve} -- just in case...
		if [ -n "${verbose}" ]
		then
			echo "Moving ${PGDATA} to ${preserve}" >&2
		fi

		if [ ! -d ${preserve} ]
		then
			mkdir ${preserve} || exit 8
		fi

		# Use cp rather than mv in case we are being asked
		# to move between filesystems.
		cp -a $PGDATA/* ${preserve} ||
			(echo Could not copy ${PGDATA}/\* to ${preserve} >&2; exit 9)
	fi
	if [ -n "${verbose}" ]
	then
		echo "Deleting ${PGDATA}" >&2
	fi

	rm -rf ${PGDATA}/* ||
		(echo Could not delete ${PGDATA}/\* >&2
		exit 10)
fi

set +e
unset POSTGRES
unset POSTMASTER
unset PGLIB
unset PGDATA
unset PORT
unset BINDIR
. /etc/postgresql/postgresql.env
set -e

if [ -n "${create}" ]
then
	if [ -n "${verbose}" ]
	then
		echo "Creating the new database structure" >&2
	fi

	initdb || echo :
fi

if [ -n "${verbose}" ]
then
	echo "Checking that the postmaster is running" >&2
fi

if [ -n "${load}" ]
then
	if [ -z "`ps ax | grep postmaster | grep -v grep`" ]
	then
		# start up the postmaster
		. /etc/postgresql/postmaster.init
		PGDATA=${POSTGRES_DATA:-/var/postgres/data}
		PGLIB=/usr/lib/postgresql
		export PGLIB PGDATA
		POSTMASTER=${PGLIB}/bin/postmaster
		POSTGRES=${PGLIB}/bin/postgres
		OPTIONS=

		if [ -n "${verbose}" ]
		then
			echo "Starting the postmaster" >&2
		fi

		# Shared-memory buffers
		if [ ! -z "${PGBUFFERS}" ]
		then
		    BUFFERS="-B ${PGBUFFERS}"
		fi

		# Debugging level
		if [ ! -z "${PGDEBUG}" ]
		then
		    DEBUGLEVEL="-d ${PGDEBUG}"
		fi

		# TCP port
		if [ ! -z "${PGPORT}" ]
		then
		    PORT="-p ${PGPORT}"
		fi
		
		# Backend options
		# Query echoing
		if [ "${PGECHO}" = yes ]
		then
		    add_option -E
		fi

		# Timing stats
		if [ "${PGSTATS}" = yes ]
		then
		    add_option -s
		fi

		# Fsync()
		if [ "${PGFSYNC}" = no ]
		then
		    add_option -F
		fi

		# Sort memory
		if [ ! -z "${PGSORTMEM}" ]
		then
		    add_option -S ${PGSORTMEM}
		fi

		# American or European date format
		if [ "${PGDATESTYLE}" != American ]
		then
		    add_option -e
		fi

		if [ ! -z "${OPTIONS}" ]
		then
		    if [ `echo "${OPTIONS}" | wc -w` -gt 1 ]
		    then
			    OPTIONS='-o '\"${OPTIONS}\"
		    else
			    OPTIONS='-o '${OPTIONS}
		    fi
		fi

		# Make sure that we don't get started if there is no options file.
		# We certainly don't want to get started if the executable is missing.
		if [ ! -x ${POSTMASTER} ]
		then
		    echo No postmaster executable for postgresql
		    exit 3
		fi

		# Ready to go: stand clear...
		echo Starting PostgreSQL postmaster
        	echo ${POSTMASTER} -i -d 2 -b ${POSTGRES} ${BUFFERS} \
		    -D ${PGDATA} ${DEBUGLEVEL} ${PORT} ${OPTIONS} \>/var/log/postgreslog 2\>\&1 \&
        	nohup ${POSTMASTER} -i -d 2 -b ${POSTGRES} ${BUFFERS} \
		    -D ${PGDATA} ${DEBUGLEVEL} ${PORT} ${OPTIONS} >/var/log/postgreslog 2>&1 &
	fi

	if [ -n "${verbose}" ]
	then
		echo "Loading the database from ${target}" >&2
	fi

	echo PGPORT is \`$PGPORT\`
	psql -e template1 <${target}
fi

exit 0
