diff options
author | Heikki Kallasjoki <fis@zem.fi> | 2018-08-05 23:50:20 +0100 |
---|---|---|
committer | Heikki Kallasjoki <fis@zem.fi> | 2018-08-05 23:50:20 +0100 |
commit | c73aca3d51a9ce915b089d92ea9a43ec49ef63f0 (patch) | |
tree | 4a75083b7470679ad7aed217749a457bbf259996 /hwmon.c | |
download | nano-exporter-c73aca3d51a9ce915b089d92ea9a43ec49ef63f0.tar.gz nano-exporter-c73aca3d51a9ce915b089d92ea9a43ec49ef63f0.tar.xz nano-exporter-c73aca3d51a9ce915b089d92ea9a43ec49ef63f0.zip |
Initial prototype.
Diffstat (limited to 'hwmon.c')
-rw-r--r-- | hwmon.c | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/hwmon.c b/hwmon.c new file mode 100644 index 0000000..70c3129 --- /dev/null +++ b/hwmon.c @@ -0,0 +1,200 @@ +#define _POSIX_C_SOURCE 200809L + +#include <dirent.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "hwmon.h" + +// size of input buffer for paths and lines +#define BUF_SIZE 256 +// size of input buffer for labels +#define LABEL_SIZE 32 + +static void hwmon_collect(scrape_req *req, void *ctx); + +const struct collector hwmon_collector = { + .name = "hwmon", + .collect = hwmon_collect, +}; + +static double hwmon_conv_millis(const char *text) { + char *endptr; + double value = strtod(text, &endptr); + if (*endptr == '\n' || *endptr == '\0') + return value / 1000; + return NAN; +} + +static double hwmon_conv_id(const char *text) { + char *endptr; + double value = strtod(text, &endptr); + if (*endptr == '\n' || *endptr == '\0') + return value; + return NAN; +} + +static double hwmon_conv_flag(const char *text) { + if ((text[0] == '0' || text[0] == '1') && (text[1] == '\n' || text[1] == '\0')) + return text[0] - '0'; + return NAN; +} + +struct metric_type { + const char *suffix; + const char *metric; + double (*conv)(const char *text); +}; + +struct metric_data { + const char *prefix; + const struct metric_type *types; +}; + +static const struct metric_data metrics[] = { + { + .prefix = "in", + .types = (const struct metric_type[]){ + { + .suffix = "_input", + .metric = "node_hwmon_in_volts", + .conv = hwmon_conv_millis, + }, + { + .suffix = "_min", + .metric = "node_hwmon_in_min_volts", + .conv = hwmon_conv_millis, + }, + { + .suffix = "_max", + .metric = "node_hwmon_in_max_volts", + .conv = hwmon_conv_millis, + }, + { + .suffix = "_alarm", + .metric = "node_hwmon_in_alarm", + .conv = hwmon_conv_flag, + }, + { .suffix = 0 }, + }, + }, + { + .prefix = "fan", + .types = (const struct metric_type[]){ + { + .suffix = "_input", + .metric = "node_hwmon_fan_rpm", + .conv = hwmon_conv_id, + }, + { + .suffix = "_min", + .metric = "node_hwmon_fan_min_rpm", + .conv = hwmon_conv_id, + }, + { + .suffix = "_alarm", + .metric = "node_hwmon_fan_alarm", + .conv = hwmon_conv_flag, + }, + { .suffix = 0 }, + }, + }, + { + .prefix = "temp", + .types = (const struct metric_type[]){ + { + .suffix = "_input", + .metric = "node_hwmon_temp_celsius", + .conv = hwmon_conv_millis, + }, + { .suffix = 0 }, + }, + }, + { .prefix = 0 }, +}; + +static void hwmon_collect(scrape_req *req, void *ctx) { + // buffers + + char chip_label[LABEL_SIZE]; + char sensor_label[LABEL_SIZE]; + + const char *labels[][2] = { + { "chip", chip_label }, + { "sensor", sensor_label }, + { 0, 0 }, + }; + + char path[BUF_SIZE]; + char buf[BUF_SIZE]; + size_t len; + + FILE *f; + + DIR *root; + struct dirent *dent; + + // iterate over all hwmon instances in /sys/class/hwmon + + root = opendir("/sys/class/hwmon"); + if (!root) + return; + + while ((dent = readdir(root))) { + if (strncmp(dent->d_name, "hwmon", 5) != 0) + continue; + snprintf(path, sizeof path, "/sys/class/hwmon/%s", dent->d_name); + + len = readlink(path, buf, sizeof buf); + if (len > 14 && memcmp(buf, "../../devices/", 14) == 0) { + char *start = buf + 14; + char *end = strchr(start, '/'); + if (end) + end = strchr(end + 1, '/'); + if (end) + *end = '\0'; + snprintf(chip_label, sizeof chip_label, "%s", start); + } else { + snprintf(chip_label, sizeof chip_label, "unknown"); + } + + DIR *dir = opendir(path); + if (!dir) + continue; + + while ((dent = readdir(dir))) { + for (const struct metric_data *metric = metrics; metric->prefix; metric++) { + if (strncmp(dent->d_name, metric->prefix, strlen(metric->prefix)) != 0) + continue; + char *suffix = strchr(dent->d_name, '_'); + if (!suffix) + continue; + + snprintf(sensor_label, sizeof sensor_label, "%.*s", (int)(suffix - dent->d_name), dent->d_name); + + for (const struct metric_type *type = metric->types; type->suffix; type++) { + if (strcmp(suffix, type->suffix) != 0) + continue; + + snprintf(buf, sizeof buf, "%s/%s", path, dent->d_name); + f = fopen(buf, "r"); + if (!f) + continue; + if (fgets(buf, sizeof buf, f)) { + double value = type->conv(buf); + if (!isnan(value)) + scrape_write(req, type->metric, labels, value); + } + fclose(f); + } + } + } + + closedir(dir); + } + + closedir(root); +} |