#!/bin/sh
# Phase-1 bootstrap init for the Nucleic Linux base build (docs/LINUX_VM.md §base build).
#
# macOS has no Linux filesystem tooling, so the ext4 root disk is assembled *inside the guest*: this
# runs as PID 1 from a tiny busybox initramfs, formats the raw block device, unpacks the Ubuntu rootfs
# tarball handed in as a raw tar on a second virtio-blk disk, bakes in the Nucleic agent + first-boot
# provisioning hooks, then powers off. The host then boots phase 2 (the installed rootfs) to provision
# the desktop + toolchain.
#
# Payload delivery is a block device, not virtiofs: modern Ubuntu arm64 kernels ship `virtiofs` as a
# module (absent from this busybox initramfs), whereas `virtio_blk` is built-in.
#
# Everything here is a single static busybox — keep it POSIX sh and busybox-applet only.
set -e
export PATH=/bin:/sbin:/usr/bin:/usr/sbin

log() { echo "[nucleic-bootstrap] $*"; }

# busybox applet symlinks + core pseudo-filesystems.
/bin/busybox --install -s /bin 2>/dev/null || true
mkdir -p /proc /sys /dev /mnt/root /mnt/prov
mount -t proc proc /proc 2>/dev/null || true
mount -t sysfs sysfs /sys 2>/dev/null || true
mount -t devtmpfs devtmpfs /dev 2>/dev/null || true

fail() { log "FATAL: $*"; log "dropping to a shell for diagnosis"; exec /bin/sh; }

# Identify the two virtio-blk disks by content (enumeration order isn't guaranteed): the provisioning
# payload is a raw tar (its first header carries a `ustar` magic at byte 257); the root disk is empty.
log "locating the payload + root disks"
PROV=""; ROOTDISK=""
for d in /dev/vda /dev/vdb /dev/vdc /dev/vdd; do
    [ -b "$d" ] || continue
    if dd if="$d" bs=1 skip=257 count=5 2>/dev/null | grep -q ustar; then
        PROV="$d"
    else
        ROOTDISK="$d"
    fi
done
[ -n "$PROV" ] || fail "no provisioning payload disk (ustar tar) found on any virtio-blk device"
[ -n "$ROOTDISK" ] || fail "no root disk found"
log "payload disk=$PROV root disk=$ROOTDISK"

log "extracting the provisioning payload from $PROV"
tar -x -f "$PROV" -C /mnt/prov || fail "extracting the provisioning payload from $PROV failed"

log "formatting the root disk ($ROOTDISK)"
# busybox provides the `mke2fs` applet (there is no `mkfs.ext2` alias); it makes an ext2 fs that the
# kernel's ext4 driver mounts read/write, which is fine for a disposable clone. Use 4 KiB blocks: at
# the default 1 KiB block size a ~96 GiB disk has tens of thousands of block groups, which is slow and
# fragile; 4 KiB quarters the block count. (A journalled ext4 via a static mke2fs is a documented
# upgrade — see docs/LINUX_VM.md.)
mke2fs -F -b 4096 "$ROOTDISK" || fail "mke2fs $ROOTDISK failed"
mount -t ext2 "$ROOTDISK" /mnt/root || fail "could not mount the new root filesystem"

log "unpacking the root filesystem"
cd /mnt/root
if [ -f /mnt/prov/rootfs.tar.xz ]; then
    unxz < /mnt/prov/rootfs.tar.xz | tar -x || fail "unpacking rootfs.tar.xz failed"
elif [ -f /mnt/prov/rootfs.tar.gz ]; then
    tar -xzf /mnt/prov/rootfs.tar.gz || fail "unpacking rootfs.tar.gz failed"
else
    fail "no rootfs tarball found in the provisioning share"
fi

# Bake the kernel modules matching our external kernel into the rootfs. The cloud rootfs ships none,
# and modern Ubuntu kernels modularize vsock/virtiofs/gpu — without these the agent can't bind vsock
# (the whole control plane) and the desktop has no virtio_gpu. The nucleic-modsetup unit (installed
# below) runs depmod + loads the drivers early on the first boot, before the agent.
if [ -f /mnt/prov/modules.tar.gz ]; then
    log "baking kernel modules into the rootfs"
    tar -xzf /mnt/prov/modules.tar.gz -C /mnt/root || fail "extracting kernel modules failed"
else
    log "WARN: no kernel modules payload — vsock/gpu drivers may be missing"
fi

log "installing the Nucleic agent + provisioning hooks"
mkdir -p /mnt/root/usr/local/bin /mnt/root/usr/local/sbin \
         /mnt/root/etc/systemd/system/multi-user.target.wants \
         /mnt/root/etc/systemd/system/sysinit.target.wants \
         /mnt/root/etc/systemd/network /mnt/root/var/lib/nucleic
# busybox has no `install` applet — use cp + chmod. (0755 for executables, 0644 for units.)
cp /mnt/prov/nucleic-linux-agent          /mnt/root/usr/local/bin/nucleic-linux-agent
chmod 0755                                /mnt/root/usr/local/bin/nucleic-linux-agent
cp /mnt/prov/nucleic-linux-agent.service  /mnt/root/etc/systemd/system/nucleic-linux-agent.service
chmod 0644                                /mnt/root/etc/systemd/system/nucleic-linux-agent.service
cp /mnt/prov/provision-linux-guest.sh     /mnt/root/usr/local/sbin/provision-linux-guest.sh
chmod 0755                                /mnt/root/usr/local/sbin/provision-linux-guest.sh
cp /mnt/prov/nucleic-firstboot.service    /mnt/root/etc/systemd/system/nucleic-firstboot.service
chmod 0644                                /mnt/root/etc/systemd/system/nucleic-firstboot.service
cp /mnt/prov/nucleic-modsetup.service     /mnt/root/etc/systemd/system/nucleic-modsetup.service
chmod 0644                                /mnt/root/etc/systemd/system/nucleic-modsetup.service
if [ -f /mnt/prov/nucleic-provision.env ]; then
    cp /mnt/prov/nucleic-provision.env /mnt/root/etc/nucleic-provision.env
    chmod 0644 /mnt/root/etc/nucleic-provision.env
fi
# Optional AT-SPI semantic agent (Rust) + its systemd USER unit — present only when the artifact was
# baked into the payload. It runs inside the graphical session (a11y bus) on vsock port 2036; the
# provisioner enables the user service globally. Absent → the host falls back to screenshot + pixels.
if [ -f /mnt/prov/nucleic-a11y-agent ]; then
    cp /mnt/prov/nucleic-a11y-agent /mnt/root/usr/local/bin/nucleic-a11y-agent
    chmod 0755 /mnt/root/usr/local/bin/nucleic-a11y-agent
    mkdir -p /mnt/root/etc/systemd/user
    cp /mnt/prov/nucleic-a11y-agent.service /mnt/root/etc/systemd/user/nucleic-a11y-agent.service
    chmod 0644 /mnt/root/etc/systemd/user/nucleic-a11y-agent.service
    # Phase 2: the Mutter geometry helper GNOME Shell extension (focused-window global origin).
    if [ -f /mnt/prov/geometry-extension.js ]; then
        EXTDIR="/mnt/root/usr/share/gnome-shell/extensions/nucleic-geometry@nucleic.xyz"
        mkdir -p "$EXTDIR"
        cp /mnt/prov/geometry-metadata.json "$EXTDIR/metadata.json"
        cp /mnt/prov/geometry-extension.js  "$EXTDIR/extension.js"
        chmod 0644 "$EXTDIR/metadata.json" "$EXTDIR/extension.js"
    fi
fi

# Enable: module setup EARLY (sysinit, before the agent), then the agent (always) + first-boot
# provisioning (removes itself when done).
ln -sf ../nucleic-modsetup.service    /mnt/root/etc/systemd/system/sysinit.target.wants/nucleic-modsetup.service
ln -sf ../nucleic-linux-agent.service /mnt/root/etc/systemd/system/multi-user.target.wants/nucleic-linux-agent.service
ln -sf ../nucleic-firstboot.service   /mnt/root/etc/systemd/system/multi-user.target.wants/nucleic-firstboot.service

# Root fstab (the ext4 driver mounts the ext2 image); the session workspace share is mounted lazily by
# the provisioner/agent, not here.
cat > /mnt/root/etc/fstab <<EOF
/dev/vda / ext4 defaults 0 1
EOF

# NAT DHCP so phase-2 apt has network (the cloud rootfs uses systemd-networkd).
cat > /mnt/root/etc/systemd/network/10-nucleic-nat.network <<EOF
[Match]
Name=en* eth*
[Network]
DHCP=yes
EOF
mkdir -p /mnt/root/etc/systemd/system/multi-user.target.wants
ln -sf /lib/systemd/system/systemd-networkd.service \
       /mnt/root/etc/systemd/system/multi-user.target.wants/systemd-networkd.service 2>/dev/null || true

log "syncing"
cd /
sync
# /mnt/prov is a tar we untarred, not a mount (the payload rides a block device now) — nothing to
# unmount there; only the root fs needs unmounting before poweroff.
umount /mnt/root || true
log "done — powering off"
poweroff -f
