#!/bin/bash

export PATH=/sbin:/usr/sbin:/bin:/usr/bin

# Default values go here.  It is important to _not_ initialize some
# variables here.  They are:
#
# PM_CMDLINE
# RESUME_MODULES
#
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_BIN=/usr/sbin/s2disk
S2DISK_CONF=/var/lib/s2disk.conf

[ -f /etc/pm/config ] && . /etc/pm/config

GLOBAL_CONFIG_VARIABLES=""
add_global() {
	export $1
	GLOBAL_CONFIG_VARIABLES="$GLOBAL_CONFIG_VARIABLES $1"
}

# export them all here
add_global HIBERNATE_RESUME_POST_VIDEO
add_global INHIBIT
add_global PM_LOGFILE
add_global PM_CMDLINE
add_global RESUME_MODULES
add_global SUSPEND_MODULES
add_global HIBERNATE_METHOD
add_global S2DISK_BIN
add_global S2DISK_CONF

source_configs()
{
	cfgs="/etc/pm/config.d/*[^~]"
	for cfg in $cfgs ; do
		[ -x $cfg ] || continue
		while read LINE ; do
			case "$GLOBAL_CONFIG_VARIABLES " in
				*" ${LINE%=*} "*) continue ;;
				*) eval $LINE ;;
			esac
		done < $cfg
	done
}

source_configs

take_suspend_lock()
{
	VT=$(fgconsole)
	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 &
}

run_hooks()
{
	[ -z "$1" ] && return 0

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

	echo "$(date): running $1 hooks."

	files="/etc/pm/hooks/*[^~]"
	if [ "$2" = "reverse" ]; then
		filea=($files)
		filen=${#filea[*]}
		while [ "$filen" -gt 0 ]; do
			let filen--
			file="${filea[$filen]}"
			if [ -x $file ]; then
				echo "===== $(date): running hook: $file ====="
				$file $1
			fi
		done
	else
		for file in $files ; do
			if [ -x $file ]; then
				echo "===== $(date): running hook: $file ====="
				$file $1
			fi
		done
	fi

	echo "$(date): finished $1 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
}

do_suspend()
{
	if [ -x /usr/sbin/s2ram ]; then
		/usr/sbin/s2ram $S2RAM_OPTS
	else
		pm-pmu --suspend || echo -n "mem" > /sys/power/state
	fi
}

do_hibernate()
{
	if [ -z "$HIBERNATE_METHOD" ]; then
		if [ -x $S2DISK_BIN -a -c /dev/snapshot ]; then
			HIBERNATE_METHOD="userspace"
		else
			HIBERNATE_METHOD="kernel"
		fi
	fi
	case $HIBERNATE_METHOD in
		userspace)
			$S2DISK_BIN -f $S2DISK_CONF
			;;
		kernel)
			echo -n "platform" > /sys/power/disk
			echo -n "disk" > /sys/power/state
			;;
	esac
}

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

	rm -f "$INHIBIT"

	run_hooks "$1"

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

	run_hooks "$2" reverse

	remove_suspend_lock 200

	return 0
}

_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
	# 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 $UNL $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 $UNL
			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
}

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

restartservice()
{
	if [ "x$(eval echo \$${1}_SERVICE_ACTIVATE)" == "xyes" \
			-a -x "/etc/init.d/$1" ]; then
		"/etc/init.d/$1" start 2>&1
	fi
}

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

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