#!/bin/sh -efu

. girar-sh-functions
PROG='girar-task run'

usage()
{
	[ -z "$*" ] || message "$*"
	echo >&2 "usage: $PROG [--dry-run] [--test-only] [<task_id>]"
	exit 1
}

if [ "${1-}" = '--help' ]; then
	usage
fi

dry_run=
if [ "${1-}" = '--dry-run' ]; then
	dry_run=1
	shift
fi

test_only=
if [ "${1-}" = '--test-only' ]; then
	test_only=1
	shift
fi

if [ "$#" -gt 1 ]; then
	usage 'Too many arguments.'
fi

cd "$GB_TASKS"

id="$(PROG="$PROG" girar-task-find-current "$@")"
cd "$id"

enable -f /usr/lib/bash/lockf lockf
# obtain an exclusive lock on the TASKS structure
builtin lockf -n . ||
        fatal "task #$id is locked"

repo="$(cat task/repo)"
owner="$(cat task/owner)"
state="$(cat task/state)"
pocket="$(cat task/pocket 2>/dev/null ||:)"

case "$state" in
	NEW|TESTED|EPERM|FAILED)
		;;
	AWAITING|POSTPONED)
		fatal "task #$id is already scheduled for run" ;;
	BUILDING|PENDING|COMMITTING)
		fatal "task #$id is a work in progress" ;;
	DONE)
		fatal "task #$id is already successfully processed" ;;
	*)
		fatal "task #$id is in unrecognized state \"$state\"" ;;
esac

[ -n "$(find acl -mindepth 1 -maxdepth 1 -path 'acl/[1-7]*' -type d 2>/dev/null)" ] ||
	fatal "cannot run empty task #$id"

check_acl_i_src()
{
	local i="$1" src="$2"; shift 2
	local u a

	# check author permissions
	u="$(cat gears/$i/userid)"
	env GIRAR_USER="$u" girar-check-perms "$src" "$repo" && return ||:

	# check confirmer permissions
	for a in $(cd acl/$i 2>/dev/null && shopt -s nullglob && set +f && echo [a-z]*); do
		env GIRAR_USER="$a" girar-check-perms "$src" "$repo" && return ||:
	done

	# author is not permitted and nobody approved
	message "$src: Operation not permitted"
	return 1
}

check_acl()
{
	[ "$state" = EPERM -a -s plan/check_acl ] || return
	local i src rc=0
	while read -r i src; do
		check_acl_i_src "$i" "$src" || rc=$?
	done < plan/check_acl
	return $rc
}

[ "$owner" = "$GIRAR_USER" ] ||
	girar-check-superuser "$repo" ||
		check_acl ||
			fatal "task #$id belongs to $owner"

nums=$(gear_nums)
check_copy_del()
{
	local i package action a_repo a_pocket= rc=0
	for i in $nums; do
		[ -s gears/$i/package -a ! -s gears/$i/dir ] || continue
		package="$(cat gears/$i/package)"
		if [ -s gears/$i/copy_repo ]; then
			action=copy
			a_repo="$(cat gears/$i/copy_repo)"
			if [ -s gears/$i/copy_pocket ]; then
				a_pocket="$(cat gears/$i/copy_pocket)"
			fi
		else
			action=delete
			a_repo=$repo
			a_pocket=$pocket
		fi
		if [ -z "$a_pocket" ]; then
			girar-check-package-in-repo "$package" "$a_repo"
		else
			girar-check-package-in-pocket "$package" "$a_pocket"
		fi ||
		{
			message "task #$id: subtask #$i: invalid request to $action nonexistent package \`$package' from \`$a_repo'"
			rc=1
		}
	done
	return $rc
}

check_srpm()
{
	local i nevr rc=0
	for i in $nums; do
		[ -s gears/$i/srpm -a -s gears/$i/nevr ] || continue
		nevr="$(cat gears/$i/nevr)"
		girar-check-nevr-in-repo "${nevr%	*}" "${nevr#*	}" "$repo" ||
		{
			message "task #$id: subtask #$i: package \`$(cat gears/$i/srpm)' is too old for \`$repo'"
			rc=1
		}
	done
	return $rc
}

check_depends()
{
	[ -s task/depends ] || return 0
	local i k rc=0
	set -- $(cat task/depends)
	for i; do
		k="_$(($i/1024))"
		if [ -d "$GB_TASKS_DONE_DIR/$k/$i/task" ]; then
			sed -i "/^$i\$/d" task/depends
		elif [ ! -d "$GB_TASKS/$i/task" ]; then
			message "required task #$i not found"
			rc=1
		fi
	done
	return $rc
}

fail=
check_copy_del || fail=1
check_srpm || fail=1
check_depends || fail=1
[ -z "$fail" ]

try=$(cat task/try 2>/dev/null ||:)
if [ -n "$try" ]; then
	try=$(($try+1))
else
	try=1
fi
iter=1

next_state=AWAITING
if [ "$state" = TESTED -a -z "$test_only" ]; then
	next_state=PENDING
fi
if [ "$next_state" = AWAITING -a -s task/depends ]; then
	next_state=POSTPONED
fi
if [ -n "$dry_run" ]; then
	echo >&2 "task #$id: try #$try could be placed to $next_state queue"
	exit 0
fi

# create group writable directories for build results
mkdir -pm3775 bugmail install logs report
mkdir -pm2775 arepo build plan

# create group writable files required for build
(umask 002; touch logs/events.$try.$iter.log task/iter task/try) || false

# set/remove test-only flag
if [ -n "$test_only" ]; then
	[ -f task/test-only ] ||
		touch task/test-only
else
	[ ! -f task/test-only ] ||
		rm task/test-only
fi

# remove "abort" flag
rm -f task/abort

echo "$GIRAR_USER" > task/run

logger -t "$PROG" "user=$GIRAR_USER task=$id try=$try repo=$repo"

trap '' HUP INT QUIT PIPE TERM
echo $try > task/try
echo 1 > task/iter
girar-task-change-state "$id" "$next_state"

echo >&2 "task #$id: try #$try is $next_state, result will be emailed to $owner@$EMAIL_DOMAIN"
