#!/bin/bash
# Copyright (c) 2000-2002 SuSE Linux AG, Nuernberg, Germany.
# All rights reserved.
#
# Author: Christian Zoz <zoz@suse.de>
# 
# apmd_proxy - program dispatcher for APM daemon
#
# This script is based on the apmd_proxy by
# Craig Markwardt (craigm@lheamail.gsfc.nasa.gov) 21 May 1999
# David Brownell (db@post.harvard.edu) 9 June 1999
# It can be found in /usr/share/doc/packages/apmd/
#
# This shell script is called by the APM daemon (apmd) when the state
# of any power management function has changed.  The exact events that
# trigger the calling of apmd_proxy depend on how apmd was configured
# at compile time.
#
# Within this script the system administrator can put any additional 
# commands or actions which should be performed upon state transitions.
# But for basic customizing you should use the variables in 
# /etc/sysconfig/powermanagement
#
# apmd_proxy is called with specific arguments that describe the event
# that has occurred.  It is this script's responsibility to query the
# hardware or the APM service (via /proc/apm) for more information,
# and to take the appropriate action.
#
# For example, apmd will call "apmd_proxy suspend system" just before
# the system is scheduled to go into suspend mode.  The administrator
# may wish to perform site-specific actions like unloading drivers or
# disabling the network interface.  When the system is resumed later,
# apmd will call "apmd_proxy resume normal", at which time those actions
# should be reversed.
#
# If the kernel APM driver is version 1.10 or higher (use the "apm"
# command to find out), apmd_proxy can return an error code for the
# suspend and standby events, indicating whether the pending mode
# should be rejected.  For example, apmd_proxy may decide if, based on
# CPU or network activity or user instructions, a suspend initiated by
# the APM BIOS should be rejected.
#
#   RETURN VALUE:
#     0 - nominal return; suspend and standby events are accepted
#     1 - reject a suspend or standby (MUST HAVE APM DRIVER 1.10 OR HIGHER)
#
# Here are the calling sequences for apmd_proxy:
#
# apmd_proxy start              - APM daemon has started
# apmd_proxy stop               - APM daemon is shutting down
# apmd_proxy suspend system     - APM system has requested suspend mode
# apmd_proxy suspend critical   - APM system indicates critical suspend (*)
# apmd_proxy standby system     - APM system has requested standby mode 
# apmd_proxy suspend user       - User has requested suspend mode
# apmd_proxy standby user       - User has requested standby mode
# apmd_proxy resume suspend     - System has resumed from suspend mode
# apmd_proxy resume standby     - System has resumed from standby mode
# apmd_proxy resume critical    - System has resumed from critical suspend
# apmd_proxy change battery     - APM system reported low battery
# apmd_proxy change power       - APM system reported AC/battery change
# apmd_proxy change time        - APM system reported need for time update (*)
# apmd_proxy change capability  - APM system reported config. change (+)
#
# (*) - APM daemon may be modified to call these sequences
# (+) - Available if kernel APM driver supports it (driver ver. 1.10 or higher)
#
# SIMPLIFIED CONFIGURATION  
#
# The operation of this script can be controlled either by setting the
# according variables in /etc/sysconfig/powermanagement appropriately.

. /etc/sysconfig/powermanagement

# Make sure that the following vaariables are integer and some >= 0
TMP=$APMD_BATTERY_DISK_TIMEOUT
declare -i APMD_BATTERY_DISK_TIMEOUT=$TMP
test $APMD_BATTERY_DISK_TIMEOUT -lt 0 && APMD_BATTERY_DISK_TIMEOUT=0
TMP=$APMD_AC_DISK_TIMEOUT
declare -i APMD_AC_DISK_TIMEOUT=$TMP
test $APMD_AC_DISK_TIMEOUT -lt 0 && APMD_AC_DISK_TIMEOUT=0
TMP=$APMD_BATTERY_LOW_SHUTDOWN
declare -i APMD_BATTERY_LOW_SHUTDOWN=$TMP

# needed commands
CARDCTL=/sbin/pccardctl
CHVT=/usr/bin/chvt
WTTYHX=/usr/bin/wttyhx
ONACPOWER=/usr/bin/on_ac_power
HDPARM=/sbin/hdparm
CLOCK=/sbin/clock
SHUTDOWN=/sbin/shutdown
LOGGER="/bin/logger -t apmd_proxy[$$]"
XAUTH=/usr/X11R6/bin/xauth
XLOCK=/usr/X11R6/bin/xlock
KBDRATE=/sbin/kbdrate
# used files
STOPPED_IFACE_FILE=/var/lib/apmd/stopped_interfaces

# Checks if one or more given commands are available and executable
command_available () {
  CA=yes
  test -x $1 || CA=no
  shift
  while [ $# -gt 0 ] ; do
    test -x $1 || CA=no
    shift
  done
  test "$CA" = yes && return
  test -z "$APMD_DEBUG" -o "$APMD_DEBUG" = no && return 1
  test -x $LOGGER || return 1
  ${LOGGER} "command $1 not found"
  return 1
}

# Kills all applications that are currently using any sounddevice
killsoundapps_old () {
  DEVS=`find /dev/ -follow -maxdepth 2 -type c -ls 2>/dev/null |
          sed -n '/1*1[46],/s/^.*\(\/dev\/.*\)$/\1/p'`
  fuser -k $DEVS
  sleep 1
  fuser -9 -k $DEVS
}

unload_modules() {
  while [ -n "$*" ] ; do
    echo $*
    if ! lsmod | grep -qs $1; then
      shift
      continue
    fi
    if ! rmmod -r $1 ; then
      OLD=$*
      set -- `lsmod | sed -n "/^$1 /s/^.*\[\(.*\)\].*$/\1/p"` $*
      test "$OLD" = "$*" && return 1
      continue
    else
      shift
    fi
  done
}                                                                               

# DEBUGGING

case "$APMD_DEBUG" in
  yes)
    # METHOD 1 - Logs command line arguments of apmd_proxy only
    ${LOGGER} $*
    ;;
  error)
    # METHOD 2 - Logs cmd line args, errors and any ordinary output
    ${LOGGER} $*
    exec  > >(${LOGGER}-1)
    exec 2> >(${LOGGER}-2)
    ;;
  all)
    # METHOD 3 - Logs every command executed by apmd_proxy and anything else
    ${LOGGER} $*
    exec  > >(${LOGGER}-1)
    exec 2> >(${LOGGER}-2)
    set -x
    ;;
esac

# Start of main procedure.

umask 022

# If there is no X running or X is version 4, don't change the virtual terminal
if command_available $WTTYHX ; then
  while read XTTY XUSER DISPLAY XVERSION; do
    break
  done < <($WTTYHX -v)
  : XTTY=$XTTY
  : XUSER=$XUSER
  : DISPLAY=$DISPLAY
  : XVERSION=$XVERSION
  if [ "$XVERSION" = 4 ] ; then
    APMD_LEAVE_X_BEFORE_STANDBY=no
    APMD_LEAVE_X_BEFORE_SUSPEND=no
  fi
else
  XTTY=7
  XUSER=""
fi

case "$1" in

  # --------------------------- STANDBY -------------------------------
  "standby")
      # 
      if [ "$APMD_LEAVE_X_BEFORE_STANDBY" = yes ] ; then
        if command_available $CHVT ; then
          $CHVT 1
          sleep 1
        fi
      fi
      ;;

  # --------------------------- SUSPEND -------------------------------
  "suspend")
      # Depending on 'APMD_SUSPEND_ON_AC' this segment disables normal suspend
      # events when you are on AC power.  This segment only works if
      # your APM BIOS sends "suspend system" events after an idle
      # period.  Also, you must be running a Linux kernel APM
      # driver version 1.10 or higher (run "apm" to find out).
      #
      if [ "$APMD_SUSPEND_ON_AC" = no -a "$2" = system ]; then
  	if command_available $ONACPOWER ; then
  	  $ONACPOWER -q && exit 1  # Reject
  	fi 
      fi

      # Lock all X displays
      if [ "$APMD_LOCK_X_ON_SUSPEND" = "yes" ] ; then
        if command_available $XLOCK $WTTYHX; then
          while read XUSER XDISP; do
            su $XUSER -c "$XLOCK ${XDISP:+-display $XDISP} \
                                 ${APMD_LOCK_MODE:+-mode $APMD_LOCK_MODE} \
                         " &>/dev/null </dev/null &
          done < <($WTTYHX -a)
        fi
      fi

      # There are some weired notebooks that do not resume properly from
      # suspend when the harddisk was in DMA mode. (e.g. some FSC)
      for DISK in $APMD_TURN_OFF_IDEDMA_BEFORE_SUSPEND; do
        if command_available $HDPARM ; then
          $HDPARM -q -d 0 /dev/$DISK
        fi
      done

      # Make sure all buffered data get flushed on disk on case of
      # extreme setting of kupdated/bdflush. There is a second sync at
      # the very end of this section
      sync 

      # Activate this segment if you wish to disable PCMCIA services
      # upon suspend events.  The PCMCIA driver nominally will
      # suspend all cards before reaching this point, but certain
      # cards cannot be suspended properly (notably, SCSI cards).
      # These cards must be forcefully software-ejected.  If you
      # uncomment this code, then be sure to also uncomment the
      # corresponding segment under RESUME.  Calling "cardctl
      # suspend" is needed for systems whose PCMCIA modules are
      # available but not APM-aware.  Calling it more than once is
      # not harmful.
      #
      
      if [ "$APMD_PCMCIA_EJECT_ON_SUSPEND" = yes ] ; then
  	if command_available $CARDCTL ; then
  	  $CARDCTL eject
  	fi
      else
        if [ "$APMD_PCMCIA_SUSPEND_ON_SUSPEND" = yes ] ; then
          if command_available $CARDCTL ; then
            $CARDCTL suspend
          fi
        fi
      fi

      # Sometimes we have to stop the network interface for build in NICS
      echo > $STOPPED_IFACE_FILE
      for IFACE in $APMD_INTERFACES_TO_STOP; do
        /sbin/ifstatus $IFACE && echo $IFACE >> $STOPPED_IFACE_FILE
        /sbin/ifdown $IFACE 
      done
      # And sometimes we have even to unload the module
      for IFACE in $APMD_INTERFACES_TO_UNLOAD; do
        /sbin/ifstatus $IFACE && echo $IFACE >> $STOPPED_IFACE_FILE
        /sbin/ifdown $IFACE 
        /sbin/modprobe -r $IFACE
      done
  
      # Uncomment this segment if your graphics card does not resume
      # in graphics mode properly (ie, in X windows).  This action
      # changes the screen to virtual console number 1, which is
      # usually a text console.  Upon resume, you will need to
      # change back to your X console manually.
      
      if [ "$APMD_LEAVE_X_BEFORE_SUSPEND" = yes ] ; then
  	if command_available $CHVT ; then
  	  $CHVT 1
  	  sleep 1
  	fi
      fi
      
      # other common actions:  unload troublesome drivers
      # EXAMPLE: OSS sound may not suspend/resume properly
      #    - Unload the drivers here and then reload upon resume
      #      Path may vary.  Be sure to enable "soundon" below.
      #	/usr/local/bin/soundoff

      case "$APMD_STOP_SOUND_BEFORE_SUSPEND" in 
        "alsa")
          killsoundapps
          /usr/sbin/rcalsasound stop
          ;;
        "oss")
          killsoundapps
          soundoff
          ;;
        "kernel")
          killsoundapps
          # to be improved
          # rmmod -r sound
	  # improved version
	  unload_modules sound
          ;;
      esac

      # Make sure all buffered data get flushed on disk on case of
      # extreme setting of kupdated/bdflush. 
      sync
      ;;

  # ------------------------------- RESUME ---------------------------
  "resume")

      # Lock all X displays. If it failed somwhow when suspendig, lets do it again.
      if [ "$APMD_LOCK_X_ON_SUSPEND" = "yes" ] ; then
        if command_available $XLOCK $WTTYHX; then
          while read XUSER XDISP; do
            su $XUSER -c "$XLOCK ${XDISP:+-display $XDISP} \
                                 ${APMD_LOCK_MODE:+-mode $APMD_LOCK_MODE} \
                         " &>/dev/null </dev/null &
          done < <($WTTYHX -a)
        fi
      fi

      # Typically the Linux system clock needs to be reset.
      # NOTE:  APM BIOS drivers in kernel 2.2 and later have handled this, but
      # on some machines it doesn't work properly.
      if [ "$APMD_SET_CLOCK_ON_RESUME" = "yes" ] ; then
  	if command_available $CLOCK ; then
  	  $CLOCK -s $GMT
  	fi 
      fi
  
      if [ -n "$APMD_KBD_RATE" -o -n "$APMD_KBD_DELAY" ] ; then
        if command_available "$KBDRATE" ; then
          $KBDRATE ${APMD_KBD_RATE:+-r $APMD_KBD_RATE} \
                   ${APMD_KBD_DELAY:+-d $APMD_KBD_DELAY}
        fi
      fi

      # If we resume from standby there is nothing more to do
      test "$2" != suspend && exit 0

      # There are some weired notebooks that do not resume properly from
      # suspend when the harddisk was in DMA mode. (e.g. some FSC)
      for DISK in $APMD_TURN_OFF_IDEDMA_BEFORE_SUSPEND; do
        if command_available $HDPARM ; then
          $HDPARM -q -d 1 /dev/$DISK
        fi
      done

      # Reactivate stopped or unloaded network interfaces
      #for IFACE in $APMD_INTERFACES_TO_STOP $APMD_INTERFACES_TO_UNLOAD; do
      for IFACE in `cat $STOPPED_IFACE_FILE`; do
        /sbin/ifup $IFACE
      done

      # Activate this segment if you "ejected" PCMCIA cards above.
      # The default operation is to "resume", which is required for
      # systems whose PCMCIA modules are not APM-aware.
      #
      if [ "$APMD_PCMCIA_EJECT_ON_SUSPEND" = yes ] ; then
  	if command_available "$CARDCTL" ; then
          sleep 1
  	  $CARDCTL insert
  	fi
      else
        if [ "$APMD_PCMCIA_SUSPEND_ON_SUSPEND" = yes ] ; then
  	  if command_available "$CARDCTL" ; then
  	    $CARDCTL resume
  	  fi
        fi
      fi
      
      # return to X
      if [ "$APMD_LEAVE_X_BEFORE_SUSPEND" = yes ] ; then
  	if command_available $CHVT ; then
  	  $CHVT $XTTY
  	fi
      fi
      
      # other common actions:  reload troublesome drivers
      # EXAMPLE: reload OSS sound drivers.  Path may vary.
      #	/usr/local/bin/soundon
      case "$APMD_STOP_SOUND_BEFORE_SUSPEND" in
        "alsa")
          /usr/sbin/rcalsasound start
          ;;
        "oss")
          soundon
          ;;
        "kernel")
          # nothing to be done
	  cat /dev/sndstat
          ;;
      esac

      # Adjust disk performance like everytime when power changes
      $0 change power

      ;;

  # ------------------------------- START ----------------------------
  # Called when apmd first starts.
  # If we are on battery power, then attempt to "conserve" power.
  "start")
      # Remove stale temopary files
      rm -f $STOPPED_IFACE_FILE
      # Adjust disk performance like everytime when power changes
      $0 change power
      ;;

  # ------------------------------- STOP -----------------------------
  # Called when apmd is terminated.
  "stop")
      ;; 

  # ------------------------ CHANGE in STATUS ------------------------
  "change")
      case $2 in
        "power")
            # Set disk performance depending to the state of powersupply
            if [ "$APMD_ADJUST_DISK_PERF" = yes ] ; then
              if command_available $ONACPOWER $HDPARM ; then
                if $ONACPOWER -q ; then
                  $HDPARM -S "$APMD_AC_DISK_TIMEOUT" /dev/hda
                else
                  $HDPARM -S "$APMD_BATTERY_DISK_TIMEOUT" /dev/hda
                fi
              fi
            fi

            # Cancel a running shutdown if on AC power in time
            if [ "$APMD_BATTERY_LOW_SHUTDOWN" -gt 0 \
                 -a -e /var/run/shutdown.pid ] ; then
              if command_available $ONACPOWER $SHUTDOWN ; then
                $ONACPOWER -q && $SHUTDOWN -c
                 if [ -e /var/run/shutdown.pid ]; then
                        echo "Warning: Shutdown still running!"
                 else
                        echo "AC Power back! Shutdown canceled!"
                 fi | wall
              fi &
            fi

            ;;
        "battery")

            # battery is at "low" level (BIOS defines -- e.g. as 5%)
            if [ "$APMD_BATTERY_LOW_SHUTDOWN" -gt 0 ] ; then
              if command_available $SHUTDOWN ; then
                startproc $SHUTDOWN -h +$APMD_BATTERY_LOW_SHUTDOWN \
                          >/dev/null 2>&1 </dev/null &
              fi &
            fi

            ;;
        "capability")
            # e.g. added new hardware (not battery!)
            ;;
      esac
      ;;
esac

exit 0

