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