savecore.c revision 244217
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.
3496049Sfenner *
3596049Sfenner * Copyright (c) 1986, 1992, 1993
3696049Sfenner *	The Regents of the University of California.  All rights reserved.
3796049Sfenner *
3896049Sfenner * Redistribution and use in source and binary forms, with or without
3996049Sfenner * modification, are permitted provided that the following conditions
4096049Sfenner * are met:
4196049Sfenner * 1. Redistributions of source code must retain the above copyright
4296049Sfenner *    notice, this list of conditions and the following disclaimer.
4396049Sfenner * 2. Redistributions in binary form must reproduce the above copyright
4496049Sfenner *    notice, this list of conditions and the following disclaimer in the
4596049Sfenner *    documentation and/or other materials provided with the distribution.
4696049Sfenner * 4. Neither the name of the University nor the names of its contributors
4796049Sfenner *    may be used to endorse or promote products derived from this software
4896049Sfenner *    without specific prior written permission.
4996049Sfenner *
5096049Sfenner * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5196049Sfenner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5296049Sfenner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5396049Sfenner * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5496049Sfenner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5596049Sfenner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5696049Sfenner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
5796049Sfenner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
5896049Sfenner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
5996049Sfenner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6096049Sfenner * SUCH DAMAGE.
611558Srgrimes */
621558Srgrimes
6395183Scharnier#include <sys/cdefs.h>
6495183Scharnier__FBSDID("$FreeBSD: head/sbin/savecore/savecore.c 244217 2012-12-14 15:04:39Z pjd $");
6595183Scharnier
6696049Sfenner#include <sys/param.h>
6794580Smarcel#include <sys/disk.h>
6894580Smarcel#include <sys/kerneldump.h>
6996025Smux#include <sys/mount.h>
7094580Smarcel#include <sys/stat.h>
7194580Smarcel#include <errno.h>
7293492Sphk#include <fcntl.h>
7393492Sphk#include <fstab.h>
7496025Smux#include <paths.h>
75234069Srmh#include <signal.h>
7696049Sfenner#include <stdarg.h>
7794580Smarcel#include <stdio.h>
7894580Smarcel#include <stdlib.h>
7994580Smarcel#include <string.h>
8096049Sfenner#include <syslog.h>
8193492Sphk#include <time.h>
8293492Sphk#include <unistd.h>
831558Srgrimes
8497746Smarcel/* The size of the buffer used for I/O. */
8597746Smarcel#define	BUFFERSIZE	(1024*1024)
8697746Smarcel
87142533Sobrien#define	STATUS_BAD	0
88142533Sobrien#define	STATUS_GOOD	1
89142533Sobrien#define	STATUS_UNKNOWN	2
90142359Sobrien
91133814Srustatic int checkfor, compress, clear, force, keep, verbose;	/* flags */
92133814Srustatic int nfound, nsaved, nerr;			/* statistics */
9394580Smarcel
9496049Sfennerextern FILE *zopen(const char *, const char *);
9596049Sfenner
96196528Slulfstatic sig_atomic_t got_siginfo;
97196528Slulfstatic void infohandler(int);
98196528Slulf
9993492Sphkstatic void
10094580Smarcelprintheader(FILE *f, const struct kerneldumpheader *h, const char *device,
101142359Sobrien    int bounds, const int status)
1021558Srgrimes{
10393717Smarcel	uint64_t dumplen;
10493492Sphk	time_t t;
105142359Sobrien	const char *stat_str;
1061558Srgrimes
107142359Sobrien	fprintf(f, "Dump header from device %s\n", device);
10893492Sphk	fprintf(f, "  Architecture: %s\n", h->architecture);
109150818Smaxim	fprintf(f, "  Architecture Version: %u\n",
110150818Smaxim	    dtoh32(h->architectureversion));
11193717Smarcel	dumplen = dtoh64(h->dumplength);
112142359Sobrien	fprintf(f, "  Dump Length: %lldB (%lld MB)\n", (long long)dumplen,
11393717Smarcel	    (long long)(dumplen >> 20));
11493717Smarcel	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
11593717Smarcel	t = dtoh64(h->dumptime);
11693492Sphk	fprintf(f, "  Dumptime: %s", ctime(&t));
11793492Sphk	fprintf(f, "  Hostname: %s\n", h->hostname);
118142359Sobrien	fprintf(f, "  Magic: %s\n", h->magic);
119142359Sobrien	fprintf(f, "  Version String: %s", h->versionstring);
120142359Sobrien	fprintf(f, "  Panic String: %s\n", h->panicstring);
121142359Sobrien	fprintf(f, "  Dump Parity: %u\n", h->parity);
12296049Sfenner	fprintf(f, "  Bounds: %d\n", bounds);
123142359Sobrien
124142359Sobrien	switch(status) {
125142359Sobrien	case STATUS_BAD:
126142533Sobrien		stat_str = "bad";
127142533Sobrien		break;
128142359Sobrien	case STATUS_GOOD:
129142533Sobrien		stat_str = "good";
130142533Sobrien		break;
131142359Sobrien	default:
132142533Sobrien		stat_str = "unknown";
133142359Sobrien	}
134142359Sobrien	fprintf(f, "  Dump Status: %s\n", stat_str);
13595039Sphk	fflush(f);
1361558Srgrimes}
1371558Srgrimes
13896049Sfennerstatic int
13996049Sfennergetbounds(void) {
14096049Sfenner	FILE *fp;
14196049Sfenner	char buf[6];
14296049Sfenner	int ret;
14396049Sfenner
14496049Sfenner	ret = 0;
14596049Sfenner
14696049Sfenner	if ((fp = fopen("bounds", "r")) == NULL) {
147150105Srwatson		if (verbose)
148150105Srwatson			printf("unable to open bounds file, using 0\n");
149147506Sdwhite		return (ret);
15096049Sfenner	}
15196049Sfenner
15296049Sfenner	if (fgets(buf, sizeof buf, fp) == NULL) {
15396049Sfenner		syslog(LOG_WARNING, "unable to read from bounds, using 0");
15496049Sfenner		fclose(fp);
155147506Sdwhite		return (ret);
15696049Sfenner	}
15796049Sfenner
15896049Sfenner	errno = 0;
15996049Sfenner	ret = (int)strtol(buf, NULL, 10);
16096049Sfenner	if (ret == 0 && (errno == EINVAL || errno == ERANGE))
16196049Sfenner		syslog(LOG_WARNING, "invalid value found in bounds, using 0");
162147506Sdwhite	return (ret);
163147506Sdwhite}
16496049Sfenner
165147506Sdwhitestatic void
166147506Sdwhitewritebounds(int bounds) {
167147506Sdwhite	FILE *fp;
16896049Sfenner
16996049Sfenner	if ((fp = fopen("bounds", "w")) == NULL) {
17096049Sfenner		syslog(LOG_WARNING, "unable to write to bounds file: %m");
171147506Sdwhite		return;
17296049Sfenner	}
17396049Sfenner
17496049Sfenner	if (verbose)
175147506Sdwhite		printf("bounds number: %d\n", bounds);
17696049Sfenner
177147506Sdwhite	fprintf(fp, "%d\n", bounds);
17896049Sfenner	fclose(fp);
17996049Sfenner}
18096049Sfenner
18196025Smux/*
18296025Smux * Check that sufficient space is available on the disk that holds the
18396025Smux * save directory.
18496025Smux */
18596025Smuxstatic int
186146763Sdelphijcheck_space(const char *savedir, off_t dumpsize)
18796025Smux{
18896025Smux	FILE *fp;
18996049Sfenner	off_t minfree, spacefree, totfree, needed;
19096025Smux	struct statfs fsbuf;
19196025Smux	char buf[100], path[MAXPATHLEN];
19293717Smarcel
19396049Sfenner	if (statfs(savedir, &fsbuf) < 0) {
19496049Sfenner		syslog(LOG_ERR, "%s: %m", savedir);
19596049Sfenner		exit(1);
19696049Sfenner	}
197244215Spjd	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
19896025Smux	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
19996025Smux
20096025Smux	(void)snprintf(path, sizeof(path), "%s/minfree", savedir);
20196025Smux	if ((fp = fopen(path, "r")) == NULL)
20296025Smux		minfree = 0;
20396025Smux	else {
20496025Smux		if (fgets(buf, sizeof(buf), fp) == NULL)
20596025Smux			minfree = 0;
20696025Smux		else
20796025Smux			minfree = atoi(buf);
20896025Smux		(void)fclose(fp);
20996025Smux	}
21096025Smux
21196049Sfenner	needed = dumpsize / 1024 + 2;	/* 2 for info file */
212244215Spjd	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
21396049Sfenner		syslog(LOG_WARNING,
21496049Sfenner	"no dump, not enough free space on device (%lld available, need %lld)",
21596025Smux		    (long long)(minfree > 0 ? spacefree : totfree),
21696025Smux		    (long long)needed);
21796025Smux		return (0);
21896025Smux	}
21996025Smux	if (spacefree - needed < 0)
22096049Sfenner		syslog(LOG_WARNING,
22196049Sfenner		    "dump performed, but free space threshold crossed");
22296025Smux	return (1);
22396025Smux}
22496025Smux
22596049Sfenner#define BLOCKSIZE (1<<12)
22696049Sfenner#define BLOCKMASK (~(BLOCKSIZE-1))
22796025Smux
228174923Srwatsonstatic int
229174923SrwatsonDoRegularFile(int fd, off_t dumpsize, char *buf, const char *device,
230174923Srwatson    const char *filename, FILE *fp)
231174923Srwatson{
232174923Srwatson	int he, hs, nr, nw, wl;
233196528Slulf	off_t dmpcnt, origsize;
234174923Srwatson
235174923Srwatson	dmpcnt = 0;
236196528Slulf	origsize = dumpsize;
237174923Srwatson	he = 0;
238174923Srwatson	while (dumpsize > 0) {
239174923Srwatson		wl = BUFFERSIZE;
240174923Srwatson		if (wl > dumpsize)
241174923Srwatson			wl = dumpsize;
242174923Srwatson		nr = read(fd, buf, wl);
243174923Srwatson		if (nr != wl) {
244174923Srwatson			if (nr == 0)
245174923Srwatson				syslog(LOG_WARNING,
246174923Srwatson				    "WARNING: EOF on dump device");
247174923Srwatson			else
248174923Srwatson				syslog(LOG_ERR, "read error on %s: %m", device);
249174923Srwatson			nerr++;
250174923Srwatson			return (-1);
251174923Srwatson		}
252174923Srwatson		if (compress) {
253174923Srwatson			nw = fwrite(buf, 1, wl, fp);
254174923Srwatson		} else {
255174923Srwatson			for (nw = 0; nw < nr; nw = he) {
256174923Srwatson				/* find a contiguous block of zeroes */
257174923Srwatson				for (hs = nw; hs < nr; hs += BLOCKSIZE) {
258174923Srwatson					for (he = hs; he < nr && buf[he] == 0;
259174923Srwatson					    ++he)
260174923Srwatson						/* nothing */ ;
261174923Srwatson					/* is the hole long enough to matter? */
262174923Srwatson					if (he >= hs + BLOCKSIZE)
263174923Srwatson						break;
264174923Srwatson				}
265244215Spjd
266174923Srwatson				/* back down to a block boundary */
267174923Srwatson				he &= BLOCKMASK;
268174923Srwatson
269174923Srwatson				/*
270174923Srwatson				 * 1) Don't go beyond the end of the buffer.
271174923Srwatson				 * 2) If the end of the buffer is less than
272174923Srwatson				 *    BLOCKSIZE bytes away, we're at the end
273174923Srwatson				 *    of the file, so just grab what's left.
274174923Srwatson				 */
275174923Srwatson				if (hs + BLOCKSIZE > nr)
276174923Srwatson					hs = he = nr;
277174923Srwatson
278174923Srwatson				/*
279174923Srwatson				 * At this point, we have a partial ordering:
280174923Srwatson				 *     nw <= hs <= he <= nr
281174923Srwatson				 * If hs > nw, buf[nw..hs] contains non-zero data.
282174923Srwatson				 * If he > hs, buf[hs..he] is all zeroes.
283174923Srwatson				 */
284174923Srwatson				if (hs > nw)
285174923Srwatson					if (fwrite(buf + nw, hs - nw, 1, fp)
286174923Srwatson					    != 1)
287174923Srwatson					break;
288174923Srwatson				if (he > hs)
289174923Srwatson					if (fseeko(fp, he - hs, SEEK_CUR) == -1)
290174923Srwatson						break;
291174923Srwatson			}
292174923Srwatson		}
293174923Srwatson		if (nw != wl) {
294174923Srwatson			syslog(LOG_ERR,
295174923Srwatson			    "write error on %s file: %m", filename);
296174923Srwatson			syslog(LOG_WARNING,
297174923Srwatson			    "WARNING: vmcore may be incomplete");
298174923Srwatson			nerr++;
299174923Srwatson			return (-1);
300174923Srwatson		}
301174923Srwatson		if (verbose) {
302174923Srwatson			dmpcnt += wl;
303174923Srwatson			printf("%llu\r", (unsigned long long)dmpcnt);
304174923Srwatson			fflush(stdout);
305174923Srwatson		}
306174923Srwatson		dumpsize -= wl;
307196528Slulf		if (got_siginfo) {
308196528Slulf			printf("%s %.1lf%%\n", filename, (100.0 - (100.0 *
309196528Slulf			    (double)dumpsize / (double)origsize)));
310196528Slulf			got_siginfo = 0;
311196528Slulf		}
312174923Srwatson	}
313174923Srwatson	return (0);
314174923Srwatson}
315174923Srwatson
316174923Srwatson/*
317174923Srwatson * Specialized version of dump-reading logic for use with textdumps, which
318174923Srwatson * are written backwards from the end of the partition, and must be reversed
319174923Srwatson * before being written to the file.  Textdumps are small, so do a bit less
320174923Srwatson * work to optimize/sparsify.
321174923Srwatson */
322174923Srwatsonstatic int
323174923SrwatsonDoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf,
324174923Srwatson    const char *device, const char *filename, FILE *fp)
325174923Srwatson{
326174923Srwatson	int nr, nw, wl;
327174923Srwatson	off_t dmpcnt, totsize;
328174923Srwatson
329174923Srwatson	totsize = dumpsize;
330174923Srwatson	dmpcnt = 0;
331174923Srwatson	wl = 512;
332174923Srwatson	if ((dumpsize % wl) != 0) {
333174923Srwatson		syslog(LOG_ERR, "textdump uneven multiple of 512 on %s",
334174923Srwatson		    device);
335174923Srwatson		nerr++;
336174923Srwatson		return (-1);
337174923Srwatson	}
338174923Srwatson	while (dumpsize > 0) {
339174923Srwatson		nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl);
340174923Srwatson		if (nr != wl) {
341174923Srwatson			if (nr == 0)
342174923Srwatson				syslog(LOG_WARNING,
343174923Srwatson				    "WARNING: EOF on dump device");
344174923Srwatson			else
345174923Srwatson				syslog(LOG_ERR, "read error on %s: %m", device);
346174923Srwatson			nerr++;
347174923Srwatson			return (-1);
348174923Srwatson		}
349174923Srwatson		nw = fwrite(buf, 1, wl, fp);
350174923Srwatson		if (nw != wl) {
351174923Srwatson			syslog(LOG_ERR,
352174923Srwatson			    "write error on %s file: %m", filename);
353174923Srwatson			syslog(LOG_WARNING,
354174923Srwatson			    "WARNING: textdump may be incomplete");
355174923Srwatson			nerr++;
356174923Srwatson			return (-1);
357174923Srwatson		}
358174923Srwatson		if (verbose) {
359174923Srwatson			dmpcnt += wl;
360174923Srwatson			printf("%llu\r", (unsigned long long)dmpcnt);
361174923Srwatson			fflush(stdout);
362174923Srwatson		}
363174923Srwatson		dumpsize -= wl;
364174923Srwatson	}
365174923Srwatson	return (0);
366174923Srwatson}
367174923Srwatson
36893492Sphkstatic void
369146763SdelphijDoFile(const char *savedir, const char *device)
3701558Srgrimes{
371174923Srwatson	static char filename[PATH_MAX];
37297340Smarcel	static char *buf = NULL;
37394580Smarcel	struct kerneldumpheader kdhf, kdhl;
374174923Srwatson	off_t mediasize, dumpsize, firsthd, lasthd;
37596049Sfenner	FILE *info, *fp;
376142533Sobrien	mode_t oumask;
377174923Srwatson	int fd, fdinfo, error;
378142359Sobrien	int bounds, status;
37994580Smarcel	u_int sectorsize;
380174923Srwatson	int istextdump;
3818871Srgrimes
382142359Sobrien	bounds = getbounds();
38396049Sfenner	mediasize = 0;
384142359Sobrien	status = STATUS_UNKNOWN;
38596049Sfenner
38697340Smarcel	if (buf == NULL) {
38797746Smarcel		buf = malloc(BUFFERSIZE);
38897340Smarcel		if (buf == NULL) {
38997340Smarcel			syslog(LOG_ERR, "%m");
39097340Smarcel			return;
39197340Smarcel		}
39297340Smarcel	}
39397340Smarcel
39494580Smarcel	if (verbose)
39596049Sfenner		printf("checking for kernel dump on device %s\n", device);
39694580Smarcel
397244216Spjd	fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR);
39893492Sphk	if (fd < 0) {
39996049Sfenner		syslog(LOG_ERR, "%s: %m", device);
40093492Sphk		return;
40147095Sluoqi	}
40297340Smarcel
40393492Sphk	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
40493492Sphk	if (!error)
40593492Sphk		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
40693492Sphk	if (error) {
40796049Sfenner		syslog(LOG_ERR,
40896049Sfenner		    "couldn't find media and/or sector size of %s: %m", device);
40994580Smarcel		goto closefd;
4101558Srgrimes	}
41194580Smarcel
41294580Smarcel	if (verbose) {
41396049Sfenner		printf("mediasize = %lld\n", (long long)mediasize);
41496049Sfenner		printf("sectorsize = %u\n", sectorsize);
41594580Smarcel	}
41694580Smarcel
41793492Sphk	lasthd = mediasize - sectorsize;
41893492Sphk	lseek(fd, lasthd, SEEK_SET);
41993492Sphk	error = read(fd, &kdhl, sizeof kdhl);
42093492Sphk	if (error != sizeof kdhl) {
42196049Sfenner		syslog(LOG_ERR,
42296049Sfenner		    "error reading last dump header at offset %lld in %s: %m",
42394580Smarcel		    (long long)lasthd, device);
42494580Smarcel		goto closefd;
4251558Srgrimes	}
426174923Srwatson	istextdump = 0;
427174944Srwatson	if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) {
42894580Smarcel		if (verbose)
429174923Srwatson			printf("textdump magic on last dump header on %s\n",
430174923Srwatson			    device);
431174923Srwatson		istextdump = 1;
432174944Srwatson		if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) {
433174944Srwatson			syslog(LOG_ERR,
434174944Srwatson			    "unknown version (%d) in last dump header on %s",
435174944Srwatson			    dtoh32(kdhl.version), device);
436244215Spjd
437174944Srwatson			status = STATUS_BAD;
438174944Srwatson			if (force == 0)
439174944Srwatson				goto closefd;
440174944Srwatson		}
441174944Srwatson	} else if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic) ==
442174944Srwatson	    0) {
443174944Srwatson		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
444174944Srwatson			syslog(LOG_ERR,
445174944Srwatson			    "unknown version (%d) in last dump header on %s",
446174944Srwatson			    dtoh32(kdhl.version), device);
447244215Spjd
448174944Srwatson			status = STATUS_BAD;
449174944Srwatson			if (force == 0)
450174944Srwatson				goto closefd;
451174944Srwatson		}
452174944Srwatson	} else {
453174923Srwatson		if (verbose)
45496049Sfenner			printf("magic mismatch on last dump header on %s\n",
45594580Smarcel			    device);
45696049Sfenner
457142359Sobrien		status = STATUS_BAD;
45896049Sfenner		if (force == 0)
45996049Sfenner			goto closefd;
46096049Sfenner
46196049Sfenner		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
46296049Sfenner			    sizeof kdhl.magic) == 0) {
46396049Sfenner			if (verbose)
46496049Sfenner				printf("forcing magic on %s\n", device);
46596049Sfenner			memcpy(kdhl.magic, KERNELDUMPMAGIC,
46696049Sfenner			    sizeof kdhl.magic);
46796049Sfenner		} else {
46896049Sfenner			syslog(LOG_ERR, "unable to force dump - bad magic");
46996049Sfenner			goto closefd;
47096049Sfenner		}
471174944Srwatson		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
472174944Srwatson			syslog(LOG_ERR,
473174944Srwatson			    "unknown version (%d) in last dump header on %s",
474174944Srwatson			    dtoh32(kdhl.version), device);
475244215Spjd
476174944Srwatson			status = STATUS_BAD;
477174944Srwatson			if (force == 0)
478174944Srwatson				goto closefd;
479174944Srwatson		}
4801558Srgrimes	}
481142359Sobrien
48294580Smarcel	nfound++;
48394580Smarcel	if (clear)
48494580Smarcel		goto nuke;
48594580Smarcel
48694580Smarcel	if (kerneldump_parity(&kdhl)) {
48796049Sfenner		syslog(LOG_ERR,
48896049Sfenner		    "parity error on last dump header on %s", device);
48996025Smux		nerr++;
490142359Sobrien		status = STATUS_BAD;
491142359Sobrien		if (force == 0)
492142359Sobrien			goto closefd;
49394580Smarcel	}
49493717Smarcel	dumpsize = dtoh64(kdhl.dumplength);
49593717Smarcel	firsthd = lasthd - dumpsize - sizeof kdhf;
49693492Sphk	lseek(fd, firsthd, SEEK_SET);
49793492Sphk	error = read(fd, &kdhf, sizeof kdhf);
49893492Sphk	if (error != sizeof kdhf) {
49996049Sfenner		syslog(LOG_ERR,
50096049Sfenner		    "error reading first dump header at offset %lld in %s: %m",
50194580Smarcel		    (long long)firsthd, device);
50296025Smux		nerr++;
50394580Smarcel		goto closefd;
5041558Srgrimes	}
505142359Sobrien
506142359Sobrien	if (verbose >= 2) {
507142359Sobrien		printf("First dump headers:\n");
508142359Sobrien		printheader(stdout, &kdhf, device, bounds, -1);
509142359Sobrien
510142359Sobrien		printf("\nLast dump headers:\n");
511142359Sobrien		printheader(stdout, &kdhl, device, bounds, -1);
512142359Sobrien		printf("\n");
513142359Sobrien	}
514142359Sobrien
51593492Sphk	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
51696049Sfenner		syslog(LOG_ERR,
51796049Sfenner		    "first and last dump headers disagree on %s", device);
51896025Smux		nerr++;
519142359Sobrien		status = STATUS_BAD;
520142359Sobrien		if (force == 0)
521142359Sobrien			goto closefd;
522142359Sobrien	} else {
523142359Sobrien		status = STATUS_GOOD;
52466429Sdes	}
52594580Smarcel
526119734Sdougb	if (checkfor) {
527119734Sdougb		printf("A dump exists on %s\n", device);
528119734Sdougb		close(fd);
529119734Sdougb		exit(0);
530119734Sdougb	}
531119734Sdougb
53296049Sfenner	if (kdhl.panicstring[0])
53396049Sfenner		syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
53496049Sfenner	else
53596049Sfenner		syslog(LOG_ALERT, "reboot");
53694580Smarcel
53796049Sfenner	if (verbose)
53896049Sfenner		printf("Checking for available free space\n");
53996025Smux	if (!check_space(savedir, dumpsize)) {
54096025Smux		nerr++;
54196025Smux		goto closefd;
54296025Smux	}
54396049Sfenner
544147506Sdwhite	writebounds(bounds + 1);
545147506Sdwhite
54696049Sfenner	sprintf(buf, "info.%d", bounds);
54796049Sfenner
54894580Smarcel	/*
549142359Sobrien	 * Create or overwrite any existing dump header files.
55094580Smarcel	 */
55194580Smarcel	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
55293492Sphk	if (fdinfo < 0) {
55396049Sfenner		syslog(LOG_ERR, "%s: %m", buf);
55496025Smux		nerr++;
55594580Smarcel		goto closefd;
5561558Srgrimes	}
55796049Sfenner	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
55896049Sfenner	if (compress) {
559174923Srwatson		sprintf(filename, "%s.%d.gz", istextdump ? "textdump.tar" :
560174923Srwatson		    "vmcore", bounds);
561174923Srwatson		fp = zopen(filename, "w");
56296049Sfenner	} else {
563174923Srwatson		sprintf(filename, "%s.%d", istextdump ? "textdump.tar" :
564174923Srwatson		    "vmcore", bounds);
565174923Srwatson		fp = fopen(filename, "w");
56696049Sfenner	}
56796049Sfenner	if (fp == NULL) {
568174923Srwatson		syslog(LOG_ERR, "%s: %m", filename);
56994580Smarcel		close(fdinfo);
57096025Smux		nerr++;
57194580Smarcel		goto closefd;
57293492Sphk	}
57396049Sfenner	(void)umask(oumask);
57496049Sfenner
57593492Sphk	info = fdopen(fdinfo, "w");
57694580Smarcel
577170054Skevlo	if (info == NULL) {
578170054Skevlo		syslog(LOG_ERR, "fdopen failed: %m");
579170054Skevlo		nerr++;
580170054Skevlo		goto closefd;
581170054Skevlo	}
582170054Skevlo
58394580Smarcel	if (verbose)
584142359Sobrien		printheader(stdout, &kdhl, device, bounds, status);
58594580Smarcel
586142359Sobrien	printheader(info, &kdhl, device, bounds, status);
58796049Sfenner	fclose(info);
58894580Smarcel
58996049Sfenner	syslog(LOG_NOTICE, "writing %score to %s",
590174923Srwatson	    compress ? "compressed " : "", filename);
59194580Smarcel
592174923Srwatson	if (istextdump) {
593174923Srwatson		if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device,
594174923Srwatson		    filename, fp) < 0)
59594580Smarcel			goto closeall;
596174923Srwatson	} else {
597174923Srwatson		if (DoRegularFile(fd, dumpsize, buf, device, filename, fp)
598174923Srwatson		    < 0)
59994580Smarcel			goto closeall;
60067264Sdes	}
60196049Sfenner	if (verbose)
60296049Sfenner		printf("\n");
60396049Sfenner
60496049Sfenner	if (fclose(fp) < 0) {
605174923Srwatson		syslog(LOG_ERR, "error on %s: %m", filename);
60696049Sfenner		nerr++;
60796049Sfenner		goto closeall;
60896049Sfenner	}
60996025Smux	nsaved++;
61094580Smarcel
61194580Smarcel	if (verbose)
61296049Sfenner		printf("dump saved\n");
61394580Smarcel
61496049Sfennernuke:
615244216Spjd	if (!keep) {
61694580Smarcel		if (verbose)
61796049Sfenner			printf("clearing dump header\n");
61896049Sfenner		memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
61994580Smarcel		lseek(fd, lasthd, SEEK_SET);
62094580Smarcel		error = write(fd, &kdhl, sizeof kdhl);
62194580Smarcel		if (error != sizeof kdhl)
62296049Sfenner			syslog(LOG_ERR,
62396049Sfenner			    "error while clearing the dump header: %m");
62494580Smarcel	}
62594580Smarcel	close(fd);
62694580Smarcel	return;
62794580Smarcel
62896049Sfennercloseall:
62996049Sfenner	fclose(fp);
63094580Smarcel
63196049Sfennerclosefd:
63294580Smarcel	close(fd);
6331558Srgrimes}
6341558Srgrimes
63593492Sphkstatic void
63693492Sphkusage(void)
6371558Srgrimes{
638141611Sru	fprintf(stderr, "%s\n%s\n%s\n",
639141611Sru	    "usage: savecore -c",
640141611Sru	    "       savecore -C [-v] [directory device]",
641141611Sru	    "       savecore [-fkvz] [directory [device ...]]");
64293492Sphk	exit (1);
6431558Srgrimes}
6441558Srgrimes
64518914Sfennerint
64693492Sphkmain(int argc, char **argv)
6471558Srgrimes{
648146763Sdelphij	const char *savedir = ".";
649142533Sobrien	struct fstab *fsp;
65093492Sphk	int i, ch, error;
6511558Srgrimes
652142359Sobrien	checkfor = compress = clear = force = keep = verbose = 0;
653142359Sobrien	nfound = nsaved = nerr = 0;
654142359Sobrien
65596049Sfenner	openlog("savecore", LOG_PERROR, LOG_DAEMON);
656196528Slulf	signal(SIGINFO, infohandler);
65796049Sfenner
658126347Smaxim	while ((ch = getopt(argc, argv, "Ccfkvz")) != -1)
65993492Sphk		switch(ch) {
660119734Sdougb		case 'C':
661119734Sdougb			checkfor = 1;
662119734Sdougb			break;
66393492Sphk		case 'c':
66494580Smarcel			clear = 1;
66594580Smarcel			break;
66694580Smarcel		case 'k':
66794580Smarcel			keep = 1;
66894580Smarcel			break;
66993492Sphk		case 'v':
670142359Sobrien			verbose++;
67194580Smarcel			break;
67293492Sphk		case 'f':
67394580Smarcel			force = 1;
67494580Smarcel			break;
67596049Sfenner		case 'z':
67696049Sfenner			compress = 1;
67796049Sfenner			break;
67893492Sphk		case '?':
67993492Sphk		default:
68093492Sphk			usage();
68193492Sphk		}
682119734Sdougb	if (checkfor && (clear || force || keep))
683119734Sdougb		usage();
684244217Spjd	if (clear && (compress || keep))
685244217Spjd		usage();
68693492Sphk	argc -= optind;
68793492Sphk	argv += optind;
68893492Sphk	if (argc >= 1) {
68993492Sphk		error = chdir(argv[0]);
69096049Sfenner		if (error) {
69196049Sfenner			syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
69296049Sfenner			exit(1);
69396049Sfenner		}
69496025Smux		savedir = argv[0];
69593492Sphk		argc--;
69693492Sphk		argv++;
6971558Srgrimes	}
69893492Sphk	if (argc == 0) {
69993492Sphk		for (;;) {
70093492Sphk			fsp = getfsent();
70193492Sphk			if (fsp == NULL)
70293492Sphk				break;
70393492Sphk			if (strcmp(fsp->fs_vfstype, "swap") &&
70493492Sphk			    strcmp(fsp->fs_vfstype, "dump"))
70593492Sphk				continue;
70696025Smux			DoFile(savedir, fsp->fs_spec);
70793492Sphk		}
70893492Sphk	} else {
70993492Sphk		for (i = 0; i < argc; i++)
71096025Smux			DoFile(savedir, argv[i]);
7111558Srgrimes	}
71294580Smarcel
71394580Smarcel	/* Emit minimal output. */
714119734Sdougb	if (nfound == 0) {
715119734Sdougb		if (checkfor) {
716119734Sdougb			printf("No dump exists\n");
717119734Sdougb			exit(1);
718119734Sdougb		}
71996049Sfenner		syslog(LOG_WARNING, "no dumps found");
720119734Sdougb	}
72196025Smux	else if (nsaved == 0) {
72296025Smux		if (nerr != 0)
72396049Sfenner			syslog(LOG_WARNING, "unsaved dumps found but not saved");
72496025Smux		else
72596049Sfenner			syslog(LOG_WARNING, "no unsaved dumps found");
72696025Smux	}
72794580Smarcel
72893492Sphk	return (0);
7291558Srgrimes}
730196528Slulf
731196528Slulfstatic void
732196528Slulfinfohandler(int sig __unused)
733196528Slulf{
734196528Slulf	got_siginfo = 1;
735196528Slulf}
736