#!/bin/sh
# PID 1 of the build VM. It builds the context from the job disk with
# buildah and writes the image to the "hf.image" port as a tar of a
# containers-image dir: layout.
# Every exit path reboots, and QEMU runs with -no-reboot, so it exits.

finish() {
	echo "$1"
	sync
	reboot -f
}

# crun cannot pivot_root out of the initramfs, so the rest runs on a tmpfs.
if [ ! -e /.hf-tmpfs ]; then
	mount -t tmpfs -o mode=0755 tmpfs /mnt
	tar -c -C / --exclude=./mnt . | tar -x -C /mnt
	touch /mnt/.hf-tmpfs
	exec switch_root /mnt /init
fi

mount -t devtmpfs dev /dev
exec </dev/null >/dev/console 2>&1
mount -t proc proc /proc
mount -t sysfs sys /sys
mount -t cgroup2 cgroup2 /sys/fs/cgroup
mkdir -p /dev/pts /dev/shm
mount -t devpts devpts /dev/pts
mount -t tmpfs tmpfs /dev/shm
mount -t tmpfs tmpfs /tmp
mount -t tmpfs tmpfs /run

for ko in /lib/hf-modules/*.ko; do
	insmod "$ko" || finish "hearthforge: cannot load $ko"
done

# QEMU user networking: fixed guest address, gateway and DNS.
ip link set lo up
ip link set eth0 up
ip addr add 10.0.2.15/24 dev eth0
ip route add default via 10.0.2.2
echo "nameserver 10.0.2.3" > /etc/resolv.conf
echo "127.0.0.1 localhost" > /etc/hosts

job=
scratch=
for b in /sys/block/vd*; do
	case $(cat "$b/serial" 2>/dev/null) in
	hfjob) job=/dev/${b##*/} ;;
	hfscratch) scratch=/dev/${b##*/} ;;
	esac
done
[ -n "$job" ] && [ -n "$scratch" ] || finish "hearthforge: disks missing"
mkfs.ext4 -q -F -E lazy_itable_init=1,lazy_journal_init=1 "$scratch" ||
	finish "hearthforge: cannot format the scratch disk"
mount -o noatime "$scratch" /var/lib/containers ||
	finish "hearthforge: cannot mount the scratch disk"
# The root is a RAM tmpfs. The job and buildah's layer staging in /var/tmp
# go to the scratch disk instead.
hf=/var/lib/containers/hf
mkdir -p "$hf" /var/lib/containers/tmp
mount --bind /var/lib/containers/tmp /var/tmp
tar -x -f "$job" -C "$hf" || finish "hearthforge: cannot read the job"

# The engine extracts the context under its own directory name.
ctx=$(find "$hf/context" -mindepth 1 -maxdepth 1)
[ -d "$ctx" ] || finish "hearthforge: the build context is not a single directory"

out=
for port in /sys/class/virtio-ports/*; do
	[ "$(cat "$port/name" 2>/dev/null)" = hf.image ] && out=/dev/${port##*/}
done
[ -n "$out" ] || finish "hearthforge: image port missing"

set --
[ -f "$hf/job/file" ] && set -- "$@" -f "$ctx/$(cat "$hf/job/file")"
[ -f "$hf/job/target" ] && set -- "$@" --target "$(cat "$hf/job/target")"
for f in "$hf"/job/args/*; do
	[ -f "$f" ] && set -- "$@" --build-arg "${f##*/}=$(cat "$f")"
done
for f in "$hf"/job/secrets/*; do
	[ -f "$f" ] && set -- "$@" --secret "id=${f##*/},src=$f"
done

# Docker format keeps HEALTHCHECK, SHELL and ONBUILD, which OCI drops.
# --layers gives one layer per instruction, like docker build.
buildah build --format docker --layers --network host "$@" \
	-t localhost/hf-build "$ctx" ||
	finish "hearthforge: build failed"
buildah push --quiet --format v2s2 --compression-format gzip \
	localhost/hf-build dir:/var/lib/containers/hf-out ||
	finish "hearthforge: cannot export the image"
tar -c -f "$out" -C /var/lib/containers/hf-out . ||
	finish "hearthforge: cannot write the image"
finish "hearthforge: image written"
