Home | History | Annotate | Line # | Download | only in savecore
savecore.c revision 1.80
      1 /*	$NetBSD: savecore.c,v 1.80 2009/04/06 12:32:30 lukem 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.80 2009/04/06 12:32:30 lukem 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 	if (stat(kernel, &st) < 0) {
    915 		syslog(LOG_ERR, "%s: %m", kernel);
    916 		exit(1);
    917 	}
    918 	kernelsize = st.st_blocks * S_BLKSIZE;
    919 	if (statvfs(dirname, &fsbuf) < 0) {
    920 		syslog(LOG_ERR, "%s: %m", dirname);
    921 		exit(1);
    922 	}
    923 	spacefree = fsbuf.f_bavail;
    924 	spacefree *= fsbuf.f_frsize;
    925 	spacefree /= 1024;
    926 
    927 	(void)snprintf(path, sizeof(path), "%s/minfree", dirname);
    928 	if ((fp = fopen(path, "r")) == NULL)
    929 		minfree = 0;
    930 	else {
    931 		if (fgets(mbuf, sizeof(mbuf), fp) == NULL)
    932 			minfree = 0;
    933 		else
    934 			minfree = atoi(mbuf);
    935 		(void)fclose(fp);
    936 	}
    937 
    938 	needed = (dumpbytes + kernelsize) / 1024;
    939  	if (minfree > 0 && spacefree - needed < minfree) {
    940 		syslog(LOG_WARNING,
    941 		    "no dump, not enough free space in %s", dirname);
    942 		return (0);
    943 	}
    944 	if (spacefree - needed < minfree)
    945 		syslog(LOG_WARNING,
    946 		    "dump performed, but free space threshold crossed");
    947 	return (1);
    948 }
    949 
    950 int
    951 Open(const char *name, int rw)
    952 {
    953 	int fd;
    954 
    955 	if ((fd = open(name, rw, 0)) < 0) {
    956 		syslog(LOG_ERR, "%s: %m", name);
    957 		exit(1);
    958 	}
    959 	return (fd);
    960 }
    961 
    962 void
    963 Lseek(int fd, off_t off, int flag)
    964 {
    965 	off_t ret;
    966 
    967 	ret = lseek(fd, off, flag);
    968 	if (ret == -1) {
    969 		syslog(LOG_ERR, "lseek: %m");
    970 		exit(1);
    971 	}
    972 }
    973 
    974 int
    975 Create(char *file, int mode)
    976 {
    977 	int fd;
    978 
    979 	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
    980 	if (fd < 0) {
    981 		syslog(LOG_ERR, "%s: %m", file);
    982 		exit(1);
    983 	}
    984 	return (fd);
    985 }
    986 
    987 void
    988 Write(int fd, void *bp, int size)
    989 {
    990 	int n;
    991 
    992 	if ((n = write(fd, bp, size)) < size) {
    993 		syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
    994 		exit(1);
    995 	}
    996 }
    997 
    998 void
    999 usage(void)
   1000 {
   1001 	(void)syslog(LOG_ERR,
   1002 	    "usage: savecore [-cfnvz] [-N system] [-Z level] directory");
   1003 	exit(1);
   1004 }
   1005