#! /bin/sh
#
# Script to start or stop laptop_mode, and to control various settings of the
# kernel, hardware etc. that influence power consumption.
#
# This script is a part of Laptop Mode Tools. If you are running a supported
# power management daemon, this script will be automatically called on power
# state change.
#
# Configure laptop mode tools in /etc/laptop-mode/laptop-mode.conf, and in
# the broken-out config files in /etc/laptop-mode/conf.d.
#
# Please consult the manual pages laptop-mode.conf(8) and laptop_mode(8) for
# additional information.
#
# Maintainer:        Bart Samwel (bart@samwel.tk)
# Project home page: http://samwel.tk/laptop_mode
#
# Contributors to this script:   Bart Samwel
#				 Kiko Piris
#				 Micha Feigin
#				 Andrew Morton
#				 Herve Eychenne
#				 Dax Kelson
#				 Jan Polacek
#				 ... and many others that I've stopped
#				 keeping track of.
#
# Based on a script for Linux 2.4 written by Jens Axboe.
#
#############################################################################

set -a

# The laptop mode tools version number. Extracted by the installer makefile
# as well, so don't change the format!
LMTVERSION=1.47

# This script is loaded from multiple scripts to set the config defaults
# and to read the configuration on top of those. Only when the command is
# recognized does this script do anything else.
VERBOSE_OUTPUT=0
ENABLE_LAPTOP_MODE_ON_BATTERY=1
ENABLE_LAPTOP_MODE_ON_AC=0
ENABLE_LAPTOP_MODE_WHEN_LID_CLOSED=0
PARTITIONS="auto /dev/mapper/*"
LM_BATT_MAX_LOST_WORK_SECONDS=600
LM_AC_MAX_LOST_WORK_SECONDS=360
DEF_MAX_AGE=30
LM_READAHEAD=3072
NOLM_READAHEAD=128
CONTROL_READAHEAD=1	
CONTROL_NOATIME=0
USE_RELATIME=1
CONTROL_HD_IDLE_TIMEOUT=1
LM_AC_HD_IDLE_TIMEOUT=4    # 20 seconds
LM_BATT_HD_IDLE_TIMEOUT=4  # 20 seconds
NOLM_HD_IDLE_TIMEOUT=244   # 2 hours
DEF_UPDATE=5
DEF_XFS_AGE_BUFFER=15
DEF_XFS_SYNC_INTERVAL=30
DEF_XFS_BUFD_INTERVAL=1
XFS_HZ=100
CONTROL_MOUNT_OPTIONS=1
BATT_HD_POWERMGMT=1
LM_AC_HD_POWERMGMT=254
NOLM_AC_HD_POWERMGMT=254
CONTROL_HD_POWERMGMT=0
CONTROL_HD_WRITECACHE=0
NOLM_AC_HD_WRITECACHE=1
NOLM_BATT_HD_WRITECACHE=0
LM_HD_WRITECACHE=0
LM_DIRTY_RATIO=60
LM_DIRTY_BACKGROUND_RATIO=1
NOLM_DIRTY_BACKGROUND_RATIO=10
NOLM_DIRTY_RATIO=40
LM_SECONDS_BEFORE_SYNC=2	
BATT_CPU_MAXFREQ=medium
BATT_CPU_MINFREQ=slowest
BATT_CPU_GOVERNOR=ondemand
BATT_CPU_IGNORE_NICE_LOAD=1
LM_AC_CPU_MAXFREQ=fastest
LM_AC_CPU_MINFREQ=slowest
LM_AC_CPU_GOVERNOR=ondemand
LM_AC_CPU_IGNORE_NICE_LOAD=1
NOLM_AC_CPU_MAXFREQ=fastest
NOLM_AC_CPU_MINFREQ=slowest
NOLM_AC_CPU_GOVERNOR=ondemand
NOLM_AC_CPU_IGNORE_NICE_LOAD=0
CONTROL_CPU_FREQUENCY=0
HD="/dev/[hs]d[abcdefgh]"
CONTROL_SYSLOG_CONF=0
LM_AC_SYSLOG_CONF=/etc/syslog-on-ac-with-lm.conf
NOLM_AC_SYSLOG_CONF=/etc/syslog-on-ac-without-lm.conf
BATT_SYSLOG_CONF=/etc/syslog-on-battery.conf
SYSLOG_CONF_SIGNAL_PROGRAM=syslogd
SYSLOG_CONF=/etc/syslog.conf
CONTROL_DPMS_STANDBY=0
BATT_DPMS_STANDBY=300
LM_AC_DPMS_STANDBY=1200
NOLM_AC_DPMS_STANDBY=1200
CONTROL_CPU_THROTTLING=0
BATT_CPU_THROTTLING=medium
LM_AC_CPU_THROTTLING=minimum
NOLM_AC_CPU_THROTTLING=minimum
CONTROL_START_STOP=1
CONTROL_TERMINAL=0
TERMINALS="/dev/vc/1 /dev/vcs1"
BATT_TERMINAL_BLANK_MINUTES=1
BATT_TERMINAL_POWERDOWN_MINUTES=2
LM_AC_TERMINAL_BLANK_MINUTES=10
LM_AC_TERMINAL_POWERDOWN_MINUTES=10
NOLM_AC_TERMINAL_BLANK_MINUTES=10
NOLM_AC_TERMINAL_POWERDOWN_MINUTES=50
ENABLE_AUTO_HIBERNATION=0
HIBERNATE_COMMAND=/usr/sbin/hibernate
AUTO_HIBERNATION_ON_CRITICAL_BATTERY_LEVEL=1
DISABLE_LAPTOP_MODE_ON_CRITICAL_BATTERY_LEVEL=1
AUTO_HIBERNATION_BATTERY_CHARGE_MAH=0
AUTO_HIBERNATION_BATTERY_CHARGE_MWH=0
MINIMUM_BATTERY_CHARGE_MAH=0
MINIMUM_BATTERY_CHARGE_MWH=0
ASSUME_SCSI_IS_SATA=1
CONTROL_BRIGHTNESS=0
BATT_BRIGHTNESS_COMMAND=false
LM_AC_BRIGHTNESS_COMMAND=false
NOLM_AC_BRIGHTNESS_COMMAND=false

# No default on these ones -- we need to detect if they have been set, for
# backward compatibility with MINIMUM_BATTERY_MINUTES etc.
AUTO_HIBERNATION_BATTERY_CHARGE_PERCENT=
MINIMUM_BATTERY_CHARGE_PERCENT=

# Backward compatibility variable that is sometimes
# set externally (Debian init system)
unset VERBOSE

# Source config. Some config settings have been moved from the main config file
# to modular configuration files, and to support existing laptop-mode.conf
# files from earlier versions, we source the modular configuration files FIRST.
if [ -d /etc/laptop-mode/conf.d ] ; then
	for CONF in /etc/laptop-mode/conf.d/*.conf ; do
		if [ -r "$CONF" ] ; then
			. "$CONF"
		else
			echo "Warning: Configuration file $CONF is not readable, skipping."
		fi
	done
fi
if [ -r /etc/laptop-mode/laptop-mode.conf ] ; then
	. /etc/laptop-mode/laptop-mode.conf
else
	echo "$0: Configuration file /etc/laptop-mode/laptop-mode.conf not present or not readable."
	exit 1
fi

# Support for old config settings
if [ "$AC_HD" != "" ] ; then
	AC_HD_WITHOUT_LM="$AC_HD"
	AC_HD_WITH_LM="$AC_HD"
fi
if [ "$VERBOSE" != "" ] ; then
	VERBOSE_OUTPUT="$VERBOSE"
fi
if [ "$CPU_MAXFREQ" != "" ] ; then
	BATT_CPU_MAXFREQ="$CPU_MAXFREQ"
fi
if [ "$MAX_AGE" != "" ] ; then
	LM_BATT_MAX_LOST_WORK_SECONDS="$MAX_AGE"
	LM_AC_MAX_LOST_WORK_SECONDS="$MAX_AGE"
fi
if [ "$DEF_AGE" != "" ] ; then
	DEF_MAX_AGE="$DEF_AGE"
fi
if [ "$LAPTOP_MODE_ALWAYS_ON" != "" ] ; then
	ENABLE_LAPTOP_MODE_ALWAYS="$LAPTOP_MODE_ALWAYS_ON"
fi
if [ "$LM_WHEN_LID_CLOSED" != "" ] ; then
	ENABLE_LAPTOP_MODE_WHEN_LID_CLOSED="$LM_WHEN_LID_CLOSED"
fi
if [ "$REMOUNT_PARTITIONS" != "" ] ; then
	PARTITIONS="$REMOUNT_PARTITIONS"
fi
if [ "$READAHEAD" != "" ] ; then
	LM_READAHEAD="$READAHEAD"
fi
if [ "$DO_REMOUNT_NOATIME" != "" ] ; then
	CONTROL_NOATIME="$DO_REMOUNT_NOATIME"
fi
if [ "$DO_HD" != "" ] ; then
	CONTROL_HD_IDLE_TIMEOUT="$DO_HD"
fi
if [ "$AC_HD_WITH_LM" != "" ] ; then
	LM_AC_HD_IDLE_TIMEOUT="$AC_HD_WITH_LM"
fi
if [ "$AC_HD_WITHOUT_LM" != "" ] ; then
	NOLM_HD_IDLE_TIMEOUT="$AC_HD_WITHOUT_LM"
fi
if [ "$BATT_HD" != "" ] ; then
	LM_BATT_HD_IDLE_TIMEOUT="$BATT_HD"
fi		
if [ "$DO_REMOUNTS" != "" ] ; then
	CONTROL_MOUNT_OPTIONS="$DO_REMOUNTS"
fi	
if [ "$DO_HD_POWERMGMT" != "" ] ; then
	CONTROL_HD_POWERMGMT="$DO_HD_POWERMGMT"
fi
if [ "$AC_HDPARM_POWERMGMT_WITH_LM" != "" ] ; then
	LM_AC_HD_POWERMGMT="$AC_HDPARM_POWERMGMT_WITH_LM"
fi
if [ "$AC_HDPARM_POWERMGMT_WITHOUT_LM" != "" ] ; then
	NOLM_AC_HD_POWERMGMT="$AC_HDPARM_POWERMGMT_WITHOUT_LM"
fi
if [ "$BATT_HDPARM_POWERMGMT" != "" ] ; then
	BATT_HD_POWERMGMT="$BATT_HDPARM_POWERMGMT"
fi
if [ "$DO_WRITECACHE" != "" ] ; then
	CONTROL_HD_WRITECACHE="$DO_WRITECACHE"
fi
if [ "$AC_WRITECACHE_WITHOUT_LM" != "" ] ; then
	NOLM_AC_HD_WRITECACHE="$AC_WRITECACHE_WITHOUT_LM"
fi
if [ "$BATT_WRITECACHE" != "" ] ; then
	LM_HD_WRITECACHE="$BATT_WRITECACHE"
fi
if [ "$DIRTY_RATIO" != "" ]; then
	LM_DIRTY_RATIO="$DIRTY_RATIO"
fi
if [ "$DIRTY_BACKGROUND_RATIO" != "" ] ; then
	LM_DIRTY_BACKGROUND_RATIO="$DIRTY_BACKGROUND_RATIO"
fi
if [ "$DEF_DIRTY_RATIO" != "" ]; then
	NOLM_DIRTY_RATIO="$DEF_DIRTY_RATIO"
fi
if [ "$DEF_DIRTY_BACKGROUND_RATIO" != "" ] ; then
	NOLM_DIRTY_BACKGROUND_RATIO="$DEF_DIRTY_BACKGROUND_RATIO"
fi
if [ "$DO_CPU" != "" ] ; then
	CONTROL_CPU_FREQUENCY="$DO_CPU"
fi
if [ "$CONTROL_CPU_MAXFREQ" != "" ] ; then
	CONTROL_CPU_FREQUENCY="$CONTROL_CPU_MAXFREQ"
fi
if [ "$AC_CPU_MAXFREQ_WITH_LM" != "" ] ; then
	LM_AC_CPU_MAXFREQ="$AC_CPU_MAXFREQ_WITH_LM"
fi
if [ "$AC_CPU_MAXFREQ_WITHOUT_LM" != "" ] ; then
	NOLM_AC_CPU_MAXFREQ="$AC_CPU_MAXFREQ_WITHOUT_LM"
fi
if [ "$DO_SYSLOG" != "" ] ; then
	CONTROL_SYSLOG_CONF="$DO_SYSLOG"
fi
if [ "$SYSLOG_SIGNAL_PROGRAM" != "" ] ;then
	SYSLOG_CONF_SIGNAL_PROGRAM="$SYSLOG_SIGNAL_PROGRAM"
fi
if [ "$AC_SYSLOG_WITH_LM" != "" ] ; then
	LM_AC_SYSLOG_CONF="$AC_SYSLOG_WITH_LM"
fi
if [ "$AC_SYSLOG_WITHOUT_LM" != "" ] ; then
	NOLM_AC_SYSLOG_CONF="$AC_SYSLOG_WITHOUT_LM"
fi
if [ "$BATT_SYSLOG" != "" ] ; then
	BATT_SYSLOG_CONF="$BATT_SYSLOG"
fi
if [ "$ENABLE_LAPTOP_MODE_ALWAYS" != "" ] ; then
	ENABLE_LAPTOP_MODE_ON_AC="$ENABLE_LAPTOP_MODE_ALWAYS"
fi
if [ "$MINIMUM_BATTERY_MINUTES" != "" -a "$MINIMUM_BATTERY_CHARGE_PERCENT" = "" ] ; then
	# Use a very conservative estimate (1% = 1 battery minute, 100 minutes in a battery)
	# for backward compatibility.
	MINIMUM_BATTERY_CHARGE_PERCENT="$MINIMUM_BATTERY_MINUTES"
fi
if [ -z "$MINIMUM_BATTERY_CHARGE_PERCENT" ] ; then
	# Apply the default, now that we've determined that this is the minimum.
	MINIMUM_BATTERY_CHARGE_PERCENT=3
fi
if [ "$AUTO_HIBERNATION_BATTERY_MINUTES" != "" -a "$AUTO_HIBERNATION_BATTERY_CHARGE_PERCENT" = "" ] ; then
	# Use a very conservative estimate (1% = 1 battery minute, 100 minutes in a battery)
	# for backward compatibility.
	AUTO_HIBERNATION_BATTERY_CHARGE_PERCENT="$AUTO_HIBERNATION_BATTERY_MINUTES"
fi
if [ -z "$AUTO_HIBERNATION_BATTERY_CHARGE_PERCENT" ] ; then
	# Apply the default, now that we've determined that this is the minimum.
	AUTO_HIBERNATION_BATTERY_CHARGE_PERCENT=2
fi

# Postprocessing
if [ "$VERBOSE_OUTPUT" -ne 0 ] ; then
	OUTPUT="/dev/stdout"
	LM_VERBOSE="[ 1 = 1 ]"
else
	OUTPUT="/dev/null"
	LM_VERBOSE="[ 1 = 0 ]"
fi

if [ "$PARTITIONS" = "" ] ; then
	PARTITIONS="auto /dev/mapper/*"
fi


# Expand shell wild cards immediately.
PARTITIONS=$( echo $PARTITIONS )
TERMINALS=$( echo $TERMINALS )

# Convert seconds to hdparm -S format
# Everything over 20 minutes is interpreted as 2 hours.
seconds_to_hdparm_S() {
  if [ "$1" -eq 0 ] ; then
	# disable.
	echo 0
 elif [ "$1" -gt 0 -a "$1" -lt 5 ] ; then
	# 5 seconds minimum
	echo 1 	
  elif [ "$1" -le $((240*5)) ] ; then
	# Values between 1 and 240 signify increments of 5 seconds
	echo $(($1 / 5))
  elif [ "$1" -lt $((30*60)) ] ; then
	# Values between 20 and 30 minutes are rounded up to 30 minutes.
	echo 241
  elif [ "$1" -lt $((12*30*60)) ] ; then
	# Values between 30 minutes and 6 hours (exclusive) yield values between
	# 241 and 251, in 30-minute increments.
	echo $(( 240 + ($1 / (30*60)) ))
  else
	# Larger values effectively indicate no timeout at all.
	echo 0
  fi
}

# Convert configured idle timeouts to hdparm -S format.
if [ "$LM_AC_HD_IDLE_TIMEOUT_SECONDS" != "" ] ; then
	LM_AC_HD_IDLE_TIMEOUT=$(seconds_to_hdparm_S $LM_AC_HD_IDLE_TIMEOUT_SECONDS)
fi
if [ "$LM_BATT_HD_IDLE_TIMEOUT_SECONDS" != "" ] ; then
	LM_BATT_HD_IDLE_TIMEOUT=$(seconds_to_hdparm_S $LM_BATT_HD_IDLE_TIMEOUT_SECONDS)
fi
if [ "$NOLM_HD_IDLE_TIMEOUT_SECONDS" != "" ] ; then
	NOLM_HD_IDLE_TIMEOUT=$(seconds_to_hdparm_S $NOLM_HD_IDLE_TIMEOUT_SECONDS)
fi

if [ "$1" = "status" ] ; then
	# Display a status report.
	echo "Mounts:"
	mount | sed "s/^/   /"
	echo
	echo "Drive power status:"
	for disk in $HD; do
		if [ -r $disk ]; then
			hdparm -C $disk 2>/dev/null | sed "s/^/   /"
		else
			echo "   Cannot read $disk, permission denied - $0 needs to be run as root"
		fi
	done
	echo
	echo "(NOTE: drive settings affected by Laptop Mode cannot be retrieved.)"
	echo
	echo "Readahead states:"
	cat /etc/mtab | while read DEV MP FST OPTS DUMP PASS ; do
		# skip funny stuff
		case "$FST" in 
		  rootfs|unionfs|tmpfs|squashfs|sysfs|usbfs|proc|devpts) continue
		  ;;
		esac
		if [ -b $DEV ] ; then
			if [ -r $DEV ] ; then
				echo "   $DEV: $((`blockdev --getra $DEV` / 2)) kB"
			else
				echo "   Cannot read $DEV, permission denied - $0 needs to be run as root"
			fi
		fi
	done
	echo
	if [ -e /var/run/laptop-mode-tools/enabled ] ; then
		echo "Laptop Mode Tools is allowed to run: /var/run/laptop-mode-tools/enabled exists."
	else
		echo "Laptop Mode Tools is NOT allowed to run: /var/run/laptop-mode-tools/enabled does not exist."
	fi
	echo
	STATFILES="/proc/sys/vm/laptop_mode /proc/apm /proc/pmu/info /proc/sys/vm/bdflush /proc/sys/vm/dirty_ratio /proc/sys/fs/xfs/age_buffer /proc/sys/fs/xfs/sync_interval /proc/sys/fs/xfs/lm_age_buffer /proc/sys/fs/xfs/lm_sync_interval /proc/sys/vm/pagebuf/lm_flush_age /proc/sys/fs/xfs/xfsbufd_centisecs /proc/sys/fs/xfs/xfssyncd_centisecs /proc/sys/vm/dirty_background_ratio /proc/sys/vm/dirty_expire_centisecs /proc/sys/fs/xfs/age_buffer/centisecs /proc/sys/vm/dirty_writeback_centisecs /sys/devices/system/cpu/*/cpufreq/cpuinfo_*_freq /sys/devices/system/cpu/*/cpufreq/scaling_governor /proc/acpi/button/lid/*/state /proc/acpi/ac_adapter/*/state /proc/acpi/battery/*/state /sys/class/power_supply/*/online /sys/class/power_supply/*/state"
	for THISFILE in $STATFILES ; do
		if [ -e "$THISFILE" ] ; then
			echo "$THISFILE:"
			if [ -r "$THISFILE" ] ; then
				cat "$THISFILE" | sed "s/^/   /"
			else
				echo "   Not accessible, permission denied - $0 needs to be run as root."
			fi
			echo
		fi
	done

elif [ "$1" != "readconfig" -a "$1" != "defaults" ] ; then

#############################################################################

KLEVEL="$(uname -r |
			 {
				 IFS='.-' read a b c
				 echo $a.$b
			 }
)"
KMINOR="$(uname -r |
			{
				IFS='.-' read a b c d
				# Strip any stuff from the end -- only the initial digits are part of the KMINOR.
				echo $c | sed -e 's/\([[:digit:]]*\).*/\1/'
			}
)"

case "$KLEVEL" in
	"2.4" ) ;;
	"2.6" ) ;;
	*)
		echo "Unhandled kernel version: $KLEVEL ('uname -r' = '$(uname -r)')" >&2
		exit 1
		;;
esac

# Stop exporting everything -- what we do from here is private.
set +a

if [ "$1" = "--version" ] ; then
	echo "Laptop Mode Tools $LMTVERSION"
	exit 0
fi
echo "Laptop Mode Tools $LMTVERSION" >> $OUTPUT

if [ ! -e /proc/sys/vm/laptop_mode ] ; then
	echo "Kernel does not have support for laptop mode. Please apply the laptop mode" >&2
	echo "patch or install a newer kernel." >&2
	exit 1
fi

if [ ! -w /proc/sys/vm/laptop_mode ] ; then
	echo "You do not have enough privileges to enable laptop_mode." >&2
	exit 1
fi

INIT=0          # Display info in init script format?
FORCE=0         # Force reapplying the current state?
while [ "$1" != "" ] ; do
	case "$1" in 
		init) INIT=1 ;;
		force) FORCE=1 ;;
		# Old options. We always do "auto" for any option now, but
		# we still have to accept the options.
		start) ;;
		stop) ;;
		auto) ;;
		*) echo "Unrecognized option $1."
		   exit 1 ;;
	esac
	shift
done

mkdir -p /var/run/laptop-mode-tools

# Used to display laptop mode state later on. This is the enabled/disabled 
# state for laptop mode processing, it tells us nothing about whether laptop
# mode is actually _active_.
STATE=enabled
if [ "$ENABLE_LAPTOP_MODE_ON_BATTERY" -eq 0 -a "$ENABLE_LAPTOP_MODE_ON_AC" -eq 0 -a "$ENABLE_LAPTOP_MODE_WHEN_LID_CLOSED" -eq 0 ] ; then
	STATE=disabled
fi

# Determine the power state.

# First try /sys/class/power_supply/*
FOUND_SYS_CLASS_POWER_SUPPLY_AC=0
ON_AC=0
for POWER_SUPPLY in /sys/class/power_supply/* ; do
	if [ -f $POWER_SUPPLY/type ] ; then
		if [ "$(cat $POWER_SUPPLY/type)" = "Mains" ] ;then
			$LM_VERBOSE && echo "Determining power state from $POWER_SUPPLY/online." >> $OUTPUT
			FOUND_SYS_CLASS_POWER_SUPPLY_AC=1
			if [ "$(cat $POWER_SUPPLY/online)" = 1 ] ; then
				ON_AC=1
			fi
		fi
	fi
done

if [ $FOUND_SYS_CLASS_POWER_SUPPLY_AC = 1 ] ; then
	# Already found it!
	$LM_VERBOSE && echo "Not trying other options, already found a power supply." >> $OUTPUT
elif [ -d /proc/acpi/ac_adapter ] ; then
	$LM_VERBOSE && echo "Determining power state from /proc/acpi/ac_adapter." >> $OUTPUT
	ADAPTERS_FOUND=0
	ON_AC=0
	for ADAPTER in /proc/acpi/ac_adapter/* ; do
		if [ -f $ADAPTER/state ] ; then
			ADAPTERS_FOUND=1
			STATUS=`awk '/^state: / { print $2 }' $ADAPTER/state`
			if [ "$STATUS" = "on-line" ] ; then
				ON_AC=1
			fi
		fi
	done
	if [ "$ADAPTERS_FOUND" -eq 0 ] ; then
		ON_AC=1
	fi
elif [ -f /proc/pmu/info ] ; then
	$LM_VERBOSE && echo "Determining power state from /proc/pmu/info." >> $OUTPUT
	if ( grep -q "^AC Power.*0$" /proc/pmu/info ) ; then
		$LM_VERBOSE && echo "/proc/pmu/info indicates absence of AC power." >> $OUTPUT
		ON_AC=0
	else
		# It is possible that there is no AC Power = 1 in the file,
		# but we always assume AC power when we're not sure.
		ON_AC=1
		$LM_VERBOSE && echo "/proc/pmu/info indicates presence of AC power." >> $OUTPUT
	fi
elif [ -f /proc/apm ] ; then
	$LM_VERBOSE && echo "Determining power state from /proc/apm." >> $OUTPUT
	read D1 D2 D3 APM_AC_STATE D0 </proc/apm
	if [ "$APM_AC_STATE" = "0x00" ] ; then
		ON_AC=0
	else
		ON_AC=1
	fi
else
	$LM_VERBOSE && echo "No /sys/class/power_supply, ACPI, APM or PMU power management information found -- assuming AC power is present." >> $OUTPUT
	ON_AC=1
fi


# Determine whether to activate or deactivate laptop mode.
ACTIVATE=0

if [ "$ON_AC" -eq 1 ] ; then
	if [ "$ENABLE_LAPTOP_MODE_ON_AC" -ne 0 ] ; then
		$LM_VERBOSE && echo "On AC power: Activating, because ENABLE_LAPTOP_MODE_ON_AC is set." >> $OUTPUT
		ACTIVATE=1
	else
		$LM_VERBOSE && echo "On AC power: Deactivating, because ENABLE_LAPTOP_MODE_ON_AC is not set." >> $OUTPUT
		ACTIVATE=0
	fi
else
	if [ "$ENABLE_LAPTOP_MODE_ON_BATTERY" -ne 0 ] ; then
		$LM_VERBOSE && echo "On battery power: Activating, because ENABLE_LAPTOP_MODE_ON_BATTERY is set." >> $OUTPUT
		ACTIVATE=1
	else
		$LM_VERBOSE && echo "On battery power: Deactivating, because ENABLE_LAPTOP_MODE_ON_BATTERY is not set." >> $OUTPUT
		ACTIVATE=0
	fi
fi

if [ "$ENABLE_LAPTOP_MODE_WHEN_LID_CLOSED" -ne 0 -a "$ACTIVATE" -eq 0 ] ; then
	if [ -x "`which hal-find-by-property`" ] ; then
		HAL_LID_BUTTON=$(hal-find-by-property --key "button.type" --string "lid")
	fi
	if [ "$HAL_LID_BUTTON" != "" ] ; then
		HAL_LID_BUTTON_STATE=$(hal-get-property --udi $(hal-find-by-property --key "button.type" --string "lid") --key "button.state.value")
		if [ "$HAL_LID_BUTTON_STATE" = "true" ] ; then
			$LM_VERBOSE && echo "Setting action to \"start\" because the lid is closed (says HAL)." >> $OUTPUT
			ACTIVATE=1			
		fi
	elif [ -f /proc/acpi/button/lid/*/state ] ; then
		if ( grep -q "closed" /proc/acpi/button/lid/*/state ) ; then
			$LM_VERBOSE && echo 'Setting action to "start" because the lid is closed (says /proc/acpi/button/lid/*/state).' >> $OUTPUT
			ACTIVATE=1
		fi
	else
		echo "Warning: ENABLE_LAPTOP_MODE_WHEN_LID_CLOSED is set, but there is no file"
		echo "/proc/acpi/button/lid/.../state, and hal information is not available either!"
	fi
fi

# If the init script has not been run or has been run with the "stop"
# argument, then we should never start laptop mode.
if [ ! -f /var/run/laptop-mode-tools/enabled ] ; then
	$LM_VERBOSE && echo "Laptop mode disabled because /var/run/laptop-mode-tools/enabled is missing." >> $OUTPUT
	STATE=disabled
fi

if [ "$ACTIVATE" -eq 1 -a -f /etc/default/laptop-mode ] ; then
	. /etc/default/laptop-mode
	if ! ( echo "$ENABLE_LAPTOP_MODE" |grep y ) ; then
		$LM_VERBOSE && echo "Not starting laptop mode because it is disabled in /etc/default/laptop-mode." >> $OUTPUT
		STATE=disabled
	fi
fi

if [ "$STATE" = "disabled" ] ; then
	ACTIVATE=0
fi

# Check whether we are allowed to activate the data-loss-sensitive stuff.
# If the battery charge is too low, we want to disable this, but not the
# other power-saving stuff.

if [ "$ACTIVATE" -eq 0 ] ; then
	ACTIVATE_WITH_POSSIBLE_DATA_LOSS=0
elif [ "$ON_AC" = 1 ] ; then
	$LM_VERBOSE && echo "On AC, not checking minimum battery charge." >> $OUTPUT
	ACTIVATE_WITH_POSSIBLE_DATA_LOSS=1
else
	ACTIVATE_WITH_POSSIBLE_DATA_LOSS=1
	ENOUGH_CHARGE=0
	ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=0

	# Weird way of checking that /sys/class/power_supply is not empty -- but it works.
	if [ "$(echo /sys/class/power_supply/*)" != '/sys/class/power_supply/*' ] ; then
		$LM_VERBOSE && echo 'Not on AC and we have battery information in /sys/class/power_supply/BAT* -- checking minimum battery charge.' >> $OUTPUT
		for BATT in /sys/class/power_supply/* ; do
			BATT_TYPE=$(cat $BATT/type)
			$LM_VERBOSE && echo "$BATT is of type $BATT_TYPE." >> $OUTPUT
			if [ "$BATT_TYPE" != "Battery" ] ; then
				$LM_VERBOSE && echo "Not of type \"Battery\", skipping." >> $OUTPUT
			else
				PREV_ENOUGH_CHARGE=$ENOUGH_CHARGE
				PREV_ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=$ENOUGH_CHARGE_TO_PREVENT_HIBERNATION

				$LM_VERBOSE && echo "Checking levels for $BATT." >> $OUTPUT
				PRESENT=$(cat $BATT/present)
				$LM_VERBOSE && echo "Present: $PRESENT." >> $OUTPUT

				# Only do if the battery is present
				if [ "$PRESENT" -eq 1 ] ; then
					FOUND_AN_ENABLED_CHECK=0
					FOUND_AN_ENABLED_HIBERNATION_CHECK=0
			
					# Get the remaining capacity.
					IN_UAH=0
					IN_UWH=0
					if [ -f $BATT/charge_now ] ; then					
						REMAINING=$(cat $BATT/charge_now)
						IN_UAH=1 # charge_* is in microAmpere-hours
					elif [ -f $BATT/energy_now ] ; then
						REMAINING=$(cat $BATT/energy_now)
						IN_UWH=1 # energy_* is in microWatt-hours
					else
						REMAINING=0
					fi
					if [ -z "$REMAINING" -o "$REMAINING" -eq 0 ] ; then
						$LM_VERBOSE && echo "Battery does not report remaining charge. Perhaps it is not present?" >> $OUTPUT
						REMAINING=0
					fi
					$LM_VERBOSE && echo "Remaining charge: $REMAINING" >> $OUTPUT

					if [ -f $BATT/charge_full_design ] ; then
						CAPACITY=$(cat $BATT/charge_full_design)
					elif [ -f $BATT/energy_full_design ] ; then
						CAPACITY=$(cat $BATT/energy_full_design)
					else
						CAPACITY=0
					fi
					if [ -z "$CAPACITY" -o "$CAPACITY" -eq 0 ] ; then
						$LM_VERBOSE && echo "Battery does not report design full charge, using non-design full charge." >> $OUTPUT					
						if [ -f $BATT/charge_full ] ; then
							CAPACITY=$(cat $BATT/charge_full)
						elif [ -f $BATT/energy_full_design ] ; then
							CAPACITY=$(cat $BATT/energy_full)
						else
							CAPACITY=0
						fi
						if [ -z "$CAPACITY" -o "$CAPACITY" -eq 0] ; then
							$LM_VERBOSE && echo "Battery does not report non-design full charge." >> $OUTPUT					
							CAPACITY=0
						fi
					fi
					$LM_VERBOSE && echo "Full capacity: $CAPACITY" >> $OUTPUT

					# Check the charge percentage
					if [ "$MINIMUM_BATTERY_CHARGE_PERCENT" -ne 0 ] ; then
						FOUND_AN_ENABLED_CHECK=1
						if [ "$CAPACITY" -eq 0 ] ; then
							echo "WARNING: Battery does not report a capacity. Minimum battery"
							echo "charge checking does not work without a design capacity."
							ENOUGH_CHARGE=1
						elif [ "$(($REMAINING * 100 / $CAPACITY))" -ge "$MINIMUM_BATTERY_CHARGE_PERCENT" ] ; then
							ENOUGH_CHARGE=1
						fi
					fi
					if [ "$AUTO_HIBERNATION_BATTERY_CHARGE_PERCENT" -ne 0 ] ; then
						FOUND_AN_ENABLED_HIBERNATION_CHECK=1
						if [ "$CAPACITY" -eq 0 ] ; then
							echo "WARNING: Battery does not report a design capacity. Auto hibernation"
							echo "does not work without a design capacity."
							ENOUGH_CHARGE=1
						elif [ "$(($REMAINING * 100 / $CAPACITY))" -ge "$AUTO_HIBERNATION_BATTERY_CHARGE_PERCENT" ] ; then
							ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=1
						fi
					fi

					ALARM_LEVEL=$(cat $BATT/alarm)
					if [ "$ALARM_LEVEL" -ne 0 ] ; then
						if [ "$REMAINING" -le "$ALARM_LEVEL" ] ; then				
							# Restore the state we had before checking this battery, so that
							# this battery does not count as having enough charge.
							ENOUGH_CHARGE=$PREV_ENOUGH_CHARGE
						elif [ "$FOUND_AN_ENABLED_CHECK" -eq 0 ] ; then
							# This is the only check that is enabled. In that case a non-critical
							# battery level counts as "enough". (If we would count non-critical
							# battery levels as enough *always*, then the other settings would
							# have no effect; this is only a final fallback.)
							ENOUGH_CHARGE=1
						fi
					fi
					if [ "$AUTO_HIBERNATION_ON_CRITICAL_BATTERY_LEVEL" -ne 0 ] ; then
						if [ "$REMAINING" -le "$ALARM_LEVEL" ] ; then				
							ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=$PREV_ENOUGH_CHARGE_TO_PREVENT_HIBERNATION
						elif [ "$FOUND_AN_ENABLED_HIBERNATION_CHECK" -eq 0 ] ; then
							ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=1
						fi
					fi
					
					#
					# Fallback: hard values
					#
					if [ "$IN_UAH" -ne 0 ] ; then
						if [ "$MINIMUM_BATTERY_CHARGE_MAH" -ne 0 ] ; then
							FOUND_AN_ENABLED_CHECK=1
							if [ "$REMAINING" -ge $((1000*"$MINIMUM_BATTERY_CHARGE_MAH")) ] ; then
								ENOUGH_CHARGE=1
							fi
						fi
						if [ "$AUTO_HIBERNATION_BATTERY_CHARGE_MAH" -ne 0 ] ; then
							FOUND_AN_ENABLED_HIBERNATION_CHECK=1
							if [ "$REMAINING" -ge $((1000*"$AUTO_HIBERNATION_BATTERY_CHARGE_MAH")) ] ; then
								ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=1
							fi
						fi
					elif [ "$IN_UWH" -ne 0 ] ; then
						if [ "$MINIMUM_BATTERY_CHARGE_MWH" -ne 0 ] ; then
							FOUND_AN_ENABLED_CHECK=1
							if [ "$REMAINING" -ge $((1000*"$MINIMUM_BATTERY_CHARGE_MWH")) ] ; then
								ENOUGH_CHARGE=1
							fi
						fi
						if [ "$AUTO_HIBERNATION_BATTERY_CHARGE_MWH" -ne 0 ] ; then
							FOUND_AN_ENABLED_HIBERNATION_CHECK=1
							if [ "$REMAINING" -ge $((1000*"$AUTO_HIBERNATION_BATTERY_CHARGE_MWH")) ] ; then
								ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=1
							fi
						fi
					else
						echo "Failed to determine battery charge. Battery charge units are not in"
						echo "mWh, uWh, mAh or uAh."
					fi
										
				else
					$LM_VERBOSE && echo "Battery is not present." >> $OUTPUT
				fi
			fi
		done		
	elif [ "$(echo /proc/acpi/battery/*)" != '/proc/acpi/battery/*' ] ; then
		$LM_VERBOSE && echo "Not on AC and we have batteries in /proc/acpi/battery -- checking minimum battery charge." >> $OUTPUT
		for BATT in /proc/acpi/battery/* ; do
			PREV_ENOUGH_CHARGE=$ENOUGH_CHARGE
			PREV_ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=$ENOUGH_CHARGE_TO_PREVENT_HIBERNATION

			BATT_STATE=$BATT/state
			BATT_INFO=$BATT/info
			$LM_VERBOSE && echo "Checking info and state for $BATT." >> $OUTPUT

			# Only do if the battery is present
			if ( grep -q 'present:.*yes' $BATT_INFO ) ; then
				FOUND_AN_ENABLED_CHECK=0
				FOUND_AN_ENABLED_HIBERNATION_CHECK=0
			
				# Get the remaining capacity.
				REMAINING=`grep "remaining capacity:" $BATT_STATE | sed  "s/.* \([0-9][0-9]* \).*/\1/" `
				if [ -z "$REMAINING" ] ; then
					$LM_VERBOSE && echo "Battery does not report remaining charte. Perhaps it is not present?" >> $OUTPUT
					REMAINING=0
				fi
				$LM_VERBOSE && echo "Remaining charge: $REMAINING" >> $OUTPUT

				CAPACITY=`grep "design capacity:" $BATT_INFO | sed  "s/.* \([0-9][0-9]* \).*/\1/" `
				if [ -z "$CAPACITY" ] ; then
					$LM_VERBOSE && echo "Battery does not report capacity. Perhaps it is not present?" >> $OUTPUT
					CAPACITY=0
				fi
				$LM_VERBOSE && echo "Design capacity: $CAPACITY" >> $OUTPUT

				# Check the charge percentage
				if [ "$MINIMUM_BATTERY_CHARGE_PERCENT" -ne 0 ] ; then
					FOUND_AN_ENABLED_CHECK=1
					if [ "$CAPACITY" -eq 0 ] ; then
						echo "WARNING: Battery does not report a design capacity. Minimum battery"
						echo "charge checking does not work without a design capacity."
						ENOUGH_CHARGE=1
					elif [ "$(($REMAINING * 100 / $CAPACITY))" -ge "$MINIMUM_BATTERY_CHARGE_PERCENT" ] ; then
						ENOUGH_CHARGE=1
					fi
				fi
				if [ "$AUTO_HIBERNATION_BATTERY_CHARGE_PERCENT" -ne 0 ] ; then
					FOUND_AN_ENABLED_HIBERNATION_CHECK=1
					if [ "$CAPACITY" -eq 0 ] ; then
						echo "WARNING: Battery does not report a design capacity. Auto hibernation"
						echo "does not work without a design capacity."
						ENOUGH_CHARGE=1
					elif [ "$(($REMAINING * 100 / $CAPACITY))" -ge "$AUTO_HIBERNATION_BATTERY_CHARGE_PERCENT" ] ; then
						ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=1
					fi
				fi

				#
				# Fallback: hard values.
				#
				# Determine the reporting unit.
				IN_MAH=0
				IN_MWH=0
				if ( grep -q mWh $BATT_INFO ) ; then
					IN_MWH=1
				elif ( grep -q mAh $BATT_INFO ) ; then
					IN_MAH=1
				fi
			
				if [ "$IN_MAH" -ne 0 ] ; then
					if [ "$MINIMUM_BATTERY_CHARGE_MAH" -ne 0 ] ; then
						FOUND_AN_ENABLED_CHECK=1
						if [ "$REMAINING" -ge "$MINIMUM_BATTERY_CHARGE_MAH" ] ; then
							ENOUGH_CHARGE=1
						fi
					fi
					if [ "$AUTO_HIBERNATION_BATTERY_CHARGE_MAH" -ne 0 ] ; then
						FOUND_AN_ENABLED_HIBERNATION_CHECK=1
						if [ "$REMAINING" -ge "$AUTO_HIBERNATION_BATTERY_CHARGE_MAH" ] ; then
							ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=1
						fi
					fi
				elif [ "$IN_MWH" -ne 0 ] ; then
					if [ "$MINIMUM_BATTERY_CHARGE_MWH" -ne 0 ] ; then
						FOUND_AN_ENABLED_CHECK=1
						if [ "$REMAINING" -ge "$MINIMUM_BATTERY_CHARGE_MWH" ] ; then
							ENOUGH_CHARGE=1
						fi
					fi
					if [ "$AUTO_HIBERNATION_BATTERY_CHARGE_MWH" -ne 0 ] ; then
						FOUND_AN_ENABLED_HIBERNATION_CHECK=1
						if [ "$REMAINING" -ge "$AUTO_HIBERNATION_BATTERY_CHARGE_MWH" ] ; then
							ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=1
						fi
					fi
				else
					echo "Failed to determine battery charge. Battery charge units are not in"
					echo "mWh or mAh."
				fi
			
				CAP_STATE=`sed -r 's/^capacity state:\s*(.*)\s*$/\1/;t;d' "$BATT_STATE"`
				if [ "$DISABLE_LAPTOP_MODE_ON_CRITICAL_BATTERY_LEVEL" -ne 0 ] ; then
					if [ "$CAP_STATE" = "critical" ] ; then				
						# Restore the state we had before checking this battery, so that
						# this battery does not count as having enough charge.
						ENOUGH_CHARGE=$PREV_ENOUGH_CHARGE
					elif [ "$FOUND_AN_ENABLED_CHECK" -eq 0 ] ; then
						# This is the only check that is enabled. In that case a non-critical
						# battery level counts as "enough". (If we would count non-critical
						# battery levels as enough *always*, then the other settings would
						# have no effect; this is only a final fallback.)
						ENOUGH_CHARGE=1
					fi
				fi
				if [ "$AUTO_HIBERNATION_ON_CRITICAL_BATTERY_LEVEL" -ne 0 ] ; then
					if [ "$CAP_STATE" = "critical" ] ; then				
						ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=$PREV_ENOUGH_CHARGE_TO_PREVENT_HIBERNATION
					elif [ "$FOUND_AN_ENABLED_HIBERNATION_CHECK" -eq 0 ] ; then
						ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=1
					fi
				fi
			else
				$LM_VERBOSE && echo "Battery is not present." >> $OUTPUT
			fi
		done
	else
		ENOUGH_CHARGE=1
		ENOUGH_CHARGE_TO_PREVENT_HIBERNATION=1
		$LM_VERBOSE && echo "Not on AC and could not check battery state -- data loss sensitive features stay enabled and auto-hibernation will not work." >> $OUTPUT
	fi
	if [ "$ENABLE_AUTO_HIBERNATION" -ne 0 -a "$ENOUGH_CHARGE_TO_PREVENT_HIBERNATION" -eq 0 ] ; then
		$LM_VERBOSE && echo "None of the batteries have a charge above the auto-hibernation level." >> $OUTPUT
		$LM_VERBOSE && echo "Starting hibernation." >> $OUTPUT
		$HIBERNATE_COMMAND
		# Don't continue -- if things are configured correctly, then we
		# will be called on resume.
		exit 0			
	fi
	if [ "$ENOUGH_CHARGE" -eq 0 ] ; then
		$LM_VERBOSE && echo "None of the batteries have a charge above the minimum level." >> $OUTPUT
		$LM_VERBOSE && echo "Deactivating data loss sensitive features." >> $OUTPUT
		ACTIVATE_WITH_POSSIBLE_DATA_LOSS=0
	fi
fi

if [ "$INIT" -eq 0 ] ; then
	echo -n "Laptop mode "
fi

# WAS_ACTIVE is used later on. If there is no /var/run/laptop-mode-tools/state, then
# we know that laptop mode wasn't active before.
WAS_ACTIVE=0
$LM_VERBOSE && echo "Checking if desired state is different from current state." >> $OUTPUT
if [ -f /var/run/laptop-mode-tools/state ] ; then
	read WAS_ACTIVE WAS_ON_AC WAS_ACTIVATE_WITH_POSSIBLE_DATA_LOSS WAS_STATE < /var/run/laptop-mode-tools/state
	if [ "$WAS_STATE" != "" ] ; then
		if [ "$WAS_ACTIVE" -eq "$ACTIVATE" -a "$WAS_ON_AC" -eq "$ON_AC" -a "$WAS_ACTIVATE_WITH_POSSIBLE_DATA_LOSS" -eq "$ACTIVATE_WITH_POSSIBLE_DATA_LOSS" -a "$WAS_STATE" = "$STATE" -a "$FORCE" -eq 0 ] ; then
			echo -n "$STATE, "
			if [ "$WAS_ACTIVE" -eq 1 ] ; then
				echo -n "active [unchanged]"
				if [ "$ACTIVATE_WITH_POSSIBLE_DATA_LOSS" -eq 0 ] ; then
					echo -n " (Data-loss sensitive features disabled.)"
				fi
				echo
			else
				echo "not active [unchanged]"
			fi
			exit 0
		fi
	fi
else
	$LM_VERBOSE && echo "/var/run/laptop-mode-tools/state does not exist, no previous state." >> $OUTPUT
fi
echo "$ACTIVATE $ON_AC $ACTIVATE_WITH_POSSIBLE_DATA_LOSS $STATE" > /var/run/laptop-mode-tools/state

if [ "$ACTIVATE" -eq 1 ] ; then
	echo -n "$STATE, active"
	if [ "$ACTIVATE_WITH_POSSIBLE_DATA_LOSS" -eq 0 ] ; then
		echo -n " (Data-loss sensitive features disabled.)"
	fi
	echo
else
	echo "$STATE, not active"
fi





# Finally, call laptop-mode-tools modules. The modules can use the settings
# from the config files, but they may NOT assume the settings actually exist,
# as no defaults have been given for them.

# Note that the /usr/local/lib path is deprecated.
export FORCE STATE ON_AC ACTIVATE ACTIVATE_WITH_POSSIBLE_DATA_LOSS KLEVEL KMINOR WAS_ACTIVE LM_VERBOSE
for SCRIPT in /usr/share/laptop-mode-tools/modules/* /usr/local/lib/laptop-mode-tools/modules/* /usr/local/share/laptop-mode-tools/modules/* /etc/laptop-mode/modules/*; do
	if [ -x "$SCRIPT" ] ; then
		$LM_VERBOSE && echo "Invoking module $SCRIPT." >> $OUTPUT
		"$SCRIPT" >> $OUTPUT
	else
		$LM_VERBOSE && echo "Module $SCRIPT is not executable." >> $OUTPUT
	fi
done


exit 0

# This fi closes the if for "readconfig". If I would have indented this one
# I would have indented the whole file. :)
fi
