#!/bin/bash
# vim:noexpandtab

export PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/lib/pm-utils/bin

# Default values go here.  It is important to _not_ initialize some
# variables here.  They are:
#
# PM_CMDLINE
# RESUME_MODULES
#
set -a
### for compatibility with the other pm-utils project ###
PM_UTILS_LIBDIR="/usr/lib/pm-utils"
PM_UTILS_ETCDIR="/etc/pm"
PM_UTILS_RUNDIR="/var/run/pm-utils"
PATH=/sbin:/usr/sbin:/bin:/usr/bin:"${PM_UTILS_LIBDIR}"/bin
#LOCKDIR="${PM_UTILS_RUNDIR}/locks"
#STORAGEDIR="${PM_UTILS_RUNDIR}/${STASHNAME}/storage"
NA=254
NX=253
DX=252
PM_FUNCTIONS="$PM_UTILS_LIBDIR/functions"
# Use c sort order
LC_COLLATE=C
PM_CMDLINE="$*"
### end compatibility section ###########################

HIBERNATE_RESUME_POST_VIDEO=no
INHIBIT=/var/run/pm-utils.inhibit
PM_LOGFILE=${PM_LOGFILE:=/var/log/pm-suspend.log}
SUSPEND_MODULES=""
HIBERNATE_METHOD=""
S2DISK_CONF="/var/lib/s2disk.conf"
TEMPORARY_CPUFREQ_GOVERNOR="performance"

[ -f /usr/lib/pm-utils/defaults ] && . /usr/lib/pm-utils/defaults
set +a

# set nullglob to make glob results empty in case the pattern does not
# match any files
shopt -s nullglob

source_configs()
{
	cfgs="/etc/pm/config.d/*[^~]"
	for cfg in $cfgs ; do
		[ -f $cfg ] || continue
		set -a
		. $cfg
		set +a
	done
}

source_configs

take_suspend_lock()
{
	VT=$(fgconsole)
	# don't switch console here, suspend tools are doing this anyway later
	# on, and it looks better with splashy support
	#chvt 63
	if [ -f /.suspended ]; then
		read pid < /.suspended
		if [ -d /proc/$pid ]; then
			return 1
		fi
	fi
        echo "$$" > /.suspended
	rm -f /var/run/pm-suspend
	touch /var/run/pm-suspend
	return 0
}

remove_suspend_lock()
{
#	rm -f /var/run/pm-suspend
	#chvt 1
	#chvt $VT
	openvt -- sh -c "usleep $1 ; rm -f /.suspended >/dev/null 2>&1 0<&1" >/dev/null 2>&1 0<&1 &
}

find_sleepd_files()
{
	flist="/etc/pm/sleep.d/*[^~] /usr/lib/pm-utils/sleep.d/*[^~]"
	bases=$(for file in $flist ; do echo $(basename $file) ; done | sort -n | uniq)
	for base in $bases ; do
		if [ -x "/etc/pm/sleep.d/$base" ]; then
			echo /etc/pm/sleep.d/$base
		elif [ -x "/usr/lib/pm-utils/sleep.d/$base" ]; then
			echo /usr/lib/pm-utils/sleep.d/$base
		fi
	done
}

run_hooks()
{
	# $1=suspend/suspend_hybrid/hibernate $2=suspend/hibernate/thaw/resume $3=reverse/""
	[ -z "$1" ] && return 0

	[ -f /var/run/pm-suspend ] && . /var/run/pm-suspend
#	rm -f /var/run/pm-suspend

	echo "$(date '+%F %T.%N %z'): running '$1'/'$2'/'$3' hooks."

	files=$(find_sleepd_files)
	if [ "$3" = "reverse" ]; then
		filea=($files)
		filen=${#filea[*]}
		while [ "$filen" -gt 0 ]; do
			let filen--
			file="${filea[$filen]}"
			echo "===== $(date '+%F %T.%N') running hook: $file ====="
			$file $2 $1
		done
	else
		for file in $files ; do
			echo "===== $(date '+%F %T.%N') running hook: $file ====="
			$file $2 $1
		done
	fi

	echo "$(date '+%F %T.%N'): done running $1/$2 hooks."
}

get_power_status()
{
	RETVAL=0
	on_ac_power
	case "$?" in
		"0")
			echo "ac"
			;;
		"1")
			echo "battery"
			;;
		"255")
			echo "error"
			RETVAL=1
			;;
	esac
	return $RETVAL
}

get_s2ram_cmdline()
{
	local ACPI_FLAG UNHANDLED
	let ACPI_FLAG=0
	if [ -n "$QUIRK_NONE" ]; then
		echo "INFO: HAL quirks state no quirk is neeed."
		return;
	fi
	[ -n "$QUIRK_S3_BIOS" ] && ACPI_FLAG=1
	[ -n "$QUIRK_S3_MODE" ] && let ACPI_FLAG+=2
	[ $ACPI_FLAG -ne 0 ] &&			S2RAM_OPTS+="--acpi_sleep $ACPI_FLAG "
	[ -n "$QUIRK_VBE_POST" ] &&		S2RAM_OPTS+="--vbe_post "
	[ -n "$QUIRK_VBEMODE_RESTORE" ] &&	S2RAM_OPTS+="--vbe_mode "
	[ -n "$QUIRK_VBESTATE_RESTORE" ] &&	S2RAM_OPTS+="--vbe_save "
	[ -n "$QUIRK_RADEON_OFF" ] &&		S2RAM_OPTS+="--radeontool "
	# the unhandled quirks...
	[ -n "$QUIRK_DPMS_ON" ] &&		UNHANDLED+="QUIRK_DPMS_ON "
	[ -n "$QUIRK_DPMS_SUSPEND" ] &&		UNHANDLED+="QUIRK_DPMS_SUSPEND "
	[ -n "$QUIRK_RESET_BRIGHTNESS" ] &&	UNHANDLED+="QUIRK_RESET_BRIGHTNESS "
	[ -n "$QUIRK_VGA_MODE_3" ] &&		UNHANDLED+="QUIRK_VGA_MODE_3 "
	if [ -n "$UNHANDLED" ]; then
		echo "INFO: those quirks are not handled yet: $UNHANDLED"
	fi
	echo "INFO: S2RAM_OPTS from HAL quirks: '$S2RAM_OPTS'."
}

# this function tries to assemble the best s2ram options from various sources, falling back
# to other methods...
get_s2ram_opts()
{
	# if S2RAM_OPTS is set - then use it. The user told us so. Obey his wish.
	if [ -n "$S2RAM_OPTS" ]; then
		echo "INFO: using user-supplied options: S2RAM_OPTS='$S2RAM_OPTS' for suspending."
		return
	fi

	# The user did not tell us the options, so let's check if he expressed a preference for
	# quirks passed by HAL via the command line...
	if [ "$S2RAM_QUIRKS_SOURCE" = "hal" ]; then
		# use the quirks from HAL
		S2RAM_OPTS="--force"
		get_s2ram_cmdline
		return
	fi

	# ...or if he prefers the s2ram built in list...
	if [ "$S2RAM_QUIRKS_SOURCE" = "s2ram" ]; then
		if  /usr/sbin/s2ram -n >/dev/null; then
			echo "INFO: using s2ram built-in database, machine is supported."
			return
		else
			echo "WARN: S2RAM_QUIRKS_SOURCE=s2ram, but machine is unknown, continuing..."
		fi
	fi

	# still nothing. So let's try to be smart.
	# first check if a "good" kernel module is loaded.
	for MODULE in i915 fglrx nvidia; do
		if [ -d /sys/module/$MODULE ]; then
			echo "INFO: module $MODULE is loaded, trusting it to restore video after resume."
			S2RAM_OPTS="--force"
			return
		fi
	done

	# now we check if s2ram knows the machine.
	if /usr/sbin/s2ram -n >/dev/null; then
		echo "INFO: machine is in s2ram database, using it."
		return;
	fi

	# s2ram does not know the machine, ask HAL...
	echo "INFO: no quirks found, using info passed by HAL."
	get_s2ram_cmdline
	if [ -n "$S2RAM_OPTS" ]; then
		S2RAM_OPTS+="--force "
	fi

	# if we came here and S2RAM_OPTS is empty, suspend won't work :-(
}

do_suspend()
{
	local RET
	echo "INFO: going to suspend. In case of problems with the selected suspend options,"
	echo "INFO: please read /usr/share/doc/packages/pm-utils/README.smart-suspend-to-RAM"
	get_s2ram_opts
	if [ -x /usr/sbin/s2ram ]; then
		set -x
		/usr/sbin/s2ram $S2RAM_OPTS
		RET=$?
		set +x
	else
		pm-pmu --suspend || echo -n "mem" > /sys/power/state
		RET=$?
	fi
	return $RET
}

do_hibernate()
{
	local RET=1
	if [ -z "$HIBERNATE_METHOD" ]; then
		if [ -x /usr/sbin/s2disk -a -c /dev/snapshot ]; then
			HIBERNATE_METHOD="userspace"
		else
			HIBERNATE_METHOD="kernel"
		fi
	fi
	case $HIBERNATE_METHOD in
		userspace)
			set -x
			/usr/sbin/s2disk --config $S2DISK_CONF
			RET=$?
			set +x
			;;
		kernel)
			echo -n "platform" > /sys/power/disk
			echo -n "disk" > /sys/power/state
			RET=$?
			;;
	esac
	return $RET
}

do_suspend_hybrid()
{
	local RET=1
	if [ -z "$HIBERNATE_METHOD" ]; then
		if [ -x /usr/sbin/s2both -a -c /dev/snapshot ]; then
			HIBERNATE_METHOD="userspace"
		else
			HIBERNATE_METHOD="kernel"
		fi
	fi
	case $HIBERNATE_METHOD in
		userspace)
			get_s2ram_opts
			set -x
			/usr/sbin/s2both --config $S2DISK_CONF
			RET=$?
			set +x
			;;
		*)
			RET=1
			;;
	esac
	return $RET
}

pm_main()
{
	local RET=1
	if [ -n "$PM_LOGFILE" ]; then
		exec > "$PM_LOGFILE" 2>&1
	fi
	take_suspend_lock || exit 1

	rm -f "$INHIBIT"

	run_hooks "$1" "$2"

	if [ ! -e "$INHIBIT" -a "$(type -t "do_$1")" == "function" ]; then
		sync ; sync ; sync
		"do_$1"
		RET=$?
	fi

	run_hooks "$1" "$3" reverse

	remove_suspend_lock 200

	return $RET
}

_rmmod() {
	if modprobe -r $1; then
		echo "export RESUME_MODULES=\"$1 \$RESUME_MODULES\"" \
						>> /var/run/pm-suspend
		return 0
	else
		echo "# could not unload '$1', usage count was $2"
		return 1
	fi
}

# this recursively unloads the given modules and all that depend on it
# first parameter is the module to be unloaded
modunload()
{
	local MOD D C USED MODS I
	local UNL=$1 RET=1
	# the kernel only knows underscores in module names, no dashes
	UNL=${UNL//-/_}
	# RET is the return code.
	# If at least one module was unloaded, return 0.
	# if the module was not loaded, also return 0 since this is no error.
	# if no module was unloaded successfully, return 1
	while read MOD D C USED D; do
		[ "$MOD" = "$UNL" ] || continue
		if [ "$USED" == "-" ]; then
			_rmmod $MOD $C
			RET=$?
		else
			USED=${USED//,/ }
			MODS=($USED)
			# it seems slightly more likely to rmmod in one pass,
			# if we try backwards.
			for I in `seq $[${#MODS[@]}-1] -1 0`; do
				MOD=${MODS[$I]}
				modunload $MOD && RET=0
			done
			# if we unloaded at least one module, then let's
			# try again!
			[ $RET -eq 0 ] && modunload $MOD
			RET=$?
		fi
		return $RET
	done < /proc/modules
	# if we came this far, there was nothing to do, 
	# the module is no longer loaded.
	return 0
}

modreload()
{
	if [ "$(eval echo \$${1}_MODULE_LOAD)" == "yes" ]; then
		modprobe "$1" >/dev/null 2>&1
	fi
}

_service=$(type -p service)
if [ -z "$_service" ]; then
	service() {
		if [ -x "/etc/init.d/$1" ]; then
			"/etc/init.d/$@"
		else
			echo "$1" $": unrecognized service" 1>&2
			return 1
		fi
	}
fi
unset _service

stopservice()
{
	service "$1" status 2>/dev/null | grep -c -q running
	if [ "$?" == "0" ]; then
		N=${1//[-.]/_}
		echo "export ${N}_SERVICE_ACTIVATE=yes" >> /var/run/pm-suspend
		service "$1" stop
	fi
}

restartservice()
{
	N=${1//[-.]/_}
	if [ "x$(eval echo \$${N}_SERVICE_ACTIVATE)" == "xyes" ]; then
		service "$1" start
	fi
}

savestate()
{
	echo "export ${1}_STATE=$2" >> /var/run/pm-suspend
}

restorestate()
{
	eval echo \$${1}_STATE
}
