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