savecore.c revision 94580
11558Srgrimes/*- 293492Sphk * Copyright (c) 2002 Poul-Henning Kamp 393492Sphk * Copyright (c) 2002 Networks Associates Technology, Inc. 493492Sphk * All rights reserved. 51558Srgrimes * 693492Sphk * This software was developed for the FreeBSD Project by Poul-Henning Kamp 793492Sphk * and NAI Labs, the Security Research Division of Network Associates, Inc. 893492Sphk * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 993492Sphk * DARPA CHATS research program. 1093492Sphk * 111558Srgrimes * Redistribution and use in source and binary forms, with or without 121558Srgrimes * modification, are permitted provided that the following conditions 131558Srgrimes * are met: 141558Srgrimes * 1. Redistributions of source code must retain the above copyright 151558Srgrimes * notice, this list of conditions and the following disclaimer. 161558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 171558Srgrimes * notice, this list of conditions and the following disclaimer in the 181558Srgrimes * documentation and/or other materials provided with the distribution. 1993492Sphk * 3. The names of the authors may not be used to endorse or promote 2093492Sphk * products derived from this software without specific prior written 2193492Sphk * permission. 221558Srgrimes * 2393492Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 241558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 251558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2693492Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 271558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 281558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 291558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 301558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 311558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 321558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 331558Srgrimes * SUCH DAMAGE. 3493492Sphk * 3593492Sphk * $FreeBSD: head/sbin/savecore/savecore.c 94580 2002-04-13 08:20:15Z marcel $ 361558Srgrimes */ 371558Srgrimes 3894580Smarcel#include <sys/types.h> 3994580Smarcel#include <sys/disk.h> 4094580Smarcel#include <sys/kerneldump.h> 4194580Smarcel#include <sys/stat.h> 4293492Sphk#include <err.h> 4394580Smarcel#include <errno.h> 4493492Sphk#include <fcntl.h> 4593492Sphk#include <fstab.h> 4694580Smarcel#include <md5.h> 4794580Smarcel#include <stdio.h> 4894580Smarcel#include <stdlib.h> 4994580Smarcel#include <string.h> 5093492Sphk#include <time.h> 5193492Sphk#include <unistd.h> 521558Srgrimes 5394580Smarcelint clear, force, keep, verbose; /* flags */ 5494580Smarcelint nfound, nsaved; /* statistics */ 5594580Smarcel 5693492Sphkstatic void 5794580Smarcelprintheader(FILE *f, const struct kerneldumpheader *h, const char *device, 5893717Smarcel const char *md5) 591558Srgrimes{ 6093717Smarcel uint64_t dumplen; 6193492Sphk time_t t; 621558Srgrimes 6394580Smarcel fprintf(f, "Good dump found on device %s\n", device); 6493492Sphk fprintf(f, " Architecture: %s\n", h->architecture); 6593717Smarcel fprintf(f, " Architecture version: %d\n", 6693717Smarcel dtoh32(h->architectureversion)); 6793717Smarcel dumplen = dtoh64(h->dumplength); 6893717Smarcel fprintf(f, " Dump length: %lldB (%lld MB)\n", (long long)dumplen, 6993717Smarcel (long long)(dumplen >> 20)); 7093717Smarcel fprintf(f, " Blocksize: %d\n", dtoh32(h->blocksize)); 7193717Smarcel t = dtoh64(h->dumptime); 7293492Sphk fprintf(f, " Dumptime: %s", ctime(&t)); 7393492Sphk fprintf(f, " Hostname: %s\n", h->hostname); 7493492Sphk fprintf(f, " Versionstring: %s", h->versionstring); 7593492Sphk fprintf(f, " Panicstring: %s\n", h->panicstring); 7693492Sphk fprintf(f, " MD5: %s\n", md5); 771558Srgrimes} 781558Srgrimes 7993717Smarcel 8093492Sphkstatic void 8194580SmarcelDoFile(const char *device) 821558Srgrimes{ 8394580Smarcel struct kerneldumpheader kdhf, kdhl; 8494580Smarcel char buf[BUFSIZ]; 8594580Smarcel struct stat sb; 8693492Sphk off_t mediasize, dumpsize, firsthd, lasthd; 8793492Sphk char *md5; 8893492Sphk FILE *info; 8994580Smarcel int fd, fdcore, fdinfo, error, wl; 9094580Smarcel u_int sectorsize; 918871Srgrimes 9294580Smarcel if (verbose) 9394580Smarcel printf("Checking for kernel dump on device %s\n", device); 9494580Smarcel 9593492Sphk mediasize = 0; 9694580Smarcel fd = open(device, O_RDWR); 9793492Sphk if (fd < 0) { 9894580Smarcel warn("%s", device); 9993492Sphk return; 10047095Sluoqi } 10193492Sphk error = ioctl(fd, DIOCGMEDIASIZE, &mediasize); 10293492Sphk if (!error) 10393492Sphk error = ioctl(fd, DIOCGSECTORSIZE, §orsize); 10493492Sphk if (error) { 10594580Smarcel warn("Couldn't find media and/or sector size of %s)", device); 10694580Smarcel goto closefd; 1071558Srgrimes } 10894580Smarcel 10994580Smarcel if (verbose) { 11094580Smarcel printf("Mediasize = %lld\n", (long long)mediasize); 11194580Smarcel printf("Sectorsize = %u\n", sectorsize); 11294580Smarcel } 11394580Smarcel 11493492Sphk lasthd = mediasize - sectorsize; 11593492Sphk lseek(fd, lasthd, SEEK_SET); 11693492Sphk error = read(fd, &kdhl, sizeof kdhl); 11793492Sphk if (error != sizeof kdhl) { 11894580Smarcel warn("Error reading last dump header at offset %lld in %s", 11994580Smarcel (long long)lasthd, device); 12094580Smarcel goto closefd; 1211558Srgrimes } 12293492Sphk if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) { 12394580Smarcel if (verbose) 12494580Smarcel warnx("Magic mismatch on last dump header on %s", 12594580Smarcel device); 12694580Smarcel goto closefd; 1271558Srgrimes } 12893717Smarcel if (dtoh32(kdhl.version) != KERNELDUMPVERSION) { 12994580Smarcel warnx("Unknown version (%d) in last dump header on %s", 13094580Smarcel dtoh32(kdhl.version), device); 13194580Smarcel goto closefd; 13266429Sdes } 13394580Smarcel 13494580Smarcel nfound++; 13594580Smarcel if (clear) 13694580Smarcel goto nuke; 13794580Smarcel 13894580Smarcel if (kerneldump_parity(&kdhl)) { 13994580Smarcel warnx("Parity error on last dump header on %s", device); 14094580Smarcel goto closefd; 14194580Smarcel } 14293717Smarcel dumpsize = dtoh64(kdhl.dumplength); 14393717Smarcel firsthd = lasthd - dumpsize - sizeof kdhf; 14493492Sphk lseek(fd, firsthd, SEEK_SET); 14593492Sphk error = read(fd, &kdhf, sizeof kdhf); 14693492Sphk if (error != sizeof kdhf) { 14794580Smarcel warn("Error reading first dump header at offset %lld in %s", 14894580Smarcel (long long)firsthd, device); 14994580Smarcel goto closefd; 1501558Srgrimes } 15193492Sphk if (memcmp(&kdhl, &kdhf, sizeof kdhl)) { 15294580Smarcel warn("First and last dump headers disagree on %s", device); 15394580Smarcel goto closefd; 15466429Sdes } 15593492Sphk md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL); 15693492Sphk sprintf(buf, "%s.info", md5); 15794580Smarcel 15894580Smarcel /* 15994580Smarcel * See if the dump has been saved already. Don't save the dump 16094580Smarcel * again, unless 'force' is in effect. 16194580Smarcel */ 16294580Smarcel if (stat(buf, &sb) == 0) { 16394580Smarcel if (!force) { 16494580Smarcel if (verbose) 16594580Smarcel printf("Dump on device %s already saved\n", 16694580Smarcel device); 16794580Smarcel goto closefd; 16894580Smarcel } 16994580Smarcel } else if (errno != ENOENT) { 17094580Smarcel warn("Error while checking for pre-saved core file"); 17194580Smarcel goto closefd; 1721558Srgrimes } 17394580Smarcel 17494580Smarcel /* 17594580Smarcel * Create or overwrite any existing files. 17694580Smarcel */ 17794580Smarcel fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 17893492Sphk if (fdinfo < 0) { 17993492Sphk warn("%s", buf); 18094580Smarcel goto closefd; 1811558Srgrimes } 18293492Sphk sprintf(buf, "%s.core", md5); 18394580Smarcel fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600); 18493492Sphk if (fdcore < 0) { 18593492Sphk warn("%s", buf); 18694580Smarcel close(fdinfo); 18794580Smarcel goto closefd; 18893492Sphk } 18993492Sphk info = fdopen(fdinfo, "w"); 19094580Smarcel 19194580Smarcel if (verbose) 19294580Smarcel printheader(stdout, &kdhl, device, md5); 19394580Smarcel 19494580Smarcel printf("Saving dump to file %s\n", buf); 19594580Smarcel nsaved++; 19694580Smarcel 19794580Smarcel printheader(info, &kdhl, device, md5); 19894580Smarcel 19993492Sphk while (dumpsize > 0) { 20093492Sphk wl = sizeof(buf); 20193492Sphk if (wl > dumpsize) 20293492Sphk wl = dumpsize; 20393492Sphk error = read(fd, buf, wl); 20493492Sphk if (error != wl) { 20594580Smarcel warn("Read error on %s", device); 20694580Smarcel goto closeall; 20767264Sdes } 20893492Sphk error = write(fdcore, buf, wl); 20993492Sphk if (error != wl) { 21094580Smarcel warn("Write error on %s.core file", md5); 21194580Smarcel goto closeall; 21293492Sphk } 21393492Sphk dumpsize -= wl; 21467264Sdes } 21594580Smarcel close(fdinfo); 21694580Smarcel close(fdcore); 21794580Smarcel 21894580Smarcel if (verbose) 21994580Smarcel printf("Dump saved\n"); 22094580Smarcel 22194580Smarcel nuke: 22294580Smarcel if (clear || !keep) { 22394580Smarcel if (verbose) 22494580Smarcel printf("Clearing dump header\n"); 22594580Smarcel memset(&kdhl, 0, sizeof kdhl); 22694580Smarcel lseek(fd, lasthd, SEEK_SET); 22794580Smarcel error = write(fd, &kdhl, sizeof kdhl); 22894580Smarcel if (error != sizeof kdhl) 22994580Smarcel warn("Error while clearing the dump header"); 23094580Smarcel } 23194580Smarcel close(fd); 23294580Smarcel return; 23394580Smarcel 23494580Smarcel closeall: 23594580Smarcel close(fdinfo); 23694580Smarcel close(fdcore); 23794580Smarcel 23894580Smarcel closefd: 23994580Smarcel close(fd); 2401558Srgrimes} 2411558Srgrimes 24293492Sphkstatic void 24393492Sphkusage(void) 2441558Srgrimes{ 24594580Smarcel errx(1, "usage: savecore [-cfkv] [directory [device...]]"); 24693492Sphk exit (1); 2471558Srgrimes} 2481558Srgrimes 24918914Sfennerint 25093492Sphkmain(int argc, char **argv) 2511558Srgrimes{ 25293492Sphk int i, ch, error; 25393492Sphk struct fstab *fsp; 2541558Srgrimes 25593492Sphk while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1) 25693492Sphk switch(ch) { 25793492Sphk case 'c': 25894580Smarcel clear = 1; 25994580Smarcel break; 26094580Smarcel case 'k': 26194580Smarcel keep = 1; 26294580Smarcel break; 26393492Sphk case 'v': 26494580Smarcel verbose = 1; 26594580Smarcel break; 26693492Sphk case 'f': 26794580Smarcel force = 1; 26894580Smarcel break; 26994580Smarcel case 'd': /* Obsolete */ 27093492Sphk case 'N': 27193492Sphk case 'z': 27293492Sphk case '?': 27393492Sphk default: 27493492Sphk usage(); 27593492Sphk } 27693492Sphk argc -= optind; 27793492Sphk argv += optind; 27893492Sphk if (argc >= 1) { 27993492Sphk error = chdir(argv[0]); 28093492Sphk if (error) 28193492Sphk err(1, "chdir(%s)", argv[0]); 28293492Sphk argc--; 28393492Sphk argv++; 2841558Srgrimes } 28593492Sphk if (argc == 0) { 28693492Sphk for (;;) { 28793492Sphk fsp = getfsent(); 28893492Sphk if (fsp == NULL) 28993492Sphk break; 29093492Sphk if (strcmp(fsp->fs_vfstype, "swap") && 29193492Sphk strcmp(fsp->fs_vfstype, "dump")) 29293492Sphk continue; 29393492Sphk DoFile(fsp->fs_spec); 29493492Sphk } 29593492Sphk } else { 29693492Sphk for (i = 0; i < argc; i++) 29793492Sphk DoFile(argv[i]); 2981558Srgrimes } 29994580Smarcel 30094580Smarcel /* Emit minimal output. */ 30194580Smarcel if (nfound == 0) 30294580Smarcel printf("No dumps found\n"); 30394580Smarcel else if (nsaved == 0) 30494580Smarcel printf("No unsaved dumps found\n"); 30594580Smarcel 30693492Sphk return (0); 3071558Srgrimes} 308