mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-14 10:09:48 +00:00
Remove `diskcheckd'. It is now in ports/sysutils.
Consensus on: freebsd-current.
This commit is contained in:
parent
49cb22edf2
commit
437cd38796
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=82475
@ -1,30 +0,0 @@
|
||||
# $FreeBSD$
|
||||
#
|
||||
# Configuration file for diskcheckd, refer to the diskcheckd.conf(5) manual
|
||||
# page for further information.
|
||||
#
|
||||
# First, a line starting with an exclamation mark should contain only one
|
||||
# extra field which specifies disk types which should not be included in
|
||||
# wildcard expansions. By default CD-ROM drives and memory disks are
|
||||
# excluded.
|
||||
#
|
||||
!acd
|
||||
!cd
|
||||
!matcd
|
||||
!mcd
|
||||
!scd
|
||||
!md
|
||||
#
|
||||
# The device name may be specified as "*", in which case all devices are
|
||||
# checked.
|
||||
#
|
||||
# The size may be specified as "*", in which case the size is automatically
|
||||
# determined from the disklabel.
|
||||
#
|
||||
# One of either "Days" or "Rate" must be specified, to specify a complete
|
||||
# scan should happen once every n days, or that scanning should take place
|
||||
# at n KB/s. Note that the "Days" value will not cause a scan exactly every
|
||||
# n days since diskcheckd will always read in power-of-2 size blocks.
|
||||
#
|
||||
# Device Size Days Rate
|
||||
* * 28 *
|
@ -1,9 +0,0 @@
|
||||
# $FreeBSD$
|
||||
|
||||
BINDIR?= /usr/sbin
|
||||
|
||||
PROG= diskcheckd
|
||||
MAN8= diskcheckd.8
|
||||
MLINKS= diskcheckd.8 diskcheckd.conf.5
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -1,190 +0,0 @@
|
||||
.\" Copyright (c) 2000, 2001 Ben Smithurst <ben@FreeBSD.org>
|
||||
.\" All rights reserved.
|
||||
.\"
|
||||
.\" Redistribution and use in source and binary forms, with or without
|
||||
.\" modification, are permitted provided that the following conditions
|
||||
.\" are met:
|
||||
.\" 1. Redistributions of source code must retain the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer.
|
||||
.\" 2. Redistributions in binary form must reproduce the above copyright
|
||||
.\" notice, this list of conditions and the following disclaimer in the
|
||||
.\" documentation and/or other materials provided with the distribution.
|
||||
.\"
|
||||
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
.\" SUCH DAMAGE.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd July 4, 2001
|
||||
.Dt DISKCHECKD 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm diskcheckd
|
||||
.Nd daemon to check for disk read errors
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl d
|
||||
.Op Fl f Ar conf_file
|
||||
.Op Fl o Ar save_file
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is a daemon which runs in the background,
|
||||
reading entire disks to find any read errors on those disks.
|
||||
The disks which should be scanned,
|
||||
and the rates at which they should be scanned,
|
||||
must be specified in the configuration file,
|
||||
which is
|
||||
.Pa /etc/diskcheckd.conf
|
||||
by default.
|
||||
.Pp
|
||||
Any blank lines or lines starting with a
|
||||
.Ql #
|
||||
character in this file are ignored.
|
||||
Each non-comment line of this file must be in one of two formats.
|
||||
The first format is
|
||||
.Ql !xx ,
|
||||
and specifies that device names matching
|
||||
.Pa /dev/xx*
|
||||
should not be included in expansion of wildcards (see below).
|
||||
The second format consists of four white space separated
|
||||
fields,
|
||||
which are the full pathname of the disk device,
|
||||
the size of that disk,
|
||||
the frequency in days at which to check that disk,
|
||||
and the rate in kilobytes per second at which to check this disk.
|
||||
Naturally,
|
||||
it would be contradictory to specify both the frequency and the rate,
|
||||
so only one of these should be specified.
|
||||
Additionally,
|
||||
the size of the disk should not be specified if the rate is specified,
|
||||
as this information is unnecessary.
|
||||
.Pp
|
||||
If the disk is specified as
|
||||
.Dq * ,
|
||||
then
|
||||
.Nm
|
||||
will apply the given settings to all disks in the system,
|
||||
obtained using the
|
||||
.Va kern.disks
|
||||
sysctl variable.
|
||||
If the size is specified as
|
||||
.Dq *
|
||||
(recommended),
|
||||
then the size of the disk will be automatically determined from the
|
||||
disklabel,
|
||||
if possible.
|
||||
Fields which are not specified should contain a single
|
||||
.Dq *
|
||||
character.
|
||||
.Pp
|
||||
Note that
|
||||
.Nm
|
||||
always reads data from the disk in 64KB blocks,
|
||||
so the rate your specify may not be the exact rate at which the disk is
|
||||
actually checked.
|
||||
Similarly,
|
||||
if you specify the third field (days for complete scan) it is unlikely
|
||||
that a complete scan will actually take exactly this many days.
|
||||
.Pp
|
||||
To run
|
||||
.Nm
|
||||
automatically at boot time,
|
||||
the
|
||||
.Va diskcheckd_enable
|
||||
variable in
|
||||
.Xr rc.conf 5
|
||||
should be set to
|
||||
.Dq YES .
|
||||
.Pp
|
||||
When
|
||||
.Nm
|
||||
receives a
|
||||
.Dv SIGTERM
|
||||
or
|
||||
.Dv SIGINT
|
||||
signal,
|
||||
it saves its current state information to a file,
|
||||
so that after a reboot
|
||||
.Nm
|
||||
can resume reading from where it left off,
|
||||
rather than starting from the beginning of the disk again.
|
||||
The information saved to this file consists of the device filename and the
|
||||
current offset into that device.
|
||||
.Pp
|
||||
.Nm
|
||||
can be instructed to reload the configuration file by sending it a
|
||||
.Dv SIGHUP
|
||||
signal.
|
||||
.Pp
|
||||
.Nm
|
||||
accepts the following command line options:
|
||||
.Bl -tag -width Fl
|
||||
.It Fl d
|
||||
If this flag is specified,
|
||||
.Nm
|
||||
will not fork into the background and detach from its controlling terminal
|
||||
to become a daemon.
|
||||
This flag is primarily used for debugging.
|
||||
.It Fl f
|
||||
Specify the configuration file to use,
|
||||
instead of the default
|
||||
.Pa /etc/diskcheckd.conf .
|
||||
.It Fl o
|
||||
Specify the file to save disk offsets to,
|
||||
instead of the default
|
||||
.Pa /var/db/diskcheckd.offsets .
|
||||
.El
|
||||
.Sh FILES
|
||||
.Bl -tag -width /var/db/diskcheckd.offsets -compact
|
||||
.It Pa /etc/diskcheckd.conf
|
||||
Default configuration file.
|
||||
.It Pa /var/db/diskcheckd.offsets
|
||||
Default location of saved offsets.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
To check all of
|
||||
.Pa /dev/ad0
|
||||
for errors once every two weeks,
|
||||
use this entry in
|
||||
.Pa diskcheckd.conf :
|
||||
.Bd -literal -offset indent
|
||||
/dev/ad0 * 14 *
|
||||
.Ed
|
||||
.Pp
|
||||
To check the first SCSI disk for errors at approximately 64KB/s,
|
||||
use the following entry:
|
||||
.Bd -literal -offset indent
|
||||
/dev/da0 * * 64
|
||||
.Ed
|
||||
.Pp
|
||||
To check all disks once every four weeks:
|
||||
.Bd -literal -offset indent
|
||||
* * 28 *
|
||||
.Ed
|
||||
.Sh DIAGNOSTICS
|
||||
If any errors occur,
|
||||
they will be written to
|
||||
.Xr syslogd 8 .
|
||||
.Sh HISTORY
|
||||
.Nm
|
||||
first appeared in
|
||||
.Fx 5.0 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
.Nm
|
||||
and this manual page were written by
|
||||
.An Ben Smithurst Aq ben@FreeBSD.org ,
|
||||
with input from
|
||||
.An Poul-Henning Kamp Aq phk@FreeBSD.org .
|
||||
.Sh BUGS
|
||||
.Nm
|
||||
assumes all disks have 512 byte sectors.
|
@ -1,863 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2000, 2001 Ben Smithurst <ben@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
static const char rcsid[] =
|
||||
"$FreeBSD$";
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define DKTYPENAMES
|
||||
#include <sys/disklabel.h>
|
||||
|
||||
#define _PATH_CONF "/etc/diskcheckd.conf"
|
||||
#define _PATH_SAVE _PATH_VARDB"diskcheckd.offsets"
|
||||
|
||||
#define READ_SIZE (64 << 10)
|
||||
|
||||
struct disk {
|
||||
int fd;
|
||||
char *device;
|
||||
off_t size;
|
||||
int secsize;
|
||||
int days, rate, errors, interval, next;
|
||||
};
|
||||
|
||||
volatile sig_atomic_t got_sighup = 0, got_sigterm = 0;
|
||||
|
||||
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 sighup(int);
|
||||
void sigterm(int);
|
||||
void updateproctitle(struct disk *);
|
||||
void usage(void);
|
||||
void writeoffsets(struct disk *, const char *);
|
||||
|
||||
int
|
||||
main(int argc, char *argv[]) {
|
||||
char *buf;
|
||||
struct disk *disks, *dp;
|
||||
int ch, ok, minwait, nextwait;
|
||||
struct sigaction sa;
|
||||
int counter, debug;
|
||||
const char *conf_file, *save_file;
|
||||
|
||||
conf_file = _PATH_CONF;
|
||||
save_file = _PATH_SAVE;
|
||||
debug = 0;
|
||||
|
||||
while ((ch = getopt(argc, argv, "df:o:")) != -1)
|
||||
switch (ch) {
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
case 'f':
|
||||
conf_file = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
save_file = optarg;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
argv += optind;
|
||||
argc -= optind;
|
||||
|
||||
if (argc != 0)
|
||||
usage();
|
||||
|
||||
openlog("diskcheckd", LOG_CONS|LOG_PID, LOG_DAEMON);
|
||||
|
||||
if (!debug && daemon(0, 0) < 0) {
|
||||
syslog(LOG_NOTICE, "daemon failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
sa.sa_handler = sigterm;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
|
||||
sa.sa_handler = sighup;
|
||||
sigaction(SIGHUP, &sa, NULL);
|
||||
|
||||
/* Read the configuration file and the saved offsets */
|
||||
disks = readconf(conf_file);
|
||||
readoffsets(disks, save_file);
|
||||
|
||||
if ((buf = malloc(READ_SIZE)) == NULL) {
|
||||
syslog(LOG_NOTICE, "malloc failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
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) {
|
||||
syslog(LOG_EMERG, "all disks had read errors");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (counter >= 300) {
|
||||
updateproctitle(disks);
|
||||
writeoffsets(disks, save_file);
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
minwait = nextwait;
|
||||
sleep(minwait);
|
||||
counter += minwait;
|
||||
|
||||
if (got_sighup) {
|
||||
/*
|
||||
* Got a SIGHUP, so save the offsets, free the
|
||||
* memory used for the disk structures, and then
|
||||
* re-read the config file and the disk offsets.
|
||||
*/
|
||||
writeoffsets(disks, save_file);
|
||||
for (dp = disks; dp->device != NULL; dp++) {
|
||||
free(dp->device);
|
||||
close(dp->fd);
|
||||
}
|
||||
free(disks);
|
||||
disks = readconf(conf_file);
|
||||
readoffsets(disks, save_file);
|
||||
got_sighup = 0;
|
||||
}
|
||||
}
|
||||
|
||||
writeoffsets(disks, save_file);
|
||||
return (EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the next chunk from the specified disk, retrying if necessary.
|
||||
*/
|
||||
void
|
||||
readchunk(struct disk *dp, char *buf) {
|
||||
ssize_t n;
|
||||
int s;
|
||||
|
||||
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",
|
||||
dp->device, dp->errors);
|
||||
dseek(dp, 0, SEEK_SET);
|
||||
dp->errors = 0;
|
||||
return;
|
||||
} else if (n > 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Read error, retry in smaller chunks.
|
||||
*/
|
||||
logreaderror(dp, READ_SIZE);
|
||||
|
||||
for (s = 0; s < READ_SIZE; s += 512) {
|
||||
n = read(dp->fd, buf, 512);
|
||||
if (n == 0)
|
||||
goto eof;
|
||||
else if (n < 0) {
|
||||
/* log the error and seek past it. */
|
||||
logreaderror(dp, 512);
|
||||
dseek(dp, 512, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
fstypename(u_int8_t type) {
|
||||
static char buf[32];
|
||||
|
||||
if (type < FSMAXTYPES)
|
||||
return (fstypenames[type]);
|
||||
else {
|
||||
snprintf(buf, sizeof buf, "%u", type);
|
||||
return (buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Report a read error, logging how many bytes were trying to be read, which
|
||||
* sector they were being read from, and try to also find out what that sector
|
||||
* is used for.
|
||||
*/
|
||||
void
|
||||
logreaderror(struct disk *dp, int nbytes) {
|
||||
quad_t secno;
|
||||
off_t saved_offset;
|
||||
int fd, slice, part;
|
||||
struct dos_partition *dos;
|
||||
struct disklabel label;
|
||||
char buf[512];
|
||||
char newdev[512];
|
||||
|
||||
saved_offset = dseek(dp, 0, SEEK_CUR);
|
||||
secno = (quad_t)saved_offset / dp->secsize;
|
||||
dp->errors++;
|
||||
|
||||
syslog(LOG_NOTICE, "error reading %d bytes from sector %qd on %s",
|
||||
nbytes, secno, dp->device);
|
||||
|
||||
/*
|
||||
* First, find out which slice it's in. To do this, we seek to the
|
||||
* start of the disk, read the first sector, and go through the DOS
|
||||
* slice table.
|
||||
*/
|
||||
if (dseek(dp, 0, SEEK_SET) == -1)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (read(dp->fd, buf, sizeof buf) != sizeof buf) {
|
||||
dseek(dp, saved_offset, SEEK_SET);
|
||||
return;
|
||||
}
|
||||
|
||||
/* seek back to where we were */
|
||||
if (dseek(dp, saved_offset, SEEK_SET) == -1)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
dos = (struct dos_partition *)&buf[DOSPARTOFF];
|
||||
for (slice = 0; slice < NDOSPART; slice++)
|
||||
if (dos[slice].dp_start <= secno &&
|
||||
secno < dos[slice].dp_start + dos[slice].dp_size)
|
||||
break;
|
||||
|
||||
if (slice == NDOSPART) {
|
||||
syslog(LOG_NOTICE,
|
||||
"sector %qd on %s doesn't appear "
|
||||
"to be within any DOS slice", secno, dp->device);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make secno relative to this slice */
|
||||
secno -= dos[slice].dp_start;
|
||||
|
||||
snprintf(newdev, sizeof newdev, "%ss%d", dp->device, slice + 1);
|
||||
syslog(LOG_DEBUG, "bad sector seems to be within %s", newdev);
|
||||
|
||||
/* Check the type of that partition. */
|
||||
if (dos[slice].dp_typ != DOSPTYP_386BSD) {
|
||||
/* If not a BSD slice, we can't do much more. */
|
||||
syslog(LOG_NOTICE, "last bad sector is sector %qd "
|
||||
"on device %s, type %02x", secno, newdev,
|
||||
dos[slice].dp_typ);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((fd = open(newdev, O_RDONLY)) < 0) {
|
||||
syslog(LOG_NOTICE, "open %s failure: %m", newdev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to read the disklabel from that device. */
|
||||
if (ioctl(fd, DIOCGDINFO, &label) < 0) {
|
||||
syslog(LOG_NOTICE, "DIOCGDINFO on %s failed: %m",
|
||||
newdev);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check which partition this sector is in. */
|
||||
for (part = 0; part < MAXPARTITIONS; part++)
|
||||
if (part != 2 && /* skip 'c' partition */
|
||||
label.d_partitions[part].p_offset <= secno &&
|
||||
secno < label.d_partitions[part].p_offset +
|
||||
label.d_partitions[part].p_size)
|
||||
break;
|
||||
|
||||
if (part == MAXPARTITIONS) {
|
||||
syslog(LOG_NOTICE,
|
||||
"sector %qd on %s doesn't appear "
|
||||
"to be within any BSD partition", secno, newdev);
|
||||
return;
|
||||
}
|
||||
|
||||
secno -= label.d_partitions[part].p_offset;
|
||||
snprintf(newdev, sizeof newdev, "%ss%d%c",
|
||||
dp->device, slice + 1, 'a' + part);
|
||||
syslog(LOG_DEBUG, "bad sector seems to be within %s", newdev);
|
||||
if (label.d_partitions[part].p_fstype != FS_BSDFFS) {
|
||||
/* Again, if not a BSD partition, can't do much. */
|
||||
syslog(LOG_NOTICE, "last bad sector is sector %qd "
|
||||
"on device %s, type %s", secno, newdev,
|
||||
fstypename(label.d_partitions[part].p_fstype));
|
||||
return;
|
||||
}
|
||||
|
||||
syslog(LOG_NOTICE, "last bad sector is sector %qd "
|
||||
"on 4.2BSD filesystem %s", secno, newdev);
|
||||
|
||||
/*
|
||||
* XXX: todo: find out which file on the BSD filesystem uses this
|
||||
* sector...
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the offsets written by writeoffsets().
|
||||
*/
|
||||
void
|
||||
readoffsets(struct disk *disks, const char *save_file) {
|
||||
FILE *fp;
|
||||
struct disk *dp;
|
||||
char *space, buf[1024];
|
||||
|
||||
if ((fp = fopen(save_file, "r")) == NULL) {
|
||||
if (errno != ENOENT)
|
||||
syslog(LOG_NOTICE, "open %s failed: %m", save_file);
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(buf, sizeof buf, fp) != NULL) {
|
||||
if ((space = strchr(buf, ' ')) == NULL)
|
||||
continue;
|
||||
*space = '\0';
|
||||
|
||||
for (dp = disks;
|
||||
dp->device != NULL && strcmp(dp->device, buf) != 0; dp++)
|
||||
; /* nothing */
|
||||
|
||||
if (dp->device != NULL)
|
||||
dseek(dp, (off_t)strtoq(space + 1, NULL, 0), SEEK_SET);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Save the offsets we've reached for each disk to a file, so we can start
|
||||
* at that position next time the daemon is started.
|
||||
*/
|
||||
void
|
||||
writeoffsets(struct disk *disks, const char *save_file) {
|
||||
FILE *fp;
|
||||
struct disk *dp;
|
||||
|
||||
if ((fp = fopen(save_file, "w")) == NULL) {
|
||||
syslog(LOG_NOTICE, "open %s failed: %m", save_file);
|
||||
return;
|
||||
}
|
||||
|
||||
for (dp = disks; dp->device != NULL; dp++)
|
||||
if (strcmp(dp->device, "*") != 0)
|
||||
fprintf(fp, "%s %qd\n", dp->device,
|
||||
(quad_t)dseek(dp, 0, SEEK_CUR));
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the process title so it's easy to see using ps(1) how much has been
|
||||
* done.
|
||||
*/
|
||||
void
|
||||
updateproctitle(struct disk *disks) {
|
||||
struct disk *dp;
|
||||
char *bp, *p;
|
||||
static char *buf;
|
||||
static size_t bufsize;
|
||||
size_t size;
|
||||
int inc, ret;
|
||||
double percent;
|
||||
|
||||
bp = buf;
|
||||
size = bufsize;
|
||||
for (dp = disks; dp->device != NULL; dp++) {
|
||||
p = dp->device;
|
||||
if (strcmp(p, "*") == 0)
|
||||
continue;
|
||||
if (strncmp(p, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
|
||||
p += sizeof _PATH_DEV - 1;
|
||||
|
||||
percent = 100 * (double)dseek(dp, 0, SEEK_CUR) / dp->size;
|
||||
if ((ret = snprintf(bp, size, "%s %.2f%%, ", p, percent)) < 0)
|
||||
ret = 0;
|
||||
if ((size_t)ret >= size) {
|
||||
inc = ((ret + 1023) >> 10) << 10;
|
||||
size += inc;
|
||||
bufsize += inc;
|
||||
if ((buf = reallocf(buf, bufsize)) == NULL) {
|
||||
/* Not fatal. */
|
||||
syslog(LOG_NOTICE, "reallocf failure: %m");
|
||||
bufsize = 0;
|
||||
return;
|
||||
}
|
||||
bp = buf + bufsize - size;
|
||||
ret = snprintf(bp, size, "%s %.2f%%, ", p, percent);
|
||||
if (ret < 0)
|
||||
ret = 0;
|
||||
}
|
||||
bp += ret;
|
||||
size -= ret;
|
||||
}
|
||||
|
||||
if (buf != NULL) {
|
||||
/* Remove the trailing comma. */
|
||||
if (&bp[-2] >= buf)
|
||||
bp[-2] = '\0';
|
||||
setproctitle("%s", buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* used to keep track of which fields have been specified */
|
||||
#define FL_SIZE_SPEC 1
|
||||
#define FL_DAYS_SPEC 2
|
||||
#define FL_RATE_SPEC 4
|
||||
|
||||
/*
|
||||
* Read the configuration file, set the rate appropriately for each disk,
|
||||
* and get a file descriptor for each disk.
|
||||
*/
|
||||
struct disk *
|
||||
readconf(const char *conf_file) {
|
||||
FILE *fp;
|
||||
char buf[1024], *line, *field, *ep, **np, **np0;
|
||||
int fields, flags;
|
||||
struct disk *disks, *dp, *odisks;
|
||||
int numdisks, onumdisks;
|
||||
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);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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) {
|
||||
line = buf;
|
||||
linenum++;
|
||||
while (isspace(*line))
|
||||
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')
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If this is the first field on a line, allocate
|
||||
* space for one more disk structure.
|
||||
*/
|
||||
if (fields == 0 && (disks = reallocf(disks,
|
||||
(numdisks + 1) * sizeof (*disks))) == NULL) {
|
||||
syslog(LOG_NOTICE, "reallocf failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dp = &disks[numdisks];
|
||||
switch (fields++) {
|
||||
case 0:
|
||||
/* device name */
|
||||
if ((dp->device = strdup(field)) == NULL) {
|
||||
syslog(LOG_NOTICE,
|
||||
"strdup failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
dp->fd = -1;
|
||||
dp->rate = -1;
|
||||
dp->size = -1;
|
||||
dp->interval = -1;
|
||||
dp->next = 0;
|
||||
break;
|
||||
case 1:
|
||||
/* size */
|
||||
if (strcmp(field, "*") == 0)
|
||||
break;
|
||||
flags |= FL_SIZE_SPEC;
|
||||
dval = strtod(field, &ep);
|
||||
if (dval < 0) {
|
||||
syslog(LOG_NOTICE,
|
||||
"%s:%d: size cannot be negative",
|
||||
conf_file, linenum);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (strcasecmp(ep, "M") == 0)
|
||||
dp->size = dval * 1024 * 1024;
|
||||
else if (strcasecmp(ep, "G") == 0)
|
||||
dp->size = dval * 1024 * 1024 * 1024;
|
||||
else if (*ep == '\0')
|
||||
dp->size = dval;
|
||||
else {
|
||||
syslog(LOG_NOTICE,
|
||||
"%s:%d: bad suffix \"%s\" on "
|
||||
"size \"%s\"", conf_file,
|
||||
linenum, ep, field);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* days */
|
||||
if (strcmp(field, "*") == 0)
|
||||
break;
|
||||
flags |= FL_DAYS_SPEC;
|
||||
lval = strtol(field, &ep, 0);
|
||||
if (ep == field || *ep != '\0' ||
|
||||
lval <= 0) {
|
||||
syslog(LOG_NOTICE,
|
||||
"%s:%d: bad number of days",
|
||||
conf_file, linenum);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
dp->days = lval;
|
||||
break;
|
||||
case 3:
|
||||
/* rate */
|
||||
if (strcmp(field, "*") == 0)
|
||||
break;
|
||||
flags |= FL_RATE_SPEC;
|
||||
lval = strtol(field, &ep, 0);
|
||||
if (ep == field || *ep != '\0' ||
|
||||
lval <= 0) {
|
||||
syslog(LOG_NOTICE, "%s:%d: bad rate",
|
||||
conf_file, linenum);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
dp->rate = lval * 1024;
|
||||
break;
|
||||
default:
|
||||
/* Report error at end of line. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fields != 4) {
|
||||
syslog(LOG_NOTICE,
|
||||
"%s:%d: %d fields, should be 4",
|
||||
conf_file, linenum, fields);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (flags != (FL_SIZE_SPEC|FL_DAYS_SPEC) &&
|
||||
flags != FL_DAYS_SPEC &&
|
||||
flags != FL_RATE_SPEC) {
|
||||
syslog(LOG_NOTICE,
|
||||
"%s:%d: should specify frequency "
|
||||
"or rate, not both", conf_file,
|
||||
linenum);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
numdisks++;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
if (numdisks == 0) {
|
||||
syslog(LOG_NOTICE, "no disks specified");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Step 2: expand any dp->device == "*" entries. onumdisks is used
|
||||
* so that we don't keep checking dp->device for the new disks as
|
||||
* they're added, since that's pointless.
|
||||
*/
|
||||
onumdisks = numdisks;
|
||||
for (dp = disks; dp < disks + onumdisks; dp++) {
|
||||
if (strcmp(dp->device, "*") == 0) {
|
||||
for (np = np0 = getdisknames(skip, numskip); *np != NULL; np++) {
|
||||
odisks = disks;
|
||||
if ((disks = reallocf(disks,
|
||||
(numdisks + 1) * sizeof (*disks))) == NULL) {
|
||||
syslog(LOG_NOTICE,
|
||||
"reallocf failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* "disks" pointer may have changed. */
|
||||
dp = disks + (dp - odisks);
|
||||
|
||||
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++;
|
||||
}
|
||||
|
||||
/* Don't free the individual pointers, since they're
|
||||
* used as new dp->device entries.
|
||||
*/
|
||||
free(np0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Step 3: open all the disks and set the rate to check at
|
||||
* appropriately. Use "onumdisks" because numdisks changes within
|
||||
* the loop.
|
||||
*/
|
||||
onumdisks = numdisks;
|
||||
for (dp = disks; dp < disks + onumdisks; dp++) {
|
||||
if (strcmp(dp->device, "*") == 0) {
|
||||
numdisks--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((dp->fd = open(dp->device, O_RDONLY)) < 0) {
|
||||
syslog(LOG_NOTICE, "open %s failed: %m", dp->device);
|
||||
numdisks--;
|
||||
continue;
|
||||
}
|
||||
|
||||
dp->errors = 0;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
if (dp->rate == 0)
|
||||
/* paranoia, should never really happen */
|
||||
dp->interval = READ_SIZE;
|
||||
else
|
||||
dp->interval = READ_SIZE / dp->rate;
|
||||
}
|
||||
|
||||
if (numdisks == 0) {
|
||||
syslog(LOG_NOTICE, "no disks usable");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Add a final entry with dp->device == NULL to end the array. */
|
||||
if ((disks = reallocf(disks,
|
||||
(onumdisks + 1) * sizeof (*disks))) == NULL) {
|
||||
syslog(LOG_NOTICE, "reallocf failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
disks[onumdisks].device = NULL;
|
||||
|
||||
return (disks);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read in the specified disk's size from the disklabel.
|
||||
*/
|
||||
void
|
||||
getdisksize(struct disk *dp) {
|
||||
struct disklabel label;
|
||||
|
||||
if (ioctl(dp->fd, DIOCGDINFO, &label) < 0) {
|
||||
syslog(LOG_NOTICE, "DIOCGDINFO on %s failed: %m",
|
||||
dp->device);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dp->secsize = label.d_secsize;
|
||||
dp->size = (off_t)label.d_secperunit * label.d_secsize;
|
||||
|
||||
if (label.d_secsize != 512)
|
||||
syslog(LOG_NOTICE,
|
||||
"%s has %d byte sectors, may cause minor problems",
|
||||
dp->device, label.d_secsize);
|
||||
}
|
||||
|
||||
off_t
|
||||
dseek(struct disk *dp, off_t offset, int whence) {
|
||||
off_t n;
|
||||
|
||||
if ((n = lseek(dp->fd, offset, whence)) == -1) {
|
||||
syslog(LOG_NOTICE, "seek failure on %s: %m", dp->device);
|
||||
close(dp->fd);
|
||||
dp->fd = -1;
|
||||
}
|
||||
return (n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to get a list of all disk names using the `kern.disks' sysctl
|
||||
* variable. An NULL terminated list of pointers to these disks' pathnames
|
||||
* is returned.
|
||||
*/
|
||||
char **
|
||||
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) {
|
||||
syslog(LOG_NOTICE, "sysctl kern.disks failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if ((string = malloc(size)) == NULL) {
|
||||
syslog(LOG_NOTICE, "malloc failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (sysctlbyname("kern.disks", string, &size, NULL, 0) != 0) {
|
||||
syslog(LOG_NOTICE, "sysctl kern.disks failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
if (asprintf(&disks[numdisks],
|
||||
"%s%s", _PATH_DEV, field) < 0) {
|
||||
syslog(LOG_NOTICE, "asprintf failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
numdisks++;
|
||||
}
|
||||
|
||||
if ((disks = reallocf(disks,
|
||||
(numdisks + 1) * sizeof (*disks))) == NULL) {
|
||||
syslog(LOG_NOTICE, "strdup failure: %m");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
disks[numdisks] = NULL;
|
||||
|
||||
free(string);
|
||||
return (disks);
|
||||
}
|
||||
|
||||
void
|
||||
sighup(int sig) {
|
||||
|
||||
sig = sig;
|
||||
got_sighup = 1;
|
||||
}
|
||||
|
||||
void
|
||||
sigterm(int sig) {
|
||||
|
||||
sig = sig;
|
||||
got_sigterm = 1;
|
||||
}
|
||||
|
||||
void
|
||||
usage(void) {
|
||||
|
||||
fprintf(stderr,
|
||||
"usage: diskcheckd [-d] [-f conf_file] [-o save_file]\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
Loading…
Reference in New Issue
Block a user