xref: /netbsd-src/sbin/savecore/savecore.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: savecore.c,v 1.81 2009/08/18 04:02:39 dogcow Exp $	*/
2 
3 /*-
4  * Copyright (c) 1986, 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1986, 1992, 1993\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)savecore.c	8.5 (Berkeley) 4/28/95";
41 #else
42 __RCSID("$NetBSD: savecore.c,v 1.81 2009/08/18 04:02:39 dogcow Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #define _KSYMS_PRIVATE
47 
48 #include <stdbool.h>
49 
50 #include <sys/param.h>
51 #include <sys/mount.h>
52 #include <sys/msgbuf.h>
53 #include <sys/syslog.h>
54 #include <sys/time.h>
55 #include <sys/ksyms.h>
56 
57 #include <dirent.h>
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <nlist.h>
61 #include <paths.h>
62 #include <stddef.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <time.h>
67 #include <tzfile.h>
68 #include <unistd.h>
69 #include <util.h>
70 #include <limits.h>
71 #include <kvm.h>
72 
73 extern FILE *zopen(const char *fname, const char *mode);
74 
75 #define	KREAD(kd, addr, p)\
76 	(kvm_read(kd, addr, (char *)(p), sizeof(*(p))) != sizeof(*(p)))
77 
78 struct nlist current_nl[] = {	/* Namelist for currently running system. */
79 #define	X_DUMPDEV	0
80 	{ .n_name = "_dumpdev" },
81 #define	X_DUMPLO	1
82 	{ .n_name = "_dumplo" },
83 #define	X_TIME_SECOND	2
84 	{ .n_name = "_time_second" },
85 #define X_TIME		3
86 	{ .n_name = "_time" },
87 #define	X_DUMPSIZE	4
88 	{ .n_name = "_dumpsize" },
89 #define	X_VERSION	5
90 	{ .n_name = "_version" },
91 #define	X_DUMPMAG	6
92 	{ .n_name = "_dumpmag" },
93 #define	X_PANICSTR	7
94 	{ .n_name = "_panicstr" },
95 #define	X_PANICSTART	8
96 	{ .n_name = "_panicstart" },
97 #define	X_PANICEND	9
98 	{ .n_name = "_panicend" },
99 #define	X_MSGBUF	10
100 	{ .n_name = "_msgbufp" },
101 #define	X_DUMPCDEV	11
102 	{ .n_name = "_dumpcdev" },
103 #define X_SYMSZ		12
104 	{ .n_name = "_ksyms_symsz" },
105 #define X_STRSZ		13
106 	{ .n_name = "_ksyms_strsz" },
107 #define X_KHDR		14
108 	{ .n_name = "_ksyms_hdr" },
109 #define X_SYMTABS	15
110 	{ .n_name = "_ksyms_symtabs" },
111 	{ .n_name = NULL },
112 };
113 int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, X_DUMPCDEV, -1 };
114 int dumpsyms[] = { X_TIME_SECOND, X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR,
115     X_DUMPMAG, X_SYMSZ, X_STRSZ, X_KHDR, X_SYMTABS, -1 };
116 
117 struct nlist dump_nl[] = {	/* Name list for dumped system. */
118 	{ .n_name = "_dumpdev" },	/* Entries MUST be the same as */
119 	{ .n_name = "_dumplo" },	/*	those in current_nl[].  */
120 	{ .n_name = "_time_second" },
121 	{ .n_name = "_time" },
122 	{ .n_name = "_dumpsize" },
123 	{ .n_name = "_version" },
124 	{ .n_name = "_dumpmag" },
125 	{ .n_name = "_panicstr" },
126 	{ .n_name = "_panicstart" },
127 	{ .n_name = "_panicend" },
128 	{ .n_name = "_msgbufp" },
129 	{ .n_name = "_dumpcdev" },
130 	{ .n_name = "_ksyms_symsz" },
131 	{ .n_name = "_ksyms_strsz" },
132 	{ .n_name = "_ksyms_hdr" },
133 	{ .n_name = "_ksyms_symtabs" },
134 	{ .n_name = NULL },
135 };
136 
137 /* Types match kernel declarations. */
138 off_t	dumplo;				/* where dump starts on dumpdev */
139 u_int32_t dumpmag;			/* magic number in dump */
140 int	dumpsize;			/* amount of memory dumped */
141 off_t dumpbytes;			/* in bytes */
142 
143 const char	*kernel;		/* name of used kernel */
144 char	*dirname;			/* directory to save dumps in */
145 char	*ddname;			/* name of dump device */
146 dev_t	dumpdev;			/* dump device */
147 dev_t	dumpcdev = NODEV;		/* dump device (char equivalent) */
148 int	dumpfd;				/* read/write descriptor on dev */
149 kvm_t	*kd_dump;			/* kvm descriptor on dev	*/
150 time_t	now;				/* current date */
151 char	panic_mesg[1024];
152 long	panicstr;
153 char	vers[1024];
154 char	gzmode[3];
155 
156 static int	clear, compress, force, verbose;	/* flags */
157 
158 void	check_kmem(void);
159 int	check_space(void);
160 void	clear_dump(void);
161 int	Create(char *, int);
162 int	dump_exists(void);
163 char	*find_dev(dev_t, mode_t);
164 int	get_crashtime(void);
165 void	kmem_setup(void);
166 void	Lseek(int, off_t, int);
167 int	main(int, char *[]);
168 int	Open(const char *, int rw);
169 char	*rawname(char *s);
170 void	save_core(void);
171 void	usage(void);
172 void	Write(int, void *, int);
173 
174 int
175 main(int argc, char *argv[])
176 {
177 	int ch, level, testonly;
178 	char *ep;
179 
180 	dirname = NULL;
181 	kernel = NULL;
182 	level = 1;		/* default to fastest gzip compression */
183 	testonly = 0;
184 	gzmode[0] = 'w';
185 
186 	openlog("savecore", LOG_PERROR, LOG_DAEMON);
187 
188 	while ((ch = getopt(argc, argv, "cdfnN:vzZ:")) != -1)
189 		switch(ch) {
190 		case 'c':
191 			clear = 1;
192 			break;
193 		case 'd':		/* Not documented. */
194 		case 'v':
195 			verbose = 1;
196 			break;
197 		case 'f':
198 			force = 1;
199 			break;
200 		case 'n':
201 			testonly = 1;
202 			break;
203 		case 'N':
204 			kernel = optarg;
205 			break;
206 		case 'z':
207 			compress = 1;
208 			break;
209 		case 'Z':
210 			level = (int)strtol(optarg, &ep, 10);
211 			if (level < 0 || level > 9) {
212 				(void)syslog(LOG_ERR, "invalid compression %s",
213 				    optarg);
214 				usage();
215 			}
216 			break;
217 		case '?':
218 		default:
219 			usage();
220 		}
221 	argc -= optind;
222 	argv += optind;
223 
224 	if (argc != ((clear || testonly) ? 0 : 1))
225 		usage();
226 
227 	gzmode[1] = level + '0';
228 	if (!clear)
229 		dirname = argv[0];
230 
231 	(void)time(&now);
232 	kmem_setup();
233 
234 	if (clear && !testonly) {
235 		clear_dump();
236 		exit(0);
237 	}
238 
239 	if (!dump_exists() && !force)
240 		exit(1);
241 
242 	if (testonly)
243 		/* If -n was passed and there was a dump, exit at level 0 */
244 		exit(0);
245 
246 	check_kmem();
247 
248 	if (panicstr)
249 		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
250 	else
251 		syslog(LOG_ALERT, "reboot");
252 
253 	if ((!get_crashtime() || !check_space()) && !force)
254 		exit(1);
255 
256 	save_core();
257 
258 	clear_dump();
259 	exit(0);
260 }
261 
262 void
263 kmem_setup(void)
264 {
265 	kvm_t *kd_kern;
266 	char errbuf[_POSIX2_LINE_MAX];
267 	int i, hdrsz;
268 
269 	/*
270 	 * Some names we need for the currently running system, others for
271 	 * the system that was running when the dump was made.  The values
272 	 * obtained from the current system are used to look for things in
273 	 * /dev/kmem that cannot be found in the kernel namelist, but are
274 	 * presumed to be the same (since the disk partitions are probably
275 	 * the same!)
276 	 */
277 	kd_kern = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, errbuf);
278 	if (kd_kern == NULL) {
279 		syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
280 		exit(1);
281 	}
282 	if (kvm_nlist(kd_kern, current_nl) == -1)
283 		syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
284 		    kvm_geterr(kd_kern));
285 
286 	for (i = 0; cursyms[i] != -1; i++) {
287 		if (current_nl[cursyms[i]].n_value != 0)
288 			continue;
289 		switch (cursyms[i]) {
290 		case X_TIME_SECOND:
291 		case X_TIME:
292 		case X_DUMPCDEV:
293 			break;
294 		default:
295 			syslog(LOG_ERR, "%s: %s not in namelist",
296 			    kernel, current_nl[cursyms[i]].n_name);
297 			exit(1);
298 		}
299 	}
300 
301 	if (KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev) != 0) {
302 		if (verbose)
303 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
304 		exit(1);
305 	}
306 	if (dumpdev == NODEV) {
307 		syslog(LOG_WARNING, "no core dump (no dumpdev)");
308 		exit(1);
309 	}
310 	{
311 	    long l_dumplo;
312 
313 	    if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &l_dumplo) != 0) {
314 		    if (verbose)
315 			syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
316 		    exit(1);
317 	    }
318 	    if (l_dumplo == -1) {
319 		syslog(LOG_WARNING, "no core dump (invalid dumplo)");
320 		exit(1);
321 	    }
322 	    dumplo = DEV_BSIZE * (off_t) l_dumplo;
323 	}
324 
325 	if (verbose)
326 		(void)printf("dumplo = %lld (%ld * %ld)\n",
327 		    (long long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE);
328 	if (KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag) != 0) {
329 		if (verbose)
330 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
331 		exit(1);
332 	}
333 
334 	(void)kvm_read(kd_kern, current_nl[X_VERSION].n_value, vers,
335 	    sizeof(vers));
336 	vers[sizeof(vers) - 1] = '\0';
337 
338 	if (current_nl[X_DUMPCDEV].n_value != 0) {
339 		if (KREAD(kd_kern, current_nl[X_DUMPCDEV].n_value,
340 		    &dumpcdev) != 0) {
341 			if (verbose)
342 				syslog(LOG_WARNING, "kvm_read: %s",
343 			            kvm_geterr(kd_kern));
344 			exit(1);
345 		}
346 		ddname = find_dev(dumpcdev, S_IFCHR);
347 	} else
348 		ddname = find_dev(dumpdev, S_IFBLK);
349 	if (strncmp(ddname, "/dev/cons", 8) == 0 ||
350 	    strncmp(ddname, "/dev/tty", 7) == 0 ||
351 	    strncmp(ddname, "/dev/pty", 7) == 0 ||
352 	    strncmp(ddname, "/dev/pts", 7) == 0) {
353 		syslog(LOG_ERR, "dumpdev %s is tty; override kernel", ddname);
354 		exit(1);
355 	}
356 	dumpfd = Open(ddname, O_RDWR);
357 
358 	kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf);
359 	if (kd_dump == NULL) {
360 		syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
361 		exit(1);
362 	}
363 
364 	if (kvm_nlist(kd_dump, dump_nl) == -1)
365 		syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
366 		    kvm_geterr(kd_dump));
367 
368 	for (i = 0; dumpsyms[i] != -1; i++)
369 		if (dump_nl[dumpsyms[i]].n_value == 0 &&
370 			dumpsyms[i] != X_TIME_SECOND &&
371 			dumpsyms[i] != X_TIME) {
372 			syslog(LOG_ERR, "%s: %s not in namelist",
373 			    kernel, dump_nl[dumpsyms[i]].n_name);
374 			exit(1);
375 		}
376 	hdrsz = kvm_dump_mkheader(kd_dump, dumplo);
377 
378 	/*
379 	 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number
380 	 * checks, ergo no dump is present...
381 	 */
382 	if (hdrsz == 0) {
383 		syslog(LOG_WARNING, "no core dump");
384 		exit(1);
385 	}
386 	if (hdrsz == -1) {
387 		syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", kernel,
388 		    kvm_geterr(kd_dump));
389 		exit(1);
390 	}
391 	dumplo += hdrsz;
392 	kvm_close(kd_kern);
393 }
394 
395 void
396 check_kmem(void)
397 {
398 	char *cp, *bufdata;
399 	struct kern_msgbuf msgbuf, *bufp;
400 	long panicloc, panicstart, panicend;
401 	char core_vers[1024];
402 
403 	(void)kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers,
404 	    sizeof(core_vers));
405 	core_vers[sizeof(core_vers) - 1] = '\0';
406 
407 	if (strcmp(vers, core_vers) != 0)
408 		syslog(LOG_WARNING,
409 		    "warning: %s version mismatch:\n\t%s\nand\t%s\n",
410 		    kernel, vers, core_vers);
411 
412 	panicstart = panicend = 0;
413 	if (KREAD(kd_dump, dump_nl[X_PANICSTART].n_value, &panicstart) != 0) {
414 		if (verbose)
415 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
416 		goto nomsguf;
417 	}
418 	if (KREAD(kd_dump, dump_nl[X_PANICEND].n_value, &panicend) != 0) {
419 		if (verbose)
420 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
421 		goto nomsguf;
422 	}
423 	if (panicstart != 0 && panicend != 0) {
424 		if (KREAD(kd_dump, dump_nl[X_MSGBUF].n_value, &bufp)) {
425 			if (verbose)
426 				syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
427 			goto nomsguf;
428 		}
429 		if (kvm_read(kd_dump, (long)bufp, &msgbuf,
430 		    offsetof(struct kern_msgbuf, msg_bufc)) !=
431 		    offsetof(struct kern_msgbuf, msg_bufc)) {
432 			if (verbose)
433 				syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
434 			goto nomsguf;
435 		}
436 		if (msgbuf.msg_magic != MSG_MAGIC) {
437 			if (verbose)
438 				syslog(LOG_WARNING, "msgbuf magic incorrect");
439 			goto nomsguf;
440 		}
441 		bufdata = malloc(msgbuf.msg_bufs);
442 		if (bufdata == NULL) {
443 			if (verbose)
444 				syslog(LOG_WARNING, "couldn't allocate space for msgbuf data");
445 			goto nomsguf;
446 		}
447 		if (kvm_read(kd_dump, (long)&bufp->msg_bufc, bufdata,
448 		    msgbuf.msg_bufs) != msgbuf.msg_bufs) {
449 			if (verbose)
450 				syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
451 			free(bufdata);
452 			goto nomsguf;
453 		}
454 		cp = panic_mesg;
455 		while (panicstart != panicend && cp < &panic_mesg[sizeof(panic_mesg)-1]) {
456 			*cp++ = bufdata[panicstart];
457 			panicstart++;
458 			if (panicstart >= msgbuf.msg_bufs)
459 				panicstart = 0;
460 		}
461 		/* Don't end in a new-line */
462 		cp = &panic_mesg[strlen(panic_mesg)] - 1;
463 		if (*cp == '\n')
464 			*cp = '\0';
465 		panic_mesg[sizeof(panic_mesg) - 1] = '\0';
466 		free(bufdata);
467 
468 		panicstr = 1;	/* anything not zero */
469 		return;
470 	}
471 nomsguf:
472 	if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) {
473 		if (verbose)
474 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
475 		return;
476 	}
477 	if (panicstr) {
478 		cp = panic_mesg;
479 		panicloc = panicstr;
480 		do {
481 			if (KREAD(kd_dump, panicloc, cp) != 0) {
482 				if (verbose)
483 				    syslog(LOG_WARNING, "kvm_read: %s",
484 					kvm_geterr(kd_dump));
485 				break;
486 			}
487 			panicloc++;
488 		} while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]);
489 		panic_mesg[sizeof(panic_mesg) - 1] = '\0';
490 	}
491 }
492 
493 int
494 dump_exists(void)
495 {
496 	u_int32_t newdumpmag;
497 
498 	if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) {
499 		if (verbose)
500 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
501 		return (0);
502 	}
503 
504 	/* Read the dump size. */
505 	if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) {
506 		if (verbose)
507 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
508 		return (0);
509 	}
510 	dumpbytes = (off_t)dumpsize * getpagesize();
511 
512 	/*
513 	 * Return zero if core dump doesn't seem to be there, and note
514 	 * it for syslog.  This check and return happens after the dump size
515 	 * is read, so dumpsize is whether or not the core is valid (for -f).
516 	 */
517 	if (newdumpmag != dumpmag) {
518 		if (verbose)
519 			syslog(LOG_WARNING,
520 			    "magic number mismatch (0x%x != 0x%x)",
521 			    newdumpmag, dumpmag);
522 		syslog(LOG_WARNING, "no core dump");
523 		return (0);
524 	}
525 	return (1);
526 }
527 
528 void
529 clear_dump(void)
530 {
531 	if (kvm_dump_inval(kd_dump) == -1)
532 		syslog(LOG_ERR, "%s: kvm_dump_inval: %s", ddname,
533 		    kvm_geterr(kd_dump));
534 
535 }
536 
537 char buf[1024 * 1024];
538 
539 static void
540 save_kernel(int ofd, FILE *fp, char *path)
541 {
542 	int nw, nr, ifd;
543 
544 	ifd = Open(kernel, O_RDONLY);
545 	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
546 		if (compress)
547 			nw = fwrite(buf, 1, nr, fp);
548 		else
549 			nw = write(ofd, buf, nr);
550 		if (nw != nr) {
551 			syslog(LOG_ERR, "%s: %s",
552 			    path, strerror(nw == 0 ? EIO : errno));
553 			syslog(LOG_WARNING,
554 			    "WARNING: kernel may be incomplete");
555 			exit(1);
556 		}
557 	}
558 	if (nr < 0) {
559 		syslog(LOG_ERR, "%s: %m", kernel);
560 		syslog(LOG_WARNING, "WARNING: kernel may be incomplete");
561 		exit(1);
562 	}
563 }
564 
565 static int
566 ksymsget(u_long addr, void *ptr, size_t size)
567 {
568 
569 	if ((size_t)kvm_read(kd_dump, addr, ptr, size) != size) {
570 		if (verbose)
571 			syslog(LOG_WARNING, "kvm_read: %s",
572 			    kvm_geterr(kd_dump));
573 		return 1;
574 	}
575 	return 0;
576 }
577 
578 static int
579 save_ksyms(int ofd, FILE *fp, char *path)
580 {
581 	struct ksyms_hdr khdr;
582 	int nw, symsz, strsz;
583 	TAILQ_HEAD(, ksyms_symtab) symtabs;
584 	struct ksyms_symtab st, *stptr;
585 	void *p;
586 
587 	/* Get basic info and ELF headers, check if ksyms was on. */
588 	if (ksymsget(dump_nl[X_KHDR].n_value, &khdr, sizeof(khdr)))
589 		return 1;
590 	if (ksymsget(dump_nl[X_SYMSZ].n_value, &symsz, sizeof(symsz)))
591 		return 1;
592 	if (ksymsget(dump_nl[X_STRSZ].n_value, &strsz, sizeof(strsz)))
593 		return 1;
594 	if (symsz == 0 || strsz == 0)
595 		return 1;
596 
597 	/* Update the ELF section headers for symbols/strings. */
598 	khdr.kh_shdr[SYMTAB].sh_size = symsz;
599 	khdr.kh_shdr[SYMTAB].sh_info = symsz / sizeof(Elf_Sym);
600 	khdr.kh_shdr[STRTAB].sh_offset = symsz +
601 	    khdr.kh_shdr[SYMTAB].sh_offset;
602 	khdr.kh_shdr[STRTAB].sh_size = strsz;
603 
604 	/* Write out the ELF headers. */
605 	if (compress)
606 		nw = fwrite(&khdr, 1, sizeof(khdr), fp);
607 	else
608 		nw = write(ofd, &khdr, sizeof(khdr));
609 	if (nw != sizeof(khdr)) {
610 		syslog(LOG_ERR, "%s: %s",
611 		    path, strerror(nw == 0 ? EIO : errno));
612 		syslog(LOG_WARNING,
613 		    "WARNING: kernel may be incomplete");
614 		exit(1);
615         }
616 
617         /* Dump symbol table. */
618 	if (ksymsget(dump_nl[X_SYMTABS].n_value, &symtabs, sizeof(symtabs)))
619 		return 1;
620 	stptr = TAILQ_FIRST(&symtabs);
621 	while (stptr != NULL) {
622 		if (ksymsget((u_long)stptr, &st, sizeof(st)))
623 			return 1;
624 		stptr = TAILQ_NEXT(&st, sd_queue);
625 		if ((p = malloc(st.sd_symsize)) == NULL)
626 			return 1;
627 		if (ksymsget((u_long)st.sd_symstart, p, st.sd_symsize)) {
628 			free(p);
629 			return 1;
630 		}
631 		if (compress)
632 			nw = fwrite(p, 1, st.sd_symsize, fp);
633 		else
634 			nw = write(ofd, p, st.sd_symsize);
635 		free(p);
636 		if (nw != st.sd_symsize) {
637 			syslog(LOG_ERR, "%s: %s",
638 			    path, strerror(nw == 0 ? EIO : errno));
639 			syslog(LOG_WARNING,
640 			    "WARNING: kernel may be incomplete");
641 			exit(1);
642 		}
643 	}
644 
645 	/* Dump string table. */
646 	if (ksymsget(dump_nl[X_SYMTABS].n_value, &symtabs, sizeof(symtabs)))
647 		return 1;
648 	stptr = TAILQ_FIRST(&symtabs);
649 	while (stptr != NULL) {
650 		if (ksymsget((u_long)stptr, &st, sizeof(st)))
651 			return 1;
652 		stptr = TAILQ_NEXT(&st, sd_queue);
653 		if ((p = malloc(st.sd_symsize)) == NULL)
654 			return 1;
655 		if (ksymsget((u_long)st.sd_strstart, p, st.sd_strsize)) {
656 			free(p);
657 			return 1;
658 		}
659 		if (compress)
660 			nw = fwrite(p, 1, st.sd_strsize, fp);
661 		else
662 			nw = write(ofd, p, st.sd_strsize);
663 		free(p);
664 		if (nw != st.sd_strsize) {
665 			syslog(LOG_ERR, "%s: %s",
666 			    path, strerror(nw == 0 ? EIO : errno));
667 			syslog(LOG_WARNING,
668 			    "WARNING: kernel may be incomplete");
669 			exit(1);
670 		}
671 	}
672 
673 	return 0;
674 }
675 
676 void
677 save_core(void)
678 {
679 	FILE *fp;
680 	int bounds, ifd, nr, nw, ofd, tryksyms;
681 	char *rawp, path[MAXPATHLEN];
682 
683 	ofd = -1;
684 	/*
685 	 * Get the current number and update the bounds file.  Do the update
686 	 * now, because may fail later and don't want to overwrite anything.
687 	 */
688 	umask(066);
689 	(void)snprintf(path, sizeof(path), "%s/bounds", dirname);
690 	if ((fp = fopen(path, "r")) == NULL)
691 		goto err1;
692 	if (fgets(buf, sizeof(buf), fp) == NULL) {
693 		if (ferror(fp))
694 err1:			syslog(LOG_WARNING, "%s: %m", path);
695 		bounds = 0;
696 	} else
697 		bounds = atoi(buf);
698 	if (fp != NULL)
699 		(void)fclose(fp);
700 	if ((fp = fopen(path, "w")) == NULL)
701 		syslog(LOG_ERR, "%s: %m", path);
702 	else {
703 		(void)fprintf(fp, "%d\n", bounds + 1);
704 		(void)fclose(fp);
705 	}
706 
707 	/* Create the core file. */
708 	(void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s",
709 	    dirname, bounds, compress ? ".gz" : "");
710 	if (compress) {
711 		if ((fp = zopen(path, gzmode)) == NULL) {
712 			syslog(LOG_ERR, "%s: %m", path);
713 			exit(1);
714 		}
715 	} else {
716 		ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
717 		fp  = fdopen(ofd, "w");
718 		if (fp == NULL) {
719 			syslog(LOG_ERR, "%s: fdopen: %m", path);
720 			exit(1);
721 		}
722 	}
723 
724 	if (dumpcdev == NODEV) {
725 		/* Open the raw device. */
726 		rawp = rawname(ddname);
727 		if ((ifd = open(rawp, O_RDONLY)) == -1) {
728 			syslog(LOG_WARNING, "%s: %m; using block device",
729 			    rawp);
730 			ifd = dumpfd;
731 		}
732 	} else {
733 		rawp = ddname;
734 		ifd = dumpfd;
735 	}
736 
737 	/* Seek to the start of the core. */
738 	Lseek(ifd, dumplo, SEEK_SET);
739 
740 	if (kvm_dump_wrtheader(kd_dump, fp, (int32_t)dumpbytes) == -1) {
741 		syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path,
742 		    kvm_geterr(kd_dump));
743 		exit(1);
744 	}
745 
746 	/* Copy the core file. */
747 	syslog(LOG_NOTICE, "writing %score to %s",
748 	    compress ? "compressed " : "", path);
749 	for (; dumpbytes > (off_t)0; dumpbytes -= (off_t)nr) {
750 		char nbuf[7];
751 		humanize_number(nbuf, 7, dumpbytes, "", HN_AUTOSCALE, 0);
752 		(void)printf("%7s\r", nbuf);
753 		(void)fflush(stdout);
754 		nr = read(ifd, buf, MIN(dumpbytes, (off_t)sizeof(buf)));
755 		if (nr <= 0) {
756 			if (nr == 0)
757 				syslog(LOG_WARNING,
758 				    "WARNING: EOF on dump device");
759 			else
760 				syslog(LOG_ERR, "%s: %m", rawp);
761 			goto err2;
762 		}
763 		nw = fwrite(buf, 1, nr, fp);
764 		if (nw != nr) {
765 			syslog(LOG_ERR, "%s: %s",
766 			    path, strerror(nw == 0 ? EIO : errno));
767 err2:			syslog(LOG_WARNING,
768 			    "WARNING: core may be incomplete");
769 			(void)printf("\n");
770 			exit(1);
771 		}
772 	}
773 	if (dumpcdev == NODEV)
774 		(void)close(ifd);
775 	(void)fclose(fp);
776 
777 	/* Create a kernel. */
778 	(void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
779 	    dirname, bounds, compress ? ".gz" : "");
780 	syslog(LOG_NOTICE, "writing %skernel to %s",
781 	    compress ? "compressed " : "", path);
782 	for (tryksyms = 1;; tryksyms = 0) {
783 		if (compress) {
784 			if ((fp = zopen(path, gzmode)) == NULL) {
785 				syslog(LOG_ERR, "%s: %m", path);
786 				exit(1);
787 			}
788 		} else
789 			ofd = Create(path, S_IRUSR | S_IWUSR);
790 		if (tryksyms) {
791 			if (!save_ksyms(ofd, fp, path))
792 				break;
793 			if (compress)
794 				(void)fclose(fp);
795 			else
796 				(void)close(ofd);
797 			unlink(path);
798 		} else {
799 			save_kernel(ofd, fp, path);
800 			break;
801 		}
802 	}
803 	if (compress)
804 		(void)fclose(fp);
805 	else
806 		(void)close(ofd);
807 
808 	/*
809 	 * For development systems where the crash occurs during boot
810 	 * to multiuser.
811 	 */
812 	sync();
813 	sleep(1);
814 	sync();
815 	sleep(1);
816 }
817 
818 char *
819 find_dev(dev_t dev, mode_t type)
820 {
821 	DIR *dfd;
822 	struct dirent *dir;
823 	struct stat sb;
824 	char *dp, device[MAXPATHLEN + 1], *p;
825 	size_t l;
826 
827 	if ((dfd = opendir(_PATH_DEV)) == NULL) {
828 		syslog(LOG_ERR, "%s: %m", _PATH_DEV);
829 		exit(1);
830 	}
831 	strlcpy(device, _PATH_DEV, sizeof(device));
832 	p = &device[strlen(device)];
833 	l = sizeof(device) - strlen(device);
834 	while ((dir = readdir(dfd))) {
835 		strlcpy(p, dir->d_name, l);
836 		if (lstat(device, &sb)) {
837 			syslog(LOG_ERR, "%s: %m", device);
838 			continue;
839 		}
840 		if ((sb.st_mode & S_IFMT) != type)
841 			continue;
842 		if (dev == sb.st_rdev) {
843 			closedir(dfd);
844 			if ((dp = strdup(device)) == NULL) {
845 				syslog(LOG_ERR, "%m");
846 				exit(1);
847 			}
848 			return (dp);
849 		}
850 	}
851 	closedir(dfd);
852 	syslog(LOG_ERR, "can't find device %lld/%lld",
853 	    (long long)major(dev), (long long)minor(dev));
854 	exit(1);
855 }
856 
857 char *
858 rawname(char *s)
859 {
860 	char *sl;
861 	char name[MAXPATHLEN];
862 
863 	if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
864 		syslog(LOG_ERR,
865 		    "can't make raw dump device name from %s", s);
866 		return (s);
867 	}
868 	(void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s,
869 	    sl + 1);
870 	if ((sl = strdup(name)) == NULL) {
871 		syslog(LOG_ERR, "%m");
872 		exit(1);
873 	}
874 	return (sl);
875 }
876 
877 int
878 get_crashtime(void)
879 {
880 	time_t dumptime;			/* Time the dump was taken. */
881 	struct timeval dtime;
882 
883 	if (KREAD(kd_dump, dump_nl[X_TIME_SECOND].n_value, &dumptime) != 0) {
884 		if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &dtime) != 0) {
885 			if (verbose)
886 				syslog(LOG_WARNING, "kvm_read: %s (and _time_second is not defined also)", kvm_geterr(kd_dump));
887 			return (0);
888 		}
889 		dumptime = dtime.tv_sec;
890 	}
891 	if (dumptime == 0) {
892 		if (verbose)
893 			syslog(LOG_ERR, "dump time is zero");
894 		return (0);
895 	}
896 	(void)printf("savecore: system went down at %s", ctime(&dumptime));
897 #define	LEEWAY	(60 * SECSPERDAY)
898 	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
899 		(void)printf("dump time is unreasonable\n");
900 		return (0);
901 	}
902 	return (1);
903 }
904 
905 int
906 check_space(void)
907 {
908 	FILE *fp;
909 	off_t minfree, spacefree, kernelsize, needed;
910 	struct stat st;
911 	struct statvfs fsbuf;
912 	char mbuf[100], path[MAXPATHLEN];
913 
914 	/* XXX assume a reasonable default, unless we find a kernel. */
915 	kernelsize = 20 * 1024 * 1024;
916 	if (!stat(kernel, &st)) kernelsize = st.st_blocks * S_BLKSIZE;
917 	if (statvfs(dirname, &fsbuf) < 0) {
918 		syslog(LOG_ERR, "%s: %m", dirname);
919 		exit(1);
920 	}
921 	spacefree = fsbuf.f_bavail;
922 	spacefree *= fsbuf.f_frsize;
923 	spacefree /= 1024;
924 
925 	(void)snprintf(path, sizeof(path), "%s/minfree", dirname);
926 	if ((fp = fopen(path, "r")) == NULL)
927 		minfree = 0;
928 	else {
929 		if (fgets(mbuf, sizeof(mbuf), fp) == NULL)
930 			minfree = 0;
931 		else
932 			minfree = atoi(mbuf);
933 		(void)fclose(fp);
934 	}
935 
936 	needed = (dumpbytes + kernelsize) / 1024;
937  	if (minfree > 0 && spacefree - needed < minfree) {
938 		syslog(LOG_WARNING,
939 		    "no dump, not enough free space in %s", dirname);
940 		return (0);
941 	}
942 	if (spacefree - needed < minfree)
943 		syslog(LOG_WARNING,
944 		    "dump performed, but free space threshold crossed");
945 	return (1);
946 }
947 
948 int
949 Open(const char *name, int rw)
950 {
951 	int fd;
952 
953 	if ((fd = open(name, rw, 0)) < 0) {
954 		syslog(LOG_ERR, "%s: %m", name);
955 		exit(1);
956 	}
957 	return (fd);
958 }
959 
960 void
961 Lseek(int fd, off_t off, int flag)
962 {
963 	off_t ret;
964 
965 	ret = lseek(fd, off, flag);
966 	if (ret == -1) {
967 		syslog(LOG_ERR, "lseek: %m");
968 		exit(1);
969 	}
970 }
971 
972 int
973 Create(char *file, int mode)
974 {
975 	int fd;
976 
977 	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
978 	if (fd < 0) {
979 		syslog(LOG_ERR, "%s: %m", file);
980 		exit(1);
981 	}
982 	return (fd);
983 }
984 
985 void
986 Write(int fd, void *bp, int size)
987 {
988 	int n;
989 
990 	if ((n = write(fd, bp, size)) < size) {
991 		syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
992 		exit(1);
993 	}
994 }
995 
996 void
997 usage(void)
998 {
999 	(void)syslog(LOG_ERR,
1000 	    "usage: savecore [-cfnvz] [-N system] [-Z level] directory");
1001 	exit(1);
1002 }
1003