#!/bin/bash
#
# SPDX-License-Identifier: GPL-3.0-or-later

_DEVTOOLS_LIBRARY_DIR=${_DEVTOOLS_LIBRARY_DIR:-/usr/share/devtools}
# shellcheck source=src/lib/common.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/common.sh
# shellcheck source=src/lib/archroot.sh
source "${_DEVTOOLS_LIBRARY_DIR}"/lib/archroot.sh


# umask might have been changed in /etc/profile
# ensure that sane default is set again
umask 0022

working_dir=''

files=()

usage() {
	echo "Usage: ${0##*/} [options] working-dir [systemd-nspawn arguments]"
	echo "A wrapper around systemd-nspawn. Provides support for pacman."
	echo
	echo ' options:'
	echo '    -C <file>           Location of a pacman config file'
	echo '    -M <file>           Location of a makepkg config file'
	echo '    -c <dir>            Set pacman cache'
	echo '    -f <src>[:<dst>]    Copy src file from the host to the chroot.'
	echo '                        If dst file is not provided, it defaults to src'
	echo '    -s                  Do not run setarch'
	echo '    -k                  Do not create a scope unit (--keep-unit)'
	echo '    -h                  This message'
	exit 1
}

# save all args for check_root
orig_args=("$@")

while getopts 'hC:M:c:f:sk' arg; do
	case "$arg" in
		C) pac_conf="$OPTARG" ;;
		M) makepkg_conf="$OPTARG" ;;
		c) cache_dirs+=("$OPTARG") ;;
		f) files+=("$OPTARG") ;;
		s) nosetarch=1 ;;
		k) keepunit=1 ;;
		h|?) usage ;;
		*) error "invalid argument '%s'" "$arg"; usage ;;
	esac
done
shift $((OPTIND - 1))

(( $# < 1 )) && die 'You must specify a directory.'
check_root "" "${BASH_SOURCE[0]}" "${orig_args[@]}"

working_dir=$(readlink -f "$1")
shift 1

[[ -z $working_dir ]] && die 'Please specify a working directory.'

nspawn_args=(
	--quiet
	--directory="$working_dir"
	--setenv="PATH=/usr/local/sbin:/usr/local/bin:/usr/bin"
	--as-pid2
	--console=autopipe
	--timezone=off
)
[[ $keepunit ]] || nspawn_args+=(
	--register=no
	--slice="devtools-$(systemd-escape "${SUDO_USER:-$USER}")"
	--machine="arch-nspawn-$$"
)
[[ $keepunit ]] && nspawn_args+=(
	# duplicated for the sake of clarity --
	# we explicitly want the combination of these two flags
	# to avoid any cgroup change
	--register=no
	--keep-unit
)

nspawn_args_podman=(
	--system-call-filter="@keyring"
)
for fstype in proc sysfs; do
	fsroots=()

	shopt -s lastpipe
	findmnt -t "$fstype" -o FSROOT,TARGET --json --submounts \
	| jq -r '.filesystems | map(select(.fsroot == "/")) | map(.target) | sort | .[]' \
	| readarray -t fsroots

	if [[ ${fsroots+set} ]]; then
		nspawn_args_podman+=(
			--bind "$fsroots:/run/exposed/${fsroots##*/}:norbind"
		)
	else
		warning "Cannot find a non-subtree mount of $fstype; podman won't work inside arch-nspawn"
		nspawn_args_podman=()
		break
	fi
done
if [[ ${nspawn_args_podman+set} ]]; then
	install -dm000 "$working_dir/run/exposed"
	nspawn_args+=( "${nspawn_args_podman[@]}" )
fi

# {{{ functions
copy_hostconf () {
	unshare --fork --pid gpg --homedir "$working_dir"/etc/pacman.d/gnupg/ --no-permission-warning --quiet --batch --import --import-options import-local-sigs "$(pacman-conf GpgDir)"/pubring.gpg >/dev/null 2>&1
	pacman-key --gpgdir "$working_dir"/etc/pacman.d/gnupg/ --import-trustdb "$(pacman-conf GpgDir)" >/dev/null 2>&1

	# if we did not extract host mirrors, then we are using a different package cache
	# (thus likely building for a different Arch target) and it is unsafe to use host mirrors
	if [[ -n $host_mirrors ]]; then
		printf 'Server = %s\n' "${host_mirrors[@]}" >"$working_dir/etc/pacman.d/mirrorlist"
		if [[ -n $host_cachemirrors ]]; then
			printf 'CacheServer = %s\n' "${host_cachemirrors[@]}" >>"$working_dir/etc/pacman.d/mirrorlist"
		fi
	fi

	[[ -n $pac_conf ]] && cp "$pac_conf" "$working_dir/etc/pacman.conf"
	if [[ -n $makepkg_conf ]]; then
		cp "$makepkg_conf" "$working_dir/etc/makepkg.conf"
		if [[ -d "${makepkg_conf}.d" ]] && is_globfile "${makepkg_conf}.d"/*.conf; then
			mkdir --parents "$working_dir/etc/makepkg.conf.d/"
			cp "${makepkg_conf}.d/"*.conf "$working_dir/etc/makepkg.conf.d/"
		fi
	fi

	local file
	for file in "${files[@]}"; do
		src="${file%%:*}"
		dst="${file#*:}"
		mkdir -p "$(dirname "$working_dir$dst")"
		cp -T "$src" "$working_dir$dst"
	done

	sed -r "s|^#?\\s*CacheDir.+|CacheDir = ${cache_dirs_ro[*]} ${cache_dirs[*]}|g" \
		-i "$working_dir/etc/pacman.conf"
}
# }}}

umask 0022

# Sanity check
if [[ ! -f "$working_dir/.arch-chroot" ]]; then
	die "'%s' does not appear to be an Arch chroot." "$working_dir"
elif [[ $(cat "$working_dir/.arch-chroot") != "$CHROOT_VERSION" ]]; then
	die "chroot '%s' is not at version %s. Please rebuild." "$working_dir" "$CHROOT_VERSION"
fi

get_cache_dirs "${pac_conf:-$working_dir/etc/pacman.conf}" \
	cache_dirs_ro cache_dirs

for cache_dir in "${cache_dirs_ro[@]}"; do
	nspawn_args+=(--bind-ro="${cache_dir//:/\\:}")
done
for cache_dir in "${cache_dirs[@]}"; do
	nspawn_args+=(--bind="${cache_dir//:/\\:}")
done

copy_hostconf

eval "$(grep -a '^CARCH=' "$working_dir/etc/makepkg.conf")"

[[ -z $nosetarch ]] || unset CARCH
if [[ -f "/usr/share/devtools/setarch-aliases.d/${CARCH}" ]]; then
	read -r set_arch < "/usr/share/devtools/setarch-aliases.d/${CARCH}"
else
	set_arch="${CARCH}"
fi

exec ${CARCH:+setarch "$set_arch"} systemd-nspawn "${nspawn_args[@]}" "$@"
