summaryrefslogtreecommitdiffstats
path: root/sbin/savecore
diff options
context:
space:
mode:
authorobrien <obrien@FreeBSD.org>2013-02-08 16:10:16 +0000
committerobrien <obrien@FreeBSD.org>2013-02-08 16:10:16 +0000
commit3028e3f8aba938dfd0bf9fda987b8a72140b8027 (patch)
treeb2f038222ff8a70f687652441df00d2b564c8abe /sbin/savecore
parent952a6d5a7cd3d3f9007acfa06805262fc04a105f (diff)
parent1d08d5f677c1dfa810e381073590adbae19cc69f (diff)
downloadFreeBSD-src-3028e3f8aba938dfd0bf9fda987b8a72140b8027.zip
FreeBSD-src-3028e3f8aba938dfd0bf9fda987b8a72140b8027.tar.gz
Sync with HEAD.
Diffstat (limited to 'sbin/savecore')
-rw-r--r--sbin/savecore/savecore.815
-rw-r--r--sbin/savecore/savecore.c173
2 files changed, 147 insertions, 41 deletions
diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8
index 01be723..c7d7e95 100644
--- a/sbin/savecore/savecore.8
+++ b/sbin/savecore/savecore.8
@@ -28,7 +28,7 @@
.\" From: @(#)savecore.8 8.1 (Berkeley) 6/5/93
.\" $FreeBSD$
.\"
-.Dd December 24, 2008
+.Dd December 17, 2012
.Dt SAVECORE 8
.Os
.Sh NAME
@@ -37,12 +37,15 @@
.Sh SYNOPSIS
.Nm
.Fl c
+.Op Fl v
+.Op Ar device ...
.Nm
.Fl C
.Op Fl v
-.Op Ar directory device
+.Op Ar device ...
.Nm
.Op Fl fkvz
+.Op Fl m Ar maxdumps
.Op Ar directory Op Ar device ...
.Sh DESCRIPTION
The
@@ -57,7 +60,7 @@ and enters a reboot message and information about the core dump into
the system log.
.Pp
The options are as follows:
-.Bl -tag -width indent
+.Bl -tag -width ".Fl m Ar maxdumps"
.It Fl C
Check to see if a dump exists,
and display a brief message to indicate the status.
@@ -75,6 +78,12 @@ Force a dump to be taken even if either the dump was cleared or if the
dump header information is inconsistent.
.It Fl k
Do not clear the dump after saving it.
+.It Fl m Ar maxdumps
+Maximum number of dumps to store.
+Once the number of stored dumps is equal to
+.Ar maxdumps
+the counter will restart from
+.Dv 0 .
.It Fl v
Print out some additional debugging information.
Specify twice for more information.
diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c
index b3f2902..bdbf7e3 100644
--- a/sbin/savecore/savecore.c
+++ b/sbin/savecore/savecore.c
@@ -90,6 +90,7 @@ __FBSDID("$FreeBSD$");
static int checkfor, compress, clear, force, keep, verbose; /* flags */
static int nfound, nsaved, nerr; /* statistics */
+static int maxdumps;
extern FILE *zopen(const char *, const char *);
@@ -178,27 +179,87 @@ writebounds(int bounds) {
fclose(fp);
}
+static off_t
+file_size(const char *path)
+{
+ struct stat sb;
+
+ /* Ignore all errors, those file may not exists. */
+ if (stat(path, &sb) == -1)
+ return (0);
+ return (sb.st_size);
+}
+
+static off_t
+saved_dump_size(int bounds)
+{
+ static char path[PATH_MAX];
+ off_t dumpsize;
+
+ dumpsize = 0;
+
+ (void)snprintf(path, sizeof(path), "info.%d", bounds);
+ dumpsize += file_size(path);
+ (void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
+ dumpsize += file_size(path);
+ (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
+ dumpsize += file_size(path);
+ (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
+ dumpsize += file_size(path);
+ (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
+ dumpsize += file_size(path);
+
+ return (dumpsize);
+}
+
+static void
+saved_dump_remove(int bounds)
+{
+ static char path[PATH_MAX];
+
+ (void)snprintf(path, sizeof(path), "info.%d", bounds);
+ (void)unlink(path);
+ (void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
+ (void)unlink(path);
+ (void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
+ (void)unlink(path);
+ (void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
+ (void)unlink(path);
+ (void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
+ (void)unlink(path);
+}
+
+static void
+symlinks_remove(void)
+{
+
+ (void)unlink("info.last");
+ (void)unlink("vmcore.last");
+ (void)unlink("vmcore.last.gz");
+ (void)unlink("textdump.tar.last");
+ (void)unlink("textdump.tar.last.gz");
+}
+
/*
* Check that sufficient space is available on the disk that holds the
* save directory.
*/
static int
-check_space(const char *savedir, off_t dumpsize)
+check_space(const char *savedir, off_t dumpsize, int bounds)
{
FILE *fp;
off_t minfree, spacefree, totfree, needed;
struct statfs fsbuf;
- char buf[100], path[MAXPATHLEN];
+ char buf[100];
- if (statfs(savedir, &fsbuf) < 0) {
+ if (statfs(".", &fsbuf) < 0) {
syslog(LOG_ERR, "%s: %m", savedir);
exit(1);
}
- spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
+ 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)
+ if ((fp = fopen("minfree", "r")) == NULL)
minfree = 0;
else {
if (fgets(buf, sizeof(buf), fp) == NULL)
@@ -209,7 +270,8 @@ check_space(const char *savedir, off_t dumpsize)
}
needed = dumpsize / 1024 + 2; /* 2 for info file */
- if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
+ needed -= saved_dump_size(bounds);
+ 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),
@@ -262,7 +324,7 @@ DoRegularFile(int fd, off_t dumpsize, char *buf, const char *device,
if (he >= hs + BLOCKSIZE)
break;
}
-
+
/* back down to a block boundary */
he &= BLOCKMASK;
@@ -368,7 +430,7 @@ DoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf,
static void
DoFile(const char *savedir, const char *device)
{
- static char filename[PATH_MAX];
+ static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX];
static char *buf = NULL;
struct kerneldumpheader kdhf, kdhl;
off_t mediasize, dumpsize, firsthd, lasthd;
@@ -383,6 +445,9 @@ DoFile(const char *savedir, const char *device)
mediasize = 0;
status = STATUS_UNKNOWN;
+ if (maxdumps > 0 && bounds == maxdumps)
+ bounds = 0;
+
if (buf == NULL) {
buf = malloc(BUFFERSIZE);
if (buf == NULL) {
@@ -394,7 +459,7 @@ DoFile(const char *savedir, const char *device)
if (verbose)
printf("checking for kernel dump on device %s\n", device);
- fd = open(device, O_RDWR);
+ fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR);
if (fd < 0) {
syslog(LOG_ERR, "%s: %m", device);
return;
@@ -433,7 +498,7 @@ DoFile(const char *savedir, const char *device)
syslog(LOG_ERR,
"unknown version (%d) in last dump header on %s",
dtoh32(kdhl.version), device);
-
+
status = STATUS_BAD;
if (force == 0)
goto closefd;
@@ -444,7 +509,7 @@ DoFile(const char *savedir, const char *device)
syslog(LOG_ERR,
"unknown version (%d) in last dump header on %s",
dtoh32(kdhl.version), device);
-
+
status = STATUS_BAD;
if (force == 0)
goto closefd;
@@ -472,7 +537,7 @@ DoFile(const char *savedir, const char *device)
syslog(LOG_ERR,
"unknown version (%d) in last dump header on %s",
dtoh32(kdhl.version), device);
-
+
status = STATUS_BAD;
if (force == 0)
goto closefd;
@@ -536,19 +601,22 @@ DoFile(const char *savedir, const char *device)
if (verbose)
printf("Checking for available free space\n");
- if (!check_space(savedir, dumpsize)) {
+
+ if (!check_space(savedir, dumpsize, bounds)) {
nerr++;
goto closefd;
}
writebounds(bounds + 1);
- sprintf(buf, "info.%d", bounds);
+ saved_dump_remove(bounds);
+
+ snprintf(infoname, sizeof(infoname), "info.%d", bounds);
/*
* Create or overwrite any existing dump header files.
*/
- fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ fdinfo = open(infoname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
if (fdinfo < 0) {
syslog(LOG_ERR, "%s: %m", buf);
nerr++;
@@ -556,16 +624,16 @@ DoFile(const char *savedir, const char *device)
}
oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
if (compress) {
- sprintf(filename, "%s.%d.gz", istextdump ? "textdump.tar" :
- "vmcore", bounds);
- fp = zopen(filename, "w");
+ snprintf(corename, sizeof(corename), "%s.%d.gz",
+ istextdump ? "textdump.tar" : "vmcore", bounds);
+ fp = zopen(corename, "w");
} else {
- sprintf(filename, "%s.%d", istextdump ? "textdump.tar" :
- "vmcore", bounds);
- fp = fopen(filename, "w");
+ snprintf(corename, sizeof(corename), "%s.%d",
+ istextdump ? "textdump.tar" : "vmcore", bounds);
+ fp = fopen(corename, "w");
}
if (fp == NULL) {
- syslog(LOG_ERR, "%s: %m", filename);
+ syslog(LOG_ERR, "%s: %m", corename);
close(fdinfo);
nerr++;
goto closefd;
@@ -586,15 +654,15 @@ DoFile(const char *savedir, const char *device)
printheader(info, &kdhl, device, bounds, status);
fclose(info);
- syslog(LOG_NOTICE, "writing %score to %s",
- compress ? "compressed " : "", filename);
+ syslog(LOG_NOTICE, "writing %score to %s/%s",
+ compress ? "compressed " : "", savedir, corename);
if (istextdump) {
if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device,
- filename, fp) < 0)
+ corename, fp) < 0)
goto closeall;
} else {
- if (DoRegularFile(fd, dumpsize, buf, device, filename, fp)
+ if (DoRegularFile(fd, dumpsize, buf, device, corename, fp)
< 0)
goto closeall;
}
@@ -602,17 +670,35 @@ DoFile(const char *savedir, const char *device)
printf("\n");
if (fclose(fp) < 0) {
- syslog(LOG_ERR, "error on %s: %m", filename);
+ syslog(LOG_ERR, "error on %s: %m", corename);
nerr++;
goto closeall;
}
+
+ symlinks_remove();
+ if (symlink(infoname, "info.last") == -1) {
+ syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
+ savedir, "info.last");
+ }
+ if (compress) {
+ snprintf(linkname, sizeof(linkname), "%s.last.gz",
+ istextdump ? "textdump.tar" : "vmcore");
+ } else {
+ snprintf(linkname, sizeof(linkname), "%s.last",
+ istextdump ? "textdump.tar" : "vmcore");
+ }
+ if (symlink(corename, linkname) == -1) {
+ syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
+ savedir, linkname);
+ }
+
nsaved++;
if (verbose)
printf("dump saved\n");
nuke:
- if (clear || !keep) {
+ if (!keep) {
if (verbose)
printf("clearing dump header\n");
memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
@@ -636,10 +722,10 @@ static void
usage(void)
{
fprintf(stderr, "%s\n%s\n%s\n",
- "usage: savecore -c",
- " savecore -C [-v] [directory device]",
- " savecore [-fkvz] [directory [device ...]]");
- exit (1);
+ "usage: savecore -c [-v] [device ...]",
+ " savecore -C [-v] [device ...]",
+ " savecore [-fkvz] [-m maxdumps] [directory [device ...]]");
+ exit(1);
}
int
@@ -655,7 +741,7 @@ main(int argc, char **argv)
openlog("savecore", LOG_PERROR, LOG_DAEMON);
signal(SIGINFO, infohandler);
- while ((ch = getopt(argc, argv, "Ccfkvz")) != -1)
+ while ((ch = getopt(argc, argv, "Ccfkm:vz")) != -1)
switch(ch) {
case 'C':
checkfor = 1;
@@ -663,15 +749,22 @@ main(int argc, char **argv)
case 'c':
clear = 1;
break;
+ case 'f':
+ force = 1;
+ break;
case 'k':
keep = 1;
break;
+ case 'm':
+ maxdumps = atoi(optarg);
+ if (maxdumps <= 0) {
+ syslog(LOG_ERR, "Invalid maxdump value");
+ exit(1);
+ }
+ break;
case 'v':
verbose++;
break;
- case 'f':
- force = 1;
- break;
case 'z':
compress = 1;
break;
@@ -681,9 +774,13 @@ main(int argc, char **argv)
}
if (checkfor && (clear || force || keep))
usage();
+ if (clear && (compress || keep))
+ usage();
+ if (maxdumps > 0 && (checkfor || clear))
+ usage();
argc -= optind;
argv += optind;
- if (argc >= 1) {
+ if (argc >= 1 && !checkfor && !clear) {
error = chdir(argv[0]);
if (error) {
syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
OpenPOWER on IntegriCloud