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