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