Fancontrol
This program monitors CPU and hard drive temperatures and changes fan speeds accordingly.
C program fwcontrol.c
// Author: Frank Wulf
// Version: 1.1 (2017-12-28)
//
// This program can be used to control fan speeds automatically. It runs as
// a daemon and sets the PWM values of the fans based on CPU temperature
// and/or hard drive temperatures. The behaviour of this program can be
// configured in file /etc/fwcontrol.conf.
//
// Version history:
// 1.0 2017-11-17 Initial release
// 1.1 2017-12-28 Code optimized
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <scsi/sg.h>
#define BUFSIZE 256
#define MAX_FANS 10
#define MAX_STEP 10
#define MAX_HDD 32
#define SMART_BUFFER_SIZE 512
#define SMART_SENSE_BUFFER_SIZE 32
#define SMART_CMD_LENGTH 12
enum {
ATA_OP_CHECKPOWERMODE1 = 0xe5,
ATA_OP_CHECKPOWERMODE2 = 0x98,
ATA_USING_LBA = (1 << 6),
ATA_STAT_DRQ = (1 << 3),
ATA_STAT_ERR = (1 << 0),
};
enum {
SG_ATA_16 = 0x85,
SG_ATA_16_LEN = 16,
SG_ATA_PROTO_NON_DATA = (3 << 1)
};
enum {SG_CDB2_CHECK_COND = (1 << 5)};
enum {CPU, HDD};
struct s_fan {
char name[20];
char control_by_cpu;
char control_by_hdd;
char pwm_enable[50];
char pwm_write[50];
char cpu_input[50];
unsigned char idle_pwm;
short stop_delay;
short decr_delay;
short interval_cpu;
short interval_hdd;
char interpolate_cpu;
char interpolate_hdd;
short hyst_cpu;
short hyst_hdd;
short count_scan_hdd;
short count_step_cpu;
short count_step_hdd;
time_t next_cpu_check;
time_t next_hdd_check;
char first_check;
short cpu_temp;
short hdd_temp;
unsigned char cpu_pwm;
unsigned char hdd_pwm;
unsigned char actual_pwm;
time_t change_time;
time_t min_decr_time;
time_t min_stop_time;
} fan[MAX_FANS];
struct s_temp_to_pwm {
short temp;
unsigned char pwm;
} data_pwm[2][MAX_FANS][MAX_STEP];
struct s_scan_hdd {
char name[10];
} scan_hdd[MAX_FANS][MAX_HDD];
struct s_hdd_temp {
char name[10];
short temp;
} hdd_buffer[MAX_HDD];
short count_fans = 0;
time_t now;
time_t next_check;
typedef void (*sighandler_t)(int);
static sighandler_t
handle_signal(int sig_nr, sighandler_t signalhandler) {
struct sigaction new_sig, old_sig;
new_sig.sa_handler = signalhandler;
sigemptyset(&new_sig.sa_mask);
new_sig.sa_flags = SA_RESTART;
if (sigaction(sig_nr, &new_sig, &old_sig) < 0)
return SIG_ERR;
return old_sig.sa_handler;
}
static void start_daemon(const char *log_name, int facility) {
int i;
pid_t pid;
/* Fork off parent process */
if ((pid = fork()) != 0)
exit(EXIT_FAILURE);
/* Create new SID for child process */
if (setsid() < 0)
exit(EXIT_FAILURE);
/* Ignore signal SIGHUP */
handle_signal(SIGHUP, SIG_IGN);
/* Fork off child process */
if ((pid = fork()) != 0)
exit(EXIT_FAILURE);
/* Change working directory */
chdir("/");
/* Change file mode mask */
umask(0);
/* Close all file descriptors */
for (i = sysconf(_SC_OPEN_MAX); i > 0; i--)
close(i);
openlog(log_name, LOG_PID | LOG_CONS | LOG_NDELAY, facility );
}
int read_conf(void) {
FILE *fp;
char *name, *value, buf[BUFSIZE];
int i = -1, j, k;
now = time(NULL);
if ((fp = fopen("/etc/fwcontrol.conf", "r")) == NULL) {
syslog(LOG_ERR, "Error opening configuration file\n");
return -1;
}
while (fgets(buf, BUFSIZE, fp) != NULL) {
if (buf[0] == '[') {
if (++i >= MAX_FANS)
break;
count_fans = i + 1;
strcpy(fan[i].name, strtok(buf, "[]\n"));
fan[i].first_check = 1;
k = 0;
} else if (i >= 0) {
name = strtok(buf, "=");
value = strtok(NULL, "=");
value = strtok(value, "\n");
if (strcmp(name, "control_by_cpu") == 0) {
fan[i].control_by_cpu = atoi(value);
fan[i].next_cpu_check = now;
} else if (strcmp(name, "control_by_hdd") == 0) {
fan[i].control_by_hdd = atoi(value);
fan[i].next_hdd_check = now;
} else if (strcmp(name, "pwm_enable") == 0) {
strcpy(fan[i].pwm_enable, value);
} else if (strcmp(name, "pwm_write") == 0) {
strcpy(fan[i].pwm_write, value);
} else if (strcmp(name, "cpu_input") == 0) {
strcpy(fan[i].cpu_input, value);
} else if (strcmp(name, "stop_delay") == 0) {
fan[i].stop_delay = atoi(value);
} else if (strcmp(name, "decrease_delay") == 0) {
fan[i].decr_delay = atoi(value);
} else if (strcmp(name, "interval_cpu") == 0) {
fan[i].interval_cpu = atoi(value);
} else if (strcmp(name, "interval_hdd") == 0) {
fan[i].interval_hdd = atoi(value);
} else if (strcmp(name, "interpolate_cpu") == 0) {
fan[i].interpolate_cpu = atoi(value);
} else if (strcmp(name, "interpolate_hdd") == 0) {
fan[i].interpolate_hdd = atoi(value);
} else if (strcmp(name, "hyst_cpu") == 0) {
fan[i].hyst_cpu = atoi(value);
} else if (strcmp(name, "hyst_hdd") == 0) {
fan[i].hyst_hdd = atoi(value);
} else if (strcmp(name, "idle_pwm") == 0) {
fan[i].idle_pwm = atoi(value);
} else if (strcmp(name, "scan_hdd") == 0) {
value = strtok(value, ",");
while ((value != NULL) && (++k <= MAX_HDD)) {
strcpy(scan_hdd[i][k-1].name, value);
value = strtok(NULL, ",");
}
fan[i].count_scan_hdd = k;
} else if (strcmp(name, "temp_pwm_hdd") == 0) {
j = 0;
value = strtok(value, ",");
while ((value != NULL) && (++j <= MAX_STEP)) {
data_pwm[HDD][i][j-1].temp = atoi(value);
value = strtok(NULL, ",");
data_pwm[HDD][i][j-1].pwm = atoi(value);
value = strtok(NULL, ",");
}
fan[i].count_step_hdd = j;
} else if (strcmp(name, "temp_pwm_cpu") == 0) {
j = 0;
value = strtok(value, ",");
while ((value != NULL) && (++j <= MAX_STEP)) {
data_pwm[CPU][i][j-1].temp = atoi(value);
value = strtok(NULL, ",");
data_pwm[CPU][i][j-1].pwm = atoi(value);
value = strtok(NULL, ",");
}
fan[i].count_step_cpu = j;
}
}
}
fclose(fp);
return 0;
}
static inline int sgio_send_for_status(int fd, unsigned char cmd, unsigned char *rv) {
unsigned char cdb[SG_ATA_16_LEN], sb[32], *desc, status, error;
sg_io_hdr_t io_hdr;
memset(&cdb, 0, sizeof(cdb));
memset(&sb, 0, sizeof(sb));
memset(&io_hdr, 0, sizeof(io_hdr));
cdb[0] = SG_ATA_16;
cdb[1] = SG_ATA_PROTO_NON_DATA;
cdb[2] = SG_CDB2_CHECK_COND;
cdb[13] = ATA_USING_LBA;
cdb[14] = cmd;
io_hdr.cmd_len = SG_ATA_16_LEN;
io_hdr.interface_id = 'S';
io_hdr.mx_sb_len = sizeof(sb);
io_hdr.dxfer_direction = SG_DXFER_NONE;
io_hdr.dxfer_len = 0;
io_hdr.dxferp = NULL;
io_hdr.cmdp = cdb;
io_hdr.sbp = sb;
io_hdr.pack_id = 0;
io_hdr.timeout = 500; // Milliseconds
if (ioctl(fd, SG_IO, &io_hdr) == -1) {
syslog(LOG_ERR, "ioctl() failed (cmd %u, %s)\n", cmd, strerror(errno));
return -1;
}
desc = sb + 8;
status = desc[13];
error = desc[3];
if (rv)
*rv = desc[5];
if (status & (ATA_STAT_ERR | ATA_STAT_DRQ)) {
syslog(LOG_ERR, "SG_IO command %u failed (status %u, error %u)\n",
cmd, status, error);
return -1;
}
return 0;
}
static inline char get_hdd_status(char *name) {
int fd, ret;
unsigned char state;
if ((fd = open(name, O_RDONLY)) < 0) {
syslog(LOG_ERR, "Error opening file %s!\n", name);
return -1;
}
ret = 1;
if (sgio_send_for_status(fd, ATA_OP_CHECKPOWERMODE1, &state) &&
sgio_send_for_status(fd, ATA_OP_CHECKPOWERMODE2, &state))
ret = 0;
close(fd);
return (state == 0) ? 0 : 1; // 0 = Sleeping, 1 = Running
}
static inline short get_hdd_temp(char *name) {
short hdd_temp;
int fd;
unsigned char buffer[SMART_BUFFER_SIZE] = "";
unsigned char sense_buffer[SMART_SENSE_BUFFER_SIZE];
unsigned char smart_read_cdb[SMART_CMD_LENGTH] =
{0xa1, 0x0c, 0x0e, 0xd0, 1, 0, 0x4f, 0xc2, 0, 0xb0, 0, 0};
sg_io_hdr_t io_hdr;
if ((fd = open(name, O_RDONLY)) < 0) {
syslog(LOG_ERR, "Error opening file %s!\n", name);
return -1;
}
memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
io_hdr.interface_id = 'S';
io_hdr.cmd_len = SMART_CMD_LENGTH;
io_hdr.mx_sb_len = SMART_SENSE_BUFFER_SIZE;
io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
io_hdr.dxfer_len = SMART_BUFFER_SIZE;
io_hdr.dxferp = buffer;
io_hdr.cmdp = smart_read_cdb;
io_hdr.sbp = sense_buffer;
io_hdr.timeout = 500; // Milliseconds
if (ioctl(fd, SG_IO, &io_hdr) < 0) {
syslog(LOG_ERR, "ioctl() call for reading temperature failed\n");
close(fd);
return -1;
}
for (register int i = 2; i < 361; i+=12)
if ((int)buffer[i] == 194) {
hdd_temp = ((long long int)((buffer[i+5])|
(buffer[i+6]<<8)|
(buffer[i+7]<<16)|
(buffer[i+8]<<24)|
((long long int)buffer[i+9]<<32)|
((long long int)buffer[i+10]<<40))) & 0xFF;
break;
}
close(fd);
return hdd_temp;
}
static inline short get_cpu_temp(char *name) {
FILE *fp;
char buf[BUFSIZE];
short cpu_temp;
if ((fp = fopen(name, "r")) == NULL) {
syslog(LOG_ERR, "Error opening file %s!\n", name);
return -1;
}
if (fgets(buf, BUFSIZE, fp) != NULL)
cpu_temp = atoi(buf) / 1000;
fclose(fp);
return cpu_temp;
}
int set_fan_mode(void) {
FILE *fp;
for (int i = 0; i < count_fans; i++) {
if ((fp = fopen(fan[i].pwm_enable, "w")) == NULL) {
syslog(LOG_ERR, "Error opening file %s!\n", fan[i].pwm_enable);
return -1;
}
fprintf(fp, "1");
fclose(fp);
}
return 0;
}
static inline int set_fan_speed(char *name, unsigned char value) {
FILE *fp;
if ((fp = fopen(name, "w")) == NULL) {
syslog(LOG_ERR, "Error opening file %s!\n", name);
return -1;
}
fprintf(fp, "%d", value);
fclose(fp);
return 0;
}
static inline unsigned char calc_pwm(char type, int fan, short temp, char interpolate,
short count_step) {
unsigned char pwm = 0;
short j = 0;
while (temp >= data_pwm[type][fan][j].temp) {
pwm = data_pwm[type][fan][j].pwm;
if (++j >= count_step)
break;
}
if ((interpolate == 1) && (j > 0) && (j < count_step))
pwm = ((float)data_pwm[type][fan][j].pwm -
data_pwm[type][fan][j-1].pwm) /
(data_pwm[type][fan][j].temp - data_pwm[type][fan][j-1].temp) *
(temp - data_pwm[type][fan][j-1].temp) +
data_pwm[type][fan][j-1].pwm + 0.5;
return pwm;
}
static inline int control_fan_speed(void) {
struct stat device;
unsigned char new_pwm;
char sw_checked_cpu, sw_checked_hdd, sw_hdd_buffered, last_cpu_input[50];
short cpu_temp, hdd_temp, dev_temp, count_hdd_buffer = 0;
time_t last_cpu_check = 0;
now = time(NULL);
next_check = 0;
for (int i = 0; i < count_fans; i++) {
sw_checked_cpu = 0;
sw_checked_hdd = 0;
if ((fan[i].control_by_cpu == 1) && (now >= fan[i].next_cpu_check)) {
if ((last_cpu_check != now) ||
(strcmp(last_cpu_input, fan[i].cpu_input) != 0)) {
// Get CPU temperature
cpu_temp = get_cpu_temp(fan[i].cpu_input);
last_cpu_check = now;
strcpy(last_cpu_input, fan[i].cpu_input);
}
if (!((cpu_temp < fan[i].cpu_temp) &&
(((fan[i].cpu_temp - cpu_temp) < fan[i].hyst_cpu) ||
(now < fan[i].min_decr_time))) &&
(cpu_temp != fan[i].cpu_temp)) {
// Determine PWM value
fan[i].cpu_pwm = calc_pwm(CPU, i, cpu_temp,
fan[i].interpolate_cpu, fan[i].count_step_cpu);
fan[i].cpu_temp = cpu_temp;
}
while ((fan[i].next_cpu_check += fan[i].interval_cpu) <= now);
sw_checked_cpu = 1;
}
if ((fan[i].control_by_hdd == 1) && (now >= fan[i].next_hdd_check)) {
hdd_temp = 0;
for (int j = 0; j < fan[i].count_scan_hdd; j++) {
dev_temp = 0;
sw_hdd_buffered = 0;
for (int k = 0; k < count_hdd_buffer; k++)
if (strcmp(hdd_buffer[k].name, scan_hdd[i][j].name) == 0) {
dev_temp = hdd_buffer[k].temp;
sw_hdd_buffered = 1;
break;
}
if (sw_hdd_buffered == 0) {
// Check if hard drive exists
if (!((stat(scan_hdd[i][j].name, &device) != 0) ||
((device.st_mode & S_IFMT) != S_IFBLK)))
if (get_hdd_status(scan_hdd[i][j].name) == 1)
// Get temperature from hard drive
dev_temp = get_hdd_temp(scan_hdd[i][j].name);
strcpy(hdd_buffer[count_hdd_buffer].name,
scan_hdd[i][j].name);
hdd_buffer[count_hdd_buffer].temp = dev_temp;
++count_hdd_buffer;
}
if (dev_temp > hdd_temp)
hdd_temp = dev_temp;
}
if (!((hdd_temp < fan[i].hdd_temp) &&
(((fan[i].hdd_temp - hdd_temp) < fan[i].hyst_hdd) ||
(now < fan[i].min_decr_time))) &&
(hdd_temp != fan[i].hdd_temp)) {
// Determine PWM value
fan[i].hdd_pwm = calc_pwm(HDD, i, hdd_temp,
fan[i].interpolate_hdd, fan[i].count_step_hdd);
fan[i].hdd_temp = hdd_temp;
}
while ((fan[i].next_hdd_check += fan[i].interval_hdd) <= now);
sw_checked_hdd = 1;
}
if ((fan[i].control_by_cpu == 1) &&
((next_check == 0) || (next_check > fan[i].next_cpu_check)))
next_check = fan[i].next_cpu_check;
if ((fan[i].control_by_hdd == 1) &&
((next_check == 0) || (next_check > fan[i].next_hdd_check)))
next_check = fan[i].next_hdd_check;
if ((sw_checked_cpu == 1) || (sw_checked_hdd == 1)) {
if (fan[i].cpu_pwm > fan[i].hdd_pwm)
new_pwm = fan[i].cpu_pwm;
else
new_pwm = fan[i].hdd_pwm;
if (fan[i].first_check == 1)
// First check since program start
fan[i].first_check = 0;
else if (new_pwm == fan[i].actual_pwm)
continue;
else if (new_pwm < fan[i].actual_pwm)
// Check if decrease delay time has passed
if (now < fan[i].min_decr_time)
continue;
else if ((fan[i].control_by_cpu == 1) &&
(sw_checked_cpu == 0) && (new_pwm < fan[i].cpu_pwm))
// CPU check skipped and new PWM would be lower than
// calculated for last CPU check
if (fan[i].cpu_pwm != fan[i].actual_pwm)
new_pwm = fan[i].cpu_pwm;
else
continue;
else if ((fan[i].control_by_hdd == 1) &&
(sw_checked_hdd == 0) && (new_pwm < fan[i].hdd_pwm))
// Hard drive check skipped and new PWM would be lower than
// calculated for last hard drive check
if (fan[i].hdd_pwm != fan[i].actual_pwm)
new_pwm = fan[i].hdd_pwm;
else
continue;
else if (new_pwm == 0)
// Check if stop delay time has passed
if (now < fan[i].min_stop_time)
if ((fan[i].idle_pwm != 0) &&
(fan[i].idle_pwm != fan[i].actual_pwm))
// Set fan speed to idle
new_pwm = fan[i].idle_pwm;
else
continue;
// Set new fan speed
set_fan_speed(fan[i].pwm_write, new_pwm);
syslog(LOG_NOTICE,
"%s: PWM changed from %d to %d (CPU: %d°C, HDD: %d°C)\n",
fan[i].name, fan[i].actual_pwm, new_pwm, fan[i].cpu_temp,
fan[i].hdd_temp);
if (fan[i].actual_pwm < new_pwm)
fan[i].min_decr_time = now + fan[i].decr_delay;
if ((fan[i].actual_pwm == 0) && (new_pwm != 0))
fan[i].min_stop_time = now + fan[i].stop_delay;
fan[i].actual_pwm = new_pwm;
fan[i].change_time = now;
}
}
return 0;
}
int main (int argc, char **argv) {
start_daemon ("fwcontrol", LOG_LOCAL0);
syslog(LOG_NOTICE, "fwcontrol started ...\n");
read_conf();
set_fan_mode();
while (1) {
control_fan_speed();
sleep(next_check - now);
}
syslog(LOG_NOTICE, "fwcontrol terminated.\n");
closelog();
return EXIT_SUCCESS;
}
Configuration file /etc/fwcontrol.conf
# Configuration file for fwcontrol
#
# Parameter Value Description
# ================+===========+=========================================
# control_by_cpu | 0 or 1 | If set to 1 the fan is controlled by
# | | CPU temperature.
# control_by_hdd | 0 or 1 | If set to 1 the fan is controlled by
# | | hard drive temperature.
# pwm_enable | filename | Used to set the fan mode.
# pwm_write | filename | Used to change the fan speed.
# cpu_input | filename | Used to read the CPU temperature sensor.
# temp_pwm_cpu | temp,pwm | Defines the PWM value for a given CPU
# | | temperature. Up to 10 data pairs are
# | | possible: temp1,pwm1,temp2,pwm2,...
# interpolate_cpu | 0 or 1 | If set to 1 the program calculates
# | | intermediate values between the data
# | | points defined in "temp_cpu".
# hyst_cpu | temp | CPU temperature must be decreased by at
# | | least this value until fan speed can be
# | | reduced.
# scan_hdd | device(s) | Defines which hard drives should be
# | | checked. Up to 32 hard drives possible:
# | | hdd1,hdd2,hdd3,...
# temp_pwm_hdd | temp,pwm | Defines the PWM value for a given hard
# | | drive temperature. Up to 10 data pairs
# | | are possible: temp1,pwm1,temp2,pwm2,...
# interpolate_hdd | 0 or 1 | If set to 1 the program calculates
# | | intermediate values between the data
# | | points defined in "temp_hdd".
# hyst_hdd | temp | Hard drive temperature must be decreased
# | | by at least this value until fan speed
# | | can be reduced.
# idle_pwm | 0-255 | If the fan should be stopped but stop
# | | delay time has not yet passed, then the
# | | fan speed is set to this value.
# decrease_delay | seconds | Defines how many seconds must have
# | | passed since last speed increase until
# | | the speed can be decreased.
# stop_delay | seconds | Defines how many seconds must have
# | | passed since start of the fan until the
# | | fan can be stopped.
# interval_cpu | seconds | Check CPU temperature every n seconds.
# interval_hdd | seconds | Check hard drive temperatures every
# | | n seconds.
# ================+===========+=========================================
[CPU Cooler]
control_by_cpu=1
control_by_hdd=0
pwm_enable=/sys/class/hwmon/hwmon2/pwm2_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm2
cpu_input=/sys/class/hwmon/hwmon1/temp1_input
temp_pwm_cpu=0,20,29,20,30,46,41,46,80,255
interpolate_cpu=1
hyst_cpu=3
idle_pwm=20
decrease_delay=30
stop_delay=120
interval_cpu=3
[Front left]
control_by_cpu=1
control_by_hdd=1
pwm_enable=/sys/class/hwmon/hwmon2/pwm3_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm3
cpu_input=/sys/class/hwmon/hwmon1/temp1_input
scan_hdd=/dev/sdb
temp_pwm_cpu=0,78,30,140,41,140,60,255
temp_pwm_hdd=33,140,38,255
interpolate_cpu=1
interpolate_hdd=1
hyst_cpu=3
hyst_hdd=2
idle_pwm=78
decrease_delay=30
stop_delay=120
interval_cpu=15
interval_hdd=60
[Front right]
control_by_cpu=1
control_by_hdd=1
pwm_enable=/sys/class/hwmon/hwmon2/pwm4_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm4
cpu_input=/sys/class/hwmon/hwmon1/temp1_input
scan_hdd=/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf
temp_pwm_cpu=55,110,60,255
temp_pwm_hdd=33,78,38,255
interpolate_cpu=1
interpolate_hdd=1
hyst_cpu=3
hyst_hdd=2
idle_pwm=78
decrease_delay=30
stop_delay=120
interval_cpu=15
interval_hdd=60
[Rear left]
control_by_cpu=1
control_by_hdd=1
pwm_enable=/sys/class/hwmon/hwmon2/pwm1_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm1
cpu_input=/sys/class/hwmon/hwmon1/temp1_input
scan_hdd=/dev/sdb,/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf
temp_pwm_cpu=0,90,30,140,41,140,60,255
temp_pwm_hdd=33,140,38,255
interpolate_cpu=1
interpolate_hdd=1
hyst_cpu=3
hyst_hdd=2
idle_pwm=90
decrease_delay=30
stop_delay=120
interval_cpu=15
interval_hdd=60
[Rear right]
control_by_cpu=1
control_by_hdd=1
pwm_enable=/sys/class/hwmon/hwmon2/pwm5_enable
pwm_write=/sys/class/hwmon/hwmon2/pwm5
cpu_input=/sys/class/hwmon/hwmon1/temp1_input
scan_hdd=/dev/sdc,/dev/sdd,/dev/sde,/dev/sdf
temp_pwm_cpu=55,135,60,255
temp_pwm_hdd=37,135,40,255
interpolate_cpu=1
interpolate_hdd=1
hyst_cpu=3
hyst_hdd=2
idle_pwm=135
decrease_delay=30
stop_delay=120
interval_cpu=15
interval_hdd=60
Start fwcontrol during boot with script /etc/init.d/fwcontrol
#! /bin/sh
#
### BEGIN INIT INFO
# Provides: fwcontrol
# Required-Start: $syslog $remote_fs
# Required-Stop: $syslog $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: fwcontrol
# Description: Control of fan speeds
### END INIT INFO
NAME="fwcontrol"
DESC="Control of fan speeds"
DAEMON=/usr/sbin/${NAME}
FWUSER=root
PATH=/sbin:/bin:/usr/sbin:/usr/bin
test -f $DAEMON || exit 0
if [ -f /etc/default/$NAME ]; then
. /etc/default/$NAME
fi
mkdir -p /var/run
PIDFILE=/var/run/${NAME}.pid
RETVAL=0
case "$1" in
start)
echo -n "Starting ${DESC}: "
start-stop-daemon --start --quiet --make-pidfile --pidfile ${PIDFILE} --chuid ${FWUSER} --exec ${DAEMON} -- ${BOPTIONS}
RETVAL=$?
echo "${NAME}"
;;
stop)
echo -n "Stopping ${DESC}: "
start-stop-daemon --oknodo --stop --quiet --chuid ${FWUSER} --exec ${DAEMON} -- ${BOPTIONS}
RETVAL=$?
echo "${NAME}"
;;
restart|force-reload)
$0 stop
sleep 5
$0 start
RETVAL=$?
;;
*)
echo "Usage: /etc/init.d/${NAME} {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit $RETVAL
Logfile Configuration
File /etc/rsyslog.d/10-fwcontrol.conf:
local0.* /var/log/fwcontrol.log
& stop
Logfile rotation configured in file /etc/logrotate.d/fwcontrol:
/var/log/fwcontrol.log {
weekly
rotate 4
compress
delaycompress
notifempty
missingok
}