xref: /original-bsd/local/ukc/dump/dumptape.c (revision 7211505a)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char sccsid[] = "@(#)dumptape.c	1.4 (UKC) 08/11/87	5.8 (Berkeley) 2/23/87";
9 #endif not lint
10 
11 #include "dump.h"
12 #include <sys/file.h>
13 #include <sys/ioctl.h>
14 #include <sys/mtio.h>
15 
16 
17 char	(*tblock)[TP_BSIZE];	/* pointer to malloc()ed buffer for tape */
18 int	writesize;		/* size of malloc()ed buffer for tape */
19 long	lastspclrec = -1;	/* tape block number of last written header */
20 int	trecno = 0;		/* next record to write in current block */
21 extern int ntrec;		/* blocking factor on tape */
22 extern int cartridge;
23 extern int rewindoffline;
24 extern int read(), write();
25 #ifdef RDUMP
26 extern char *host;
27 #endif RDUMP
28 
29 /*
30  * Concurrent dump mods (Caltech) - disk block reading and tape writing
31  * are exported to several slave processes.  While one slave writes the
32  * tape, the others read disk blocks; they pass control of the tape in
33  * a ring via flock().	The parent process traverses the filesystem and
34  * sends spclrec()'s and lists of daddr's to the slaves via pipes.
35  */
36 struct req {			/* instruction packets sent to slaves */
37 	daddr_t dblk;
38 	int count;
39 } *req;
40 int reqsiz;
41 
42 #define SLAVES 3		/* 1 slave writing, 1 reading, 1 for slack */
43 int slavefd[SLAVES];		/* pipes from master to each slave */
44 int slavepid[SLAVES];		/* used by killall() */
45 int rotor;			/* next slave to be instructed */
46 int master;			/* pid of master, for sending error signals */
47 int tenths;			/* length of tape used per block written */
48 
49 alloctape()
50 {
51 	int pgoff = getpagesize() - 1;
52 
53 	writesize = ntrec * TP_BSIZE;
54 	reqsiz = ntrec * sizeof(struct req);
55 	/*
56 	 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
57 	 * (see DEC TU80 User's Guide).  The shorter gaps of 6250-bpi require
58 	 * repositioning after stopping, i.e, streaming mode, where the gap is
59 	 * variable, 0.30" to 0.45".  The gap is maximal when the tape stops.
60 	 */
61 	tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8);
62 	/*
63 	 * Allocate tape buffer contiguous with the array of instruction
64 	 * packets, so flusht() can write them together with one write().
65 	 * Align tape buffer on page boundary to speed up tape write().
66 	 */
67 	req = (struct req *)malloc(reqsiz + writesize + pgoff);
68 	if (req == NULL)
69 		return(0);
70 	tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff);
71 	req = (struct req *)tblock - ntrec;
72 	return(1);
73 }
74 
75 
76 taprec(dp)
77 	char *dp;
78 {
79 	req[trecno].dblk = (daddr_t)0;
80 	req[trecno].count = 1;
81 	*(union u_spcl *)(*tblock++) = *(union u_spcl *)dp;	/* movc3 */
82 	lastspclrec = spcl.c_tapea;
83 	trecno++;
84 	spcl.c_tapea++;
85 	if(trecno >= ntrec)
86 		flusht();
87 }
88 
89 dmpblk(blkno, size)
90 	daddr_t blkno;
91 	int size;
92 {
93 	int avail, tpblks, dblkno;
94 
95 	dblkno = fsbtodb(sblock, blkno);
96 	tpblks = size / TP_BSIZE;
97 	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
98 		req[trecno].dblk = dblkno;
99 		req[trecno].count = avail;
100 		trecno += avail;
101 		spcl.c_tapea += avail;
102 		if (trecno >= ntrec)
103 			flusht();
104 		dblkno += avail * (TP_BSIZE / DEV_BSIZE);
105 		tpblks -= avail;
106 	}
107 }
108 
109 int	nogripe = 0;
110 
111 tperror() {
112 	if (pipeout) {
113 		msg("Tape write error on %s\n", tape);
114 		msg("Cannot recover\n");
115 		dumpabort();
116 		/* NOTREACHED */
117 	}
118 	msg("Tape write error %d feet into tape %d\n", asize/120L, tapeno);
119 	broadcast("TAPE ERROR!\n");
120 	if (!query("Do you want to restart?"))
121 		dumpabort();
122 	msg("This tape will rewind.  After it is rewound,\n");
123 	msg("replace the faulty tape with a new one;\n");
124 	msg("this dump volume will be rewritten.\n");
125 	killall();
126 	nogripe = 1;
127 	close_rewind();
128 	Exit(X_REWRITE);
129 }
130 
131 sigpipe()
132 {
133 
134 	msg("Broken pipe\n");
135 	dumpabort();
136 }
137 
138 #ifdef RDUMP
139 /*
140  * compatibility routine
141  */
142 tflush(i)
143 	int i;
144 {
145 
146 	for (i = 0; i < ntrec; i++)
147 		spclrec();
148 }
149 #endif RDUMP
150 
151 flusht()
152 {
153 	int siz = (char *)tblock - (char *)req;
154 
155 	if (atomic(write, slavefd[rotor], req, siz) != siz) {
156 		perror("  DUMP: error writing command pipe");
157 		dumpabort();
158 	}
159 	if (++rotor >= SLAVES) rotor = 0;
160 	tblock = (char (*)[TP_BSIZE]) &req[ntrec];
161 	trecno = 0;
162 	asize += tenths;
163 	blockswritten += ntrec;
164 	if (!pipeout && asize > tsize) {
165 		close_rewind();
166 		otape();
167 	}
168 	timeest();
169 }
170 
171 rewind()
172 {
173 	int f;
174 
175 	if (pipeout)
176 		return;
177 	for (f = 0; f < SLAVES; f++)
178 		close(slavefd[f]);
179 	while (wait(NULL) >= 0)    ;	/* wait for any signals from slaves */
180 	msg("Tape rewinding\n");
181 #ifdef RDUMP
182 	if (host) {
183 		if (rewindoffline) {
184 			rewind_offline(to);
185 			rmtclose();
186 			return;
187 		}
188 		rmtclose();
189 		while (rmtopen(tape, 0) < 0)
190 			sleep(10);
191 		rmtclose();
192 		return;
193 	}
194 #endif RDUMP
195 	if (rewindoffline) {
196 		rewind_offline(to);
197 		close(to);
198 		return;
199 	}
200 	close(to);
201 	while ((f = open(tape, 0)) < 0)
202 		sleep (10);
203 	close(f);
204 }
205 
206 close_rewind()
207 {
208 	extern int userlabel;
209 	char *label;
210 	char *createlabel();
211 
212 	rewind();
213 	if (!nogripe) {
214 		if (userlabel) {
215 			label = createlabel(tapeno+1);
216 			msg("Change Tapes: Mount tape labelled `%s' reel %d of this dump\n",
217 				label, tapeno+1);
218 		}
219 		else
220 			msg("Change Tapes: Mount tape #%d\n", tapeno+1);
221 		broadcast("CHANGE TAPES!\7\7\n");
222 	}
223 	while (!query("Is the new tape mounted and ready to go?"))
224 		if (query("Do you want to abort?")) {
225 			dumpabort();
226 			/*NOTREACHED*/
227 		}
228 }
229 
230 /*
231  *	We implement taking and restoring checkpoints on the tape level.
232  *	When each tape is opened, a new process is created by forking; this
233  *	saves all of the necessary context in the parent.  The child
234  *	continues the dump; the parent waits around, saving the context.
235  *	If the child returns X_REWRITE, then it had problems writing that tape;
236  *	this causes the parent to fork again, duplicating the context, and
237  *	everything continues as if nothing had happened.
238  */
239 
240 otape()
241 {
242 	int	parentpid;
243 	int	childpid;
244 	int	status;
245 	int	waitpid;
246 	int	(*interrupt)() = signal(SIGINT, SIG_IGN);
247 	int	blks, i;
248 
249 	parentpid = getpid();
250 
251     restore_check_point:
252 	signal(SIGINT, interrupt);
253 	/*
254 	 *	All signals are inherited...
255 	 */
256 	childpid = fork();
257 	if (childpid < 0) {
258 		msg("Context save fork fails in parent %d\n", parentpid);
259 		Exit(X_ABORT);
260 	}
261 	if (childpid != 0) {
262 		/*
263 		 *	PARENT:
264 		 *	save the context by waiting
265 		 *	until the child doing all of the work returns.
266 		 *	don't catch the interrupt
267 		 */
268 		signal(SIGINT, SIG_IGN);
269 #ifdef TDEBUG
270 		msg("Tape: %d; parent process: %d child process %d\n",
271 			tapeno+1, parentpid, childpid);
272 #endif TDEBUG
273 		while ((waitpid = wait(&status)) != childpid)
274 			msg("Parent %d waiting for child %d has another child %d return\n",
275 				parentpid, childpid, waitpid);
276 		if (status & 0xFF) {
277 			msg("Child %d returns LOB status %o\n",
278 				childpid, status&0xFF);
279 		}
280 		status = (status >> 8) & 0xFF;
281 #ifdef TDEBUG
282 		switch(status) {
283 			case X_FINOK:
284 				msg("Child %d finishes X_FINOK\n", childpid);
285 				break;
286 			case X_ABORT:
287 				msg("Child %d finishes X_ABORT\n", childpid);
288 				break;
289 			case X_REWRITE:
290 				msg("Child %d finishes X_REWRITE\n", childpid);
291 				break;
292 			default:
293 				msg("Child %d finishes unknown %d\n",
294 					childpid, status);
295 				break;
296 		}
297 #endif TDEBUG
298 		switch(status) {
299 			case X_FINOK:
300 				Exit(X_FINOK);
301 			case X_ABORT:
302 				Exit(X_ABORT);
303 			case X_REWRITE:
304 				goto restore_check_point;
305 			default:
306 				msg("Bad return code from dump: %d\n", status);
307 				Exit(X_ABORT);
308 		}
309 		/*NOTREACHED*/
310 	} else {	/* we are the child; just continue */
311 #ifdef TDEBUG
312 		sleep(4);	/* allow time for parent's message to get out */
313 		msg("Child on Tape %d has parent %d, my pid = %d\n",
314 			tapeno+1, parentpid, getpid());
315 #endif TDEBUG
316 		while (labelcheck(tapeno+1) < 0)
317 			if (!query("Problem with tape label.  Do you want to retry the open?"))
318 				dumpabort();
319 #ifdef RDUMP
320 		while ((to = (host ? rmtopen(tape, 2) :
321 			pipeout ? 1 : creat(tape, 0666))) < 0)
322 #else RDUMP
323 		while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
324 #endif RDUMP
325 			if (!query("Cannot open tape.  Do you want to retry the open?"))
326 				dumpabort();
327 
328 		enslave();  /* Share open tape file descriptor with slaves */
329 
330 		asize = 0;
331 		tapeno++;		/* current tape sequence */
332 		newtape++;		/* new tape signal */
333 		blks = 0;
334 		if (spcl.c_type != TS_END)
335 			for (i = 0; i < spcl.c_count; i++)
336 				if (spcl.c_addr[i] != 0)
337 					blks++;
338 		spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec;
339 		spcl.c_volume++;
340 		spcl.c_type = TS_TAPE;
341 		spcl.c_flags |= DR_NEWHEADER;
342 		spclrec();
343 		spcl.c_flags &=~ DR_NEWHEADER;
344 		/*
345 		 *	It may seem that the logging ought to be done
346 		 *	at the end of the write but
347 		 *	a)	for cyclic dumps the tape has been destroyed
348 		 *	b)	if this dump fails due to a bad tape
349 		 *		then the new tape ought to be relabelled
350 		 *	c)	if this dump fails for other reasons
351 		 *		then the successful dump will also be logged
352 		 */
353 		log_volume(spcl.c_label);
354 
355 		if (tapeno > 1){
356 			if (userlabel)
357 				msg("Tape `%s' (reel %d) begins with blocks from ino %d\n",
358 					spcl.c_label, tapeno, ino);
359 			else
360 				msg("Tape %d begins with blocks from ino %d\n",
361 					tapeno, ino);
362 		}
363 	}
364 }
365 
366 dumpabort()
367 {
368 	if (master != 0 && master != getpid())
369 		kill(master, SIGTERM);	/* Signals master to call dumpabort */
370 	else {
371 		killall();
372 		msg("The ENTIRE dump is aborted.\n");
373 	}
374 	Exit(X_ABORT);
375 }
376 
377 Exit(status)
378 {
379 #ifdef TDEBUG
380 	msg("pid = %d exits with status %d\n", getpid(), status);
381 #endif TDEBUG
382 	exit(status);
383 }
384 
385 /*
386  * could use pipe() for this if flock() worked on pipes
387  */
388 lockfile(fd)
389 	int fd[2];
390 {
391 	char tmpname[20];
392 
393 	strcpy(tmpname, "/tmp/dumplockXXXXXX");
394 	mktemp(tmpname);
395 	if ((fd[1] = creat(tmpname, 0400)) < 0) {
396 		msg("Could not create lockfile ");
397 		perror(tmpname);
398 		dumpabort();
399 	}
400 	if ((fd[0] = open(tmpname, 0)) < 0) {
401 		msg("Could not reopen lockfile ");
402 		perror(tmpname);
403 		dumpabort();
404 	}
405 	unlink(tmpname);
406 }
407 
408 enslave()
409 {
410 	int first[2], prev[2], next[2], cmd[2];     /* file descriptors */
411 	register int i, j;
412 
413 	master = getpid();
414 	signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
415 	signal(SIGPIPE, sigpipe);
416 	signal(SIGUSR1, tperror);    /* Slave sends SIGUSR1 on tape errors */
417 	lockfile(first);
418 	for (i = 0; i < SLAVES; i++) {
419 		if (i == 0) {
420 			prev[0] = first[1];
421 			prev[1] = first[0];
422 		} else {
423 			prev[0] = next[0];
424 			prev[1] = next[1];
425 			flock(prev[1], LOCK_EX);
426 		}
427 		if (i < SLAVES - 1) {
428 			lockfile(next);
429 		} else {
430 			next[0] = first[0];
431 			next[1] = first[1];	    /* Last slave loops back */
432 		}
433 		if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) {
434 			msg("too many slaves, %d (recompile smaller) ", i);
435 			perror("");
436 			dumpabort();
437 		}
438 		slavefd[i] = cmd[1];
439 		if (slavepid[i] == 0) { 	    /* Slave starts up here */
440 			for (j = 0; j <= i; j++)
441 				close(slavefd[j]);
442 			signal(SIGINT, SIG_IGN);    /* Master handles this */
443 			doslave(cmd[0], prev, next);
444 			Exit(X_FINOK);
445 		}
446 		close(cmd[0]);
447 		if (i > 0) {
448 			close(prev[0]);
449 			close(prev[1]);
450 		}
451 	}
452 	close(first[0]);
453 	close(first[1]);
454 	master = 0; rotor = 0;
455 }
456 
457 killall()
458 {
459 	register int i;
460 
461 	for (i = 0; i < SLAVES; i++)
462 		if (slavepid[i] > 0)
463 			kill(slavepid[i], SIGKILL);
464 }
465 
466 /*
467  * Synchronization - each process has a lockfile, and shares file
468  * descriptors to the following process's lockfile.  When our write
469  * completes, we release our lock on the following process's lock-
470  * file, allowing the following process to lock it and proceed. We
471  * get the lock back for the next cycle by swapping descriptors.
472  */
473 doslave(cmd, prev, next)
474 	register int cmd, prev[2], next[2];
475 {
476 	register int nread, toggle = 0;
477 
478 	close(fi);
479 	if ((fi = open(disk, 0)) < 0) { 	/* Need our own seek pointer */
480 		perror("  DUMP: slave couldn't reopen disk");
481 		dumpabort();
482 	}
483 	/*
484 	 * Get list of blocks to dump, read the blocks into tape buffer
485 	 */
486 	while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) {
487 		register struct req *p = req;
488 		for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
489 			if (p->dblk) {
490 				bread(p->dblk, tblock[trecno],
491 					p->count * TP_BSIZE);
492 			} else {
493 				if (p->count != 1 || atomic(read, cmd,
494 				    tblock[trecno], TP_BSIZE) != TP_BSIZE) {
495 					msg("Master/slave protocol botched.\n");
496 					dumpabort();
497 				}
498 			}
499 		}
500 		flock(prev[toggle], LOCK_EX);	/* Wait our turn */
501 
502 #ifdef RDUMP
503 		if ((host ? rmtwrite(tblock[0], writesize)
504 			: write(to, tblock[0], writesize)) != writesize) {
505 #else RDUMP
506 		if (write(to, tblock[0], writesize) != writesize) {
507 #endif RDUMP
508 			kill(master, SIGUSR1);
509 			for (;;)
510 				sigpause(0);
511 		}
512 		toggle ^= 1;
513 		flock(next[toggle], LOCK_UN);	/* Next slave's turn */
514 	}					/* Also jolts him awake */
515 	if (nread != 0) {
516 		perror("  DUMP: error reading command pipe");
517 		dumpabort();
518 	}
519 }
520 
521 /*
522  * Since a read from a pipe may not return all we asked for,
523  * or a write may not write all we ask if we get a signal,
524  * loop until the count is satisfied (or error).
525  */
526 atomic(func, fd, buf, count)
527 	int (*func)(), fd, count;
528 	char *buf;
529 {
530 	int got, need = count;
531 
532 	while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
533 		buf += got;
534 	return (got < 0 ? got : count - need);
535 }
536 
537 /*
538  *	Added by Peter C
539  *	put a tape drive offline
540  */
541 rewind_offline(fd)
542 {
543 	struct mtop mtop;
544 	char *eofr;
545 	char *eoff;
546 
547 	eofr = "Cannot write end of file record";
548 	eoff = "Cannot put the tape offline";
549 #ifdef RDUMP
550 	if (host) {
551 		mtop.mt_op = MTWEOF;
552 		mtop.mt_count = 1;
553 		if (rmtioctl(fd, MTIOCTOP, &mtop) < 0)
554 			perror(eofr);
555 		mtop.mt_op = MTWEOF;
556 		mtop.mt_count = 1;
557 		if (rmtioctl(fd, MTIOCTOP, &mtop) < 0)
558 			perror(eofr);
559 		mtop.mt_op = MTOFFL;
560 		mtop.mt_count = 1;
561 		if (rmtioctl(fd, MTIOCTOP, &mtop) < 0)
562 			perror(eoff);
563 		return;
564 	}
565 #endif RDUMP
566 	mtop.mt_op = MTWEOF;
567 	mtop.mt_count = 1;
568 	if (ioctl(fd, MTIOCTOP, &mtop) < 0)
569 		perror(eofr);
570 	mtop.mt_op = MTWEOF;
571 	mtop.mt_count = 1;
572 	if (ioctl(fd, MTIOCTOP, &mtop) < 0)
573 		perror(eofr);
574 	mtop.mt_op = MTOFFL;
575 	mtop.mt_count = 1;
576 	if (ioctl(fd, MTIOCTOP, &mtop) < 0)
577 		perror(eoff);
578 
579 }
580