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
alloctape()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
taprec(dp)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
dmpblk(blkno,size)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
tperror()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
sigpipe()131 sigpipe()
132 {
133
134 msg("Broken pipe\n");
135 dumpabort();
136 }
137
138 #ifdef RDUMP
139 /*
140 * compatibility routine
141 */
tflush(i)142 tflush(i)
143 int i;
144 {
145
146 for (i = 0; i < ntrec; i++)
147 spclrec();
148 }
149 #endif RDUMP
150
flusht()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
rewind()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
close_rewind()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
otape()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
dumpabort()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
Exit(status)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 */
lockfile(fd)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
enslave()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
killall()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 */
doslave(cmd,prev,next)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 */
rewind_offline(fd)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