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
44static 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
72extern 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
77struct 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};
100int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
101int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
102
103struct 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. */
118long	dumplo;				/* where dump starts on dumpdev */
119int	dumpmag;			/* magic number in dump */
120int	dumpsize;			/* amount of memory dumped */
121
122const char	*kernel;		/* name of used kernel */
123char	*dirname;			/* directory to save dumps in */
124char	*ddname;			/* name of dump device */
125dev_t	dumpdev;			/* dump device */
126int	dumpfd;				/* read/write descriptor on block dev */
127kvm_t	*kd_dump;			/* kvm descriptor on block dev	*/
128time_t	now;				/* current date */
129char	panic_mesg[1024];
130long	panicstr;
131char	vers[1024];
132
133static int	clear, compress, force, verbose;	/* flags */
134
135void	check_kmem(void);
136int	check_space(void);
137void	clear_dump(void);
138int	Create(char *, int);
139int	dump_exists(void);
140char	*find_dev(dev_t, int);
141int	get_crashtime(void);
142void	kmem_setup(void);
143void	log(int, char *, ...);
144void	Lseek(int, off_t, int);
145int	main(int, char *[]);
146int	Open(const char *, int rw);
147char	*rawname(char *s);
148void	save_core(void);
149void	usage(void);
150void	Write(int, void *, int);
151
152int
153main(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
224void
225kmem_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
325void
326check_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	}
399nomsguf:
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
421int
422dump_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
456void
457clear_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
465char buf[1024 * 1024];
466
467void
468save_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))
485err1:			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));
550err2:			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
596char *
597find_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
631char *
632rawname(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
651int
652get_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
677int
678check_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
726int
727Open(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
738void
739Lseek(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
750int
751Create(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
763void
764Write(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
774void
775usage(void)
776{
777	(void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory");
778	exit(1);
779}
780