/** * collectd - src/sensors.c * Copyright (C) 2005-2008 Florian octo Forster * Copyright (C) 2006 Luboš Staněk * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; only version 2 of the License is applicable. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Authors: * Florian octo Forster * * Lubos Stanek Wed Oct 27, 2006 * - config ExtendedSensorNaming option * - precise sensor feature selection (chip-bus-address/type-feature) * with ExtendedSensorNaming * - more sensor features (finite list) * - honor sensors.conf's ignored * - config Sensor option * - config IgnoreSelected option * * Henrique de Moraes Holschuh * - use default libsensors config file on API 0x400 * - config SensorConfigFile option **/ #include "collectd.h" #include "plugin.h" #include "utils/common/common.h" #include "utils/ignorelist/ignorelist.h" #if defined(HAVE_SENSORS_SENSORS_H) #include #endif #if !defined(SENSORS_API_VERSION) #define SENSORS_API_VERSION 0x000 #endif static const char *config_keys[] = {"Sensor", "IgnoreSelected", "SensorConfigFile", "UseLabels"}; static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); #if SENSORS_API_VERSION < 0x400 typedef struct featurelist { const sensors_chip_name *chip; const sensors_feature_data *data; int type; struct featurelist *next; } featurelist_t; #ifndef SENSORS_CONF_PATH #define SENSORS_CONF_PATH "/etc/sensors.conf" #endif static char *conffile = SENSORS_CONF_PATH; /* #endif SENSORS_API_VERSION < 0x400 */ #elif (SENSORS_API_VERSION >= 0x400) typedef struct featurelist { const sensors_chip_name *chip; const sensors_feature *feature; const sensors_subfeature *subfeature; struct featurelist *next; } featurelist_t; static char *conffile; static bool use_labels; #endif static featurelist_t *first_feature; static ignorelist_t *sensor_list; static int sensors_config(const char *key, const char *value) { if (sensor_list == NULL) sensor_list = ignorelist_create(1); /* TODO: This setting exists for compatibility with old versions of * lm-sensors. Remove support for those ancient versions in the next * major release. */ if (strcasecmp(key, "SensorConfigFile") == 0) { char *tmp = strdup(value); if (tmp != NULL) { sfree(conffile); conffile = tmp; } } else if (strcasecmp(key, "Sensor") == 0) { if (ignorelist_add(sensor_list, value)) { ERROR("sensors plugin: " "Cannot add value to ignorelist."); return 1; } } else if (strcasecmp(key, "IgnoreSelected") == 0) { ignorelist_set_invert(sensor_list, 1); if (IS_TRUE(value)) ignorelist_set_invert(sensor_list, 0); } #if (SENSORS_API_VERSION >= 0x400) else if (strcasecmp(key, "UseLabels") == 0) { use_labels = IS_TRUE(value); } #endif else { return -1; } return 0; } static void sensors_free_features(void) { featurelist_t *nextft; if (first_feature == NULL) return; sensors_cleanup(); for (featurelist_t *thisft = first_feature; thisft != NULL; thisft = nextft) { nextft = thisft->next; sfree(thisft); } first_feature = NULL; } static int sensors_load_conf(void) { static int call_once; FILE *fh = NULL; featurelist_t *last_feature = NULL; const sensors_chip_name *chip; int chip_num; int status; if (call_once) return 0; call_once = 1; if (conffile != NULL) { fh = fopen(conffile, "r"); if (fh == NULL) { ERROR("sensors plugin: fopen(%s) failed: %s", conffile, STRERRNO); return -1; } } status = sensors_init(fh); if (fh) fclose(fh); if (status != 0) { ERROR("sensors plugin: Cannot initialize sensors. " "Data will not be collected."); return -1; } #if SENSORS_API_VERSION < 0x400 chip_num = 0; while ((chip = sensors_get_detected_chips(&chip_num)) != NULL) { int feature_num0 = 0; int feature_num1 = 0; while (42) { const sensors_feature_data *feature; int feature_type; featurelist_t *fl; feature = sensors_get_all_features(*chip, &feature_num0, &feature_num1); /* Check if all features have been read. */ if (feature == NULL) break; /* "master features" only */ if (feature->mapping != SENSORS_NO_MAPPING) { DEBUG("sensors plugin: sensors_load_conf: " "Ignoring subfeature `%s', " "because (feature->mapping " "!= SENSORS_NO_MAPPING).", feature->name); continue; } /* skip ignored in sensors.conf */ if (sensors_get_ignored(*chip, feature->number) == 0) { DEBUG("sensors plugin: sensors_load_conf: " "Ignoring subfeature `%s', " "because " "`sensors_get_ignored' told " "me so.", feature->name); continue; } feature_type = sensors_feature_name_to_type(feature->name); if (feature_type == SENSOR_TYPE_UNKNOWN) { DEBUG("sensors plugin: sensors_load_conf: " "Ignoring subfeature `%s', " "because its type is " "unknown.", feature->name); continue; } fl = calloc(1, sizeof(*fl)); if (fl == NULL) { ERROR("sensors plugin: calloc failed."); continue; } fl->chip = chip; fl->data = feature; fl->type = feature_type; if (first_feature == NULL) first_feature = fl; else last_feature->next = fl; last_feature = fl; } /* while sensors_get_all_features */ } /* while sensors_get_detected_chips */ /* #endif SENSORS_API_VERSION < 0x400 */ #elif (SENSORS_API_VERSION >= 0x400) chip_num = 0; while ((chip = sensors_get_detected_chips(NULL, &chip_num)) != NULL) { const sensors_feature *feature; int feature_num = 0; while ((feature = sensors_get_features(chip, &feature_num)) != NULL) { const sensors_subfeature *subfeature; int subfeature_num = 0; /* Only handle voltage, fanspeeds and temperatures */ if ((feature->type != SENSORS_FEATURE_IN) && (feature->type != SENSORS_FEATURE_FAN) && (feature->type != SENSORS_FEATURE_TEMP) && #if SENSORS_API_VERSION >= 0x402 (feature->type != SENSORS_FEATURE_CURR) && #endif #if SENSORS_API_VERSION >= 0x431 (feature->type != SENSORS_FEATURE_HUMIDITY) && #endif (feature->type != SENSORS_FEATURE_POWER)) { DEBUG("sensors plugin: sensors_load_conf: " "Ignoring feature `%s', " "because its type is not " "supported.", feature->name); continue; } while ((subfeature = sensors_get_all_subfeatures( chip, feature, &subfeature_num)) != NULL) { featurelist_t *fl; if ((subfeature->type != SENSORS_SUBFEATURE_IN_INPUT) && (subfeature->type != SENSORS_SUBFEATURE_FAN_INPUT) && (subfeature->type != SENSORS_SUBFEATURE_TEMP_INPUT) && #if SENSORS_API_VERSION >= 0x402 (subfeature->type != SENSORS_SUBFEATURE_CURR_INPUT) && #endif #if SENSORS_API_VERSION >= 0x431 (subfeature->type != SENSORS_SUBFEATURE_HUMIDITY_INPUT) && #endif (subfeature->type != SENSORS_SUBFEATURE_POWER_INPUT)) continue; fl = calloc(1, sizeof(*fl)); if (fl == NULL) { ERROR("sensors plugin: calloc failed."); continue; } fl->chip = chip; fl->feature = feature; fl->subfeature = subfeature; if (first_feature == NULL) first_feature = fl; else last_feature->next = fl; last_feature = fl; } /* while (subfeature) */ } /* while (feature) */ } /* while (chip) */ #endif /* (SENSORS_API_VERSION >= 0x400) */ if (first_feature == NULL) { sensors_cleanup(); INFO("sensors plugin: lm_sensors reports no " "features. Data will not be collected."); return -1; } return 0; } /* int sensors_load_conf */ static int sensors_shutdown(void) { sensors_free_features(); ignorelist_free(sensor_list); return 0; } /* int sensors_shutdown */ static void sensors_submit(const char *plugin_instance, const char *type, const char *type_instance, double value) { char match_key[1024]; int status; value_list_t vl = VALUE_LIST_INIT; status = snprintf(match_key, sizeof(match_key), "%s/%s-%s", plugin_instance, type, type_instance); if (status < 1) return; if (sensor_list != NULL) { DEBUG("sensors plugin: Checking ignorelist for `%s'", match_key); if (ignorelist_match(sensor_list, match_key)) return; } vl.values = &(value_t){.gauge = value}; vl.values_len = 1; sstrncpy(vl.plugin, "sensors", sizeof(vl.plugin)); sstrncpy(vl.plugin_instance, plugin_instance, sizeof(vl.plugin_instance)); sstrncpy(vl.type, type, sizeof(vl.type)); sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); plugin_dispatch_values(&vl); } /* void sensors_submit */ static int sensors_read(void) { if (sensors_load_conf() != 0) return -1; #if SENSORS_API_VERSION < 0x400 for (featurelist_t *fl = first_feature; fl != NULL; fl = fl->next) { double value; int status; char plugin_instance[DATA_MAX_NAME_LEN]; char type_instance[DATA_MAX_NAME_LEN]; status = sensors_get_feature(*fl->chip, fl->data->number, &value); if (status < 0) continue; status = sensors_snprintf_chip_name(plugin_instance, sizeof(plugin_instance), fl->chip); if (status < 0) continue; sstrncpy(type_instance, fl->data->name, sizeof(type_instance)); sensors_submit(plugin_instance, sensor_type_name_map[fl->type], type_instance, value); } /* for fl = first_feature .. NULL */ /* #endif SENSORS_API_VERSION < 0x400 */ #elif (SENSORS_API_VERSION >= 0x400) for (featurelist_t *fl = first_feature; fl != NULL; fl = fl->next) { double value; int status; char plugin_instance[DATA_MAX_NAME_LEN]; char type_instance[DATA_MAX_NAME_LEN]; char *sensor_label; const char *type; status = sensors_get_value(fl->chip, fl->subfeature->number, &value); if (status < 0) continue; status = sensors_snprintf_chip_name(plugin_instance, sizeof(plugin_instance), fl->chip); if (status < 0) continue; if (use_labels) { sensor_label = sensors_get_label(fl->chip, fl->feature); sstrncpy(type_instance, sensor_label, sizeof(type_instance)); free(sensor_label); } else { sstrncpy(type_instance, fl->feature->name, sizeof(type_instance)); } if (fl->feature->type == SENSORS_FEATURE_IN) type = "voltage"; else if (fl->feature->type == SENSORS_FEATURE_FAN) type = "fanspeed"; else if (fl->feature->type == SENSORS_FEATURE_TEMP) type = "temperature"; else if (fl->feature->type == SENSORS_FEATURE_POWER) type = "power"; #if SENSORS_API_VERSION >= 0x402 else if (fl->feature->type == SENSORS_FEATURE_CURR) type = "current"; #endif #if SENSORS_API_VERSION >= 0x431 else if (fl->feature->type == SENSORS_FEATURE_HUMIDITY) type = "humidity"; #endif else continue; sensors_submit(plugin_instance, type, type_instance, value); } /* for fl = first_feature .. NULL */ #endif /* (SENSORS_API_VERSION >= 0x400) */ return 0; } /* int sensors_read */ void module_register(void) { plugin_register_config("sensors", sensors_config, config_keys, config_keys_num); plugin_register_read("sensors", sensors_read); plugin_register_shutdown("sensors", sensors_shutdown); } /* void module_register */