]> git.ipfire.org Git - oddments/collecty.git/commitdiff
sources: sensors: Implement reading temp sensors
authorMichael Tremer <michael.tremer@ipfire.org>
Thu, 23 Oct 2025 15:55:53 +0000 (15:55 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Thu, 23 Oct 2025 16:17:57 +0000 (16:17 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
Makefile.am
src/daemon/sources.c
src/daemon/sources/sensors.c [new file with mode: 0644]
src/daemon/sources/sensors.h [new file with mode: 0644]

index 0bd0691a9026a7bc7cce6bc0a5d95d2071536d31..58303651584f8fea56dd97a96fcaeaf23088547d 100644 (file)
@@ -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)
 
index f5a33c6c7679e72aca20776870dbd0e321e20a1b..38ee8b58cd1049e2b06761ea272d04d3d13d0ec3 100644 (file)
 # 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 (file)
index 0000000..85a190a
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#include <ctype.h>
+#include <errno.h>
+
+#include <sensors/error.h>
+#include <sensors/sensors.h>
+
+#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 (file)
index 0000000..5e6ec9d
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+#############################################################################*/
+
+#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 */