#!/bin/sh
# Build a debian qemu image and rootfs tarball
set -eu

# Other options (environment overridable)
#  debootstrap does not handle duplicate options well, so you must put all extra
#  packages into "BQ_EXTRA_PKGS" instead of adding arguments to debootstrap.
: ${BQ_DEBOOTSTRAP_ARGS:=""}
: ${BQ_EXTRA_PKGS:=""}
: ${BQ_IMAGE_SIZE:="25G"}
: ${BQ_MIRROR:="http://deb.debian.org/debian"}
: ${BQ_SECCOMP:=0}

alias echo_err='>&2 echo'

# Converts a debian arch into a qemu arch
debian_to_qemu() {
	case "$1" in
	amd64)
		echo "x86_64"
		;;
	i386|mips|mipsel|mips64|mips64el)
		echo "$1"
		;;
	*)
		echo_err "Unknown arch - please update debian_to_qemu"
		exit 1
	esac
}

# Check args
if [ $# -ne 3 ]; then
	echo_err "$0: <suite> <arch> <kpkg>"
	echo_err " Creates a debian QEMU qcow2 image into debian-<suite>-<arch>.qcow2"
	echo_err "  Installs the given kernel package into the image and copies it"
	echo_err "  externally alongside the qcow2 image."
	exit 1
fi

SUITE="$1"
ARCH="$2"
KPKG="$3"

echo "qemu-build suite=$SUITE arch=$ARCH kernel=$KPKG"

QEMU_ARCH=$(debian_to_qemu "$ARCH")
IMAGE_FILE="debian-$SUITE-$ARCH.qcow2"
ROOTFS_FILE="debian-$SUITE-$ARCH.tar.xz"

# Check environment
if [ "$(id -u)" != "0" ]; then
	echo_err "Must be run as root"
	exit 1
fi

if dpkg --compare-versions $(debootstrap --version | cut -d' ' -f2) '<<' 1.0.72; then
	echo_err "Install debootstrap >= 1.0.72"
	exit 1
fi

if ! which virt-make-fs > /dev/null; then
	echo_err "Install libguestfs-tools"
	exit 1
fi

if ! which qemu-img > /dev/null; then
	echo_err "Install qemu-utils"
	exit 1
fi

if ! which "qemu-$QEMU_ARCH-static" > /dev/null; then
	echo_err "Install qemu-user-static"
	exit 1
fi

# Create temp image directory
TMP_DIR=$(mktemp -d)
MOUNT_DIR="$TMP_DIR/rootfs"
QEMU_DIR="$TMP_DIR/qemu"
RAW_IMAGE="$TMP_DIR/image"

cleanup() {
	umount "$QEMU_DIR/dev" >/dev/null 2>&1 || true
	umount "$QEMU_DIR/proc" >/dev/null 2>&1 || true
	umount "$QEMU_DIR/sys" >/dev/null 2>&1 || true
	umount "$MOUNT_DIR/dev" >/dev/null 2>&1 || true
	umount "$MOUNT_DIR/proc" >/dev/null 2>&1 || true
	umount "$MOUNT_DIR/sys" >/dev/null 2>&1 || true
	rm -rf "$TMP_DIR" >/dev/null 2>&1 || true
}

trap cleanup EXIT INT
chmod 700 "$TMP_DIR"
mkdir "$MOUNT_DIR"

# mips64el Debian requires R2
if [ "$ARCH" = "mips64el" ]; then
	export QEMU_CPU=MIPS64R2-generic
fi

export DEBIAN_FRONTEND=noninteractive
export LC_ALL=C

# Bootstrap debian
echo 'Running debootstrap...'
BQ_DEBOOTSTRAP_ARGS="$BQ_DEBOOTSTRAP_ARGS --foreign --arch=$ARCH --include=locales,$BQ_EXTRA_PKGS"
debootstrap $BQ_DEBOOTSTRAP_ARGS "$SUITE" "$MOUNT_DIR" "$BQ_MIRROR"
cp $(which "qemu-$QEMU_ARCH-static") "$MOUNT_DIR/usr/bin"
echo 'Running debootstrap --second-stage...'
chroot "$MOUNT_DIR" debootstrap/debootstrap --second-stage


# Setup apt sources
echo 'Configuring rootfs...'
echo "deb $BQ_MIRROR $SUITE main" > "$MOUNT_DIR/etc/apt/sources.list"
echo "deb-src $BQ_MIRROR $SUITE main" >> "$MOUNT_DIR/etc/apt/sources.list"
[ "$BQ_SECCOMP" = '0' ] && echo 'APT::Sandbox::Seccomp false;' > "$MOUNT_DIR/etc/apt/apt.conf.d/01seccomp"

# Set default locales
sed -i '/^# en_GB.UTF-8/s/^# //' "$MOUNT_DIR/etc/locale.gen"
sed -i '/^# en_US.UTF-8/s/^# //' "$MOUNT_DIR/etc/locale.gen"
chroot "$MOUNT_DIR" dpkg-reconfigure -f noninteractive locales 2>&1

# Set hostname
echo "debian-$SUITE-$ARCH" > "$MOUNT_DIR/etc/hostname"
echo "127.0.1.1 debian-$SUITE-$ARCH" >> "$MOUNT_DIR/etc/hosts"

# Set blank root password
sed -i 's/^root:\*:/root::/' "$MOUNT_DIR/etc/shadow"

# Update APT sources
chroot "$MOUNT_DIR" apt-get update

# Erase machine-id
cat /dev/null > "$MOUNT_DIR/etc/machine-id"


# Clone the rootfs and do qemu specific modifications
echo 'Configuring qemu image...'
cp -alx "$MOUNT_DIR" "$QEMU_DIR"

# Setup network interfaces
echo >> "$QEMU_DIR/etc/network/interfaces"
echo 'auto eth0' >> "$QEMU_DIR/etc/network/interfaces"
echo 'iface eth0 inet dhcp' >> "$QEMU_DIR/etc/network/interfaces"

# Setup FAKE fstab
#  The initramfs-tools package scans the list of devices to see what fsck hooks
#  it needs to install. However, for the root filesystem it will try to probe
#  it's type instead of reading it from fstab. Since /dev/vda may not exist on
#  the host system we can use it.
#  Workaround this by inserting a fake entry for /usr which IS trusted. Revert
#  back to the correct entry later
echo "fake /usr ext4 fake 0 1" > "$QEMU_DIR/etc/fstab"

# Install the kernel
chroot "$QEMU_DIR" apt-get install -y "$KPKG"

# Prevent kernel from being upgraded automatically which will break unless
#  the external kernel is also updated
chroot "$QEMU_DIR" sh -c 'dpkg-query -f="\${Package}\n" -W "linux-image*" | xargs apt-mark hold'

# Setup REAL fstab
echo "/dev/vda / ext4 errors=remount-ro 0 1" > "$QEMU_DIR/etc/fstab"


# Cleanup mounts, apt files and qemu static
rm -rf "$MOUNT_DIR/var/cache/apt" "$MOUNT_DIR/var/lib/apt/lists"
rm -rf "$QEMU_DIR/var/cache/apt" "$QEMU_DIR/var/lib/apt/lists"
rm -f "$MOUNT_DIR/usr/bin/qemu-$QEMU_ARCH-static"
rm -f "$QEMU_DIR/usr/bin/qemu-$QEMU_ARCH-static"


# Create output files
echo 'Building tarballs and qcow2 file...'
tar -caf "$ROOTFS_FILE" --one-file-system -C "$MOUNT_DIR/.." rootfs &
ROOTFS_PID=$!

#  virt-make-fs insists on creating sparse images which are a pain for
#  distribition, so we'll use qemu-img to create a non-sparse qcow2 image
virt-make-fs --type=ext4 --size="$BQ_IMAGE_SIZE" "$QEMU_DIR" "$RAW_IMAGE"
qemu-img convert -c -f raw -O qcow2 "$RAW_IMAGE" "$IMAGE_FILE"
wait "$ROOTFS_PID"

# Copy kernel image and initrd to outside world
echo 'Extracting kernel image...'
KERNEL_BASE=$(basename "$QEMU_DIR"/boot/vmlinu*)
cp "$QEMU_DIR/boot/$KERNEL_BASE" "$KERNEL_BASE.$ARCH.$SUITE"
INITRD_BASE=$(basename "$QEMU_DIR"/boot/initrd.img-*)
cp "$QEMU_DIR/boot/$INITRD_BASE" "$INITRD_BASE.$ARCH.$SUITE"

echo 'Cleaning up...'
