+/*
+ * Interpose something between the process writing out the dump (writing it to
+ * some extraction program, really) and the socket from which we're reading, so
+ * that we can do things like prompt for human interaction for multiple tapes.
+ */
+void writer_intermediary(ctl_fd, data_fd, elist)
+ int ctl_fd, data_fd;
+ EXTRACT_LIST *elist;
+{
+ int child_pipe[2];
+ pid_t pid;
+ char buffer[DISK_BLOCK_BYTES];
+ ssize_t s;
+ ssize_t bytes_read;
+ amwait_t extractor_status;
+ int max_fd, nfound;
+ fd_set readset, selectset;
+ struct timeval timeout;
+
+ /*
+ * If there's no distinct data channel (such as if we're talking to an
+ * older server), don't bother doing anything complicated. Just run the
+ * extraction.
+ */
+ if(data_fd == -1){
+ extract_files_child(ctl_fd, elist);
+ /* NOTREACHED */
+ }
+
+ if(pipe(child_pipe) == -1) {
+ error("extract_list - error setting up pipe to extractor: %s\n",
+ strerror(errno));
+ /* NOTREACHED */
+ }
+
+ /* okay, ready to extract. fork a child to do the actual work */
+ if ((pid = fork()) == 0) {
+ /* this is the child process */
+ /* never gets out of this clause */
+ aclose(child_pipe[1]);
+ extract_files_child(child_pipe[0], elist);
+ /* NOTREACHED */
+ }
+
+ /* This is the parent */
+ if (pid == -1) {
+ error("writer_intermediary - error forking child");
+ /* NOTREACHED */
+ }
+
+ aclose(child_pipe[0]);
+
+ if(data_fd > ctl_fd) max_fd = data_fd+1;
+ else max_fd = ctl_fd+1;
+ FD_ZERO(&readset);
+ FD_SET(data_fd, &readset);
+ FD_SET(ctl_fd, &readset);
+
+ do {
+ timeout.tv_sec = READ_TIMEOUT;
+ timeout.tv_usec = 0;
+ FD_COPY(&readset, &selectset);
+
+ nfound = select(max_fd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL,
+ &timeout);
+ if(nfound < 0) {
+ fprintf(stderr,"select error: %s\n", strerror(errno));
+ break;
+ }
+
+ if (nfound == 0) { /* timeout */
+ fprintf(stderr, "timeout waiting %d seconds for restore\n",
+ READ_TIMEOUT);
+ fprintf(stderr, "increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
+ break;
+ }
+
+ if(FD_ISSET(ctl_fd, &selectset)) {
+ bytes_read = read(ctl_fd, buffer, sizeof(buffer)-1);
+ switch(bytes_read) {
+ case -1:
+ if ((errno != EINTR) && (errno != EAGAIN)) {
+ if (errno != EPIPE) {
+ fprintf(stderr,"writer ctl fd read error: %s",
+ strerror(errno));
+ }
+ FD_CLR(ctl_fd, &readset);
+ }
+ break;
+
+ case 0:
+ FD_CLR(ctl_fd, &readset);
+ break;
+
+ default: {
+ char desired_tape[MAX_TAPE_LABEL_BUF];
+
+ buffer[bytes_read] = '\0';
+ /* if prompted for a tape, relay said prompt to the user */
+ if(sscanf(buffer, "FEEDME %s\n", desired_tape) == 1) {
+ int done = 0;
+ while (!done) {
+ char *input = NULL;
+ printf("Please insert tape %s. Continue? [Y|n]: ",
+ desired_tape);
+ fflush(stdout);
+
+ input = agets(stdin); /* strips \n */
+ if (strcasecmp("", input) == 0||
+ strcasecmp("y", input) == 0||
+ strcasecmp("yes", input) == 0) {
+ send_to_tape_server(tape_control_sock, "OK");
+ done = 1;
+ } else if (strcasecmp("n", input) == 0||
+ strcasecmp("no", input) == 0) {
+ send_to_tape_server(tape_control_sock, "ERROR");
+ /* Abort!
+ We are the middle process, so just die. */
+ exit(EXIT_FAILURE);
+ }
+ amfree(input);
+ }
+ } else {
+ fprintf(stderr, "Strange message from tape server: %s", buffer);
+
+ break;
+ }
+ }
+ }
+ }
+
+ /* now read some dump data */
+ if(FD_ISSET(data_fd, &selectset)) {
+ bytes_read = read(data_fd, buffer, sizeof(buffer)-1);
+ switch(bytes_read) {
+ case -1:
+ if ((errno != EINTR) && (errno != EAGAIN)) {
+ if (errno != EPIPE) {
+ fprintf(stderr,"writer data fd read error: %s",
+ strerror(errno));
+ }
+ FD_CLR(data_fd, &readset);
+ }
+ break;
+
+ case 0:
+ FD_CLR(data_fd, &readset);
+ break;
+
+ default:
+ /*
+ * spit what we got from the server to the child
+ * process handling actual dumpfile extraction
+ */
+ if((s = fullwrite(child_pipe[1], buffer, bytes_read)) < 0){
+ if(errno == EPIPE) {
+ error("%s: pipe data reader has quit: %s\n",
+ get_pname(), strerror(errno));
+ /* NOTREACHED */
+ }
+ error("Write error to extract child: %s\n",
+ strerror(errno));
+ /* NOTREACHED */
+ }
+ break;
+ }
+ }
+ } while(FD_ISSET(ctl_fd, &readset) || FD_ISSET(data_fd, &readset));
+
+ aclose(child_pipe[1]);
+
+ waitpid(pid, &extractor_status, 0);
+ if(WEXITSTATUS(extractor_status) != 0){
+ int ret = WEXITSTATUS(extractor_status);
+ if(ret == 255) ret = -1;
+ error("Extractor child exited with status %d\n", ret);
+ /* NOTREACHED */
+ }
+
+ exit(0);
+}
+
+/* exec restore to do the actual restoration */