Ultrix-3.1/src/cmd/el/ccd.c

Compare this file to the similar file:
Show the results in this format:


/**********************************************************************
 *   Copyright (c) Digital Equipment Corporation 1984, 1985, 1986.    *
 *   All Rights Reserved. 					      *
 *   Reference "/usr/src/COPYRIGHT" for applicable restrictions.      *
 **********************************************************************/

static char Sccsid[] = "@(#)ccd.c	3.1	7/8/87";
/*
 * ULTRIX-11 crash dump copy program (CCD)
 *
 * Fred Canter 10/10/83
 * Ohms 8/84
 * Chung-Wu Lee 2/12/85 - add tk50
 *
 * Copies the crash dump from:
 *	1) magtape
 *	2) TK50 magtape
 *	3) rx50/rx33 diskettes
 *	4) the swap area of the system disk
 * to a file (default: /usr/crash/core)
 *
 * Usage:	interactive !
 *
 * 1.	For dumps to the swap area of the system disk the crash 
 *	dump is written to the swap area starting at block number 
 *	(swaplo + 300). The 300 block offset allows the system to 
 *	reboot without stepping on the crash dump in the swap area.
 *
 * 2.	CCD must be run immediately after the system has been rebooted
 *	and any file system repairs have been completed.
 *
 * 3.	CCD makes gross checks to verify that there is a valid crash
 *	dump in the swap area.
 *
 * 4.	Only super-user can run CCD.
 */

#include <stdio.h>
#include <a.out.h>
#include <sys/devmaj.h>
#include <sys/types.h>
#include <sys/tk_info.h>
#include <sys/mtio.h>
#include <signal.h>

#define	YES	1
#define	NO	0

char	*unixname = "/unix";		/* Default kernel name		*/
char	*memfile = "/dev/mem";		/* /dev/mem			*/
char	*dumpfile = "/dev/dump";	/* Default name for dump device	*/
char	*corefile = "/usr/crash/core";	/* Default place to dump to	*/
char	ifile[50];			/* Name of the dump device	*/
char	ofile[50];			/* Place to put the core dump	*/
char	buf[10240];			/* Buffer for doing the copy	*/
char	line[50];			/* Temp buffer for user input	*/
int	fi = -1;			/* Input file descriptor	*/
int	fo = -1;			/* Output file descriptor	*/
char	tk_ctid;			/* Tmscp id - TK50 or TU81	*/
long	dataoff;			/* Offset of (text)data segment	*/
					/* for (non-)split I/D kernel	*/

/* Argument to pass to fd_exit() */
#define	READ_ERROR	0
#define	WRITE_ERROR	1
#define	COPY_DONE	2

/* Bit values for density argument for getdensity() */
#define	D800	01
#define	D1600	02
#define	D6250	04

/* This is used to rewind the tape after determining the blocking factor */
struct mtop mtop = {
	MTREW, 1,
};

/*
 * We have two namelists, one for the running kernel and one
 * for the kernel that crashed.  -Dave Borman 9/30/85
 */
struct nlist nlr[] = {
#define	X_REALMEM	0
	{ "_realmem" },
#define	X_TK_CTID	1
	{ "_tk_ctid" },
	{ "" },
};

struct nlist nld[] = {
#define	X_DUMP		0
	{ "dump" },
#define	X_DMP_DN	1
	{ "dmp_dn" },
#define	X_DMP_RX	2
	{ "dmp_rx" },
#define	X_DMP_TK	3
	{ "dmp_tk" },
#define	X_RL_DMPLO	4
	{ "rl_dmplo" },
#define	X_RL_DMPHI	5
	{ "rl_dmphi" },
#define	X_RA_DMPLO	6
	{ "ra_dmplo" },
#define	X_RA_DMPHI	7
	{ "ra_dmphi" },
#define	X_RP_DMPLO	8
	{ "rp_dmplo" },
#define	X_RP_DMPHI	9
	{ "rp_dmphi" },
#define	X_HK_DMPLO	10
	{ "hk_dmplo" },
#define	X_HK_DMPHI	11
	{ "hk_dmphi" },
#define	X_HP_DMPLO	12
	{ "hp_dmplo" },
#define	X_HP_DMPHI	13
	{ "hp_dmphi" },
	{ "" },
};

char	*rxnm = "\nFloppy disk unit number < 1 > ? ";
char	*rxnh[] =
{
	"",
	"Enter the unit number of the floppy drive where the dump diskettes",
	"will be loaded, then press <RETURN>. The default unit number is",
	"1, press <RETURN> to use the default unit.",
	"",
	0
};

char	*mtnm = "\nMagtape unit number < 0 > ? ";
char	*mtnh[] =
{
	"",
	"Enter the unit number of the tape drive where the crash dump",
	"tape is mounted, then press <RETURN>. To use the default unit",
	"number of zero, press <RETURN>.",
	"",
	0
};

char	*mtdh[] =
{
	"",
	"Enter the density at which the crash dump tape was written, then",
	&buf[512],			/* filled in by getdensity() */
	"there is no default.",
	"",
	0
};

/*
 * This string gets sprintf()ed into a buffer along with the default.
 */
char	*idevm = "\nCopy dump from ([m]agtape, [s]wap area, [r]x50/33, [t]k50) < %c > ? ";
char	*idevh[] =
{
	"",
	"Enter the first letter of the crash dump input device, that is:",
	"",
	"  m - copy the dump from a magtape",
	"  s - copy the dump from the system disk swap area",
	"  r - copy the dump from RX50/RX33 diskette(s)",
	"  t - copy the dump from TK50 magtape",
	"  ? - no default, current kernel has no dump device",
	"",
	"The letter enclosed in < > is the default response. This will be",
	"the dump device configured into the current ULTRIX-11 kernel. To",
	"use the default dump input device press <RETURN>.",
	"",
	0
};

char	*filem = "\nCopy crash dump to file < /usr/crash/core > ? ";
char	*fileh[] =
{
	"",
	"Enter the full pathname of the file that is to receive the crash",
	"dump, followed by <RETURN>. Type just <RETURN> to use the default",
	"file (/usr/crash/core).",
	"",
	"Ensure that the file system, where the output file will be created,",
	"has sufficient free space to hold the crash dump.  Use the disk free",
	"(df) command to print the amount of free space.",
	"",
	0
};
char	*nlrm = "\nFile containing currently running kernel < /unix > ? ";
char	*nlrh[] =
{
	"",
	"Enter the name of the file from which the currently running system",
	"was bootstrapped. Type just <RETURN> to use the default /unix.",
	"",
	"The file containg the currently running system contains the namelist",
	"which is a symbol table used to obtain the values of certain",
	"operating system parameters from memory. These parameters include",
	"how much memory is on the system.",
	"",
	"The name of the currently running kernel should always be /unix.",
	"However, if you booted a different kernel file and have not yet",
	"renamed it /unix, make sure you enter the correct name.",
	"",
	0
};
char	*nldm = "\nFile containing kernel that crashed < /unix > ? ";
char	*nldh[] =
{
	"",
	"Enter the name of the file from which contains the kernel that",
	"crashed. Type just <RETURN> to use the default /unix.",
	"",
	"The file containing the kernel that crashed contains the namelist",
	"which is a symbol table used to obtain the values of certain",
	"operating system parameters at the time of the crash. These",
	"parameters include the crash dump device.",
	"",
	0
};
char	*confm = "\nReady to begin Copy <y or n> ? ";
char	*confh[] =
{
	"",
	"This is the last chance to abort the copy. Type y or n followed",
	"by <RETURN>, y for yes or n for no. Typing just <RETURN> will be",
	"treated as a no answer.",
	"",
	0
};

/*
 * nblkm gets sprintf()ed into a buffer along with the block count
 *	-Dave Borman 9/29/85
 */
char	*nblkm = "\nNumber of K bytes to copy < %d > ? ";
char	*nblkh[] =
{
	"",
	"Enter the number of K bytes (1024 bytes) to be copied followed by",
	"<RETURN>.  Type just <RETURN> to use the default, which is based",
	"upon the amount of memory on the system.",
	"",
	"This allows the length of the output file to be limited to a given",
	"number of K bytes. This saves copy time and disk space.  The copy",
	"always begins at the start of the crash dump.",
	"",
	"If the disk space is available, it is wise to copy the entire crash",
	"dump. Remember that if the dump is saved in the swap area, once the",
	"system comes up in multi-user mode and becomes busy, the swap area",
	"will be overwritten!",
	"",
	0
};

main()
{
	register int	i, j, k;
	int		mem, fdd, dd_in, dd_maj, unit, *dumpbuf;
	unsigned int	dmplo, dmphi, nblk, dsize, ov[16];
	int		onintr();
	struct exec	dump_exc;


	signal(SIGINT, onintr);
	signal(SIGQUIT, onintr);

	printf("\n\nULTRIX-11 Crash Dump Copy Program\n");
	printf("<Respond to a question with ?<RETURN> for help>\n");
	printf("<Help text contains important information, must reading!>\n");
	/*
	 * Get names of the currently running kernel
	 * and the crashed kernel.
	 */
	if (getline(line, nlrm, nlrh) == 1)
		strcpy(line, unixname);
	if (nlist(line, nlr) < 0)
		errexit(0, "\nCan't access namelist in %s\n", line);
	if ((mem = open(memfile, 0)) < 0)
		errexit(1, "\nCan't access memory (%s)\n", memfile);

	if (getline(line, nldm, nldh) == 1)
		strcpy(line, unixname);
	if (nlist(line, nld) < 0)
		errexit(0, "\nCan't access namelist in %s\n", line);
	if ((fdd = open(line, 0)) < 0)
		errexit(1, "\nCan't open %s\n", line);

	/*
	 * Set up to read values from /unix.  If we are
	 * split I/D, the the stuff we want is in the
	 * data segement, if we aren't split I/D then
	 * the stuff we want is in the text segment.
	 * So, dataoff points to the data for split I/D,
	 * and to the text for non-split I/D.
	 */
	dataoff = sizeof(struct exec);
	read(fdd, (char *)&dump_exc, sizeof(struct exec));
	read(fdd, (char *)&ov[0], 32);	/* get overlay sizes */
	switch (dump_exc.a_magic) {
	case 0450:
		dataoff += 2L*sizeof(struct ovlhdr);
		break;
	case 0451:
		dataoff += sizeof(struct ovlhdr);
		dataoff += ov[8] + ov[9] + ov[10] + ov[11];
		dataoff += ov[12] + ov[13] + ov[14] + ov[15];
	case 0431:
		dataoff += (unsigned)dump_exc.a_text;
		dataoff += ov[1] + ov[2] + ov[3] + ov[4];
		dataoff += ov[5] + ov[6] + ov[7];
		/*FALLTHROUGH*/
	case 0430:
		dataoff += sizeof(struct ovlhdr);
		break;
	}

	/* read symbols from kernel that crashed */
	if (nld[X_REALMEM].n_value == 0)
		errexit(0, "\nCan't find symbol realmem in namelist\n");
	if(nld[X_DUMP].n_value == 0)		/* no dump dev */
		dd_in = '?';
	else if(nld[X_DMP_TK].n_value)		/* TK50 */
		dd_in = 't';
	else if(nld[X_DMP_RX].n_value)		/* RX50/RX33 */
		dd_in = 'r';
	else if(nld[X_DMP_DN].n_value)		/* swap area */
		dd_in = 's';
	else
		dd_in = 'm';		/* magtape */

	sprintf(buf, idevm, dd_in);
	for (;;) {
		if (getline(line, buf, idevh) == 1) {
			if (dd_in == '?') {
			   printf("\nNO DEFAULT: please enter input device!\n");
			   continue;
			}
		} else {
			switch (line[0]) {
			case 't':
			case 'm':
			case 'r':
			case 's':
				dd_in = line[0];
				break;
			default:
				printf("\n(%s) - not a valid input device!\n",
									line);
				continue;
			}
		}
		if (dd_in == 't') {
			if (nlr[X_TK_CTID].n_value) {
				lseek(mem, (long)nlr[X_TK_CTID].n_value, 0);
				read(mem, (char *)&tk_ctid, sizeof(tk_ctid));
				i = (tk_ctid >> 4) & 017;
				if (i != TK50 && i != TU81) {
					printf("\n(%o) - unknown type of TMSCP magtape !\n", i);
					continue;
				}
			} else {
				printf("\n(%s) - kernel not configured for TMSCP magtape !\n", line);
				continue;
			}
		}
		break;
	}
	i = D1600|D6250;
	unit = 0;
	switch (dd_in) {
	case 'm':		/* magtape */
		unit = getunit(mtnm, mtnh, 0, 7);
		i = D800|D1600;
		/*FALLTHROUGH*/
	case 't':		/* TK50 magtape */
		if (dd_in == 't' && ((tk_ctid>>4)&017) == TK50)
			sprintf(ifile, "/dev/rtk0");
		else
			getdensity(ifile, unit, i);
		break;
	case 'r':		/* RX50/RX33 */
		unit = getunit(rxnm, rxnh, 1, 3);
		sprintf(ifile, "/dev/rrx%d", unit);
		break;
	case 's':		/* swap area */
		/* read values from a.out of kernel or /dev/mem */
		if (nld[X_DMP_DN].n_value == 0) {
			errexit(0, "\ndmp_dn missing: system disk not dump device!\n");
		} else {
			lseek(fdd, (long)nld[X_DMP_DN].n_value + dataoff, 0);
			read(fdd, (char *)&unit, sizeof(unit));
		}
		if (nld[X_RL_DMPLO].n_value && nld[X_RL_DMPHI].n_value) {
			dd_maj = RL_RMAJ;
			dmplo = nld[X_RL_DMPLO].n_value;
			dmphi = nld[X_RL_DMPHI].n_value;
		} else if(nld[X_RA_DMPLO].n_value && nld[X_RA_DMPHI].n_value) {
			dd_maj = RA_RMAJ;
			dmplo = nld[X_RA_DMPLO].n_value;
			dmphi = nld[X_RA_DMPHI].n_value;
		} else if(nld[X_RP_DMPLO].n_value && nld[X_RP_DMPHI].n_value) {
			dd_maj = RP_RMAJ;
			dmplo = nld[X_RP_DMPLO].n_value;
			dmphi = nld[X_RP_DMPHI].n_value;
		} else if(nld[X_HK_DMPLO].n_value && nld[X_HK_DMPHI].n_value) {
			dd_maj = HK_RMAJ;
			dmplo = nld[X_HK_DMPLO].n_value;
			dmphi = nld[X_HK_DMPHI].n_value;
		} else if(nld[X_HP_DMPLO].n_value && nld[X_HP_DMPHI].n_value) {
			dd_maj = HP_RMAJ;
			dmplo = nld[X_HP_DMPLO].n_value;
			dmphi = nld[X_HP_DMPHI].n_value;
		} else
			errexit(0, "\nCan't find dump disk!\n");
		lseek(fdd, (long)dmplo + dataoff, 0);
		read(fdd, (char *)&dmplo, sizeof(dmplo));
		lseek(fdd, (long)dmphi + dataoff, 0);
		read(fdd, (char *)&dmphi, sizeof(dmphi));
		strcpy(ifile, dumpfile);
		unlink(ifile);
		i = (dd_maj << 8) | (unit << 3) | 7;
		if (mknod(ifile, 020400, i) < 0)
			errexit(1, "\nCan't create %s file!\n", ifile);
		break;
	}
	close(fdd);			/* all done with the crashed kernel */
	if (getline(ofile, filem, fileh) == 1)
		strcpy(ofile, corefile);
	if ((access(ofile, 0) == 0) &&
	    (getline(line, "\n\7\7\7File exists, ok to overwrite it", 0) == NO))
		errexit(0, "\nCopy aborted!\n");
	if ((fo = creat(ofile, 0644)) < 0)
		errexit(1, "\nCan't create %s\n", ofile);
	/*
	 * Get memory size for current system, and convert
	 * it to 1K bytes (from clicks).
	 */
	lseek(mem, (long)nlr[X_REALMEM].n_value, 0);
	read(mem, (char *)&nblk, sizeof(nblk));
	close(mem);			/* all done with /dev/mem */
	nblk = (nblk+017)>>4;		/* convert from clicks to K bytes */
	if (nblk == 0)
		nblk = 4096;			/* 4 meg */
	sprintf(buf, nblkm, nblk);		/* put count in help message */
	for (;;) {
		if (getline(line, buf, nblkh) == 1)
			i = nblk;
		else
			i = atoi(line);
		if (i > 0) {
			nblk = i;
			break;
		}
		printf("\nBad block count, try again!\n");
	}
	ulimit(2, (long)nblk*2L);/* set max limit in 512 byte blocks */
	switch (dd_in) {
		int *tmpdump; 
	case 's':
		if ((fi = open(ifile, 0)) < 0)
			errexit(1,"\nCan't open input file (%s) for reading!\n",
					ifile);
		/*
		 * Verify valid dump in swap area, look for 5227 (inc
		 * instruction) at address dump: and >= 0 at dump+2.
		 */
		lseek(fi, (dmplo + (long)nld[X_DUMP].n_value/512L) * 512L, 0);
		dumpbuf = (int *)(buf + (nld[X_DUMP].n_value % 512));
		tmpdump = dumpbuf; 
		read(fi, (char *)dumpbuf, (sizeof(int) * 256));

		if ((*dumpbuf != 05227) || (*++dumpbuf < 0)) 
			if(*tmpdump != 00167)  /* for Non sep-id, it will be jmp instruction */
		     		printf("\n\7\7\7Valid dump not present in swap area!\n");
		i = (dmphi - dmplo)/2;		/* get size in K bytes */
		if (nblk < (unsigned)i) 
			i = nblk;
		printf("\nCopy %d K bytes starting at disk block", i);
		printf(" %d in swap area to %s\n", dmplo, ofile);
		if ((getline(line, confm, confh) == 1) || (line[0] != 'y'))
			errexit(0, "\nCopy aborted!\n");
		lseek(fi, (long)dmplo * 512L, 0);
		i = docopy(i, 0, 5120);
		break;
	case 'r':
		k = 0;	/* floppy disk number */
		i = 0;	/* number of Kbytes copied */
		do {
			k++;
			printf("\nInsert dump diskette number");
			printf(" %d into floppy disk unit %d.", k, unit);
			prtc();
			if ((fi = open(ifile, 0)) < 0)
			    errexit(1, "\nCan't open input file (%s) for reading!\n", ifile);
			/*
			 * Determine which type diskette, rx50 or rx33?
			 * If we can read block 800, its rx33,
			 * otherwise assume rx50.
			 */
			lseek(fi, (long)(512L * 800L), 0);
			if(read(fi, buf, 512) == 512)
				dsize = 1200;
			else
				dsize = 400;
			lseek(fi, (long)0L, 0);
			j = (nblk > dsize) ? dsize : nblk;
			i = docopy(j, i, 5120);
				nblk -= j;
			if (nblk <= 0)
				fd_exit(COPY_DONE, i, 0);
			close(fi);
			printf("\n\nTotal of %d K bytes copied", i);
			printf(" from %d diskette(s).", k);
			printf("\n\nRemove dump diskette number");
			printf(" %d from floppy disk unit %d.", k, unit);
			prtc();
		} while (getline(line, "\nCopy more diskettes ", 0) == YES);
		break;
	case 'm':
	case 't':
		printf("\nMount the crash dump tape on magtape unit %d.", unit);
		prtc();
		if ((fi = open(ifile, 0)) < 0)
			errexit(1,"\nCan't open input file (%s) for reading!\n",
					ifile);
		j = read(fi, buf, 10240);	/* determine blocking factor */
		i = (j/512)*512;		/* round to 512 byte boundry */
		if (j < 0 || i != j)
			fd_exit(READ_ERROR, 0, j);
		ioctl(fi, MTIOCTOP, &mtop);	/* rewind the tape */
		i = (i+1023)/1024;		/* get Kbytes for rounding */
		nblk = (nblk + i - 1)/i;	/* round up block count */
		nblk *= i;
		i = docopy(nblk, 0, j);
		break;
	}
	fd_exit(COPY_DONE, i, 0);
	exit(0);
}

/*
 * Get density fills in "str" with the tape name.
 * unit is the tape unit, and density is a bit mask
 * of the valid tape densities.
 */
getdensity(str, unit, density)
char	*str;
int	unit;
register int	density;
{
	register int c;

	/* set up question and help message */
	sprintf(&buf[512], "press <RETURN>. The density will be%s%s%s%s,",
		density&D800  ? " 800 BPI" : "",
		density&D800  ? ((density==D800|D1600) ? " or" : ",") : "",
		density&D1600 ? " 1600 BPI" : "",
		density&D6250 ? " or 6250 BPI" : "");
	sprintf(buf, "\nCrash dump tape density <%s%s%s > ? ",
		density&D800  ? " 800" : "",
		density&D1600 ? " 1600" : "",
		density&D6250 ? " 6250" : "");
	for (;;) {
		if (getline(line, buf, mtdh) == 1)
			continue;
		if (density&D800 && strcmp("800", line) == 0)
			c = 'm';
		else if(density&D1600 && strcmp("1600", line) == 0)
			c = 'h';
		else if(density&D1600 && strcmp("6250", line) == 0)
			c = 'g';
		else {
			printf("\nBad tape density!\n");
			continue;
		}
		break;
	}
	sprintf(str, "/dev/r%ct%d", c, unit);
}

/*
 * Get unit number. Arguments are: question, help message,
 * default unit number, and maximum unit number.
 */
getunit(nm, nh, defunit, maxunit)
{
	for (;;) {
		if (getline(line, nm, nh) == 1)
			return(defunit);
		if ((line[0] < '0') || (line[0] > '0' + maxunit)) {
			printf("\nBad unit number!\n");
			continue;
		}
		return(line[0] - '0');
	}
	/*NOTREACHED*/
}

docopy(bcnt, nb, bufsize)
register int bcnt;
int nb, bufsize;
{
	register int i, cnt;
	int dblk = 0;

	/*
	 * We do the copy in terms of 512 byte blocks, because
	 * tapes get dumped with a 512 byte blocking factor.
	 * So, we convert to 512 byte blocks, but return values
	 * based on 1K byte blocks.
	 */
	bcnt *=2;
	while (bcnt > 0) {
		cnt = (bcnt > (bufsize/512)) ? bufsize : bcnt*512;
		if ((i = read(fi, buf, cnt)) != cnt) {
			if (i > 0) {	/* write out partial block */
				if ((cnt = write(fo, buf, i)) > 0)
					dblk += cnt/512;
			}
			fd_exit(READ_ERROR, nb + dblk/2, i);
		}
		if ((i = write(fo, buf, cnt)) > 0)
			dblk += i/512;
		if (i != cnt)
			fd_exit(WRITE_ERROR, nb + dblk/2, i);
		bcnt -= i/512;
	}
	return(nb + dblk/2);	/* return cum. total of K bytes copied */
}

fd_exit(how, bcnt, err)
int how, bcnt;
{
	printf("\n%s %d K bytes copied\n",
		(how == READ_ERROR) ? "Read error:" :
		(how == WRITE_ERROR) ? "Write error:" : "Copy done:", bcnt);
	if (err < 0)
		perror("ccd");
	closeup();
	if (how == COPY_DONE)
		exit(0);
	exit(1);
}

/*
 * msg  - question to be ask
 * hmsg - help message (0 = no help, must answer y or n)
 */
getline(buf, msg, hmsg)
char	*buf;
char	*msg;
char	**hmsg;
{
	register int	i, cc;
	char		line[132];

	for (;;) {
		printf("%s", msg);
		if (hmsg == 0)
			printf(" <y or n> ? ");
		fflush(stdout);
		cc = read(0, (char *)line, 132);
		if (cc <= 0)
			continue;
		if (cc > 50) {
			printf("\nToo many characters, try again!\n");
			continue;
		}
		if (hmsg && (cc == 2) && (line[0] == '?')) {
			for(i=0; hmsg[i]; i++)
				printf("\n%s", hmsg[i]);
			continue;
		}
		line[50] = '\0';
		for (i=0; i<50; i++) {
			if (((buf[i] = line[i]) >= 'A') && (buf[i] <= 'Z'))
				buf[i] |= 040;	/* force lower case */
			else if ((buf[i] == '\r') || (buf[i] == '\n'))
				buf[i] = 0;
		}
		if (hmsg == 0) {
			if (!strcmp(buf, "y") || !strcmp(buf, "yes"))
				return(YES);
			if (!strcmp(buf, "n") || !strcmp(buf, "no"))
				return(NO);
			printf("\nPlease answer yes or no!\n");
			continue;
		}
		return(cc);
	}
	/*NOTREACHED*/
}

prtc()
{
	printf("\n\nPress <RETURN> to continue: ");
	while (getchar() != '\n')
		;
}

onintr()
{
	errexit(0, "\n\n\7\7\7Interrupt: ccd aborted\n\n");
}

errexit(doperror, fmt, a1, a2)
{
	printf(fmt, a1, a2);
	if (doperror)
		perror("ccd");
	closeup();
	exit(1);
}

closeup()
{
	unlink(dumpfile);
	if (fi >= 0)
		close(fi);
	if (fo >= 0)
		close(fo);
	sync();
}