#! /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>
#
# 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

# 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="boot=fll lang=${FLL_GRUB2_LANG} tz=${FLL_GRUB2_TZ} ${FLL_GRUB2_CHEATCODE} "

# 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
	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

	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

	if ${ISOINFO} -R -i ${iso_image} -f 2>/dev/null | grep -q /boot/grub/kernels.cfg; then
		${ISOINFO} -R -i ${iso_image} -x /boot/grub/kernels.cfg 2>/dev/null | \
			awk -v fromhd="${cur_fromhd}" -v fromiso="${stripped_path}" \
				-v iso_image="$(basename ${iso_image})" -v boot_cache="${boot_cache}" \
				-v cmdline="${CMDLINE}" -v submenu="${FLL_GRUB2_SUBMENU}" \
			'BEGIN {
				lines = 0
			}
			{
				sub(/^[ \t]+/, "", $0)
				sub(/\/boot/, "(loop)/boot", $0)
				sub(/[ ]+\$kopts$/, "", $0)
				if (/menuentry/ && !/find.none/) {
					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 (/\(loop\)\/boot\/vmlinuz/) {
					n = split(cmdline, cheatcode, " ")
					for (i = 1; i <= n; i++) {
						if (cheatcode[i] ~ /=$/)
							sub(cheatcode[i], "", cmdline)
						else
							sub(cheatcode[i], "", $0)
					}
					gsub(/[ ]+/, " ", $0)
					gsub(/[ ]+$/, "", cmdline)
					kernels[++lines] = sprintf("\t%s fromhd=%s fromiso=%s %s\n",
						$0, fromhd, fromiso, cmdline)
				}
				if (/\(loop\)\/boot\/initrd.img/) {
					kernels[++lines] = sprintf("\t%s\n}\n", $0)
				}
			}
			END {
				if (lines == 0)
					exit(0)
				if (submenu == "true")
					printf "submenu \"%s\" {\n", iso_image
				for (line = 1; line <= lines; line++)
					printf kernels[line]
				if (submenu == "true")
					printf "}\n"
			}'
	else
		kernels="$( ( ${ISOINFO} -J -f -i ${iso_image} 2>/dev/null || \
			${ISOINFO} -R -f -i ${iso_image} 2>/dev/null || ${ISOINFO} -f -i ${iso_image} ) | \
			sed -n -e 's|\;[0-9]*$||' -e 'y|ABCDEFGHIJKLMNOPQRSTUVWXYZ|abcdefghijklmnopqrstuvwxyz|' \
			-e 's|/boot/\(vmlinuz.*\)|\1|p')"
		if ( ${ISOINFO} -J -l -i ${iso_image} 2> /dev/null || \
			${ISOINFO} -R -l -i ${iso_image} 2>/dev/null || ${ISOINFO} -l -i ${iso_image}) | \
			tr A-Z a-z | grep -q initrd\\.img; then
			initrd_suffix=".img"
		fi
		if [ -n "${kernels}" ] && [ "${FLL_GRUB2_SUBMENU}" = "true" ]; then
			printf "submenu \"$(basename ${iso_image} .iso)\" {\n"
		fi
		for kernel in ${kernels}; do
			printf "menuentry \"$(basename ${iso_image} .iso) (${kernel#vmlinuz\-})\" {\n"
			printf "\tinsmod iso9660\n"
			printf '%s\n' "${boot_cache}"

			cat << EOF
	loopback loop ${stripped_path}
	linux (loop)/boot/${kernel} fromhd=${cur_fromhd} fromiso=${stripped_path} $CMDLINE
	initrd (loop)/boot/initrd${initrd_suffix}${kernel#vmlinuz}
}
EOF
		done
		[ -n "${kernels}" ] && [ "${FLL_GRUB2_SUBMENU}" = "true" ] && printf "}\n"
	fi
done

rm -f "${DEVICE_MAP_TMP}"
