#!/usr/bin/sh -e
#
# Restore pScheduler from a file
#
# Usage:  restore FILE
#


#
# Sanity Check
#

if [ "$1" = "--help" -o "$#" -ne 1 ]
then
    echo Usage: $(basename $0) FILE
    exit 0
fi

if [ "$(id -u)" != "0" ]
then
    echo "This must be done as root." 1>&2
    exit 1
fi

FILE="$1"


[ -t 0 -a "${FILE}" != "-" ] && INTERACTIVE=true || INTERACTIVE=false
[ -t 1 ] && OUT_INTERACTIVE=true || OUT_INTERACTIVE=false

#
# Give interactive users some warning
#

if $INTERACTIVE && $OUT_INTERACTIVE
then
    cat <<EOF

WARNING

You are about to obliterate the contents of the pScheduler
configuration and database on this host.  Please confirm this by
typing today's date in YYYY-MM-DD format at the prompt below.

EOF
    printf "Confirm (YYYY-MM-DD): "
    read DATE
    if [ "${DATE}" != "$(date +%F)" ]
    then
	printf "\nDate does not match today's date.  Doing nothing.\n\n"
	exit 1
    fi

    echo
fi

#
# Working Directory
#

WORK_DIR="${TMPDIR:-/var/tmp}/$(basename $0).$$"

cleanup()
{
    rm -rf "${WORK_DIR}"

    # The cat prevents output from reaching the screen
    pscheduler internal service restart | cat

    if ${ERROR}
    then
	echo
	echo "WARNING:  Because of errors," \
	    "pScheduler may be in an inoperable state." 1>&2
    fi
}
trap cleanup EXIT

rm -rf "${WORK_DIR}"
mkdir -p "${WORK_DIR}"


#
# Restore the backup
#

ERROR=false

$OUT_INTERACTIVE && printf 'Restoring pScheduler from backup:\n' 1>&2 || true

$OUT_INTERACTIVE && printf '  Unpacking backup...' 1>&2 || true
zcat -f "${FILE}" \
    | (cd "${WORK_DIR}" && tar xf -)
$OUT_INTERACTIVE && printf ' Done.\n' 1>&2 || true


for FILE in package-version config-dir.tar database-dump
do
    if [ ! -f "${WORK_DIR}/${FILE}" ]
    then
	echo "ERROR: Backup file is missing data." 1>&2
	exit 1
    fi
done

# Make sure the local version of pscheduler-server is >= what's in the
# backup.  We can't restore from future versions because what the
# database is set up for may not match what the server expects.

BACKUP_VERSION=$(cat "${WORK_DIR}/package-version")
INSTALLED_VERSION="5.2.4"


# PORT: This requires GNU sort for its -V switch
LESSER_VERSION=$( \
    printf "%s\n%s\n" "${BACKUP_VERSION}" "${INSTALLED_VERSION}" \
    | sort -V \
    | head -1 \
    )

if [ \
    "${BACKUP_VERSION}" != "${INSTALLED_VERSION}" \
    -a "${LESSER_VERSION}" != "${BACKUP_VERSION}" \
    ]
then
    cat 1>&2 <<EOF

This system has a version of the pScheduler server package that is
older than the one used to make this backup.  To restore it, please
upgrade to at least version ${BACKUP_VERSION}.

EOF
    exit 1
fi


# Any error beyond this point could leave the system in an unstable
# state.
ERROR=true

# Hard stopping makes sure the API server is fully disconnected from
# the database.
pscheduler internal service stop --hard | cat

#
# Configuration
#

CONFIG_DIR="/etc/pscheduler"
$OUT_INTERACTIVE && printf '  Restoring configuration...' 1>&2 || true
mv "${CONFIG_DIR}" "${WORK_DIR}/old-config"
mkdir -p "${CONFIG_DIR}"
(cd "${CONFIG_DIR}" && tar xpf "${WORK_DIR}/config-dir.tar")
$OUT_INTERACTIVE && printf ' Done.\n' 1>&2 || true


#
# Database
#

DATABASE="pscheduler"
DATABASE_SAVE="${DATABASE}_save"
$OUT_INTERACTIVE && printf '  Restoring database...' 1>&2 || true
SQL_WORK="${WORK_DIR}/restore.sql"
cat >> "${SQL_WORK}" <<EOF
DO \$\$
BEGIN

    -- Disconnect all users of either database
    PERFORM pg_terminate_backend(pg_stat_activity.pid)
    FROM pg_stat_activity
    WHERE
        pg_stat_activity.datname IN ('${DATABASE}', '${DATABASE_SAVE}')
        AND pid <> pg_backend_pid()     -- Not this connection
        ;

END;
\$\$ LANGUAGE plpgsql;

-- Remove the saved database if it exists
DROP DATABASE IF EXISTS ${DATABASE_SAVE};

-- Hold what's current as saved
DO \$\$
BEGIN
    IF EXISTS (SELECT * FROM pg_catalog.pg_database
               WHERE datname = '${DATABASE}')
    THEN
        ALTER DATABASE ${DATABASE} RENAME TO ${DATABASE_SAVE};
    END IF;
END;
\$\$ LANGUAGE plpgsql;
EOF

cat "${WORK_DIR}/database-dump" >> "${SQL_WORK}"

cat >> "${SQL_WORK}" <<EOF
-- Remove the saved database if there was one.
DROP DATABASE IF EXISTS ${DATABASE_SAVE};
EOF


DB_OUTPUT="${WORK_DIR}/db-output"
LOAD_EXIT=0

postgresql-load "${SQL_WORK}" > "${DB_OUTPUT}" 2>&1 \
    || LOAD_EXIT=$?

if [ "${LOAD_EXIT}" -ne 0 ]
then
    printf "\n\nDatabase restore failed:\n" 1>&2
    cat "${DB_OUTPUT}" 1>&2

    printf "\nRestoring prior database..." 1>&2
    cat > "${SQL_WORK}" <<EOF
DROP DATABASE IF EXISTS ${DATABASE};

DO \$\$
BEGIN
    IF EXISTS (SELECT * FROM pg_catalog.pg_database
               WHERE datname = '${DATABASE_SAVE}')
    THEN
        ALTER DATABASE ${DATABASE_SAVE} RENAME TO ${DATABASE};
    END IF;
END;
\$\$ LANGUAGE plpgsql;
EOF

    LOAD_EXIT=0
    postgresql-load "${SQL_WORK}" > "${DB_OUTPUT}" 2>&1 \
	|| LOAD_EXIT=$?

    if [ "${LOAD_EXIT}" -eq 0 ]
    then
	printf " Done.\n" 1>&2
	ERROR=false
    else
	printf " FAILED." 1>&2
	cat "${DB_OUTPUT}" 1>&2
    fi

    exit 1
fi

$OUT_INTERACTIVE && printf ' Done.\n' 1>&2 || true

$OUT_INTERACTIVE && printf '  Updating database...' 1>&2 || true
# The redirect makes this run quietly.
pscheduler internal db-update < /dev/null
$OUT_INTERACTIVE && printf ' Done.\n' 1>&2 || true

$OUT_INTERACTIVE && printf '  Resetting password...' 1>&2 || true
# The redirect makes this run quietly.
pscheduler internal db-change-password < /dev/null
$OUT_INTERACTIVE && printf ' Done.\n' 1>&2 || true

# If we got here, all was good.
ERROR=false

exit 0
