master
c 1,331 lines 47.8 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "debugfs_plugin.h"
4
5 #define NETDATA_CALCULATED_STATES 1
6
7 #include "libsensors/vendored/lib/sensors.h"
8 #include "libsensors/vendored/lib/error.h"
9
10 typedef short SENSOR_BUS_TYPE;
11 ENUM_STR_MAP_DEFINE(SENSOR_BUS_TYPE) = {
12 { .id = SENSORS_BUS_TYPE_ANY, .name = "any", },
13 { .id = SENSORS_BUS_TYPE_I2C, .name = "i2c", },
14 { .id = SENSORS_BUS_TYPE_ISA, .name = "isa", },
15 { .id = SENSORS_BUS_TYPE_PCI, .name = "pci", },
16 { .id = SENSORS_BUS_TYPE_SPI, .name = "spi", },
17 { .id = SENSORS_BUS_TYPE_VIRTUAL, .name = "virtual", },
18 { .id = SENSORS_BUS_TYPE_ACPI, .name = "acpi", },
19 { .id = SENSORS_BUS_TYPE_HID, .name = "hid", },
20 { .id = SENSORS_BUS_TYPE_MDIO, .name = "mdio", },
21 { .id = SENSORS_BUS_TYPE_SCSI, .name = "scsi", },
22
23 // terminator
24 {.id = 0, .name = NULL}
25 };
26 ENUM_STR_DEFINE_FUNCTIONS(SENSOR_BUS_TYPE, SENSORS_BUS_TYPE_ANY, "any");
27
28 typedef sensors_feature_type SENSOR_TYPE;
29 ENUM_STR_MAP_DEFINE(SENSOR_TYPE) = {
30 { .id = SENSORS_FEATURE_IN, .name = "voltage", },
31 { .id = SENSORS_FEATURE_FAN, .name = "fan", },
32 { .id = SENSORS_FEATURE_TEMP, .name = "temperature", },
33 { .id = SENSORS_FEATURE_POWER, .name = "power", },
34 { .id = SENSORS_FEATURE_ENERGY, .name = "energy", },
35 { .id = SENSORS_FEATURE_CURR, .name = "curr", },
36 { .id = SENSORS_FEATURE_HUMIDITY, .name = "humidity", },
37 { .id = SENSORS_FEATURE_VID, .name = "vid", },
38 { .id = SENSORS_FEATURE_INTRUSION, .name = "intrusion", },
39 { .id = SENSORS_FEATURE_BEEP_ENABLE, .name = "beep_enable", },
40 { .id = SENSORS_FEATURE_UNKNOWN, .name = "unknown", },
41
42 // terminator
43 {.id = 0, .name = NULL}
44 };
45 ENUM_STR_DEFINE_FUNCTIONS(SENSOR_TYPE, SENSORS_FEATURE_UNKNOWN, "unknown");
46
47 typedef sensors_subfeature_type SENSOR_SUBFEATURE_TYPE;
48 ENUM_STR_MAP_DEFINE(SENSOR_SUBFEATURE_TYPE) = {
49 // Voltage input subfeatures
50 { .id = SENSORS_SUBFEATURE_IN_INPUT, .name = "input", },
51 { .id = SENSORS_SUBFEATURE_IN_MIN, .name = "minimum", },
52 { .id = SENSORS_SUBFEATURE_IN_MAX, .name = "maximum", },
53 { .id = SENSORS_SUBFEATURE_IN_LCRIT, .name = "critical low", },
54 { .id = SENSORS_SUBFEATURE_IN_CRIT, .name = "critical high", },
55 { .id = SENSORS_SUBFEATURE_IN_AVERAGE, .name = "average", },
56 { .id = SENSORS_SUBFEATURE_IN_LOWEST, .name = "lowest", },
57 { .id = SENSORS_SUBFEATURE_IN_HIGHEST, .name = "highest", },
58 { .id = SENSORS_SUBFEATURE_IN_ALARM, .name = "alarm", },
59 { .id = SENSORS_SUBFEATURE_IN_MIN_ALARM, .name = "alarm low", },
60 { .id = SENSORS_SUBFEATURE_IN_MAX_ALARM, .name = "alarm high", },
61 { .id = SENSORS_SUBFEATURE_IN_BEEP, .name = "beep", },
62 { .id = SENSORS_SUBFEATURE_IN_LCRIT_ALARM, .name = "critical alarm low", },
63 { .id = SENSORS_SUBFEATURE_IN_CRIT_ALARM, .name = "critical alarm high", },
64
65 // Fan subfeatures
66 { .id = SENSORS_SUBFEATURE_FAN_INPUT, .name = "input", },
67 { .id = SENSORS_SUBFEATURE_FAN_MIN, .name = "minimum", },
68 { .id = SENSORS_SUBFEATURE_FAN_MAX, .name = "maximum", },
69 { .id = SENSORS_SUBFEATURE_FAN_ALARM, .name = "alarm", },
70 { .id = SENSORS_SUBFEATURE_FAN_FAULT, .name = "fault", },
71 { .id = SENSORS_SUBFEATURE_FAN_DIV, .name = "divisor", },
72 { .id = SENSORS_SUBFEATURE_FAN_BEEP, .name = "beep", },
73 { .id = SENSORS_SUBFEATURE_FAN_PULSES, .name = "pulses", },
74 { .id = SENSORS_SUBFEATURE_FAN_MIN_ALARM, .name = "alarm low", },
75 { .id = SENSORS_SUBFEATURE_FAN_MAX_ALARM, .name = "alarm high", },
76
77 // Temperature subfeatures
78 { .id = SENSORS_SUBFEATURE_TEMP_INPUT, .name = "input", },
79 { .id = SENSORS_SUBFEATURE_TEMP_MAX, .name = "maximum", },
80 { .id = SENSORS_SUBFEATURE_TEMP_MAX_HYST, .name = "maximum hysteresis", },
81 { .id = SENSORS_SUBFEATURE_TEMP_MIN, .name = "minimum", },
82 { .id = SENSORS_SUBFEATURE_TEMP_CRIT, .name = "critical high", },
83 { .id = SENSORS_SUBFEATURE_TEMP_CRIT_HYST, .name = "critical hysteresis", },
84 { .id = SENSORS_SUBFEATURE_TEMP_LCRIT, .name = "critical low", },
85 { .id = SENSORS_SUBFEATURE_TEMP_EMERGENCY, .name = "emergency", },
86 { .id = SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST, .name = "emergency hysteresis", },
87 { .id = SENSORS_SUBFEATURE_TEMP_LOWEST, .name = "lowest", },
88 { .id = SENSORS_SUBFEATURE_TEMP_HIGHEST, .name = "highest", },
89 { .id = SENSORS_SUBFEATURE_TEMP_MIN_HYST, .name = "minimum hysteresis", },
90 { .id = SENSORS_SUBFEATURE_TEMP_LCRIT_HYST, .name = "critical low hysteresis", },
91 { .id = SENSORS_SUBFEATURE_TEMP_ALARM, .name = "alarm", },
92 { .id = SENSORS_SUBFEATURE_TEMP_MAX_ALARM, .name = "alarm high", },
93 { .id = SENSORS_SUBFEATURE_TEMP_MIN_ALARM, .name = "alarm low", },
94 { .id = SENSORS_SUBFEATURE_TEMP_CRIT_ALARM, .name = "critical alarm high", },
95 { .id = SENSORS_SUBFEATURE_TEMP_FAULT, .name = "fault", },
96 { .id = SENSORS_SUBFEATURE_TEMP_TYPE, .name = "type", },
97 { .id = SENSORS_SUBFEATURE_TEMP_OFFSET, .name = "offset", },
98 { .id = SENSORS_SUBFEATURE_TEMP_BEEP, .name = "beep", },
99 { .id = SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM, .name = "emergency alarm", },
100 { .id = SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM, .name = "critical alarm low", },
101
102 // Power subfeatures
103 { .id = SENSORS_SUBFEATURE_POWER_AVERAGE, .name = "average", },
104 { .id = SENSORS_SUBFEATURE_POWER_AVERAGE_HIGHEST, .name = "average highest", },
105 { .id = SENSORS_SUBFEATURE_POWER_AVERAGE_LOWEST, .name = "average lowest", },
106 { .id = SENSORS_SUBFEATURE_POWER_INPUT, .name = "input", },
107 { .id = SENSORS_SUBFEATURE_POWER_INPUT_HIGHEST, .name = "input highest", },
108 { .id = SENSORS_SUBFEATURE_POWER_INPUT_LOWEST, .name = "input lowest", },
109 { .id = SENSORS_SUBFEATURE_POWER_CAP, .name = "cap", },
110 { .id = SENSORS_SUBFEATURE_POWER_CAP_HYST, .name = "cap hysteresis", },
111 { .id = SENSORS_SUBFEATURE_POWER_MAX, .name = "maximum", },
112 { .id = SENSORS_SUBFEATURE_POWER_CRIT, .name = "critical high", },
113 { .id = SENSORS_SUBFEATURE_POWER_MIN, .name = "minimum", },
114 { .id = SENSORS_SUBFEATURE_POWER_LCRIT, .name = "critical low", },
115 { .id = SENSORS_SUBFEATURE_POWER_AVERAGE_INTERVAL, .name = "average interval", },
116 { .id = SENSORS_SUBFEATURE_POWER_ALARM, .name = "alarm", },
117 { .id = SENSORS_SUBFEATURE_POWER_CAP_ALARM, .name = "cap alarm", },
118 { .id = SENSORS_SUBFEATURE_POWER_MAX_ALARM, .name = "alarm high", },
119 { .id = SENSORS_SUBFEATURE_POWER_CRIT_ALARM, .name = "critical alarm high", },
120 { .id = SENSORS_SUBFEATURE_POWER_MIN_ALARM, .name = "alarm low", },
121 { .id = SENSORS_SUBFEATURE_POWER_LCRIT_ALARM, .name = "critical alarm low", },
122
123 // Energy subfeatures
124 { .id = SENSORS_SUBFEATURE_ENERGY_INPUT, .name = "input", },
125
126 // Current subfeatures
127 { .id = SENSORS_SUBFEATURE_CURR_INPUT, .name = "input", },
128 { .id = SENSORS_SUBFEATURE_CURR_MIN, .name = "minimum", },
129 { .id = SENSORS_SUBFEATURE_CURR_MAX, .name = "maximum", },
130 { .id = SENSORS_SUBFEATURE_CURR_LCRIT, .name = "critical low", },
131 { .id = SENSORS_SUBFEATURE_CURR_CRIT, .name = "critical high", },
132 { .id = SENSORS_SUBFEATURE_CURR_AVERAGE, .name = "average", },
133 { .id = SENSORS_SUBFEATURE_CURR_LOWEST, .name = "lowest", },
134 { .id = SENSORS_SUBFEATURE_CURR_HIGHEST, .name = "highest", },
135 { .id = SENSORS_SUBFEATURE_CURR_ALARM, .name = "alarm", },
136 { .id = SENSORS_SUBFEATURE_CURR_MIN_ALARM, .name = "alarm low", },
137 { .id = SENSORS_SUBFEATURE_CURR_MAX_ALARM, .name = "alarm high", },
138 { .id = SENSORS_SUBFEATURE_CURR_BEEP, .name = "beep", },
139 { .id = SENSORS_SUBFEATURE_CURR_LCRIT_ALARM, .name = "critical alarm low", },
140 { .id = SENSORS_SUBFEATURE_CURR_CRIT_ALARM, .name = "critical alarm high", },
141
142 // Humidity subfeatures
143 { .id = SENSORS_SUBFEATURE_HUMIDITY_INPUT, .name = "input", },
144
145 // VID subfeatures
146 { .id = SENSORS_SUBFEATURE_VID, .name = "value", },
147
148 // Intrusion subfeatures
149 { .id = SENSORS_SUBFEATURE_INTRUSION_ALARM, .name = "alarm", },
150 { .id = SENSORS_SUBFEATURE_INTRUSION_BEEP, .name = "beep", },
151
152 // Beep enable subfeatures
153 { .id = SENSORS_SUBFEATURE_BEEP_ENABLE, .name = "enable", },
154
155 // Unknown subfeature
156 { .id = SENSORS_SUBFEATURE_UNKNOWN, .name = "unknown", },
157
158 // terminator
159 {.id = 0, .name = NULL}
160 };
161 ENUM_STR_DEFINE_FUNCTIONS(SENSOR_SUBFEATURE_TYPE, SENSORS_SUBFEATURE_UNKNOWN, "unknown");
162
163 typedef enum {
164 SENSOR_STATE_NONE = 0, // unset
165 SENSOR_STATE_CLEAR = (1 << 0), // everything is good
166 SENSOR_STATE_WARNING = (1 << 1), // our own calculations indicate an alarm, but not the driver
167 SENSOR_STATE_CAP = (1 << 2), // our own calculations or the driver, indicate cap
168 SENSOR_STATE_ALARM = (1 << 3), // the kernel driver has raised an alarm
169 SENSOR_STATE_CRITICAL = (1 << 4), // our own calculations, or the driver, indicate a critical condition
170 SENSOR_STATE_EMERGENCY = (1 << 5), // our own calculations, or the driver, indicate an emergency
171 SENSOR_STATE_FAULT = (1 << 6), // our own calculations, or the driver, indicate a fault
172 } SENSOR_STATE;
173
174 ENUM_STR_MAP_DEFINE(SENSOR_STATE) = {
175 { .id = SENSOR_STATE_CLEAR, .name = "clear", },
176 { .id = SENSOR_STATE_WARNING, .name = "warning", },
177 { .id = SENSOR_STATE_CAP, .name = "cap", },
178 { .id = SENSOR_STATE_ALARM, .name = "alarm", },
179 { .id = SENSOR_STATE_CRITICAL, .name = "critical", },
180 { .id = SENSOR_STATE_EMERGENCY, .name = "emergency", },
181 { .id = SENSOR_STATE_FAULT, .name = "fault", },
182
183 // terminator
184 { .id = 0, .name = NULL, },
185 };
186 ENUM_STR_DEFINE_FUNCTIONS(SENSOR_STATE, SENSOR_STATE_NONE, "unknown");
187
188 #define NOT_SUPPORTED (SENSORS_SUBFEATURE_UNKNOWN)
189
190 struct sensor_config {
191 bool enabled;
192
193 bool report_state;
194 bool report_value;
195 const char *title;
196 const char *units;
197 const char *context;
198 const char *family;
199 int priority;
200
201 // sensor readings
202 SENSOR_SUBFEATURE_TYPE input;
203 SENSOR_SUBFEATURE_TYPE average;
204
205 // thresholds
206 SENSOR_SUBFEATURE_TYPE min;
207 SENSOR_SUBFEATURE_TYPE max;
208 SENSOR_SUBFEATURE_TYPE lcrit;
209 SENSOR_SUBFEATURE_TYPE crit;
210 SENSOR_SUBFEATURE_TYPE cap;
211 SENSOR_SUBFEATURE_TYPE emergency;
212
213 // alarms
214 SENSOR_SUBFEATURE_TYPE fault;
215 SENSOR_SUBFEATURE_TYPE alarm;
216 SENSOR_SUBFEATURE_TYPE min_alarm;
217 SENSOR_SUBFEATURE_TYPE max_alarm;
218 SENSOR_SUBFEATURE_TYPE lcrit_alarm;
219 SENSOR_SUBFEATURE_TYPE crit_alarm;
220 SENSOR_SUBFEATURE_TYPE cap_alarm;
221 SENSOR_SUBFEATURE_TYPE emergency_alarm;
222 } sensors_configurations[] = {
223 [SENSORS_FEATURE_IN] = {
224 .enabled = true,
225 .title = "Sensor Voltage",
226 .units = "Volts",
227 .context = "system.hw.sensor.voltage",
228 .family = "Voltage",
229 .priority = 70002,
230 .report_value = true,
231 .report_state = true,
232
233 .input = SENSORS_SUBFEATURE_IN_INPUT,
234 .average = SENSORS_SUBFEATURE_IN_AVERAGE,
235
236 .min = SENSORS_SUBFEATURE_IN_MIN,
237 .max = SENSORS_SUBFEATURE_IN_MAX,
238 .lcrit = SENSORS_SUBFEATURE_IN_LCRIT,
239 .crit = SENSORS_SUBFEATURE_IN_CRIT,
240 .cap = NOT_SUPPORTED,
241 .emergency = NOT_SUPPORTED,
242
243 .fault = NOT_SUPPORTED,
244 .alarm = SENSORS_SUBFEATURE_IN_ALARM,
245 .min_alarm = SENSORS_SUBFEATURE_IN_MIN_ALARM,
246 .max_alarm = SENSORS_SUBFEATURE_IN_MAX_ALARM,
247 .lcrit_alarm = SENSORS_SUBFEATURE_IN_LCRIT_ALARM,
248 .crit_alarm = SENSORS_SUBFEATURE_IN_CRIT_ALARM,
249 .cap_alarm = NOT_SUPPORTED,
250 .emergency_alarm = NOT_SUPPORTED,
251 },
252
253 [SENSORS_FEATURE_FAN] = {
254 .enabled = true,
255 .title = "Sensor Fan Speed",
256 .units = "rotations per minute",
257 .context = "system.hw.sensor.fan",
258 .family = "Fan",
259 .priority = 70005,
260 .report_value = true,
261 .report_state = true,
262
263 .input = SENSORS_SUBFEATURE_FAN_INPUT,
264 .average = NOT_SUPPORTED,
265
266 .min = SENSORS_SUBFEATURE_FAN_MIN,
267 .max = SENSORS_SUBFEATURE_FAN_MAX,
268 .lcrit = NOT_SUPPORTED,
269 .crit = NOT_SUPPORTED,
270 .cap = NOT_SUPPORTED,
271 .emergency = NOT_SUPPORTED,
272
273 .fault = SENSORS_SUBFEATURE_FAN_FAULT,
274 .alarm = SENSORS_SUBFEATURE_FAN_ALARM,
275 .min_alarm = SENSORS_SUBFEATURE_FAN_MIN_ALARM,
276 .max_alarm = SENSORS_SUBFEATURE_FAN_MAX_ALARM,
277 .lcrit_alarm = NOT_SUPPORTED,
278 .crit_alarm = NOT_SUPPORTED,
279 .cap_alarm = NOT_SUPPORTED,
280 .emergency_alarm = NOT_SUPPORTED,
281 },
282
283 [SENSORS_FEATURE_TEMP] = {
284 .enabled = true,
285 .title = "Sensor Temperature",
286 .units = "degrees Celsius",
287 .context = "system.hw.sensor.temperature",
288 .family = "Temperature",
289 .priority = 70000,
290 .report_value = true,
291 .report_state = true,
292
293 .input = SENSORS_SUBFEATURE_TEMP_INPUT,
294 .average = NOT_SUPPORTED,
295
296 .min = SENSORS_SUBFEATURE_TEMP_MIN,
297 .max = SENSORS_SUBFEATURE_TEMP_MAX,
298 .lcrit = SENSORS_SUBFEATURE_TEMP_LCRIT,
299 .crit = SENSORS_SUBFEATURE_TEMP_CRIT,
300 .cap = NOT_SUPPORTED,
301 .emergency = SENSORS_SUBFEATURE_TEMP_EMERGENCY,
302
303 .fault = SENSORS_SUBFEATURE_TEMP_FAULT,
304 .alarm = SENSORS_SUBFEATURE_TEMP_ALARM,
305 .min_alarm = SENSORS_SUBFEATURE_TEMP_MIN_ALARM,
306 .max_alarm = SENSORS_SUBFEATURE_TEMP_MAX_ALARM,
307 .lcrit_alarm = SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM,
308 .crit_alarm = SENSORS_SUBFEATURE_TEMP_CRIT_ALARM,
309 .cap_alarm = NOT_SUPPORTED,
310 .emergency_alarm = SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM,
311 },
312
313 [SENSORS_FEATURE_POWER] = {
314 .enabled = true,
315 .title = "Sensor Power",
316 .units = "Watts",
317 .context = "system.hw.sensor.power",
318 .family = "Power",
319 .priority = 70006,
320 .report_value = true,
321 .report_state = true,
322
323 .input = SENSORS_SUBFEATURE_POWER_INPUT,
324 .average = SENSORS_SUBFEATURE_POWER_AVERAGE,
325
326 .min = SENSORS_SUBFEATURE_POWER_MIN,
327 .max = SENSORS_SUBFEATURE_POWER_MAX,
328 .lcrit = SENSORS_SUBFEATURE_POWER_LCRIT,
329 .crit = SENSORS_SUBFEATURE_POWER_CRIT,
330 .cap = SENSORS_SUBFEATURE_POWER_CAP,
331 .emergency = NOT_SUPPORTED,
332
333 .fault = NOT_SUPPORTED,
334 .alarm = SENSORS_SUBFEATURE_POWER_ALARM,
335 .min_alarm = SENSORS_SUBFEATURE_POWER_MIN_ALARM,
336 .max_alarm = SENSORS_SUBFEATURE_POWER_MAX_ALARM,
337 .lcrit_alarm = SENSORS_SUBFEATURE_POWER_LCRIT_ALARM,
338 .crit_alarm = SENSORS_SUBFEATURE_POWER_CRIT_ALARM,
339 .cap_alarm = SENSORS_SUBFEATURE_POWER_CAP_ALARM,
340 .emergency_alarm = NOT_SUPPORTED,
341 },
342
343 [SENSORS_FEATURE_ENERGY] = {
344 .enabled = true,
345 .title = "Sensor Energy",
346 .units = "Joules",
347 .context = "system.hw.sensor.energy",
348 .family = "Energy",
349 .priority = 70007,
350 .report_value = true,
351 .report_state = true,
352
353 .input = SENSORS_SUBFEATURE_ENERGY_INPUT,
354 .average = NOT_SUPPORTED,
355
356 .min = NOT_SUPPORTED,
357 .max = NOT_SUPPORTED,
358 .lcrit = NOT_SUPPORTED,
359 .crit = NOT_SUPPORTED,
360 .cap = NOT_SUPPORTED,
361 .emergency = NOT_SUPPORTED,
362
363 .fault = NOT_SUPPORTED,
364 .alarm = NOT_SUPPORTED,
365 .min_alarm = NOT_SUPPORTED,
366 .max_alarm = NOT_SUPPORTED,
367 .lcrit_alarm = NOT_SUPPORTED,
368 .crit_alarm = NOT_SUPPORTED,
369 .cap_alarm = NOT_SUPPORTED,
370 .emergency_alarm = NOT_SUPPORTED,
371 },
372
373 [SENSORS_FEATURE_CURR] = {
374 .enabled = true,
375 .title = "Sensor Current",
376 .units = "Amperes",
377 .context = "system.hw.sensor.current",
378 .family = "Current",
379 .priority = 70003,
380 .report_value = true,
381 .report_state = true,
382
383 .input = SENSORS_SUBFEATURE_CURR_INPUT,
384 .average = SENSORS_SUBFEATURE_CURR_AVERAGE,
385
386 .min = SENSORS_SUBFEATURE_CURR_MIN,
387 .max = SENSORS_SUBFEATURE_CURR_MAX,
388 .lcrit = SENSORS_SUBFEATURE_CURR_LCRIT,
389 .crit = SENSORS_SUBFEATURE_CURR_CRIT,
390 .cap = NOT_SUPPORTED,
391 .emergency = NOT_SUPPORTED,
392
393 .fault = NOT_SUPPORTED,
394 .alarm = SENSORS_SUBFEATURE_CURR_ALARM,
395 .min_alarm = SENSORS_SUBFEATURE_CURR_MIN_ALARM,
396 .max_alarm = SENSORS_SUBFEATURE_CURR_MAX_ALARM,
397 .lcrit_alarm = SENSORS_SUBFEATURE_CURR_LCRIT_ALARM,
398 .crit_alarm = SENSORS_SUBFEATURE_CURR_CRIT_ALARM,
399 .cap_alarm = NOT_SUPPORTED,
400 .emergency_alarm = NOT_SUPPORTED,
401 },
402
403 [SENSORS_FEATURE_HUMIDITY] = {
404 .enabled = true,
405 .title = "Sensor Humidity",
406 .units = "percentage",
407 .context = "system.hw.sensor.humidity",
408 .family = "Humidity",
409 .priority = 70004,
410 .report_value = true,
411 .report_state = true,
412
413 .input = SENSORS_SUBFEATURE_HUMIDITY_INPUT,
414 .average = NOT_SUPPORTED,
415
416 .min = NOT_SUPPORTED,
417 .max = NOT_SUPPORTED,
418 .lcrit = NOT_SUPPORTED,
419 .crit = NOT_SUPPORTED,
420 .cap = NOT_SUPPORTED,
421 .emergency = NOT_SUPPORTED,
422
423 .fault = NOT_SUPPORTED,
424 .alarm = NOT_SUPPORTED,
425 .min_alarm = NOT_SUPPORTED,
426 .max_alarm = NOT_SUPPORTED,
427 .lcrit_alarm = NOT_SUPPORTED,
428 .crit_alarm = NOT_SUPPORTED,
429 .cap_alarm = NOT_SUPPORTED,
430 .emergency_alarm = NOT_SUPPORTED,
431 },
432
433 [SENSORS_FEATURE_INTRUSION] = {
434 .enabled = true,
435 .title = "Sensor Intrusion",
436 .units = "", // No specific unit, as this is a binary state
437 .context = "system.hw.sensor.intrusion",
438 .family = "Intrusion",
439 .priority = 70008,
440 .report_value = false, // there is not value in intrusion
441 .report_state = true,
442
443 .input = NOT_SUPPORTED,
444 .average = NOT_SUPPORTED,
445
446 .min = NOT_SUPPORTED,
447 .max = NOT_SUPPORTED,
448 .lcrit = NOT_SUPPORTED,
449 .crit = NOT_SUPPORTED,
450 .cap = NOT_SUPPORTED,
451 .emergency = NOT_SUPPORTED,
452
453 .fault = NOT_SUPPORTED,
454 .alarm = SENSORS_SUBFEATURE_INTRUSION_ALARM,
455 .min_alarm = NOT_SUPPORTED,
456 .max_alarm = NOT_SUPPORTED,
457 .lcrit_alarm = NOT_SUPPORTED,
458 .crit_alarm = NOT_SUPPORTED,
459 .cap_alarm = NOT_SUPPORTED,
460 .emergency_alarm = NOT_SUPPORTED,
461 },
462 };
463
464 typedef struct subfeature {
465 STRING *name;
466 bool read;
467 double value;
468 } SUBFEATURE;
469 DEFINE_JUDYL_TYPED(SUBFEATURES, SUBFEATURE *);
470
471 typedef struct sensor {
472 bool read;
473
474 bool exposed_input;
475 bool exposed_average;
476 SENSOR_STATE exposed_states;
477
478 // double divisor; // the divisor required to convert to base units
479 double input;
480 double average;
481
482 STRING *id;
483
484 struct {
485 STRING *id;
486 STRING *driver;
487 STRING *adapter;
488 STRING *path;
489 STRING *device;
490 STRING *subsystem;
491 short bus;
492 int addr;
493 } chip;
494
495 struct {
496 SENSOR_TYPE type;
497 STRING *name;
498 STRING *label;
499 } feature;
500
501 SENSOR_STATE state;
502 SENSOR_STATE state_logged;
503 SENSOR_STATE supported_states;
504 SUBFEATURES_JudyLSet values;
505
506 struct sensor_config config;
507 STRING *log_msg;
508 } SENSOR;
509
510 static inline msec_t chip_update_interval(const char *path, msec_t default_interval_ms) {
511 char filename[FILENAME_MAX];
512 snprintfz(filename, sizeof(filename), "%s/update_interval", path);
513
514 unsigned long long result = 0;
515 if(read_single_number_file(filename, &result) != 0)
516 result = default_interval_ms;
517
518 return result;
519 }
520
521 static STRING *get_device_name(const char *hwmon_path) {
522 char device_path[FILENAME_MAX];
523 char link_target[FILENAME_MAX];
524
525 // Construct path to the device symlink
526 snprintfz(device_path, sizeof(device_path), "%s/device", hwmon_path);
527
528 // Read the symlink
529 ssize_t len = readlink(device_path, link_target, sizeof(link_target) - 1);
530 if (len < 0) return NULL;
531 link_target[len] = '\0';
532
533 // Extract the last component of the path
534 char *last_slash = strrchr(link_target, '/');
535 if (last_slash)
536 return string_strdupz(last_slash + 1);
537
538 return NULL;
539 }
540
541 static STRING *get_subsystem_name(const char *hwmon_path) {
542 char device_path[FILENAME_MAX];
543 char link_target[FILENAME_MAX];
544
545 // Construct path to the device symlink
546 snprintfz(device_path, sizeof(device_path), "%s/device/subsystem", hwmon_path);
547
548 // Read the symlink
549 ssize_t len = readlink(device_path, link_target, sizeof(link_target) - 1);
550 if (len < 0) return NULL;
551 link_target[len] = '\0';
552
553 // Extract the last component of the path
554 char *last_slash = strrchr(link_target, '/');
555 if (last_slash)
556 return string_strdupz(last_slash + 1);
557
558 return NULL;
559 }
560
561 static inline bool sensor_subfeature_needed(SENSOR *s, SENSOR_SUBFEATURE_TYPE type) {
562 return
563 type != NOT_SUPPORTED &&
564 (
565 type == s->config.input ||
566 type == s->config.average ||
567 type == s->config.min ||
568 type == s->config.max ||
569 type == s->config.lcrit ||
570 type == s->config.crit ||
571 type == s->config.cap ||
572 type == s->config.emergency ||
573 type == s->config.fault ||
574 type == s->config.alarm ||
575 type == s->config.min_alarm ||
576 type == s->config.max_alarm ||
577 type == s->config.lcrit_alarm ||
578 type == s->config.crit_alarm ||
579 type == s->config.cap_alarm ||
580 type == s->config.emergency_alarm
581 );
582 }
583
584 static inline double sensor_value(SENSOR *s, SENSOR_SUBFEATURE_TYPE type) {
585 double value = NAN;
586
587 SUBFEATURE *sft = SUBFEATURES_GET(&s->values, type);
588 if(sft && sft->read && !isinf(sft->value) && !isnan(sft->value))
589 value = sft->value;
590
591 return value;
592 }
593
594 static inline void transition_to_state(SENSOR *s) {
595 if(s->state_logged == s->state) {
596 string_freez(s->log_msg);
597 s->log_msg = NULL;
598 return;
599 }
600
601 ND_LOG_STACK lgs[] = {
602 ND_LOG_FIELD_UUID(NDF_MESSAGE_ID, &sensors_state_transition_msgid),
603 ND_LOG_FIELD_END(),
604 };
605 ND_LOG_STACK_PUSH(lgs);
606
607 ND_LOG_FIELD_PRIORITY prio;
608 switch(s->state) {
609 default:
610 case SENSOR_STATE_CLEAR:
611 prio = NDLP_NOTICE;
612 break;
613
614 case SENSOR_STATE_CAP:
615 case SENSOR_STATE_WARNING:
616 prio = NDLP_WARNING;
617 break;
618
619 case SENSOR_STATE_FAULT:
620 case SENSOR_STATE_ALARM:
621 prio = NDLP_ERR;
622 break;
623
624 case SENSOR_STATE_CRITICAL:
625 prio = NDLP_CRIT;
626 break;
627
628 case SENSOR_STATE_EMERGENCY:
629 prio = NDLP_ALERT;
630 break;
631 }
632
633 nd_log(NDLS_COLLECTORS, prio,
634 "%s sensor '%s' transitioned from state '%s' to '%s' [device '%s', driver '%s', subsystem '%s', path '%s']%s%s",
635 SENSOR_TYPE_2str(s->feature.type),
636 string2str(s->id),
637 SENSOR_STATE_2str(s->state_logged), SENSOR_STATE_2str(s->state),
638 string2str(s->chip.device), string2str(s->chip.driver),
639 string2str(s->chip.subsystem), string2str(s->chip.path),
640 s->log_msg ? ": " : "", string2str(s->log_msg));
641
642 string_freez(s->log_msg);
643 s->log_msg = NULL;
644
645 s->state_logged = s->state;
646 }
647
648 static inline void check_value_greater_than_zero(SENSOR *s, SENSOR_SUBFEATURE_TYPE *config, SENSOR_STATE state) {
649 if(*config == NOT_SUPPORTED)
650 return;
651
652 double status = sensor_value(s, *config);
653 if(isnan(status)) {
654 // we cannot read this
655 // exclude it from future iterations for this sensor
656 *config = NOT_SUPPORTED;
657 }
658 else {
659 // the sensor supports this state
660 s->supported_states |= state;
661
662 // set it to this state if it is raised
663 if(status > 0 && s->state == SENSOR_STATE_CLEAR) {
664 s->state = state;
665
666 string_freez(s->log_msg);
667 char buf[1024];
668 snprintf(buf, sizeof(buf), "%s == %f (kernel driver generated)",
669 SENSOR_SUBFEATURE_TYPE_2str(*config), status);
670 s->log_msg = string_strdupz(buf);
671 }
672 }
673 }
674
675 static void userspace_evaluation_log_msg(SENSOR *s, const char *reading_txt, const char *condition, const char *threshold_txt, double reading, double threshold) {
676 string_freez(s->log_msg);
677 char buf[1024];
678 snprintf(buf, sizeof(buf), "%s %f %s %s %f (userspace evaluation using kernel provided thresholds)",
679 reading_txt, reading, condition, threshold_txt, threshold);
680 s->log_msg = string_strdupz(buf);
681 }
682
683 static inline void check_smaller_than_threshold(SENSOR *s, SENSOR_SUBFEATURE_TYPE *config, SENSOR_STATE state) {
684 if(*config == NOT_SUPPORTED)
685 return;
686
687 double threshold = sensor_value(s, *config);
688 if(isnan(threshold)) {
689 // we cannot read this
690 // exclude it from future iterations for this sensor
691 *config = NOT_SUPPORTED;
692 }
693 else {
694 // the sensor supports this state
695 s->supported_states |= state;
696
697 // set it to this state if it is raised
698 if(s->input < threshold && s->state == SENSOR_STATE_CLEAR) {
699 s->state = state;
700 userspace_evaluation_log_msg(
701 s, "input", "<", SENSOR_SUBFEATURE_TYPE_2str(*config),
702 s->input, threshold);
703 }
704 else if(s->average < threshold && s->state == SENSOR_STATE_CLEAR) {
705 s->state = state;
706 userspace_evaluation_log_msg(
707 s, "average", "<", SENSOR_SUBFEATURE_TYPE_2str(*config),
708 s->average, threshold);
709 }
710 }
711 }
712
713 static inline void check_greater_than_threshold(SENSOR *s, SENSOR_SUBFEATURE_TYPE *config, SENSOR_STATE state) {
714 if(*config == NOT_SUPPORTED)
715 return;
716
717 double threshold = sensor_value(s, *config);
718 if(isnan(threshold)) {
719 // we cannot read this
720 // exclude it from future iterations for this sensor
721 *config = NOT_SUPPORTED;
722 }
723 else {
724 // the sensor supports this state
725 s->supported_states |= state;
726
727 // set it to this state if it is raised
728 if(s->input > threshold && s->state == SENSOR_STATE_CLEAR) {
729 s->state = state;
730 userspace_evaluation_log_msg(
731 s, "input", ">", SENSOR_SUBFEATURE_TYPE_2str(*config),
732 s->input, threshold);
733 }
734 else if(s->average > threshold && s->state == SENSOR_STATE_CLEAR) {
735 s->state = state;
736 userspace_evaluation_log_msg(
737 s, "average", ">", SENSOR_SUBFEATURE_TYPE_2str(*config),
738 s->average, threshold);
739 }
740 }
741 }
742
743 static inline void check_smaller_or_equal_to_threshold(SENSOR *s, SENSOR_SUBFEATURE_TYPE *config, SENSOR_STATE state) {
744 if(*config == NOT_SUPPORTED)
745 return;
746
747 double threshold = sensor_value(s, *config);
748 if(isnan(threshold)) {
749 // we cannot read this
750 // exclude it from future iterations for this sensor
751 *config = NOT_SUPPORTED;
752 }
753 else {
754 // the sensor supports this state
755 s->supported_states |= state;
756
757 // set it to this state if it is raised
758 if(s->input <= threshold && s->state == SENSOR_STATE_CLEAR) {
759 s->state = state;
760 userspace_evaluation_log_msg(
761 s, "input", "<=", SENSOR_SUBFEATURE_TYPE_2str(*config),
762 s->input, threshold);
763 }
764 else if(s->average <= threshold && s->state == SENSOR_STATE_CLEAR) {
765 s->state = state;
766 userspace_evaluation_log_msg(
767 s, "average", "<=", SENSOR_SUBFEATURE_TYPE_2str(*config),
768 s->average, threshold);
769 }
770 }
771 }
772
773 static inline void check_greater_or_equal_to_threshold(SENSOR *s, SENSOR_SUBFEATURE_TYPE *config, SENSOR_STATE state) {
774 if(*config == NOT_SUPPORTED)
775 return;
776
777 double threshold = sensor_value(s, *config);
778 if(isnan(threshold)) {
779 // we cannot read this
780 // exclude it from future iterations for this sensor
781 *config = NOT_SUPPORTED;
782 }
783 else {
784 // the sensor supports this state
785 s->supported_states |= state;
786
787 // set it to this state if it is raised
788 if(s->input >= threshold && s->state == SENSOR_STATE_CLEAR) {
789 s->state = state;
790 userspace_evaluation_log_msg(
791 s, "input", ">=", SENSOR_SUBFEATURE_TYPE_2str(*config),
792 s->input, threshold);
793 }
794 else if(s->average >= threshold && s->state == SENSOR_STATE_CLEAR) {
795 s->state = state;
796 userspace_evaluation_log_msg(
797 s, "average", ">=", SENSOR_SUBFEATURE_TYPE_2str(*config),
798 s->average, threshold);
799 }
800 }
801 }
802
803 static void set_sensor_state(SENSOR *s) {
804 s->supported_states = SENSOR_STATE_CLEAR;
805 s->state = SENSOR_STATE_CLEAR;
806
807 // ----------------------------------------------------------------------------------------------------------------
808 // read the values
809
810 if(s->config.input != NOT_SUPPORTED) {
811 s->input = sensor_value(s, s->config.input);
812 if(isnan(s->input) && !s->exposed_input) {
813 s->config.input = NOT_SUPPORTED;
814 s->input = NAN;
815 }
816 }
817
818 if(s->config.average != NOT_SUPPORTED) {
819 s->average = sensor_value(s, s->config.average);
820 if(isnan(s->average) && !s->exposed_average) {
821 s->config.average = NOT_SUPPORTED;
822 s->average = NAN;
823 }
824 }
825
826 // ----------------------------------------------------------------------------------------------------------------
827 // read the sensor alarms as exposed by the kernel driver
828
829 check_value_greater_than_zero(s, &s->config.fault, SENSOR_STATE_FAULT);
830 check_value_greater_than_zero(s, &s->config.emergency_alarm, SENSOR_STATE_EMERGENCY);
831 check_value_greater_than_zero(s, &s->config.crit_alarm, SENSOR_STATE_CRITICAL);
832 check_value_greater_than_zero(s, &s->config.lcrit_alarm, SENSOR_STATE_CRITICAL);
833 check_value_greater_than_zero(s, &s->config.max_alarm, SENSOR_STATE_ALARM);
834 check_value_greater_than_zero(s, &s->config.min_alarm, SENSOR_STATE_ALARM);
835 check_value_greater_than_zero(s, &s->config.alarm, SENSOR_STATE_ALARM);
836 check_value_greater_than_zero(s, &s->config.cap_alarm, SENSOR_STATE_CAP);
837
838 #ifdef NETDATA_CALCULATED_STATES
839
840 // ----------------------------------------------------------------------------------------------------------------
841 // our custom logic for triggering state changes
842
843 // if the sensor is already exposed to netdata, but now it cannot give values,
844 // set it to faulty state
845 s->supported_states |= SENSOR_STATE_FAULT;
846 if(isnan(s->input) && isnan(s->average) && (s->exposed_input || s->exposed_average) &&
847 s->state == SENSOR_STATE_CLEAR) {
848 s->state = SENSOR_STATE_FAULT;
849 }
850
851 check_greater_or_equal_to_threshold(s, &s->config.emergency, SENSOR_STATE_EMERGENCY);
852 check_greater_or_equal_to_threshold(s, &s->config.crit, SENSOR_STATE_CRITICAL);
853 check_smaller_or_equal_to_threshold(s, &s->config.lcrit, SENSOR_STATE_CRITICAL);
854 check_greater_than_threshold(s, &s->config.cap, SENSOR_STATE_CAP);
855 check_greater_than_threshold(s, &s->config.max, SENSOR_STATE_WARNING);
856 check_smaller_than_threshold(s, &s->config.min, SENSOR_STATE_WARNING);
857
858 #endif
859
860 // ----------------------------------------------------------------------------------------------------------------
861 // log any transitions
862
863 transition_to_state(s);
864 }
865
866 static SENSOR *sensor_get_or_create(DICTIONARY *dict, const sensors_chip_name *chip, const sensors_feature *feature) {
867 static __thread char buf[4096];
868
869 struct sensor_config *config = NULL;
870 if(feature->type < _countof(sensors_configurations))
871 config = &sensors_configurations[feature->type];
872
873 if(!config || !config->enabled)
874 return NULL;
875
876 snprintfz(buf, sizeof(buf),
877 "%s|%s-%d-%d-%s",
878 chip->path, chip->prefix, chip->bus.type, chip->addr, feature->name);
879
880 SENSOR *s = dictionary_get(dict, buf);
881 if(s) return s;
882
883 s = dictionary_set(dict, buf, NULL, sizeof(SENSOR));
884 s->config = *config;
885 s->state_logged = SENSOR_STATE_CLEAR;
886 s->input = NAN;
887 s->average = NAN;
888 s->feature.label = NULL;
889
890 sensors_snprintf_chip_name(buf, sizeof(buf), chip);
891 s->chip.id = string_strdupz(buf);
892 s->chip.driver = string_strdupz(chip->prefix);
893 s->chip.adapter = string_strdupz(sensors_get_adapter_name(&chip->bus));
894 s->chip.path = string_strdupz(chip->path);
895 s->chip.device = get_device_name(chip->path);
896 s->chip.subsystem = get_subsystem_name(chip->path);
897 s->chip.bus = chip->bus.type;
898 s->chip.addr = chip->addr;
899
900 s->feature.name = string_strdupz(feature->name);
901 s->feature.type = feature->type;
902
903 // returns feature->name if no label
904 // https://github.com/lm-sensors/lm-sensors/blob/master/lib/access.c#L199-L200
905 const char *label = sensors_get_label(chip, feature);
906
907 if (label && strcmp(label, string2str(s->feature.name)) != 0) {
908 s->feature.label = string_strdupz(label);
909 snprintfz(
910 buf,
911 sizeof(buf),
912 "%s_%s_%s_%s",
913 SENSOR_TYPE_2str(s->feature.type),
914 string2str(s->chip.id),
915 string2str(s->feature.name),
916 string2str(s->feature.label));
917 } else {
918 snprintfz(
919 buf,
920 sizeof(buf),
921 "%s_%s_%s",
922 SENSOR_TYPE_2str(s->feature.type),
923 string2str(s->chip.id),
924 string2str(s->feature.name));
925 }
926
927 // we have to free this, because it is malloced from libsensors
928 if(label)
929 free((void *)label); // do not use freez() here - libsensors uses malloc()
930
931 netdata_fix_chart_id(buf);
932 s->id = string_strdupz(buf);
933
934 return s;
935 }
936
937 static void sensor_labels(SENSOR *ft) {
938 printf(PLUGINSD_KEYWORD_CLABEL " feature '%s' 1\n", string2str(ft->feature.name));
939 printf(PLUGINSD_KEYWORD_CLABEL " label '%s' 1\n", string2str(ft->feature.label));
940 printf(PLUGINSD_KEYWORD_CLABEL " chip_id '%s' 1\n", string2str(ft->chip.id));
941 printf(PLUGINSD_KEYWORD_CLABEL " path '%s' 1\n", string2str(ft->chip.path));
942 printf(PLUGINSD_KEYWORD_CLABEL " subsystem '%s' 1\n", string2str(ft->chip.subsystem));
943 printf(PLUGINSD_KEYWORD_CLABEL " driver '%s' 1\n", string2str(ft->chip.driver));
944
945 // printf(
946 // PLUGINSD_KEYWORD_CLABEL " sensor '%s - %s' 1\n",
947 // string2str(ft->chip.name),
948 // string2str(ft->feature.label));
949
950 printf(PLUGINSD_KEYWORD_CLABEL_COMMIT "\n");
951 }
952
953 static size_t states_count(SENSOR_STATE state) {
954 // the gcc way
955 return __builtin_popcount(state);
956
957 // size_t count = 0;
958 // while (state) {
959 // state &= (state - 1); // Clear the least significant set bit
960 // count++;
961 // }
962 // return count;
963 }
964
965 static void sensor_process(SENSOR *s, int update_every, const char *name) {
966 // evaluate the state of the feature
967 set_sensor_state(s);
968 internal_fatal(s->state == 0,
969 "SENSORS: state %u is not a valid state", s->state);
970
971 internal_fatal((s->state & s->supported_states) == 0,
972 "SENSORS: state %u is not in the supported list of states %u",
973 s->state,
974 s->supported_states);
975
976 bool do_input = s->config.report_value && !isnan(s->input);
977 bool do_average = s->config.report_value && !isnan(s->average);
978 bool do_state = s->config.report_state && states_count(s->supported_states) > 1;
979
980 // send the feature data to netdata
981 if(do_input && !s->exposed_input) {
982 printf(
983 PLUGINSD_KEYWORD_CHART " 'sensors.%s_input' '' '%s' '%s' '%s' '%s.input' line %d %d '' debugfs %s\n",
984 string2str(s->id),
985 s->config.title,
986 s->config.units,
987 s->config.family,
988 s->config.context,
989 s->config.priority,
990 update_every,
991 name);
992
993 printf(PLUGINSD_KEYWORD_DIMENSION " input '' absolute 1 10000 ''\n");
994 sensor_labels(s);
995 s->exposed_input = true;
996 }
997
998 if(do_average && !s->exposed_average) {
999 printf(
1000 PLUGINSD_KEYWORD_CHART " 'sensors.%s_average' '' '%s Average' '%s' '%s' '%s.average' line %d %d '' debugfs %s\n",
1001 string2str(s->id),
1002 s->config.title,
1003 s->config.units,
1004 s->config.family,
1005 s->config.context,
1006 s->config.priority + 1,
1007 update_every,
1008 name);
1009
1010 printf(PLUGINSD_KEYWORD_DIMENSION " average '' absolute 1 10000 ''\n");
1011 sensor_labels(s);
1012 s->exposed_average = true;
1013 }
1014
1015 if(do_state && s->exposed_states != s->supported_states) {
1016 printf(
1017 PLUGINSD_KEYWORD_CHART " 'sensors.%s_alarm' '' '%s Alarm Status' '%s' '%s' '%s.alarm' line %d %d '' debugfs %s\n",
1018 string2str(s->id),
1019 s->config.title,
1020 "status",
1021 s->config.family,
1022 s->config.context,
1023 s->config.priority + 2,
1024 update_every,
1025 name);
1026
1027 if(s->supported_states & SENSOR_STATE_CLEAR)
1028 printf(PLUGINSD_KEYWORD_DIMENSION " clear '' absolute 1 1 ''\n");
1029 if(s->supported_states & SENSOR_STATE_WARNING)
1030 printf(PLUGINSD_KEYWORD_DIMENSION " warning '' absolute 1 1 ''\n");
1031 if(s->supported_states & SENSOR_STATE_CAP)
1032 printf(PLUGINSD_KEYWORD_DIMENSION " cap '' absolute 1 1 ''\n");
1033 if(s->supported_states & SENSOR_STATE_ALARM)
1034 printf(PLUGINSD_KEYWORD_DIMENSION " alarm '' absolute 1 1 ''\n");
1035 if(s->supported_states & SENSOR_STATE_CRITICAL)
1036 printf(PLUGINSD_KEYWORD_DIMENSION " critical '' absolute 1 1 ''\n");
1037 if(s->supported_states & SENSOR_STATE_EMERGENCY)
1038 printf(PLUGINSD_KEYWORD_DIMENSION " emergency '' absolute 1 1 ''\n");
1039 if(s->supported_states & SENSOR_STATE_FAULT)
1040 printf(PLUGINSD_KEYWORD_DIMENSION " fault '' absolute 1 1 ''\n");
1041
1042 sensor_labels(s);
1043 s->exposed_states = s->supported_states;
1044 }
1045
1046 #if 0
1047 // ----------------------------------------------------------------------------------------------------------------
1048 // debugging
1049
1050 fprintf(stderr,
1051 "SENSORS: "
1052 "{ chip id '%s', name '%s', addr %d }, "
1053 "{ adapter '%s', bus '%s', path '%s'}, "
1054 "{ feature label '%s', name '%s', type '%s' }\n",
1055 string2str(s->chip.id),
1056 string2str(s->chip.driver),
1057 s->chip.addr,
1058 string2str(s->chip.adapter),
1059 SENSOR_BUS_TYPE_2str(s->chip.bus),
1060 string2str(s->chip.path),
1061 string2str(s->feature.label),
1062 string2str(s->feature.name),
1063 SENSOR_TYPE_2str(s->feature.type));
1064
1065 Word_t idx = 0;
1066 for(SUBFEATURE *sft = SUBFEATURES_FIRST(&s->values, &idx);
1067 sft;
1068 sft = SUBFEATURES_NEXT(&s->values, &idx)) {
1069 fprintf(stderr,
1070 " ------------ >>> "
1071 "{ subfeature '%s', type '%s' } "
1072 "value %f, %s\n",
1073 string2str(sft->name), SENSOR_SUBFEATURE_TYPE_2str(idx),
1074 sft->value, sft->read ? "OK" : "FAILED");
1075 }
1076
1077 if(do_input)
1078 fprintf(stderr, " ------------ >>> %f (input)\n", s->input);
1079
1080 if(do_average)
1081 fprintf(stderr, " ------------ >>> %f (average)\n", s->average);
1082
1083 if(do_state)
1084 fprintf(stderr, " ------------ >>> %u (state)\n", s->state);
1085 #endif
1086
1087 // ----------------------------------------------------------------------------------------------------------------
1088 // send the data
1089
1090 if(do_input) {
1091 printf(PLUGINSD_KEYWORD_BEGIN " 'sensors.%s_input'\n", string2str(s->id));
1092
1093 printf(PLUGINSD_KEYWORD_SET " input = %lld\n", (long long)(s->input * 10000.0));
1094 printf(PLUGINSD_KEYWORD_END "\n");
1095 }
1096
1097 if(do_average) {
1098 printf(PLUGINSD_KEYWORD_BEGIN " 'sensors.%s_average'\n", string2str(s->id));
1099
1100 printf(PLUGINSD_KEYWORD_SET " average = %lld\n", (long long)(s->average * 10000.0));
1101 printf(PLUGINSD_KEYWORD_END "\n");
1102 }
1103
1104 if(do_state) {
1105 printf(
1106 PLUGINSD_KEYWORD_BEGIN " 'sensors.%s_alarm'\n",
1107 string2str(s->id));
1108
1109 if(s->supported_states & SENSOR_STATE_CLEAR)
1110 printf(PLUGINSD_KEYWORD_SET " clear = %d\n", s->state == SENSOR_STATE_CLEAR ? 1 : 0);
1111 if(s->supported_states & SENSOR_STATE_WARNING)
1112 printf(PLUGINSD_KEYWORD_SET " warning = %d\n", s->state == SENSOR_STATE_WARNING ? 1 : 0);
1113 if(s->supported_states & SENSOR_STATE_CAP)
1114 printf(PLUGINSD_KEYWORD_SET " cap = %d\n", s->state == SENSOR_STATE_CAP ? 1 : 0);
1115 if(s->supported_states & SENSOR_STATE_ALARM)
1116 printf(PLUGINSD_KEYWORD_SET " alarm = %d\n", s->state == SENSOR_STATE_ALARM ? 1 : 0);
1117 if(s->supported_states & SENSOR_STATE_CRITICAL)
1118 printf(PLUGINSD_KEYWORD_SET " critical = %d\n", s->state == SENSOR_STATE_CRITICAL ? 1 : 0);
1119 if(s->supported_states & SENSOR_STATE_EMERGENCY)
1120 printf(PLUGINSD_KEYWORD_SET " emergency = %d\n", s->state == SENSOR_STATE_EMERGENCY ? 1 : 0);
1121 if(s->supported_states & SENSOR_STATE_FAULT)
1122 printf(PLUGINSD_KEYWORD_SET " fault = %d\n", s->state == SENSOR_STATE_FAULT ? 1 : 0);
1123
1124 printf(PLUGINSD_KEYWORD_END "\n");
1125 }
1126 }
1127
1128 static FILE *sensors_open_file(const char *env_var, const char *def_dir, const char *file) {
1129 const char *dir = getenv(env_var);
1130 if(!dir || !*dir)
1131 dir = def_dir;
1132
1133 if (dir && *dir) {
1134 char filename[FILENAME_MAX];
1135 snprintfz(filename, sizeof(filename), "%s/%s", dir, file);
1136 return fopen(filename, "r");
1137 }
1138
1139 return NULL;
1140 }
1141
1142 static DICTIONARY *sensors_dict = NULL;
1143
1144 static int sensors_collect_data(void) {
1145 // ----------------------------------------------------------------------------------------------------------------
1146 // reset all sensors to unread
1147
1148 SENSOR *s;
1149 dfe_start_read(sensors_dict, s) {
1150 s->read = false;
1151 }
1152 dfe_done(s);
1153
1154 // ----------------------------------------------------------------------------------------------------------------
1155 // Iterate over all detected chips
1156
1157 size_t subfeatures_collected = 0;
1158
1159 const sensors_chip_name *chip;
1160 int chip_nr = 0;
1161 while ((chip = sensors_get_detected_chips(NULL, &chip_nr)) != NULL) {
1162
1163 // Iterate over all features of the chip
1164 const sensors_feature *feature;
1165 int feature_nr = 0;
1166 while ((feature = sensors_get_features(chip, &feature_nr)) != NULL) {
1167 s = sensor_get_or_create(sensors_dict, chip, feature);
1168 if(!s) continue;
1169
1170 internal_fatal(s->read, "The features key is not unique!");
1171 s->read = true;
1172
1173 // --------------------------------------------------------------------------------------------------------
1174 // mark all existing subfeatures as unread
1175
1176 Word_t idx = 0;
1177 for(SUBFEATURE *sf = SUBFEATURES_FIRST(&s->values, &idx); sf; sf = SUBFEATURES_NEXT(&s->values, &idx)) {
1178 sf->read = false;
1179 sf->value = NAN;
1180 }
1181
1182 // --------------------------------------------------------------------------------------------------------
1183 // iterate over all subfeatures of the feature
1184
1185 const sensors_subfeature *subfeature;
1186 int subfeature_nr = 0;
1187 while ((subfeature = sensors_get_all_subfeatures(chip, feature, &subfeature_nr)) != NULL) {
1188 if(!(subfeature->flags & SENSORS_MODE_R) || // not readable
1189 !sensor_subfeature_needed(s, subfeature->type)) // we don't need it
1190 continue;
1191
1192 SUBFEATURE *sft = SUBFEATURES_GET(&s->values, subfeature->type);
1193 if(!sft) {
1194 sft = callocz(1, sizeof(*sft));
1195 sft->name = string_strdupz(subfeature->name);
1196 SUBFEATURES_SET(&s->values, subfeature->type, sft);
1197 }
1198
1199 if (sensors_get_value(chip, subfeature->number, &sft->value) == 0) {
1200 sft->read = true;
1201 subfeatures_collected++;
1202 }
1203 else {
1204 sft->value = NAN;
1205 sft->read = false;
1206 }
1207 }
1208 }
1209 }
1210
1211 return subfeatures_collected ? 0 : 1;
1212 }
1213
1214 static bool libsensors_running = false;
1215 static int libsensors_update_every = 1;
1216
1217 void libsensors_thread(void *ptr __maybe_unused) {
1218 int update_every = libsensors_update_every;
1219
1220 FILE *fp = NULL;
1221 if(access("/etc/sensors3.conf", R_OK) != 0 &&
1222 access("/etc/sensors.conf", R_OK) != 0 &&
1223 access("/etc/sensors.d", R_OK | X_OK) != 0) {
1224 fp = sensors_open_file("NETDATA_CONFIG_DIR", CONFIG_DIR, "../sensors3.conf");
1225 if(!fp) fp = sensors_open_file("NETDATA_CONFIG_DIR", CONFIG_DIR, "sensors3.conf");
1226 if(!fp) fp = sensors_open_file("NETDATA_STOCK_CONFIG_DIR", LIBCONFIG_DIR, "sensors3.conf");
1227 }
1228
1229 if (sensors_init(fp) != 0) {
1230 nd_log(NDLS_COLLECTORS, NDLP_ERR, "cannot initialize libsensors - disabling sensors monitoring");
1231 if(fp) fclose(fp);
1232 goto cleanup;
1233 }
1234 if(fp) fclose(fp);
1235
1236 sensors_dict = dictionary_create_advanced(DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_SINGLE_THREADED | DICT_OPTION_FIXED_SIZE, NULL, sizeof(SENSOR));
1237
1238 // preflight to check data collection latency
1239 {
1240 SENSOR *s;
1241
1242 sensors_collect_data(); // do the first before starting measurements
1243 dfe_start_read(sensors_dict, s) { set_sensor_state(s); } dfe_done(s);
1244
1245 usec_t max_ut = 0;
1246 size_t samples = 0;
1247 usec_t started_ut = now_monotonic_usec();
1248 for(size_t i = 0; i < 5; i++) {
1249 usec_t before_ut = now_monotonic_usec();
1250 sensors_collect_data();
1251 dfe_start_read(sensors_dict, s) { set_sensor_state(s); } dfe_done(s);
1252 usec_t after_ut = now_monotonic_usec();
1253 max_ut = MAX(max_ut, after_ut - before_ut);
1254 samples++;
1255 }
1256 usec_t ended_ut = now_monotonic_usec();
1257 usec_t average_ut = (ended_ut - started_ut) / samples;
1258
1259 if(average_ut < 1)
1260 average_ut = 1;
1261
1262 if(max_ut < 1)
1263 max_ut = 1;
1264
1265 // List of valid intervals in seconds (divisors and multiples of 60)
1266 static const int valid_update_every_intervals[] = {1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60, 120, 180, 240, 300, 600, 900, 1200, 1800, 3600};
1267
1268 // Find the smallest valid interval that satisfies our timing requirement
1269 int best_update_every = update_every;
1270 for(size_t i = 0; i < _countof(valid_update_every_intervals); i++) {
1271 if(valid_update_every_intervals[i] >= update_every &&
1272 max_ut <= (valid_update_every_intervals[i] * USEC_PER_SEC / 5)) {
1273 best_update_every = valid_update_every_intervals[i];
1274 break;
1275 }
1276 }
1277
1278 char avg[64], max[64];
1279 duration_snprintf(avg, sizeof(avg), (int64_t)average_ut, "us", false);
1280 duration_snprintf(max, sizeof(max), (int64_t)max_ut, "us", false);
1281
1282 nd_log(NDLS_COLLECTORS, NDLP_NOTICE,
1283 "SENSORS max data collection latency is %s (average %s), setting update_every to %ds (default is %ds)",
1284 max, avg, best_update_every, update_every);
1285
1286 update_every = best_update_every;
1287 }
1288
1289 heartbeat_t hb;
1290 heartbeat_init(&hb, update_every * USEC_PER_SEC);
1291
1292 while(!nd_thread_signaled_to_cancel()) {
1293 heartbeat_next(&hb);
1294
1295 if(sensors_collect_data())
1296 break;
1297
1298 netdata_mutex_lock(&stdout_mutex);
1299
1300 SENSOR *s;
1301 dfe_start_read(sensors_dict, s) {
1302 sensor_process(s, update_every, "sensors");
1303 }
1304 dfe_done(s);
1305
1306 fflush(stdout);
1307 netdata_mutex_unlock(&stdout_mutex);
1308 }
1309
1310 cleanup:
1311 libsensors_running = false;
1312
1313 dictionary_destroy(sensors_dict);
1314 sensors_dict = NULL;
1315 }
1316
1317 static ND_THREAD *libsensors = NULL;
1318 int do_module_libsensors(int update_every, const char *name __maybe_unused) {
1319 if(!libsensors) {
1320 libsensors_update_every = update_every;
1321 libsensors_running = true;
1322 libsensors = nd_thread_create("LIBSENSORS", NETDATA_THREAD_OPTION_DEFAULT, libsensors_thread, NULL);
1323 }
1324
1325 return libsensors && libsensors_running ? 0 : 1;
1326 }
1327
1328 void module_libsensors_cleanup(void) {
1329 nd_thread_signal_cancel(libsensors);
1330 nd_thread_join(libsensors);
1331 }