xref: /netbsd-src/sbin/savecore/savecore.c (revision 3b01aba77a7a698587faaae455bbfe740923c1f5)
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