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