#!/bin/bash
set -e

# install-arch.sh - installs arch in a chroot
# ed <irc.rizon.net>, MIT-licensed, 2020-01-02, v1.3
# https://ocv.me/dev/?install-arch.sh
#
# known problems:
#   will fail after install with a "failed to unmount" thing
#   (thanks to arch requiring the root bindmount meme)
#   so manually invoke /c/deactivate-arch after installation finishes

##
## config

# url to download bootstrap from, expects <URL>.sig to exist as well
bootstrap_url="https://mirror.leaseweb.net/archlinux/iso/latest/"

# https://www.archlinux.org/master-keys/
keys() { cat <<'EOF'
4AA4 767B BC9C 4B1D 18AE  28B7 7F2D 434B 9741 E8AC
EOF
}


##
## helper methods

msg() { printf '\033[1;30m>\033[0;34m>\033[1m>\033[0m %s\n' "$1" >&2; }
inf() { printf '\n\033[1;30m>\033[0;32m>\033[1m>\033[0;32m %s\033[0m\n' "$1" >&2; }
wrn() { printf '\n\033[1;30m>\033[0;33m>\033[1m>\033[0;33m %s\033[0m\n' "$1" >&2; }
err() { printf '\n\033[1;30m>\033[0;31m>\033[1m>\033[0;31m %s\033[0m\n' "$1" >&2; }


##
## ensure dependencies

miss=''
for cmd in gpg wget awk sed
do
	command -v "$cmd" >/dev/null ||
		miss="$miss $cmd"
done

[ -z "$miss" ] || {
	err "missing programs: $miss"
	exit 1
}


download() {
	##
	## add all missing gpg keys

	keys |
	tr -d ' ' |
	while IFS= read -r x
	do
		gpg --list-keys "$x" >/dev/null &&
			msg "key [$x] ok" ||
		{
			msg "key [$x] being added..."
			gpg --recv-keys "$x"
			echo
		}
	done


	##
	## determine bootstrap filename and download it

	bootstrap_line="$(
		wget -qO- "$bootstrap_url"/sha1sums.txt |
		tee /dev/shm/arch.sum |
		grep -E '[^ ]+  archlinux-bootstrap-.*\.tar.gz$'
	)" ||
	{
		err "failed to determine bootstrap filename"
		exit 1
	}

	bootstrap_sha="${bootstrap_line%% *}"
	bootstrap_fn="${bootstrap_line##* }"

	inf "using bootstrap [$bootstrap_fn]"

	shachk() {
		printf '%s  %s\n' "$bootstrap_sha" "$bootstrap_fn" | sha1sum -c
	}

	shachk &&
		inf "sha1sum ok, skip download" ||
	{
		msg "sha1sum fail, downloading..."
		wget "$bootstrap_url$bootstrap_fn"
		shachk
	}

	[ -e "$bootstrap_fn.sig" ] &&
		inf "sig exists, skip download" ||
	{
		msg "sig not found, downloading..."
		wget "$bootstrap_url$bootstrap_fn.sig"
	}


	##
	## gpg-verify bootstrap and collect the key it was signed with

	key="$(
		gpg --verify "$bootstrap_fn".sig 2>&1 |
		tee /dev/shm/gpgtmp |
		awk '
			BEGIN {ok=0;nkeys=0}
			/^gpg: Good signature from "/ {ok=1}
			/^gpg: +using RSA key / {nkeys++;key=$5}
			{pkey=""}
			/^Primary key fingerprint: [0-9A-F ]{40,50}$/ {
				sub(/.*: /,"");
				gsub(/ /,"");
				pkey=$0
			}
			END {
				if (nkeys>0) {print key} else {print pkey};
				if (nkeys<=1 && ok==1) exit 0;
				exit 1
			}
		'
		
		echo "${PIPESTATUS[*]}" |
		grep -qE '^0 0 0$'
	)" &&
		inf "gpg verification approves of the archive" ||
	{
		err "gpg verification rejected the archive"
		cat /dev/shm/gpgtmp
		exit 1
	}


	##
	## check if that was one of the expected keys

	ok=0
	while IFS= read -r goodkey
	do
		[ "$goodkey" = "$key" ] &&
			ok=1
	done < <(
		keys |
		tr -d ' ' |
		tr '[:lower:]' '[:upper:]'
	)

	[ $ok -eq 1 ] &&
		inf "collected key is known" ||
	{
		err "the key was unexpected: $key"
		cat /dev/shm/gpgtmp
		exit 1
	}
}


unpack() {
	# check that it's safe to continue
	[ -e "$cr" ] &&
	{
		err "chroot exists, aborting"
		exit 1
	}

	echo
	msg "unpacking to:  $cr"

	mkdir -p "$cr"
	tar -xC"$cr" --strip-components=1 -f"$bootstrap_fn"
}


addscripts() {
	echo
	msg "adding activate"
	cat >"${cr%/*}/activate-${cr##*/}" <<EOF
#!/bin/bash
set -e

# sans trailing slash
cr="$cr"

echo chroot prep >&2

mount |
grep -qF " on \$cr " ||
	mount --bind "\$cr" "\$cr"

mount |
grep -qF " on \$cr/tmp " ||
	mount -t tmpfs tmpfs "\$cr/tmp"

cd "\$cr"
# OPTIONAL: home

{
	printf '/%s\n' sys dev proc run home;
	mount | sed -r 's/[^ ]+ on //;s/ .*//'
} |
grep -E '^/(sys|dev|proc|run|home)(/|$)' |
cut -c2- |
LC_ALL=C sort |
uniq |
while IFS= read d
do
	printf 'mount [%s] ' "\$d";
	[ -e "\$d" ] && [ -e "/\$d" ] ||
		{ printf 'NG\n'; continue; }
	
	printf 'ok\n';
	
	mount |
	grep -qF " on \$cr/\$d " ||
		mount --make-rslave --bind /\$d \$d
		#mount --make-rslave --rbind /\$d \$d
done

cp -p /etc/resolv.conf etc/
chmod 1777 /dev/shm

# OPTIONAL: collect Xorg session key
xauth list > /dev/shm/Xauth || true

# OPTIONAL: allow access to the sound card from other users through unix socket only
sudo -u ed pactl load-module module-native-protocol-unix auth-anonymous=1 socket=/dev/shm/pulse-socket 2>/dev/null || true

echo chroot activate >&2
rv=0
[ -z "\$1" ] && { chroot "\$cr" /bin/bash || rv=\$?; }
[ -z "\$1" ] || { chroot "\$cr" "\$@"     || rv=\$?; }

[ \$rv -eq 0 ] ||
{
	printf "\033[1;31m\n[!] left chroot with exit code \$rv\033[0m\n\n" >&2
	exit \$rv
}

printf '\033[1;30m'
find /proc/ -mindepth 2 -maxdepth 2 -name exe -printf '%l\n' 2>/dev/null |
grep -F "\$cr/" &&
{
	printf '\033[33mother shells using the chroot; will not deactivate\033[0m\n' >&2
} ||
{
	"\${cr%/*}/deactivate-\${cr##*/}"
}
printf '\033[0m'
EOF

	msg "adding deactivate"
	cat >"${cr%/*}/deactivate-${cr##*/}" <<EOF
#!/bin/bash
set -e

# sans trailing slash
cr="$cr"
ecr="\$(
	printf '%s\n' "\$cr" |
	sed -e 's/[]\/\$*.^|?+(){}[]/\\\&/g'
)"

deactivate()
{
	printf '\033[33munmounting chroot stuff\033[0m\n'
	mount |
	awk '/[^ ]+ on '"\$ecr"'[/ ]/ && v[\$3]++==0 {print \$3}' |
	LC_ALL=C sort -r |
	tee /dev/stderr |
	tr '\n' '\0' |
	xargs -0 umount
}

deactivate ||
deactivate ||
deactivate ||
echo "giving up, just reboot"
EOF

	chmod 755 "${cr%/*}/"*activate-"${cr##*/}"
}


fixup() {
	cd "$cr"
	cp -npv etc/pacman.d/mirrorlist{,.arch-stock}
	cat >etc/pacman.d/mirrorlist <<'EOF'
Server = https://mirror.leaseweb.net/archlinux/$repo/os/$arch
Server = https://archlinux.uib.no/$repo/os/$arch
EOF
	
	cp -pv ~ed/.{bashrc,tmux.conf}* . ||
		wget https://ocv.me/dot/.{bashrc,tmux.conf}
	
	sed -ri 's/\\h\\$/\\h-ARCH\\/' .bashrc
	mv .{bashrc,tmux.conf}* root/
}


inst() {
	cat >"$cr/t.sh" <<'EOF'
m() { printf '\n\n\n\033[1;37;44m[ %s ]\033[0m\n' "$*"; "$@"; }
w() { read -u1 -r -p 'press enter'; }

m df -h
w

m pacman-key --init
w

m pacman-key --populate archlinux
w

m pacman -Syuu base
m useradd -m -s /bin/bash -u 1000 ed

#m cp -pv root/.{bashrc,tmux.conf} home/ed
#m chown ed.ed home/ed/.{bashrc,tmux.conf}

# fix locales
m sed -ri 's/^#(en_US.UTF-8)/\1/' /etc/locale.gen
m locale-gen
localectl set-locale LANG=en_US.UTF-8
echo LANG=en_US.UTF-8 > /etc/locale.conf

# kill gpg things
killall gpg-agent || true
killall /usr/bin/gpg-agent || true

EOF
	"${cr%/*}/activate-${cr##*/}" /bin/bash /t.sh
	rm "$cr/t.sh"
}


# chroot location
cr="$1"
[ -z "$cr" ] && {
	err "need argument 1: location to create chroot at"
	exit 1
}

# ensure no trailing slash in the chroot path
cr="${cr%/}"

# do it
download
unpack
addscripts
( fixup )
inst

cp -pv "$0" "$cr/root/"
inf "ready: ${cr%/*}/activate-${cr##*/}"

exit 0



# https://wiki.archlinux.org/index.php/Install_from_existing_Linux
# https://wiki.archlinux.org/index.php/Pacman/Package_signing#Initializing_the_keyring

# pacman -Syuu
# pacman -S strace code vim tmux htop strace python2 python3 git python-black python-pylint grep sed wget curl gzip bzip2 sudo dbus xorg-xauth pulseaudio pulseaudio-alsa firefox ttf-dejavu
# rust go gcc ttf-dejavu ttf-ubuntu-font-family ttf-roboto ttf-hanazono ttf-droid 

# vscode settings
"telemetry.enableTelemetry": false,
"debug.showInStatusBar": "always",
"debug.toolBarLocation": "docked",
"go.formatTool": "goimports",
"python.formatting.provider": "black",
"editor.formatOnSave": true,

# permit Xorg
xauth list > /dev/shm/Xauth
/c/arch/activate
su ed
cat /dev/shm/Xauth | while IFS= read -r x; do xauth add $x; done
XDG_RUNTIME_DIR=/run/user/1000 dbus-launch code --verbose

# fix pulse
{ cat <<'EOF'
# https://github.com/TheBiggerGuy/docker-pulseaudio-example/blob/master/pulse-client.conf

# Connect to the host's server using the mounted UNIX socket
default-server = unix:/dev/shm/pulse-socket

# Prevent a server running in the container
autospawn = no
daemon-binary = /bin/true

# Prevent the use of shared memory
enable-shm = false
EOF
} | "/c/arch/activate" tee /etc/pulse/client.conf
