#!/bin/sh
# Generator of svctl daemon-reload: resolves the hints of the units in the
# package and the admin store and the entries of fstab and crypttab into the
# generated store, see svctl(8). Writes every generated or skipped edge to
# /var/lib/svctl/edges. Runs under the svctl lock; -v reports counts.
# shellcheck disable=SC1091 # library of svctl
set -u
set -f

. /usr/lib/svctl/common.sh

FSTAB=/etc/fstab
CRYPTTAB=/etc/crypttab
VERBOSE=0
[ "${1:-}" = -v ] && VERBOSE=1

mkdir -p "$RUNDIR" "$STATE" || exit 1
W=$(mktemp -d "$RUNDIR/generator.XXXXXX") || exit 1
NEW=$STORE_GEN.new.$$
trap 'rm -rf "$W" "$NEW"' EXIT

fail() {
	say "generator: $*"
	exit 1
}

# store scan: units, their flags and the entries of their *.d directories
i=0
for s in "$STORE_PKG" "$STORE_ADM"; do
	i=$((i + 1))
	[ -d "$s" ] || continue
	find "$s" -mindepth 2 -maxdepth 3 \
		\( -path "$s/*/*/*" -path "$s/*/*.d/*" -printf "D$i\t%h\t%f\n" \) -o \
		\( -path "$s/*/*/*" -prune \) -o \
		\( -name type -printf "T$i\t%h\n" \) -o \
		\( -name 'flag-*' -printf "F$i\t%h\t%f\n" \)
done >"$W/scan" || fail "cannot read the stores"

if [ -d "$REPO" ]; then
	timeout "$((REPO_WAIT / 1000))" s6-rc-set-status "$SET" >"$W/rx" 2>/dev/null || : >"$W/rx"
else
	: >"$W/rx"
fi

set --
[ -f "$CRYPTTAB" ] && set -- "$@" "$CRYPTTAB"
[ -f "$FSTAB" ] && set -- "$@" "$FSTAB"
LC_ALL=C awk -f "$LIBDIR/fstab.awk" -v crypttab="$CRYPTTAB" -v fstab="$FSTAB" -v libexec="$LIBEXEC" "$@" </dev/null >"$W/fs" ||
	fail "cannot read $FSTAB or $CRYPTTAB"

LC_ALL=C awk -f "$LIBDIR/plan.awk" -v rxfile="$W/rx" -v scanfile="$W/scan" -v fsfile="$W/fs" \
	"$W/rx" "$W/scan" "$W/fs" >"$W/plan" || fail "planning failed"

rm -rf "$NEW"
mkdir -p "$NEW" || fail "cannot create $NEW"
awk -F '\t' '$1 == "COPY" { print $3 }' "$W/plan" | xargs -r -d '\n' cp -a -t "$NEW" -- ||
	fail "cannot copy units into $NEW"
find "$NEW" -mindepth 2 -maxdepth 2 \( -name wanted-by.d -o -name required-by.d -o -name before.d -o -name after.d \) \
	-exec rm -rf {} + || fail "cannot remove hints in $NEW"
awk -F '\t' -v d="$NEW" '
	$1 == "NEW" || $1 == "COPY" { print d "/" $2 "/dependencies.d" }
	$1 == "BUNDLE" { print d "/" $2 "/contents.d" }' "$W/plan" | xargs -r -d '\n' mkdir -p -- ||
	fail "cannot create directories in $NEW"
awk -F '\t' -v d="$NEW" '
	$1 == "FILE" { f = d "/" $2 "/" $3; if ($4 == "") printf "" > f; else print $4 > f; close(f) }
	$1 == "BUNDLE" { f = d "/" $2 "/type"; print "bundle" > f; close(f) }' "$W/plan" ||
	fail "cannot write files in $NEW"
awk -F '\t' -v d="$NEW" '
	$1 == "DEP" { print d "/" $2 "/dependencies.d/" $3 }
	$1 == "BUNDLE" { print d "/" $2 "/contents.d/" $3 }' "$W/plan" | xargs -r -d '\n' touch -- ||
	fail "cannot write edges in $NEW"

awk -F '\t' 'BEGIN { OFS = "\t" }
	$1 == "EDGE" { print "edge", $2, $3, $4, $5, "" }
	$1 == "SKIP" { print "skip", $2, $3, $4, $5, $6 }' "$W/plan" | sort >"$W/edges"

awk -F '\t' '$1 == "WARN" { print "svctl: " $2 }' "$W/plan" >&2

mkdir -p "${STORE_GEN%/*}" || fail "cannot create ${STORE_GEN%/*}"
if [ -d "$STORE_GEN" ]; then
	mv "$STORE_GEN" "$STORE_GEN.old.$$" || fail "cannot replace $STORE_GEN"
fi
mv "$NEW" "$STORE_GEN" || fail "cannot replace $STORE_GEN"
rm -rf "$STORE_GEN.old.$$"
mv "$W/edges" "$EDGES" || fail "cannot write $EDGES"

if [ "$VERBOSE" = 1 ]; then
	awk -F '\t' '{ c[$1]++ } END {
		printf "svctl: generator: %d edges, %d skipped, %d copies, %d units from fstab and crypttab\n", c["EDGE"], c["SKIP"], c["COPY"], c["NEW"]
	}' "$W/plan" >&2
fi
exit 0
