+++ /dev/null
-
-/*
-
- breezed is a fan speed control daemon for Linux computers.
-
- Copyright (c) 2008, 2009 Francois Fleuret
- Written by Francois Fleuret <francois@fleuret.org>
-
- This file is part of breezed.
-
- breezed is free software: you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 3 as
- published by the Free Software Foundation.
-
- breezed 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 breezed. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <unistd.h>
-#include <string.h>
-
-#define BUFFER_SIZE 4096
-
-using namespace std;
-
-const int major_version_number = 1;
-const int minor_version_number = 2;
-
-const int buffer_size = 1024;
-
-const char *default_configuration_file = "/etc/breezed.conf";
-
-// The time period to check the temperature.
-const int polling_delay = 5;
-
-// Minimum time between last change and a change down.
-const int minimum_delay_to_go_down = 30;
-
-// Gap between a threshold to go up and a threshold to go down to
-// reduce the oscillations.
-const int down_temperature_delta = 2;
-
-char *file_fan = 0;
-
-int debug = 0;
-int nb_rounds_since_last_change = 0;
-int last_level = -1;
-
-int file_fan_fd;
-
-int nb_temperature_thresholds;
-int *temperature_thresholds = 0;
-
-int nb_file_thermal = 0;
-char **file_thermal = 0;
-int *file_thermal_fd = 0;
-
-char *configuration_file;
-
-//////////////////////////////////////////////////////////////////////
-
-char *next_word(char *buffer, char *r, int buffer_size) {
- char *s;
- s = buffer;
-
- if(r != 0) {
- while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
- if(*r == '"') {
- r++;
- while((*r != '"') && (*r != '\0') &&
- (s<buffer+buffer_size-1))
- *s++ = *r++;
- if(*r == '"') r++;
- } else {
- while((*r != '\r') && (*r != '\n') && (*r != '\0') &&
- (*r != '\t') && (*r != ' ') && (*r != ',')) {
- if(s == buffer + buffer_size) {
- fprintf(stderr, "Buffer overflow in next_word.\n");
- exit(1);
- }
- *s++ = *r++;
- }
- }
-
- while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
- if((*r == '\0') || (*r=='\r') || (*r=='\n')) r = 0;
- }
- *s = '\0';
-
- return r;
-}
-
-void set_fan_level(int fan_fd, int f, int max_fan_level) {
- char buffer[buffer_size];
- if(f < 0 || f > max_fan_level) f = max_fan_level;
- sprintf(buffer, "level %d\n", f);
- if(write(fan_fd, buffer, strlen(buffer)) < 0) {
- fprintf(stderr, "Error in setting the fan level (%s).\n",
- strerror(errno));
- exit(1);
- }
-}
-
-void define_thermal_files(char *definition) {
- char token[buffer_size];
-
- if(file_thermal) {
- fprintf(stderr, "Thermal files already defined.\n");
- exit(1);
- }
-
- char *s;
- s = definition;
- while(s) {
- s = next_word(token, s, buffer_size);
- nb_file_thermal++;
- }
- file_thermal = new char *[nb_file_thermal];
- file_thermal_fd = new int[nb_file_thermal];
- s = definition;
- int k = 0;
- while(s) {
- s = next_word(token, s, buffer_size);
- file_thermal[k] = new char[strlen(token) + 1];
- strcpy(file_thermal[k], token);
- k++;
- }
-}
-
-void define_temperature_thresholds(char *definition) {
- char token[buffer_size];
-
- if(temperature_thresholds) {
- fprintf(stderr, "Temperature thresholds already defined.\n");
- exit(1);
- }
-
- nb_temperature_thresholds = 1;
-
- char *s;
- s = definition;
- while(s) {
- s = next_word(token, s, buffer_size);
- nb_temperature_thresholds++;
- }
-
- temperature_thresholds = new int[nb_temperature_thresholds];
-
- temperature_thresholds[0] = -1;
-
- s = definition;
- int k = 1;
- while(s) {
- s = next_word(token, s, buffer_size);
- temperature_thresholds[k] = atoi(token);
- if(k > 0 &&
- temperature_thresholds[k] < temperature_thresholds[k-1]) {
- fprintf(stderr, "The temperature thresholds have to be increasing.\n");
- exit(0);
- }
- k++;
- }
-}
-
-//////////////////////////////////////////////////////////////////////
-
-int main(int argc, char **argv) {
-
- char buffer[buffer_size], token[buffer_size];
-
- configuration_file = new char[strlen(default_configuration_file) + 1];
- strcpy(configuration_file, default_configuration_file);
-
- int i = 1;
- while(i < argc) {
-
- if(strcmp(argv[i], "--debug") == 0 || strcmp(argv[i], "-d") == 0) {
- debug = 1;
- i++;
- }
-
- else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
- printf("Breezed v%d.%d. Written by Francois Fleuret (francois@fleuret.org).\n",
- major_version_number, minor_version_number);
- exit(0);
- }
-
- else if(strcmp(argv[i], "--no-configuration-file") == 0 ||
- strcmp(argv[i], "-ncf") == 0) {
- delete[] configuration_file;
- configuration_file = 0;
- i++;
- }
-
- else if(strcmp(argv[i], "--configuration-file") == 0 ||
- strcmp(argv[i], "-cf") == 0) {
- i++;
- if(i == argc) {
- fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
- exit(1);
- }
-
- delete[] configuration_file;
- configuration_file = new char[strlen(argv[i]) + 1];
- strcpy(configuration_file, argv[i]);
-
- i++;
- }
-
- else if(strcmp(argv[i], "--thermal-files") == 0 ||
- strcmp(argv[i], "-tf") == 0) {
- i++;
- if(i == argc) {
- fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
- exit(1);
- }
- define_thermal_files(argv[i]);
- i++;
- }
-
- else if(strcmp(argv[i], "--fan-file") == 0 ||
- strcmp(argv[i], "-ff") == 0) {
- i++;
- if(i == argc) {
- fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
- exit(1);
- }
-
- if(file_fan) {
- fprintf(stderr, "Fan file already defined.\n");
- exit(1);
- }
- file_fan = new char[strlen(argv[i]) + 1];
- strcpy(file_fan, argv[i]);
-
- i++;
- }
-
- else if(strcmp(argv[i], "--temperature-thresholds") == 0 ||
- strcmp(argv[i], "-tt") == 0) {
- i++;
-
- if(i == argc) {
- fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
- exit(1);
- }
-
- define_temperature_thresholds(argv[i]);
- i++;
- }
-
- else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
-
- printf("%s [--version|-v] [--help|-h] [--debug|-d]\\\n\
- [--configuration-file|-cf <file>]\\\n\
- [--no-configuration-file|-ncf]\\\n\
- [--thermal-files|-tf <thermalfile1,thermalfile2,...>] \\\n\
- [--fanfile|-ff <fan file>] \\\n\
- [--temperature-thresholds|-tt <t1,t2,t3,t4,...>]\n\
-\n\
- --help|-h : shows this help\n\
- --version|-v : shows the version number\n\
- --debug|-d : prints out additional information\n\
- --configuration-file|-cf sets the configuration file\n\
- --no-configuration-file|-ncf do not load a configuration file\n\
- --thermal-files|-tf : sets where to look for temperatures\n\
- --fanfile|-ff : sets where to control the fan level\n\
- --temperature-thresholds|-tt : sets the temperature thresholds\n\
-\n\
-This daemon polls the temperatures every 5s, takes the max and sets\n\
-the fan level accordingly. It uses as temperatures all the numbers it\n\
-finds in the provided \"thermal files\". Hence you can use as well\n\
-/proc/acpi/thermal_zone/THM*/temperature or /proc/acpi/ibm/thermal.\n\
-\n\
-The fan speed is set by echoing into the provided fan file.\n\
-\n\
-To reduce oscillations, it will not reduce the fan speed less than 30s\n\
-after the last previous change and the thresholds actually used to\n\
-reduce the fan speed are two degrees lower than the provided\n\
-thresholds, which are used to increase the fan speed.\n\
-\n\
-This daemon should be started through the adequate shell script in\n\
-/etc/init.d.\n\
-\n\
-Options can be set either in the configuration file, or on the \n\
-command line. Options can not be set twice.\n\
-\n\
-Version %d.%d, November 2009.\n\
-\n\
-Written by Francois Fleuret (francois@fleuret.org).\n",
- argv[0],
- major_version_number, minor_version_number);
-
- exit(0);
- }
-
- else {
- fprintf(stderr, "Unknown argument %s.\n", argv[i]);
- exit(1);
- }
- }
-
-
- //////////////////////////////////////////////////////////////////////
-
- if(configuration_file) {
- char raw_line[BUFFER_SIZE];
- int start, end, eol, k;
- FILE *file;
-
- file = fopen(configuration_file, "r");
-
- if(!file) {
- fprintf(stderr, "Can not open `%s' for reading.\n", configuration_file);
- exit(1);
- }
-
- start = 0;
- end = 0;
-
- char *s;
-
- int line_number = 0;
-
- while(end > start || !feof(file)) {
- eol = start;
-
- /* Look for the end of a line in what is already in the buffer */
- while(eol < end && raw_line[eol] != '\n') eol++;
-
- /* if we did not find the of a line, move what has not been
- processed and is in the buffer to the beginning of the buffer,
- fill the buffer with new data from the file, and look for the
- end of a line */
- if(eol == end) {
- for(k = 0; k < end - start; k++) {
- raw_line[k] = raw_line[k + start];
- }
- end -= start;
- eol -= start;
- start = 0;
- end += fread(raw_line + end, sizeof(char), BUFFER_SIZE - end, file);
- while(eol < end && raw_line[eol] != '\n') eol++;
- }
-
- /* The end of the line is the buffer size, which means the line is
- too long */
-
- if(eol == BUFFER_SIZE) {
- raw_line[BUFFER_SIZE - 1] = '\0';
- fprintf(stderr, "Selector: Line too long (max is %d characters):\n",
- BUFFER_SIZE);
- fprintf(stderr, raw_line);
- fprintf(stderr, "\n");
- exit(1);
- }
-
- /* If we got a line, we replace the carriage return by a \0 to
- finish the string */
-
- raw_line[eol] = '\0';
-
- /* here we process the line */
-
- line_number++;
-
- if(debug) {
- printf("%s:%d \"%s\"\n",
- configuration_file, line_number, raw_line + start);
- }
-
- s = next_word(token, raw_line + start, buffer_size);
-
- if(strcmp(token, "thermal_files") == 0) {
- if(s == 0) {
- fprintf(stderr, "Missing parameter in %s:%d\n",
- configuration_file, line_number);
- exit(1);
- }
- define_thermal_files(s);
- }
-
- else if(strcmp(token, "debug") == 0) {
- debug = 1;
- }
-
- else if(strcmp(token, "fan_file") == 0) {
- if(file_fan) {
- fprintf(stderr, "Fan file already defined.\n");
- exit(1);
- }
- if(s == 0) {
- fprintf(stderr, "Missing parameter in %s:%d\n",
- configuration_file, line_number);
- exit(1);
- }
- file_fan = new char[strlen(s) + 1];
- strcpy(file_fan, s);
- }
-
- else if(strcmp(token, "temperature_thresholds") == 0) {
- if(s == 0) {
- fprintf(stderr, "Missing parameter in %s:%d\n",
- configuration_file, line_number);
- exit(1);
- }
- define_temperature_thresholds(s);
- }
-
- else if(token[0] && token[0] != '#') {
- fprintf(stderr, "Unknown keyword '%s' in %s:%d.\n",
- token, configuration_file, line_number);
- exit(1);
- }
-
- start = eol + 1;
- }
-
- }
-
- //////////////////////////////////////////////////////////////////////
-
- if(nb_temperature_thresholds == 0) {
- fprintf(stderr, "No temperature threshold was provided.\n");
- exit(1);
- }
-
- if(nb_file_thermal == 0) {
- fprintf(stderr, "No thermal file was provided.\n");
- exit(1);
- }
-
- if(file_fan == 0){
- fprintf(stderr, "No fan file was provided.\n");
- exit(1);
- }
-
- for(int i = 0; i < nb_file_thermal; i++) {
- file_thermal_fd[i] = open(file_thermal[i], O_RDONLY);
- if(file_thermal_fd[i] < 0) {
- fprintf(stderr, "Can not open %s for reading (%s).\n",
- file_thermal[i], strerror(errno));
- exit(1);
- }
- }
-
- file_fan_fd = open(file_fan, O_WRONLY);
-
- if(file_fan_fd < 0) {
- fprintf(stderr, "Can not open %s for writing (%s).\n",
- file_fan, strerror(errno));
- exit(1);
- }
-
- //////////////////////////////////////////////////////////////////////
-
- if(debug) {
- for(int t = 0; t < nb_file_thermal; t++) {
- printf("file_thermal[%d] %s\n", t, file_thermal[t]);
- }
-
- printf("file_fan %s\n", file_fan);
-
- for(int t = 0; t < nb_temperature_thresholds; t++) {
- printf("temperature_thresholds[%d] %d",
- t, temperature_thresholds[t]);
- }
- }
-
- //////////////////////////////////////////////////////////////////////
-
- while(1) {
-
- int temperature = -1;
-
- if(debug) {
- printf("Temperature:");
- }
-
- for(int i = 0; i < nb_file_thermal; i++) {
- lseek(file_thermal_fd[i], 0, SEEK_SET);
- int size = read(file_thermal_fd[i], buffer, buffer_size);
-
- if(size > 0 && size < buffer_size) {
- buffer[size] = '\0';
-
- char *s = buffer;
-
- while(*s) {
- while(*s && *s != '-' && (*s < '0') || (*s > '9')) s++;
-
- if(*s) {
- int t = 0, sign = 1;
- if(*s == '-') {
- sign = -1;
- s++;
- }
-
- while(*s >= '0' && *s <= '9') {
- t = t * 10 + (*s - '0');
- s++;
- }
-
- t *= sign;
- if(debug) {
- printf(" %d", t);
- }
-
- // So that we can deal with the new files where the
- // temperature are in 1/1000th of C
- if(t > 1000) t /= 1000;
-
- if(t > temperature) temperature = t;
- }
- }
-
- if(debug) {
- if(i < nb_file_thermal - 1)
- printf(" /");
- else
- printf("\n");
- }
- } else {
- if(size == 0) {
- fprintf(stderr, "Nothing to read in %d.\n", file_thermal[i]);
- } else if(size < 0) {
- fprintf(stderr, "Error while reading %s (%s).\n",
- file_thermal[i], strerror(errno));
- } else {
- fprintf(stderr, "Error while reading %s (too large).\n",
- file_thermal[i]);
- }
- exit(1);
- }
- }
-
- if(temperature < 0) {
- fprintf(stderr, "Could not read a meaningful temperature.\n");
- exit(1);
- }
-
- nb_rounds_since_last_change++;
-
- int new_level = last_level;
-
- int new_level_up = nb_temperature_thresholds - 1;
- while(new_level_up > 0 &&
- temperature < temperature_thresholds[new_level_up]) {
- new_level_up--;
- }
-
- if(new_level_up > last_level) {
- new_level = new_level_up;
- } else {
- int new_level_down = nb_temperature_thresholds - 1;
- while(new_level_down > 0 &&
- temperature <
- temperature_thresholds[new_level_down] - down_temperature_delta)
- new_level_down--;
- if(new_level_down < last_level &&
- nb_rounds_since_last_change * polling_delay >=
- minimum_delay_to_go_down) {
- new_level = new_level_down;
- }
- }
-
- // We set it every time, even when there is no change, to handle
- // when the level has been modified somewhere else (for instance
- // when combing back from suspend).
-
- set_fan_level(file_fan_fd, new_level, nb_temperature_thresholds - 1);
-
- if(new_level != last_level) {
- nb_rounds_since_last_change = 0;
- last_level = new_level;
- if(debug) {
- printf("Temperature is %dC setting the fan level to %d.",
- temperature, new_level);
- }
- }
-
- sleep(polling_delay);
- }
-}