mirror of
https://github.com/lm-sensors/lm-sensors
synced 2025-10-13 14:25:52 +00:00
"Appended to this message is the current version of the fancontrol script that I am happily using. It uses POSIX::_exit() to avoid the double-call to the END block, plus it logs STDOUT and STDERR to different logfiles." git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@2606 7894878c-1315-0410-8ee3-d5d059ff63e0
428 lines
10 KiB
Perl
Executable File
428 lines
10 KiB
Perl
Executable File
#!/usr/bin/perl -wT
|
|
# $Id$
|
|
#
|
|
# Perl script for temperature dependent fan speed control.
|
|
#
|
|
# Copyright 2004 dean takemori <deant@hawaii.rr.com>
|
|
#
|
|
# This is a reimplementation in perl of Marius Reiner's bash script for
|
|
# fan speed control. It has advantages in that it can daemonize itself
|
|
# and needn't spawn subprocesses for grep, sleep etc. Much of the structure
|
|
# of the bash script is preserved to make mirroring changes easier, so
|
|
# this is seriously non-idiomatic perl but at the same time it should not
|
|
# be considered a a direct bash to perl translation.
|
|
#
|
|
# Usage: fancontrol [CONFIGFILE]
|
|
#
|
|
# For configuration instructions and warnings please see fancontrol.txt,
|
|
# which can be found in the doc/ directory or at the website mentioned
|
|
# elsewhere.
|
|
#
|
|
# This script is derived from Marius Reiner's bash version, so it is
|
|
# hereby placed under the GPL.
|
|
#
|
|
# Copyright 2003 Marius Reiner <marius.reiner@hdev.de>
|
|
#
|
|
# 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 your 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
#
|
|
|
|
use warnings;
|
|
use strict;
|
|
use IO::Handle;
|
|
use Getopt::Std;
|
|
use POSIX;
|
|
|
|
$ENV{PATH} = "/bin:/usr/bin";
|
|
|
|
##### Configuration #####
|
|
use constant DEBUG => 1;
|
|
use constant MAX => 255;
|
|
|
|
use constant PIDFILE => '/var/run/fancontrol.pid';
|
|
use constant CONFFILE => '/etc/fancontrol';
|
|
use constant LOGFILE => '/var/log/fancontrol/fancontrol.log';
|
|
use constant ERRFILE => '/var/log/fancontrol/fancontrol.err';
|
|
|
|
use constant SDIR => '/sys/bus/i2c/devices';
|
|
### End Configuration ###
|
|
|
|
our $interval;
|
|
our $pwmo;
|
|
our @afcpwm;
|
|
our @afctemp;
|
|
our @afcfan;
|
|
our @afcmaxtemp;
|
|
our @afcmintemp;
|
|
our @afcminstart;
|
|
our @afcminstop;
|
|
|
|
sub loadconfig($);
|
|
sub pwmdisable($);
|
|
sub pwmenable($);
|
|
sub restorefans();
|
|
sub calc(@);
|
|
sub UpdateFanSpeeds();
|
|
END { restorefans(); }
|
|
|
|
our $opt_d;
|
|
getopts('d');
|
|
|
|
my $config = shift;
|
|
if (defined($config))
|
|
{ loadconfig($config); }
|
|
else
|
|
{ loadconfig(CONFFILE); }
|
|
|
|
### Daemonize
|
|
if ( defined($opt_d) && ($opt_d == 1) )
|
|
{
|
|
my $pid = fork;
|
|
POSIX::_exit(0) if $pid;
|
|
|
|
unless (defined($pid))
|
|
{ die("Couldn't fork: $!"); }
|
|
|
|
open(*STDERR, '>', ERRFILE);
|
|
IO::Handle::autoflush(*STDERR);
|
|
open(*STDOUT, '>>', LOGFILE);
|
|
IO::Handle::autoflush(*STDOUT);
|
|
|
|
unless (POSIX::setsid())
|
|
{ die("Couldn't open new session: $!"); }
|
|
|
|
}
|
|
|
|
### Pidfile
|
|
if (open(FILE, ">" . PIDFILE))
|
|
{
|
|
print(FILE "$$\n");
|
|
close(FILE);
|
|
}
|
|
else
|
|
{ print(PIDFILE . ": $!\n"); }
|
|
|
|
|
|
### What kind of interface?
|
|
our $sysfs = 0;
|
|
our $dir = '/proc/sys/dev/sensors';
|
|
if (!(-d $dir))
|
|
{
|
|
if (!(-d SDIR))
|
|
{ die("No sensors found! (are the necessary modules loaded?) :
|
|
$!\n"); }
|
|
else
|
|
{
|
|
$sysfs = 1;
|
|
$dir = SDIR;
|
|
}
|
|
}
|
|
|
|
### Trap signals
|
|
$SIG{TERM} = \&restorefans;
|
|
$SIG{HUP} = \&restorefans;
|
|
$SIG{INT} = \&restorefans;
|
|
$SIG{QUIT} = \&restorefans;
|
|
|
|
### Enable PWM
|
|
print("Enabling PWM on fans...\n");
|
|
my $fcvcount = 0;
|
|
while ($fcvcount < $#afcpwm+1)
|
|
{
|
|
$pwmo = $afcpwm[$fcvcount];
|
|
unless (pwmenable($pwmo))
|
|
{ die("Error enabling PWM on $dir/$pwmo : $!\n"); }
|
|
$fcvcount++;
|
|
}
|
|
|
|
print("Starting automatic fan control...\n");
|
|
|
|
while(1)
|
|
{
|
|
UpdateFanSpeeds();
|
|
sleep($interval);
|
|
}
|
|
|
|
1;
|
|
|
|
################################################################
|
|
loadconfig($)
|
|
sub loadconfig($)
|
|
{
|
|
my $file = shift;
|
|
|
|
print("Loading configuration from $file ...\n");
|
|
|
|
unless ( (-e $file) && (-r $file) )
|
|
{ die("Unable to read config file $file: $!"); }
|
|
|
|
open(F, $file);
|
|
|
|
our ($interval, $fctemps, $fcfans, $mintemp, $maxtemp, $minstart,
|
|
$minstop);
|
|
while($_ = <F>)
|
|
{
|
|
if ($_ =~ /^\s+$/) { next; }
|
|
elsif ($_ =~ /^INTERVAL=(.*)$/) { $interval = $1; next; }
|
|
elsif ($_ =~ /^FCTEMPS=(.*)$/) { $fctemps = $1; next; }
|
|
elsif ($_ =~ /^FCFANS=(.*)$/) { $fcfans = $1; next; }
|
|
elsif ($_ =~ /^MINTEMP=(.*)$/) { $mintemp = $1; next; }
|
|
elsif ($_ =~ /^MAXTEMP=(.*)$/) { $maxtemp = $1; next; }
|
|
elsif ($_ =~ /^MINSTART=(.*)$/) { $minstart = $1; next; }
|
|
elsif ($_ =~ /^MINSTOP=(.*)$/) { $minstop = $1; next; }
|
|
}
|
|
close(F);
|
|
|
|
unless (defined($interval))
|
|
{ die("Some settings missing ..."); }
|
|
|
|
print("\nCommon settings: \n");
|
|
print(" INTERVAL=$interval\n");
|
|
|
|
my $fcvcount = 0;
|
|
foreach my $fcv (split(/\s+/, $fctemps))
|
|
{
|
|
($afcpwm[$fcvcount], $afctemp[$fcvcount]) = split(/=/, $fcv);
|
|
|
|
$fcfans =~ s/^\S*=(\S+)\s*//; $afcfan[$fcvcount] = $1;
|
|
$mintemp =~ s/^\S*=(\S+)\s*//; $afcmintemp[$fcvcount] = $1;
|
|
$maxtemp =~ s/^\S*=(\S+)\s*//; $afcmaxtemp[$fcvcount] = $1;
|
|
$minstart =~ s/^\S*=(\S+)\s*//; $afcminstart[$fcvcount] = $1;
|
|
$minstop =~ s/^\S*=(\S+)\s*//; $afcminstop[$fcvcount] = $1;
|
|
|
|
print("\nSettings for $afcpwm[$fcvcount]:\n");
|
|
print(" Depends on $afctemp[$fcvcount]\n");
|
|
print(" Controls $afcfan[$fcvcount]\n");
|
|
print(" MINTEMP = $afcmintemp[$fcvcount]\n");
|
|
print(" MAXTEMP = $afcmaxtemp[$fcvcount]\n");
|
|
print(" MINSTART = $afcminstart[$fcvcount]\n");
|
|
print(" MINSTOP = $afcminstop[$fcvcount]\n");
|
|
|
|
$fcvcount++;
|
|
}
|
|
}
|
|
|
|
|
|
################################################################
|
|
pwmdisable($)
|
|
sub pwmdisable($)
|
|
{
|
|
my $p = shift;
|
|
|
|
if ($sysfs == 1)
|
|
{
|
|
if (open(F, ">$dir/$p"))
|
|
{
|
|
print(F MAX . '\n');
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("$dir/$p : $!"); }
|
|
|
|
my $enable = "$dir/$p/pwm/pwm_enable";
|
|
if (-f $enable)
|
|
{
|
|
if (open(F, ">$enable"))
|
|
{
|
|
print(F '0');
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("$dir/$p/pwm/pwm_enable : $!"); }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (open(F, ">$dir/$p"))
|
|
{
|
|
print(F MAX . ' 0');
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("$dir/$p : $!"); }
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
#################################################################
|
|
pwmenable($)
|
|
sub pwmenable($)
|
|
{
|
|
my $p = shift;
|
|
|
|
if ($sysfs == 1)
|
|
{
|
|
my $enable = "$dir/$p/pwm/pwm_enable";
|
|
if (-f $enable)
|
|
{
|
|
if (open(F, ">$enable"))
|
|
{
|
|
print(F "1\n");
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("$dir/$p : $!\n"); }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (open(F, ">$dir/$p"))
|
|
{
|
|
print(F MAX . " 1\n");
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("$dir/$p : $!\n"); }
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
|
|
################################################################
|
|
restorefans()
|
|
sub restorefans()
|
|
{
|
|
$SIG{TERM} = 'IGNORE';
|
|
$SIG{HUP} = 'IGNORE';
|
|
$SIG{INT} = 'IGNORE';
|
|
$SIG{QUIT} = 'IGNORE';
|
|
|
|
print("Aborting, restoring fans...\n");
|
|
my $fcvcount = 0;
|
|
|
|
while ( $fcvcount < $#afcpwm+1)
|
|
{
|
|
my $pwmo = $afcpwm[$fcvcount];
|
|
&pwmdisable($afcpwm[$fcvcount]);
|
|
$fcvcount++;
|
|
}
|
|
print("Verify fans have returned to full speed\n");
|
|
POSIX:_exit(-1);
|
|
}
|
|
|
|
|
|
############################################################
|
|
UpdateFanSpeeds()
|
|
sub UpdateFanSpeeds()
|
|
{
|
|
my $fcvcount = 0;
|
|
|
|
while ($fcvcount < $#afcpwm+1)
|
|
{
|
|
my $pwmo = $afcpwm[$fcvcount];
|
|
my $tsens = $afctemp[$fcvcount];
|
|
my $fan = $afcfan[$fcvcount];
|
|
my $mint = $afcmintemp[$fcvcount];
|
|
my $maxt = $afcmaxtemp[$fcvcount];
|
|
my $minsa = $afcminstart[$fcvcount];
|
|
my $minso = $afcminstop[$fcvcount];
|
|
|
|
### tval
|
|
my $tval = 0;
|
|
if (open(F, "$dir/$tsens"))
|
|
{
|
|
$tval = <F>;
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("Error reading temperature from $dir/$tsens"); }
|
|
$tval =~ /([.\d]+)\s*$/;
|
|
$tval = int($1);
|
|
if ($sysfs == 1)
|
|
{ $tval /= 1000; }
|
|
|
|
### pwmpval
|
|
my $pwmpval = 0;
|
|
if (open(F, "$dir/$pwmo"))
|
|
{
|
|
$pwmpval = <F>;
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("Error reading PWM value from $dir/$pwmo"); }
|
|
($pwmpval) = split(/\s/, $pwmpval);
|
|
|
|
### fanval
|
|
my $fanval = 0;
|
|
if (open(F, "$dir/$fan"))
|
|
{
|
|
$fanval = <F>;
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("Error reading Fan value from $dir/$fan"); }
|
|
$fanval =~ /(\d+)\s$/;
|
|
$fanval = $1;
|
|
|
|
### DEBUG
|
|
if (DEBUG == 1)
|
|
{
|
|
print("pwmo=$pwmo\n");
|
|
print("tsens=$tsens\n");
|
|
print("fan=$fan\n");
|
|
print("mint=$mint\n");
|
|
print("maxt=$maxt\n");
|
|
print("minsa=$minsa\n");
|
|
print("minso=$minso\n");
|
|
print("tval=$tval\n");
|
|
print("pwmpval=$pwmpval\n");
|
|
print("fanval=$fanval\n");
|
|
print("\n");
|
|
}
|
|
|
|
my $pwmval;
|
|
if ($tval <= $mint)
|
|
{ $pwmval = 0; }
|
|
elsif ($tval >= $maxt)
|
|
{ $pwmval = MAX; }
|
|
else
|
|
{
|
|
$pwmval = eval ( ($tval - $mint) / ($maxt - $mint) )**2 ;
|
|
$pwmval *= (255 - $minso);
|
|
$pwmval += $minso;
|
|
$pwmval = int($pwmval);
|
|
if ( ($pwmval == 0) || ($fanval == 0) )
|
|
{
|
|
if (open(F, ">$dir/$pwmo"))
|
|
{
|
|
print(F "$minsa\n");
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("Error writing PWM value to $dir/$pwmo : $!\n"); }
|
|
sleep 1;
|
|
}
|
|
}
|
|
|
|
if (open(F, ">$dir/$pwmo"))
|
|
{
|
|
print(F "$pwmval\n");
|
|
close(F);
|
|
}
|
|
else
|
|
{ die("Error writing PWM value to $dir/$pwmo : $!\n"); }
|
|
|
|
if (DEBUG == 1)
|
|
{ print("new pwmval = $pwmval\n"); }
|
|
|
|
$fcvcount++;
|
|
}
|
|
|
|
}
|
|
|
|
1;
|
|
|
|
|