savecore.c revision 119734
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 * 3. All advertising materials mentioning features or use of this software
4796049Sfenner *    must display the following acknowledgement:
4896049Sfenner *	This product includes software developed by the University of
4996049Sfenner *	California, Berkeley and its contributors.
5096049Sfenner * 4. Neither the name of the University nor the names of its contributors
5196049Sfenner *    may be used to endorse or promote products derived from this software
5296049Sfenner *    without specific prior written permission.
5396049Sfenner *
5496049Sfenner * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5596049Sfenner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5696049Sfenner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5796049Sfenner * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5896049Sfenner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5996049Sfenner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6096049Sfenner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6196049Sfenner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6296049Sfenner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6396049Sfenner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6496049Sfenner * SUCH DAMAGE.
651558Srgrimes */
661558Srgrimes
6795183Scharnier#include <sys/cdefs.h>
6895183Scharnier__FBSDID("$FreeBSD: head/sbin/savecore/savecore.c 119734 2003-09-04 10:07:01Z dougb $");
6995183Scharnier
7096049Sfenner#include <sys/param.h>
7194580Smarcel#include <sys/disk.h>
7294580Smarcel#include <sys/kerneldump.h>
7396025Smux#include <sys/param.h>
7496025Smux#include <sys/mount.h>
7594580Smarcel#include <sys/stat.h>
7694580Smarcel#include <errno.h>
7793492Sphk#include <fcntl.h>
7893492Sphk#include <fstab.h>
7996025Smux#include <paths.h>
8096049Sfenner#include <stdarg.h>
8194580Smarcel#include <stdio.h>
8294580Smarcel#include <stdlib.h>
8394580Smarcel#include <string.h>
8496049Sfenner#include <syslog.h>
8593492Sphk#include <time.h>
8693492Sphk#include <unistd.h>
871558Srgrimes
8897746Smarcel/* The size of the buffer used for I/O. */
8997746Smarcel#define	BUFFERSIZE	(1024*1024)
9097746Smarcel
91119734Sdougbint checkfor, compress, clear, force, keep, verbose;	/* flags */
9296049Sfennerint nfound, nsaved, nerr;			/* statistics */
9394580Smarcel
9496049Sfennerextern FILE *zopen(const char *, const char *);
9596049Sfenner
9693492Sphkstatic void
9794580Smarcelprintheader(FILE *f, const struct kerneldumpheader *h, const char *device,
9896049Sfenner    int bounds)
991558Srgrimes{
10093717Smarcel	uint64_t dumplen;
10193492Sphk	time_t t;
1021558Srgrimes
10394580Smarcel	fprintf(f, "Good dump found on device %s\n", device);
10493492Sphk	fprintf(f, "  Architecture: %s\n", h->architecture);
10593717Smarcel	fprintf(f, "  Architecture version: %d\n",
10693717Smarcel	    dtoh32(h->architectureversion));
10793717Smarcel	dumplen = dtoh64(h->dumplength);
10893717Smarcel	fprintf(f, "  Dump length: %lldB (%lld MB)\n", (long long)dumplen,
10993717Smarcel	    (long long)(dumplen >> 20));
11093717Smarcel	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
11193717Smarcel	t = dtoh64(h->dumptime);
11293492Sphk	fprintf(f, "  Dumptime: %s", ctime(&t));
11393492Sphk	fprintf(f, "  Hostname: %s\n", h->hostname);
11493492Sphk	fprintf(f, "  Versionstring: %s", h->versionstring);
11593492Sphk	fprintf(f, "  Panicstring: %s\n", h->panicstring);
11696049Sfenner	fprintf(f, "  Bounds: %d\n", bounds);
11795039Sphk	fflush(f);
1181558Srgrimes}
1191558Srgrimes
12096049Sfennerstatic int
12196049Sfennergetbounds(void) {
12296049Sfenner	FILE *fp;
12396049Sfenner	char buf[6];
12496049Sfenner	int ret;
12596049Sfenner
12696049Sfenner	ret = 0;
12796049Sfenner
12896049Sfenner	if ((fp = fopen("bounds", "r")) == NULL) {
12996049Sfenner		syslog(LOG_WARNING, "unable to open bounds file, using 0");
13096049Sfenner		goto newfile;
13196049Sfenner	}
13296049Sfenner
13396049Sfenner	if (fgets(buf, sizeof buf, fp) == NULL) {
13496049Sfenner		syslog(LOG_WARNING, "unable to read from bounds, using 0");
13596049Sfenner		fclose(fp);
13696049Sfenner		goto newfile;
13796049Sfenner	}
13896049Sfenner
13996049Sfenner	errno = 0;
14096049Sfenner	ret = (int)strtol(buf, NULL, 10);
14196049Sfenner	if (ret == 0 && (errno == EINVAL || errno == ERANGE))
14296049Sfenner		syslog(LOG_WARNING, "invalid value found in bounds, using 0");
14396049Sfenner
14496049Sfennernewfile:
14596049Sfenner
14696049Sfenner	if ((fp = fopen("bounds", "w")) == NULL) {
14796049Sfenner		syslog(LOG_WARNING, "unable to write to bounds file: %m");
14896049Sfenner		goto done;
14996049Sfenner	}
15096049Sfenner
15196049Sfenner	if (verbose)
15296049Sfenner		printf("bounds number: %d\n", ret);
15396049Sfenner
15496049Sfenner	fprintf(fp, "%d\n", (ret + 1));
15596049Sfenner	fclose(fp);
15696049Sfenner
15796049Sfennerdone:
15896049Sfenner	return (ret);
15996049Sfenner}
16096049Sfenner
16196025Smux/*
16296025Smux * Check that sufficient space is available on the disk that holds the
16396025Smux * save directory.
16496025Smux */
16596025Smuxstatic int
16696025Smuxcheck_space(char *savedir, off_t dumpsize)
16796025Smux{
16896025Smux	FILE *fp;
16996049Sfenner	off_t minfree, spacefree, totfree, needed;
17096025Smux	struct statfs fsbuf;
17196025Smux	char buf[100], path[MAXPATHLEN];
17293717Smarcel
17396049Sfenner	if (statfs(savedir, &fsbuf) < 0) {
17496049Sfenner		syslog(LOG_ERR, "%s: %m", savedir);
17596049Sfenner		exit(1);
17696049Sfenner	}
17796025Smux 	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
17896025Smux	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
17996025Smux
18096025Smux	(void)snprintf(path, sizeof(path), "%s/minfree", savedir);
18196025Smux	if ((fp = fopen(path, "r")) == NULL)
18296025Smux		minfree = 0;
18396025Smux	else {
18496025Smux		if (fgets(buf, sizeof(buf), fp) == NULL)
18596025Smux			minfree = 0;
18696025Smux		else
18796025Smux			minfree = atoi(buf);
18896025Smux		(void)fclose(fp);
18996025Smux	}
19096025Smux
19196049Sfenner	needed = dumpsize / 1024 + 2;	/* 2 for info file */
19296025Smux 	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
19396049Sfenner		syslog(LOG_WARNING,
19496049Sfenner	"no dump, not enough free space on device (%lld available, need %lld)",
19596025Smux		    (long long)(minfree > 0 ? spacefree : totfree),
19696025Smux		    (long long)needed);
19796025Smux		return (0);
19896025Smux	}
19996025Smux	if (spacefree - needed < 0)
20096049Sfenner		syslog(LOG_WARNING,
20196049Sfenner		    "dump performed, but free space threshold crossed");
20296025Smux	return (1);
20396025Smux}
20496025Smux
20596049Sfenner#define BLOCKSIZE (1<<12)
20696049Sfenner#define BLOCKMASK (~(BLOCKSIZE-1))
20796025Smux
20893492Sphkstatic void
20996025SmuxDoFile(char *savedir, const char *device)
2101558Srgrimes{
21197340Smarcel	static char *buf = NULL;
21294580Smarcel	struct kerneldumpheader kdhf, kdhl;
21396049Sfenner	off_t mediasize, dumpsize, firsthd, lasthd, dmpcnt;
21496049Sfenner	FILE *info, *fp;
21596049Sfenner	int fd, fdinfo, error, wl;
21696049Sfenner	int nr, nw, hs, he;
21796049Sfenner	int bounds;
21894580Smarcel	u_int sectorsize;
21996049Sfenner	mode_t oumask;
2208871Srgrimes
22196049Sfenner	dmpcnt = 0;
22296049Sfenner	mediasize = 0;
22396049Sfenner
22497340Smarcel	/*
22597340Smarcel	 * XXX On ia64 something breaks when the buffer is put on the
22697340Smarcel	 * stack. When the buffer is roughly larger than 128K the read()
22797340Smarcel	 * below simply fails with errno=14 (EFAULT). We work around
228108533Sschweikh	 * this by doing a one-time allocation...
22997340Smarcel	 */
23097340Smarcel	if (buf == NULL) {
23197746Smarcel		buf = malloc(BUFFERSIZE);
23297340Smarcel		if (buf == NULL) {
23397340Smarcel			syslog(LOG_ERR, "%m");
23497340Smarcel			return;
23597340Smarcel		}
23697340Smarcel	}
23797340Smarcel
23894580Smarcel	if (verbose)
23996049Sfenner		printf("checking for kernel dump on device %s\n", device);
24094580Smarcel
24194580Smarcel	fd = open(device, O_RDWR);
24293492Sphk	if (fd < 0) {
24396049Sfenner		syslog(LOG_ERR, "%s: %m", device);
24493492Sphk		return;
24547095Sluoqi	}
24697340Smarcel
24793492Sphk	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
24893492Sphk	if (!error)
24993492Sphk		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
25093492Sphk	if (error) {
25196049Sfenner		syslog(LOG_ERR,
25296049Sfenner		    "couldn't find media and/or sector size of %s: %m", device);
25394580Smarcel		goto closefd;
2541558Srgrimes	}
25594580Smarcel
25694580Smarcel	if (verbose) {
25796049Sfenner		printf("mediasize = %lld\n", (long long)mediasize);
25896049Sfenner		printf("sectorsize = %u\n", sectorsize);
25994580Smarcel	}
26094580Smarcel
26193492Sphk	lasthd = mediasize - sectorsize;
26293492Sphk	lseek(fd, lasthd, SEEK_SET);
26393492Sphk	error = read(fd, &kdhl, sizeof kdhl);
26493492Sphk	if (error != sizeof kdhl) {
26596049Sfenner		syslog(LOG_ERR,
26696049Sfenner		    "error reading last dump header at offset %lld in %s: %m",
26794580Smarcel		    (long long)lasthd, device);
26894580Smarcel		goto closefd;
2691558Srgrimes	}
27093492Sphk	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
27194580Smarcel		if (verbose)
27296049Sfenner			printf("magic mismatch on last dump header on %s\n",
27394580Smarcel			    device);
27496049Sfenner
27596049Sfenner		if (force == 0)
27696049Sfenner			goto closefd;
27796049Sfenner
27896049Sfenner		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
27996049Sfenner			    sizeof kdhl.magic) == 0) {
28096049Sfenner			if (verbose)
28196049Sfenner				printf("forcing magic on %s\n", device);
28296049Sfenner			memcpy(kdhl.magic, KERNELDUMPMAGIC,
28396049Sfenner			    sizeof kdhl.magic);
28496049Sfenner		} else {
28596049Sfenner			syslog(LOG_ERR, "unable to force dump - bad magic");
28696049Sfenner			goto closefd;
28796049Sfenner		}
2881558Srgrimes	}
28993717Smarcel	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
29096049Sfenner		syslog(LOG_ERR,
29196049Sfenner		    "unknown version (%d) in last dump header on %s",
29294580Smarcel		    dtoh32(kdhl.version), device);
29394580Smarcel		goto closefd;
29466429Sdes	}
29594580Smarcel
29694580Smarcel	nfound++;
29794580Smarcel	if (clear)
29894580Smarcel		goto nuke;
29994580Smarcel
30094580Smarcel	if (kerneldump_parity(&kdhl)) {
30196049Sfenner		syslog(LOG_ERR,
30296049Sfenner		    "parity error on last dump header on %s", device);
30396025Smux		nerr++;
30494580Smarcel		goto closefd;
30594580Smarcel	}
30693717Smarcel	dumpsize = dtoh64(kdhl.dumplength);
30793717Smarcel	firsthd = lasthd - dumpsize - sizeof kdhf;
30893492Sphk	lseek(fd, firsthd, SEEK_SET);
30993492Sphk	error = read(fd, &kdhf, sizeof kdhf);
31093492Sphk	if (error != sizeof kdhf) {
31196049Sfenner		syslog(LOG_ERR,
31296049Sfenner		    "error reading first dump header at offset %lld in %s: %m",
31394580Smarcel		    (long long)firsthd, device);
31496025Smux		nerr++;
31594580Smarcel		goto closefd;
3161558Srgrimes	}
31793492Sphk	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
31896049Sfenner		syslog(LOG_ERR,
31996049Sfenner		    "first and last dump headers disagree on %s", device);
32096025Smux		nerr++;
32194580Smarcel		goto closefd;
32266429Sdes	}
32394580Smarcel
324119734Sdougb	if (checkfor) {
325119734Sdougb		printf("A dump exists on %s\n", device);
326119734Sdougb		close(fd);
327119734Sdougb		exit(0);
328119734Sdougb	}
329119734Sdougb
33096049Sfenner	if (kdhl.panicstring[0])
33196049Sfenner		syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
33296049Sfenner	else
33396049Sfenner		syslog(LOG_ALERT, "reboot");
33494580Smarcel
33596049Sfenner	if (verbose)
33696049Sfenner		printf("Checking for available free space\n");
33796025Smux	if (!check_space(savedir, dumpsize)) {
33896025Smux		nerr++;
33996025Smux		goto closefd;
34096025Smux	}
34196049Sfenner
34296049Sfenner	bounds = getbounds();
34396049Sfenner
34496049Sfenner	sprintf(buf, "info.%d", bounds);
34596049Sfenner
34694580Smarcel	/*
34794580Smarcel	 * Create or overwrite any existing files.
34894580Smarcel	 */
34994580Smarcel	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
35093492Sphk	if (fdinfo < 0) {
35196049Sfenner		syslog(LOG_ERR, "%s: %m", buf);
35296025Smux		nerr++;
35394580Smarcel		goto closefd;
3541558Srgrimes	}
35596049Sfenner	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
35696049Sfenner	if (compress) {
35796049Sfenner		sprintf(buf, "vmcore.%d.gz", bounds);
35896049Sfenner		fp = zopen(buf, "w");
35996049Sfenner	} else {
36096049Sfenner		sprintf(buf, "vmcore.%d", bounds);
36196049Sfenner		fp = fopen(buf, "w");
36296049Sfenner	}
36396049Sfenner	if (fp == NULL) {
36496049Sfenner		syslog(LOG_ERR, "%s: %m", buf);
36594580Smarcel		close(fdinfo);
36696025Smux		nerr++;
36794580Smarcel		goto closefd;
36893492Sphk	}
36996049Sfenner	(void)umask(oumask);
37096049Sfenner
37193492Sphk	info = fdopen(fdinfo, "w");
37294580Smarcel
37394580Smarcel	if (verbose)
37496049Sfenner		printheader(stdout, &kdhl, device, bounds);
37594580Smarcel
37696049Sfenner	printheader(info, &kdhl, device, bounds);
37796049Sfenner	fclose(info);
37894580Smarcel
37996049Sfenner	syslog(LOG_NOTICE, "writing %score to %s",
38096049Sfenner	    compress ? "compressed " : "", buf);
38194580Smarcel
38293492Sphk	while (dumpsize > 0) {
38397746Smarcel		wl = BUFFERSIZE;
38493492Sphk		if (wl > dumpsize)
38593492Sphk			wl = dumpsize;
38696049Sfenner		nr = read(fd, buf, wl);
38796049Sfenner		if (nr != wl) {
38896049Sfenner			if (nr == 0)
38996049Sfenner				syslog(LOG_WARNING,
39096049Sfenner				    "WARNING: EOF on dump device");
39196049Sfenner			else
39296049Sfenner				syslog(LOG_ERR, "read error on %s: %m", device);
39396025Smux			nerr++;
39494580Smarcel			goto closeall;
39567264Sdes		}
39696049Sfenner		if (compress) {
39796049Sfenner			nw = fwrite(buf, 1, wl, fp);
39896049Sfenner		} else {
39996049Sfenner			for (nw = 0; nw < nr; nw = he) {
40096049Sfenner			    /* find a contiguous block of zeroes */
40196049Sfenner			    for (hs = nw; hs < nr; hs += BLOCKSIZE) {
40296049Sfenner				for (he = hs; he < nr && buf[he] == 0; ++he)
40396049Sfenner				    /* nothing */ ;
40496049Sfenner				/* is the hole long enough to matter? */
40596049Sfenner				if (he >= hs + BLOCKSIZE)
40696049Sfenner				    break;
40796049Sfenner			    }
40896049Sfenner
40996049Sfenner			    /* back down to a block boundary */
41096049Sfenner			    he &= BLOCKMASK;
41196049Sfenner
41296049Sfenner			    /*
41396049Sfenner			     * 1) Don't go beyond the end of the buffer.
41496049Sfenner			     * 2) If the end of the buffer is less than
41596049Sfenner			     *    BLOCKSIZE bytes away, we're at the end
41696049Sfenner			     *    of the file, so just grab what's left.
41796049Sfenner			     */
41896049Sfenner			    if (hs + BLOCKSIZE > nr)
41996049Sfenner				hs = he = nr;
42096049Sfenner
42196049Sfenner			    /*
42296049Sfenner			     * At this point, we have a partial ordering:
42396049Sfenner			     *     nw <= hs <= he <= nr
42496049Sfenner			     * If hs > nw, buf[nw..hs] contains non-zero data.
42596049Sfenner			     * If he > hs, buf[hs..he] is all zeroes.
42696049Sfenner			     */
42796049Sfenner			    if (hs > nw)
42896049Sfenner				if (fwrite(buf + nw, hs - nw, 1, fp) != 1)
42996049Sfenner				    break;
43096049Sfenner			    if (he > hs)
43196049Sfenner				if (fseek(fp, he - hs, SEEK_CUR) == -1)
43296049Sfenner				    break;
43396049Sfenner			}
43496049Sfenner		}
43596049Sfenner		if (nw != wl) {
43696049Sfenner			syslog(LOG_ERR,
43796049Sfenner			    "write error on vmcore.%d file: %m", bounds);
43896049Sfenner			syslog(LOG_WARNING,
43996049Sfenner			    "WARNING: vmcore may be incomplete");
44096025Smux			nerr++;
44194580Smarcel			goto closeall;
44293492Sphk		}
44396049Sfenner		if (verbose) {
44496049Sfenner			dmpcnt += wl;
44596103Smarcel			printf("%llu\r", (unsigned long long)dmpcnt);
44696049Sfenner			fflush(stdout);
44796049Sfenner		}
44893492Sphk		dumpsize -= wl;
44967264Sdes	}
45096049Sfenner	if (verbose)
45196049Sfenner		printf("\n");
45296049Sfenner
45396049Sfenner	if (fclose(fp) < 0) {
45496049Sfenner		syslog(LOG_ERR, "error on vmcore.%d: %m", bounds);
45596049Sfenner		nerr++;
45696049Sfenner		goto closeall;
45796049Sfenner	}
45896025Smux	nsaved++;
45994580Smarcel
46094580Smarcel	if (verbose)
46196049Sfenner		printf("dump saved\n");
46294580Smarcel
46396049Sfennernuke:
46494580Smarcel	if (clear || !keep) {
46594580Smarcel		if (verbose)
46696049Sfenner			printf("clearing dump header\n");
46796049Sfenner		memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
46894580Smarcel		lseek(fd, lasthd, SEEK_SET);
46994580Smarcel		error = write(fd, &kdhl, sizeof kdhl);
47094580Smarcel		if (error != sizeof kdhl)
47196049Sfenner			syslog(LOG_ERR,
47296049Sfenner			    "error while clearing the dump header: %m");
47394580Smarcel	}
47494580Smarcel	close(fd);
47594580Smarcel	return;
47694580Smarcel
47796049Sfennercloseall:
47896049Sfenner	fclose(fp);
47994580Smarcel
48096049Sfennerclosefd:
48194580Smarcel	close(fd);
4821558Srgrimes}
4831558Srgrimes
48493492Sphkstatic void
48593492Sphkusage(void)
4861558Srgrimes{
487119734Sdougb	fprintf(stderr, "usage: savecore [-Cv|-cfkv] [directory [device...]]\n");
48893492Sphk	exit (1);
4891558Srgrimes}
4901558Srgrimes
49118914Sfennerint
49293492Sphkmain(int argc, char **argv)
4931558Srgrimes{
49493492Sphk	int i, ch, error;
49593492Sphk	struct fstab *fsp;
49696025Smux	char *savedir;
4971558Srgrimes
49896049Sfenner	openlog("savecore", LOG_PERROR, LOG_DAEMON);
49996049Sfenner
50096025Smux	savedir = strdup(".");
50196049Sfenner	if (savedir == NULL) {
50296049Sfenner		syslog(LOG_ERR, "Cannot allocate memory");
50396049Sfenner		exit(1);
50496049Sfenner	}
505119734Sdougb	while ((ch = getopt(argc, argv, "CcdfkN:vz")) != -1)
50693492Sphk		switch(ch) {
507119734Sdougb		case 'C':
508119734Sdougb			checkfor = 1;
509119734Sdougb			break;
51093492Sphk		case 'c':
51194580Smarcel			clear = 1;
51294580Smarcel			break;
51394580Smarcel		case 'k':
51494580Smarcel			keep = 1;
51594580Smarcel			break;
51693492Sphk		case 'v':
51794580Smarcel			verbose = 1;
51894580Smarcel			break;
51993492Sphk		case 'f':
52094580Smarcel			force = 1;
52194580Smarcel			break;
52296049Sfenner		case 'z':
52396049Sfenner			compress = 1;
52496049Sfenner			break;
52594580Smarcel		case 'd':	/* Obsolete */
52693492Sphk		case 'N':
52793492Sphk		case '?':
52893492Sphk		default:
52993492Sphk			usage();
53093492Sphk		}
531119734Sdougb	if (checkfor && (clear || force || keep))
532119734Sdougb		usage();
53393492Sphk	argc -= optind;
53493492Sphk	argv += optind;
53593492Sphk	if (argc >= 1) {
53693492Sphk		error = chdir(argv[0]);
53796049Sfenner		if (error) {
53896049Sfenner			syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
53996049Sfenner			exit(1);
54096049Sfenner		}
54196025Smux		savedir = argv[0];
54293492Sphk		argc--;
54393492Sphk		argv++;
5441558Srgrimes	}
54593492Sphk	if (argc == 0) {
54693492Sphk		for (;;) {
54793492Sphk			fsp = getfsent();
54893492Sphk			if (fsp == NULL)
54993492Sphk				break;
55093492Sphk			if (strcmp(fsp->fs_vfstype, "swap") &&
55193492Sphk			    strcmp(fsp->fs_vfstype, "dump"))
55293492Sphk				continue;
55396025Smux			DoFile(savedir, fsp->fs_spec);
55493492Sphk		}
55593492Sphk	} else {
55693492Sphk		for (i = 0; i < argc; i++)
55796025Smux			DoFile(savedir, argv[i]);
5581558Srgrimes	}
55994580Smarcel
56094580Smarcel	/* Emit minimal output. */
561119734Sdougb	if (nfound == 0) {
562119734Sdougb		if (checkfor) {
563119734Sdougb			printf("No dump exists\n");
564119734Sdougb			exit(1);
565119734Sdougb		}
56696049Sfenner		syslog(LOG_WARNING, "no dumps found");
567119734Sdougb	}
56896025Smux	else if (nsaved == 0) {
56996025Smux		if (nerr != 0)
57096049Sfenner			syslog(LOG_WARNING, "unsaved dumps found but not saved");
57196025Smux		else
57296049Sfenner			syslog(LOG_WARNING, "no unsaved dumps found");
57396025Smux	}
57494580Smarcel
57593492Sphk	return (0);
5761558Srgrimes}
577