Home | History | Annotate | Line # | Download | only in savecore
savecore.c revision 1.13
      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 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 static char sccsid[] = "@(#)savecore.c	8.3 (Berkeley) 1/2/94";
     42 #endif /* not lint */
     43 
     44 #include <sys/param.h>
     45 #include <sys/stat.h>
     46 #include <sys/mount.h>
     47 #include <sys/syslog.h>
     48 #include <sys/time.h>
     49 
     50 #include <dirent.h>
     51 #include <errno.h>
     52 #include <fcntl.h>
     53 #include <nlist.h>
     54 #include <paths.h>
     55 #include <stdio.h>
     56 #include <stdlib.h>
     57 #include <string.h>
     58 #include <tzfile.h>
     59 #include <unistd.h>
     60 
     61 #define ok(number) ((number) - KERNBASE)
     62 
     63 struct nlist current_nl[] = {	/* Namelist for currently running system. */
     64 #define X_DUMPDEV	0
     65 	{ "_dumpdev" },
     66 #define X_DUMPLO	1
     67 	{ "_dumplo" },
     68 #define X_TIME		2
     69 	{ "_time" },
     70 #define	X_DUMPSIZE	3
     71 	{ "_dumpsize" },
     72 #define X_VERSION	4
     73 	{ "_version" },
     74 #define X_PANICSTR	5
     75 	{ "_panicstr" },
     76 #define	X_DUMPMAG	6
     77 	{ "_dumpmag" },
     78 	{ "" },
     79 };
     80 int cursyms[] = { X_DUMPDEV, X_DUMPLO, X_VERSION, X_DUMPMAG, -1 };
     81 int dumpsyms[] = { X_TIME, X_DUMPSIZE, X_VERSION, X_PANICSTR, X_DUMPMAG, -1 };
     82 
     83 struct nlist dump_nl[] = {	/* Name list for dumped system. */
     84 	{ "_dumpdev" },		/* Entries MUST be the same as */
     85 	{ "_dumplo" },		/*	those in current_nl[].  */
     86 	{ "_time" },
     87 	{ "_dumpsize" },
     88 	{ "_version" },
     89 	{ "_panicstr" },
     90 	{ "_dumpmag" },
     91 	{ "" },
     92 };
     93 
     94 /* Types match kernel declarations. */
     95 long	dumplo;				/* where dump starts on dumpdev */
     96 int	dumpmag;			/* magic number in dump */
     97 int	dumpsize;			/* amount of memory dumped */
     98 
     99 char	*vmunix;
    100 char	*dirname;			/* directory to save dumps in */
    101 char	*ddname;			/* name of dump device */
    102 dev_t	dumpdev;			/* dump device */
    103 int	dumpfd;				/* read/write descriptor on block dev */
    104 time_t	now;				/* current date */
    105 char	panic_mesg[1024];
    106 int	panicstr;
    107 char	vers[1024];
    108 
    109 int	clear, compress, force, verbose;	/* flags */
    110 
    111 void	 check_kmem __P((void));
    112 int	 check_space __P((void));
    113 void	 clear_dump __P((void));
    114 int	 Create __P((char *, int));
    115 int	 dump_exists __P((void));
    116 char	*find_dev __P((dev_t, int));
    117 int	 get_crashtime __P((void));
    118 void	 kmem_setup __P((void));
    119 void	 log __P((int, char *, ...));
    120 void	 Lseek __P((int, off_t, int));
    121 int	 Open __P((char *, int rw));
    122 int	 Read __P((int, void *, int));
    123 char	*rawname __P((char *s));
    124 void	 save_core __P((void));
    125 void	 usage __P((void));
    126 void	 Write __P((int, void *, int));
    127 
    128 int
    129 main(argc, argv)
    130 	int argc;
    131 	char *argv[];
    132 {
    133 	int ch;
    134 
    135 	openlog("savecore", LOG_PERROR, LOG_DAEMON);
    136 
    137 	while ((ch = getopt(argc, argv, "cdfNvz")) != EOF)
    138 		switch(ch) {
    139 		case 'c':
    140 			clear = 1;
    141 			break;
    142 		case 'd':		/* Not documented. */
    143 		case 'v':
    144 			verbose = 1;
    145 			break;
    146 		case 'f':
    147 			force = 1;
    148 			break;
    149 		case 'N':
    150 			vmunix = optarg;
    151 			break;
    152 		case 'z':
    153 			compress = 1;
    154 			break;
    155 		case '?':
    156 		default:
    157 			usage();
    158 		}
    159 	argc -= optind;
    160 	argv += optind;
    161 
    162 	if (!clear) {
    163 		if (argc != 1 && argc != 2)
    164 			usage();
    165 		dirname = argv[0];
    166 	}
    167 	if (argc == 2)
    168 		vmunix = argv[1];
    169 
    170 	(void)time(&now);
    171 	kmem_setup();
    172 
    173 	if (clear) {
    174 		clear_dump();
    175 		exit(0);
    176 	}
    177 
    178 	if (!dump_exists() && !force)
    179 		exit(1);
    180 
    181 	check_kmem();
    182 
    183 	if (panicstr)
    184 		syslog(LOG_ALERT, "reboot after panic: %s", panic_mesg);
    185 	else
    186 		syslog(LOG_ALERT, "reboot");
    187 
    188 	if ((!get_crashtime() || !check_space()) && !force)
    189 		exit(1);
    190 
    191 	save_core();
    192 
    193 	clear_dump();
    194 	exit(0);
    195 }
    196 
    197 void
    198 kmem_setup()
    199 {
    200 	FILE *fp;
    201 	int kmem, i;
    202 	char *dump_sys;
    203 
    204 	/*
    205 	 * Some names we need for the currently running system, others for
    206 	 * the system that was running when the dump was made.  The values
    207 	 * obtained from the current system are used to look for things in
    208 	 * /dev/kmem that cannot be found in the dump_sys namelist, but are
    209 	 * presumed to be the same (since the disk partitions are probably
    210 	 * the same!)
    211 	 */
    212 	if ((nlist(_PATH_UNIX, current_nl)) == -1)
    213 		syslog(LOG_ERR, "%s: nlist: %s", _PATH_UNIX, strerror(errno));
    214 	for (i = 0; cursyms[i] != -1; i++)
    215 		if (current_nl[cursyms[i]].n_value == 0) {
    216 			syslog(LOG_ERR, "%s: %s not in namelist",
    217 			    _PATH_UNIX, current_nl[cursyms[i]].n_name);
    218 			exit(1);
    219 		}
    220 
    221 	dump_sys = vmunix ? vmunix : _PATH_UNIX;
    222 	if ((nlist(dump_sys, dump_nl)) == -1)
    223 		syslog(LOG_ERR, "%s: nlist: %s", dump_sys, strerror(errno));
    224 	for (i = 0; dumpsyms[i] != -1; i++)
    225 		if (dump_nl[dumpsyms[i]].n_value == 0) {
    226 			syslog(LOG_ERR, "%s: %s not in namelist",
    227 			    dump_sys, dump_nl[dumpsyms[i]].n_name);
    228 			exit(1);
    229 		}
    230 
    231 	kmem = Open(_PATH_KMEM, O_RDONLY);
    232 	Lseek(kmem, (off_t)current_nl[X_DUMPDEV].n_value, L_SET);
    233 	(void)Read(kmem, &dumpdev, sizeof(dumpdev));
    234 	if (dumpdev == NODEV) {
    235 		syslog(LOG_WARNING, "no core dump (no dumpdev)");
    236 		exit(1);
    237 	}
    238 	Lseek(kmem, (off_t)current_nl[X_DUMPLO].n_value, L_SET);
    239 	(void)Read(kmem, &dumplo, sizeof(dumplo));
    240 	if (verbose)
    241 		(void)printf("dumplo = %d (%d * %d)\n",
    242 		    dumplo, dumplo/DEV_BSIZE, DEV_BSIZE);
    243 	Lseek(kmem, (off_t)current_nl[X_DUMPMAG].n_value, L_SET);
    244 	(void)Read(kmem, &dumpmag, sizeof(dumpmag));
    245 	dumplo *= DEV_BSIZE;
    246 	ddname = find_dev(dumpdev, S_IFBLK);
    247 	dumpfd = Open(ddname, O_RDWR);
    248 	fp = fdopen(kmem, "r");
    249 	if (fp == NULL) {
    250 		syslog(LOG_ERR, "%s: fdopen: %m", _PATH_KMEM);
    251 		exit(1);
    252 	}
    253 	if (vmunix)
    254 		return;
    255 	(void)fseek(fp, (off_t)current_nl[X_VERSION].n_value, L_SET);
    256 	(void)fgets(vers, sizeof(vers), fp);
    257 
    258 	/* Don't fclose(fp), we use dumpfd later. */
    259 }
    260 
    261 void
    262 check_kmem()
    263 {
    264 	register char *cp;
    265 	FILE *fp;
    266 	char core_vers[1024];
    267 
    268 	fp = fdopen(dumpfd, "r");
    269 	if (fp == NULL) {
    270 		syslog(LOG_ERR, "%s: fdopen: %m", ddname);
    271 		exit(1);
    272 	}
    273 	fseek(fp, (off_t)(dumplo + ok(dump_nl[X_VERSION].n_value)), L_SET);
    274 	fgets(core_vers, sizeof(core_vers), fp);
    275 	if (strcmp(vers, core_vers) && vmunix == 0)
    276 		syslog(LOG_WARNING,
    277 		    "warning: %s version mismatch:\n\t%s\nand\t%s\n",
    278 		    _PATH_UNIX, vers, core_vers);
    279 	(void)fseek(fp,
    280 	    (off_t)(dumplo + ok(dump_nl[X_PANICSTR].n_value)), L_SET);
    281 	(void)fread(&panicstr, sizeof(panicstr), 1, fp);
    282 	if (panicstr) {
    283 		(void)fseek(fp, dumplo + ok(panicstr), L_SET);
    284 		cp = panic_mesg;
    285 		do
    286 			*cp = getc(fp);
    287 		while (*cp++ && cp < &panic_mesg[sizeof(panic_mesg)]);
    288 	}
    289 	/* Don't fclose(fp), we use dumpfd later. */
    290 }
    291 
    292 void
    293 clear_dump()
    294 {
    295 	long newdumplo;
    296 
    297 	newdumplo = 0;
    298 	Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
    299 	Write(dumpfd, &newdumplo, sizeof(newdumplo));
    300 }
    301 
    302 int
    303 dump_exists()
    304 {
    305 	int newdumpmag;
    306 
    307 	Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPMAG].n_value)), L_SET);
    308 	(void)Read(dumpfd, &newdumpmag, sizeof(newdumpmag));
    309 	if (newdumpmag != dumpmag) {
    310 		if (verbose)
    311 			syslog(LOG_WARNING, "magic number mismatch (%x != %x)",
    312 			    newdumpmag, dumpmag);
    313 		syslog(LOG_WARNING, "no core dump");
    314 		return (0);
    315 	}
    316 	return (1);
    317 }
    318 
    319 char buf[1024 * 1024];
    320 
    321 void
    322 save_core()
    323 {
    324 	register FILE *fp;
    325 	register int bounds, ifd, nr, nw, ofd;
    326 	char *rawp, path[MAXPATHLEN];
    327 
    328 	/*
    329 	 * Get the current number and update the bounds file.  Do the update
    330 	 * now, because may fail later and don't want to overwrite anything.
    331 	 */
    332 	(void)snprintf(path, sizeof(path), "%s/bounds", dirname);
    333 	if ((fp = fopen(path, "r")) == NULL)
    334 		goto err1;
    335 	if (fgets(buf, sizeof(buf), fp) == NULL) {
    336 		if (ferror(fp))
    337 err1:			syslog(LOG_WARNING, "%s: %s", path, strerror(errno));
    338 		bounds = 0;
    339 	} else
    340 		bounds = atoi(buf);
    341 	if (fp != NULL)
    342 		(void)fclose(fp);
    343 	if ((fp = fopen(path, "w")) == NULL)
    344 		syslog(LOG_ERR, "%s: %m", path);
    345 	else {
    346 		(void)fprintf(fp, "%d\n", bounds + 1);
    347 		(void)fclose(fp);
    348 	}
    349 	(void)fclose(fp);
    350 
    351 	/* Create the core file. */
    352 	(void)snprintf(path, sizeof(path), "%s/vmcore.%d%s",
    353 	    dirname, bounds, compress ? ".Z" : "");
    354 	if (compress) {
    355 		if ((fp = zopen(path, "w", 0)) == NULL) {
    356 			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
    357 			exit(1);
    358 		}
    359 	} else
    360 		ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    361 
    362 	/* Open the raw device. */
    363 	rawp = rawname(ddname);
    364 	if ((ifd = open(rawp, O_RDONLY)) == -1) {
    365 		syslog(LOG_WARNING, "%s: %m; using block device", rawp);
    366 		ifd = dumpfd;
    367 	}
    368 
    369 	/* Read the dump size. */
    370 	Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_DUMPSIZE].n_value)), L_SET);
    371 	(void)Read(dumpfd, &dumpsize, sizeof(dumpsize));
    372 
    373 	/* Seek to the start of the core. */
    374 	Lseek(ifd, (off_t)dumplo, L_SET);
    375 
    376 	/* Copy the core file. */
    377 	dumpsize *= NBPG;
    378 	syslog(LOG_NOTICE, "writing %score to %s",
    379 	    compress ? "compressed " : "", path);
    380 	for (; dumpsize > 0; dumpsize -= nr) {
    381 		(void)printf("%6dK\r", dumpsize / 1024);
    382 		(void)fflush(stdout);
    383 		nr = read(ifd, buf, MIN(dumpsize, sizeof(buf)));
    384 		if (nr <= 0) {
    385 			if (nr == 0)
    386 				syslog(LOG_WARNING,
    387 				    "WARNING: EOF on dump device");
    388 			else
    389 				syslog(LOG_ERR, "%s: %m", rawp);
    390 			goto err2;
    391 		}
    392 		if (compress)
    393 			nw = fwrite(buf, 1, nr, fp);
    394 		else
    395 			nw = write(ofd, buf, nr);
    396 		if (nw != nr) {
    397 			syslog(LOG_ERR, "%s: %s",
    398 			    path, strerror(nw == 0 ? EIO : errno));
    399 err2:			syslog(LOG_WARNING,
    400 			    "WARNING: vmcore may be incomplete");
    401 			(void)printf("\n");
    402 			exit(1);
    403 		}
    404 	}
    405 	(void)printf("\n");
    406 	(void)close(ifd);
    407 	if (compress)
    408 		(void)fclose(fp);
    409 	else
    410 		(void)close(ofd);
    411 
    412 	/* Copy the kernel. */
    413 	ifd = Open(vmunix ? vmunix : _PATH_UNIX, O_RDONLY);
    414 	(void)snprintf(path, sizeof(path), "%s/netbsd.%d%s",
    415 	    dirname, bounds, compress ? ".Z" : "");
    416 	if (compress) {
    417 		if ((fp = zopen(path, "w", 0)) == NULL) {
    418 			syslog(LOG_ERR, "%s: %s", path, strerror(errno));
    419 			exit(1);
    420 		}
    421 	} else
    422 		ofd = Create(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    423 	syslog(LOG_NOTICE, "writing %skernel to %s",
    424 	    compress ? "compressed " : "", path);
    425 	while ((nr = read(ifd, buf, sizeof(buf))) > 0) {
    426 		if (compress)
    427 			nw = fwrite(buf, 1, nr, fp);
    428 		else
    429 			nw = write(ofd, buf, nr);
    430 		if (nw != nr) {
    431 			syslog(LOG_ERR, "%s: %s",
    432 			    path, strerror(nw == 0 ? EIO : errno));
    433 			syslog(LOG_WARNING,
    434 			    "WARNING: vmunix may be incomplete");
    435 			exit(1);
    436 		}
    437 	}
    438 	if (nr < 0) {
    439 		syslog(LOG_ERR, "%s: %s",
    440 		    vmunix ? vmunix : _PATH_UNIX, strerror(errno));
    441 		syslog(LOG_WARNING,
    442 		    "WARNING: vmunix may be incomplete");
    443 		exit(1);
    444 	}
    445 	if (compress)
    446 		(void)fclose(fp);
    447 	else
    448 		(void)close(ofd);
    449 }
    450 
    451 char *
    452 find_dev(dev, type)
    453 	register dev_t dev;
    454 	register int type;
    455 {
    456 	register DIR *dfd;
    457 	struct dirent *dir;
    458 	struct stat sb;
    459 	char *dp, devname[MAXPATHLEN + 1];
    460 
    461 	if ((dfd = opendir(_PATH_DEV)) == NULL) {
    462 		syslog(LOG_ERR, "%s: %s", _PATH_DEV, strerror(errno));
    463 		exit(1);
    464 	}
    465 	(void)strcpy(devname, _PATH_DEV);
    466 	while ((dir = readdir(dfd))) {
    467 		(void)strcpy(devname + sizeof(_PATH_DEV) - 1, dir->d_name);
    468 		if (lstat(devname, &sb)) {
    469 			syslog(LOG_ERR, "%s: %s", devname, strerror(errno));
    470 			continue;
    471 		}
    472 		if ((sb.st_mode & S_IFMT) != type)
    473 			continue;
    474 		if (dev == sb.st_rdev) {
    475 			closedir(dfd);
    476 			if ((dp = strdup(devname)) == NULL) {
    477 				syslog(LOG_ERR, "%s", strerror(errno));
    478 				exit(1);
    479 			}
    480 			return (dp);
    481 		}
    482 	}
    483 	closedir(dfd);
    484 	syslog(LOG_ERR, "can't find device %d/%d", major(dev), minor(dev));
    485 	exit(1);
    486 }
    487 
    488 char *
    489 rawname(s)
    490 	char *s;
    491 {
    492 	char *sl, name[MAXPATHLEN];
    493 
    494 	if ((sl = rindex(s, '/')) == NULL || sl[1] == '0') {
    495 		syslog(LOG_ERR,
    496 		    "can't make raw dump device name from %s", s);
    497 		return (s);
    498 	}
    499 	(void)snprintf(name, sizeof(name), "%.*s/r%s", sl - s, s, sl + 1);
    500 	if ((sl = strdup(name)) == NULL) {
    501 		syslog(LOG_ERR, "%s", strerror(errno));
    502 		exit(1);
    503 	}
    504 	return (sl);
    505 }
    506 
    507 int
    508 get_crashtime()
    509 {
    510 	time_t dumptime;			/* Time the dump was taken. */
    511 
    512 	Lseek(dumpfd, (off_t)(dumplo + ok(dump_nl[X_TIME].n_value)), L_SET);
    513 	(void)Read(dumpfd, &dumptime, sizeof(dumptime));
    514 	if (dumptime == 0) {
    515 		if (verbose)
    516 			syslog(LOG_ERR, "dump time is zero");
    517 		return (0);
    518 	}
    519 	(void)printf("savecore: system went down at %s", ctime(&dumptime));
    520 #define	LEEWAY	(7 * SECSPERDAY)
    521 	if (dumptime < now - LEEWAY || dumptime > now + LEEWAY) {
    522 		(void)printf("dump time is unreasonable\n");
    523 		return (0);
    524 	}
    525 	return (1);
    526 }
    527 
    528 int
    529 check_space()
    530 {
    531 	register FILE *fp;
    532 	char *tvmunix;
    533 	off_t minfree, spacefree, vmunixsize, needed;
    534 	struct stat st;
    535 	struct statfs fsbuf;
    536 	char buf[100], path[MAXPATHLEN];
    537 
    538 	tvmunix = vmunix ? vmunix : _PATH_UNIX;
    539 	if (stat(tvmunix, &st) < 0) {
    540 		syslog(LOG_ERR, "%s: %m", tvmunix);
    541 		exit(1);
    542 	}
    543 	vmunixsize = st.st_blocks * S_BLKSIZE;
    544 	if (statfs(dirname, &fsbuf) < 0) {
    545 		syslog(LOG_ERR, "%s: %m", dirname);
    546 		exit(1);
    547 	}
    548  	spacefree = (fsbuf.f_bavail * fsbuf.f_bsize) / 1024;
    549 
    550 	(void)snprintf(path, sizeof(path), "%s/minfree", dirname);
    551 	if ((fp = fopen(path, "r")) == NULL)
    552 		minfree = 0;
    553 	else {
    554 		if (fgets(buf, sizeof(buf), fp) == NULL)
    555 			minfree = 0;
    556 		else
    557 			minfree = atoi(buf);
    558 		(void)fclose(fp);
    559 	}
    560 
    561 	needed = (dumpsize + vmunixsize) / 1024;
    562  	if (minfree > 0 && spacefree - needed < minfree) {
    563 		syslog(LOG_WARNING,
    564 		    "no dump, not enough free space on device");
    565 		return (0);
    566 	}
    567 	if (spacefree - needed < minfree)
    568 		syslog(LOG_WARNING,
    569 		    "dump performed, but free space threshold crossed");
    570 	return (1);
    571 }
    572 
    573 int
    574 Open(name, rw)
    575 	char *name;
    576 	int rw;
    577 {
    578 	int fd;
    579 
    580 	if ((fd = open(name, rw, 0)) < 0) {
    581 		syslog(LOG_ERR, "%s: %m", name);
    582 		exit(1);
    583 	}
    584 	return (fd);
    585 }
    586 
    587 int
    588 Read(fd, bp, size)
    589 	int fd, size;
    590 	void *bp;
    591 {
    592 	int nr;
    593 
    594 	nr = read(fd, bp, size);
    595 	if (nr != size) {
    596 		syslog(LOG_ERR, "read: %m");
    597 		exit(1);
    598 	}
    599 	return (nr);
    600 }
    601 
    602 void
    603 Lseek(fd, off, flag)
    604 	int fd, flag;
    605 	off_t off;
    606 {
    607 	off_t ret;
    608 
    609 	ret = lseek(fd, off, flag);
    610 	if (ret == -1) {
    611 		syslog(LOG_ERR, "lseek: %m");
    612 		exit(1);
    613 	}
    614 }
    615 
    616 int
    617 Create(file, mode)
    618 	char *file;
    619 	int mode;
    620 {
    621 	register int fd;
    622 
    623 	fd = creat(file, mode);
    624 	if (fd < 0) {
    625 		syslog(LOG_ERR, "%s: %m", file);
    626 		exit(1);
    627 	}
    628 	return (fd);
    629 }
    630 
    631 void
    632 Write(fd, bp, size)
    633 	int fd, size;
    634 	void *bp;
    635 {
    636 	int n;
    637 
    638 	if ((n = write(fd, bp, size)) < size) {
    639 		syslog(LOG_ERR, "write: %s", strerror(n == -1 ? errno : EIO));
    640 		exit(1);
    641 	}
    642 }
    643 
    644 void
    645 usage()
    646 {
    647 	(void)syslog(LOG_ERR, "usage: savecore [-cfvz] [-N system] directory");
    648 	exit(1);
    649 }
    650