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) {
154272434Sbdrewery		if (feof(fp))
155272434Sbdrewery			syslog(LOG_WARNING, "bounds file is empty, using 0");
156272434Sbdrewery		else
157272434Sbdrewery			syslog(LOG_WARNING, "bounds file: %s", strerror(errno));
15896049Sfenner		fclose(fp);
159147506Sdwhite		return (ret);
16096049Sfenner	}
16196049Sfenner
16296049Sfenner	errno = 0;
16396049Sfenner	ret = (int)strtol(buf, NULL, 10);
16496049Sfenner	if (ret == 0 && (errno == EINVAL || errno == ERANGE))
16596049Sfenner		syslog(LOG_WARNING, "invalid value found in bounds, using 0");
166272434Sbdrewery	fclose(fp);
167147506Sdwhite	return (ret);
168147506Sdwhite}
16996049Sfenner
170147506Sdwhitestatic void
171147506Sdwhitewritebounds(int bounds) {
172147506Sdwhite	FILE *fp;
17396049Sfenner
17496049Sfenner	if ((fp = fopen("bounds", "w")) == NULL) {
17596049Sfenner		syslog(LOG_WARNING, "unable to write to bounds file: %m");
176147506Sdwhite		return;
17796049Sfenner	}
17896049Sfenner
17996049Sfenner	if (verbose)
180147506Sdwhite		printf("bounds number: %d\n", bounds);
18196049Sfenner
182147506Sdwhite	fprintf(fp, "%d\n", bounds);
18396049Sfenner	fclose(fp);
18496049Sfenner}
18596049Sfenner
186244320Spjdstatic off_t
187244320Spjdfile_size(const char *path)
188244320Spjd{
189244320Spjd	struct stat sb;
190244320Spjd
191244320Spjd	/* Ignore all errors, those file may not exists. */
192244320Spjd	if (stat(path, &sb) == -1)
193244320Spjd		return (0);
194244320Spjd	return (sb.st_size);
195244320Spjd}
196244320Spjd
197244320Spjdstatic off_t
198244320Spjdsaved_dump_size(int bounds)
199244320Spjd{
200244320Spjd	static char path[PATH_MAX];
201244320Spjd	off_t dumpsize;
202244320Spjd
203244320Spjd	dumpsize = 0;
204244320Spjd
205244320Spjd	(void)snprintf(path, sizeof(path), "info.%d", bounds);
206244320Spjd	dumpsize += file_size(path);
207244320Spjd	(void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
208244320Spjd	dumpsize += file_size(path);
209244320Spjd	(void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
210244320Spjd	dumpsize += file_size(path);
211244320Spjd	(void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
212244320Spjd	dumpsize += file_size(path);
213244320Spjd	(void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
214244320Spjd	dumpsize += file_size(path);
215244320Spjd
216244320Spjd	return (dumpsize);
217244320Spjd}
218244320Spjd
219244320Spjdstatic void
220244320Spjdsaved_dump_remove(int bounds)
221244320Spjd{
222244320Spjd	static char path[PATH_MAX];
223244320Spjd
224244320Spjd	(void)snprintf(path, sizeof(path), "info.%d", bounds);
225244320Spjd	(void)unlink(path);
226244320Spjd	(void)snprintf(path, sizeof(path), "vmcore.%d", bounds);
227244320Spjd	(void)unlink(path);
228244320Spjd	(void)snprintf(path, sizeof(path), "vmcore.%d.gz", bounds);
229244320Spjd	(void)unlink(path);
230244320Spjd	(void)snprintf(path, sizeof(path), "textdump.tar.%d", bounds);
231244320Spjd	(void)unlink(path);
232244320Spjd	(void)snprintf(path, sizeof(path), "textdump.tar.%d.gz", bounds);
233244320Spjd	(void)unlink(path);
234244320Spjd}
235244320Spjd
236244321Spjdstatic void
237244321Spjdsymlinks_remove(void)
238244321Spjd{
239244321Spjd
240244321Spjd	(void)unlink("info.last");
241244321Spjd	(void)unlink("vmcore.last");
242244321Spjd	(void)unlink("vmcore.last.gz");
243244321Spjd	(void)unlink("textdump.tar.last");
244244321Spjd	(void)unlink("textdump.tar.last.gz");
245244321Spjd}
246244321Spjd
24796025Smux/*
24896025Smux * Check that sufficient space is available on the disk that holds the
24996025Smux * save directory.
25096025Smux */
25196025Smuxstatic int
252244320Spjdcheck_space(const char *savedir, off_t dumpsize, int bounds)
25396025Smux{
25496025Smux	FILE *fp;
25596049Sfenner	off_t minfree, spacefree, totfree, needed;
25696025Smux	struct statfs fsbuf;
257244319Spjd	char buf[100];
25893717Smarcel
259244319Spjd	if (statfs(".", &fsbuf) < 0) {
26096049Sfenner		syslog(LOG_ERR, "%s: %m", savedir);
26196049Sfenner		exit(1);
26296049Sfenner	}
263244215Spjd	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
26496025Smux	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
26596025Smux
266244319Spjd	if ((fp = fopen("minfree", "r")) == NULL)
26796025Smux		minfree = 0;
26896025Smux	else {
26996025Smux		if (fgets(buf, sizeof(buf), fp) == NULL)
27096025Smux			minfree = 0;
27196025Smux		else
27296025Smux			minfree = atoi(buf);
27396025Smux		(void)fclose(fp);
27496025Smux	}
27596025Smux
27696049Sfenner	needed = dumpsize / 1024 + 2;	/* 2 for info file */
277244320Spjd	needed -= saved_dump_size(bounds);
278244320Spjd	if ((minfree > 0 ? spacefree : totfree) - needed < minfree) {
27996049Sfenner		syslog(LOG_WARNING,
28096049Sfenner	"no dump, not enough free space on device (%lld available, need %lld)",
28196025Smux		    (long long)(minfree > 0 ? spacefree : totfree),
28296025Smux		    (long long)needed);
28396025Smux		return (0);
28496025Smux	}
28596025Smux	if (spacefree - needed < 0)
28696049Sfenner		syslog(LOG_WARNING,
28796049Sfenner		    "dump performed, but free space threshold crossed");
28896025Smux	return (1);
28996025Smux}
29096025Smux
29196049Sfenner#define BLOCKSIZE (1<<12)
29296049Sfenner#define BLOCKMASK (~(BLOCKSIZE-1))
29396025Smux
294174923Srwatsonstatic int
295174923SrwatsonDoRegularFile(int fd, off_t dumpsize, char *buf, const char *device,
296174923Srwatson    const char *filename, FILE *fp)
297174923Srwatson{
298174923Srwatson	int he, hs, nr, nw, wl;
299196528Slulf	off_t dmpcnt, origsize;
300174923Srwatson
301174923Srwatson	dmpcnt = 0;
302196528Slulf	origsize = dumpsize;
303174923Srwatson	he = 0;
304174923Srwatson	while (dumpsize > 0) {
305174923Srwatson		wl = BUFFERSIZE;
306174923Srwatson		if (wl > dumpsize)
307174923Srwatson			wl = dumpsize;
308174923Srwatson		nr = read(fd, buf, wl);
309174923Srwatson		if (nr != wl) {
310174923Srwatson			if (nr == 0)
311174923Srwatson				syslog(LOG_WARNING,
312174923Srwatson				    "WARNING: EOF on dump device");
313174923Srwatson			else
314174923Srwatson				syslog(LOG_ERR, "read error on %s: %m", device);
315174923Srwatson			nerr++;
316174923Srwatson			return (-1);
317174923Srwatson		}
318174923Srwatson		if (compress) {
319174923Srwatson			nw = fwrite(buf, 1, wl, fp);
320174923Srwatson		} else {
321174923Srwatson			for (nw = 0; nw < nr; nw = he) {
322174923Srwatson				/* find a contiguous block of zeroes */
323174923Srwatson				for (hs = nw; hs < nr; hs += BLOCKSIZE) {
324174923Srwatson					for (he = hs; he < nr && buf[he] == 0;
325174923Srwatson					    ++he)
326174923Srwatson						/* nothing */ ;
327174923Srwatson					/* is the hole long enough to matter? */
328174923Srwatson					if (he >= hs + BLOCKSIZE)
329174923Srwatson						break;
330174923Srwatson				}
331244215Spjd
332174923Srwatson				/* back down to a block boundary */
333174923Srwatson				he &= BLOCKMASK;
334174923Srwatson
335174923Srwatson				/*
336174923Srwatson				 * 1) Don't go beyond the end of the buffer.
337174923Srwatson				 * 2) If the end of the buffer is less than
338174923Srwatson				 *    BLOCKSIZE bytes away, we're at the end
339174923Srwatson				 *    of the file, so just grab what's left.
340174923Srwatson				 */
341174923Srwatson				if (hs + BLOCKSIZE > nr)
342174923Srwatson					hs = he = nr;
343174923Srwatson
344174923Srwatson				/*
345174923Srwatson				 * At this point, we have a partial ordering:
346174923Srwatson				 *     nw <= hs <= he <= nr
347174923Srwatson				 * If hs > nw, buf[nw..hs] contains non-zero data.
348174923Srwatson				 * If he > hs, buf[hs..he] is all zeroes.
349174923Srwatson				 */
350174923Srwatson				if (hs > nw)
351174923Srwatson					if (fwrite(buf + nw, hs - nw, 1, fp)
352174923Srwatson					    != 1)
353174923Srwatson					break;
354174923Srwatson				if (he > hs)
355174923Srwatson					if (fseeko(fp, he - hs, SEEK_CUR) == -1)
356174923Srwatson						break;
357174923Srwatson			}
358174923Srwatson		}
359174923Srwatson		if (nw != wl) {
360174923Srwatson			syslog(LOG_ERR,
361174923Srwatson			    "write error on %s file: %m", filename);
362174923Srwatson			syslog(LOG_WARNING,
363174923Srwatson			    "WARNING: vmcore may be incomplete");
364174923Srwatson			nerr++;
365174923Srwatson			return (-1);
366174923Srwatson		}
367174923Srwatson		if (verbose) {
368174923Srwatson			dmpcnt += wl;
369174923Srwatson			printf("%llu\r", (unsigned long long)dmpcnt);
370174923Srwatson			fflush(stdout);
371174923Srwatson		}
372174923Srwatson		dumpsize -= wl;
373196528Slulf		if (got_siginfo) {
374196528Slulf			printf("%s %.1lf%%\n", filename, (100.0 - (100.0 *
375196528Slulf			    (double)dumpsize / (double)origsize)));
376196528Slulf			got_siginfo = 0;
377196528Slulf		}
378174923Srwatson	}
379174923Srwatson	return (0);
380174923Srwatson}
381174923Srwatson
382174923Srwatson/*
383174923Srwatson * Specialized version of dump-reading logic for use with textdumps, which
384174923Srwatson * are written backwards from the end of the partition, and must be reversed
385174923Srwatson * before being written to the file.  Textdumps are small, so do a bit less
386174923Srwatson * work to optimize/sparsify.
387174923Srwatson */
388174923Srwatsonstatic int
389174923SrwatsonDoTextdumpFile(int fd, off_t dumpsize, off_t lasthd, char *buf,
390174923Srwatson    const char *device, const char *filename, FILE *fp)
391174923Srwatson{
392174923Srwatson	int nr, nw, wl;
393174923Srwatson	off_t dmpcnt, totsize;
394174923Srwatson
395174923Srwatson	totsize = dumpsize;
396174923Srwatson	dmpcnt = 0;
397174923Srwatson	wl = 512;
398174923Srwatson	if ((dumpsize % wl) != 0) {
399174923Srwatson		syslog(LOG_ERR, "textdump uneven multiple of 512 on %s",
400174923Srwatson		    device);
401174923Srwatson		nerr++;
402174923Srwatson		return (-1);
403174923Srwatson	}
404174923Srwatson	while (dumpsize > 0) {
405174923Srwatson		nr = pread(fd, buf, wl, lasthd - (totsize - dumpsize) - wl);
406174923Srwatson		if (nr != wl) {
407174923Srwatson			if (nr == 0)
408174923Srwatson				syslog(LOG_WARNING,
409174923Srwatson				    "WARNING: EOF on dump device");
410174923Srwatson			else
411174923Srwatson				syslog(LOG_ERR, "read error on %s: %m", device);
412174923Srwatson			nerr++;
413174923Srwatson			return (-1);
414174923Srwatson		}
415174923Srwatson		nw = fwrite(buf, 1, wl, fp);
416174923Srwatson		if (nw != wl) {
417174923Srwatson			syslog(LOG_ERR,
418174923Srwatson			    "write error on %s file: %m", filename);
419174923Srwatson			syslog(LOG_WARNING,
420174923Srwatson			    "WARNING: textdump may be incomplete");
421174923Srwatson			nerr++;
422174923Srwatson			return (-1);
423174923Srwatson		}
424174923Srwatson		if (verbose) {
425174923Srwatson			dmpcnt += wl;
426174923Srwatson			printf("%llu\r", (unsigned long long)dmpcnt);
427174923Srwatson			fflush(stdout);
428174923Srwatson		}
429174923Srwatson		dumpsize -= wl;
430174923Srwatson	}
431174923Srwatson	return (0);
432174923Srwatson}
433174923Srwatson
43493492Sphkstatic void
435146763SdelphijDoFile(const char *savedir, const char *device)
4361558Srgrimes{
437244321Spjd	static char infoname[PATH_MAX], corename[PATH_MAX], linkname[PATH_MAX];
43897340Smarcel	static char *buf = NULL;
43994580Smarcel	struct kerneldumpheader kdhf, kdhl;
440174923Srwatson	off_t mediasize, dumpsize, firsthd, lasthd;
44196049Sfenner	FILE *info, *fp;
442142533Sobrien	mode_t oumask;
443174923Srwatson	int fd, fdinfo, error;
444142359Sobrien	int bounds, status;
44594580Smarcel	u_int sectorsize;
446174923Srwatson	int istextdump;
4478871Srgrimes
448142359Sobrien	bounds = getbounds();
44996049Sfenner	mediasize = 0;
450142359Sobrien	status = STATUS_UNKNOWN;
45196049Sfenner
452244320Spjd	if (maxdumps > 0 && bounds == maxdumps)
453244320Spjd		bounds = 0;
454244320Spjd
45597340Smarcel	if (buf == NULL) {
45697746Smarcel		buf = malloc(BUFFERSIZE);
45797340Smarcel		if (buf == NULL) {
45897340Smarcel			syslog(LOG_ERR, "%m");
45997340Smarcel			return;
46097340Smarcel		}
46197340Smarcel	}
46297340Smarcel
46394580Smarcel	if (verbose)
46496049Sfenner		printf("checking for kernel dump on device %s\n", device);
46594580Smarcel
466244216Spjd	fd = open(device, (checkfor || keep) ? O_RDONLY : O_RDWR);
46793492Sphk	if (fd < 0) {
46896049Sfenner		syslog(LOG_ERR, "%s: %m", device);
46993492Sphk		return;
47047095Sluoqi	}
47197340Smarcel
47293492Sphk	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
47393492Sphk	if (!error)
47493492Sphk		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
47593492Sphk	if (error) {
47696049Sfenner		syslog(LOG_ERR,
47796049Sfenner		    "couldn't find media and/or sector size of %s: %m", device);
47894580Smarcel		goto closefd;
4791558Srgrimes	}
48094580Smarcel
48194580Smarcel	if (verbose) {
48296049Sfenner		printf("mediasize = %lld\n", (long long)mediasize);
48396049Sfenner		printf("sectorsize = %u\n", sectorsize);
48494580Smarcel	}
48594580Smarcel
48693492Sphk	lasthd = mediasize - sectorsize;
48793492Sphk	lseek(fd, lasthd, SEEK_SET);
48893492Sphk	error = read(fd, &kdhl, sizeof kdhl);
48993492Sphk	if (error != sizeof kdhl) {
49096049Sfenner		syslog(LOG_ERR,
49196049Sfenner		    "error reading last dump header at offset %lld in %s: %m",
49294580Smarcel		    (long long)lasthd, device);
49394580Smarcel		goto closefd;
4941558Srgrimes	}
495174923Srwatson	istextdump = 0;
496174944Srwatson	if (strncmp(kdhl.magic, TEXTDUMPMAGIC, sizeof kdhl) == 0) {
49794580Smarcel		if (verbose)
498174923Srwatson			printf("textdump magic on last dump header on %s\n",
499174923Srwatson			    device);
500174923Srwatson		istextdump = 1;
501174944Srwatson		if (dtoh32(kdhl.version) != KERNELDUMP_TEXT_VERSION) {
502174944Srwatson			syslog(LOG_ERR,
503174944Srwatson			    "unknown version (%d) in last dump header on %s",
504174944Srwatson			    dtoh32(kdhl.version), device);
505244215Spjd
506174944Srwatson			status = STATUS_BAD;
507174944Srwatson			if (force == 0)
508174944Srwatson				goto closefd;
509174944Srwatson		}
510174944Srwatson	} else if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic) ==
511174944Srwatson	    0) {
512174944Srwatson		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
513174944Srwatson			syslog(LOG_ERR,
514174944Srwatson			    "unknown version (%d) in last dump header on %s",
515174944Srwatson			    dtoh32(kdhl.version), device);
516244215Spjd
517174944Srwatson			status = STATUS_BAD;
518174944Srwatson			if (force == 0)
519174944Srwatson				goto closefd;
520174944Srwatson		}
521174944Srwatson	} else {
522174923Srwatson		if (verbose)
52396049Sfenner			printf("magic mismatch on last dump header on %s\n",
52494580Smarcel			    device);
52596049Sfenner
526142359Sobrien		status = STATUS_BAD;
52796049Sfenner		if (force == 0)
52896049Sfenner			goto closefd;
52996049Sfenner
53096049Sfenner		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
53196049Sfenner			    sizeof kdhl.magic) == 0) {
53296049Sfenner			if (verbose)
53396049Sfenner				printf("forcing magic on %s\n", device);
53496049Sfenner			memcpy(kdhl.magic, KERNELDUMPMAGIC,
53596049Sfenner			    sizeof kdhl.magic);
53696049Sfenner		} else {
53796049Sfenner			syslog(LOG_ERR, "unable to force dump - bad magic");
53896049Sfenner			goto closefd;
53996049Sfenner		}
540174944Srwatson		if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
541174944Srwatson			syslog(LOG_ERR,
542174944Srwatson			    "unknown version (%d) in last dump header on %s",
543174944Srwatson			    dtoh32(kdhl.version), device);
544244215Spjd
545174944Srwatson			status = STATUS_BAD;
546174944Srwatson			if (force == 0)
547174944Srwatson				goto closefd;
548174944Srwatson		}
5491558Srgrimes	}
550142359Sobrien
55194580Smarcel	nfound++;
55294580Smarcel	if (clear)
55394580Smarcel		goto nuke;
55494580Smarcel
55594580Smarcel	if (kerneldump_parity(&kdhl)) {
55696049Sfenner		syslog(LOG_ERR,
55796049Sfenner		    "parity error on last dump header on %s", device);
55896025Smux		nerr++;
559142359Sobrien		status = STATUS_BAD;
560142359Sobrien		if (force == 0)
561142359Sobrien			goto closefd;
56294580Smarcel	}
56393717Smarcel	dumpsize = dtoh64(kdhl.dumplength);
56493717Smarcel	firsthd = lasthd - dumpsize - sizeof kdhf;
56593492Sphk	lseek(fd, firsthd, SEEK_SET);
56693492Sphk	error = read(fd, &kdhf, sizeof kdhf);
56793492Sphk	if (error != sizeof kdhf) {
56896049Sfenner		syslog(LOG_ERR,
56996049Sfenner		    "error reading first dump header at offset %lld in %s: %m",
57094580Smarcel		    (long long)firsthd, device);
57196025Smux		nerr++;
57294580Smarcel		goto closefd;
5731558Srgrimes	}
574142359Sobrien
575142359Sobrien	if (verbose >= 2) {
576142359Sobrien		printf("First dump headers:\n");
577142359Sobrien		printheader(stdout, &kdhf, device, bounds, -1);
578142359Sobrien
579142359Sobrien		printf("\nLast dump headers:\n");
580142359Sobrien		printheader(stdout, &kdhl, device, bounds, -1);
581142359Sobrien		printf("\n");
582142359Sobrien	}
583142359Sobrien
58493492Sphk	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
58596049Sfenner		syslog(LOG_ERR,
58696049Sfenner		    "first and last dump headers disagree on %s", device);
58796025Smux		nerr++;
588142359Sobrien		status = STATUS_BAD;
589142359Sobrien		if (force == 0)
590142359Sobrien			goto closefd;
591142359Sobrien	} else {
592142359Sobrien		status = STATUS_GOOD;
59366429Sdes	}
59494580Smarcel
595119734Sdougb	if (checkfor) {
596119734Sdougb		printf("A dump exists on %s\n", device);
597119734Sdougb		close(fd);
598119734Sdougb		exit(0);
599119734Sdougb	}
600119734Sdougb
60196049Sfenner	if (kdhl.panicstring[0])
60296049Sfenner		syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
60396049Sfenner	else
60496049Sfenner		syslog(LOG_ALERT, "reboot");
60594580Smarcel
60696049Sfenner	if (verbose)
60796049Sfenner		printf("Checking for available free space\n");
608244320Spjd
609244320Spjd	if (!check_space(savedir, dumpsize, bounds)) {
61096025Smux		nerr++;
61196025Smux		goto closefd;
61296025Smux	}
61396049Sfenner
614147506Sdwhite	writebounds(bounds + 1);
615147506Sdwhite
616244320Spjd	saved_dump_remove(bounds);
61796049Sfenner
618244320Spjd	snprintf(infoname, sizeof(infoname), "info.%d", bounds);
619244320Spjd
62094580Smarcel	/*
621142359Sobrien	 * Create or overwrite any existing dump header files.
62294580Smarcel	 */
623244320Spjd	fdinfo = open(infoname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
62493492Sphk	if (fdinfo < 0) {
625265075Smarkj		syslog(LOG_ERR, "%s: %m", infoname);
62696025Smux		nerr++;
62794580Smarcel		goto closefd;
6281558Srgrimes	}
62996049Sfenner	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
63096049Sfenner	if (compress) {
631244320Spjd		snprintf(corename, sizeof(corename), "%s.%d.gz",
632244316Spjd		    istextdump ? "textdump.tar" : "vmcore", bounds);
633244320Spjd		fp = zopen(corename, "w");
63496049Sfenner	} else {
635244320Spjd		snprintf(corename, sizeof(corename), "%s.%d",
636244316Spjd		    istextdump ? "textdump.tar" : "vmcore", bounds);
637244320Spjd		fp = fopen(corename, "w");
63896049Sfenner	}
63996049Sfenner	if (fp == NULL) {
640244320Spjd		syslog(LOG_ERR, "%s: %m", corename);
64194580Smarcel		close(fdinfo);
64296025Smux		nerr++;
64394580Smarcel		goto closefd;
64493492Sphk	}
64596049Sfenner	(void)umask(oumask);
64696049Sfenner
64793492Sphk	info = fdopen(fdinfo, "w");
64894580Smarcel
649170054Skevlo	if (info == NULL) {
650170054Skevlo		syslog(LOG_ERR, "fdopen failed: %m");
651170054Skevlo		nerr++;
652170054Skevlo		goto closefd;
653170054Skevlo	}
654170054Skevlo
65594580Smarcel	if (verbose)
656142359Sobrien		printheader(stdout, &kdhl, device, bounds, status);
65794580Smarcel
658142359Sobrien	printheader(info, &kdhl, device, bounds, status);
65996049Sfenner	fclose(info);
66094580Smarcel
661244320Spjd	syslog(LOG_NOTICE, "writing %score to %s/%s",
662244320Spjd	    compress ? "compressed " : "", savedir, corename);
66394580Smarcel
664174923Srwatson	if (istextdump) {
665174923Srwatson		if (DoTextdumpFile(fd, dumpsize, lasthd, buf, device,
666244320Spjd		    corename, fp) < 0)
66794580Smarcel			goto closeall;
668174923Srwatson	} else {
669244320Spjd		if (DoRegularFile(fd, dumpsize, buf, device, corename, fp)
670174923Srwatson		    < 0)
67194580Smarcel			goto closeall;
67267264Sdes	}
67396049Sfenner	if (verbose)
67496049Sfenner		printf("\n");
67596049Sfenner
67696049Sfenner	if (fclose(fp) < 0) {
677244320Spjd		syslog(LOG_ERR, "error on %s: %m", corename);
67896049Sfenner		nerr++;
679264806Sbrueffer		goto closefd;
68096049Sfenner	}
681244320Spjd
682244321Spjd	symlinks_remove();
683244321Spjd	if (symlink(infoname, "info.last") == -1) {
684244321Spjd		syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
685244321Spjd		    savedir, "info.last");
686244321Spjd	}
687244321Spjd	if (compress) {
688244321Spjd		snprintf(linkname, sizeof(linkname), "%s.last.gz",
689244321Spjd		    istextdump ? "textdump.tar" : "vmcore");
690244321Spjd	} else {
691244321Spjd		snprintf(linkname, sizeof(linkname), "%s.last",
692244321Spjd		    istextdump ? "textdump.tar" : "vmcore");
693244321Spjd	}
694244321Spjd	if (symlink(corename, linkname) == -1) {
695244321Spjd		syslog(LOG_WARNING, "unable to create symlink %s/%s: %m",
696244321Spjd		    savedir, linkname);
697244321Spjd	}
698244321Spjd
69996025Smux	nsaved++;
70094580Smarcel
70194580Smarcel	if (verbose)
70296049Sfenner		printf("dump saved\n");
70394580Smarcel
70496049Sfennernuke:
705244216Spjd	if (!keep) {
70694580Smarcel		if (verbose)
70796049Sfenner			printf("clearing dump header\n");
70896049Sfenner		memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
70994580Smarcel		lseek(fd, lasthd, SEEK_SET);
71094580Smarcel		error = write(fd, &kdhl, sizeof kdhl);
71194580Smarcel		if (error != sizeof kdhl)
71296049Sfenner			syslog(LOG_ERR,
71396049Sfenner			    "error while clearing the dump header: %m");
71494580Smarcel	}
71594580Smarcel	close(fd);
71694580Smarcel	return;
71794580Smarcel
71896049Sfennercloseall:
71996049Sfenner	fclose(fp);
72094580Smarcel
72196049Sfennerclosefd:
72294580Smarcel	close(fd);
7231558Srgrimes}
7241558Srgrimes
72593492Sphkstatic void
72693492Sphkusage(void)
7271558Srgrimes{
728141611Sru	fprintf(stderr, "%s\n%s\n%s\n",
729244218Spjd	    "usage: savecore -c [-v] [device ...]",
730244218Spjd	    "       savecore -C [-v] [device ...]",
731244320Spjd	    "       savecore [-fkvz] [-m maxdumps] [directory [device ...]]");
732244320Spjd	exit(1);
7331558Srgrimes}
7341558Srgrimes
73518914Sfennerint
73693492Sphkmain(int argc, char **argv)
7371558Srgrimes{
738146763Sdelphij	const char *savedir = ".";
739142533Sobrien	struct fstab *fsp;
74093492Sphk	int i, ch, error;
7411558Srgrimes
742142359Sobrien	checkfor = compress = clear = force = keep = verbose = 0;
743142359Sobrien	nfound = nsaved = nerr = 0;
744142359Sobrien
74596049Sfenner	openlog("savecore", LOG_PERROR, LOG_DAEMON);
746196528Slulf	signal(SIGINFO, infohandler);
74796049Sfenner
748244320Spjd	while ((ch = getopt(argc, argv, "Ccfkm:vz")) != -1)
74993492Sphk		switch(ch) {
750119734Sdougb		case 'C':
751119734Sdougb			checkfor = 1;
752119734Sdougb			break;
75393492Sphk		case 'c':
75494580Smarcel			clear = 1;
75594580Smarcel			break;
756244317Spjd		case 'f':
757244317Spjd			force = 1;
758244317Spjd			break;
75994580Smarcel		case 'k':
76094580Smarcel			keep = 1;
76194580Smarcel			break;
762244320Spjd		case 'm':
763244320Spjd			maxdumps = atoi(optarg);
764244320Spjd			if (maxdumps <= 0) {
765244320Spjd				syslog(LOG_ERR, "Invalid maxdump value");
766244320Spjd				exit(1);
767244320Spjd			}
768244320Spjd			break;
76993492Sphk		case 'v':
770142359Sobrien			verbose++;
77194580Smarcel			break;
77296049Sfenner		case 'z':
77396049Sfenner			compress = 1;
77496049Sfenner			break;
77593492Sphk		case '?':
77693492Sphk		default:
77793492Sphk			usage();
77893492Sphk		}
779119734Sdougb	if (checkfor && (clear || force || keep))
780119734Sdougb		usage();
781244217Spjd	if (clear && (compress || keep))
782244217Spjd		usage();
783244320Spjd	if (maxdumps > 0 && (checkfor || clear))
784244320Spjd		usage();
78593492Sphk	argc -= optind;
78693492Sphk	argv += optind;
787244218Spjd	if (argc >= 1 && !checkfor && !clear) {
78893492Sphk		error = chdir(argv[0]);
78996049Sfenner		if (error) {
79096049Sfenner			syslog(LOG_ERR, "chdir(%s): %m", argv[0]);
79196049Sfenner			exit(1);
79296049Sfenner		}
79396025Smux		savedir = argv[0];
79493492Sphk		argc--;
79593492Sphk		argv++;
7961558Srgrimes	}
79793492Sphk	if (argc == 0) {
79893492Sphk		for (;;) {
79993492Sphk			fsp = getfsent();
80093492Sphk			if (fsp == NULL)
80193492Sphk				break;
80293492Sphk			if (strcmp(fsp->fs_vfstype, "swap") &&
80393492Sphk			    strcmp(fsp->fs_vfstype, "dump"))
80493492Sphk				continue;
80596025Smux			DoFile(savedir, fsp->fs_spec);
80693492Sphk		}
80793492Sphk	} else {
80893492Sphk		for (i = 0; i < argc; i++)
80996025Smux			DoFile(savedir, argv[i]);
8101558Srgrimes	}
81194580Smarcel
81294580Smarcel	/* Emit minimal output. */
813119734Sdougb	if (nfound == 0) {
814119734Sdougb		if (checkfor) {
815119734Sdougb			printf("No dump exists\n");
816119734Sdougb			exit(1);
817119734Sdougb		}
81896049Sfenner		syslog(LOG_WARNING, "no dumps found");
819119734Sdougb	}
82096025Smux	else if (nsaved == 0) {
82196025Smux		if (nerr != 0)
82296049Sfenner			syslog(LOG_WARNING, "unsaved dumps found but not saved");
82396025Smux		else
82496049Sfenner			syslog(LOG_WARNING, "no unsaved dumps found");
82596025Smux	}
82694580Smarcel
82793492Sphk	return (0);
8281558Srgrimes}
829196528Slulf
830196528Slulfstatic void
831196528Slulfinfohandler(int sig __unused)
832196528Slulf{
833196528Slulf	got_siginfo = 1;
834196528Slulf}
835