Package: grub-common Version: 2.02~beta2-22+deb8u1 Scenario: * Say the Debian-derived Linux OS from which "update-grub" is run is "A". * Say another Linux OS on the same hard drive is "B". * Say "B" has multiple kernel versions (when each version was installed, the previous one wasn't deleted).
In this case, GRUB2 makes a submenu for the available kernels in B, plus a main- menu entry for one of those kernels. That is expected. The problem is that, in my testing, the submenu looked like: Ubuntu XX.YY (XX.YY) Ubuntu XX.YY (XX.YY) Ubuntu XX.YY (XX.YY) Ubuntu XX.YY (XX.YY) ... where XX was the same in every menu entry (*including the main-menu entry*), and YY was also the same. There was no way to tell which menu entry would start which kernel version. In fact, I believe the kernel versions were sorted backward, so that the main-menu entry started the *oldest* available kernel version of B, whereas a user would expect the main-menu entry to start the newest available kernel of B. The following patch fixed this in my testing. Please consider adding this patch (or a modified version of it, if bugs are found in this patch) to GRUB2. For my testing, I renamed the os-prober script from 30_os-prober to 15_os-prober, because I wanted the OS boot options to be before memtest86+. In the diff, the name difference indicates the original vs. modified files: 30_os-prober = existing file in Debian package 15_os-prober = my modified file --- 30_os-prober 2015-12-14 12:37:32.000000000 -0500 +++ 15_os-prober 2017-02-23 17:28:47.692140837 -0500 @@ -27,6 +27,9 @@ . "${datarootdir}/grub/grub-mkconfig_lib" +safe_echo () { printf %s\\n "$*" ; } +# from http://www.etalabs.net/sh_tricks.html + found_other_os= adjust_timeout () { @@ -232,6 +235,11 @@ title_correction_code= OS="${LONGNAME}" + LINUXPROBED=$(safe_echo "${LINUXPROBED}" | tr " " "\n" | \ + sort -t ":" -k 4 -rV | tr "\n" " ") + # Try to sort the kernels with the newest first, so the newest one will + # be the default. + for LINUX in ${LINUXPROBED} ; do LROOT="`echo ${LINUX} | cut -d ':' -f 1`" LBOOT="`echo ${LINUX} | cut -d ':' -f 2`" @@ -254,6 +262,68 @@ [ "${prepare_boot_cache}" ] || continue fi +LSB_QUERY_MOUNT_POINT=$(findmnt -flno TARGET ${DEVICE}) + +if [ -z "${LSB_QUERY_MOUNT_POINT}" ] ; then + LSB_QUERY_MOUNT_POINT=$(mktemp -d) + mount ${DEVICE} ${LSB_QUERY_MOUNT_POINT} + TEMP_MOUNT="true" + # It appears that this code doesn't run: Although syslog indicates that + # /usr/lib/os-probes/mounted/40lsb indeed scans unmounted partitions, it + # seems that /usr/bin/os-prober (which is indirectly the parent process of + # "40lsb") still doesn't report any OS that exists on such a partition. The + # partition tested was formatted as btrfs. +else + TEMP_MOUNT="false" +fi + +LSB_DATABASE=${LSB_QUERY_MOUNT_POINT}/etc/lsb-release + +LCODENAME=$(grep ^DISTRIB_CODENAME ${LSB_DATABASE} | cut -d "=" -f 2) +LDESC=$(grep ^DISTRIB_DESCRIPTION ${LSB_DATABASE} | cut -d "=" -f 2) + +if [ ${TEMP_MOUNT} = true ] ; then + # not "x${TEMP_MOUNT}=xtrue", because ${TEMP_MOUNT} is known to have a value + umount ${LSB_QUERY_MOUNT_POINT} +fi + +if safe_echo "${LDESC}" | egrep "^(['\"]).*\1$" >/dev/null; then + LDESC=$(safe_echo "${LDESC}" | cut -c 2- | rev | cut -c 2- | rev) +fi # If ${LDESC} is quoted, then remove the quote marks. + +LKERNEL_DISPLAY=$(safe_echo "${LKERNEL}" | cut -d "-" -f 2-) +# Try to extract the version number of the kernel image file. + +name_to_compare () { echo "${1}" | tr -c "[:alpha:]" " " | tr -s " " | \ + tr "[:upper:]" "[:lower:]" | \ + sed "s/[ ]\+$//" ; } +# Convert all non-letters in ${1} to spaces. +# Convert multiple consecutive spaces to single spaces. +# Convert all uppercase letters to lowercase. +# Then remove trailing spaces if present +# (sed "s/ $//" doesn't seem to work for this). +# We do these things so an LCODENAME of "betsy" will be detected at the end of +# an LDESC of "LMDE 2 (Betsy)", but not in an LDESC of "LMDE blah b_e.t-s.y" + +LDESC_CMP=$(name_to_compare "${LDESC}") +LCODENAME_CMP=$(name_to_compare "${LCODENAME}") + +if safe_echo "${LDESC_CMP}" | egrep "${LCODENAME_CMP}$" >/dev/null; then + # ${LDESC} ends in ${LCODENAME}. + OS_SUBMENU="${LDESC}" +else + # ${LDESC} doesn't end in ${LCODENAME}. + OS_SUBMENU="${LDESC} \"${LCODENAME}\"" +fi + +OS="${OS_SUBMENU}, with Linux kernel ${LKERNEL_DISPLAY}" +# Examples: +# LMDE 2 Betsy, with Linux kernel 3.16.0-4-amd64 +# (if codename is "betsy" & description ends in "Betsy") +# Ubuntu 13.04 "raring", with Linux kernel 3.8.0-35-generic + +printf "Adding '%s' to menu\n" "${OS}" >&2 + found_other_os=1 onstr="$(gettext_printf "(on %s)" "${DEVICE}")" recovery_params="$(echo "${LPARAMS}" | grep 'single\|recovery')" || true @@ -283,10 +353,11 @@ cat << EOF } EOF - echo "submenu '$(gettext_printf "Advanced options for %s" "${OS} $onstr" | grub_quote)' \$menuentry_id_option 'osprober-gnulinux-advanced-$boot_device_id' {" + echo "submenu '$(gettext_printf "Advanced options for %s" "${OS_SUBMENU} $onstr" | grub_quote)' \$menuentry_id_option 'osprober-gnulinux-advanced-$boot_device_id' {" + is_top_level=false fi - title="${LLABEL} $onstr" + title="${OS} $onstr" cat << EOF menuentry '$(echo "$title" | grub_quote)' --class gnu-linux --class gnu --class os \$menuentry_id_option 'osprober-gnulinux-$LKERNEL-${recovery_params}-$boot_device_id' { EOF Thanks for considering this. -dg1727