savecore.c revision 150105
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 150105 2005-09-13 19:15:28Z rwatson $");
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
91142533Sobrien#define	STATUS_BAD	0
92142533Sobrien#define	STATUS_GOOD	1
93142533Sobrien#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:
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	}
19796025Smux 	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 */
21296025Smux 	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
22893492Sphkstatic void
229146763SdelphijDoFile(const char *savedir, const char *device)
2301558Srgrimes{
23197340Smarcel	static char *buf = NULL;
23294580Smarcel	struct kerneldumpheader kdhf, kdhl;
23396049Sfenner	off_t mediasize, dumpsize, firsthd, lasthd, dmpcnt;
23496049Sfenner	FILE *info, *fp;
235142533Sobrien	mode_t oumask;
23696049Sfenner	int fd, fdinfo, error, wl;
237139975Sdelphij	int nr, nw, hs, he = 0;
238142359Sobrien	int bounds, status;
23994580Smarcel	u_int sectorsize;
2408871Srgrimes
241142359Sobrien	bounds = getbounds();
24296049Sfenner	dmpcnt = 0;
24396049Sfenner	mediasize = 0;
244142359Sobrien	status = STATUS_UNKNOWN;
24596049Sfenner
24697340Smarcel	if (buf == NULL) {
24797746Smarcel		buf = malloc(BUFFERSIZE);
24897340Smarcel		if (buf == NULL) {
24997340Smarcel			syslog(LOG_ERR, "%m");
25097340Smarcel			return;
25197340Smarcel		}
25297340Smarcel	}
25397340Smarcel
25494580Smarcel	if (verbose)
25596049Sfenner		printf("checking for kernel dump on device %s\n", device);
25694580Smarcel
25794580Smarcel	fd = open(device, O_RDWR);
25893492Sphk	if (fd < 0) {
25996049Sfenner		syslog(LOG_ERR, "%s: %m", device);
26093492Sphk		return;
26147095Sluoqi	}
26297340Smarcel
26393492Sphk	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
26493492Sphk	if (!error)
26593492Sphk		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
26693492Sphk	if (error) {
26796049Sfenner		syslog(LOG_ERR,
26896049Sfenner		    "couldn't find media and/or sector size of %s: %m", device);
26994580Smarcel		goto closefd;
2701558Srgrimes	}
27194580Smarcel
27294580Smarcel	if (verbose) {
27396049Sfenner		printf("mediasize = %lld\n", (long long)mediasize);
27496049Sfenner		printf("sectorsize = %u\n", sectorsize);
27594580Smarcel	}
27694580Smarcel
27793492Sphk	lasthd = mediasize - sectorsize;
27893492Sphk	lseek(fd, lasthd, SEEK_SET);
27993492Sphk	error = read(fd, &kdhl, sizeof kdhl);
28093492Sphk	if (error != sizeof kdhl) {
28196049Sfenner		syslog(LOG_ERR,
28296049Sfenner		    "error reading last dump header at offset %lld in %s: %m",
28394580Smarcel		    (long long)lasthd, device);
28494580Smarcel		goto closefd;
2851558Srgrimes	}
28693492Sphk	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
28794580Smarcel		if (verbose)
28896049Sfenner			printf("magic mismatch on last dump header on %s\n",
28994580Smarcel			    device);
29096049Sfenner
291142359Sobrien		status = STATUS_BAD;
29296049Sfenner		if (force == 0)
29396049Sfenner			goto closefd;
29496049Sfenner
29596049Sfenner		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
29696049Sfenner			    sizeof kdhl.magic) == 0) {
29796049Sfenner			if (verbose)
29896049Sfenner				printf("forcing magic on %s\n", device);
29996049Sfenner			memcpy(kdhl.magic, KERNELDUMPMAGIC,
30096049Sfenner			    sizeof kdhl.magic);
30196049Sfenner		} else {
30296049Sfenner			syslog(LOG_ERR, "unable to force dump - bad magic");
30396049Sfenner			goto closefd;
30496049Sfenner		}
3051558Srgrimes	}
30693717Smarcel	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
30796049Sfenner		syslog(LOG_ERR,
30896049Sfenner		    "unknown version (%d) in last dump header on %s",
30994580Smarcel		    dtoh32(kdhl.version), device);
310142359Sobrien
311142359Sobrien		status = STATUS_BAD;
312142359Sobrien		if (force == 0)
313142359Sobrien			goto closefd;
31466429Sdes	}
31594580Smarcel
31694580Smarcel	nfound++;
31794580Smarcel	if (clear)
31894580Smarcel		goto nuke;
31994580Smarcel
32094580Smarcel	if (kerneldump_parity(&kdhl)) {
32196049Sfenner		syslog(LOG_ERR,
32296049Sfenner		    "parity error on last dump header on %s", device);
32396025Smux		nerr++;
324142359Sobrien		status = STATUS_BAD;
325142359Sobrien		if (force == 0)
326142359Sobrien			goto closefd;
32794580Smarcel	}
32893717Smarcel	dumpsize = dtoh64(kdhl.dumplength);
32993717Smarcel	firsthd = lasthd - dumpsize - sizeof kdhf;
33093492Sphk	lseek(fd, firsthd, SEEK_SET);
33193492Sphk	error = read(fd, &kdhf, sizeof kdhf);
33293492Sphk	if (error != sizeof kdhf) {
33396049Sfenner		syslog(LOG_ERR,
33496049Sfenner		    "error reading first dump header at offset %lld in %s: %m",
33594580Smarcel		    (long long)firsthd, device);
33696025Smux		nerr++;
33794580Smarcel		goto closefd;
3381558Srgrimes	}
339142359Sobrien
340142359Sobrien	if (verbose >= 2) {
341142359Sobrien		printf("First dump headers:\n");
342142359Sobrien		printheader(stdout, &kdhf, device, bounds, -1);
343142359Sobrien
344142359Sobrien		printf("\nLast dump headers:\n");
345142359Sobrien		printheader(stdout, &kdhl, device, bounds, -1);
346142359Sobrien		printf("\n");
347142359Sobrien	}
348142359Sobrien
34993492Sphk	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
35096049Sfenner		syslog(LOG_ERR,
35196049Sfenner		    "first and last dump headers disagree on %s", device);
35296025Smux		nerr++;
353142359Sobrien		status = STATUS_BAD;
354142359Sobrien		if (force == 0)
355142359Sobrien			goto closefd;
356142359Sobrien	} else {
357142359Sobrien		status = STATUS_GOOD;
35866429Sdes	}
35994580Smarcel
360119734Sdougb	if (checkfor) {
361119734Sdougb		printf("A dump exists on %s\n", device);
362119734Sdougb		close(fd);
363119734Sdougb		exit(0);
364119734Sdougb	}
365119734Sdougb
36696049Sfenner	if (kdhl.panicstring[0])
36796049Sfenner		syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
36896049Sfenner	else
36996049Sfenner		syslog(LOG_ALERT, "reboot");
37094580Smarcel
37196049Sfenner	if (verbose)
37296049Sfenner		printf("Checking for available free space\n");
37396025Smux	if (!check_space(savedir, dumpsize)) {
37496025Smux		nerr++;
37596025Smux		goto closefd;
37696025Smux	}
37796049Sfenner
378147506Sdwhite	writebounds(bounds + 1);
379147506Sdwhite
38096049Sfenner	sprintf(buf, "info.%d", bounds);
38196049Sfenner
38294580Smarcel	/*
383142359Sobrien	 * Create or overwrite any existing dump header files.
38494580Smarcel	 */
38594580Smarcel	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
38693492Sphk	if (fdinfo < 0) {
38796049Sfenner		syslog(LOG_ERR, "%s: %m", buf);
38896025Smux		nerr++;
38994580Smarcel		goto closefd;
3901558Srgrimes	}
39196049Sfenner	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
39296049Sfenner	if (compress) {
39396049Sfenner		sprintf(buf, "vmcore.%d.gz", bounds);
39496049Sfenner		fp = zopen(buf, "w");
39596049Sfenner	} else {
39696049Sfenner		sprintf(buf, "vmcore.%d", bounds);
39796049Sfenner		fp = fopen(buf, "w");
39896049Sfenner	}
39996049Sfenner	if (fp == NULL) {
40096049Sfenner		syslog(LOG_ERR, "%s: %m", buf);
40194580Smarcel		close(fdinfo);
40296025Smux		nerr++;
40394580Smarcel		goto closefd;
40493492Sphk	}
40596049Sfenner	(void)umask(oumask);
40696049Sfenner
40793492Sphk	info = fdopen(fdinfo, "w");
40894580Smarcel
40994580Smarcel	if (verbose)
410142359Sobrien		printheader(stdout, &kdhl, device, bounds, status);
41194580Smarcel
412142359Sobrien	printheader(info, &kdhl, device, bounds, status);
41396049Sfenner	fclose(info);
41494580Smarcel
41596049Sfenner	syslog(LOG_NOTICE, "writing %score to %s",
41696049Sfenner	    compress ? "compressed " : "", buf);
41794580Smarcel
41893492Sphk	while (dumpsize > 0) {
41997746Smarcel		wl = BUFFERSIZE;
42093492Sphk		if (wl > dumpsize)
42193492Sphk			wl = dumpsize;
42296049Sfenner		nr = read(fd, buf, wl);
42396049Sfenner		if (nr != wl) {
42496049Sfenner			if (nr == 0)
42596049Sfenner				syslog(LOG_WARNING,
42696049Sfenner				    "WARNING: EOF on dump device");
42796049Sfenner			else
42896049Sfenner				syslog(LOG_ERR, "read error on %s: %m", device);
42996025Smux			nerr++;
43094580Smarcel			goto closeall;
43167264Sdes		}
43296049Sfenner		if (compress) {
43396049Sfenner			nw = fwrite(buf, 1, wl, fp);
43496049Sfenner		} else {
43596049Sfenner			for (nw = 0; nw < nr; nw = he) {
436142533Sobrien				/* find a contiguous block of zeroes */
437142533Sobrien				for (hs = nw; hs < nr; hs += BLOCKSIZE) {
438142533Sobrien					for (he = hs; he < nr && buf[he] == 0;
439142533Sobrien					    ++he)
440142533Sobrien						/* nothing */ ;
441142533Sobrien					/* is the hole long enough to matter? */
442142533Sobrien					if (he >= hs + BLOCKSIZE)
443142533Sobrien						break;
444142533Sobrien				}
44596049Sfenner
446142533Sobrien				/* back down to a block boundary */
447142533Sobrien				he &= BLOCKMASK;
44896049Sfenner
449142533Sobrien				/*
450142533Sobrien				 * 1) Don't go beyond the end of the buffer.
451142533Sobrien				 * 2) If the end of the buffer is less than
452142533Sobrien				 *    BLOCKSIZE bytes away, we're at the end
453142533Sobrien				 *    of the file, so just grab what's left.
454142533Sobrien				 */
455142533Sobrien				if (hs + BLOCKSIZE > nr)
456142533Sobrien					hs = he = nr;
457142533Sobrien
458142533Sobrien				/*
459142533Sobrien				 * At this point, we have a partial ordering:
460142533Sobrien				 *     nw <= hs <= he <= nr
461142533Sobrien				 * If hs > nw, buf[nw..hs] contains non-zero data.
462142533Sobrien				 * If he > hs, buf[hs..he] is all zeroes.
463142533Sobrien				 */
464142533Sobrien				if (hs > nw)
465142533Sobrien					if (fwrite(buf + nw, hs - nw, 1, fp)
466142533Sobrien					    != 1)
467142533Sobrien					break;
468142533Sobrien				if (he > hs)
469142533Sobrien					if (fseeko(fp, he - hs, SEEK_CUR) == -1)
470142533Sobrien						break;
47196049Sfenner			}
47296049Sfenner		}
47396049Sfenner		if (nw != wl) {
47496049Sfenner			syslog(LOG_ERR,
47596049Sfenner			    "write error on vmcore.%d file: %m", bounds);
47696049Sfenner			syslog(LOG_WARNING,
47796049Sfenner			    "WARNING: vmcore may be incomplete");
47896025Smux			nerr++;
47994580Smarcel			goto closeall;
48093492Sphk		}
48196049Sfenner		if (verbose) {
48296049Sfenner			dmpcnt += wl;
48396103Smarcel			printf("%llu\r", (unsigned long long)dmpcnt);
48496049Sfenner			fflush(stdout);
48596049Sfenner		}
48693492Sphk		dumpsize -= wl;
48767264Sdes	}
48896049Sfenner	if (verbose)
48996049Sfenner		printf("\n");
49096049Sfenner
49196049Sfenner	if (fclose(fp) < 0) {
49296049Sfenner		syslog(LOG_ERR, "error on vmcore.%d: %m", bounds);
49396049Sfenner		nerr++;
49496049Sfenner		goto closeall;
49596049Sfenner	}
49696025Smux	nsaved++;
49794580Smarcel
49894580Smarcel	if (verbose)
49996049Sfenner		printf("dump saved\n");
50094580Smarcel
50196049Sfennernuke:
50294580Smarcel	if (clear || !keep) {
50394580Smarcel		if (verbose)
50496049Sfenner			printf("clearing dump header\n");
50596049Sfenner		memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
50694580Smarcel		lseek(fd, lasthd, SEEK_SET);
50794580Smarcel		error = write(fd, &kdhl, sizeof kdhl);
50894580Smarcel		if (error != sizeof kdhl)
50996049Sfenner			syslog(LOG_ERR,
51096049Sfenner			    "error while clearing the dump header: %m");
51194580Smarcel	}
51294580Smarcel	close(fd);
51394580Smarcel	return;
51494580Smarcel
51596049Sfennercloseall:
51696049Sfenner	fclose(fp);
51794580Smarcel
51896049Sfennerclosefd:
51994580Smarcel	close(fd);
5201558Srgrimes}
5211558Srgrimes
52293492Sphkstatic void
52393492Sphkusage(void)
5241558Srgrimes{
525141611Sru	fprintf(stderr, "%s\n%s\n%s\n",
526141611Sru	    "usage: savecore -c",
527141611Sru	    "       savecore -C [-v] [directory device]",
528141611Sru	    "       savecore [-fkvz] [directory [device ...]]");
52993492Sphk	exit (1);
5301558Srgrimes}
5311558Srgrimes
53218914Sfennerint
53393492Sphkmain(int argc, char **argv)
5341558Srgrimes{
535146763Sdelphij	const char *savedir = ".";
536142533Sobrien	struct fstab *fsp;
53793492Sphk	int i, ch, error;
5381558Srgrimes
539142359Sobrien	checkfor = compress = clear = force = keep = verbose = 0;
540142359Sobrien	nfound = nsaved = nerr = 0;
541142359Sobrien
54296049Sfenner	openlog("savecore", LOG_PERROR, LOG_DAEMON);
54396049Sfenner
544126347Smaxim	while ((ch = getopt(argc, argv, "Ccfkvz")) != -1)
54593492Sphk		switch(ch) {
546119734Sdougb		case 'C':
547119734Sdougb			checkfor = 1;
548119734Sdougb			break;
54993492Sphk		case 'c':
55094580Smarcel			clear = 1;
55194580Smarcel			break;
55294580Smarcel		case 'k':
55394580Smarcel			keep = 1;
55494580Smarcel			break;
55593492Sphk		case 'v':
556142359Sobrien			verbose++;
55794580Smarcel			break;
55893492Sphk		case 'f':
55994580Smarcel			force = 1;
56094580Smarcel			break;
56196049Sfenner		case 'z':
56296049Sfenner			compress = 1;
56396049Sfenner			break;
56493492Sphk		case '?':
56593492Sphk		default:
56693492Sphk			usage();
56793492Sphk		}
568119734Sdougb	if (checkfor && (clear || force || keep))
569119734Sdougb		usage();
57093492Sphk	argc -= optind;
57193492Sphk	argv += optind;
57293492Sphk	if (argc >= 1) {
57393492Sphk		error = chdir(argv[0]);
57496049Sfenner		if (error) {
57596049Sfenner			syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
57696049Sfenner			exit(1);
57796049Sfenner		}
57896025Smux		savedir = argv[0];
57993492Sphk		argc--;
58093492Sphk		argv++;
5811558Srgrimes	}
58293492Sphk	if (argc == 0) {
58393492Sphk		for (;;) {
58493492Sphk			fsp = getfsent();
58593492Sphk			if (fsp == NULL)
58693492Sphk				break;
58793492Sphk			if (strcmp(fsp->fs_vfstype, "swap") &&
58893492Sphk			    strcmp(fsp->fs_vfstype, "dump"))
58993492Sphk				continue;
59096025Smux			DoFile(savedir, fsp->fs_spec);
59193492Sphk		}
59293492Sphk	} else {
59393492Sphk		for (i = 0; i < argc; i++)
59496025Smux			DoFile(savedir, argv[i]);
5951558Srgrimes	}
59694580Smarcel
59794580Smarcel	/* Emit minimal output. */
598119734Sdougb	if (nfound == 0) {
599119734Sdougb		if (checkfor) {
600119734Sdougb			printf("No dump exists\n");
601119734Sdougb			exit(1);
602119734Sdougb		}
60396049Sfenner		syslog(LOG_WARNING, "no dumps found");
604119734Sdougb	}
60596025Smux	else if (nsaved == 0) {
60696025Smux		if (nerr != 0)
60796049Sfenner			syslog(LOG_WARNING, "unsaved dumps found but not saved");
60896025Smux		else
60996049Sfenner			syslog(LOG_WARNING, "no unsaved dumps found");
61096025Smux	}
61194580Smarcel
61293492Sphk	return (0);
6131558Srgrimes}
614