root/lm-sensors/trunk/prog/pwm/fancontrol @ 6001

Revision 6001, 12.4 KB (checked in by khali, 2 years ago)

fancontrol: Output error messages to stderr

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/bin/bash
2#
3# Simple script implementing a temperature dependent fan speed control
4# Supported Linux kernel versions: 2.6.5 and later
5#
6# Version 0.70
7#
8# Usage: fancontrol [CONFIGFILE]
9#
10# Dependencies:
11#   bash, egrep, sed, cut, sleep, readlink, lm_sensors :)
12#
13# Please send any questions, comments or success stories to
14# marius.reiner@hdev.de
15# Thanks!
16#
17# For configuration instructions and warnings please see fancontrol.txt, which
18# can be found in the doc/ directory or at the website mentioned above.
19#
20#
21#    Copyright 2003 Marius Reiner <marius.reiner@hdev.de>
22#    Copyright (C) 2007-2009 Jean Delvare <khali@linux-fr.org>
23#
24#    This program is free software; you can redistribute it and/or modify
25#    it under the terms of the GNU General Public License as published by
26#    the Free Software Foundation; either version 2 of the License, or
27#    (at your option) any later version.
28#
29#    This program is distributed in the hope that it will be useful,
30#    but WITHOUT ANY WARRANTY; without even the implied warranty of
31#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32#    GNU General Public License for more details.
33#
34#    You should have received a copy of the GNU General Public License
35#    along with this program; if not, write to the Free Software
36#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
37#    MA 02110-1301 USA.
38#
39#
40
41PIDFILE="/var/run/fancontrol.pid"
42
43#DEBUG=1
44MAX=255
45
46declare -i pwmval
47
48function LoadConfig {
49        echo "Loading configuration from $1 ..."
50        if [ ! -r "$1" ]
51        then
52                echo "Error: Can't read configuration file" >&2
53                exit 1
54        fi
55
56        # grep configuration from file
57        INTERVAL=`egrep '^INTERVAL=.*$' $1 | sed -e 's/INTERVAL=//g'`
58        DEVPATH=`egrep '^DEVPATH=.*$' $1 | sed -e 's/DEVPATH= *//g'`
59        DEVNAME=`egrep '^DEVNAME=.*$' $1 | sed -e 's/DEVNAME= *//g'`
60        FCTEMPS=`egrep '^FCTEMPS=.*$' $1 | sed -e 's/FCTEMPS=//g'`
61        MINTEMP=`egrep '^MINTEMP=.*$' $1 | sed -e 's/MINTEMP=//g'`
62        MAXTEMP=`egrep '^MAXTEMP=.*$' $1 | sed -e 's/MAXTEMP=//g'`
63        MINSTART=`egrep '^MINSTART=.*$' $1 | sed -e 's/MINSTART=//g'`
64        MINSTOP=`egrep '^MINSTOP=.*$' $1 | sed -e 's/MINSTOP=//g'`
65        # optional settings:
66        FCFANS=`egrep '^FCFANS=.*$' $1 | sed -e 's/FCFANS=//g'`
67        MINPWM=`egrep '^MINPWM=.*$' $1 | sed -e 's/MINPWM=//g'`
68        MAXPWM=`egrep '^MAXPWM=.*$' $1 | sed -e 's/MAXPWM=//g'`
69
70        # Check whether all mandatory settings are set
71        if [[ -z ${INTERVAL} || -z ${FCTEMPS} || -z ${MINTEMP} || -z ${MAXTEMP} || -z ${MINSTART} || -z ${MINSTOP} ]]
72        then
73                echo "Some mandatory settings missing, please check your config file!" >&2
74                exit 1
75        fi
76        if [ "$INTERVAL" -le 0 ]
77        then
78                echo "Error in configuration file:" >&2
79                echo "INTERVAL must be at least 1" >&2
80                exit 1
81        fi
82
83        # write settings to arrays for easier use and print them
84        echo
85        echo "Common settings:"
86        echo "  INTERVAL=$INTERVAL"
87
88        let fcvcount=0
89        for fcv in $FCTEMPS
90        do
91                if ! echo $fcv | egrep -q '='
92                then
93                        echo "Error in configuration file:" >&2
94                        echo "FCTEMPS value is improperly formatted" >&2
95                        exit 1
96                fi
97
98                AFCPWM[$fcvcount]=`echo $fcv |cut -d'=' -f1`
99                AFCTEMP[$fcvcount]=`echo $fcv |cut -d'=' -f2`
100                AFCFAN[$fcvcount]=`echo $FCFANS |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
101                AFCMINTEMP[$fcvcount]=`echo $MINTEMP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
102                AFCMAXTEMP[$fcvcount]=`echo $MAXTEMP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
103                AFCMINSTART[$fcvcount]=`echo $MINSTART |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
104                AFCMINSTOP[$fcvcount]=`echo $MINSTOP |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
105                AFCMINPWM[$fcvcount]=`echo $MINPWM |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
106                [ -z "${AFCMINPWM[$fcvcount]}" ] && AFCMINPWM[$fcvcount]=0
107                AFCMAXPWM[$fcvcount]=`echo $MAXPWM |sed -e 's/ /\n/g' |egrep "${AFCPWM[$fcvcount]}" |cut -d'=' -f2`
108                [ -z "${AFCMAXPWM[$fcvcount]}" ] && AFCMAXPWM[$fcvcount]=255
109
110                # verify the validity of the settings
111                if [ "${AFCMINTEMP[$fcvcount]}" -ge "${AFCMAXTEMP[$fcvcount]}" ]
112                then
113                        echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2
114                        echo "MINTEMP must be less than MAXTEMP" >&2
115                        exit 1
116                fi
117                if [ "${AFCMAXPWM[$fcvcount]}" -gt 255 ]
118                then
119                        echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2
120                        echo "MAXPWM must be at most 255" >&2
121                        exit 1
122                fi
123                if [ "${AFCMINSTOP[$fcvcount]}" -ge "${AFCMAXPWM[$fcvcount]}" ]
124                then
125                        echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2
126                        echo "MINSTOP must be less than MAXPWM" >&2
127                        exit 1
128                fi
129                if [ "${AFCMINSTOP[$fcvcount]}" -lt "${AFCMINPWM[$fcvcount]}" ]
130                then
131                        echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2
132                        echo "MINSTOP must be greater than or equal to MINPWM" >&2
133                        exit 1
134                fi
135                if [ "${AFCMINPWM[$fcvcount]}" -lt 0 ]
136                then
137                        echo "Error in configuration file (${AFCPWM[$fcvcount]}):" >&2
138                        echo "MINPWM must be at least 0" >&2
139                        exit 1
140                fi
141
142                echo
143                echo "Settings for ${AFCPWM[$fcvcount]}:"
144                echo "  Depends on ${AFCTEMP[$fcvcount]}"
145                echo "  Controls ${AFCFAN[$fcvcount]}"
146                echo "  MINTEMP=${AFCMINTEMP[$fcvcount]}"
147                echo "  MAXTEMP=${AFCMAXTEMP[$fcvcount]}"
148                echo "  MINSTART=${AFCMINSTART[$fcvcount]}"
149                echo "  MINSTOP=${AFCMINSTOP[$fcvcount]}"
150                echo "  MINPWM=${AFCMINPWM[$fcvcount]}"
151                echo "  MAXPWM=${AFCMAXPWM[$fcvcount]}"
152                let fcvcount=fcvcount+1
153        done
154        echo
155}
156
157function DevicePath()
158{
159        if [ -h "$1/device" ]
160        then
161                readlink -f "$1/device" | sed -e 's/^\/sys\///'
162        fi
163}
164
165function DeviceName()
166{
167        if [ -r "$1/name" ]
168        then
169                cat "$1/name" | sed -e 's/[[:space:]=]/_/g'
170        elif [ -r "$1/device/name" ]
171        then
172                cat "$1/device/name" | sed -e 's/[[:space:]=]/_/g'
173        fi
174}
175
176function ValidateDevices()
177{
178        local OLD_DEVPATH="$1" OLD_DEVNAME="$2" outdated=0
179        local entry device name path
180
181        for entry in $OLD_DEVPATH
182        do
183                device=`echo "$entry" | sed -e 's/=[^=]*$//'`
184                path=`echo "$entry" | sed -e 's/^[^=]*=//'`
185
186                if [ "`DevicePath "$device"`" != "$path" ]
187                then
188                        echo "Device path of $device has changed" >&2
189                        outdated=1
190                fi
191        done
192
193        for entry in $OLD_DEVNAME
194        do
195                device=`echo "$entry" | sed -e 's/=[^=]*$//'`
196                name=`echo "$entry" | sed -e 's/^[^=]*=//'`
197
198                if [ "`DeviceName "$device"`" != "$name" ]
199                then
200                        echo "Device name of $device has changed" >&2
201                        outdated=1
202                fi
203        done
204
205        return $outdated
206}
207
208# Check that all referenced sysfs files exist
209function CheckFiles {
210        local outdated=0
211
212        let fcvcount=0
213        while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs
214        do
215                pwmo=${AFCPWM[$fcvcount]}
216                if [ ! -w $pwmo ]
217                then
218                        echo "Error: file $pwmo doesn't exist" >&2
219                        outdated=1
220                fi
221                let fcvcount=$fcvcount+1
222        done
223
224        let fcvcount=0
225        while (( $fcvcount < ${#AFCTEMP[@]} )) # go through all temp inputs
226        do
227                tsen=${AFCTEMP[$fcvcount]}
228                if [ ! -r $tsen ]
229                then
230                        echo "Error: file $tsen doesn't exist" >&2
231                        outdated=1
232                fi
233                let fcvcount=$fcvcount+1
234        done
235
236        let fcvcount=0
237        while (( $fcvcount < ${#AFCFAN[@]} )) # go through all fan inputs
238        do
239                # A given PWM output can control several fans
240                for fan in $(echo ${AFCFAN[$fcvcount]} | sed -e 's/+/ /')
241                do
242                        if [ ! -r $fan ]
243                        then
244                                echo "Error: file $fan doesn't exist" >&2
245                                outdated=1
246                        fi
247                done
248                let fcvcount=$fcvcount+1
249        done
250
251        if [ $outdated -eq 1 ]
252        then
253                echo >&2
254                echo "At least one referenced file is missing. Either some required kernel" >&2
255                echo "modules haven't been loaded, or your configuration file is outdated." >&2
256                echo "In the latter case, you should run pwmconfig again." >&2
257        fi
258
259        return $outdated
260}
261
262if [ -f "$1" ]
263then
264        LoadConfig $1
265else
266        LoadConfig /etc/fancontrol
267fi
268
269# Detect path to sensors
270if echo "${AFCPWM[0]}" | egrep -q '^/'
271then
272        DIR=/
273elif echo "${AFCPWM[0]}" | egrep -q '^hwmon[0-9]'
274then
275        DIR=/sys/class/hwmon
276elif echo "${AFCPWM[0]}" | egrep -q '^[1-9]*[0-9]-[0-9abcdef]{4}'
277then
278        DIR=/sys/bus/i2c/devices
279else
280        echo "$0: Invalid path to sensors" >&2
281        exit 1
282fi
283
284if [ ! -d $DIR ]
285then
286        echo $0: 'No sensors found! (did you load the necessary modules?)' >&2
287        exit 1
288fi
289cd $DIR
290
291# Check for configuration change
292if [ -z "$DEVPATH" -o -z "$DEVNAME" ]
293then
294        echo "Configuration is too old, please run pwmconfig again" >&2
295        exit 1
296fi
297if ! ValidateDevices "$DEVPATH" "$DEVNAME"
298then
299        echo "Configuration appears to be outdated, please run pwmconfig again" >&2
300        exit 1
301fi
302CheckFiles || exit 1
303
304if [ -f "$PIDFILE" ]
305then
306        echo "File $PIDFILE exists, is fancontrol already running?" >&2
307        exit 1
308fi
309echo $$ > "$PIDFILE"
310
311# $1 = pwm file name
312function pwmdisable()
313{
314        ENABLE=${1}_enable
315        # No enable file? Just set to max
316        if [ ! -f $ENABLE ]
317        then
318                echo $MAX > $1
319                return 0
320        fi
321
322        # Try pwmN_enable=0
323        echo 0 > $ENABLE 2> /dev/null
324        if [ `cat $ENABLE` -eq 0 ]
325        then
326                # Success
327                return 0
328        fi
329
330        # It didn't work, try pwmN_enable=1 pwmN=255
331        echo 1 > $ENABLE 2> /dev/null
332        echo $MAX > $1
333        if [ `cat $ENABLE` -eq 1 -a `cat $1` -ge 190 ]
334        then
335                # Success
336                return 0
337        fi
338
339        # Nothing worked
340        echo "$ENABLE stuck to" `cat $ENABLE` >&2
341        return 1
342}
343
344# $1 = pwm file name
345function pwmenable()
346{
347        ENABLE=${1}_enable
348        if [ -f $ENABLE ]
349        then
350                echo 1 > $ENABLE 2> /dev/null
351                if [ $? -ne 0 ]
352                then
353                        return 1
354                fi
355        fi
356        echo $MAX > $1
357}
358
359function restorefans()
360{
361        local status=$1
362        echo 'Aborting, restoring fans...'
363        let fcvcount=0
364        while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs
365        do
366                pwmo=${AFCPWM[$fcvcount]}
367                pwmdisable $pwmo
368                let fcvcount=$fcvcount+1
369        done
370        echo 'Verify fans have returned to full speed'
371        rm -f "$PIDFILE"
372        exit $status
373}
374
375trap 'restorefans 0' SIGQUIT SIGTERM
376trap 'restorefans 1' SIGHUP SIGINT
377
378# main function
379function UpdateFanSpeeds {
380        let fcvcount=0
381        while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs
382        do
383                #hopefully shorter vars will improve readability:
384                pwmo=${AFCPWM[$fcvcount]}
385                tsens=${AFCTEMP[$fcvcount]}
386                fan=${AFCFAN[$fcvcount]}
387                let mint="${AFCMINTEMP[$fcvcount]}*1000"
388                let maxt="${AFCMAXTEMP[$fcvcount]}*1000"
389                minsa=${AFCMINSTART[$fcvcount]}
390                minso=${AFCMINSTOP[$fcvcount]}
391                minpwm=${AFCMINPWM[$fcvcount]}
392                maxpwm=${AFCMAXPWM[$fcvcount]}
393
394                read tval < ${tsens}
395                if [ $? -ne 0 ]
396                then
397                        echo "Error reading temperature from $DIR/$tsens"
398                        restorefans 1
399                fi
400
401                read pwmpval < ${pwmo}
402                if [ $? -ne 0 ]
403                then
404                        echo "Error reading PWM value from $DIR/$pwmo"
405                        restorefans 1
406                fi
407
408                # If fanspeed-sensor output shall be used, do it
409                if [[ -n ${fan} ]]
410                then
411                        min_fanval=100000
412                        fanval=
413                        # A given PWM output can control several fans
414                        for one_fan in $(echo $fan | sed -e 's/+/ /')
415                        do
416                                read one_fanval < ${one_fan}
417                                if [ $? -ne 0 ]
418                                then
419                                        echo "Error reading Fan value from $DIR/$one_fan" >&2
420                                        restorefans 1
421                                fi
422
423                                # Remember the minimum, it only matters if it is 0
424                                if [ $one_fanval -lt $min_fanval ]
425                                then
426                                        min_fanval=$one_fanval
427                                fi
428
429                                if [ -z "$fanval" ]
430                                then
431                                        fanval=$one_fanval
432                                else
433                                        fanval="$fanval/$one_fanval"
434                                fi
435                        done
436                else
437                        fanval=1  # set it to a non zero value, so the rest of the script still works
438                fi
439
440                # debug info
441                if [ "$DEBUG" != "" ]
442                then
443                        echo "pwmo=$pwmo"
444                        echo "tsens=$tsens"
445                        echo "fan=$fan"
446                        echo "mint=$mint"
447                        echo "maxt=$maxt"
448                        echo "minsa=$minsa"
449                        echo "minso=$minso"
450                        echo "minpwm=$minpwm"
451                        echo "maxpwm=$maxpwm"
452                        echo "tval=$tval"
453                        echo "pwmpval=$pwmpval"
454                        echo "fanval=$fanval"
455                        echo "min_fanval=$min_fanval"
456                fi
457
458                if (( $tval <= $mint ))
459                  then pwmval=$minpwm # below min temp, use defined min pwm
460                elif (( $tval >= $maxt ))
461                  then pwmval=$maxpwm # over max temp, use defined max pwm
462                else
463                  # calculate the new value from temperature and settings
464                  pwmval="(${tval}-${mint})*(${maxpwm}-${minso})/(${maxt}-${mint})+${minso}"
465                  if [ $pwmpval -eq 0 -o $min_fanval -eq 0 ]
466                  then # if fan was stopped start it using a safe value
467                        echo $minsa > $pwmo
468                        # Sleep while still handling signals
469                        sleep 1 &
470                        wait $!
471                  fi
472                fi
473                echo $pwmval > $pwmo # write new value to pwm output
474                if [ $? -ne 0 ]
475                then
476                        echo "Error writing PWM value to $DIR/$pwmo" >&2
477                        restorefans 1
478                fi
479                if [ "$DEBUG" != "" ]
480                then
481                        echo "new pwmval=$pwmval"
482                fi
483                let fcvcount=$fcvcount+1
484        done
485}
486
487echo 'Enabling PWM on fans...'
488let fcvcount=0
489while (( $fcvcount < ${#AFCPWM[@]} )) # go through all pwm outputs
490do
491        pwmo=${AFCPWM[$fcvcount]}
492        pwmenable $pwmo
493        if [ $? -ne 0 ]
494        then
495                echo "Error enabling PWM on $DIR/$pwmo" >&2
496                restorefans 1
497        fi
498        let fcvcount=$fcvcount+1
499done
500
501echo 'Starting automatic fan control...'
502
503# main loop calling the main function at specified intervals
504while true
505do
506        UpdateFanSpeeds
507        # Sleep while still handling signals
508        sleep $INTERVAL &
509        wait $!
510done
Note: See TracBrowser for help on using the browser.