Home | History | Annotate | Line # | Download | only in savecore
savecore.c revision 1.52
      1 /*	$NetBSD: savecore.c,v 1.52 2001/06/13 23:16:27 wiz 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. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 #ifndef lint
     38 __COPYRIGHT("@(#) Copyright (c) 1986, 1992, 1993\n\
     39 	The Regents of the University of California.  All rights reserved.\n");
     40 #endif /* not lint */
     41 
     42 #ifndef lint
     43 #if 0
     44 static char sccsid[] = "@(#)savecore.c	8.5 (Berkeley) 4/28/95";
     45 #else
     46 __RCSID("$NetBSD: savecore.c,v 1.52 2001/06/13 23:16:27 wiz Exp $");
     47 #endif
     48 #endif /* not lint */
     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 
     56 #include <dirent.h>
     57 #include <errno.h>
     58 #include <fcntl.h>
     59 #include <nlist.h>
     60 #include <paths.h>
     61 #include <stddef.h>
     62 #include <stdio.h>
     63 #include <stdlib.h>
     64 #include <string.h>
     65 #include <time.h>
     66 #include <tzfile.h>
     67 #include <unistd.h>
     68 #include <util.h>
     69 #include <limits.h>
     70 #include <kvm.h>
     71 
     72 extern FILE *zopen(const char *fname, const char *mode);
     73 
     74 #define	KREAD(kd, addr, p)\
     75 	(kvm_read(kd, addr, (char *)(p), sizeof(*(p))) != sizeof(*(p)))
     76 
     77 struct nlist current_nl[] = {	/* Namelist for currently running system. */
     78 #define	X_DUMPDEV	0
     79 	{ "_dumpdev" },
     80 #define	X_DUMPLO	1
     81 	{ "_dumplo" },
     82 #define	X_TIME		2
     83 	{ "_time" },
     84 #define	X_DUMPSIZE	3
     85 	{ "_dumpsize" },
     86 #define	X_VERSION	4
     87 	{ "_version" },
     88 #define	X_DUMPMAG	5
     89 	{ "_dumpmag" },
     90 #define	X_PANICSTR	6
     91 	{ "_panicstr" },
     92 #define	X_PANICSTART	7
     93 	{ "_panicstart" },
     94 #define	X_PANICEND	8
     95 	{ "_panicend" },
     96 #define	X_MSGBUF	9
     97 	{ "_msgbufp" },
     98 	{ NULL },
     99 };
    100 int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
    101 int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
    102 
    103 struct nlist dump_nl[] = {	/* Name list for dumped system. */
    104 	{ "_dumpdev" },		/* Entries MUST be the same as */
    105 	{ "_dumplo" },		/*	those in current_nl[].  */
    106 	{ "_time" },
    107 	{ "_dumpsize" },
    108 	{ "_version" },
    109 	{ "_dumpmag" },
    110 	{ "_panicstr" },
    111 	{ "_panicstart" },
    112 	{ "_panicend" },
    113 	{ "_msgbufp" },
    114 	{ NULL },
    115 };
    116 
    117 /* Types match kernel declarations. */
    118 long	dumplo;				/* where dump starts on dumpdev */
    119 int	dumpmag;			/* magic number in dump */
    120 int	dumpsize;			/* amount of memory dumped */
    121 
    122 const char	*kernel;		/* name of used kernel */
    123 char	*dirname;			/* directory to save dumps in */
    124 char	*ddname;			/* name of dump device */
    125 dev_t	dumpdev;			/* dump device */
    126 int	dumpfd;				/* read/write descriptor on block dev */
    127 kvm_t	*kd_dump;			/* kvm descriptor on block dev	*/
    128 time_t	now;				/* current date */
    129 char	panic_mesg[1024];
    130 long	panicstr;
    131 char	vers[1024];
    132 
    133 static int	clear, compress, force, verbose;	/* flags */
    134 
    135 void	check_kmem(void);
    136 int	check_space(void);
    137 void	clear_dump(void);
    138 int	Create(char *, int);
    139 int	dump_exists(void);
    140 char	*find_dev(dev_t, int);
    141 int	get_crashtime(void);
    142 void	kmem_setup(void);
    143 void	log(int, char *, ...);
    144 void	Lseek(int, off_t, int);
    145 int	main(int, char *[]);
    146 int	Open(const char *, int rw);
    147 char	*rawname(char *s);
    148 void	save_core(void);
    149 void	usage(void);
    150 void	Write(int, void *, int);
    151 
    152 int
    153 main(int argc, char *argv[])
    154 {
    155 	int ch;
    156 
    157 	dirname = NULL;
    158 	kernel = NULL;
    159 
    160 	openlog("savecore", LOG_PERROR, LOG_DAEMON);
    161 
    162 	while ((ch = getopt(argc, argv, "cdfN:vz")) != -1)
    163 		switch(ch) {
    164 		case 'c':
    165 			clear = 1;
    166 			break;
    167 		case 'd':		/* Not documented. */
    168 		case 'v':
    169 			verbose = 1;
    170 			break;
    171 		case 'f':
    172 			force = 1;
    173 			break;
    174 		case 'N':
    175 			kernel = optarg;
    176 			break;
    177 		case 'z':
    178 			compress = 1;
    179 			break;
    180 		case '?':
    181 		default:
    182 			usage();
    183 		}
    184 	argc -= optind;
    185 	argv += optind;
    186 
    187 	if (argc != (clear ? 0 : 1))
    188 		usage();
    189 
    190 	if (!clear)
    191 		dirname = argv[0];
    192 
    193 	if (kernel == NULL) {
    194 		kernel = getbootfile();
    195 	}
    196 
    197 	(void)time(&now);
    198 	kmem_setup();
    199 
    200 	if (clear) {
    201 		clear_dump();
    202 		exit(0);
    203 	}
    204 
    205 	if (!dump_exists() && !force)
    206 		exit(1);
    207 
    208 	check_kmem();
    209 
    210 	if (panicstr)
    211 		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
    212 	else
    213 		syslog(LOG_ALERT, "reboot");
    214 
    215 	if ((!get_crashtime() || !check_space()) && !force)
    216 		exit(1);
    217 
    218 	save_core();
    219 
    220 	clear_dump();
    221 	exit(0);
    222 }
    223 
    224 void
    225 kmem_setup(void)
    226 {
    227 	kvm_t *kd_kern;
    228 	char errbuf[_POSIX2_LINE_MAX];
    229 	int i, hdrsz;
    230 
    231 	/*
    232 	 * Some names we need for the currently running system, others for
    233 	 * the system that was running when the dump was made.  The values
    234 	 * obtained from the current system are used to look for things in
    235 	 * /dev/kmem that cannot be found in the kernel namelist, but are
    236 	 * presumed to be the same (since the disk partitions are probably
    237 	 * the same!)
    238 	 */
    239 	kd_kern = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, errbuf);
    240 	if (kd_kern == NULL) {
    241 		syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
    242 		exit(1);
    243 	}
    244 	if (kvm_nlist(kd_kern, current_nl) == -1)
    245 		syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
    246 		    kvm_geterr(kd_kern));
    247 
    248 	for (i = 0; cursyms[i] != -1; i++)
    249 		if (current_nl[cursyms[i]].n_value == 0) {
    250 			syslog(LOG_ERR, "%s: %s not in namelist",
    251 			    kernel, current_nl[cursyms[i]].n_name);
    252 			exit(1);
    253 		}
    254 
    255 	if (KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev) != 0) {
    256 		if (verbose)
    257 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
    258 		exit(1);
    259 	}
    260 	if (dumpdev == NODEV) {
    261 		syslog(LOG_WARNING, "no core dump (no dumpdev)");
    262 		exit(1);
    263 	}
    264 	if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &dumplo) != 0) {
    265 		if (verbose)
    266 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
    267 		exit(1);
    268 	}
    269 	if (dumplo == -1) {
    270 	    syslog(LOG_WARNING, "no core dump (invalid dumplo)");
    271 	    exit(1);
    272 	}
    273 	dumplo *= DEV_BSIZE;
    274 	if (verbose)
    275 		(void)printf("dumplo = %ld (%ld * %ld)\n",
    276 		    (long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE);
    277 	if (KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag) != 0) {
    278 		if (verbose)
    279 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
    280 		exit(1);
    281 	}
    282 
    283 	(void)kvm_read(kd_kern, current_nl[X_VERSION].n_value, vers,
    284 	    sizeof(vers));
    285 	vers[sizeof(vers) - 1] = '\0';
    286 
    287 	ddname = find_dev(dumpdev, S_IFBLK);
    288 	dumpfd = Open(ddname, O_RDWR);
    289 
    290 	kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf);
    291 	if (kd_dump == NULL) {
    292 		syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
    293 		exit(1);
    294 	}
    295 
    296 	if (kvm_nlist(kd_dump, dump_nl) == -1)
    297 		syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
    298 		    kvm_geterr(kd_dump));
    299 
    300 	for (i = 0; dumpsyms[i] != -1; i++)
    301 		if (dump_nl[dumpsyms[i]].n_value == 0) {
    302 			syslog(LOG_ERR, "%s: %s not in namelist",
    303 			    kernel, dump_nl[dumpsyms[i]].n_name);
    304 			exit(1);
    305 		}
    306 	hdrsz = kvm_dump_mkheader(kd_dump, (off_t)dumplo);
    307 
    308 	/*
    309 	 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number
    310 	 * checks, ergo no dump is present...
    311 	 */
    312 	if (hdrsz == 0) {
    313 		syslog(LOG_WARNING, "no core dump");
    314 		exit(1);
    315 	}
    316 	if (hdrsz == -1) {
    317 		syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", kernel,
    318 		    kvm_geterr(kd_dump));
    319 		exit(1);
    320 	}
    321 	dumplo += hdrsz;
    322 	kvm_close(kd_kern);
    323 }
    324 
    325 void
    326 check_kmem(void)
    327 {
    328 	char *cp, *bufdata;
    329 	struct kern_msgbuf msgbuf, *bufp;
    330 	long panicloc, panicstart, panicend;
    331 	char core_vers[1024];
    332 
    333 	(void)kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers,
    334 	    sizeof(core_vers));
    335 	core_vers[sizeof(core_vers) - 1] = '\0';
    336 
    337 	if (strcmp(vers, core_vers) != 0)
    338 		syslog(LOG_WARNING,
    339 		    "warning: %s version mismatch:\n\t%s\nand\t%s\n",
    340 		    kernel, vers, core_vers);
    341 
    342 	panicstart = panicend = 0;
    343 	if (KREAD(kd_dump, dump_nl[X_PANICSTART].n_value, &panicstart) != 0) {
    344 		if (verbose)
    345 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
    346 		goto nomsguf;
    347 	}
    348 	if (KREAD(kd_dump, dump_nl[X_PANICEND].n_value, &panicend) != 0) {
    349 		if (verbose)
    350 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
    351 		goto nomsguf;
    352 	}
    353 	if (panicstart != 0 && panicend != 0) {
    354 		if (KREAD(kd_dump, dump_nl[X_MSGBUF].n_value, &bufp)) {
    355 			if (verbose)
    356 				syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
    357 			goto nomsguf;
    358 		}
    359 		if (kvm_read(kd_dump, (long)bufp, &msgbuf,
    360 		    offsetof(struct kern_msgbuf, msg_bufc)) !=
    361 		    offsetof(struct kern_msgbuf, msg_bufc)) {
    362 			if (verbose)
    363 				syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
    364 			goto nomsguf;
    365 		}
    366 		if (msgbuf.msg_magic != MSG_MAGIC) {
    367 			if (verbose)
    368 				syslog(LOG_WARNING, "msgbuf magic incorrect");
    369 			goto nomsguf;
    370 		}
    371 		bufdata = malloc(msgbuf.msg_bufs);
    372 		if (bufdata == NULL) {
    373 			if (verbose)
    374 				syslog(LOG_WARNING, "couldn't allocate space for msgbuf data");
    375 			goto nomsguf;
    376 		}
    377 		if (kvm_read(kd_dump, (long)&bufp->msg_bufc, bufdata,
    378 		    msgbuf.msg_bufs) != msgbuf.msg_bufs) {
    379 			if (verbose)
    380 				syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
    381 			goto nomsguf;
    382 		}
    383 		cp = panic_mesg;
    384 		while (panicstart != panicend && cp < &panic_mesg[sizeof(panic_mesg)-1]) {
    385 			*cp++ = bufdata[panicstart];
    386 			panicstart++;
    387 			if (panicstart >= msgbuf.msg_bufs)
    388 				panicstart = 0;
    389 		}
    390 		/* Don't end in a new-line */
    391 		cp = &panic_mesg[strlen(panic_mesg)] - 1;
    392 		if (*cp == '\n')
    393 			*cp = '\0';
    394 		panic_mesg[sizeof(panic_mesg) - 1] = '\0';
    395 
    396 		panicstr = 1;	/* anything not zero */
    397 		return;
    398 	}
    399 nomsguf:
    400 	if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) {
    401 		if (verbose)
    402 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
    403 		return;
    404 	}
    405 	if (panicstr) {
    406 		cp = panic_mesg;
    407 		panicloc = panicstr;
    408 		do {
    409 			if (KREAD(kd_dump, panicloc, cp) != 0) {
    410 				if (verbose)
    411 				    syslog(LOG_WARNING, "kvm_read: %s",
    412 					kvm_geterr(kd_dump));
    413 				break;
    414 			}
    415 			panicloc++;
    416 		} while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]);
    417 		panic_mesg[sizeof(panic_mesg) - 1] = '\0';
    418 	}
    419 }
    420 
    421 int
    422 dump_exists(void)
    423 {
    424 	int newdumpmag;
    425 
    426 	if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) {
    427 		if (verbose)
    428 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
    429 		return (0);
    430 	}
    431 
    432 	/* Read the dump size. */
    433 	if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) {
    434 		if (verbose)
    435 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
    436 		return (0);
    437 	}
    438 	dumpsize *= getpagesize();
    439 
    440 	/*
    441 	 * Return zero if core dump doesn't seem to be there, and note
    442 	 * it for syslog.  This check and return happens after the dump size
    443 	 * is read, so dumpsize is whether or not the core is valid (for -f).
    444 	 */
    445 	if (newdumpmag != dumpmag) {
    446 		if (verbose)
    447 			syslog(LOG_WARNING,
    448 			    "magic number mismatch (0x%x != 0x%x)",
    449 			    newdumpmag, dumpmag);
    450 		syslog(LOG_WARNING, "no core dump");
    451 		return (0);
    452 	}
    453 	return (1);
    454 }
    455 
    456 void
    457 clear_dump(void)
    458 {
    459 	if (kvm_dump_inval(kd_dump) == -1)
    460 		syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname,
    461 		    kvm_geterr(kd_dump));
    462 
    463 }
    464 
    465 char buf[1024 * 1024];
    466 
    467 void
    468 save_core(void)
    469 {
    470 	FILE *fp;
    471 	int bounds, ifd, nr, nw, ofd;
    472 	char *rawp, path[MAXPATHLEN];
    473 
    474 	ofd = -1;
    475 	/*
    476 	 * Get the current number and update the bounds file.  Do the update
    477 	 * now, because may fail later and don't want to overwrite anything.
    478 	 */
    479 	umask(066);
    480 	(void)snprintf(path, sizeof(path), "%s/bounds", dirname);
    481 	if ((fp = fopen(path, "r")) == NULL)
    482 		goto err1;
    483 	if (fgets(buf, sizeof(buf), fp) == NULL) {
    484 		if (ferror(fp))
    485 err1:			syslog(LOG_WARNING, "%s: %m", path);
    486 		bounds = 0;
    487 	} else
    488 		bounds = atoi(buf);
    489 	if (fp != NULL)
    490 		(void)fclose(fp);
    491 	if ((fp = fopen(path, "w")) == NULL)
    492 		syslog(LOG_ERR, "%s: %m", path);
    493 	else {
    494 		(void)fprintf(fp, "%d\n", bounds + 1);
    495 		(void)fclose(fp);
    496 	}
    497 
    498 	/* Create the core file. */
    499 	(void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s",
    500 	    dirname, bounds, compress ? ".gz" : "");
    501 	if (compress) {
    502 		if ((fp = zopen(path, "w")) == NULL) {
    503 			syslog(LOG_ERR, "%s: %m", path);
    504 			exit(1);
    505 		}
    506 	} else {
    507 		ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    508 		fp  = fdopen(ofd, "w");
    509 		if (fp == NULL) {
    510 			syslog(LOG_ERR, "%s: fdopen: %m", path);
    511 			exit(1);
    512 		}
    513 	}
    514 
    515 	/* Open the raw device. */
    516 	rawp = rawname(ddname);
    517 	if ((ifd = open(rawp, O_RDONLY)) == -1) {
    518 		syslog(LOG_WARNING, "%s: %m; using block device", rawp);
    519 		ifd = dumpfd;
    520 	}
    521 
    522 	/* Seek to the start of the core. */
    523 	Lseek(ifd, (off_t)dumplo, SEEK_SET);
    524 
    525 	if (kvm_dump_wrtheader(kd_dump, fp, dumpsize) == -1) {
    526 		syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path,
    527 		    kvm_geterr(kd_dump));
    528 		exit(1);
    529 	}
    530 
    531 	/* Copy the core file. */
    532 	syslog(LOG_NOTICE, "writing %score to %s",
    533 	    compress ? "compressed " : "", path);
    534 	for (; dumpsize > 0; dumpsize -= nr) {
    535 		(void)printf("%8dK\r", dumpsize / 1024);
    536 		(void)fflush(stdout);
    537 		nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
    538 		if (nr <= 0) {
    539 			if (nr == 0)
    540 				syslog(LOG_WARNING,
    541 				    "WARNING: EOF on dump device");
    542 			else
    543 				syslog(LOG_ERR, "%s: %m", rawp);
    544 			goto err2;
    545 		}
    546 		nw = fwrite(buf, 1, nr, fp);
    547 		if (nw != nr) {
    548 			syslog(LOG_ERR, "%s: %s",
    549 			    path, strerror(nw == 0 ? EIO : errno));
    550 err2:			syslog(LOG_WARNING,
    551 			    "WARNING: core may be incomplete");
    552 			(void)printf("\n");
    553 			exit(1);
    554 		}
    555 	}
    556 	(void)close(ifd);
    557 	(void)fclose(fp);
    558 
    559 	/* Copy the kernel. */
    560 	ifd = Open(kernel, O_RDONLY);
    561 	(void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
    562 	    dirname, bounds, compress ? ".gz" : "");
    563 	if (compress) {
    564 		if ((fp = zopen(path, "w")) == NULL) {
    565 			syslog(LOG_ERR, "%s: %m", path);
    566 			exit(1);
    567 		}
    568 	} else
    569 		ofd = Create(path, S_IRUSR | S_IWUSR);
    570 	syslog(LOG_NOTICE, "writing %skernel to %s",
    571 	    compress ? "compressed " : "", path);
    572 	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
    573 		if (compress)
    574 			nw = fwrite(buf, 1, nr, fp);
    575 		else
    576 			nw = write(ofd, buf, nr);
    577 		if (nw != nr) {
    578 			syslog(LOG_ERR, "%s: %s",
    579 			    path, strerror(nw == 0 ? EIO : errno));
    580 			syslog(LOG_WARNING,
    581 			    "WARNING: kernel may be incomplete");
    582 			exit(1);
    583 		}
    584 	}
    585 	if (nr < 0) {
    586 		syslog(LOG_ERR, "%s: %m", kernel);
    587 		syslog(LOG_WARNING, "WARNING: kernel may be incomplete");
    588 		exit(1);
    589 	}
    590 	if (compress)
    591 		(void)fclose(fp);
    592 	else
    593 		(void)close(ofd);
    594 }
    595 
    596 char *
    597 find_dev(dev_t dev, int type)
    598 {
    599 	DIR *dfd;
    600 	struct dirent *dir;
    601 	struct stat sb;
    602 	char *dp, devname[MAXPATHLEN + 1];
    603 
    604 	if ((dfd = opendir(_PATH_DEV)) == NULL) {
    605 		syslog(LOG_ERR, "%s: %m", _PATH_DEV);
    606 		exit(1);
    607 	}
    608 	(void)strcpy(devname, _PATH_DEV);
    609 	while ((dir = readdir(dfd))) {
    610 		(void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name);
    611 		if (lstat(devname, &sb)) {
    612 			syslog(LOG_ERR, "%s: %m", devname);
    613 			continue;
    614 		}
    615 		if ((sb.st_mode & S_IFMT) != type)
    616 			continue;
    617 		if (dev == sb.st_rdev) {
    618 			closedir(dfd);
    619 			if ((dp = strdup(devname)) == NULL) {
    620 				syslog(LOG_ERR, "%m");
    621 				exit(1);
    622 			}
    623 			return (dp);
    624 		}
    625 	}
    626 	closedir(dfd);
    627 	syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
    628 	exit(1);
    629 }
    630 
    631 char *
    632 rawname(char *s)
    633 {
    634 	char *sl;
    635 	char name[MAXPATHLEN];
    636 
    637 	if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
    638 		syslog(LOG_ERR,
    639 		    "can't make raw dump device name from %s", s);
    640 		return (s);
    641 	}
    642 	(void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s,
    643 	    sl + 1);
    644 	if ((sl = strdup(name)) == NULL) {
    645 		syslog(LOG_ERR, "%m");
    646 		exit(1);
    647 	}
    648 	return (sl);
    649 }
    650 
    651 int
    652 get_crashtime(void)
    653 {
    654 	struct timeval dtime;
    655 	time_t dumptime;			/* Time the dump was taken. */
    656 
    657 	if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &dtime) != 0) {
    658 		if (verbose)
    659 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
    660 		return (0);
    661 	}
    662 	dumptime = dtime.tv_sec;
    663 	if (dumptime == 0) {
    664 		if (verbose)
    665 			syslog(LOG_ERR, "dump time is zero");
    666 		return (0);
    667 	}
    668 	(void)printf("savecore: system went down at %s", ctime(&dumptime));
    669 #define	LEEWAY	(7 * SECSPERDAY)
    670 	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
    671 		(void)printf("dump time is unreasonable\n");
    672 		return (0);
    673 	}
    674 	return (1);
    675 }
    676 
    677 int
    678 check_space(void)
    679 {
    680 	FILE *fp;
    681 	off_t minfree, spacefree, kernelsize, needed;
    682 	struct stat st;
    683 	struct statfs fsbuf;
    684 	char mbuf[100], path[MAXPATHLEN];
    685 
    686 #ifdef __GNUC__
    687 	(void) &minfree;
    688 #endif
    689 
    690 	if (stat(kernel, &st) < 0) {
    691 		syslog(LOG_ERR, "%s: %m", kernel);
    692 		exit(1);
    693 	}
    694 	kernelsize = st.st_blocks * S_BLKSIZE;
    695 	if (statfs(dirname, &fsbuf) < 0) {
    696 		syslog(LOG_ERR, "%s: %m", dirname);
    697 		exit(1);
    698 	}
    699 	spacefree = fsbuf.f_bavail;
    700 	spacefree *= fsbuf.f_bsize;
    701 	spacefree /= 1024;
    702 
    703 	(void)snprintf(path, sizeof(path), "%s/minfree", dirname);
    704 	if ((fp = fopen(path, "r")) == NULL)
    705 		minfree = 0;
    706 	else {
    707 		if (fgets(mbuf, sizeof(mbuf), fp) == NULL)
    708 			minfree = 0;
    709 		else
    710 			minfree = atoi(mbuf);
    711 		(void)fclose(fp);
    712 	}
    713 
    714 	needed = (dumpsize + kernelsize) / 1024;
    715  	if (minfree > 0 && spacefree - needed < minfree) {
    716 		syslog(LOG_WARNING,
    717 		    "no dump, not enough free space in %s", dirname);
    718 		return (0);
    719 	}
    720 	if (spacefree - needed < minfree)
    721 		syslog(LOG_WARNING,
    722 		    "dump performed, but free space threshold crossed");
    723 	return (1);
    724 }
    725 
    726 int
    727 Open(const char *name, int rw)
    728 {
    729 	int fd;
    730 
    731 	if ((fd = open(name, rw, 0)) < 0) {
    732 		syslog(LOG_ERR, "%s: %m", name);
    733 		exit(1);
    734 	}
    735 	return (fd);
    736 }
    737 
    738 void
    739 Lseek(int fd, off_t off, int flag)
    740 {
    741 	off_t ret;
    742 
    743 	ret = lseek(fd, off, flag);
    744 	if (ret == -1) {
    745 		syslog(LOG_ERR, "lseek: %m");
    746 		exit(1);
    747 	}
    748 }
    749 
    750 int
    751 Create(char *file, int mode)
    752 {
    753 	int fd;
    754 
    755 	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
    756 	if (fd < 0) {
    757 		syslog(LOG_ERR, "%s: %m", file);
    758 		exit(1);
    759 	}
    760 	return (fd);
    761 }
    762 
    763 void
    764 Write(int fd, void *bp, int size)
    765 {
    766 	int n;
    767 
    768 	if ((n = write(fd, bp, size)) < size) {
    769 		syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
    770 		exit(1);
    771 	}
    772 }
    773 
    774 void
    775 usage(void)
    776 {
    777 	(void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory");
    778 	exit(1);
    779 }
    780