#!/bin/sh -eu

. shell-error
. shell-getopt
. shell-var

BOOTDEVICE=
ROOTDIR=
ADD_KERNELS='yes'
KERNELS_GROUP='Details Menu'
TEST=

show_help() {
	cat <<-EOF
	Usage: $PROG [options] [extlinux options]

	[Options]
	  -b, --boot=DEVICE  specify the boot device;
	  -r, --root=DIR     use the system rooted at DIR;
	  -d, --dry-run      show what will be done and exit;
	  -v, --verbose      print a message for each action;
	  -V, --version      print program version and exit;
	  -h, --help         show this text and exit.

	Report bugs to authors.

	EOF
	exit
}

print_version() {
	cat <<-EOF
	$PROG version 2.0
	Written by Alexey Gladkov.

	Copyright (C) 2010-2012  Alexey Gladkov <gladkov.alexey@gmail.com>
	This is free software; see the source for copying conditions.  There is NO
	warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	EOF
	exit
}

GETOPT_ALLOW_UNKNOWN=1
TEMP=`getopt -n $PROG -o b:,d,r:,v,V,h -l boot:,dry-run,root:,verbose,version,help -- "$@"` ||
	show_help
eval set -- "$TEMP"

while :; do
	case "$1" in
		-b|--boot) shift
			BOOTDEVICE="$1"
			;;
		-r|--root) shift
			ROOTDIR="${1%/}"
			;;
		-d|--dry-run)
			TEST=message
			verbose=-v
			;;
		-v|--verbose) verbose=-v
			;;
		-V|--version) show_version
			;;
		-h|--help) show_help
			;;
		--) shift; break
			;;
	esac
	shift
done

. "$ROOTDIR"/etc/sysconfig/extlinux

boot_majmin="$(mountpoint -d "$ROOTDIR/boot")" ||:
root_majmin="$(mountpoint -d "$ROOTDIR/")" ||:

[ "$boot_majmin" = "$root_majmin" -o -L "$ROOTDIR/boot/boot" ] ||
	fatal "$ROOTDIR/boot a separate partition, but there's no '/boot/boot' symlink"

sysfs_path="$(readlink -ev "/sys/dev/block/$boot_majmin" 2>/dev/null)" ||
	fatal "Unable to get sysfs path"

[ -f "$sysfs_path/partition" ] ||
	fatal "Boot device must be partition"

read partition < "$sysfs_path/partition"

dev="$(sed -n -e 's,^DEVNAME=,/dev/,p' "${sysfs_path%/*}/uevent")"
partition_table="$(LANG=C parted -ms "$dev" print | grep ^/ |cut -d: -f6)"

verbose "Partition table: $partition_table"

case "$partition_table" in
	gpt)
		mbr_bin='gptmbr.bin'
		flag='legacy_boot'
		;;
	msdos)
		mbr_bin='mbr.bin'
		flag='boot'
		;;
	*)	fatal "Unknown partition table: $partition_table" ;;
esac

bootflags="$(LANG=C parted -ms "$dev" print | grep "^$partition:" |cut -d: -f7)"

[ -z "${bootflags##*$flag*}" ] ||
	fatal "partiton not bootable"

DEVNAME=
dir="$(readlink -m "$ROOTDIR/")"
while read dev mpoint dummy; do
	[ "$mpoint" = "$dir" -a "$dev" != 'rootfs' ] ||
		continue
	DEVNAME="$dev"
	break
done < /proc/mounts

[ -n "$DEVNAME" ] ||
	fatal "Unable to get device info '$ROOTDIR/'"

UUID="$(blkid -o value -s UUID -c /dev/null "$DEVNAME")"

cd "$ROOTDIR/boot/extlinux"

if [ -z "$TEST" ]; then
	verbose "Replace 'root=@ROOTDEV@' by 'root=UUID=$UUID'"

	for f in extlinux.conf extlinux.conf.d/*.conf; do
		[ ! -f "$f" ] ||
			sed -i -e "s#@ROOTDEV@#UUID=$UUID#ig" "$f"
	done
fi

rm $verbose -f -- extlinux.conf.d/kernels.conf
if shell_var_is_yes "$ADD_KERNELS"; then
	verbose "Creating new extlinux.conf.d/kernels.conf ..."

	printf '# Generated by %s. Do not edit manually.\n' "$PROG" \
		> extlinux.conf.d/kernels.conf

	if [ -n "$KERNELS_GROUP" ]; then
		cat >> extlinux.conf.d/kernels.conf <<-EOF
		MENU BEGIN
		MENU TITLE $KERNELS_GROUP
		EOF
	fi

	for f in "$ROOTDIR"/boot/vmlinuz*; do
		[ -f "$f" ] || continue

		vmlinuz="${f##*/}"
		version="${vmlinuz#vmlinuz}"
		initrd="initrd$version.img"

		[ -f "$ROOTDIR/boot/$initrd" ] ||
			initrd=

		[ -n "$version" ] &&
			label="Kernel ${version:+(${version#-})}" ||
			label='Kernel image'

		cat <<-EOF
		LABEL ${vmlinuz##*/}
		MENU LABEL $label
		LINUX  /boot/$vmlinuz${initrd:+
		INITRD /boot/$initrd}${APPEND:+
		APPEND $APPEND}
		EOF
	done >> extlinux.conf.d/kernels.conf

	if [ -n "$KERNELS_GROUP" ]; then
		cat >> extlinux.conf.d/kernels.conf <<-EOF
		MENU SEPARATOR
		LABEL return_main
		MENU LABEL ^Return to main Menu
		MENU EXIT
		MENU END
		EOF
	fi
fi

verbose "Creating new extlinux.conf.d/.index.conf ..."
printf '# Generated by %s. Do not edit manually.\n' "$PROG" \
	> extlinux.conf.d/.index.conf

def_label=
for f in extlinux.conf.d/*.conf; do
	[ -f "$f" ] || continue
	if [ -z "$def_label" ]; then
		while read name value; do
			case "$name" in
				[Ll][Aa][Bb][Ee][Ll])
					def_label="$value"
					break
					;;
			esac
		done < "$f"
		printf 'DEFAULT %s\n' "$def_label"
	fi
	printf 'INCLUDE %s\n' "$f"
done >> extlinux.conf.d/.index.conf

mode='install'
if [ -b "$BOOTDEVICE" ]; then
	ldr="$(sha1sum < "$mbr_bin")"
	siz="$(stat -c '%s' "$mbr_bin")"
	mbr="$(dd ibs="$siz" count=1 if="$BOOTDEVICE" 2>/dev/null |sha1sum)"

	[ "$mbr" != "$ldr" ] || mode='update'
	verbose "Using $mode mode"
fi

if [ "$mode" = 'install' ]; then
	if [ -b "$BOOTDEVICE" ]; then
		verbose "installing master boot record: $BOOTDEVICE"
		$TEST dd if="$mbr_bin"  of="$BOOTDEVICE"
	else
		message "WARNING: MBR not installed. BOOTDEVICE not specified."
	fi
fi

$TEST extlinux --$mode "$PWD" "$@"
