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