]> git.ipfire.org Git - oddments/collecty.git/commitdiff
sources: disk: Create a metrics object and populate as we go
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 22 Oct 2025 17:44:44 +0000 (17:44 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 22 Oct 2025 17:44:44 +0000 (17:44 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/daemon/sources/disk.c

index da0ab21e5c11a1dd6c66eaeafdfcc8bb688c7f30..996dfce7a31df33e081861d00b76f6912c6cf435 100644 (file)
@@ -51,46 +51,16 @@ static int is_ignored(const char* node) {
        return 0;
 }
 
-typedef struct td_disk {
-       uint64_t read_ios;
-       uint64_t read_merges;
-       uint64_t read_sectors;
-       uint64_t read_ticks;
-       uint64_t write_ios;
-       uint64_t write_merges;
-       uint64_t write_sectors;
-       uint64_t write_ticks;
-       uint64_t in_flight;
-       uint64_t io_ticks;
-       uint64_t time_in_queue;
-       uint64_t discard_ios;
-       uint64_t discard_merges;
-       uint64_t discard_sectors;
-       uint64_t discard_ticks;
-
-       // Is the disk awake?
+#ifdef HAVE_LIBATASMART
+static int disk_read_smart(td_ctx* ctx, const char* node, td_metrics* metrics) {
+       SkDisk* disk = NULL;
+       SkBool available = 0;
        SkBool awake;
-
-       // Power On Time (in msec)
-       uint64_t power_on_time;
-
-       // SMART Status
        SkBool smart_status;
-
-       // Power Cycles
-       uint64_t power_cycles;
-
-       // Bad Sectors
        uint64_t bad_sectors;
-
-       // Temperature (in mK)
+       uint64_t power_cycles;
+       uint64_t power_on_time;
        uint64_t temperature;
-} td_disk;
-
-#ifdef HAVE_LIBATASMART
-static int disk_read_smart(td_ctx* ctx, const char* node, td_disk* stat) {
-       SkBool available = 0;
-       SkDisk* disk = NULL;
        int r;
 
        // Open the disk
@@ -120,20 +90,25 @@ static int disk_read_smart(td_ctx* ctx, const char* node, td_disk* stat) {
 
        // Skip if this disk does not support SMART
        if (!available)
-               goto ERROR;
+               goto DONE;
 
        // Check if the disk is asleep
-       r = sk_disk_check_sleep_mode(disk, &stat->awake);
+       r = sk_disk_check_sleep_mode(disk, &awake);
        if (r < 0) {
                ERROR(ctx, "Failed to check if %s is asleep: %m\n", node);
                r = -errno;
                goto ERROR;
        }
 
-       // Skip if the disk is asleep (because fetching SMART data might wake up the device)
-       if (!stat->awake)
+       // Store whether the disk is awake
+       r = td_metrics_push_bool(metrics, "awake", awake);
+       if (r < 0)
                goto ERROR;
 
+       // Skip if the disk is asleep (because fetching SMART data might wake up the device)
+       if (!awake)
+               goto DONE;
+
        // Read the SMART data
        r = sk_disk_smart_read_data(disk);
        if (r < 0) {
@@ -143,7 +118,7 @@ static int disk_read_smart(td_ctx* ctx, const char* node, td_disk* stat) {
        }
 
        // Read SMART status
-       r = sk_disk_smart_status(disk, &stat->smart_status);
+       r = sk_disk_smart_status(disk, &smart_status);
        if (r < 0) {
                ERROR(ctx, "Failed to read SMART status from %s: %m\n", node);
                r = -errno;
@@ -151,7 +126,7 @@ static int disk_read_smart(td_ctx* ctx, const char* node, td_disk* stat) {
        }
 
        // Read power on time
-       r = sk_disk_smart_get_power_on(disk, &stat->power_on_time);
+       r = sk_disk_smart_get_power_on(disk, &power_on_time);
        if (r < 0) {
                ERROR(ctx, "Failed to read power on time from %s: %m\n", node);
                r = -errno;
@@ -159,7 +134,7 @@ static int disk_read_smart(td_ctx* ctx, const char* node, td_disk* stat) {
        }
 
        // Read power cycles
-       r = sk_disk_smart_get_power_cycle(disk, &stat->power_cycles);
+       r = sk_disk_smart_get_power_cycle(disk, &power_cycles);
        if (r < 0) {
                ERROR(ctx, "Failed to read power cycles from %s: %m\n", node);
                r = -errno;
@@ -167,11 +142,12 @@ static int disk_read_smart(td_ctx* ctx, const char* node, td_disk* stat) {
        }
 
        // Read bad sectors
-       r = sk_disk_smart_get_bad(disk, &stat->bad_sectors);
+       r = sk_disk_smart_get_bad(disk, &bad_sectors);
        if (r < 0) {
                switch (errno) {
                        // ENOENT means that there are no bad sectors
                        case ENOENT:
+                               bad_sectors = 0;
                                break;
 
                        // Handle any other errors
@@ -183,14 +159,25 @@ static int disk_read_smart(td_ctx* ctx, const char* node, td_disk* stat) {
        }
 
        // Read temperature
-       r = sk_disk_smart_get_temperature(disk, &stat->temperature);
+       r = sk_disk_smart_get_temperature(disk, &temperature);
        if (r < 0) {
                ERROR(ctx, "Failed to read temperature from %s: %m\n", node);
                r = -errno;
                goto ERROR;
        }
 
+       // Push all values
+       r = td_metrics_set(metrics, VALUES(
+               VALUE_BOOL  ("awake",         &awake),
+               VALUE_UINT64("power_on_time", &power_on_time),
+               VALUE_BOOL  ("smart_status",  &smart_status),
+               VALUE_UINT64("power_cycle",   &power_cycles),
+               VALUE_UINT64("bad_sectors",   &bad_sectors),
+               VALUE_FLOAT ("temperature",   &temperature)
+       ));
+
 ERROR:
+DONE:
        if (disk)
                sk_disk_free(disk);
 
@@ -200,12 +187,11 @@ ERROR:
 
 static int __disk_read_stat(td_ctx* ctx, td_file* file, unsigned long lineno,
                char* line, size_t length, void* data) {
+       td_metrics* metrics = data;
        unsigned int token = 0;
        uint64_t value = 0;
        int r;
 
-       td_disk* disk = data;
-
        // We only care about one line (there should not be more)
        if (lineno > 1)
                return -EINVAL;
@@ -239,78 +225,86 @@ static int __disk_read_stat(td_ctx* ctx, td_file* file, unsigned long lineno,
 
                switch (token++) {
                        case 0:
-                               disk->read_ios = value;
+                               r = td_metrics_push_uint64(metrics, "read_ios", value);
                                break;
 
                        case 1:
-                               disk->read_merges = value;
+                               r = td_metrics_push_uint64(metrics, "read_merges", value);
                                break;
 
                        case 2:
-                               disk->read_sectors = value;
+                               r = td_metrics_push_uint64(metrics, "read_sectors", value);
                                break;
 
                        case 3:
-                               disk->read_ticks = value;
+                               r = td_metrics_push_uint64(metrics, "read_ticks", value);
                                break;
 
                        case 4:
-                               disk->write_ios = value;
+                               r = td_metrics_push_uint64(metrics, "write_ios", value);
                                break;
 
                        case 5:
-                               disk->write_merges = value;
+                               r = td_metrics_push_uint64(metrics, "write_merges", value);
                                break;
 
                        case 6:
-                               disk->write_sectors = value;
+                               r = td_metrics_push_uint64(metrics, "write_sectors", value);
                                break;
 
                        case 7:
-                               disk->write_ticks = value;
+                               r = td_metrics_push_uint64(metrics, "write_ticks", value);
                                break;
 
                        case 8:
-                               disk->in_flight = value;
+                               r = td_metrics_push_uint64(metrics, "in_flight", value);
                                break;
 
                        case 9:
-                               disk->io_ticks = value;
+                               r = td_metrics_push_uint64(metrics, "io_ticks", value);
                                break;
 
                        case 10:
-                               disk->time_in_queue = value;
+                               r = td_metrics_push_uint64(metrics, "time_in_queue", value);
                                break;
 
                        case 11:
-                               disk->discard_ios = value;
+                               r = td_metrics_push_uint64(metrics, "discard_ios", value);
                                break;
 
                        case 12:
-                               disk->discard_merges = value;
+                               r = td_metrics_push_uint64(metrics, "discard_merges", value);
                                break;
 
                        case 13:
-                               disk->discard_sectors = value;
+                               r = td_metrics_push_uint64(metrics, "discard_sectors", value);
                                break;
 
                        case 14:
-                               disk->discard_ticks = value;
+                               r = td_metrics_push_uint64(metrics, "discard_ticks", value);
                                break;
 
                        default:
                                return 0;
                }
+
+               // Handle errors
+               if (r < 0)
+                       return r;
        }
 
        return 0;
 }
 
-static int disk_read_stat(td_ctx* ctx, const char* syspath, td_disk* disk) {
+static int disk_read_stat(td_ctx* ctx, struct udev_device* dev, td_metrics* metrics) {
+       const char* syspath = NULL;
        td_file* file = NULL;
        char path[PATH_MAX];
        int r;
 
+       // Fetch the sys path
+       syspath = udev_device_get_syspath(dev);
+
        // Make the path
        r = td_string_format(path, "%s/stat", syspath);
        if (r < 0)
@@ -324,7 +318,7 @@ static int disk_read_stat(td_ctx* ctx, const char* syspath, td_disk* disk) {
        }
 
        // Walk through all lines
-       r = td_file_walk(file, __disk_read_stat, disk);
+       r = td_file_walk(file, __disk_read_stat, metrics);
 
 ERROR:
        if (file)
@@ -335,10 +329,9 @@ ERROR:
 
 // Process a single device
 static int disk_heartbeat_device(td_ctx* ctx, td_source* source, struct udev_device* dev) {
-       const char* syspath = NULL;
+       td_metrics* metrics = NULL;
        const char* serial = NULL;
        const char* node = NULL;
-       td_disk disk = {};
        int r;
 
        // Fetch the device node
@@ -357,31 +350,31 @@ static int disk_heartbeat_device(td_ctx* ctx, td_source* source, struct udev_dev
                return 0;
        }
 
-       // Fetch the sys path
-       syspath = udev_device_get_syspath(dev);
+       // Create a new metrics object
+       r = td_source_create_metrics(source, &metrics, serial);
+       if (r < 0)
+               goto ERROR;
 
        // Parse stats
-       r = disk_read_stat(ctx, syspath, &disk);
+       r = disk_read_stat(ctx, dev, metrics);
        if (r < 0)
                return r;
 
 #ifdef HAVE_LIBATASMART
        // Fetch SMART information
-       r = disk_read_smart(ctx, node, &disk);
+       r = disk_read_smart(ctx, node, metrics);
        if (r < 0)
                return r;
 #endif /* HAVE_LIBATASMART */
 
-       // Submit stats
-       return td_source_submit(source, serial,
-               "%lu:%lu:%lu:%lu:%lu:%lu:%lu:%lu:%lu:%lu:%lu:%lu:%lu:%lu:%lu:%d:%lu:%d:%lu:%lu:%lf",
-               disk.read_ios, disk.read_merges, disk.read_sectors, disk.read_ticks,
-               disk.write_ios, disk.write_merges, disk.write_sectors, disk.write_ticks,
-               disk.in_flight, disk.io_ticks, disk.time_in_queue,
-               disk.discard_ios, disk.discard_merges, disk.discard_sectors, disk.discard_ticks,
-               (disk.awake) ? 1 : 0, disk.power_on_time, (disk.smart_status) ? 1 : 0,
-               disk.power_cycles, disk.bad_sectors, (double)disk.temperature / 1000
-       );
+       // Submit metrics
+       r = td_source_submit_metrics(source, metrics);
+
+ERROR:
+       if (metrics)
+               td_metrics_unref(metrics);
+
+       return r;
 }
 
 static int disk_heartbeat(td_ctx* ctx, td_source* source) {