#!/bin/bash
#
# Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.

set -x

prepare-parameters()
{
	eval `grep LOADER_TYPE= /etc/sysconfig/bootloader`

	if [ x"$LOADER_TYPE" = "xgrub2" -o x"$LOADER_TYPE" = "xgrub2-efi" ]; then
		GRUBONCE="/usr/sbin/grub2-once"
		GRUBDEFAULT="/boot/grub2/grubenv"
		GRUB2EDITENV="/usr/bin/grub2-editenv"
		GRUB2CONF="/boot/grub2/grub.cfg"
		BLKID="/usr/sbin/blkid"
		getkernels="getkernels-grub2"
	fi
}

#####################################################################
# gets a list of available kernels from /boot/grub2/grub.cfg
# kernels are in the array $KERNELS, output to stdout to be eval-ed.
getkernels-grub2()
{
	local I DUMMY MNT ROOTDEV
	declare -i I=0 J=-1

	# we need the root partition later to decide if this is the kernel to select
	while read ROOTDEV MNT DUMMY; do
	    [ "$ROOTDEV" = "rootfs" ] && continue # not what we are searching for
	    if [ "$MNT" = "/" ]; then
		break
	    fi
	done < /proc/mounts

	while read LINE; do
	    case $LINE in
	    menuentry\ *)
		let J++
		;;
	    set\ default*)
	        local DEFAULT=${LINE#*default=}

		if echo $DEFAULT | grep -q saved_entry ; then
		    local SAVED=`$GRUB2EDITENV $GRUBDEFAULT list | sed -n s/^saved_entry=//p`
		    if [ -n "$SAVED" ]; then
			DEFAULT_BOOT=$($GRUBONCE --show-mapped "$SAVED")
		    fi
		fi

                ;;
	    linux*noresume*|module*xen*noresume*)
		echo "  Skipping grub entry #${J}, because it has the noresume option" >&2
		;;
	    linux*root=*|module*xen*root=*)
		local ROOT
		ROOT=${LINE#*root=}
		DUMMY=($ROOT)
		ROOT=${DUMMY[0]}

		if [ x"${ROOT:0:5}" = "xUUID=" ]; then
		    UUID=${ROOT#UUID=}
		    if [ -n "$UUID" ]; then
			ROOT=$($BLKID -U $UUID)
		    fi
		fi

		if [ "$(stat -Lc '%t:%T' $ROOT)" != "$(stat -Lc '%t:%T' $ROOTDEV)" ]; then
			echo "  Skipping grub entry #${J}, because its root= parameter ($ROOT)" >&2
			echo "    does not match the current root device ($ROOTDEV)." >&2
			continue
		fi
		DUMMY=($LINE) # kernel (hd0,1)/boot/vmlinuz-ABC root=/dev/hda2
		echo "KERNELS[$I]='${DUMMY[1]##*/}'" # vmlinuz-ABC
		echo "MENU_ENTRIES[$I]=$J"
		# DEBUG "Found kernel entry #${I}: '${DUMMY[1]##*/}'" INFO
		let I++
		;;
	    linux*|module*xen*)
		# a kernel without "root="? We better skip that one...
		echo "  Skipping grub entry #${J}, because it has no root= option" >&2
		;;
	    *)  ;;
	    esac
	done < "$GRUB2CONF"
}

#############################################################
# runs grubonce from the grub package to select which kernel
# to boot on next startup
grub-once()
{
        if [ -x "$GRUBONCE" ]; then
		echo "  running '$GRUBONCE $1'"
		$GRUBONCE $1
	else
		echo "WARNING: $GRUBONCE not found, not preparing bootloader"
	fi
}

#############################################################
# restore grub default after (eventually failed) resume
grub-once-restore()
{
	echo "INFO: running grub-once-restore"
	prepare-parameters
	$GRUB2EDITENV $GRUBDEFAULT unset next_entry
}

#############################################################################
# try to find a kernel image that matches the actually running kernel.
# We need this, if more than one kernel is installed. This works reasonably
# well with grub, if all kernels are named "vmlinuz-`uname -r`" and are
# located in /boot. If they are not, good luck ;-)
find-kernel-entry()
{
	NEXT_BOOT=-1
	ARCH=`uname -m`
	declare -i I=0
	# DEBUG "running kernel: $RUNNING" DIAG
	while [ -n "${KERNELS[$I]}" ]; do
		BOOTING="${KERNELS[$I]}"
		if IMAGE=`readlink /boot/$BOOTING` && [ -e "/boot/${IMAGE##*/}" ]; then
			# DEBUG "Found kernel symlink $BOOTING => $IMAGE" INFO
			BOOTING=$IMAGE
		fi
		case $ARCH in
			ppc*)   BOOTING="${BOOTING#*vmlinux-}" ;;
			*)      BOOTING="${BOOTING#*vmlinuz-}" ;;
		esac
		if [ "$RUNNING" == "$BOOTING" ]; then
			NEXT_BOOT=${MENU_ENTRIES[$I]}
			echo "  running kernel is grub menu entry $NEXT_BOOT (${KERNELS[$I]})"
			break
		fi
		let I++
	done
	# if we have not found a kernel, issue a warning.
	# if we have found a kernel, we'll do "grub-once" later, after
	# prepare_suspend finished.
	if [ $NEXT_BOOT -eq -1 ]; then
		echo "WARNING: no kernelfile matching the running kernel found"
	fi
}

#############################################################################
# if we did not find a kernel (or BOOT_LOADER is not GRUB) check,
# if the running kernel is still the one that will (probably) be booted for
# resume (default entry in menu.lst or, if there is none, the kernel file
# /boot/vmlinuz points to.)
# This will only work, if you use "original" SUSE kernels.
# you can always override with the config variable set to "yes"
prepare-grub()
{
	echo "INFO: running prepare-grub"
	prepare-parameters
	eval `$getkernels`
	RUNNING=`uname -r`
	find-kernel-entry

	RET=0

	if [ $NEXT_BOOT -eq -1 ]; then
		# which kernel is booted with the default entry?
		BOOTING="${KERNELS[$DEFAULT_BOOT]}"
		# if there is no default entry (no menu.lst?) we fall back to
		# the default of /boot/vmlinuz.
		[ -z "$BOOTING" ] && BOOTING="vmlinuz"
		if IMAGE=`readlink /boot/$BOOTING` && [ -e "/boot/${IMAGE##*/}" ]; then
			BOOTING=$IMAGE
		fi
		BOOTING="${BOOTING#*vmlinuz-}"
		echo  "running kernel: '$RUNNING', probably booting kernel: '$BOOTING'"
		if [ "$BOOTING" != "$RUNNING" ]; then
			echo "ERROR: kernel version mismatch, cannot suspend to disk"
			echo "running: $RUNNING booting: $BOOTING"  >> $INHIBIT
			RET=1
		fi
	else
		# set the bootloader to the running kernel
		echo "  preparing boot-loader: selecting entry $NEXT_BOOT, kernel /boot/$BOOTING"
		T1=`date +"%s%N"`
		sync; sync; sync # this is needed to speed up grub-once on reiserfs
		T2=`date +"%s%N"`
		echo "  grub-once: `grub-once $NEXT_BOOT`"
		T3=`date +"%s%N"`
		S=$(((T2-T1)/100000000)); S="$((S/10)).${S:0-1}"
		G=$(((T3-T2)/100000000)); G="$((G/10)).${G:0-1}"
		echo "    time needed for sync: $S seconds, time needed for grub: $G seconds."
	fi

	return $RET
}


###### main()

if [ "$1" = pre ] ; then
		prepare-grub
fi
if [ "$1" = post ] ; then
		grub-once-restore
fi
