Savecore:
Commandline compatible with the previous savecore unless you specify any options, none of them are implemented (yet). Scans all devices marked "dump" or "swap" for dump header signatures and saves dumps off under a name which is a MD5 hash of the header information. This should give unique filenames. A *.info file contains ascii version of the header information.
This commit is contained in:
parent
9e9c1cad4c
commit
d503fad022
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=93492
@ -1,12 +1,7 @@
|
||||
# @(#)Makefile 8.2 (Berkeley) 4/17/94
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= savecore
|
||||
SRCS= savecore.c
|
||||
WARNS= 0
|
||||
MAN= savecore.8
|
||||
|
||||
DPADD+= ${LIBZ}
|
||||
LDADD+= -lz
|
||||
PROG= savekerneldump
|
||||
WARNS= 4
|
||||
NOMAN= sorry, not yet.
|
||||
LDADD= -lmd
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -1,6 +1,12 @@
|
||||
/*-
|
||||
* Copyright (c) 1986, 1992, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
* Copyright (c) 2002 Poul-Henning Kamp
|
||||
* Copyright (c) 2002 Networks Associates Technology, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed for the FreeBSD Project by Poul-Henning Kamp
|
||||
* and NAI Labs, the Security Research Division of Network Associates, Inc.
|
||||
* under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
|
||||
* DARPA CHATS research program.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
@ -10,18 +16,14 @@
|
||||
* 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.
|
||||
* 3. The names of the authors may not 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
|
||||
* 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 REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* 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)
|
||||
@ -29,766 +31,192 @@
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef lint
|
||||
static const char copyright[] =
|
||||
"@(#) Copyright (c) 1986, 1992, 1993\n\
|
||||
The Regents of the University of California. All rights reserved.\n";
|
||||
#endif /* not lint */
|
||||
|
||||
#ifndef lint
|
||||
#if 0
|
||||
static char sccsid[] = "@(#)savecore.c 8.3 (Berkeley) 1/2/94";
|
||||
#endif
|
||||
static const char rcsid[] =
|
||||
"$FreeBSD$";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/vm_param.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <nlist.h>
|
||||
#include <paths.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <fstab.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <md5.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/disklabel.h>
|
||||
#include <sys/kerneldump.h>
|
||||
|
||||
extern FILE *zopen(const char *fname, const char *mode);
|
||||
static void
|
||||
printheader(FILE *f, const struct kerneldumpheader *h, const char *devname, const char *md5)
|
||||
{
|
||||
time_t t;
|
||||
|
||||
#if defined(__i386__) || defined(__sparc64__)
|
||||
#define ok(number) ((number) - kernbase)
|
||||
#elif defined(__alpha__)
|
||||
#define ok(number) ALPHA_K0SEG_TO_PHYS(number)
|
||||
#else
|
||||
#error savecore has not been ported to this platform yet.
|
||||
#endif
|
||||
fprintf(f, "Good dump found on device %s\n", devname);
|
||||
fprintf(f, " Architecture: %s\n", h->architecture);
|
||||
fprintf(f, " Architecture version: %d\n", h->architectureversion);
|
||||
fprintf(f, " Dump length: %lldB (%lld MB)\n",
|
||||
h->dumplength, h->dumplength / (1024 * 1024));
|
||||
fprintf(f, " Blocksize: %d\n", h->blocksize);
|
||||
t = h->dumptime;
|
||||
fprintf(f, " Dumptime: %s", ctime(&t));
|
||||
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);
|
||||
}
|
||||
|
||||
struct nlist current_nl[] = { /* Namelist for currently running system. */
|
||||
#define X_DUMPLO 0
|
||||
{ "_dumplo" },
|
||||
#define X_TIME 1
|
||||
{ "_time_second" },
|
||||
#define X_DUMPSIZE 2
|
||||
{ "_dumpsize" },
|
||||
#define X_VERSION 3
|
||||
{ "_version" },
|
||||
#define X_PANICSTR 4
|
||||
{ "_panicstr" },
|
||||
#define X_DUMPMAG 5
|
||||
{ "_dumpmag" },
|
||||
#define X_KERNBASE 6
|
||||
{ "_kernbase" },
|
||||
{ "" },
|
||||
};
|
||||
int cursyms[] = { X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
|
||||
int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
|
||||
|
||||
struct nlist dump_nl[] = { /* Name list for dumped system. */
|
||||
{ "_dumplo" }, /* Entries MUST be the same as */
|
||||
{ "_time_second" }, /* those in current_nl[]. */
|
||||
{ "_dumpsize" },
|
||||
{ "_version" },
|
||||
{ "_panicstr" },
|
||||
{ "_dumpmag" },
|
||||
{ "_kernbase" },
|
||||
{ "" },
|
||||
};
|
||||
static void
|
||||
DoFile(const char *devname)
|
||||
{
|
||||
int fd, fdcore, fdinfo, error, wl;
|
||||
off_t mediasize, dumpsize, firsthd, lasthd;
|
||||
u_int sectorsize;
|
||||
struct kerneldumpheader kdhf, kdhl;
|
||||
char *md5;
|
||||
FILE *info;
|
||||
char buf[BUFSIZ];
|
||||
|
||||
/* Types match kernel declarations. */
|
||||
u_long dumpmag; /* magic number in dump */
|
||||
mediasize = 0;
|
||||
fd = open(devname, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
warn("%s", devname);
|
||||
return;
|
||||
}
|
||||
error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
|
||||
if (!error)
|
||||
error = ioctl(fd, DIOCGSECTORSIZE, §orsize);
|
||||
if (error) {
|
||||
warn("Couldn't find media and/or sector size of %s)", devname);
|
||||
return;
|
||||
}
|
||||
printf("Mediasize = %lld\n", 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",
|
||||
lasthd, devname);
|
||||
return;
|
||||
}
|
||||
if (kerneldump_parity(&kdhl)) {
|
||||
warnx("Parity error on last dump header on %s\n", devname);
|
||||
return;
|
||||
}
|
||||
if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
|
||||
warnx("Magic mismatch on last dump header on %s\n", devname);
|
||||
return;
|
||||
}
|
||||
if (kdhl.version != KERNELDUMPVERSION) {
|
||||
warnx("Unknown version (%d) in last dump header on %s\n",
|
||||
kdhl.version, devname);
|
||||
return;
|
||||
}
|
||||
firsthd = lasthd - kdhl.dumplength - sizeof kdhf;
|
||||
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",
|
||||
firsthd, devname);
|
||||
return;
|
||||
}
|
||||
if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
|
||||
warn("First and last dump headers disagree on %s\n", devname);
|
||||
return;
|
||||
}
|
||||
md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL);
|
||||
sprintf(buf, "%s.info", md5);
|
||||
fdinfo = open(buf, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fdinfo < 0 && errno == EEXIST) {
|
||||
printf("Dump on device %s already saved\n", devname);
|
||||
return;
|
||||
}
|
||||
if (fdinfo < 0) {
|
||||
warn("%s", buf);
|
||||
return;
|
||||
}
|
||||
sprintf(buf, "%s.core", md5);
|
||||
fdcore = open(buf, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||
if (fdcore < 0) {
|
||||
warn("%s", buf);
|
||||
return;
|
||||
}
|
||||
info = fdopen(fdinfo, "w");
|
||||
printheader(stdout, &kdhl, devname, md5);
|
||||
printheader(info, &kdhl, devname, md5);
|
||||
dumpsize = kdhl.dumplength;
|
||||
printf("Saving dump to file...\n");
|
||||
while (dumpsize > 0) {
|
||||
wl = sizeof(buf);
|
||||
if (wl > dumpsize)
|
||||
wl = dumpsize;
|
||||
error = read(fd, buf, wl);
|
||||
if (error != wl) {
|
||||
warn("read error on %s\n", devname);
|
||||
return;
|
||||
}
|
||||
error = write(fdcore, buf, wl);
|
||||
if (error != wl) {
|
||||
warn("write error on %s.core file\n", md5);
|
||||
return;
|
||||
}
|
||||
dumpsize -= wl;
|
||||
}
|
||||
close (fdinfo);
|
||||
close (fdcore);
|
||||
printf("Dump saved\n");
|
||||
}
|
||||
|
||||
/* Based on kernel variables, but with more convenient types. */
|
||||
off_t dumplo; /* where dump starts on dumpdev */
|
||||
off_t dumpsize; /* amount of memory dumped */
|
||||
|
||||
char *kernel; /* user-specified kernel */
|
||||
char *savedir; /* directory to save dumps in */
|
||||
char ddname[MAXPATHLEN]; /* name of dump device */
|
||||
dev_t dumpdev; /* dump device */
|
||||
int dumpfd; /* read/write descriptor on char dev */
|
||||
time_t now; /* current date */
|
||||
char panic_mesg[1024]; /* panic message */
|
||||
int panicstr; /* flag: dump was caused by panic */
|
||||
char vers[1024]; /* version of kernel that crashed */
|
||||
|
||||
#if defined(__i386__) || defined(__sparc64__)
|
||||
u_long kernbase; /* offset of kvm to core file */
|
||||
#endif
|
||||
|
||||
int clear, compress, force, verbose; /* flags */
|
||||
int keep; /* keep dump on device */
|
||||
|
||||
void check_kmem(void);
|
||||
int check_space(void);
|
||||
void clear_dump(void);
|
||||
void DumpRead(int fd, void *bp, int size, off_t off, int flag);
|
||||
void DumpWrite(int fd, void *bp, int size, off_t off, int flag);
|
||||
int dump_exists(void);
|
||||
void find_dev(dev_t);
|
||||
int get_crashtime(void);
|
||||
void get_dumpsize(void);
|
||||
void kmem_setup(void);
|
||||
void Lseek(int, off_t, int);
|
||||
int Open(const char *, int rw);
|
||||
int Read(int, void *, int);
|
||||
void save_core(void);
|
||||
void usage(void);
|
||||
void Write(int, void *, int);
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
errx(1, "usage: ...");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
int
|
||||
main(argc, argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int ch;
|
||||
|
||||
openlog("savecore", LOG_PERROR, LOG_DAEMON);
|
||||
int i, ch, error;
|
||||
struct fstab *fsp;
|
||||
|
||||
while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1)
|
||||
switch(ch) {
|
||||
case 'c':
|
||||
clear = 1;
|
||||
break;
|
||||
case 'd': /* Not documented. */
|
||||
case 'd':
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case 'f':
|
||||
force = 1;
|
||||
break;
|
||||
case 'k':
|
||||
keep = 1;
|
||||
break;
|
||||
case 'N':
|
||||
kernel = optarg;
|
||||
break;
|
||||
case 'z':
|
||||
compress = 1;
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (!clear) {
|
||||
if (argc != 1 && argc != 2)
|
||||
usage();
|
||||
savedir = argv[0];
|
||||
if (argc >= 1) {
|
||||
error = chdir(argv[0]);
|
||||
if (error)
|
||||
err(1, "chdir(%s)", argv[0]);
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
if (argc == 2)
|
||||
kernel = argv[1];
|
||||
|
||||
(void)time(&now);
|
||||
kmem_setup();
|
||||
|
||||
if (clear) {
|
||||
clear_dump();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (!dump_exists() && !force)
|
||||
exit(1);
|
||||
|
||||
check_kmem();
|
||||
|
||||
if (panicstr)
|
||||
syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
|
||||
else
|
||||
syslog(LOG_ALERT, "reboot");
|
||||
|
||||
get_dumpsize();
|
||||
|
||||
if ((!get_crashtime() || !check_space()) && !force)
|
||||
exit(1);
|
||||
|
||||
save_core();
|
||||
|
||||
if (!keep)
|
||||
clear_dump();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void
|
||||
kmem_setup()
|
||||
{
|
||||
int kmem, i;
|
||||
const char *dump_sys;
|
||||
size_t len;
|
||||
long kdumplo; /* block number where dump starts on dumpdev */
|
||||
char *p;
|
||||
|
||||
/*
|
||||
* Some names we need for the currently running system, others for
|
||||
* the system that was running when the dump was made. The values
|
||||
* obtained from the current system are used to look for things in
|
||||
* /dev/kmem that cannot be found in the dump_sys namelist, but are
|
||||
* presumed to be the same (since the disk partitions are probably
|
||||
* the same!)
|
||||
*/
|
||||
if ((nlist(getbootfile(), current_nl)) == -1)
|
||||
syslog(LOG_ERR, "%s: nlist: %m", getbootfile());
|
||||
for (i = 0; cursyms[i] != -1; i++)
|
||||
if (current_nl[cursyms[i]].n_value == 0) {
|
||||
syslog(LOG_ERR, "%s: %s not in namelist",
|
||||
getbootfile(), current_nl[cursyms[i]].n_name);
|
||||
exit(1);
|
||||
if (argc == 0) {
|
||||
for (;;) {
|
||||
fsp = getfsent();
|
||||
if (fsp == NULL)
|
||||
break;
|
||||
if (strcmp(fsp->fs_vfstype, "swap") &&
|
||||
strcmp(fsp->fs_vfstype, "dump"))
|
||||
continue;
|
||||
DoFile(fsp->fs_spec);
|
||||
}
|
||||
|
||||
dump_sys = kernel ? kernel : getbootfile();
|
||||
if ((nlist(dump_sys, dump_nl)) == -1)
|
||||
syslog(LOG_ERR, "%s: nlist: %m", dump_sys);
|
||||
for (i = 0; dumpsyms[i] != -1; i++)
|
||||
if (dump_nl[dumpsyms[i]].n_value == 0) {
|
||||
syslog(LOG_ERR, "%s: %s not in namelist",
|
||||
dump_sys, dump_nl[dumpsyms[i]].n_name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__sparc64__)
|
||||
if (dump_nl[X_KERNBASE].n_value != 0)
|
||||
kernbase = dump_nl[X_KERNBASE].n_value;
|
||||
else
|
||||
kernbase = KERNBASE;
|
||||
#endif
|
||||
|
||||
len = sizeof dumpdev;
|
||||
if (sysctlbyname("kern.dumpdev", &dumpdev, &len, NULL, 0) == -1) {
|
||||
syslog(LOG_ERR, "sysctl: kern.dumpdev: %m");
|
||||
exit(1);
|
||||
} else {
|
||||
for (i = 0; i < argc; i++)
|
||||
DoFile(argv[i]);
|
||||
}
|
||||
if (dumpdev == NODEV) {
|
||||
syslog(LOG_WARNING, "no core dump (no dumpdev)");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
kmem = Open(_PATH_KMEM, O_RDONLY);
|
||||
Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, SEEK_SET);
|
||||
(void)Read(kmem, &kdumplo, sizeof(kdumplo));
|
||||
dumplo = (off_t)kdumplo * DEV_BSIZE;
|
||||
if (verbose)
|
||||
(void)printf("dumplo = %lld (%ld * %d)\n",
|
||||
(long long)dumplo, kdumplo, DEV_BSIZE);
|
||||
Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, SEEK_SET);
|
||||
(void)Read(kmem, &dumpmag, sizeof(dumpmag));
|
||||
find_dev(dumpdev);
|
||||
dumpfd = Open(ddname, O_RDWR);
|
||||
if (kernel)
|
||||
return;
|
||||
|
||||
Lseek(kmem, (off_t)current_nl[X_VERSION].n_value, SEEK_SET);
|
||||
Read(kmem, vers, sizeof(vers));
|
||||
vers[sizeof(vers) - 1] = '\0';
|
||||
p = strchr(vers, '\n');
|
||||
if (p)
|
||||
p[1] = '\0';
|
||||
|
||||
/* Don't fclose(fp), we use kmem later. */
|
||||
}
|
||||
|
||||
void
|
||||
check_kmem()
|
||||
{
|
||||
char core_vers[1024], *p;
|
||||
|
||||
DumpRead(dumpfd, core_vers, sizeof(core_vers),
|
||||
dumplo + ok(dump_nl[X_VERSION].n_value), SEEK_SET);
|
||||
core_vers[sizeof(core_vers) - 1] = '\0';
|
||||
p = strchr(core_vers, '\n');
|
||||
if (p)
|
||||
p[1] = '\0';
|
||||
if (strcmp(vers, core_vers) && kernel == 0)
|
||||
syslog(LOG_WARNING,
|
||||
"warning: %s version mismatch:\n\t\"%s\"\nand\t\"%s\"\n",
|
||||
getbootfile(), vers, core_vers);
|
||||
DumpRead(dumpfd, &panicstr, sizeof(panicstr),
|
||||
dumplo + ok(dump_nl[X_PANICSTR].n_value), SEEK_SET);
|
||||
if (panicstr) {
|
||||
DumpRead(dumpfd, panic_mesg, sizeof(panic_mesg),
|
||||
dumplo + ok(panicstr), SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the magic number in the dump header.
|
||||
*/
|
||||
void
|
||||
clear_dump()
|
||||
{
|
||||
u_long newdumpmag;
|
||||
|
||||
newdumpmag = 0;
|
||||
DumpWrite(dumpfd, &newdumpmag, sizeof(newdumpmag),
|
||||
dumplo + ok(dump_nl[X_DUMPMAG].n_value), SEEK_SET);
|
||||
close(dumpfd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if a dump exists by looking for a magic number in the dump
|
||||
* header.
|
||||
*/
|
||||
int
|
||||
dump_exists()
|
||||
{
|
||||
u_long newdumpmag;
|
||||
|
||||
DumpRead(dumpfd, &newdumpmag, sizeof(newdumpmag),
|
||||
dumplo + ok(dump_nl[X_DUMPMAG].n_value), SEEK_SET);
|
||||
if (newdumpmag != dumpmag) {
|
||||
if (verbose)
|
||||
syslog(LOG_WARNING, "magic number mismatch (%x != %x)",
|
||||
newdumpmag, dumpmag);
|
||||
syslog(LOG_WARNING, "no core dump");
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
char buf[1024 * 1024];
|
||||
#define BLOCKSIZE (1<<12)
|
||||
#define BLOCKMASK (~(BLOCKSIZE-1))
|
||||
|
||||
/*
|
||||
* Save the core dump.
|
||||
*/
|
||||
void
|
||||
save_core()
|
||||
{
|
||||
FILE *fp;
|
||||
int bounds, ifd, nr, nw;
|
||||
int hs, he; /* start and end of hole */
|
||||
char path[MAXPATHLEN];
|
||||
mode_t oumask;
|
||||
|
||||
/*
|
||||
* Get the current number and update the bounds file. Do the update
|
||||
* now, because may fail later and don't want to overwrite anything.
|
||||
*/
|
||||
(void)snprintf(path, sizeof(path), "%s/bounds", savedir);
|
||||
if ((fp = fopen(path, "r")) == NULL)
|
||||
goto err1;
|
||||
if (fgets(buf, sizeof(buf), fp) == NULL) {
|
||||
if (ferror(fp))
|
||||
err1: syslog(LOG_WARNING, "%s: %m", path);
|
||||
bounds = 0;
|
||||
} else
|
||||
bounds = atoi(buf);
|
||||
if (fp != NULL)
|
||||
(void)fclose(fp);
|
||||
if ((fp = fopen(path, "w")) == NULL)
|
||||
syslog(LOG_ERR, "%s: %m", path);
|
||||
else {
|
||||
(void)fprintf(fp, "%d\n", bounds + 1);
|
||||
(void)fclose(fp);
|
||||
}
|
||||
|
||||
/* Create the core file. */
|
||||
oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
|
||||
(void)snprintf(path, sizeof(path), "%s/vmcore.%d%s",
|
||||
savedir, bounds, compress ? ".gz" : "");
|
||||
if (compress)
|
||||
fp = zopen(path, "w");
|
||||
else
|
||||
fp = fopen(path, "w");
|
||||
if (fp == NULL) {
|
||||
syslog(LOG_ERR, "%s: %m", path);
|
||||
exit(1);
|
||||
}
|
||||
(void)umask(oumask);
|
||||
|
||||
/* Seek to the start of the core. */
|
||||
Lseek(dumpfd, dumplo, SEEK_SET);
|
||||
|
||||
/* Copy the core file. */
|
||||
syslog(LOG_NOTICE, "writing %score to %s",
|
||||
compress ? "compressed " : "", path);
|
||||
for (; dumpsize > 0; dumpsize -= nr) {
|
||||
(void)printf("%6dK\r", dumpsize / 1024);
|
||||
(void)fflush(stdout);
|
||||
nr = read(dumpfd, buf, MIN(dumpsize, sizeof(buf)));
|
||||
if (nr <= 0) {
|
||||
if (nr == 0)
|
||||
syslog(LOG_WARNING,
|
||||
"WARNING: EOF on dump device");
|
||||
else
|
||||
syslog(LOG_ERR, "%s: %m", ddname);
|
||||
goto err2;
|
||||
}
|
||||
|
||||
if (compress) {
|
||||
nw = fwrite(buf, 1, nr, 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 != nr) {
|
||||
syslog(LOG_ERR, "%s: %m", path);
|
||||
err2: syslog(LOG_WARNING,
|
||||
"WARNING: vmcore may be incomplete");
|
||||
(void)printf("\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
(void)fclose(fp);
|
||||
|
||||
/* Copy the kernel. */
|
||||
ifd = Open(kernel ? kernel : getbootfile(), O_RDONLY);
|
||||
(void)snprintf(path, sizeof(path), "%s/kernel.%d%s",
|
||||
savedir, bounds, compress ? ".gz" : "");
|
||||
if (compress)
|
||||
fp = zopen(path, "w");
|
||||
else
|
||||
fp = fopen(path, "w");
|
||||
if (fp == NULL) {
|
||||
syslog(LOG_ERR, "%s: %m", path);
|
||||
exit(1);
|
||||
}
|
||||
syslog(LOG_NOTICE, "writing %skernel to %s",
|
||||
compress ? "compressed " : "", path);
|
||||
while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
|
||||
nw = fwrite(buf, 1, nr, fp);
|
||||
if (nw != nr) {
|
||||
syslog(LOG_ERR, "%s: %m", path);
|
||||
syslog(LOG_WARNING,
|
||||
"WARNING: kernel may be incomplete");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (nr < 0) {
|
||||
syslog(LOG_ERR, "%s: %m", kernel ? kernel : getbootfile());
|
||||
syslog(LOG_WARNING,
|
||||
"WARNING: kernel may be incomplete");
|
||||
exit(1);
|
||||
}
|
||||
(void)fclose(fp);
|
||||
close(ifd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that the specified device node exists and matches the
|
||||
* specified device.
|
||||
*/
|
||||
int
|
||||
verify_dev(name, dev)
|
||||
char *name;
|
||||
dev_t dev;
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (lstat(name, &sb) == -1)
|
||||
return (-1);
|
||||
if (!S_ISCHR(sb.st_mode) || sb.st_rdev != dev)
|
||||
return (-1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the dump device.
|
||||
*
|
||||
* 1) try devname(3); see if it returns something sensible
|
||||
* 2) scan /dev for the desired node
|
||||
* 3) as a last resort, try to create the node we need
|
||||
*/
|
||||
void
|
||||
find_dev(dev)
|
||||
dev_t dev;
|
||||
{
|
||||
struct dirent *ent;
|
||||
char *dn, *dnp;
|
||||
DIR *d;
|
||||
|
||||
strcpy(ddname, _PATH_DEV);
|
||||
dnp = ddname + sizeof _PATH_DEV - 1;
|
||||
if ((dn = devname(dev, S_IFCHR)) != NULL) {
|
||||
strcpy(dnp, dn);
|
||||
if (verify_dev(ddname, dev) == 0)
|
||||
return;
|
||||
}
|
||||
if ((d = opendir(_PATH_DEV)) != NULL) {
|
||||
while ((ent = readdir(d))) {
|
||||
strcpy(dnp, ent->d_name);
|
||||
if (verify_dev(ddname, dev) == 0) {
|
||||
closedir(d);
|
||||
return;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
strcpy(dnp, "dump");
|
||||
if (mknod(ddname, S_IFCHR|S_IRUSR|S_IWUSR, dev) == 0)
|
||||
return;
|
||||
syslog(LOG_ERR, "can't find device %d/%#x", major(dev), minor(dev));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the date and time of the crash from the dump header, and
|
||||
* make sure it looks sane (within one week of current date and time).
|
||||
*/
|
||||
int
|
||||
get_crashtime()
|
||||
{
|
||||
time_t dumptime; /* Time the dump was taken. */
|
||||
|
||||
DumpRead(dumpfd, &dumptime, sizeof(dumptime),
|
||||
dumplo + ok(dump_nl[X_TIME].n_value), SEEK_SET);
|
||||
if (dumptime == 0) {
|
||||
if (verbose)
|
||||
syslog(LOG_ERR, "dump time is zero");
|
||||
return (0);
|
||||
}
|
||||
(void)printf("savecore: system went down at %s", ctime(&dumptime));
|
||||
#define LEEWAY (7 * 86400)
|
||||
if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
|
||||
(void)printf("dump time is unreasonable\n");
|
||||
return (0);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the size of the dump from the dump header.
|
||||
*/
|
||||
void
|
||||
get_dumpsize()
|
||||
{
|
||||
int kdumpsize;
|
||||
|
||||
/* Read the dump size. */
|
||||
DumpRead(dumpfd, &kdumpsize, sizeof(kdumpsize),
|
||||
dumplo + ok(dump_nl[X_DUMPSIZE].n_value), SEEK_SET);
|
||||
dumpsize = (off_t)kdumpsize * getpagesize();
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that sufficient space is available on the disk that holds the
|
||||
* save directory.
|
||||
*/
|
||||
int
|
||||
check_space()
|
||||
{
|
||||
FILE *fp;
|
||||
const char *tkernel;
|
||||
off_t minfree, spacefree, totfree, kernelsize, needed;
|
||||
struct stat st;
|
||||
struct statfs fsbuf;
|
||||
char buf[100], path[MAXPATHLEN];
|
||||
|
||||
tkernel = kernel ? kernel : getbootfile();
|
||||
if (stat(tkernel, &st) < 0) {
|
||||
syslog(LOG_ERR, "%s: %m", tkernel);
|
||||
exit(1);
|
||||
}
|
||||
kernelsize = st.st_blocks * S_BLKSIZE;
|
||||
|
||||
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;
|
||||
|
||||
(void)snprintf(path, sizeof(path), "%s/minfree", savedir);
|
||||
if ((fp = fopen(path, "r")) == NULL)
|
||||
minfree = 0;
|
||||
else {
|
||||
if (fgets(buf, sizeof(buf), fp) == NULL)
|
||||
minfree = 0;
|
||||
else
|
||||
minfree = atoi(buf);
|
||||
(void)fclose(fp);
|
||||
}
|
||||
|
||||
needed = (dumpsize + kernelsize) / 1024;
|
||||
if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
|
||||
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)
|
||||
syslog(LOG_WARNING,
|
||||
"dump performed, but free space threshold crossed");
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
Open(name, rw)
|
||||
const char *name;
|
||||
int rw;
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(name, rw, 0);
|
||||
if (fd < 0) {
|
||||
syslog(LOG_ERR, "%s: %m", name);
|
||||
exit(1);
|
||||
}
|
||||
return (fd);
|
||||
}
|
||||
|
||||
int
|
||||
Read(fd, bp, size)
|
||||
int fd, size;
|
||||
void *bp;
|
||||
{
|
||||
int nr;
|
||||
|
||||
nr = read(fd, bp, size);
|
||||
if (nr != size) {
|
||||
syslog(LOG_ERR, "read: %m");
|
||||
exit(1);
|
||||
}
|
||||
return (nr);
|
||||
}
|
||||
|
||||
void
|
||||
Lseek(fd, off, flag)
|
||||
int fd, flag;
|
||||
off_t off;
|
||||
{
|
||||
off_t ret;
|
||||
|
||||
ret = lseek(fd, off, flag);
|
||||
if (ret == -1) {
|
||||
syslog(LOG_ERR, "lseek: %m");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* DumpWrite and DumpRead block io requests to the * dump device.
|
||||
*/
|
||||
#define DUMPBUFSIZE 8192
|
||||
void
|
||||
DumpWrite(fd, bp, size, off, flag)
|
||||
int fd, size, flag;
|
||||
void *bp;
|
||||
off_t off;
|
||||
{
|
||||
unsigned char buf[DUMPBUFSIZE], *p, *q;
|
||||
off_t pos;
|
||||
int i, j;
|
||||
|
||||
if (flag != SEEK_SET) {
|
||||
syslog(LOG_ERR, "lseek: not SEEK_SET");
|
||||
exit(2);
|
||||
}
|
||||
q = bp;
|
||||
while (size) {
|
||||
pos = off & ~(DUMPBUFSIZE - 1);
|
||||
Lseek(fd, pos, flag);
|
||||
(void)Read(fd, buf, sizeof(buf));
|
||||
j = off & (DUMPBUFSIZE - 1);
|
||||
p = buf + j;
|
||||
i = size;
|
||||
if (i > DUMPBUFSIZE - j)
|
||||
i = DUMPBUFSIZE - j;
|
||||
memcpy(p, q, i);
|
||||
Lseek(fd, pos, flag);
|
||||
(void)Write(fd, buf, sizeof(buf));
|
||||
size -= i;
|
||||
q += i;
|
||||
off += i;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DumpRead(fd, bp, size, off, flag)
|
||||
int fd, size, flag;
|
||||
void *bp;
|
||||
off_t off;
|
||||
{
|
||||
unsigned char buf[DUMPBUFSIZE], *p, *q;
|
||||
off_t pos;
|
||||
int i, j;
|
||||
|
||||
if (flag != SEEK_SET) {
|
||||
syslog(LOG_ERR, "lseek: not SEEK_SET");
|
||||
exit(2);
|
||||
}
|
||||
q = bp;
|
||||
while (size) {
|
||||
pos = off & ~(DUMPBUFSIZE - 1);
|
||||
Lseek(fd, pos, flag);
|
||||
(void)Read(fd, buf, sizeof(buf));
|
||||
j = off & (DUMPBUFSIZE - 1);
|
||||
p = buf + j;
|
||||
i = size;
|
||||
if (i > DUMPBUFSIZE - j)
|
||||
i = DUMPBUFSIZE - j;
|
||||
memcpy(q, p, i);
|
||||
size -= i;
|
||||
q += i;
|
||||
off += i;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Write(fd, bp, size)
|
||||
int fd, size;
|
||||
void *bp;
|
||||
{
|
||||
int n;
|
||||
|
||||
n = write(fd, bp, size);
|
||||
if (n < size) {
|
||||
syslog(LOG_ERR, "write: %m");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
usage()
|
||||
{
|
||||
(void)syslog(LOG_ERR, "usage: savecore [-cfkvz] [-N system] directory");
|
||||
exit(1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user