Merge branch 'collectd-5.4' into collectd-5.5
[collectd.git] / src / sensors.c
1 /**
2  * collectd - src/sensors.c
3  * Copyright (C) 2005-2008  Florian octo Forster
4  * Copyright (C) 2006       Luboš Staněk
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; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at collectd.org>
21  *   
22  *   Lubos Stanek <lubek at users.sourceforge.net> Wed Oct 27, 2006
23  *   - config ExtendedSensorNaming option
24  *   - precise sensor feature selection (chip-bus-address/type-feature)
25  *     with ExtendedSensorNaming
26  *   - more sensor features (finite list)
27  *   - honor sensors.conf's ignored
28  *   - config Sensor option
29  *   - config IgnoreSelected option
30  *
31  *   Henrique de Moraes Holschuh <hmh at debian.org>
32  *   - use default libsensors config file on API 0x400
33  *   - config SensorConfigFile option
34  **/
35
36 #include "collectd.h"
37 #include "common.h"
38 #include "plugin.h"
39 #include "configfile.h"
40 #include "utils_ignorelist.h"
41
42 #if defined(HAVE_SENSORS_SENSORS_H)
43 # include <sensors/sensors.h>
44 #endif
45
46 #if !defined(SENSORS_API_VERSION)
47 # define SENSORS_API_VERSION 0x000
48 #endif
49
50 /*
51  * The sensors library prior to version 3.0 (internal version 0x400) didn't
52  * report the type of values, only a name. The following lists are there to
53  * convert from the names to the type. They are not used with the new
54  * interface.
55  */
56 #if SENSORS_API_VERSION < 0x400
57 static char *sensor_type_name_map[] =
58 {
59 # define SENSOR_TYPE_VOLTAGE     0
60         "voltage",
61 # define SENSOR_TYPE_FANSPEED    1
62         "fanspeed",
63 # define SENSOR_TYPE_TEMPERATURE 2
64         "temperature",
65 # define SENSOR_TYPE_POWER       3
66         "power",
67 # define SENSOR_TYPE_UNKNOWN     4
68         NULL
69 };
70
71 struct sensors_labeltypes_s
72 {
73         char *label;
74         int type;
75 };
76 typedef struct sensors_labeltypes_s sensors_labeltypes_t;
77
78 /* finite list of known labels extracted from lm_sensors */
79 static sensors_labeltypes_t known_features[] = 
80 {
81         { "fan1", SENSOR_TYPE_FANSPEED },
82         { "fan2", SENSOR_TYPE_FANSPEED },
83         { "fan3", SENSOR_TYPE_FANSPEED },
84         { "fan4", SENSOR_TYPE_FANSPEED },
85         { "fan5", SENSOR_TYPE_FANSPEED },
86         { "fan6", SENSOR_TYPE_FANSPEED },
87         { "fan7", SENSOR_TYPE_FANSPEED },
88         { "AIN2", SENSOR_TYPE_VOLTAGE },
89         { "AIN1", SENSOR_TYPE_VOLTAGE },
90         { "in10", SENSOR_TYPE_VOLTAGE },
91         { "in9", SENSOR_TYPE_VOLTAGE },
92         { "in8", SENSOR_TYPE_VOLTAGE },
93         { "in7", SENSOR_TYPE_VOLTAGE },
94         { "in6", SENSOR_TYPE_VOLTAGE },
95         { "in5", SENSOR_TYPE_VOLTAGE },
96         { "in4", SENSOR_TYPE_VOLTAGE },
97         { "in3", SENSOR_TYPE_VOLTAGE },
98         { "in2", SENSOR_TYPE_VOLTAGE },
99         { "in0", SENSOR_TYPE_VOLTAGE },
100         { "CPU_Temp", SENSOR_TYPE_TEMPERATURE },
101         { "remote_temp", SENSOR_TYPE_TEMPERATURE },
102         { "temp1", SENSOR_TYPE_TEMPERATURE },
103         { "temp2", SENSOR_TYPE_TEMPERATURE },
104         { "temp3", SENSOR_TYPE_TEMPERATURE },
105         { "temp4", SENSOR_TYPE_TEMPERATURE },
106         { "temp5", SENSOR_TYPE_TEMPERATURE },
107         { "temp6", SENSOR_TYPE_TEMPERATURE },
108         { "temp7", SENSOR_TYPE_TEMPERATURE },
109         { "temp", SENSOR_TYPE_TEMPERATURE },
110         { "Vccp2", SENSOR_TYPE_VOLTAGE },
111         { "Vccp1", SENSOR_TYPE_VOLTAGE },
112         { "vdd", SENSOR_TYPE_VOLTAGE },
113         { "vid5", SENSOR_TYPE_VOLTAGE },
114         { "vid4", SENSOR_TYPE_VOLTAGE },
115         { "vid3", SENSOR_TYPE_VOLTAGE },
116         { "vid2", SENSOR_TYPE_VOLTAGE },
117         { "vid1", SENSOR_TYPE_VOLTAGE },
118         { "vid", SENSOR_TYPE_VOLTAGE },
119         { "vin4", SENSOR_TYPE_VOLTAGE },
120         { "vin3", SENSOR_TYPE_VOLTAGE },
121         { "vin2", SENSOR_TYPE_VOLTAGE },
122         { "vin1", SENSOR_TYPE_VOLTAGE },
123         { "voltbatt", SENSOR_TYPE_VOLTAGE },
124         { "volt12", SENSOR_TYPE_VOLTAGE },
125         { "volt5", SENSOR_TYPE_VOLTAGE },
126         { "vrm", SENSOR_TYPE_VOLTAGE },
127         { "5.0V", SENSOR_TYPE_VOLTAGE },
128         { "5V", SENSOR_TYPE_VOLTAGE },
129         { "3.3V", SENSOR_TYPE_VOLTAGE },
130         { "2.5V", SENSOR_TYPE_VOLTAGE },
131         { "2.0V", SENSOR_TYPE_VOLTAGE },
132         { "12V", SENSOR_TYPE_VOLTAGE },
133         { "power1", SENSOR_TYPE_POWER }
134 };
135 static int known_features_num = STATIC_ARRAY_SIZE (known_features);
136 /* end new naming */
137 #endif /* SENSORS_API_VERSION < 0x400 */
138
139 static const char *config_keys[] =
140 {
141         "Sensor",
142         "IgnoreSelected",
143         "SensorConfigFile"
144 };
145 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
146
147 #if SENSORS_API_VERSION < 0x400
148 typedef struct featurelist
149 {
150         const sensors_chip_name    *chip;
151         const sensors_feature_data *data;
152         int                         type;
153         struct featurelist         *next;
154 } featurelist_t;
155
156 # ifndef SENSORS_CONF_PATH
157 #  define SENSORS_CONF_PATH "/etc/sensors.conf"
158 # endif
159 static char *conffile = SENSORS_CONF_PATH;
160 /* #endif SENSORS_API_VERSION < 0x400 */
161
162 #elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500)
163 typedef struct featurelist
164 {
165         const sensors_chip_name    *chip;
166         const sensors_feature      *feature;
167         const sensors_subfeature   *subfeature;
168         struct featurelist         *next;
169 } featurelist_t;
170
171 static char *conffile = NULL;
172 /* #endif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */
173
174 #else /* if SENSORS_API_VERSION >= 0x500 */
175 # error "This version of libsensors is not supported yet. Please report this " \
176         "as bug."
177 #endif
178
179 featurelist_t *first_feature = NULL;
180 static ignorelist_t *sensor_list;
181
182 #if SENSORS_API_VERSION < 0x400
183 /* full chip name logic borrowed from lm_sensors */
184 static int sensors_snprintf_chip_name (char *buf, size_t buf_size,
185                 const sensors_chip_name *chip)
186 {
187         int status = -1;
188
189         if (chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
190         {
191                 status = ssnprintf (buf, buf_size,
192                                 "%s-isa-%04x",
193                                 chip->prefix,
194                                 chip->addr);
195         }
196         else if (chip->bus == SENSORS_CHIP_NAME_BUS_DUMMY)
197         {
198                 status = snprintf (buf, buf_size, "%s-%s-%04x",
199                                 chip->prefix,
200                                 chip->busname,
201                                 chip->addr);
202         }
203         else
204         {
205                 status = snprintf (buf, buf_size, "%s-i2c-%d-%02x",
206                                 chip->prefix,
207                                 chip->bus,
208                                 chip->addr);
209         }
210
211         return (status);
212 } /* int sensors_snprintf_chip_name */
213
214 static int sensors_feature_name_to_type (const char *name)
215 {
216         int i;
217
218         /* Yes, this is slow, but it's only ever done during initialization, so
219          * it's a one time cost.. */
220         for (i = 0; i < known_features_num; i++)
221                 if (strcasecmp (known_features[i].label, name) == 0)
222                         return (known_features[i].type);
223
224         return (SENSOR_TYPE_UNKNOWN);
225 } /* int sensors_feature_name_to_type */
226 #endif
227
228 static int sensors_config (const char *key, const char *value)
229 {
230         if (sensor_list == NULL)
231                 sensor_list = ignorelist_create (1);
232
233         /* TODO: This setting exists for compatibility with old versions of
234          * lm-sensors. Remove support for those ancient versions in the next
235          * major release. */
236         if (strcasecmp (key, "SensorConfigFile") == 0)
237         {
238                 char *tmp = strdup (value);
239                 if (tmp != NULL)
240                 {
241                         sfree (conffile);
242                         conffile = tmp;
243                 }
244         }
245         else if (strcasecmp (key, "Sensor") == 0)
246         {
247                 if (ignorelist_add (sensor_list, value))
248                 {
249                         ERROR ("sensors plugin: "
250                                         "Cannot add value to ignorelist.");
251                         return (1);
252                 }
253         }
254         else if (strcasecmp (key, "IgnoreSelected") == 0)
255         {
256                 ignorelist_set_invert (sensor_list, 1);
257                 if (IS_TRUE (value))
258                         ignorelist_set_invert (sensor_list, 0);
259         }
260         else
261         {
262                 return (-1);
263         }
264
265         return (0);
266 }
267
268 static void sensors_free_features (void)
269 {
270         featurelist_t *thisft;
271         featurelist_t *nextft;
272
273         if (first_feature == NULL)
274                 return;
275
276         sensors_cleanup ();
277
278         for (thisft = first_feature; thisft != NULL; thisft = nextft)
279         {
280                 nextft = thisft->next;
281                 sfree (thisft);
282         }
283         first_feature = NULL;
284 }
285
286 static int sensors_load_conf (void)
287 {
288         static int call_once = 0;
289
290         FILE *fh = NULL;
291         featurelist_t *last_feature = NULL;
292         
293         const sensors_chip_name *chip;
294         int chip_num;
295
296         int status;
297
298         if (call_once)
299                 return 0;
300
301         call_once = 1;
302
303         if (conffile != NULL)
304         {
305                 fh = fopen (conffile, "r");
306                 if (fh == NULL)
307                 {
308                         char errbuf[1024];
309                         ERROR ("sensors plugin: fopen(%s) failed: %s", conffile,
310                                         sstrerror (errno, errbuf, sizeof (errbuf)));
311                         return (-1);
312                 }
313         }
314
315         status = sensors_init (fh);
316         if (fh)
317                 fclose (fh);
318
319         if (status != 0)
320         {
321                 ERROR ("sensors plugin: Cannot initialize sensors. "
322                                 "Data will not be collected.");
323                 return (-1);
324         }
325
326 #if SENSORS_API_VERSION < 0x400
327         chip_num = 0;
328         while ((chip = sensors_get_detected_chips (&chip_num)) != NULL)
329         {
330                 int feature_num0 = 0;
331                 int feature_num1 = 0;
332
333                 while (42)
334                 {
335                         const sensors_feature_data *feature;
336                         int feature_type;
337                         featurelist_t *fl;
338
339                         feature = sensors_get_all_features (*chip,
340                                         &feature_num0, &feature_num1);
341
342                         /* Check if all features have been read. */
343                         if (feature == NULL)
344                                 break;
345
346                         /* "master features" only */
347                         if (feature->mapping != SENSORS_NO_MAPPING)
348                         {
349                                 DEBUG ("sensors plugin: sensors_load_conf: "
350                                                 "Ignoring subfeature `%s', "
351                                                 "because (feature->mapping "
352                                                 "!= SENSORS_NO_MAPPING).",
353                                                 feature->name);
354                                 continue;
355                         }
356
357                         /* skip ignored in sensors.conf */
358                         if (sensors_get_ignored (*chip, feature->number) == 0)
359                         {
360                                 DEBUG ("sensors plugin: sensors_load_conf: "
361                                                 "Ignoring subfeature `%s', "
362                                                 "because "
363                                                 "`sensors_get_ignored' told "
364                                                 "me so.",
365                                                 feature->name);
366                                 continue;
367                         }
368
369                         feature_type = sensors_feature_name_to_type (
370                                         feature->name);
371                         if (feature_type == SENSOR_TYPE_UNKNOWN)
372                         {
373                                 DEBUG ("sensors plugin: sensors_load_conf: "
374                                                 "Ignoring subfeature `%s', "
375                                                 "because its type is "
376                                                 "unknown.",
377                                                 feature->name);
378                                 continue;
379                         }
380
381                         fl = (featurelist_t *) malloc (sizeof (featurelist_t));
382                         if (fl == NULL)
383                         {
384                                 ERROR ("sensors plugin: malloc failed.");
385                                 continue;
386                         }
387                         memset (fl, '\0', sizeof (featurelist_t));
388
389                         fl->chip = chip;
390                         fl->data = feature;
391                         fl->type = feature_type;
392
393                         if (first_feature == NULL)
394                                 first_feature = fl;
395                         else
396                                 last_feature->next = fl;
397                         last_feature = fl;
398                 } /* while sensors_get_all_features */
399         } /* while sensors_get_detected_chips */
400 /* #endif SENSORS_API_VERSION < 0x400 */
401
402 #elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500)
403         chip_num = 0;
404         while ((chip = sensors_get_detected_chips (NULL, &chip_num)) != NULL)
405         {
406                 const sensors_feature *feature;
407                 int feature_num = 0;
408
409                 while ((feature = sensors_get_features (chip, &feature_num)) != NULL)
410                 {
411                         const sensors_subfeature *subfeature;
412                         int subfeature_num = 0;
413
414                         /* Only handle voltage, fanspeeds and temperatures */
415                         if ((feature->type != SENSORS_FEATURE_IN)
416                                         && (feature->type != SENSORS_FEATURE_FAN)
417                                         && (feature->type != SENSORS_FEATURE_TEMP)
418                                         && (feature->type != SENSORS_FEATURE_POWER))
419                         {
420                                 DEBUG ("sensors plugin: sensors_load_conf: "
421                                                 "Ignoring feature `%s', "
422                                                 "because its type is not "
423                                                 "supported.", feature->name);
424                                 continue;
425                         }
426
427                         while ((subfeature = sensors_get_all_subfeatures (chip,
428                                                         feature, &subfeature_num)) != NULL)
429                         {
430                                 featurelist_t *fl;
431
432                                 if ((subfeature->type != SENSORS_SUBFEATURE_IN_INPUT)
433                                                 && (subfeature->type != SENSORS_SUBFEATURE_FAN_INPUT)
434                                                 && (subfeature->type != SENSORS_SUBFEATURE_TEMP_INPUT)
435                                                 && (subfeature->type != SENSORS_SUBFEATURE_POWER_INPUT))
436                                         continue;
437
438                                 fl = (featurelist_t *) malloc (sizeof (featurelist_t));
439                                 if (fl == NULL)
440                                 {
441                                         ERROR ("sensors plugin: malloc failed.");
442                                         continue;
443                                 }
444                                 memset (fl, '\0', sizeof (featurelist_t));
445
446                                 fl->chip = chip;
447                                 fl->feature = feature;
448                                 fl->subfeature = subfeature;
449
450                                 if (first_feature == NULL)
451                                         first_feature = fl;
452                                 else
453                                         last_feature->next = fl;
454                                 last_feature  = fl;
455                         } /* while (subfeature) */
456                 } /* while (feature) */
457         } /* while (chip) */
458 #endif /* (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */
459
460         if (first_feature == NULL)
461         {
462                 sensors_cleanup ();
463                 INFO ("sensors plugin: lm_sensors reports no "
464                                 "features. Data will not be collected.");
465                 return (-1);
466         }
467
468         return (0);
469 } /* int sensors_load_conf */
470
471 static int sensors_shutdown (void)
472 {
473         sensors_free_features ();
474         ignorelist_free (sensor_list);
475
476         return (0);
477 } /* int sensors_shutdown */
478
479 static void sensors_submit (const char *plugin_instance,
480                 const char *type, const char *type_instance,
481                 double val)
482 {
483         char match_key[1024];
484         int status;
485
486         value_t values[1];
487         value_list_t vl = VALUE_LIST_INIT;
488
489         status = ssnprintf (match_key, sizeof (match_key), "%s/%s-%s",
490                         plugin_instance, type, type_instance);
491         if (status < 1)
492                 return;
493
494         if (sensor_list != NULL)
495         {
496                 DEBUG ("sensors plugin: Checking ignorelist for `%s'", match_key);
497                 if (ignorelist_match (sensor_list, match_key))
498                         return;
499         }
500
501         values[0].gauge = val;
502
503         vl.values = values;
504         vl.values_len = 1;
505
506         sstrncpy (vl.host, hostname_g, sizeof (vl.host));
507         sstrncpy (vl.plugin, "sensors", sizeof (vl.plugin));
508         sstrncpy (vl.plugin_instance, plugin_instance,
509                         sizeof (vl.plugin_instance));
510         sstrncpy (vl.type, type, sizeof (vl.type));
511         sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
512
513         plugin_dispatch_values (&vl);
514 } /* void sensors_submit */
515
516 static int sensors_read (void)
517 {
518         featurelist_t *fl;
519
520         if (sensors_load_conf () != 0)
521                 return (-1);
522
523 #if SENSORS_API_VERSION < 0x400
524         for (fl = first_feature; fl != NULL; fl = fl->next)
525         {
526                 double value;
527                 int status;
528                 char plugin_instance[DATA_MAX_NAME_LEN];
529                 char type_instance[DATA_MAX_NAME_LEN];
530
531                 status = sensors_get_feature (*fl->chip,
532                                 fl->data->number, &value);
533                 if (status < 0)
534                         continue;
535
536                 status = sensors_snprintf_chip_name (plugin_instance,
537                                 sizeof (plugin_instance), fl->chip);
538                 if (status < 0)
539                         continue;
540
541                 sstrncpy (type_instance, fl->data->name,
542                                 sizeof (type_instance));
543
544                 sensors_submit (plugin_instance,
545                                 sensor_type_name_map[fl->type],
546                                 type_instance,
547                                 value);
548         } /* for fl = first_feature .. NULL */
549 /* #endif SENSORS_API_VERSION < 0x400 */
550
551 #elif (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500)
552         for (fl = first_feature; fl != NULL; fl = fl->next)
553         {
554                 double value;
555                 int status;
556                 char plugin_instance[DATA_MAX_NAME_LEN];
557                 char type_instance[DATA_MAX_NAME_LEN];
558                 const char *type;
559
560                 status = sensors_get_value (fl->chip,
561                                 fl->subfeature->number, &value);
562                 if (status < 0)
563                         continue;
564
565                 status = sensors_snprintf_chip_name (plugin_instance,
566                                 sizeof (plugin_instance), fl->chip);
567                 if (status < 0)
568                         continue;
569
570                 sstrncpy (type_instance, fl->feature->name,
571                                 sizeof (type_instance));
572
573                 if (fl->feature->type == SENSORS_FEATURE_IN)
574                         type = "voltage";
575                 else if (fl->feature->type
576                                 == SENSORS_FEATURE_FAN)
577                         type = "fanspeed";
578                 else if (fl->feature->type
579                                 == SENSORS_FEATURE_TEMP)
580                         type = "temperature";
581                 else if (fl->feature->type
582                                 == SENSORS_FEATURE_POWER)
583                         type = "power";
584                 else
585                         continue;
586
587                 sensors_submit (plugin_instance, type, type_instance, value);
588         } /* for fl = first_feature .. NULL */
589 #endif /* (SENSORS_API_VERSION >= 0x400) && (SENSORS_API_VERSION < 0x500) */
590
591         return (0);
592 } /* int sensors_read */
593
594 void module_register (void)
595 {
596         plugin_register_config ("sensors", sensors_config,
597                         config_keys, config_keys_num);
598         plugin_register_read ("sensors", sensors_read);
599         plugin_register_shutdown ("sensors", sensors_shutdown);
600 } /* void module_register */