savecore.c revision 94580
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 * $FreeBSD: head/sbin/savecore/savecore.c 94580 2002-04-13 08:20:15Z marcel $
36 */
37
38#include <sys/types.h>
39#include <sys/disk.h>
40#include <sys/kerneldump.h>
41#include <sys/stat.h>
42#include <err.h>
43#include <errno.h>
44#include <fcntl.h>
45#include <fstab.h>
46#include <md5.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <time.h>
51#include <unistd.h>
52
53int clear, force, keep, verbose;	/* flags */
54int nfound, nsaved;			/* statistics */
55
56static void
57printheader(FILE *f, const struct kerneldumpheader *h, const char *device,
58    const char *md5)
59{
60	uint64_t dumplen;
61	time_t t;
62
63	fprintf(f, "Good dump found on device %s\n", device);
64	fprintf(f, "  Architecture: %s\n", h->architecture);
65	fprintf(f, "  Architecture version: %d\n",
66	    dtoh32(h->architectureversion));
67	dumplen = dtoh64(h->dumplength);
68	fprintf(f, "  Dump length: %lldB (%lld MB)\n", (long long)dumplen,
69	    (long long)(dumplen >> 20));
70	fprintf(f, "  Blocksize: %d\n", dtoh32(h->blocksize));
71	t = dtoh64(h->dumptime);
72	fprintf(f, "  Dumptime: %s", ctime(&t));
73	fprintf(f, "  Hostname: %s\n", h->hostname);
74	fprintf(f, "  Versionstring: %s", h->versionstring);
75	fprintf(f, "  Panicstring: %s\n", h->panicstring);
76	fprintf(f, "  MD5: %s\n", md5);
77}
78
79
80static void
81DoFile(const char *device)
82{
83	struct kerneldumpheader kdhf, kdhl;
84	char buf[BUFSIZ];
85	struct stat sb;
86	off_t mediasize, dumpsize, firsthd, lasthd;
87	char *md5;
88	FILE *info;
89	int fd, fdcore, fdinfo, error, wl;
90	u_int sectorsize;
91
92	if (verbose)
93		printf("Checking for kernel dump on device %s\n", device);
94
95	mediasize = 0;
96	fd = open(device, O_RDWR);
97	if (fd < 0) {
98		warn("%s", device);
99		return;
100	}
101	error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
102	if (!error)
103		error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
104	if (error) {
105		warn("Couldn't find media and/or sector size of %s)", device);
106		goto closefd;
107	}
108
109	if (verbose) {
110		printf("Mediasize = %lld\n", (long long)mediasize);
111		printf("Sectorsize = %u\n", sectorsize);
112	}
113
114	lasthd = mediasize - sectorsize;
115	lseek(fd, lasthd, SEEK_SET);
116	error = read(fd, &kdhl, sizeof kdhl);
117	if (error != sizeof kdhl) {
118		warn("Error reading last dump header at offset %lld in %s",
119		    (long long)lasthd, device);
120		goto closefd;
121	}
122	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
123		if (verbose)
124			warnx("Magic mismatch on last dump header on %s",
125			    device);
126		goto closefd;
127	}
128	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
129		warnx("Unknown version (%d) in last dump header on %s",
130		    dtoh32(kdhl.version), device);
131		goto closefd;
132	}
133
134	nfound++;
135	if (clear)
136		goto nuke;
137
138	if (kerneldump_parity(&kdhl)) {
139		warnx("Parity error on last dump header on %s", device);
140		goto closefd;
141	}
142	dumpsize = dtoh64(kdhl.dumplength);
143	firsthd = lasthd - dumpsize - sizeof kdhf;
144	lseek(fd, firsthd, SEEK_SET);
145	error = read(fd, &kdhf, sizeof kdhf);
146	if (error != sizeof kdhf) {
147		warn("Error reading first dump header at offset %lld in %s",
148		    (long long)firsthd, device);
149		goto closefd;
150	}
151	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
152		warn("First and last dump headers disagree on %s", device);
153		goto closefd;
154	}
155	md5 = MD5Data((unsigned char *)&kdhl, sizeof kdhl, NULL);
156	sprintf(buf, "%s.info", md5);
157
158	/*
159	 * See if the dump has been saved already. Don't save the dump
160	 * again, unless 'force' is in effect.
161	 */
162	if (stat(buf, &sb) == 0) {
163		if (!force) {
164			if (verbose)
165				printf("Dump on device %s already saved\n",
166				    device);
167			goto closefd;
168		}
169	} else if (errno != ENOENT) {
170		warn("Error while checking for pre-saved core file");
171		goto closefd;
172	}
173
174	/*
175	 * Create or overwrite any existing files.
176	 */
177	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
178	if (fdinfo < 0) {
179		warn("%s", buf);
180		goto closefd;
181	}
182	sprintf(buf, "%s.core", md5);
183	fdcore = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
184	if (fdcore < 0) {
185		warn("%s", buf);
186		close(fdinfo);
187		goto closefd;
188	}
189	info = fdopen(fdinfo, "w");
190
191	if (verbose)
192		printheader(stdout, &kdhl, device, md5);
193
194	printf("Saving dump to file %s\n", buf);
195	nsaved++;
196
197	printheader(info, &kdhl, device, md5);
198
199	while (dumpsize > 0) {
200		wl = sizeof(buf);
201		if (wl > dumpsize)
202			wl = dumpsize;
203		error = read(fd, buf, wl);
204		if (error != wl) {
205			warn("Read error on %s", device);
206			goto closeall;
207		}
208		error = write(fdcore, buf, wl);
209		if (error != wl) {
210			warn("Write error on %s.core file", md5);
211			goto closeall;
212		}
213		dumpsize -= wl;
214	}
215	close(fdinfo);
216	close(fdcore);
217
218	if (verbose)
219		printf("Dump saved\n");
220
221 nuke:
222	if (clear || !keep) {
223		if (verbose)
224			printf("Clearing dump header\n");
225		memset(&kdhl, 0, sizeof kdhl);
226		lseek(fd, lasthd, SEEK_SET);
227		error = write(fd, &kdhl, sizeof kdhl);
228		if (error != sizeof kdhl)
229			warn("Error while clearing the dump header");
230	}
231	close(fd);
232	return;
233
234 closeall:
235	close(fdinfo);
236	close(fdcore);
237
238 closefd:
239	close(fd);
240}
241
242static void
243usage(void)
244{
245	errx(1, "usage: savecore [-cfkv] [directory [device...]]");
246	exit (1);
247}
248
249int
250main(int argc, char **argv)
251{
252	int i, ch, error;
253	struct fstab *fsp;
254
255	while ((ch = getopt(argc, argv, "cdfkN:vz")) != -1)
256		switch(ch) {
257		case 'c':
258			clear = 1;
259			break;
260		case 'k':
261			keep = 1;
262			break;
263		case 'v':
264			verbose = 1;
265			break;
266		case 'f':
267			force = 1;
268			break;
269		case 'd':	/* Obsolete */
270		case 'N':
271		case 'z':
272		case '?':
273		default:
274			usage();
275		}
276	argc -= optind;
277	argv += optind;
278	if (argc >= 1) {
279		error = chdir(argv[0]);
280		if (error)
281			err(1, "chdir(%s)", argv[0]);
282		argc--;
283		argv++;
284	}
285	if (argc == 0) {
286		for (;;) {
287			fsp = getfsent();
288			if (fsp == NULL)
289				break;
290			if (strcmp(fsp->fs_vfstype, "swap") &&
291			    strcmp(fsp->fs_vfstype, "dump"))
292				continue;
293			DoFile(fsp->fs_spec);
294		}
295	} else {
296		for (i = 0; i < argc; i++)
297			DoFile(argv[i]);
298	}
299
300	/* Emit minimal output. */
301	if (nfound == 0)
302		printf("No dumps found\n");
303	else if (nsaved == 0)
304		printf("No unsaved dumps found\n");
305
306	return (0);
307}
308