Bug#855976: [Patch] In os-prober script in /etc/grub.d/, better detection of other Linux OS partitions' codename & kernel version

dg1727 dg1727 at protonmail.com
Thu Feb 23 23:00:46 UTC 2017


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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.alioth.debian.org/pipermail/pkg-grub-devel/attachments/20170223/ddbdbcbb/attachment-0001.html>


More information about the Pkg-grub-devel mailing list