From: Michael Tremer Date: Thu, 23 Oct 2025 15:55:53 +0000 (+0000) Subject: sources: sensors: Implement reading temp sensors X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=251f96577588a48d0a8ea0e7809df934d1a11a4e;p=telemetry.git sources: sensors: Implement reading temp sensors Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index 0bd0691..5830365 100644 --- a/Makefile.am +++ b/Makefile.am @@ -175,6 +175,8 @@ dist_telemetryd_SOURCES = \ src/daemon/sources/pressure-memory.h \ src/daemon/sources/processor.c \ src/daemon/sources/processor.h \ + src/daemon/sources/sensors.c \ + src/daemon/sources/sensors.h \ src/daemon/sources/softirq.c \ src/daemon/sources/softirq.h \ src/daemon/sources/suricata.c \ @@ -202,6 +204,7 @@ telemetryd_CFLAGS = \ $(LIBATASMART_CFLAGS) \ $(LIBIPTC_CFLAGS) \ $(RRD_CFLAGS) \ + $(SENSORS_CFLAGS) \ $(SYSTEMD_CFLAGS) \ $(UDEV_CFLAGS) @@ -210,6 +213,7 @@ telemetryd_LDFLAGS = \ $(LIBATASMART_LDFLAGS) \ $(LIBIPTC_LDFLAGS) \ $(RRD_LDFLAGS) \ + $(SENSORS_LDFLAGS) \ $(SYSTEMD_LDFLAGS) \ $(UDEV_LDFLAGS) @@ -217,6 +221,7 @@ telemetryd_LDADD = \ $(LIBATASMART_LIBS) \ $(LIBIPTC_LIBS) \ $(RRD_LIBS) \ + $(SENSORS_LIBS) \ $(SYSTEMD_LIBS) \ $(UDEV_LIBS) diff --git a/src/daemon/sources.c b/src/daemon/sources.c index f5a33c6..38ee8b5 100644 --- a/src/daemon/sources.c +++ b/src/daemon/sources.c @@ -51,6 +51,11 @@ # include "sources/iptables.h" #endif /* HAVE_LIBIPTC */ +// sensors +#ifdef HAVE_SENSORS +# include "sources/sensors.h" +#endif /* HAVE_SENSORS */ + // Load test sources #if ENABLE_TESTS #include "sources/test-error.h" @@ -82,6 +87,11 @@ static const td_source_impl* source_impls[] = { &iptables_source, #endif /* HAVE_LIBIPTC */ + // sensors +#ifdef HAVE_SENSORS + &sensors_temp_source, +#endif /* HAVE_SENSORS */ + #if ENABLE_TESTS // Tests &test_error_source, diff --git a/src/daemon/sources/sensors.c b/src/daemon/sources/sensors.c new file mode 100644 index 0000000..85a190a --- /dev/null +++ b/src/daemon/sources/sensors.c @@ -0,0 +1,305 @@ +/*############################################################################# +# # +# telemetryd - The IPFire Telemetry Collection Service # +# Copyright (C) 2025 IPFire Development Team # +# # +# 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, either version 3 of the License, or # +# (at your option) any later version. # +# # +# 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, see . # +# # +#############################################################################*/ + +#include +#include + +#include +#include + +#include "../ctx.h" +#include "../metrics.h" +#include "../source.h" +#include "../string.h" +#include "sensors.h" + +#define sensors_make_name(name, chip, label) \ + __sensors_make_name(name, sizeof(name), chip, label) + +static int __sensors_make_name(char* name, size_t length, const char* chip, const char* label) { + int r; + + // Copy everything info the buffer + r = __td_string_format(name, length, "%s-%s", chip, label); + if (r < 0) + return r; + + // Sanitize the name + for (char* p = name; *p; p++) { + // Convert everything to lower-case + if (isupper(*p)) { + *p = tolower(*p); + continue; + + // Replace whitespace with dashes + } else if (isspace(*p)) { + *p = '-'; + continue; + } + + // Replace a couple of special characters, too + switch (*p) { + case '/': + case ':': + *p = '-'; + break; + + default: + break; + } + } + + return 0; +} + +typedef int (*find_sensors_callback)(td_ctx* ctx, td_source* source, + const sensors_chip_name* chip, const sensors_feature* feature, td_metrics* metrics); + +static int find_sensors(td_ctx* ctx, td_source* source, + sensors_feature_type type, find_sensors_callback callback) { + const sensors_feature* feature = NULL; + const sensors_chip_name* chip = NULL; + td_metrics* metrics = NULL; + const char* label = NULL; + char chip_name[NAME_MAX]; + char name[NAME_MAX]; + int feature_index = 0; + int chip_index = 0; + int r; + + // Initialize sensors + r = sensors_init(NULL); + if (r) { + ERROR(ctx, "Failed to initialize sensors: %s\n", sensors_strerror(r)); + return -ENOTSUP; + } + + // Iterate over all chips + for (;;) { + chip = sensors_get_detected_chips(NULL, &chip_index); + if (!chip) + break; + + // Make the chip name + r = sensors_snprintf_chip_name(chip_name, sizeof(chip_name), chip); + if (r < 0) + goto ERROR; + + // Reset the feature index + feature_index = 0; + + // Iterate over all features + for (;;) { + feature = sensors_get_features(chip, &feature_index); + if (!feature) + break; + + // Skip anything that does not match + if (feature->type != type) + continue; + + // Fetch the label of the chip + label = sensors_get_label(chip, feature); + + // Format the whole name + r = sensors_make_name(name, chip_name, label); + if (r < 0) + goto ERROR; + + // Create metrics + r = td_source_create_metrics(source, &metrics, name); + if (r < 0) + goto ERROR; + + // Call the callback + r = callback(ctx, source, chip, feature, metrics); + if (r < 0) + goto ERROR; + + // Submit the metrics + r = td_source_submit_metrics(source, metrics); + if (r < 0) + goto ERROR; + + // Free the metrics + if (metrics) { + td_metrics_unref(metrics); + metrics = NULL; + } + } + } + +ERROR: + if (metrics) + td_metrics_unref(metrics); + sensors_cleanup(); + + return r; +} + +typedef struct sensors_value { + // Field + const char* field; + + // Type + sensors_subfeature_type type; +} sensors_value; + +static int read_sensors(td_ctx* ctx, const sensors_chip_name* chip, + const sensors_feature* feature, td_metrics* metrics, sensors_value* values) { + const sensors_subfeature* subfeature = NULL; + double value = 0; + int index = 0; + int r; + + // Iterate over all subfeatures + for (;;) { + subfeature = sensors_get_all_subfeatures(chip, feature, &index); + if (!subfeature) + break; + + // Search for any matching values and read them + for (sensors_value* v = values; v->field; v++) { + if (subfeature->type == v->type) { + // Read the value + r = sensors_get_value(chip, subfeature->number, &value); + if (r < 0) + return r; + + // Convert some temperature values from Celsius to Kelvin + switch (v->type) { + case SENSORS_SUBFEATURE_TEMP_INPUT: + case SENSORS_SUBFEATURE_TEMP_MAX: + case SENSORS_SUBFEATURE_TEMP_MAX_HYST: + case SENSORS_SUBFEATURE_TEMP_MIN: + case SENSORS_SUBFEATURE_TEMP_CRIT: + case SENSORS_SUBFEATURE_TEMP_CRIT_HYST: + case SENSORS_SUBFEATURE_TEMP_LCRIT: + case SENSORS_SUBFEATURE_TEMP_EMERGENCY: + case SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST: + case SENSORS_SUBFEATURE_TEMP_LOWEST: + case SENSORS_SUBFEATURE_TEMP_HIGHEST: + case SENSORS_SUBFEATURE_TEMP_ALARM: + case SENSORS_SUBFEATURE_TEMP_MAX_ALARM: + case SENSORS_SUBFEATURE_TEMP_MIN_ALARM: + case SENSORS_SUBFEATURE_TEMP_CRIT_ALARM: + case SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM: + case SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM: + case SENSORS_SUBFEATURE_TEMP_FAULT: + case SENSORS_SUBFEATURE_TEMP_TYPE: + case SENSORS_SUBFEATURE_TEMP_OFFSET: + case SENSORS_SUBFEATURE_TEMP_BEEP: + value += 273.15; + break; + + default: + break; + } + + // Push the value + r = td_metrics_push_float(metrics, v->field, value); + if (r < 0) + return r; + + // We are done for this subfeature + break; + } + } + } + + return 0; +} + + +static int read_temp_sensor(td_ctx* ctx, td_source* source, const sensors_chip_name* chip, + const sensors_feature* feature, td_metrics* metrics) { + sensors_value values[] = { + { "current", SENSORS_SUBFEATURE_TEMP_INPUT }, + { "max", SENSORS_SUBFEATURE_TEMP_MAX }, + { "max_hyst", SENSORS_SUBFEATURE_TEMP_MAX_HYST }, + { "min", SENSORS_SUBFEATURE_TEMP_MIN }, + { "min_hyst", SENSORS_SUBFEATURE_TEMP_MIN_HYST }, + { "crit", SENSORS_SUBFEATURE_TEMP_CRIT }, + { "crit_hyst", SENSORS_SUBFEATURE_TEMP_CRIT_HYST }, + { "lcrit", SENSORS_SUBFEATURE_TEMP_LCRIT }, + { "emergency", SENSORS_SUBFEATURE_TEMP_EMERGENCY }, + { "emergency_hyst", SENSORS_SUBFEATURE_TEMP_EMERGENCY_HYST }, + { "lowest", SENSORS_SUBFEATURE_TEMP_LOWEST }, + { "highest", SENSORS_SUBFEATURE_TEMP_HIGHEST }, + { "alarm", SENSORS_SUBFEATURE_TEMP_ALARM }, + { "alarm_max", SENSORS_SUBFEATURE_TEMP_MAX_ALARM }, + { "alarm_min", SENSORS_SUBFEATURE_TEMP_MIN_ALARM }, + { "alarm_crit", SENSORS_SUBFEATURE_TEMP_CRIT_ALARM }, + { "alarm_emergency", SENSORS_SUBFEATURE_TEMP_EMERGENCY_ALARM }, + { "alarm_lcrit", SENSORS_SUBFEATURE_TEMP_LCRIT_ALARM }, + { "fault", SENSORS_SUBFEATURE_TEMP_FAULT }, + { "type", SENSORS_SUBFEATURE_TEMP_TYPE }, + { "offset", SENSORS_SUBFEATURE_TEMP_OFFSET }, + { "beep", SENSORS_SUBFEATURE_TEMP_BEEP }, + { NULL }, + }; + + // Read values + return read_sensors(ctx, chip, feature, metrics, values); +} + +static int sensors_temp_heartbeat(td_ctx* ctx, td_source* source) { + int r; + + // Find temperature sensors + r = find_sensors(ctx, source, SENSORS_FEATURE_TEMP, read_temp_sensor); + if (r < 0) + return r; + + return 0; +} + +const td_source_impl sensors_temp_source = { + .name = "sensors-temp", + + // RRD Data Sources + .rrd_dss = { + { "current", "GAUGE", 0, -1, }, + { "max", "GAUGE", 0, -1, }, + { "max_hyst", "GAUGE", 0, -1, }, + { "min", "GAUGE", 0, -1, }, + { "crit", "GAUGE", 0, -1, }, + { "crit_hyst", "GAUGE", 0, -1, }, + { "lcrit", "GAUGE", 0, -1, }, + { "emergency", "GAUGE", 0, -1, }, + { "emergency_hyst", "GAUGE", 0, -1, }, + { "lowest", "GAUGE", 0, -1, }, + { "highest", "GAUGE", 0, -1, }, + { "alarm", "GAUGE", 0, -1, }, + { "alarm_max", "GAUGE", 0, -1, }, + { "alarm_min", "GAUGE", 0, -1, }, + { "alarm_crit", "GAUGE", 0, -1, }, + { "alarm_emergency", "GAUGE", 0, -1, }, + { "alarm_lcrit", "GAUGE", 0, -1, }, + { "fault", "GAUGE", 0, -1, }, + { "type", "GAUGE", 0, -1, }, + { "offset", "GAUGE", 0, -1, }, + { "beep", "GAUGE", 0, -1, }, + { NULL }, + }, + + // Methods + .heartbeat = sensors_temp_heartbeat, +}; diff --git a/src/daemon/sources/sensors.h b/src/daemon/sources/sensors.h new file mode 100644 index 0000000..5e6ec9d --- /dev/null +++ b/src/daemon/sources/sensors.h @@ -0,0 +1,28 @@ +/*############################################################################# +# # +# telemetryd - The IPFire Telemetry Collection Service # +# Copyright (C) 2025 IPFire Development Team # +# # +# 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, either version 3 of the License, or # +# (at your option) any later version. # +# # +# 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, see . # +# # +#############################################################################*/ + +#ifndef TELEMETRY_SOURCE_SENSORS_H +#define TELEMETRY_SOURCE_SENSORS_H + +#include "../source.h" + +extern const td_source_impl sensors_temp_source; + +#endif /* TELEMETRY_SOURCE_SENSORS_H */