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