*/
-#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 */
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;
/*
}
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
* 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)
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);
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;
}
}