savecore.c revision 142359
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 142359 2005-02-24 02:45:10Z obrien $");
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
91142359Sobrien#define STATUS_BAD	0
92142359Sobrien#define STATUS_GOOD	1
93142359Sobrien#define STATUS_UNKNOWN	2
94142359Sobrien
95133814Srustatic int checkfor, compress, clear, force, keep, verbose;	/* flags */
96133814Srustatic int nfound, nsaved, nerr;			/* statistics */
9794580Smarcel
9896049Sfennerextern FILE *zopen(const char *, const char *);
9996049Sfenner
10093492Sphkstatic void
10194580Smarcelprintheader(FILE *f, const struct kerneldumpheader *h, const char *device,
102142359Sobrien    int bounds, const int status)
1031558Srgrimes{
10493717Smarcel	uint64_t dumplen;
10593492Sphk	time_t t;
106142359Sobrien	const char *stat_str;
1071558Srgrimes
108142359Sobrien	fprintf(f, "Dump header from device %s\n", device);
10993492Sphk	fprintf(f, "  Architecture: %s\n", h->architecture);
110142359Sobrien	fprintf(f, "  Architecture Version: %u\n", 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:
126142359Sobrien	  stat_str = "bad";
127142359Sobrien	  break;
128142359Sobrien	case STATUS_GOOD:
129142359Sobrien	  stat_str = "good";
130142359Sobrien	  break;
131142359Sobrien	default:
132142359Sobrien	  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) {
14796049Sfenner		syslog(LOG_WARNING, "unable to open bounds file, using 0");
14896049Sfenner		goto newfile;
14996049Sfenner	}
15096049Sfenner
15196049Sfenner	if (fgets(buf, sizeof buf, fp) == NULL) {
15296049Sfenner		syslog(LOG_WARNING, "unable to read from bounds, using 0");
15396049Sfenner		fclose(fp);
15496049Sfenner		goto newfile;
15596049Sfenner	}
15696049Sfenner
15796049Sfenner	errno = 0;
15896049Sfenner	ret = (int)strtol(buf, NULL, 10);
15996049Sfenner	if (ret == 0 && (errno == EINVAL || errno == ERANGE))
16096049Sfenner		syslog(LOG_WARNING, "invalid value found in bounds, using 0");
16196049Sfenner
16296049Sfennernewfile:
16396049Sfenner
16496049Sfenner	if ((fp = fopen("bounds", "w")) == NULL) {
16596049Sfenner		syslog(LOG_WARNING, "unable to write to bounds file: %m");
16696049Sfenner		goto done;
16796049Sfenner	}
16896049Sfenner
16996049Sfenner	if (verbose)
17096049Sfenner		printf("bounds number: %d\n", ret);
17196049Sfenner
17296049Sfenner	fprintf(fp, "%d\n", (ret + 1));
17396049Sfenner	fclose(fp);
17496049Sfenner
17596049Sfennerdone:
17696049Sfenner	return (ret);
17796049Sfenner}
17896049Sfenner
17996025Smux/*
18096025Smux * Check that sufficient space is available on the disk that holds the
18196025Smux * save directory.
18296025Smux */
18396025Smuxstatic int
18496025Smuxcheck_space(char *savedir, off_t dumpsize)
18596025Smux{
18696025Smux	FILE *fp;
18796049Sfenner	off_t minfree, spacefree, totfree, needed;
18896025Smux	struct statfs fsbuf;
18996025Smux	char buf[100], path[MAXPATHLEN];
19093717Smarcel
19196049Sfenner	if (statfs(savedir, &fsbuf) < 0) {
19296049Sfenner		syslog(LOG_ERR, "%s: %m", savedir);
19396049Sfenner		exit(1);
19496049Sfenner	}
19596025Smux 	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
19696025Smux	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
19796025Smux
19896025Smux	(void)snprintf(path, sizeof(path), "%s/minfree", savedir);
19996025Smux	if ((fp = fopen(path, "r")) == NULL)
20096025Smux		minfree = 0;
20196025Smux	else {
20296025Smux		if (fgets(buf, sizeof(buf), fp) == NULL)
20396025Smux			minfree = 0;
20496025Smux		else
20596025Smux			minfree = atoi(buf);
20696025Smux		(void)fclose(fp);
20796025Smux	}
20896025Smux
20996049Sfenner	needed = dumpsize / 1024 + 2;	/* 2 for info file */
21096025Smux 	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
21196049Sfenner		syslog(LOG_WARNING,
21296049Sfenner	"no dump, not enough free space on device (%lld available, need %lld)",
21396025Smux		    (long long)(minfree > 0 ? spacefree : totfree),
21496025Smux		    (long long)needed);
21596025Smux		return (0);
21696025Smux	}
21796025Smux	if (spacefree - needed < 0)
21896049Sfenner		syslog(LOG_WARNING,
21996049Sfenner		    "dump performed, but free space threshold crossed");
22096025Smux	return (1);
22196025Smux}
22296025Smux
22396049Sfenner#define BLOCKSIZE (1<<12)
22496049Sfenner#define BLOCKMASK (~(BLOCKSIZE-1))
22596025Smux
22693492Sphkstatic void
22796025SmuxDoFile(char *savedir, const char *device)
2281558Srgrimes{
22997340Smarcel	static char *buf = NULL;
23094580Smarcel	struct kerneldumpheader kdhf, kdhl;
23196049Sfenner	off_t mediasize, dumpsize, firsthd, lasthd, dmpcnt;
23296049Sfenner	FILE *info, *fp;
23396049Sfenner	int fd, fdinfo, error, wl;
234139975Sdelphij	int nr, nw, hs, he = 0;
235142359Sobrien	int bounds, status;
23694580Smarcel	u_int sectorsize;
23796049Sfenner	mode_t oumask;
2388871Srgrimes
239142359Sobrien	bounds = getbounds();
24096049Sfenner	dmpcnt = 0;
24196049Sfenner	mediasize = 0;
242142359Sobrien	status = STATUS_UNKNOWN;
24396049Sfenner
24497340Smarcel	if (buf == NULL) {
24597746Smarcel		buf = malloc(BUFFERSIZE);
24697340Smarcel		if (buf == NULL) {
24797340Smarcel			syslog(LOG_ERR, "%m");
24897340Smarcel			return;
24997340Smarcel		}
25097340Smarcel	}
25197340Smarcel
25294580Smarcel	if (verbose)
25396049Sfenner		printf("checking for kernel dump on device %s\n", device);
25494580Smarcel
25594580Smarcel	fd = open(device, O_RDWR);
25693492Sphk	if (fd < 0) {
25796049Sfenner		syslog(LOG_ERR, "%s: %m", device);
25893492Sphk		return;
25947095Sluoqi	}
26097340Smarcel
26193492Sphk	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
26293492Sphk	if (!error)
26393492Sphk		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
26493492Sphk	if (error) {
26596049Sfenner		syslog(LOG_ERR,
26696049Sfenner		    "couldn't find media and/or sector size of %s: %m", device);
26794580Smarcel		goto closefd;
2681558Srgrimes	}
26994580Smarcel
27094580Smarcel	if (verbose) {
27196049Sfenner		printf("mediasize = %lld\n", (long long)mediasize);
27296049Sfenner		printf("sectorsize = %u\n", sectorsize);
27394580Smarcel	}
27494580Smarcel
27593492Sphk	lasthd = mediasize - sectorsize;
27693492Sphk	lseek(fd, lasthd, SEEK_SET);
27793492Sphk	error = read(fd, &kdhl, sizeof kdhl);
27893492Sphk	if (error != sizeof kdhl) {
27996049Sfenner		syslog(LOG_ERR,
28096049Sfenner		    "error reading last dump header at offset %lld in %s: %m",
28194580Smarcel		    (long long)lasthd, device);
28294580Smarcel		goto closefd;
2831558Srgrimes	}
28493492Sphk	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
28594580Smarcel		if (verbose)
28696049Sfenner			printf("magic mismatch on last dump header on %s\n",
28794580Smarcel			    device);
28896049Sfenner
289142359Sobrien		status = STATUS_BAD;
29096049Sfenner		if (force == 0)
29196049Sfenner			goto closefd;
29296049Sfenner
29396049Sfenner		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
29496049Sfenner			    sizeof kdhl.magic) == 0) {
29596049Sfenner			if (verbose)
29696049Sfenner				printf("forcing magic on %s\n", device);
29796049Sfenner			memcpy(kdhl.magic, KERNELDUMPMAGIC,
29896049Sfenner			    sizeof kdhl.magic);
29996049Sfenner		} else {
30096049Sfenner			syslog(LOG_ERR, "unable to force dump - bad magic");
30196049Sfenner			goto closefd;
30296049Sfenner		}
3031558Srgrimes	}
30493717Smarcel	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
30596049Sfenner		syslog(LOG_ERR,
30696049Sfenner		    "unknown version (%d) in last dump header on %s",
30794580Smarcel		    dtoh32(kdhl.version), device);
308142359Sobrien
309142359Sobrien		status = STATUS_BAD;
310142359Sobrien		if (force == 0)
311142359Sobrien			goto closefd;
31266429Sdes	}
31394580Smarcel
31494580Smarcel	nfound++;
31594580Smarcel	if (clear)
31694580Smarcel		goto nuke;
31794580Smarcel
31894580Smarcel	if (kerneldump_parity(&kdhl)) {
31996049Sfenner		syslog(LOG_ERR,
32096049Sfenner		    "parity error on last dump header on %s", device);
32196025Smux		nerr++;
322142359Sobrien		status = STATUS_BAD;
323142359Sobrien		if (force == 0)
324142359Sobrien			goto closefd;
32594580Smarcel	}
32693717Smarcel	dumpsize = dtoh64(kdhl.dumplength);
32793717Smarcel	firsthd = lasthd - dumpsize - sizeof kdhf;
32893492Sphk	lseek(fd, firsthd, SEEK_SET);
32993492Sphk	error = read(fd, &kdhf, sizeof kdhf);
33093492Sphk	if (error != sizeof kdhf) {
33196049Sfenner		syslog(LOG_ERR,
33296049Sfenner		    "error reading first dump header at offset %lld in %s: %m",
33394580Smarcel		    (long long)firsthd, device);
33496025Smux		nerr++;
33594580Smarcel		goto closefd;
3361558Srgrimes	}
337142359Sobrien
338142359Sobrien	if (verbose >= 2) {
339142359Sobrien		printf("First dump headers:\n");
340142359Sobrien		printheader(stdout, &kdhf, device, bounds, -1);
341142359Sobrien
342142359Sobrien		printf("\nLast dump headers:\n");
343142359Sobrien		printheader(stdout, &kdhl, device, bounds, -1);
344142359Sobrien		printf("\n");
345142359Sobrien	}
346142359Sobrien
34793492Sphk	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
34896049Sfenner		syslog(LOG_ERR,
34996049Sfenner		    "first and last dump headers disagree on %s", device);
35096025Smux		nerr++;
351142359Sobrien		status = STATUS_BAD;
352142359Sobrien		if (force == 0)
353142359Sobrien			goto closefd;
354142359Sobrien	} else {
355142359Sobrien		status = STATUS_GOOD;
35666429Sdes	}
35794580Smarcel
358119734Sdougb	if (checkfor) {
359119734Sdougb		printf("A dump exists on %s\n", device);
360119734Sdougb		close(fd);
361119734Sdougb		exit(0);
362119734Sdougb	}
363119734Sdougb
36496049Sfenner	if (kdhl.panicstring[0])
36596049Sfenner		syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
36696049Sfenner	else
36796049Sfenner		syslog(LOG_ALERT, "reboot");
36894580Smarcel
36996049Sfenner	if (verbose)
37096049Sfenner		printf("Checking for available free space\n");
37196025Smux	if (!check_space(savedir, dumpsize)) {
37296025Smux		nerr++;
37396025Smux		goto closefd;
37496025Smux	}
37596049Sfenner
37696049Sfenner	sprintf(buf, "info.%d", bounds);
37796049Sfenner
37894580Smarcel	/*
379142359Sobrien	 * Create or overwrite any existing dump header files.
38094580Smarcel	 */
38194580Smarcel	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
38293492Sphk	if (fdinfo < 0) {
38396049Sfenner		syslog(LOG_ERR, "%s: %m", buf);
38496025Smux		nerr++;
38594580Smarcel		goto closefd;
3861558Srgrimes	}
38796049Sfenner	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
38896049Sfenner	if (compress) {
38996049Sfenner		sprintf(buf, "vmcore.%d.gz", bounds);
39096049Sfenner		fp = zopen(buf, "w");
39196049Sfenner	} else {
39296049Sfenner		sprintf(buf, "vmcore.%d", bounds);
39396049Sfenner		fp = fopen(buf, "w");
39496049Sfenner	}
39596049Sfenner	if (fp == NULL) {
39696049Sfenner		syslog(LOG_ERR, "%s: %m", buf);
39794580Smarcel		close(fdinfo);
39896025Smux		nerr++;
39994580Smarcel		goto closefd;
40093492Sphk	}
40196049Sfenner	(void)umask(oumask);
40296049Sfenner
40393492Sphk	info = fdopen(fdinfo, "w");
40494580Smarcel
40594580Smarcel	if (verbose)
406142359Sobrien		printheader(stdout, &kdhl, device, bounds, status);
40794580Smarcel
408142359Sobrien	printheader(info, &kdhl, device, bounds, status);
40996049Sfenner	fclose(info);
41094580Smarcel
41196049Sfenner	syslog(LOG_NOTICE, "writing %score to %s",
41296049Sfenner	    compress ? "compressed " : "", buf);
41394580Smarcel
41493492Sphk	while (dumpsize > 0) {
41597746Smarcel		wl = BUFFERSIZE;
41693492Sphk		if (wl > dumpsize)
41793492Sphk			wl = dumpsize;
41896049Sfenner		nr = read(fd, buf, wl);
41996049Sfenner		if (nr != wl) {
42096049Sfenner			if (nr == 0)
42196049Sfenner				syslog(LOG_WARNING,
42296049Sfenner				    "WARNING: EOF on dump device");
42396049Sfenner			else
42496049Sfenner				syslog(LOG_ERR, "read error on %s: %m", device);
42596025Smux			nerr++;
42694580Smarcel			goto closeall;
42767264Sdes		}
42896049Sfenner		if (compress) {
42996049Sfenner			nw = fwrite(buf, 1, wl, fp);
43096049Sfenner		} else {
43196049Sfenner			for (nw = 0; nw < nr; nw = he) {
43296049Sfenner			    /* find a contiguous block of zeroes */
43396049Sfenner			    for (hs = nw; hs < nr; hs += BLOCKSIZE) {
43496049Sfenner				for (he = hs; he < nr && buf[he] == 0; ++he)
43596049Sfenner				    /* nothing */ ;
43696049Sfenner				/* is the hole long enough to matter? */
43796049Sfenner				if (he >= hs + BLOCKSIZE)
43896049Sfenner				    break;
43996049Sfenner			    }
44096049Sfenner
44196049Sfenner			    /* back down to a block boundary */
44296049Sfenner			    he &= BLOCKMASK;
44396049Sfenner
44496049Sfenner			    /*
44596049Sfenner			     * 1) Don't go beyond the end of the buffer.
44696049Sfenner			     * 2) If the end of the buffer is less than
44796049Sfenner			     *    BLOCKSIZE bytes away, we're at the end
44896049Sfenner			     *    of the file, so just grab what's left.
44996049Sfenner			     */
45096049Sfenner			    if (hs + BLOCKSIZE > nr)
45196049Sfenner				hs = he = nr;
45296049Sfenner
45396049Sfenner			    /*
45496049Sfenner			     * At this point, we have a partial ordering:
45596049Sfenner			     *     nw <= hs <= he <= nr
45696049Sfenner			     * If hs > nw, buf[nw..hs] contains non-zero data.
45796049Sfenner			     * If he > hs, buf[hs..he] is all zeroes.
45896049Sfenner			     */
45996049Sfenner			    if (hs > nw)
46096049Sfenner				if (fwrite(buf + nw, hs - nw, 1, fp) != 1)
46196049Sfenner				    break;
46296049Sfenner			    if (he > hs)
463126042Stjr				if (fseeko(fp, he - hs, SEEK_CUR) == -1)
46496049Sfenner				    break;
46596049Sfenner			}
46696049Sfenner		}
46796049Sfenner		if (nw != wl) {
46896049Sfenner			syslog(LOG_ERR,
46996049Sfenner			    "write error on vmcore.%d file: %m", bounds);
47096049Sfenner			syslog(LOG_WARNING,
47196049Sfenner			    "WARNING: vmcore may be incomplete");
47296025Smux			nerr++;
47394580Smarcel			goto closeall;
47493492Sphk		}
47596049Sfenner		if (verbose) {
47696049Sfenner			dmpcnt += wl;
47796103Smarcel			printf("%llu\r", (unsigned long long)dmpcnt);
47896049Sfenner			fflush(stdout);
47996049Sfenner		}
48093492Sphk		dumpsize -= wl;
48167264Sdes	}
48296049Sfenner	if (verbose)
48396049Sfenner		printf("\n");
48496049Sfenner
48596049Sfenner	if (fclose(fp) < 0) {
48696049Sfenner		syslog(LOG_ERR, "error on vmcore.%d: %m", bounds);
48796049Sfenner		nerr++;
48896049Sfenner		goto closeall;
48996049Sfenner	}
49096025Smux	nsaved++;
49194580Smarcel
49294580Smarcel	if (verbose)
49396049Sfenner		printf("dump saved\n");
49494580Smarcel
49596049Sfennernuke:
49694580Smarcel	if (clear || !keep) {
49794580Smarcel		if (verbose)
49896049Sfenner			printf("clearing dump header\n");
49996049Sfenner		memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
50094580Smarcel		lseek(fd, lasthd, SEEK_SET);
50194580Smarcel		error = write(fd, &kdhl, sizeof kdhl);
50294580Smarcel		if (error != sizeof kdhl)
50396049Sfenner			syslog(LOG_ERR,
50496049Sfenner			    "error while clearing the dump header: %m");
50594580Smarcel	}
50694580Smarcel	close(fd);
50794580Smarcel	return;
50894580Smarcel
50996049Sfennercloseall:
51096049Sfenner	fclose(fp);
51194580Smarcel
51296049Sfennerclosefd:
51394580Smarcel	close(fd);
5141558Srgrimes}
5151558Srgrimes
51693492Sphkstatic void
51793492Sphkusage(void)
5181558Srgrimes{
519141611Sru	fprintf(stderr, "%s\n%s\n%s\n",
520141611Sru	    "usage: savecore -c",
521141611Sru	    "       savecore -C [-v] [directory device]",
522141611Sru	    "       savecore [-fkvz] [directory [device ...]]");
52393492Sphk	exit (1);
5241558Srgrimes}
5251558Srgrimes
52618914Sfennerint
52793492Sphkmain(int argc, char **argv)
5281558Srgrimes{
52993492Sphk	int i, ch, error;
53093492Sphk	struct fstab *fsp;
53196025Smux	char *savedir;
5321558Srgrimes
533142359Sobrien	checkfor = compress = clear = force = keep = verbose = 0;
534142359Sobrien	nfound = nsaved = nerr = 0;
535142359Sobrien
53696049Sfenner	openlog("savecore", LOG_PERROR, LOG_DAEMON);
53796049Sfenner
53896025Smux	savedir = strdup(".");
53996049Sfenner	if (savedir == NULL) {
54096049Sfenner		syslog(LOG_ERR, "Cannot allocate memory");
54196049Sfenner		exit(1);
54296049Sfenner	}
543126347Smaxim	while ((ch = getopt(argc, argv, "Ccfkvz")) != -1)
54493492Sphk		switch(ch) {
545119734Sdougb		case 'C':
546119734Sdougb			checkfor = 1;
547119734Sdougb			break;
54893492Sphk		case 'c':
54994580Smarcel			clear = 1;
55094580Smarcel			break;
55194580Smarcel		case 'k':
55294580Smarcel			keep = 1;
55394580Smarcel			break;
55493492Sphk		case 'v':
555142359Sobrien			verbose++;
55694580Smarcel			break;
55793492Sphk		case 'f':
55894580Smarcel			force = 1;
55994580Smarcel			break;
56096049Sfenner		case 'z':
56196049Sfenner			compress = 1;
56296049Sfenner			break;
56393492Sphk		case '?':
56493492Sphk		default:
56593492Sphk			usage();
56693492Sphk		}
567119734Sdougb	if (checkfor && (clear || force || keep))
568119734Sdougb		usage();
56993492Sphk	argc -= optind;
57093492Sphk	argv += optind;
57193492Sphk	if (argc >= 1) {
57293492Sphk		error = chdir(argv[0]);
57396049Sfenner		if (error) {
57496049Sfenner			syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
57596049Sfenner			exit(1);
57696049Sfenner		}
57796025Smux		savedir = argv[0];
57893492Sphk		argc--;
57993492Sphk		argv++;
5801558Srgrimes	}
58193492Sphk	if (argc == 0) {
58293492Sphk		for (;;) {
58393492Sphk			fsp = getfsent();
58493492Sphk			if (fsp == NULL)
58593492Sphk				break;
58693492Sphk			if (strcmp(fsp->fs_vfstype, "swap") &&
58793492Sphk			    strcmp(fsp->fs_vfstype, "dump"))
58893492Sphk				continue;
58996025Smux			DoFile(savedir, fsp->fs_spec);
59093492Sphk		}
59193492Sphk	} else {
59293492Sphk		for (i = 0; i < argc; i++)
59396025Smux			DoFile(savedir, argv[i]);
5941558Srgrimes	}
59594580Smarcel
59694580Smarcel	/* Emit minimal output. */
597119734Sdougb	if (nfound == 0) {
598119734Sdougb		if (checkfor) {
599119734Sdougb			printf("No dump exists\n");
600119734Sdougb			exit(1);
601119734Sdougb		}
60296049Sfenner		syslog(LOG_WARNING, "no dumps found");
603119734Sdougb	}
60496025Smux	else if (nsaved == 0) {
60596025Smux		if (nerr != 0)
60696049Sfenner			syslog(LOG_WARNING, "unsaved dumps found but not saved");
60796025Smux		else
60896049Sfenner			syslog(LOG_WARNING, "no unsaved dumps found");
60996025Smux	}
61094580Smarcel
61193492Sphk	return (0);
6121558Srgrimes}
613