diff --git a/Dockerfile b/Dockerfile index d79f198..5a41cd7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,4 +46,6 @@ RUN toilet Upgrading && \ update-kernel -v -l && \ ls -la /boot +COPY analyze-kmodules /usr/sbin/ + # There now should be two kernels. diff --git a/Makefile b/Makefile index 39f8a56..7fa7185 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,8 @@ check: bash -n update-kernel bash -n remove-old-kernels - shellcheck -x update-kernel remove-old-kernels + bash -n analyze-kmodules + shellcheck -x update-kernel remove-old-kernels analyze-kmodules docker-build: docker build . diff --git a/analyze-kmodules b/analyze-kmodules new file mode 100755 index 0000000..318d006 --- /dev/null +++ b/analyze-kmodules @@ -0,0 +1,152 @@ +#!/bin/bash -efu +# +# Compare modules of some kernel with the booted kernel +# +# Copyright (C) 2022 Vitaly Chikunov +# +# 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 diff --git a/update-kernel.spec b/update-kernel.spec index 856a0e6..dfe7df6 100644 --- a/update-kernel.spec +++ b/update-kernel.spec @@ -38,6 +38,7 @@ http://lists.altlinux.org/pipermail/sisyphus/2006-November/192226.html mkdir -p %buildroot%_sbindir install -pm755 update-kernel %buildroot%_sbindir/ install -pm755 remove-old-kernels %buildroot%_sbindir/ +install -pm755 analyze-kmodules %buildroot%_sbindir/ %check make check @@ -45,6 +46,7 @@ make check %files %_sbindir/update-kernel %_sbindir/remove-old-kernels +%_sbindir/analyze-kmodules %changelog * Sun Apr 10 2022 Vitaly Chikunov 1.1.1-alt1