#!/bin/bash
## PMON Bootloader Menu Updater For Debian
## Copyright (C) 2020 Boyue Zhang <boyue.zhang@pzhang.net>
## Copyright (C) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
##
## Based on u-boot-update, which is:
## Copyright (C) 2006-2012 Daniel Baumann <daniel.baumann@progress-technologies.net>
## Copyright (C) 2016-2017 Riku Voipio    <riku.voipio@linaro.org>
##
## This program comes with ABSOLUTELY NO WARRANTY; for details see COPYING.
## This is free software, and you are welcome to redistribute it
## under certain conditions; see COPYING for details.

set -e

_PMON_DIRECTORY="/boot"

Update ()
{
	 # Upate target file using source content
	_TARGET="${1}"
	_SOURCE="${2}"

	_TMPFILE="${_TARGET}.tmp"
	rm -f "${_TMPFILE}"

	echo "${_SOURCE}" > "${_TMPFILE}"

	if [ -e "${_TARGET}" ] && cmp -s "${_TARGET}" "${_TMPFILE}"
	then
		rm -f "${_TMPFILE}"
	else
		# FIXME: should use fsync here
		echo "P: Updating ${_TARGET}..."
		mv -f "${_TMPFILE}" "${_TARGET}"
	fi
}


function sd2wd
{
	flag=-1
	index=0

	local sdpart=${1##*/}
	value=$(echo $sdpart | grep nvme)
	if [ $? -eq 0 ]
	then
		local disk=$(echo "$sdpart" | grep '[[:digit:]]\+' -o | head -n1)
		local order=$(echo "$sdpart" | grep '[[:digit:]]\+' -o | tail -n1)
		str1=-1
		flag=1
	fi

	value=$(echo $sdpart | grep -E 'sd|hd')
	if [ $? -eq 0 ];
	then
		str=`echo ${sdpart//[0-9]}`
		str1=`echo ${str:0-1}`
		local order=$(echo "$sdpart" | grep '[[:digit:]]\+' -o | head -n1)
		flag=0
	fi

	for i in {a..z}
	do
		if [[ $str1 != -1 && "$str1" == "$i" ]];
		then
			disk=$index
		fi

		((index++))

		if [ "$order" == "$index" ];
		then
			part=$i
		fi
	done

	if [ $flag == 1 ]
	then
		wdpart="nvme$disk$part"
	else
		wdpart="wd$disk$part"
	fi

	echo $wdpart
}

# FIXME: switch to check boot.cfg file can be written to
# User is unprivileged
if [ "$(id -u)" -ne 0 ]
then
	echo "E: need root privileges"
	exit 1
fi

# Redirect stdout to stderr due Debconf usage
exec 1>&2

# Reading the default file
if [ -e /etc/default/pmon-update ]
then
	. /etc/default/pmon-update
fi

PMON_UPDATE="${PMON_UPDATE:-true}"

if [ "${PMON_UPDATE}" != "true" ]
then
	echo "P: pmon-update is disabled in /etc/default/pmon-update."

	exit 0
fi


# Setting defaults if /etc/default/pmon-update is missing

PMON_ALTERNATIVES="${PMON_ALTERNATIVES:-default recovery}"
PMON_DEFAULT="${PMON_DEFAULT:-0}"
PMON_ENTRIES="${PMON_ENTRIES:-all}"
PMON_TIMEOUT="${PMON_TIMEOUT:-5}"
PMON_MENU_LABEL="${PMON_MENU_LABEL:-Loongnix-20 GNU/Linux kernel}"
PMON_PARAMETERS="${PMON_PARAMETERS:-ro quiet}"

BOOTDEV=($(LANG= df ${_PMON_DIRECTORY} --output=source))
BOOTDEV=${BOOTDEV[1]}
WD_BOOTDEV=$(sd2wd ${BOOTDEV})

# Find parameter for root and boot from /etc/fstab
if [ -e /etc/fstab ]
then
	# Find root and boot partition
	while read _LINE
	do

read _FS_SPEC _FS_FILE _FS_VFSTYPE _FS_MNTOPS _FS_FREQ _FS_PASSNO << EOF
${_LINE}
EOF

		if [ "${_FS_FILE}" = "/" ]
		then
			case "${_FS_SPEC}" in
				"#"*) ;;
				*) PMON_ROOT="root=${_FS_SPEC}"
					continue ;;
			esac
		fi

		if [ "${_FS_FILE}" = "${_PMON_DIRECTORY}" ]
		then
			case "${_FS_SPEC}" in
				"#"*) ;;
				*) PMON_BOOTDEV="/dev/fs/${_FS_VFSTYPE}@${WD_BOOTDEV}"
					continue ;;
			esac
		fi
	done < /etc/fstab
fi

# if not in fstab, try from current kernel arguments
if [ -z "${PMON_ROOT}" ]
then
	for param in `cat /proc/cmdline`
	do
		if [[ $param == root=* ]]
		then
			PMON_ROOT="$param"
			break
		fi
	done
fi

# if not in fstab, get from df command
if [ -z "${PMON_BOOTDEV}" ]
then
	BOOT_FSTYPE=($(LANG= df ${_PMON_DIRECTORY} --output=fstype))
	BOOT_FSTYPE=${BOOT_FSTYPE[1]}
	PMON_BOOTDEV="/dev/fs/${BOOT_FSTYPE}@${WD_BOOTDEV}"
fi

# Create extlinux.conf
_CONFIG="\
## ${_PMON_DIRECTORY}/boot.cfg
##
## IMPORTANT WARNING
##
## The configuration of this file is generated automatically.
## Do not edit this file manually, use: pmon-update

default ${PMON_DEFAULT}
timeout ${PMON_TIMEOUT}
showmenu 1
"

# Find linux versions
_KERNELS=$(linux-version list --paths | linux-version sort --reverse | sed -e 's,.*/boot/,,g')

# Find boot directory as seen in pmon, and path prefix while in linux
if [ "$(stat --printf %d /)" = "$(stat --printf %d /boot)" ]
then
	# / and /boot are on the same filesystem
	_BOOT_PATH="${PMON_BOOTDEV}/boot"
else
	# / and /boot are not on the same filesystem
	_BOOT_PATH="${PMON_BOOTDEV}"
fi


for _KERNEL in ${_KERNELS}
do
	# Strip kernel prefix to derive version.
	_VERSION=${_KERNEL#*-}
	echo "P: Writing config for ${_KERNEL}..."

	_ENTRY="${_ENTRY:-1}"

	if [ -e /boot/initrd.img-${_VERSION} ]
	then
		_INITRD="initrd ${_BOOT_PATH}/initrd.img-${_VERSION}"
	else
		_INITRD=""
	fi

	if echo ${PMON_ALTERNATIVES} | grep -q default
	then

	# Writing default entry
	_CONFIG="${_CONFIG}

title ${PMON_MENU_LABEL} ${_VERSION}
	kernel ${_BOOT_PATH}/${_KERNEL}
	${_INITRD}
	args ${PMON_ROOT} ${PMON_PARAMETERS}"

	fi

	if echo ${PMON_ALTERNATIVES} | grep -q recovery
	then

	# Writing recovery entry
	_CONFIG="${_CONFIG}

title ${PMON_MENU_LABEL} ${_VERSION} (Rescue Target)
	kernel ${_BOOT_PATH}/${_KERNEL}
	${_INITRD}
	args ${PMON_ROOT} $(echo ${PMON_PARAMETERS} | sed -e 's| quiet||') single
	"

	fi

	if [ "${PMON_ENTRIES}" = "${_ENTRY}" ]
	then
		break
	fi
done

# Append extra custom config
if [ -e /boot/pmon/custom.cfg ]
then
	_CONFIG="${_CONFIG}

$(cat /boot/pmon/custom.cfg)
"
fi

Update "${_PMON_DIRECTORY}/boot.cfg" "${_CONFIG}"

