xref: /netbsd-src/sbin/savecore/savecore.c (revision 481fca6e59249d8ffcf24fef7cfbe7b131bfb080)
1 /*	$NetBSD: savecore.c,v 1.40 1999/08/02 00:33:01 mycroft Exp $	*/
2 
3 /*-
4  * Copyright (c) 1986, 1992, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1986, 1992, 1993\n\
39 	The Regents of the University of California.  All rights reserved.\n");
40 #endif /* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)savecore.c	8.5 (Berkeley) 4/28/95";
45 #else
46 __RCSID("$NetBSD: savecore.c,v 1.40 1999/08/02 00:33:01 mycroft Exp $");
47 #endif
48 #endif /* not lint */
49 
50 #include <sys/param.h>
51 #include <sys/stat.h>
52 #include <sys/mount.h>
53 #include <sys/syslog.h>
54 #include <sys/time.h>
55 
56 #include <dirent.h>
57 #include <errno.h>
58 #include <fcntl.h>
59 #include <nlist.h>
60 #include <paths.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <time.h>
65 #include <tzfile.h>
66 #include <unistd.h>
67 #include <limits.h>
68 #include <kvm.h>
69 
70 extern FILE *zopen __P((const char *fname, const char *mode, int bits));
71 
72 #define KREAD(kd, addr, p)\
73 	(kvm_read(kd, addr, (char *)(p), sizeof(*(p))) != sizeof(*(p)))
74 
75 struct nlist current_nl[] = {	/* Namelist for currently running system. */
76 #define X_DUMPDEV	0
77 	{ "_dumpdev" },
78 #define X_DUMPLO	1
79 	{ "_dumplo" },
80 #define X_TIME		2
81 	{ "_time" },
82 #define	X_DUMPSIZE	3
83 	{ "_dumpsize" },
84 #define X_VERSION	4
85 	{ "_version" },
86 #define X_PANICSTR	5
87 	{ "_panicstr" },
88 #define	X_DUMPMAG	6
89 	{ "_dumpmag" },
90 	{ NULL },
91 };
92 int cursyms[] = { X_DUMPDEV, 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 	{ "_dumpdev" },		/* Entries MUST be the same as */
97 	{ "_dumplo" },		/*	those in current_nl[].  */
98 	{ "_time" },
99 	{ "_dumpsize" },
100 	{ "_version" },
101 	{ "_panicstr" },
102 	{ "_dumpmag" },
103 	{ NULL },
104 };
105 
106 /* Types match kernel declarations. */
107 long	dumplo;				/* where dump starts on dumpdev */
108 int	dumpmag;			/* magic number in dump */
109 int	dumpsize;			/* amount of memory dumped */
110 
111 char	*kernel;
112 char	*dirname;			/* directory to save dumps in */
113 char	*ddname;			/* name of dump device */
114 dev_t	dumpdev;			/* dump device */
115 int	dumpfd;				/* read/write descriptor on block dev */
116 kvm_t	*kd_dump;			/* kvm descriptor on block dev	*/
117 time_t	now;				/* current date */
118 char	panic_mesg[1024];
119 long	panicstr;
120 char	vers[1024];
121 
122 int	clear, compress, force, verbose;	/* flags */
123 
124 void	 check_kmem __P((void));
125 int	 check_space __P((void));
126 void	 clear_dump __P((void));
127 int	 Create __P((char *, int));
128 int	 dump_exists __P((void));
129 char	*find_dev __P((dev_t, int));
130 int	 get_crashtime __P((void));
131 void	 kmem_setup __P((void));
132 void	 log __P((int, char *, ...));
133 void	 Lseek __P((int, off_t, int));
134 int	 main __P((int, char *[]));
135 int	 Open __P((char *, int rw));
136 char	*rawname __P((char *s));
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 	if ((!get_crashtime() || !check_space()) && !force)
202 		exit(1);
203 
204 	save_core();
205 
206 	clear_dump();
207 	exit(0);
208 }
209 
210 void
211 kmem_setup()
212 {
213 	kvm_t	*kd_kern;
214 	char	errbuf[_POSIX2_LINE_MAX];
215 	int	i, hdrsz;
216 	char	*dump_sys;
217 
218 	/*
219 	 * Some names we need for the currently running system, others for
220 	 * the system that was running when the dump was made.  The values
221 	 * obtained from the current system are used to look for things in
222 	 * /dev/kmem that cannot be found in the dump_sys namelist, but are
223 	 * presumed to be the same (since the disk partitions are probably
224 	 * the same!)
225 	 */
226 	kd_kern = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, errbuf);
227 	if (kd_kern == NULL) {
228 		syslog(LOG_ERR, "%s: kvm_openfiles: %s", _PATH_UNIX, errbuf);
229 		exit(1);
230 	}
231 	if (kvm_nlist(kd_kern, current_nl) == -1)
232 		syslog(LOG_ERR, "%s: kvm_nlist: %s", _PATH_UNIX,
233 			kvm_geterr(kd_kern));
234 
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 			    _PATH_UNIX, current_nl[cursyms[i]].n_name);
239 			exit(1);
240 		}
241 
242 	if (KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev) != 0) {
243 		if (verbose)
244 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
245 		exit(1);
246 	}
247 	if (dumpdev == NODEV) {
248 		syslog(LOG_WARNING, "no core dump (no dumpdev)");
249 		exit(1);
250 	}
251 	if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &dumplo) != 0) {
252 		if (verbose)
253 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
254 		exit(1);
255 	}
256 	dumplo *= DEV_BSIZE;
257 	if (verbose)
258 		(void)printf("dumplo = %ld (%ld * %ld)\n",
259 		    (long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE);
260 	if (KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag) != 0) {
261 		if (verbose)
262 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
263 		exit(1);
264 	}
265 
266 	if (kernel == NULL) {
267 		(void)kvm_read(kd_kern, current_nl[X_VERSION].n_value,
268 			vers, sizeof(vers));
269 		vers[sizeof(vers) - 1] = '\0';
270 	}
271 
272 	ddname = find_dev(dumpdev, S_IFBLK);
273 	dumpfd = Open(ddname, O_RDWR);
274 
275 	dump_sys = kernel ? kernel : _PATH_UNIX;
276 
277 	kd_dump = kvm_openfiles(dump_sys, ddname, NULL, O_RDWR, errbuf);
278 	if (kd_dump == NULL) {
279 		syslog(LOG_ERR, "%s: kvm_openfiles: %s", dump_sys, errbuf);
280 		exit(1);
281 	}
282 
283 	if (kvm_nlist(kd_dump, dump_nl) == -1)
284 		syslog(LOG_ERR, "%s: kvm_nlist: %s", dump_sys,
285 			kvm_geterr(kd_dump));
286 
287 	for (i = 0; dumpsyms[i] != -1; i++)
288 		if (dump_nl[dumpsyms[i]].n_value == 0) {
289 			syslog(LOG_ERR, "%s: %s not in namelist",
290 			    dump_sys, dump_nl[dumpsyms[i]].n_name);
291 			exit(1);
292 		}
293 	hdrsz = kvm_dump_mkheader(kd_dump, (off_t)dumplo);
294 
295 	/*
296 	 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number
297 	 * checks, ergo no dump is present...
298 	 */
299 	if (hdrsz == 0) {
300 		syslog(LOG_WARNING, "no core dump");
301 		exit(1);
302 	}
303 	if (hdrsz == -1) {
304 		syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", dump_sys,
305 			kvm_geterr(kd_dump));
306 		exit(1);
307 	}
308 	dumplo += hdrsz;
309 	kvm_close(kd_kern);
310 }
311 
312 void
313 check_kmem()
314 {
315 	char	*cp;
316 	long	panicloc;
317 	char core_vers[1024];
318 
319 	(void)kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers,
320 		sizeof(core_vers));
321 	core_vers[sizeof(core_vers) - 1] = '\0';
322 
323 	if (strcmp(vers, core_vers) && kernel == 0)
324 		syslog(LOG_WARNING,
325 		    "warning: %s version mismatch:\n\t%s\nand\t%s\n",
326 		    _PATH_UNIX, vers, core_vers);
327 
328 	if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) {
329 		if (verbose)
330 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
331 		return;
332 	}
333 	if (panicstr) {
334 		cp       = panic_mesg;
335 		panicloc = panicstr;
336 		do {
337 			if (KREAD(kd_dump, panicloc, cp) != 0) {
338 				if (verbose)
339 				    syslog(LOG_WARNING, "kvm_read: %s",
340 					   kvm_geterr(kd_dump));
341 				break;
342 			}
343 			panicloc++;
344 		} while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]);
345 		panic_mesg[sizeof(panic_mesg) - 1] = '\0';
346 	}
347 }
348 
349 int
350 dump_exists()
351 {
352 	int newdumpmag;
353 
354 	if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) {
355 		if (verbose)
356 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
357 		return (0);
358 	}
359 
360 	/* Read the dump size. */
361 	if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) {
362 		if (verbose)
363 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
364 		return (0);
365 	}
366 	dumpsize *= getpagesize();
367 
368 	/*
369 	 * Return zero if core dump doesn't seem to be there, and note
370 	 * it for syslog.  This check and return happens after the dump size
371 	 * is read, so dumpsize is whether or not the core is valid (for -f).
372 	 */
373 	if (newdumpmag != dumpmag) {
374 		if (verbose)
375 			syslog(LOG_WARNING,
376 			    "magic number mismatch (0x%x != 0x%x)",
377 			    newdumpmag, dumpmag);
378 		syslog(LOG_WARNING, "no core dump");
379 		return (0);
380 	}
381 	return (1);
382 }
383 
384 void
385 clear_dump()
386 {
387 	if (kvm_dump_inval(kd_dump) == -1)
388 		syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname,
389 			kvm_geterr(kd_dump));
390 
391 }
392 
393 char buf[1024 * 1024];
394 
395 void
396 save_core()
397 {
398 	FILE *fp;
399 	int bounds, ifd, nr, nw, ofd;
400 	char *rawp, path[MAXPATHLEN];
401 
402 	ofd = -1;
403 	/*
404 	 * Get the current number and update the bounds file.  Do the update
405 	 * now, because may fail later and don't want to overwrite anything.
406 	 */
407 	umask(066);
408 	(void)snprintf(path, sizeof(path), "%s/bounds", dirname);
409 	if ((fp = fopen(path, "r")) == NULL)
410 		goto err1;
411 	if (fgets(buf, sizeof(buf), fp) == NULL) {
412 		if (ferror(fp))
413 err1:			syslog(LOG_WARNING, "%s: %s", path, strerror(errno));
414 		bounds = 0;
415 	} else
416 		bounds = atoi(buf);
417 	if (fp != NULL)
418 		(void)fclose(fp);
419 	if ((fp = fopen(path, "w")) == NULL)
420 		syslog(LOG_ERR, "%s: %m", path);
421 	else {
422 		(void)fprintf(fp, "%d\n", bounds + 1);
423 		(void)fclose(fp);
424 	}
425 
426 	/* Create the core file. */
427 	(void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s",
428 	    dirname, bounds, compress ? ".Z" : "");
429 	if (compress) {
430 		if ((fp = zopen(path, "w", 0)) == NULL) {
431 			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
432 			exit(1);
433 		}
434 	} else {
435 		ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
436 		fp  = fdopen(ofd, "w");
437 		if (fp == NULL) {
438 			syslog(LOG_ERR, "%s: fdopen: %s", path,
439 			    strerror(errno));
440 			exit(1);
441 		}
442 	}
443 
444 	/* Open the raw device. */
445 	rawp = rawname(ddname);
446 	if ((ifd = open(rawp, O_RDONLY)) == -1) {
447 		syslog(LOG_WARNING, "%s: %m; using block device", rawp);
448 		ifd = dumpfd;
449 	}
450 
451 	/* Seek to the start of the core. */
452 	Lseek(ifd, (off_t)dumplo, SEEK_SET);
453 
454 	if (kvm_dump_wrtheader(kd_dump, fp, dumpsize) == -1) {
455 		syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path,
456 			kvm_geterr(kd_dump));
457 		exit(1);
458 	}
459 
460 	/* Copy the core file. */
461 	syslog(LOG_NOTICE, "writing %score to %s",
462 	    compress ? "compressed " : "", path);
463 	for (; dumpsize > 0; dumpsize -= nr) {
464 		(void)printf("%6dK\r", dumpsize / 1024);
465 		(void)fflush(stdout);
466 		nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
467 		if (nr <= 0) {
468 			if (nr == 0)
469 				syslog(LOG_WARNING,
470 				    "WARNING: EOF on dump device");
471 			else
472 				syslog(LOG_ERR, "%s: %m", rawp);
473 			goto err2;
474 		}
475 		nw = fwrite(buf, 1, nr, fp);
476 		if (nw != nr) {
477 			syslog(LOG_ERR, "%s: %s",
478 			    path, strerror(nw == 0 ? EIO : errno));
479 err2:			syslog(LOG_WARNING,
480 			    "WARNING: core may be incomplete");
481 			(void)printf("\n");
482 			exit(1);
483 		}
484 	}
485 	(void)close(ifd);
486 	(void)fclose(fp);
487 
488 	/* Copy the kernel. */
489 	ifd = Open(kernel ? kernel : _PATH_UNIX, O_RDONLY);
490 	(void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
491 	    dirname, bounds, compress ? ".Z" : "");
492 	if (compress) {
493 		if ((fp = zopen(path, "w", 0)) == NULL) {
494 			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
495 			exit(1);
496 		}
497 	} else
498 		ofd = Create(path, S_IRUSR | S_IWUSR);
499 	syslog(LOG_NOTICE, "writing %skernel to %s",
500 	    compress ? "compressed " : "", path);
501 	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
502 		if (compress)
503 			nw = fwrite(buf, 1, nr, fp);
504 		else
505 			nw = write(ofd, buf, nr);
506 		if (nw != nr) {
507 			syslog(LOG_ERR, "%s: %s",
508 			    path, strerror(nw == 0 ? EIO : errno));
509 			syslog(LOG_WARNING,
510 			    "WARNING: kernel may be incomplete");
511 			exit(1);
512 		}
513 	}
514 	if (nr < 0) {
515 		syslog(LOG_ERR, "%s: %s",
516 		    kernel ? kernel : _PATH_UNIX, strerror(errno));
517 		syslog(LOG_WARNING,
518 		    "WARNING: kernel may be incomplete");
519 		exit(1);
520 	}
521 	if (compress)
522 		(void)fclose(fp);
523 	else
524 		(void)close(ofd);
525 }
526 
527 char *
528 find_dev(dev, type)
529 	dev_t dev;
530 	int type;
531 {
532 	DIR *dfd;
533 	struct dirent *dir;
534 	struct stat sb;
535 	char *dp, devname[MAXPATHLEN + 1];
536 
537 	if ((dfd = opendir(_PATH_DEV)) == NULL) {
538 		syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno));
539 		exit(1);
540 	}
541 	(void)strcpy(devname, _PATH_DEV);
542 	while ((dir = readdir(dfd))) {
543 		(void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name);
544 		if (lstat(devname, &sb)) {
545 			syslog(LOG_ERR, "%s: %s", devname, strerror(errno));
546 			continue;
547 		}
548 		if ((sb.st_mode & S_IFMT) != type)
549 			continue;
550 		if (dev == sb.st_rdev) {
551 			closedir(dfd);
552 			if ((dp = strdup(devname)) == NULL) {
553 				syslog(LOG_ERR, "%s", strerror(errno));
554 				exit(1);
555 			}
556 			return (dp);
557 		}
558 	}
559 	closedir(dfd);
560 	syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
561 	exit(1);
562 }
563 
564 char *
565 rawname(s)
566 	char *s;
567 {
568 	char *sl, name[MAXPATHLEN];
569 
570 	if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
571 		syslog(LOG_ERR,
572 		    "can't make raw dump device name from %s", s);
573 		return (s);
574 	}
575 	(void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s,
576 	    sl + 1);
577 	if ((sl = strdup(name)) == NULL) {
578 		syslog(LOG_ERR, "%s", strerror(errno));
579 		exit(1);
580 	}
581 	return (sl);
582 }
583 
584 int
585 get_crashtime()
586 {
587 	time_t dumptime;			/* Time the dump was taken. */
588 
589 	if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &dumptime) != 0) {
590 		if (verbose)
591 		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
592 		return (0);
593 	}
594 	if (dumptime == 0) {
595 		if (verbose)
596 			syslog(LOG_ERR, "dump time is zero");
597 		return (0);
598 	}
599 	(void)printf("savecore: system went down at %s", ctime(&dumptime));
600 #define	LEEWAY	(7 * SECSPERDAY)
601 	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
602 		(void)printf("dump time is unreasonable\n");
603 		return (0);
604 	}
605 	return (1);
606 }
607 
608 int
609 check_space()
610 {
611 	FILE *fp;
612 	char *tkernel;
613 	off_t minfree, spacefree, kernelsize, needed;
614 	struct stat st;
615 	struct statfs fsbuf;
616 	char buf[100], path[MAXPATHLEN];
617 
618 #ifdef __GNUC__
619 	(void) &minfree;
620 #endif
621 
622 	tkernel = kernel ? kernel : _PATH_UNIX;
623 	if (stat(tkernel, &st) < 0) {
624 		syslog(LOG_ERR, "%s: %m", tkernel);
625 		exit(1);
626 	}
627 	kernelsize = st.st_blocks * S_BLKSIZE;
628 	if (statfs(dirname, &fsbuf) < 0) {
629 		syslog(LOG_ERR, "%s: %m", dirname);
630 		exit(1);
631 	}
632 	spacefree = fsbuf.f_bavail;
633 	spacefree *= fsbuf.f_bsize;
634 	spacefree /= 1024;
635 
636 	(void)snprintf(path, sizeof(path), "%s/minfree", dirname);
637 	if ((fp = fopen(path, "r")) == NULL)
638 		minfree = 0;
639 	else {
640 		if (fgets(buf, sizeof(buf), fp) == NULL)
641 			minfree = 0;
642 		else
643 			minfree = atoi(buf);
644 		(void)fclose(fp);
645 	}
646 
647 	needed = (dumpsize + kernelsize) / 1024;
648  	if (minfree > 0 && spacefree - needed < minfree) {
649 		syslog(LOG_WARNING,
650 		    "no dump, not enough free space in %s", dirname);
651 		return (0);
652 	}
653 	if (spacefree - needed < minfree)
654 		syslog(LOG_WARNING,
655 		    "dump performed, but free space threshold crossed");
656 	return (1);
657 }
658 
659 int
660 Open(name, rw)
661 	char *name;
662 	int rw;
663 {
664 	int fd;
665 
666 	if ((fd = open(name, rw, 0)) < 0) {
667 		syslog(LOG_ERR, "%s: %m", name);
668 		exit(1);
669 	}
670 	return (fd);
671 }
672 
673 void
674 Lseek(fd, off, flag)
675 	int fd, flag;
676 	off_t off;
677 {
678 	off_t ret;
679 
680 	ret = lseek(fd, off, flag);
681 	if (ret == -1) {
682 		syslog(LOG_ERR, "lseek: %m");
683 		exit(1);
684 	}
685 }
686 
687 int
688 Create(file, mode)
689 	char *file;
690 	int mode;
691 {
692 	int fd;
693 
694 	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, 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