Merge branch 'collectd-4.0'
[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 #define SENSOR_TYPE_VOLTAGE     0
42 #define SENSOR_TYPE_FANSPEED    1
43 #define SENSOR_TYPE_TEMPERATURE 2
44 #define SENSOR_TYPE_UNKNOWN     3
45
46 static char *sensor_to_type[] =
47 {
48         "voltage",
49         "fanspeed",
50         "temperature",
51         NULL
52 };
53
54 struct sensors_labeltypes_s
55 {
56         char *label;
57         int type;
58 };
59 typedef struct sensors_labeltypes_s sensors_labeltypes_t;
60
61 /*
62  * finite list of known labels extracted from lm_sensors
63  */
64 static sensors_labeltypes_t known_features[] = 
65 {
66         { "fan1", SENSOR_TYPE_FANSPEED },
67         { "fan2", SENSOR_TYPE_FANSPEED },
68         { "fan3", SENSOR_TYPE_FANSPEED },
69         { "fan4", SENSOR_TYPE_FANSPEED },
70         { "fan5", SENSOR_TYPE_FANSPEED },
71         { "fan6", SENSOR_TYPE_FANSPEED },
72         { "fan7", SENSOR_TYPE_FANSPEED },
73         { "AIN2", SENSOR_TYPE_VOLTAGE },
74         { "AIN1", SENSOR_TYPE_VOLTAGE },
75         { "in10", SENSOR_TYPE_VOLTAGE },
76         { "in9", SENSOR_TYPE_VOLTAGE },
77         { "in8", SENSOR_TYPE_VOLTAGE },
78         { "in7", SENSOR_TYPE_VOLTAGE },
79         { "in6", SENSOR_TYPE_VOLTAGE },
80         { "in5", SENSOR_TYPE_VOLTAGE },
81         { "in4", SENSOR_TYPE_VOLTAGE },
82         { "in3", SENSOR_TYPE_VOLTAGE },
83         { "in2", SENSOR_TYPE_VOLTAGE },
84         { "in0", SENSOR_TYPE_VOLTAGE },
85         { "CPU_Temp", SENSOR_TYPE_TEMPERATURE },
86         { "remote_temp", SENSOR_TYPE_TEMPERATURE },
87         { "temp1", SENSOR_TYPE_TEMPERATURE },
88         { "temp2", SENSOR_TYPE_TEMPERATURE },
89         { "temp3", SENSOR_TYPE_TEMPERATURE },
90         { "temp4", SENSOR_TYPE_TEMPERATURE },
91         { "temp5", SENSOR_TYPE_TEMPERATURE },
92         { "temp6", SENSOR_TYPE_TEMPERATURE },
93         { "temp7", SENSOR_TYPE_TEMPERATURE },
94         { "temp", SENSOR_TYPE_TEMPERATURE },
95         { "Vccp2", SENSOR_TYPE_VOLTAGE },
96         { "Vccp1", SENSOR_TYPE_VOLTAGE },
97         { "vdd", SENSOR_TYPE_VOLTAGE },
98         { "vid5", SENSOR_TYPE_VOLTAGE },
99         { "vid4", SENSOR_TYPE_VOLTAGE },
100         { "vid3", SENSOR_TYPE_VOLTAGE },
101         { "vid2", SENSOR_TYPE_VOLTAGE },
102         { "vid1", SENSOR_TYPE_VOLTAGE },
103         { "vid", SENSOR_TYPE_VOLTAGE },
104         { "vin4", SENSOR_TYPE_VOLTAGE },
105         { "vin3", SENSOR_TYPE_VOLTAGE },
106         { "vin2", SENSOR_TYPE_VOLTAGE },
107         { "vin1", SENSOR_TYPE_VOLTAGE },
108         { "voltbatt", SENSOR_TYPE_VOLTAGE },
109         { "volt12", SENSOR_TYPE_VOLTAGE },
110         { "volt5", SENSOR_TYPE_VOLTAGE },
111         { "vrm", SENSOR_TYPE_VOLTAGE },
112         { "5.0V", SENSOR_TYPE_VOLTAGE },
113         { "5V", SENSOR_TYPE_VOLTAGE },
114         { "3.3V", SENSOR_TYPE_VOLTAGE },
115         { "2.5V", SENSOR_TYPE_VOLTAGE },
116         { "2.0V", SENSOR_TYPE_VOLTAGE },
117         { "12V", SENSOR_TYPE_VOLTAGE }
118 };
119 static int known_features_num = STATIC_ARRAY_SIZE (known_features);
120 /* end new naming */
121
122 static const char *config_keys[] =
123 {
124         "Sensor",
125         "IgnoreSelected",
126         NULL
127 };
128 static int config_keys_num = 2;
129
130 static ignorelist_t *sensor_list;
131
132 #ifndef SENSORS_CONF_PATH
133 # define SENSORS_CONF_PATH "/etc/sensors.conf"
134 #endif
135
136 static const char *conffile = SENSORS_CONF_PATH;
137 /* SENSORS_CONF_PATH */
138
139 /*
140  * remember stat of the loaded config
141  */
142 static time_t sensors_config_mtime = 0;
143
144 typedef struct featurelist
145 {
146         const sensors_chip_name    *chip;
147         const sensors_feature_data *data;
148         int                         type;
149         struct featurelist         *next;
150 } featurelist_t;
151
152 featurelist_t *first_feature = NULL;
153
154 static int sensors_config (const char *key, const char *value)
155 {
156         if (sensor_list == NULL)
157                 sensor_list = ignorelist_create (1);
158
159         if (strcasecmp (key, "Sensor") == 0)
160         {
161                 if (ignorelist_add (sensor_list, value))
162                 {
163                         ERROR ("sensors plugin: "
164                                         "Cannot add value to ignorelist.");
165                         return (1);
166                 }
167         }
168         else if (strcasecmp (key, "IgnoreSelected") == 0)
169         {
170                 ignorelist_set_invert (sensor_list, 1);
171                 if ((strcasecmp (value, "True") == 0)
172                                 || (strcasecmp (value, "Yes") == 0)
173                                 || (strcasecmp (value, "On") == 0))
174                         ignorelist_set_invert (sensor_list, 0);
175         }
176         else
177         {
178                 return (-1);
179         }
180
181         return (0);
182 }
183
184 void sensors_free_features (void)
185 {
186         featurelist_t *thisft;
187         featurelist_t *nextft;
188
189         if (first_feature == NULL)
190                 return;
191
192         sensors_cleanup ();
193
194         for (thisft = first_feature; thisft != NULL; thisft = nextft)
195         {
196                 nextft = thisft->next;
197                 sfree (thisft);
198         }
199         first_feature = NULL;
200 }
201
202 static int sensors_load_conf (void)
203 {
204         FILE *fh;
205         featurelist_t *last_feature = NULL;
206         featurelist_t *new_feature = NULL;
207         
208         const sensors_chip_name *chip;
209         int chip_num;
210
211         const sensors_feature_data *data;
212         int data_num0, data_num1;
213
214         struct stat statbuf;
215         int status;
216         
217         status = stat (conffile, &statbuf);
218         if (status != 0)
219         {
220                 char errbuf[1024];
221                 ERROR ("sensors plugin: stat (%s) failed: %s", conffile,
222                                 sstrerror (errno, errbuf, sizeof (errbuf)));
223                 sensors_config_mtime = 0;
224         }
225
226         if ((sensors_config_mtime != 0)
227                         && (sensors_config_mtime == statbuf.st_mtime))
228                 return (0);
229
230         if (sensors_config_mtime != 0)
231         {
232                 NOTICE ("sensors plugin: Reloading config from %s",
233                                 conffile);
234                 sensors_free_features ();
235                 sensors_config_mtime = 0;
236         }
237
238         fh = fopen (conffile, "r");
239         if (fh == NULL)
240         {
241                 char errbuf[1024];
242                 ERROR ("sensors plugin: fopen(%s) failed: %s", conffile,
243                                 sstrerror (errno, errbuf, sizeof (errbuf)));
244                 return (-1);
245         }
246
247         status = sensors_init (fh);
248         fclose (fh);
249         if (status != 0)
250         {
251                 ERROR ("sensors plugin: Cannot initialize sensors. "
252                                 "Data will not be collected.");
253                 return (-1);
254         }
255
256         sensors_config_mtime = statbuf.st_mtime;
257
258         chip_num = 0;
259         while ((chip = sensors_get_detected_chips (&chip_num)) != NULL)
260         {
261                 data = NULL;
262                 data_num0 = data_num1 = 0;
263
264                 while ((data = sensors_get_all_features (*chip, &data_num0, &data_num1))
265                                 != NULL)
266                 {
267                         int i;
268
269                         /* "master features" only */
270                         if (data->mapping != SENSORS_NO_MAPPING)
271                                 continue;
272
273                         /* Only known features */
274                         for (i = 0; i < known_features_num; i++)
275                         {
276                                 if (strcmp (data->name, known_features[i].label) != 0)
277                                         continue;
278
279                                 /* skip ignored in sensors.conf */
280                                 if (sensors_get_ignored (*chip, data->number) == 0)
281                                         break;
282
283                                 DEBUG ("Adding feature: %s-%s-%s",
284                                                 chip->prefix,
285                                                 sensor_to_type[known_features[i].type],
286                                                 data->name);
287
288                                 if ((new_feature = (featurelist_t *) malloc (sizeof (featurelist_t))) == NULL)
289                                 {
290                                         char errbuf[1024];
291                                         ERROR ("sensors plugin: malloc: %s",
292                                                         sstrerror (errno, errbuf, sizeof (errbuf)));
293                                         break;
294                                 }
295
296                                 new_feature->chip = chip;
297                                 new_feature->data = data;
298                                 new_feature->type = known_features[i].type;
299                                 new_feature->next = NULL;
300
301                                 if (first_feature == NULL)
302                                 {
303                                         first_feature = new_feature;
304                                         last_feature  = new_feature;
305                                 }
306                                 else
307                                 {
308                                         last_feature->next = new_feature;
309                                         last_feature = new_feature;
310                                 }
311
312                                 /* stop searching known features at first found */
313                                 break;
314                         } /* for i */
315                 } /* while sensors_get_all_features */
316         } /* while sensors_get_detected_chips */
317
318         if (first_feature == NULL)
319         {
320                 sensors_cleanup ();
321                 INFO ("sensors plugin: lm_sensors reports no "
322                                 "features. Data will not be collected.");
323                 return (-1);
324         }
325
326         return (0);
327 } /* int sensors_load_conf */
328
329 static int sensors_shutdown (void)
330 {
331         sensors_free_features ();
332         ignorelist_free (sensor_list);
333
334         return (0);
335 } /* int sensors_shutdown */
336
337 static void sensors_submit (const char *plugin_instance,
338                 const char *type, const char *type_instance,
339                 double val)
340 {
341         char match_key[1024];
342         int status;
343
344         value_t values[1];
345         value_list_t vl = VALUE_LIST_INIT;
346
347         status = snprintf (match_key, sizeof (match_key), "%s/%s-%s",
348                         plugin_instance, type, type_instance);
349         if ((status < 1) || (status >= sizeof (match_key)))
350                 return;
351         match_key[sizeof (match_key) - 1] = '\0';
352
353         if (sensor_list != NULL)
354         {
355                 DEBUG ("sensors plugin: Checking ignorelist for `%s'", match_key);
356                 if (ignorelist_match (sensor_list, match_key))
357                         return;
358         }
359
360         values[0].gauge = val;
361
362         vl.values = values;
363         vl.values_len = 1;
364         vl.time = time (NULL);
365         strcpy (vl.host, hostname_g);
366         strcpy (vl.plugin, "sensors");
367         strcpy (vl.plugin_instance, plugin_instance);
368         strcpy (vl.type_instance, type_instance);
369
370         plugin_dispatch_values (type, &vl);
371 } /* void sensors_submit */
372
373 static int sensors_read (void)
374 {
375         featurelist_t *feature;
376         double value;
377
378         char plugin_instance[DATA_MAX_NAME_LEN];
379         char type_instance[DATA_MAX_NAME_LEN];
380
381         if (sensors_load_conf () != 0)
382                 return (-1);
383
384         for (feature = first_feature; feature != NULL; feature = feature->next)
385         {
386                 if (sensors_get_feature (*feature->chip, feature->data->number, &value) < 0)
387                         continue;
388
389                 /* full chip name logic borrowed from lm_sensors */
390                 if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_ISA)
391                 {
392                         if (snprintf (plugin_instance, DATA_MAX_NAME_LEN, "%s-isa-%04x",
393                                                 feature->chip->prefix,
394                                                 feature->chip->addr)
395                                         >= 512)
396                                 continue;
397                 }
398                 else if (feature->chip->bus == SENSORS_CHIP_NAME_BUS_DUMMY)
399                 {
400                         if (snprintf (plugin_instance, 512, "%s-%s-%04x",
401                                                 feature->chip->prefix,
402                                                 feature->chip->busname,
403                                                 feature->chip->addr)
404                                         >= 512)
405                                 continue;
406                 }
407                 else
408                 {
409                         if (snprintf (plugin_instance, 512, "%s-i2c-%d-%02x",
410                                                 feature->chip->prefix,
411                                                 feature->chip->bus,
412                                                 feature->chip->addr)
413                                         >= 512)
414                                 continue;
415                 }
416
417                 strncpy (type_instance, feature->data->name, DATA_MAX_NAME_LEN);
418
419                 sensors_submit (plugin_instance,
420                                 sensor_to_type[feature->type],
421                                 type_instance,
422                                 value);
423         } /* for feature = first_feature .. NULL */
424
425         return (0);
426 } /* int sensors_read */
427
428 void module_register (void)
429 {
430         plugin_register_config ("sensors", sensors_config,
431                         config_keys, config_keys_num);
432         plugin_register_read ("sensors", sensors_read);
433         plugin_register_shutdown ("sensors", sensors_shutdown);
434 } /* void module_register */