xref: /dragonfly/sbin/savecore/savecore.c (revision 521a7b05)
1 /*-
2  * Copyright (c) 1986, 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1986, 1992, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)savecore.c	8.3 (Berkeley) 1/2/94
35  * $FreeBSD: src/sbin/savecore/savecore.c,v 1.28.2.14 2005/01/05 09:14:34 maxim Exp $
36  * $DragonFly: src/sbin/savecore/savecore.c,v 1.14 2006/10/20 18:30:12 corecode Exp $
37  */
38 
39 #define _KERNEL_STRUCTURES
40 
41 #include <sys/param.h>
42 
43 #undef _KERNEL_STRUCTURES
44 
45 #include <sys/stat.h>
46 #include <sys/mount.h>
47 #include <sys/syslog.h>
48 #include <sys/sysctl.h>
49 
50 #include <vm/vm.h>
51 #include <vm/vm_param.h>
52 #include <vm/pmap.h>
53 
54 #include <dirent.h>
55 #include <fcntl.h>
56 #include <nlist.h>
57 #include <paths.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 #include <err.h>
63 
64 extern FILE *zopen(const char *fname, const char *mode);
65 
66 #define ok(number) ((number) - kernbase)
67 
68 struct nlist current_nl[] = {	/* Namelist for currently running system. */
69 #define X_DUMPLO	0
70 	{ "_dumplo", 0, 0, 0, 0 },
71 #define X_TIME		1
72 	{ "_time_second", 0, 0, 0, 0 },
73 #define	X_DUMPSIZE	2
74 	{ "_dumpsize", 0, 0, 0, 0 },
75 #define X_VERSION	3
76 	{ "_version", 0, 0, 0, 0 },
77 #define X_PANICSTR	4
78 	{ "_panicstr", 0, 0, 0, 0 },
79 #define	X_DUMPMAG	5
80 	{ "_dumpmag", 0, 0, 0, 0 },
81 #define	X_KERNBASE	6
82 	{ "_kernbase", 0, 0, 0, 0 },
83 #define	X_MAXMEM	7
84 	{ "_Maxmem", 0, 0, 0, 0 },
85 	{ "", 0, 0, 0, 0 },
86 };
87 int cursyms[] = { X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
88 int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
89 
90 struct nlist dump_nl[] = {               /* Name list for dumped system. */
91 	{ "_dumplo", 0, 0, 0, 0 },       /* Entries MUST be the same as  */
92 	{ "_time_second", 0, 0, 0, 0 },	 /* those in current_nl[].       */
93 	{ "_dumpsize", 0, 0, 0, 0 },
94 	{ "_version", 0, 0, 0, 0 },
95 	{ "_panicstr", 0, 0, 0, 0 },
96 	{ "_dumpmag", 0, 0, 0, 0 },
97 	{ "_kernbase", 0, 0, 0, 0 },
98 	{ "_Maxmem", 0, 0, 0, 0 },
99 	{ "", 0, 0, 0, 0 },
100 };
101 
102 /* Types match kernel declarations. */
103 u_long	dumpmag;			/* magic number in dump */
104 
105 /* Based on kernel variables, but with more convenient types. */
106 off_t	dumplo;				/* where dump starts on dumpdev */
107 off_t	dumpsize;			/* amount of memory dumped */
108 
109 char	*kernel;			/* user-specified kernel */
110 char	*savedir;			/* directory to save dumps in */
111 char	ddname[MAXPATHLEN];		/* name of dump device */
112 dev_t	dumpdev;			/* dump device */
113 int	dumpfd;				/* read/write descriptor on char dev */
114 time_t	now;				/* current date */
115 char	panic_mesg[1024];		/* panic message */
116 int	panicstr;		        /* flag: dump was caused by panic */
117 char	vers[1024];			/* version of kernel that crashed */
118 char    *physmem;			/* physmem value used with dumped session */
119 long    dkdumplo;			/* directly specified kernel dumplo value */
120 
121 u_long	kernbase;			/* offset of kvm to core file */
122 
123 static int	clear, compress, force, verbose, directdumplo;	/* flags */
124 static int	keep;			/* keep dump on device */
125 
126 static void	check_kmem(void);
127 static int	check_space(void);
128 static void	clear_dump(void);
129 static void	DumpRead(int fd, void *bp, int size, off_t off, int flag);
130 static void	DumpWrite(int fd, void *bp, int size, off_t off, int flag);
131 static int	dump_exists(void);
132 static void	find_dev(dev_t);
133 static int	get_crashtime(void);
134 static void	get_dumpsize(void);
135 static void	kmem_setup(void);
136 static void	Lseek(int, off_t, int);
137 static int	Open(const char *, int rw);
138 static int	Read(int, void *, int);
139 static void	save_core(void);
140 static void	usage(void);
141 static int	verify_dev(char *, dev_t);
142 static void	Write(int, void *, int);
143 static void	kdumplo_adjust(char *cp, int kmem, long *kdumplop);
144 
145 int
146 main(int argc, char **argv)
147 {
148 	int ch;
149 	char *ep;
150 
151 	openlog("savecore", LOG_PERROR, LOG_DAEMON);
152 
153 	while ((ch = getopt(argc, argv, "cD:dfkN:vzP:B:")) != -1)
154 		switch(ch) {
155 		case 'c':
156 			clear = 1;
157 			break;
158 		case 'D':
159 			strncpy(ddname, optarg, sizeof(ddname));
160 			break;
161 		case 'd':		/* Not documented. */
162 		case 'v':
163 			verbose = 1;
164 			break;
165 		case 'f':
166 			force = 1;
167 			break;
168 		case 'k':
169 			keep = 1;
170 			break;
171 		case 'N':
172 			kernel = optarg;
173 			break;
174 		case 'z':
175 			compress = 1;
176 			break;
177 		case 'P':
178 			physmem = optarg;
179 			break;
180 		case 'B':
181 			directdumplo = 1;
182 			dkdumplo = strtol(optarg, &ep, 10);
183 			if (*ep != '\0')
184 				errx(1, "invalid offset: '%s'", optarg);
185 			break;
186 		case '?':
187 		default:
188 			usage();
189 		}
190 	argc -= optind;
191 	argv += optind;
192 
193 	if (!clear) {
194 		if (argc != 1 && argc != 2)
195 			usage();
196 		savedir = argv[0];
197 	}
198 	if (argc == 2)
199 		kernel = argv[1];
200 
201 	time(&now);
202 	kmem_setup();
203 
204 	if (clear) {
205 		clear_dump();
206 		exit(0);
207 	}
208 
209 	if (!dump_exists() && !force)
210 		exit(1);
211 
212 	check_kmem();
213 
214 	if (panicstr)
215 		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
216 	else
217 		syslog(LOG_ALERT, "reboot");
218 
219 	get_dumpsize();
220 
221 	if ((!get_crashtime() || !check_space()) && !force)
222 		exit(1);
223 
224 	save_core();
225 
226 	if (!keep)
227 		clear_dump();
228 
229 	exit(0);
230 }
231 
232 static void
233 kmem_setup(void)
234 {
235 	int kmem, i;
236 	const char *dump_sys;
237 	size_t len;
238 	long kdumplo;		/* block number where dump starts on dumpdev */
239 	char *p;
240 
241 	/*
242 	 * Some names we need for the currently running system, others for
243 	 * the system that was running when the dump was made.  The values
244 	 * obtained from the current system are used to look for things in
245 	 * /dev/kmem that cannot be found in the dump_sys namelist, but are
246 	 * presumed to be the same (since the disk partitions are probably
247 	 * the same!)
248 	 */
249 	if ((nlist(getbootfile(), current_nl)) == -1)
250 		syslog(LOG_ERR, "%s: nlist: %m", getbootfile());
251 	for (i = 0; cursyms[i] != -1; i++)
252 		if (current_nl[cursyms[i]].n_value == 0) {
253 			syslog(LOG_ERR, "%s: %s not in namelist",
254 			    getbootfile(), current_nl[cursyms[i]].n_name);
255 			exit(1);
256 		}
257 
258 	dump_sys = kernel ? kernel : getbootfile();
259 	if ((nlist(dump_sys, dump_nl)) == -1)
260 		syslog(LOG_ERR, "%s: nlist: %m", dump_sys);
261 	for (i = 0; dumpsyms[i] != -1; i++)
262 		if (dump_nl[dumpsyms[i]].n_value == 0) {
263 			syslog(LOG_ERR, "%s: %s not in namelist",
264 			    dump_sys, dump_nl[dumpsyms[i]].n_name);
265 			exit(1);
266 		}
267 
268 	if (dump_nl[X_KERNBASE].n_value != 0)
269 		kernbase = dump_nl[X_KERNBASE].n_value;
270 	else
271 		kernbase = KERNBASE;
272 
273 	if (*ddname == 0) {
274 		len = sizeof dumpdev;
275 		if (sysctlbyname("kern.dumpdev", &dumpdev, &len, NULL, 0) == -1) {
276 			syslog(LOG_ERR, "sysctl: kern.dumpdev: %m");
277 			exit(1);
278 		}
279 		if (dumpdev == NODEV) {
280 			syslog(LOG_WARNING, "no core dump (no dumpdev)");
281 			exit(1);
282 		}
283 		find_dev(dumpdev);
284 	}
285 
286 	kmem = Open(_PATH_KMEM, O_RDONLY);
287 	if (directdumplo)
288 		kdumplo = dkdumplo;
289 	else {
290 		Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET);
291 		Read(kmem, &kdumplo, sizeof(kdumplo));
292 		if (physmem)
293 			kdumplo_adjust(physmem, kmem, &kdumplo);
294 	}
295 		dumplo = (off_t)kdumplo * DEV_BSIZE;
296 	if (verbose)
297 		printf("dumplo = %lld (%ld * %d)\n",
298 		    (long long)dumplo, kdumplo, DEV_BSIZE);
299 	Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET);
300 	Read(kmem, &dumpmag, sizeof(dumpmag));
301 	dumpfd = Open(ddname, O_RDWR);
302 	if (kernel)
303 		return;
304 
305 	lseek(kmem, (off_t)current_nl[X_VERSION].n_value, SEEK_SET);
306 	Read(kmem, vers, sizeof(vers));
307 	vers[sizeof(vers) - 1] = '\0';
308 	p = strchr(vers, '\n');
309 	if (p)
310 		p[1] = '\0';
311 
312 	/* Don't fclose(fp), we use kmem later. */
313 }
314 
315 static void
316 check_kmem(void)
317 {
318 	char core_vers[1024], *p;
319 
320 	DumpRead(dumpfd, core_vers, sizeof(core_vers),
321 	    (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET);
322 	core_vers[sizeof(core_vers) - 1] = '\0';
323 	p = strchr(core_vers, '\n');
324 	if (p)
325 		p[1] = '\0';
326 	if (strcmp(vers, core_vers) && kernel == 0)
327 		syslog(LOG_WARNING,
328 		    "warning: %s version mismatch:\n\t\"%s\"\nand\t\"%s\"\n",
329 		    getbootfile(), vers, core_vers);
330 	DumpRead(dumpfd, &panicstr, sizeof(panicstr),
331 	    (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
332 	if (panicstr) {
333 		DumpRead(dumpfd, panic_mesg, sizeof(panic_mesg),
334 		    (off_t)(dumplo + ok(panicstr)), L_SET);
335 	}
336 }
337 
338 /*
339  * Clear the magic number in the dump header.
340  */
341 static void
342 clear_dump(void)
343 {
344 	u_long newdumpmag;
345 
346 	newdumpmag = 0;
347 	DumpWrite(dumpfd, &newdumpmag, sizeof(newdumpmag),
348 	    (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
349 	close(dumpfd);
350 }
351 
352 /*
353  * Check if a dump exists by looking for a magic number in the dump
354  * header.
355  */
356 static int
357 dump_exists(void)
358 {
359 	u_long newdumpmag;
360 
361 	DumpRead(dumpfd, &newdumpmag, sizeof(newdumpmag),
362 	    (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
363 	if (newdumpmag != dumpmag) {
364 		if (verbose)
365 			syslog(LOG_WARNING, "magic number mismatch (%lx != %lx)",
366 			    newdumpmag, dumpmag);
367 		syslog(LOG_WARNING, "no core dump");
368 		return (0);
369 	}
370 	return (1);
371 }
372 
373 char buf[1024 * 1024];
374 #define BLOCKSIZE (1<<12)
375 #define BLOCKMASK (~(BLOCKSIZE-1))
376 
377 /*
378  * Save the core dump.
379  */
380 static void
381 save_core(void)
382 {
383 	FILE *fp;
384 	int bounds, ifd, nr, nw;
385 	int hs, he = 0;		/* start and end of hole */
386 	char path[MAXPATHLEN];
387 	mode_t oumask;
388 
389 	/*
390 	 * Get the current number and update the bounds file.  Do the update
391 	 * now, because may fail later and don't want to overwrite anything.
392 	 */
393 	snprintf(path, sizeof(path), "%s/bounds", savedir);
394 	if ((fp = fopen(path, "r")) == NULL)
395 		goto err1;
396 	if (fgets(buf, sizeof(buf), fp) == NULL) {
397 		if (ferror(fp))
398 err1:			syslog(LOG_WARNING, "%s: %m", path);
399 		bounds = 0;
400 	} else
401 		bounds = atoi(buf);
402 	if (fp != NULL)
403 		fclose(fp);
404 	if ((fp = fopen(path, "w")) == NULL)
405 		syslog(LOG_ERR, "%s: %m", path);
406 	else {
407 		fprintf(fp, "%d\n", bounds + 1);
408 		fclose(fp);
409 	}
410 
411 	/* Create the core file. */
412 	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
413 	snprintf(path, sizeof(path), "%s/vmcore.%d%s",
414 	    savedir, bounds, compress ? ".gz" : "");
415 	if (compress)
416 		fp = zopen(path, "w");
417 	else
418 		fp = fopen(path, "w");
419 	if (fp == NULL) {
420 		syslog(LOG_ERR, "%s: %m", path);
421 		exit(1);
422 	}
423 	umask(oumask);
424 
425 	/* Seek to the start of the core. */
426 	Lseek(dumpfd, (off_t)dumplo, L_SET);
427 
428 	/* Copy the core file. */
429 	syslog(LOG_NOTICE, "writing %score to %s",
430 	    compress ? "compressed " : "", path);
431 	for (; dumpsize > 0; dumpsize -= nr) {
432 		printf("%6ldK\r", (long)(dumpsize / 1024));
433 		fflush(stdout);
434 		nr = read(dumpfd, buf, MIN(dumpsize, sizeof(buf)));
435 		if (nr <= 0) {
436 			if (nr == 0)
437 				syslog(LOG_WARNING,
438 				    "WARNING: EOF on dump device");
439 			else
440 				syslog(LOG_ERR, "%s: %m", ddname);
441 			goto err2;
442 		}
443 		if (compress) {
444 			nw = fwrite(buf, 1, nr, fp);
445 		} else {
446 		for (nw = 0; nw < nr; nw = he) {
447 			/* find a contiguous block of zeroes */
448 			for (hs = nw; hs < nr; hs += BLOCKSIZE) {
449 				for (he = hs; he < nr && buf[he] == 0; ++he)
450 					/* nothing */ ;
451 
452 				/* is the hole long enough to matter? */
453 				if (he >= hs + BLOCKSIZE)
454 					break;
455 			}
456 
457 			/* back down to a block boundary */
458 			he &= BLOCKMASK;
459 
460 			/*
461 			 * 1) Don't go beyond the end of the buffer.
462 			 * 2) If the end of the buffer is less than
463 			 *    BLOCKSIZE bytes away, we're at the end
464 			 *    of the file, so just grab what's left.
465 			 */
466 			if (hs + BLOCKSIZE > nr)
467 				hs = he = nr;
468 
469 			/*
470 			 * At this point, we have a partial ordering:
471 			 *     nw <= hs <= he <= nr
472 			 * If hs > nw, buf[nw..hs] contains non-zero data.
473 			 * If he > hs, buf[hs..he] is all zeroes.
474 			 */
475 			if (hs > nw)
476 				if (fwrite(buf + nw, hs - nw, 1, fp) != 1)
477 					break;
478 			if (he > hs)
479 				if (fseeko(fp, he - hs, SEEK_CUR) == -1)
480 					break;
481 			}
482 		}
483 		if (nw != nr) {
484 			syslog(LOG_ERR, "%s: %m", path);
485 err2:			syslog(LOG_WARNING,
486 			    "WARNING: vmcore may be incomplete");
487 			printf("\n");
488 			exit(1);
489 		}
490 	}
491 
492 	fclose(fp);
493 
494 	/* Copy the kernel. */
495 	ifd = Open(kernel ? kernel : getbootfile(), O_RDONLY);
496 	snprintf(path, sizeof(path), "%s/kernel.%d%s",
497 	    savedir, bounds, compress ? ".gz" : "");
498 	if (compress)
499 		fp = zopen(path, "w");
500 	else
501 		fp = fopen(path, "w");
502 	if (fp == NULL) {
503 		syslog(LOG_ERR, "%s: %m", path);
504 		exit(1);
505 	}
506 	syslog(LOG_NOTICE, "writing %skernel to %s",
507 	    compress ? "compressed " : "", path);
508 	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
509 		nw = fwrite(buf, 1, nr, fp);
510 		if (nw != nr) {
511 			syslog(LOG_ERR, "%s: %m", path);
512 			syslog(LOG_WARNING,
513 			    "WARNING: kernel may be incomplete");
514 			exit(1);
515 		}
516 	}
517 	if (nr < 0) {
518 		syslog(LOG_ERR, "%s: %m", kernel ? kernel : getbootfile());
519 		syslog(LOG_WARNING,
520 		    "WARNING: kernel may be incomplete");
521 		exit(1);
522 	}
523 	fclose(fp);
524 	close(ifd);
525 }
526 
527 /*
528  * Verify that the specified device node exists and matches the
529  * specified device.
530  */
531 static int
532 verify_dev(char *name, dev_t dev)
533 {
534 	struct stat sb;
535 
536 	if (lstat(name, &sb) == -1)
537 		return (-1);
538 	if (!S_ISCHR(sb.st_mode) || sb.st_rdev != dev)
539 		return (-1);
540 	return (0);
541 }
542 
543 /*
544  * Find the dump device.
545  *
546  *  1) try devname(3); see if it returns something sensible
547  *  2) scan /dev for the desired node
548  *  3) as a last resort, try to create the node we need
549  */
550 static void
551 find_dev(dev_t dev)
552 {
553 	struct dirent *ent;
554 	char *dn, *dnp;
555 	DIR *d;
556 
557 	strcpy(ddname, _PATH_DEV);
558 	dnp = ddname + sizeof _PATH_DEV - 1;
559 	if ((dn = devname(dev, S_IFCHR)) != NULL) {
560 		strcpy(dnp, dn);
561 		if (verify_dev(ddname, dev) == 0)
562 			return;
563 	}
564 	if ((d = opendir(_PATH_DEV)) != NULL) {
565 		while ((ent = readdir(d))) {
566 			strcpy(dnp, ent->d_name);
567 			if (verify_dev(ddname, dev) == 0) {
568 				closedir(d);
569 				return;
570 			}
571 		}
572 		closedir(d);
573 	}
574 	strcpy(dnp, "dump");
575 	if (mknod(ddname, S_IFCHR|S_IRUSR|S_IWUSR, dev) == 0)
576 		return;
577 	syslog(LOG_ERR, "can't find device %d/%#x", major(dev), minor(dev));
578 	exit(1);
579 }
580 
581 /*
582  * Extract the date and time of the crash from the dump header, and
583  * make sure it looks sane (within one week of current date and time).
584  */
585 static int
586 get_crashtime(void)
587 {
588 	time_t dumptime;			/* Time the dump was taken. */
589 
590 	DumpRead(dumpfd, &dumptime, sizeof(dumptime),
591 	    (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
592 	if (dumptime == 0) {
593 		if (verbose)
594 			syslog(LOG_ERR, "dump time is zero");
595 		return (0);
596 	}
597 	printf("savecore: system went down at %s", ctime(&dumptime));
598 #define	LEEWAY	(7 * 86400)
599 	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
600 		printf("dump time is unreasonable\n");
601 		return (0);
602 	}
603 	return (1);
604 }
605 
606 /*
607  * Extract the size of the dump from the dump header.
608  */
609 static void
610 get_dumpsize(void)
611 {
612 	int kdumpsize;	/* Number of pages in dump. */
613 
614 	/* Read the dump size. */
615 	DumpRead(dumpfd, &kdumpsize, sizeof(kdumpsize),
616 	    (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
617 	dumpsize = (off_t)kdumpsize * getpagesize();
618 }
619 
620 /*
621  * Check that sufficient space is available on the disk that holds the
622  * save directory.
623  */
624 static int
625 check_space(void)
626 {
627 	FILE *fp;
628 	const char *tkernel;
629 	off_t minfree, spacefree, totfree, kernelsize, needed;
630 	struct stat st;
631 	struct statfs fsbuf;
632 	char mybuf[100], path[MAXPATHLEN];
633 
634 	tkernel = kernel ? kernel : getbootfile();
635 	if (stat(tkernel, &st) < 0) {
636 		syslog(LOG_ERR, "%s: %m", tkernel);
637 		exit(1);
638 	}
639 	kernelsize = st.st_blocks * S_BLKSIZE;
640 
641 	if (statfs(savedir, &fsbuf) < 0) {
642 		syslog(LOG_ERR, "%s: %m", savedir);
643 		exit(1);
644 	}
645  	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
646 	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
647 
648 	snprintf(path, sizeof(path), "%s/minfree", savedir);
649 	if ((fp = fopen(path, "r")) == NULL)
650 		minfree = 0;
651 	else {
652 		if (fgets(mybuf, sizeof(mybuf), fp) == NULL)
653 			minfree = 0;
654 		else
655 			minfree = atoi(mybuf);
656 		fclose(fp);
657 	}
658 
659 	needed = (dumpsize + kernelsize) / 1024;
660  	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
661 		syslog(LOG_WARNING,
662 	"no dump, not enough free space on device (%lld available, need %lld)",
663 		    (long long)(minfree > 0 ? spacefree : totfree),
664 		    (long long)needed);
665 		return (0);
666 	}
667 	if (spacefree - needed < 0)
668 		syslog(LOG_WARNING,
669 		    "dump performed, but free space threshold crossed");
670 	return (1);
671 }
672 
673 static int
674 Open(const char *name, int rw)
675 {
676 	int fd;
677 
678 	if ((fd = open(name, rw, 0)) < 0) {
679 		syslog(LOG_ERR, "%s: %m", name);
680 		exit(1);
681 	}
682 	return (fd);
683 }
684 
685 static int
686 Read(int fd, void *bp, int size)
687 {
688 	int nr;
689 
690 	nr = read(fd, bp, size);
691 	if (nr != size) {
692 		syslog(LOG_ERR, "read: %m");
693 		exit(1);
694 	}
695 	return (nr);
696 }
697 
698 static void
699 Lseek(int fd, off_t off, int flag)
700 {
701 	off_t ret;
702 
703 	ret = lseek(fd, off, flag);
704 	if (ret == -1) {
705 		syslog(LOG_ERR, "lseek: %m");
706 		exit(1);
707 	}
708 }
709 
710 /*
711  * DumpWrite and DumpRead block io requests to the * dump device.
712  */
713 #define DUMPBUFSIZE	8192
714 static void
715 DumpWrite(int fd, void *bp, int size, off_t off, int flag)
716 {
717 	unsigned char mybuf[DUMPBUFSIZE], *p, *q;
718 	off_t pos;
719 	int i, j;
720 
721 	if (flag != L_SET) {
722 		syslog(LOG_ERR, "lseek: not LSET");
723 		exit(2);
724 	}
725 	q = bp;
726 	while (size) {
727 		pos = off & ~(DUMPBUFSIZE - 1);
728 		Lseek(fd, pos, flag);
729 		Read(fd, mybuf, sizeof(mybuf));
730 		j = off & (DUMPBUFSIZE - 1);
731 		p = mybuf + j;
732 		i = size;
733 		if (i > DUMPBUFSIZE - j)
734 			i = DUMPBUFSIZE - j;
735 		memcpy(p, q, i);
736 		Lseek(fd, pos, flag);
737 		Write(fd, mybuf, sizeof(mybuf));
738 		size -= i;
739 		q += i;
740 		off += i;
741 	}
742 }
743 
744 static void
745 DumpRead(int fd, void *bp, int size, off_t off, int flag)
746 {
747 	unsigned char mybuf[DUMPBUFSIZE], *p, *q;
748 	off_t pos;
749 	int i, j;
750 
751 	if (flag != L_SET) {
752 		syslog(LOG_ERR, "lseek: not LSET");
753 		exit(2);
754 	}
755 	q = bp;
756 	while (size) {
757 		pos = off & ~(DUMPBUFSIZE - 1);
758 		Lseek(fd, pos, flag);
759 		Read(fd, mybuf, sizeof(mybuf));
760 		j = off & (DUMPBUFSIZE - 1);
761 		p = mybuf + j;
762 		i = size;
763 		if (i > DUMPBUFSIZE - j)
764 			i = DUMPBUFSIZE - j;
765 		memcpy(q, p, i);
766 		size -= i;
767 		q += i;
768 		off += i;
769 	}
770 }
771 
772 static void
773 Write(int fd, void *bp, int size)
774 {
775 	int n;
776 
777 	if ((n = write(fd, bp, size)) < size) {
778 		syslog(LOG_ERR, "write: %m");
779 		exit(1);
780 	}
781 }
782 
783 static void
784 kdumplo_adjust(char *cp, int kmem, long *kdumplop)
785 {
786 	uint64_t AllowMem, sanity, Maxmem, CurrMaxmem;
787 	char *ep;
788 
789 	/* based on getmemsize() in i386/i386/machdep.c */
790 	sanity = AllowMem = strtouq(cp, &ep, 0);
791 	if ((ep != cp) && (*ep != 0)) {
792 		switch(*ep) {
793 		case 'g':
794 		case 'G':
795 			AllowMem <<= 10;
796 		case 'm':
797 		case 'M':
798 			AllowMem <<= 10;
799 		case 'k':
800 		case 'K':
801 			AllowMem <<= 10;
802 			break;
803 		default:
804 			AllowMem = 0;
805 		}
806 		if (AllowMem < sanity)
807 			AllowMem = 0;
808 	}
809 	if (AllowMem == 0)
810 		errx(1, "invalid memory size: '%s'\n", cp);
811 	else
812 		Maxmem = atop(AllowMem);
813 
814 	Lseek(kmem, (off_t)current_nl[X_MAXMEM].n_value, L_SET);
815 	Read(kmem, &CurrMaxmem, sizeof(CurrMaxmem));
816 
817 	/* based on setdumpdev() in kern_shutdown.c */
818 	*kdumplop += CurrMaxmem * (PAGE_SIZE / DEV_BSIZE);
819 	*kdumplop -= Maxmem * (PAGE_SIZE / DEV_BSIZE);
820 }
821 
822 static void
823 usage(void)
824 {
825 	syslog(LOG_ERR,
826 	       "usage: savecore [-cfkvz] [-N system] [-P physmem|-B blkno] directory");
827 	exit(1);
828 }
829