xref: /netbsd-src/sbin/savecore/savecore.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /*	$NetBSD: savecore.c,v 1.78 2008/12/28 20:17:11 christos 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.78 2008/12/28 20:17:11 christos 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, int);
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 	if (kernel == NULL) {
232 		kernel = getbootfile();
233 	}
234 
235 	(void)time(&now);
236 	kmem_setup();
237 
238 	if (clear && !testonly) {
239 		clear_dump();
240 		exit(0);
241 	}
242 
243 	if (!dump_exists() && !force)
244 		exit(1);
245 
246 	if (testonly)
247 		/* If -n was passed and there was a dump, exit at level 0 */
248 		exit(0);
249 
250 	check_kmem();
251 
252 	if (panicstr)
253 		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
254 	else
255 		syslog(LOG_ALERT, "reboot");
256 
257 	if ((!get_crashtime() || !check_space()) && !force)
258 		exit(1);
259 
260 	save_core();
261 
262 	clear_dump();
263 	exit(0);
264 }
265 
266 void
267 kmem_setup(void)
268 {
269 	kvm_t *kd_kern;
270 	char errbuf[_POSIX2_LINE_MAX];
271 	int i, hdrsz;
272 
273 	/*
274 	 * Some names we need for the currently running system, others for
275 	 * the system that was running when the dump was made.  The values
276 	 * obtained from the current system are used to look for things in
277 	 * /dev/kmem that cannot be found in the kernel namelist, but are
278 	 * presumed to be the same (since the disk partitions are probably
279 	 * the same!)
280 	 */
281 	kd_kern = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, errbuf);
282 	if (kd_kern == NULL) {
283 		syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
284 		exit(1);
285 	}
286 	if (kvm_nlist(kd_kern, current_nl) == -1)
287 		syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
288 		    kvm_geterr(kd_kern));
289 
290 	for (i = 0; cursyms[i] != -1; i++) {
291 		if (current_nl[cursyms[i]].n_value != 0)
292 			continue;
293 		switch (cursyms[i]) {
294 		case X_TIME_SECOND:
295 		case X_TIME:
296 		case X_DUMPCDEV:
297 			break;
298 		default:
299 			syslog(LOG_ERR, "%s: %s not in namelist",
300 			    kernel, current_nl[cursyms[i]].n_name);
301 			exit(1);
302 		}
303 	}
304 
305 	if (KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev) != 0) {
306 		if (verbose)
307 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
308 		exit(1);
309 	}
310 	if (dumpdev == NODEV) {
311 		syslog(LOG_WARNING, "no core dump (no dumpdev)");
312 		exit(1);
313 	}
314 	{
315 	    long l_dumplo;
316 
317 	    if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &l_dumplo) != 0) {
318 		    if (verbose)
319 			syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
320 		    exit(1);
321 	    }
322 	    if (l_dumplo == -1) {
323 		syslog(LOG_WARNING, "no core dump (invalid dumplo)");
324 		exit(1);
325 	    }
326 	    dumplo = DEV_BSIZE * (off_t) l_dumplo;
327 	}
328 
329 	if (verbose)
330 		(void)printf("dumplo = %lld (%ld * %ld)\n",
331 		    (long long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE);
332 	if (KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag) != 0) {
333 		if (verbose)
334 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
335 		exit(1);
336 	}
337 
338 	(void)kvm_read(kd_kern, current_nl[X_VERSION].n_value, vers,
339 	    sizeof(vers));
340 	vers[sizeof(vers) - 1] = '\0';
341 
342 	if (current_nl[X_DUMPCDEV].n_value != 0) {
343 		if (KREAD(kd_kern, current_nl[X_DUMPCDEV].n_value,
344 		    &dumpcdev) != 0) {
345 			if (verbose)
346 				syslog(LOG_WARNING, "kvm_read: %s",
347 			            kvm_geterr(kd_kern));
348 			exit(1);
349 		}
350 		ddname = find_dev(dumpcdev, S_IFCHR);
351 	} else
352 		ddname = find_dev(dumpdev, S_IFBLK);
353 	if (strncmp(ddname, "/dev/cons", 8) == 0 ||
354 	    strncmp(ddname, "/dev/tty", 7) == 0 ||
355 	    strncmp(ddname, "/dev/pty", 7) == 0 ||
356 	    strncmp(ddname, "/dev/pts", 7) == 0) {
357 		syslog(LOG_ERR, "dumpdev %s is tty; override kernel", ddname);
358 		exit(1);
359 	}
360 	dumpfd = Open(ddname, O_RDWR);
361 
362 	kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf);
363 	if (kd_dump == NULL) {
364 		syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
365 		exit(1);
366 	}
367 
368 	if (kvm_nlist(kd_dump, dump_nl) == -1)
369 		syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
370 		    kvm_geterr(kd_dump));
371 
372 	for (i = 0; dumpsyms[i] != -1; i++)
373 		if (dump_nl[dumpsyms[i]].n_value == 0 &&
374 			dumpsyms[i] != X_TIME_SECOND &&
375 			dumpsyms[i] != X_TIME) {
376 			syslog(LOG_ERR, "%s: %s not in namelist",
377 			    kernel, dump_nl[dumpsyms[i]].n_name);
378 			exit(1);
379 		}
380 	hdrsz = kvm_dump_mkheader(kd_dump, dumplo);
381 
382 	/*
383 	 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number
384 	 * checks, ergo no dump is present...
385 	 */
386 	if (hdrsz == 0) {
387 		syslog(LOG_WARNING, "no core dump");
388 		exit(1);
389 	}
390 	if (hdrsz == -1) {
391 		syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", kernel,
392 		    kvm_geterr(kd_dump));
393 		exit(1);
394 	}
395 	dumplo += hdrsz;
396 	kvm_close(kd_kern);
397 }
398 
399 void
400 check_kmem(void)
401 {
402 	char *cp, *bufdata;
403 	struct kern_msgbuf msgbuf, *bufp;
404 	long panicloc, panicstart, panicend;
405 	char core_vers[1024];
406 
407 	(void)kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers,
408 	    sizeof(core_vers));
409 	core_vers[sizeof(core_vers) - 1] = '\0';
410 
411 	if (strcmp(vers, core_vers) != 0)
412 		syslog(LOG_WARNING,
413 		    "warning: %s version mismatch:\n\t%s\nand\t%s\n",
414 		    kernel, vers, core_vers);
415 
416 	panicstart = panicend = 0;
417 	if (KREAD(kd_dump, dump_nl[X_PANICSTART].n_value, &panicstart) != 0) {
418 		if (verbose)
419 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
420 		goto nomsguf;
421 	}
422 	if (KREAD(kd_dump, dump_nl[X_PANICEND].n_value, &panicend) != 0) {
423 		if (verbose)
424 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
425 		goto nomsguf;
426 	}
427 	if (panicstart != 0 && panicend != 0) {
428 		if (KREAD(kd_dump, dump_nl[X_MSGBUF].n_value, &bufp)) {
429 			if (verbose)
430 				syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
431 			goto nomsguf;
432 		}
433 		if (kvm_read(kd_dump, (long)bufp, &msgbuf,
434 		    offsetof(struct kern_msgbuf, msg_bufc)) !=
435 		    offsetof(struct kern_msgbuf, msg_bufc)) {
436 			if (verbose)
437 				syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
438 			goto nomsguf;
439 		}
440 		if (msgbuf.msg_magic != MSG_MAGIC) {
441 			if (verbose)
442 				syslog(LOG_WARNING, "msgbuf magic incorrect");
443 			goto nomsguf;
444 		}
445 		bufdata = malloc(msgbuf.msg_bufs);
446 		if (bufdata == NULL) {
447 			if (verbose)
448 				syslog(LOG_WARNING, "couldn't allocate space for msgbuf data");
449 			goto nomsguf;
450 		}
451 		if (kvm_read(kd_dump, (long)&bufp->msg_bufc, bufdata,
452 		    msgbuf.msg_bufs) != msgbuf.msg_bufs) {
453 			if (verbose)
454 				syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
455 			free(bufdata);
456 			goto nomsguf;
457 		}
458 		cp = panic_mesg;
459 		while (panicstart != panicend && cp < &panic_mesg[sizeof(panic_mesg)-1]) {
460 			*cp++ = bufdata[panicstart];
461 			panicstart++;
462 			if (panicstart >= msgbuf.msg_bufs)
463 				panicstart = 0;
464 		}
465 		/* Don't end in a new-line */
466 		cp = &panic_mesg[strlen(panic_mesg)] - 1;
467 		if (*cp == '\n')
468 			*cp = '\0';
469 		panic_mesg[sizeof(panic_mesg) - 1] = '\0';
470 		free(bufdata);
471 
472 		panicstr = 1;	/* anything not zero */
473 		return;
474 	}
475 nomsguf:
476 	if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) {
477 		if (verbose)
478 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
479 		return;
480 	}
481 	if (panicstr) {
482 		cp = panic_mesg;
483 		panicloc = panicstr;
484 		do {
485 			if (KREAD(kd_dump, panicloc, cp) != 0) {
486 				if (verbose)
487 				    syslog(LOG_WARNING, "kvm_read: %s",
488 					kvm_geterr(kd_dump));
489 				break;
490 			}
491 			panicloc++;
492 		} while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]);
493 		panic_mesg[sizeof(panic_mesg) - 1] = '\0';
494 	}
495 }
496 
497 int
498 dump_exists(void)
499 {
500 	u_int32_t newdumpmag;
501 
502 	if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) {
503 		if (verbose)
504 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
505 		return (0);
506 	}
507 
508 	/* Read the dump size. */
509 	if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) {
510 		if (verbose)
511 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
512 		return (0);
513 	}
514 	dumpbytes = (off_t)dumpsize * getpagesize();
515 
516 	/*
517 	 * Return zero if core dump doesn't seem to be there, and note
518 	 * it for syslog.  This check and return happens after the dump size
519 	 * is read, so dumpsize is whether or not the core is valid (for -f).
520 	 */
521 	if (newdumpmag != dumpmag) {
522 		if (verbose)
523 			syslog(LOG_WARNING,
524 			    "magic number mismatch (0x%x != 0x%x)",
525 			    newdumpmag, dumpmag);
526 		syslog(LOG_WARNING, "no core dump");
527 		return (0);
528 	}
529 	return (1);
530 }
531 
532 void
533 clear_dump(void)
534 {
535 	if (kvm_dump_inval(kd_dump) == -1)
536 		syslog(LOG_ERR, "%s: kvm_dump_inval: %s", ddname,
537 		    kvm_geterr(kd_dump));
538 
539 }
540 
541 char buf[1024 * 1024];
542 
543 static void
544 save_kernel(int ofd, FILE *fp, char *path)
545 {
546 	int nw, nr, ifd;
547 
548 	ifd = Open(kernel, O_RDONLY);
549 	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
550 		if (compress)
551 			nw = fwrite(buf, 1, nr, fp);
552 		else
553 			nw = write(ofd, buf, nr);
554 		if (nw != nr) {
555 			syslog(LOG_ERR, "%s: %s",
556 			    path, strerror(nw == 0 ? EIO : errno));
557 			syslog(LOG_WARNING,
558 			    "WARNING: kernel may be incomplete");
559 			exit(1);
560 		}
561 	}
562 	if (nr < 0) {
563 		syslog(LOG_ERR, "%s: %m", kernel);
564 		syslog(LOG_WARNING, "WARNING: kernel may be incomplete");
565 		exit(1);
566 	}
567 }
568 
569 static int
570 ksymsget(u_long addr, void *ptr, size_t size)
571 {
572 
573 	if (kvm_read(kd_dump, addr, ptr, size) != size) {
574 		if (verbose)
575 			syslog(LOG_WARNING, "kvm_read: %s",
576 			    kvm_geterr(kd_dump));
577 		return 1;
578 	}
579 	return 0;
580 }
581 
582 static int
583 save_ksyms(int ofd, FILE *fp, char *path)
584 {
585 	struct ksyms_hdr khdr;
586 	int nw, symsz, strsz;
587 	TAILQ_HEAD(, ksyms_symtab) symtabs;
588 	struct ksyms_symtab st, *stptr;
589 	void *p;
590 
591 	/* Get basic info and ELF headers, check if ksyms was on. */
592 	if (ksymsget(dump_nl[X_KHDR].n_value, &khdr, sizeof(khdr)))
593 		return 1;
594 	if (ksymsget(dump_nl[X_SYMSZ].n_value, &symsz, sizeof(symsz)))
595 		return 1;
596 	if (ksymsget(dump_nl[X_STRSZ].n_value, &strsz, sizeof(strsz)))
597 		return 1;
598 	if (symsz == 0 || strsz == 0)
599 		return 1;
600 
601 	/* Update the ELF section headers for symbols/strings. */
602 	khdr.kh_shdr[SYMTAB].sh_size = symsz;
603 	khdr.kh_shdr[SYMTAB].sh_info = symsz / sizeof(Elf_Sym);
604 	khdr.kh_shdr[STRTAB].sh_offset = symsz +
605 	    khdr.kh_shdr[SYMTAB].sh_offset;
606 	khdr.kh_shdr[STRTAB].sh_size = strsz;
607 
608 	/* Write out the ELF headers. */
609 	if (compress)
610 		nw = fwrite(&khdr, 1, sizeof(khdr), fp);
611 	else
612 		nw = write(ofd, &khdr, sizeof(khdr));
613 	if (nw != sizeof(khdr)) {
614 		syslog(LOG_ERR, "%s: %s",
615 		    path, strerror(nw == 0 ? EIO : errno));
616 		syslog(LOG_WARNING,
617 		    "WARNING: kernel may be incomplete");
618 		exit(1);
619         }
620 
621         /* Dump symbol table. */
622 	if (ksymsget(dump_nl[X_SYMTABS].n_value, &symtabs, sizeof(symtabs)))
623 		return 1;
624 	stptr = TAILQ_FIRST(&symtabs);
625 	while (stptr != NULL) {
626 		if (ksymsget((u_long)stptr, &st, sizeof(st)))
627 			return 1;
628 		stptr = TAILQ_NEXT(&st, sd_queue);
629 		if ((p = malloc(st.sd_symsize)) == NULL)
630 			return 1;
631 		if (ksymsget((u_long)st.sd_symstart, p, st.sd_symsize)) {
632 			free(p);
633 			return 1;
634 		}
635 		if (compress)
636 			nw = fwrite(p, 1, st.sd_symsize, fp);
637 		else
638 			nw = write(ofd, p, st.sd_symsize);
639 		free(p);
640 		if (nw != st.sd_symsize) {
641 			syslog(LOG_ERR, "%s: %s",
642 			    path, strerror(nw == 0 ? EIO : errno));
643 			syslog(LOG_WARNING,
644 			    "WARNING: kernel may be incomplete");
645 			exit(1);
646 		}
647 	}
648 
649 	/* Dump string table. */
650 	if (ksymsget(dump_nl[X_SYMTABS].n_value, &symtabs, sizeof(symtabs)))
651 		return 1;
652 	stptr = TAILQ_FIRST(&symtabs);
653 	while (stptr != NULL) {
654 		if (ksymsget((u_long)stptr, &st, sizeof(st)))
655 			return 1;
656 		stptr = TAILQ_NEXT(&st, sd_queue);
657 		if ((p = malloc(st.sd_symsize)) == NULL)
658 			return 1;
659 		if (ksymsget((u_long)st.sd_strstart, p, st.sd_strsize)) {
660 			free(p);
661 			return 1;
662 		}
663 		if (compress)
664 			nw = fwrite(p, 1, st.sd_strsize, fp);
665 		else
666 			nw = write(ofd, p, st.sd_strsize);
667 		free(p);
668 		if (nw != st.sd_strsize) {
669 			syslog(LOG_ERR, "%s: %s",
670 			    path, strerror(nw == 0 ? EIO : errno));
671 			syslog(LOG_WARNING,
672 			    "WARNING: kernel may be incomplete");
673 			exit(1);
674 		}
675 	}
676 
677 	return 0;
678 }
679 
680 void
681 save_core(void)
682 {
683 	FILE *fp;
684 	int bounds, ifd, nr, nw, ofd, tryksyms;
685 	char *rawp, path[MAXPATHLEN];
686 
687 	ofd = -1;
688 	/*
689 	 * Get the current number and update the bounds file.  Do the update
690 	 * now, because may fail later and don't want to overwrite anything.
691 	 */
692 	umask(066);
693 	(void)snprintf(path, sizeof(path), "%s/bounds", dirname);
694 	if ((fp = fopen(path, "r")) == NULL)
695 		goto err1;
696 	if (fgets(buf, sizeof(buf), fp) == NULL) {
697 		if (ferror(fp))
698 err1:			syslog(LOG_WARNING, "%s: %m", path);
699 		bounds = 0;
700 	} else
701 		bounds = atoi(buf);
702 	if (fp != NULL)
703 		(void)fclose(fp);
704 	if ((fp = fopen(path, "w")) == NULL)
705 		syslog(LOG_ERR, "%s: %m", path);
706 	else {
707 		(void)fprintf(fp, "%d\n", bounds + 1);
708 		(void)fclose(fp);
709 	}
710 
711 	/* Create the core file. */
712 	(void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s",
713 	    dirname, bounds, compress ? ".gz" : "");
714 	if (compress) {
715 		if ((fp = zopen(path, gzmode)) == NULL) {
716 			syslog(LOG_ERR, "%s: %m", path);
717 			exit(1);
718 		}
719 	} else {
720 		ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
721 		fp  = fdopen(ofd, "w");
722 		if (fp == NULL) {
723 			syslog(LOG_ERR, "%s: fdopen: %m", path);
724 			exit(1);
725 		}
726 	}
727 
728 	if (dumpcdev == NODEV) {
729 		/* Open the raw device. */
730 		rawp = rawname(ddname);
731 		if ((ifd = open(rawp, O_RDONLY)) == -1) {
732 			syslog(LOG_WARNING, "%s: %m; using block device",
733 			    rawp);
734 			ifd = dumpfd;
735 		}
736 	} else {
737 		rawp = ddname;
738 		ifd = dumpfd;
739 	}
740 
741 	/* Seek to the start of the core. */
742 	Lseek(ifd, dumplo, SEEK_SET);
743 
744 	if (kvm_dump_wrtheader(kd_dump, fp, (int32_t)dumpbytes) == -1) {
745 		syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path,
746 		    kvm_geterr(kd_dump));
747 		exit(1);
748 	}
749 
750 	/* Copy the core file. */
751 	syslog(LOG_NOTICE, "writing %score to %s",
752 	    compress ? "compressed " : "", path);
753 	for (; dumpbytes > (off_t)0; dumpbytes -= (off_t)nr) {
754 		char nbuf[7];
755 		humanize_number(nbuf, 7, dumpbytes, "", HN_AUTOSCALE, 0);
756 		(void)printf("%7s\r", nbuf);
757 		(void)fflush(stdout);
758 		nr = read(ifd, buf, MIN(dumpbytes, sizeof(buf)));
759 		if (nr <= 0) {
760 			if (nr == 0)
761 				syslog(LOG_WARNING,
762 				    "WARNING: EOF on dump device");
763 			else
764 				syslog(LOG_ERR, "%s: %m", rawp);
765 			goto err2;
766 		}
767 		nw = fwrite(buf, 1, nr, fp);
768 		if (nw != nr) {
769 			syslog(LOG_ERR, "%s: %s",
770 			    path, strerror(nw == 0 ? EIO : errno));
771 err2:			syslog(LOG_WARNING,
772 			    "WARNING: core may be incomplete");
773 			(void)printf("\n");
774 			exit(1);
775 		}
776 	}
777 	if (dumpcdev == NODEV)
778 		(void)close(ifd);
779 	(void)fclose(fp);
780 
781 	/* Create a kernel. */
782 	(void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
783 	    dirname, bounds, compress ? ".gz" : "");
784 	syslog(LOG_NOTICE, "writing %skernel to %s",
785 	    compress ? "compressed " : "", path);
786 	for (tryksyms = 1;; tryksyms = 0) {
787 		if (compress) {
788 			if ((fp = zopen(path, gzmode)) == NULL) {
789 				syslog(LOG_ERR, "%s: %m", path);
790 				exit(1);
791 			}
792 		} else
793 			ofd = Create(path, S_IRUSR | S_IWUSR);
794 		if (tryksyms) {
795 			if (!save_ksyms(ofd, fp, path))
796 				break;
797 			if (compress)
798 				(void)fclose(fp);
799 			else
800 				(void)close(ofd);
801 			unlink(path);
802 		} else {
803 			save_kernel(ofd, fp, path);
804 			break;
805 		}
806 	}
807 	if (compress)
808 		(void)fclose(fp);
809 	else
810 		(void)close(ofd);
811 
812 	/*
813 	 * For development systems where the crash occurs during boot
814 	 * to multiuser.
815 	 */
816 	sync();
817 	sleep(1);
818 	sync();
819 	sleep(1);
820 }
821 
822 char *
823 find_dev(dev_t dev, int type)
824 {
825 	DIR *dfd;
826 	struct dirent *dir;
827 	struct stat sb;
828 	char *dp, device[MAXPATHLEN + 1], *p;
829 	size_t l;
830 
831 	if ((dfd = opendir(_PATH_DEV)) == NULL) {
832 		syslog(LOG_ERR, "%s: %m", _PATH_DEV);
833 		exit(1);
834 	}
835 	strlcpy(device, _PATH_DEV, sizeof(device));
836 	p = &device[strlen(device)];
837 	l = sizeof(device) - strlen(device);
838 	while ((dir = readdir(dfd))) {
839 		strlcpy(p, dir->d_name, l);
840 		if (lstat(device, &sb)) {
841 			syslog(LOG_ERR, "%s: %m", device);
842 			continue;
843 		}
844 		if ((sb.st_mode & S_IFMT) != type)
845 			continue;
846 		if (dev == sb.st_rdev) {
847 			closedir(dfd);
848 			if ((dp = strdup(device)) == NULL) {
849 				syslog(LOG_ERR, "%m");
850 				exit(1);
851 			}
852 			return (dp);
853 		}
854 	}
855 	closedir(dfd);
856 	syslog(LOG_ERR, "can't find device %lld/%lld",
857 	    (long long)major(dev), (long long)minor(dev));
858 	exit(1);
859 }
860 
861 char *
862 rawname(char *s)
863 {
864 	char *sl;
865 	char name[MAXPATHLEN];
866 
867 	if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
868 		syslog(LOG_ERR,
869 		    "can't make raw dump device name from %s", s);
870 		return (s);
871 	}
872 	(void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s,
873 	    sl + 1);
874 	if ((sl = strdup(name)) == NULL) {
875 		syslog(LOG_ERR, "%m");
876 		exit(1);
877 	}
878 	return (sl);
879 }
880 
881 int
882 get_crashtime(void)
883 {
884 	time_t dumptime;			/* Time the dump was taken. */
885 	struct timeval dtime;
886 
887 	if (KREAD(kd_dump, dump_nl[X_TIME_SECOND].n_value, &dumptime) != 0) {
888 		if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &dtime) != 0) {
889 			if (verbose)
890 				syslog(LOG_WARNING, "kvm_read: %s (and _time_second is not defined also)", kvm_geterr(kd_dump));
891 			return (0);
892 		}
893 		dumptime = dtime.tv_sec;
894 	}
895 	if (dumptime == 0) {
896 		if (verbose)
897 			syslog(LOG_ERR, "dump time is zero");
898 		return (0);
899 	}
900 	(void)printf("savecore: system went down at %s", ctime(&dumptime));
901 #define	LEEWAY	(60 * SECSPERDAY)
902 	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
903 		(void)printf("dump time is unreasonable\n");
904 		return (0);
905 	}
906 	return (1);
907 }
908 
909 int
910 check_space(void)
911 {
912 	FILE *fp;
913 	off_t minfree, spacefree, kernelsize, needed;
914 	struct stat st;
915 	struct statvfs fsbuf;
916 	char mbuf[100], path[MAXPATHLEN];
917 
918 	if (stat(kernel, &st) < 0) {
919 		syslog(LOG_ERR, "%s: %m", kernel);
920 		exit(1);
921 	}
922 	kernelsize = st.st_blocks * S_BLKSIZE;
923 	if (statvfs(dirname, &fsbuf) < 0) {
924 		syslog(LOG_ERR, "%s: %m", dirname);
925 		exit(1);
926 	}
927 	spacefree = fsbuf.f_bavail;
928 	spacefree *= fsbuf.f_frsize;
929 	spacefree /= 1024;
930 
931 	(void)snprintf(path, sizeof(path), "%s/minfree", dirname);
932 	if ((fp = fopen(path, "r")) == NULL)
933 		minfree = 0;
934 	else {
935 		if (fgets(mbuf, sizeof(mbuf), fp) == NULL)
936 			minfree = 0;
937 		else
938 			minfree = atoi(mbuf);
939 		(void)fclose(fp);
940 	}
941 
942 	needed = (dumpbytes + kernelsize) / 1024;
943  	if (minfree > 0 && spacefree - needed < minfree) {
944 		syslog(LOG_WARNING,
945 		    "no dump, not enough free space in %s", dirname);
946 		return (0);
947 	}
948 	if (spacefree - needed < minfree)
949 		syslog(LOG_WARNING,
950 		    "dump performed, but free space threshold crossed");
951 	return (1);
952 }
953 
954 int
955 Open(const char *name, int rw)
956 {
957 	int fd;
958 
959 	if ((fd = open(name, rw, 0)) < 0) {
960 		syslog(LOG_ERR, "%s: %m", name);
961 		exit(1);
962 	}
963 	return (fd);
964 }
965 
966 void
967 Lseek(int fd, off_t off, int flag)
968 {
969 	off_t ret;
970 
971 	ret = lseek(fd, off, flag);
972 	if (ret == -1) {
973 		syslog(LOG_ERR, "lseek: %m");
974 		exit(1);
975 	}
976 }
977 
978 int
979 Create(char *file, int mode)
980 {
981 	int fd;
982 
983 	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
984 	if (fd < 0) {
985 		syslog(LOG_ERR, "%s: %m", file);
986 		exit(1);
987 	}
988 	return (fd);
989 }
990 
991 void
992 Write(int fd, void *bp, int size)
993 {
994 	int n;
995 
996 	if ((n = write(fd, bp, size)) < size) {
997 		syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
998 		exit(1);
999 	}
1000 }
1001 
1002 void
1003 usage(void)
1004 {
1005 	(void)syslog(LOG_ERR,
1006 	    "usage: savecore [-cfnvz] [-N system] [-Z level] directory");
1007 	exit(1);
1008 }
1009