Index: etc/defaults/rc.conf =================================================================== --- etc/defaults/rc.conf (wersja 244272) +++ etc/defaults/rc.conf (kopia robocza) @@ -605,7 +605,7 @@ chkprintcap_flags="-d" # Create missing directories by default. dumpdev="AUTO" # Device to crashdump to (device name, AUTO, or NO). dumpdir="/var/crash" # Directory where crash dumps are to be stored -savecore_flags="" # Used if dumpdev is enabled above, and present. +savecore_flags="-m 10" # Used if dumpdev is enabled above, and present. crashinfo_enable="YES" # Automatically generate crash dump summary. crashinfo_program="/usr/sbin/crashinfo" # Script to generate crash dump summary. quota_enable="NO" # turn on quotas on startup (or NO). Index: sbin/savecore/savecore.8 =================================================================== --- sbin/savecore/savecore.8 (wersja 244272) +++ sbin/savecore/savecore.8 (kopia robocza) @@ -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 @@ 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 @@ 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. Index: sbin/savecore/savecore.c =================================================================== --- sbin/savecore/savecore.c (wersja 244272) +++ sbin/savecore/savecore.c (kopia robocza) @@ -90,6 +90,7 @@ 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 @@ 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; 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 @@ } 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), @@ -368,7 +430,7 @@ 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 @@ mediasize = 0; status = STATUS_UNKNOWN; + if (maxdumps > 0 && bounds == maxdumps) + bounds = 0; + if (buf == NULL) { buf = malloc(BUFFERSIZE); if (buf == NULL) { @@ -536,19 +601,22 @@ 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 @@ } 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 @@ 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,10 +670,28 @@ 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) @@ -638,8 +724,8 @@ 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 @@ -655,7 +741,7 @@ 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 @@ 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; @@ -683,6 +776,8 @@ usage(); if (clear && (compress || keep)) usage(); + if (maxdumps > 0 && (checkfor || clear)) + usage(); argc -= optind; argv += optind; if (argc >= 1 && !checkfor && !clear) {