# Copyright 1999-2022 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

EAPI=8

DESCRIPTION="Build secure boot EFI kernel with LUKS, LVM and plymouth"
BASE_SERVER_URI="https://github.com/sakaki-"
HOMEPAGE="${BASE_SERVER_URI}/${PN}"
SRC_URI="${BASE_SERVER_URI}/${PN}/releases/download/${PV}/${P}.tar.gz"

LICENSE="GPL-3+"
SLOT="0"
KEYWORDS="~amd64"
IUSE="+plymouth"

RESTRICT="mirror"

DEPEND=">=sys-apps/gptfdisk-0.8.8
	>=sys-fs/cryptsetup-1.6.2
	>=app-shells/bash-4.2:*"
RDEPEND="${DEPEND}
	>=sys-libs/ncurses-5.9-r2
	>=virtual/linux-sources-3
	>=app-crypt/sbsigntools-0.6-r1
	plymouth? ( >=sys-boot/plymouth-0.8.8-r4[gdm(+),pango] )
	>=sys-kernel/genkernel-next-58[cryptsetup,gpg,plymouth?]
	=app-crypt/staticgpg-1.4.16-r1
	>=sys-boot/efibootmgr-0.5.4-r1
	>=sys-apps/debianutils-4.9.1[installkernel(+)]"

# ebuild function overrides
src_prepare() {
	# if the plymouth use flag not set, set script variable accordingly
	if ! use plymouth; then
		elog "plymouth USE flag not selected - patching script accordingly."
		sed -i -e 's@USE_PLYMOUTH=true@USE_PLYMOUTH=false@g' "${S}/${PN}" || \
			die "Failed to patch script to reflect omitted plymouth USE flag."
	fi
	eapply_user
}
src_install() {
	dosbin "${PN}"
	insinto "/etc"
	doins "${PN}.conf"
	doman "${PN}.8"
	doman "${PN}.conf.5"
}
pkg_preinst() {
	if [ -e "${ROOT}/etc/${PN}.conf" ]; then
		# don't overwrite buildkernel.conf, user already has one,
		# and we don't want to force an unneeded dispatch-conf
		elog "/etc/${PN}.conf already exists, not overwriting."
		# install an identical copy; this is safer than deleting from
		# the pre-install image
		cp "${ROOT}/etc/${PN}.conf" "${D}/etc/${PN}.conf"
	else
		# attempt to set the LUKS and EFI partition ids automatically
		# in the config file (in ${D}) if this can be done unambiguously
		set_luks_partuuid_if_exactly_one_found
		set_efi_partuuid_if_exactly_one_found_on_usb
	fi
}
pkg_postinst() {
	elog "Be sure to check the CRYPTPARTUUID and EFIPARTUUID variables are"
	elog "set correctly in /etc/buildkernel.conf, and also ensure that you have an"
	elog "appropriate value for KEYMAP in this file, before running buildkernel."
}

# helper functions
set_luks_partuuid_if_exactly_one_found() {
	# checks all partitions for LUKS and, if exactly one is found, will set that
	# for the CRYPTPARTUUID in buildkernel.conf
	# if no candidate or multiple candidates found, print a warning
	local BKCONFPATH="$(sed 's#//*#/#g' <<< "${D}/etc/buildkernel.conf")"
	local REALBKCONFPATH="$(sed 's#//*#/#g' <<< "${ROOT}/etc/buildkernel.conf")"
	local PARTUUIDDEVDIR="$(sed 's#//*#/#g' <<< "${ROOT}/dev/disk/by-partuuid")"
	if ! grep -q '^[[:space:]]*#[[:space:]]*CRYPTPARTUUID=' "${BKCONFPATH}"; then
		elog "CRYPTPARTUUID is already set in ${BKCONFPATH}, leaving as is"
		return
	fi
	elog "Attempting to find singleton LUKS partition for CRYPTPARTUUID..."
	local NEXTPART CANDIDATE=""
	shopt -s nullglob
	for NEXTPART in "${PARTUUIDDEVDIR}"/*; do
		if [ -e "${NEXTPART}" ]; then
			if cryptsetup isLuks "${NEXTPART}"; then
				if [ -z "${CANDIDATE}" ]; then
					CANDIDATE="${NEXTPART,,}"
				else
					ewarn " Multiple candidates for LUKS partition found!"
					ewarn " Please set CRYPTPARTUUID manually in ${REALBKCONFPATH}"
					shopt -u nullglob
					return
				fi
			fi
		fi
	done
	shopt -u nullglob
	if [ ! -z "${CANDIDATE}" ]; then
		elog " Found exactly one candidate: $(basename "${CANDIDATE}")"
		elog " (which is $(readlink --canonicalize "${CANDIDATE}"))"
		elog " Setting this for CRYPTPARTUUID in ${REALBKCONFPATH}"
		sed -i "s@^[[:space:]]*#[[:space:]]*CRYPTPARTUUID=.*@CRYPTPARTUUID=\"$(basename "${CANDIDATE}")\"@" "${BKCONFPATH}"
	else
		ewarn " No LUKS partition candidates found!"
		ewarn " Please set CRYPTPARTUUID manually in ${REALBKCONFPATH}"
	fi
}
set_efi_partuuid_if_exactly_one_found_on_usb() {
	# checks all partitions on USB devices only; if exactly one EFI system
	# partition is found, will set that for EFIPARTUUID in buildkernel.conf
	local BKCONFPATH="$(sed 's#//*#/#g' <<< "${D}/etc/buildkernel.conf")"
	local REALBKCONFPATH="$(sed 's#//*#/#g' <<< "${ROOT}/etc/buildkernel.conf")"
	local PARTUUIDDEVDIR="$(sed 's#//*#/#g' <<< "${ROOT}/dev/disk/by-partuuid")"
	local DISKIDDEVDIR="$(sed 's#//*#/#g' <<< "${ROOT}/dev/disk/by-id")"
	if ! grep -q '^[[:space:]]*#[[:space:]]*EFIPARTUUID=' "${BKCONFPATH}"; then
		ewarn "EFIPARTUUID is already set in ${BKCONFPATH}, leaving as is"
		return
	fi
	elog "Attempting to find singleton USB EFI system partition for EFIPARTUUID..."
	local NEXTPART CANDIDATE=""
	declare -A ISUSBPART
	# lookup all USB partitions, and store
	local NEXTID
	for NEXTID in "${DISKIDDEVDIR}/usb-"*"-part"*[[:digit:]]; do
		if [ -e "${NEXTID}" ]; then
			# remember this
			local NEXTCANONPART="$(readlink --canonicalize "${NEXTID}")" # e.g. /dev/sda3
			ISUSBPART["${NEXTCANONPART}"]="1"
		fi
	done
	shopt -s nullglob
	for NEXTPART in "${PARTUUIDDEVDIR}"/*; do
		if [ -e "${NEXTPART}" ]; then
			local PARTNAME="$(readlink --canonicalize "${NEXTPART}")" # e.g. /dev/sda3
			if [[ "${ISUSBPART[${PARTNAME}]-0}" == "1" ]]; then
				local DEVNAME="${PARTNAME%%[[:digit:]]*}"			   # e.g. /dev/sda
				local PARTNUM="${PARTNAME##*[^[:digit:]]}"			  # e.g. 3
				# stat returns device type in hex only
				if (sgdisk --info "${PARTNUM}" "${DEVNAME}" | grep -qi 'EFI System'); then
					if [ -z "${CANDIDATE}" ]; then
						CANDIDATE="${NEXTPART,,}"
					else
						ewarn " Multiple candidates for EFI system partition found!"
						ewarn " Please set EFIPARTUUID manually in ${REALBKCONFPATH}"
						shopt -u nullglob
						return
					fi
				fi
			fi
		fi
	done
	shopt -u nullglob
	if [ ! -z "${CANDIDATE}" ]; then
		elog " Found exactly one candidate: $(basename "${CANDIDATE}")"
		elog " (which is $(readlink --canonicalize "${CANDIDATE}"))"
		elog " Setting this for EFIPARTUUID in ${REALBKCONFPATH}"
		sed -i "s@^[[:space:]]*#[[:space:]]*EFIPARTUUID=.*@EFIPARTUUID=\"$(basename "${CANDIDATE}")\"@" "${BKCONFPATH}"
	else
		ewarn " No USB EFI system partition candidates found!"
		ewarn " Please set EFIPARTUUID manually in ${REALBKCONFPATH}"
	fi
}