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