util/hdnoidle.c: Cleanup; use BSD SLIST macros also in glibc.
authorMatthew Mondor <mmondor@pulsar-zone.net>
Wed, 9 Aug 2023 04:33:53 +0000 (04:33 +0000)
committerMatthew Mondor <mmondor@pulsar-zone.net>
Wed, 9 Aug 2023 04:33:53 +0000 (04:33 +0000)
mmsoftware/util/hdnoidle.c

index 3fa7aa7..10c4172 100644 (file)
@@ -63,8 +63,9 @@
  */
 
 
-#define CONFIG "/usr/local/etc/hdnoidle.conf"
-#define CONFIG_LZ 256
+#define CONFIG         "/usr/local/etc/hdnoidle.conf"
+#define CONFIG_LZ      256
+#define BLOCK_BUFSIZE  65536
 
 
 /* Linux may use 32-bit off_t, other unix systems long use 64-bit. */
 #if defined (__NetBSD__) || defined (__FreeBSD__)
 #include <sys/disk.h> /* DIOCGMEDIASIZE, DIOCGSECTORSIZE */
 #endif
+#include <sys/queue.h> /* BSD SLIST macros, also in glibc. */
 
 
 /* Control structure for each disk */
 typedef struct noidledisk {
+       SLIST_ENTRY(noidledisk) next;
        const char *dev;        /* Device file */
        int fd;                 /* Raw or unbuffered device file handle */
        unsigned int delay, t;  /* Delay in seconds between reads, current */
@@ -108,15 +111,18 @@ typedef struct noidledisk {
        uint8_t *sbuf;          /* Buffer to use when reading a block */
 } noidledisk_t;
 
+SLIST_HEAD(noidledisk_list, noidledisk);
+
 
 int    main(void);
-void   conf_read(noidledisk_t **, int *);
-void   conf_setup(noidledisk_t *, int);
+void   conf_read(void);
+void   conf_setup(void);
 int    disk_getsize(int, off_t *);
 ssize_t        disk_getblocksize(int);
 
 
 uint8_t        *membuf = NULL;
+struct noidledisk_list disks;
 
 
 /*
@@ -150,75 +156,70 @@ straspl(char **argv, char *str, int maxcols)
 }
 
 void
-conf_read(noidledisk_t **disks, int *dn)
+conf_read(void)
 {
-       noidledisk_t *dmem = NULL;
        FILE *fh;
-       int n = 0, i;
+       int n = 0, l = 0;
        char *cols[3] = { NULL, NULL, NULL };
        char line[CONFIG_LZ + 1];
 
+       SLIST_INIT(&disks);
+
+       /* Read configuration file, populating list <disks> */
        if ((fh = fopen(CONFIG, "r")) == NULL)
                err(EXIT_FAILURE, "fopen(%s)", CONFIG);
-       /* Count non-comment lines matching required number of columns */
        while (fgets(line, CONFIG_LZ, fh) != NULL) {
-               line[CONFIG_LZ] = '\0';
-               if (*line != '#') {
-                       if (straspl(cols, line, 2) == 2)
-                               n++;
-               }
-       }
-       (void)fclose(fh);
+               noidledisk_t *d = NULL, *d2;
 
-       /* Allocate structs */
-       if ((dmem = malloc(sizeof(noidledisk_t) * n)) == NULL)
-               err(EXIT_FAILURE, "malloc()");
-
-       /*
-        * Reopen and fill structs.  There's a possible race condition between
-        * the above and this, but we ensure not to configure more than <n>.
-        * If there are more lines than expected, we ignore them.  If there
-        * are less, we adjust <n>.
-        */
-       if ((fh = fopen(CONFIG, "r")) == NULL)
-               err(EXIT_FAILURE, "fopen(%s)", CONFIG);
-       for (i = 0; i < n && fgets(line, CONFIG_LZ, fh) != NULL; ) {
+               l++;
                line[CONFIG_LZ] = '\0';
                if (*line == '#')
                        continue;
-               if (straspl(cols, line, 2) == 2) {
-                       noidledisk_t *d = &dmem[i];
-
-                       if ((d->dev = strdup(cols[0])) == NULL)
-                               err(EXIT_FAILURE, "strdup()");
-                       if ((d->delay = atoi(cols[1])) < 1)
-                               err(EXIT_FAILURE, "atoi(delay) < 1");
-                       i++;
+               if (straspl(cols, line, 2) != 2)
+                       err(EXIT_FAILURE,
+                           "hdnoidle: Invalid configuration line #%d in %s",
+                           l, CONFIG);
+
+               /* Check if unique */
+               SLIST_FOREACH(d2, &disks, next) {
+                       if (strcmp(d2->dev, cols[0]) == 0)
+                               err(EXIT_FAILURE,
+                                   "hdnoidle: Duplicate entry for %s at "
+                                   "line #%d in %s", d2->dev, l, CONFIG);
                }
+
+               /* Allocate new entry and add to list */
+               n++;
+               if ((d = malloc(sizeof(noidledisk_t))) == NULL)
+                       err(EXIT_FAILURE, "malloc()");
+               if ((d->dev = strdup(cols[0])) == NULL)
+                       err(EXIT_FAILURE, "strdup()");
+               if ((d->delay = atoi(cols[1])) < 1)
+                       err(EXIT_FAILURE,
+                           "hdnoidle: Invalid delay %d at line #%d in %s",
+                           d->delay, l, CONFIG);
+               SLIST_INSERT_HEAD(&disks, d, next);
        }
        (void)fclose(fh);
-       if (i < 1)
-               err(EXIT_FAILURE, "hdnoidle: Quitting, no disk configured.");
 
-       *disks = dmem;
-       *dn = i;
+       if (n < 1)
+               err(EXIT_FAILURE, "hdnoidle: Quitting, no disk configured.");
 }
 
 void
-conf_setup(noidledisk_t *disks, int nd)
+conf_setup(void)
 {
-       int i;
+       noidledisk_t *d;
 
        /*
         * Large buffer in which we'll custom-align smaller regions that can
         * overlap and will only be used one at a time then discarded.
         */
-       if ((membuf = malloc(65536)) == NULL)
-               err(EXIT_FAILURE, "malloc(65536)");
+       if ((membuf = malloc(BLOCK_BUFSIZE)) == NULL)
+               err(EXIT_FAILURE, "malloc()");
 
-       for (i = 0; i < nd; i++) {
+       SLIST_FOREACH(d, &disks, next) {
                int pad;
-               noidledisk_t *d = &disks[i];
 
                /*
                 * O_DIRECT has no effect when already using a raw device but
@@ -226,7 +227,8 @@ conf_setup(noidledisk_t *disks, int nd)
                 * this to hopefully offer similar behavior.
                 */
                if ((d->fd = open(d->dev, O_RDONLY | O_DIRECT)) == -1)
-                       err(EXIT_FAILURE, "open(%s)", d->dev);
+                       err(EXIT_FAILURE, "hdnoidle: Cannot open \"%s\"",
+                           d->dev);
                if (disk_getsize(d->fd, &(d->size)) == -1)
                        err(EXIT_FAILURE, "disk_getsize(%s)", d->dev);
                if ((d->bsize = disk_getblocksize(d->fd)) == -1)
@@ -244,7 +246,7 @@ conf_setup(noidledisk_t *disks, int nd)
                if ((pad = (uintptr_t)d->sbuf % d->bsize) > 0)
                        pad = d->bsize - pad;
                d->sbuf += pad;
-               if (&d->sbuf[d->bsize] > &membuf[65535])
+               if (&d->sbuf[d->bsize] > &membuf[BLOCK_BUFSIZE - 1])
                        err(EXIT_FAILURE,
                            "Device block size for %s larger than expected!",
                            d->dev);
@@ -296,61 +298,57 @@ disk_getblocksize(int fd)
 int
 main(void)
 {
-       noidledisk_t *disks = NULL;
-       int nd = 0, i;
+       noidledisk_t *d;
 
-       conf_read(&disks, &nd);
-       conf_setup(disks, nd);
+       conf_read();
+       conf_setup();
 
        /*
         * Loop through configured disks, reading data and updating the offset
         * when their timer expires.
         */
        for (;;) {
+               /* Every second */
                (void)sleep(1);
-               for (i = 0; i < nd; i++) {
-                       noidledisk_t *d = &disks[i];
 
-                       if (--d->t < 1) {
-                               off_t pad;
-                               ssize_t rz;
+               SLIST_FOREACH(d, &disks, next) {
+                       off_t pad;
+                       ssize_t rz;
 
-                               /* Reset timer */
-                               d->t = d->delay;
+                       if (--d->t > 0)
+                               continue;
 
-                               /* Seek to current offset */
-                               if (lseek(d->fd, d->offset, SEEK_SET) == -1) {
-                                       warn("seek(%s, %" PRIu64 ")", d->dev,
-                                           d->offset);
-                                       continue;
-                               }
-
-                               /* Read and discard by zeroing. */
-                               if ((rz = read(d->fd, d->sbuf, d->bsize))
-                                   == -1)
-                                       warn("read(dev=%s (fd=%d), buf=%p, "
-                                           "sz=%lu) off=%" PRIu64,
-                                           d->dev, d->fd, d->sbuf,
-                                           (unsigned long)d->bsize,
+                       /* Expired, reset timer */
+                       d->t = d->delay;
+
+                       /* Seek to current offset */
+                       if (lseek(d->fd, d->offset, SEEK_SET) == -1) {
+                               warn("seek(%s, %" PRIu64 ")", d->dev,
                                            d->offset);
-                               else
-                                       (void)memset(d->sbuf, '\0', rz);
-
-                               /*
-                                * Skip forward somewhat arbitrarily hoping to
-                                * hit a non-cached block and to defeat
-                                * readahead.
-                                */
-                               d->offset += (d->bsize * 1000) +
-                                   (random() % (d->bsize * 100000));
-                               /* Align to block size */
-                               if ((pad = d->offset % d->bsize) > 0)
-                                       pad = d->bsize - pad;
-                               d->offset += pad;
-                               /* Wrap if overflow */
-                               if (d->offset + d->bsize >= d->size)
-                                       d->offset = 0;
+                               continue;
                        }
+
+                       /* Read and discard by zeroing. */
+                       if ((rz = read(d->fd, d->sbuf, d->bsize)) == -1)
+                               warn("read(dev=%s (fd=%d), buf=%p, sz=%lu) "
+                                   "off=%" PRIu64, d->dev, d->fd, d->sbuf,
+                                   (unsigned long)d->bsize, d->offset);
+                       else
+                               (void)memset(d->sbuf, '\0', rz);
+
+                       /*
+                        * Skip forward somewhat arbitrarily hoping to hit a
+                        * non-cached block and to defeat readahead.
+                        */
+                       d->offset += (d->bsize * 1000) +
+                           (random() % (d->bsize * 100000));
+                       /* Align to block size */
+                       if ((pad = d->offset % d->bsize) > 0)
+                               pad = d->bsize - pad;
+                       d->offset += pad;
+                       /* Wrap if overflow */
+                       if (d->offset + d->bsize >= d->size)
+                               d->offset = 0;
                }
        }