#!/bin/sh
[ ! "$KERNEL_PACKAGE_SCRIPT_TRACE" ] || set -x

op=${0##*-}

name=""
version=""
release=""
kernelrelease=""
flavor=""
variant=""
image=""
certs=""

while true ; do
    case $1 in
	--name)
	    name="$2"
	    shift
	    ;;
	--version)
	    version="$2"
	    shift
	    ;;
	--release)
	    release="$2"
	    shift
	    ;;

	--kernelrelease)
	    kernelrelease="$2"
	    shift
	    ;;
	--flavor)
	    flavor="$2"
	    shift
	    ;;
	--variant)
	    variant="$2"
	    shift
	    ;;

	--usrmerged)
	    # ignored for legacy compat reasons
	    shift
	    ;;
	--image)
	    image="$2"
	    shift
	    ;;
	--certs)
	    certs="$2"
	    shift
	    ;;

	*) break
	    ;;
    esac
    shift
done

wm2=/usr/lib/module-init-tools/weak-modules2

modules_dir=/usr/lib/modules/$kernelrelease-$flavor
system_map=${modules_dir}/System.map

# During the image creation (KIWI) we can install the kernel before
# there is any bootloader installed.  If we detect that sdbootutil
# package is installed, but the bootloader installation test fails,
# then we assume that sdbootutil will be used.
if [ -e "/usr/bin/sdbootutil" ] && { /usr/bin/sdbootutil is-installed || [ -e /.buildenv ]; } then
    is_sdbootutil=1
fi

trigger_purge_kernels() {
    [ -z "$KERNEL_PACKAGE_SCRIPT_DEBUG" ] || echo Triggering purge-kernels >&2
    touch /boot/do_purge_kernels
}

disarm_purge_kernels() {
    [ -z "$KERNEL_PACKAGE_SCRIPT_DEBUG" ] || echo Disarming purge-kernels >&2
    rm -f /boot/do_purge_kernels
}

run_wm2() {
    [ -z "$KERNEL_PACKAGE_SCRIPT_DEBUG" ] || echo wm2 "$@" >&2
    $wm2 "$@"
}

message_install_bl () {
    echo "You may need to setup and install the boot loader using the"
    echo "available bootloader for your platform (e.g. grub, lilo, zipl, ...)."
}

update_bootloader_entry() {
    if [ ! -e /.buildenv ] ; then
	if [ -n "$is_sdbootutil" ]; then
	    # Transactional systems use the snapper plugins to add new
	    # bootloader entries, and this same hook is used in normal
	    # Tumbleweed installations that uses btrfs.
	    #
	    # For transactional systems we should not mangle with the
	    # /boot inside the transaction, to keep the atomicity
	    # promise.
	    #
	    # For non-transactional ones, we will generate the boot
	    # entries here, ignoring the snapper plugin, so we have a
	    # chance of implementing FDE in case that the system is
	    # not using btrfs.
	    if [ -z "$TRANSACTIONAL_UPDATE" ]; then
		/usr/bin/sdbootutil --image="$image" add-kernel "$kernelrelease-$flavor"
		/usr/bin/sdbootutil set-default-snapshot
	    fi
	elif [ -f /etc/fstab ] ; then
	    # only run the bootloader if the usual bootloader configuration
	    # files are there -- this is different on every architecture
	    initrd=initrd-"$kernelrelease"-"$flavor"
	    if [ "$flavor" = rt ]; then
		default=force-default
	    fi
	    # Note: the 2nd condition is for removing the bootloader
	    # entry for an uninstalled kernel.
	    if [ -e "/boot/$initrd" ] || [ ! -e "$modules_dir" ]; then
		[ -e "/boot/$initrd" ] || initrd=
		if [ -x /usr/lib/bootloader/bootloader_entry ]; then
		    /usr/lib/bootloader/bootloader_entry \
			add \
			"$flavor" \
			"$kernelrelease"-"$flavor" \
			"$image"-"$kernelrelease"-"$flavor" \
			"$initrd" \
			"$default" || script_rc=$?
		else
		    message_install_bl
		fi
	    fi
	else
	    message_install_bl
	fi
    fi
    create_boot_symlinks "$kernelrelease-$flavor"
}

check_space_in_boot() {
    # see bug #259303
    # this script runs when the kernel gets updated with YaST
    # YaST calls rpm always with -U
    # -U replaces all packages with the new one
    # rpm removes the files from the old packages after the postinstall script ran
    # this will double the required space below /boot
    # remove the files from the old packages to make room for the new initrd
    # rpm may complain about low disk space if /boot/vmlinux does not fit
    #
    # When sdbootutil is used, the kernel and initrd should be living
    # in sysroot, and transferred into /boot later by the snapper
    # plugin (that calls sdbootutil add-all-kernels)
    #
    if [ "$YAST_IS_RUNNING" != "" ]; then
	mydf="$( POSIXLY_CORRECT=1 df -P /boot/ | awk '/^(\/|-[[:blank:]])/{ print $4}' )"
	if test "$mydf" != "" ; then
	    echo "Free diskspace below /boot: $mydf blocks"
	    # echo "512 byte blocks: $(( 2 * 1024 * 20 ))"
	    if test "$mydf" -lt  "40960" ; then
		echo "make room for new kernel '$flavor' because there are less than 20MB available."
		# disabled because it breaks patch rpms
		#rm -fv /boot/"$image"-*-"$flavor"
		rm -fv /boot/initrd-*-"$flavor"
	    fi
	fi
    fi
}

copy_or_link_legacy_files() {
    # compat stuff for /boot.
    # if /boot and /usr are not separate partitions we can just link
    # the kernel there to save space. Otherwise copy.
    if mountpoint -q /boot || mountpoint -q /usr; then
	copy_or_link="cp -a --remove-destination"
	separate_boot='1'
    else
	copy_or_link="ln -sf"
	separate_boot=""
    fi

    for x in "$image" sysctl.conf System.map config; do
	if [ "$separate_boot" = 1 ] || [ ! -e "/boot/$x-$kernelrelease-$flavor" ]; then
	    $copy_or_link "..$modules_dir/$x" "/boot/$x-$kernelrelease-$flavor" || script_rc=$?
	    if [ -e "$modules_dir/.$x.hmac" ]; then
		$copy_or_link "..$modules_dir/.$x.hmac" "/boot/.$x-$kernelrelease-$flavor".hmac || script_rc=$?
	    fi
	fi
    done
}

# In build environments like kiwi, we need to create the symlinks.
# kiwi will create the missing initrd a later stage in the boot process.
is_build_environment() {
    [ -e /.buildenv ] || [ -e /image/config.xml ]
}

create_boot_symlinks() {
    rel_flav=$1
    broken=

    [ -n "$is_sdbootutil" ] && return
    for x in /boot/"$image" /boot/initrd; do
	[ -f "$x-$rel_flav" ] || broken=yes
    done
    if [ "$broken" ] && ! is_build_environment; then
	echo "ERROR: cannot create symlinks /boot/$image and /boot/initrd" >&2
    else
	for x in /boot/"$image" /boot/initrd; do
	    rm -f "$x"
	    ln -s "${x##*/}-$rel_flav" "$x"
	done
    fi
    rm -f /boot/.vmlinuz.hmac
    [ ! -e "/boot/.vmlinuz-$rel_flav.hmac" ] ||
	ln -s ".vmlinuz-$rel_flav.hmac" /boot/.vmlinuz.hmac
}

find_latest_kernel() {
    # shellcheck disable=SC2012
    ls -rv /boot/initrd-* 2>/dev/null | \
	while read -r _x; do
	    [ -f "$_x" ] || continue
	    _rel=${_x#/boot/initrd-}
	    [ -f "/boot/$image-$_rel" ] || continue
	    echo "$_rel"
	    return
	done
}

check_arm_pagesize() {
    # On AArch64 we switched from 64k PAGE_SIZE to 4k PAGE_SIZE. Unfortunately
    # btrfs can only use file systems created with the same PAGE_SIZE. So we
    # check if the user has any btrfs file systems mounted and refuse to install
    # in that case.
    # FORCE_4K: The user knows what he's doing, let him be.
    # YAST_IS_RUNNING: We're probably test installing the kernel, that should succeed
    if [ "$flavor" != default ] || [ "$( uname -m )" != aarch64 ] || \
	   [ "$FORCE_4K" = 1 ] || [ "$YAST_IS_RUNNING" = instsys ] || \
	   ! zgrep -q CONFIG_ARM64_64K_PAGES=y /proc/config.gz; then
	return
    fi
    cat >&2 <<-EOF
You are running on a 64kb PAGE_SIZE kernel. The default kernel
switched to 4kb PAGE_SIZE which will prevent it from mounting btrfs
or the swap partition.

To ensure that your system still works, I am refusing to install
this kernel. If you want to force installation regardlesss, reinstall
with the environment variable FORCE_4K set to 1.

To stay with a 64kb PAGE_SIZE kernel, please follow these steps:

        $ zypper in kernel-64kb
        [ reboot into the new kernel ]
        $ zypper rm kernel-default

You will then be on the 64kb PAGE_SIZE kernel and can update your
system normally.
EOF
    script_rc=1
}

run_cert_script() {
    [ -z "$certs" ] || \
	"/usr/lib/module-init-tools/kernel-scriptlets/cert-$op" \
	    --ca-check 1 --certs "$certs" "$@" || \
	script_rc=$?
}

[ -z "$KERNEL_PACKAGE_SCRIPT_DEBUG" ] || \
    echo "$op" name: "$name" version: "$version" release: "$release" \
    kernelrelease: "$kernelrelease" flavor: "$flavor" variant: "$variant" \
    image: "$image" certs: "$certs" -- "$@" >&2

script_rc=0

case $op in
    pre)
	[ -n "$is_sdbootutil" ] || check_space_in_boot
	check_arm_pagesize
	run_cert_script "$@"
	;;
    post)
	# Flag to trigger /etc/init.d/purge-kernels on next reboot (fate#312018)
	# ... but avoid the first installation (bsc#1180058)
	if [ "$1" -gt 1 ]; then
	    trigger_purge_kernels || script_rc=$?
	fi

	[ -n "$is_sdbootutil" ] || copy_or_link_legacy_files

	# Add symlinks of compatible modules to /lib/modules/$krel/weak-updates/,
	# run depmod and dracut
	if [ -x $wm2 ]; then
	    run_wm2 --add-kernel "$kernelrelease"-"$flavor" || script_rc=$?
	else
	    echo "$wm2 does not exist, please run depmod and dracut/mkosi-initrd manually" >&2
	    script_rc=1
	fi

	[ "$INITRD_IN_POSTTRANS" ] || update_bootloader_entry
	run_cert_script "$@"
	;;
    preun)
	[ ! -L /boot/.vmlinuz.hmac ] ||
	    [ "$(readlink /boot/.vmlinuz.hmac)" != ".vmlinuz-$kernelrelease-$flavor.hmac" ] ||
	    rm -f /boot/.vmlinuz.hmac
	run_cert_script "$@"
	;;
    postun)
	# If a kernel package is removed before the next reboot, we assume that the
	# multiversion variable in /etc/zypp/zypp.conf is not configured and we delete
	# the flag again (fate#312018)
	disarm_purge_kernels

	if [ -e "$system_map" ]; then
	    # the same package was reinstalled or just rebuilt, otherwise the files
	    # would have been deleted by now
	    # do not remove anything in this case (bnc#533766)
	    exit 0
	fi

	# Remove symlinks from "$modules_dir"/weak-updates/.
	if [ -x $wm2 ]; then
	    run_wm2 --remove-kernel "$kernelrelease"-"$flavor"
	fi

	# can't check $1 as kernel is usually multiversion. So need to check if
	# that particular kernel was actually removed from disk.
	if [ -n "$is_sdbootutil" ] && [ ! -e /.buildenv ] \
	    && [ ! -e /lib/modules/"$kernelrelease-$flavor/$image" ]; then
	    if [ -z "$TRANSACTIONAL_UPDATE" ]; then
		/usr/bin/sdbootutil --image="$image" remove-kernel "$kernelrelease-$flavor"
	    fi
	# remove fstab check once perl-Bootloader can cope with it
	elif [ -f /etc/fstab ]; then
	    if [ -x /usr/lib/bootloader/bootloader_entry ]; then
		/usr/lib/bootloader/bootloader_entry \
		    remove \
		    "$flavor" \
		    "$kernelrelease"-"$flavor" \
		    "$image"-"$kernelrelease"-"$flavor" \
		    initrd-"$kernelrelease"-"$flavor"
	    fi
	    if [ -L /boot/initrd ] && \
		   { [ ! -f /boot/initrd ] || \
			 [ "$(readlink /boot/initrd)" = "initrd-$kernelrelease-$flavor" ]; }; then
		rm -f /boot/initrd "/boot/$image"
	    fi
	    if [ ! -f /boot/initrd ] || [ ! -f "/boot/$image" ]; then
		latest=$(find_latest_kernel)
		if [ "$latest" ]; then
		    create_boot_symlinks "$latest"
		else
		    echo "WARNING: no installed kernel found after deinstallation of $kernelrelease-$flavor" >&2
		fi
	    fi
	fi

	run_cert_script "$@"
	;;
    posttrans)
	if [ -x /usr/lib/module-init-tools/regenerate-initrd-posttrans ]; then
	    /bin/bash -c 'set +e; /usr/lib/module-init-tools/regenerate-initrd-posttrans' || script_rc=$?
	fi
	[ ! "$INITRD_IN_POSTTRANS" ] || update_bootloader_entry
	;;
    *)
	echo Unknown scriptlet "$op" >&2
	exit 255
	;;
esac

exit $script_rc

# vim: set sts=4 sw=4 ts=8 noet:
