#!/bin/sh

alterator_api_version=1

. alterator-sh-functions
. shell-config

# chroot /mnt/destination ?
if mountpoint -q /mnt/destination/dev; then
export DESTDIR=/mnt/destination
if_exec_chroot()
{
	env -i PATH="$PATH" HOME="/root" TMPDIR="/tmp" LANG="$LANG" \
		chroot "$DESTDIR" "$@"
}
else
export DESTDIR=
if_exec_chroot()
{
	"$@"
}
fi

. grub-raid-boot
. grub-disk
. grub-bootable
. grub-md-list

locale_conf="$DESTDIR"/etc/locale.conf
legacy_locale_conf="$DESTDIR"/etc/sysconfig/i18n

# effects grub menu l18n
if [ -n "$in_language" ]; then
	LANG="$in_language"
else
	if [ -s "$locale_conf" ]; then
		. "$locale_conf"
	elif [ -s "$legacy_locale_conf" ]; then
		. "$legacy_locale_conf"
	fi
fi

export LANG

arch="$(uname -m)"

efi_dir="$DESTDIR"/boot/efi

pwconf="$DESTDIR"/etc/grub.d/50_password

errflag=
gruboutput=

passwd_hash=

make_gruboutput() {
	gruboutput="$(mktemp --tmpdir grub.XXXXXXXXXX 2>&1 ||:)"
	[ -n "$gruboutput" -a -f "$gruboutput" ] || {
		local msg="$(_ "Can't create tempfile ")"
		write_error "$msg$gruboutput!"
		return 1
	}
}

make_grubcfg() {
	if ! if_exec_chroot grub-mkconfig -o /boot/grub/grub.cfg > "$gruboutput" 2>&1; then
		local msg="$(_ "Can't create grub menu")"
		write_error "$msg: $(cat "$gruboutput")"
		return 1
	fi
	[ -f "$DESTDIR"/_NEW_SYSTEM_ ] && cp -ab "$gruboutput" /tmp/grub.log ||:
}

backup_and_clear_nvram() {
	local mngr="$(command -v efibootmgr 2>/dev/null ||:)"
	[ -x "$mngr" ] || return 1

	local backup="/root/.install-log"
	if [ -d "/mnt/destination/$backup" ]; then
		backup="/mnt/destination/$backup"
	elif [ ! -d "$backup" ]; then
		return 1
	fi
	backup="$backup/NVRAM-$(date "+%Y%m%d").bak"
	[ -f "$backup" ] || (
		echo "# Boot variables:"
		"$mngr" -v ||:
		echo; echo "# Driver variables:"
		"$mngr" -v -r ||:
		echo; echo "# SysPrep variables:"
		"$mngr" -v -y ||:
	) > "$backup" 2>&1

	local bootnum=
	( "$mngr" |grep -E "^Boot[0-9A-F][0-9A-F][0-9A-F][0-9A-F]" |
	  while read bootnum backup; do
		"$mngr" -q -B -b "${bootnum:4:4}" ||:
	  done
	) >/dev/null 2>&1
}

grub_uefi() {
	if if_exec_chroot sh -c 'command -v grub-efi-install >/dev/null 2>&1'; then
		if_exec_chroot grub-efi-install --verbose --first-install "$@" \
			>> "$gruboutput" 2>&1 || errflag=1
	else
		if_exec_chroot grub-install --uefi-secure-boot --recheck "$@" \
			>> "$gruboutput" 2>&1 || errflag=1
	fi
}

turn_luks_crypto_on() {
	# when a root partition is chosen to be encrypted with LUKS
	# GRUB_ENABLE_CRYPTODISK should be enabled explicitly
	local crypto_luks_present=
	for slink in $(ls /dev/mapper/*_luks); do
		[ -L $slink ] && crypto_luks_present="y"
	done
	if [ "$crypto_luks_present" = "y" ]; then
		if ! grep "^GRUB_ENABLE_CRYPTODISK=" "$DESTDIR"/etc/sysconfig/grub2 > /dev/null 2>&1; then
			echo "GRUB_ENABLE_CRYPTODISK=y" >> "$DESTDIR"/etc/sysconfig/grub2
		else
			sed -i "s|GRUB_ENABLE_CRYPTODISK=.*|GRUB_ENABLE_CRYPTODISK=y|" "$DESTDIR"/etc/sysconfig/grub2
		fi
	fi
}

write_efi() {
	make_gruboutput || return 1

	turn_luks_crypto_on

	errflag=0
	case "$1" in
	eficlearnvram)
		backup_and_clear_nvram || {
			write_error "$(_ "Can't backup or/and clear NVRAM!")"
			return 1
		}
		grub_uefi --force-extra-removable
		;;
	efinonvram)
		rm -fr "$efi_dir"/EFI/BOOT
		grub_uefi --force-extra-removable --no-nvram
		;;
	efiremovable)
		rm -fr "$efi_dir"/EFI/BOOT
		grub_uefi --removable
		;;
	efi)
		grub_uefi --force-extra-removable
		;;
	esac
	[ $errflag -eq 0 ] || {
		local msg="$(_ "Can't install grub into")"
		write_error "$msg $efi_dir: $(cat "$gruboutput")"
		return 1
	}

	make_grubcfg
}

change_passwd() {
	case "$in_passwd" in
	"#f")	del_passwd;;
	"#t")	set_passwd;;
	*)	write_error "`_ "Internal error"`";;
	esac
}

hash_passwd() {
	echo -e "$in_passwd_1\n$in_passwd_1" \
	| LANG=C grub-mkpasswd-pbkdf2 \
	| sed -rn 's,^.* (grub\.pbkdf2.*)$,\1,p'
}

set_passwd() {
	case "$in_iscrypted" in
	"#t")
		passwd_hash="$in_passwd_1"
		set_passwd_config
		return
		;;
	*);;
	esac

	if [ -z "$in_passwd_1" -a -z "$in_passwd_2" ]; then
		write_error "`_ "You should define a password for boot with options"`"
	elif [ "$in_passwd_1" != "$in_passwd_2" ]; then
		write_error "`_ "Passwords mismatch"`"
	elif echo "$in_passwd_1" | grep -qP "[^\x00-\x7F]"; then
		write_error "`_ "Password contain non-ASCII characters"`"
	else
		passwd_hash="`hash_passwd`"
		set_passwd_config
	fi
}

set_passwd_config() {
	install -pm700 /dev/null $pwconf
	cat > $pwconf <<-EOF_CONF
	#!/bin/sh
	cat << EOF
	set superusers="boot"
	password_pbkdf2 boot "$passwd_hash"
	EOF
	EOF_CONF
	grub-mkconfig -o /boot/grub/grub.cfg
}

del_passwd() {
	shred -u $pwconf
}

read_passwd_status() {
	[ -s $pwconf ] &&
		write_bool_param 'passwd' 'yes' ||
		write_bool_param 'passwd' 'no'
}

list_devices() {
	if [ -d "$efi_dir" ] && [ -d "$DESTDIR/sys/firmware/efi" ]; then
		[ ! -f "$DESTDIR"/etc/efi_removable.flag ] ||
			write_enum_item "efiremovable" \
						"$(_ 'EFI (for removable device)')"
		write_enum_item "efi"           "$(_ 'EFI (recommended)')"
		write_enum_item "eficlearnvram" "$(_ 'EFI (clear NVRAM before)')"
		write_enum_item "efinonvram"    "$(_ 'EFI (disable write to NVRAM)')"
		[ -f "$DESTDIR"/etc/efi_removable.flag ]   ||
			write_enum_item "efiremovable" \
						"$(_ 'EFI (for removable device)')"
	else
		case "$arch" in
			ppc*)
				list_bootloader_places_ppc
				;;
			i?86|pentium?|k6|athlon*|x86_64)
				list_bootloader_places
		esac
	fi
	write_enum_item "none" "$(_ "Skip bootloader install")"
}

write_device() {
	make_gruboutput || return 1

	# fallback: BIOS bootloader installation when booted in EFI mode
	if [ -d "/sys/firmware/efi" ]; then
		grep -q efivars /etc/modules || echo efivars >> /etc/modules
		target="--target=i386-pc"
	fi

	bootdev="$(readlink -e $1)"
	if [ -z "$bootdev" ]; then
		write_error "$(_ "No device for install given!")"
		return 1
	fi
	raid_members=$(grub_md_list "${bootdev#/dev/}" 2>/dev/null)
	[ -z "$raid_members" ] || bootdev=$raid_members
	#tout=`mktemp`
	GRUB_FORCE="no"
	GRUB_DEV=
	for dev in $bootdev; do
		GRUB_DEV="$(blockdev_get_symlink "$dev") $GRUB_DEV"
		case "$arch" in
			ppc*)
				# grub-install won't work with unclean
				# PReP partition.
				dd if=/dev/zero of="$dev" ||:
				;;
		esac

		turn_luks_crypto_on

		if_exec_chroot grub-install $target "$dev" >> "$gruboutput" 2>&1
		if [ $? -ne 0 ]; then
			local msg="$(_ "Can't install grub on ")"
			if grep blocklists "$gruboutput"; then
				GRUB_FORCE="yes"
				if_exec_chroot grub-install $target "$dev" --force >> "$gruboutput" 2>&1
				if [ $? -ne 0 ]; then
					local msg2="$(_ " even using blocklists :")"
					write_error "$msg$dev$msg2$(cat "$gruboutput")"
					return 1
				fi
			else
				write_error "$msg$dev :$(cat "$gruboutput")"
				return 1
			fi
		fi
		case "$arch" in
			ppc*)
				# Activate PReP partition.
				# set_bootable_flag() is useless and broken.
				local disk="$(partition_parent ${dev#/dev/})"
				local pnum="$(partition_num ${dev#/dev/})"
				if [ -n "$disk" ] && [ -n "$pnum" ]; then
					sfdisk -A /dev/"$disk" "$pnum"
				fi
				;;
			*)
				set_bootable_flag "$dev"
				;;
		esac
	done

	# Uncomment existing variables if any (see ALT#56951)
	sed -i \
		-e '/^#GRUB_AUTOUPDATE_DEVICE=/ s/^#//' \
		-e '/^#GRUB_AUTOUPDATE_FORCE=/ s/^#//' \
		"$DESTDIR/etc/sysconfig/grub2"

	if ! grep "^GRUB_AUTOUPDATE_DEVICE=" "$DESTDIR"/etc/sysconfig/grub2 > /dev/null 2>&1; then
		echo "GRUB_AUTOUPDATE_DEVICE='$GRUB_DEV'" >> "$DESTDIR"/etc/sysconfig/grub2
	else
		sed -i "s|GRUB_AUTOUPDATE_DEVICE=.*|GRUB_AUTOUPDATE_DEVICE='$GRUB_DEV'|" "$DESTDIR"/etc/sysconfig/grub2
	fi
	if ! grep "^GRUB_AUTOUPDATE_FORCE=" "$DESTDIR"/etc/sysconfig/grub2 > /dev/null 2>&1; then
		echo "GRUB_AUTOUPDATE_FORCE='$GRUB_FORCE'" >> "$DESTDIR"/etc/sysconfig/grub2
	else
		sed -i "s|GRUB_AUTOUPDATE_FORCE=.*|GRUB_AUTOUPDATE_FORCE='$GRUB_FORCE'|" "$DESTDIR"/etc/sysconfig/grub2
	fi

	make_grubcfg
}

on_message() {
	case "$in_action" in
	list)
		case "$in__objects" in
		"devices") list_devices;;
		esac
		;;
	read)
		case "$in__objects" in
		"passwd")  read_passwd_status;;
		"devices") list_devices;;
		esac
		;;
	write)
		change_passwd
		case "$in_device" in
		"none")
			;;
		"efi"|"eficlearnvram"|"efinonvram"|"efiremovable")
			write_efi "$in_device"
			;;
		"configonly")
			make_gruboutput || return 1
			make_grubcfg
			;;
		*)
			write_device "$in_device" "$in_runhooks"
			;;
		esac
		;;
	esac
}

message_loop
