#!/bin/sh

set -eu

# The paths can be overridden, which is what the unit tests do.
LOCKFILE=${PKGOS_SBUILD_LOCK_FILE:-/var/lock/pkgos-repository.lock}
PIDFILE=${PKGOS_SBUILD_LOCK_PIDFILE:-/var/lock/pkgos-repository-lock.pid}

ME=$(basename $0)

usage() {
	echo "Usage: $ME {acquire|release}" >&2
	exit 2
}

if [ $# != 1 ] ; then
	usage
fi

case "$1" in
acquire)
	# The holder takes an exclusive flock on the lock file in the
	# background, detached from our own stdio: sbuild captures the
	# output of external commands through a pipe, and waits for its
	# end, so inheriting it made every build wait forever. The
	# holder writes both its own PID and the PID of the process
	# which called us to the PID file: while the caller is alive,
	# the lock is held, and when it is gone (ex: the build was
	# aborted), the holder exits by itself, so that the lock never
	# stays behind. acquire only returns once our holder owns the
	# lock, so that two concurrent acquires cannot run their
	# critical section at the same time. The holder runs through an
	# explicit "sh -c", as $$ inside a subshell would hold the PID
	# of the parent shell, and not of the holder itself.
	CLIENT_PID=$PPID
	sh -c '
		exec </dev/null >/dev/null 2>&1
		exec 9>"$1"
		flock -x 9
		echo "$$ $2" > "$3"
		while [ -f "$3" ] && kill -0 "$2" 2>/dev/null ; do
			sleep 1
		done
	' sh "${LOCKFILE}" "${CLIENT_PID}" "${PIDFILE}" &
	HOLDER_PID=$!
	while : ; do
		if [ -f "${PIDFILE}" ] ; then
			LOCK_OWNER=$(cat "${PIDFILE}" 2>/dev/null || true)
			LOCK_OWNER=${LOCK_OWNER%% *}
			if [ "${LOCK_OWNER}" = "${HOLDER_PID}" ] ; then
				break
			fi
		fi
		if ! kill -0 "${HOLDER_PID}" 2>/dev/null ; then
			echo "${ME}: lock holder died, giving up" >&2
			exit 1
		fi
		sleep 0.1
	done
;;
release)
	if [ -f "${PIDFILE}" ] ; then
		HOLDER=$(cat "${PIDFILE}")
		HOLDER=${HOLDER%% *}
		rm -f "${PIDFILE}"
		kill "${HOLDER}" 2>/dev/null || true
	fi
;;
*)
	usage
;;
esac
