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