#!/bin/bash

################################################################################
# Author John Duncan, 2013.0109 version, based on the kernel docs,
# with hints from the suspend script found at suspend.sourceforge.net
#
# Calls chvt and fgconsole, from the kbd package, clear, from ncurses,
# and swapon, swapoff, and logger, from util-linux.
# SUSP_SERVICES are designed for openrc, but should work without too
# much modification with any sysv-init style init, with runnable service
# scripts in /etc/init.d/
#
# Note that to look right on wakeup, echoes to the VT
# before the suspend need the -e switch and a terminating \r added

# setup our default values. these can be overridden in the conf file
# (exept for the conf file location itself, naturally)

#SUSPEND_CONF="/usr/local/etc/suspend.conf"
SUSPEND_CONF="/l/etc/suspend.conf"

SUSP_INITDIR="/etc/init.d"
SUSP_VT=0 # 0=disabled

calledas="${0##*/}"

# SUSP_MASTER_TYPE default should be based on what we were called as
case $calledas in
	s2both)		SUSP_MASTER_TYPE=disk ;;
	s2disk)		SUSP_MASTER_TYPE=disk ;;
	s2ram)		SUSP_MASTER_TYPE=mem ;;
	s2standby)	SUSP_MASTER_TYPE=standby ;;
	*)		logger "suspend: Called incorrectly as $calledas. Exiting."; exit 1;;
esac
grep -q "$SUSP_MASTER_TYPE" /sys/power/state || {
	logger "Suspend type $SUSP_MASTER_TYPE not registered in /sys/power/state. Exiting."
	exit 2
}

# Default to shutdown for suspend to disk...
SUSP_DISK_TYPE=shutdown
# ... but be suspend to both aware, too.
[[ $calledas = s2both ]] && SUSP_DISK_TYPE=suspend

#SUSP_DISK_PART= # keep unset to use kernel default; this lets us manage the swap end,
#		 # but the kernel uses its command line or kconfig setting.
SUSP_DISK_SIZE_KB=$((500*1024))	# kernel default 500*1024, set 0 to use full partition size. \
				# NOTE, swsusp only used <= 1/2 physical memory due to how it works!

# SUSP_SERVICES don't suspend/resume unless SUSP_MASTER_TYPE=disk

SUSP_SERVICES="hwclock_nd" # _nd appended for no-deps

# RUN_AT_* commands always run.
#RUN_AT_SUSPEND[1]='echo susp1'
#RUN_AT_SUSPEND[2]='echo susp2'
#RUN_AT_RESUME[1]='echo resu1'

# source suspend.conf if it exists
[[ -e  $SUSPEND_CONF ]] && . "$SUSPEND_CONF"


################################################################################
# switch to suspend VT if feature is enabled
if [[ $SUSP_VT -ne 0 && -c /dev/tty$SUSP_VT ]] ; then
		orig_vt=$(/usr/bin/fgconsole)
	[[ $orig_vt -ne $SUSP_VT ]] && /usr/bin/chvt $SUSP_VT && exec &> /dev/tty$SUSP_VT
	/usr/bin/clear
	echo
	echo -e "*** Switched to suspend VT $SUSP_VT from VT $orig_vt\r"
else
	echo -e "*** SUSP_VT is set to VT $SUSP_VT which is not valid!\r"
	SUSP_VT=0
fi

# run the RUN_AT_SUSPEND entries
num=1
until [[ $num -gt ${#RUN_AT_SUSPEND[*]} ]] ; do
	[[ ${RUN_AT_SUSPEND[$num]} ]] && ${RUN_AT_SUSPEND[$num]}
	num=$(( $num + 1 ))
done

# Only bother with this if we're suspending to disk.
[[ $SUSP_MASTER_TYPE = disk ]] && {
	# If we're actually suspending to both, don't stop services
	[[ $SUSP_DISK_TYPE = suspend ]] || {
		echo
		echo -e "*** Stopping necessary services\r"
		svc_restart=
		# split out space separated services
		while [[ $SUSP_SERVICES ]] ; do

			# first, delete any spaces at the beginning
			until [[ $SUSP_SERVICES = ${SUSP_SERVICES# } ]]; do
				SUSP_SERVICES=${SUSP_SERVICES# }
			done
			# if there's nothing left, exit loop
			[[ $SUSP_SERVICES ]] || break
			# set $svc to first service, remove it from $SUSP_SERVICES
			svc=${SUSP_SERVICES%% *}
			SUSP_SERVICES=${SUSP_SERVICES#$svc}

			### OK, we have our service, now process it
			nodeps=
			# parse for _nd, remove and set nodeps if there
			[[ ! $svc = ${svc%_nd} ]] && nodeps="--nodeps" svc=${svc%_nd}
			svc_cmd="$SUSP_INITDIR/$svc"

			# if svc is executable and running, stop and add to restart list
			if [[ -x $svc_cmd ]]; then
				if "$svc_cmd" --quiet status; then
					$svc_cmd $nodeps stop
					svc_restart="$svc_restart $svc"
				else
					echo -e "$svc is not running, skipping\r"
				fi
			else
				echo -e "$svc is not a valid service in $SUSP_INITDIR\r"
			fi
		done
		# sleep to give services time to stop
		sleep 2
	}

	echo
	echo -e "*** Managing swap\r"
	swapoff -a; swapon -a

	echo -e "*** Setting suspend parameters\r"
	# swapon $SUSP_DISK_PART if necessary
	flag_swap_act=0
	[[ $SUSP_DISK_PART && $SUSP_MASTER_TYPE = disk ]] && {
		swapsline="$(grep -s $SUSP_DISK_PART /proc/swaps)" || {
			echo -e "Activating $SUSP_DISK_PART for suspend\r"
			swapon -p 0 "$SUSP_DISK_PART" || {
				echo -e "Failed activation, exiting (services may be stopped)\r"
				echo
				exit 100
			}
			flag_swap_act=1
			swapsline="$(grep -s $SUSP_DISK_PART /proc/swaps)"
		}
		[[ $SUSP_DISK_SIZE_KB = 0 ]] && {
			SUSP_DISK_SIZE_KB=$(echo $swapsline | awk '{ print $3 }')
			# apparently there's a bit of overhead that needs figured in... say 4 MB
			SUSP_DISK_SIZE_KB=$(( $SUSP_DISK_SIZE_KB - 4*1024 ))
		}
	}
	[[ $SUSP_DISK_SIZE_KB = 0 ]] && SUSP_DISK_SIZE_KB=$((500*1024))
	echo -e "Suspend image size: $((${SUSP_DISK_SIZE_KB}*1024))\r"
	echo $((${SUSP_DISK_SIZE_KB}*1024)) >> /sys/power/image_size
	echo $SUSP_DISK_TYPE >> /sys/power/disk
}

echo
echo -e "*** Syncing disk\r"
sync; sync; sync

################################################################################
############                      Suspend now                       ############
################################################################################
echo -e "*** Suspending....\r"
sleep 3
# type of suspend, setting this triggers it!
echo $SUSP_MASTER_TYPE >> /sys/power/state

################################################################################
############                       Wakeup now                       ############
################################################################################

echo
echo "*******************************************************************************"
echo "********************************** WAKING UP **********************************"
echo "*******************************************************************************"
echo

#echo "*** Resetting console font"
#resetres

# only bother with this if we're resuming from disk
[[ $SUSP_MASTER_TYPE = disk ]] && {
	[[ $flag_swap_act = 1 ]] && {
		echo "Deactivating $SUSP_DISK_PART for resume"
		swapoff "$SUSP_DISK_PART"
	}

	echo "*** Managing swap"
	swapoff -a; swapon -a

	# if we suspended to both, we didn't stop services, otherwise...
	[[ $SUSP_DISK_TYPE = suspend ]] || {
		echo "*** Restarting services stopped for suspend"
		while [[ $svc_restart ]] ; do
			# first, delete any spaces at the beginning
			until [[ $svc_restart = ${svc_restart# } ]]; do
				svc_restart=${svc_restart# }
			done
			# if there's nothing left, exit loop
			[[ $svc_restart ]] || break
			# set $svc to first service, remove it from $svc_restart
			svc=${svc_restart%% *}
			svc_restart=${svc_restart#$svc}
			### OK, we have our service, start it
			"$SUSP_INITDIR/$svc" start
		done
	}
}

# run the RUN_AT_RESUME entries
num=1
until [[ $num -gt ${#RUN_AT_RESUME[*]} ]] ; do
	[[ ${RUN_AT_RESUME[$num]} ]] && ${RUN_AT_RESUME[$num]}
	num=$(( $num + 1 ))
done

echo
[[ $SUSP_VT -ne 0 ]] && {
	echo "*** Switching back to VT $orig_vt"
	[[ $orig_vt -ne $SUSP_VT ]] && sleep 1 && /usr/bin/chvt $orig_vt
}
