From eeff0b1b27b01cdde0e95df5ef87f0efd9b7f30a Mon Sep 17 00:00:00 2001 From: Pawel Jakub Dawidek Date: Sun, 16 Dec 2012 23:06:12 +0000 Subject: [PATCH] Implement -m option to savecore(8) that allows to limit number of kernel dumps stored. Once the limit is reached it restarts from 0. Reviewed by: avg Obtained from: WHEEL Systems --- sbin/savecore/savecore.8 | 11 +++- sbin/savecore/savecore.c | 106 ++++++++++++++++++++++++++++++++------- 2 files changed, 96 insertions(+), 21 deletions(-) diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8 index 3e22f5ad8ba9..c7d7e953682b 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 14, 2012 +.Dd December 17, 2012 .Dt SAVECORE 8 .Os .Sh NAME @@ -45,6 +45,7 @@ .Op Ar device ... .Nm .Op Fl fkvz +.Op Fl m Ar maxdumps .Op Ar directory Op Ar device ... .Sh DESCRIPTION The @@ -59,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. @@ -77,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 3a2547aa72fb..ca1cf1321aad 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,12 +179,62 @@ 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); +} + /* * 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; @@ -208,7 +259,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), @@ -367,7 +419,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]; static char *buf = NULL; struct kerneldumpheader kdhf, kdhl; off_t mediasize, dumpsize, firsthd, lasthd; @@ -382,6 +434,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) { @@ -535,19 +590,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); - snprintf(buf, sizeof(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++; @@ -555,16 +613,16 @@ DoFile(const char *savedir, const char *device) } oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/ if (compress) { - snprintf(filename, sizeof(filename), "%s.%d.gz", + snprintf(corename, sizeof(corename), "%s.%d.gz", istextdump ? "textdump.tar" : "vmcore", bounds); - fp = zopen(filename, "w"); + fp = zopen(corename, "w"); } else { - snprintf(filename, sizeof(filename), "%s.%d", + snprintf(corename, sizeof(corename), "%s.%d", istextdump ? "textdump.tar" : "vmcore", bounds); - fp = fopen(filename, "w"); + fp = fopen(corename, "w"); } if (fp == NULL) { - syslog(LOG_ERR, "%s: %m", filename); + syslog(LOG_ERR, "%s: %m", corename); close(fdinfo); nerr++; goto closefd; @@ -585,15 +643,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; } @@ -601,10 +659,11 @@ 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; } + nsaved++; if (verbose) @@ -637,8 +696,8 @@ usage(void) fprintf(stderr, "%s\n%s\n%s\n", "usage: savecore -c [-v] [device ...]", " savecore -C [-v] [device ...]", - " savecore [-fkvz] [directory [device ...]]"); - exit (1); + " savecore [-fkvz] [-m maxdumps] [directory [device ...]]"); + exit(1); } int @@ -654,7 +713,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; @@ -668,6 +727,13 @@ main(int argc, char **argv) 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; @@ -682,6 +748,8 @@ main(int argc, char **argv) usage(); if (clear && (compress || keep)) usage(); + if (maxdumps > 0 && (checkfor || clear)) + usage(); argc -= optind; argv += optind; if (argc >= 1 && !checkfor && !clear) {