#! /bin/sh
set -e

# Generate grub.cfg by inspecting /boot contents.
# Copyright (C) 2006,2007,2008,2009,2010,2026 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/>.

prefix="/usr"
exec_prefix="/usr"
datarootdir="/usr/share"

prefix="/usr"
exec_prefix="/usr"
sbindir="${exec_prefix}/sbin"
bindir="${exec_prefix}/bin"
sysconfdir="/etc"
PACKAGE_NAME=GRUB2
PACKAGE_VERSION=2.16
host_os=linux-gnu
datadir="${datarootdir}"
if [ "x$pkgdatadir" = x ]; then
    pkgdatadir="${datadir}/grub2"
fi
# export it for scripts
export pkgdatadir

grub_cfg=""
grub_mkconfig_dir="${sysconfdir}"/grub.d

self=`basename $0`

grub_probe="${sbindir}/grub2-probe"
grub_file="${bindir}/grub2-file"
grub_editenv="${bindir}/grub2-editenv"
grub_script_check="${bindir}/grub2-script-check"

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

. "${pkgdatadir}/grub-mkconfig_lib"

# Usage: usage
# Print the usage.
usage () {
    gettext_printf "Usage: %s [OPTION]\n" "$self"
    gettext "Generate a grub config file"; echo
    echo
    print_option_help "-o, --output=$(gettext FILE)" "$(gettext "output generated config to FILE [default=stdout]")"
    print_option_help "-h, --help" "$(gettext "print this message and exit")"
    print_option_help "-V, --version" "$(gettext "print the version information and exit")"
    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 ;;
    -o | --output)
	grub_cfg=`argument $option "$@"`; shift;;
    --output=*)
	grub_cfg=`echo "$option" | sed 's/--output=//'`
	;;
    -*)
	gettext_printf "Unrecognized option \`%s'\n" "$option" 1>&2
	usage
	exit 1
	;;
    # Explicitly ignore non-option arguments, for compatibility.
    esac
done

if [ "x$EUID" = "x" ] ; then
  EUID=`id -u`
fi

if [ "$EUID" != 0 ] ; then
  root=f
  case "`uname 2>/dev/null`" in
    CYGWIN*)
      # Cygwin: Assume root if member of admin group
      for g in `id -G 2>/dev/null` ; do
	case $g in
	  0|544) root=t ;;
	esac
      done ;;
  esac
  if [ $root != t ] ; then
    gettext_printf "%s: You must run this as root\n" "$self" >&2
    exit 1
  fi
fi

set $grub_probe dummy
if test -f "$1"; then
    :
else
    gettext_printf "%s: Not found.\n" "$1" 1>&2
    exit 1
fi

probe_nfsroot_device () {
    while read line ; do
        part1=`echo $line | sed -e 's! - .*$!!'`
        part2=`echo $line | sed -n -e 's! - \(.*\)$!\n\1!p' | sed 1d`

        set -- $part1
        path=$5

        set -- $part2
        fstype=$1
        device=$2

        if [ "x${path}" = "x/" ] &&
           [ "x${fstype}" = "xnfs" -o "x${fstype}" = "xnfs4" ] ; then
            echo "${fstype}:$device"
            return
        fi
    done
}

NFSROOT_DEVICE="`awk '($9!="autofs")&&($9!="securityfs")' /proc/self/mountinfo | probe_nfsroot_device`"

# Disable os-prober by default due to security reasons.
GRUB_DISABLE_OS_PROBER="true"

if [ "x${NFSROOT_DEVICE}" != "x" ]; then
    GRUB_DEVICE=""
    GRUB_DEVICE_UUID=""
    GRUB_DEVICE_PARTUUID=""
    GRUB_FS="unknown"
else
    # Device containing our userland.  Typically used for root= parameter.
    GRUB_DEVICE="`${grub_probe} --target=device /`"
    # Filesystem for the device containing our userland.  Used for stuff like
    # choosing Hurd filesystem module.
    GRUB_FS="`${grub_probe} --device ${GRUB_DEVICE} --target=fs 2> /dev/null || echo unknown`"
    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
fi

# Strive to circumvent grub to enable unsupported filesystem, for eg, nfsroot
if [ x"$GRUB_FS" = x ] || [ x"$GRUB_FS" = xunknown ]; then
    GRUB_FS="$(stat -f -c %T / || echo unknown)"
fi

# Device containing our /boot partition.  Usually the same as GRUB_DEVICE.
GRUB_DEVICE_BOOT="`${grub_probe} --target=device /boot`"
GRUB_DEVICE_BOOT_UUID="`${grub_probe} --device ${GRUB_DEVICE_BOOT} --target=fs_uuid 2> /dev/null`" || true

# Provide a default set of stock linux early initrd images.
# Define here so the list can be modified in the sourced config file.
if [ "x${GRUB_EARLY_INITRD_LINUX_STOCK}" = "x" ]; then
	GRUB_EARLY_INITRD_LINUX_STOCK="intel-uc.img intel-ucode.img amd-uc.img amd-ucode.img early_ucode.cpio microcode.cpio"
fi

if test -f ${sysconfdir}/default/grub ; then
  . ${sysconfdir}/default/grub
fi

if [ "x${GRUB_DISABLE_UUID}" = "xtrue" ]; then
  if [ -z "${GRUB_DISABLE_LINUX_UUID}" ]; then
    GRUB_DISABLE_LINUX_UUID="true"
  fi
  if [ -z "${GRUB_DISABLE_LINUX_PARTUUID}" ]; then
    GRUB_DISABLE_LINUX_PARTUUID="true"
  fi
fi

# XXX: should this be deprecated at some point?
if [ "x${GRUB_TERMINAL}" != "x" ] ; then
# bnc#771393, bsc#1187565 - fix error: terminal 'gfxterm' isn't found.
  GRUB_TERMINAL_INPUT="`echo ${GRUB_TERMINAL} | sed -e '/\bgfxterm\b/{s/\bconsole\b//g;s/\bgfxterm\b/console/}'`"
  GRUB_TERMINAL_OUTPUT="${GRUB_TERMINAL}"
fi

termoutdefault=0
if [ "x${GRUB_TERMINAL_OUTPUT}" = "x" ]; then
    GRUB_TERMINAL_OUTPUT=gfxterm;
    termoutdefault=1;
fi

for x in ${GRUB_TERMINAL_OUTPUT}; do
    case "x${x}" in
	xgfxterm) ;;
	xconsole | xserial | xofconsole | xvga_text)
            # make sure all our children behave in conformance with ascii..
	    export LANG=C;;
	*) echo "Invalid output terminal \"${GRUB_TERMINAL_OUTPUT}\"" >&2 ; exit 1 ;;
    esac
done

GRUB_ACTUAL_DEFAULT="$GRUB_DEFAULT"

if [ "x${GRUB_ACTUAL_DEFAULT}" = "xsaved" ] ; then GRUB_ACTUAL_DEFAULT="`"${grub_editenv}" - list | sed -n '/^saved_entry=/ s,^saved_entry=,,p'`" ; fi

# We have to be careful here to not break existing password-protected setups.
# The "Authentication and authorization" section of the GRUB manual documents
# a method for setting the superuser password without access level support,
# which relies on the assumption that all boot entries are only usable by the
# superuser by default. Therefore setting the default access control level to
# 0 would break things, because it would make all boot entries unrestricted by
# default. Setting it to 1 ensures that if the user does not define any users
# in GRUB_USERS and does not configure any grub.d scripts to use access levels
# other than 0, all boot entries will require the superuser password to
# access.
if [ "x${GRUB_UNRESTRICTED_ACCESS_LEVEL}" = "x" ] ; then GRUB_UNRESTRICTED_ACCESS_LEVEL="1" ; fi
if [ "x${GRUB_SIMPLE_ACCESS_LEVEL}" = "x" ] ; then GRUB_SIMPLE_ACCESS_LEVEL="0" ; fi
if [ "x${GRUB_ADVANCED_ACCESS_LEVEL}" = "x" ] ; then GRUB_ADVANCED_ACCESS_LEVEL="0" ; fi
if [ "x${GRUB_RECOVERY_ACCESS_LEVEL}" = "x" ] ; then GRUB_RECOVERY_ACCESS_LEVEL="0" ; fi

# Use /etc/SUSE-brand for os label (bsc#1239169)
grub_os_id () (
    VERSION=""
    GRUB_OS_ID=""
    # Source /etc/SUSE-brand only if it defines GRUB_OS_ID
    ! grep -q "^GRUB_OS_ID=" "${sysconfdir}/SUSE-brand" 2>/dev/null || source "${sysconfdir}/SUSE-brand"
    # Check VERSION is 16.0 or newer before applying for backward compatibility
    [ x"`echo -e "${VERSION}\n16.0" | sort -V | head -n1`" = x"16.0" ] || GRUB_OS_ID=""
    echo "$GRUB_OS_ID"
)

if [ x"${GRUB_DISTRIBUTOR}" = x ] ; then
    for i in "${sysconfdir}/os-release" "/usr/lib/os-release" ; do
      if [ -f "$i" ] ; then
        . "$i"
        break
      fi
    done

    OS_ID=`grub_os_id`

    if [ x"${OS_ID}" != x ] ; then
      GRUB_DISTRIBUTOR="${OS_ID}"
    elif [ x"${NAME}" != x ] && [ x"${VERSION}" != x ] ; then
      GRUB_DISTRIBUTOR="${NAME} ${VERSION}"
    else
      GRUB_DISTRIBUTOR="${NAME}"
    fi
fi

# These are defined in this script, export them here so that user can
# override them.
export GRUB_DEVICE \
  GRUB_DEVICE_UUID \
  GRUB_DEVICE_PARTUUID \
  GRUB_DEVICE_BOOT \
  GRUB_DEVICE_BOOT_UUID \
  GRUB_DISABLE_OS_PROBER \
  GRUB_FS \
  GRUB_FONT \
  GRUB_PRELOAD_MODULES \
  GRUB_ACTUAL_DEFAULT

# These are optional, user-defined variables.
export GRUB_DEFAULT \
  GRUB_HIDDEN_TIMEOUT \
  GRUB_HIDDEN_TIMEOUT_QUIET \
  GRUB_TIMEOUT \
  GRUB_TIMEOUT_STYLE \
  GRUB_DEFAULT_BUTTON \
  GRUB_HIDDEN_TIMEOUT_BUTTON \
  GRUB_TIMEOUT_BUTTON \
  GRUB_TIMEOUT_STYLE_BUTTON \
  GRUB_BUTTON_CMOS_ADDRESS \
  GRUB_BUTTON_CMOS_CLEAN \
  GRUB_DISTRIBUTOR \
  GRUB_CMDLINE_LINUX \
  GRUB_CMDLINE_LINUX_DEFAULT \
  GRUB_CMDLINE_LINUX_RECOVERY \
  GRUB_CMDLINE_XEN \
  GRUB_CMDLINE_XEN_DEFAULT \
  GRUB_CMDLINE_LINUX_XEN_REPLACE \
  GRUB_CMDLINE_LINUX_XEN_REPLACE_DEFAULT \
  GRUB_CMDLINE_NETBSD \
  GRUB_CMDLINE_NETBSD_DEFAULT \
  GRUB_CMDLINE_GNUMACH \
  GRUB_TOP_LEVEL \
  GRUB_TOP_LEVEL_XEN \
  GRUB_TOP_LEVEL_OS_PROBER \
  GRUB_EARLY_INITRD_LINUX_CUSTOM \
  GRUB_EARLY_INITRD_LINUX_STOCK \
  GRUB_TERMINAL_INPUT \
  GRUB_TERMINAL_OUTPUT \
  GRUB_SERIAL_COMMAND \
  GRUB_DISABLE_UUID \
  GRUB_DISABLE_LINUX_UUID \
  GRUB_DISABLE_LINUX_PARTUUID \
  GRUB_DISABLE_RECOVERY \
  GRUB_VIDEO_BACKEND \
  GRUB_GFXMODE \
  GRUB_BACKGROUND \
  GRUB_THEME \
  GRUB_GFXPAYLOAD_LINUX \
  GRUB_INIT_TUNE \
  GRUB_SAVEDEFAULT \
  GRUB_ENABLE_CRYPTODISK \
  GRUB_BADRAM \
  GRUB_OS_PROBER_SKIP_LIST \
  GRUB_DISABLE_BOOTNEXT \
  GRUB_DISABLE_SUBMENU \
  GRUB_FORCE_EFI_ALL_VIDEO \
  GRUB_USERS \
  GRUB_UNRESTRICTED_ACCESS_LEVEL \
  GRUB_SIMPLE_ACCESS_LEVEL \
  GRUB_ADVANCED_ACCESS_LEVEL \
  GRUB_RECOVERY_ACCESS_LEVEL \
  GRUB_BOOTNEXT_OMIT_INACTIVE \
  GRUB_DISABLE_MODULE_DEDUPLICATION \
  SUSE_BTRFS_SNAPSHOT_BOOTING \
  SUSE_CMDLINE_XENEFI \
  SUSE_REMOVE_LINUX_ROOT_PARAM

if test "x${grub_cfg}" != "x"; then
  rm -f "${grub_cfg}.new"
  oldumask=$(umask); umask 077
  exec > "${grub_cfg}.new"
  umask $oldumask
fi
gettext "Generating grub configuration file ..." >&2
echo >&2

cat << EOF
#
# DO NOT EDIT THIS FILE
#
# It is automatically generated by $self using templates
# from ${grub_mkconfig_dir} and settings from ${sysconfdir}/default/grub
#
EOF

# Track modules loaded unconditionally at the top level of grub.cfg
# in a state file so that following scripts can omit them.
if [ "x${GRUB_DISABLE_MODULE_DEDUPLICATION}" != "xtrue" ]; then
  if test "x${grub_cfg}" != "x"; then
    GRUB_MODULE_STATE_FILE="${grub_cfg}.modules"
    rm -f "${GRUB_MODULE_STATE_FILE}"
  elif GRUB_MODULE_STATE_FILE="`mktemp "${TMPDIR:-/tmp}/grub-mkconfig-modules.XXXXXXXXXX" 2>/dev/null`"; then
    :
  else
    # Never fall back to a predictable name in a world-writable directory.
    # Without a state file, modules are not deduplicated across scripts.
    unset GRUB_MODULE_STATE_FILE
  fi
  if [ -n "${GRUB_MODULE_STATE_FILE}" ]; then
    export GRUB_MODULE_STATE_FILE
    oldumask=$(umask); umask 077
    > "${GRUB_MODULE_STATE_FILE}"
    umask $oldumask
    trap 'test -n "${GRUB_MODULE_STATE_FILE}" && rm -f "${GRUB_MODULE_STATE_FILE}"' EXIT
    # Some shells (e.g. dash) skip the EXIT trap when killed by a signal.
    # Exit explicitly instead, so the EXIT trap runs and the script stops.
    trap 'exit 129' HUP
    trap 'exit 130' INT
    trap 'exit 131' QUIT
    trap 'exit 143' TERM
  fi
fi


for i in "${grub_mkconfig_dir}"/* ; do
  case "$i" in
    # emacsen backup files. FIXME: support other editors
    *~) ;;
    # emacsen autosave files. FIXME: support other editors
    */\#*\#) ;;
    *)
      if grub_file_is_not_garbage "$i" && test -x "$i" ; then
        echo
        echo "### BEGIN $i ###"
        "$i"
        echo "### END $i ###"
      fi
    ;;
  esac
done

if [ -n "${GRUB_MODULE_STATE_FILE}" ]; then
  rm -f "${GRUB_MODULE_STATE_FILE}"
  unset GRUB_MODULE_STATE_FILE
fi

sync_fs_journal () {
  if test "x$GRUB_DEVICE" = "x$GRUB_DEVICE_BOOT" &&
     test "x$GRUB_FS" = "xxfs" -o "x$GRUB_FS" = "xext2" &&
     test "x${grub_cfg}" != "x" -a "x`make_system_path_relative_to_its_root $grub_cfg`" = "x/boot/grub2/grub.cfg" &&
     test -x /usr/sbin/fsfreeze; then
    /usr/sbin/fsfreeze --freeze / && /usr/sbin/fsfreeze --unfreeze /
  fi
} >&2

if test "x${grub_cfg}" != "x" ; then
  if ! ${grub_script_check} ${grub_cfg}.new; then
    # TRANSLATORS: %s is replaced by filename
    gettext_printf "Syntax errors are detected in generated GRUB config file.
Ensure that there are no errors in /etc/default/grub
and /etc/grub.d/* files or please file a bug report with
%s file attached." "${grub_cfg}.new" >&2
    echo >&2
    exit 1
  else
    # none of the children aborted with error, install the new grub.cfg
    oldumask=$(umask)
    umask 077
    cat ${grub_cfg}.new > ${grub_cfg}
    umask $oldumask
    rm -f ${grub_cfg}.new
    # check if default entry need to be corrected for updated distributor version
    # and/or use fallback entry if default kernel entry removed
    if test -x /usr/sbin/grub2-check-default; then
       /usr/sbin/grub2-check-default >&2
    fi
    sync_fs_journal || true
  fi
fi

gettext "done" >&2
echo >&2
