savecore.c revision 265075
112732Sfyuan/*-
212732Sfyuan * Copyright (c) 2002 Poul-Henning Kamp
312732Sfyuan * Copyright (c) 2002 Networks Associates Technology, Inc.
412732Sfyuan * All rights reserved.
512732Sfyuan *
612732Sfyuan * This software was developed for the FreeBSD Project by Poul-Henning Kamp
712732Sfyuan * and NAI Labs, the Security Research Division of Network Associates, Inc.
812732Sfyuan * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
912732Sfyuan * DARPA CHATS research program.
1012732Sfyuan *
1112732Sfyuan * Redistribution and use in source and binary forms, with or without
1212732Sfyuan * modification, are permitted provided that the following conditions
1312732Sfyuan * are met:
1412732Sfyuan * 1. Redistributions of source code must retain the above copyright
1512732Sfyuan *    notice, this list of conditions and the following disclaimer.
1612732Sfyuan * 2. Redistributions in binary form must reproduce the above copyright
1712732Sfyuan *    notice, this list of conditions and the following disclaimer in the
1812732Sfyuan *    documentation and/or other materials provided with the distribution.
1912732Sfyuan * 3. The names of the authors may not be used to endorse or promote
2012732Sfyuan *    products derived from this software without specific prior written
2112732Sfyuan *    permission.
2212732Sfyuan *
2312732Sfyuan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2412732Sfyuan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2512732Sfyuan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2612732Sfyuan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2712732Sfyuan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2812732Sfyuan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2912732Sfyuan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3012732Sfyuan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3112732Sfyuan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3212732Sfyuan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3312732Sfyuan * SUCH DAMAGE.
3412732Sfyuan *
3512732Sfyuan * Copyright (c) 1986, 1992, 1993
3612732Sfyuan *	The Regents of the University of California.  All rights reserved.
3712732Sfyuan *
3812732Sfyuan * Redistribution and use in source and binary forms, with or without
3912732Sfyuan * modification, are permitted provided that the following conditions
4012732Sfyuan * are met:
4112732Sfyuan * 1. Redistributions of source code must retain the above copyright
4212732Sfyuan *    notice, this list of conditions and the following disclaimer.
4312732Sfyuan * 2. Redistributions in binary form must reproduce the above copyright
4412732Sfyuan *    notice, this list of conditions and the following disclaimer in the
4512732Sfyuan *    documentation and/or other materials provided with the distribution.
4612732Sfyuan * 4. Neither the name of the University nor the names of its contributors
4712732Sfyuan *    may be used to endorse or promote products derived from this software
4812732Sfyuan *    without specific prior written permission.
4912732Sfyuan *
5012732Sfyuan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5112732Sfyuan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5212732Sfyuan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5312732Sfyuan * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5412732Sfyuan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5512732Sfyuan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5612732Sfyuan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5712732Sfyuan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5812732Sfyuan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5912732Sfyuan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6012732Sfyuan * SUCH DAMAGE.
6112732Sfyuan */
6212732Sfyuan
6312732Sfyuan#include <sys/cdefs.h>
6412732Sfyuan__FBSDID("$FreeBSD: stable/10/sbin/savecore/savecore.c 265075 2014-04-29 03:49:40Z markj $");
6512732Sfyuan
6612732Sfyuan#include <sys/param.h>
6712732Sfyuan#include <sys/disk.h>
6812732Sfyuan#include <sys/kerneldump.h>
6912732Sfyuan#include <sys/mount.h>
7012732Sfyuan#include <sys/stat.h>
7112732Sfyuan#include <errno.h>
7212732Sfyuan#include <fcntl.h>
7312732Sfyuan#include <fstab.h>
7412732Sfyuan#include <paths.h>
7512732Sfyuan#include <signal.h>
7612732Sfyuan#include <stdarg.h>
7712732Sfyuan#include <stdio.h>
7812732Sfyuan#include <stdlib.h>
7912732Sfyuan#include <string.h>
8012732Sfyuan#include <syslog.h>
8112732Sfyuan#include <time.h>
8212732Sfyuan#include <unistd.h>
8312732Sfyuan
8412732Sfyuan/* The size of the buffer used for I/O. */
8512732Sfyuan#define	BUFFERSIZE	(1024*1024)
8612732Sfyuan
8712732Sfyuan#define	STATUS_BAD	0
8812732Sfyuan#define	STATUS_GOOD	1
8912732Sfyuan#define	STATUS_UNKNOWN	2
9012732Sfyuan
9112732Sfyuanstatic int checkfor, compress, clear, force, keep, verbose;	/* flags */
9212732Sfyuanstatic int nfound, nsaved, nerr;			/* statistics */
9312732Sfyuanstatic int maxdumps;
9412732Sfyuan
9512732Sfyuanextern FILE *zopen(const char *, const char *);
9612732Sfyuan
9712732Sfyuanstatic sig_atomic_t got_siginfo;
9812732Sfyuanstatic void infohandler(int);
9912732Sfyuan
10012732Sfyuanstatic void
10112732Sfyuanprintheader(FILE *f, const struct kerneldumpheader *h, const char *device,
10212732Sfyuan    int bounds, const int status)
10312732Sfyuan{
10412732Sfyuan	uint64_t dumplen;
10512732Sfyuan	time_t t;
10612732Sfyuan	const char *stat_str;
10712732Sfyuan
10812732Sfyuan	fprintf(f, "Dump header from device %s\n", device);
10912732Sfyuan	fprintf(f, "  Architecture: %s\n", h->architecture);
11012732Sfyuan	fprintf(f, "  Architecture Version: %u\n",
11112732Sfyuan	    dtoh32(h->architectureversion));
11212732Sfyuan	dumplen = dtoh64(h->dumplength);
11312732Sfyuan	fprintf(f, "  Dump Length: %lldB (%lld MB)\n", (long long)dumplen,
11412732Sfyuan	    (long long)(dumplen >> 20));
11512732Sfyuan	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
11612732Sfyuan	t = dtoh64(h->dumptime);
11712732Sfyuan	fprintf(f, "  Dumptime: %s", ctime(&t));
11812732Sfyuan	fprintf(f, "  Hostname: %s\n", h->hostname);
11912732Sfyuan	fprintf(f, "  Magic: %s\n", h->magic);
12012732Sfyuan	fprintf(f, "  Version String: %s", h->versionstring);
12112732Sfyuan	fprintf(f, "  Panic String: %s\n", h->panicstring);
12212732Sfyuan	fprintf(f, "  Dump Parity: %u\n", h->parity);
12312732Sfyuan	fprintf(f, "  Bounds: %d\n", bounds);
12412732Sfyuan
12512732Sfyuan	switch(status) {
12612732Sfyuan	case STATUS_BAD:
12712732Sfyuan		stat_str = "bad";
12812732Sfyuan		break;
12912732Sfyuan	case STATUS_GOOD:
13012732Sfyuan		stat_str = "good";
13112732Sfyuan		break;
13212732Sfyuan	default:
13312732Sfyuan		stat_str = "unknown";
13412732Sfyuan	}
13512732Sfyuan	fprintf(f, "  Dump Status: %s\n", stat_str);
13612732Sfyuan	fflush(f);
13712732Sfyuan}
13812732Sfyuan
13912732Sfyuanstatic int
14012732Sfyuangetbounds(void) {
14112732Sfyuan	FILE *fp;
14212732Sfyuan	char buf[6];
14312732Sfyuan	int ret;
14412732Sfyuan
14512732Sfyuan	ret = 0;
14612732Sfyuan
14712732Sfyuan	if ((fp = fopen("bounds", "r")) == NULL) {
14812732Sfyuan		if (verbose)
14912732Sfyuan			printf("unable to open bounds file, using 0\n");
15012732Sfyuan		return (ret);
15112732Sfyuan	}
15212732Sfyuan
15312732Sfyuan	if (fgets(buf, sizeof buf, fp) == NULL) {
15412732Sfyuan		syslog(LOG_WARNING, "unable to read from bounds, using 0");
15512732Sfyuan		fclose(fp);
15612732Sfyuan		return (ret);
15712732Sfyuan	}
15812732Sfyuan
15912732Sfyuan	errno = 0;
16012732Sfyuan	ret = (int)strtol(buf, NULL, 10);
16112732Sfyuan	if (ret == 0 && (errno == EINVAL || errno == ERANGE))
16212732Sfyuan		syslog(LOG_WARNING, "invalid value found in bounds, using 0");
16312732Sfyuan	return (ret);
16412732Sfyuan}
16512732Sfyuan
16612732Sfyuanstatic void
16712732Sfyuanwritebounds(int bounds) {
16812732Sfyuan	FILE *fp;
16912732Sfyuan
17012732Sfyuan	if ((fp = fopen("bounds", "w")) == NULL) {
17112732Sfyuan		syslog(LOG_WARNING, "unable to write to bounds file: %m");
17212732Sfyuan		return;
17312732Sfyuan	}
17412732Sfyuan
17512732Sfyuan	if (verbose)
17612732Sfyuan		printf("bounds number: %d\n", bounds);
17712732Sfyuan
17812732Sfyuan	fprintf(fp, "%d\n", bounds);
17912732Sfyuan	fclose(fp);
18012732Sfyuan}
18112732Sfyuan
18212732Sfyuanstatic off_t
18312732Sfyuanfile_size(const char *path)
18412732Sfyuan{
18512732Sfyuan	struct stat sb;
18612732Sfyuan
18712732Sfyuan	/* Ignore all errors, those file may not exists. */
18812732Sfyuan	if (stat(path, &sb) == -1)
18912732Sfyuan		return (0);
19012732Sfyuan	return (sb.st_size);
19112732Sfyuan}
19212732Sfyuan
19312732Sfyuanstatic off_t
19412732Sfyuansaved_dump_size(int bounds)
19512732Sfyuan{
19612732Sfyuan	static char path[PATH_MAX];
19712732Sfyuan	off_t dumpsize;
19812732Sfyuan
19912732Sfyuan	dumpsize = 0;
20012732Sfyuan
20112732Sfyuan	(void)snprintf(path, sizeof(path), "info.%d", bounds);
20212732Sfyuan	dumpsize += file_size(path);
20312732Sfyuan	(void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
20412732Sfyuan	dumpsize += file_size(path);
20512732Sfyuan	(void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
20612732Sfyuan	dumpsize += file_size(path);
20712732Sfyuan	(void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
20812732Sfyuan	dumpsize += file_size(path);
20912732Sfyuan	(void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
21012732Sfyuan	dumpsize += file_size(path);
21112732Sfyuan
21212732Sfyuan	return (dumpsize);
21312732Sfyuan}
21412732Sfyuan
21512732Sfyuanstatic void
21612732Sfyuansaved_dump_remove(int bounds)
21712732Sfyuan{
21812732Sfyuan	static char path[PATH_MAX];
21912732Sfyuan
22012732Sfyuan	(void)snprintf(path, sizeof(path), "info.%d", bounds);
22112732Sfyuan	(void)unlink(path);
22212732Sfyuan	(void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
22312732Sfyuan	(void)unlink(path);
22412732Sfyuan	(void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
22512732Sfyuan	(void)unlink(path);
22612732Sfyuan	(void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
22712732Sfyuan	(void)unlink(path);
22812732Sfyuan	(void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
22912732Sfyuan	(void)unlink(path);
23012732Sfyuan}
23112732Sfyuan
23212732Sfyuanstatic void
23312732Sfyuansymlinks_remove(void)
23412732Sfyuan{
23512732Sfyuan
23612732Sfyuan	(void)unlink("info.last");
23712732Sfyuan	(void)unlink("vmcore.last");
23812732Sfyuan	(void)unlink("vmcore.last.gz");
23912732Sfyuan	(void)unlink("textdump.tar.last");
24012732Sfyuan	(void)unlink("textdump.tar.last.gz");
24112732Sfyuan}
24212732Sfyuan
24312732Sfyuan/*
24412732Sfyuan * Check that sufficient space is available on the disk that holds the
24512732Sfyuan * save directory.
24612732Sfyuan */
24712732Sfyuanstatic int
24812732Sfyuancheck_space(const char *savedir, off_t dumpsize, int bounds)
24912732Sfyuan{
25012732Sfyuan	FILE *fp;
25112732Sfyuan	off_t minfree, spacefree, totfree, needed;
25212732Sfyuan	struct statfs fsbuf;
25312732Sfyuan	char buf[100];
25412732Sfyuan
25512732Sfyuan	if (statfs(".", &fsbuf) < 0) {
25612732Sfyuan		syslog(LOG_ERR, "%s: %m", savedir);
25712732Sfyuan		exit(1);
25812732Sfyuan	}
25912732Sfyuan	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
26012732Sfyuan	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
26112732Sfyuan
26212732Sfyuan	if ((fp = fopen("minfree", "r")) == NULL)
26312732Sfyuan		minfree = 0;
26412732Sfyuan	else {
26512732Sfyuan		if (fgets(buf, sizeof(buf), fp) == NULL)
26612732Sfyuan			minfree = 0;
26712732Sfyuan		else
26812732Sfyuan			minfree = atoi(buf);
26912732Sfyuan		(void)fclose(fp);
27012732Sfyuan	}
27112732Sfyuan
27212732Sfyuan	needed = dumpsize / 1024 + 2;	/* 2 for info file */
27312732Sfyuan	needed -= saved_dump_size(bounds);
27412732Sfyuan	if ((minfree > 0 ? spacefree : totfree) - needed < minfree) {
27512732Sfyuan		syslog(LOG_WARNING,
27612732Sfyuan	"no dump, not enough free space on device (%lld available, need %lld)",
27712732Sfyuan		    (long long)(minfree > 0 ? spacefree : totfree),
27812732Sfyuan		    (long long)needed);
27912732Sfyuan		return (0);
28012732Sfyuan	}
28112732Sfyuan	if (spacefree - needed < 0)
28212732Sfyuan		syslog(LOG_WARNING,
28312732Sfyuan		    "dump performed, but free space threshold crossed");
28412732Sfyuan	return (1);
28512732Sfyuan}
28612732Sfyuan
28712732Sfyuan#define BLOCKSIZE (1<<12)
28812732Sfyuan#define BLOCKMASK (~(BLOCKSIZE-1))
28912732Sfyuan
29012732Sfyuanstatic int
29112732SfyuanDoRegularFile(int fd, off_t dumpsize, char *buf, const char *device,
29212732Sfyuan    const char *filename, FILE *fp)
29312732Sfyuan{
29412732Sfyuan	int he, hs, nr, nw, wl;
29512732Sfyuan	off_t dmpcnt, origsize;
29612732Sfyuan
29712732Sfyuan	dmpcnt = 0;
29812732Sfyuan	origsize = dumpsize;
29912732Sfyuan	he = 0;
30012732Sfyuan	while (dumpsize > 0) {
30112732Sfyuan		wl = BUFFERSIZE;
30212732Sfyuan		if (wl > dumpsize)
30312732Sfyuan			wl = dumpsize;
30412732Sfyuan		nr = read(fd, buf, wl);
30512732Sfyuan		if (nr != wl) {
30612732Sfyuan			if (nr == 0)
30712732Sfyuan				syslog(LOG_WARNING,
30812732Sfyuan				    "WARNING: EOF on dump device");
30912732Sfyuan			else
31012732Sfyuan				syslog(LOG_ERR, "read error on %s: %m", device);
31112732Sfyuan			nerr++;
31212732Sfyuan			return (-1);
31312732Sfyuan		}
31412732Sfyuan		if (compress) {
31512732Sfyuan			nw = fwrite(buf, 1, wl, fp);
31612732Sfyuan		} else {
31712732Sfyuan			for (nw = 0; nw < nr; nw = he) {
31812732Sfyuan				/* find a contiguous block of zeroes */
31912732Sfyuan				for (hs = nw; hs < nr; hs += BLOCKSIZE) {
32012732Sfyuan					for (he = hs; he < nr && buf[he] == 0;
32112732Sfyuan					    ++he)
32212732Sfyuan						/* nothing */ ;
32312732Sfyuan					/* is the hole long enough to matter? */
32412732Sfyuan					if (he >= hs + BLOCKSIZE)
32512732Sfyuan						break;
32612732Sfyuan				}
32712732Sfyuan
32812732Sfyuan				/* back down to a block boundary */
32912732Sfyuan				he &= BLOCKMASK;
33012732Sfyuan
33112732Sfyuan				/*
33212732Sfyuan				 * 1) Don't go beyond the end of the buffer.
33312732Sfyuan				 * 2) If the end of the buffer is less than
33412732Sfyuan				 *    BLOCKSIZE bytes away, we're at the end
33512732Sfyuan				 *    of the file, so just grab what's left.
33612732Sfyuan				 */
33712732Sfyuan				if (hs + BLOCKSIZE > nr)
33812732Sfyuan					hs = he = nr;
33912732Sfyuan
34012732Sfyuan				/*
34112732Sfyuan				 * At this point, we have a partial ordering:
34212732Sfyuan				 *     nw <= hs <= he <= nr
34312732Sfyuan				 * If hs > nw, buf[nw..hs] contains non-zero data.
34412732Sfyuan				 * If he > hs, buf[hs..he] is all zeroes.
34512732Sfyuan				 */
34612732Sfyuan				if (hs > nw)
34712732Sfyuan					if (fwrite(buf + nw, hs - nw, 1, fp)
34812732Sfyuan					    != 1)
34912732Sfyuan					break;
35012732Sfyuan				if (he > hs)
35112732Sfyuan					if (fseeko(fp, he - hs, SEEK_CUR) == -1)
35212732Sfyuan						break;
35312732Sfyuan			}
35412732Sfyuan		}
35512732Sfyuan		if (nw != wl) {
35612732Sfyuan			syslog(LOG_ERR,
35712732Sfyuan			    "write error on %s file: %m", filename);
35812732Sfyuan			syslog(LOG_WARNING,
35912732Sfyuan			    "WARNING: vmcore may be incomplete");
36012732Sfyuan			nerr++;
36112732Sfyuan			return (-1);
36212732Sfyuan		}
36312732Sfyuan		if (verbose) {
36412732Sfyuan			dmpcnt += wl;
36512732Sfyuan			printf("%llu\r", (unsigned long long)dmpcnt);
36612732Sfyuan			fflush(stdout);
36712732Sfyuan		}
36812732Sfyuan		dumpsize -= wl;
36912732Sfyuan		if (got_siginfo) {
37012732Sfyuan			printf("%s %.1lf%%\n", filename, (100.0 - (100.0 *
37112732Sfyuan			    (double)dumpsize / (double)origsize)));
37212732Sfyuan			got_siginfo = 0;
37312732Sfyuan		}
37412732Sfyuan	}
37512732Sfyuan	return (0);
37612732Sfyuan}
37712732Sfyuan
37812732Sfyuan/*
37912732Sfyuan * Specialized version of dump-reading logic for use with textdumps, which
38012732Sfyuan * are written backwards from the end of the partition, and must be reversed
38112732Sfyuan * before being written to the file.  Textdumps are small, so do a bit less
38212732Sfyuan * work to optimize/sparsify.
38312732Sfyuan */
38412732Sfyuanstatic int
38512732SfyuanDoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf,
38612732Sfyuan    const char *device, const char *filename, FILE *fp)
38712732Sfyuan{
38812732Sfyuan	int nr, nw, wl;
38912732Sfyuan	off_t dmpcnt, totsize;
39012732Sfyuan
39112732Sfyuan	totsize = dumpsize;
39212732Sfyuan	dmpcnt = 0;
39312732Sfyuan	wl = 512;
39412732Sfyuan	if ((dumpsize % wl) != 0) {
39512732Sfyuan		syslog(LOG_ERR, "textdump uneven multiple of 512 on %s",
39612732Sfyuan		    device);
39712732Sfyuan		nerr++;
39812732Sfyuan		return (-1);
39912732Sfyuan	}
40012732Sfyuan	while (dumpsize > 0) {
40112732Sfyuan		nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl);
40212732Sfyuan		if (nr != wl) {
40312732Sfyuan			if (nr == 0)
40412732Sfyuan				syslog(LOG_WARNING,
40512732Sfyuan				    "WARNING: EOF on dump device");
40612732Sfyuan			else
40712732Sfyuan				syslog(LOG_ERR, "read error on %s: %m", device);
40812732Sfyuan			nerr++;
40912732Sfyuan			return (-1);
41012732Sfyuan		}
41112732Sfyuan		nw = fwrite(buf, 1, wl, fp);
41212732Sfyuan		if (nw != wl) {
41312732Sfyuan			syslog(LOG_ERR,
41412732Sfyuan			    "write error on %s file: %m", filename);
41512732Sfyuan			syslog(LOG_WARNING,
41612732Sfyuan			    "WARNING: textdump may be incomplete");
41712732Sfyuan			nerr++;
41812732Sfyuan			return (-1);
41912732Sfyuan		}
42012732Sfyuan		if (verbose) {
42112732Sfyuan			dmpcnt += wl;
42212732Sfyuan			printf("%llu\r", (unsigned long long)dmpcnt);
42312732Sfyuan			fflush(stdout);
42412732Sfyuan		}
42512732Sfyuan		dumpsize -= wl;
42612732Sfyuan	}
42712732Sfyuan	return (0);
42812732Sfyuan}
42912732Sfyuan
43012732Sfyuanstatic void
43112732SfyuanDoFile(const char *savedir, const char *device)
43212732Sfyuan{
43312732Sfyuan	static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX];
43412732Sfyuan	static char *buf = NULL;
43512732Sfyuan	struct kerneldumpheader kdhf, kdhl;
43612732Sfyuan	off_t mediasize, dumpsize, firsthd, lasthd;
43712732Sfyuan	FILE *info, *fp;
43812732Sfyuan	mode_t oumask;
43912732Sfyuan	int fd, fdinfo, error;
44012732Sfyuan	int bounds, status;
44112732Sfyuan	u_int sectorsize;
44212732Sfyuan	int istextdump;
44312732Sfyuan
44412732Sfyuan	bounds = getbounds();
44512732Sfyuan	mediasize = 0;
44612732Sfyuan	status = STATUS_UNKNOWN;
44712732Sfyuan
44812732Sfyuan	if (maxdumps > 0 && bounds == maxdumps)
44912732Sfyuan		bounds = 0;
45012732Sfyuan
45112732Sfyuan	if (buf == NULL) {
45212732Sfyuan		buf = malloc(BUFFERSIZE);
45312732Sfyuan		if (buf == NULL) {
45412732Sfyuan			syslog(LOG_ERR, "%m");
45512732Sfyuan			return;
45612732Sfyuan		}
45712732Sfyuan	}
45812732Sfyuan
45912732Sfyuan	if (verbose)
46012732Sfyuan		printf("checking for kernel dump on device %s\n", device);
46112732Sfyuan
46212732Sfyuan	fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR);
46312732Sfyuan	if (fd < 0) {
46412732Sfyuan		syslog(LOG_ERR, "%s: %m", device);
46512732Sfyuan		return;
46612732Sfyuan	}
46712732Sfyuan
46812732Sfyuan	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
46912732Sfyuan	if (!error)
47012732Sfyuan		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
47112732Sfyuan	if (error) {
47212732Sfyuan		syslog(LOG_ERR,
47312732Sfyuan		    "couldn't find media and/or sector size of %s: %m", device);
47412732Sfyuan		goto closefd;
47512732Sfyuan	}
47612732Sfyuan
47712732Sfyuan	if (verbose) {
47812732Sfyuan		printf("mediasize = %lld\n", (long long)mediasize);
47912732Sfyuan		printf("sectorsize = %u\n", sectorsize);
48012732Sfyuan	}
48112732Sfyuan
48212732Sfyuan	lasthd = mediasize - sectorsize;
48312732Sfyuan	lseek(fd, lasthd, SEEK_SET);
48412732Sfyuan	error = read(fd, &kdhl, sizeof kdhl);
48512732Sfyuan	if (error != sizeof kdhl) {
48612732Sfyuan		syslog(LOG_ERR,
48712732Sfyuan		    "error reading last dump header at offset %lld in %s: %m",
48812732Sfyuan		    (long long)lasthd, device);
48912732Sfyuan		goto closefd;
49012732Sfyuan	}
49112732Sfyuan	istextdump = 0;
49212732Sfyuan	if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) {
49312732Sfyuan		if (verbose)
49412732Sfyuan			printf("textdump magic on last dump header on %s\n",
49512732Sfyuan			    device);
49612732Sfyuan		istextdump = 1;
49712732Sfyuan		if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) {
49812732Sfyuan			syslog(LOG_ERR,
49912732Sfyuan			    "unknown version (%d) in last dump header on %s",
50012732Sfyuan			    dtoh32(kdhl.version), device);
50112732Sfyuan
50212732Sfyuan			status = STATUS_BAD;
50312732Sfyuan			if (force == 0)
50412732Sfyuan				goto closefd;
50512732Sfyuan		}
50612732Sfyuan	} else if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic) ==
50712732Sfyuan	    0) {
50812732Sfyuan		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
50912732Sfyuan			syslog(LOG_ERR,
51012732Sfyuan			    "unknown version (%d) in last dump header on %s",
51112732Sfyuan			    dtoh32(kdhl.version), device);
51212732Sfyuan
51312732Sfyuan			status = STATUS_BAD;
51412732Sfyuan			if (force == 0)
51512732Sfyuan				goto closefd;
51612732Sfyuan		}
51712732Sfyuan	} else {
51812732Sfyuan		if (verbose)
51912732Sfyuan			printf("magic mismatch on last dump header on %s\n",
52012732Sfyuan			    device);
52112732Sfyuan
52212732Sfyuan		status = STATUS_BAD;
52312732Sfyuan		if (force == 0)
52412732Sfyuan			goto closefd;
52512732Sfyuan
52612732Sfyuan		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
52712732Sfyuan			    sizeof kdhl.magic) == 0) {
52812732Sfyuan			if (verbose)
52912732Sfyuan				printf("forcing magic on %s\n", device);
53012732Sfyuan			memcpy(kdhl.magic, KERNELDUMPMAGIC,
53112732Sfyuan			    sizeof kdhl.magic);
53212732Sfyuan		} else {
53312732Sfyuan			syslog(LOG_ERR, "unable to force dump - bad magic");
53412732Sfyuan			goto closefd;
53512732Sfyuan		}
53612732Sfyuan		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
53712732Sfyuan			syslog(LOG_ERR,
53812732Sfyuan			    "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", infoname);
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