1 /*
2 Copyright 2021 Northern.tech AS
3
4 This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; version 3.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18
19 To the extent this program is licensed as part of the Enterprise
20 versions of CFEngine, the applicable Commercial Open Source License
21 (COSL) may apply to this file if you as a licensee so wish it. See
22 included file COSL.txt.
23 */
24
25 #include <cf3.defs.h>
26
27 #include <mon.h>
28 #include <dir.h>
29 #include <item_lib.h>
30 #include <files_interfaces.h>
31 #include <pipes.h>
32
33 /* Globals */
34
35 static bool ACPI = false;
36 static bool SYSTHERMAL = false;
37 static bool LMSENSORS = false;
38
39 /* Prototypes */
40
41 #if defined(__linux__)
42 static bool GetAcpi(double *cf_this);
43 static bool GetSysThermal(double *cf_this);
44 static bool GetLMSensors(double *cf_this);
45 #endif
46
47 /* Implementation */
48
49 /******************************************************************************
50 * Motherboard sensors - how to standardize this somehow
51 * We're mainly interested in temperature and power consumption, but only the
52 * temperature is generally available. Several temperatures exist too ...
53 ******************************************************************************/
54
55 #if defined(__linux__)
56
MonTempGatherData(double * cf_this)57 void MonTempGatherData(double *cf_this)
58 {
59 if (ACPI && GetAcpi(cf_this))
60 {
61 return;
62 }
63
64 if (SYSTHERMAL && GetSysThermal(cf_this))
65 {
66 return;
67 }
68
69 if (LMSENSORS && GetLMSensors(cf_this))
70 {
71 return;
72 }
73 }
74
75 #else
76
MonTempGatherData(ARG_UNUSED double * cf_this)77 void MonTempGatherData(ARG_UNUSED double *cf_this)
78 {
79 }
80
81 #endif
82
83 /******************************************************************************/
84
MonTempInit(void)85 void MonTempInit(void)
86 {
87 struct stat statbuf;
88
89 for (int i = 0; i < 4; i++)
90 {
91 char s[128];
92 xsnprintf(s, sizeof(s),
93 "/sys/devices/virtual/thermal/thermal_zone%d", i);
94
95 if (stat(s, &statbuf) != -1)
96 {
97 Log(LOG_LEVEL_DEBUG, "Found a thermal device in /sys");
98 SYSTHERMAL = true;
99 }
100 }
101
102 if (stat("/proc/acpi/thermal_zone", &statbuf) != -1)
103 {
104 Log(LOG_LEVEL_DEBUG, "Found an acpi service");
105 ACPI = true;
106 }
107
108 if (stat("/usr/bin/sensors", &statbuf) != -1)
109 {
110 if (statbuf.st_mode & 0111)
111 {
112 Log(LOG_LEVEL_DEBUG, "Found an lmsensor system");
113 LMSENSORS = true;
114 }
115 }
116 }
117
118 /******************************************************************************/
119
120 #if defined(__linux__)
GetAcpi(double * cf_this)121 static bool GetAcpi(double *cf_this)
122 {
123 Dir *dirh;
124 FILE *fp;
125 const struct dirent *dirp;
126 int count;
127 char path[CF_BUFSIZE], buf[CF_BUFSIZE], index[4];
128 double temp;
129
130 if ((dirh = DirOpen("/proc/acpi/thermal_zone")) == NULL)
131 {
132 Log(LOG_LEVEL_VERBOSE, "Can't open directory '%s'. (opendir: %s)", path, GetErrorStr());
133 return false;
134 }
135
136 for (dirp = DirRead(dirh); dirp != NULL; dirp = DirRead(dirh))
137 {
138 if (!strcmp(dirp->d_name, ".") || !strcmp(dirp->d_name, ".."))
139 {
140 continue;
141 }
142
143 snprintf(path, CF_BUFSIZE, "/proc/acpi/thermal_zone/%s/temperature", dirp->d_name);
144
145 if ((fp = fopen(path, "r")) == NULL)
146 {
147 Log(LOG_LEVEL_ERR, "Couldn't open '%s' to gather temperature data (fopen: %s)", path, GetErrorStr());
148 continue;
149 }
150
151 if (fgets(buf, sizeof(buf), fp) == NULL)
152 {
153 Log(LOG_LEVEL_ERR, "Failed to read line from stream '%s'", path);
154 fclose(fp);
155 continue;
156 }
157
158 temp = 0.0;
159 sscanf(buf, "%*s %lf", &temp);
160
161 for (count = 0; count < 4; count++)
162 {
163 snprintf(index, 2, "%d", count);
164
165 if (strstr(dirp->d_name, index))
166 {
167 switch (count)
168 {
169 case 0:
170 cf_this[ob_temp0] = temp;
171 break;
172 case 1:
173 cf_this[ob_temp1] = temp;
174 break;
175 case 2:
176 cf_this[ob_temp2] = temp;
177 break;
178 case 3:
179 cf_this[ob_temp3] = temp;
180 break;
181 }
182
183 Log(LOG_LEVEL_DEBUG, "Set temp%d to %lf", count, temp);
184 }
185 }
186 fclose(fp);
187 }
188
189 DirClose(dirh);
190 return true;
191 }
192
193 /******************************************************************************/
194
GetSysThermal(double * cf_this)195 static bool GetSysThermal(double *cf_this)
196 {
197 FILE *fp;
198 int count;
199 bool retval = false;
200
201 for (count = 0; count < 4; count++)
202 {
203 double temp = 0;
204
205 char path[128];
206 xsnprintf(path, sizeof(path),
207 "/sys/devices/virtual/thermal/thermal_zone%d/temp", count);
208
209 if ((fp = fopen(path, "r")) == NULL)
210 {
211 Log(LOG_LEVEL_INFO, "Couldn't open '%s'", path);
212 continue;
213 }
214
215 char buf[128];
216 if (fgets(buf, sizeof(buf), fp) == NULL)
217 {
218 Log(LOG_LEVEL_INFO, "Failed to read line from stream '%s'", path);
219 fclose(fp);
220 continue;
221 }
222
223 int ret = sscanf(buf, "%lf", &temp);
224 if (ret == 1)
225 {
226 switch (count)
227 {
228 case 0:
229 cf_this[ob_temp0] = temp;
230 break;
231 case 1:
232 cf_this[ob_temp1] = temp;
233 break;
234 case 2:
235 cf_this[ob_temp2] = temp;
236 break;
237 case 3:
238 cf_this[ob_temp3] = temp;
239 break;
240 }
241
242 Log(LOG_LEVEL_DEBUG, "Set temp%d to %lf", count, temp);
243 retval = true;
244 }
245 else
246 {
247 Log(LOG_LEVEL_INFO, "Failed to read number from: %s", path);
248 }
249
250 fclose(fp);
251 }
252
253 return retval;
254 }
255
256 /******************************************************************************/
257
GetLMSensors(double * cf_this)258 static bool GetLMSensors(double *cf_this)
259 {
260 FILE *pp;
261 Item *ip, *list = NULL;
262 double temp = 0;
263 char name[CF_BUFSIZE];
264 int count;
265
266 cf_this[ob_temp0] = 0.0;
267 cf_this[ob_temp1] = 0.0;
268 cf_this[ob_temp2] = 0.0;
269 cf_this[ob_temp3] = 0.0;
270
271 if ((pp = cf_popen("/usr/bin/sensors", "r", true)) == NULL)
272 {
273 LMSENSORS = false; /* Broken */
274 return false;
275 }
276
277 {
278 size_t vbuff_size = CF_BUFSIZE;
279 char *vbuff = xmalloc(vbuff_size);
280
281 ssize_t res = CfReadLine(&vbuff, &vbuff_size, pp);
282 if (res <= 0)
283 {
284 /* FIXME: do we need to log anything here? */
285 cf_pclose(pp);
286 free(vbuff);
287 return false;
288 }
289
290 for (;;)
291 {
292 ssize_t res = CfReadLine(&vbuff, &vbuff_size, pp);
293 if (res == -1)
294 {
295 if (!feof(pp))
296 {
297 /* FIXME: Do we need to log anything here? */
298 cf_pclose(pp);
299 free(vbuff);
300 return false;
301 }
302 else
303 {
304 break;
305 }
306 }
307
308 if (strstr(vbuff, "Temp") || strstr(vbuff, "temp"))
309 {
310 PrependItem(&list, vbuff, NULL);
311 }
312 }
313
314 cf_pclose(pp);
315 free(vbuff);
316 }
317
318 if (ListLen(list) > 0)
319 {
320 Log(LOG_LEVEL_DEBUG, "LM Sensors seemed to return ok data");
321 }
322 else
323 {
324 return false;
325 }
326
327 /* lmsensor names are hopelessly inconsistent - so try a few things */
328
329 for (ip = list; ip != NULL; ip = ip->next)
330 {
331 for (count = 0; count < 4; count++)
332 {
333 snprintf(name, 16, "CPU%d Temp:", count);
334
335 if (strncmp(ip->name, name, strlen(name)) == 0)
336 {
337 sscanf(ip->name, "%*[^:]: %lf", &temp);
338
339 switch (count)
340 {
341 case 0:
342 cf_this[ob_temp0] = temp;
343 break;
344 case 1:
345 cf_this[ob_temp1] = temp;
346 break;
347 case 2:
348 cf_this[ob_temp2] = temp;
349 break;
350 case 3:
351 cf_this[ob_temp3] = temp;
352 break;
353 }
354
355 Log(LOG_LEVEL_DEBUG, "Set temp%d to %lf from what looks like cpu temperature", count, temp);
356 }
357 }
358 }
359
360 if (cf_this[ob_temp0] != 0)
361 {
362 /* We got something plausible */
363 return true;
364 }
365
366 /* Alternative name Core x: */
367
368 for (ip = list; ip != NULL; ip = ip->next)
369 {
370 for (count = 0; count < 4; count++)
371 {
372 snprintf(name, 16, "Core %d:", count);
373
374 if (strncmp(ip->name, name, strlen(name)) == 0)
375 {
376 sscanf(ip->name, "%*[^:]: %lf", &temp);
377
378 switch (count)
379 {
380 case 0:
381 cf_this[ob_temp0] = temp;
382 break;
383 case 1:
384 cf_this[ob_temp1] = temp;
385 break;
386 case 2:
387 cf_this[ob_temp2] = temp;
388 break;
389 case 3:
390 cf_this[ob_temp3] = temp;
391 break;
392 }
393
394 Log(LOG_LEVEL_DEBUG, "Set temp%d to %lf from what looks like core temperatures", count, temp);
395 }
396 }
397 }
398
399 if (cf_this[ob_temp0] != 0)
400 {
401 /* We got something plausible */
402 return true;
403 }
404
405 for (ip = list; ip != NULL; ip = ip->next)
406 {
407 if (strncmp(ip->name, "CPU Temp:", strlen("CPU Temp:")) == 0)
408 {
409 sscanf(ip->name, "%*[^:]: %lf", &temp);
410 Log(LOG_LEVEL_DEBUG, "Setting temp0 to CPU Temp");
411 cf_this[ob_temp0] = temp;
412 }
413
414 if (strncmp(ip->name, "M/B Temp:", strlen("M/B Temp:")) == 0)
415 {
416 sscanf(ip->name, "%*[^:]: %lf", &temp);
417 Log(LOG_LEVEL_DEBUG, "Setting temp0 to M/B Temp");
418 cf_this[ob_temp1] = temp;
419 }
420
421 if (strncmp(ip->name, "Sys Temp:", strlen("Sys Temp:")) == 0)
422 {
423 sscanf(ip->name, "%*[^:]: %lf", &temp);
424 Log(LOG_LEVEL_DEBUG, "Setting temp0 to Sys Temp");
425 cf_this[ob_temp2] = temp;
426 }
427
428 if (strncmp(ip->name, "AUX Temp:", strlen("AUX Temp:")) == 0)
429 {
430 sscanf(ip->name, "%*[^:]: %lf", &temp);
431 Log(LOG_LEVEL_DEBUG, "Setting temp0 to AUX Temp");
432 cf_this[ob_temp3] = temp;
433 }
434 }
435
436 if (cf_this[ob_temp0] != 0)
437 {
438 /* We got something plausible */
439 return true;
440 }
441
442 /* Alternative name Core x: */
443
444 for (ip = list; ip != NULL; ip = ip->next)
445 {
446 for (count = 0; count < 4; count++)
447 {
448 snprintf(name, 16, "temp%d:", count);
449
450 if (strncmp(ip->name, name, strlen(name)) == 0)
451 {
452 sscanf(ip->name, "%*[^:]: %lf", &temp);
453
454 switch (count)
455 {
456 case 0:
457 cf_this[ob_temp0] = temp;
458 break;
459 case 1:
460 cf_this[ob_temp1] = temp;
461 break;
462 case 2:
463 cf_this[ob_temp2] = temp;
464 break;
465 case 3:
466 cf_this[ob_temp3] = temp;
467 break;
468 }
469
470 Log(LOG_LEVEL_DEBUG, "Set temp%d to %lf", count, temp);
471 }
472 }
473 }
474
475 /* Give up? */
476 DeleteItemList(list);
477 return true;
478 }
479
480 #endif
481