#!/bin/sh
#
# Copyright 2021 Johannes Schauer Marin Rodrigues <josch@debian.org>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# Since Debian bug #898446 was closed, usernamespaces are enabled by default in
# Debian. Unfortunately, salsaci and debci both do not allow processes to
# unshare. To still be able to test software that requires namespace support we
# spin up a qemu virtual machine, install EXTRA_DEPS in it and then run the
# target SCRIPT as a normal user on that machine.
#
# Another advantage of using this wrapper is, that it can be run by any
# unprivileged user without building the package and spinning up autopkgtest
# first.

set -exu

EXTRA_DEPS=gnupg,sbuild,mmdebstrap,build-essential,uidmap,fakeroot,diffoscope,devscripts,python3-apt
PORT=$(python3 -c 'from socket import socket; s=socket(); s.bind(("", 0)); print(s.getsockname()[1]);' || echo 10022)
SCRIPT=./debian/tests/unshare

if debian/tests/check-unshare-support; then
	echo "Skipping test, because the unshare test can be executed directly." >&2
	exit 77
fi

release=$(./debian/tests/get_default_release.py)
if [ -z "$release" ]; then
	echo "cannot get default release" >&2
	exit 1
fi

# for $ID
eval "$(grep "^ID=" /etc/os-release)"
if [ "$ID" = ubuntu ]; then
	COMPONENTS=main,universe
else
	COMPONENTS=main
fi

[ -e debian/tests/control ]
SOURCES="$(pwd)"

if [ -z ${AUTOPKGTEST_TMP+x} ]; then
	# if AUTOPKGTEST_TMP is not set, then this script is probably not
	# executed under autopkgtest
	TMPDIR=$(mktemp --directory --tmpdir sbuild.XXXXXXXXXX)
else
	# since AUTOPKGTEST_TMP is set, we assume that this script is executed
	# under autopkgtest --> switch to the temporary directory
	TMPDIR="$AUTOPKGTEST_TMP"
	mkdir -p "$TMPDIR"
fi

# make TMPDIR world-readable or otherwise it cannot be accessed in unshare mode
chmod a+rx "$TMPDIR"
cd "$TMPDIR"

# generate a new ssh key for us, so that we can authenticate ourselves
if [ ! -e "$TMPDIR/id_rsa" ]; then
	ssh-keygen -q -t rsa -f "$TMPDIR/id_rsa" -N ""
fi

if [ ! -e "$TMPDIR/${ID}-${release}-host.ext4" ]; then
# shellcheck disable=SC2016
	debvm-create \
		--output="$TMPDIR/${ID}-${release}-host.ext4" \
		--release="${release}" \
		--size=4G \
		--sshkey="$TMPDIR/id_rsa.pub" \
		--skip=usrmerge \
		-- \
		--mode=unshare \
		--hook-dir=/usr/share/mmdebstrap/hooks/copy-host-apt-sources-and-preferences \
		--hook-dir=/usr/share/mmdebstrap/hooks/file-mirror-automount \
		--hook-dir=/usr/share/mmdebstrap/hooks/maybe-merged-usr \
		--include="$EXTRA_DEPS" \
		--customize-hook='chroot "$1" useradd --create-home user' \
		--customize-hook='mkdir "$1"/build' \
		--customize-hook='sync-in '"$SOURCES/"' /build/' \
		--components="$COMPONENTS" \
		""
fi

debvm-run --sshport="$PORT" --image="$TMPDIR/${ID}-${release}-host.ext4" \
	> "$TMPDIR/debvm.log" </dev/null 2>&1 &

# store the pid
QEMUPID=$!

onerror() {
	# attempt poweroff
	if [ -n "${ssh+x}" ]; then
		$ssh -o ConnectTimeout=20 root@localhost systemctl poweroff || :
		# give a few seconds for poweroff
		sleep 20
		kill $QEMUPID || :
	fi
	# turn off verbose output
	set +x
	echo "script failed -- temporary files are stored in $TMPDIR:"
	echo
	ls -lha "$TMPDIR"
	echo
	echo "to test yourself, run qemu with:"
	echo
	echo "    $ debvm-run --sshport=$PORT --image=$TMPDIR/${ID}-${release}-host.ext4"
	echo
	echo "and log in as root (without a password)"
	echo
	echo "or connect to it via ssh:"
	echo
	echo "    $ $ssh root@localhost"
	echo
	echo "when you are done, cleanup temporary files with:"
	echo
	echo "    $ rm -r \"$TMPDIR\""
}

# show the log and kill qemu in case the script exits first
trap 'cat --show-nonprinting "$TMPDIR/debvm.log"; onerror' EXIT

# the default of 60 seconds can be too slow for salsaci
debvm-waitssh -t 120 "$PORT"

trap onerror EXIT

# the default ssh command does not store known hosts and even ignores host keys
# it identifies itself with the rsa key generated above
# pseudo terminal allocation is disabled or otherwise, programs executed via
# ssh might wait for input on stdin of the ssh process
ssh="ssh -o NoHostAuthenticationForLocalhost=yes -i "$TMPDIR/id_rsa" -T -p $PORT"

$ssh root@localhost env --chdir=/build/ AUTOPKGTEST_TMP=/tmp runuser -u user -- "$SCRIPT"

# shut the system off
trap - EXIT
$ssh root@localhost systemctl poweroff || :
wait $QEMUPID

# cleanup
[ -e "$TMPDIR/.guestfs-$(id -u)" ] && rm -r "$TMPDIR/.guestfs-$(id -u)"
for f in "${ID}-${release}-host.ext4" id_rsa id_rsa.pub debvm.log; do
	rm "$TMPDIR/$f"
done
if [ -z ${AUTOPKGTEST_TMP+x} ]; then
	rmdir "$TMPDIR"
fi
