#!/bin/bash
###########################################################################
#                                                                         #
#                         Powersave Daemon                                #
#                                                                         #
#          Copyright (C) 2004,2005 SUSE Linux Products GmbH               #
#                                                                         #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the   #
# Free Software Foundation; either version 2 of the License, or (at you   #
# option) any later version.                                              #
#                                                                         #
# This program is distributed in the hope that it will be useful, but     #
# WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       #
# General Public License for more details.                                #
#                                                                         #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., #
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA                  #
#                                                                         #
###########################################################################

. $SYSCONF_DIR/sleep

# the statefiles are used to record the system state before the sleep state
# and restore it after resume. Stopped services, unloaded modules etc. are
# recorded in these files.
STATE1=/var/lib/suspend2disk-state   # statefile for suspend-to-disk
STATE2=/var/lib/suspend2ram-state    # statefile for suspend-to-ram
STATE3=/var/lib/standby-state        # statefile for standby

# the logfiles log information (such as the output of rc-scripts) in addition
# to the statefiles to aid users in debugging suspend failures.
LSMOD_LOG1=/var/log/suspend2disk.log # logfile for suspend-to-disk
LSMOD_LOG2=/var/log/suspend2ram.log  # logfile for suspend-to-ram
LSMOD_LOG3=/var/log/standby.log      # logfile for standby

POWERSAVE="$BIN_DIR/powersave"

PCCARDCTL="/sbin/pccardctl"
# if you still have cardmgr, use "/sbin/cardctl"
test -x "$PCCARDCTL" || PCCARDCTL="/sbin/cardctl"

GRUBONCE="/usr/sbin/grubonce"
GRUBDEFAULT="/boot/grub/default"
GRUBDEFSAVE="/var/run/suspend.grubonce.default"

# this recursively unloads the given modules and all that depend on it
# first parameter is the module to be unloaded
# second parameter is the state file, where all unloaded modules are
#        recorded for reloading after resume.
# third parameter is the log file for the user.
unload_module(){
    local LOGFILE STATEFILE
    local MOD D C USED MODS I
    local UNL=$1 RET=1
    # we need the statefile to be set
    [ -z "$2" ] && DEBUG "no STATEFILE set in unload_module" ERROR && return 255
    [ -n "$3" ] && LOGFILE="$3" || LOGFILE=/dev/null
    STATEFILE="$2"
    # RET is the return code. If at least one module was unloaded, return 0.
    #     if no module was unloaded successfully, return 1
    #     if there was no module to unload, SUCCESS!, return 0
    while read MOD D C USED D; do
        [ "$MOD" != "$UNL" ] && continue
        if [ "$USED" == "-" ]; then
            if [ $C -eq 0 ]; then
                progress "${_M08} $UNL"
                echo "# trying to unload: $UNL" >> $STATEFILE
                echo "# trying to unload: $UNL" >> $LOGFILE
                if rmmod $UNL; then
                    echo "unloaded: $UNL" >> $STATEFILE
                    echo "unloaded: $UNL" >> $LOGFILE
                    RET=0
                else
                    DEBUG "could not unload module '$UNL'" WARN
                    echo "could not unload module '$UNL', usage count was 0." >> $LOGFILE
                fi
            else
                echo "could not unload module '$UNL', usage count: $C" >> $LOGFILE
            fi
        else
            USED=${USED//,/ }
            MODS=($USED)
            # it is slightly more likely to rmmod in one pass, if we try backwards.
            # is this really true? I don't know, but it doesn't hurt either.
            for I in `seq $[${#MODS[@]}-1] -1 0`; do
                MOD=${MODS[$I]}
                unload_module $MOD $STATEFILE $LOGFILE && RET=0
            done
            # if we unloaded at least one module, then let's try again!
            [ $RET -eq 0 ] && unload_module $UNL $STATEFILE $LOGFILE
            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
}

#######################################################
# unmount_fatfs function
# unmounts all (v)fat/ntfs partitions if possible.
#
unmount_fatfs(){
    local I J K # counters
    local D E   # dummies
    local RET  DEV MNT OPT TYPE MESSAGE MTAB
    declare -a DEV MNT OPT TYPE # arrays for the mtab entries

    progress "${_M04}"
    echo "== Unmounting FAT/NTFS filesystems: ==" >> $LSMOD_LOG
    MESSAGE=""; K=0
    MTAB=/etc/mtab # or /proc/mounts? which one is better?
    exec 2>&1   # why?
    # ugh, this is ugly. But remember, we could be having a mountpoint named
    # "/mnt/mount point name with whitespace in it"
    # yes, ugly quoting hell.
    eval `awk '
    BEGIN {X=0}
    { if (($3=="ntfs")||($3=="vfat")||($3==fat)) {
        Y=$2; gsub("\\\\\\\\", "\\\\\\\\\\\\", Y);
        print "DEV["X"]="$1;
        print "MNT["X"]=$(echo -e "Y")";
        print "TYP["X"]="$3;
        print "OPT["X"]="$4;
        X++}
    }' $MTAB`
    echo "Checking for mounted fat/ntfs filesystems:"
    # now do something with the list of devices / mountpoints...
    for (( I=0; ${#DEV[$I]}; I++ )); do
        echo "  device ${DEV[$I]} mounted on '${MNT[$I]}'" >> $LSMOD_LOG
        # stderr is additional information from fuser which we have to drop.
        # We need only the PIDs. They are on stdout.
        for J in $(fuser -m "${MNT[$I]}" 2>/dev/null);do
            while read D E; do
                case $D in
                    Name:)
                        if [ "$E" != "prepare_suspend" ]; then
                            MESSAGE=$MESSAGE" $E($J)"
                            echo "    is accessed by $E($J)" >> $LSMOD_LOG
                            let K++
                        fi
                        break ;;
                    *)  ;;
                esac
            done < /proc/$J/status
        done
    done
    [ $I -eq 0 ] && echo "  none found in $MTAB" >> $LSMOD_LOG
    exec 3>&2   # why?
    if [ $K -ne 0 ];then # one or more filesystems in use
        echo "  filesystems in use, asking user..." >> $LSMOD_LOG
        case $K in
            1)  MESSAGE="Process $MESSAGE is" ;;
            *)  MESSAGE="Processes $MESSAGE are" ;;
        esac
        MESSAGE=$MESSAGE" accessing an ntfs/fat mountpoint. \
Please make sure fat/ntfs mountpoints are unmountable before proceeding."
        question "$MESSAGE" "1"
        RET=$?
        if [ $RET -ne 0 ]; then
            echo "  user does not want to continue. aborting suspend" >> $LSMOD_LOG
            progress_finish
            $SCRIPT_RETURN $EV_ID 1 "prepare_sleep failed ($1): $MESSAGE"
            restore_after_sleep "$1"
            EXIT 1;
        fi
        echo "  user wants to continue anyway. Good luck." >> $LSMOD_LOG
    fi
    # no filesystem in use. Or user answered "proceed anyway".
    # umount ntfs/fat mount points ...
    for (( I=0; ${#DEV[$I]}; I++ )); do
        echo "# trying to umount device: ${DEV[$I]}" >> $STATE
        echo "trying to umount device: '${DEV[$I]}' '${MNT[$I]}' -t '${TYP[$I]}' -o '${OPT[$I]}'" >> $LSMOD_LOG
        umount ${MNT[$I]}; RET=$?
        if [ $RET -eq 0 ]; then
            # very verbose, but we have the whole mount command line ready for remount.
            # every variable in one line, makes it easier to re-parse (remember:
            # whitespace danger!)
            echo "unmounted: ${DEV[$I]}"              >> $STATE
            echo "# mountpoint unmounted: ${MNT[$I]}" >> $STATE
            echo "# fstype unmounted: -t ${TYP[$I]}"  >> $STATE
            echo "# options unmounted: -o ${OPT[$I]}" >> $STATE
            echo "  success." >> $LSMOD_LOG
        else
            echo "umount failed. Asking user what to do." >> $LSMOD_LOG
            MESSAGE="Unable to unmount device ${DEV[$I]}."
            DEBUG "$MESSAGE Asking the user" ERROR
            question "$MESSAGE Proceed anyway?" "1"
            RET=$?
            if [ $RET -ne 0 ]; then
                echo "user does not want to continue (good). Aborting suspend." >> $LSMOD_LOG
                progress_finish
                $SCRIPT_RETURN $EV_ID 1 "prepare_sleep failed ($1): $MESSAGE"
                restore_after_sleep "$1"
                EXIT 1;
            fi
            echo "user wants to continue anyway. Good luck." >> $LSMOD_LOG
        fi
    done
    echo "== FAT/NTFS filesystems unmounted ==" >> $LSMOD_LOG
}   ##### unmount_fatfs()

########################################################
# remount_fatfs function
# remounts the filesystems that were unmounted by
# unmount_fatfs()
remount_fatfs(){

    local D I   # dummy, counter
    local      DEV MNT TYP OPT
    declare -a DEV MNT TYP OPT
    
    echo >> $LSMOD_LOG
    echo "Remounting filesystems:" >> $LSMOD_LOG
    I=0
    while read DEV[$I]; do
        read MNT[$I]
        read TYP[$I]
        read OPT[$I]
        let I++
    done < <(awk -F 'unmounted: ' '/^unmounted:/ {
                print $2; getline;
                print $2; getline;
                print $2; getline;
                print $2 }' ${STATE}.resume )

    [ $I -eq 0 ] && echo "  not necessary." >> $LSMOD_LOG

    let I-- # incremented once too many times
    # we remount in reverse order...
    for (( ; I>=0 ; I-- )) ; do
        mount ${DEV[$I]} ${MNT[$I]} ${OPT[$I]} ; D=$?
        if [ $D -eq 0 ];then
            DEBUG "remounted ${DEV[$I]}" DIAG
            echo "  mounted '${DEV[$I]}' to '${MNT[$I]}', options '${OPT[$I]}'" >> $LSMOD_LOG
        else
            DEBUG "unable to remount ${DEV[$I]}" WARN
            echo "  could not mount '${DEV[$I]}' to '${MNT[$I]}', options '${OPT[$I]}', error $D" >> $LSMOD_LOG
        fi
    done
    echo >> $LSMOD_LOG
    
}   # remount_fatfs


#######################################################
# set_variables func
#
# function to set the variables according to what
# we are going to do.
# internally, use only in sleep_helper_functions
DEFAULT_S2D_UNLOAD="usb_storage sbp2 ohci_hcd uhci_hcd stir4200 ohci1394 ipw2200 rt2500 prism54 ath_pci r8169 lt_modem Intel536 Intel537 ndiswrapper"
DEFAULT_S2R_UNLOAD="usb_storage sbp2 ohci_hcd uhci_hcd stir4200 ohci1394 ipw2200 rt2500 prism54 ath_pci r8169 lt_modem Intel536 Intel537 ndiswrapper"
DEFAULT_STB_UNLOAD="usb_storage sbp2 ohci_hcd uhci_hcd stir4200 ohci1394 ipw2200 rt2500 prism54 ath_pci r8169 lt_modem Intel536 Intel537 ndiswrapper"
DEFAULT_S2D_RESTART="slmodemd irda upsd apcupsd"
DEFAULT_S2R_RESTART="slmodemd irda upsd apcupsd"
DEFAULT_STB_RESTART="slmodemd irda upsd apcupsd"

set_variables(){
    case "$1" in
        suspend2disk)
            STATE=$STATE1
            LSMOD_LOG=$LSMOD_LOG1
            EJECT_PCMCIA=$SUSPEND2DISK_EJECT_PCMCIA
            MODULES_TO_UNLOAD="${UNLOAD_MODULES_BEFORE_SUSPEND2DISK:-$DEFAULT_S2D_UNLOAD}"
            SERVICES_TO_RESTART="${SUSPEND2DISK_RESTART_SERVICES:-$DEFAULT_S2D_RESTART}"
            UNMOUNT_FATFS="${SUSPEND2DISK_UNMOUNT_FATFS:-yes}"
            SWITCH_VT="$SUSPEND2DISK_SWITCH_VT"
            ;;
        suspend2ram)
            STATE=$STATE2
            LSMOD_LOG=$LSMOD_LOG2
            EJECT_PCMCIA=$SUSPEND2RAM_EJECT_PCMCIA
            MODULES_TO_UNLOAD="${UNLOAD_MODULES_BEFORE_SUSPEND2RAM:-$DEFAULT_S2R_UNLOAD}"
            SERVICES_TO_RESTART="${SUSPEND2RAM_RESTART_SERVICES:-$DEFAULT_S2R_RESTART}"
            UNMOUNT_FATFS="${SUSPEND2RAM_UNMOUNT_FATFS:-no}"
            SWITCH_VT="$SUSPEND2RAM_SWITCH_VT"
            ;;
        standby)
            STATE=$STATE3
            LSMOD_LOG=$LSMOD_LOG3
            EJECT_PCMCIA=$STANDBY_EJECT_PCMCIA
            MODULES_TO_UNLOAD="${UNLOAD_MODULES_BEFORE_STANDBY:-$DEFAULT_STB_UNLOAD}"
            SERVICES_TO_RESTART="${STANDBY_RESTART_SERVICES:-$DEFAULT_STB_RESTART}"
            UNMOUNT_FATFS="${STANDBY_UNMOUNT_FATFS:-no}"
            SWITCH_VT="$STANDBY_SWITCH_VT"
            ;;
        *)
            echo "Wrong parameter '$1' in set_variables function in sleep_helper_functions script"
            return
            ;;
    esac
}

#######################################################
# prepare the logfiles
# parameter: sleep type
prepare_logs() {
    # here we prepare the statefile that records what we have done
    # and the logfile for users to troubleshoot.
    rm -f $STATE
    rm -f $LSMOD_LOG
    # there should be at least one line in $STATE or we will get an
    # harmless but ugly error in restore_after_sleep.
    echo "# this file records the system state at entering $1." > $STATE
    echo "$1 initiated: `date +'%F %X'`" > $LSMOD_LOG
    echo "Debug info follows here, please include in your bug reports. Thanks." >> $LSMOD_LOG
    echo "--------------------------------------------------------------------" >> $LSMOD_LOG
    echo "Loaded modules:" >> $LSMOD_LOG
    lsmod >> $LSMOD_LOG
    echo >> $LSMOD_LOG
    echo "Memory info:" >> $LSMOD_LOG
    free >> $LSMOD_LOG
    echo >> $LSMOD_LOG
    echo "/proc/cmdline: `cat /proc/cmdline`" >> $LSMOD_LOG
    echo "------------------------------------------------------------------------------" >> $LSMOD_LOG
    echo "========we are going to sleep, preparing.========" >> $LSMOD_LOG
}

#######################################################
# Function to unload modules and stop services for
# the upcoming sleep mode
#
# first parameter is the sleep type:
# - suspend2disk
# - suspend2ram
# - standby

prepare_sleep(){
    local D E X # dummies,counters
    local RET TYPE
    
    [ -z $1 ] && echo "Do not invoke this script from console, it is automatically invoked by the powersave daemon" && EXIT 1

    DEBUG "Stop services: $SERVICES_TO_RESTART" DEBUG
    DEBUG "Modules to unload: $MODULES_TO_UNLOAD" DEBUG

    ####### first check if the machine is shutting down right now.
    echo "== checking runlevel ==" >> $LSMOD_LOG
    D=`runlevel`
    case $D in
        *0|*6)
            echo "  system is shutting down or rebooting. Not suspending. '$D'" >> $LSMOD_LOG
            notify "Shutdown or reboot in progress, not suspending." ERROR CONTINUE $EV_ID
            progress_finish
            $SCRIPT_RETURN $EV_ID 1 "Shutdown/reboot in progress, not suspending"
            EXIT 1
            ;;
    esac
    echo "  no shutdown/reboot in progress, good." >> $LSMOD_LOG

    ####### Check for devices which need to be remounted after suspend #######
    let PERCENT+=$STEP
    [ "$UNMOUNT_FATFS" == "yes" ] && unmount_fatfs "$1" "$EV_ID"

    ####### S t o p   S e r v i c e s   ##########
    let PERCENT+=$STEP
    progress "${_M05}"

    D="$SERVICES_TO_RESTART"
    E=${D:+\'}${D:-none}${D:+\'}
    echo "Stopping services: ($E configured)" >> $LSMOD_LOG
    D=true
    if [ "$SERVICES_TO_RESTART" != "NONE" ]; then
        D=false
        for X in $SERVICES_TO_RESTART; do
            /etc/init.d/$X status >/dev/null 2>&1 ; RET=$? # redirect needed to workaround initscript bug.
            if [ $RET -eq 0 ]; then
                echo "stopping $X:" >> $LSMOD_LOG
                progress "${_M06} $X"
                /etc/init.d/$X stop 2>&1 | awk '{print "##  "$0}' >> $LSMOD_LOG
                echo "stopped: $X" >> $STATE
                DEBUG "Service $X stopped" DIAG
		D=true
            else
                DEBUG "Service $X stop requested but was not running. Return code: '$RET'" INFO
            fi
        done
    fi
    $D || echo "none running." >> $LSMOD_LOG
    ####### S t o p   S e r v i c e s   ##########

    ####### Eject PCMCIA cards          ##########
    let PERCENT+=$STEP
    if [ "$EJECT_PCMCIA" == "yes" ]; then
        progress "${_M03}"
        echo "ejecting PCMCIA cards..." >> $LSMOD_LOG
        $PCCARDCTL eject
    fi

    ####### U n l o a d   M o d u l e s ##########
    let PERCENT+=$STEP
    progress "${_M07}"

    echo >> $LSMOD_LOG
    D="$MODULES_TO_UNLOAD"
    E=${D:+\'}${D:-none}${D:+\'}
    echo "------------------------------------------------------------------------------" >> $LSMOD_LOG
    echo "Unloading modules: ($E configured)" >> $LSMOD_LOG
    if [ "$MODULES_TO_UNLOAD" ]; then
        for module in $MODULES_TO_UNLOAD; do
            [ "$module" == "NONE" ] && continue
            echo "checking $module" >> $LSMOD_LOG
            # unload_module handles not-loaded modules gracefully.
            if ! unload_module $module $STATE $LSMOD_LOG; then
                MESSAGE="$1 failed on unloading '$module'."
                if [ "$UNL" != "$module" -a -n "$UNL" ]; then
                    MESSAGE="$MESSAGE The module that refused to unload was '$UNL'."
                fi
                MESSAGE="$MESSAGE Trying to recover..."
                DEBUG "$MESSAGE" ERROR
                notify "$MESSAGE" ERROR CONTINUE $EV_ID
                progress_finish
                $SCRIPT_RETURN $EV_ID 1 "$MESSAGE"
                restore_after_sleep "$1"
                EXIT 1
            fi
        done
        DEBUG "Modules unloaded" DIAG
    fi
    ####### U n l o a d   M o d u l e s ##########

    let PERCENT+=$STEP
    progress ""

    echo "------------------------------------------------------------------------------" >> $LSMOD_LOG
    echo "prepare_sleep finished for $1" >> $LSMOD_LOG
    echo "------------------------------------------------------------------------------" >> $LSMOD_LOG

}

########################################################
# restore_after_sleep FUNC
#
# Function to load previously unloaded modules and start
# the stopped services for after resuming from a sleep
# sleep mode. Modules are loaded in reverse unloading
# order, services are started in reverse stopping order.
# Only hotplug is an exception: it is always started
# first to handle hotplug events generated by module loading.
#
# give first param:
# suspend2disk
# suspend2ram
# standby
restore_after_sleep() {

    local D X

    [ -z $1 ] && echo "Do not invoke this script from console, it is automatically invoked by the powersave daemon" && EXIT 1

    echo >> $LSMOD_LOG
    echo "== restore_after_sleep: restart and reload everything ==" >> $LSMOD_LOG
    
    # sanity check. We had this once...
    if [ ! -e $STATE ]; then
        echo "WARNING: Statefile '$STATE' disappeared during suspend!" >> $LSMOD_LOG
        DEBUG "Statefile '$STATE' disappeared during suspend." ERROR
    fi

    # clean up after ourselves..
    touch $STATE                        # if it is not there for any reason, create it.
    mv -f $STATE ${STATE}.resume        # now move it out of the way.

    echo >> $LSMOD_LOG
    echo "Resuming:" >> $LSMOD_LOG
    echo "---------" >> $LSMOD_LOG

    switch_to_X
    kick_fan

    # special case: hotplug should be started before inserting modules
    if D=`awk 'BEGIN {X=1} /^stopped: (boot\.|)hotplug/ {print $2; X=0} END {exit X}' ${STATE}.resume`; then
        echo "first starting $D:" >> $LSMOD_LOG
        /etc/init.d/$D start 2>&1 | awk '{print "##  "$0}' >> $LSMOD_LOG
        DEBUG "Service $D started again" DIAG
    fi

#    echo $STATE
    echo >> $LSMOD_LOG
    echo "Reloading modules:" >> $LSMOD_LOG
    ####### L o a d   M o d u l e s       ##########
    if [ "$MODULES_TO_UNLOAD" -a -s "${STATE}.resume" ]; then
        for module in `tac ${STATE}.resume| awk '/^unloaded:/ { print $2 }'`; do
            echo "  $module" >> $LSMOD_LOG
            modprobe $module
        done
        DEBUG "Modules loaded" DIAG
    fi
    # rm ${STATE}.resume # disabled for debugging
    ####### L o a d   M o d u l e s       ##########

    ####### reinsert PCMCIA cards         ##########
    if [ "$EJECT_PCMCIA" == "yes" ]; then
        echo "inserting PCMCIA cards..." >> $LSMOD_LOG
        $PCCARDCTL insert
    fi

    echo >> $LSMOD_LOG
    echo "Restarting services:" >> $LSMOD_LOG
    ####### S t a r t   S e r v i c e s  (but not hotplug or boot.hotplug)  ##########
    if [ "$SERVICES_TO_RESTART" -a -s "${STATE}.resume" ]; then
        for X in `tac ${STATE}.resume | awk '/^stopped:/ { if (($2!="hotplug")&&($2!="boot.hotplug")) print $2 }'`; do
            echo "starting $X:" >> $LSMOD_LOG
            /etc/init.d/$X start 2>&1 | awk '{print "##  "$0}' >> $LSMOD_LOG
            DEBUG "Service $X started again" DIAG
        done
    fi
    ####### S t a r t   S e r v i c e s   ##########

    ####### remount previously unmounted devices #######
    remount_fatfs

    $SCRIPT_RETURN $EV_ID 0 "restore_after_$1 finished"

    return 0
}

#####################################################################
# get_kernels
# gets a list of available kernels from /boot/grub/menu.lst
# kernels are in the array $KERNELS, output to stdout to be eval-ed.
get_kernels(){
    DEBUG "Running get_kernels()" INFO
    local MENU_LST="/boot/grub/menu.lst"
    local I DUMMY
    declare -i I=0 J=-1

    # build an array KERNELS with all the kernels in /boot/grub/menu.lst
    # the array MENU_ENTRIES contains the corresponding menu entry numbers
    # DEFAULT_BOOT contains the default entry.
    while read LINE; do
        case $LINE in
        title*)
            let J++ # increase for every menu entry, even for non-linux
            DEBUG "Found grub menu entry #${J}: '${LINE}'" INFO
            ;;
        default*)
            DUMMY=($LINE)                   # "default 0 #maybe a comment"
            echo "DEFAULT_BOOT=${DUMMY[1]}" #  ^^[0]^^ 1 ^^[2]^ 3 ^^[4]^^
            DEBUG "Default boot entry is '${DUMMY[1]}'" INFO
            ;;
        kernel*)
            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++
            ;;
        *)  ;;
        esac
    done < $MENU_LST
}

#############################################################
# grub-once()
# runs grubonce from the grub package to select which kernel
# to boot on next startup
grub-once() {
    if [ -x "$GRUBONCE" ]; then
        rm -f "$GRUBDEFSAVE"
        if [ -e "$GRUBDEFAULT" ]; then
            echo "saving original $GRUBDEFAULT" >> $LSMOD_LOG
            cp "$GRUBDEFAULT" "$GRUBDEFSAVE"
        fi
        echo "running '$GRUBONCE $1'" >> $LSMOD_LOG
        $GRUBONCE $1
    else
        echo "$GRUBONCE not found, not preparing bootloader" >> $LSMOD_LOG
    fi
}

#############################################################
# grub-once-restore()
# restore grub default after (probably failed) resume
grub-once-restore() {
    rm -f "$GRUBDEFAULT"
    if [ -e "$GRUBDEFSAVE" ]; then
        echo "restoring original $GRUBDEFAULT" >> $LSMOD_LOG
        mv "$GRUBDEFSAVE" "$GRUBDEFAULT"
    fi
}

#############################################################
# progress
# pops up and updates a progress bar.
# needs global variables:
#  PERCENT
#  EV_ID
# takes one argument: the message.
progress() {
    if [ -z "$PERCENT" -o -z "$EV_ID" -o -z "$SCRIPT_RETURN" ]; then
        DEBUG "progress called without variables set." ERROR
        return 1
    fi
    $SCRIPT_RETURN $EV_ID 4 "${PERCENT}${1:+|$1}"
}

#############################################################
# progress_finish
# closes the progress bar
progress_finish() {
    PERCENT=101
    progress ""
}

#############################################################
# switch_to_vt
# switches to text console 1
switch_to_vt() {
    if [ "$SWITCH_VT" = "yes" ]; then
        echo "console no: `fgconsole`" >> $STATE
        chvt 1
    fi
}

#############################################################
# switch_to_X
# switches back to the console we were before suspend
switch_to_X() {
    if [ "$SWITCH_VT" = "yes" ]; then
        local CONS
        CONS=$(awk -F : '/^console no:/ {print $2}' ${STATE}.resume)
        [ -n "$CONS" ] && chvt $CONS
        echo "switched back to console: '$CONS'" >> $LSMOD_LOG
    fi
}

#############################################################
# kick_fan
# triggers the ACPI fan(s) after resume. Since ACPI drivers
# have no suspend support, this is sometimes necessary.
# see http://article.gmane.org/gmane.linux.acpi.devel/16643
kick_fan() {
    local FAN DUMMY STATE 
    for FAN in /proc/acpi/fan/*/state; do
        [ ! -e $FAN ] && continue
        read DUMMY STATE < $FAN
        if [ "$STATE" = "on" ]; then
            DUMMY=${FAN#/proc/acpi/fan/}
            echo "reactivating ACPI fan ${DUMMY%/state}" >> $LSMOD_LOG
            echo -n 3 > $FAN
            echo -n 0 > $FAN
        fi
    done
}
