[net.sources] Tape Testing Program: Here it is!

john@x.UUCP (John Woods) (05/08/85)

Due to a surprising number of requests for the CCC tape tester program, I
am submitting it for your amusement.  It worked well for a number of years
at CCC, testing scavenged tapes on scavenged hardware...

-------------chop--slice--grate--and--cut--here----------------------------
echo extract with sh, not csh
echo x README
cat > README << '!Funky!Stuff!'
This is the tape testing program from the Concourse Computer Center at the
Massachussetts Institute of Technology.  I believe it was originated by
Mark Plotnick, but most of the CCC crowd has had a hand in doing things to
it since then.

Arguments to it are:
	-v	Verbose, tells what's going on (quite chatty)
	-n	Nice, renices itself and does a lot of sleep(2)ing.  This
		flag may be repeated for increased effect.
	-r	Set the record size of testing.
	-o	Set the initial offset from beginning of tape (which is
		achieved by doing a write that large).
	-f	Change the default drive from /dev/rmt0.

tptest writes a pattern out to the tape in uniform blocks until an error
occurs (which is assumed to be End of Tape).  It then rewinds and tries to
verify the pattern.  Then, a second pass writes a short block at the beginning
of the tape and then writes the pattern to end of tape; this verifies (or
tries to verify) that the tape in the inter-record-gaps of the first pass
is OK.

When it is all done, if the tape was OK, it gets a sequence number from
/usr/lib/tapeseq and tells it to you (so you can uniquely label tapes).  We
generally ignored this after the first few dozen, but it seemed a keen idea
at first.

This program ran under V7 and 2.8BSD, on our IBM 729 tape drive (7 track,
200/556/800 bpi, well over a half-ton weight!) and on a slightly more modern
TU-10.  The tapeopen() routine assumes that if it can't open the tape drive,
it must be rewinding.  Those with different drivers may need to change that.

		John Woods,
		ihnp4!mit-eddie!jfw, decvax!frog!john
!Funky!Stuff!
echo x makefile
cat > makefile << '!Funky!Stuff!'
CFLAGS = -O

tptest: tptest.o
	cc -o tptest tptest.o

install: tptest
	cp tptest /usr/lbin

clean:
	rm *.o tptest
!Funky!Stuff!
echo x tptest.c
cat > tptest.c << '!Funky!Stuff!'
#include <stdio.h>

#define	DFLTRECSIZE	5120
#define DFLTOFFSET	2048
#define MAXRECSIZE (5120*4)
#define SLEEPEVERY 10

char	buf[MAXRECSIZE];
char	pattern[MAXRECSIZE];
int	Recsize = DFLTRECSIZE;
struct flags {
	int slow;
	int verbose;
} Flags;
int Tapefd, Seed;
char *drivename = "/dev/rmt0";	/* default drive name */


genpat(seed)
{
	register int i = Recsize;
	register char *patp = pattern;
	register char *p = buf;

	seed += Seed;
	while(--i) {
		*patp++ = seed++ /* & 0377  */  ;
		*p++    = 0;
	}
	patp = pattern;
	strncpy(patp,"tptest",6);
}

check()
{
	register int i = Recsize;
	register char *patp = pattern;
	register char *p = buf;

	while(--i)
		if (*patp++ != *p++)
			return (-1);
	return (0);
}


main(argc,argv)
int argc;
char **argv;
{
	int nrecords;
	int offset = DFLTOFFSET;
	long time();


	if (getuid()) nice(20);
	close(2);
	dup(1);
	setbuf(stdout, NULL);	/* both stdout and stderr go to same place
				   and are unbuffered */

	printf("pid %d\n",getpid());
	while (argc > 1 && argv[1][0]=='-') {
		switch(argv[1][1]) {
		case 'n':
			Flags.slow++;
			break;
		case 'v':
			Flags.verbose++;
			break;
		case 'r':
			Recsize = atoi(&argv[1][2]);
			if (Recsize <= 0 || Recsize > MAXRECSIZE) {
				printf("recsize %d too large\n", Recsize);
				exit(1);
			}
			break;
		case 'o':
			offset = atoi(&argv[1][2]);
			if (offset <= 0 || offset > MAXRECSIZE) {
				printf("offset %d too large\n",	offset);
				exit(1);
			}
			if (offset > DFLTRECSIZE)
				printf("offset %d is rather large\n", offset);

			break;
		case 'f':
			drivename = &argv[1][2];
			break;
		}
		argv++, argc--;
	}


	printf("slow=%d, Recsize=%d, offset for pass2 = %d\n",
		Flags.slow, Recsize, offset);

	printf("Pass 1:\n");
	Seed = (int)time((char *)0);
	if (Flags.verbose)
		printf("Seed=%d\n", Seed);
	tapeopen(1, 0);
	nrecords = tapewrite(0);	/* test with an offset of 0 */
	tapeopen(0, 10);
	taperead(0, nrecords);
	printf("Finished pass 1\n");

	tapeopen(1, 10);
	printf("Beginning pass 2\n");
	Seed = (int)time((char *)0);
	if (Flags.verbose)
		printf("Seed=%d\n", Seed);
	nrecords = tapewrite(offset);	
	tapeopen(0, 10);
	taperead(offset, nrecords);
	printf("finished pass 2\nDone, no errors\n");
	newseqnumber();
	exit(0);

}

tapewrite(offset)
{
	int nrecords, n;
	long tplength = 0;

	printf("Writing...\n");
	if (Flags.verbose) printf("offset=%d\n", offset);
	if (offset)
		write(Tapefd, buf, offset); /* who cares what buf contains? */

	for(nrecords=0;;nrecords++) {
		genpat(nrecords);
		if ((n=write (Tapefd,pattern,Recsize))!=Recsize) {
			if (n == -1)
				perror("Tape write error (presumably EOT)");
				/* this isn't fatal, really...*/
			break;
		}
		if (Flags.verbose)
			printf("w");
		tplength += Recsize;
		if (Flags.slow && ((nrecords%SLEEPEVERY) == 0)) {
			sleep(Flags.slow);
			if (Flags.verbose) printf("s");
		}
	}

	printf ("%d records, unformatted tape length = %ld bytes\n",
			nrecords, tplength);
	printf("Finished writing; Rewinding...\n");
	close (Tapefd);
	return(nrecords);
}

taperead(offset, nrecords)
{
	register int r2, n;
	printf ("Reading ...\n");
	if (Flags.verbose)
		printf("offset=%d, nrecords=%d\n", offset, nrecords);
	
	if (offset)
		read(Tapefd, buf, offset);
	for (r2=0;r2<nrecords;r2++) {
		genpat(r2);
		if ((n=read(Tapefd,buf,Recsize)) != Recsize) {
			if (n == -1)
				perror("Read error");
			else
				printf ("recsiz error, record %d;", r2),
				printf ("actual=%d, expected=%d\n", n, Recsize);
			exit(3);
		}
		if (Flags.verbose) printf("r");
		if (check()<0) {
			printf ("data error: record %d\n", r2);
			exit(4);
		}
		if (Flags.slow && ((r2%SLEEPEVERY) == 0)) {
			sleep(Flags.slow);
			if(Flags.verbose) printf("s");
		}
	}

	printf("Done reading; Rewinding...\n");
	close(Tapefd);
}

newseqnumber()
{
	FILE *seqfd;
	int seqnum;

	if ((seqfd=fopen ("/usr/lib/tapeseq","r+")) == NULL) {
		printf ("warning: creating new sequence file\n");
		umask(000);
		close(creat ("/usr/lib/tapeseq",0666));
		seqfd = fopen ("/usr/lib/tapeseq","r+");
		fprintf(seqfd, "0");
		rewind(seqfd);
	}

	fscanf (seqfd,"%d",&seqnum);
	rewind(seqfd);
	fprintf(seqfd,"%d\n",seqnum+1);
	printf("Sequence number = %d\n", seqnum+1);

	fclose (seqfd);
}

tapeopen(mode, sleeptime)
{
	if (Flags.verbose)
		printf("mode=%d, sleeptime=%d\n", mode, sleeptime);

	if (sleeptime==0) {	/* means die if can't open immediately */
		Tapefd = open (drivename, mode);
		if (Tapefd == -1) {
			perror("Can't open tape drive");
			exit(2);
		} else
			return;
	}

	while ((Tapefd=open(drivename, mode)) < 0) {
		if(Flags.verbose) printf("s");
		sleep(sleeptime);
	}

	return;
}
!Funky!Stuff!
exit 0
-- 
John Woods, Charles River Data Systems, Framingham MA, (617) 626-1101
...!decvax!frog!john, ...!mit-eddie!jfw, jfw%mit-ccc@MIT-XX.ARPA

"MU" said the Sacred Chao...