153 lines
3.7 KiB
Bash
Executable File
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
|