savecore.c revision 1.44
1/*	$NetBSD: savecore.c,v 1.44 2000/12/07 03:17:17 wiz 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
44static char sccsid[] = "@(#)savecore.c	8.5 (Berkeley) 4/28/95";
45#else
46__RCSID("$NetBSD: savecore.c,v 1.44 2000/12/07 03:17:17 wiz 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#include <sys/sysctl.h>
56#include <machine/cpu.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 <time.h>
67#include <tzfile.h>
68#include <unistd.h>
69#include <limits.h>
70#include <kvm.h>
71
72extern FILE *zopen __P((const char *fname, const char *mode, int bits));
73
74#define KREAD(kd, addr, p)\
75	(kvm_read(kd, addr, (char *)(p), sizeof(*(p))) != sizeof(*(p)))
76
77struct nlist current_nl[] = {	/* Namelist for currently running system. */
78#define X_DUMPDEV	0
79	{ "_dumpdev" },
80#define X_DUMPLO	1
81	{ "_dumplo" },
82#define X_TIME		2
83	{ "_time" },
84#define	X_DUMPSIZE	3
85	{ "_dumpsize" },
86#define X_VERSION	4
87	{ "_version" },
88#define X_PANICSTR	5
89	{ "_panicstr" },
90#define	X_DUMPMAG	6
91	{ "_dumpmag" },
92	{ NULL },
93};
94int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
95int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
96
97struct nlist dump_nl[] = {	/* Name list for dumped system. */
98	{ "_dumpdev" },		/* Entries MUST be the same as */
99	{ "_dumplo" },		/*	those in current_nl[].  */
100	{ "_time" },
101	{ "_dumpsize" },
102	{ "_version" },
103	{ "_panicstr" },
104	{ "_dumpmag" },
105	{ NULL },
106};
107
108/* Types match kernel declarations. */
109long	dumplo;				/* where dump starts on dumpdev */
110int	dumpmag;			/* magic number in dump */
111int	dumpsize;			/* amount of memory dumped */
112
113char	*kernel;			/* name of used kernel */
114char	*dirname;			/* directory to save dumps in */
115char	*ddname;			/* name of dump device */
116dev_t	dumpdev;			/* dump device */
117int	dumpfd;				/* read/write descriptor on block dev */
118kvm_t	*kd_dump;			/* kvm descriptor on block dev	*/
119time_t	now;				/* current date */
120char	panic_mesg[1024];
121long	panicstr;
122char	vers[1024];
123
124int	clear, compress, force, verbose;	/* flags */
125
126void	 check_kmem __P((void));
127int	 check_space __P((void));
128void	 clear_dump __P((void));
129int	 Create __P((char *, int));
130int	 dump_exists __P((void));
131char	*find_dev __P((dev_t, int));
132int	 get_crashtime __P((void));
133void	 kmem_setup __P((void));
134void	 log __P((int, char *, ...));
135void	 Lseek __P((int, off_t, int));
136int	 main __P((int, char *[]));
137int	 Open __P((char *, int rw));
138char	*rawname __P((char *s));
139void	 save_core __P((void));
140void	 usage __P((void));
141void	 Write __P((int, void *, int));
142
143int
144main(argc, argv)
145	int argc;
146	char *argv[];
147{
148	int ch;
149#ifdef CPU_BOOTED_KERNEL
150	int mib[2];
151	size_t size;
152	char buf[MAXPATHLEN];
153#endif
154
155	dirname = NULL;
156	kernel = NULL;
157
158	openlog("savecore", LOG_PERROR, LOG_DAEMON);
159
160	while ((ch = getopt(argc, argv, "cdfN:vz")) != -1)
161		switch(ch) {
162		case 'c':
163			clear = 1;
164			break;
165		case 'd':		/* Not documented. */
166		case 'v':
167			verbose = 1;
168			break;
169		case 'f':
170			force = 1;
171			break;
172		case 'N':
173			kernel = optarg;
174			break;
175		case 'z':
176			compress = 1;
177			break;
178		case '?':
179		default:
180			usage();
181		}
182	argc -= optind;
183	argv += optind;
184
185	if (argc != (clear ? 0 : 1))
186		usage();
187
188	if (!clear)
189		dirname = argv[0];
190
191	if (kernel == NULL) {
192		kernel = _PATH_UNIX;
193#ifdef CPU_BOOTED_KERNEL
194		/* find real boot-kernel name */
195		mib[0] = CTL_MACHDEP;
196		mib[1] = CPU_BOOTED_KERNEL;
197		size = sizeof(buf) - 1;
198		if (sysctl(mib, 2, buf + 1, &size, NULL, NULL) == 0) {
199			/*
200			 * traditionally, this sysctl returns the
201			 * relative path of the kernel with the
202			 * leading slash stripped -- could be empty,
203			 * though (e.g. when netbooting).
204			 */
205			if (buf[1] != '\0') {
206				buf[0] = '/';
207				kernel = buf;
208			}
209		}
210#endif
211	}
212
213	(void)time(&now);
214	kmem_setup();
215
216	if (clear) {
217		clear_dump();
218		exit(0);
219	}
220
221	if (!dump_exists() && !force)
222		exit(1);
223
224	check_kmem();
225
226	if (panicstr)
227		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
228	else
229		syslog(LOG_ALERT, "reboot");
230
231	if ((!get_crashtime() || !check_space()) && !force)
232		exit(1);
233
234	save_core();
235
236	clear_dump();
237	exit(0);
238}
239
240void
241kmem_setup()
242{
243	kvm_t	*kd_kern;
244	char	errbuf[_POSIX2_LINE_MAX];
245	int	i, hdrsz;
246
247	/*
248	 * Some names we need for the currently running system, others for
249	 * the system that was running when the dump was made.  The values
250	 * obtained from the current system are used to look for things in
251	 * /dev/kmem that cannot be found in the kernel namelist, but are
252	 * presumed to be the same (since the disk partitions are probably
253	 * the same!)
254	 */
255	kd_kern = kvm_openfiles(kernel, NULL, NULL, O_RDONLY, errbuf);
256	if (kd_kern == NULL) {
257		syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
258		exit(1);
259	}
260	if (kvm_nlist(kd_kern, current_nl) == -1)
261		syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
262			kvm_geterr(kd_kern));
263
264	for (i = 0; cursyms[i] != -1; i++)
265		if (current_nl[cursyms[i]].n_value == 0) {
266			syslog(LOG_ERR, "%s: %s not in namelist",
267			    kernel, current_nl[cursyms[i]].n_name);
268			exit(1);
269		}
270
271	if (KREAD(kd_kern, current_nl[X_DUMPDEV].n_value, &dumpdev) != 0) {
272		if (verbose)
273		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
274		exit(1);
275	}
276	if (dumpdev == NODEV) {
277		syslog(LOG_WARNING, "no core dump (no dumpdev)");
278		exit(1);
279	}
280	if (KREAD(kd_kern, current_nl[X_DUMPLO].n_value, &dumplo) != 0) {
281		if (verbose)
282		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
283		exit(1);
284	}
285	dumplo *= DEV_BSIZE;
286	if (verbose)
287		(void)printf("dumplo = %ld (%ld * %ld)\n",
288		    (long)dumplo, (long)(dumplo / DEV_BSIZE), (long)DEV_BSIZE);
289	if (KREAD(kd_kern, current_nl[X_DUMPMAG].n_value, &dumpmag) != 0) {
290		if (verbose)
291		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_kern));
292		exit(1);
293	}
294
295	(void)kvm_read(kd_kern, current_nl[X_VERSION].n_value,
296	    vers, sizeof(vers));
297	vers[sizeof(vers) - 1] = '\0';
298
299	ddname = find_dev(dumpdev, S_IFBLK);
300	dumpfd = Open(ddname, O_RDWR);
301
302	kd_dump = kvm_openfiles(kernel, ddname, NULL, O_RDWR, errbuf);
303	if (kd_dump == NULL) {
304		syslog(LOG_ERR, "%s: kvm_openfiles: %s", kernel, errbuf);
305		exit(1);
306	}
307
308	if (kvm_nlist(kd_dump, dump_nl) == -1)
309		syslog(LOG_ERR, "%s: kvm_nlist: %s", kernel,
310		    kvm_geterr(kd_dump));
311
312	for (i = 0; dumpsyms[i] != -1; i++)
313		if (dump_nl[dumpsyms[i]].n_value == 0) {
314			syslog(LOG_ERR, "%s: %s not in namelist",
315			    kernel, dump_nl[dumpsyms[i]].n_name);
316			exit(1);
317		}
318	hdrsz = kvm_dump_mkheader(kd_dump, (off_t)dumplo);
319
320	/*
321	 * If 'hdrsz' == 0, kvm_dump_mkheader() failed on the magic-number
322	 * checks, ergo no dump is present...
323	 */
324	if (hdrsz == 0) {
325		syslog(LOG_WARNING, "no core dump");
326		exit(1);
327	}
328	if (hdrsz == -1) {
329		syslog(LOG_ERR, "%s: kvm_dump_mkheader: %s", kernel,
330			kvm_geterr(kd_dump));
331		exit(1);
332	}
333	dumplo += hdrsz;
334	kvm_close(kd_kern);
335}
336
337void
338check_kmem()
339{
340	char	*cp;
341	long	panicloc;
342	char core_vers[1024];
343
344	(void)kvm_read(kd_dump, dump_nl[X_VERSION].n_value, core_vers,
345		sizeof(core_vers));
346	core_vers[sizeof(core_vers) - 1] = '\0';
347
348	if (strcmp(vers, core_vers) && kernel == 0)
349		syslog(LOG_WARNING,
350		    "warning: %s version mismatch:\n\t%s\nand\t%s\n",
351		    kernel, vers, core_vers);
352
353	if (KREAD(kd_dump, dump_nl[X_PANICSTR].n_value, &panicstr) != 0) {
354		if (verbose)
355		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
356		return;
357	}
358	if (panicstr) {
359		cp       = panic_mesg;
360		panicloc = panicstr;
361		do {
362			if (KREAD(kd_dump, panicloc, cp) != 0) {
363				if (verbose)
364				    syslog(LOG_WARNING, "kvm_read: %s",
365					   kvm_geterr(kd_dump));
366				break;
367			}
368			panicloc++;
369		} while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)-1]);
370		panic_mesg[sizeof(panic_mesg) - 1] = '\0';
371	}
372}
373
374int
375dump_exists()
376{
377	int newdumpmag;
378
379	if (KREAD(kd_dump, dump_nl[X_DUMPMAG].n_value, &newdumpmag) != 0) {
380		if (verbose)
381		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
382		return (0);
383	}
384
385	/* Read the dump size. */
386	if (KREAD(kd_dump, dump_nl[X_DUMPSIZE].n_value, &dumpsize) != 0) {
387		if (verbose)
388		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
389		return (0);
390	}
391	dumpsize *= getpagesize();
392
393	/*
394	 * Return zero if core dump doesn't seem to be there, and note
395	 * it for syslog.  This check and return happens after the dump size
396	 * is read, so dumpsize is whether or not the core is valid (for -f).
397	 */
398	if (newdumpmag != dumpmag) {
399		if (verbose)
400			syslog(LOG_WARNING,
401			    "magic number mismatch (0x%x != 0x%x)",
402			    newdumpmag, dumpmag);
403		syslog(LOG_WARNING, "no core dump");
404		return (0);
405	}
406	return (1);
407}
408
409void
410clear_dump()
411{
412	if (kvm_dump_inval(kd_dump) == -1)
413		syslog(LOG_ERR, "%s: kvm_clear_dump: %s", ddname,
414			kvm_geterr(kd_dump));
415
416}
417
418char buf[1024 * 1024];
419
420void
421save_core()
422{
423	FILE *fp;
424	int bounds, ifd, nr, nw, ofd;
425	char *rawp, path[MAXPATHLEN];
426
427	ofd = -1;
428	/*
429	 * Get the current number and update the bounds file.  Do the update
430	 * now, because may fail later and don't want to overwrite anything.
431	 */
432	umask(066);
433	(void)snprintf(path, sizeof(path), "%s/bounds", dirname);
434	if ((fp = fopen(path, "r")) == NULL)
435		goto err1;
436	if (fgets(buf, sizeof(buf), fp) == NULL) {
437		if (ferror(fp))
438err1:			syslog(LOG_WARNING, "%s: %s", path, strerror(errno));
439		bounds = 0;
440	} else
441		bounds = atoi(buf);
442	if (fp != NULL)
443		(void)fclose(fp);
444	if ((fp = fopen(path, "w")) == NULL)
445		syslog(LOG_ERR, "%s: %m", path);
446	else {
447		(void)fprintf(fp, "%d\n", bounds + 1);
448		(void)fclose(fp);
449	}
450
451	/* Create the core file. */
452	(void)snprintf(path, sizeof(path), "%s/netbsd.%d.core%s",
453	    dirname, bounds, compress ? ".Z" : "");
454	if (compress) {
455		if ((fp = zopen(path, "w", 0)) == NULL) {
456			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
457			exit(1);
458		}
459	} else {
460		ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
461		fp  = fdopen(ofd, "w");
462		if (fp == NULL) {
463			syslog(LOG_ERR, "%s: fdopen: %s", path,
464			    strerror(errno));
465			exit(1);
466		}
467	}
468
469	/* Open the raw device. */
470	rawp = rawname(ddname);
471	if ((ifd = open(rawp, O_RDONLY)) == -1) {
472		syslog(LOG_WARNING, "%s: %m; using block device", rawp);
473		ifd = dumpfd;
474	}
475
476	/* Seek to the start of the core. */
477	Lseek(ifd, (off_t)dumplo, SEEK_SET);
478
479	if (kvm_dump_wrtheader(kd_dump, fp, dumpsize) == -1) {
480		syslog(LOG_ERR, "kvm_dump_wrtheader: %s : %s", path,
481			kvm_geterr(kd_dump));
482		exit(1);
483	}
484
485	/* Copy the core file. */
486	syslog(LOG_NOTICE, "writing %score to %s",
487	    compress ? "compressed " : "", path);
488	for (; dumpsize > 0; dumpsize -= nr) {
489		(void)printf("%6dK\r", dumpsize / 1024);
490		(void)fflush(stdout);
491		nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
492		if (nr <= 0) {
493			if (nr == 0)
494				syslog(LOG_WARNING,
495				    "WARNING: EOF on dump device");
496			else
497				syslog(LOG_ERR, "%s: %m", rawp);
498			goto err2;
499		}
500		nw = fwrite(buf, 1, nr, fp);
501		if (nw != nr) {
502			syslog(LOG_ERR, "%s: %s",
503			    path, strerror(nw == 0 ? EIO : errno));
504err2:			syslog(LOG_WARNING,
505			    "WARNING: core may be incomplete");
506			(void)printf("\n");
507			exit(1);
508		}
509	}
510	(void)close(ifd);
511	(void)fclose(fp);
512
513	/* Copy the kernel. */
514	ifd = Open(kernel ? kernel : _PATH_UNIX, O_RDONLY);
515	(void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
516	    dirname, bounds, compress ? ".Z" : "");
517	if (compress) {
518		if ((fp = zopen(path, "w", 0)) == NULL) {
519			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
520			exit(1);
521		}
522	} else
523		ofd = Create(path, S_IRUSR | S_IWUSR);
524	syslog(LOG_NOTICE, "writing %skernel to %s",
525	    compress ? "compressed " : "", path);
526	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
527		if (compress)
528			nw = fwrite(buf, 1, nr, fp);
529		else
530			nw = write(ofd, buf, nr);
531		if (nw != nr) {
532			syslog(LOG_ERR, "%s: %s",
533			    path, strerror(nw == 0 ? EIO : errno));
534			syslog(LOG_WARNING,
535			    "WARNING: kernel may be incomplete");
536			exit(1);
537		}
538	}
539	if (nr < 0) {
540		syslog(LOG_ERR, "%s: %s",
541		    kernel ? kernel : _PATH_UNIX, strerror(errno));
542		syslog(LOG_WARNING,
543		    "WARNING: kernel may be incomplete");
544		exit(1);
545	}
546	if (compress)
547		(void)fclose(fp);
548	else
549		(void)close(ofd);
550}
551
552char *
553find_dev(dev, type)
554	dev_t dev;
555	int type;
556{
557	DIR *dfd;
558	struct dirent *dir;
559	struct stat sb;
560	char *dp, devname[MAXPATHLEN + 1];
561
562	if ((dfd = opendir(_PATH_DEV)) == NULL) {
563		syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno));
564		exit(1);
565	}
566	(void)strcpy(devname, _PATH_DEV);
567	while ((dir = readdir(dfd))) {
568		(void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name);
569		if (lstat(devname, &sb)) {
570			syslog(LOG_ERR, "%s: %s", devname, strerror(errno));
571			continue;
572		}
573		if ((sb.st_mode & S_IFMT) != type)
574			continue;
575		if (dev == sb.st_rdev) {
576			closedir(dfd);
577			if ((dp = strdup(devname)) == NULL) {
578				syslog(LOG_ERR, "%s", strerror(errno));
579				exit(1);
580			}
581			return (dp);
582		}
583	}
584	closedir(dfd);
585	syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
586	exit(1);
587}
588
589char *
590rawname(s)
591	char *s;
592{
593	char *sl, name[MAXPATHLEN];
594
595	if ((sl = strrchr(s, '/')) == NULL || sl[1] == '0') {
596		syslog(LOG_ERR,
597		    "can't make raw dump device name from %s", s);
598		return (s);
599	}
600	(void)snprintf(name, sizeof(name), "%.*s/r%s", (int)(sl - s), s,
601	    sl + 1);
602	if ((sl = strdup(name)) == NULL) {
603		syslog(LOG_ERR, "%s", strerror(errno));
604		exit(1);
605	}
606	return (sl);
607}
608
609int
610get_crashtime()
611{
612	struct timeval time;
613	time_t dumptime;			/* Time the dump was taken. */
614
615	if (KREAD(kd_dump, dump_nl[X_TIME].n_value, &time) != 0) {
616		if (verbose)
617		    syslog(LOG_WARNING, "kvm_read: %s", kvm_geterr(kd_dump));
618		return (0);
619	}
620	dumptime = time.tv_sec;
621	if (dumptime == 0) {
622		if (verbose)
623			syslog(LOG_ERR, "dump time is zero");
624		return (0);
625	}
626	(void)printf("savecore: system went down at %s", ctime(&dumptime));
627#define	LEEWAY	(7 * SECSPERDAY)
628	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
629		(void)printf("dump time is unreasonable\n");
630		return (0);
631	}
632	return (1);
633}
634
635int
636check_space()
637{
638	FILE *fp;
639	off_t minfree, spacefree, kernelsize, needed;
640	struct stat st;
641	struct statfs fsbuf;
642	char buf[100], path[MAXPATHLEN];
643
644#ifdef __GNUC__
645	(void) &minfree;
646#endif
647
648	if (stat(kernel, &st) < 0) {
649		syslog(LOG_ERR, "%s: %m", kernel);
650		exit(1);
651	}
652	kernelsize = st.st_blocks * S_BLKSIZE;
653	if (statfs(dirname, &fsbuf) < 0) {
654		syslog(LOG_ERR, "%s: %m", dirname);
655		exit(1);
656	}
657	spacefree = fsbuf.f_bavail;
658	spacefree *= fsbuf.f_bsize;
659	spacefree /= 1024;
660
661	(void)snprintf(path, sizeof(path), "%s/minfree", dirname);
662	if ((fp = fopen(path, "r")) == NULL)
663		minfree = 0;
664	else {
665		if (fgets(buf, sizeof(buf), fp) == NULL)
666			minfree = 0;
667		else
668			minfree = atoi(buf);
669		(void)fclose(fp);
670	}
671
672	needed = (dumpsize + kernelsize) / 1024;
673 	if (minfree > 0 && spacefree - needed < minfree) {
674		syslog(LOG_WARNING,
675		    "no dump, not enough free space in %s", dirname);
676		return (0);
677	}
678	if (spacefree - needed < minfree)
679		syslog(LOG_WARNING,
680		    "dump performed, but free space threshold crossed");
681	return (1);
682}
683
684int
685Open(name, rw)
686	char *name;
687	int rw;
688{
689	int fd;
690
691	if ((fd = open(name, rw, 0)) < 0) {
692		syslog(LOG_ERR, "%s: %m", name);
693		exit(1);
694	}
695	return (fd);
696}
697
698void
699Lseek(fd, off, flag)
700	int fd, flag;
701	off_t off;
702{
703	off_t ret;
704
705	ret = lseek(fd, off, flag);
706	if (ret == -1) {
707		syslog(LOG_ERR, "lseek: %m");
708		exit(1);
709	}
710}
711
712int
713Create(file, mode)
714	char *file;
715	int mode;
716{
717	int fd;
718
719	fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, mode);
720	if (fd < 0) {
721		syslog(LOG_ERR, "%s: %m", file);
722		exit(1);
723	}
724	return (fd);
725}
726
727void
728Write(fd, bp, size)
729	int fd, size;
730	void *bp;
731{
732	int n;
733
734	if ((n = write(fd, bp, size)) < size) {
735		syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
736		exit(1);
737	}
738}
739
740void
741usage()
742{
743	(void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory");
744	exit(1);
745}
746