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, &sectorsize);
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