1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail    : see the 'copyright' file.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
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 * You should have received a copy of the GNU General Public License
17 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Adapted from the 'sensors' program.
20 */
21 
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <math.h>
26 
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <unistd.h>
30 
31 #include <sensors/sensors.h>
32 #include <sensors/error.h>
33 
34 #include "applet-struct.h"
35 #include "applet-sensors.h"
36 
37 /**************************************************
38 it87-isa-0290
39 Adapter: ISA adapter
40 VCore 1: +1.57 V (min = +1.42 V, max = +1.57 V) ALARM
41 VCore 2: +2.66 V (min = +2.40 V, max = +2.61 V) ALARM
42 +3.3V: +6.59 V (min = +3.14 V, max = +3.46 V) ALARM
43 +5V: +5.11 V (min = +4.76 V, max = +5.24 V)
44 +12V: +11.78 V (min = +11.39 V, max = +12.61 V)
45 -12V: -19.14 V (min = -12.63 V, max = -11.41 V) ALARM
46 -5V: +0.77 V (min = -5.26 V, max = -4.77 V) ALARM
47 Stdby: +5.00 V (min = +4.76 V, max = +5.24 V)
48 VBat: +3.12 V
49 fan1: 3668 RPM (min = 0 RPM, div =
50 fan2: 0 RPM (min = 664 RPM, div =  ALARM
51 fan3: 0 RPM (min = 2657 RPM, div = 2) ALARM
52 M/B Temp: +39°C (low = +15°C, high = +40°C) sensor = thermistor
53 CPU Temp: +36°C (low = +15°C, high = +45°C) sensor = thermistor
54 Temp3: +96°C (low = +15°C, high = +45°C) sensor = diode
55 **************************************************/
56 
57 static int s_iSensorsState = 0;  // on en fait une variable globale plutot qu'un parametre de myData, car la libsensors ne doit etre fermee qu'une seule fois (meme si l'applet est instancee plusieurs fois, le .so du plug-in n'est ouvert une seule fois).
58 
cd_sysmonitor_clean_sensors(void)59 void cd_sysmonitor_clean_sensors (void)
60 {
61 	if (s_iSensorsState == 1)
62 		sensors_cleanup();
63 	s_iSensorsState = 0;
64 }
65 
get_value(const sensors_chip_name * name,const sensors_subfeature * sub)66 static double get_value (const sensors_chip_name *name, const sensors_subfeature *sub)
67 {
68 	double val;
69 	int err;
70 
71 	err = sensors_get_value(name, sub->number, &val);
72 	if (err) {
73 		fprintf(stderr, "ERROR: Can't get value of subfeature %s: %s\n",
74 			sub->name, sensors_strerror(err));
75 		val = 0;
76 	}
77 	return val;
78 }
79 
_init_sensors(void)80 static inline void _init_sensors (void)
81 {
82 	if (s_iSensorsState == 0)
83 	{
84 		int err = sensors_init (NULL);
85 		if (err != 0)
86 		{
87 			s_iSensorsState = -1;
88 			cd_warning ("couldn't initialize libsensors: %s\nTry running 'sensors-detect' as root in a terminal.", sensors_strerror (err));
89 			return;
90 		}
91 		s_iSensorsState = 1;
92 	}
93 }
94 
cd_sysmonitor_get_sensors_data(GldiModuleInstance * myApplet)95 void cd_sysmonitor_get_sensors_data (GldiModuleInstance *myApplet)
96 {
97 	_init_sensors ();
98 	if (s_iSensorsState != 1)
99 		return;
100 
101 	const sensors_chip_name *chip;
102 	const sensors_subfeature *sf;
103 	double val;
104 	int chip_nr;
105 
106 	chip_nr = 0;
107 	double fCpuTempPercentMax = 0;
108 	myData.iFanSpeed = 0;
109 	myData.iCPUTemp = 0;
110 	myData.bCpuTempAlarm = FALSE;
111 	myData.bFanAlarm = FALSE;
112 	while ((chip = sensors_get_detected_chips (NULL, &chip_nr)))
113 	{
114 		const sensors_feature *feature;
115 		int i;
116 
117 		i = 0;
118 		while ((feature = sensors_get_features (chip, &i)))
119 		{
120 			switch (feature->type)
121 			{
122 				case SENSORS_FEATURE_TEMP:  // une sonde de temperature
123 				{
124 					double limit1=0, limit2=100;
125 
126 					sf = sensors_get_subfeature (chip, feature,
127 						SENSORS_SUBFEATURE_TEMP_FAULT);
128 					if (sf && get_value (chip, sf))  // fault
129 						break;
130 
131 					// valeur
132 					sf = sensors_get_subfeature (chip, feature,
133 						SENSORS_SUBFEATURE_TEMP_INPUT);
134 					if (!sf)
135 						break;
136 					val = get_value(chip, sf);
137 					if (val == 0)
138 						break;
139 
140 					// alarme
141 					sf = sensors_get_subfeature (chip, feature,
142 						SENSORS_SUBFEATURE_TEMP_ALARM);
143 					if (sf && get_value (chip, sf))
144 						myData.bCpuTempAlarm = TRUE;
145 
146 					// min limit
147 					sf = sensors_get_subfeature (chip, feature,
148 						SENSORS_SUBFEATURE_TEMP_MIN);
149 					if (sf)
150 					{
151 						limit1 = get_value(chip, sf);
152 
153 						sf = sensors_get_subfeature (chip, feature,
154 							SENSORS_SUBFEATURE_TEMP_MIN_ALARM);
155 						if (sf && get_value (chip, sf))
156 							myData.bCpuTempAlarm = TRUE;
157 					}
158 
159 					// max limit
160 					sf = sensors_get_subfeature(chip, feature,
161 						SENSORS_SUBFEATURE_TEMP_MAX);
162 					if (sf)
163 					{
164 						limit2 = get_value (chip, sf);
165 
166 						sf = sensors_get_subfeature (chip, feature,
167 							SENSORS_SUBFEATURE_TEMP_MAX_ALARM);
168 						if (sf && get_value (chip, sf))
169 							myData.bCpuTempAlarm = TRUE;
170 					}
171 					else  // pas de valeur max, on regarde si une valeur critique existe.
172 					{
173 						sf = sensors_get_subfeature(chip, feature,
174 							SENSORS_SUBFEATURE_TEMP_CRIT);
175 						if (sf)
176 						{
177 							limit2 = get_value(chip, sf);
178 
179 							sf = sensors_get_subfeature(chip, feature,
180 								SENSORS_SUBFEATURE_TEMP_CRIT_ALARM);
181 							if (sf && get_value(chip, sf))
182 								myData.bCpuTempAlarm = TRUE;
183 						}
184 					}
185 					if (limit2 <= limit1 + 1)
186 						limit2 = limit1 + 1;
187 
188 					double fCpuTempPercent = 100. * (val - limit1) / (limit2 - limit1);
189 					if (fCpuTempPercent > fCpuTempPercentMax)  // on ne va garder qu'une seule valeur : celle qui est la plus grande en valeur relative.
190 					{
191 						fCpuTempPercentMax = fCpuTempPercent;
192 						myData.fCpuTempPercent = fCpuTempPercent;
193 						myData.iCPUTemp = val;
194 						myData.iCPUTempMin = limit1;
195 						myData.iCPUTempMax = limit2;
196 					}
197 					//g_print ("CPU : %.2f %d(%d) %.2f\n", limit1, myData.iCPUTemp, myData.bCpuTempAlarm, limit2);
198 				}
199 				break;
200 
201 				case SENSORS_FEATURE_FAN:  // un ventilo
202 				{
203 					double min = 0;
204 
205 					sf = sensors_get_subfeature (chip, feature,
206 						SENSORS_SUBFEATURE_FAN_FAULT);
207 					if (sf && get_value (chip, sf))  // fault
208 						break;
209 
210 					// valeur
211 					sf = sensors_get_subfeature (chip, feature,
212 						SENSORS_SUBFEATURE_FAN_INPUT);
213 					if (!sf)
214 						break;
215 					val = get_value (chip, sf);  // rpm
216 					if (val == 0)
217 						break;
218 
219 					// alarm
220 					sf = sensors_get_subfeature (chip, feature,
221 						SENSORS_SUBFEATURE_FAN_MIN);
222 					if (sf)
223 						min = get_value(chip, sf);
224 
225 					sf = sensors_get_subfeature (chip, feature,
226 						SENSORS_SUBFEATURE_FAN_ALARM);
227 					if (sf && get_value(chip, sf) && val > min)  // on elimine les cas ou le min a une valeur aberrante.
228 						myData.bFanAlarm = TRUE;
229 
230 					// max speed
231 					myData.fMaxFanSpeed = 8000.;  // pour l'instant on la laisse en dur a une valeur pas trop bete, car libsensors ne fournit pas de max pour les fans (elle fournit un min, mais sans le max ca a peu d'interet).
232 					if (val > myData.fMaxFanSpeed)
233 						val = myData.fMaxFanSpeed;
234 
235 					myData.iFanSpeed = MAX (myData.iFanSpeed, val);  // on ne garde qu'une valeur : la plus grande.
236 
237 					myData.fFanSpeedPercent = 100. * myData.iFanSpeed / myData.fMaxFanSpeed;
238 				}
239 				break;
240 
241 				default:
242 				break;
243 			}
244 		}
245 	}
246 
247 	if (fabs (myData.fCpuTempPercent - myData.fPrevCpuTempPercent) > 1)
248 	{
249 		myData.fPrevCpuTempPercent = myData.fCpuTempPercent;
250 		myData.bNeedsUpdate = TRUE;
251 	}
252 	if (fabs (myData.fFanSpeedPercent - myData.fPrevFanSpeedPercent) > 1)
253 	{
254 		myData.fPrevFanSpeedPercent = myData.fFanSpeedPercent;
255 		myData.bNeedsUpdate = TRUE;
256 	}
257 }
258 
259 
cd_sysmonitor_get_sensors_info(GldiModuleInstance * myApplet,GString * pInfo)260 void cd_sysmonitor_get_sensors_info (GldiModuleInstance *myApplet, GString *pInfo)
261 {
262 	_init_sensors ();
263 	if (s_iSensorsState != 1)
264 		return;
265 
266 	const sensors_chip_name *chip;
267 	const sensors_subfeature *sf;
268 	double val;
269 	int chip_nr;
270 
271 	chip_nr = 0;
272 	char *label;
273 	gboolean alarm;
274 	while ((chip = sensors_get_detected_chips (NULL, &chip_nr)))
275 	{
276 		const sensors_feature *feature;
277 		int i;
278 
279 		i = 0;
280 		while ((feature = sensors_get_features (chip, &i)))
281 		{
282 			label = NULL;
283 			alarm = 0;
284 			switch (feature->type)
285 			{
286 				case SENSORS_FEATURE_TEMP:  // une sonde de temperature
287 				{
288 					// name
289 					label = sensors_get_label(chip, feature);
290 					if (!label)
291 						break;
292 
293 					double limit1=-100, limit2=-100;
294 
295 					sf = sensors_get_subfeature(chip, feature,
296 						SENSORS_SUBFEATURE_TEMP_FAULT);
297 					if (sf && get_value(chip, sf))  // fault
298 						break;
299 
300 					// valeur
301 					sf = sensors_get_subfeature(chip, feature,
302 						SENSORS_SUBFEATURE_TEMP_INPUT);
303 					if (!sf)
304 						break;
305 					val = get_value(chip, sf);
306 					if (val == 0)
307 						break;
308 
309 					// alarme
310 					sf = sensors_get_subfeature(chip, feature,
311 						SENSORS_SUBFEATURE_TEMP_ALARM);
312 					if (sf && get_value(chip, sf))
313 						alarm = TRUE;
314 
315 					// min limit
316 					sf = sensors_get_subfeature(chip, feature,
317 						SENSORS_SUBFEATURE_TEMP_MIN);
318 					if (sf)
319 					{
320 						limit1 = get_value(chip, sf);
321 
322 						sf = sensors_get_subfeature(chip, feature,
323 							SENSORS_SUBFEATURE_TEMP_MIN_ALARM);
324 						if (sf && get_value(chip, sf))
325 							alarm = TRUE;
326 					}
327 
328 					// max limit
329 					sf = sensors_get_subfeature(chip, feature,
330 						SENSORS_SUBFEATURE_TEMP_MAX);
331 					if (sf)
332 					{
333 						limit2 = get_value(chip, sf);
334 
335 						sf = sensors_get_subfeature(chip, feature,
336 							SENSORS_SUBFEATURE_TEMP_MAX_ALARM);
337 						if (sf && get_value(chip, sf))
338 							alarm = TRUE;
339 					}
340 					else  // pas de valeur max, on regarde si une valeur critique existe.
341 					{
342 						sf = sensors_get_subfeature(chip, feature,
343 							SENSORS_SUBFEATURE_TEMP_CRIT);
344 						if (sf)
345 						{
346 							limit2 = get_value(chip, sf);
347 
348 							sf = sensors_get_subfeature(chip, feature,
349 								SENSORS_SUBFEATURE_TEMP_CRIT_ALARM);
350 							if (sf && get_value(chip, sf))
351 								alarm = TRUE;
352 						}
353 					}
354 
355 					//g_print ("CPU : %.2f %d(%d) %.2f\n", limit1, myData.iCPUTemp, myData.bCpuTempAlarm, limit2);
356 					g_string_append_printf (pInfo, "\n%s: %d°C", label, (int)val);
357 					if (limit1 > -99)
358 						g_string_append_printf (pInfo, ", %s: %d°C", D_("min"), (int)limit1);
359 					if (limit2 > -99)
360 						g_string_append_printf (pInfo, ", %s: %d°C", D_("max"), (int)limit2);
361 
362 					if (alarm)
363 						g_string_append_printf (pInfo, "  (%s)", D_("alarm"));
364 					free(label);
365 				}
366 				break;
367 
368 				case SENSORS_FEATURE_FAN:  // un ventilo
369 					// name
370 					label = sensors_get_label(chip, feature);
371 					if (!label)
372 						break;
373 
374 					sf = sensors_get_subfeature (chip, feature,
375 						SENSORS_SUBFEATURE_FAN_FAULT);
376 					if (sf && get_value(chip, sf))  // fault
377 						break;
378 
379 					// valeur
380 					sf = sensors_get_subfeature (chip, feature,
381 						SENSORS_SUBFEATURE_FAN_INPUT);
382 					if (!sf)
383 						break;
384 					val = get_value (chip, sf);  // rpm
385 					if (val == 0)
386 						break;
387 
388 					// alarm
389 					sf = sensors_get_subfeature (chip, feature,
390 						SENSORS_SUBFEATURE_FAN_ALARM);
391 					if (sf && get_value(chip, sf))
392 						alarm = TRUE;
393 
394 					g_string_append_printf (pInfo, "\n%s: %d %s", label, (int)val, D_("rpm"));
395 					if (alarm)
396 						g_string_append_printf (pInfo, "  (%s)", D_("alarm"));
397 					free(label);
398 				break;
399 
400 				default:  // les autres ne nous interessent pas.
401 				break;
402 			}
403 		}
404 	}
405 }
406 
cd_cpu_alert(GldiModuleInstance * myApplet)407 void cd_cpu_alert (GldiModuleInstance *myApplet)
408 {
409 	if (myData.bCPUAlerted || ! myConfig.bAlert)
410 		return;
411 
412 	gldi_dialogs_remove_on_icon (myIcon);
413 	gldi_dialog_show_temporary_with_icon_printf (D_("CPU temperature has reached %d°C"), myIcon, myContainer, 4e3, MY_APPLET_SHARE_DATA_DIR"/"MY_APPLET_ICON_FILE, myData.iCPUTemp);
414 
415 	if (myConfig.bAlertSound)
416 		cairo_dock_play_sound (myConfig.cSoundPath);
417 
418 	myData.bCPUAlerted = TRUE;
419 }
420 
cd_fan_alert(GldiModuleInstance * myApplet)421 void cd_fan_alert (GldiModuleInstance *myApplet)
422 {
423 	if (myData.bFanAlerted || ! myConfig.bAlert)
424 		return;
425 
426 	gldi_dialogs_remove_on_icon (myIcon);
427 	gldi_dialog_show_temporary_with_icon_printf (D_("Fan speed has reached %d rpm"), myIcon, myContainer, 4e3, MY_APPLET_SHARE_DATA_DIR"/"MY_APPLET_ICON_FILE, myData.iFanSpeed);
428 
429 	if (myConfig.bAlertSound)
430 		cairo_dock_play_sound (myConfig.cSoundPath);
431 
432 	myData.bFanAlerted = TRUE;
433 }
434