+
+/*
+ * could use pipe() for this if flock() worked on pipes
+ */
+lockfile(fd)
+ int fd[2];
+{
+ char tmpname[20];
+
+ strcpy(tmpname, _PATH_LOCK);
+ mktemp(tmpname);
+ if ((fd[1] = creat(tmpname, 0400)) < 0) {
+ msg("Could not create lockfile ");
+ perror(tmpname);
+ dumpabort();
+ }
+ if ((fd[0] = open(tmpname, 0)) < 0) {
+ msg("Could not reopen lockfile ");
+ perror(tmpname);
+ dumpabort();
+ }
+ unlink(tmpname);
+}
+
+enslave()
+{
+ int first[2], prev[2], next[2], cmd[2]; /* file descriptors */
+ register int i, j;
+
+ master = getpid();
+ signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
+ signal(SIGPIPE, sigpipe);
+ signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */
+ lockfile(first);
+ for (i = 0; i < SLAVES; i++) {
+ if (i == 0) {
+ prev[0] = first[1];
+ prev[1] = first[0];
+ } else {
+ prev[0] = next[0];
+ prev[1] = next[1];
+ flock(prev[1], LOCK_EX);
+ }
+ if (i < SLAVES - 1) {
+ lockfile(next);
+ } else {
+ next[0] = first[0];
+ next[1] = first[1]; /* Last slave loops back */
+ }
+ if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) {
+ msg("too many slaves, %d (recompile smaller) ", i);
+ perror("");
+ dumpabort();
+ }
+ slavefd[i] = cmd[1];
+ if (slavepid[i] == 0) { /* Slave starts up here */
+ for (j = 0; j <= i; j++)
+ close(slavefd[j]);
+ signal(SIGINT, SIG_IGN); /* Master handles this */
+ doslave(cmd[0], prev, next);
+ Exit(X_FINOK);
+ }
+ close(cmd[0]);
+ if (i > 0) {
+ close(prev[0]);
+ close(prev[1]);
+ }
+ }
+ close(first[0]);
+ close(first[1]);
+ master = 0; rotor = 0;
+}
+
+killall()
+{
+ register int i;
+
+ for (i = 0; i < SLAVES; i++)
+ if (slavepid[i] > 0)
+ kill(slavepid[i], SIGKILL);
+}
+
+/*
+ * Synchronization - each process has a lockfile, and shares file
+ * descriptors to the following process's lockfile. When our write
+ * completes, we release our lock on the following process's lock-
+ * file, allowing the following process to lock it and proceed. We
+ * get the lock back for the next cycle by swapping descriptors.
+ */
+doslave(cmd, prev, next)
+ register int cmd, prev[2], next[2];
+{
+ register int nread, toggle = 0;
+
+ close(fi);
+ if ((fi = open(disk, 0)) < 0) { /* Need our own seek pointer */
+ perror(" DUMP: slave couldn't reopen disk");
+ dumpabort();
+ }
+ /*
+ * Get list of blocks to dump, read the blocks into tape buffer
+ */
+ while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) {
+ register struct req *p = req;
+ for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
+ if (p->dblk) {
+ bread(p->dblk, tblock[trecno],
+ p->count * TP_BSIZE);
+ } else {
+ if (p->count != 1 || atomic(read, cmd,
+ tblock[trecno], TP_BSIZE) != TP_BSIZE) {
+ msg("Master/slave protocol botched.\n");
+ dumpabort();
+ }
+ }
+ }
+ flock(prev[toggle], LOCK_EX); /* Wait our turn */
+
+#ifdef RDUMP
+ if ((host ? rmtwrite(tblock[0], writesize)
+ : write(to, tblock[0], writesize)) != writesize) {
+#else RDUMP
+ if (write(to, tblock[0], writesize) != writesize) {
+#endif RDUMP
+ kill(master, SIGUSR1);
+ for (;;)
+ sigpause(0);
+ }
+ toggle ^= 1;
+ flock(next[toggle], LOCK_UN); /* Next slave's turn */
+ } /* Also jolts him awake */
+ if (nread != 0) {
+ perror(" DUMP: error reading command pipe");
+ dumpabort();
+ }
+}
+
+/*
+ * Since a read from a pipe may not return all we asked for,
+ * or a write may not write all we ask if we get a signal,
+ * loop until the count is satisfied (or error).
+ */
+atomic(func, fd, buf, count)
+ int (*func)(), fd, count;
+ char *buf;
+{
+ int got, need = count;
+
+ while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
+ buf += got;
+ return (got < 0 ? got : count - need);
+}