#!/bin/bash

USAGE="$0 <check|install|remove> <package-version-release>"

if test "$1" = "-h" -o "$1" = "--help"; then
	echo "$USAGE"
	exit 0
fi
if test "$#" -lt 2; then
	echo "$USAGE" >&2
	exit 1
fi

shopt -s nullglob

do_check()
{
	local dir pid pids=() status counter
	local proc_mounted=false kgr_supported=false read_ok=true

	if test -e /.buildenv; then
		echo "Skipping kGraft patches in buildroot"
		return 0
	fi
	if test "$(uname -r)" != "$KREL"; then
		return 0
	fi
	for dir in /proc/*; do
		proc_mounted=true
		pid=${dir##*/}
		case "$pid" in
		*[^0-9]*)
			continue
		esac
		if test ! -f "$dir/kgr_in_progress"; then
			continue
		fi
		kgr_supported=true
		status=$(< "$dir/kgr_in_progress")
		if test $? -ne 0; then
			read_ok=false
			continue
		fi
		if test "$status" -ne 0; then
			pids[${#pids[@]}]=$pid
		fi
	done
	if ! $proc_mounted; then
		echo "Warning: /proc is not mounted" >&2
		return 0
	fi
	if ! $kgr_supported; then
		echo "Warning: the running kernel does not have kGraft support" >&2
		return 0
	fi
	if ! $read_ok; then
		echo "Warning: could not read some files in /proc"
	fi
	if test ${#pids[@]} -ne 0; then
		echo "Error: following processes have not finished a previous kGraft patch yet:" >&2
		echo "${pids[@]}" >&2
		return 1
	fi
	status=$(< /sys/kernel/kgraft/in_progress)
	if test "$status" != "1"; then
		return 0
	fi
	echo "Waiting for the global kGraft flag to be cleared (/sys/kernel/kgraft/in_progress)" >&2
	counter=0
	while sleep 1; do
		status=$(< /sys/kernel/kgraft/in_progress)
		if test "$status" != "1"; then
			return 0
		fi
		let counter++
		if test $counter -gt 60; then
			echo "Giving up" >&2
			return 1
		fi
	done
}

package_krel()
{
	local package=$1 n krel

	n=${package%-*}
	n=${n%-*}
	krel=${n#kgraft-patch-}
	krel=${krel//_/.}

	echo "$krel"
}

refresh_initrd()
{
	local image

	/sbin/depmod -F "/boot/System.map-$KREL" -e "$KREL" || return
	# copied from weak-modules2
	for image in vmlinuz image vmlinux linux bzImage uImage Image ""; do
		if test -f "/boot/$image-$KREL"; then
			break
		fi
	done
	if test -z "$image"; then
		return
	fi
	if test "$1" = "--force"; then
		/sbin/mkinitrd -k "/boot/$image-$KREL" -i "/boot/initrd-$KREL"
	else
		mkdir -p /var/run/regenerate-initrd
		touch "/var/run/regenerate-initrd/$image-$KREL"
	fi
}

do_install()
{
	local mod modules err

	if test -e /.buildenv; then
		return 0
	fi

	refresh_initrd
	if test "$(uname -r)" != "$KREL"; then
		return 0
	fi
	err=0
	modules=($(grep -l 0 /sys/module/kgraft[-_]patch*/refcnt /dev/null | sed 's:/refcnt::; s:/sys/module/::'))
	for mod in "${modules[@]}"; do
		echo "[kGraft] Unloading $mod"
		# Can't use modprobe -r, as the modules do not exist on disk
		# anymore
		rmmod "$mod" || :
	done
	modules=($(rpm -ql "$PACKAGE" | sed -rn 's:.*/(kgraft-patch[^/]*)\.ko$:\1:p'))
	for mod in "${modules[@]}"; do
		echo "[kGraft] Loading $mod"
		modprobe "$mod" || err=$?
	done
	return $err
}

do_remove()
{
	if test -e /.buildenv; then
		return 0
	fi

	if test "$NUM_PACKAGES" -eq 0; then
		# bnc#904867
		refresh_initrd --force
	else
		refresh_initrd
	fi
}

cmd=$1
PACKAGE=$2
KREL=$(package_krel "$PACKAGE")
NUM_PACKAGES=${3-0}
case "$cmd" in
check|install|remove)
	do_$cmd
	exit
	;;
*)
	echo "$USAGE" >&2
	exit 1
esac
