Ultrix-3.1/src/cmd/olx/mtx2.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[] = "@(#)mtx2.c	3.0	4/22/86";
/*
 * ULTRIX-11 mag tape disk exerciser program (mtx).
 *
 * PART 2 - (mtx2.c)
 *
 *	Part 2 is called from part 1 (mtx) with arguments in the
 *	file `mtx_??.arg' (?? = ts, tm, ht, tk). This is the actual
 *	magtape exerciser. The mtx program is split into two sections
 *	to optimize memory usage.
 *
 * Fred Canter 10/15/82
 * Bill Burns 4/82
 *	added event flag usage
 * Chung-Wu Lee 2/22/85
 *	added TMSCP
 * George Mathew 7/2/85
 *	changes for 1K file system
 *
 * This program exercises the tm11 - tu10/ts03, ts11/tsv05/tu80/tk25,
 * tm02/3 - tu16/te16/tu77, or tk50 - tk50/tu81 mag tape subsystems,
 * using the unix block and raw I/O interfaces at the user level.
 * Each of the four types of tape controllers requires
 * a dedicated copy of this program to exercise that
 * tape controller. There can be a maximum of four
 * copies of (mtx) running, the text segment is shared.
 * Each copy of (mtx) can exercise all drives that
 * are attached to its specified controller.
 * (mtx) can only report on errors that are detected at
 * the user level, i.e., hard errors.
 * Detailed error information must be obtained
 * from the error log (elp -ht, elp -tm, elp -ts or elp -tk).
 *
 * USAGE:
 *
 *	mtx mtx_??.arg efpis efids
 *		^      ^
 *		|      event flag bit position and id
 *		argument passing file name; ht, mt, or ts
 *
 */

#include <sys/param.h>	/* Don't matter which one ! */
#include <sys/devmaj.h>
#include <sys/tk_info.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <signal.h>

#define	RD	0
#define	WRT	1
#define	DMM	2
#define	MEOF	3
#define	MT_OFL	0100
#define	MT_WL	040
#define	MT_OPN	020

/*
 * Mag tape block and raw I/O file names.
 * mt & rmt are for 800 BPI.
 * ht & rht are for 1600 BPI.
 * gt & rgt are for 6250 BPI.
 * tk & rtk are for tk50 only.
 */

char	mtn[]	"/dev/mt";
char	rmtn[]	"/dev/rmt";
char	htn[]	"/dev/ht";
char	rhtn[]	"/dev/rht";
char	gtn[]	"/dev/gt";
char	rgtn[]	"/dev/rgt";
char	tkn[]	"/dev/tk";
char	rtkn[]	"/dev/rtk";
char	devn[12];

char	*afp;	/* Argument file pointer */

char	eplmsg[] "\n\n[error printout limit exceeded]";
char	noeof[] "\n\n[missing EOF or extra record(s) at end of file]";

/*
 * Mag tape drive type array.
 * 0 = drive is not selected or special file does not exist.
 * 1 = drive is on tm11 controller & special file exists.
 * 2 = drive is on tm02/3 controller & special file exists.
 * 3 = drive is on ts11 controller & special file exists.
 * 4 = drive is tu81 and is on TMSCP controller & special file exists.
 * 5 = drive is tk50 and is on TMSCP controller & special file exists.
 * For each of the mtx processes the drive types will
 * all be the same.
 */

char	mt_dt[64];

/*
 * Tape write, read, hard error counts.
 */

long	rdcnt[64];
long	wrtcnt[64];
long	hecnt[64];

/*
 * The following three lines of code
 * are the interface to the system call
 * error return messages.
 */

int	errno;
int	sys_nerr;
char	*sys_errlist[];

time_t	stbuf;	/* I/O statistics time */
time_t	btbuf;	/* Exerciser run beginning time */
time_t	etbuf;	/* Exerciser run ending time */
time_t	timbuf;
struct tm *tl;	/* structure for localtime */
char	btime[13];	/* ascii beg time yymmddhhmmss */
char	etime[13];	/* ascii end time yymmddhhmmss */

char	*tapen;	/* name of tape controller */

int	wrterr;
int	werec;
int	rderr;
long	randx;
int	nfeet[MAXTK];
int	pat[] =
{
	0000000,	/* lo & hi byte all 0's */
	0177777,	/* lo & hi byte all 1's */
	0000377,	/* lo byte all 1's, hi byte all 0's */
	0177400,	/* lo byte all 0's, hi byte all 1's */
	0052525,	/* lo & hi byte alt 1's & 0's */
	0125252,	/* lo & hi byte alt 0's & 1's */
	0125125,	/* lo byte alt 1's & 0's, hi byte comp. */
	0052652,	/* lo byte alt 0's & 1's, hi byte comp. */
	0177001,	/* lo byte walking 1, hi byte walking 0 */
	0176402,	/* "	*/
	0175404,	/* "	*/
	0173410,	/* "	*/
	0167420,	/* "	*/
	0157440,	/* "	*/
	0137500,	/* "	*/
	0077600,	/* "	*/
	0125220,	/* address pattern, in case of  */
	0052521,	/* write buffer overrun	*/
	0166422,
	0177423,
};

/*
 * Write and read buffer address offsets.
 */

int	w_off, r_off;
int	testno;

/*
 * Write/Read buffer,
 * large enough for 20 512-byte records.
 */

#ifdef UCB_NKB
char	buf[20*1024];
#else
char	buf[20*512];
#endif

int	sflag, tmflag, htflag, tsflag, tkflag;

#ifdef EFLG
#include <sys/eflg.h>
char	*efpis;
char	*efids;
int	efbit;
int	efid;
long	evntflg();
int	zflag;
#else
char	*killfn;
#endif

int	stopsig;

int	ndrop, ndep;

int	rbc;	/* byte count returned from read/write calls */

main(argc, argv)
char *argv[];
int argc;
{
	int	stop(), intr();
	register *wp, *rp;
	register int k;
	FILE	*argf;
	int	*wba, *rba;
	int	*wbp, *rbp;
	int 	i, j;
	int	dn, fd, md, maxdrvs;
	int	nrec, nbytes;
	int	cnt, iomode, density;
	int	fsterr, neb;
	int	dpat;
	char	*p;
	char	c;
#ifdef UCB_NKB
	int     maxrec, recsize;
#endif

/*
 * Read needed data from the `mtx_??.arg' file,
 * and start the exerciser.
 */

	signal(SIGINT, SIG_IGN);
	signal(SIGTERM, intr);
	signal(SIGQUIT, stop);
	close(stdin);
	if((argc < 2) || (argc > 4))
		exit(1);
	afp = argv[1];
	if((fd = open(afp, RD)) < 0) {
		fprintf(stderr, "\nmtx: Can't open %s\n", afp);
		exit(1);
		}
	if((rbc = read(fd, (char *)&buf, 512)) != 512) {
		fprintf(stderr, "\nmtx: %s read error\n", afp);
		exit(1);
		}
	p = &buf;
	for(i=0; i<64; i++)
		mt_dt[i] = *p++;
	rbp = p;
	sflag = *rbp++;
	tsflag = *rbp++;
	tmflag = *rbp++;
	htflag = *rbp++;
	tkflag = *rbp++;
	for(i=0; i<MAXTK; i++)
		nfeet[i] = *rbp++;
	ndep = *rbp++;
	ndrop = *rbp++;
	maxdrvs = *rbp++;
#ifdef EFLG
	zflag = *rbp++;
#endif
	close(fd);
	unlink(afp);
#ifdef EFLG
	if(zflag) {
		efpis = argv[2];
		efids = argv[3];
	}
#else
	killfn = argv[2];	/* run/stop control file name */
#endif

/*
 * Print the mtx started message.
 */
	time(&btbuf);
	randx = btbuf & 0777;
	if(tmflag)
		tapen = "TM11";
	if(tsflag)
		tapen = "TS11/TSV05/TSU05/TU80/TK25";
	if(htflag)
		tapen = "TM02/3";
	if(tkflag)
		tapen = "TK50/TU81";
	printf("\n\n%s magtape exerciser started - %s\n", tapen, ctime(&btbuf));
	fflush(stdout);
#ifdef EFLG
	if(zflag) {
		efbit = atoi(efpis);
		efid = atoi(efids);
		evntflg(EFCLR, efid, (long)efbit);
	}
#else
	unlink(killfn);
#endif
/*
 * TEST 1 - SHORT FILE TEST
 *
 * The following test is done on each of the selected tape
 * units that is available. 
 * The test is done in block and raw I/O modes for 800, 1600
 * 6250 BPI density or just tk50
 * The test consists of writing a number of 512 byte records
 * to tape from the write buffer.
 * Reading the same number of records into the read buffer
 * and comparing the data in the write and read buffers.
 * A pass is 32 of the above write/read/compare tests.
 * The number of records starts at one and increases to 16,
 * and then decreases from 16 back down to one.
 * The test insures that the correct number of tape records
 * were written by attempting to read one extra record
 * and checking for an end of file error.
 * The write address is rotated from the end of the
 * write buffer towards the beginning write buffer.
 * The read address is rotated, within a very narrow range
 * ( 32 words), from the start towards the end of
 * the read buffer.
 * The word before the start of the read data in the read
 * buffer and the word after the read data are tested
 * to insure that the data was transferred only to the
 * correct addresses in the read buffer.
 * The data patterns in pat[0] thru pat[19] are loaded
 * into the write buffer at the start of the test and
 * are constant throughout the test 1.
 */
loop:
	wba = &buf;
	rba = wba + (18*256);
	wp = wba;
	*wp++ = pat[0];		/* load first word of write buffer */
	for(j=0; j<17; j++)	/* fill write buffer with data patterns */
		for(k=0; k<256; k++)
			*wp++ = pat[j];
	testno = 1;
	for(cnt=0; cnt<256; cnt++)
	{			/* BIG loop, tests all units in all modes */

	dn = cnt & 077;		/* unit # to be tested */
	iomode = cnt & 0100;	/* block or raw I/O mode */
/*
 * density 0 -  800 BPI (tm11, tm02/3), 1600 BPI (tu81) or tk50
 *         1 - 1600 BPI (ts11, tm02/3), 6250 BPI (tu81) but no tk50
 */
	density = cnt & 0200;	/* 800 ro 1600 BPI density */
	if(!mt_dt[dn])
		continue;	/* unit not selected */
	if(!density && ((mt_dt[dn] & 07) == 3))
		continue;	/* no 800 BPI on ts11 */
	if(density && ((mt_dt[dn] & 07) == 1 || (mt_dt[dn] & 07) == 5))
		continue;	/* no 1600 BPI if unit is tm11 */
	mtname(dn, density, iomode);	/* generate magtape filename */
#ifndef UCB_NKB
	for(i=0; i<32; i++) {	/* one test pass */
	fd = mtopen(WRT);
	if(i < 16)	/* set up record count & write offset */
		nrec = i + 1;
	else
		nrec = 32 - i;
	w_off = (16 - nrec)*256+1;
	wbp = wba + w_off;
	for(j=0; j<nrec; j++) {
		wrterr = 0;
		wrtcnt[dn]++;
		wp = wbp + (j*256);
		/* intr();	OLD: poll for stop */
		if((rbc = write(fd, (char *)wp, 512)) != 512) {
			trwse(WRT,dn,density,iomode,512,nrec,j+1);
/*
 * WRITE errors are fatal, the "tuready"
 * function waits a minimum of 15 minutes
 * for the tape unit to be made ready.
 */
			fd = tuready(fd);
			break;
			}
		}
		close(fd);
		if(wrterr) {	/* write error, so read must be adjusted */
			printf("\n\n******\n");
			if(werec == 0) {
				printf("READ ABORTED - no records written");
				printf("\n******\n");
				continue;
			} else {
				nrec = werec;
			printf("READ SHORTENED - %d records will be read",nrec);
				}
			printf("\n******\n");
			}
		fd = mtopen(RD);
		for(j=0; j<nrec; j++) {
/*
 * The read buffer offset is random
 * and in the range of 1 - 4096.
 */
			rderr = 0;
			r_off = i + j;
			rbp = rba + r_off;
			for(k= -1; k<257; k++)
				*(rbp + k) = ~*(wbp + (j*256) + k);
			rdcnt[dn]++;
			/* intr(); */
			if((rbc = read(fd, (char *)rbp, 512)) != 512)
				trwse(RD,dn,density,iomode,512,nrec,j+1);
			fsterr = 1;
			neb = 0;
/*
 * Buffer over/under flow test.
 * Check the words before and after the buffer
 * to insure that data was read into the
 * assigned buffer area and no where else.
 */
	if((*(rbp - 1) != ~*(wbp + (j*256) - 1)) ||
	(*(rbp + 256) != ~*(wbp + (j*256) + 256))) {
		if(!rderr)
			trwse(DMM,dn,density,iomode,512,nrec,j+1);
		rderr++;
		printf("\n\nREAD BUFFER OVERRUN ERROR");
		printf("\n\nWord before read buffer");
		pgb(-1, *(wbp + (j*256) - 1), *(rbp - 1));
		printf("\n\nWord after read buffer");
		pgb(-1, *(wbp + (j*256) + 256), *(rbp + 256));
		}
			for(k=0; k<256; k++) {
				if(*(rbp + k) != *(wbp + (j*256) + k)) {
					if(fsterr && !rderr)
						trwse(DMM,dn,density,iomode,512,nrec,j+1);
					if(fsterr)
		printf("\n\nDATA COMPARE ERROR - RECORD %u",j+1);
					fsterr = 0;
					if(++neb > ndep) {
						printf("%s",&eplmsg);
						break;
						}
			pgb(k, *(wbp + (j*256) + k), *(rbp + k));
					}
				}
				if(!fsterr)
					printf("\n******\n");
				if(rderr && (errno == ETPL)) {
					printf("\n\n[READ ABORTED - ");
					printf("fatal read error]\n******\n");
					fd = tuready(fd); /* wait for tape ready */
					goto fte1;
					}
				if(rderr && fsterr)
					printf("\n******\n");
			}
#else UCB_NKB
	if (iomode) {
		maxrec = 16;
		recsize = 512;
	} else {
		maxrec = 8;
		recsize = 1024;
	}
	for(i=0; i<(maxrec*2); i++) {	/* one test pass */
	fd = mtopen(WRT);
	if(i < maxrec)	/* set up record count & write offset */
		nrec = i + 1;
	else
		nrec = maxrec*2  - i;
	w_off = (maxrec - nrec)*(recsize/2)+1;
	wbp = wba + w_off;
	for(j=0; j<nrec; j++) {
		wrterr = 0;
		wrtcnt[dn]++;
		wp = wbp + (j*(recsize/2));
		/* intr();	OLD: poll for stop */
		/*printf("before write: stopsig= %d\n",stopsig); */
		if((rbc = write(fd, (char *)wp, recsize)) != recsize) {
			trwse(WRT,dn,density,iomode,recsize,nrec,j+1);
/*
 * WRITE errors are fatal, the "tuready"
 * function waits a minimum of 15 minutes
 * for the tape unit to be made ready.
 */
			fd = tuready(fd);
			break;
			} /* else */
			/*printf("\n*** write successful, record no=%d, nrec=%d\n",j+1,nrec); */
		}
		close(fd);
		if(wrterr) {	/* write error, so read must be adjusted */
			printf("\n\n******\n");
			if(werec == 0) {
				printf("READ ABORTED - no records written");
				printf("\n******\n");
				continue;
			} else {
				nrec = werec;
			printf("READ SHORTENED - %d records will be read",nrec);
				}
			printf("\n******\n");
			}
		fd = mtopen(RD);
		for(j=0; j<nrec; j++) {
/*
 * The read buffer offset is random
 * and in the range of 1 - 4096.
 */
			rderr = 0;
			r_off = i + j;
			/* printf("r_off=%d\t",r_off); */
			rbp = rba + r_off;
			/* printf("read buffer ptr: %d\n",rbp);*/
			/* printf("before loop: stopsig= %d\t",stopsig); */
			for(k= -1; k<(recsize/2 +1) ; k++)
				*(rbp + k) = ~*(wbp + (j*(recsize/2)) + k);
			/* printf("after loop: stopsig= %d\n",stopsig); */
			rdcnt[dn]++;
			/* intr(); */
			if((rbc = read(fd, (char *)rbp, recsize)) != recsize) {
				trwse(RD,dn,density,iomode,recsize,nrec,j+1);
				/* printf("\n** no. of chars read=%d\n",rbc); */
			} else {
				/* printf("\n** read successful, record no=%d, nrec=%d",j+1,nrec); */
			}
			/* printf("after read: stopsig= %d\n", stopsig); */
			fsterr = 1;
			neb = 0;
/*
 * Buffer over/under flow test.
 * Check the words before and after the buffer
 * to insure that data was read into the
 * assigned buffer area and no where else.
 */
	if((*(rbp - 1) != ~*(wbp + (j*(recsize/2)) - 1)) ||
	(*(rbp + (recsize/2)) != ~*(wbp + (j*(recsize/2)) + (recsize/2)))) {
		if(!rderr)
		/* printf("\ncalling trwse after read, stopsig=%d\n",stopsig); */
			trwse(DMM,dn,density,iomode,recsize,nrec,j+1);
		rderr++;
		printf("\n\nREAD BUFFER OVERRUN ERROR");
		printf("\n\nWord before read buffer");
		pgb(-1, *(wbp + (j*(recsize/2)) - 1), *(rbp - 1));
		printf("\n\nWord after read buffer");
		pgb(-1, *(wbp + (j*(recsize/2)) + (recsize/2)), *(rbp + (recsize/2)));
		}
			for(k=0; k<(recsize/2); k++) {
				if(*(rbp + k) != *(wbp + (j*(recsize/2)) + k)) {
					if(fsterr && !rderr)
						trwse(DMM,dn,density,iomode,recsize,nrec,j+1);
					if(fsterr)
		printf("\n\nDATA COMPARE ERROR - RECORD %u",j+1);
					fsterr = 0;
					if(++neb > ndep) {
						printf("%s",&eplmsg);
						break;
						}
			pgb(k, *(wbp + (j*(recsize/2)) + k), *(rbp + k));
					}
				}
				if(!fsterr)
					printf("\n******\n");
				if(rderr && (errno == ETPL)) {
					printf("\n\n[READ ABORTED - ");
					printf("fatal read error]\n******\n");
					fd = tuready(fd); /* wait for tape ready */
					goto fte1;
					}
				if(rderr && fsterr)
					printf("\n******\n");
			}
#endif
/*
 * Check for end of file (EOF).
 * Attempt to read one more record, if an error is
 * returned then EOF is present. If read is successful
 * then the EOF is missing or an extra record was written.
 */
		if(iomode)	/* only RAW mode for now */
		{
		/* intr(); */
		if((rbc = read(fd, (char *)rba, 512)) != 0) {
			trwse(MEOF,dn,density,iomode,512,nrec,(nrec+1));
			printf("%s", &noeof);
			if(wrterr) {
				printf("\n[Most likely due to previous");
				printf(" fatal write error]\n******\n");
				}
			}
		}
	fte1:
		fflush(stdout);
		close(fd);
		}
/*
 * The following is necessary because the tm03 tape controller
 * will not allow the density to be changed unless the
 * tape is at load point.
 * This causes the first write after a density change to fail.
 * To avoid this problem, the tape is opened at the previous
 * density, the first record is read, the tape is closed, and
 * then the process sleeps for 2 seconds ( or so ) while the
 * tape drive is rewound to load point. 
 */
		if(htflag && ((!density && iomode) || (density && iomode))) {
			fd = mtopen(RD);
			read(fd, (char *)rba, 512);
			close(fd);
			sleep(1);
			sleep(1);
			}
	}

/*
 * TEST 2 - VARIABLE LENGTH RECORD TEST
 *
 * This test writes one record with a length that varies
 * from 512 bytes to 10240 bytes in 512 byte increments.
 * The record is read and the data comapared with
 * the test pattern written.
 * The write/read address is the start of the buffer.
 * The data patterns are the complement of those
 * in test 1, pat[0] thru pat[19].
 * The test is done for all selected and available drives,
 * in raw I/O mode, at 800 BPI and 1600 BPI
 * (if rm02/3) densities.
 */

	wba = &buf;
	rba = &buf;
	w_off = 0;
	r_off = 0;
	iomode = 1;	/* RAW I/O mode only ! */
	testno = 2;
	for(cnt=0; cnt<128; cnt++)
	{			/* BIG loop, tests all units */

	dn = cnt & 077;		/* unit # to be tested */
/*
 * density 0 -  800 BPI (tm11, tm02/3), 1600 BPI (tu81) or tk50
 *         1 - 1600 BPI (ts11, tm02/3), 6250 BPI (tu81) but no tk50
 */
	density = cnt & 0100;	/* 800 ro 1600 BPI density */
	if(!mt_dt[dn])
		continue;	/* unit not selected */
	if(!density && ((mt_dt[dn] & 07) == 3))
		continue;	/* no 800 BPI on ts11 */
	if(density && ((mt_dt[dn] & 07) == 1 || (mt_dt[dn] & 07) == 5))
		continue;	/* no 1600 BPI if unit is tm11 */
	mtname(dn, density, iomode);	/* generate magtape filename */
	for(i=0; i<20; i++) {	/* one test pass */
	fd = mtopen(WRT);
	nbytes = 512 + (i*512);
	wp = wba;			/* load write buffer with pattern */
	for(j=0; j<(i+1); j++)
		for(k=0; k<256; k++)
			*wp++ = ~pat[j];
	nrec = 1;
	wrterr = 0;
	wrtcnt[dn]++;
	/* intr(); */
	if((rbc = write(fd, (char *)wba, nbytes)) != nbytes) {
		trwse(WRT,dn,density,iomode,nbytes,nrec,1);
		fd = tuready(fd);	/* wait for tape unit ready */
		}
	close(fd);
	if(wrterr) {	/* fatal write error, no read */
		printf("\n\n******\nREAD ABORTED - no records");
		printf(" written\n******\n");
		continue;
		}
	fd = mtopen(RD);
	rderr = 0;
	rp = rba;		/* load read buffer with complement pattern */
	for(j=0; j<(i+1); j++)
		for(k=0; k<256; k++)
			*rp++ = pat[j];
	rdcnt[dn]++;
	/* intr(); */
	if((rbc = read(fd, (char *)rba, nbytes)) != nbytes)
		trwse(RD,dn,density,iomode,nbytes,nrec,1);
	fsterr = 1;
	neb = 0;
	rp = rba;
	for(k=0; k<(nbytes/2); k++) {
		if(*rp++ != ~pat[k/256]) {
			if(fsterr && !rderr)
				trwse(DMM,dn,density,iomode,nbytes,nrec,1);
			if(fsterr)
		printf("\n\nDATA COMPARE ERROR - RECORD %u",1);
			fsterr = 0;
			if(++neb > ndep) {
				printf("%s",&eplmsg);
				break;
				}
			pgb(k, ~pat[k/256], *(rba + k));
			}
		}
		if(!fsterr)
			printf("\n******\n");
		if(rderr && (errno == ETPL)) {
			printf("\n\n[READ ABORTED - ");
			printf("fatal read error]\n******\n");
			fd = tuready(fd);
			goto fte2;
			}
		if(rderr && fsterr)
			printf("\n******\n");
/*
 * Check for end of file (EOF).
 * Attempt to read one more record, if an error is
 * returned then EOF is present. If read is successful
 * then the EOF is missing or an extra record was written.
 */
		/* intr(); */
		if((rbc = read(fd, (char *)rba, 512)) != 0) {
			trwse(MEOF,dn,density,iomode,512,nrec,(nrec+1));
			printf("%s", &noeof);
			}
fte2:
	fflush(stdout);
	close(fd);
	}
/*
 * The following is necessary because the tm03 tape controller
 * will not allow the density to be changed unless the
 * tape is at load point.
 * This causes the first write after a density change to fail.
 * To avoid this problem, the tape is opened at the previous
 * density, the first record is read, the tape is closed, and
 * then the process sleeps for 2 seconds ( or so ) while the
 * tape drive is rewound to load point. 
 */
	if(htflag) {
		fd = mtopen(RD);
		read(fd, (char *)rba, 10240);
		close(fd);
		sleep(1);
		sleep(1);
		}
	}

/*
 * TEST 3 - LARGE FILE TEST
 *
 * This test simulates very large files, such as dump/restore
 * tapes. It writes enough records to fill the
 * number of feet of tape specified by "nfeet".
 * The value of "nfeet" can be specifed by the [-f#] option,
 * the default value is 500 feet. The minimum value is 10 feet and
 * the maximum is 2400 feet.
 * The test insures that the correct number of tape records
 * were written by attempting to read an extra record
 * and checking for an end of file error.
 * The test is done in block and raw I/O modes at 800
 * and 1600 BPI density.
 * The record size is 1024 bytes for block mode and
 * 10240 bytes for raw mode.
 * For the 1024 bytes records the write/read addresses are
 * rotated thru the buffer on block boundries.
 * For the 10240 byte records the write/read address
 * is the start of the buffer.
 * Test data patterns (pat[0] -> pat[19]) are used
 * for this test.
 */

	testno = 3;
	wba = &buf;
	rba = &buf;
	for(cnt=0; cnt<256; cnt++)
	{			/* BIG loop, tests all units in all modes */

	dn = cnt & 077;		/* unit # to be tested */
	iomode = cnt & 0100;	/* block or raw I/O mode */
/*
 * density 0 -  800 BPI (tm11, tm02/3), 1600 BPI (tu81) or tk50
 *         1 - 1600 BPI (ts11, tm02/3), 6250 BPI (tu81) but no tk50
 */
	density = cnt & 0200;	/* 800 ro 1600 BPI density */
	if(!mt_dt[dn])
		continue;	/* unit not selected */
	if(!density && ((mt_dt[dn] & 07) == 3))
		continue;	/* no 800 BPI on ts11 */
	if(density && ((mt_dt[dn] & 07) == 1 || (mt_dt[dn] & 07) == 5))
		continue;	/* no 1600 BPI if unit is tm11 */
/*
 * Use the density and iomode to construct the name of the
 * mag tape special file to be opened, the number of records
 * to write , and the record size as follows:
 *
 *	density	mode	size	# rec's per 10 feet
 *	800	block	512	55
 *	800	raw	10240	8
 *	1600	block	512	70
 *	1600	raw	10240	15
 *	1600	block	1024	70  - tu81
 *	1600	raw	10240	15  - tu81
 *	6250	block	1024	160
 *	6250	raw	10240	35
 *	tk50	block	512	95
 *	tk50	raw	10240	15
 */
	if(!density && !iomode) {		/* low density & block mode */
		if (mt_dt[dn] == 5) {		/* tk50 */
			nbytes = 1024;
			nrec = nfeet[dn];
			}
		else if (mt_dt[dn] == 4) {	/* 1600 BPI for tu81 */
			nbytes = 1024;
			nrec = (nfeet[dn]/10)*70;
			}
		else {				/* 800 BPI for the rest */
#ifdef UCB_NKB
			nbytes = 1024;
			nrec = (nfeet[0]/10)*55;
#else
			nbytes = 512;
			nrec = (nfeet[0]/10)*76;
#endif
			}
		}
	if(!density && iomode) {		/* low density & raw mode */
		if (mt_dt[dn] == 5) {		/* tk50 */
			nbytes = 10240;
			nrec = nfeet[dn];
			}
		else if (mt_dt[dn] == 4) {	/* 1600 BPI for tu81 */
			nbytes = 10240;
			nrec = (nfeet[dn]/10)*15;
			}
		else {				/* 800 BPI for the rest */
			nbytes = 10240;
			nrec = (nfeet[0]/10)*8;
			}
		}
	if(density && !iomode) {		/* high density & block mode */
		if (mt_dt[dn] == 4) {		/* 6250 BPI for tu81 */
			nbytes = 1024;
			nrec = (nfeet[dn]/10)*160;
			}
		else {				/* 1600 BPI for the resr */
#ifdef UCB_NKB
			nbytes = 1024;
			nrec = (nfeet[0]/10)*70;
#else
			nbytes = 512;
			nrec = (nfeet[0]/10)*95;
#endif
			}
		}
	if(density && iomode) {			/* high density & raw mode */
		if (mt_dt[dn] == 4) {		/* 6250 BPI for tu81 */
			nbytes = 10240;
			nrec = (nfeet[dn]/10)*35;
			}
		else {				/* 1600 BPI for the rest */
			nbytes = 10240;
			nrec = (nfeet[0]/10)*15;
			}
		}
	mtname(dn, density, iomode);	/* generate magtape filename */
	fd = mtopen(WRT);
	wp = wba;		/* load write buffer with test pattern */
	for(j=0; j<5120; j++)
		*wp++ = pat[j/256];
	for(j=0; j<nrec; j++) {
#ifdef UCB_NKB
		if((nbytes == 512) || (nbytes == 1024))
#else
		if(nbytes == 512)
#endif
			w_off = (j&017)*256;
		else
			w_off = 0;
		wrterr = 0;
		wrtcnt[dn]++;
		wbp = wba + w_off;
		/* intr(); */
		if((rbc = write(fd, (char *)wbp, nbytes)) != nbytes) {
			trwse(WRT,dn,density,iomode,nbytes,nrec,j+1);
			fd = tuready(fd);	/* wait for tape unit ready */
			break;
			}
		}
		close(fd);
		if(wrterr) {	/* write error, so read must be adjusted */
			printf("\n\n******\n");
			if(werec == 0) {
				printf("READ ABORTED - no records written");
				printf("\n******\n");
				continue;
			} else {
				nrec = werec;
				printf("READ SHORTENED - ");
				printf("%d records will be read",nrec);
				}
			printf("\n******\n");
			}
		fd = mtopen(RD);
		for(j=0; j<nrec; j++) {
#ifdef UCB_NKB
			if((nbytes == 512) || (nbytes == 1024))
#else
			if(nbytes == 512)
#endif
				r_off = (j&017)*256;
			else
				r_off = 0;
			w_off = r_off;
			rderr = 0;
			rp = rba + r_off;
			for(k=0; k<(nbytes/2); k++)
				*rp++ = 0;
			rdcnt[dn]++;
			rbp = rba + r_off;
			/* intr(); */
			if((rbc = read(fd, (char *)rbp, nbytes)) != nbytes)
				trwse(RD,dn,density,iomode,nbytes,nrec,j+1);
			fsterr = 1;
			neb = 0;
			rp = rba + r_off;
			for(k=0; k<(nbytes/2); k++) {
				dpat = (r_off/256) + (k/256);
				if(*rp++ != pat[dpat]) {
					if(fsterr && !rderr)
					trwse(DMM,dn,density,iomode,nbytes,nrec,j+1);
					if(fsterr)
		printf("\n\nDATA COMPARE ERROR - RECORD %u",j+1);
					fsterr = 0;
					if(++neb > ndep) {
						printf("%s",&eplmsg);
						break;
						}
			pgb(k, pat[dpat], *(rbp + k));
					}
				}
				if(!fsterr)
					printf("\n******\n");
				if(rderr && (errno == ETPL)) {
					printf("\n[READ ABORTED - ");
					printf("fatal read error]\n******\n");
					fd = tuready(fd);
					goto fte3;
					}
				if(rderr && fsterr)
					printf("\n******\n");
			}
/*
 * Check for end of file (EOF).
 * Attempt to read one more record, if an error is
 * returned the EOF is present. If read is successful
 * then EOF is missing or an extra record was written.
 */
		if(iomode)	/* only in RAW mode for now ! */
		{
		/* intr(); */
		if((rbc = read(fd, (char *)rba, nbytes)) != 0) {
			trwse(MEOF,dn,density,iomode,nbytes,nrec,(nrec+1));
			printf("%s", &noeof);
			if(wrterr) {
				printf("\n[Most likely due to previous");
				printf(" fatal write error]\n******\n");
				}
			}
		}
	fte3:
		fflush(stdout);
		close(fd);
/*
 * The following is necessary because the tm03 tape controller
 * will not allow the density to be changed unless the
 * tape is at load point.
 * This causes the first write after a density change to fail.
 * To avoid this problem, the tape is opened at the previous
 * density, the first record is read, the tape is closed, and
 * then the process sleeps for 2 seconds ( or so ) while the
 * tape drive is rewound to load point. 
 */
		if(htflag && ((!density && iomode) || (density && iomode))) {
			fd = mtopen(RD);
			read(fd, (char *)rba, 10240);
			close(fd);
			sleep(1);
			sleep(1);
			}
		}
	time(&timbuf);
	printf("\n%s exerciser end of pass - %s", tapen, ctime(&timbuf));
	if(!sflag)
		pios();	/* print I/O stats */
	fflush(stdout);
	if((i = fork()) == 0)
		goto loop;
	if(i == -1) {
		fprintf(stderr, "\nmtx: Can't fork new copy of mtx !\n");
		goto loop;
	}
	exit(0);
}

/*
 * This function is called when a exerciser run is
 * terminated via the delete key. This is done by
 * catching the interrupt signal.
 * The final I/O statistics are printed and the
 * the error log printout program is called via execl.
 * `elp' will print only the errors for the selected
 * tape controller which occurred durring the exerciser run.
 */

intr()
{
	signal(SIGTERM, intr);
#ifdef EFLG
	if(zflag) {
		if(!checkflg())
			return;
	} else
		return;
#else
	if(access(killfn, 0) != 0)
		return;
#endif
	stop();
}

stop()
{
	register char *tn;

	stopsig++;
	signal(SIGTERM, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	time(&etbuf);	/* ending time */
	printf("\n\n%s magtape exerciser stopped - %s\n", tapen, ctime(&etbuf));
	pios();		/* print I/O stats */
	tconv(&btbuf, &btime);	/* convert beg time to ascii */
	tconv(&etbuf, &etime);	/* convert end time to ascii */
	fflush(stdout);
	if(tsflag)
		tn = "-ts";
	else if(tmflag)
		tn = "-tm";
	else if(htflag)
		tn = "-ht";
	else
		tn = "-tk";
	if(fork() == 0)
	  	execl("/bin/elp","elp","-s",tn,"-d",&btime,&etime,(char *)0);
	else
		while(wait() != -1) ;
#ifdef EFLG
	if(zflag)
		evntflg(EFCLR, efid, (long)efbit);
#else
	unlink(killfn);
#endif
	exit(0);
}

/*
 * Tape read/write status error printout function.
 * rw = type of function being performed.
 *  0 = read, 1 = write, 2 = data mismatch, 3 = missing EOF
 * un = tape unit number
 * den = 0 for 800 BPI, den = !0 for 1600 BPI
 * iom = 0 for block I/O mode , iom = !0 for raw I/O mode
 * rl = record length in bytes
 * nr = number of records
 * rn = record number
 */

trwse(rw, un, den, iom, rl, nr, rn)
{
	char tp;
	register int i, j;

	if(stopsig)
		stop();	/* in case of signal durring system call */
	tp = mt_dt[un];
	hecnt[un]++;
	time(&timbuf);
	printf("\n\n******\n");
	if(rw == DMM)
		printf("TEST %d - DATA MISMATCH WITHOUT READ", testno);
	else {
		printf("TEST %d - HARD TAPE ", testno);
		if(rw == WRT)
			printf("WRITE");
		else
			printf("READ");
		}
	printf(" ERROR - %s",ctime(&timbuf));
	if(rw < DMM) {
		printf("Returned byte = %d (-1 = error)", rbc);
		printf("\nError type: ");
		if(errno < sys_nerr)
			printf("%s\n", sys_errlist[errno]);
		else
			printf("Unknown error\n");
		}
	printf("\nunit\tdensity\tI/O\trecord\t# of\trecord");
	printf("\nnumber\tBPI\tmode\tnumber\trecords\tlength\n");
	printf("\n%d\t",un);
	if(den) {
		if (tp == 2 || tp == 3)
			printf("1600\t");
		else
			printf("6250\t");
		}
	else {
		if (tp == 1 || tp == 2)
			printf("800\t");
		else if (tp == 4)
			printf("1600\t");
		else
			printf("tk50\t");
		}
	if(iom)
		printf("raw\t");
	else
		printf("block\t");
	printf("%d\t%d\t%d bytes",rn,nr,rl);
	if(rw != MEOF)
		printf("\n\nWrite buffer address = %o", w_off);
	if(rw == RD || rw == DMM)
		printf("\nRead  buffer address = %o", r_off);
	if(rw == WRT) {
		printf("\n\n[FATAL ERROR - write termintated");
		printf(" at record %d of %d]", rn, nr);
		wrterr++;
		werec = rn - 1;
		printf("\n******\n");
		}
	if(rw == RD)
		rderr++;
	if(hecnt[un] >= ndrop) {
	    printf("\n\nTotal error limit exceeded, unit %d dropped !\n", un);
		mt_dt[un] = 0;
	}
	fflush(stdout);
	for(i=0, j=0; i<64; i++)
		j += mt_dt[i];
	if(j == 0) {
		for( ;; )
			sleep(3600);
	}
}

/*
 * Print the tape read/write/hard error
 * statistics.
 */

pios()
{
	register j;

	time(&stbuf);
	randx = stbuf & 0777;
	printf("\n\nI/O statistics - %s", ctime(&stbuf));
	printf("\ndrive   write       read        hard");
	printf("\nnumber  operations  operations  errors\n");
	for(j=0; j<64; j++)
		if(mt_dt[j]) {
			printf("\n%6.d  %10.D", j, wrtcnt[j]);
			printf("  %10.D  %6.D", rdcnt[j], hecnt[j]);
			}
	printf("\n");
	fflush(stdout);
}

/*
 * Wait for tape unit ready.
 * The tape is closed, then approximately
 * once per second an atttempt is made to
 * open the tape again. If after 15 minutes
 * the tape can't be opened, a fatal error
 * message is printed and the tape exerciser
 * exits. If the tape can be opened then
 * the file descriptor is returned.
 *
 * The 15 minute wait is necessary to allow
 * for manual intervention to make the tape
 * unit ready after such things as opening the
 * door on a TS11 or taking the drive off-line.
 */

tuready(fd)
{
	register int i;

	close(fd);
	for(i=0; i<900; i++) {
		if((fd = open(&devn, RD)) >= 0)
			return(fd);
		sleep(1);
		}
	fprintf(stderr,"\n\nmtx: FATAL TAPE ERROR - can't open %s", &devn);
	printf(" after 15 minutes\n\n");
	exit(1);
}

/*
 * The "mtopen" function opens the magtape for
 * actual read/write operations.
 * If the open fails, mtopen prints the reason
 * for the failure and the calls "tuready"
 * which retries the open once per second for 15
 * minutes, and prints a fatal error message if
 * the open can't be completed by that time.
 */

mtopen(rw)
{

	int fd;

	while((fd = open(&devn, rw)) < 0) {
		fprintf(stderr,"\n\nmtx: can't open %s ", &devn);
		if(errno == ETOL)
			fprintf(stderr,"[off-line]");
		else if(errno == ETWL)
			fprintf(stderr,"[write locked]");
		else if(errno == ETO)
			fprintf(stderr,"[already open]");
		else
			fprintf(stderr,"[unknown error]");
   		fprintf(stderr,"\n\nmtx: [will retry for at least 15 minutes, then quit !]");
		fd = tuready(fd);
		close(fd);
	}
	return(fd);
}

/*
 * This function converts the time from a time_t
 * ,as returned by time(), to ascii in the form of
 * yymmddhhmmss.
 */

tconv(tim, timbuf)
time_t	*tim;
char	*timbuf;
{
	register int i;
	register char *p;
	int tb[6];

	tl = localtime(tim);
	tb[0] = tl->tm_year;
	tb[1] = tl->tm_mon + 1;
	tb[2] = tl->tm_mday;
	tb[3] = tl->tm_hour;
	tb[4] = tl->tm_min;
	tb[5] = tl->tm_sec;
	p = timbuf;
	for(i=0; i<6; i++) {
		*p++ = (tb[i]/10) + '0';
		*p++ = (tb[i]%10) + '0';
		}
	*p++ = 0;
}

/*
 * Generate the special filename for the
 * selected magtape unit, depending upon
 * the density and iomode arguments.
 */

mtname(dn, den, mode)
{
	char tp;

	tp = mt_dt[dn];
	if(!den && !mode) {
		if (tp == 1 || tp == 2)
			sprintf(&devn, "%s%d", mtn, dn);
		else if (tp == 4)
			sprintf(&devn, "%s%d", htn, dn);
		else
			sprintf(&devn, "%s%d", tkn, dn);
		}
	if(!den && mode) {
		if (tp == 1 || tp == 2)
			sprintf(&devn, "%s%d", rmtn, dn);
		else if (tp == 4)
			sprintf(&devn, "%s%d", rhtn, dn);
		else
			sprintf(&devn, "%s%d", rtkn, dn);
		}
	if(den && !mode) {
		if (tp == 2 || tp == 3)
			sprintf(&devn, "%s%d", htn, dn);
		else
			sprintf(&devn, "%s%d", gtn, dn);
		}
	if(den && mode) {
		if (tp == 2 || tp == 3)
			sprintf(&devn, "%s%d", rhtn, dn);
		else
			sprintf(&devn, "%s%d", rgtn, dn);
		}
}

/*
 * Print the address of the word in error
 * ( if word not equal to -1)
 * followed by the GOOD/BAD data.
 */

pgb(word, good, bad)
{
	if(word != -1)
		printf("\n\nWORD = %6.d", word);
	printf("\nGOOD = %06.o", good);
	printf("\nBAD  = %06.o", bad);
}

/*
 * Check eventflags to stop
 * return 0 for continuation
 * return 1 to stop
 */
extern int errno;
checkflg()
{
	union efrt {
		long	efret;
		struct {
			int	a;
			int	b;
		} retval
	} ef;
	errno = 0;
	ef.efret = evntflg(EFRD, efid, (long)0);
	if(errno && ef.retval.a == -1) {
		zflag = 0;
		return(0);
	}
	if(ef.efret & (1L << efbit))
		return(1);
	return(0);
}