savecore.c revision 96025
1/*-
2 * Copyright (c) 2002 Poul-Henning Kamp
3 * Copyright (c) 2002 Networks Associates Technology, Inc.
4 * All rights reserved.
5 *
6 * This software was developed for the FreeBSD Project by Poul-Henning Kamp
7 * and NAI Labs, the Security Research Division of Network Associates, Inc.
8 * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the
9 * DARPA CHATS research program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. The names of the authors may not be used to endorse or promote
20 *    products derived from this software without specific prior written
21 *    permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD: head/sbin/savecore/savecore.c 96025 2002-05-04 10:36:35Z mux $");
38
39#include <sys/types.h>
40#include <sys/disk.h>
41#include <sys/kerneldump.h>
42#include <sys/param.h>
43#include <sys/mount.h>
44#include <sys/stat.h>
45#include <err.h>
46#include <errno.h>
47#include <fcntl.h>
48#include <fstab.h>
49#include <md5.h>
50#include <paths.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <string.h>
54#include <time.h>
55#include <unistd.h>
56
57int clear, force, keep, verbose;	/* flags */
58int nfound, nsaved, nerr;		/* statistics */
59
60static void
61printheader(FILE *f, const struct kerneldumpheader *h, const char *device,
62    const char *md5)
63{
64	uint64_t dumplen;
65	time_t t;
66
67	fprintf(f, "Good dump found on device %s\n", device);
68	fprintf(f, "  Architecture: %s\n", h->architecture);
69	fprintf(f, "  Architecture version: %d\n",
70	    dtoh32(h->architectureversion));
71	dumplen = dtoh64(h->dumplength);
72	fprintf(f, "  Dump length: %lldB (%lld MB)\n", (long long)dumplen,
73	    (long long)(dumplen >> 20));
74	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
75	t = dtoh64(h->dumptime);
76	fprintf(f, "  Dumptime: %s", ctime(&t));
77	fprintf(f, "  Hostname: %s\n", h->hostname);
78	fprintf(f, "  Versionstring: %s", h->versionstring);
79	fprintf(f, "  Panicstring: %s\n", h->panicstring);
80	fprintf(f, "  MD5: %s\n", md5);
81	fflush(f);
82}
83
84/*
85 * Check that sufficient space is available on the disk that holds the
86 * save directory.
87 */
88static int
89check_space(char *savedir, off_t dumpsize)
90{
91	FILE *fp;
92	const char *tkernel;
93	off_t minfree, spacefree, totfree, kernelsize, needed;
94	struct stat st;
95	struct statfs fsbuf;
96	char buf[100], path[MAXPATHLEN];
97
98	tkernel = getbootfile();
99	if (stat(tkernel, &st) < 0)
100		err(1, "%s", tkernel);
101	kernelsize = st.st_blocks * S_BLKSIZE;
102
103	if (statfs(savedir, &fsbuf) < 0)
104		err(1, "%s", savedir);
105 	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
106	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
107
108	(void)snprintf(path, sizeof(path), "%s/minfree", savedir);
109	if ((fp = fopen(path, "r")) == NULL)
110		minfree = 0;
111	else {
112		if (fgets(buf, sizeof(buf), fp) == NULL)
113			minfree = 0;
114		else
115			minfree = atoi(buf);
116		(void)fclose(fp);
117	}
118
119	needed = (dumpsize + kernelsize) / 1024;
120 	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
121		warnx("no dump, not enough free space on device"
122		    " (%lld available, need %lld)",
123		    (long long)(minfree > 0 ? spacefree : totfree),
124		    (long long)needed);
125		return (0);
126	}
127	if (spacefree - needed < 0)
128		warnx("dump performed, but free space threshold crossed");
129	return (1);
130}
131
132
133
134static void
135DoFile(char *savedir, const char *device)
136{
137	struct kerneldumpheader kdhf, kdhl;
138	char buf[BUFSIZ];
139	struct stat sb;
140	off_t mediasize, dumpsize, firsthd, lasthd;
141	char *md5;
142	FILE *info;
143	int fd, fdcore, fdinfo, error, wl;
144	u_int sectorsize;
145
146	if (verbose)
147		printf("Checking for kernel dump on device %s\n", device);
148
149	mediasize = 0;
150	fd = open(device, O_RDWR);
151	if (fd < 0) {
152		warn("%s", device);
153		return;
154	}
155	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
156	if (!error)
157		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
158	if (error) {
159		warn("couldn't find media and/or sector size of %s", device);
160		goto closefd;
161	}
162
163	if (verbose) {
164		printf("Mediasize = %lld\n", (long long)mediasize);
165		printf("Sectorsize = %u\n", sectorsize);
166	}
167
168	lasthd = mediasize - sectorsize;
169	lseek(fd, lasthd, SEEK_SET);
170	error = read(fd, &kdhl, sizeof kdhl);
171	if (error != sizeof kdhl) {
172		warn("error reading last dump header at offset %lld in %s",
173		    (long long)lasthd, device);
174		goto closefd;
175	}
176	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
177		if (verbose)
178			warnx("magic mismatch on last dump header on %s",
179			    device);
180		goto closefd;
181	}
182	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
183		warnx("unknown version (%d) in last dump header on %s",
184		    dtoh32(kdhl.version), device);
185		goto closefd;
186	}
187
188	nfound++;
189	if (clear)
190		goto nuke;
191
192	if (kerneldump_parity(&kdhl)) {
193		warnx("parity error on last dump header on %s", device);
194		nerr++;
195		goto closefd;
196	}
197	dumpsize = dtoh64(kdhl.dumplength);
198	firsthd = lasthd - dumpsize - sizeof kdhf;
199	lseek(fd, firsthd, SEEK_SET);
200	error = read(fd, &kdhf, sizeof kdhf);
201	if (error != sizeof kdhf) {
202		warn("error reading first dump header at offset %lld in %s",
203		    (long long)firsthd, device);
204		nerr++;
205		goto closefd;
206	}
207	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
208		warn("first and last dump headers disagree on %s", device);
209		nerr++;
210		goto closefd;
211	}
212	md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL);
213	sprintf(buf, "%s.info", md5);
214
215	/*
216	 * See if the dump has been saved already. Don't save the dump
217	 * again, unless 'force' is in effect.
218	 */
219	if (stat(buf, &sb) == 0) {
220		if (!force) {
221			if (verbose)
222				printf("Dump on device %s already saved\n",
223				    device);
224			goto closefd;
225		}
226	} else if (errno != ENOENT) {
227		warn("error while checking for pre-saved core file");
228		nerr++;
229		goto closefd;
230	}
231
232	if (!check_space(savedir, dumpsize)) {
233		nerr++;
234		goto closefd;
235	}
236	/*
237	 * Create or overwrite any existing files.
238	 */
239	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
240	if (fdinfo < 0) {
241		warn("%s", buf);
242		nerr++;
243		goto closefd;
244	}
245	sprintf(buf, "%s.core", md5);
246	fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
247	if (fdcore < 0) {
248		warn("%s", buf);
249		close(fdinfo);
250		nerr++;
251		goto closefd;
252	}
253	info = fdopen(fdinfo, "w");
254
255	if (verbose)
256		printheader(stdout, &kdhl, device, md5);
257
258	printf("Saving dump to file %s\n", buf);
259
260	printheader(info, &kdhl, device, md5);
261
262	while (dumpsize > 0) {
263		wl = sizeof(buf);
264		if (wl > dumpsize)
265			wl = dumpsize;
266		error = read(fd, buf, wl);
267		if (error != wl) {
268			warn("read error on %s", device);
269			nerr++;
270			goto closeall;
271		}
272		error = write(fdcore, buf, wl);
273		if (error != wl) {
274			warn("write error on %s.core file", md5);
275			nerr++;
276			goto closeall;
277		}
278		dumpsize -= wl;
279	}
280	nsaved++;
281	close(fdinfo);
282	close(fdcore);
283
284	if (verbose)
285		printf("Dump saved\n");
286
287 nuke:
288	if (clear || !keep) {
289		if (verbose)
290			printf("Clearing dump header\n");
291		memset(&kdhl, 0, sizeof kdhl);
292		lseek(fd, lasthd, SEEK_SET);
293		error = write(fd, &kdhl, sizeof kdhl);
294		if (error != sizeof kdhl)
295			warn("error while clearing the dump header");
296	}
297	close(fd);
298	return;
299
300 closeall:
301	close(fdinfo);
302	close(fdcore);
303
304 closefd:
305	close(fd);
306}
307
308static void
309usage(void)
310{
311	fprintf(stderr, "usage: savecore [-cfkv] [directory [device...]]\n");
312	exit (1);
313}
314
315int
316main(int argc, char **argv)
317{
318	int i, ch, error;
319	struct fstab *fsp;
320	char *savedir;
321
322	savedir = strdup(".");
323	if (savedir == NULL)
324		errx(1, "Cannot allocate memory");
325	while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1)
326		switch(ch) {
327		case 'c':
328			clear = 1;
329			break;
330		case 'k':
331			keep = 1;
332			break;
333		case 'v':
334			verbose = 1;
335			break;
336		case 'f':
337			force = 1;
338			break;
339		case 'd':	/* Obsolete */
340		case 'N':
341		case 'z':
342		case '?':
343		default:
344			usage();
345		}
346	argc -= optind;
347	argv += optind;
348	if (argc >= 1) {
349		error = chdir(argv[0]);
350		if (error)
351			err(1, "chdir(%s)", argv[0]);
352		savedir = argv[0];
353		argc--;
354		argv++;
355	}
356	if (argc == 0) {
357		for (;;) {
358			fsp = getfsent();
359			if (fsp == NULL)
360				break;
361			if (strcmp(fsp->fs_vfstype, "swap") &&
362			    strcmp(fsp->fs_vfstype, "dump"))
363				continue;
364			DoFile(savedir, fsp->fs_spec);
365		}
366	} else {
367		for (i = 0; i < argc; i++)
368			DoFile(savedir, argv[i]);
369	}
370
371	/* Emit minimal output. */
372	if (nfound == 0)
373		printf("No dumps found\n");
374	else if (nsaved == 0) {
375		if (nerr != 0)
376			printf("Unsaved dumps found but not saved\n");
377		else
378			printf("No unsaved dumps found\n");
379	}
380
381	return (0);
382}
383