/* * Copyright (c) 1980, 1986, 1989 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. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1980, 1986, 1989 The Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ #ifndef lint /*static char sccsid[] = "from: @(#)savecore.c 5.26 (Berkeley) 4/8/91";*/ static char rcsid[] = "$Id: savecore.c,v 1.12 1994/05/31 09:38:12 pk Exp $"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DAY (60L*60L*24L) #define LEEWAY (3*DAY) #define eq(a,b) (!strcmp(a,b)) #define ok(number) ((number) - KERNBASE) struct nlist current_nl[] = { /* namelist for currently running system */ #define X_DUMPDEV 0 { "_dumpdev" }, #define X_DUMPLO 1 { "_dumplo" }, #define X_TIME 2 { "_time" }, #define X_DUMPSIZE 3 { "_dumpsize" }, #define X_VERSION 4 { "_version" }, #define X_PANICSTR 5 { "_panicstr" }, #define X_DUMPMAG 6 { "_dumpmag" }, { "" }, }; struct nlist dump_nl[] = { /* name list for dumped system */ { "_dumpdev" }, /* entries MUST be the same as */ { "_dumplo" }, /* those in current_nl[] */ { "_time" }, { "_dumpsize" }, { "_version" }, { "_panicstr" }, { "_dumpmag" }, { "" }, }; char *kernel; char *dirname; /* directory to save dumps in */ char *ddname; /* name of dump device */ int dumpfd; /* read/write descriptor on block dev */ dev_t dumpdev; /* dump device */ time_t dumptime; /* time the dump was taken */ int dumplo; /* where dump starts on dumpdev */ int dumpsize; /* amount of memory dumped */ int dumpmag; /* magic number in dump */ time_t now; /* current date */ char vers[80]; char core_vers[80]; char panic_mesg[80]; int panicstr; int verbose; int force; int clear; char *path __P((char *)); char *find_dev __P((dev_t, int)); off_t Lseek __P((int, long, int)); main(argc, argv) char **argv; int argc; { int ch; char *cp; while ((ch = getopt(argc, argv, "cdfv")) != EOF) switch(ch) { case 'c': clear = 1; break; case 'd': /* not documented */ case 'v': verbose = 1; break; case 'f': force = 1; break; case '?': default: usage(); } argc -= optind; argv += optind; /* This is wrong, but I want "savecore -c" to work. */ if (!clear) { if (argc != 1 && argc != 2) usage(); dirname = argv[0]; } if (argc == 2) kernel = argv[1]; openlog("savecore", LOG_ODELAY, LOG_AUTH); read_kmem(); if (!dump_exists()) { /* (void)fprintf(stderr, "savecore: no core dump\n");*/ if (!force) exit(0); } if (clear) { clear_dump(); exit(0); } (void) time(&now); check_kmem(); if (panicstr) log(LOG_CRIT, "reboot after panic: %s\n", panic_mesg); else syslog(LOG_CRIT, "reboot\n"); if (access(dirname, W_OK) < 0) { Perror(LOG_ERR, "%s: %m\n", dirname); exit(1); } if ((!get_crashtime() || !check_space()) && !force) exit(1); if (save_core() == 0) clear_dump(); exit(0); } dump_exists() { int word; Lseek(dumpfd, dumplo + ok(dump_nl[X_DUMPMAG].n_value), L_SET); Read(dumpfd, (char *)&word, sizeof (word)); if (verbose && word != dumpmag) printf("magic number mismatch: %x != %x\n", word, dumpmag); return (word == dumpmag); } clear_dump() { int zero = 0; Lseek(dumpfd, dumplo + ok(dump_nl[X_DUMPMAG].n_value), L_SET); Write(dumpfd, (char *)&zero, sizeof (zero)); } char * find_dev(dev, type) register dev_t dev; register int type; { register DIR *dfd = opendir(_PATH_DEV); struct dirent *dir; struct stat statb; static char devname[MAXPATHLEN + 1]; char *dp; strcpy(devname, _PATH_DEV); while ((dir = readdir(dfd))) { strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name); if (stat(devname, &statb)) { perror(devname); continue; } if ((statb.st_mode&S_IFMT) != type) continue; if (dev == statb.st_rdev) { closedir(dfd); dp = malloc(strlen(devname)+1); strcpy(dp, devname); return (dp); } } closedir(dfd); log(LOG_ERR, "Can't find device %d/%d\n", major(dev), minor(dev)); exit(1); /*NOTREACHED*/ } char * rawname(s) char *s; { static char name[MAXPATHLEN]; char *sl, *rindex(); if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') { log(LOG_ERR, "can't make raw dump device name from %s?\n", s); return (s); } sprintf(name, "%.*s/r%s", sl - s, s, sl + 1); return (name); } int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 }; int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 }; read_kmem() { register char *cp; FILE *fp; char *dump_sys; int kmem, i; dump_sys = kernel ? kernel : _PATH_UNIX; nlist(_PATH_UNIX, current_nl); nlist(dump_sys, dump_nl); /* * 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!) */ for (i = 0; cursyms[i] != -1; i++) if (current_nl[cursyms[i]].n_value == 0) { log(LOG_ERR, "%s: %s not in namelist\n", _PATH_UNIX, current_nl[cursyms[i]].n_name); exit(1); } for (i = 0; dumpsyms[i] != -1; i++) if (dump_nl[dumpsyms[i]].n_value == 0) { log(LOG_ERR, "%s: %s not in namelist\n", dump_sys, dump_nl[dumpsyms[i]].n_name); exit(1); } kmem = Open(_PATH_KMEM, O_RDONLY); Lseek(kmem, current_nl[X_DUMPDEV].n_value, L_SET); Read(kmem, (char *)&dumpdev, sizeof (dumpdev)); if (dumpdev == NODEV) { if (verbose) printf("dumpdev = NODEV\n"); exit(2); } Lseek(kmem, current_nl[X_DUMPLO].n_value, L_SET); Read(kmem, (char *)&dumplo, sizeof (dumplo)); if (verbose) printf("dumplo = %d (%d * %d)\n", dumplo, dumplo/DEV_BSIZE, DEV_BSIZE); Lseek(kmem, current_nl[X_DUMPMAG].n_value, L_SET); Read(kmem, (char *)&dumpmag, sizeof (dumpmag)); dumplo *= DEV_BSIZE; ddname = find_dev(dumpdev, S_IFBLK); dumpfd = Open(ddname, O_RDWR); fp = fdopen(kmem, "r"); if (fp == NULL) { log(LOG_ERR, "Couldn't fdopen kmem\n"); exit(1); } if (kernel) return; fseek(fp, (long)current_nl[X_VERSION].n_value, L_SET); fgets(vers, sizeof (vers), fp); fclose(fp); } check_kmem() { FILE *fp; register char *cp; fp = fdopen(dumpfd, "r"); if (fp == NULL) { log(LOG_ERR, "Can't fdopen dumpfd\n"); exit(1); } fseek(fp, (dumplo+ok(dump_nl[X_VERSION].n_value)), L_SET); fgets(core_vers, sizeof (core_vers), fp); if (!eq(vers, core_vers) && kernel == 0) { log(LOG_WARNING, "Warning: %s version mismatch:\n", _PATH_UNIX); log(LOG_WARNING, "\t%s\n", vers); log(LOG_WARNING, "and\t%s\n", core_vers); } fseek(fp, (dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET); fread((char *)&panicstr, sizeof (panicstr), 1, fp); if (panicstr) { fseek(fp, dumplo + ok(panicstr), L_SET); cp = panic_mesg; do *cp = getc(fp); while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]); } /* don't fclose(fp); we want the file descriptor */ } get_crashtime() { time_t clobber = (time_t)0; Lseek(dumpfd, dumplo + ok(dump_nl[X_TIME].n_value), L_SET); Read(dumpfd, (char *)&dumptime, sizeof dumptime); if (dumptime == 0) { if (verbose) printf("Dump time is zero.\n"); return (0); } printf("System went down at %s", ctime(&dumptime)); if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) { printf("dump time is unreasonable\n"); return (0); } return (1); } char * path(file) char *file; { register char *cp = malloc(strlen(file) + strlen(dirname) + 2); (void) strcpy(cp, dirname); (void) strcat(cp, "/"); (void) strcat(cp, file); return (cp); } check_space() { long minfree, spacefree; struct statfs fsbuf; if (statfs(dirname, &fsbuf) < 0) { Perror(LOG_ERR, "%s: %m\n", dirname); exit(1); } spacefree = fsbuf.f_bavail * fsbuf.f_bsize / 1024; minfree = read_number("minfree"); if (minfree > 0 && spacefree - dumpsize < minfree) { log(LOG_WARNING, "Dump omitted, not enough space on device\n"); return (0); } if (spacefree - dumpsize < minfree) log(LOG_WARNING, "Dump performed, but free space threshold crossed\n"); return (1); } read_number(fn) char *fn; { char lin[80]; register FILE *fp; fp = fopen(path(fn), "r"); if (fp == NULL) return (0); if (fgets(lin, 80, fp) == NULL) { fclose(fp); return (0); } fclose(fp); return (atoi(lin)); } /*#define BUFSIZE (256*1024) /* 1/4 Mb */ #define BUFSIZE (8*1024) save_core() { register int n; register char *cp; register int ifd, ofd, bounds; int ret; char *bfile; register FILE *fp; cp = malloc(BUFSIZE); if (cp == 0) { log(LOG_ERR, "savecore: Can't allocate i/o buffer.\n"); return -1; } bounds = read_number("bounds"); ifd = Open(kernel ? kernel : _PATH_UNIX, O_RDONLY); (void)sprintf(cp, "system.%d", bounds); ofd = Create(path(cp), 0644); while((n = Read(ifd, cp, BUFSIZE)) > 0) Write(ofd, cp, n); close(ifd); close(ofd); if ((ifd = open(rawname(ddname), O_RDONLY)) == -1) { log(LOG_WARNING, "Can't open %s (%m); using block device", rawname(ddname)); ifd = dumpfd; } Lseek(dumpfd, dumplo + ok(dump_nl[X_DUMPSIZE].n_value), L_SET); Read(dumpfd, (char *)&dumpsize, sizeof (dumpsize)); (void)sprintf(cp, "ram.%d", bounds); ofd = Create(path(cp), 0644); Lseek(ifd, dumplo, L_SET); dumpsize *= NBPG; log(LOG_NOTICE, "Saving %d bytes of image in ram.%d\n", dumpsize, bounds); ret = 0; while (dumpsize > 0) { n = read(ifd, cp, dumpsize > BUFSIZE ? BUFSIZE : dumpsize); if (n <= 0) { if (n == 0) log(LOG_WARNING, "WARNING: EOF on dump device; %s\n", "ram file may be incomplete"); else Perror(LOG_ERR, "read from dumpdev: %m", "read"); ret = -1; break; } if ((ret = write(ofd, cp, n)) < n) { if (ret < 0) Perror(LOG_ERR, "write: %m", "write"); else log(LOG_ERR, "short write: wrote %d of %d\n", ret, n); log(LOG_WARNING, "WARNING: ram file may be incomplete\n"); ret = -1; break; } ret = 0; dumpsize -= n; } close(ifd); close(ofd); bfile = path("bounds"); fp = fopen(bfile, "w"); if (fp) { fprintf(fp, "%d\n", bounds+1); fclose(fp); } else Perror(LOG_ERR, "Can't create bounds file %s: %m", bfile); free(cp); return ret; } /* * Versions of std routines that exit on error. */ Open(name, rw) char *name; int rw; { int fd; fd = open(name, rw); if (fd < 0) { Perror(LOG_ERR, "%s: %m", name); exit(1); } return (fd); } Read(fd, buff, size) int fd, size; char *buff; { int ret; ret = read(fd, buff, size); if (ret < 0) { Perror(LOG_ERR, "read: %m", "read"); exit(1); } return (ret); } off_t Lseek(fd, off, flag) int fd, flag; long off; { long ret; ret = lseek(fd, off, flag); if (ret == -1) { Perror(LOG_ERR, "lseek: %m", "lseek"); exit(1); } return (ret); } Create(file, mode) char *file; int mode; { register int fd; fd = creat(file, mode); if (fd < 0) { Perror(LOG_ERR, "%s: %m", file); exit(1); } return (fd); } Write(fd, buf, size) int fd, size; char *buf; { int n; if ((n = write(fd, buf, size)) < size) { if (n < 0) Perror(LOG_ERR, "write: %m", "write"); else log(LOG_ERR, "short write: wrote %d of %d\n", n, size); exit(1); } } /* VARARGS2 */ log(level, msg, a1, a2) int level; char *msg; { fprintf(stderr, msg, a1, a2); syslog(level, msg, a1, a2); } Perror(level, msg, s) int level; char *msg, *s; { int oerrno = errno; perror(s); errno = oerrno; syslog(level, msg, s); } usage() { (void)fprintf(stderr, "usage: savecore [-cfv] dirname [system]\n"); exit(1); }