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