- revert back to vmcore.#

- reimplement -z
- use syslog()
- improve consistancy of messages
- allow -f to recover cleared dumps
- return bufsize to 1024 * 1024
- return the ability to write sparse files
- update man page
- fix minfree to require 2k for info file instead of the kernel size
- include Berkeley copyright too due to amount of old code copied

Submitted by:	Chad David <davidc@acns.ab.ca>
This commit is contained in:
Bill Fenner 2002-05-05 01:04:00 +00:00
parent f5a8f1482c
commit edc4f96e24
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=96049
3 changed files with 272 additions and 110 deletions

View File

@ -1,7 +1,8 @@
# $FreeBSD$
PROG= savecore
WARNS= 4
NOMAN= sorry, not yet.
LDADD= -lmd
MAN8= savecore.8
DPADD+= ${LIBZ}
LDADD+= -lz
.include <bsd.prog.mk>

View File

@ -43,12 +43,14 @@
.Fl c
.Nm
.Op Fl fkvz
.Op Fl N Ar system
.Ar directory
.Op Ar directory Op Ar device ...
.Sh DESCRIPTION
.Nm Savecore
copies the currently running kernel and its associated core dump into
copies a core dump into
.Fa directory ,
or the current working directory if no
.Fa directory
argument is given,
and enters a reboot message and information about the core dump into
the system log.
.Pp
@ -59,15 +61,9 @@ Clear the dump, so that future invocations of
.Nm
will ignore it.
.It Fl f
Force a dump to be taken even if the dump doesn't appear correct or there
is insufficient disk space.
Force a dump to be taken even if the dump was cleared.
.It Fl k
Do not clear the dump after saving it.
.It Fl N
Use
.Ar system
as the kernel instead of the running kernel (as determined from
.Xr getbootfile 3 ) .
.It Fl v
Print out some additional debugging information.
.It Fl z
@ -76,12 +72,17 @@ Compress the core dump and kernel (see
.El
.Pp
.Nm Savecore
checks the core dump in various ways to make sure that it is current and
that it corresponds to the currently running system.
looks for dumps on each device specified by the
.Ar device
argument(s), or on each device in
.Pa /etc/fstab
marked as "dump" or "swap".
.Nm Savecore
checks the core dump in various ways to make sure that it is complete.
If it passes these checks, it saves the core image in
.Ar directory Ns Pa /vmcore.#
and the system in
.Ar directory Ns Pa /kernel.#
and information about the core in
.Ar directory Ns Pa /info.#
The ``#'' is the number from the first line of the file
.Ar directory Ns Pa /bounds ,
and it is incremented and stored back into the file each time
@ -111,7 +112,7 @@ is meant to be called near the end of the initialization file
(see
.Xr rc 8 ) .
.Sh BUGS
The minfree code does not consider the effect of compression.
The minfree code does not consider the effect of compression or sparse files.
.Sh SEE ALSO
.Xr gzip 1 ,
.Xr getbootfile 3 ,

View File

@ -31,35 +31,68 @@
* 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.
*
* Copyright (c) 1986, 1992, 1993
* The Regents of the University of California. 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/disk.h>
#include <sys/kerneldump.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <fstab.h>
#include <md5.h>
#include <paths.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include <unistd.h>
int clear, force, keep, verbose; /* flags */
int nfound, nsaved, nerr; /* statistics */
int compress, clear, force, keep, verbose; /* flags */
int nfound, nsaved, nerr; /* statistics */
extern FILE *zopen(const char *, const char *);
static void
printheader(FILE *f, const struct kerneldumpheader *h, const char *device,
const char *md5)
int bounds)
{
uint64_t dumplen;
time_t t;
@ -77,10 +110,51 @@ printheader(FILE *f, const struct kerneldumpheader *h, const char *device,
fprintf(f, " Hostname: %s\n", h->hostname);
fprintf(f, " Versionstring: %s", h->versionstring);
fprintf(f, " Panicstring: %s\n", h->panicstring);
fprintf(f, " MD5: %s\n", md5);
fprintf(f, " Bounds: %d\n", bounds);
fflush(f);
}
static int
getbounds(void) {
FILE *fp;
char buf[6];
int ret;
ret = 0;
if ((fp = fopen("bounds", "r")) == NULL) {
syslog(LOG_WARNING, "unable to open bounds file, using 0");
goto newfile;
}
if (fgets(buf, sizeof buf, fp) == NULL) {
syslog(LOG_WARNING, "unable to read from bounds, using 0");
fclose(fp);
goto newfile;
}
errno = 0;
ret = (int)strtol(buf, NULL, 10);
if (ret == 0 && (errno == EINVAL || errno == ERANGE))
syslog(LOG_WARNING, "invalid value found in bounds, using 0");
newfile:
if ((fp = fopen("bounds", "w")) == NULL) {
syslog(LOG_WARNING, "unable to write to bounds file: %m");
goto done;
}
if (verbose)
printf("bounds number: %d\n", ret);
fprintf(fp, "%d\n", (ret + 1));
fclose(fp);
done:
return (ret);
}
/*
* Check that sufficient space is available on the disk that holds the
* save directory.
@ -89,19 +163,14 @@ static int
check_space(char *savedir, off_t dumpsize)
{
FILE *fp;
const char *tkernel;
off_t minfree, spacefree, totfree, kernelsize, needed;
struct stat st;
off_t minfree, spacefree, totfree, needed;
struct statfs fsbuf;
char buf[100], path[MAXPATHLEN];
tkernel = getbootfile();
if (stat(tkernel, &st) < 0)
err(1, "%s", tkernel);
kernelsize = st.st_blocks * S_BLKSIZE;
if (statfs(savedir, &fsbuf) < 0)
err(1, "%s", savedir);
if (statfs(savedir, &fsbuf) < 0) {
syslog(LOG_ERR, "%s: %m", savedir);
exit(1);
}
spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
@ -116,71 +185,92 @@ check_space(char *savedir, off_t dumpsize)
(void)fclose(fp);
}
needed = (dumpsize + kernelsize) / 1024;
needed = dumpsize / 1024 + 2; /* 2 for info file */
if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
warnx("no dump, not enough free space on device"
" (%lld available, need %lld)",
syslog(LOG_WARNING,
"no dump, not enough free space on device (%lld available, need %lld)",
(long long)(minfree > 0 ? spacefree : totfree),
(long long)needed);
return (0);
}
if (spacefree - needed < 0)
warnx("dump performed, but free space threshold crossed");
syslog(LOG_WARNING,
"dump performed, but free space threshold crossed");
return (1);
}
#define BLOCKSIZE (1<<12)
#define BLOCKMASK (~(BLOCKSIZE-1))
static void
DoFile(char *savedir, const char *device)
{
struct kerneldumpheader kdhf, kdhl;
char buf[BUFSIZ];
struct stat sb;
off_t mediasize, dumpsize, firsthd, lasthd;
char *md5;
FILE *info;
int fd, fdcore, fdinfo, error, wl;
char buf[1024 * 1024];
off_t mediasize, dumpsize, firsthd, lasthd, dmpcnt;
FILE *info, *fp;
int fd, fdinfo, error, wl;
int nr, nw, hs, he;
int bounds;
u_int sectorsize;
mode_t oumask;
dmpcnt = 0;
mediasize = 0;
if (verbose)
printf("Checking for kernel dump on device %s\n", device);
printf("checking for kernel dump on device %s\n", device);
mediasize = 0;
fd = open(device, O_RDWR);
if (fd < 0) {
warn("%s", device);
syslog(LOG_ERR, "%s: %m", device);
return;
}
error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
if (!error)
error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
if (error) {
warn("couldn't find media and/or sector size of %s", device);
syslog(LOG_ERR,
"couldn't find media and/or sector size of %s: %m", device);
goto closefd;
}
if (verbose) {
printf("Mediasize = %lld\n", (long long)mediasize);
printf("Sectorsize = %u\n", sectorsize);
printf("mediasize = %lld\n", (long long)mediasize);
printf("sectorsize = %u\n", sectorsize);
}
lasthd = mediasize - sectorsize;
lseek(fd, lasthd, SEEK_SET);
error = read(fd, &kdhl, sizeof kdhl);
if (error != sizeof kdhl) {
warn("error reading last dump header at offset %lld in %s",
syslog(LOG_ERR,
"error reading last dump header at offset %lld in %s: %m",
(long long)lasthd, device);
goto closefd;
}
if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
if (verbose)
warnx("magic mismatch on last dump header on %s",
printf("magic mismatch on last dump header on %s\n",
device);
goto closefd;
if (force == 0)
goto closefd;
if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
sizeof kdhl.magic) == 0) {
if (verbose)
printf("forcing magic on %s\n", device);
memcpy(kdhl.magic, KERNELDUMPMAGIC,
sizeof kdhl.magic);
} else {
syslog(LOG_ERR, "unable to force dump - bad magic");
goto closefd;
}
}
if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
warnx("unknown version (%d) in last dump header on %s",
syslog(LOG_ERR,
"unknown version (%d) in last dump header on %s",
dtoh32(kdhl.version), device);
goto closefd;
}
@ -190,7 +280,8 @@ DoFile(char *savedir, const char *device)
goto nuke;
if (kerneldump_parity(&kdhl)) {
warnx("parity error on last dump header on %s", device);
syslog(LOG_ERR,
"parity error on last dump header on %s", device);
nerr++;
goto closefd;
}
@ -199,109 +290,170 @@ DoFile(char *savedir, const char *device)
lseek(fd, firsthd, SEEK_SET);
error = read(fd, &kdhf, sizeof kdhf);
if (error != sizeof kdhf) {
warn("error reading first dump header at offset %lld in %s",
syslog(LOG_ERR,
"error reading first dump header at offset %lld in %s: %m",
(long long)firsthd, device);
nerr++;
goto closefd;
}
if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
warn("first and last dump headers disagree on %s", device);
nerr++;
goto closefd;
}
md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL);
sprintf(buf, "%s.info", md5);
/*
* See if the dump has been saved already. Don't save the dump
* again, unless 'force' is in effect.
*/
if (stat(buf, &sb) == 0) {
if (!force) {
if (verbose)
printf("Dump on device %s already saved\n",
device);
goto closefd;
}
} else if (errno != ENOENT) {
warn("error while checking for pre-saved core file");
syslog(LOG_ERR,
"first and last dump headers disagree on %s", device);
nerr++;
goto closefd;
}
if (kdhl.panicstring[0])
syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
else
syslog(LOG_ALERT, "reboot");
if (verbose)
printf("Checking for available free space\n");
if (!check_space(savedir, dumpsize)) {
nerr++;
goto closefd;
}
bounds = getbounds();
sprintf(buf, "info.%d", bounds);
/*
* Create or overwrite any existing files.
*/
fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fdinfo < 0) {
warn("%s", buf);
syslog(LOG_ERR, "%s: %m", buf);
nerr++;
goto closefd;
}
sprintf(buf, "%s.core", md5);
fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fdcore < 0) {
warn("%s", buf);
oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
if (compress) {
sprintf(buf, "vmcore.%d.gz", bounds);
fp = zopen(buf, "w");
} else {
sprintf(buf, "vmcore.%d", bounds);
fp = fopen(buf, "w");
}
if (fp == NULL) {
syslog(LOG_ERR, "%s: %m", buf);
close(fdinfo);
nerr++;
goto closefd;
}
(void)umask(oumask);
info = fdopen(fdinfo, "w");
if (verbose)
printheader(stdout, &kdhl, device, md5);
printheader(stdout, &kdhl, device, bounds);
printf("Saving dump to file %s\n", buf);
printheader(info, &kdhl, device, bounds);
fclose(info);
printheader(info, &kdhl, device, md5);
syslog(LOG_NOTICE, "writing %score to %s",
compress ? "compressed " : "", buf);
while (dumpsize > 0) {
wl = sizeof(buf);
if (wl > dumpsize)
wl = dumpsize;
error = read(fd, buf, wl);
if (error != wl) {
warn("read error on %s", device);
nr = read(fd, buf, wl);
if (nr != wl) {
if (nr == 0)
syslog(LOG_WARNING,
"WARNING: EOF on dump device");
else
syslog(LOG_ERR, "read error on %s: %m", device);
nerr++;
goto closeall;
}
error = write(fdcore, buf, wl);
if (error != wl) {
warn("write error on %s.core file", md5);
if (compress) {
nw = fwrite(buf, 1, wl, fp);
} else {
for (nw = 0; nw < nr; nw = he) {
/* find a contiguous block of zeroes */
for (hs = nw; hs < nr; hs += BLOCKSIZE) {
for (he = hs; he < nr && buf[he] == 0; ++he)
/* nothing */ ;
/* is the hole long enough to matter? */
if (he >= hs + BLOCKSIZE)
break;
}
/* back down to a block boundary */
he &= BLOCKMASK;
/*
* 1) Don't go beyond the end of the buffer.
* 2) If the end of the buffer is less than
* BLOCKSIZE bytes away, we're at the end
* of the file, so just grab what's left.
*/
if (hs + BLOCKSIZE > nr)
hs = he = nr;
/*
* At this point, we have a partial ordering:
* nw <= hs <= he <= nr
* If hs > nw, buf[nw..hs] contains non-zero data.
* If he > hs, buf[hs..he] is all zeroes.
*/
if (hs > nw)
if (fwrite(buf + nw, hs - nw, 1, fp) != 1)
break;
if (he > hs)
if (fseek(fp, he - hs, SEEK_CUR) == -1)
break;
}
}
if (nw != wl) {
syslog(LOG_ERR,
"write error on vmcore.%d file: %m", bounds);
syslog(LOG_WARNING,
"WARNING: vmcore may be incomplete");
nerr++;
goto closeall;
}
if (verbose) {
dmpcnt += wl;
printf("%llu\r", dmpcnt);
fflush(stdout);
}
dumpsize -= wl;
}
if (verbose)
printf("\n");
if (fclose(fp) < 0) {
syslog(LOG_ERR, "error on vmcore.%d: %m", bounds);
nerr++;
goto closeall;
}
nsaved++;
close(fdinfo);
close(fdcore);
if (verbose)
printf("Dump saved\n");
printf("dump saved\n");
nuke:
nuke:
if (clear || !keep) {
if (verbose)
printf("Clearing dump header\n");
memset(&kdhl, 0, sizeof kdhl);
printf("clearing dump header\n");
memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
lseek(fd, lasthd, SEEK_SET);
error = write(fd, &kdhl, sizeof kdhl);
if (error != sizeof kdhl)
warn("error while clearing the dump header");
syslog(LOG_ERR,
"error while clearing the dump header: %m");
}
close(fd);
return;
closeall:
close(fdinfo);
close(fdcore);
closeall:
fclose(fp);
closefd:
closefd:
close(fd);
}
@ -319,9 +471,13 @@ main(int argc, char **argv)
struct fstab *fsp;
char *savedir;
openlog("savecore", LOG_PERROR, LOG_DAEMON);
savedir = strdup(".");
if (savedir == NULL)
errx(1, "Cannot allocate memory");
if (savedir == NULL) {
syslog(LOG_ERR, "Cannot allocate memory");
exit(1);
}
while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1)
switch(ch) {
case 'c':
@ -336,9 +492,11 @@ main(int argc, char **argv)
case 'f':
force = 1;
break;
case 'z':
compress = 1;
break;
case 'd': /* Obsolete */
case 'N':
case 'z':
case '?':
default:
usage();
@ -347,8 +505,10 @@ main(int argc, char **argv)
argv += optind;
if (argc >= 1) {
error = chdir(argv[0]);
if (error)
err(1, "chdir(%s)", argv[0]);
if (error) {
syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
exit(1);
}
savedir = argv[0];
argc--;
argv++;
@ -370,12 +530,12 @@ main(int argc, char **argv)
/* Emit minimal output. */
if (nfound == 0)
printf("No dumps found\n");
syslog(LOG_WARNING, "no dumps found");
else if (nsaved == 0) {
if (nerr != 0)
printf("Unsaved dumps found but not saved\n");
syslog(LOG_WARNING, "unsaved dumps found but not saved");
else
printf("No unsaved dumps found\n");
syslog(LOG_WARNING, "no unsaved dumps found");
}
return (0);