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