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