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