#!/usr/bin/bash -eu

readonly arg0=${0##*/}

verbose=1

dbg() { if (( verbose )); then say "$*"; fi; }
say() { echo "$arg0: $*"; }
err() { echo "$arg0: $*" >&2; return 1; }
die() { echo "$arg0: $*" >&2; exit 1; }

getkey() {
	local path=$1 key=$2
	local k v
	while read -r k v; do
		if [[ $k == "$key" ]]; then
			echo "$v"
			return
		fi
	done < "$path"
	return 1
}

get_kversion() {
	case $(uname -m) in
	x86_64)
		perl -w -e '
		my $kernel = $ARGV[0];
		open(my $fh, "<", $kernel);
		seek($fh, 0x200 + 0x0e, 0);
		read($fh, my $buf1, 2);
		my ($pos) = unpack("S", $buf1);
		seek($fh, 0x200 + $pos, 0);
		read($fh, my $buf2, 128);
		close($fh);
		$buf2 =~ s/[\0\n ].*//sm;
		print "$buf2\n";
		' "$kernel";;
	*)
		err "cannot guess kernel version on this architecture";;
	esac
}

install_kernel() {
	local kernel=$1
	local kname=${kernel#/boot/vmlinuz-}
	local title=$PRETTY_NAME
	local version=$(get_kversion "$kernel")
	local entryid=
	if [[ $kname == linux?(-*) ]]; then
		entryid=${kname/#linux/"$ID"}
	else
		title="$kernel"
		entryid=$kname
	fi
	local mucode=/boot/intel-ucode.img
	local initrd=/boot/initramfs-$kname.img
	local config=$ESP/loader/entries/$entryid.conf

	say "installing kernel $kernel ($version) as \"$title\" ($entryid)"
	parameters=(
		"title"		"$title"
		"version"	"$version"
		"machine-id"	"$MACHINE_ID"
		"linux"		"${kernel#/boot/}"
		"initrd"	"${mucode#/boot/}"
		"initrd"	"${initrd#/boot/}"
		"options"	"$BOOT_OPTIONS"
	)

	if ! [[ $ESP/. -ef /boot/. ]]; then
		dbg "copying $kernel to EFI system partition"
		mkdir -p "$ESP/EFI/$ID"
		cp -uf "$kernel"	"$ESP/EFI/$ID/vmlinuz-$kname.efi"
		cp -uf "$mucode"	"$ESP/EFI/$ID/intel-ucode.img"
		cp -uf "$initrd"	"$ESP/EFI/$ID/initramfs-$kname.img"
		sync -f "$ESP"
		parameters=(
			"title"		"$title"
			"version"	"$version"
			"machine-id"	"$MACHINE_ID"
			"linux"		"/EFI/$ID/vmlinuz-$kname.efi"
			"initrd"	"/EFI/$ID/intel-ucode.img"
			"initrd"	"/EFI/$ID/initramfs-$kname.img"
			"options"	"$BOOT_OPTIONS"
		)
	fi

	dbg "generating entry '$entryid'"
	mkdir -p "$ESP/loader/entries"
	printf '%-15s %s\n' "${parameters[@]}" > "$ESP/loader/entries/$entryid.conf"
	sync -f "$ESP"
}

remove_kernel() {
	local kernel=$1
	local kname=${kernel#/boot/vmlinuz-}
	local entryid=
	if [[ $kname == linux?(-*) ]]; then
		entryid=${kname/#"linux"/"$ID"}
	else
		entryid=$kname
	fi
	local initrd=/boot/initramfs-$kname.img
	local config=$ESP/loader/entries/$entryid.conf

	say "removing kernel '$kernel'"
	rm -f "$ESP/EFI/$ID/vmlinuz-$kname.efi"
	rm -f "$ESP/EFI/$ID/initramfs-$kname.img"
	rm -f "$ESP/loader/entries/$entryid.conf"
}

check_kernel() {
	local kernel=$1

	if [[ $kernel != /boot/vmlinuz-?* ]]; then
		return 1
	fi

	if [[ -s $kernel ]]; then
		install_kernel "$kernel"
	else
		remove_kernel "$kernel"
	fi
}

check_config() {
	local path=$1
	local config=${path##**/}; config=${config%.conf}

	local machine=$(getkey "$path" machine-id)
	if [[ $machine != "$MACHINE_ID" ]]; then
		dbg "keeping entry '$config' (foreign machine-id)"
		return
	fi

	local kernel=$(getkey "$path" linux | tr '\\' '/')
	if [[ ! $kernel ]]; then
		dbg "keeping entry '$config' (not a kernel entry)"
		return
	fi
	if [[ $kernel != ?(/)vmlinuz-* ]]; then
		dbg "keeping entry '$config' (unusual kernel path)"
		return
	fi
	if [[ -s $ESP/$kernel ]]; then
		dbg "keeping entry '$config' (kernel exists)"
		return
	fi

	dbg "removing old entry '$config'"
	kernel=${kernel##*/}
	kernel=${kernel#vmlinuz-}
	remove_kernel "$kernel" "" "$config"
}

declare ESP= os_release=
unset ID NAME PRETTY_NAME MACHINE_ID BOOT_OPTIONS

for path in /efi /boot/efi /boot; do
	if
		mountpoint -q "$path" &&
		[[ ! -L "$path" ]] &&
		[[ -d "$path/EFI" ]] &&
		[[ -d "$path/loader" ]]
	then
		dbg "found EFI System Partition at '$path'"
		ESP=$path
		break
	fi
done

[[ $ESP ]] ||
	die "EFI system partition not found; please \`mkdir <efisys>/loader\`"

for f in /etc/os-release /usr/lib/os-release; do
	[[ $os_release ]] || { [[ -e $f ]] && os_release=$f; }
done

[[ $os_release ]] ||
	die "/usr/lib/os-release not found or invalid; see os-release(5)"

. "$os_release" ||
	die "$os_release not found or invalid; see os-release(5)"

[[ ${PRETTY_NAME:=$NAME} ]] ||
	die "$os_release is missing both PRETTY_NAME and NAME; see os-release(5)"

[[ $ID ]] ||
	die "$os_release is missing ID; see os-release(5)"

read -r MACHINE_ID < /etc/machine-id ||
	die "/etc/machine-id not found or empty; see machine-id(5)"

[[ -s /etc/kernel/cmdline ]] ||
	die "/etc/kernel/cmdline not found or empty; please configure it"

BOOT_OPTIONS=(`grep -v "^#" /etc/kernel/cmdline`)
BOOT_OPTIONS=${BOOT_OPTIONS[*]}

exec {lock_fd}> "/run/$arg0.lock"
flock -x -w 60 $lock_fd ||
	die "failed to take lock; is another $arg0 instance running?"

if [[ ${1-} ]]; then
	check_kernel "$1"
else
	for path in "$ESP/loader/entries"/*.conf; do
		dbg "found entry '$path'"
		check_config "$path"
	done

	for path in /boot/vmlinuz-*; do
		dbg "found kernel '$path'"
		check_kernel "$path"
	done
fi
