update-kernel/analyze-kmodules
2022-04-10 09:11:13 +03:00

153 lines
3.7 KiB
Bash
Executable File

#!/bin/bash -efu
#
# Compare modules of some kernel with the booted kernel
#
# Copyright (C) 2022 Vitaly Chikunov <vt@altlinux.org>
#
# This program 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 2 of the License, or (at your option) any later
# version.
#
# This program 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
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
export LC_ALL=C
PROG=$(basename "$0")
message() {
echo >&2 "$PROG: $*"
}
fatal() {
message "$@"
exit 1
}
for opt do
shift
case "$opt" in
--all) ALL=y ;;
--kver=*) KVER=$(basename "${opt#*=}") ;;
--kallsyms) KALLSYM=y ;;
--lsmod) LSMOD=y ;;
--sys-all) SYS=all ;;
--sys-drv) SYS=drivers ;;
--sys) SYS=filtered ;;
-v|--verbose) VERBOSE=1 ;;
*) message "Unknown opeion '$opt'" ;;
esac
done
if [ ! -v KVER ]; then
vmlinuz=$(readlink /boot/vmlinuz)
KVER=${vmlinuz#vmlinuz-}
fi
KFLV=${KVER#*-}
KFLV=${KFLV%-*}
BOOTEDVER=$(uname -r)
BOOTFLV=${BOOTEDVER#*-}
BOOTFLV=${BOOTFLV%-*}
if [ ! -d "/lib/modules/$KVER" ]; then
fatal "Kernel $KVER not found."
else
M="/lib/modules/$KVER"
fi
if [ ! -v LSMOD ] && [ ! -v KALLSYM ] && [ ! -v SYS ]; then
if [ -d /sys/module ]; then
SYS=drivers
elif [ -r /proc/modules ]; then
LSMOD=y
fi
fi
if [ -v SYS ]; then
# Contains built-in modules (which is better than lsmod), but also some
# non-modules, such as kernel, block, xen.
if [ "$SYS" != "drivers" ]; then
MODULES=$(cd /sys/module; ls)
else
# shellcheck disable=SC2046
MODULES=$(dirname $(cd /sys/module; set +f; ls -d -- */drivers))
fi
elif [ -v LSMOD ]; then
MODULES=$(cut -d' ' -f1 /proc/modules | sort)
elif [ -v KALLSYM ]; then
# kallsyms is almost the same as lsmod excluding built-in modules.
# (But contains 'bpf' for some reason.)
MODULES=$(grep -o '\[.*\]' /proc/kallsyms | sort -u | tr -d "[]")
fi
if [ ! -v MODULES ]; then
fatal "Cannot determine list of loaded modules."
fi
echo "Checking for modules of booted kernel ($BOOTEDVER) in $KVER"
[ -v ALL ] && header= || header="Following modules are not found:"
# Some are packaged in separate packages (such as drm), these modules
# still present in modules.* database.
installable() {
local m=$1 ord pkg
local mm=${m//[-_]/[-_]}
ord=$(grep "/$mm\.ko" "$M/modules.order")
if [ -n "$ord" ]; then
pkg=$(rpm -qf "/lib/modules/$BOOTEDVER/$ord" 2>/dev/null)
if [ -n "$pkg" ]; then
pkg=${pkg%-"$BOOTFLV"-*}
pkg+="-$KFLV"
echo "(try installing $pkg)"
else
echo "(can be installed)"
fi
fi
}
notfound=
for m in $MODULES; do
# shellcheck disable=SC2012
fnum=$(ls "/sys/module/$m" 2>/dev/null | wc -l)
if [ "${SYS-}" = "filtered" ]; then
[ "$fnum" -le 2 ] && continue
fi
if [ -v VERBOSE ]; then
fnum_text="[$fnum]"
else
fnum_text=
fi
# Perhaps, "drivers" are peripherals and more important than other modules.
# shellcheck disable=SC2086
[ -d "/sys/module/$m/drivers" ] && drv="(driver)" || drv=
if ! modinfo -k "$KVER" "$m" &>/dev/null; then
inst=$(installable "$m")
if [ -n "$header" ]; then
echo "$header"
header=
fi
# shellcheck disable=SC2086
echo " - $m" $drv $fnum_text $inst
notfound=1
elif [ -v ALL ]; then
# shellcheck disable=SC2086
echo " + $m" $drv $fnum_text
fi
done
if [ -z "$notfound" ]; then
exit 1
else
exit 0
fi