tape.c revision 1.4 1 /*-
2 * Copyright (c) 1980, 1991, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34 #ifndef lint
35 /*static char sccsid[] = "from: @(#)tape.c 8.2 (Berkeley) 3/17/94";*/
36 static char *rcsid = "$Id: tape.c,v 1.4 1994/09/23 14:27:06 mycroft Exp $";
37 #endif /* not lint */
38
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <sys/time.h>
42 #include <sys/wait.h>
43 #ifdef sunos
44 #include <sys/vnode.h>
45
46 #include <ufs/fs.h>
47 #include <ufs/inode.h>
48 #else
49 #include <ufs/ffs/fs.h>
50 #include <ufs/ufs/dinode.h>
51 #endif
52
53 #include <protocols/dumprestore.h>
54
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <setjmp.h>
58 #include <signal.h>
59 #include <stdio.h>
60 #ifdef __STDC__
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64 #else
65 int write(), read();
66 #endif
67
68 #include "dump.h"
69 #include "pathnames.h"
70
71 int writesize; /* size of malloc()ed buffer for tape */
72 long lastspclrec = -1; /* tape block number of last written header */
73 int trecno = 0; /* next record to write in current block */
74 extern long blocksperfile; /* number of blocks per output file */
75 long blocksthisvol; /* number of blocks on current output file */
76 extern int ntrec; /* blocking factor on tape */
77 extern int cartridge;
78 extern char *host;
79 char *nexttape;
80
81 static int atomic __P((int (*)(), int, char *, int));
82 static void doslave __P((int, int));
83 static void enslave __P((void));
84 static void flushtape __P((void));
85 static void killall __P((void));
86 static void rollforward __P((void));
87
88 /*
89 * Concurrent dump mods (Caltech) - disk block reading and tape writing
90 * are exported to several slave processes. While one slave writes the
91 * tape, the others read disk blocks; they pass control of the tape in
92 * a ring via signals. The parent process traverses the filesystem and
93 * sends writeheader()'s and lists of daddr's to the slaves via pipes.
94 * The following structure defines the instruction packets sent to slaves.
95 */
96 struct req {
97 daddr_t dblk;
98 int count;
99 };
100 int reqsiz;
101
102 #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */
103 struct slave {
104 int tapea; /* header number at start of this chunk */
105 int count; /* count to next header (used for TS_TAPE */
106 /* after EOT) */
107 int inode; /* inode that we are currently dealing with */
108 int fd; /* FD for this slave */
109 int pid; /* PID for this slave */
110 int sent; /* 1 == we've sent this slave requests */
111 int firstrec; /* record number of this block */
112 char (*tblock)[TP_BSIZE]; /* buffer for data blocks */
113 struct req *req; /* buffer for requests */
114 } slaves[SLAVES+1];
115 struct slave *slp;
116
117 char (*nextblock)[TP_BSIZE];
118
119 int master; /* pid of master, for sending error signals */
120 int tenths; /* length of tape used per block written */
121 static int caught; /* have we caught the signal to proceed? */
122 static int ready; /* have we reached the lock point without having */
123 /* received the SIGUSR2 signal from the prev slave? */
124 static jmp_buf jmpbuf; /* where to jump to if we are ready when the */
125 /* SIGUSR2 arrives from the previous slave */
126
127 int
128 alloctape()
129 {
130 int pgoff = getpagesize() - 1;
131 char *buf;
132 int i;
133
134 writesize = ntrec * TP_BSIZE;
135 reqsiz = (ntrec + 1) * sizeof(struct req);
136 /*
137 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
138 * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require
139 * repositioning after stopping, i.e, streaming mode, where the gap is
140 * variable, 0.30" to 0.45". The gap is maximal when the tape stops.
141 */
142 if (blocksperfile == 0)
143 tenths = writesize / density +
144 (cartridge ? 16 : density == 625 ? 5 : 8);
145 /*
146 * Allocate tape buffer contiguous with the array of instruction
147 * packets, so flushtape() can write them together with one write().
148 * Align tape buffer on page boundary to speed up tape write().
149 */
150 for (i = 0; i <= SLAVES; i++) {
151 buf = (char *)
152 malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE));
153 if (buf == NULL)
154 return(0);
155 slaves[i].tblock = (char (*)[TP_BSIZE])
156 (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
157 slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
158 }
159 slp = &slaves[0];
160 slp->count = 1;
161 slp->tapea = 0;
162 slp->firstrec = 0;
163 nextblock = slp->tblock;
164 return(1);
165 }
166
167 void
168 writerec(dp, isspcl)
169 char *dp;
170 int isspcl;
171 {
172
173 slp->req[trecno].dblk = (daddr_t)0;
174 slp->req[trecno].count = 1;
175 *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp;
176 if (isspcl)
177 lastspclrec = spcl.c_tapea;
178 trecno++;
179 spcl.c_tapea++;
180 if (trecno >= ntrec)
181 flushtape();
182 }
183
184 void
185 dumpblock(blkno, size)
186 daddr_t blkno;
187 int size;
188 {
189 int avail, tpblks, dblkno;
190
191 dblkno = fsbtodb(sblock, blkno);
192 tpblks = size >> tp_bshift;
193 while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
194 slp->req[trecno].dblk = dblkno;
195 slp->req[trecno].count = avail;
196 trecno += avail;
197 spcl.c_tapea += avail;
198 if (trecno >= ntrec)
199 flushtape();
200 dblkno += avail << (tp_bshift - dev_bshift);
201 tpblks -= avail;
202 }
203 }
204
205 int nogripe = 0;
206
207 void
208 tperror(signo)
209 int signo;
210 {
211
212 if (pipeout) {
213 msg("write error on %s\n", tape);
214 quit("Cannot recover\n");
215 /* NOTREACHED */
216 }
217 msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno);
218 broadcast("DUMP WRITE ERROR!\n");
219 if (!query("Do you want to restart?"))
220 dumpabort(0);
221 msg("Closing this volume. Prepare to restart with new media;\n");
222 msg("this dump volume will be rewritten.\n");
223 killall();
224 nogripe = 1;
225 close_rewind();
226 Exit(X_REWRITE);
227 }
228
229 void
230 sigpipe(signo)
231 int signo;
232 {
233
234 quit("Broken pipe\n");
235 }
236
237 static void
238 flushtape()
239 {
240 int i, blks, got;
241 long lastfirstrec;
242
243 int siz = (char *)nextblock - (char *)slp->req;
244
245 slp->req[trecno].count = 0; /* Sentinel */
246
247 if (atomic(write, slp->fd, (char *)slp->req, siz) != siz)
248 quit("error writing command pipe: %s\n", strerror(errno));
249 slp->sent = 1; /* we sent a request, read the response later */
250
251 lastfirstrec = slp->firstrec;
252
253 if (++slp >= &slaves[SLAVES])
254 slp = &slaves[0];
255
256 /* Read results back from next slave */
257 if (slp->sent) {
258 if (atomic(read, slp->fd, (char *)&got, sizeof got)
259 != sizeof got) {
260 perror(" DUMP: error reading command pipe in master");
261 dumpabort(0);
262 }
263 slp->sent = 0;
264
265 /* Check for end of tape */
266 if (got < writesize) {
267 msg("End of tape detected\n");
268
269 /*
270 * Drain the results, don't care what the values were.
271 * If we read them here then trewind won't...
272 */
273 for (i = 0; i < SLAVES; i++) {
274 if (slaves[i].sent) {
275 if (atomic(read, slaves[i].fd,
276 (char *)&got, sizeof got)
277 != sizeof got) {
278 perror(" DUMP: error reading command pipe in master");
279 dumpabort(0);
280 }
281 slaves[i].sent = 0;
282 }
283 }
284
285 close_rewind();
286 rollforward();
287 return;
288 }
289 }
290
291 blks = 0;
292 if (spcl.c_type != TS_END) {
293 for (i = 0; i < spcl.c_count; i++)
294 if (spcl.c_addr[i] != 0)
295 blks++;
296 }
297 slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
298 slp->tapea = spcl.c_tapea;
299 slp->firstrec = lastfirstrec + ntrec;
300 slp->inode = curino;
301 nextblock = slp->tblock;
302 trecno = 0;
303 asize += tenths;
304 blockswritten += ntrec;
305 blocksthisvol += ntrec;
306 if (!pipeout && (blocksperfile ?
307 (blocksthisvol >= blocksperfile) : (asize > tsize))) {
308 close_rewind();
309 startnewtape(0);
310 }
311 timeest();
312 }
313
314 void
315 trewind()
316 {
317 int f;
318 int got;
319
320 for (f = 0; f < SLAVES; f++) {
321 /*
322 * Drain the results, but unlike EOT we DO (or should) care
323 * what the return values were, since if we detect EOT after
324 * we think we've written the last blocks to the tape anyway,
325 * we have to replay those blocks with rollforward.
326 *
327 * fixme: punt for now.
328 */
329 if (slaves[f].sent) {
330 if (atomic(read, slaves[f].fd, (char *)&got, sizeof got)
331 != sizeof got) {
332 perror(" DUMP: error reading command pipe in master");
333 dumpabort(0);
334 }
335 slaves[f].sent = 0;
336 if (got != writesize) {
337 msg("EOT detected in last 2 tape records!\n");
338 msg("Use a longer tape, decrease the size estimate\n");
339 quit("or use no size estimate at all.\n");
340 }
341 }
342 (void) close(slaves[f].fd);
343 }
344 while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */
345 /* void */;
346
347 if (pipeout)
348 return;
349
350 msg("Closing %s\n", tape);
351
352 #ifdef RDUMP
353 if (host) {
354 rmtclose();
355 while (rmtopen(tape, 0) < 0)
356 sleep(10);
357 rmtclose();
358 return;
359 }
360 #endif
361 (void) close(tapefd);
362 while ((f = open(tape, 0)) < 0)
363 sleep (10);
364 (void) close(f);
365 }
366
367 void
368 close_rewind()
369 {
370 trewind();
371 if (nexttape)
372 return;
373 if (!nogripe) {
374 msg("Change Volumes: Mount volume #%d\n", tapeno+1);
375 broadcast("CHANGE DUMP VOLUMES!\7\7\n");
376 }
377 while (!query("Is the new volume mounted and ready to go?"))
378 if (query("Do you want to abort?")) {
379 dumpabort(0);
380 /*NOTREACHED*/
381 }
382 }
383
384 void
385 rollforward()
386 {
387 register struct req *p, *q, *prev;
388 register struct slave *tslp;
389 int i, size, savedtapea, got;
390 union u_spcl *ntb, *otb;
391 tslp = &slaves[SLAVES];
392 ntb = (union u_spcl *)tslp->tblock[1];
393
394 /*
395 * Each of the N slaves should have requests that need to
396 * be replayed on the next tape. Use the extra slave buffers
397 * (slaves[SLAVES]) to construct request lists to be sent to
398 * each slave in turn.
399 */
400 for (i = 0; i < SLAVES; i++) {
401 q = &tslp->req[1];
402 otb = (union u_spcl *)slp->tblock;
403
404 /*
405 * For each request in the current slave, copy it to tslp.
406 */
407
408 prev = NULL;
409 for (p = slp->req; p->count > 0; p += p->count) {
410 *q = *p;
411 if (p->dblk == 0)
412 *ntb++ = *otb++; /* copy the datablock also */
413 prev = q;
414 q += q->count;
415 }
416 if (prev == NULL)
417 quit("rollforward: protocol botch");
418 if (prev->dblk != 0)
419 prev->count -= 1;
420 else
421 ntb--;
422 q -= 1;
423 q->count = 0;
424 q = &tslp->req[0];
425 if (i == 0) {
426 q->dblk = 0;
427 q->count = 1;
428 trecno = 0;
429 nextblock = tslp->tblock;
430 savedtapea = spcl.c_tapea;
431 spcl.c_tapea = slp->tapea;
432 startnewtape(0);
433 spcl.c_tapea = savedtapea;
434 lastspclrec = savedtapea - 1;
435 }
436 size = (char *)ntb - (char *)q;
437 if (atomic(write, slp->fd, (char *)q, size) != size) {
438 perror(" DUMP: error writing command pipe");
439 dumpabort(0);
440 }
441 slp->sent = 1;
442 if (++slp >= &slaves[SLAVES])
443 slp = &slaves[0];
444
445 q->count = 1;
446
447 if (prev->dblk != 0) {
448 /*
449 * If the last one was a disk block, make the
450 * first of this one be the last bit of that disk
451 * block...
452 */
453 q->dblk = prev->dblk +
454 prev->count * (TP_BSIZE / DEV_BSIZE);
455 ntb = (union u_spcl *)tslp->tblock;
456 } else {
457 /*
458 * It wasn't a disk block. Copy the data to its
459 * new location in the buffer.
460 */
461 q->dblk = 0;
462 *((union u_spcl *)tslp->tblock) = *ntb;
463 ntb = (union u_spcl *)tslp->tblock[1];
464 }
465 }
466 slp->req[0] = *q;
467 nextblock = slp->tblock;
468 if (q->dblk == 0)
469 nextblock++;
470 trecno = 1;
471
472 /*
473 * Clear the first slaves' response. One hopes that it
474 * worked ok, otherwise the tape is much too short!
475 */
476 if (slp->sent) {
477 if (atomic(read, slp->fd, (char *)&got, sizeof got)
478 != sizeof got) {
479 perror(" DUMP: error reading command pipe in master");
480 dumpabort(0);
481 }
482 slp->sent = 0;
483
484 if (got != writesize) {
485 quit("EOT detected at start of the tape!\n");
486 }
487 }
488 }
489
490 /*
491 * We implement taking and restoring checkpoints on the tape level.
492 * When each tape is opened, a new process is created by forking; this
493 * saves all of the necessary context in the parent. The child
494 * continues the dump; the parent waits around, saving the context.
495 * If the child returns X_REWRITE, then it had problems writing that tape;
496 * this causes the parent to fork again, duplicating the context, and
497 * everything continues as if nothing had happened.
498 */
499 void
500 startnewtape(top)
501 int top;
502 {
503 int parentpid;
504 int childpid;
505 int status;
506 int waitpid;
507 char *p;
508 #ifdef sunos
509 void (*interrupt_save)();
510 #else
511 sig_t interrupt_save;
512 #endif
513
514 interrupt_save = signal(SIGINT, SIG_IGN);
515 parentpid = getpid();
516
517 restore_check_point:
518 (void)signal(SIGINT, interrupt_save);
519 /*
520 * All signals are inherited...
521 */
522 childpid = fork();
523 if (childpid < 0) {
524 msg("Context save fork fails in parent %d\n", parentpid);
525 Exit(X_ABORT);
526 }
527 if (childpid != 0) {
528 /*
529 * PARENT:
530 * save the context by waiting
531 * until the child doing all of the work returns.
532 * don't catch the interrupt
533 */
534 signal(SIGINT, SIG_IGN);
535 #ifdef TDEBUG
536 msg("Tape: %d; parent process: %d child process %d\n",
537 tapeno+1, parentpid, childpid);
538 #endif /* TDEBUG */
539 while ((waitpid = wait(&status)) != childpid)
540 msg("Parent %d waiting for child %d has another child %d return\n",
541 parentpid, childpid, waitpid);
542 if (status & 0xFF) {
543 msg("Child %d returns LOB status %o\n",
544 childpid, status&0xFF);
545 }
546 status = (status >> 8) & 0xFF;
547 #ifdef TDEBUG
548 switch(status) {
549 case X_FINOK:
550 msg("Child %d finishes X_FINOK\n", childpid);
551 break;
552 case X_ABORT:
553 msg("Child %d finishes X_ABORT\n", childpid);
554 break;
555 case X_REWRITE:
556 msg("Child %d finishes X_REWRITE\n", childpid);
557 break;
558 default:
559 msg("Child %d finishes unknown %d\n",
560 childpid, status);
561 break;
562 }
563 #endif /* TDEBUG */
564 switch(status) {
565 case X_FINOK:
566 Exit(X_FINOK);
567 case X_ABORT:
568 Exit(X_ABORT);
569 case X_REWRITE:
570 goto restore_check_point;
571 default:
572 msg("Bad return code from dump: %d\n", status);
573 Exit(X_ABORT);
574 }
575 /*NOTREACHED*/
576 } else { /* we are the child; just continue */
577 #ifdef TDEBUG
578 sleep(4); /* allow time for parent's message to get out */
579 msg("Child on Tape %d has parent %d, my pid = %d\n",
580 tapeno+1, parentpid, getpid());
581 #endif /* TDEBUG */
582 /*
583 * If we have a name like "/dev/rmt0,/dev/rmt1",
584 * use the name before the comma first, and save
585 * the remaining names for subsequent volumes.
586 */
587 tapeno++; /* current tape sequence */
588 if (nexttape || strchr(tape, ',')) {
589 if (nexttape && *nexttape)
590 tape = nexttape;
591 if ((p = strchr(tape, ',')) != NULL) {
592 *p = '\0';
593 nexttape = p + 1;
594 } else
595 nexttape = NULL;
596 msg("Dumping volume %d on %s\n", tapeno, tape);
597 }
598 #ifdef RDUMP
599 while ((tapefd = (host ? rmtopen(tape, 2) :
600 pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
601 #else
602 while ((tapefd = (pipeout ? 1 :
603 open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
604 #endif
605 {
606 msg("Cannot open output \"%s\".\n", tape);
607 if (!query("Do you want to retry the open?"))
608 dumpabort(0);
609 }
610
611 enslave(); /* Share open tape file descriptor with slaves */
612
613 asize = 0;
614 blocksthisvol = 0;
615 if (top)
616 newtape++; /* new tape signal */
617 spcl.c_count = slp->count;
618 /*
619 * measure firstrec in TP_BSIZE units since restore doesn't
620 * know the correct ntrec value...
621 */
622 spcl.c_firstrec = slp->firstrec;
623 spcl.c_volume++;
624 spcl.c_type = TS_TAPE;
625 spcl.c_flags |= DR_NEWHEADER;
626 writeheader((ino_t)slp->inode);
627 spcl.c_flags &=~ DR_NEWHEADER;
628 if (tapeno > 1)
629 msg("Volume %d begins with blocks from inode %d\n",
630 tapeno, slp->inode);
631 }
632 }
633
634 void
635 dumpabort(signo)
636 int signo;
637 {
638
639 if (master != 0 && master != getpid())
640 /* Signals master to call dumpabort */
641 (void) kill(master, SIGTERM);
642 else {
643 killall();
644 msg("The ENTIRE dump is aborted.\n");
645 }
646 #ifdef RDUMP
647 rmtclose();
648 #endif
649 Exit(X_ABORT);
650 }
651
652 __dead void
653 Exit(status)
654 int status;
655 {
656
657 #ifdef TDEBUG
658 msg("pid = %d exits with status %d\n", getpid(), status);
659 #endif /* TDEBUG */
660 exit(status);
661 }
662
663 /*
664 * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
665 */
666 void
667 proceed(signo)
668 int signo;
669 {
670
671 if (ready)
672 longjmp(jmpbuf, 1);
673 caught++;
674 }
675
676 void
677 enslave()
678 {
679 int cmd[2];
680 register int i, j;
681
682 master = getpid();
683
684 signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
685 signal(SIGPIPE, sigpipe);
686 signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */
687 signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */
688
689 for (i = 0; i < SLAVES; i++) {
690 if (i == slp - &slaves[0]) {
691 caught = 1;
692 } else {
693 caught = 0;
694 }
695
696 if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 ||
697 (slaves[i].pid = fork()) < 0)
698 quit("too many slaves, %d (recompile smaller): %s\n",
699 i, strerror(errno));
700
701 slaves[i].fd = cmd[1];
702 slaves[i].sent = 0;
703 if (slaves[i].pid == 0) { /* Slave starts up here */
704 for (j = 0; j <= i; j++)
705 (void) close(slaves[j].fd);
706 signal(SIGINT, SIG_IGN); /* Master handles this */
707 doslave(cmd[0], i);
708 Exit(X_FINOK);
709 }
710 }
711
712 for (i = 0; i < SLAVES; i++)
713 (void) atomic(write, slaves[i].fd,
714 (char *) &slaves[(i + 1) % SLAVES].pid,
715 sizeof slaves[0].pid);
716
717 master = 0;
718 }
719
720 void
721 killall()
722 {
723 register int i;
724
725 for (i = 0; i < SLAVES; i++)
726 if (slaves[i].pid > 0)
727 (void) kill(slaves[i].pid, SIGKILL);
728 }
729
730 /*
731 * Synchronization - each process has a lockfile, and shares file
732 * descriptors to the following process's lockfile. When our write
733 * completes, we release our lock on the following process's lock-
734 * file, allowing the following process to lock it and proceed. We
735 * get the lock back for the next cycle by swapping descriptors.
736 */
737 static void
738 doslave(cmd, slave_number)
739 register int cmd;
740 int slave_number;
741 {
742 register int nread;
743 int nextslave, size, wrote, eot_count;
744
745 /*
746 * Need our own seek pointer.
747 */
748 (void) close(diskfd);
749 if ((diskfd = open(disk, O_RDONLY)) < 0)
750 quit("slave couldn't reopen disk: %s\n", strerror(errno));
751
752 /*
753 * Need the pid of the next slave in the loop...
754 */
755 if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave))
756 != sizeof nextslave) {
757 quit("master/slave protocol botched - didn't get pid of next slave.\n");
758 }
759
760 /*
761 * Get list of blocks to dump, read the blocks into tape buffer
762 */
763 while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) {
764 register struct req *p = slp->req;
765
766 for (trecno = 0; trecno < ntrec;
767 trecno += p->count, p += p->count) {
768 if (p->dblk) {
769 bread(p->dblk, slp->tblock[trecno],
770 p->count * TP_BSIZE);
771 } else {
772 if (p->count != 1 || atomic(read, cmd,
773 (char *)slp->tblock[trecno],
774 TP_BSIZE) != TP_BSIZE)
775 quit("master/slave protocol botched.\n");
776 }
777 }
778 if (setjmp(jmpbuf) == 0) {
779 ready = 1;
780 if (!caught)
781 (void) pause();
782 }
783 ready = 0;
784 caught = 0;
785
786 /* Try to write the data... */
787 eot_count = 0;
788 size = 0;
789
790 while (eot_count < 10 && size < writesize) {
791 #ifdef RDUMP
792 if (host)
793 wrote = rmtwrite(slp->tblock[0]+size,
794 writesize-size);
795 else
796 #endif
797 wrote = write(tapefd, slp->tblock[0]+size,
798 writesize-size);
799 #ifdef WRITEDEBUG
800 printf("slave %d wrote %d\n", slave_number, wrote);
801 #endif
802 if (wrote < 0)
803 break;
804 if (wrote == 0)
805 eot_count++;
806 size += wrote;
807 }
808
809 #ifdef WRITEDEBUG
810 if (size != writesize)
811 printf("slave %d only wrote %d out of %d bytes and gave up.\n",
812 slave_number, size, writesize);
813 #endif
814
815 if (eot_count > 0)
816 size = 0;
817
818 /*
819 * fixme: Pyramids running OSx return ENOSPC
820 * at EOT on 1/2 inch drives.
821 */
822 if (size < 0) {
823 (void) kill(master, SIGUSR1);
824 for (;;)
825 (void) sigpause(0);
826 } else {
827 /*
828 * pass size of write back to master
829 * (for EOT handling)
830 */
831 (void) atomic(write, cmd, (char *)&size, sizeof size);
832 }
833
834 /*
835 * If partial write, don't want next slave to go.
836 * Also jolts him awake.
837 */
838 (void) kill(nextslave, SIGUSR2);
839 }
840 if (nread != 0)
841 quit("error reading command pipe: %s\n", strerror(errno));
842 }
843
844 /*
845 * Since a read from a pipe may not return all we asked for,
846 * or a write may not write all we ask if we get a signal,
847 * loop until the count is satisfied (or error).
848 */
849 static int
850 atomic(func, fd, buf, count)
851 int (*func)(), fd;
852 char *buf;
853 int count;
854 {
855 int got, need = count;
856
857 while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
858 buf += got;
859 return (got < 0 ? got : count - need);
860 }
861