#! /bin/sh
#
# Set a default boot entry for GRUB.
# Copyright (C) 2004,2009  Free Software Foundation, Inc.
#
# GRUB is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GRUB is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GRUB.  If not, see <http://www.gnu.org/licenses/>.

#set -eu

# Initialize some variables.
prefix=/usr
exec_prefix=/usr
sbindir=${exec_prefix}/sbin
bindir=${exec_prefix}/bin
sysconfdir="/etc"
PACKAGE_NAME=GRUB2
PACKAGE_VERSION=2.12
datarootdir="/usr/share"
datadir="${datarootdir}"
if [ -z "${pkgdatadir+x}" ]; then
    pkgdatadir="${datadir}/grub2"
fi

self=`basename $0`

grub_editenv=${bindir}/grub2-editenv
grub_probe="${sbindir}/grub2-probe"
etcdefaultgrub=${sysconfdir}/default/grub

if test -f "$etcdefaultgrub" ; then
    # shellcheck source=/etc/default/grub
    . "$etcdefaultgrub"
fi

grubdir=`echo "/boot/grub2" | sed 's,//*,/,g'`
startlink="${grubdir}/grub.cfg"

blsdir=`echo "/boot/loader/entries" | sed 's,//*,/,g'`

backupsuffix=.bak

arch="$(uname -m)"

export TEXTDOMAIN=grub2
export TEXTDOMAINDIR="${datarootdir}/locale"

# shellcheck source=/usr/share/grub2/grub-mkconfig_lib
. "${pkgdatadir}/grub-mkconfig_lib"

# FIXME: Abort if grub_probe fails

GRUB_DEVICE="`${grub_probe} --target=device /`"
GRUB_DEVICE_UUID="`${grub_probe} --device ${GRUB_DEVICE} --target=fs_uuid 2> /dev/null`" || true
GRUB_DEVICE_PARTUUID="`${grub_probe} --device ${GRUB_DEVICE} --target=partuuid 2> /dev/null`" || true
GRUB_FS="`${grub_probe} --device ${GRUB_DEVICE} --target=fs 2> /dev/null || echo unknown`"

# loop-AES arranges things so that /dev/loop/X can be our root device, but
# the initrds that Linux uses don't like that.
case ${GRUB_DEVICE} in
  /dev/loop/*|/dev/loop[0-9])
    GRUB_DEVICE=$(losetup "${GRUB_DEVICE}" | sed -e "s/^[^(]*(\([^)]\+\)).*/\1/")
  ;;
esac

# Default to disabling partition uuid support to maintian compatibility with
# older kernels.
GRUB_DISABLE_LINUX_PARTUUID=${GRUB_DISABLE_LINUX_PARTUUID-true}

# btrfs may reside on multiple devices. We cannot pass them as value of root= parameter
# and mounting btrfs requires user space scanning, so force UUID in this case.
if ( [ "x${GRUB_DEVICE_UUID}" = "x" ] && [ "x${GRUB_DEVICE_PARTUUID}" = "x" ] ) \
    || ( [ "x${GRUB_DISABLE_LINUX_UUID}" = "xtrue" ] \
	&& [ "x${GRUB_DISABLE_LINUX_PARTUUID}" = "xtrue" ] ) \
    || ( ! test -e "/dev/disk/by-uuid/${GRUB_DEVICE_UUID}" \
	&& ! test -e "/dev/disk/by-partuuid/${GRUB_DEVICE_PARTUUID}" ) \
    || ( test -e "${GRUB_DEVICE}" && uses_abstraction "${GRUB_DEVICE}" lvm ); then
  LINUX_ROOT_DEVICE=${GRUB_DEVICE}
elif [ "x${GRUB_DEVICE_UUID}" = "x" ] \
    || [ "x${GRUB_DISABLE_LINUX_UUID}" = "xtrue" ]; then
  LINUX_ROOT_DEVICE=PARTUUID=${GRUB_DEVICE_PARTUUID}
else
  LINUX_ROOT_DEVICE=UUID=${GRUB_DEVICE_UUID}
fi

if [ "x$GRUB_CONMODE" != "x" ]; then
  GRUB_CMDLINE_LINUX="conmode=${GRUB_CONMODE} ${GRUB_CMDLINE_LINUX}"
fi

case x"$GRUB_FS" in
    xbtrfs)
	if [ "x${SUSE_BTRFS_SNAPSHOT_BOOTING}" != "xtrue" ]; then
	    rootsubvol="`make_system_path_relative_to_its_root /`"
	    rootsubvol="${rootsubvol#/}"
	    if [ "x${rootsubvol}" != x ] && [ "x$SUSE_REMOVE_LINUX_ROOT_PARAM" != "xtrue" ]; then
	        GRUB_CMDLINE_LINUX="rootflags=subvol=${rootsubvol} ${GRUB_CMDLINE_LINUX}"
	    fi
	fi
	;;
    xzfs)
	rpool=`${grub_probe} --device ${GRUB_DEVICE} --target=fs_label 2>/dev/null || true`
	bootfs="`make_system_path_relative_to_its_root / | sed -e "s,@$,,"`"
	LINUX_ROOT_DEVICE="ZFS=${rpool}${bootfs%/}"
	;;
esac

if [ "x$SUSE_REMOVE_LINUX_ROOT_PARAM" = "xtrue" ]; then
  LINUX_ROOT_DEVICE=""
fi

# Usage: usage
# Print the usage.
usage () {
    gettext_printf "Usage: %s\n" "$self"
    gettext "Switch to BLS config files. Only for testing purpose !!!\n"; echo
    echo
    print_option_help "-h, --help" "$(gettext "print this message and exit")"
    print_option_help "-V, --version" "$(gettext "print the version information and exit")"
    echo
    print_option_help "--backup-suffix=$(gettext "SUFFIX")" "$backupsuffix"
    print_option_help "--bls-directory=$(gettext "DIR")" "Noop, always $blsdir"
    print_option_help "--config-file=$(gettext "FILE")" "$startlink"
    print_option_help "--grub-defaults=$(gettext "FILE")" "$etcdefaultgrub"
    print_option_help "--grub-directory=$(gettext "DIR")" "$grubdir"
    # echo
    # gettext "Report bugs to <bug-grub@gnu.org>."; echo
}

argument () {
    opt=$1
    shift

    if test $# -eq 0; then
        gettext_printf "%s: option requires an argument -- \`%s'\n" "$self" "$opt" 1>&2
        exit 1
    fi
    echo $1
}

# Check the arguments.
while test $# -gt 0
do
    option=$1
    shift

    case "$option" in
    -h | --help)
        usage
        exit 0 ;;
    -V | --version)
        echo "$self (${PACKAGE_NAME}) ${PACKAGE_VERSION}"
        exit 0 ;;

    --backup-suffix)
        backupsuffix=`argument $option "$@"`
        shift
        ;;
    --backup-suffix=*)
        backupsuffix=`echo "$option" | sed 's/--backup-suffix=//'`
        ;;

    --bls-directory)
        # blsdir=`argument $option "$@"`
        gettext_printf "WARN: --bls-directory is currently disabled, it's always $blsdir !!!\n"
        gettext_printf "WARN: use kernel-install instead if you want to test bls directory on ESP !!!\n"
        shift
        ;;
    --bls-directory=*)
        # blsdir=`echo "$option" | sed 's/--bls-directory=//'`
        gettext_printf "WARN: --bls-directory is currently disabled, it's always $blsdir !!!\n"
        gettext_printf "WARN: use kernel-install instead if you want to test bls directory on ESP !!!\n"
        ;;

    --config-file)
        startlink=`argument $option "$@"`
        shift
        ;;
    --config-file=*)
        startlink=`echo "$option" | sed 's/--config-file=//'`
        ;;

    --grub-defaults)
        etcdefaultgrub=`argument $option "$@"`
        shift
        ;;
    --grub-defaults=*)
        etcdefaultgrub=`echo "$option" | sed 's/--grub-defaults=//'`
        ;;

    --grub-directory)
        grubdir=`argument $option "$@"`
        shift
        ;;
    --grub-directory=*)
        grubdir=`echo "$option" | sed 's/--grub-directory=//'`
        ;;

    *)
        gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2
        usage
        exit 1
        ;;
    esac
done

find_grub_cfg() {
    local candidate=""
    while [ -e "${candidate}" -o $# -gt 0 ]
    do
        if [ ! -e "${candidate}" ] ; then
            candidate="$1"
            shift
        fi

        if [ -L "${candidate}" ]; then
            candidate="$(realpath "${candidate}")"
        fi

        if [ -f "${candidate}" ]; then
            export GRUB_CONFIG_FILE="${candidate}"
            return 0
        fi
    done
    return 1
}

if ! find_grub_cfg "${startlink}" ; then
  gettext_printf "Couldn't find config file\n" 1>&2
  exit 1
fi

if [ ! -d "${blsdir}" ]; then
    install -m 700 -d "${blsdir}"
fi

if [ -f /etc/machine-id ]; then
    MACHINE_ID=$(cat /etc/machine-id)
else
    MACHINE_ID=$(dmesg | sha256sum)
fi

mkbls() {
    local kernelver=$1 && shift
    local datetime=$1 && shift
    local prefix=$1 && shift
    local kernelopts=$1 && shift

    local flavor=""

    case "$kernelver" in
      *-*-*)
	flavor=-"${kernelver##*-}"
	;;
    esac
    (
        . /etc/os-release

        cat <<EOF
title ${NAME} (${kernelver}) ${VERSION}
version ${kernelver}
linux ${prefix}/vmlinuz-${kernelver}
initrd ${prefix}/initrd-${kernelver}
options ${kernelopts}
grub_users \$grub_users
grub_arg --unrestricted
grub_class kernel${flavor}
EOF
    ) | cat
}

copy_bls() {
    for kernelver in $(cd /lib/modules/ ; ls -1) "" ; do
	bls_target="${blsdir}/${MACHINE_ID}-${kernelver}.conf"
	linux="/vmlinuz-${kernelver}"
	linux_path="/boot${linux}"
	kernel_dir="/lib/modules/${kernelver}"

	if [ ! -d "${kernel_dir}" ] ; then
            continue
	fi
	if [ ! -f "${linux_path}" ]; then
            continue
	fi

	bootprefix="$(make_system_path_relative_to_its_root /boot)"
	cmdline="root=${LINUX_ROOT_DEVICE} ro ${GRUB_CMDLINE_LINUX} ${GRUB_CMDLINE_LINUX_DEFAULT}"

	mkbls "${kernelver}" \
	      "$(date -u +%Y%m%d%H%M%S -d "$(stat -c '%y' "${kernel_dir}")")" \
	      "${bootprefix}" "${cmdline}" >"${bls_target}"
    done
}

GENERATE=0
if grep '^GRUB_ENABLE_BLSCFG=.*' "${etcdefaultgrub}" \
        | grep -vq '^GRUB_ENABLE_BLSCFG="*true"*\s*$' ; then
    if ! sed -i"${backupsuffix}" \
            -e 's,^GRUB_ENABLE_BLSCFG=.*,GRUB_ENABLE_BLSCFG=true,' \
            "${etcdefaultgrub}" ; then
        gettext_printf "Updating %s failed\n" "${etcdefaultgrub}"
        exit 1
    fi
    GENERATE=1
elif ! grep -q '^GRUB_ENABLE_BLSCFG=.*' "${etcdefaultgrub}" ; then
    if ! echo 'GRUB_ENABLE_BLSCFG=true' >> "${etcdefaultgrub}" ; then
        gettext_printf "Updating %s failed\n" "${etcdefaultgrub}"
        exit 1
    fi
    GENERATE=1
fi

if [ "${GENERATE}" -eq 1 ] ; then
    copy_bls

    if [ $arch = "x86_64" ] && [ ! -d /sys/firmware/efi ]; then
	mod_dir="i386-pc"
    elif [ $arch = "ppc64" -o $arch = "ppc64le" ] && [ ! -d /sys/firmware/opal ]; then
	mod_dir="powerpc-ieee1275"
    fi

    if [ -n "${mod_dir}" ]; then
	install -m 700 "${pkgdatadir}/${mod_dir}/blscfg.mod" "${grubdir}/$mod_dir/" || exit 1
    fi

    cp -af "${GRUB_CONFIG_FILE}" "${GRUB_CONFIG_FILE}${backupsuffix}"
    if ! grub2-mkconfig -o "${GRUB_CONFIG_FILE}" ; then
        install -m 700 "${GRUB_CONFIG_FILE}${backupsuffix}" "${GRUB_CONFIG_FILE}"
        sed -i"${backupsuffix}" \
            -e 's,^GRUB_ENABLE_BLSCFG=.*,GRUB_ENABLE_BLSCFG=false,' \
            "${etcdefaultgrub}"
        gettext_printf "Updating %s failed\n" "${GRUB_CONFIG_FILE}"
        exit 1
    fi
else
    gettext_printf "Do nothing because \$GRUB_ENABLE_BLSCFG is already true in %s\n" "${GRUB_CONFIG_FILE}"
fi

# Bye.
exit 0
