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