xref: /freebsd/sbin/savecore/savecore.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
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 
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1986, 1992, 1993\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 #if 0
42 static char sccsid[] = "@(#)savecore.c	8.3 (Berkeley) 1/2/94";
43 #endif
44 static const char rcsid[] =
45   "$FreeBSD$";
46 #endif /* not lint */
47 
48 #include <sys/param.h>
49 #include <sys/stat.h>
50 #include <sys/mount.h>
51 #include <sys/syslog.h>
52 #include <sys/sysctl.h>
53 
54 #include <vm/vm.h>
55 #include <vm/vm_param.h>
56 #include <vm/pmap.h>
57 
58 #include <dirent.h>
59 #include <fcntl.h>
60 #include <nlist.h>
61 #include <paths.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66 #include "zopen.h"
67 
68 #ifdef __alpha__
69 #define ok(number) ALPHA_K0SEG_TO_PHYS(number)
70 #endif
71 
72 #ifdef __i386__
73 #define ok(number) ((number) - KERNBASE)
74 #endif
75 
76 struct nlist current_nl[] = {	/* Namelist for currently running system. */
77 #define X_DUMPLO	0
78 	{ "_dumplo" },
79 #define X_TIME		1
80 	{ "_time_second" },
81 #define	X_DUMPSIZE	2
82 	{ "_dumpsize" },
83 #define X_VERSION	3
84 	{ "_version" },
85 #define X_PANICSTR	4
86 	{ "_panicstr" },
87 #define	X_DUMPMAG	5
88 	{ "_dumpmag" },
89 	{ "" },
90 };
91 int cursyms[] = { X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
92 int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
93 
94 struct nlist dump_nl[] = {	/* Name list for dumped system. */
95 	{ "_dumplo" },		/* Entries MUST be the same as */
96 	{ "_time_second" },	/*	those in current_nl[].  */
97 	{ "_dumpsize" },
98 	{ "_version" },
99 	{ "_panicstr" },
100 	{ "_dumpmag" },
101 	{ "" },
102 };
103 
104 /* Types match kernel declarations. */
105 off_t	dumplo;				/* where dump starts on dumpdev */
106 int	dumpmag;			/* magic number in dump */
107 int	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 
119 int	clear, compress, force, verbose;	/* flags */
120 
121 void	 check_kmem __P((void));
122 int	 check_space __P((void));
123 void	 clear_dump __P((void));
124 void	 DumpRead __P((int fd, void *bp, int size, off_t off, int flag));
125 void	 DumpWrite __P((int fd, void *bp, int size, off_t off, int flag));
126 int	 dump_exists __P((void));
127 void     find_dev __P((dev_t));
128 int	 get_crashtime __P((void));
129 void	 get_dumpsize __P((void));
130 void	 kmem_setup __P((void));
131 void	 log __P((int, char *, ...));
132 void	 Lseek __P((int, off_t, int));
133 int	 Open __P((const char *, int rw));
134 int	 Read __P((int, void *, int));
135 void	 save_core __P((void));
136 void	 usage __P((void));
137 void	 Write __P((int, void *, int));
138 
139 int
140 main(argc, argv)
141 	int argc;
142 	char *argv[];
143 {
144 	int ch;
145 
146 	openlog("savecore", LOG_PERROR, LOG_DAEMON);
147 
148 	while ((ch = getopt(argc, argv, "cdfN:vz")) != -1)
149 		switch(ch) {
150 		case 'c':
151 			clear = 1;
152 			break;
153 		case 'd':		/* Not documented. */
154 		case 'v':
155 			verbose = 1;
156 			break;
157 		case 'f':
158 			force = 1;
159 			break;
160 		case 'N':
161 			kernel = optarg;
162 			break;
163 		case 'z':
164 			compress = 1;
165 			break;
166 		case '?':
167 		default:
168 			usage();
169 		}
170 	argc -= optind;
171 	argv += optind;
172 
173 	if (!clear) {
174 		if (argc != 1 && argc != 2)
175 			usage();
176 		savedir = argv[0];
177 	}
178 	if (argc == 2)
179 		kernel = argv[1];
180 
181 	(void)time(&now);
182 	kmem_setup();
183 
184 	if (clear) {
185 		clear_dump();
186 		exit(0);
187 	}
188 
189 	if (!dump_exists() && !force)
190 		exit(1);
191 
192 	check_kmem();
193 
194 	if (panicstr)
195 		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
196 	else
197 		syslog(LOG_ALERT, "reboot");
198 
199 	get_dumpsize();
200 
201 	if ((!get_crashtime() || !check_space()) && !force)
202 		exit(1);
203 
204 	save_core();
205 
206 	clear_dump();
207 	exit(0);
208 }
209 
210 void
211 kmem_setup()
212 {
213 	FILE *fp;
214 	int kmem, i;
215 	const char *dump_sys;
216 	int mib[2];
217 	size_t len;
218 	long kdumplo;		/* block number where dump starts on dumpdev */
219 
220 	/*
221 	 * Some names we need for the currently running system, others for
222 	 * the system that was running when the dump was made.  The values
223 	 * obtained from the current system are used to look for things in
224 	 * /dev/kmem that cannot be found in the dump_sys namelist, but are
225 	 * presumed to be the same (since the disk partitions are probably
226 	 * the same!)
227 	 */
228 	if ((nlist(getbootfile(), current_nl)) == -1)
229 		syslog(LOG_ERR, "%s: nlist: %m", getbootfile());
230 	for (i = 0; cursyms[i] != -1; i++)
231 		if (current_nl[cursyms[i]].n_value == 0) {
232 			syslog(LOG_ERR, "%s: %s not in namelist",
233 			    getbootfile(), current_nl[cursyms[i]].n_name);
234 			exit(1);
235 		}
236 
237 	dump_sys = kernel ? kernel : getbootfile();
238 	if ((nlist(dump_sys, dump_nl)) == -1)
239 		syslog(LOG_ERR, "%s: nlist: %m", dump_sys);
240 	for (i = 0; dumpsyms[i] != -1; i++)
241 		if (dump_nl[dumpsyms[i]].n_value == 0) {
242 			syslog(LOG_ERR, "%s: %s not in namelist",
243 			    dump_sys, dump_nl[dumpsyms[i]].n_name);
244 			exit(1);
245 		}
246 
247 	mib[0] = CTL_KERN;
248 	mib[1] = KERN_DUMPDEV;
249 	len = sizeof dumpdev;
250 	if (sysctl(mib, 2, &dumpdev, &len, NULL, 0) == -1) {
251 		syslog(LOG_ERR, "sysctl: kern.dumpdev: %m");
252 		exit(1);
253 	}
254 	if (dumpdev == NODEV) {
255 		syslog(LOG_WARNING, "no core dump (no dumpdev)");
256 		exit(1);
257 	}
258 
259 	kmem = Open(_PATH_KMEM, O_RDONLY);
260 	Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET);
261 	(void)Read(kmem, &kdumplo, sizeof(kdumplo));
262 	dumplo = (off_t)kdumplo * DEV_BSIZE;
263 	if (verbose)
264 		(void)printf("dumplo = %lld (%ld * %d)\n",
265 		    (long long)dumplo, kdumplo, DEV_BSIZE);
266 	Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET);
267 	(void)Read(kmem, &dumpmag, sizeof(dumpmag));
268 	find_dev(dumpdev);
269 	dumpfd = Open(ddname, O_RDWR);
270 	fp = fdopen(kmem, "r");
271 	if (fp == NULL) {
272 		syslog(LOG_ERR, "%s: fdopen: %m", _PATH_KMEM);
273 		exit(1);
274 	}
275 	if (kernel)
276 		return;
277 	(void)fseek(fp, (off_t)current_nl[X_VERSION].n_value, L_SET);
278 	(void)fgets(vers, sizeof(vers), fp);
279 
280 	/* Don't fclose(fp), we use dumpfd later. */
281 }
282 
283 void
284 check_kmem()
285 {
286 	char core_vers[1024], *p;
287 
288 	DumpRead(dumpfd, core_vers, sizeof(core_vers),
289 	    (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET);
290 	core_vers[sizeof(core_vers) - 1] = '\0';
291 	p = strchr(core_vers, '\n');
292 	if (p)
293 		p[1] = '\0';
294 	if (strcmp(vers, core_vers) && kernel == 0)
295 		syslog(LOG_WARNING,
296 		    "warning: %s version mismatch:\n\t\"%s\"\nand\t\"%s\"\n",
297 		    getbootfile(), vers, core_vers);
298 	DumpRead(dumpfd, &panicstr, sizeof(panicstr),
299 	    (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
300 	if (panicstr) {
301 		DumpRead(dumpfd, panic_mesg, sizeof(panic_mesg),
302 		    (off_t)(dumplo + ok(panicstr)), L_SET);
303 	}
304 }
305 
306 /*
307  * Clear the magic number in the dump header.
308  */
309 void
310 clear_dump()
311 {
312 	int newdumpmag;
313 
314 	newdumpmag = 0;
315 	DumpWrite(dumpfd, &newdumpmag, sizeof(newdumpmag),
316 	    (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
317 	close(dumpfd);
318 }
319 
320 /*
321  * Check if a dump exists by looking for a magic number in the dump
322  * header.
323  */
324 int
325 dump_exists()
326 {
327 	int newdumpmag;
328 
329 	DumpRead(dumpfd, &newdumpmag, sizeof(newdumpmag),
330 	    (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
331 	if (newdumpmag != dumpmag) {
332 		if (verbose)
333 			syslog(LOG_WARNING, "magic number mismatch (%x != %x)",
334 			    newdumpmag, dumpmag);
335 		syslog(LOG_WARNING, "no core dump");
336 		return (0);
337 	}
338 	return (1);
339 }
340 
341 char buf[1024 * 1024];
342 
343 /*
344  * Save the core dump.
345  */
346 void
347 save_core()
348 {
349 	register FILE *fp;
350 	register int bounds, ifd, nr, nw;
351 	char path[MAXPATHLEN];
352 	mode_t oumask;
353 
354 	/*
355 	 * Get the current number and update the bounds file.  Do the update
356 	 * now, because may fail later and don't want to overwrite anything.
357 	 */
358 	(void)snprintf(path, sizeof(path), "%s/bounds", savedir);
359 	if ((fp = fopen(path, "r")) == NULL)
360 		goto err1;
361 	if (fgets(buf, sizeof(buf), fp) == NULL) {
362 		if (ferror(fp))
363 err1:			syslog(LOG_WARNING, "%s: %m", path);
364 		bounds = 0;
365 	} else
366 		bounds = atoi(buf);
367 	if (fp != NULL)
368 		(void)fclose(fp);
369 	if ((fp = fopen(path, "w")) == NULL)
370 		syslog(LOG_ERR, "%s: %m", path);
371 	else {
372 		(void)fprintf(fp, "%d\n", bounds + 1);
373 		(void)fclose(fp);
374 	}
375 
376 	/* Create the core file. */
377 	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
378 	(void)snprintf(path, sizeof(path), "%s/vmcore.%d%s",
379 	    savedir, bounds, compress ? ".Z" : "");
380 	if (compress)
381 		fp = zopen(path, "w", 0);
382 	else
383 		fp = fopen(path, "w");
384 	if (fp == NULL) {
385 		syslog(LOG_ERR, "%s: %m", path);
386 		exit(1);
387 	}
388 	(void)umask(oumask);
389 
390 	/* Seek to the start of the core. */
391 	Lseek(dumpfd, (off_t)dumplo, L_SET);
392 
393 	/* Copy the core file. */
394 	syslog(LOG_NOTICE, "writing %score to %s",
395 	    compress ? "compressed " : "", path);
396 	for (; dumpsize > 0; dumpsize -= nr) {
397 		(void)printf("%6dK\r", dumpsize / 1024);
398 		(void)fflush(stdout);
399 		nr = read(dumpfd, buf, MIN(dumpsize, sizeof(buf)));
400 		if (nr <= 0) {
401 			if (nr == 0)
402 				syslog(LOG_WARNING,
403 				    "WARNING: EOF on dump device");
404 			else
405 				syslog(LOG_ERR, "%s: %m", ddname);
406 			goto err2;
407 		}
408 		nw = fwrite(buf, 1, nr, fp);
409 		if (nw != nr) {
410 			syslog(LOG_ERR, "%s: %m", path);
411 err2:			syslog(LOG_WARNING,
412 			    "WARNING: vmcore may be incomplete");
413 			(void)printf("\n");
414 			exit(1);
415 		}
416 	}
417 
418 	(void)fclose(fp);
419 
420 	/* Copy the kernel. */
421 	ifd = Open(kernel ? kernel : getbootfile(), O_RDONLY);
422 	(void)snprintf(path, sizeof(path), "%s/kernel.%d%s",
423 	    savedir, bounds, compress ? ".Z" : "");
424 	if (compress)
425 		fp = zopen(path, "w", 0);
426 	else
427 		fp = fopen(path, "w");
428 	if (fp == NULL) {
429 		syslog(LOG_ERR, "%s: %m", path);
430 		exit(1);
431 	}
432 	syslog(LOG_NOTICE, "writing %skernel to %s",
433 	    compress ? "compressed " : "", path);
434 	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
435 		nw = fwrite(buf, 1, nr, fp);
436 		if (nw != nr) {
437 			syslog(LOG_ERR, "%s: %m", path);
438 			syslog(LOG_WARNING,
439 			    "WARNING: kernel may be incomplete");
440 			exit(1);
441 		}
442 	}
443 	if (nr < 0) {
444 		syslog(LOG_ERR, "%s: %m", kernel ? kernel : getbootfile());
445 		syslog(LOG_WARNING,
446 		    "WARNING: kernel may be incomplete");
447 		exit(1);
448 	}
449 	(void)fclose(fp);
450 	close(ifd);
451 }
452 
453 /*
454  * Verify that the specified device node exists and matches the
455  * specified device.
456  */
457 int
458 verify_dev(name, dev)
459 	char *name;
460 	register dev_t dev;
461 {
462 	struct stat sb;
463 
464 	if (lstat(name, &sb) == -1)
465 		return (-1);
466 	if (!S_ISCHR(sb.st_mode) || sb.st_rdev != dev)
467 		return (-1);
468 	return (0);
469 }
470 
471 /*
472  * Find the dump device.
473  *
474  *  1) try devname(3); see if it returns something sensible
475  *  2) scan /dev for the desired node
476  *  3) as a last resort, try to create the node we need
477  */
478 void
479 find_dev(dev)
480 	register dev_t dev;
481 {
482 	struct dirent *ent;
483 	char *dn, *dnp;
484 	DIR *d;
485 
486 	strcpy(ddname, _PATH_DEV);
487 	dnp = ddname + sizeof _PATH_DEV - 1;
488 	if ((dn = devname(dev, S_IFCHR)) != NULL) {
489 		strcpy(dnp, dn);
490 		if (verify_dev(ddname, dev) == 0)
491 			return;
492 	}
493 	if ((d = opendir(_PATH_DEV)) != NULL) {
494 		while ((ent = readdir(d))) {
495 			strcpy(dnp, ent->d_name);
496 			if (verify_dev(ddname, dev) == 0) {
497 				closedir(d);
498 				return;
499 			}
500 		}
501 		closedir(d);
502 	}
503 	strcpy(dnp, "dump");
504 	if (mknod(ddname, S_IFCHR|S_IRUSR|S_IWUSR, dev) == 0)
505 		return;
506 	syslog(LOG_ERR, "can't find device %d/%#x", major(dev), minor(dev));
507 	exit(1);
508 }
509 
510 /*
511  * Extract the date and time of the crash from the dump header, and
512  * make sure it looks sane (within one week of current date and time).
513  */
514 int
515 get_crashtime()
516 {
517 	time_t dumptime;			/* Time the dump was taken. */
518 
519 	DumpRead(dumpfd, &dumptime, sizeof(dumptime),
520 	    (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
521 	if (dumptime == 0) {
522 		if (verbose)
523 			syslog(LOG_ERR, "dump time is zero");
524 		return (0);
525 	}
526 	(void)printf("savecore: system went down at %s", ctime(&dumptime));
527 #define	LEEWAY	(7 * 86400)
528 	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
529 		(void)printf("dump time is unreasonable\n");
530 		return (0);
531 	}
532 	return (1);
533 }
534 
535 /*
536  * Extract the size of the dump from the dump header.
537  */
538 void
539 get_dumpsize()
540 {
541 	/* Read the dump size. */
542 	DumpRead(dumpfd, &dumpsize, sizeof(dumpsize),
543 	    (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
544 	dumpsize *= getpagesize();
545 }
546 
547 /*
548  * Check that sufficient space is available on the disk that holds the
549  * save directory.
550  */
551 int
552 check_space()
553 {
554 	register FILE *fp;
555 	const char *tkernel;
556 	off_t minfree, spacefree, totfree, kernelsize, needed;
557 	struct stat st;
558 	struct statfs fsbuf;
559 	char buf[100], path[MAXPATHLEN];
560 
561 	tkernel = kernel ? kernel : getbootfile();
562 	if (stat(tkernel, &st) < 0) {
563 		syslog(LOG_ERR, "%s: %m", tkernel);
564 		exit(1);
565 	}
566 	kernelsize = st.st_blocks * S_BLKSIZE;
567 
568 	if (statfs(savedir, &fsbuf) < 0) {
569 		syslog(LOG_ERR, "%s: %m", savedir);
570 		exit(1);
571 	}
572  	spacefree = ((off_t) fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
573 	totfree = ((off_t) fsbuf.f_bfree * fsbuf.f_bsize) / 1024;
574 
575 	(void)snprintf(path, sizeof(path), "%s/minfree", savedir);
576 	if ((fp = fopen(path, "r")) == NULL)
577 		minfree = 0;
578 	else {
579 		if (fgets(buf, sizeof(buf), fp) == NULL)
580 			minfree = 0;
581 		else
582 			minfree = atoi(buf);
583 		(void)fclose(fp);
584 	}
585 
586 	needed = (dumpsize + kernelsize) / 1024;
587  	if (((minfree > 0) ? spacefree : totfree) - needed < minfree) {
588 		syslog(LOG_WARNING,
589 	"no dump, not enough free space on device (%lld available, need %lld)",
590 		    (long long)(minfree > 0 ? spacefree : totfree),
591 		    (long long)needed);
592 		return (0);
593 	}
594 	if (spacefree - needed < 0)
595 		syslog(LOG_WARNING,
596 		    "dump performed, but free space threshold crossed");
597 	return (1);
598 }
599 
600 int
601 Open(name, rw)
602 	const char *name;
603 	int rw;
604 {
605 	int fd;
606 
607 	if ((fd = open(name, rw, 0)) < 0) {
608 		syslog(LOG_ERR, "%s: %m", name);
609 		exit(1);
610 	}
611 	return (fd);
612 }
613 
614 int
615 Read(fd, bp, size)
616 	int fd, size;
617 	void *bp;
618 {
619 	int nr;
620 
621 	nr = read(fd, bp, size);
622 	if (nr != size) {
623 		syslog(LOG_ERR, "read: %m");
624 		exit(1);
625 	}
626 	return (nr);
627 }
628 
629 void
630 Lseek(fd, off, flag)
631 	int fd, flag;
632 	off_t off;
633 {
634 	off_t ret;
635 
636 	ret = lseek(fd, off, flag);
637 	if (ret == -1) {
638 		syslog(LOG_ERR, "lseek: %m");
639 		exit(1);
640 	}
641 }
642 
643 /*
644  * DumpWrite and DumpRead block io requests to the * dump device.
645  */
646 #define DUMPBUFSIZE	8192
647 void
648 DumpWrite(fd, bp, size, off, flag)
649 	int fd, size, flag;
650 	void *bp;
651 	off_t off;
652 {
653 	unsigned char buf[DUMPBUFSIZE], *p, *q;
654 	off_t pos;
655 	int i, j;
656 
657 	if (flag != L_SET) {
658 		syslog(LOG_ERR, "lseek: not LSET");
659 		exit(2);
660 	}
661 	q = bp;
662 	while (size) {
663 		pos = off & ~(DUMPBUFSIZE - 1);
664 		Lseek(fd, pos, flag);
665 		(void)Read(fd, buf, sizeof(buf));
666 		j = off & (DUMPBUFSIZE - 1);
667 		p = buf + j;
668 		i = size;
669 		if (i > DUMPBUFSIZE - j)
670 			i = DUMPBUFSIZE - j;
671 		memcpy(p, q, i);
672 		Lseek(fd, pos, flag);
673 		(void)Write(fd, buf, sizeof(buf));
674 		size -= i;
675 		q += i;
676 		off += i;
677 	}
678 }
679 
680 void
681 DumpRead(fd, bp, size, off, flag)
682 	int fd, size, flag;
683 	void *bp;
684 	off_t off;
685 {
686 	unsigned char buf[DUMPBUFSIZE], *p, *q;
687 	off_t pos;
688 	int i, j;
689 
690 	if (flag != L_SET) {
691 		syslog(LOG_ERR, "lseek: not LSET");
692 		exit(2);
693 	}
694 	q = bp;
695 	while (size) {
696 		pos = off & ~(DUMPBUFSIZE - 1);
697 		Lseek(fd, pos, flag);
698 		(void)Read(fd, buf, sizeof(buf));
699 		j = off & (DUMPBUFSIZE - 1);
700 		p = buf + j;
701 		i = size;
702 		if (i > DUMPBUFSIZE - j)
703 			i = DUMPBUFSIZE - j;
704 		memcpy(q, p, i);
705 		size -= i;
706 		q += i;
707 		off += i;
708 	}
709 }
710 
711 void
712 Write(fd, bp, size)
713 	int fd, size;
714 	void *bp;
715 {
716 	int n;
717 
718 	if ((n = write(fd, bp, size)) < size) {
719 		syslog(LOG_ERR, "write: %m");
720 		exit(1);
721 	}
722 }
723 
724 void
725 usage()
726 {
727 	(void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory");
728 	exit(1);
729 }
730