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$");
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 */
93244320Spjdstatic int maxdumps;
9494580Smarcel
9596049Sfennerextern FILE *zopen(const char *, const char *);
9696049Sfenner
97196528Slulfstatic sig_atomic_t got_siginfo;
98196528Slulfstatic void infohandler(int);
99196528Slulf
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);
110150818Smaxim	fprintf(f, "  Architecture Version: %u\n",
111150818Smaxim	    dtoh32(h->architectureversion));
11293717Smarcel	dumplen = dtoh64(h->dumplength);
113142359Sobrien	fprintf(f, "  Dump Length: %lldB (%lld MB)\n", (long long)dumplen,
11493717Smarcel	    (long long)(dumplen >> 20));
11593717Smarcel	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
11693717Smarcel	t = dtoh64(h->dumptime);
11793492Sphk	fprintf(f, "  Dumptime: %s", ctime(&t));
11893492Sphk	fprintf(f, "  Hostname: %s\n", h->hostname);
119142359Sobrien	fprintf(f, "  Magic: %s\n", h->magic);
120142359Sobrien	fprintf(f, "  Version String: %s", h->versionstring);
121142359Sobrien	fprintf(f, "  Panic String: %s\n", h->panicstring);
122142359Sobrien	fprintf(f, "  Dump Parity: %u\n", h->parity);
12396049Sfenner	fprintf(f, "  Bounds: %d\n", bounds);
124142359Sobrien
125142359Sobrien	switch(status) {
126142359Sobrien	case STATUS_BAD:
127142533Sobrien		stat_str = "bad";
128142533Sobrien		break;
129142359Sobrien	case STATUS_GOOD:
130142533Sobrien		stat_str = "good";
131142533Sobrien		break;
132142359Sobrien	default:
133142533Sobrien		stat_str = "unknown";
134142359Sobrien	}
135142359Sobrien	fprintf(f, "  Dump Status: %s\n", stat_str);
13695039Sphk	fflush(f);
1371558Srgrimes}
1381558Srgrimes
13996049Sfennerstatic int
14096049Sfennergetbounds(void) {
14196049Sfenner	FILE *fp;
14296049Sfenner	char buf[6];
14396049Sfenner	int ret;
14496049Sfenner
14596049Sfenner	ret = 0;
14696049Sfenner
14796049Sfenner	if ((fp = fopen("bounds", "r")) == NULL) {
148150105Srwatson		if (verbose)
149150105Srwatson			printf("unable to open bounds file, using 0\n");
150147506Sdwhite		return (ret);
15196049Sfenner	}
15296049Sfenner
15396049Sfenner	if (fgets(buf, sizeof buf, fp) == NULL) {
15496049Sfenner		syslog(LOG_WARNING, "unable to read from bounds, using 0");
15596049Sfenner		fclose(fp);
156147506Sdwhite		return (ret);
15796049Sfenner	}
15896049Sfenner
15996049Sfenner	errno = 0;
16096049Sfenner	ret = (int)strtol(buf, NULL, 10);
16196049Sfenner	if (ret == 0 && (errno == EINVAL || errno == ERANGE))
16296049Sfenner		syslog(LOG_WARNING, "invalid value found in bounds, using 0");
163147506Sdwhite	return (ret);
164147506Sdwhite}
16596049Sfenner
166147506Sdwhitestatic void
167147506Sdwhitewritebounds(int bounds) {
168147506Sdwhite	FILE *fp;
16996049Sfenner
17096049Sfenner	if ((fp = fopen("bounds", "w")) == NULL) {
17196049Sfenner		syslog(LOG_WARNING, "unable to write to bounds file: %m");
172147506Sdwhite		return;
17396049Sfenner	}
17496049Sfenner
17596049Sfenner	if (verbose)
176147506Sdwhite		printf("bounds number: %d\n", bounds);
17796049Sfenner
178147506Sdwhite	fprintf(fp, "%d\n", bounds);
17996049Sfenner	fclose(fp);
18096049Sfenner}
18196049Sfenner
182244320Spjdstatic off_t
183244320Spjdfile_size(const char *path)
184244320Spjd{
185244320Spjd	struct stat sb;
186244320Spjd
187244320Spjd	/* Ignore all errors, those file may not exists. */
188244320Spjd	if (stat(path, &sb) == -1)
189244320Spjd		return (0);
190244320Spjd	return (sb.st_size);
191244320Spjd}
192244320Spjd
193244320Spjdstatic off_t
194244320Spjdsaved_dump_size(int bounds)
195244320Spjd{
196244320Spjd	static char path[PATH_MAX];
197244320Spjd	off_t dumpsize;
198244320Spjd
199244320Spjd	dumpsize = 0;
200244320Spjd
201244320Spjd	(void)snprintf(path, sizeof(path), "info.%d", bounds);
202244320Spjd	dumpsize += file_size(path);
203244320Spjd	(void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
204244320Spjd	dumpsize += file_size(path);
205244320Spjd	(void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
206244320Spjd	dumpsize += file_size(path);
207244320Spjd	(void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
208244320Spjd	dumpsize += file_size(path);
209244320Spjd	(void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
210244320Spjd	dumpsize += file_size(path);
211244320Spjd
212244320Spjd	return (dumpsize);
213244320Spjd}
214244320Spjd
215244320Spjdstatic void
216244320Spjdsaved_dump_remove(int bounds)
217244320Spjd{
218244320Spjd	static char path[PATH_MAX];
219244320Spjd
220244320Spjd	(void)snprintf(path, sizeof(path), "info.%d", bounds);
221244320Spjd	(void)unlink(path);
222244320Spjd	(void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
223244320Spjd	(void)unlink(path);
224244320Spjd	(void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
225244320Spjd	(void)unlink(path);
226244320Spjd	(void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
227244320Spjd	(void)unlink(path);
228244320Spjd	(void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
229244320Spjd	(void)unlink(path);
230244320Spjd}
231244320Spjd
232244321Spjdstatic void
233244321Spjdsymlinks_remove(void)
234244321Spjd{
235244321Spjd
236244321Spjd	(void)unlink("info.last");
237244321Spjd	(void)unlink("vmcore.last");
238244321Spjd	(void)unlink("vmcore.last.gz");
239244321Spjd	(void)unlink("textdump.tar.last");
240244321Spjd	(void)unlink("textdump.tar.last.gz");
241244321Spjd}
242244321Spjd
24396025Smux/*
24496025Smux * Check that sufficient space is available on the disk that holds the
24596025Smux * save directory.
24696025Smux */
24796025Smuxstatic int
248244320Spjdcheck_space(const char *savedir, off_t dumpsize, int bounds)
24996025Smux{
25096025Smux	FILE *fp;
25196049Sfenner	off_t minfree, spacefree, totfree, needed;
25296025Smux	struct statfs fsbuf;
253244319Spjd	char buf[100];
25493717Smarcel
255244319Spjd	if (statfs(".", &fsbuf) < 0) {
25696049Sfenner		syslog(LOG_ERR, "%s: %m", savedir);
25796049Sfenner		exit(1);
25896049Sfenner	}
259244215Spjd	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
26096025Smux	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
26196025Smux
262244319Spjd	if ((fp = fopen("minfree", "r")) == NULL)
26396025Smux		minfree = 0;
26496025Smux	else {
26596025Smux		if (fgets(buf, sizeof(buf), fp) == NULL)
26696025Smux			minfree = 0;
26796025Smux		else
26896025Smux			minfree = atoi(buf);
26996025Smux		(void)fclose(fp);
27096025Smux	}
27196025Smux
27296049Sfenner	needed = dumpsize / 1024 + 2;	/* 2 for info file */
273244320Spjd	needed -= saved_dump_size(bounds);
274244320Spjd	if ((minfree > 0 ? spacefree : totfree) - needed < minfree) {
27596049Sfenner		syslog(LOG_WARNING,
27696049Sfenner	"no dump, not enough free space on device (%lld available, need %lld)",
27796025Smux		    (long long)(minfree > 0 ? spacefree : totfree),
27896025Smux		    (long long)needed);
27996025Smux		return (0);
28096025Smux	}
28196025Smux	if (spacefree - needed < 0)
28296049Sfenner		syslog(LOG_WARNING,
28396049Sfenner		    "dump performed, but free space threshold crossed");
28496025Smux	return (1);
28596025Smux}
28696025Smux
28796049Sfenner#define BLOCKSIZE (1<<12)
28896049Sfenner#define BLOCKMASK (~(BLOCKSIZE-1))
28996025Smux
290174923Srwatsonstatic int
291174923SrwatsonDoRegularFile(int fd, off_t dumpsize, char *buf, const char *device,
292174923Srwatson    const char *filename, FILE *fp)
293174923Srwatson{
294174923Srwatson	int he, hs, nr, nw, wl;
295196528Slulf	off_t dmpcnt, origsize;
296174923Srwatson
297174923Srwatson	dmpcnt = 0;
298196528Slulf	origsize = dumpsize;
299174923Srwatson	he = 0;
300174923Srwatson	while (dumpsize > 0) {
301174923Srwatson		wl = BUFFERSIZE;
302174923Srwatson		if (wl > dumpsize)
303174923Srwatson			wl = dumpsize;
304174923Srwatson		nr = read(fd, buf, wl);
305174923Srwatson		if (nr != wl) {
306174923Srwatson			if (nr == 0)
307174923Srwatson				syslog(LOG_WARNING,
308174923Srwatson				    "WARNING: EOF on dump device");
309174923Srwatson			else
310174923Srwatson				syslog(LOG_ERR, "read error on %s: %m", device);
311174923Srwatson			nerr++;
312174923Srwatson			return (-1);
313174923Srwatson		}
314174923Srwatson		if (compress) {
315174923Srwatson			nw = fwrite(buf, 1, wl, fp);
316174923Srwatson		} else {
317174923Srwatson			for (nw = 0; nw < nr; nw = he) {
318174923Srwatson				/* find a contiguous block of zeroes */
319174923Srwatson				for (hs = nw; hs < nr; hs += BLOCKSIZE) {
320174923Srwatson					for (he = hs; he < nr && buf[he] == 0;
321174923Srwatson					    ++he)
322174923Srwatson						/* nothing */ ;
323174923Srwatson					/* is the hole long enough to matter? */
324174923Srwatson					if (he >= hs + BLOCKSIZE)
325174923Srwatson						break;
326174923Srwatson				}
327244215Spjd
328174923Srwatson				/* back down to a block boundary */
329174923Srwatson				he &= BLOCKMASK;
330174923Srwatson
331174923Srwatson				/*
332174923Srwatson				 * 1) Don't go beyond the end of the buffer.
333174923Srwatson				 * 2) If the end of the buffer is less than
334174923Srwatson				 *    BLOCKSIZE bytes away, we're at the end
335174923Srwatson				 *    of the file, so just grab what's left.
336174923Srwatson				 */
337174923Srwatson				if (hs + BLOCKSIZE > nr)
338174923Srwatson					hs = he = nr;
339174923Srwatson
340174923Srwatson				/*
341174923Srwatson				 * At this point, we have a partial ordering:
342174923Srwatson				 *     nw <= hs <= he <= nr
343174923Srwatson				 * If hs > nw, buf[nw..hs] contains non-zero data.
344174923Srwatson				 * If he > hs, buf[hs..he] is all zeroes.
345174923Srwatson				 */
346174923Srwatson				if (hs > nw)
347174923Srwatson					if (fwrite(buf + nw, hs - nw, 1, fp)
348174923Srwatson					    != 1)
349174923Srwatson					break;
350174923Srwatson				if (he > hs)
351174923Srwatson					if (fseeko(fp, he - hs, SEEK_CUR) == -1)
352174923Srwatson						break;
353174923Srwatson			}
354174923Srwatson		}
355174923Srwatson		if (nw != wl) {
356174923Srwatson			syslog(LOG_ERR,
357174923Srwatson			    "write error on %s file: %m", filename);
358174923Srwatson			syslog(LOG_WARNING,
359174923Srwatson			    "WARNING: vmcore may be incomplete");
360174923Srwatson			nerr++;
361174923Srwatson			return (-1);
362174923Srwatson		}
363174923Srwatson		if (verbose) {
364174923Srwatson			dmpcnt += wl;
365174923Srwatson			printf("%llu\r", (unsigned long long)dmpcnt);
366174923Srwatson			fflush(stdout);
367174923Srwatson		}
368174923Srwatson		dumpsize -= wl;
369196528Slulf		if (got_siginfo) {
370196528Slulf			printf("%s %.1lf%%\n", filename, (100.0 - (100.0 *
371196528Slulf			    (double)dumpsize / (double)origsize)));
372196528Slulf			got_siginfo = 0;
373196528Slulf		}
374174923Srwatson	}
375174923Srwatson	return (0);
376174923Srwatson}
377174923Srwatson
378174923Srwatson/*
379174923Srwatson * Specialized version of dump-reading logic for use with textdumps, which
380174923Srwatson * are written backwards from the end of the partition, and must be reversed
381174923Srwatson * before being written to the file.  Textdumps are small, so do a bit less
382174923Srwatson * work to optimize/sparsify.
383174923Srwatson */
384174923Srwatsonstatic int
385174923SrwatsonDoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf,
386174923Srwatson    const char *device, const char *filename, FILE *fp)
387174923Srwatson{
388174923Srwatson	int nr, nw, wl;
389174923Srwatson	off_t dmpcnt, totsize;
390174923Srwatson
391174923Srwatson	totsize = dumpsize;
392174923Srwatson	dmpcnt = 0;
393174923Srwatson	wl = 512;
394174923Srwatson	if ((dumpsize % wl) != 0) {
395174923Srwatson		syslog(LOG_ERR, "textdump uneven multiple of 512 on %s",
396174923Srwatson		    device);
397174923Srwatson		nerr++;
398174923Srwatson		return (-1);
399174923Srwatson	}
400174923Srwatson	while (dumpsize > 0) {
401174923Srwatson		nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl);
402174923Srwatson		if (nr != wl) {
403174923Srwatson			if (nr == 0)
404174923Srwatson				syslog(LOG_WARNING,
405174923Srwatson				    "WARNING: EOF on dump device");
406174923Srwatson			else
407174923Srwatson				syslog(LOG_ERR, "read error on %s: %m", device);
408174923Srwatson			nerr++;
409174923Srwatson			return (-1);
410174923Srwatson		}
411174923Srwatson		nw = fwrite(buf, 1, wl, fp);
412174923Srwatson		if (nw != wl) {
413174923Srwatson			syslog(LOG_ERR,
414174923Srwatson			    "write error on %s file: %m", filename);
415174923Srwatson			syslog(LOG_WARNING,
416174923Srwatson			    "WARNING: textdump may be incomplete");
417174923Srwatson			nerr++;
418174923Srwatson			return (-1);
419174923Srwatson		}
420174923Srwatson		if (verbose) {
421174923Srwatson			dmpcnt += wl;
422174923Srwatson			printf("%llu\r", (unsigned long long)dmpcnt);
423174923Srwatson			fflush(stdout);
424174923Srwatson		}
425174923Srwatson		dumpsize -= wl;
426174923Srwatson	}
427174923Srwatson	return (0);
428174923Srwatson}
429174923Srwatson
43093492Sphkstatic void
431146763SdelphijDoFile(const char *savedir, const char *device)
4321558Srgrimes{
433244321Spjd	static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX];
43497340Smarcel	static char *buf = NULL;
43594580Smarcel	struct kerneldumpheader kdhf, kdhl;
436174923Srwatson	off_t mediasize, dumpsize, firsthd, lasthd;
43796049Sfenner	FILE *info, *fp;
438142533Sobrien	mode_t oumask;
439174923Srwatson	int fd, fdinfo, error;
440142359Sobrien	int bounds, status;
44194580Smarcel	u_int sectorsize;
442174923Srwatson	int istextdump;
4438871Srgrimes
444142359Sobrien	bounds = getbounds();
44596049Sfenner	mediasize = 0;
446142359Sobrien	status = STATUS_UNKNOWN;
44796049Sfenner
448244320Spjd	if (maxdumps > 0 && bounds == maxdumps)
449244320Spjd		bounds = 0;
450244320Spjd
45197340Smarcel	if (buf == NULL) {
45297746Smarcel		buf = malloc(BUFFERSIZE);
45397340Smarcel		if (buf == NULL) {
45497340Smarcel			syslog(LOG_ERR, "%m");
45597340Smarcel			return;
45697340Smarcel		}
45797340Smarcel	}
45897340Smarcel
45994580Smarcel	if (verbose)
46096049Sfenner		printf("checking for kernel dump on device %s\n", device);
46194580Smarcel
462244216Spjd	fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR);
46393492Sphk	if (fd < 0) {
46496049Sfenner		syslog(LOG_ERR, "%s: %m", device);
46593492Sphk		return;
46647095Sluoqi	}
46797340Smarcel
46893492Sphk	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
46993492Sphk	if (!error)
47093492Sphk		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
47193492Sphk	if (error) {
47296049Sfenner		syslog(LOG_ERR,
47396049Sfenner		    "couldn't find media and/or sector size of %s: %m", device);
47494580Smarcel		goto closefd;
4751558Srgrimes	}
47694580Smarcel
47794580Smarcel	if (verbose) {
47896049Sfenner		printf("mediasize = %lld\n", (long long)mediasize);
47996049Sfenner		printf("sectorsize = %u\n", sectorsize);
48094580Smarcel	}
48194580Smarcel
48293492Sphk	lasthd = mediasize - sectorsize;
48393492Sphk	lseek(fd, lasthd, SEEK_SET);
48493492Sphk	error = read(fd, &kdhl, sizeof kdhl);
48593492Sphk	if (error != sizeof kdhl) {
48696049Sfenner		syslog(LOG_ERR,
48796049Sfenner		    "error reading last dump header at offset %lld in %s: %m",
48894580Smarcel		    (long long)lasthd, device);
48994580Smarcel		goto closefd;
4901558Srgrimes	}
491174923Srwatson	istextdump = 0;
492174944Srwatson	if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) {
49394580Smarcel		if (verbose)
494174923Srwatson			printf("textdump magic on last dump header on %s\n",
495174923Srwatson			    device);
496174923Srwatson		istextdump = 1;
497174944Srwatson		if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) {
498174944Srwatson			syslog(LOG_ERR,
499174944Srwatson			    "unknown version (%d) in last dump header on %s",
500174944Srwatson			    dtoh32(kdhl.version), device);
501244215Spjd
502174944Srwatson			status = STATUS_BAD;
503174944Srwatson			if (force == 0)
504174944Srwatson				goto closefd;
505174944Srwatson		}
506174944Srwatson	} else if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic) ==
507174944Srwatson	    0) {
508174944Srwatson		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
509174944Srwatson			syslog(LOG_ERR,
510174944Srwatson			    "unknown version (%d) in last dump header on %s",
511174944Srwatson			    dtoh32(kdhl.version), device);
512244215Spjd
513174944Srwatson			status = STATUS_BAD;
514174944Srwatson			if (force == 0)
515174944Srwatson				goto closefd;
516174944Srwatson		}
517174944Srwatson	} else {
518174923Srwatson		if (verbose)
51996049Sfenner			printf("magic mismatch on last dump header on %s\n",
52094580Smarcel			    device);
52196049Sfenner
522142359Sobrien		status = STATUS_BAD;
52396049Sfenner		if (force == 0)
52496049Sfenner			goto closefd;
52596049Sfenner
52696049Sfenner		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
52796049Sfenner			    sizeof kdhl.magic) == 0) {
52896049Sfenner			if (verbose)
52996049Sfenner				printf("forcing magic on %s\n", device);
53096049Sfenner			memcpy(kdhl.magic, KERNELDUMPMAGIC,
53196049Sfenner			    sizeof kdhl.magic);
53296049Sfenner		} else {
53396049Sfenner			syslog(LOG_ERR, "unable to force dump - bad magic");
53496049Sfenner			goto closefd;
53596049Sfenner		}
536174944Srwatson		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
537174944Srwatson			syslog(LOG_ERR,
538174944Srwatson			    "unknown version (%d) in last dump header on %s",
539174944Srwatson			    dtoh32(kdhl.version), device);
540244215Spjd
541174944Srwatson			status = STATUS_BAD;
542174944Srwatson			if (force == 0)
543174944Srwatson				goto closefd;
544174944Srwatson		}
5451558Srgrimes	}
546142359Sobrien
54794580Smarcel	nfound++;
54894580Smarcel	if (clear)
54994580Smarcel		goto nuke;
55094580Smarcel
55194580Smarcel	if (kerneldump_parity(&kdhl)) {
55296049Sfenner		syslog(LOG_ERR,
55396049Sfenner		    "parity error on last dump header on %s", device);
55496025Smux		nerr++;
555142359Sobrien		status = STATUS_BAD;
556142359Sobrien		if (force == 0)
557142359Sobrien			goto closefd;
55894580Smarcel	}
55993717Smarcel	dumpsize = dtoh64(kdhl.dumplength);
56093717Smarcel	firsthd = lasthd - dumpsize - sizeof kdhf;
56193492Sphk	lseek(fd, firsthd, SEEK_SET);
56293492Sphk	error = read(fd, &kdhf, sizeof kdhf);
56393492Sphk	if (error != sizeof kdhf) {
56496049Sfenner		syslog(LOG_ERR,
56596049Sfenner		    "error reading first dump header at offset %lld in %s: %m",
56694580Smarcel		    (long long)firsthd, device);
56796025Smux		nerr++;
56894580Smarcel		goto closefd;
5691558Srgrimes	}
570142359Sobrien
571142359Sobrien	if (verbose >= 2) {
572142359Sobrien		printf("First dump headers:\n");
573142359Sobrien		printheader(stdout, &kdhf, device, bounds, -1);
574142359Sobrien
575142359Sobrien		printf("\nLast dump headers:\n");
576142359Sobrien		printheader(stdout, &kdhl, device, bounds, -1);
577142359Sobrien		printf("\n");
578142359Sobrien	}
579142359Sobrien
58093492Sphk	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
58196049Sfenner		syslog(LOG_ERR,
58296049Sfenner		    "first and last dump headers disagree on %s", device);
58396025Smux		nerr++;
584142359Sobrien		status = STATUS_BAD;
585142359Sobrien		if (force == 0)
586142359Sobrien			goto closefd;
587142359Sobrien	} else {
588142359Sobrien		status = STATUS_GOOD;
58966429Sdes	}
59094580Smarcel
591119734Sdougb	if (checkfor) {
592119734Sdougb		printf("A dump exists on %s\n", device);
593119734Sdougb		close(fd);
594119734Sdougb		exit(0);
595119734Sdougb	}
596119734Sdougb
59796049Sfenner	if (kdhl.panicstring[0])
59896049Sfenner		syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
59996049Sfenner	else
60096049Sfenner		syslog(LOG_ALERT, "reboot");
60194580Smarcel
60296049Sfenner	if (verbose)
60396049Sfenner		printf("Checking for available free space\n");
604244320Spjd
605244320Spjd	if (!check_space(savedir, dumpsize, bounds)) {
60696025Smux		nerr++;
60796025Smux		goto closefd;
60896025Smux	}
60996049Sfenner
610147506Sdwhite	writebounds(bounds + 1);
611147506Sdwhite
612244320Spjd	saved_dump_remove(bounds);
61396049Sfenner
614244320Spjd	snprintf(infoname, sizeof(infoname), "info.%d", bounds);
615244320Spjd
61694580Smarcel	/*
617142359Sobrien	 * Create or overwrite any existing dump header files.
61894580Smarcel	 */
619244320Spjd	fdinfo = open(infoname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
62093492Sphk	if (fdinfo < 0) {
62196049Sfenner		syslog(LOG_ERR, "%s: %m", buf);
62296025Smux		nerr++;
62394580Smarcel		goto closefd;
6241558Srgrimes	}
62596049Sfenner	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
62696049Sfenner	if (compress) {
627244320Spjd		snprintf(corename, sizeof(corename), "%s.%d.gz",
628244316Spjd		    istextdump ? "textdump.tar" : "vmcore", bounds);
629244320Spjd		fp = zopen(corename, "w");
63096049Sfenner	} else {
631244320Spjd		snprintf(corename, sizeof(corename), "%s.%d",
632244316Spjd		    istextdump ? "textdump.tar" : "vmcore", bounds);
633244320Spjd		fp = fopen(corename, "w");
63496049Sfenner	}
63596049Sfenner	if (fp == NULL) {
636244320Spjd		syslog(LOG_ERR, "%s: %m", corename);
63794580Smarcel		close(fdinfo);
63896025Smux		nerr++;
63994580Smarcel		goto closefd;
64093492Sphk	}
64196049Sfenner	(void)umask(oumask);
64296049Sfenner
64393492Sphk	info = fdopen(fdinfo, "w");
64494580Smarcel
645170054Skevlo	if (info == NULL) {
646170054Skevlo		syslog(LOG_ERR, "fdopen failed: %m");
647170054Skevlo		nerr++;
648170054Skevlo		goto closefd;
649170054Skevlo	}
650170054Skevlo
65194580Smarcel	if (verbose)
652142359Sobrien		printheader(stdout, &kdhl, device, bounds, status);
65394580Smarcel
654142359Sobrien	printheader(info, &kdhl, device, bounds, status);
65596049Sfenner	fclose(info);
65694580Smarcel
657244320Spjd	syslog(LOG_NOTICE, "writing %score to %s/%s",
658244320Spjd	    compress ? "compressed " : "", savedir, corename);
65994580Smarcel
660174923Srwatson	if (istextdump) {
661174923Srwatson		if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device,
662244320Spjd		    corename, fp) < 0)
66394580Smarcel			goto closeall;
664174923Srwatson	} else {
665244320Spjd		if (DoRegularFile(fd, dumpsize, buf, device, corename, fp)
666174923Srwatson		    < 0)
66794580Smarcel			goto closeall;
66867264Sdes	}
66996049Sfenner	if (verbose)
67096049Sfenner		printf("\n");
67196049Sfenner
67296049Sfenner	if (fclose(fp) < 0) {
673244320Spjd		syslog(LOG_ERR, "error on %s: %m", corename);
67496049Sfenner		nerr++;
67596049Sfenner		goto closeall;
67696049Sfenner	}
677244320Spjd
678244321Spjd	symlinks_remove();
679244321Spjd	if (symlink(infoname, "info.last") == -1) {
680244321Spjd		syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
681244321Spjd		    savedir, "info.last");
682244321Spjd	}
683244321Spjd	if (compress) {
684244321Spjd		snprintf(linkname, sizeof(linkname), "%s.last.gz",
685244321Spjd		    istextdump ? "textdump.tar" : "vmcore");
686244321Spjd	} else {
687244321Spjd		snprintf(linkname, sizeof(linkname), "%s.last",
688244321Spjd		    istextdump ? "textdump.tar" : "vmcore");
689244321Spjd	}
690244321Spjd	if (symlink(corename, linkname) == -1) {
691244321Spjd		syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
692244321Spjd		    savedir, linkname);
693244321Spjd	}
694244321Spjd
69596025Smux	nsaved++;
69694580Smarcel
69794580Smarcel	if (verbose)
69896049Sfenner		printf("dump saved\n");
69994580Smarcel
70096049Sfennernuke:
701244216Spjd	if (!keep) {
70294580Smarcel		if (verbose)
70396049Sfenner			printf("clearing dump header\n");
70496049Sfenner		memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
70594580Smarcel		lseek(fd, lasthd, SEEK_SET);
70694580Smarcel		error = write(fd, &kdhl, sizeof kdhl);
70794580Smarcel		if (error != sizeof kdhl)
70896049Sfenner			syslog(LOG_ERR,
70996049Sfenner			    "error while clearing the dump header: %m");
71094580Smarcel	}
71194580Smarcel	close(fd);
71294580Smarcel	return;
71394580Smarcel
71496049Sfennercloseall:
71596049Sfenner	fclose(fp);
71694580Smarcel
71796049Sfennerclosefd:
71894580Smarcel	close(fd);
7191558Srgrimes}
7201558Srgrimes
72193492Sphkstatic void
72293492Sphkusage(void)
7231558Srgrimes{
724141611Sru	fprintf(stderr, "%s\n%s\n%s\n",
725244218Spjd	    "usage: savecore -c [-v] [device ...]",
726244218Spjd	    "       savecore -C [-v] [device ...]",
727244320Spjd	    "       savecore [-fkvz] [-m maxdumps] [directory [device ...]]");
728244320Spjd	exit(1);
7291558Srgrimes}
7301558Srgrimes
73118914Sfennerint
73293492Sphkmain(int argc, char **argv)
7331558Srgrimes{
734146763Sdelphij	const char *savedir = ".";
735142533Sobrien	struct fstab *fsp;
73693492Sphk	int i, ch, error;
7371558Srgrimes
738142359Sobrien	checkfor = compress = clear = force = keep = verbose = 0;
739142359Sobrien	nfound = nsaved = nerr = 0;
740142359Sobrien
74196049Sfenner	openlog("savecore", LOG_PERROR, LOG_DAEMON);
742196528Slulf	signal(SIGINFO, infohandler);
74396049Sfenner
744244320Spjd	while ((ch = getopt(argc, argv, "Ccfkm:vz")) != -1)
74593492Sphk		switch(ch) {
746119734Sdougb		case 'C':
747119734Sdougb			checkfor = 1;
748119734Sdougb			break;
74993492Sphk		case 'c':
75094580Smarcel			clear = 1;
75194580Smarcel			break;
752244317Spjd		case 'f':
753244317Spjd			force = 1;
754244317Spjd			break;
75594580Smarcel		case 'k':
75694580Smarcel			keep = 1;
75794580Smarcel			break;
758244320Spjd		case 'm':
759244320Spjd			maxdumps = atoi(optarg);
760244320Spjd			if (maxdumps <= 0) {
761244320Spjd				syslog(LOG_ERR, "Invalid maxdump value");
762244320Spjd				exit(1);
763244320Spjd			}
764244320Spjd			break;
76593492Sphk		case 'v':
766142359Sobrien			verbose++;
76794580Smarcel			break;
76896049Sfenner		case 'z':
76996049Sfenner			compress = 1;
77096049Sfenner			break;
77193492Sphk		case '?':
77293492Sphk		default:
77393492Sphk			usage();
77493492Sphk		}
775119734Sdougb	if (checkfor && (clear || force || keep))
776119734Sdougb		usage();
777244217Spjd	if (clear && (compress || keep))
778244217Spjd		usage();
779244320Spjd	if (maxdumps > 0 && (checkfor || clear))
780244320Spjd		usage();
78193492Sphk	argc -= optind;
78293492Sphk	argv += optind;
783244218Spjd	if (argc >= 1 && !checkfor && !clear) {
78493492Sphk		error = chdir(argv[0]);
78596049Sfenner		if (error) {
78696049Sfenner			syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
78796049Sfenner			exit(1);
78896049Sfenner		}
78996025Smux		savedir = argv[0];
79093492Sphk		argc--;
79193492Sphk		argv++;
7921558Srgrimes	}
79393492Sphk	if (argc == 0) {
79493492Sphk		for (;;) {
79593492Sphk			fsp = getfsent();
79693492Sphk			if (fsp == NULL)
79793492Sphk				break;
79893492Sphk			if (strcmp(fsp->fs_vfstype, "swap") &&
79993492Sphk			    strcmp(fsp->fs_vfstype, "dump"))
80093492Sphk				continue;
80196025Smux			DoFile(savedir, fsp->fs_spec);
80293492Sphk		}
80393492Sphk	} else {
80493492Sphk		for (i = 0; i < argc; i++)
80596025Smux			DoFile(savedir, argv[i]);
8061558Srgrimes	}
80794580Smarcel
80894580Smarcel	/* Emit minimal output. */
809119734Sdougb	if (nfound == 0) {
810119734Sdougb		if (checkfor) {
811119734Sdougb			printf("No dump exists\n");
812119734Sdougb			exit(1);
813119734Sdougb		}
81496049Sfenner		syslog(LOG_WARNING, "no dumps found");
815119734Sdougb	}
81696025Smux	else if (nsaved == 0) {
81796025Smux		if (nerr != 0)
81896049Sfenner			syslog(LOG_WARNING, "unsaved dumps found but not saved");
81996025Smux		else
82096049Sfenner			syslog(LOG_WARNING, "no unsaved dumps found");
82196025Smux	}
82294580Smarcel
82393492Sphk	return (0);
8241558Srgrimes}
825196528Slulf
826196528Slulfstatic void
827196528Slulfinfohandler(int sig __unused)
828196528Slulf{
829196528Slulf	got_siginfo = 1;
830196528Slulf}
831