savecore.c revision 264806
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.
341541Srgrimes *
351541Srgrimes * Copyright (c) 1986, 1992, 1993
361541Srgrimes *	The Regents of the University of California.  All rights reserved.
371541Srgrimes *
3850477Speter * Redistribution and use in source and binary forms, with or without
391541Srgrimes * modification, are permitted provided that the following conditions
401541Srgrimes * are met:
412531Swollman * 1. Redistributions of source code must retain the above copyright
422531Swollman *    notice, this list of conditions and the following disclaimer.
432531Swollman * 2. Redistributions in binary form must reproduce the above copyright
442531Swollman *    notice, this list of conditions and the following disclaimer in the
452531Swollman *    documentation and/or other materials provided with the distribution.
469209Swollman * 4. Neither the name of the University nor the names of its contributors
4714622Sfenner *    may be used to endorse or promote products derived from this software
482531Swollman *    without specific prior written permission.
4914622Sfenner *
502531Swollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
511541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52101091Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53101091Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
541541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
551549Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
56101091Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5729024Sbde * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
581541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
591541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
601541Srgrimes * SUCH DAMAGE.
6112296Sphk */
626472Swollman
631541Srgrimes#include <sys/cdefs.h>
641541Srgrimes__FBSDID("$FreeBSD: stable/10/sbin/savecore/savecore.c 264806 2014-04-23 07:33:51Z brueffer $");
651541Srgrimes
661541Srgrimes#include <sys/param.h>
671541Srgrimes#include <sys/disk.h>
681541Srgrimes#include <sys/kerneldump.h>
691541Srgrimes#include <sys/mount.h>
701541Srgrimes#include <sys/stat.h>
711541Srgrimes#include <errno.h>
721541Srgrimes#include <fcntl.h>
731541Srgrimes#include <fstab.h>
741541Srgrimes#include <paths.h>
7560105Sjlemon#include <signal.h>
7660105Sjlemon#include <stdarg.h>
7742776Sfenner#include <stdio.h>
7830309Sphk#include <stdlib.h>
79107113Sluigi#include <string.h>
80107113Sluigi#include <syslog.h>
8112579Sbde#include <time.h>
8212704Sphk#include <unistd.h>
832531Swollman
8478667Sru/* The size of the buffer used for I/O. */
8512296Sphk#define	BUFFERSIZE	(1024*1024)
8612296Sphk
879209Swollman#define	STATUS_BAD	0
881541Srgrimes#define	STATUS_GOOD	1
8914622Sfenner#define	STATUS_UNKNOWN	2
9014622Sfenner
91119180Srwatsonstatic int checkfor, compress, clear, force, keep, verbose;	/* flags */
921541Srgrimesstatic int nfound, nsaved, nerr;			/* statistics */
9392723Salfredstatic int maxdumps;
941541Srgrimes
95119180Srwatsonextern FILE *zopen(const char *, const char *);
96119180Srwatson
97119180Srwatsonstatic sig_atomic_t got_siginfo;
98119180Srwatsonstatic void infohandler(int);
99119180Srwatson
100119180Srwatsonstatic void
1011541Srgrimesprintheader(FILE *f, const struct kerneldumpheader *h, const char *device,
102107113Sluigi    int bounds, const int status)
1031541Srgrimes{
10414622Sfenner	uint64_t dumplen;
10514622Sfenner	time_t t;
1061541Srgrimes	const char *stat_str;
1071541Srgrimes
1081541Srgrimes	fprintf(f, "Dump header from device %s\n", device);
1091541Srgrimes	fprintf(f, "  Architecture: %s\n", h->architecture);
11014622Sfenner	fprintf(f, "  Architecture Version: %u\n",
1119209Swollman	    dtoh32(h->architectureversion));
1129209Swollman	dumplen = dtoh64(h->dumplength);
1139209Swollman	fprintf(f, "  Dump Length: %lldB (%lld MB)\n", (long long)dumplen,
11414622Sfenner	    (long long)(dumplen >> 20));
11514622Sfenner	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
11614622Sfenner	t = dtoh64(h->dumptime);
117111119Simp	fprintf(f, "  Dumptime: %s", ctime(&t));
11814622Sfenner	fprintf(f, "  Hostname: %s\n", h->hostname);
11914622Sfenner	fprintf(f, "  Magic: %s\n", h->magic);
12014622Sfenner	fprintf(f, "  Version String: %s", h->versionstring);
12114622Sfenner	fprintf(f, "  Panic String: %s\n", h->panicstring);
12214622Sfenner	fprintf(f, "  Dump Parity: %u\n", h->parity);
12314622Sfenner	fprintf(f, "  Bounds: %d\n", bounds);
12414622Sfenner
12514622Sfenner	switch(status) {
126119180Srwatson	case STATUS_BAD:
1271541Srgrimes		stat_str = "bad";
1281541Srgrimes		break;
12912704Sphk	case STATUS_GOOD:
130107113Sluigi		stat_str = "good";
131107113Sluigi		break;
1322531Swollman	default:
133119180Srwatson		stat_str = "unknown";
1342531Swollman	}
135119180Srwatson	fprintf(f, "  Dump Status: %s\n", stat_str);
136119180Srwatson	fflush(f);
137119180Srwatson}
138119180Srwatson
139119180Srwatsonstatic int
140119180Srwatsongetbounds(void) {
1412531Swollman	FILE *fp;
1422531Swollman	char buf[6];
1432531Swollman	int ret;
14442776Sfenner
14514622Sfenner	ret = 0;
14614622Sfenner
14714622Sfenner	if ((fp = fopen("bounds", "r")) == NULL) {
148119180Srwatson		if (verbose)
149119180Srwatson			printf("unable to open bounds file, using 0\n");
150119180Srwatson		return (ret);
1512531Swollman	}
1522531Swollman
1532531Swollman	if (fgets(buf, sizeof buf, fp) == NULL) {
1541541Srgrimes		syslog(LOG_WARNING, "unable to read from bounds, using 0");
155107113Sluigi		fclose(fp);
156107113Sluigi		return (ret);
157107113Sluigi	}
1581541Srgrimes
159107113Sluigi	errno = 0;
160107113Sluigi	ret = (int)strtol(buf, NULL, 10);
161107113Sluigi	if (ret == 0 && (errno == EINVAL || errno == ERANGE))
162107113Sluigi		syslog(LOG_WARNING, "invalid value found in bounds, using 0");
163107113Sluigi	return (ret);
164107113Sluigi}
165107113Sluigi
166107113Sluigistatic void
1671541Srgrimeswritebounds(int bounds) {
1682531Swollman	FILE *fp;
1699209Swollman
1708546Sdg	if ((fp = fopen("bounds", "w")) == NULL) {
1711541Srgrimes		syslog(LOG_WARNING, "unable to write to bounds file: %m");
1721541Srgrimes		return;
1731541Srgrimes	}
1741541Srgrimes
1751541Srgrimes	if (verbose)
1761541Srgrimes		printf("bounds number: %d\n", bounds);
1771541Srgrimes
1781541Srgrimes	fprintf(fp, "%d\n", bounds);
1791541Srgrimes	fclose(fp);
1801541Srgrimes}
1811541Srgrimes
1821541Srgrimesstatic off_t
1831541Srgrimesfile_size(const char *path)
1841541Srgrimes{
1851541Srgrimes	struct stat sb;
1861541Srgrimes
1871541Srgrimes	/* Ignore all errors, those file may not exists. */
1881541Srgrimes	if (stat(path, &sb) == -1)
1891541Srgrimes		return (0);
1901541Srgrimes	return (sb.st_size);
1911541Srgrimes}
1921541Srgrimes
1931541Srgrimesstatic off_t
1941541Srgrimessaved_dump_size(int bounds)
1951541Srgrimes{
1961541Srgrimes	static char path[PATH_MAX];
1971541Srgrimes	off_t dumpsize;
1981541Srgrimes
1991541Srgrimes	dumpsize = 0;
2001541Srgrimes
2011541Srgrimes	(void)snprintf(path, sizeof(path), "info.%d", bounds);
2021541Srgrimes	dumpsize += file_size(path);
2031541Srgrimes	(void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
2041541Srgrimes	dumpsize += file_size(path);
2052531Swollman	(void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
2061541Srgrimes	dumpsize += file_size(path);
2078546Sdg	(void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
20841702Sdillon	dumpsize += file_size(path);
20941702Sdillon	(void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
2102531Swollman	dumpsize += file_size(path);
2111541Srgrimes
21214622Sfenner	return (dumpsize);
21314622Sfenner}
21414622Sfenner
21514622Sfennerstatic void
21614622Sfennersaved_dump_remove(int bounds)
21714622Sfenner{
21814622Sfenner	static char path[PATH_MAX];
21914622Sfenner
22014622Sfenner	(void)snprintf(path, sizeof(path), "info.%d", bounds);
22114622Sfenner	(void)unlink(path);
22214622Sfenner	(void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
2231541Srgrimes	(void)unlink(path);
2241541Srgrimes	(void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
22514622Sfenner	(void)unlink(path);
2261541Srgrimes	(void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
2271541Srgrimes	(void)unlink(path);
2288090Spst	(void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
2291541Srgrimes	(void)unlink(path);
2301541Srgrimes}
2312531Swollman
23214622Sfennerstatic void
23314622Sfennersymlinks_remove(void)
23414622Sfenner{
23514622Sfenner
23614622Sfenner	(void)unlink("info.last");
2374028Spst	(void)unlink("vmcore.last");
23814622Sfenner	(void)unlink("vmcore.last.gz");
23914622Sfenner	(void)unlink("textdump.tar.last");
2404028Spst	(void)unlink("textdump.tar.last.gz");
24114622Sfenner}
2424028Spst
24314622Sfenner/*
24414622Sfenner * Check that sufficient space is available on the disk that holds the
2452531Swollman * save directory.
2462531Swollman */
2472531Swollmanstatic int
2482531Swollmancheck_space(const char *savedir, off_t dumpsize, int bounds)
24914622Sfenner{
2502531Swollman	FILE *fp;
25114622Sfenner	off_t minfree, spacefree, totfree, needed;
2522531Swollman	struct statfs fsbuf;
25314622Sfenner	char buf[100];
25414622Sfenner
25514622Sfenner	if (statfs(".", &fsbuf) < 0) {
25614622Sfenner		syslog(LOG_ERR, "%s: %m", savedir);
25714622Sfenner		exit(1);
25814622Sfenner	}
25914622Sfenner	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
26014622Sfenner	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
2612531Swollman
26214622Sfenner	if ((fp = fopen("minfree", "r")) == NULL)
26314622Sfenner		minfree = 0;
26414622Sfenner	else {
26514622Sfenner		if (fgets(buf, sizeof(buf), fp) == NULL)
26614622Sfenner			minfree = 0;
26714622Sfenner		else
26814622Sfenner			minfree = atoi(buf);
26914622Sfenner		(void)fclose(fp);
27014622Sfenner	}
27114622Sfenner
27214622Sfenner	needed = dumpsize / 1024 + 2;	/* 2 for info file */
27314622Sfenner	needed -= saved_dump_size(bounds);
27414622Sfenner	if ((minfree > 0 ? spacefree : totfree) - needed < minfree) {
27514622Sfenner		syslog(LOG_WARNING,
27614622Sfenner	"no dump, not enough free space on device (%lld available, need %lld)",
27714622Sfenner		    (long long)(minfree > 0 ? spacefree : totfree),
27814622Sfenner		    (long long)needed);
27914622Sfenner		return (0);
28014622Sfenner	}
28114622Sfenner	if (spacefree - needed < 0)
2822531Swollman		syslog(LOG_WARNING,
2832531Swollman		    "dump performed, but free space threshold crossed");
2841541Srgrimes	return (1);
2851541Srgrimes}
2861541Srgrimes
2879209Swollman#define BLOCKSIZE (1<<12)
2881541Srgrimes#define BLOCKMASK (~(BLOCKSIZE-1))
2891541Srgrimes
29014622Sfennerstatic int
29114622SfennerDoRegularFile(int fd, off_t dumpsize, char *buf, const char *device,
2929209Swollman    const char *filename, FILE *fp)
29314622Sfenner{
29414622Sfenner	int he, hs, nr, nw, wl;
29514622Sfenner	off_t dmpcnt, origsize;
29614622Sfenner
2979209Swollman	dmpcnt = 0;
29814622Sfenner	origsize = dumpsize;
29914622Sfenner	he = 0;
30014622Sfenner	while (dumpsize > 0) {
30114622Sfenner		wl = BUFFERSIZE;
3021541Srgrimes		if (wl > dumpsize)
3031541Srgrimes			wl = dumpsize;
3048090Spst		nr = read(fd, buf, wl);
3051541Srgrimes		if (nr != wl) {
3061541Srgrimes			if (nr == 0)
30714622Sfenner				syslog(LOG_WARNING,
3081541Srgrimes				    "WARNING: EOF on dump device");
3091541Srgrimes			else
3101541Srgrimes				syslog(LOG_ERR, "read error on %s: %m", device);
3111541Srgrimes			nerr++;
3121541Srgrimes			return (-1);
3131541Srgrimes		}
3141541Srgrimes		if (compress) {
3151541Srgrimes			nw = fwrite(buf, 1, wl, fp);
3161541Srgrimes		} else {
31796432Sdd			for (nw = 0; nw < nr; nw = he) {
3181541Srgrimes				/* find a contiguous block of zeroes */
3191541Srgrimes				for (hs = nw; hs < nr; hs += BLOCKSIZE) {
3201541Srgrimes					for (he = hs; he < nr && buf[he] == 0;
3211541Srgrimes					    ++he)
32214622Sfenner						/* nothing */ ;
3231541Srgrimes					/* is the hole long enough to matter? */
3241541Srgrimes					if (he >= hs + BLOCKSIZE)
3251541Srgrimes						break;
3261541Srgrimes				}
3271541Srgrimes
3281541Srgrimes				/* back down to a block boundary */
3291541Srgrimes				he &= BLOCKMASK;
3301541Srgrimes
3312531Swollman				/*
33214622Sfenner				 * 1) Don't go beyond the end of the buffer.
33314622Sfenner				 * 2) If the end of the buffer is less than
33414622Sfenner				 *    BLOCKSIZE bytes away, we're at the end
33514622Sfenner				 *    of the file, so just grab what's left.
3362531Swollman				 */
33714622Sfenner				if (hs + BLOCKSIZE > nr)
3381541Srgrimes					hs = he = nr;
3391541Srgrimes
3401541Srgrimes				/*
3411541Srgrimes				 * At this point, we have a partial ordering:
3421541Srgrimes				 *     nw <= hs <= he <= nr
3431541Srgrimes				 * If hs > nw, buf[nw..hs] contains non-zero data.
3441541Srgrimes				 * If he > hs, buf[hs..he] is all zeroes.
34582890Sjulian				 */
3461541Srgrimes				if (hs > nw)
3471541Srgrimes					if (fwrite(buf + nw, hs - nw, 1, fp)
3481541Srgrimes					    != 1)
349107113Sluigi					break;
350107113Sluigi				if (he > hs)
3511541Srgrimes					if (fseeko(fp, he - hs, SEEK_CUR) == -1)
3529209Swollman						break;
3531541Srgrimes			}
35414622Sfenner		}
35514622Sfenner		if (nw != wl) {
3561541Srgrimes			syslog(LOG_ERR,
35714622Sfenner			    "write error on %s file: %m", filename);
35814622Sfenner			syslog(LOG_WARNING,
35914622Sfenner			    "WARNING: vmcore may be incomplete");
36014622Sfenner			nerr++;
3612531Swollman			return (-1);
3622531Swollman		}
36314622Sfenner		if (verbose) {
3641541Srgrimes			dmpcnt += wl;
3651541Srgrimes			printf("%llu\r", (unsigned long long)dmpcnt);
3661541Srgrimes			fflush(stdout);
3671541Srgrimes		}
3681541Srgrimes		dumpsize -= wl;
3691541Srgrimes		if (got_siginfo) {
370107113Sluigi			printf("%s %.1lf%%\n", filename, (100.0 - (100.0 *
371107113Sluigi			    (double)dumpsize / (double)origsize)));
3721541Srgrimes			got_siginfo = 0;
37314622Sfenner		}
37414622Sfenner	}
37514622Sfenner	return (0);
37614622Sfenner}
37714622Sfenner
3781541Srgrimes/*
3791541Srgrimes * Specialized version of dump-reading logic for use with textdumps, which
3801541Srgrimes * are written backwards from the end of the partition, and must be reversed
381107113Sluigi * before being written to the file.  Textdumps are small, so do a bit less
3821541Srgrimes * work to optimize/sparsify.
383107113Sluigi */
3841541Srgrimesstatic int
3859209SwollmanDoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf,
3861541Srgrimes    const char *device, const char *filename, FILE *fp)
3871541Srgrimes{
3881541Srgrimes	int nr, nw, wl;
3891541Srgrimes	off_t dmpcnt, totsize;
3901541Srgrimes
3919209Swollman	totsize = dumpsize;
3921541Srgrimes	dmpcnt = 0;
3931541Srgrimes	wl = 512;
3941541Srgrimes	if ((dumpsize % wl) != 0) {
3951541Srgrimes		syslog(LOG_ERR, "textdump uneven multiple of 512 on %s",
3961541Srgrimes		    device);
3971541Srgrimes		nerr++;
3981541Srgrimes		return (-1);
3991541Srgrimes	}
4001541Srgrimes	while (dumpsize > 0) {
4011541Srgrimes		nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl);
40214622Sfenner		if (nr != wl) {
40314622Sfenner			if (nr == 0)
4041541Srgrimes				syslog(LOG_WARNING,
4051541Srgrimes				    "WARNING: EOF on dump device");
4061541Srgrimes			else
4071541Srgrimes				syslog(LOG_ERR, "read error on %s: %m", device);
4081541Srgrimes			nerr++;
4091541Srgrimes			return (-1);
4101541Srgrimes		}
4111541Srgrimes		nw = fwrite(buf, 1, wl, fp);
4122531Swollman		if (nw != wl) {
413107113Sluigi			syslog(LOG_ERR,
4142531Swollman			    "write error on %s file: %m", filename);
4152531Swollman			syslog(LOG_WARNING,
416119180Srwatson			    "WARNING: textdump may be incomplete");
4172531Swollman			nerr++;
418119180Srwatson			return (-1);
419119180Srwatson		}
42014622Sfenner		if (verbose) {
42114622Sfenner			dmpcnt += wl;
42214622Sfenner			printf("%llu\r", (unsigned long long)dmpcnt);
42314622Sfenner			fflush(stdout);
4242531Swollman		}
42514622Sfenner		dumpsize -= wl;
4262531Swollman	}
427119180Srwatson	return (0);
4282531Swollman}
4292531Swollman
4302531Swollmanstatic void
43115292SwollmanDoFile(const char *savedir, const char *device)
43215292Swollman{
4331541Srgrimes	static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX];
434107113Sluigi	static char *buf = NULL;
435107113Sluigi	struct kerneldumpheader kdhf, kdhl;
436107113Sluigi	off_t mediasize, dumpsize, firsthd, lasthd;
437107113Sluigi	FILE *info, *fp;
4381541Srgrimes	mode_t oumask;
4392531Swollman	int fd, fdinfo, error;
4402531Swollman	int bounds, status;
4412531Swollman	u_int sectorsize;
44215292Swollman	int istextdump;
4431541Srgrimes
444111119Simp	bounds = getbounds();
4452531Swollman	mediasize = 0;
4462531Swollman	status = STATUS_UNKNOWN;
4472531Swollman
4488090Spst	if (maxdumps > 0 && bounds == maxdumps)
449101091Srwatson		bounds = 0;
450101091Srwatson
451101091Srwatson	if (buf == NULL) {
4521541Srgrimes		buf = malloc(BUFFERSIZE);
4532531Swollman		if (buf == NULL) {
4542531Swollman			syslog(LOG_ERR, "%m");
4552531Swollman			return;
4562531Swollman		}
4572531Swollman	}
4582531Swollman
4592531Swollman	if (verbose)
4602531Swollman		printf("checking for kernel dump on device %s\n", device);
4612531Swollman
4621541Srgrimes	fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR);
4632531Swollman	if (fd < 0) {
4642531Swollman		syslog(LOG_ERR, "%s: %m", device);
4652531Swollman		return;
4662531Swollman	}
4672531Swollman
4682531Swollman	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
4692531Swollman	if (!error)
4702531Swollman		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
47114622Sfenner	if (error) {
4721541Srgrimes		syslog(LOG_ERR,
47315292Swollman		    "couldn't find media and/or sector size of %s: %m", device);
47415292Swollman		goto closefd;
47515292Swollman	}
4762531Swollman
4772531Swollman	if (verbose) {
47896432Sdd		printf("mediasize = %lld\n", (long long)mediasize);
4792531Swollman		printf("sectorsize = %u\n", sectorsize);
48015292Swollman	}
4811541Srgrimes
48215292Swollman	lasthd = mediasize - sectorsize;
48315292Swollman	lseek(fd, lasthd, SEEK_SET);
48415292Swollman	error = read(fd, &kdhl, sizeof kdhl);
48515292Swollman	if (error != sizeof kdhl) {
486105194Ssam		syslog(LOG_ERR,
4872531Swollman		    "error reading last dump header at offset %lld in %s: %m",
4882531Swollman		    (long long)lasthd, device);
4891541Srgrimes		goto closefd;
490	}
491	istextdump = 0;
492	if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) {
493		if (verbose)
494			printf("textdump magic on last dump header on %s\n",
495			    device);
496		istextdump = 1;
497		if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) {
498			syslog(LOG_ERR,
499			    "unknown version (%d) in last dump header on %s",
500			    dtoh32(kdhl.version), device);
501
502			status = STATUS_BAD;
503			if (force == 0)
504				goto closefd;
505		}
506	} else if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic) ==
507	    0) {
508		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
509			syslog(LOG_ERR,
510			    "unknown version (%d) in last dump header on %s",
511			    dtoh32(kdhl.version), device);
512
513			status = STATUS_BAD;
514			if (force == 0)
515				goto closefd;
516		}
517	} else {
518		if (verbose)
519			printf("magic mismatch on last dump header on %s\n",
520			    device);
521
522		status = STATUS_BAD;
523		if (force == 0)
524			goto closefd;
525
526		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
527			    sizeof kdhl.magic) == 0) {
528			if (verbose)
529				printf("forcing magic on %s\n", device);
530			memcpy(kdhl.magic, KERNELDUMPMAGIC,
531			    sizeof kdhl.magic);
532		} else {
533			syslog(LOG_ERR, "unable to force dump - bad magic");
534			goto closefd;
535		}
536		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
537			syslog(LOG_ERR,
538			    "unknown version (%d) in last dump header on %s",
539			    dtoh32(kdhl.version), device);
540
541			status = STATUS_BAD;
542			if (force == 0)
543				goto closefd;
544		}
545	}
546
547	nfound++;
548	if (clear)
549		goto nuke;
550
551	if (kerneldump_parity(&kdhl)) {
552		syslog(LOG_ERR,
553		    "parity error on last dump header on %s", device);
554		nerr++;
555		status = STATUS_BAD;
556		if (force == 0)
557			goto closefd;
558	}
559	dumpsize = dtoh64(kdhl.dumplength);
560	firsthd = lasthd - dumpsize - sizeof kdhf;
561	lseek(fd, firsthd, SEEK_SET);
562	error = read(fd, &kdhf, sizeof kdhf);
563	if (error != sizeof kdhf) {
564		syslog(LOG_ERR,
565		    "error reading first dump header at offset %lld in %s: %m",
566		    (long long)firsthd, device);
567		nerr++;
568		goto closefd;
569	}
570
571	if (verbose >= 2) {
572		printf("First dump headers:\n");
573		printheader(stdout, &kdhf, device, bounds, -1);
574
575		printf("\nLast dump headers:\n");
576		printheader(stdout, &kdhl, device, bounds, -1);
577		printf("\n");
578	}
579
580	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
581		syslog(LOG_ERR,
582		    "first and last dump headers disagree on %s", device);
583		nerr++;
584		status = STATUS_BAD;
585		if (force == 0)
586			goto closefd;
587	} else {
588		status = STATUS_GOOD;
589	}
590
591	if (checkfor) {
592		printf("A dump exists on %s\n", device);
593		close(fd);
594		exit(0);
595	}
596
597	if (kdhl.panicstring[0])
598		syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
599	else
600		syslog(LOG_ALERT, "reboot");
601
602	if (verbose)
603		printf("Checking for available free space\n");
604
605	if (!check_space(savedir, dumpsize, bounds)) {
606		nerr++;
607		goto closefd;
608	}
609
610	writebounds(bounds + 1);
611
612	saved_dump_remove(bounds);
613
614	snprintf(infoname, sizeof(infoname), "info.%d", bounds);
615
616	/*
617	 * Create or overwrite any existing dump header files.
618	 */
619	fdinfo = open(infoname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
620	if (fdinfo < 0) {
621		syslog(LOG_ERR, "%s: %m", buf);
622		nerr++;
623		goto closefd;
624	}
625	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
626	if (compress) {
627		snprintf(corename, sizeof(corename), "%s.%d.gz",
628		    istextdump ? "textdump.tar" : "vmcore", bounds);
629		fp = zopen(corename, "w");
630	} else {
631		snprintf(corename, sizeof(corename), "%s.%d",
632		    istextdump ? "textdump.tar" : "vmcore", bounds);
633		fp = fopen(corename, "w");
634	}
635	if (fp == NULL) {
636		syslog(LOG_ERR, "%s: %m", corename);
637		close(fdinfo);
638		nerr++;
639		goto closefd;
640	}
641	(void)umask(oumask);
642
643	info = fdopen(fdinfo, "w");
644
645	if (info == NULL) {
646		syslog(LOG_ERR, "fdopen failed: %m");
647		nerr++;
648		goto closefd;
649	}
650
651	if (verbose)
652		printheader(stdout, &kdhl, device, bounds, status);
653
654	printheader(info, &kdhl, device, bounds, status);
655	fclose(info);
656
657	syslog(LOG_NOTICE, "writing %score to %s/%s",
658	    compress ? "compressed " : "", savedir, corename);
659
660	if (istextdump) {
661		if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device,
662		    corename, fp) < 0)
663			goto closeall;
664	} else {
665		if (DoRegularFile(fd, dumpsize, buf, device, corename, fp)
666		    < 0)
667			goto closeall;
668	}
669	if (verbose)
670		printf("\n");
671
672	if (fclose(fp) < 0) {
673		syslog(LOG_ERR, "error on %s: %m", corename);
674		nerr++;
675		goto closefd;
676	}
677
678	symlinks_remove();
679	if (symlink(infoname, "info.last") == -1) {
680		syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
681		    savedir, "info.last");
682	}
683	if (compress) {
684		snprintf(linkname, sizeof(linkname), "%s.last.gz",
685		    istextdump ? "textdump.tar" : "vmcore");
686	} else {
687		snprintf(linkname, sizeof(linkname), "%s.last",
688		    istextdump ? "textdump.tar" : "vmcore");
689	}
690	if (symlink(corename, linkname) == -1) {
691		syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
692		    savedir, linkname);
693	}
694
695	nsaved++;
696
697	if (verbose)
698		printf("dump saved\n");
699
700nuke:
701	if (!keep) {
702		if (verbose)
703			printf("clearing dump header\n");
704		memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
705		lseek(fd, lasthd, SEEK_SET);
706		error = write(fd, &kdhl, sizeof kdhl);
707		if (error != sizeof kdhl)
708			syslog(LOG_ERR,
709			    "error while clearing the dump header: %m");
710	}
711	close(fd);
712	return;
713
714closeall:
715	fclose(fp);
716
717closefd:
718	close(fd);
719}
720
721static void
722usage(void)
723{
724	fprintf(stderr, "%s\n%s\n%s\n",
725	    "usage: savecore -c [-v] [device ...]",
726	    "       savecore -C [-v] [device ...]",
727	    "       savecore [-fkvz] [-m maxdumps] [directory [device ...]]");
728	exit(1);
729}
730
731int
732main(int argc, char **argv)
733{
734	const char *savedir = ".";
735	struct fstab *fsp;
736	int i, ch, error;
737
738	checkfor = compress = clear = force = keep = verbose = 0;
739	nfound = nsaved = nerr = 0;
740
741	openlog("savecore", LOG_PERROR, LOG_DAEMON);
742	signal(SIGINFO, infohandler);
743
744	while ((ch = getopt(argc, argv, "Ccfkm:vz")) != -1)
745		switch(ch) {
746		case 'C':
747			checkfor = 1;
748			break;
749		case 'c':
750			clear = 1;
751			break;
752		case 'f':
753			force = 1;
754			break;
755		case 'k':
756			keep = 1;
757			break;
758		case 'm':
759			maxdumps = atoi(optarg);
760			if (maxdumps <= 0) {
761				syslog(LOG_ERR, "Invalid maxdump value");
762				exit(1);
763			}
764			break;
765		case 'v':
766			verbose++;
767			break;
768		case 'z':
769			compress = 1;
770			break;
771		case '?':
772		default:
773			usage();
774		}
775	if (checkfor && (clear || force || keep))
776		usage();
777	if (clear && (compress || keep))
778		usage();
779	if (maxdumps > 0 && (checkfor || clear))
780		usage();
781	argc -= optind;
782	argv += optind;
783	if (argc >= 1 && !checkfor && !clear) {
784		error = chdir(argv[0]);
785		if (error) {
786			syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
787			exit(1);
788		}
789		savedir = argv[0];
790		argc--;
791		argv++;
792	}
793	if (argc == 0) {
794		for (;;) {
795			fsp = getfsent();
796			if (fsp == NULL)
797				break;
798			if (strcmp(fsp->fs_vfstype, "swap") &&
799			    strcmp(fsp->fs_vfstype, "dump"))
800				continue;
801			DoFile(savedir, fsp->fs_spec);
802		}
803	} else {
804		for (i = 0; i < argc; i++)
805			DoFile(savedir, argv[i]);
806	}
807
808	/* Emit minimal output. */
809	if (nfound == 0) {
810		if (checkfor) {
811			printf("No dump exists\n");
812			exit(1);
813		}
814		syslog(LOG_WARNING, "no dumps found");
815	}
816	else if (nsaved == 0) {
817		if (nerr != 0)
818			syslog(LOG_WARNING, "unsaved dumps found but not saved");
819		else
820			syslog(LOG_WARNING, "no unsaved dumps found");
821	}
822
823	return (0);
824}
825
826static void
827infohandler(int sig __unused)
828{
829	got_siginfo = 1;
830}
831