* Read 64k at a time with variable length pauses between instead of
reading variable sized blocks of data every second. This should be more efficient. Suggested & tested by: se * Add a syntax for excluding CD-ROM drives etc. Suggested by: des, se, many others... Manual page updates coming soon.
This commit is contained in:
parent
8ff7790b1e
commit
b903dc061d
@ -34,6 +34,7 @@ static const char rcsid[] =
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
@ -49,27 +50,25 @@ static const char rcsid[] =
|
||||
#define _PATH_CONF "/etc/diskcheckd.conf"
|
||||
#define _PATH_SAVE _PATH_VARDB"diskcheckd.offsets"
|
||||
|
||||
#define MAXRATE (128 << 10)
|
||||
#define MINRATE 512
|
||||
#define READ_SIZE (64 << 10)
|
||||
|
||||
struct disk {
|
||||
int fd;
|
||||
char *device;
|
||||
off_t size;
|
||||
int secsize;
|
||||
int days, rate, errors;
|
||||
int days, rate, errors, interval, next;
|
||||
};
|
||||
|
||||
volatile sig_atomic_t got_sighup = 0, got_sigterm = 0;
|
||||
|
||||
char **getdisknames(void);
|
||||
char **getdisknames(char **, int);
|
||||
off_t dseek(struct disk *, off_t, int);
|
||||
struct disk *readconf(const char *);
|
||||
void getdisksize(struct disk *);
|
||||
void logreaderror(struct disk *, int);
|
||||
void readchunk(struct disk *, char *);
|
||||
void readoffsets(struct disk *, const char *);
|
||||
void setdiskrate(struct disk *);
|
||||
void sighup(int);
|
||||
void sigterm(int);
|
||||
void updateproctitle(struct disk *);
|
||||
@ -80,7 +79,7 @@ int
|
||||
main(int argc, char *argv[]) {
|
||||
char *buf;
|
||||
struct disk *disks, *dp;
|
||||
int ch, ok;
|
||||
int ch, ok, minwait, nextwait;
|
||||
struct sigaction sa;
|
||||
int counter, debug;
|
||||
const char *conf_file, *save_file;
|
||||
@ -131,19 +130,41 @@ main(int argc, char *argv[]) {
|
||||
disks = readconf(conf_file);
|
||||
readoffsets(disks, save_file);
|
||||
|
||||
if ((buf = malloc(MAXRATE)) == NULL) {
|
||||
if ((buf = malloc(READ_SIZE)) == NULL) {
|
||||
syslog(LOG_NOTICE, "malloc failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* The main disk checking loop. */
|
||||
/* The main disk checking loop.
|
||||
*
|
||||
* We wait the shortest amount of time we need to before
|
||||
* another disk is due for a read -- this time is updated
|
||||
* in the 'nextwait' variable, which is then copied to
|
||||
* 'minwait'. After a sleep, 'minwait' is subtracted from
|
||||
* each disk's 'next' field, and when that reaches zero,
|
||||
* that disk is read again.
|
||||
*/
|
||||
counter = 0;
|
||||
minwait = 0;
|
||||
while (!got_sigterm) {
|
||||
ok = 0;
|
||||
nextwait = INT_MAX;
|
||||
for (dp = disks; dp->device != NULL; dp++)
|
||||
if (dp->fd != -1) {
|
||||
ok = 1;
|
||||
readchunk(dp, buf);
|
||||
if ((dp->next -= minwait) == 0) {
|
||||
ok = 1;
|
||||
readchunk(dp, buf);
|
||||
}
|
||||
|
||||
/* XXX debugging */
|
||||
if (dp->next < 0) {
|
||||
syslog(LOG_NOTICE,
|
||||
"dp->next < 0 for %s", dp->device);
|
||||
abort();
|
||||
}
|
||||
|
||||
if (dp->next < nextwait)
|
||||
nextwait = dp->next;
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
@ -151,13 +172,15 @@ main(int argc, char *argv[]) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (counter % 300 == 0) {
|
||||
if (counter >= 300) {
|
||||
updateproctitle(disks);
|
||||
writeoffsets(disks, save_file);
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
counter++;
|
||||
sleep(1);
|
||||
minwait = nextwait;
|
||||
sleep(minwait);
|
||||
counter += minwait;
|
||||
|
||||
if (got_sighup) {
|
||||
/*
|
||||
@ -189,7 +212,8 @@ readchunk(struct disk *dp, char *buf) {
|
||||
ssize_t n;
|
||||
int s;
|
||||
|
||||
n = read(dp->fd, buf, dp->rate);
|
||||
dp->next = dp->interval;
|
||||
n = read(dp->fd, buf, READ_SIZE);
|
||||
if (n == 0) {
|
||||
eof:
|
||||
syslog(LOG_INFO, "reached end of %s with %d errors",
|
||||
@ -203,9 +227,9 @@ readchunk(struct disk *dp, char *buf) {
|
||||
/*
|
||||
* Read error, retry in smaller chunks.
|
||||
*/
|
||||
logreaderror(dp, dp->rate);
|
||||
logreaderror(dp, READ_SIZE);
|
||||
|
||||
for (s = 0; s < dp->rate; s += 512) {
|
||||
for (s = 0; s < READ_SIZE; s += 512) {
|
||||
n = read(dp->fd, buf, 512);
|
||||
if (n == 0)
|
||||
goto eof;
|
||||
@ -464,6 +488,8 @@ readconf(const char *conf_file) {
|
||||
double dval;
|
||||
long lval;
|
||||
int linenum;
|
||||
char **skip;
|
||||
int numskip;
|
||||
|
||||
if ((fp = fopen(conf_file, "r")) == NULL) {
|
||||
syslog(LOG_NOTICE, "open %s failure: %m", conf_file);
|
||||
@ -473,6 +499,8 @@ readconf(const char *conf_file) {
|
||||
numdisks = 0;
|
||||
disks = NULL;
|
||||
linenum = 0;
|
||||
skip = NULL;
|
||||
numskip = 0;
|
||||
|
||||
/* Step 1: read and parse the configuration file. */
|
||||
while (fgets(buf, sizeof buf, fp) != NULL) {
|
||||
@ -482,6 +510,37 @@ readconf(const char *conf_file) {
|
||||
line++;
|
||||
if (*line == '#' || *line == '\n' || *line == '\0')
|
||||
continue;
|
||||
|
||||
/* First, if the line starts with '!', this is a disk name
|
||||
* to ignore. For example, '!md' will skip all '/dev/md*'
|
||||
* devices.
|
||||
*/
|
||||
if (*line == '!') {
|
||||
line++;
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
field = strsep(&line, " \t\n");
|
||||
if (field == NULL || *field == '\0') {
|
||||
syslog(LOG_NOTICE, "%s:%d: missing disk name",
|
||||
conf_file, linenum);
|
||||
continue;
|
||||
}
|
||||
|
||||
numskip++;
|
||||
if ((skip = reallocf(skip,
|
||||
numskip * sizeof (*skip))) == NULL) {
|
||||
syslog(LOG_NOTICE, "reallocf failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if ((skip[numskip-1] = strdup(field)) == NULL) {
|
||||
syslog(LOG_NOTICE, "strdup failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
fields = flags = 0;
|
||||
while ((field = strsep(&line, " \t\n")) != NULL) {
|
||||
if (*field == '\0')
|
||||
@ -509,6 +568,8 @@ readconf(const char *conf_file) {
|
||||
dp->fd = -1;
|
||||
dp->rate = -1;
|
||||
dp->size = -1;
|
||||
dp->interval = -1;
|
||||
dp->next = 0;
|
||||
break;
|
||||
case 1:
|
||||
/* size */
|
||||
@ -602,7 +663,7 @@ readconf(const char *conf_file) {
|
||||
onumdisks = numdisks;
|
||||
for (dp = disks; dp < disks + onumdisks; dp++) {
|
||||
if (strcmp(dp->device, "*") == 0) {
|
||||
for (np = np0 = getdisknames(); *np != NULL; np++) {
|
||||
for (np = np0 = getdisknames(skip, numskip); *np != NULL; np++) {
|
||||
odisks = disks;
|
||||
if ((disks = reallocf(disks,
|
||||
(numdisks + 1) * sizeof (*disks))) == NULL) {
|
||||
@ -617,6 +678,8 @@ readconf(const char *conf_file) {
|
||||
disks[numdisks].rate = dp->rate;
|
||||
disks[numdisks].size = dp->size;
|
||||
disks[numdisks].days = dp->days;
|
||||
disks[numdisks].interval = dp->interval;
|
||||
disks[numdisks].next = 0;
|
||||
disks[numdisks].device = *np;
|
||||
numdisks++;
|
||||
}
|
||||
@ -648,16 +711,20 @@ readconf(const char *conf_file) {
|
||||
dp->errors = 0;
|
||||
|
||||
/*
|
||||
* Set the rate appropriately. We read in blocks of this
|
||||
* size, so make it a power of 2 as close as possible to the
|
||||
* specified/calculated rate, and make it in the range
|
||||
* MINRATE..MAXRATE.
|
||||
* Set the rate appropriately. We always read 64KB blocks,
|
||||
* at a rate of 1 block per n seconds, where we adjust n to
|
||||
* make the overall rate close to what the user specified.
|
||||
*/
|
||||
if (dp->size < 0)
|
||||
getdisksize(dp);
|
||||
if (dp->rate < 0)
|
||||
dp->rate = dp->size / (dp->days * 86400);
|
||||
setdiskrate(dp);
|
||||
|
||||
if (dp->rate == 0)
|
||||
/* paranoia, should never really happen */
|
||||
dp->interval = READ_SIZE;
|
||||
else
|
||||
dp->interval = READ_SIZE / dp->rate;
|
||||
}
|
||||
|
||||
if (numdisks == 0) {
|
||||
@ -698,36 +765,6 @@ getdisksize(struct disk *dp) {
|
||||
dp->device, label.d_secsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the nearest power of 2 to the calculated or specified rate, limited
|
||||
* to somewhere between MINRATE and MAXRATE.
|
||||
*/
|
||||
void
|
||||
setdiskrate(struct disk *dp) {
|
||||
int s, r;
|
||||
int above, below;
|
||||
|
||||
if (dp->rate <= MINRATE)
|
||||
dp->rate = MINRATE;
|
||||
else if (dp->rate >= MAXRATE)
|
||||
dp->rate = MAXRATE;
|
||||
else {
|
||||
for (r = dp->rate, s = 0; r != 0; s++)
|
||||
r >>= 1;
|
||||
|
||||
/*
|
||||
* "above" and "below" are the closest power-of-2 numbers to the
|
||||
* calculated rate.
|
||||
*/
|
||||
above = 1 << s;
|
||||
below = 1 << (s - 1);
|
||||
if (above - dp->rate < dp->rate - below)
|
||||
dp->rate = above;
|
||||
else
|
||||
dp->rate = below;
|
||||
}
|
||||
}
|
||||
|
||||
off_t
|
||||
dseek(struct disk *dp, off_t offset, int whence) {
|
||||
off_t n;
|
||||
@ -746,10 +783,11 @@ dseek(struct disk *dp, off_t offset, int whence) {
|
||||
* is returned.
|
||||
*/
|
||||
char **
|
||||
getdisknames(void) {
|
||||
getdisknames(char **skip, int numskip) {
|
||||
char *string, *field;
|
||||
size_t size, numdisks;
|
||||
char **disks;
|
||||
int i;
|
||||
|
||||
if (sysctlbyname("kern.disks", NULL, &size, NULL, 0) != 0 &&
|
||||
errno != ENOMEM) {
|
||||
@ -768,6 +806,13 @@ getdisknames(void) {
|
||||
disks = NULL;
|
||||
numdisks = 0;
|
||||
while ((field = strsep(&string, " ")) != NULL) {
|
||||
/* check for disks we ignore */
|
||||
for (i = 0; i < numskip; i++)
|
||||
if (strncmp(field, skip[i], strlen(skip[i])) == 0)
|
||||
break;
|
||||
if (i < numskip)
|
||||
continue;
|
||||
|
||||
if ((disks = reallocf(disks,
|
||||
(numdisks + 1) * sizeof (*disks))) == NULL) {
|
||||
syslog(LOG_NOTICE, "reallocf failure: %m");
|
||||
|
Loading…
x
Reference in New Issue
Block a user