Last active
May 31, 2024 20:48
-
-
Save anonyco/ec7c1d69b2ddde23fc1bdade017a0ad0 to your computer and use it in GitHub Desktop.
Shellscript grub.d hook to automatically fixup /boot selected kernels for vmlinuz and initrd.img. Primarily targets ubuntu/derivatives but should work anywhere that uses grub.d
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
# DESCRIPTION: fixes up /boot selected kernels for vmlinuz and initrd.img | |
# | |
# INSTALLATION: sudo wget 'https://gist.githubusercontent.com/anonyco/ec7c1d69b2ddde23fc1bdade017a0ad0/raw' \ | |
# -O/etc/grub.d/01_fixup_boot_kernel && sudo chmod +x /etc/grub.d/01_fixup_boot_kernel | |
# | |
# ERROR HANDLING: Run this program with peace of mind knowing that any serious/critical error (that would likely bork | |
# your computer if you were to reboot) will alert you with a graphical popup error dialog to catch your attention: | |
# * IF either no initrd.img or no vmlinuz was found. This could happen if your system uses a different model | |
# and is most likely indicative of this script being incompatible with your system's boot setup | |
# * IF there's a version mismatch between the latest initrd.img/vmlinuz. This can happen, for example, if the | |
# there's no disk space left on /boot and only of these is successfully copied to /boot by update-initramfs | |
# * BOTH these errors will appear no matter what! Even if you run update-grub as a background automated task, | |
# this script will seek out and hook into whatever graphical display it can find and show an error box on it | |
# | |
# BEHAVIOR: | |
# 1. Chooses the latest available kernel version for vmlinuz and initrd.img | |
# 2. IF you are current running an older kernel than the latest, then this script makes sure vmlinuz.old and | |
# initrd.img.old point to the ones for your current running kernel (so you have a solid working backup option.) | |
# 3. IF vmlinuz.old and initrd.img.old are left as-is IF they are not at the latest kernel AND are not dead links. | |
# 4. Otherwise, vmlinuz.old and initrd.img.old are autoset to the oldest available vmlinuz and initrd.img.old | |
# 5. All checks make sure the version of vmlinuz.old and initrd.img.old are matching versions and non-zero size and | |
# you won't be notified if the versions don't match; this script will just skip choosing that version and search | |
# for the next working verison-matching non-zero-size vmlinuz and initrd.img pair to use. | |
# | |
# SPDX-License-Id: GPL-3.0-only | |
OLDDIR="$(pwd)" | |
if cd /boot 2>/dev/null ; then | |
OLDIFS="$IFS" | |
oldestinitrd="" | |
newestinitrd="" | |
oldestvmlinuz="" | |
newestvmlinuz="" | |
thisscript="$0" | |
runAsUserSourceFromPID() ( | |
thepid="$1" | |
theuser="$2" | |
thegroup="$3" | |
shift 3 | |
if cdtopwd="$(/usr/bin/grep -m1 -zahe PWD= "/proc/$thepid/environ" 2>/dev/null)" ; then | |
cd -- "${cdtopwd#"PWD="}" 2>/dev/null || true | |
elif test -d "/home/$theuser" ; then | |
cd -- "/home/$theuser" 2>/dev/null || true | |
elif test -d "/home/$thegroup" ; then | |
cd -- "/home/$thegroup" 2>/dev/null || true | |
fi | |
# shellcheck disable=SC2016 | |
cdtopwd='$cdtopwd' | |
sudo -i -A -u "$theuser" -g "$thegroup" <<EOL | |
cdtopwd='' | |
$( | |
# shellcheck disable=SC2016 | |
env -i xargs -r0a"/proc/$thepid/environ" sh -c 'export "$@"; test "$PWD" != "/boot" && export cdtopwd="$PWD"; export -p' -- | |
) | |
test -z "$cdtopwd" || cd -- "$cdtopwd" | |
$( | |
IFS="'" | |
for a in "$@"; do | |
a="$a " | |
# shellcheck disable=SC2086 | |
o="$(printf "%s'\\\''" $a)" | |
printf "'%s' " "${o%" '\\''"}" | |
done | |
) | |
EOL | |
) | |
showCriticalMessageGUI() ( | |
msg="FATAL ERROR WHILST GENERATING GRUB: $thisscript: $1! System might be borked and you should investigate this before restarting!" | |
wall "$msg" >/dev/null 2>&1 || true | |
# First, we only search non-root seated session leaders. Then, we exaustively search the non-session-leaders for any x processes who might have our needed variables | |
for psargs in -dNo -eo; do | |
IFS=' | |
' | |
# shellcheck disable=SC2009 | |
for l in $(ps "$psargs" pid,euid,seat,euser,egroup 2>/dev/null | grep -ve ' 0 ' -e ' - ' -e ' EUSER '); do | |
IFS=' ' | |
# shellcheck disable=SC2086 | |
set -- $l | |
if test "x$*" = "x" ; then | |
continue | |
fi | |
# notice the "-s" flag, which suppresses outputting any read errors, which will happen for processes we dont have permission to read their environment of | |
if grep -asqe 'XAUTHORITY=/' "/proc/$1/environ" >/dev/null 2>&1 ; then | |
#eval "$(printf "sudo -u '$2' -g '$3' /usr/bin/env "; xargs -0a"/proc/$1/environ" /usr/bin/printf "%q "; printf "-- gnome-terminal")" | |
#if test "x' '" = "x$(/usr/bin/printf "%q" " " 2>/dev/null)"; then | |
# printf "%s\n" eval "$(xargs -0a"/proc/$1/environ" /usr/bin/printf "%q\n")" | |
#else | |
# printf "%s\n" eval "$(xargs -r0a"/proc/$1/environ" sh -c 'IFS="'\''"; for a in "$@"; do o="$(printf "%s'"'\\\''"'" $a)"; printf "'\''%s'\''\n" "${o%"'"'\\\''"'"}"; done' --)" | |
#fi | |
#eval "$(env -i xargs -r0a"/proc/$1/environ" sh -c 'export "$@"; test "$PWD" != "/boot" && export cdtopwd="$PWD"; export -p' --)" | |
#test -z "$cdtopwd" || cd -- "$cdtopwd" | |
#/bin/bash -i | |
#runAsUserSourceFromPID "$1" "$4" "$5" /usr/bin/gnome-terminal | |
runAsUserSourceFromPID "$1" "$4" "$5" xmessage -default ok -geometry 300x200 -nearmouse "$(printf "%s\n" "$msg" | fold -sw 36)" | |
return 0 | |
fi | |
done | |
done | |
) | |
IFS=' | |
' | |
for file in $( printf "%s\n" initrd.img-* vmlinuz-* | sort -nt- -k2,2 ) | |
do | |
test -s "$file" || continue | |
if test "${file#vmlinuz-}" = "$file" ; then | |
oldestinitrd="${oldestinitrd:-$file}" | |
newestinitrd="$file" | |
else | |
oldestvmlinuz="${oldestinitrd:-$file}" | |
newestvmlinuz="$file" | |
fi | |
done | |
IFS="$(printf "\t\n ")" | |
if test "/$newestinitrd" = "/" -o "/$newestvmlinuz" = "/" ; then | |
xmessage "FATAL ERROR WHILST GENERATING GRUB: $thisscript: could not find vmlinuz or initrd in /boot! System might be borked and you should investigate this before restarting!" -default ok -geometry 300x200 -nearmouse | |
exit 1 | |
fi | |
if test "${newestinitrd#initrd.img-}" != "${newestvmlinuz#vmlinuz-}" ; then | |
xmessage "FATAL ERROR WHILST GENERATING GRUB: $thisscript: version mismatch between $newestvmlinuz and $newestinitrd in /boot! System might be borked and you should investigate this before restarting!" -default ok -geometry 300x200 -nearmouse | |
exit 1 | |
fi | |
#ln -sfT "$newestinitrd" initrd.img | |
echo "[INFO] $thisscript: linked /boot/initrd.img -> $newestinitrd" >&2 | |
#ln -sfT "$newestvmlinuz" vmlinuz | |
echo "[INFO] $thisscript: linked /boot/vmlinuz -> $newestvmlinuz" >&2 | |
alreadyerrored=0 | |
if test "${oldestinitrd#initrd.img-}" != "${oldestvmlinuz#vmlinuz-}" ; then | |
if test -s "vmlinuz-${oldestinitrd#initrd.img-}" ; then | |
oldestvmlinuz="vmlinuz-${oldestinitrd#initrd.img-}" | |
elif test -s "initrd.img-${oldestvmlinuz#vmlinuz-}" ; then | |
oldestinitrd="initrd.img-${oldestvmlinuz#vmlinuz-}" | |
else | |
alreadyerrored=1 | |
echo "[WARN] $thisscript: could not match old vmlinuz and initrd.img versions $oldestinitrd and $oldestvmlinuz as neither verisons have eachother. The old may be unavailable and this should be looked into." 1>&2 | |
fi | |
fi | |
didMatchRepl=n | |
if runningkernelver="$(uname -r >/dev/null 2>&1)" && test "initrd.img-$runningkernelver" != "$newestinitrd" | |
then | |
for file in initrd.img-*"$runningkernelver" vmlinuz-*"$runningkernelver" | |
do | |
test -s "$file" -a "$file" != "$newestvmlinuz" -a "$file" != "$newestinitrd" || continue | |
if test "${file#vmlinuz}" = "$file" -a -s "vmlinuz-${file#initrd.img-}" ; then | |
oldestinitrd="$file" | |
oldestvmlinuz="vmlinuz-${file#initrd.img-}" | |
didMatchRepl=y | |
break | |
elif test -s "initrd.img-${file#vmlinuz-}" ; then | |
oldestinitrd="initrd.img-${file#vmlinuz-}" | |
oldestvmlinuz="$file" | |
didMatchRepl=y | |
break | |
fi | |
done | |
test "$didMatchRepl" = "n" && echo "[WARN] $thisscript: the currently running kernel version does not exist in /boot. This shouldnt be an issue as theres another kernel to boot but proceed carefully." 1>&2 | |
fi | |
if test "$alreadyerrored" -eq 1 | |
then | |
: ; | |
elif test "$didMatchRepl" = "n" -a -s "$(readlink initrd.img.old >/dev/null 2>&1)" -a -s "$(readlink vmlinuz.old >/dev/null 2>&1)" -a "$newestinitrd" != "$(basename "$(readlink initrd.img.old >/dev/null 2>&1)" >/dev/null 2>&1)" | |
then | |
echo "[INFO] $thisscript: the existing initrd.img.old and vmlinuz.old are fine as-is and left unmodified" 1>&2 | |
elif test "$oldestinitrd" != "$newestinitrd" | |
then | |
#oldinitrdlink="$(basename "$(readlink initrd.img.old 2>/dev/null)" 2>/dev/null)" | |
#if test "/$oldinitrdlink" = "/" -o "/$oldinitrdlink" = "/$newestinitrd" -o \! -s "$oldinitrdlink" | |
#then | |
#ln -sfT "$oldestinitrd" initrd.img.old | |
echo "[INFO] $thisscript: linked /boot/initrd.img.old -> $oldestinitrd" >&2 | |
#ln -sfT "$oldestvmlinuz" vmlinuz.old | |
echo "[INFO] $thisscript: linked /boot/vmlinuz.old -> $oldestvmlinuz" >&2 | |
#fi | |
else | |
echo "[WARN] $thisscript: could not update vmlinuz.old and initrd.img.old as the oldest found versions ($oldestinitrd and $oldestvmlinuz) either were the same as the latest version or they mismatched" 1>&2 | |
fi | |
cd -- "$OLDDIR" 2>/dev/null || true | |
IFS="$OLDIFS" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment