4 breezed is a fan speed control daemon for Linux computers.
6 Copyright (c) 2008, 2009, 2010 Francois Fleuret
7 Written by Francois Fleuret <francois@fleuret.org>
9 This file is part of breezed.
11 breezed is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 3 as
13 published by the Free Software Foundation.
15 breezed is distributed in the hope that it will be useful, but
16 WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with breezed. If not, see <http://www.gnu.org/licenses/>.
32 const int major_version_number = 1;
33 const int minor_version_number = 4;
35 const int buffer_size = 1024;
37 const char *default_configuration_file = "/etc/breezed.conf";
39 /* The time period to check the temperature. */
40 const int polling_delay = 5;
42 /* Minimum time between last change and a change down. */
43 const int minimum_delay_to_go_down = 30;
45 /* Gap between a threshold to go up and a threshold to go down to
46 reduce the oscillations. */
47 const int down_temperature_delta = 2;
52 int nb_rounds_since_last_change = 0;
57 int nb_temperature_thresholds;
58 int *temperature_thresholds = 0;
59 char **speed_names = 0;
61 int nb_file_thermal = 0;
62 char **file_thermal = 0;
63 int *file_thermal_fd = 0;
65 char *configuration_file;
67 /********************************************************************/
69 /* malloc with error checking. */
71 void *safe_malloc(size_t n) {
74 fprintf(stderr, "Can not allocate memory: %s\n", strerror(errno));
80 /******************************************************************/
82 char *next_word(char *buffer, char *r, int buffer_size) {
87 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
90 while((*r != '"') && (*r != '\0') &&
91 (s<buffer+buffer_size-1))
95 while((*r != '\r') && (*r != '\n') && (*r != '\0') &&
96 (*r != '\t') && (*r != ' ') && (*r != ',')) {
97 if(s == buffer + buffer_size) {
98 fprintf(stderr, "Buffer overflow in next_word.\n");
105 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
106 if((*r == '\0') || (*r=='\r') || (*r=='\n')) r = 0;
113 void set_fan_level(int fan_fd, int f, int max_fan_level) {
114 char buffer[buffer_size];
115 if(f < 0 || f > max_fan_level) f = max_fan_level;
116 sprintf(buffer, "level %s\n", speed_names[f]);
117 if(write(fan_fd, buffer, strlen(buffer)) < 0) {
118 fprintf(stderr, "Error in setting the fan level (%s).\n",
124 void define_thermal_files(char *definition) {
125 char token[buffer_size];
128 fprintf(stderr, "Thermal files already defined.\n");
135 s = next_word(token, s, buffer_size);
139 file_thermal = safe_malloc(nb_file_thermal * sizeof(char *));
140 file_thermal_fd = safe_malloc(nb_file_thermal * sizeof(int));
144 s = next_word(token, s, buffer_size);
145 file_thermal[k] = strdup(token);
150 void define_temperature_thresholds(char *definition) {
151 char token[buffer_size];
153 if(temperature_thresholds) {
154 fprintf(stderr, "Temperature thresholds already defined.\n");
158 nb_temperature_thresholds = 1;
164 s = next_word(token, s, buffer_size);
165 nb_temperature_thresholds++;
168 temperature_thresholds =
169 safe_malloc(nb_temperature_thresholds * sizeof(int));
172 safe_malloc(nb_temperature_thresholds * sizeof(char *));
174 temperature_thresholds[0] = -1;
179 s = next_word(token, s, buffer_size);
181 while(*u && *u != ':') { u++; }
184 temperature_thresholds[k] = atoi(token);
186 speed_names[k] = strdup(u);
188 temperature_thresholds[k] = atoi(token);
189 snprintf(token, buffer_size, "%d", k);
190 speed_names[k] = strdup(token);
194 temperature_thresholds[k] < temperature_thresholds[k-1]) {
195 fprintf(stderr, "The temperature thresholds have to be increasing.\n");
202 void evaluate_one_configuration_line(char *line, int line_number) {
203 char token[buffer_size];
206 s = next_word(token, line, buffer_size);
208 if(strcmp(token, "thermal_files") == 0) {
210 fprintf(stderr, "Missing parameter in %s:%d\n",
211 configuration_file, line_number);
214 define_thermal_files(s);
217 else if(strcmp(token, "debug") == 0) {
221 else if(strcmp(token, "fan_file") == 0) {
223 fprintf(stderr, "Fan file already defined.\n");
227 fprintf(stderr, "Missing parameter in %s:%d\n",
228 configuration_file, line_number);
231 file_fan = strdup(s);
234 else if(strcmp(token, "temperature_thresholds") == 0) {
236 fprintf(stderr, "Missing parameter in %s:%d\n",
237 configuration_file, line_number);
240 define_temperature_thresholds(s);
243 else if(token[0] && token[0] != '#') {
244 fprintf(stderr, "Unknown keyword '%s' in %s:%d.\n",
245 token, configuration_file, line_number);
250 /******************************************************************/
252 int main(int argc, char **argv) {
253 char buffer[buffer_size];
256 configuration_file = strdup(default_configuration_file);
261 if(strcmp(argv[i], "--debug") == 0 || strcmp(argv[i], "-d") == 0) {
266 else if(strcmp(argv[i], "--version") == 0 || strcmp(argv[i], "-v") == 0) {
267 printf("Breezed v%d.%d. Written by Francois Fleuret (francois@fleuret.org).\n",
268 major_version_number, minor_version_number);
272 else if(strcmp(argv[i], "--no-configuration-file") == 0 ||
273 strcmp(argv[i], "-ncf") == 0) {
274 free(configuration_file);
275 configuration_file = 0;
279 else if(strcmp(argv[i], "--configuration-file") == 0 ||
280 strcmp(argv[i], "-cf") == 0) {
283 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
287 free(configuration_file);
288 configuration_file = strdup(argv[i]);
293 else if(strcmp(argv[i], "--thermal-files") == 0 ||
294 strcmp(argv[i], "-tf") == 0) {
297 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
300 define_thermal_files(argv[i]);
304 else if(strcmp(argv[i], "--fan-file") == 0 ||
305 strcmp(argv[i], "-ff") == 0) {
308 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
313 fprintf(stderr, "Fan file already defined.\n");
316 file_fan = strdup(argv[i]);
321 else if(strcmp(argv[i], "--temperature-thresholds") == 0 ||
322 strcmp(argv[i], "-tt") == 0) {
326 fprintf(stderr, "Missing parameter for %s.\n", argv[i - 1]);
330 define_temperature_thresholds(argv[i]);
334 else if(strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) {
336 printf("%s [--version|-v] [--help|-h] [--debug|-d]\\\n\
337 [--configuration-file|-cf <file>]\\\n\
338 [--no-configuration-file|-ncf]\\\n\
339 [--thermal-files|-tf <thermalfile1,thermalfile2,...>] \\\n\
340 [--fanfile|-ff <fan file>] \\\n\
341 [--temperature-thresholds|-tt <t1,t2,t3,t4,...>]\n\
343 --help|-h : shows this help\n\
344 --version|-v : shows the version number\n\
345 --debug|-d : prints out additional information\n\
346 --configuration-file|-cf sets the configuration file\n\
347 --no-configuration-file|-ncf do not load a configuration file\n\
348 --thermal-files|-tf : sets where to look for temperatures\n\
349 --fanfile|-ff : sets where to control the fan level\n\
350 --temperature-thresholds|-tt : sets the temperature thresholds\n\
352 This daemon polls the temperatures every 5s, takes the max and sets\n\
353 the fan level accordingly. It uses as temperatures all the numbers it\n\
354 finds in the provided \"thermal files\". Hence you can use as well\n\
355 /proc/acpi/thermal_zone/THM*/temperature or /proc/acpi/ibm/thermal.\n\
357 The fan speed is set by echoing into the provided fan file.\n\
359 To reduce oscillations, it will not reduce the fan speed less than 30s\n\
360 after the last previous change and the thresholds actually used to\n\
361 reduce the fan speed are two degrees lower than the provided\n\
362 thresholds, which are used to increase the fan speed.\n\
364 This daemon should be started through the adequate shell script in\n\
367 Options can be set either in the configuration file, or on the \n\
368 command line. Options can not be set twice.\n\
370 Version %d.%d, November 2009.\n\
372 Written by Francois Fleuret (francois@fleuret.org).\n",
374 major_version_number, minor_version_number);
380 fprintf(stderr, "Unknown argument %s.\n", argv[i]);
386 /******************************************************************/
388 if(configuration_file) {
389 char raw_line[buffer_size];
390 int start, end, eol, k;
393 file = fopen(configuration_file, "r");
396 fprintf(stderr, "Can not open `%s' for reading.\n", configuration_file);
407 while(end > start || !feof(file)) {
410 /* Look for the end of a line in what is already in the buffer */
411 while(eol < end && raw_line[eol] != '\n') eol++;
413 /* if we did not find the of a line, move what has not been
414 processed and is in the buffer to the beginning of the buffer,
415 fill the buffer with new data from the file, and look for the
418 for(k = 0; k < end - start; k++) {
419 raw_line[k] = raw_line[k + start];
424 end += fread(raw_line + end, sizeof(char), buffer_size - end, file);
425 while(eol < end && raw_line[eol] != '\n') eol++;
428 /* The end of the line is the buffer size, which means the line is
431 if(eol == buffer_size) {
432 raw_line[buffer_size - 1] = '\0';
433 fprintf(stderr, "Selector: Line too long (max is %d characters):\n",
435 fprintf(stderr, raw_line);
436 fprintf(stderr, "\n");
440 /* If we got a line, we replace the carriage return by a \0 to
443 raw_line[eol] = '\0';
445 /* here we process the line */
450 printf("%s:%d \"%s\"\n",
451 configuration_file, line_number, raw_line + start);
454 evaluate_one_configuration_line(raw_line + start, line_number);
461 /******************************************************************/
463 if(nb_temperature_thresholds == 0) {
464 fprintf(stderr, "No temperature threshold was provided.\n");
468 if(nb_file_thermal == 0) {
469 fprintf(stderr, "No thermal file was provided.\n");
474 fprintf(stderr, "No fan file was provided.\n");
478 for(i = 0; i < nb_file_thermal; i++) {
479 file_thermal_fd[i] = open(file_thermal[i], O_RDONLY);
480 if(file_thermal_fd[i] < 0) {
481 fprintf(stderr, "Can not open %s for reading (%s).\n",
482 file_thermal[i], strerror(errno));
487 file_fan_fd = open(file_fan, O_WRONLY);
489 if(file_fan_fd < 0) {
490 fprintf(stderr, "Can not open %s for writing (%s).\n",
491 file_fan, strerror(errno));
495 /******************************************************************/
498 for(t = 0; t < nb_file_thermal; t++) {
499 printf("file_thermal[%d] %s\n", t, file_thermal[t]);
502 printf("file_fan %s\n", file_fan);
504 for(t = 0; t < nb_temperature_thresholds; t++) {
505 printf("temperature_thresholds[%d] = %d speed_names[%d] = \"%s\"\n",
506 t, temperature_thresholds[t],
511 /******************************************************************/
515 int temperature = -1;
518 printf("Temperature:");
521 for(i = 0; i < nb_file_thermal; i++) {
522 lseek(file_thermal_fd[i], 0, SEEK_SET);
523 int size = read(file_thermal_fd[i], buffer, buffer_size);
525 if(size > 0 && size < buffer_size) {
531 while(*s && *s != '-' && (*s < '0') || (*s > '9')) s++;
540 while(*s >= '0' && *s <= '9') {
541 t = t * 10 + (*s - '0');
550 /* So that we can deal with the new files where the
551 temperature are in 1/1000th of C */
552 if(t > 1000) t /= 1000;
554 if(t > temperature) temperature = t;
559 if(i < nb_file_thermal - 1)
566 fprintf(stderr, "Nothing to read in %d.\n", file_thermal[i]);
567 } else if(size < 0) {
568 fprintf(stderr, "Error while reading %s (%s).\n",
569 file_thermal[i], strerror(errno));
571 fprintf(stderr, "Error while reading %s (too large).\n",
578 if(temperature < 0) {
579 fprintf(stderr, "Could not read a meaningful temperature.\n");
583 nb_rounds_since_last_change++;
585 int new_level = last_level;
587 int new_level_up = nb_temperature_thresholds - 1;
588 while(new_level_up > 0 &&
589 temperature < temperature_thresholds[new_level_up]) {
593 if(new_level_up > last_level) {
594 new_level = new_level_up;
596 int new_level_down = nb_temperature_thresholds - 1;
597 while(new_level_down > 0 &&
599 temperature_thresholds[new_level_down] - down_temperature_delta)
601 if(new_level_down < last_level &&
602 nb_rounds_since_last_change * polling_delay >=
603 minimum_delay_to_go_down) {
604 new_level = new_level_down;
608 /* We set it every time, even when there is no change, to handle
609 when the level has been modified somewhere else (for instance
610 when coming back from suspend). */
612 set_fan_level(file_fan_fd, new_level, nb_temperature_thresholds - 1);
614 if(new_level != last_level) {
615 nb_rounds_since_last_change = 0;
616 last_level = new_level;
618 printf("Temperature is %dC setting the fan level to %d.\n",
619 temperature, new_level);
623 sleep(polling_delay);