#!/bin/bash -efu

. bootchain-sh-functions

bcretry="${PIPE_RETRY:-}"
[ -n "$pipeline_mode" ] ||
	bcretry="${bcretry:-5}"
pidfile="/var/run/$PROG.pid"
chainsteps="${chainsteps-}"
stepnum="${stepnum:-0}"
prevdir="${prevdir-}"
BC_IM_supported=


bc_exit_handler()
{
	local rc="$?"
	trap - EXIT
	rm -f -- "$pidfile"
	exit $rc
}

debug()
{
	[ -z "$BC_DEBUG" ] ||
		message "$*"
}

bc_fatal()
{
	date "+%s" > /.initrd/rootdelay/deadline
	echo > /etc/initrd/method
	fatal "$*"
}


# Daemon restarted in foreground
if [ "${1-}" = "--foreground" ]; then
	debug "daemon has been restarted in foreground"
	trap bc_exit_handler EXIT
	echo "$$" >"$pidfile"
else
	# Daemon started
	[ ! -f "$pidfile" ] ||
		fatal "already running"
	trap bc_exit_handler EXIT
	echo "$$" >"$pidfile"

	if [ "${RDLOG-}" = console ]; then
		BC_LOGFILE=/dev/console
	elif [ "${RDLOG-}" = printk ]; then
		BC_LOGFILE=/dev/ttyprintk
		[ -e "$BC_LOGFILE" ] ||
			mknod "$BC_LOGFILE" c 5 3
	elif [ -z "${CONSOLE-}" ] &&
		[ -n "$BC_LOG_VT" ] &&
		[ -z "${NOASKUSER-}" ] &&
		[ ! -c "$BC_LOGFILE" ] &&
		command -v openvt >/dev/null
	then
		[ -z "${RDLOG-}" ] ||
			BC_LOGFILE="$RDLOG"
		touch -- "$BC_LOGFILE"
		[ -e "/dev/tty$BC_LOG_VT" ] ||
			mknod "/dev/tty$BC_LOG_VT" c 4 "$BC_LOG_VT"
		openvt -f -w -c$BC_LOG_VT -- /sbin/bootchain-logvt &
	fi

	exec >"$BC_LOGFILE" 2>&1

	message "Starting server [$(initrd_version)]..."
	debug "Booting with /proc/cmdline:"
	fdump /proc/cmdline

	run mkdir -p -- "$mntdir" "$BC_PASSED" "$BC_ROOT"

	mountpoint -q -- "$mntdir" ||
		run mount -t tmpfs -o size="$BC_TMPFS_SIZE" tmpfs "$mntdir"

	chainsteps="$BOOTCHAIN"
fi

# Check that interactive mode supports
if has_feature bootchain-interactive; then
	. interactive-sh-functions

	[ "${1-}" != "--foreground" ] ||
		IM_activate "$BC_FGVT_ACTIVATE" "$BC_LOGFILE"
	BC_IM_supported=1
fi

rc=0
while [ -n "$chainsteps" ]; do
	name="${chainsteps%%,*}"
	exe="$handlerdir/$name"

	if [ "$name" = fg ]; then
		[ -n "$BC_IM_supported" ] ||
			bc_fatal "bootchain-interactive feature required"
		assign "callnum" "\${callnum_$name:-0}"
		chainsteps="${chainsteps#"$name"}"
		chainsteps="${chainsteps#,}"

		if IM_is_active; then
			message "[$callnum] Step '$name' has been ignored"
		else
			message "[$callnum] Switching to foreground"

			callnum=$((1 + $callnum))
			assign "callnum_$name" "\$callnum"

			export stepnum chainsteps callnum_fg prevdir bcretry

			IM_exec "$0" --foreground
		fi

	elif [ "$name" = noop ]; then
		chainsteps="${chainsteps#"$name"}"
		chainsteps="${chainsteps#,}"
		prevdir=
		message "[0] Step '$name' has been passed"

	elif [ "$name" = retry ]; then
		chainsteps="${chainsteps#"$name"}"
		chainsteps="${chainsteps#,}"
		bcretry="${PIPE_RETRY:-}"
		[ -n "$pipeline_mode" ] ||
			bcretry="${bcretry:-5}"
		message "subsequent steps will restart after failure"

	elif [ "$name" = noretry ]; then
		chainsteps="${chainsteps#"$name"}"
		chainsteps="${chainsteps#,}"
		bcretry=0
		message "daemon will be stopped immediately after any step failure"

	elif [ -x "$exe" ]; then
		assign "callnum" "\${callnum_$name:-0}"
		datadir="$mntdir/src/step$stepnum"
		destdir="$mntdir/dst/step$stepnum"

		run mkdir -p -- "$datadir" "$destdir"

		if mountpoint -q -- "$destdir" ||
			[ -s "$destdir/DEVNAME" ] ||
			[ -b "$destdir/dev" ] ||
			[ -c "$destdir/dev" ]
		then
			message "[$callnum] Handler: $exe skipped"
		else
			message "[$callnum] Handler: $exe"

			export name callnum datadir destdir prevdir

			try="$bcretry"
			while :; do
				[ -z "$BC_DEBUG" ] ||
					run "$handlerdir/debug" ||:
				rc=0
				run "$exe" ||
					rc=$?
				[ "$rc" != 0 ] ||
					break
				[ "$rc" != 2 ] || [ -z "$pipeline_mode" ] ||
					break 2
				message "[$callnum] Handler failed (rc=$rc, try=$try)"
				[ ! -f "$BC_PASSED/$PROG" ] ||
					break 2
				[ -z "$try" ] || [ "$try" = 0 ] ||
					try=$(( $try - 1 ))
				[ "$try" != 0 ] ||
					break
				sleep 2
			done

			[ -r "$BC_NEXTCHAIN" ] ||
				run touch "$BC_PASSED/$name"
			[ ! -f "$BC_PASSED/$PROG" ] ||
				break
			[ "$rc" = 0 ] ||
				break
		fi

		if [ ! -r "$BC_NEXTCHAIN" ]; then
			callnum=$((1 + $callnum))
			assign "callnum_$name" "\$callnum"
			eval "export callnum_$name"
		fi

		stepnum=$((1 + $stepnum))
		prevdir="$(readlink-e "$destdir" 2>/dev/null ||:)"
	fi

	if [ ! -r "$BC_NEXTCHAIN" ]; then
		chainsteps="${chainsteps#"$name"}"
		chainsteps="${chainsteps#,}"
	else
		debug "chain will be reloaded by $BC_NEXTCHAIN:"
		fdump "$BC_NEXTCHAIN"
		. "$BC_NEXTCHAIN"
		run rm -f -- "$BC_NEXTCHAIN"
	fi

	debug "remaining steps: $chainsteps"
done

[ -z "$chainsteps" ] ||
	message "remaining steps after breaking loop: $chainsteps"

if [ "$rc" = 2 ] && [ -n "$pipeline_mode" ]; then
	debug "finishing in pipeline mode"
elif [ "$rc" = 0 ] && [ -f "$BC_PASSED/$PROG" ]; then
	debug "finishing in bootchain mode"
else
	bc_fatal "daemon terminated incorrectly (rc=$rc)"
fi

prevdir="$(readlink-e "$destdir" 2>/dev/null ||:)"

if [ -z "$BC_DEBUG" ]; then
	if [ ! -f "$mntdir"/DIRTY ]; then
		grep -qs " $mntdir/" /proc/mounts ||
			run umount -- "$mntdir" &&
			run rm -rf -- "$mntdir" ||:
	fi
else
	datadir=
	destdir=
	callnum=0
	name=STAGE2

	message "[$callnum] Handler: $handlerdir/debug"

	export name callnum datadir destdir prevdir

	run "$handlerdir/debug" ||:
	debug "last step finished with exit code $rc"
fi

if [ -f "$BC_LOGFILE" ] && [ -n "${BC_DEBUG}${BC_TEST-}" ]; then
	if [ -d "$prevdir/var/log" ]; then
		destdir="$prevdir/var/log"
	else
		run mkdir -p -- "$mntdir"
		destdir="$mntdir"
	fi

	if [ -n "${BC_TEST-}" ]; then
		debug "test '$BC_TEST' in the stage1 passed"
		echo "$BC_TEST" >"$destdir"/BC-TEST.passed
	fi

	run cp -Lf -- "$BC_LOGFILE" "$destdir/"
fi

echo localdev > /etc/initrd/method
[ -z "$prevdir" ] || ! mountpoint -q -- "$prevdir" ||
	run mount --move -- "$prevdir" "$rootmnt"
