#! /bin/sh

# export DEBCONF_DEBUG=5
set -e
. /usr/share/debconf/confmodule
#set -x

if [ "$1" ]; then
	ROOT="$1"
	chroot=chroot
else
	ROOT=
	chroot=
fi

. /usr/share/grub-installer/functions.sh
. /usr/share/grub-installer/otheros.sh

newline="
"

db_capb backup

ARCH="$(archdetect)"
info "architecture: $ARCH"
SUBARCH="${ARCH#*/}"

# XXX cjwatson 2019-03-25: This is all far too complicated and fragile, and
# should be replaced with in-target or similar.

# Ensure proc is mounted in all the $chroot calls;
# needed for RAID+LVM for example
initial_proc_contents="$(ls $ROOT/proc)"
if [ -z "$initial_proc_contents" ]; then
	info "Mounting /proc into $ROOT"
	if [ "$(udpkg --print-os)" = "kfreebsd" ]; then
		mount -t linprocfs proc $ROOT/proc
		on_exit "umount '$ROOT/proc'"
	elif [ "$(udpkg --print-os)" = "hurd" ]; then
		mount -t proc none $ROOT/proc
		on_exit "umount '$ROOT/proc'"
	else
		mountvirtfs proc $ROOT/proc
	fi
fi

# On Linux, we need /run in the chroot to work around
# https://bugs.debian.org/918590.
if [ "$(udpkg --print-os)" = "linux" ] && [ ! -d "$ROOT/run/udev" ]; then
	info "Bind mounting /run into $ROOT"
	mount --bind /run $ROOT/run
	on_exit "umount '$ROOT/run'"
fi

get_serial_console() {
	# Get the last 'console=' entry (if none, the whole string is returned)
	local defconsole="$(sed -e 's/.*\(console=[^ ]*\).*/\1/' /proc/cmdline)"
	if echo "$defconsole" | grep -qe 'console=\(ttyS\|com\)'; then
		echo "$defconsole"
	fi
}

grub_serial_console() {
	#$1=output of get_serial_console
	local serconsole=${1##console=ttyS}
	serconsole=${serconsole##console=com}
	local unit=${serconsole%%,*}
	local options=""
	if echo $serconsole | grep -q ","; then
		options=${serconsole##*,}
	fi
	local speed=$(echo "$options" | sed -e 's/^\([0-9]*\).*$/\1/')
	# Take optional 1st (parity) and 2nd (word) characters after speed
	options=${options##${speed}}
	local parity=$(echo $options | sed 's/^\(.\?\).*$/\1/')
	local word=$(echo $options | sed 's/^.\?\(.\?\).*$/\1/')
	if [ -z "$speed" ]; then
		speed="9600"
	fi
	case "$parity" in 
		n) parity="--parity=no" ;;
		e) parity="--parity=even" ;;
		o) parity="--parity=odd" ;;
		*) parity="" ;;
	esac
	if [ "$word" ]; then
		word="--word=$word"
	fi

	echo serial --unit=$unit --speed=$speed $word $parity --stop=1
}

serial="$(get_serial_console)"

grub_probe () {
	if [ "$is_grub_common_installed" != true ]; then
		apt-install grub-common
		is_grub_common_installed=true
	fi
	$chroot $ROOT grub-probe $@
}

# Usage: convert os_device
# Convert an OS device to the corresponding GRUB drive
convert () {
	tmp_drive="$(grub_probe -d -t drive "$1")" || exit $?
	echo "$tmp_drive"
}

# Convert a linux non-devfs disk device name into the hurd's syntax
hurd_convert () {
	dr_type=$(expr "$1" : '.*\([hs]d\)[a-h][0-9]*')
	dr_letter=$(expr "$1" : '.*d\([a-h]\)[0-9]*')
	dr_part=$(expr "$1" : '.*d[a-h]\([0-9]*\)')
	case "$dr_letter" in
	a) dr_num=0 ;;
	b) dr_num=1 ;;
	c) dr_num=2 ;;
	d) dr_num=3 ;;
	e) dr_num=4 ;;
	f) dr_num=5 ;;
	g) dr_num=6 ;;
	h) dr_num=7 ;;
	esac
	echo "$dr_type${dr_num}s$dr_part"
}

# This should probably be rewritten using udevadm or similar.
device_to_disk () {
	echo "$1" | \
		sed 's:\(/dev/\(cciss\|ida\|rs\)/c[0-9]d[0-9][0-9]*\|/dev/mmcblk[0-9]\|/dev/nvme[0-9][0-9]*n[0-9][0-9]*\|/dev/\(ad\|ada\|da\|vtbd\|xbd\)[0-9]\+\|/dev/[hs]d[0-9]\+\|/dev/[a-z]\+\).*:\1:'
}

# Run update-grub in $ROOT
update_grub () {
	local in_target
	if [ "$ROOT" = /target ]; then
		in_target='in-target'
	else
		in_target="log-output -t grub-installer $chroot $ROOT"
	fi
	if ! $in_target $update_grub; then
		error "Running '$update_grub' failed." 1>&2
		db_input critical grub-installer/update-grub-failed || [ $? -eq 30 ]
		db_go || true
		db_progress STOP
		exit 1
	fi
}

findfs () {
	if ! grub_probe -t device $1; then
		mount | grep "on $ROOT${1%/} " | tail -n1 | cut -d' ' -f1
	fi
}

findfstype () {
	case "$(udpkg --print-os)" in
	    hurd)
		fsysopts "$ROOT$1" | sed 's:^/hurd/\([^ ]*\)fs .*:\1:' ;;
	    *)
		mount | grep "on $ROOT${1%/} " | tail -n1 | cut -d' ' -f5 ;;
	esac
}

# by-id mapping copied from grub-pc.postinst.

cached_available_ids=
available_ids()
{
	local id path

	if [ "$cached_available_ids" ]; then
		echo "$cached_available_ids"
		return
	fi

	[ -d /dev/disk/by-id ] || return
	cached_available_ids="$(
		for path in /dev/disk/by-id/*; do
			[ -e "$path" ] || continue
			printf '%s %s\n' "$path" "$(readlink -f "$path")"
		done | sort -k2 -s -u | cut -d' ' -f1
	)"
	echo "$cached_available_ids"
}

# Returns non-zero and no output if no mapping can be found.
device_to_id()
{
	local id
	for id in $(available_ids); do
		if [ "$(readlink -f "$id")" = "$(readlink -f "$1")" ]; then
			echo "$id"
			return 0
		fi
	done
	# Fall back to the plain device name if there's no by-id link for it.
	if [ -e "$1" ]; then
		echo "$1"
		return 0
	fi
	return 1
}

devices_to_ids()
{
	local device id ids
	ids=
	for device; do
		id="$(device_to_id "$device" || true)"
		if [ "$id" ]; then
			ids="${ids:+$ids, }$id"
		fi
	done
	echo "$ids"
}

rootfs=$(findfs /)
bootfs=$(findfs /boot)
[ -n "$bootfs" ] || bootfs="$rootfs"


bootfstype=$(findfstype /boot)
rootfstype="$(findfstype /)"
[ -n "$bootfstype" ] || bootfstype=$rootfstype

case $ARCH in
    powerpc/chrp|powerpc/chrp_rs6k|powerpc/chrp_ibm|powerpc/cell)
    ;;
    ppc64/chrp|ppc64/chrp_rs6k|ppc64/chrp_ibm|ppc64/cell)
    ;;
    powerpc/*|ppc64/*)
      offs=$(findfs /boot/grub)
      [ -n "$offs" ] || error "GRUB requires that the OF partition is mounted in /boot/grub" 1>&2
    ;;
esac

# This code to set disc_offered was taken from lilo-installer
rootfs_nodevfs=$(mapdevfs $rootfs)
bootfs_nodevfs=$(mapdevfs $bootfs)
prefix=$(device_to_disk "$bootfs")

case $prefix in
    /dev/md)
	disc_offered_devfs="$bootfs"
	;;
    /dev/mapper)
	disc_offered_devfs="$bootfs"
	;;
    /dev/[hsv]d[a-z0-9]|/dev/[hsv]d[a-z0-9][a-z]*|/dev/xvd[a-z]|/dev/cciss/c[0-9]d[0-9]*|/dev/ida/c[0-9]d[0-9]*|/dev/rs/c[0-9]d[0-9]*|/dev/mmcblk[0-9]|/dev/nvme[0-9]*n[0-9]*|/dev/ad[0-9]*|/dev/da[0-9]*)
	disc_offered_devfs="$prefix"
	;;
    *)
	disc_offered_devfs=$(echo "$bootfs_nodevfs" | sed "s:\(.*\)/.*:\1/disc:")
	;;
esac
disc_offered=$(mapdevfs "$disc_offered_devfs")

# Identify partition table of the disk containing our boot partition
bootfslabel=$(partmap $disc_offered || echo unknown)

info "Identified partition label for $bootfs: $bootfslabel"

experimental_arch () {
	db_subst grub-installer/grub_not_mature_on_this_platform ARCH $ARCH
	db_input low grub-installer/grub_not_mature_on_this_platform || [ $? -eq 30 ]
	db_go || exit 10
	db_get grub-installer/grub_not_mature_on_this_platform
	if [ "$RET" != true ]; then
		exit 10
	fi
}

case $ARCH in
    arm64/efi)
	grub_package="grub-efi-arm64"
	EFI_SUFFIX=AA64
	;;
    armhf/efi)
	grub_package="grub-efi-arm"
	EFI_SUFFIX=ARM
	;;
    i386/mac|amd64/mac)
	# Note: depends on partman-efi to load the efivars module!
	if [ -d /sys/firmware/efi ]; then
		# This point can't be reached (yet).  See debian/isinstallable.
		grub_package="grub-efi"
	else
		grub_package="grub-pc"
	fi
	;;
    i386/efi|amd64/efi)
	if [ -f /var/lib/partman/ignore_uefi ]; then
		# $SUBARCH != "efi" if partman ignore_uefi flag is set
		# so this branch should not be reached
		grub_package="grub-pc"
	else
		grub_package="grub-efi"
		# Override the package choice if we can figure out the
		# right package to use directly
		if [ -f /sys/firmware/efi/fw_platform_size ] ; then
			SIZE=$(cat /sys/firmware/efi/fw_platform_size)
			if [ $SIZE -eq 64 ] ; then
				grub_package="grub-efi-amd64"
				EFI_SUFFIX=X64
			elif [ $SIZE -eq 32 ] ; then
				grub_package="grub-efi-ia32"
				EFI_SUFFIX=IA32
			fi
		fi
	fi
	;;
    i386/*|amd64/*)
	# Check for a Xen hypervisor without the hvm_callback_vector feature
	# available to the guest.  HVM guests should still use grub-pc.
	if [ "$(cat /sys/hypervisor/type 2>/dev/null || true)" = xen ] && \
	   [ -e /sys/hypervisor/properties/features ] && \
	   [ "$((0x$(cat /sys/hypervisor/properties/features) & (1 << 8)))" = 0 ]; then
		grub_package="grub-xen"
	else
		grub_package="grub-pc"
	fi
	;;
    ia64/*)
	grub_package="grub-efi-ia64"
	EFI_SUFFIX=IA64
	;;
    loong64/*)
	grub_package="grub-efi-loong64"
	EFI_SUFFIX=LOONGARCH64
	;;
    powerpc/*|ppc64/*)
	grub_package="grub-ieee1275"
	;;
    ppc64el/*)
	grub_package="grub-ieee1275"
	;;
    riscv64/efi)
	grub_package="grub-efi-riscv64"
	EFI_SUFFIX=RISCV64
	;;
    sparc/*|sparc64/*)
	grub_package="grub-ieee1275"
	;;
    mipsel/loongson-2f)
	grub_package="grub-yeeloong"
	;;
    *)
	grub_package="grub-pc"
esac

if [ "$grub_package" = "grub-efi" ]; then
	case $ARCH in
	    i386/*)
		EFI_SUFFIX=IA32
		;;
	    amd64/*)
		EFI_SUFFIX=X64
	esac
fi

if [ -f /var/lib/partman/ignore_uefi ]; then
	# propagate partman ignore_uefi flag to the target system so that
	# os-prober performs legacy BIOS probes instead of EFI probes
	# when running update-grub in target chroot
	if [ ! -e $ROOT/var/lib/partman ]; then
		if mkdir -p $ROOT/var/lib/partman; then
			on_exit "rmdir '$ROOT/var/lib/partman'"
		else
			error "Failed to create directory $ROOT/var/lib/partman"
			exit 1
		fi
	elif [ ! -d $ROOT/var/lib/partman ]; then
		error "$ROOT/var/lib/partman exists but is not a directory"
		exit 1
	fi
	if [ ! -e $ROOT/var/lib/partman/ignore_uefi ]; then
		if touch $ROOT/var/lib/partman/ignore_uefi; then
			on_exit "rm '$ROOT/var/lib/partman/ignore_uefi'"
		else
			error "Failed to create file $ROOT/var/lib/partman/ignore_uefi"
			exit 1
		fi
	elif [ ! -f $ROOT/var/lib/partman/ignore_uefi ]; then
		error "$ROOT/var/lib/partman/ignore_uefi exists but is not a regular file"
		exit 1
	fi
fi

case $ARCH:$grub_package in
    powerpc/chrp*:grub-ieee1275|ppc64/chrp*:grub-ieee1275|ppc64el/*:grub-ieee1275)
	# By default, use the first PReP partition found (prep-bootdev).
	# If available, prefer a PReP partition in the same device (disc_offered).
	# (the matching works on devices with/without disk-partition separator.)
	wipe_bootdev="$(/usr/lib/grub-installer/prep-bootdev)"
	for prep_p in $(/usr/lib/grub-installer/prep-bootdev -l); do
		if [ "${prep_p%[0-9]*}" = "${disc_offered%[0-9]*}" ]; then
			wipe_bootdev=$prep_p
			break
		fi
	done
	unset prep_p

	if [ -n "$wipe_bootdev" ]; then
		info "Wiping PReP partition $wipe_bootdev"
		log-output -t grub-installer dd if=/dev/zero of="$wipe_bootdev" bs=512 count="$(blockdev --getsz "$wipe_bootdev")"
	else
		info "WARNING: PReP partition not found."
	fi
	;;
esac

menu_file="grub.cfg"
update_grub="update-grub"

user_params=$(user-params) || true
defopt_params=""
kopt_params=""
got_quiet=""
for u_param in $user_params; do
	case "$u_param" in
	    quiet)
		got_quiet=1
		defopt_params=${defopt_params:+$defopt_params }$u_param ;;
	    *)
		kopt_params=${kopt_params:+$kopt_params }$u_param ;;
	esac
done
# Add user parameters to the menu file; some options are only added
# to the default entry for a kernel instead of all entries.
# We have to duplicate grub2's default for
# GRUB_CMDLINE_LINUX_DEFAULT here.
if [ -z "$got_quiet" ]; then
	defopt_params="quiet${defopt_params:+ $defopt_params}"
fi
# (quoting to deconfuse vim)
$chroot $ROOT 'debconf-set-selections' <<EOF
$grub_package grub2/linux_cmdline string $kopt_params
$grub_package grub2/linux_cmdline_default string $defopt_params
$grub_package grub2/gnumach_cmdline string $kopt_params
EOF
if [ "$grub_package" = grub-pc ]; then
	# Empty this for now to stop it being asked. We'll fix this up later.
	$chroot $ROOT 'debconf-set-selections' <<EOF
$grub_package grub-pc/install_devices multiselect
$grub_package grub-pc/install_devices_empty boolean true
EOF
fi

case $grub_package in
    grub-efi*)
	# If we're installing grub-efi, it wants /sys and efivarfs
	# mounted in the target. Conversely, if we're installing grub-pc,
	# efivarfs must not be mounted in the target to prevent update-grub
	# from adding a bogus menu entry for UEFI firmware settings.
	mountvirtfs sysfs $ROOT/sys
	mountvirtfs efivarfs $ROOT/sys/firmware/efi/efivars non-fatal

	# debconf priority for the EFI removable question. The happy path here
	# is that efi boot variables are available. If they are not, then the
	# user most likely has to force grub to the removable media path in
	# order to be able to boot. Note that the condition is necessary but
	# not sufficient, ie: the availability of boot variables does not
	# guarantee that rebooting will work, but the lack thereof is a
	# guarantee that it won't.
	if mount | grep -q "on /sys/firmware/efi/efivars type efivarfs .*\Wro\W.*"; then
		ER_PRIO=high
		NV_PRIO=high
	elif ls /sys/firmware/efi/efivars/ 2>/dev/null | grep -qE '^Boot[0-9A-F]{4}'; then
		ER_PRIO=low
		NV_PRIO=low
	else
		ER_PRIO=high
		NV_PRIO=high
	fi

# Mimic ubiquity 1.128ubuntu15 behaviour and only bypass low priority question. -- Ark74
#	# Should we force a copy of grub-efi to be installed
#	# to the removable media path too? Set default to true
#	# if the removable media path is empty, then ask at low
#	# priority, or can also be pre-seeded of course
#	if ! [ -e /var/lib/grub-installer/removable_media_path_checked ]; then
#		info "Checking removable media path $ROOT/boot/efi/EFI/BOOT/BOOT${EFI_SUFFIX}.EFI"
#		if ! [ -f $ROOT/boot/efi/EFI/BOOT/BOOT${EFI_SUFFIX}.EFI ]; then
#			info "Removable media path is empty"
#			db_fget grub-installer/force-efi-extra-removable seen
#			if [ "$RET" = false ]; then
#				info "force-efi-extra-removable not preseeded, set it true"
#				db_set grub-installer/force-efi-extra-removable true
#			fi
#		fi
#		mkdir -p /var/lib/grub-installer
#		touch /var/lib/grub-installer/removable_media_path_checked
#	fi
	db_input $ER_PRIO grub-installer/force-efi-extra-removable || [ $? -eq 30 ]
	db_go || exit 10
	db_get grub-installer/force-efi-extra-removable
	if [ "$RET" = true ]; then
		info "YES on force-efi-extra-removable"
		$chroot $ROOT 'debconf-set-selections' <<EOF
$grub_package grub2/force_efi_extra_removable boolean true
EOF
	fi

	# Should we update EFI variables to boot into Debian?
	# Especially useful for ARM EBBR systems where changing
	# them can be unsupported, along with force-extra-removable.
	db_input $NV_PRIO grub-installer/update-nvram || [ $? -eq 30 ]
	db_go || exit 10
	db_get grub-installer/update-nvram
	if [ "$RET" = false ]; then
		info "NO on update-nvram"
		$chroot $ROOT 'debconf-set-selections' <<EOF
$grub_package grub2/update_nvram boolean false
EOF
	fi
	;;
esac

db_progress START 0 6 grub-installer/progress/title

db_subst grub-installer/progress/step_install GRUB "$grub_package"
db_progress INFO grub-installer/progress/step_install

# apt-install passes --no-remove to apt, but grub{,2} conflict each other, so
# we need to purge them first to support users who try grub2 and then switch
# to grub legacy, or vice-versa
case "$grub_package" in
    grub-pc)
	log-output -t grub-installer $chroot $ROOT dpkg -P \
		grub grub-legacy grub-efi \
		grub-efi-amd64-bin grub-efi-amd64 grub-efi-amd64 \
		grub-efi-ia32-bin grub-efi-ia32
    ;;
    grub-efi*)
        export DEBIAN_PRIORITY=critical
        export DEBIAN_FRONTEND=noninteractive
        unset DEBIAN_HAS_FRONTEND
        unset DEBCONF_REDIR
	log-output -t grub-installer $chroot $ROOT dpkg -P \
		grub grub-legacy grub-pc-bin grub-pc grub-gfxpayload-lists
    ;;
esac

# Install os-prober *always* here, so we can rely on it
db_progress STEP 1
db_progress INFO grub-installer/progress/step_os-probe
db_settitle debian-installer/grub-installer/title
apt-install --allow-remove os-prober || true
os-prober > /tmp/os-probed || true

info "initial os-prober call found the following OSes:"
info "$(cat /tmp/os-probed)"

# Work out what probed OSes can be booted from grub
supported_os_list=""
if [ -s /tmp/os-probed ]; then
	unsupported_os_list=""

	OLDIFS="$IFS"
	IFS="$newline"
	for os in $(cat /tmp/os-probed); do
		IFS="$OLDIFS"
		title=$(echo "$os" | cut -d: -f2)
		type=$(echo "$os" | cut -d: -f4)
		case "$type" in
		    chain|efi|hurd|macosx|minix)
			: ;;
		    linux)
			# Check for linux systems that we don't
			# know how to boot
			partition=$(echo "$os" | cut -d: -f1)
			if [ -z "$(linux-boot-prober $partition)" ]; then
				if [ -n "$unsupported_os_list" ]; then
					unsupported_os_list="$unsupported_os_list, $title"
				else
					unsupported_os_list="$title"
				fi
				continue
			fi
			;;
		    *)
			if [ -n "$unsupported_os_list" ]; then
				unsupported_os_list="$unsupported_os_list, $title"
			else
				unsupported_os_list="$title"
			fi
			continue
			;;
		esac

		if [ -n "$supported_os_list" ]; then
			supported_os_list="$supported_os_list, $title"
		else
			supported_os_list="$title"
		fi
		
		IFS="$newline"
	done
	IFS="$OLDIFS"
	
	info "Supported os list: $supported_os_list"
	info "Unsupported os list: $unsupported_os_list"

	if [ -n "$unsupported_os_list" ]; then
		# Unsupported OS, jump straight to manual boot device question.
		state=2
	else
		q=grub-installer/with_other_os
		db_subst $q OS_LIST "$supported_os_list"
		state=1
	fi
else
	q=grub-installer/only_debian
	state=1
fi

if [ -n "$supported_os_list" ]; then
	# We have found other supported OSes - ask the user (at a low priority)
	# if we should run os-prober, with the default set to yes
	info "Found other OSes, triggering question about os-prober, default yes"
	default=yes
	db_subst "grub-installer/enable_os_prober_otheros_$default" OS_LIST "$supported_os_list"
else
	info "Found NO other OSes, triggering question about os-prober, default no"
	default=no
fi
db_input low "grub-installer/enable_os_prober_otheros_$default" || [ $? -eq 30 ]
db_go || exit 10
db_get "grub-installer/enable_os_prober_otheros_$default"
if [ "$RET" = true ]; then
	info "YES on enable_os_prober_otheros_$default"
	info "Enabling os-prober"
	$chroot $ROOT 'debconf-set-selections' <<EOF
$grub_package grub2/enable_os_prober boolean true
EOF
fi
# Do not ask again on subsequent runs, it has no effect (#1035085)
db_fset grub-installer/enable_os_prober_otheros_yes seen true
db_fset grub-installer/enable_os_prober_otheros_no seen true

exit_code=0
apt-install $grub_package || exit_code=$?
case $grub_package in
    # In these cases, we know we have a signed shim
    # package. There will be others in future.
    grub-efi-amd64*|grub-efi-ia32*|grub-efi-arm64*)
	info "Additionally installing shim to go with $grub_package"
	# It seems that in live installations we might have
	# dkms installed and will have to remove it to install
	# shim. Add --allow-remove here to make that
	# work for now.
	apt-install --allow-remove shim || true
	;;
esac

if [ $exit_code -ne 0 ] ; then
	db_progress STOP
	info "Calling 'apt-install $grub_package' failed"
	db_subst grub-installer/apt-install-failed GRUB "$grub_package"
	db_input critical grub-installer/apt-install-failed || true
	if ! db_go; then
		exit 10 # back up to menu
	fi
	exit 1
fi

grub_debian_version="$($chroot $ROOT dpkg-query -W -f '${Version}' $grub_package)"

# Try to avoid using (hd0) as a boot device name.  Something which can be
# turned into a stable by-id name is better.
default_bootdev_os="$($chroot $ROOT grub-mkdevicemap --no-floppy -m - | head -n1 | cut -f2)"
if [ "$default_bootdev_os" ]; then
	default_bootdev="$($chroot $ROOT readlink -f "$default_bootdev_os")"
else
	default_bootdev="(hd0)"
fi

# Set a sensible default boot device, so that we aren't installing GRUB to
# installation media which may be removed later.  The disk containing /cdrom
# is very unlikely to be a sensible default.  If we had to fall back to
# (hd0), then we can't tell exactly which disk that is, but if /cdrom seems
# to be a USB stick then (hd0) may not be safe.  If we hit either of those
# checks, then try the disk containing /boot instead.
# The same goes for /hd-media, so avoid installing there as well.
cdsrc=$(mount | grep "on /cdrom " | cut -d' ' -f1)
cdfs=$(mount | grep "on /cdrom " | cut -d' ' -f5)
hdsrc=$(mount | grep "on /hd-media " | cut -d' ' -f1)
hybrid=false
if db_get cdrom-detect/hybrid; then
	hybrid="$RET"
fi
case $ARCH:$grub_package in
    *:grub-pc)
	if [ "$(device_to_disk "$cdsrc")" = "$default_bootdev" ] || \
	   ([ -n "$hdsrc" ] && [ "$(device_to_disk "$hdsrc")" = "$default_bootdev" ]) || \
	   ([ "$default_bootdev" = '(hd0)' ] && \
	    (([ -n "$cdfs" ] && [ "$cdfs" != "iso9660" ]) || \
	     [ "$hybrid" = true ])) || \
	   ([ "$default_bootdev" != '(hd0)' ] && \
	    ! partmap "$default_bootdev" >/dev/null && \
	    ! grub_probe -t fs -d "$default_bootdev" >/dev/null); then
		db_fget grub-installer/bootdev seen
		if [ "$RET" != true ]; then
			bootfs=$(findfs /boot)
			[ "$bootfs" ] || bootfs="$(findfs /)"
			disk=$(device_to_disk "$bootfs")
			db_set grub-installer/bootdev "$disk"
			state=2
		fi
	fi
	;;
    powerpc/chrp*:grub-ieee1275|ppc64/chrp*:grub-ieee1275|ppc64el/*:grub-ieee1275)
	# Hack to pick the right boot device.  This should really be done in
	# grub-install instead, and will need to be done there in order to
	# support major GRUB upgrades properly, but this will tide us over
	# for now.
	bootdev="$wipe_bootdev"
	state=3
	;;
    powerpc/powermac_newworld:grub-ieee1275|ppc64/powermac_newworld:grub-ieee1275)
	bootdev="$(findfs /boot/grub)"
	state=3
	;;
    sparc/*:grub-ieee1275|sparc64/*:grub-ieee1275)
	bootfs=$(findfs /boot)
	[ "$bootfs" ] || bootfs="$(findfs /)"
	# see: https://github.com/esnowberg/grub2-sparc/wiki
	case $SUBARCH in
	    *_gpt)
		# For GPT disk labels, grub-install uses the device as target
		disk=$(device_to_disk "$bootfs")
		db_set grub-installer/bootdev "$disk"
		;;
	    *)
		# For Sun disk labels, grub-install uses the boot partition as target
		db_set grub-installer/bootdev "$bootfs"
		;;
	esac
	state=3
	;;
    *)
	# No need for install device selection on other platforms.
	bootdev=dummy
	state=3
	;;
esac

db_progress STEP 1
db_progress INFO grub-installer/progress/step_bootdev

select_bootdev() {
	debug "select_bootdev: arg='$1'"

	local dev_list dev_descr grubdev devices disk_id dev descr
	local default_choice chosen result

	result=""
	default_choice="$1"

	# /dev/disk/by-id has multiple links for the same physical disk.
	# Let's trust grub-mkdevicemap to select the most suitable ones
	# and correctly handle systems with no /dev/disk/by-id.
	# Use disk id string as a shortcut way to describe it.
	# FIXME switch to grub-pc's far more elegant disk_descriptions()
	dev_list=
	dev_descr=
	devices="$($chroot $ROOT grub-mkdevicemap --no-floppy -m - | cut -f2)"
	for grubdev in $devices; do
		disk_id="$(device_to_id $grubdev)"
		dev="$(readlink -f "$disk_id")"
		dev_list="${dev_list:+$dev_list, }$dev"
		descr="$(echo $disk_id |sed -e 's+^.*/++' |sed -e 's+,+\\,+g')"
		if [ "$dev" = "$disk_id" ]; then
			dev_descr="${dev_descr:+$dev_descr, }$dev"
		else
			#/dev/sdX (id)
			dev_descr="${dev_descr:+$dev_descr, }$dev  ($descr)"
		fi
	done

	debug "Bootdev Choices: '$dev_list'"
	debug "Bootdev Descriptions: '$dev_descr'"

	db_subst grub-installer/choose_bootdev DEVICES_LIST "$dev_list"
	db_subst grub-installer/choose_bootdev DESCRIPTIONS "$dev_descr"
	# set initial selection
	if [ -n "$default_choice" ] ; then
		chosen="$(readlink -f "$default_choice")"
		if [ -n "$chosen" ] ;then
			db_set grub-installer/choose_bootdev "$chosen"
		fi
	fi

	db_input high grub-installer/choose_bootdev || true
	if ! db_go; then
		log "Returning to menu"
		db_progress STOP
		exit 10
	fi

	db_get grub-installer/choose_bootdev || true
	# Choices-C (not shown to user) can be set to 'manual'
	if [ "$RET" = "manual" ] ; then
		result=""
	else
		result="$(echo "$RET" | cut -d' ' -f1)"
	fi

	debug "select_bootdev: result='$result'"
	echo "$result"
}

if [ "$bootdev" != "dummy" ]; then
	# check for a preseeded value
	db_get grub-installer/bootdev || true
	if [ -n "$RET" ] ; then
		bootdev="$RET"
	fi
fi

while : ; do

	debug "q='$q' state='$state' defbd='$default_bootdev' bd='$bootdev'"
	db_fget grub-installer/bootdev seen
	if [ "$RET" = true ] && db_get grub-installer/bootdev && [ "$RET" ] ; then
		if [ "$RET" = "default" ]; then
			bootdev=$default_bootdev
		else
			bootdev=$RET
		fi
		break
	elif [ "$state" = 1 ]; then
		db_input high $q || true
		if ! db_go; then
			# back up to menu
			db_progress STOP
			exit 10
		fi
		db_get $q
		if [ "$RET" = true ]; then
			# default_bootdev can be guessed incorrectly.
			# If the user supplied a value for bootdev,
			# ask them to resolve any conflict.
			if [ "$bootdev" != "$default_bootdev" ] ; then
				bootdev="$(select_bootdev "$bootdev")"
				previous_state=1
			fi
			if [ -e "$bootdev" ] ; then
			    break
			else
			    state=2
			fi
		else
			state=2
		fi
	elif [ "$state" = 2 ]; then

		if [ "$previous_state" != "1" ]; then
			bootdev="$(select_bootdev "$bootdev")"
			unset previous_state
		fi

		if [ ! -e "$bootdev" ]; then
			db_input critical grub-installer/bootdev || true
			if ! db_go; then
				if [ "$q" ]; then
					state=1
				else
					# back up to menu
					db_progress STOP
					exit 10
				fi
			else
				db_get grub-installer/bootdev
				bootdev=$RET
				if echo "$bootdev" | grep -qv '('; then
					mappedbootdev=$(mapdevfs "$bootdev") || true
					if [ -n "$mappedbootdev" ]; then
						bootdev="$mappedbootdev"
					fi
				fi
				break
			fi
		else
			break
		fi
	else
		break
	fi
done

db_progress STEP 1
db_subst grub-installer/progress/step_install_loader BOOTDEV "$bootdev"
db_progress INFO grub-installer/progress/step_install_loader

info "Installing grub on '$bootdev'"

update_mtab

# Install grub on each space separated disk in the list
bootdevs="$bootdev"

for bootdev in $bootdevs; do
	grub_install_params=
	if is_floppy "$bootdev"; then
		grub_install_params="$grub_install_params --allow-floppy"
	fi

	db_get grub-installer/force-efi-extra-removable
	if [ "$RET" = true ]; then
		grub_install_params="$grub_install_params --force-extra-removable"
	fi

	db_get grub-installer/update-nvram
	if [ "$RET" = false ]; then
		grub_install_params="$grub_install_params --no-nvram"
	fi

	case $ARCH in
	    powerpc/chrp_pegasos)
		# nvram is broken here
		grub_install_params="$grub_install_params --no-nvram"
		;;
	    powerpc/powermac_newworld|ppc64/powermac_newworld)
		grub_install_params="$grub_install_params --macppc-directory=/boot/grub --no-nvram"
		;;
	    sparc/*|sparc64/*)
		# see: https://github.com/esnowberg/grub2-sparc/wiki
		case $SUBARCH in
		    *_gpt)
			# No additional parameters for installing GRUB
			# on disks with GPT disk labels
			:
			;;
		    *)
			grub_install_params="$grub_install_params --skip-fs-probe"
			;;
		esac
		;;
	esac

	# install even if it requires blocklists
	grub_install_params="$grub_install_params --force"

	CODE=0
	case $ARCH:$grub_package in
	    *:grub-pc|*:grub-efi*|sparc/*:grub-ieee1275|sparc64/*:grub-ieee1275|powerpc/chrp*:grub-ieee1275|ppc64/chrp*:grub-ieee1275|ppc64el/*:grub-ieee1275)
		info "Running $chroot $ROOT grub-install $grub_install_params \"$bootdev\""
		log-output -t grub-installer $chroot $ROOT grub-install $grub_install_params "$bootdev" || CODE=$?

		# Debugging information in EFI cases, when efibootmgr is available:
		case $grub_package in
		    grub-efi*)
			if $chroot $ROOT which efibootmgr >/dev/null 2>&1; then
			    info "Dumping boot loader configuration with efibootgmr"
			    log-output -t efibootmgr $chroot $ROOT efibootmgr -v || true
			fi
			;;
		esac
		;;
	    powerpc/powermac_newworld:grub-ieee1275|ppc64/powermac_newworld:grub-ieee1275)
		info "Running $chroot $ROOT grub-install $grub_install_params"
		log-output -t grub-installer $chroot $ROOT grub-install $grub_install_params || CODE=$?
		;;
	    amd64/*:grub-xen)
		info "Running $chroot $ROOT grub-install --target=x86_64-xen $grub_install_params"
		log-output -t grub-installer $chroot $ROOT grub-install --target=x86_64-xen $grub_install_params || CODE=$?
		;;
	    i386/*:grub-xen)
		info "Running $chroot $ROOT grub-install --target=x86_64-xen $grub_install_params"
		log-output -t grub-installer $chroot $ROOT grub-install --target=x86_64-xen $grub_install_params || CODE=$?
		info "Running $chroot $ROOT grub-install --target=i386-xen $grub_install_params"
		log-output -t grub-installer $chroot $ROOT grub-install --target=i386-xen $grub_install_params || CODE=$?
		;;
	    *)
		info "Running $chroot $ROOT grub-install $grub_install_params"
		log-output -t grub-installer $chroot $ROOT grub-install $grub_install_params || CODE=$?
		;;
	esac
	if [ "$CODE" = 0 ]; then
		info "grub-install ran successfully"
		case $ARCH:$grub_package in
		    powerpc/powermac_newworld:grub-ieee1275|ppc64/powermac_newworld:grub-ieee1275)
			umount $bootdev
			$chroot $ROOT hmount $bootdev
			$chroot $ROOT hattrib -c UNIX -t tbxi :System:Library:CoreServices:BootX
			$chroot $ROOT hattrib -b :System:Library:CoreServices
			$chroot $ROOT humount
			mount $bootdev $ROOT/boot/grub
			;;
		    *)
			:
			;;
		esac
	else
		case $ARCH:$grub_package in
		    *:grub-pc|*:grub-efi*|sparc/*:grub-ieee1275|sparc64/*:grub-ieee1275|powerpc/chrp*:grub-ieee1275|ppc64/chrp*:grub-ieee1275|ppc64el/*:grub-ieee1275)
			error "Running 'grub-install $grub_install_params \"$bootdev\"' failed."
			;;
		    *)
			error "Running 'grub-install $grub_install_params failed."
			;;
		esac
		db_subst grub-installer/grub-install-failed BOOTDEV "$bootdev"
		db_input critical grub-installer/grub-install-failed || [ $? -eq 30 ]
		db_go || true
		db_progress STOP
		exit 1
	fi
done

# Split a device name into a disk device name and a partition number, if
# possible.
split_device () {
	disk=
	part=
	case $1 in
		/dev/[vhs]d[a-z]*)
			disk="$(echo "$1" | sed 's,\(/dev/[a-z]\+\).*,\1,')"
			part="$(echo "$1" | sed 's,/dev/[a-z]\+\(.*\),\1,')"
			;;
		/dev/*/c*d*)
			disk="$(echo "$1" | sed 's,\(/dev/.*/c[0-9]\+d[0-9]\+\).*,\1,')"
			part="$(echo "$1" | sed 's,/dev/.*/c[0-9]\+d[0-9]\+p\([0-9]\+\),\1,')"
			[ "$part" != "$1" ] || part=
			;;
		/dev/mmcblk*)
			disk="$(echo "$1" | sed 's,\(/dev/mmcblk[0-9]\+\).*,\1,')"
			part="$(echo "$1" | sed 's,/dev/mmcblk[0-9]\+p\([0-9]\+\),\1,')"
			[ "$part" != "$1" ] || part=
			;;
		/dev/nvme*n*p*)
			disk="$(echo "$1" | sed 's,\(/dev/nvme[0-9]\+n[0-9]\+\).*,\1,')"
			part="$(echo "$1" | sed 's,/dev/nvme[0-9]\+n[0-9]\+p\([0-9]\+\),\1,')"
			[ "$part" != "$1" ] || part=
			;;
	esac
	echo "$disk $part"
}

make_device_map () {
	# If you're preseeding (hd0) et al, stop - preseed OS device names
	# instead.  However, for backward compatibility we ensure that a
	# device.map exists if you do this.
	[ ! -e "$device_map" ] || return 0
	local no_floppy
	case $1 in
		\(fd*|fd*)
			no_floppy=
			;;
		*)
			no_floppy=--no-floppy
			;;
	esac
	$chroot $ROOT grub-mkdevicemap $no_floppy
}

make_active_partition () {
	bootdisk=
	bootpart=
	case $bootdev in
		/dev/*)
			bootdev_split="$(split_device "$bootdev")"
			bootdisk="${bootdev_split%% *}"
			bootpart="${bootdev_split#* }"
			;;
		\([hf]d[0-9]*\))
			make_device_map "$bootdev"
			bootdev_nopart="$(echo "$bootdev" | sed 's/,[^)]*//')"
			bootdisk="$(grep -v '^#' $device_map | grep "^ *$bootdev_nopart" \
				| sed 's/^ *(.*)[[:space:]]*\(.*\)/\1/')"
			bootpart="$(echo "$bootdev" | sed 's/.*,\([^)]*\).*/\1/')"
			[ "$bootpart" != "$bootdev" ] || bootpart=
			;;
		[hf]d[0-9]*)
			# The GRUB format with no parenthesis.
			make_device_map "$bootdev"
			bootdisk="$(grep -v '^#' $device_map | grep "^ *(${bootdev%%,*})" \
				| sed 's/^ *(.*)[[:space:]]*\(.*\)/\1/')"
			bootpart="${bootdev#*,}"
			[ "$bootpart" != "$bootdev" ] || bootpart=
			;;
	esac

	# Make sure that there's *some* active partition; some BIOSes
	# reportedly don't like it otherwise.  Leave well alone on GPT since
	# libparted does this for us.
	if [ "$bootdisk" ]; then
		if [ -z "$bootpart" ]; then
			# If /boot is on the same disk and is primary,
			# that's probably a reasonable default.
			bootfs="$(findfs /boot)"
			[ "$bootfs" ] || bootfs="$(findfs /)"
			bootfs="$(mapdevfs "$bootfs")"
			bootfs_split="$(split_device "$bootfs")"
			bootfs_disk="${bootfs_split%% *}"
			bootfs_part="${bootfs_split#* }"
			if [ "$bootfs_disk" = "$bootdisk" ] && \
			   ([ "$bootfs_part" -ge 1 ] && [ "$bootfs_part" -le 4 ]); then
				bootpart="$bootfs_part"
			fi
		fi

		# Don't quote $bootpart here; that argument should vanish if
		# there is no obviously appropriate partition to select.
		/usr/lib/grub-installer/ensure-active "$bootdisk" $bootpart
	fi
}

db_get grub-installer/make_active
if [ "$RET" = true ]; then
	make_active_partition
fi

if [ "$grub_package" = "grub-pc" ]; then
	# Do the same thing on upgrades.
	$chroot $ROOT 'debconf-set-selections' <<EOF
$grub_package grub-pc/install_devices multiselect $(devices_to_ids $bootdevs)
$grub_package grub-pc/install_devices_empty boolean false
$grub_package grub-pc/install_devices_empty seen false
EOF
fi

db_progress STEP 1
db_progress INFO grub-installer/progress/step_config_loader

if [ "$bootfstype" = "zfs" ] || [ "$rootfstype" = "zfs" ] ; then
	# Required by update-grub on ZFS
	mkdir -p $ROOT/boot/zfs
	cp /boot/zfs/zpool.cache $ROOT/boot/zfs/
fi

# Delete for idempotency
rm -f $ROOT/boot/grub/$menu_file
update_grub

if [ "$serial" ] ; then
	# Modify /etc/default/grub so _grub_ uses serial console.
	case $grub_package in
	    grub-pc|grub-efi*)
		if grep -q "^GRUB_TERMINAL=" $ROOT/etc/default/grub; then
			sed -i $ROOT/etc/default/grub -e "s/^\(GRUB_TERMINAL\)=.*/\1=serial/g"
		else
			echo "GRUB_TERMINAL=serial" >> $ROOT/etc/default/grub
		fi
		if grep -q "^GRUB_SERIAL_COMMAND=" $ROOT/etc/default/grub ; then
			sed -i $ROOT/etc/default/grub -e "s/^\(GRUB_SERIAL_COMMAND\)=.*/\1=\"`grub_serial_console $serial`\"/g"
		else
			echo "GRUB_SERIAL_COMMAND=\"`grub_serial_console $serial`\"" >> $ROOT/etc/default/grub
		fi
		update_grub # propagate to grub.cfg
		;;
	esac
fi 

# Generate static menu entries for other OSes if os-prober not present
if ! $chroot $ROOT which os-prober >/dev/null 2>&1; then
	tmpfile=/tmp/menu.extras
	OLDIFS="$IFS"
	IFS="$newline"

	no_floppy=""
	if $chroot $ROOT dpkg --compare-versions $grub_debian_version ge 1.96+20090609-1 ; then
		no_floppy="--no-floppy"
	fi

	# I don't think any of this is needed, as we have os-prober?
	for os in $(cat /tmp/os-probed); do
		IFS="$OLDIFS"
		title=$(echo "$os" | cut -d: -f2)
		shortname=$(echo "$os" | cut -d: -f3)
		type=$(echo "$os" | cut -d: -f4)
		case "$type" in
		    chain)
			partition=$(mapdevfs $(echo "$os" | cut -d: -f1))
			grubdrive=$(convert "$partition") || true
			if [ -n "$grubdrive" ]; then
				grub2_write_chain
			fi
			;;
		    linux)
			partition=$(echo "$os" | cut -d: -f1)
			mappedpartition=$(mapdevfs "$partition")
			IFS="$newline"
			for entry in $(linux-boot-prober "$partition"); do
				IFS="$OLDIFS"
				bootpart=$(echo "$entry" | cut -d: -f2)
				mappedbootpart=$(mapdevfs "$bootpart") || true
				if [ -z "$mappedbootpart" ]; then
					mappedbootpart="$bootpart"
				fi
				label=$(echo "$entry" | cut -d : -f3)
				if [ -z "$label" ]; then
					label="$title"
				fi
				kernel=$(echo "$entry" | cut -d : -f4)
				initrd=$(echo "$entry" | cut -d : -f5)
				if echo "$kernel" | grep -q '^/boot/' && \
				   [ "$mappedbootpart" != "$mappedpartition" ]; then
					# separate /boot partition
					kernel=$(echo "$kernel" | sed 's!^/boot!!')
					initrd=$(echo "$initrd" | sed 's!^/boot!!')
					grubdrive=$(convert "$mappedbootpart") || true
				else
					grubdrive=$(convert "$mappedpartition") || true
				fi
				params="$(echo "$entry" | cut -d : -f6-) $serial"
				grub2_write_linux
				IFS="$newline"
			done
			IFS="$OLDIFS"
			;;
		    hurd)
			partition=$(mapdevfs $(echo "$os" | cut -d: -f1))
			grubdrive=$(convert "$partition") || true
			hurddrive=$(hurd_convert "$partition") || true
			# Use the standard hurd boilerplate to boot it.
			grub2_write_hurd
			;;
		    *)
			info "unhandled: $os"
			;;
		esac
		IFS="$newline"
	done
	IFS="$OLDIFS"

	if [ -s $tmpfile ] ; then
		cat > $ROOT/etc/grub.d/30_otheros << EOF
#!/bin/sh
exec tail -n +3 \$0
EOF
		cat $tmpfile >> $ROOT/etc/grub.d/30_otheros
		chmod +x $ROOT/etc/grub.d/30_otheros
		update_grub # propagate 30_otheros to grub.cfg
	fi
	rm -f $tmpfile
fi
rm -f /tmp/os-probed

case $ARCH in
    mipsel/loongson-2f)
	# Configure PMON to load GRUB by default.
	if [ ! -e $ROOT/boot.cfg ] && [ ! -e $ROOT/boot/boot.cfg ]; then
		pmon_partition="$(grub_probe -d -t drive "$bootfs" | \
				  sed 's/.*,//; s/[^0-9]//g')"
		if [ "$pmon_partition" ]; then
			pmon_partition=$(($pmon_partition - 1))
		else
			pmon_partition=0 # fallback guess
		fi
		if [ "$rootfs" = "$bootfs" ]; then
			pmon_grub_path=/boot/grub.elf
			pmon_boot_cfg_path=$ROOT/boot.cfg
		else
			pmon_grub_path=/grub.elf
			pmon_boot_cfg_path=$ROOT/boot/boot.cfg
		fi
		cat > $pmon_boot_cfg_path <<EOF
default 0
timeout 0
showmenu 0

title Boot with GRUB
	kernel (wd0,$pmon_partition)$pmon_grub_path
	args nil
EOF
	fi
	;;
esac

db_progress STEP 1
db_progress INFO grub-installer/progress/step_update_etc

if [ -e $ROOT/etc/kernel-img.conf ] ; then
	sed -i 's/do_bootloader = yes/do_bootloader = no/' $ROOT/etc/kernel-img.conf
fi
if [ ! -e $ROOT/etc/kernel/postinst.d/zz-update-grub ] && \
   [ -z "$(grep update-grub $ROOT/etc/kernel-img.conf)" ]; then
	(
		echo "postinst_hook = update-grub"
		echo "postrm_hook   = update-grub"
	) >> $ROOT/etc/kernel-img.conf
fi

db_progress STEP 1
db_progress STOP
