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