#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Zygmunt Krynicki
set -eu
case "${1:-}" in
'' | -h | -? | --help)
	echo "Usage: image-garden make GOAL"
	echo ""
	echo "       image-garden allocate IMAGE"
	echo "       image-garden discard ADDRESS"
	echo ""
	echo "Typically GOAL is 'SYSTEM.qcow2' or 'SYSTEM.run'."
	echo "Use 'image-garden make list-systems' to see all defined systems."
	;;
version)
	echo "image-garden @VERSION@"
	;;
make)
	shift
	# This variable is updated when the package is installed so it always points
	# to the actual installation prefix of image-garden.
	INCLUDEDIR=.
	# Use special, local makefile or /dev/null as input to avoid including any
	# GNUMakefile, Makefile or makefile if one is present.
	for mk in .image-garden.mk /dev/null; do
		if [ -r "$mk" ]; then
			MAKEFILES="$INCLUDEDIR"/image-garden.mk exec make --warn-undefined-variables -f "$mk" "$@"
		fi
	done
	;;

allocate)
	shift
	while [ $# -gt 0 ]; do
		case "$1" in
		--premade)
			premade=1
			shift
			;;
		--nice)
			nice="nice"
			shift
			;;
		-*)
			echo "image-garden: unknown option: $1" >&2
			shift
			;;
		*)
			break
			;;
		esac
	done
	SPREAD_SYSTEM="$1"
	SPREAD_SYSTEM_PROPER="$(echo "$SPREAD_SYSTEM" | sed -e 's/x86-64/x86_64/g')"
	shift

	if [ "${premade:=0}" -eq 0 ]; then
		echo "Making $SPREAD_SYSTEM_PROPER..." >&2
		# Lock on a per-system lock file since parallel-invocations of make should
		# not attempt to download the same file over and over again.
		flock "$SPREAD_SYSTEM_PROPER".lock "$0" make "$SPREAD_SYSTEM_PROPER".run "$SPREAD_SYSTEM_PROPER".qcow2 >&2
	fi

	# Lock on spread.yaml as we need to prevent port numbers from clashing.
	# This artificially limits virtual machine construction but it is better
	# than having qemu racing to allocate the same port.
	exec 4<>spread.yaml
	echo "Looking available TCP port for $SPREAD_SYSTEM_PROPER..." >&2
	while ! flock --exclusive -n 4; do
		sleep 1
	done

	# Find an unused port to give to qemu as the local version of port 22 in the
	# virtual machine. This is not really very good but it's better than nothing.
	mkdir -p "${XDG_RUNTIME_DIR:=/run}/image-garden/"
	PORT=
	for MAYBE_PORT in $(seq 5000 5999); do
		if [ ! -e "${XDG_RUNTIME_DIR}/image-garden/port.$MAYBE_PORT.pid" ]; then
			PORT=$MAYBE_PORT
			# NOTE: we are not touching the pid file anymore.
			# QEMU's -pidfile option does this, utilizing flock internally.
			break
		fi
	done
	if [ -z "$PORT" ]; then
		echo "Cannot find available port for qemu forwarding" >&2
		exit 1
	fi

	# Disable display unless the user wants to have something custom.
	export QEMU_DISPLAY_OPTION="${QEMU_DISPLAY_OPTION:=-display none}"
	# Forward tcp port $PORT to port 22 in the virtual machine so that spread can connect.
	export QEMU_NETDEV_USER_EXTRA=",hostfwd=tcp:127.0.0.1:$PORT-:22"
	# Run qemu redirecting serial port to a file, disabling parallel port,
	# disabling QEMU monitor and daemonizing with a pidfile.
	parent=$$                                                              # this shell
	parent_parent="$(cut -f 4 -d ' ' </proc/"$parent"/stat)"               # bash from spread
	parent_parent_parent="$(cut -f 4 -d ' ' </proc/"$parent_parent"/stat)" # spread, hopefully
	parent_parent_parent_comm="$(cut -f 3 -d ' ' </proc/"$parent_parent_parent"/comm)"
	echo "Starting $SPREAD_SYSTEM_PROPER..." >&2
	if ${nice:-} ./"$SPREAD_SYSTEM_PROPER".run \
		-snapshot \
		-parallel none \
		-monitor none \
		-serial file:"$SPREAD_SYSTEM_PROPER"."$PORT".serial.log \
		-pidfile "${XDG_RUNTIME_DIR:=/run}"/image-garden/port."$PORT".pid \
		-daemonize \
		"$@" \
		>"$SPREAD_SYSTEM_PROPER"."$PORT".stdout.log 2>"$SPREAD_SYSTEM_PROPER"."$PORT".stderr.log \
		4<&-; then
		# Print the address where spread can connect. If spread is the parent
		# process of the bash process then implement ADDRESS directly.
		# Otherwise fall back to legacy mode where we just print the value.
		# Parent of parent - bash invoked by spread (unconditionally)
		# Parent of parent of parent - possibly spread
		if [ "$parent_parent_parent_comm" = spread ]; then
			echo "<ADDRESS localhost:$PORT>"
		else
			echo "localhost:$PORT"
		fi
		exit
	else
		rm -f "${XDG_RUNTIME_DIR:=/run}"/image-garden/port."$PORT".pid
		# Parent of parent - bash invoked by spread (unconditionally)
		# Parent of parent of parent - possibly spread
		if [ "$parent_parent_parent_comm" = spread ]; then
			echo "<FATAL $(tail -n 1 "$SPREAD_SYSTEM_PROPER"."$PORT".stderr.log)>"
			exit 213
		else
			echo "Allocation failed, see $SPREAD_SYSTEM_PROPER.$PORT.stderr.log" >&2
			exit 1
		fi
	fi
	;;

discard)
	shift
	SPREAD_SYSTEM_ADDRESS="$1"
	shift

	case "$SPREAD_SYSTEM_ADDRESS" in
	localhost:*)
		PORT="$(echo "$SPREAD_SYSTEM_ADDRESS" | sed -e 's/localhost://')"
		if [ -f "${XDG_RUNTIME_DIR:=/run}/image-garden/port.$PORT.pid" ]; then
			PID="$(cat "$XDG_RUNTIME_DIR/image-garden/port.$PORT.pid")"
			if [ -n "$PID" ]; then
				kill "$PID" || true
			fi
			rm -f "$XDG_RUNTIME_DIR/image-garden/port.$PORT.pid"
		fi
		;;
	*)
		exit 1
		;;
	esac
	;;

*)
	exit 1
	;;
esac
