#! /bin/sh -e

# grub-mkconfig helper script.
#
# Copyright © 2009 Joaquim Boura <x-un-i@sapo.pt>
# Copyright © 2009-2015 Stefan Lippers-Hollmann <s.l-h@gmx.de>
# Copyright © 2011-2015 Niall Walsh <niallwalsh@celtux.org>
# Copyright © 2025-2026 Kel Modderman <kelvmod@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# source common grub2 functions
prefix=/usr
exec_prefix=${prefix}
bindir=${exec_prefix}/bin
libdir=${exec_prefix}/lib
. ${libdir}/grub/grub-mkconfig_lib

# override tool behaviour through /etc/default/grub2-fll-fromiso
if [ -r /etc/default/grub2-fll-fromiso ]; then
	. /etc/default/grub2-fll-fromiso
fi

FLL_GRUB2_ISO_LOCATION="${FLL_GRUB2_ISO_LOCATION:-"/srv/ISO"}"
FLL_GRUB2_ISO_PREFIX="${FLL_GRUB2_ISO_PREFIX:-"aptosid fullstory"}"
FLL_GRUB2_LANG="${FLL_GRUB2_LANG:-""}"
FLL_GRUB2_TZ="${FLL_GRUB2_TZ:-""}"
FLL_GRUB2_CHEATCODE="${FLL_GRUB2_CHEATCODE:-""}"
FLL_GRUB2_SUBMENU="${FLL_GRUB2_SUBMENU:-"false"}"

# we need isoinfo, bail out if it's not available
ISOINFO="$(which isoinfo)"
if [ -z "${ISOINFO}" ]; then
	exit 1
fi

# osirrox (xorriso) and mdir/mcopy (mtools) are needed for ESP-based ISO layouts
OSIRROX="$(which osirrox)"
if [ -z "${OSIRROX}" ]; then
	exit 1
fi

MDIR="$(which mdir)"
if [ -z "${MDIR}" ]; then
	exit 1
fi

MCOPY="$(which mcopy)"
if [ -z "${MCOPY}" ]; then
	exit 1
fi

# Strip from an options string any key=value overridden by CMDLINE, and
# persist_uuid. Reads the options string as $1, prints the result to stdout.
strip_iso_options() {
	local options="$1"
	options="$(echo "${options}" | sed 's/persist_uuid=[^ ]*//g')"
	for kv in ${CMDLINE}; do
		case "${kv}" in
			*=*) options="$(echo "${options}" | sed "s/${kv%%=*}=[^ ]*//g")" ;;
		esac
	done
	echo "${options}" | sed 's/  */ /g; s/^ //; s/ $//'
}

# Extract efi.img from an ISO into a temp file using osirrox.
# Prints the path to the extracted file, or nothing on failure.
# The caller is responsible for deleting the file when done.
extract_efi_img() {
	local iso="$1"
	local efi_tmp
	efi_tmp="$(mktemp --suffix=.efi.img)"
	if "${OSIRROX}" -indev "${iso}" -extract /efi.img "${efi_tmp}" \
			>/dev/null 2>&1 && [ -s "${efi_tmp}" ]; then
		echo "${efi_tmp}"
	else
		rm -f "${efi_tmp}"
	fi
}

# Detect the bootloader used by a fll ISO.
# Returns: grub | grub-efi | systemd-boot | refind | unknown
detect_iso_bootloader() {
	local iso="$1"
	local efi_img="$2"   # path to extracted efi.img, or empty

	# grub (BIOS hybrid): kernels.cfg on the ISO9660 layer
	if "${ISOINFO}" -R -i "${iso}" -f 2>/dev/null \
			| grep -q '^/boot/grub/kernels.cfg$'; then
		echo "grub"
		return
	fi

	# ESP-based: distinguish by FAT contents
	if [ -n "${efi_img}" ]; then
		# grub-efi: kernels.cfg inside FAT under /boot/grub/
		if "${MDIR}" -i "${efi_img}" '::/boot/grub/kernels.cfg' >/dev/null 2>&1; then
			echo "grub-efi"
			return
		fi
		# systemd-boot: /loader/loader.conf inside FAT
		if "${MDIR}" -i "${efi_img}" '::/loader/loader.conf' >/dev/null 2>&1; then
			echo "systemd-boot"
			return
		fi
		# refind: /EFI/BOOT/refind.conf inside FAT
		if "${MDIR}" -i "${efi_img}" '::/EFI/BOOT/refind.conf' >/dev/null 2>&1; then
			echo "refind"
			return
		fi
	fi

	echo ""
}

# Internal shared awk processor for grub kernels.cfg content.
# Reads kernels.cfg from stdin and emits GRUB menuentry stanzas.
# When efi=1 the double-loopback layout for grub-efi ISOs is used;
# when efi=0 (default) the single-loopback layout for BIOS hybrid ISOs is used.
_fromiso_grub_awk() {
	local cur_fromhd="$1"
	local stripped_path="$2"
	local iso_label="$3"
	local boot_cache="$4"
	local efi="${5:-0}"

	awk -v fromhd="${cur_fromhd}" -v fromiso="${stripped_path}" \
		-v iso_image="${iso_label}" -v boot_cache="${boot_cache}" \
		-v cmdline="${CMDLINE}" -v submenu="${FLL_GRUB2_SUBMENU}" \
		-v efi="${efi}" \
	'BEGIN {
		lines = 0
		# Path prefix for linux/initrd lines and the regex to match them
		if (efi) {
			pfx   = "(efiloop)/"
			lmatch = "\\(efiloop\\)/"
		} else {
			pfx   = "(loop)/boot/"
			lmatch = "\\(loop\\)/boot/"
		}
	}
	{
		sub(/^[ \t]+/, "", $0)
		if (efi) {
			# kernels.cfg paths are relative to FAT root: /chroot/vmlinuz
			sub(/^linux \//, "linux (efiloop)/", $0)
			sub(/^initrd \//, "initrd (efiloop)/", $0)
		} else {
			sub(/\/boot/, "(loop)/boot", $0)
		}
		sub(/[ ]+\$kopts$/, "", $0)
		if (/menuentry/ && !/find.none/) {
			if (submenu == "true")
				sub(/"/, "\"", $0)
			else
				sub(/"/, "\"" iso_image " ", $0)
			kernels[++lines] = sprintf("%s\n", $0)
			kernels[++lines] = sprintf("\tinsmod iso9660\n")
			kernels[++lines] = sprintf("%s\n", boot_cache)
			kernels[++lines] = sprintf("\tloopback loop %s\n", fromiso)
			if (efi)
				kernels[++lines] = sprintf("\tloopback efiloop (loop)/efi.img\n")
		}
		if ($0 ~ lmatch ".*vmlinuz") {
			n = split(cmdline, cheatcode, " ")
			for (i = 1; i <= n; i++) {
				if (cheatcode[i] ~ /=$/)
					sub(cheatcode[i], "", cmdline)
				else {
					eq = index(cheatcode[i], "=")
					if (eq > 0)
						gsub(substr(cheatcode[i], 1, eq) "[^ ]*", "", $0)
					else
						sub(cheatcode[i], "", $0)
				}
			}
			gsub(/[ ]+/, " ", $0)
			gsub(/persist_uuid=[[:alnum:]-]+ /, "", $0)
			gsub(/[ ]+$/, "", cmdline)
			kernels[++lines] = sprintf("\t%s fromhd=%s fromiso=%s %s\n",
				$0, fromhd, fromiso, cmdline)
		}
		if ($0 ~ lmatch ".*initrd") {
			kernels[++lines] = sprintf("\t%s\n}\n", $0)
		}
	}
	END {
		if (lines == 0)
			exit(0)
		for (line = 1; line <= lines; line++)
			printf kernels[line]
	}'
}

fromiso_stanzas_grub_hybrid() {
	local iso_image="$1"
	local cur_fromhd="$2"
	local stripped_path="$3"
	local boot_cache="$4"

	${ISOINFO} -R -i "${iso_image}" -x /boot/grub/kernels.cfg 2>/dev/null | \
		_fromiso_grub_awk "${cur_fromhd}" "${stripped_path}" \
			"$(basename "${iso_image}")" "${boot_cache}" 0
}

fromiso_stanzas_grub_efi() {
	local iso_image="$1"
	local efi_img="$2"
	local cur_fromhd="$3"
	local stripped_path="$4"
	local boot_cache="$5"

	"${MCOPY}" -o -i "${efi_img}" '::/boot/grub/kernels.cfg' - 2>/dev/null | \
		_fromiso_grub_awk "${cur_fromhd}" "${stripped_path}" \
			"$(basename "${iso_image}")" "${boot_cache}" 1
}

fromiso_stanzas_systemd_boot() {
	local iso_image="$1"
	local efi_img="$2"
	local cur_fromhd="$3"
	local stripped_path="$4"
	local boot_cache="$5"

	# Extract all loader entries from the FAT image into a temp dir
	local entries_tmp
	entries_tmp="$(mktemp -d)"
	"${MCOPY}" -o -s -i "${efi_img}" '::/loader/entries' "${entries_tmp}" \
		>/dev/null 2>&1 || true

	local entries_dir="${entries_tmp}/entries"
	if [ ! -d "${entries_dir}" ]; then
		rm -rf "${entries_tmp}"
		return
	fi

	for entry in "${entries_dir}"/*.conf; do
		[ -f "${entry}" ] || continue

		local title="" linux="" initrd="" options=""
		while IFS= read -r line; do
			case "${line}" in
				title\ *)   title="${line#title   }"   ;;
				linux\ *)   linux="${line#linux   }"   ;;
				initrd\ *)  initrd="${line#initrd  }"  ;;
				options\ *) options="${line#options }"  ;;
			esac
		done < "${entry}"

		[ -z "${linux}" ] && continue

		options="$(strip_iso_options "${options}")"

		local iso_label
		iso_label="$(basename "${iso_image}")"

		if [ "${FLL_GRUB2_SUBMENU}" != "true" ]; then
			title="${iso_label} ${title}"
		fi

		printf 'menuentry "%s" {\n' "${title}"
		printf '\tinsmod iso9660\n'
		printf '%s\n' "${boot_cache}"
		printf '\tloopback loop %s\n' "${stripped_path}"
		printf '\tloopback efiloop (loop)/efi.img\n'
		printf '\tlinux (efiloop)%s %s fromhd=%s fromiso=%s %s\n' \
			"${linux}" "${options}" "${cur_fromhd}" "${stripped_path}" "${CMDLINE}"
		printf '\tinitrd (efiloop)%s\n' "${initrd}"
		printf '}\n'
	done

	rm -rf "${entries_tmp}"
}

fromiso_stanzas_refind() {
	local iso_image="$1"
	local efi_img="$2"
	local cur_fromhd="$3"
	local stripped_path="$4"
	local boot_cache="$5"

	local conf_tmp
	conf_tmp="$(mktemp)"
	"${MCOPY}" -o -i "${efi_img}" '::/EFI/BOOT/refind.conf' - \
		>"${conf_tmp}" 2>/dev/null || true
	[ -s "${conf_tmp}" ] || { rm -f "${conf_tmp}"; return; }

	local iso_label
	iso_label="$(basename "${iso_image}")"

	local in_entry=0
	local title="" linux="" initrd="" options=""
	while IFS= read -r line; do
		line="$(echo "${line}" | sed 's/^[[:space:]]*//')"
		case "${line}" in
			menuentry\ \"*)
				in_entry=1
				title="${line#*\"}"
				title="${title%%\"*}"
				linux=""; initrd=""; options=""
				;;
			loader\ *)
				linux="${line#loader }"
				;;
			initrd\ *)
				initrd="${line#initrd }"
				;;
			options\ *)
				options="${line#options }"
				options="${options#\"}"
				options="${options%\"}"
				;;
			"}")
				if [ "${in_entry}" = "1" ] && [ -n "${linux}" ]; then
					options="$(strip_iso_options "${options}")"
					if [ "${FLL_GRUB2_SUBMENU}" != "true" ]; then
						title="${iso_label} ${title}"
					fi
					printf 'menuentry "%s" {\n' "${title}"
					printf '\tinsmod iso9660\n'
					printf '%s\n' "${boot_cache}"
					printf '\tloopback loop %s\n' "${stripped_path}"
					printf '\tloopback efiloop (loop)/efi.img\n'
					printf '\tlinux (efiloop)%s %s fromhd=%s fromiso=%s %s\n' \
						"${linux}" "${options}" "${cur_fromhd}" "${stripped_path}" "${CMDLINE}"
					printf '\tinitrd (efiloop)%s\n' "${initrd}"
					printf '}\n'
				fi
				in_entry=0
				title=""; linux=""; initrd=""; options=""
				;;
		esac
	done < "${conf_tmp}"

	rm -f "${conf_tmp}"
}

submenu_open() {
	[ "${FLL_GRUB2_SUBMENU}" = "true" ] && printf 'submenu "%s" {\n' "$1" || true
}

submenu_close() {
	[ "${FLL_GRUB2_SUBMENU}" = "true" ] && printf '}\n' || true
}

# find ISO
for location in $FLL_GRUB2_ISO_LOCATION; do
	[ -d "${location}" ] || continue
	for prefix in $FLL_GRUB2_ISO_PREFIX; do
		FROMISO="${FROMISO} $(find ${location} -maxdepth 1 -name ${prefix}\*.iso -exec stat --format='%w %n' {} + | sort -rn | cut -d' ' -f4)"
	done
done

# no iso found, quit
if [ -z "${FROMISO}" ]; then
	exit 0
fi

# assemble cmdline
CMDLINE=""
[ -n "${FLL_GRUB2_LANG}" ]      && CMDLINE="lang=${FLL_GRUB2_LANG} "
[ -n "${FLL_GRUB2_TZ}" ]        && CMDLINE="${CMDLINE}tz=${FLL_GRUB2_TZ} "
[ -n "${FLL_GRUB2_CHEATCODE}" ] && CMDLINE="${CMDLINE}${FLL_GRUB2_CHEATCODE} "
BASE_CMDLINE="${CMDLINE}"

# create a temp device.map as the actual can be not uptodate
DEVICE_MAP_TMP=$(mktemp)
grub-mkdevicemap -m "${DEVICE_MAP_TMP}"

for iso_image in $FROMISO; do
	CMDLINE="${BASE_CMDLINE}"
	cur_device="$(grub-probe --device-map=${DEVICE_MAP_TMP} --target=device ${iso_image})"
	boot_cache="$(prepare_grub_to_access_device ${cur_device} | sed -e "s/^/\t/")"
	if [ "$(grub-probe -t abstraction --device ${cur_device} | sed -e 's,.*\(lvm\).*,\1,')" = "lvm" ]; then
		cur_fromhd="${cur_device}"
	else
		cur_fromhd="$(grub-probe --device-map=${DEVICE_MAP_TMP} --target=fs_uuid ${iso_image})"
		cur_fromhd_fs="$(/sbin/blkid -s TYPE -o value ${cur_device})"

		if [ "x${cur_fromhd_fs}" = "xmsdos" ] || [ "x${cur_fromhd_fs}" = "xntfs" ] || [ "x${cur_fromhd_fs}" = "xvfat" ]; then
			cur_fromhd="/dev/disk/by-uuid/$(/sbin/blkid -o value -s UUID ${cur_device})"
		else
			cur_fromhd="UUID=${cur_fromhd}"
		fi
	fi

	case "${cur_device}" in
		/dev/mapper/luks-*)
			CMDLINE="${CMDLINE} $(echo ${cur_device} | sed 's#/dev/mapper/luks-#rd.luks.uuid=#')"
			;;
	esac

	mountpoint=""
	subvolume=""
	while IFS=' ' read dev mount fs opts dump pass; do
		[ "$mount" = "/" ] && continue
		case $iso_image in
			$mount*)
			[ "$fs" = "btrfs" ] && subvolume=$(echo "$opts" | sed -n 's/.*subvol=\([^,]*\).*/\1/p')
			mountpoint="$mount"
			break
			;;
		esac
	done < /proc/mounts
	stripped_path="${subvolume}${iso_image##$mountpoint}"

	echo "Found fromiso: $(basename ${iso_image}) on ${cur_device}: ${stripped_path}" >&2

	# Extract efi.img once (empty string if absent or tools unavailable)
	efi_img="$(extract_efi_img "${iso_image}")"

	bootloader="$(detect_iso_bootloader "${iso_image}" "${efi_img}")"
	if [ -n "${bootloader}" ]; then
		echo "Detected bootloader: ${bootloader} in $(basename ${iso_image})" >&2
	else
		echo "Skipping $(basename ${iso_image}): unrecognised bootloader layout" >&2
		continue
	fi

	iso_label="$(basename "${iso_image}")"
	submenu_open "${iso_label}"

	case "${bootloader}" in
		grub)
			fromiso_stanzas_grub_hybrid "${iso_image}" \
				"${cur_fromhd}" "${stripped_path}" "${boot_cache}"
			;;
		grub-efi)
			fromiso_stanzas_grub_efi "${iso_image}" "${efi_img}" \
				"${cur_fromhd}" "${stripped_path}" "${boot_cache}"
			;;
		systemd-boot)
			fromiso_stanzas_systemd_boot "${iso_image}" "${efi_img}" \
				"${cur_fromhd}" "${stripped_path}" "${boot_cache}"
			;;
		refind)
			fromiso_stanzas_refind "${iso_image}" "${efi_img}" \
				"${cur_fromhd}" "${stripped_path}" "${boot_cache}"
			;;
	esac

	submenu_close

	# Clean up extracted efi.img temp file
	[ -n "${efi_img}" ] && rm -f "${efi_img}"
done

rm -f "${DEVICE_MAP_TMP}"
