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