diff options
Diffstat (limited to 'sbin')
-rw-r--r-- | sbin/savecore/Makefile | 5 | ||||
-rw-r--r-- | sbin/savecore/savecore.8 | 31 | ||||
-rw-r--r-- | sbin/savecore/savecore.c | 344 |
3 files changed, 271 insertions, 109 deletions
diff --git a/sbin/savecore/Makefile b/sbin/savecore/Makefile index 1fd2fa4..ce36776 100644 --- a/sbin/savecore/Makefile +++ b/sbin/savecore/Makefile @@ -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> diff --git a/sbin/savecore/savecore.8 b/sbin/savecore/savecore.8 index b6bafc3..34dc86b 100644 --- a/sbin/savecore/savecore.8 +++ b/sbin/savecore/savecore.8 @@ -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 , diff --git a/sbin/savecore/savecore.c b/sbin/savecore/savecore.c index f432abd..f478cb9 100644 --- a/sbin/savecore/savecore.c +++ b/sbin/savecore/savecore.c @@ -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, §orsize); 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); + syslog(LOG_ERR, + "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"); - 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); |