#!/bin/sh
# ESO Kernel boot manager - safe "trial boot" switching between the ESO Kernel
# and the Debian kernel. Usage:
#   eso-kernel-boot setup <release>   after install/upgrade: next boot tries ESO once
#   eso-kernel-boot boot              at every boot: confirm a trial, or start one on a fresh install
#   eso-kernel-boot confirm           keep ESO as default if the trial boot worked
#   eso-kernel-boot default eso|debian
#   eso-kernel-boot status            prints key=value lines (for Settings)
#   eso-kernel-boot remove            on package removal
set -u
ST=${ESO_KB_STATE:-/var/lib/eso-kernel}
CFG=${ESO_KB_CFG:-/etc/default/grub.d/90-eso-kernel.cfg}
GRUBCFG=${ESO_GRUBCFG:-/boot/grub/grub.cfg}
mkdir -p "$ST"
log() { echo "eso-kernel: $*"; logger -t eso-kernel "$*" 2>/dev/null || true; }
state() { echo "$1" > "$ST/state"; }

secure_boot() {
  f=$(ls /sys/firmware/efi/efivars/SecureBoot-* 2>/dev/null | head -n1)
  [ -n "$f" ] || return 1
  [ "$(od -An -t u1 -j4 -N1 "$f" 2>/dev/null | tr -d ' ')" = "1" ]
}
have_grub() { command -v update-grub >/dev/null 2>&1 && command -v grub-reboot >/dev/null 2>&1 && [ -f "$GRUBCFG" ]; }
# grubenv cannot be written by GRUB on these, so one-shot boots never clear
grubenv_ok() {
  fs=$(grub-probe --target=fs /boot/grub 2>/dev/null || echo unknown)
  ab=$(grub-probe --target=abstraction /boot/grub 2>/dev/null || true)
  case "$fs" in btrfs|zfs|unknown) return 1 ;; esac
  [ -z "$ab" ]
}
sub_id() { sed -n "s/^submenu .*\\\$menuentry_id_option '\(gnulinux-advanced-[^']*\)'.*/\1/p" "$GRUBCFG" | head -n1; }
entry_ids() { sed -n "s/^[[:space:]]*menuentry .*\\\$menuentry_id_option '\(gnulinux-[^']*-advanced-[^']*\)'.*/\1/p" "$GRUBCFG"; }
eso_id() { entry_ids | grep -F "gnulinux-$1-advanced-" | head -n1; }
deb_id() { entry_ids | grep -v -- '-eso-advanced-' | head -n1; }   # newest Debian kernel (grub sorts newest first)
path() { echo "$(sub_id)>$1"; }

write_cfg() {
  if [ ! -f "$CFG" ] || ! grep -q 'eso-kernel v2' "$CFG"; then
    mkdir -p "$(dirname "$CFG")"
    cp ${ESO_KB_CFGSRC:-/usr/lib/eso-kernel/90-eso-kernel.cfg} "$CFG"
  fi
}
ensure_saved() { write_cfg; update-grub >/dev/null 2>&1 || log "update-grub failed"; }

cmd_setup() {
  rel=$1
  if ! have_grub; then state "nogrub"; log "GRUB not found - $rel installed, choose it in your boot menu"; return 0; fi
  if secure_boot; then
    state "secureboot"
    log "Secure Boot is on - $rel is installed but not made default (it is not signed by Microsoft/Debian)."
    return 0
  fi
  ensure_saved
  e=$(eso_id "$rel"); d=$(deb_id)
  if [ -z "$e" ]; then state "noentry"; log "no GRUB entry for $rel"; return 0; fi
  if ! grubenv_ok; then
    grub-set-default "$(path "$e")"; echo "$rel" > "$ST/default"; state "default"
    log "$rel set as default (no trial boot on this /boot filesystem; Debian kernel is under Advanced options)"
    return 0
  fi
  if [ -n "$d" ]; then grub-set-default "$(path "$d")"; fi
  grub-reboot "$(path "$e")"
  echo "$rel" > "$ST/trial"; state "trial"
  log "next boot will try $rel once; it becomes the default after a successful boot"
}

cmd_confirm() {
  [ -f "$ST/trial" ] || return 0
  rel=$(cat "$ST/trial"); now=$(uname -r)
  if [ "$now" = "$rel" ]; then
    e=$(eso_id "$rel")
    [ -n "$e" ] && grub-set-default "$(path "$e")"
    echo "$rel" > "$ST/default"; rm -f "$ST/trial" "$ST/failed"; state "default"
    update-grub >/dev/null 2>&1 || true
    [ -n "$e" ] && grub-set-default "$(path "$e")"
    log "$rel booted fine - now the default kernel"
    return 0
  fi
  # still pending (not rebooted yet)?
  if grub-editenv list 2>/dev/null | grep -q '^next_entry=.'; then return 0; fi
  echo "$rel" > "$ST/failed"; rm -f "$ST/trial"; state "failed"
  log "trial boot of $rel did not complete - staying on $now"
}

cmd_boot() {
  if [ -f "$ST/trial" ]; then cmd_confirm; return 0; fi
  # installed from the ESO OS USB: the package was set up without GRUB - start the trial now
  case "$(cat "$ST/state" 2>/dev/null || echo none)" in
    nogrub|none)
      rel=$(cat /usr/lib/eso-kernel/release 2>/dev/null)
      [ -n "$rel" ] && [ -f "/boot/vmlinuz-$rel" ] && [ -f "/boot/initrd.img-$rel" ] && have_grub && cmd_setup "$rel" ;;
  esac
  return 0
}

cmd_default() {
  have_grub || { echo "GRUB not found" >&2; return 1; }
  write_cfg
  case "$1" in
    eso) if secure_boot; then echo "Secure Boot is on: turn it off in the firmware settings to use the ESO Kernel" >&2; return 1; fi
         rel=$(ls -1 /boot/vmlinuz-*-eso 2>/dev/null | sed 's|.*/vmlinuz-||' | sort -V | tail -n1)
         e=$(eso_id "$rel"); [ -n "$e" ] || { echo "no ESO Kernel entry" >&2; return 1; }
         echo "$rel" > "$ST/default"; rm -f "$ST/trial" "$ST/failed"; state "default"
         update-grub >/dev/null 2>&1 || true; grub-set-default "$(path "$e")" ;;
    debian) rm -f "$ST/default" "$ST/trial"; state "debian"; update-grub >/dev/null 2>&1 || true
            d=$(deb_id); [ -n "$d" ] && grub-set-default "$(path "$d")" ;;
    *) echo "usage: eso-kernel-boot default eso|debian" >&2; return 2 ;;
  esac
}

cmd_status() {
  echo "running=$(uname -r)"
  echo "state=$(cat "$ST/state" 2>/dev/null || echo none)"
  echo "installed=$(ls -1 /boot/vmlinuz-*-eso 2>/dev/null | sed 's|.*/vmlinuz-||' | sort -V | tail -n1)"
  secure_boot && echo "secureboot=1" || echo "secureboot=0"
  echo "bore=$(cat /proc/sys/kernel/sched_bore 2>/dev/null || echo -)"
  echo "preempt=$(sed -n 's/.*(\([a-z]*\)).*/\1/p' /sys/kernel/debug/sched/preempt 2>/dev/null)"
}

cmd_remove() {
  rm -f "$CFG"
  if have_grub; then
    d=$(deb_id); update-grub >/dev/null 2>&1 || true
    grub-editenv - unset saved_entry next_entry 2>/dev/null || true
  fi
  rm -rf "$ST"
}

case "${1:-}" in
  setup) cmd_setup "${2:?release}" ;;
  boot) cmd_boot ;;
  confirm) cmd_confirm ;;
  default) cmd_default "${2:-}" ;;
  status) cmd_status ;;
  remove) cmd_remove ;;
  *) echo "usage: eso-kernel-boot setup REL|confirm|default eso|debian|status|remove" >&2; exit 2 ;;
esac
