#!/bin/bash set -e # SeniorNet Kiosk ISO Builder # Takes the official Debian Trixie netinstall ISO and repacks it with: # - preseed.cfg embedded for silent auto-install # - GRUB + isolinux configured for auto-boot (UEFI + BIOS) # # The kiosk app itself is NOT bundled — it's downloaded from Gitea at # first-boot time. Rebuild the ISO only when changing Debian base or preseed. # # Run: sudo ./build-iso.sh # Output: iso-output/seniornet-kiosk.iso if [ "$EUID" -ne 0 ]; then echo "ERROR: Run as root: sudo ./build-iso.sh"; exit 1 fi REPO_DIR="$(cd "$(dirname "$0")" && pwd)" OUTPUT_DIR="$REPO_DIR/iso-output" WORK_DIR="$(mktemp -d /tmp/kiosk-iso-XXXX)" trap 'rm -rf "$WORK_DIR"' EXIT DEBIAN_ISO_URL="https://cdimage.debian.org/cdimage/daily-builds/daily/arch-latest/amd64/iso-cd/debian-testing-amd64-netinst.iso" ISO_CACHE="$OUTPUT_DIR/debian-netinst.iso" OUTPUT_ISO="$OUTPUT_DIR/seniornet-kiosk.iso" mkdir -p "$OUTPUT_DIR" # ── Dependencies ─────────────────────────────────────────────────── echo "==> Checking dependencies..." MISSING="" for pkg in xorriso isolinux curl; do dpkg -l "$pkg" &>/dev/null || MISSING="$MISSING $pkg" done if [ -n "$MISSING" ]; then apt-get install -y $MISSING fi # ── Download Debian netinstall ISO (cached) ──────────────────────── if [ -f "$ISO_CACHE" ]; then echo "==> Using cached Debian ISO: $ISO_CACHE" else echo "==> Downloading Debian Trixie netinstall (~700 MB)..." curl -L --progress-bar "$DEBIAN_ISO_URL" -o "$ISO_CACHE" fi # ── Extract ISO ──────────────────────────────────────────────────── echo "==> Extracting ISO..." mkdir -p "$WORK_DIR/iso" xorriso -osirrox on -indev "$ISO_CACHE" -extract / "$WORK_DIR/iso" 2>/dev/null chmod -R u+w "$WORK_DIR/iso" # ── Add preseed to ISO root ──────────────────────────────────────── # Accessible during install as /cdrom/preseed.cfg cp "$REPO_DIR/preseed.cfg" "$WORK_DIR/iso/preseed.cfg" # ── Auto-boot: GRUB (UEFI) ──────────────────────────────────────── echo "==> Configuring auto-boot..." cat > "$WORK_DIR/iso/boot/grub/grub.cfg" << 'GRUBEOF' set default=0 set timeout=5 menuentry "SeniorNet Kiosk — Automatic Install" { linux /install.amd/vmlinuz preseed/file=/cdrom/preseed.cfg auto=true priority=critical quiet --- initrd /install.amd/initrd.gz } menuentry "SeniorNet Kiosk — Install (verbose)" { linux /install.amd/vmlinuz preseed/file=/cdrom/preseed.cfg auto=true priority=critical --- initrd /install.amd/initrd.gz } GRUBEOF # ── Auto-boot: isolinux (BIOS legacy) ───────────────────────────── if [ -f "$WORK_DIR/iso/isolinux/isolinux.cfg" ]; then cat > "$WORK_DIR/iso/isolinux/isolinux.cfg" << 'ISOEOF' default install timeout 50 label install menu label SeniorNet Kiosk - Automatic Install kernel /install.amd/vmlinuz append initrd=/install.amd/initrd.gz preseed/file=/cdrom/preseed.cfg auto=true priority=critical quiet --- label verbose menu label SeniorNet Kiosk - Install (verbose) kernel /install.amd/vmlinuz append initrd=/install.amd/initrd.gz preseed/file=/cdrom/preseed.cfg auto=true priority=critical --- ISOEOF fi # ── Repack ISO (UEFI + BIOS hybrid) ─────────────────────────────── echo "==> Building final ISO..." cd "$WORK_DIR/iso" xorriso -as mkisofs \ -r -J -joliet-long \ -V "SeniorNet Kiosk" \ --grub2-mbr boot/grub/i386-pc/boot_hybrid.img \ -partition_offset 16 \ --mbr-force-bootable \ -append_partition 2 28732ac11ff8d211ba4b00a0c93ec93b boot/grub/efi.img \ -appended_part_as_gpt \ -iso_mbr_part_type a2a0d0ebe5b9334487c068b6b72699c7 \ -c '/boot/boot.cat' \ -b '/boot/grub/i386-pc/eltorito.img' \ -no-emul-boot -boot-load-size 4 -boot-info-table --grub2-boot-info \ -eltorito-alt-boot \ -e '--interval:appended_partition_2:::' \ -no-emul-boot \ -o "$OUTPUT_ISO" \ . 2>&1 | tail -5 echo SIZE=$(du -sh "$OUTPUT_ISO" | cut -f1) echo "==> ✓ ISO complete: $OUTPUT_ISO ($SIZE)" echo echo "Write to USB:" echo " sudo dd if=\"$OUTPUT_ISO\" of=/dev/sdX bs=4M status=progress && sync" echo echo "Boot from USB — Debian installs silently (~10 min) then reboots." echo "On first reboot: setup wizard prompts for username and senior's name." echo "Second reboot: kiosk is running."