#!/bin/bash
# $Id$
#
# Relax-and-Recover
#
#    Relax-and-Recover 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.

#    Relax-and-Recover 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 Relax-and-Recover; if not, write to the Free Software
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#
# Authors:
# Schlomo Schapiro <rear at schlomo.schapiro dot org> [GSS]
# Gratien D'haese  <gdha at sourceforge dot net> [GD]
# Jeroen Hoekx <jeroen.hoekx at hamok dot be> [JH]
# Dag Wieers <dag at wieers dot com> [DAG]

# Versioning
PRODUCT="Relax-and-Recover"
PROGRAM=${0##*/}
VERSION=1.17.2
RELEASE_DATE=Git

STARTTIME=$SECONDS

# Allow workflows to set the exit code to a different value.
EXIT_CODE=0

# Find out if we're running from checkout
SCRIPT_FILE="$(readlink -f $(type -p "$0" || echo "$0"))"
if [[ "$SCRIPT_FILE" != "$(readlink -f /usr/sbin/$PROGRAM)" ]]; then
    REAR_DIR_PREFIX=${SCRIPT_FILE%/usr/sbin/$PROGRAM}
fi

# Program directories - they must be set here. Everything else is then dynamic
SHARE_DIR="$REAR_DIR_PREFIX/usr/share/rear"
CONFIG_DIR="$REAR_DIR_PREFIX/etc/rear"
VAR_DIR="$REAR_DIR_PREFIX/var/lib/rear"
LOG_DIR="$REAR_DIR_PREFIX/var/log/rear"
CMD_OPTS=( "$@" )

# initialize defaults
STEPBYSTEP=
SIMULATE=
VERBOSE=
DEBUG=
KEEP_BUILD_DIR=
RECOVERY_MODE=

# Parse options
OPTS="$(getopt -n $PROGRAM -o "c:dDhsSvVr:" -l "help,version" -- "$@")"
if (( $? != 0 )); then
	echo "Try \`$PROGRAM --help' for more information."
	exit 1
fi

eval set -- "$OPTS"
while true; do
	case "$1" in
		(-h|--help) WORKFLOW="help";;
		(-V|--version) echo -e "$PRODUCT $VERSION / $RELEASE_DATE"; exit 0;;
		(-v) VERBOSE=1;;
		(-c) CONFIG_DIR="$2"; shift;;
		(-d) DEBUG=1; VERBOSE=1;;
		(-D) DEBUGSCRIPTS=1;;
		(-s) SIMULATE=1; VERBOSE=1;;
		(-S) STEPBYSTEP=1;;
		(-r) KERNEL_VERSION="$2"; shift;;
		(--) shift; break;;
		(-*)
			echo "$PROGNAME: unrecognized option '$option'"
			echo "Try \`$PROGRAM --help' for more information."
			exit 1
			;;
		(*) break;;
	esac
	shift
done

# set workflow to first command line argument or to usage
if [[ -z "$WORKFLOW" ]]; then
	if [[ "$1" ]]; then
		WORKFLOW=$1 ; shift # not "$1" to get rid of compound commands
	else
		WORKFLOW=help
	fi
fi

# keep the remaining command line arguments to feed to the workflow
ARGS=( "$@" )

# the following workflows are always verbose
case "$WORKFLOW" in
	(validate|dump|shell|recover)
		VERBOSE=1
	;;
esac

# Make sure we have the necessary paths (eg. in cron), /sbin will be the first path to search.
# some needed binaries are in /lib/udev or /usr/lib/udev
for path in /usr/bin /bin /usr/sbin /sbin; do
	case ":$PATH:" in
		(*:"$path":*) ;;
		(*) if [[ -d "$path" ]]; then PATH=$path:$PATH; fi ;;
	esac
done
PATH=$PATH:/lib/udev:/usr/lib/udev

# are we root ?
if [[ "$(id --user)" != "0" ]]; then
	echo "ERROR: $PRODUCT needs ROOT privileges!" >&2
	exit 1
fi

# set some bash options
shopt -s nullglob extglob
hash -r

# make sure that we use only english
export LC_CTYPE=C LC_ALL=C LANG=C

# include default config
source $SHARE_DIR/conf/default.conf

# include functions
for script in $SHARE_DIR/lib/*.sh ; do
	source $script
done

if ! has_binary pidof; then
	echo "ERROR: Required program 'pidof' missing, please check your PATH" >&2
	exit 1
fi

# do not run if another instance is running. pidof -x will always report at least $$
PID=$(pidof -x -o %PPID "$SCRIPT_FILE")
if IsInArray "$WORKFLOW" "${LOCKLESS_WORKFLOWS[@]}"; then
	LOGFILE="$LOGFILE.lockless"
elif [[ "$PID" ]] ; then
	echo "ERROR: $PROGRAM is already running, not starting again" >&2
	exit 1
fi

# keep old log file
if [[ -r "$LOGFILE" ]]; then
	mv -f "$LOGFILE" "$LOGFILE".old 2>&8
fi
mkdir -p $LOG_DIR
exec 2>"$LOGFILE" || echo "ERROR: Could not create $LOGFILE" >&2
# keep our default $LOGFILE location in a seperate variable REAR_LOGFILE
# in case end-user overruled it in the local.conf file
REAR_LOGFILE="$LOGFILE"

case "$WORKFLOW" in
	(help) ;;
	(*)
		LogPrint "$PRODUCT $VERSION / $RELEASE_DATE"
		Log "Command line options: $0 ${CMD_OPTS[@]}"
		if [[ "$VERBOSE" ]]; then
			LogPrint "Using log file: $LOGFILE"
		fi
		;;
esac

v=""
verbose=""
# enable progress subsystem only in verbose mode, set some stuff that others can use
if [[ "$VERBOSE" ]]; then
	source $SHARE_DIR/lib/progresssubsystem.nosh
	v="-v"
	verbose="--verbose"
fi

# enable debug output of the progress pipe
if [[ "$DEBUG" ]]; then
	KEEP_BUILD_DIR=1
fi

# check if we are in recovery mode
if [[ -e "/etc/rear-release" ]]; then
	RECOVERY_MODE="y"
fi

[[ -z "$SIMULATE" ]]
LogPrintIfError "Simulation mode activated, Relax-and-Recover base directory: $SHARE_DIR"

# All workflows need to read the configurations first.
# Combine configuration files
Debug "Combining configuration files"
Source "$CONFIG_DIR/os.conf"   # use this file to manually override the OS detection
Source "$CONFIG_DIR/$WORKFLOW.conf"
SetOSVendorAndVersion
# distribution configuration files
for config in "$ARCH" "$OS" \
				"$OS_MASTER_VENDOR" "$OS_MASTER_VENDOR_ARCH" "$OS_MASTER_VENDOR_VERSION" "$OS_MASTER_VENDOR_VERSION_ARCH" \
				"$OS_VENDOR" "$OS_VENDOR_ARCH" "$OS_VENDOR_VERSION" "$OS_VENDOR_VERSION_ARCH" ; do
	if [[ "$config" ]]; then
		Source "$SHARE_DIR/conf/$config.conf"
	fi
done
# user configuration files, last thing is to overwrite variables if we are in the rescue system
for config in site local rescue; do
	Source "$CONFIG_DIR/$config.conf"
done

SourceStage "init"

# check for requirements, do we have all required binaries ?
MISSING_PROGRS=()
for f in "${REQUIRED_PROGS[@]}" ; do
	if ! has_binary "$f"; then
		MISSING_PROGS=( "${MISSING_PROGS[@]}" "$f" )
	fi
done
[[ -z "$MISSING_PROGS" ]]
StopIfError "Cannot find required programs: ${MISSING_PROGS[@]}"

VERSION_INFO="
$PRODUCT $VERSION / $RELEASE_DATE

$PRODUCT comes with ABSOLUTELY NO WARRANTY; for details see
the GNU General Public License at: http://www.gnu.org/licenses/gpl.html

Host $(uname -n) using Backup $BACKUP and Output $OUTPUT
Build date: $(date -R)
"

# create temporary work area and register removal exit task
BUILD_DIR="$(mktemp -d -t rear.XXXXXXXXXXXXXXX)"
StopIfError "Could not create build area '$BUILD_DIR'"
QuietAddExitTask cleanup_build_area_and_end_program
Log "Using build area '$BUILD_DIR'"
ROOTFS_DIR=$BUILD_DIR/rootfs
TMP_DIR=$BUILD_DIR/tmp
mkdir -p $v $ROOTFS_DIR >&2
StopIfError "Could not create $ROOTFS_DIR"
mkdir -p $v $TMP_DIR >&2
StopIfError "Could not create $TMP_DIR"

# Check for and run the requested workflow
if has_binary WORKFLOW_$WORKFLOW; then
	Log "Running $WORKFLOW workflow"
	WORKFLOW_$WORKFLOW "${ARGS[@]}"
	Log "Finished running $WORKFLOW workflow"
else
	VERBOSE=1
	LogPrint "ERROR: The specified command '$WORKFLOW' does not exist !"
	EXIT_CODE=1
fi

[[ "$REAR_LOGFILE" != "$LOGFILE" ]] && cat "$REAR_LOGFILE" > "$LOGFILE"

if [[ $EXIT_CODE -eq 0 ]]; then
    LogToSyslog "DONE: rc=$EXIT_CODE"
fi

exit $EXIT_CODE
