File kgr.sh of Package kgraft
#!/bin/bash
# Check kGraft patching status
# Libor Pechacek <lpechacek@suse.com>
unset VERBOSE
function kgr_in_progress() {
for p in /sys/kernel/livepatch/*; do
[ 0$(cat "$p/transition" 2>/dev/null) -ne 0 ] && return 0
done
}
function kgr_poke_processes() {
if [[ $EUID -ne 0 ]]; then
echo "Warning: running as non-root user, only this user's processes will be poked" >&2
fi
for PROC in /proc/[0-9]*; do
if [ 0$(cat $PROC/kgr_in_progress 2>/dev/null) -ne 0 ]; then
PID=$(echo $PROC | cut -d/ -f3)
if [ -n "$VERBOSE" ]; then
echo "sending $PID STOP/CONT"
fi
kill -STOP $PID
# give kernel time to distribute the signal to all threads
sleep .1
kill -CONT $PID
fi
done
}
function kgr_dump_blocking_processes() {
local OPTIND
local THREADS
if [[ $EUID -ne 0 ]]; then
echo "Warning: running as non-root user, display will be limited" >&2
fi
while getopts t opt; do
case $opt in
t) THREADS='yes' ;;
*) echo "Warning: $FUNCNAME: ignoring unknown option \'$opt\'" >&2 ;;
esac
done
unset PIDS
TRANSITIONING_PATCH="$(grep -l '^1$' /sys/kernel/livepatch/*/transition | head -n1)"
if [ -n "$TRANSITIONING_PATCH" ]; then
TRANSITION_DIRECTION=$(cat "${TRANSITIONING_PATCH/%\/transition/\/enabled}")
for PROC in /proc/[0-9]*${THREADS:+/task/[0-9]*}; do
DIR=${PROC%/kgr_in_progress}
PATCH_STATE=$(cat $DIR/patch_state 2>/dev/null)
if [ -n "$PATCH_STATE" ] && [ "$PATCH_STATE" -ge 0 \
-a "$PATCH_STATE" -ne "$TRANSITION_DIRECTION" ]; then
PID=${DIR#/proc/}
PID=${PID%/task/*}
TID=${DIR#*/task/}
if [ -n "$VERBOSE" ]; then
COMM="$(cat $DIR/cmdline 2>/dev/null | tr \\0 \ )"
# fallback to the command name, for example for kernel threads
[ -z "$COMM" ] && COMM="[$(cat $DIR/comm 2>/dev/null | tr \\0 \ )]"
if [ ${VERBOSE:-0} -gt 1 ]; then
STACK=$(cat $DIR/stack 2>/dev/null | sed 's/^/ /')
fi
# don't write out anything in case the process has exited
if [ -e "$DIR" ]; then
echo "$PID ${THREADS:+$TID }$COMM"
[ ${VERBOSE:-0} -gt 1 ] && echo "$STACK"
fi
else
echo $PID ${THREADS:+$TID}
fi
PIDS="$PIDS $PID"
fi
done
fi
if [ -z "$PIDS" -a -n "$VERBOSE" ]; then
echo "no processes with kgr_in_progress set"
fi
}
function kgr_status() {
if kgr_in_progress ; then
echo "in_progress"
else
echo "ready"
fi
}
function kgr_check() {
if kgr_in_progress ; then
echo "Following processes have not finished a previous kGraft patch yet:"
VERBOSE=2 kgr_dump_blocking_processes -t
return 1
fi
}
function kgr_patches() {
unset PATCHES_FOUND
for d in /sys/kernel/livepatch/*; do
[ ! -d "$d" ] && continue
PATCH_NAME=${d#/sys/kernel/livepatch/}
PATCH_MOD=${PATCH_NAME}
echo "${PATCH_MOD}"
if [ -n "$VERBOSE" ]; then
kgr_detailed_patch_info "${PATCH_MOD}" | sed 's/^/ /'
echo
fi
PATCHES_FOUND=1
done
if [ -z "$PATCHES_FOUND" -a -n "$VERBOSE" ]; then
echo "no patch"
fi
}
function kgr_detailed_patch_info() {
REFCNT=$(cat "/sys/module/$1/refcnt")
ACTIVE=$([[ "$REFCNT" -eq 0 ]]; echo $?)
echo "active: ${ACTIVE}"
# srcversion is the link between loaded kernel module and its RPM
SRCVERSION=$(cat "/sys/module/$1/srcversion")
# exit when the module cannot be tracked down
MODPATH=$(/usr/sbin/modinfo -n "$1" 2>/dev/null) || exit
MODSRCVERSION=$(/usr/sbin/modinfo -F srcversion "$1")
if [ "$SRCVERSION" != "$MODSRCVERSION" ]; then
echo "Warning: patch module srcversion does not match the on-disk checksum:" \
"$1 ($SRCVERSION/$MODSRCVERSION)" >&2
exit 1
fi
RPMNAME=$(rpm -qf "${MODPATH}" 2>/dev/null) || exit
echo "RPM: ${RPMNAME}"
REFS=($(rpm -q --changelog "${RPMNAME}" | \
sed 's/^[[:space:]]*KLP:[[:space:]]*\(.*\)/\1/;t b;d;:b s/[[:space:]]/\n/g' | \
sort -ru))
declare -a CVES
declare -a BUGS_FATES
for REF in "${REFS[@]}"; do
if [ ${REF:0:3} = 'CVE' ]; then
CVES+=($REF)
else
BUGS_FATES+=($REF)
fi
done
echo -n "CVE: "
if [ ${#CVES[*]} -gt 0 ]; then
echo ${CVES[*]}
else
echo -n "(none"
[ ${#BUGS_FATES[*]} -eq 0 ] && echo -n " - this is an initial kGraft patch"
echo ")"
fi
echo -n "bug fixes and enhancements: "
if [ ${#BUGS_FATES[*]} -gt 0 ]; then
echo ${BUGS_FATES[*]}
else
echo "(none)"
fi
}
USAGE="Usage: $0 [-h][-v] COMMAND
Query and manipulate kGraft patching status.
Commands:
status: display the overall status of kGraft patching
patches: display the list of loaded patches
blocking: list processes that are preventing kGraft patching
from finishing
blocking_threads: list execution threads that are preventing kGraft
patching from finishing
poke: move forward with the kGraft patching by sending
STOP and CONT signal to the pending processes
Options:
-h print this help
-v more detailed output
Report bugs at https://bugzilla.suse.com/"
PKGVERSION="@@VERSION@@"
while getopts vh-: opt
do
case $opt$OPTARG in
-help|h)
exec echo "$USAGE" ;;
-version)
exec echo "kgr $PKGVERSION" ;;
v) VERBOSE=$((${VERBOSE:-0} + 1)) ;;
*)
echo "$0: try '$0 --help'" >&2; exit 1 ;;
esac
done
shift `expr $OPTIND - 1`
if [ $# -ne 1 ]; then
echo -e "Error: no command provided\n" >&2
echo "$USAGE"
exit 1
fi
case $1 in
blocking) kgr_dump_blocking_processes ;;
blocking_threads) kgr_dump_blocking_processes -t ;;
poke) kgr_poke_processes ;;
status) kgr_status ;;
check) kgr_check ;;
patches) kgr_patches ;;
*) echo "Error: unknown command \`$1'"; exit 1 ;;
esac
# vim: ai sw=4 et sts=4 ft=sh