applemsc.sh
#!/bin/bash
#
# Author: $Author: smurphy $
# Locked by: $Locker:  $
#
# Programm Version
VER="$Revision: 1.17 $"
#
# Original author: Joerg Mertin <smurphy(-AT-)solsys.org>
# Enhancements by Wizardry and Steamworks <office(-AT-)grimore.org>
 
###########################################################################
##  Copyright (C) Wizardry and Steamworks 2016 - License: GNU GPLv3      ##
###########################################################################
function getCoreTemperatureFile {
    for i in /sys/devices/platform/coretemp.*/hwmon/hwmon*/*_label; do
        IFS=' ' read -ra CORE <<< `cat $i`
        if [ "${CORE[0]}" == "Core" ] && [ "${CORE[1]}" == $1 ]; then
            echo "${i//label/input}"
        fi
    done
}
 
# Get Program-Name, shortened Version.
PROGNAME="`basename $0 .sh`"
LockFile=/var/run/${PROGNAME}..LOCK
LOGGER=/usr/bin/logger
# Make sure that logger is not used if not found
if [ ! -x $LOGGER ] 
    then
    DOLOGGER=false || :
    echo "WARNING: $LOGGER program not found - syslog writing disabled"
else 
    DOLOGGER=true
fi
 
VERBOSE=true
POLL_TIME=10 # Poll interval, e.g. checking temperatures
DAEMON=false
SYSLOG=false
 
# User configurable part - files we find required informations
CPU_CORE0=$(getCoreTemperatureFile 0)
CPU_CORE1=$(getCoreTemperatureFile 1)
#FAN_SPEED=/sys/devices/platform/applesmc.768/fan1_input
FAN_SPEED=/sys/devices/platform/applesmc.768/fan1_min
FAN_SET=/sys/devices/platform/applesmc.768/fan1_output
FAN_MAN=/sys/devices/platform/applesmc.768/fan1_manual
 
# Some very conservative FAN speed settings
MIN_SPEED="`cat /sys/devices/platform/applesmc.768/fan1_min`"
#MIN_SPEED=2000 # If you want to override - here's your chance.
MAX_SPEED="`cat /sys/devices/platform/applesmc.768/fan1_max`"
# MAX_SPEED=6000 # If you want to override - here's your chance.
FAN_CHANGE=500
 
# Temperaturs
MAX_TEMP=80000
HIGH_TEMP=55000
LOW_TEMP=45000
 
###############################################################################
# Nothing to change below this point !
###############################################################################
 
 
 
##############################################################################
# Errors function
errors() {
#DOC: The errors Function is called to control the exit status.
#
: ${errlvl:=9}
: ${MSG:="No Error message - Probably user interruption"}
if [ $errlvl -gt 0 ] ;
    then
    if [ $errlvl = 15 ] ;
    then
        $VERBOSE && echo -e "WARNING: $MSG"
	log "WARNING: $MSG"
    else
        #Usage
        echo -e "\a"
        echo "FATAL:  An error occured in \"${PROGNAME}(${FUNCTION})\". Bailing out..."
        echo -e "ERRMSG: $MSG"
        echo
	fan_auto_operation auto
	log "FATAL: $MSG"
	Unlock $FLock
        exit $errlvl
    fi
fi
} # errors Function
#
#
##############################################################################
# Create Lockfile - prevent double start of app
Lock() {
# Lockfile to create
tolock="$1"
Action="$2"
#
# Lock file if lockfile does not exist.
if [ -s $tolock ]
then
    # If we have provided a second Var, set Exit status using  it.
    if [ ! -n "$Action" ]
    then
	# Oops, we  found a lockfile. Loop while checking if still exists.
	while [ -s $tolock ]
	do
	    sleep 5 ;
	done
	MSG="Creating lockfile $tolock failed after 5 secs"
	# write PID into Lock-File.
	if [ "$DAEMON" == "true" ]
	    then
	    echo $! > $tolock
	    errlvl=$?
	    errors
	else
	    echo $$ > $tolock
	    errlvl=$?
	    errors
	fi
    else
	Pid="`cat $tolock`"
	Exists="`ps auxw | grep \" $Pid \" | grep -c $PROGNAME`"
	if [ $Exists = 1 ]
	then
	    MSG="\"$PROGNAME\" already running. Exiting..."
	    errlvl=$Action
	    errors
	else
	    MSG="Found stale lockfile... Removing it..."
	    rm -f $tolock
	    errlvl=$?
	    errors
	    MSG="Creating lockfile $tolock failed"
   	    # write PID into Lock-File.
	    if [ "$DAEMON" == "true" ]
		then
		echo $! > $tolock
		errlvl=$?
		errors
	    else
		echo $$ > $tolock
		errlvl=$?
		errors
	    fi
	fi
    fi
else
    # write PID into Lock-File.
    MSG="Creating lockfile $tolock failed"
    if [ "$DAEMON" == "true" ]
	then
	echo $! > $tolock
	errlvl=$?
	errors
    else
	echo $$ > $tolock
	errlvl=$?
	errors
    fi
fi
} # Lock
#
##############################################################################
# Unlock lockfile
Unlock(){
# Name of Lockfile to unlock
unlock="$1"
# Unlock the file.
if [ -s $unlock ]
then
    if [ -n "$DPID" ]
	then
	PID=$DPID
    else
	PID=$$
    fi
 
    if [ "`cat $unlock`" -ne "$PID" ]
	then
        # Lock it
	echo -e "WARNING: Wrong lock-file PID. Probably a race-condition happened...\n"
    else
        # Removing Lockfile
	rm -f $unlock
    fi
fi
#
} # Unlock
#
###############################################################################
# Usage function - very small - just givs out some help.
 
usage() {
    echo "
Program: ${PROGNAME}.sh version $VER
         RCS$Id: applesmc.sh,v 1.17 2007/10/09 14:01:03 smurphy Exp $
         (c) J. Mertin <smurphy@solsys.org>
 
Usage: $0 [OPTION]...
 
Available options:
   -s     Status - just displays actual status informations
   -q     Quiet mode
   -v     Verbose mode
   -d     Daemon mode, go into background (implies -q)
   -k     Stop daemon
   -l     Log to syslog
"
    exit 1;
} # usage end.
 
#
###############################################################################
# Log function - very small
log() {
 
    # Be verbose
    ! $VERBOSE || echo "> $PROGNAME $*"
 
    # no logger found, no syslog capabilities
    if [ $DOLOGGER ]
	then
	! $SYSLOG || $LOGGER -t "$PROGNAME" "$*"
    fi
}
 
###############################################################################
# Get's CPU Temperature. In dual-core env - returns max temp detected
get_temp() {
    TEMP0=`cat $CPU_CORE0`
    TEMP1=`cat $CPU_CORE1`
    # Check max TEMP
    if [ $TEMP0 -gt $TEMP1 ] 
	then
	TEMP=$TEMP0
    else
	TEMP=$TEMP1
    fi
    let DTEMP=($TEMP / 1000)
} # get_temp
 
###############################################################################
# Get's the FAN Status
fan_status() {
    FSPEED=`cat $FAN_SPEED`
    let CHECK_SPEED=( $MIN_SPEED + $FAN_CHANGE )
 
    if [ $FSPEED -gt $CHECK_SPEED ]
	then 
	while [ $FSPEED -lt $CHECK_SPEED ]
	  do
	  if [ $CHECK_SPEED -ge $MAX_SPEED ] 
	      then
	      FSPEED=$MAX_SPEED
	      return
	  fi
	  let CHECK_SPEED=( $CHECK_SPEED + $FAN_CHANGE )
	done
	FSPEED=$CHECK_SPEED
    else
	FSPEED=$MIN_SPEED
    fi
 
    let SPEED_UP="( $FSPEED + $FAN_CHANGE )"
    let SPEED_DOWN="( $FSPEED - $FAN_CHANGE )"
} # fan_status
 
###############################################################################
# Sets the FAN Manual=1/Automatic=0 operation
# It will only be used in Daemon mode !
fan_auto_operation() {
    if [ "$*" == "auto" ]
	then
	log "Mode: auto - CPU ${DTEMP}C, FAN @ ${FSPEED}RPM"
	echo 0 > $FAN_MAN
    else
	log "Mode: manual - CPU ${DTEMP}C, FAN @ ${FSPEED}RPM"
	echo 1 > $FAN_MAN
    fi
    # Enter a little info the Logger
 
} # fan_auto_operation
 
###############################################################################
# Get's CPU Temperature. In dual-core env - returns max temp detected
do_status() {
    get_temp
    fan_status
    SPEED=`cat $FAN_SPEED`
    echo "Status: MAX Core Temp: ${DTEMP}C, FAN @ $SPEED / set ${FSPEED}RPM"
} # get_status
 
###############################################################################
# Get's CPU Temperature. In dual-core env - returns max temp detected
kill_daemon() {
 
    if [ -f "$LockFile" ]
	then
	set -e
	MSG="Extracting PID file of previous process"
	DPID="`cat \"$LockFile\"`" 
	errlvl=$?
	errors
	MSG="Killing PID $DPID failed"
	kill "$DPID"
	errlvl=$?
	errors
	log "Killed process $DPID"
        # Set fan operaion mode to auto
	fan_auto_operation auto
        # We need to remove lock file
	Unlock $LockFile
    else
 	log "No lockfile found. Bailing out"
    fi
} # kill_daemon
 
###############################################################################
# Main loop - the actuall working loop.
do_main() {
 
    # Enable the fan in default mode if anything goes wrong:
    set -e -E -u
    trap "errors; exit 2" HUP INT ABRT QUIT SEGV TERM
    trap "errors" EXIT
    trap "log 'Got SIGUSR1'; fan_auto_operation auto; " USR1
 
    # Get the actual Temp and Fan speed
    get_temp
    fan_status
 
    if [ "$DAEMON" == "true" ]
	then
	fan_auto_operation manual
    else
	fan_auto_operation auto
    fi
 
    while :;
      do
 
      # Get the actual Temp and Fan speed
      get_temp
      fan_status
 
      # Special case - if we have reached our set max temp - max FAN Speed !
      if [ $TEMP -gt $MAX_TEMP ]
	  then
	  if [ $MAX_SPEED -ne $FSPEED ]
	      then
	      echo $MAX_SPEED > $FAN_SET
	      log "CPU ${DTEMP}C, FAN @ ${FSPEED}RPM > ${MAX_SPEED}RPM"
	  fi
      elif [ $TEMP -gt $HIGH_TEMP ]
	  then
	  if [ $SPEED_UP -lt $MAX_SPEED ]
	      then
	      if [ $SPEED_UP -gt $FSPEED ]
		  then
		  echo $SPEED_UP > $FAN_SET
		  log "CPU ${DTEMP}C, FAN @ ${FSPEED}RPM > ${SPEED_UP}RPM"
	      fi
	  fi
      elif [ $TEMP -lt $LOW_TEMP ]
	  then
	  if [ $SPEED_DOWN -gt $MIN_SPEED ]
	      then
	      if [ $SPEED_DOWN -ne $FSPEED ]
		  then
		  echo $SPEED_DOWN > $FAN_SET
		  log "CPU ${DTEMP}C, FAN @ ${FSPEED}RPM > ${SPEED_DOWN}RPM"
	      fi
	  fi
      else 
	  if [ $MIN_SPEED -ne $FSPEED ]
	      then
	      echo $MIN_SPEED > $FAN_SET
	      log "CPU ${DTEMP}C, FAN @ ${FSPEED}RPM > ${MIN_SPEED}RPM"
	  fi
      fi
      if [ "$DAEMON" == "true" ]
	  then
	  sleep $POLL_TIME
      else
	  return
      fi
    done
 
} # do_main
 
#
###############################################################################
# Parse arguments
while getopts 'sqdlhkv' OPT; do
    case "$OPT" in
        s) # test mode
	    DAEMON=false
	    VERBOSE=true
	    SYSLOG=false
	    do_status
	    exit 0
            ;;
        d) # go into background and daemonize
	    DAEMON=true
	    VERBOSE=false
	    SYSLOG=true
            ;;
        q) # quiet mode
            VERBOSE=false
	    SYSLOG=true
            ;;
        k) # Kill daemon
	    DAEMON=false
	    VERBOSE=true
	    SYSLOG=true
	    log "Daemon shutdown requested"
	    get_temp
	    fan_status
	    kill_daemon
	    exit 0
            ;;
        l) # log to syslog
            SYSLOG=true
            ;;
        v) # log to syslog
            VERBOSE=true
            ;;
        h) # short help
            usage
	    exit 0
            ;;
        \?) # error
            usage
	    exit 0
            ;;
    esac
done
[ $OPTIND -gt $# ] || usage  # no non-option args
 
 
# That's the main loop.
if [ "$DAEMON" == "true" ]
then
    # We want to run as daemon - detach all from the console
    log "Version${VER} initialisation succeeded"
    Lock $LockFile 1
    do_main 0<&- 1>&- 2>&- &
    # Lock this process.
    DPID=$!
    echo $DPID > $LockFile
else 
    # Lock this process.
    Lock $LockFile 1
    # Normal run.
    do_main
    # Unlock the program.
    Unlock $LockFile
fi
 
exit 0