#!/bin/sh # vim:noexpandtab # Default values go here. It is important to _not_ initialize some # variables here. They are: # # PM_CMDLINE # RESUME_MODULES # # for great debugging! [ "${PM_DEBUG}" = "true" ] && set -x set -a PM_UTILS_LIBDIR="@PM-UTILS-LIBDIR@" PM_UTILS_ETCDIR="@PM-UTILS-SYSCONFDIR@" PM_UTILS_RUNDIR="/var/run/pm-utils" PATH=/sbin:/usr/sbin:/bin:/usr/bin:"${PM_UTILS_LIBDIR}"/bin HIBERNATE_MODE="platform" HIBERNATE_RESUME_POST_VIDEO=no INHIBIT="${PM_UTILS_RUNDIR}/inhibit" PM_LOGFILE="${PM_LOGFILE:=/var/log/pm-suspend.log}" SUSPEND_MODULES="" TEMPORARY_CPUFREQ_GOVERNOR="performance" LOCKDIR="${PM_UTILS_RUNDIR}/locks" STORAGEDIR="${PM_UTILS_RUNDIR}/storage" SLEEP_MODULE="kernel" # Use c sort order export LC_COLLATE=C [ -O "${PM_UTILS_LIBDIR}"/defaults ] && . "${PM_UTILS_LIBDIR}"/defaults SLEEP_FUNCTIONS="${PM_UTILS_LIBDIR}/module.d/${SLEEP_MODULE}" set +a source_configs() { for cfg in "${PM_UTILS_ETCDIR}"/config.d/*[!~] ; do [ -O "$cfg" ] || continue set -a . "${cfg}" set +a done } source_configs # try to take the lock. Fail if we cannot get it. try_lock() { # $1 = file to use as lockfile # $2 (optional) content to write to the lockfile, # extra newline will be appended # make sure the directory where the lockfile should be exists mkdir -p "${LOCKDIR}" local lock="${LOCKDIR}/${1##*/}" # we use noclobber to make sure there are no race conditions (set -o noclobber; echo "${2}" > "${lock}") 2> /dev/null || return 1 return 0 } # spin waiting for the lock with optional timeout. # return once we have it, or the timeout has expired spin_lock() { # $1 = lockfile # $2 = optional timeout local elapsed=0 while ! try_lock $1; do [ "x$2" != "x" ] && [ $(( $elapsed == $2 )) -ne 0 ] && return 1 elapsed=$(($elapsed + 1)) sleep 1; done } # release the lock release_lock() { # $1 = lockfile # make sure it is ours first.i local lock="${LOCKDIR}/${1##*/}" rm -f "${lock}" return $? } take_suspend_lock() { VT=$(fgconsole) chvt 63 try_lock "pm-utils.lock" || return 1 mkdir -p "${STORAGEDIR}" return 0 } remove_suspend_lock() { rm -rf "${STORAGEDIR}" chvt 1 chvt $VT release_lock "pm-utils.lock" } command_exists() { # $1 = command to test for. It can be an executable in the path, # a shell function, or a shell builtin. type "$1" >/dev/null 2>&1 return $? } run_hooks() { # $1 = type of hook to find. # $2 = paramaters to pass to hooks. # $3 = if present and equal to "reverse", run hooks backwards. # Currently only power and sleep are meaningful. local syshooks="${PM_UTILS_ETCDIR}/$1.d" local phooks="${PM_UTILS_LIBDIR}/$1.d" local sort="sort" local base local hook local oifs="${IFS}" # the next two lines are not a typo or a formatting error! local nifs=" " IFS="${nifs}" # tolerate spaces in filenames. [ "$3" = "reverse" ] && sort="sort -r" for base in $(for f in "$syshooks/"*[!~] "$phooks/"*[!~]; do [ -O "$f" ] && echo ${f##*/} ; done | sort | uniq) ; do if [ -f "$syshooks/$base" ]; then hook="$syshooks/$base" elif [ -f "$phooks/$base" ]; then hook="$phooks/$base" fi [ -x "${hook}" ] && ( IFS="${oifs}" echo "$(date): running ${hook} $2" "${hook}" $2 ) done IFS="${oifs}" } get_power_status() { RETVAL=0 on_ac_power case "$?" in "0") echo "ac" ;; "1") echo "battery" ;; "255") echo "error" RETVAL=1 ;; esac return $RETVAL } [ -O "${SLEEP_FUNCTIONS}" ] || { echo "Requested sleep module $SLEEP_MODULE not available." exit 1 } . "${SLEEP_FUNCTIONS}" init_logfile() { if [ -h "$1" ]; then echo "$1 is a symbolic link, refusing to overwrite." return 1 elif [ -O "$1" ]; then echo "We do not own $1, refusing to overwrite." return 1 elif [ -z "$1" ]; then echo "Please pass a filename to init_logfile." return 1 fi exec > "$1" 2>&1 } pm_main() { "check_$1" || { echo "System does not support $1 sleep." return 1 } take_suspend_lock || exit 1 # make sure that our locks are unlocked no matter how the script exits trap remove_suspend_lock 0 init_logfile "$PM_LOGFILE" rm -f "$INHIBIT" run_hooks sleep "$1" command_exists "do_$1" && [ ! -e "${INHIBIT}" ] && { \ sync; "do_$1" } run_hooks sleep "$2" reverse return 0 } _rmmod() { if modprobe -r "$1"; then touch "${STORAGEDIR}/module:$1" 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="$(echo $1 |tr - _)" RET=1 while read MOD D C USED D; do [ "$MOD" = "$UNL" ] || continue if [ "$USED" = "-" ]; then # no dependent modules, just try to remove this one. _rmmod "$MOD" $C RET=$? else # modules depend on this one. try to remove them first. MODS="${USED%%*,}" while [ -n "${MODS}" ]; do # try to unload the last one first MOD="${MODS##*,}" modunload $MOD && RET=0 # prune the last one from the list MODS="${MODS%,*}" 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 } # reload all the modules in no particular order. modreload() { for x in "${STORAGEDIR}"/module:* ; do [ -O "${x}" ] && modprobe "${x##*:}" >/dev/null 2>&1 done } if ! command_exists service; then service() { if [ -x "/etc/init.d/$1" ]; then svc="$1" shift "/etc/init.d/$svc" "$@" else echo "$1" $": unrecognized service" 1>&2 return 1 fi } fi stopservice() { if service "$1" status 2>/dev/null | grep -c -q running; then touch "${STORAGEDIR}/service:$1" service "$1" stop fi } restartservice() { [ -O "${STORAGEDIR}/service:$1" ] && service "$1" start } savestate() { echo "$2" > "${STORAGEDIR}/state:$1" } restorestate() { [ -O "${STORAGEDIR}/state:${1}" ] && cat "${STORAGEDIR}/state:${1}" }