#!/usr/bin/env zsh
# shellcheck shell=bash
# Copyright (c) 2016-2021 Ivan J. <parazyd@dyne.org>
# This file is part of libdevuansdk
#
# This source code is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this source code. If not, see <http://www.gnu.org/licenses/>.

vars+=(bootstrap_cpio_stage3 bootstrap_cpio_stage4 CPIO_STAGE4)

bootstrap_complete_base()
{
	fn bootstrap_complete_base "$*"
	req=(R os arch strapdir LIBPATH release mirror)
	ckreq || return 1

	notice "Bootstrapping: ${os}:${arch} base"

	export LANG=C
	export LC_ALL=C
	export DEBIAN_FRONTEND=noninteractive
	export SOURCE_DATE_EPOCH=1610550434

	bootstrap_cpio_stage3="$R/tmp/bootstrap-${os}-${arch}-stage3.cpio.gz"
	bootstrap_cpio_stage4="$R/tmp/bootstrap-${os}-${arch}-stage4.cpio.gz"

	if [[ -n "$CPIO_STAGE4" && -f "$bootstrap_cpio_stage4" ]]; then
		act "Using the existing stage4 bootstrap cpio archive..."
		bootstrap_cpio_unpack "$bootstrap_cpio_stage4" "$strapdir" || {
			die "Failed to extract cpio archive"
			return 1
		}
		bootstrap_stage4
		return
	elif [[ -f "$bootstrap_cpio_stage3" ]]; then
		act "Using the existing stage3 bootstrap cpio archive..."
		bootstrap_cpio_unpack "$bootstrap_cpio_stage3" "$strapdir" || {
			die "Failed to extract cpio archive"
			return 1
		}
		bootstrap_stage4 || { zerr; return 1; }
		if [[ -n "$CPIO_STAGE4" ]]; then
			bootstrap_cpio_pack "$bootstrap_cpio_stage4" || { zerr; return 1; }
		fi
		return
	fi

	notice "Running stage1 debootstrap"

	sudo debootstrap \
		--keyring="$LIBPATH/extra/devuan-keyring/keyrings/devuan-archive-keyring.gpg" \
		--include=devuan-keyring,wget,ca-certificates \
		--foreign \
		--arch "$arch" "$release" "$strapdir" "$mirror" || { zerr; return 1; }

	if [[ "$arch" =~ "^arm.." ]]; then
		qemu_install_user "$strapdir" || { zerr; return 1; }
	fi

	notice "Running stage2 debootstrap"

	sudo chroot "$strapdir" /debootstrap/debootstrap --second-stage || { zerr; return 1; }

	# TODO: sys config as function
	conf_print_fstab       | sudo tee "$strapdir/etc/fstab" >/dev/null
	conf_print_hostname    | sudo tee "$strapdir/etc/hostname" >/dev/null
	conf_print_hosts       | sudo tee "$strapdir/etc/hosts" >/dev/null
	conf_print_netifaces   | sudo tee "$strapdir/etc/network/interfaces" >/dev/null
	conf_print_resolvconf  | sudo tee "$strapdir/etc/resolv.conf" >/dev/null
	conf_print_sourceslist | sudo tee "$strapdir/etc/apt/sources.list" >/dev/null

	blend_bootstrap_setup || { zerr; return 1; }

	bootstrap_stage3 || { zerr; return 1; }
	bootstrap_cpio_pack "$bootstrap_cpio_stage3" || { zerr; return 1; }

	bootstrap_stage4 || { zerr; return 1; }
	if [[ -n "$CPIO_STAGE4" ]]; then
		bootstrap_cpio_pack "$bootstrap_cpio_stage4" || { zerr; return 1; }
	fi

	return
}

bootstrap_stage3()
{
	fn bootstrap_stage3
	req=(core_packages base_packages rootcredentials)
	ckreq || return 1

	cat <<EOF | sudo tee "$strapdir/thirdstage" >/dev/null
#!/bin/sh
apt-get update
apt-get --yes --force-yes install ${core_packages_option} ${core_packages} || exit 1
apt-get --yes --force-yes install ${base_packages_option} ${base_packages} || exit 1
apt-get --yes --force-yes purge ${purge_packages_option} ${purge_packages} || exit 1
apt-get --yes --force-yes --purge autoremove || exit 1
apt-get clean

echo "${rootcredentials}" | chpasswd

rm -f /etc/ssh/ssh_host_*
rm -f /root/.bash_history
EOF

	chroot-script -d thirdstage || { zerr; return 1; }
}

bootstrap_stage4()
{
	fn bootstrap_stage4
	req=(strapdir extra_packages)
	ckreq || return 1

	sudo mkdir -p "$strapdir"/{boot,dev,proc,sys}

	cat <<EOF | sudo tee "$strapdir/fourthstage" >/dev/null
#!/bin/sh
apt-get update

# check if all our extra_packages exist
allpkgs="\$(apt-cache search '.' | cut -d' ' -f1)"
for i in ${extra_packages}; do
	printf "%s" "\$allpkgs" | grep -q "^\$i$" || {
		case "\$i" in
		--*) continue;;
		*)   missing="\$missing \$i" ;;
		esac
	}
done

if [ -n "\$missing" ]; then
	printf "\033[1;31m[!!] Some extra packages don't exist:\033[0m\n"
	printf "%s\n" "\$missing"
	exit 1
fi

apt-get --yes --force-yes upgrade || exit 1
apt-get --yes --force-yes install ${extra_packages_option} ${extra_packages} || exit 1
apt-get --yes --force-yes purge ${purge_extra_packages} || exit 1
apt-get --yes --force-yes --purge autoremove || exit 1
apt-get clean
EOF

	chroot-script -d fourthstage || { zerr; return 1; }

	for i in $inittab; do
		grep -q "$^i" "$strapdir/etc/inittab" && continue
		echo "$i" | sudo tee -a "$strapdir/etc/inittab" >/dev/null
	done || true

	for i in $custmodules; do
		grep -q "^$i" "$strapdir/etc/modules" && continue
		echo "$i" | sudo tee -a "$strapdir/etc/modules" >/dev/null
	done || true
}

qemu_install_user()
{
	fn qemu_install_user "$*"
	req=(arch _target)
	local _target="$1"
	ckreq || return 1

	case "$(uname -m)" in
	arm*|aarch*)
		return
		;;
	esac

	notice "Installing qemu-user-static"

	if [[ -f "/etc/gentoo-release" ]] && [[ "$arch" = armhf ]]; then
		cat <<EOF | gcc -O3 -static -o /tmp/qemu-wrapper -x c -
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv, char **envp) {
	char *newargv[argc+3];
	newargv[0] = argv[0];
	newargv[1] = "-cpu";
	newargv[2] = "cortex-a8";
	memcpy(&newargv[3], &argv[1], sizeof(*argv) * (argc-1));
	newargv[argc+2] = NULL;
	return execve("${armhf_qemu_bin}", newargv, envp);
}
EOF
		sudo mv /tmp/qemu-wrapper "$_target" || { zerr; return 1 ; }
	fi

	case "$arch" in
	armel)
		sudo cp -a "$armel_qemu_bin" "$_target/usr/bin/" || { zerr; return 1; }
		;;
	armhf)
		sudo cp -a "$armhf_qemu_bin" "$_target/usr/bin/" || { zerr; return 1; }
		;;
	arm64)
		sudo cp -a "$arm64_qemu_bin" "$_target/usr/bin/" || { zerr; return 1; }
		;;
	esac
}

bootstrap_cpio_pack()
{
	fn bootstrap_cpio_pack "$*"
	req=(_bootstrap_cpio strapdir)
	local _bootstrap_cpio="$1"
	ckreq || return 1

	local _dest="$(dirname "$_bootstrap_cpio")"
	if [[ -f "$_bootstrap_cpio" ]]; then
		notice "cpio archive already found in $_dest"
		return
	fi

	notice "Creating bootstrap cpio archive: $_bootstrap_cpio"
	silly

	pushd "$strapdir"
	mkdir -p "$_dest"
	sudo find . \
		-not -path "./dev/*" \
		-a -not -path "./proc/*" \
		-a -not -path "./sys/*" \
		| sudo cpio -oa --reproducible  --format=newc \
		| gzip - > "$_bootstrap_cpio" || { zerr; return 1; }
	popd
}

bootstrap_cpio_unpack()
{
	fn bootstrap_cpio_unpack "$*"
	req=(_bootstrap_cpio strapdir)
	local _bootstrap_cpio="$1"
	ckreq || return 1

	notice "Unpacking bootstrap cpio archive: $_bootstrap_cpio"
	silly

	# Danger Will Robinson
	# Check for (bind) mounts as sudo rm -rf will trash the host
	for m in sys proc dev; do
		if [[ $(mountpoint -q "${strapdir}/$m") ]]; then
			zerr
			return 1
		fi
	done
	# remove everything, including .dotdirfiles
	sudo rm -rf "$strapdir"
	mkdir -p "$strapdir"

	pushd "$strapdir" || { zerr; return 1; }
	zcat "$_bootstrap_cpio" | sudo cpio -idmn --format=newc 2>>/dev/null || { zerr; return 1; }
	popd

	sudo mkdir -p "$strapdir"/{boot,dev,proc,sys}
}

blend_bootstrap_setup()
{
	fn blend_bootstrap_setup "(noop)"
	return
}
