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