1 /*
2     KSysGuard, the KDE System Guard
3 
4     Copyright (c) 2003 Stephan Uhlmann <su@su2.info>
5     Copyright (c) 2005 Sirtaj Singh Kang <taj@kde.org> -- Battery fixes and Thermal
6     Copyright (c) 2020 Jose Jorge <lists.jjorge@free.fr> -- Add energy sensor
7 
8     This program is free software; you can redistribute it and/or
9     modify it under the terms of version 2 of the GNU General Public
10     License as published by the Free Software Foundation.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 
22 #include <dirent.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <assert.h>
31 
32 #include "Command.h"
33 #include "ksysguardd.h"
34 
35 #include "acpi.h"
36 
37 #define ACPIFILENAMELENGTHMAX 64
38 
39 /*
40 ================================ public part =================================
41 */
42 
initAcpi(struct SensorModul * sm)43 void initAcpi(struct SensorModul* sm)
44 {
45 	initAcpiBattery(sm);
46 	initAcpiThermal(sm);
47 }
48 
exitAcpi(void)49 void exitAcpi( void )
50 {
51 }
52 
53 
54 /************ ACPI Battery **********/
registerBatteryCharge(const char * name,int number,struct SensorModul * sm)55 void registerBatteryCharge(const char *name, int number, struct SensorModul *sm)
56 {
57     char sensorName [ ACPIFILENAMELENGTHMAX ];
58     snprintf(sensorName, sizeof(sensorName), "acpi/Battery/%d-%s/Charge", number, name);
59 
60     registerMonitor(sensorName, "float", printSysBatteryCharge,
61                     printSysBatteryChargeInfo, sm);
62 }
63 
registerBatteryChargeDesign(const char * name,int number,struct SensorModul * sm)64 void registerBatteryChargeDesign(const char *name, int number, struct SensorModul *sm)
65 {
66     char sensorName [ ACPIFILENAMELENGTHMAX ];
67     snprintf(sensorName, sizeof(sensorName), "acpi/Battery/%d-%s/ChargeDesign", number, name);
68 
69     registerMonitor(sensorName, "float", printSysBatteryChargeDesign,
70                     printSysBatteryChargeDesignInfo, sm);
71 }
72 
registerBatteryEnergy(const char * name,int number,struct SensorModul * sm)73 void registerBatteryEnergy(const char *name, int number, struct SensorModul *sm)
74 {
75     char sensorName [ ACPIFILENAMELENGTHMAX ];
76     snprintf(sensorName, sizeof(sensorName), "acpi/Battery/%d-%s/Energy", number, name);
77 
78     registerMonitor(sensorName, "float", printSysBatteryEnergy,
79                     printSysBatteryEnergyInfo, sm);
80 }
81 
registerBatteryEnergyDesign(const char * name,int number,struct SensorModul * sm)82 void registerBatteryEnergyDesign(const char *name, int number, struct SensorModul *sm)
83 {
84     char sensorName [ ACPIFILENAMELENGTHMAX ];
85     snprintf(sensorName, sizeof(sensorName), "acpi/Battery/%d-%s/EnergyDesign", number, name);
86 
87     registerMonitor(sensorName, "float", printSysBatteryEnergyDesign,
88                     printSysBatteryEnergyDesignInfo, sm);
89 }
90 
registerBatteryRate(const char * name,int number,struct SensorModul * sm)91 void registerBatteryRate(const char *name, int number, struct SensorModul *sm)
92 {
93     char sensorName [ ACPIFILENAMELENGTHMAX ];
94     snprintf(sensorName, sizeof(sensorName), "acpi/Battery/%d-%s/Rate", number, name);
95 
96     registerMonitor(sensorName, "integer", printSysBatteryRate,
97                     printSysBatteryRateInfo, sm);
98 }
99 
registerBatteryRatePower(const char * name,int number,struct SensorModul * sm)100 void registerBatteryRatePower(const char *name, int number, struct SensorModul *sm)
101 {
102     char sensorName [ ACPIFILENAMELENGTHMAX ];
103     snprintf(sensorName, sizeof(sensorName), "acpi/Battery/%d-%s/RatePower", number, name);
104 
105     registerMonitor(sensorName, "integer", printSysBatteryRatePower,
106                     printSysBatteryRatePowerInfo, sm);
107 }
108 
initAcpiBattery(struct SensorModul * sm)109 void initAcpiBattery( struct SensorModul* sm )
110 {
111     DIR *d;
112     struct dirent *de;
113     char name[ ACPIFILENAMELENGTHMAX ];
114 
115     d = opendir("/sys/class/power_supply/");
116     if (d != NULL) {
117         while ( (de = readdir(d)) != NULL ) {
118             if (!de->d_name || de->d_name[0] == '.')
119                 continue;
120             if (strncmp( de->d_name, "BAT", sizeof("BAT")-1) == 0) {
121                 int number = atoi(de->d_name + (sizeof("BAT")-1));
122                 readTypeFile("/sys/class/power_supply/BAT%d/type", number, name, sizeof(name));
123                 registerBatteryCharge(name, number, sm);
124                 registerBatteryChargeDesign(name, number, sm);
125                 registerBatteryEnergy(name, number, sm);
126                 registerBatteryEnergyDesign(name, number, sm);
127                 registerBatteryRate(name, number, sm);
128                 registerBatteryRatePower(name, number, sm);
129             }
130         }
131       closedir( d );
132     }
133 }
134 
printSysBatteryCharge(const char * cmd)135 void printSysBatteryCharge(const char *cmd)
136 {
137     int zone = 0;
138     if (sscanf(cmd, "acpi/Battery/%d", &zone) <= 0) {
139         output("-1\n");
140         return;
141     }
142 
143     int charge = getSysFileValue("power_supply", "BAT", zone, "charge_now");
144     int maximum = getSysFileValue("power_supply", "BAT", zone, "charge_full");
145     float state = 0;
146     if ( maximum > 0) {
147         state = charge/((float)maximum/100);/* to get 0.1% changes */
148     }
149     if (state > 100) {
150         state = 100; /* prevent insane numbers with bad hardware */
151     } else if (state < 0) {
152         state = 0; /* prevent insane numbers with bad hardware */
153     }
154     output( "%f\n", state);
155 }
156 
printSysBatteryChargeInfo(const char * cmd)157 void printSysBatteryChargeInfo(const char *cmd)
158 {
159     char name [ 200 ];
160     if (sscanf(cmd, "acpi/Battery/%199[^/]", name) > 0) {
161         output( "%s charge\t0\t100\t%%\n", name);
162     } else {
163         output( "Current charge\t0\t100\t%%\n");
164     }
165 }
166 
printSysBatteryChargeDesign(const char * cmd)167 void printSysBatteryChargeDesign(const char *cmd)
168 {
169     int zone = 0;
170     if (sscanf(cmd, "acpi/Battery/%d", &zone) <= 0) {
171         output("-1\n");
172         return;
173     }
174 
175     int charge = getSysFileValue("power_supply", "BAT", zone, "charge_now");
176     int maximum = getSysFileValue("power_supply", "BAT", zone, "charge_full_design");
177     float state = 0;
178     if (maximum > 0) {
179         state = charge/((float)maximum/100);/* to get 0.1% changes */
180     }
181     if (state > 100) {
182         state = 100; /* prevent insane numbers with bad hardware */
183     } else if (state < 0) {
184         state = 0; /* prevent insane numbers with bad hardware */
185     }
186     output( "%f\n", state);
187 }
188 
printSysBatteryChargeDesignInfo(const char * cmd)189 void printSysBatteryChargeDesignInfo(const char *cmd)
190 {
191     char name [ 200 ];
192     if (sscanf(cmd, "acpi/Battery/%199[^/]", name) > 0) {
193         output( "%s charge (by design)\t0\t100\t%%\n", name);
194     } else {
195         output( "Current charge (by design)\t0\t100\t%%\n");
196     }
197 }
198 
printSysBatteryEnergy(const char * cmd)199 void printSysBatteryEnergy(const char *cmd)
200 {
201     int zone = 0;
202     if (sscanf(cmd, "acpi/Battery/%d", &zone) <= 0) {
203         output("-1\n");
204         return;
205     }
206 
207     int charge = getSysFileValue("power_supply", "BAT", zone, "energy_now");
208     int maximum = getSysFileValue("power_supply", "BAT", zone, "energy_full");
209     float state = 0;
210     if ( maximum > 0) {
211         state = charge/((float)maximum/100);/* to get 0.1% changes */
212     }
213     if (state > 100) {
214         state = 100; /* prevent insane numbers with bad hardware */
215     } else if (state < 0) {
216         state = 0; /* prevent insane numbers with bad hardware */
217     }
218     output( "%f\n", state);
219 }
220 
printSysBatteryEnergyInfo(const char * cmd)221 void printSysBatteryEnergyInfo(const char *cmd)
222 {
223     char name [ 200 ];
224     if (sscanf(cmd, "acpi/Battery/%199[^/]", name) > 0) {
225         output( "%s energy\t0\t100\t%%\n", name);
226     } else {
227         output( "Current energy\t0\t100\t%%\n");
228     }
229 }
230 
printSysBatteryEnergyDesign(const char * cmd)231 void printSysBatteryEnergyDesign(const char *cmd)
232 {
233     int zone = 0;
234     if (sscanf(cmd, "acpi/Battery/%d", &zone) <= 0) {
235         output("-1\n");
236         return;
237     }
238 
239     int charge = getSysFileValue("power_supply", "BAT", zone, "energy_now");
240     int maximum = getSysFileValue("power_supply", "BAT", zone, "energy_full_design");
241     float state = 0;
242     if (maximum > 0) {
243         state = charge/((float)maximum/100);/* to get 0.1% changes */
244     }
245     if (state > 100) {
246         state = 100; /* prevent insane numbers with bad hardware */
247     } else if (state < 0) {
248         state = 0; /* prevent insane numbers with bad hardware */
249     }
250     output( "%f\n", state);
251 }
252 
printSysBatteryEnergyDesignInfo(const char * cmd)253 void printSysBatteryEnergyDesignInfo(const char *cmd)
254 {
255     char name [ 200 ];
256     if (sscanf(cmd, "acpi/Battery/%199[^/]", name) > 0) {
257         output( "%s energy (by design)\t0\t100\t%%\n", name);
258     } else {
259         output( "Current energy (by design)\t0\t100\t%%\n");
260     }
261 }
262 
printSysBatteryRate(const char * cmd)263 void printSysBatteryRate(const char *cmd)
264 {
265     int zone = 0;
266     if (sscanf(cmd, "acpi/Battery/%d", &zone) <= 0) {
267         output("-1\n");
268         return;
269     }
270 
271     output( "%d\n", getSysFileValue("power_supply", "BAT", zone, "current_now") / 1000);
272 }
273 
printSysBatteryRateInfo(const char * cmd)274 void printSysBatteryRateInfo(const char *cmd)
275 {
276     char name [ 200 ];
277     if (sscanf(cmd, "acpi/Battery/%199[^/]", name) > 0) {
278         output( "%s rate\t0\t0\tmA\n", name);
279     } else {
280         output( "Current rate\t0\t0\tmA\n");
281     }
282 }
283 
printSysBatteryRatePower(const char * cmd)284 void printSysBatteryRatePower(const char *cmd)
285 {
286     int zone = 0;
287     if (sscanf(cmd, "acpi/Battery/%d", &zone) <= 0) {
288         output("-1\n");
289         return;
290     }
291 
292     output( "%d\n", getSysFileValue("power_supply", "BAT", zone, "power_now") / 1000);
293 }
294 
printSysBatteryRatePowerInfo(const char * cmd)295 void printSysBatteryRatePowerInfo(const char *cmd)
296 {
297     char name [ 200 ];
298     if (sscanf(cmd, "acpi/Battery/%199[^/]", name) > 0) {
299         output( "%s rate\t0\t0\tmA\n", name);
300     } else {
301         output( "Current power rate\t0\t0\tmA\n");
302     }
303 }
304 
305 
306 /************** ACPI Thermal *****************/
307 
308 #define OLD_THERMAL_ZONE_DIR "/proc/acpi/thermal_zone"
309 #define OLD_TEMPERATURE_FILE "temperature"
310 #define OLD_TEMPERATURE_FILE_MAXLEN 255
311 
312 #define OLD_FAN_DIR "/proc/acpi/fan"
313 #define OLD_FAN_STATE_FILE "state"
314 #define OLD_FAN_STATE_FILE_MAXLEN 255
315 
316 
317 /*static char **zone_names = NULL;*/
318 
319 /** Find the thermal zone name from the command.
320  * Assumes the command is of the form acpi/thermal_zone/<zone name>/...
321  * @p startidx is set to the start of the zone name. May be set to an
322  * undefined value if zone name is not found.
323  * @return length of found name, or 0 if nothing found.
324  */
extract_zone_name(char ** startidx,const char * cmd)325 static int extract_zone_name(char **startidx, const char *cmd)
326 {
327 	char *idx = NULL;
328 	idx = strchr(cmd, '/');
329 	if (idx == NULL) return 0;
330 	idx = strchr(idx+1, '/');
331 	if (idx == NULL) return 0;
332 	*startidx = idx+1;
333 	idx = strchr(*startidx, '/');
334 	if (idx == NULL) return 0;
335 	return idx - *startidx;
336 }
337 
readTypeFile(const char * fileFormat,int number,char * buffer,int bufferSize)338 void readTypeFile(const char *fileFormat, int number, char *buffer, int bufferSize)
339 {
340     char filename[ ACPIFILENAMELENGTHMAX ];
341     snprintf(filename, sizeof(filename), fileFormat, number);
342 
343     int typeFile = open(filename, O_RDONLY);
344     if (typeFile < 0) {
345         print_error( "Cannot open file \'%s\'!\n"
346                      "Unable to fetch ACPI type", filename);
347         snprintf(buffer, bufferSize, "unknown-%d", number);
348         return;
349     }
350 
351     int readBytes = read( typeFile, buffer, bufferSize - 1 );
352     assert(readBytes > 1);
353     assert(readBytes < bufferSize);
354     buffer[readBytes-1] = '\0'; /* strip newline */
355 }
356 
registerThermalZone(int number,struct SensorModul * sm)357 void registerThermalZone(int number, struct SensorModul *sm)
358 {
359     char name[ ACPIFILENAMELENGTHMAX ];
360     readTypeFile("/sys/class/thermal/thermal_zone%d/type", number, name, sizeof(name));
361 
362     char sensorName [ ACPIFILENAMELENGTHMAX ];
363     snprintf(sensorName, sizeof(sensorName), "acpi/Thermal_Zone/%d-%s/Temperature", number, name);
364 
365     registerMonitor(sensorName, "integer", printSysThermalZoneTemperature,
366                     printSysThermalZoneTemperatureInfo, sm);
367 }
368 
registerCoolingDevice(int number,struct SensorModul * sm)369 void registerCoolingDevice(int number, struct SensorModul *sm)
370 {
371     char name[ ACPIFILENAMELENGTHMAX ];
372     readTypeFile("/sys/class/thermal/cooling_device%d/type", number, name, sizeof(name));
373 
374     char sensorName [ ACPIFILENAMELENGTHMAX ];
375     snprintf(sensorName, sizeof(sensorName), "acpi/Cooling_Device/%d-%s/Activity", number, name);
376 
377     registerMonitor(sensorName, "integer", printCoolingDeviceState,
378                     printCoolingDeviceStateInfo, sm);
379 }
380 
initAcpiThermal(struct SensorModul * sm)381 void initAcpiThermal(struct SensorModul *sm)
382 {
383   char th_ref[ ACPIFILENAMELENGTHMAX ];
384   DIR *d = NULL;
385   struct dirent *de;
386 
387   d = opendir("/sys/class/thermal/");
388   if (d != NULL) {
389       while ( (de = readdir(d)) != NULL ) {
390           if (!de->d_name || de->d_name[0] == '.')
391               continue;
392           if (strncmp( de->d_name, "thermal_zone", sizeof("thermal_zone")-1) == 0) {
393               int number = atoi(de->d_name + (sizeof("thermal_zone")-1));
394               registerThermalZone(number, sm);
395 
396               /*For compatibility, register a legacy sensor*/
397               int zone_number;
398               if (sscanf(de->d_name, "thermal_zone%d", &zone_number) > 0) {
399                   snprintf(th_ref, sizeof(th_ref),
400                           "acpi/thermal_zone/TZ%02d/temperature", zone_number);
401                   registerLegacyMonitor(th_ref, "integer", printSysCompatibilityThermalZoneTemperature,
402                           printThermalZoneTemperatureInfo, sm);
403               }
404           } else if (strncmp( de->d_name, "cooling_device", sizeof("cooling_device")-1) == 0) {
405               int number = atoi(de->d_name+( sizeof("cooling_device")-1));
406               registerCoolingDevice(number, sm);
407           }
408       }
409       closedir( d );
410   } else {
411       d = opendir(OLD_THERMAL_ZONE_DIR);
412       if (d != NULL) {
413           while ( (de = readdir(d)) != NULL ) {
414               if (!de->d_name || de->d_name[0] == '.')
415                   continue;
416 
417               snprintf(th_ref, sizeof(th_ref),
418                       "acpi/thermal_zone/%s/temperature", de->d_name);
419               registerMonitor(th_ref, "integer", printThermalZoneTemperature,
420                       printThermalZoneTemperatureInfo, sm);
421           }
422           closedir( d );
423       }
424 
425       d = opendir(OLD_FAN_DIR);
426       if (d != NULL) {
427           while ( (de = readdir(d)) != NULL ) {
428               if (!de->d_name || de->d_name[0] == '.')
429                   continue;
430 
431               snprintf(th_ref, sizeof(th_ref),
432                       "acpi/fan/%s/state", de->d_name);
433               registerMonitor(th_ref, "integer", printFanState,
434                       printFanStateInfo, sm);
435           }
436           closedir( d );
437       }
438   }
439 
440   return;
441 }
442 
getSysFileValue(const char * className,const char * group,int value,const char * file)443 static int getSysFileValue(const char *className, const char *group, int value, const char *file) {
444     static int shownError = 0;
445     char th_file[ ACPIFILENAMELENGTHMAX ];
446     char input_buf[ 100 ];
447     snprintf(th_file, sizeof(th_file), "/sys/class/%s/%s%d/%s", className, group, value, file);
448     int fd = open(th_file, O_RDONLY);
449     if (fd < 0) {
450         return -1;/* ignore failures, as sys files disappear when battery is removed */
451     }
452     int read_bytes = read( fd, input_buf, sizeof(input_buf) - 1 );
453     if ( read_bytes == sizeof(input_buf) - 1 ) {
454         if (!shownError)
455             log_error( "Internal buffer too small to read \'%s\'", th_file );
456         shownError = 1;
457         close( fd );
458         return -1;
459     }
460     close(fd);
461 
462     int result=0;
463     sscanf(input_buf, "%d", &result);
464     return result;
465 }
466 
printSysThermalZoneTemperature(const char * cmd)467 void printSysThermalZoneTemperature(const char *cmd) {
468     int zone = 0;
469     if (sscanf(cmd, "acpi/Thermal_Zone/%d", &zone) <= 0) {
470         output("-1\n");
471         return;
472     }
473 
474     output( "%d\n", getSysFileValue("thermal", "thermal_zone", zone, "temp") / 1000);
475 }
printSysCompatibilityThermalZoneTemperature(const char * cmd)476 void printSysCompatibilityThermalZoneTemperature(const char *cmd) {
477     int zone = 0;
478     if (sscanf(cmd, "acpi/thermal_zone/TZ%d", &zone) <= 0) {
479         output( "-1\n");
480         return;
481     }
482     output( "%d\n", getSysFileValue("thermal", "thermal_zone", zone, "temp")/1000);
483 }
484 
printCoolingDeviceStateInfo(const char * cmd)485 void printCoolingDeviceStateInfo(const char *cmd)
486 {
487     (void)cmd;
488     char name [ 200 ];
489     if (sscanf(cmd, "acpi/Cooling_Device/%199[^/]", name) > 0) {
490         output( "%s Cooling Activity\t0\t100\t%%\n",  name);
491     } else {
492         output( "Cooling Device Activity\t0\t100\t%%\n");
493     }
494 }
495 
printCoolingDeviceState(const char * cmd)496 void printCoolingDeviceState(const char *cmd) {
497     int fan = 0;
498     if (sscanf(cmd, "acpi/Cooling_Device/%d", &fan) <= 0) {
499         output( "-1\n");
500         return;
501     }
502     int current = getSysFileValue("thermal", "cooling_device", fan, "cur_state");
503     int maximum = getSysFileValue("thermal", "cooling_device", fan, "max_state");
504     int state = 0;
505     if (current > 0 && maximum > 0) {
506         state = (current * 100) / maximum; /* state is a percentage */
507     }
508     output( "%d\n", state);
509 }
510 
getCurrentTemperature(const char * cmd)511 static int getCurrentTemperature(const char *cmd)
512 {
513 	char th_file[ ACPIFILENAMELENGTHMAX ];
514 	char input_buf[ OLD_TEMPERATURE_FILE_MAXLEN ];
515 	char *zone_name = NULL;
516 	int read_bytes = 0, fd = 0, len_zone_name = 0;
517 	int temperature=0;
518 
519 	len_zone_name = extract_zone_name(&zone_name, cmd);
520 	if (len_zone_name <= 0) return -1;
521 
522 	snprintf(th_file, sizeof(th_file),
523 			OLD_THERMAL_ZONE_DIR "/%.*s/" OLD_TEMPERATURE_FILE,
524 			len_zone_name, zone_name);
525 
526 	fd = open(th_file, O_RDONLY);
527 	if (fd < 0) {
528 		print_error( "Cannot open file \'%s\'!\n"
529 		"Load the thermal ACPI kernel module or\n"
530 		"compile it into your kernel.\n", th_file );
531 		return -1;
532 	}
533 
534 	read_bytes = read( fd, input_buf, sizeof(input_buf) - 1 );
535 	if ( read_bytes == sizeof(input_buf) - 1 ) {
536 		log_error( "Internal buffer too small to read \'%s\'", th_file );
537 		close( fd );
538 		return -1;
539 	}
540 	close(fd);
541 
542 	sscanf(input_buf, "temperature: %d C", &temperature);
543 	return temperature;
544 }
545 
printThermalZoneTemperature(const char * cmd)546 void printThermalZoneTemperature(const char *cmd) {
547 	int temperature = getCurrentTemperature(cmd);
548 	output( "%d\n", temperature);
549 }
550 
printSysThermalZoneTemperatureInfo(const char * cmd)551 void printSysThermalZoneTemperatureInfo(const char *cmd)
552 {
553     char name [ 200 ];
554     if (sscanf(cmd, "acpi/Thermal_Zone/%199[^/]", name) > 0) {
555         output( "%s temperature\t0\t0\tC\n", name);
556     } else {
557         output( "Current temperature\t0\t0\tC\n");
558     }
559 }
560 
printThermalZoneTemperatureInfo(const char * cmd)561 void printThermalZoneTemperatureInfo(const char *cmd)
562 {
563 	(void)cmd;
564 
565 	output( "Current temperature\t0\t0\tC\n");
566 }
567 
568 /********** ACPI Fan State***************/
569 
getFanState(const char * cmd)570 static int getFanState(const char *cmd)
571 {
572 	char fan_state_file[ ACPIFILENAMELENGTHMAX ];
573 	char input_buf[ OLD_FAN_STATE_FILE_MAXLEN ];
574 	char *fan_name = NULL;
575 	int read_bytes = 0, fd = 0, len_fan_name = 0;
576 	char fan_state[4];
577 
578 	len_fan_name = extract_zone_name(&fan_name, cmd);
579 	if (len_fan_name <= 0) {
580 		return -1;
581 	}
582 
583 	snprintf(fan_state_file, sizeof(fan_state_file),
584 			OLD_FAN_DIR "/%.*s/" OLD_FAN_STATE_FILE,
585 			len_fan_name, fan_name);
586 
587 	fd = open(fan_state_file, O_RDONLY);
588 	if (fd < 0) {
589 		print_error( "Cannot open file \'%s\'!\n"
590 		"Load the fan ACPI kernel module or\n"
591 		"compile it into your kernel.\n", fan_state_file );
592 
593 		return -1;
594 	}
595 
596 	read_bytes = read( fd, input_buf, sizeof(input_buf) - 1 );
597 	if ( read_bytes == sizeof(input_buf) - 1 ) {
598 		log_error( "Internal buffer too small to read \'%s\'", fan_state_file );
599 		close( fd );
600 		return -1;
601 	}
602 	close(fd);
603 
604 	sscanf(input_buf, "status: %2s", fan_state);
605 	return (fan_state[1] == 'n') ? 1 : 0;
606 }
607 
printFanState(const char * cmd)608 void printFanState(const char *cmd) {
609 	int fan_state = getFanState(cmd);
610 	output( "%d\n", fan_state);
611 }
612 
printFanStateInfo(const char * cmd)613 void printFanStateInfo(const char *cmd)
614 {
615 	(void)cmd;
616 
617 	output( "Fan status\t0\t1\tboolean\n");
618 }
619