savecore.c revision 96025
11541Srgrimes/*-
21541Srgrimes * Copyright (c) 2002 Poul-Henning Kamp
31541Srgrimes * Copyright (c) 2002 Networks Associates Technology, Inc.
41541Srgrimes * All rights reserved.
51541Srgrimes *
61541Srgrimes * This software was developed for the FreeBSD Project by Poul-Henning Kamp
71541Srgrimes * and NAI Labs, the Security Research Division of Network Associates, Inc.
81541Srgrimes * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
91541Srgrimes * DARPA CHATS research program.
101541Srgrimes *
111541Srgrimes * Redistribution and use in source and binary forms, with or without
121541Srgrimes * modification, are permitted provided that the following conditions
131541Srgrimes * are met:
141541Srgrimes * 1. Redistributions of source code must retain the above copyright
151541Srgrimes *    notice, this list of conditions and the following disclaimer.
161541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
171541Srgrimes *    notice, this list of conditions and the following disclaimer in the
181541Srgrimes *    documentation and/or other materials provided with the distribution.
191541Srgrimes * 3. The names of the authors may not be used to endorse or promote
201541Srgrimes *    products derived from this software without specific prior written
211541Srgrimes *    permission.
221541Srgrimes *
231541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
241541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
251541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
261541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
271541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
281541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
291541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
301541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
311541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
321541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
331541Srgrimes * SUCH DAMAGE.
3413765Smpp */
351541Srgrimes
361541Srgrimes#include <sys/cdefs.h>
372165Spaul__FBSDID("$FreeBSD: head/sbin/savecore/savecore.c 96025 2002-05-04 10:36:35Z mux $");
382811Sbde
392165Spaul#include <sys/types.h>
401541Srgrimes#include <sys/disk.h>
411541Srgrimes#include <sys/kerneldump.h>
421541Srgrimes#include <sys/param.h>
431541Srgrimes#include <sys/mount.h>
441541Srgrimes#include <sys/stat.h>
451541Srgrimes#include <err.h>
461541Srgrimes#include <errno.h>
471541Srgrimes#include <fcntl.h>
481541Srgrimes#include <fstab.h>
491541Srgrimes#include <md5.h>
501541Srgrimes#include <paths.h>
511541Srgrimes#include <stdio.h>
521541Srgrimes#include <stdlib.h>
531541Srgrimes#include <string.h>
541541Srgrimes#include <time.h>
551541Srgrimes#include <unistd.h>
561541Srgrimes
571541Srgrimesint clear, force, keep, verbose;	/* flags */
581541Srgrimesint nfound, nsaved, nerr;		/* statistics */
591541Srgrimes
601541Srgrimesstatic void
617945Sjulianprintheader(FILE *f, const struct kerneldumpheader *h, const char *device,
621541Srgrimes    const char *md5)
631541Srgrimes{
641541Srgrimes	uint64_t dumplen;
651541Srgrimes	time_t t;
661541Srgrimes
671541Srgrimes	fprintf(f, "Good dump found on device %s\n", device);
681541Srgrimes	fprintf(f, "  Architecture: %s\n", h->architecture);
691541Srgrimes	fprintf(f, "  Architecture version: %d\n",
7012158Sbde	    dtoh32(h->architectureversion));
7112158Sbde	dumplen = dtoh64(h->dumplength);
721541Srgrimes	fprintf(f, "  Dump length: %lldB (%lld MB)\n", (long long)dumplen,
731541Srgrimes	    (long long)(dumplen >> 20));
741541Srgrimes	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
751541Srgrimes	t = dtoh64(h->dumptime);
761541Srgrimes	fprintf(f, "  Dumptime: %s", ctime(&t));
771541Srgrimes	fprintf(f, "  Hostname: %s\n", h->hostname);
781541Srgrimes	fprintf(f, "  Versionstring: %s", h->versionstring);
791541Srgrimes	fprintf(f, "  Panicstring: %s\n", h->panicstring);
8012158Sbde	fprintf(f, "  MD5: %s\n", md5);
811541Srgrimes	fflush(f);
821541Srgrimes}
831541Srgrimes
841541Srgrimes/*
851541Srgrimes * Check that sufficient space is available on the disk that holds the
861541Srgrimes * save directory.
871541Srgrimes */
881541Srgrimesstatic int
891541Srgrimescheck_space(char *savedir, off_t dumpsize)
901541Srgrimes{
911541Srgrimes	FILE *fp;
921541Srgrimes	const char *tkernel;
931541Srgrimes	off_t minfree, spacefree, totfree, kernelsize, needed;
941541Srgrimes	struct stat st;
951541Srgrimes	struct statfs fsbuf;
961541Srgrimes	char buf[100], path[MAXPATHLEN];
971541Srgrimes
981541Srgrimes	tkernel = getbootfile();
9913490Sdyson	if (stat(tkernel, &st) < 0)
1001541Srgrimes		err(1, "%s", tkernel);
1019356Sdg	kernelsize = st.st_blocks * S_BLKSIZE;
1021541Srgrimes
1031541Srgrimes	if (statfs(savedir, &fsbuf) < 0)
1041541Srgrimes		err(1, "%s", savedir);
1051541Srgrimes 	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
1061541Srgrimes	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
1071541Srgrimes
1081541Srgrimes	(void)snprintf(path, sizeof(path), "%s/minfree", savedir);
1091541Srgrimes	if ((fp = fopen(path, "r")) == NULL)
1101541Srgrimes		minfree = 0;
1111541Srgrimes	else {
1121541Srgrimes		if (fgets(buf, sizeof(buf), fp) == NULL)
11312873Sbde			minfree = 0;
11412873Sbde		else
11512873Sbde			minfree = atoi(buf);
11612873Sbde		(void)fclose(fp);
11712873Sbde	}
11812873Sbde
11912873Sbde	needed = (dumpsize + kernelsize) / 1024;
12012873Sbde 	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
12112873Sbde		warnx("no dump, not enough free space on device"
12212873Sbde		    " (%lld available, need %lld)",
12312873Sbde		    (long long)(minfree > 0 ? spacefree : totfree),
12412873Sbde		    (long long)needed);
12512873Sbde		return (0);
1261541Srgrimes	}
1271541Srgrimes	if (spacefree - needed < 0)
1281541Srgrimes		warnx("dump performed, but free space threshold crossed");
1291541Srgrimes	return (1);
1301541Srgrimes}
1311541Srgrimes
1321541Srgrimes
1331541Srgrimes
1341541Srgrimesstatic void
1351541SrgrimesDoFile(char *savedir, const char *device)
1361541Srgrimes{
1371541Srgrimes	struct kerneldumpheader kdhf, kdhl;
1381541Srgrimes	char buf[BUFSIZ];
1391541Srgrimes	struct stat sb;
1401541Srgrimes	off_t mediasize, dumpsize, firsthd, lasthd;
1411541Srgrimes	char *md5;
1421541Srgrimes	FILE *info;
1431541Srgrimes	int fd, fdcore, fdinfo, error, wl;
1441541Srgrimes	u_int sectorsize;
1451541Srgrimes
1461541Srgrimes	if (verbose)
1471541Srgrimes		printf("Checking for kernel dump on device %s\n", device);
1481541Srgrimes
1491541Srgrimes	mediasize = 0;
1501541Srgrimes	fd = open(device, O_RDWR);
1511541Srgrimes	if (fd < 0) {
1521541Srgrimes		warn("%s", device);
1531541Srgrimes		return;
1541541Srgrimes	}
1551541Srgrimes	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
1561541Srgrimes	if (!error)
1571541Srgrimes		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
1581541Srgrimes	if (error) {
1591541Srgrimes		warn("couldn't find media and/or sector size of %s", device);
1601541Srgrimes		goto closefd;
1611541Srgrimes	}
1621541Srgrimes
1631541Srgrimes	if (verbose) {
1641541Srgrimes		printf("Mediasize = %lld\n", (long long)mediasize);
1651541Srgrimes		printf("Sectorsize = %u\n", sectorsize);
16612873Sbde	}
1671541Srgrimes
1681541Srgrimes	lasthd = mediasize - sectorsize;
1691541Srgrimes	lseek(fd, lasthd, SEEK_SET);
1701541Srgrimes	error = read(fd, &kdhl, sizeof kdhl);
1711541Srgrimes	if (error != sizeof kdhl) {
1721541Srgrimes		warn("error reading last dump header at offset %lld in %s",
1731541Srgrimes		    (long long)lasthd, device);
1741541Srgrimes		goto closefd;
1751541Srgrimes	}
1761541Srgrimes	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
1771541Srgrimes		if (verbose)
1781541Srgrimes			warnx("magic mismatch on last dump header on %s",
1791541Srgrimes			    device);
1801541Srgrimes		goto closefd;
1811541Srgrimes	}
1821541Srgrimes	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
1831541Srgrimes		warnx("unknown version (%d) in last dump header on %s",
1841541Srgrimes		    dtoh32(kdhl.version), device);
1851541Srgrimes		goto closefd;
1861541Srgrimes	}
1871541Srgrimes
1881541Srgrimes	nfound++;
1891541Srgrimes	if (clear)
1901541Srgrimes		goto nuke;
1911541Srgrimes
1921541Srgrimes	if (kerneldump_parity(&kdhl)) {
1931541Srgrimes		warnx("parity error on last dump header on %s", device);
1941541Srgrimes		nerr++;
1951541Srgrimes		goto closefd;
1961541Srgrimes	}
1971541Srgrimes	dumpsize = dtoh64(kdhl.dumplength);
19813765Smpp	firsthd = lasthd - dumpsize - sizeof kdhf;
19913765Smpp	lseek(fd, firsthd, SEEK_SET);
2001541Srgrimes	error = read(fd, &kdhf, sizeof kdhf);
2011541Srgrimes	if (error != sizeof kdhf) {
2021541Srgrimes		warn("error reading first dump header at offset %lld in %s",
2031541Srgrimes		    (long long)firsthd, device);
2041541Srgrimes		nerr++;
2051541Srgrimes		goto closefd;
2061541Srgrimes	}
2071541Srgrimes	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
2081541Srgrimes		warn("first and last dump headers disagree on %s", device);
2091541Srgrimes		nerr++;
2101541Srgrimes		goto closefd;
2111541Srgrimes	}
2121541Srgrimes	md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL);
2131541Srgrimes	sprintf(buf, "%s.info", md5);
2141541Srgrimes
2151541Srgrimes	/*
2161541Srgrimes	 * See if the dump has been saved already. Don't save the dump
2171541Srgrimes	 * again, unless 'force' is in effect.
2181541Srgrimes	 */
2191541Srgrimes	if (stat(buf, &sb) == 0) {
2201541Srgrimes		if (!force) {
2212946Swollman			if (verbose)
22212873Sbde				printf("Dump on device %s already saved\n",
2232946Swollman				    device);
22412873Sbde			goto closefd;
2252946Swollman		}
2262946Swollman	} else if (errno != ENOENT) {
2271541Srgrimes		warn("error while checking for pre-saved core file");
2281541Srgrimes		nerr++;
2291541Srgrimes		goto closefd;
2301541Srgrimes	}
2311541Srgrimes
2327090Sbde	if (!check_space(savedir, dumpsize)) {
2331541Srgrimes		nerr++;
2341541Srgrimes		goto closefd;
2351541Srgrimes	}
2361541Srgrimes	/*
2371541Srgrimes	 * Create or overwrite any existing files.
2381541Srgrimes	 */
2391541Srgrimes	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
2401541Srgrimes	if (fdinfo < 0) {
2412997Swollman		warn("%s", buf);
2422997Swollman		nerr++;
2432997Swollman		goto closefd;
2442997Swollman	}
2451541Srgrimes	sprintf(buf, "%s.core", md5);
2467461Sdg	fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
2471541Srgrimes	if (fdcore < 0) {
2481541Srgrimes		warn("%s", buf);
2491541Srgrimes		close(fdinfo);
25012873Sbde		nerr++;
25112873Sbde		goto closefd;
2527461Sdg	}
2537461Sdg	info = fdopen(fdinfo, "w");
25412873Sbde
2552997Swollman	if (verbose)
25612873Sbde		printheader(stdout, &kdhl, device, md5);
2572997Swollman
2581541Srgrimes	printf("Saving dump to file %s\n", buf);
2591541Srgrimes
2601541Srgrimes	printheader(info, &kdhl, device, md5);
2611541Srgrimes
2621541Srgrimes	while (dumpsize > 0) {
26313765Smpp		wl = sizeof(buf);
2641541Srgrimes		if (wl > dumpsize)
2651541Srgrimes			wl = dumpsize;
2661541Srgrimes		error = read(fd, buf, wl);
2671541Srgrimes		if (error != wl) {
2681541Srgrimes			warn("read error on %s", device);
2691541Srgrimes			nerr++;
2701541Srgrimes			goto closeall;
2711541Srgrimes		}
2721541Srgrimes		error = write(fdcore, buf, wl);
2731541Srgrimes		if (error != wl) {
2741541Srgrimes			warn("write error on %s.core file", md5);
2751541Srgrimes			nerr++;
2761541Srgrimes			goto closeall;
2771541Srgrimes		}
2781541Srgrimes		dumpsize -= wl;
2791541Srgrimes	}
2801541Srgrimes	nsaved++;
2811541Srgrimes	close(fdinfo);
2821541Srgrimes	close(fdcore);
2831541Srgrimes
2841541Srgrimes	if (verbose)
2851541Srgrimes		printf("Dump saved\n");
2861541Srgrimes
2871541Srgrimes nuke:
2881541Srgrimes	if (clear || !keep) {
2891541Srgrimes		if (verbose)
2901541Srgrimes			printf("Clearing dump header\n");
2911541Srgrimes		memset(&kdhl, 0, sizeof kdhl);
2921541Srgrimes		lseek(fd, lasthd, SEEK_SET);
2931541Srgrimes		error = write(fd, &kdhl, sizeof kdhl);
2941541Srgrimes		if (error != sizeof kdhl)
2951541Srgrimes			warn("error while clearing the dump header");
2961541Srgrimes	}
2971541Srgrimes	close(fd);
2981541Srgrimes	return;
2991541Srgrimes
3001541Srgrimes closeall:
3011541Srgrimes	close(fdinfo);
3021541Srgrimes	close(fdcore);
3031541Srgrimes
3041541Srgrimes closefd:
3051541Srgrimes	close(fd);
3061541Srgrimes}
3071541Srgrimes
3081541Srgrimesstatic void
3091541Srgrimesusage(void)
3101541Srgrimes{
3111541Srgrimes	fprintf(stderr, "usage: savecore [-cfkv] [directory [device...]]\n");
3121541Srgrimes	exit (1);
3131541Srgrimes}
3141541Srgrimes
3151541Srgrimesint
3161541Srgrimesmain(int argc, char **argv)
3171541Srgrimes{
3181541Srgrimes	int i, ch, error;
3191541Srgrimes	struct fstab *fsp;
3201541Srgrimes	char *savedir;
32113765Smpp
3221541Srgrimes	savedir = strdup(".");
32313765Smpp	if (savedir == NULL)
3241541Srgrimes		errx(1, "Cannot allocate memory");
3251541Srgrimes	while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1)
3261541Srgrimes		switch(ch) {
3271541Srgrimes		case 'c':
3281541Srgrimes			clear = 1;
3291541Srgrimes			break;
3301541Srgrimes		case 'k':
3311541Srgrimes			keep = 1;
3321541Srgrimes			break;
3331541Srgrimes		case 'v':
3341541Srgrimes			verbose = 1;
3351541Srgrimes			break;
3361541Srgrimes		case 'f':
3371541Srgrimes			force = 1;
33812158Sbde			break;
3391541Srgrimes		case 'd':	/* Obsolete */
3401541Srgrimes		case 'N':
3411541Srgrimes		case 'z':
34212158Sbde		case '?':
3431541Srgrimes		default:
3441541Srgrimes			usage();
3451541Srgrimes		}
3461541Srgrimes	argc -= optind;
3471541Srgrimes	argv += optind;
3481541Srgrimes	if (argc >= 1) {
3491541Srgrimes		error = chdir(argv[0]);
3501541Srgrimes		if (error)
3511541Srgrimes			err(1, "chdir(%s)", argv[0]);
3521541Srgrimes		savedir = argv[0];
3531541Srgrimes		argc--;
3541541Srgrimes		argv++;
3551541Srgrimes	}
3561541Srgrimes	if (argc == 0) {
3571541Srgrimes		for (;;) {
3581541Srgrimes			fsp = getfsent();
3591541Srgrimes			if (fsp == NULL)
3601541Srgrimes				break;
3611541Srgrimes			if (strcmp(fsp->fs_vfstype, "swap") &&
3621541Srgrimes			    strcmp(fsp->fs_vfstype, "dump"))
3631541Srgrimes				continue;
3641541Srgrimes			DoFile(savedir, fsp->fs_spec);
3651541Srgrimes		}
3661541Srgrimes	} else {
3671541Srgrimes		for (i = 0; i < argc; i++)
3681541Srgrimes			DoFile(savedir, argv[i]);
3691541Srgrimes	}
3701541Srgrimes
3711541Srgrimes	/* Emit minimal output. */
3721541Srgrimes	if (nfound == 0)
3731541Srgrimes		printf("No dumps found\n");
3741541Srgrimes	else if (nsaved == 0) {
3751541Srgrimes		if (nerr != 0)
3761541Srgrimes			printf("Unsaved dumps found but not saved\n");
3771541Srgrimes		else
3781541Srgrimes			printf("No unsaved dumps found\n");
3791541Srgrimes	}
3801541Srgrimes
3811541Srgrimes	return (0);
3822811Sbde}
3831541Srgrimes