savecore.c revision 95183
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.
341558Srgrimes */
351558Srgrimes
3695183Scharnier#include <sys/cdefs.h>
3795183Scharnier__FBSDID("$FreeBSD: head/sbin/savecore/savecore.c 95183 2002-04-21 07:18:16Z charnier $");
3895183Scharnier
3994580Smarcel#include <sys/types.h>
4094580Smarcel#include <sys/disk.h>
4194580Smarcel#include <sys/kerneldump.h>
4294580Smarcel#include <sys/stat.h>
4393492Sphk#include <err.h>
4494580Smarcel#include <errno.h>
4593492Sphk#include <fcntl.h>
4693492Sphk#include <fstab.h>
4794580Smarcel#include <md5.h>
4894580Smarcel#include <stdio.h>
4994580Smarcel#include <stdlib.h>
5094580Smarcel#include <string.h>
5193492Sphk#include <time.h>
5293492Sphk#include <unistd.h>
531558Srgrimes
5494580Smarcelint clear, force, keep, verbose;	/* flags */
5594580Smarcelint nfound, nsaved;			/* statistics */
5694580Smarcel
5793492Sphkstatic void
5894580Smarcelprintheader(FILE *f, const struct kerneldumpheader *h, const char *device,
5993717Smarcel    const char *md5)
601558Srgrimes{
6193717Smarcel	uint64_t dumplen;
6293492Sphk	time_t t;
631558Srgrimes
6494580Smarcel	fprintf(f, "Good dump found on device %s\n", device);
6593492Sphk	fprintf(f, "  Architecture: %s\n", h->architecture);
6693717Smarcel	fprintf(f, "  Architecture version: %d\n",
6793717Smarcel	    dtoh32(h->architectureversion));
6893717Smarcel	dumplen = dtoh64(h->dumplength);
6993717Smarcel	fprintf(f, "  Dump length: %lldB (%lld MB)\n", (long long)dumplen,
7093717Smarcel	    (long long)(dumplen >> 20));
7193717Smarcel	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
7293717Smarcel	t = dtoh64(h->dumptime);
7393492Sphk	fprintf(f, "  Dumptime: %s", ctime(&t));
7493492Sphk	fprintf(f, "  Hostname: %s\n", h->hostname);
7593492Sphk	fprintf(f, "  Versionstring: %s", h->versionstring);
7693492Sphk	fprintf(f, "  Panicstring: %s\n", h->panicstring);
7793492Sphk	fprintf(f, "  MD5: %s\n", md5);
7895039Sphk	fflush(f);
791558Srgrimes}
801558Srgrimes
8193717Smarcel
8293492Sphkstatic void
8394580SmarcelDoFile(const char *device)
841558Srgrimes{
8594580Smarcel	struct kerneldumpheader kdhf, kdhl;
8694580Smarcel	char buf[BUFSIZ];
8794580Smarcel	struct stat sb;
8893492Sphk	off_t mediasize, dumpsize, firsthd, lasthd;
8993492Sphk	char *md5;
9093492Sphk	FILE *info;
9194580Smarcel	int fd, fdcore, fdinfo, error, wl;
9294580Smarcel	u_int sectorsize;
938871Srgrimes
9494580Smarcel	if (verbose)
9594580Smarcel		printf("Checking for kernel dump on device %s\n", device);
9694580Smarcel
9793492Sphk	mediasize = 0;
9894580Smarcel	fd = open(device, O_RDWR);
9993492Sphk	if (fd < 0) {
10094580Smarcel		warn("%s", device);
10193492Sphk		return;
10247095Sluoqi	}
10393492Sphk	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
10493492Sphk	if (!error)
10593492Sphk		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
10693492Sphk	if (error) {
10795183Scharnier		warn("couldn't find media and/or sector size of %s", device);
10894580Smarcel		goto closefd;
1091558Srgrimes	}
11094580Smarcel
11194580Smarcel	if (verbose) {
11294580Smarcel		printf("Mediasize = %lld\n", (long long)mediasize);
11394580Smarcel		printf("Sectorsize = %u\n", sectorsize);
11494580Smarcel	}
11594580Smarcel
11693492Sphk	lasthd = mediasize - sectorsize;
11793492Sphk	lseek(fd, lasthd, SEEK_SET);
11893492Sphk	error = read(fd, &kdhl, sizeof kdhl);
11993492Sphk	if (error != sizeof kdhl) {
12095183Scharnier		warn("error reading last dump header at offset %lld in %s",
12194580Smarcel		    (long long)lasthd, device);
12294580Smarcel		goto closefd;
1231558Srgrimes	}
12493492Sphk	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
12594580Smarcel		if (verbose)
12695183Scharnier			warnx("magic mismatch on last dump header on %s",
12794580Smarcel			    device);
12894580Smarcel		goto closefd;
1291558Srgrimes	}
13093717Smarcel	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
13195183Scharnier		warnx("unknown version (%d) in last dump header on %s",
13294580Smarcel		    dtoh32(kdhl.version), device);
13394580Smarcel		goto closefd;
13466429Sdes	}
13594580Smarcel
13694580Smarcel	nfound++;
13794580Smarcel	if (clear)
13894580Smarcel		goto nuke;
13994580Smarcel
14094580Smarcel	if (kerneldump_parity(&kdhl)) {
14195183Scharnier		warnx("parity error on last dump header on %s", device);
14294580Smarcel		goto closefd;
14394580Smarcel	}
14493717Smarcel	dumpsize = dtoh64(kdhl.dumplength);
14593717Smarcel	firsthd = lasthd - dumpsize - sizeof kdhf;
14693492Sphk	lseek(fd, firsthd, SEEK_SET);
14793492Sphk	error = read(fd, &kdhf, sizeof kdhf);
14893492Sphk	if (error != sizeof kdhf) {
14995183Scharnier		warn("error reading first dump header at offset %lld in %s",
15094580Smarcel		    (long long)firsthd, device);
15194580Smarcel		goto closefd;
1521558Srgrimes	}
15393492Sphk	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
15495183Scharnier		warn("first and last dump headers disagree on %s", device);
15594580Smarcel		goto closefd;
15666429Sdes	}
15793492Sphk	md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL);
15893492Sphk	sprintf(buf, "%s.info", md5);
15994580Smarcel
16094580Smarcel	/*
16194580Smarcel	 * See if the dump has been saved already. Don't save the dump
16294580Smarcel	 * again, unless 'force' is in effect.
16394580Smarcel	 */
16494580Smarcel	if (stat(buf, &sb) == 0) {
16594580Smarcel		if (!force) {
16694580Smarcel			if (verbose)
16794580Smarcel				printf("Dump on device %s already saved\n",
16894580Smarcel				    device);
16994580Smarcel			goto closefd;
17094580Smarcel		}
17194580Smarcel	} else if (errno != ENOENT) {
17295183Scharnier		warn("error while checking for pre-saved core file");
17394580Smarcel		goto closefd;
1741558Srgrimes	}
17594580Smarcel
17694580Smarcel	/*
17794580Smarcel	 * Create or overwrite any existing files.
17894580Smarcel	 */
17994580Smarcel	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
18093492Sphk	if (fdinfo < 0) {
18193492Sphk		warn("%s", buf);
18294580Smarcel		goto closefd;
1831558Srgrimes	}
18493492Sphk	sprintf(buf, "%s.core", md5);
18594580Smarcel	fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
18693492Sphk	if (fdcore < 0) {
18793492Sphk		warn("%s", buf);
18894580Smarcel		close(fdinfo);
18994580Smarcel		goto closefd;
19093492Sphk	}
19193492Sphk	info = fdopen(fdinfo, "w");
19294580Smarcel
19394580Smarcel	if (verbose)
19494580Smarcel		printheader(stdout, &kdhl, device, md5);
19594580Smarcel
19694580Smarcel	printf("Saving dump to file %s\n", buf);
19794580Smarcel	nsaved++;
19894580Smarcel
19994580Smarcel	printheader(info, &kdhl, device, md5);
20094580Smarcel
20193492Sphk	while (dumpsize > 0) {
20293492Sphk		wl = sizeof(buf);
20393492Sphk		if (wl > dumpsize)
20493492Sphk			wl = dumpsize;
20593492Sphk		error = read(fd, buf, wl);
20693492Sphk		if (error != wl) {
20795183Scharnier			warn("read error on %s", device);
20894580Smarcel			goto closeall;
20967264Sdes		}
21093492Sphk		error = write(fdcore, buf, wl);
21193492Sphk		if (error != wl) {
21295183Scharnier			warn("write error on %s.core file", md5);
21394580Smarcel			goto closeall;
21493492Sphk		}
21593492Sphk		dumpsize -= wl;
21667264Sdes	}
21794580Smarcel	close(fdinfo);
21894580Smarcel	close(fdcore);
21994580Smarcel
22094580Smarcel	if (verbose)
22194580Smarcel		printf("Dump saved\n");
22294580Smarcel
22394580Smarcel nuke:
22494580Smarcel	if (clear || !keep) {
22594580Smarcel		if (verbose)
22694580Smarcel			printf("Clearing dump header\n");
22794580Smarcel		memset(&kdhl, 0, sizeof kdhl);
22894580Smarcel		lseek(fd, lasthd, SEEK_SET);
22994580Smarcel		error = write(fd, &kdhl, sizeof kdhl);
23094580Smarcel		if (error != sizeof kdhl)
23195183Scharnier			warn("error while clearing the dump header");
23294580Smarcel	}
23394580Smarcel	close(fd);
23494580Smarcel	return;
23594580Smarcel
23694580Smarcel closeall:
23794580Smarcel	close(fdinfo);
23894580Smarcel	close(fdcore);
23994580Smarcel
24094580Smarcel closefd:
24194580Smarcel	close(fd);
2421558Srgrimes}
2431558Srgrimes
24493492Sphkstatic void
24593492Sphkusage(void)
2461558Srgrimes{
24795183Scharnier	fprintf(stderr, "usage: savecore [-cfkv] [directory [device...]]\n");
24893492Sphk	exit (1);
2491558Srgrimes}
2501558Srgrimes
25118914Sfennerint
25293492Sphkmain(int argc, char **argv)
2531558Srgrimes{
25493492Sphk	int i, ch, error;
25593492Sphk	struct fstab *fsp;
2561558Srgrimes
25793492Sphk	while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1)
25893492Sphk		switch(ch) {
25993492Sphk		case 'c':
26094580Smarcel			clear = 1;
26194580Smarcel			break;
26294580Smarcel		case 'k':
26394580Smarcel			keep = 1;
26494580Smarcel			break;
26593492Sphk		case 'v':
26694580Smarcel			verbose = 1;
26794580Smarcel			break;
26893492Sphk		case 'f':
26994580Smarcel			force = 1;
27094580Smarcel			break;
27194580Smarcel		case 'd':	/* Obsolete */
27293492Sphk		case 'N':
27393492Sphk		case 'z':
27493492Sphk		case '?':
27593492Sphk		default:
27693492Sphk			usage();
27793492Sphk		}
27893492Sphk	argc -= optind;
27993492Sphk	argv += optind;
28093492Sphk	if (argc >= 1) {
28193492Sphk		error = chdir(argv[0]);
28293492Sphk		if (error)
28393492Sphk			err(1, "chdir(%s)", argv[0]);
28493492Sphk		argc--;
28593492Sphk		argv++;
2861558Srgrimes	}
28793492Sphk	if (argc == 0) {
28893492Sphk		for (;;) {
28993492Sphk			fsp = getfsent();
29093492Sphk			if (fsp == NULL)
29193492Sphk				break;
29293492Sphk			if (strcmp(fsp->fs_vfstype, "swap") &&
29393492Sphk			    strcmp(fsp->fs_vfstype, "dump"))
29493492Sphk				continue;
29593492Sphk			DoFile(fsp->fs_spec);
29693492Sphk		}
29793492Sphk	} else {
29893492Sphk		for (i = 0; i < argc; i++)
29993492Sphk			DoFile(argv[i]);
3001558Srgrimes	}
30194580Smarcel
30294580Smarcel	/* Emit minimal output. */
30394580Smarcel	if (nfound == 0)
30494580Smarcel		printf("No dumps found\n");
30594580Smarcel	else if (nsaved == 0)
30694580Smarcel		printf("No unsaved dumps found\n");
30794580Smarcel
30893492Sphk	return (0);
3091558Srgrimes}
310