Imported Upstream version 2.5.2p1
[debian/amanda] / oldrecover-src / extract_list.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: extract_list.c,v 1.6 2006/08/24 01:57:15 paddy_s Exp $
28  *
29  * implements the "extract" command in amrecover
30  */
31
32 #include "amanda.h"
33 #include "version.h"
34 #include "amrecover.h"
35 #include "fileheader.h"
36 #include "dgram.h"
37 #include "stream.h"
38 #include "tapelist.h"
39 #ifdef SAMBA_CLIENT
40 #include "findpass.h"
41 #endif
42 #include "util.h"
43
44 typedef struct EXTRACT_LIST_ITEM {
45     char *path;
46
47     struct EXTRACT_LIST_ITEM *next;
48 }
49 EXTRACT_LIST_ITEM;
50
51 typedef struct EXTRACT_LIST {
52     char *date;                 /* date tape created */
53     int  level;                 /* level of dump */
54     char *tape;                 /* tape label */
55     off_t fileno;               /* fileno on tape */
56     EXTRACT_LIST_ITEM *files;   /* files to get off tape */
57
58     struct EXTRACT_LIST *next;
59 }
60 EXTRACT_LIST;
61
62 #define SKIP_TAPE 2
63 #define RETRY_TAPE 3
64
65 char *dump_device_name = NULL;
66
67 extern char *localhost;
68
69 /* global pid storage for interrupt handler */
70 pid_t extract_restore_child_pid = -1;
71
72 static EXTRACT_LIST *extract_list = NULL;
73 static int tape_control_sock = -1;
74 static int tape_data_sock = -1;
75
76 #ifdef SAMBA_CLIENT
77 unsigned short samba_extract_method = SAMBA_TAR;
78 #endif /* SAMBA_CLIENT */
79
80 #define READ_TIMEOUT    240*60
81
82 EXTRACT_LIST *first_tape_list(void);
83 EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list);
84 int is_extract_list_nonempty(void);
85 int length_of_tape_list(EXTRACT_LIST *tape_list);
86 void add_file(char *path, char *regex);
87 void add_glob(char *glob);
88 void add_regex(char *regex);
89 void clear_extract_list(void);
90 void clean_tape_list(EXTRACT_LIST *tape_list);
91 void clean_extract_list(void);
92 void delete_file(char *path, char *regex);
93 void delete_glob(char *glob);
94 void delete_regex(char *regex);
95 void delete_tape_list(EXTRACT_LIST *tape_list);
96 void display_extract_list(char *file);
97 void extract_files(void);
98 void read_file_header(char *buffer,
99                         dumpfile_t *file,
100                         size_t buflen,
101                         int tapedev);
102 void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist);
103 void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist);
104
105 static int add_extract_item(DIR_ITEM *ditem);
106 static int delete_extract_item(DIR_ITEM *ditem);
107 static int extract_files_setup(char *label, off_t fsf);
108 static int okay_to_continue(int allow_tape,
109                         int allow_skip,
110                         int allow_retry);
111 static int okay_to_continue(int, int,  int);
112 static ssize_t read_buffer(int datafd,
113                         char *buffer,
114                         size_t buflen,
115                         long timeout_s);
116 static void clear_tape_list(EXTRACT_LIST *tape_list);
117 static void extract_files_child(int in_fd, EXTRACT_LIST *elist);
118 static void send_to_tape_server(int tss, char *cmd);
119
120
121 /*
122  * Function:  ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
123  *
124  * Description:
125  *      read data from input file desciptor waiting up to timeout_s
126  *      seconds before returning data.
127  *
128  * Inputs:
129  *      datafd    - File descriptor to read from.
130  *      buffer    - Buffer to read into.
131  *      buflen    - Maximum number of bytes to read into buffer.
132  *      timeout_s - Seconds to wait before returning what was already read.
133  *
134  * Returns:
135  *      >0        - Number of data bytes in buffer.
136  *       0        - EOF
137  *      -1        - errno == ETIMEDOUT if no data available in specified time.
138  *                  errno == ENFILE if datafd is invalid.
139  *                  otherwise errno is set by select or read..
140  */
141
142 static ssize_t
143 read_buffer(
144     int         datafd,
145     char *      buffer,
146     size_t      buflen,
147     long        timeout_s)
148 {
149     ssize_t size = 0;
150     fd_set readset;
151     struct timeval timeout;
152     char *dataptr;
153     ssize_t spaceleft;
154     int nfound;
155
156     if(datafd < 0 || datafd >= (int)FD_SETSIZE) {
157         errno = EMFILE;                                 /* out of range */
158         return -1;
159     }
160
161     dataptr = buffer;
162     spaceleft = (ssize_t)buflen;
163
164     do {
165         FD_ZERO(&readset);
166         FD_SET(datafd, &readset);
167         timeout.tv_sec = timeout_s;
168         timeout.tv_usec = 0;
169         nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
170         if(nfound < 0 ) {
171             /* Select returned an error. */
172             fprintf(stderr,"select error: %s\n", strerror(errno));
173             size = -1;
174             break;
175         }
176
177         if (nfound == 0) {
178             /* Select timed out. */
179             if (timeout_s != 0)  {
180                 /* Not polling: a real read timeout */
181                 fprintf(stderr,"timeout waiting for restore\n");
182                 fprintf(stderr,"increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
183             }
184             errno = ETIMEDOUT;
185             size = -1;
186             break;
187         }
188
189         if(!FD_ISSET(datafd, &readset))
190             continue;
191
192         /* Select says data is available, so read it.  */
193         size = read(datafd, dataptr, (size_t)spaceleft);
194         if (size < 0) {
195             if ((errno == EINTR) || (errno == EAGAIN)) {
196                 continue;
197             }
198             if (errno != EPIPE) {
199                 fprintf(stderr, "read_buffer: read error - %s",
200                     strerror(errno));
201                 break;
202             }
203             size = 0;
204         }
205         spaceleft -= size;
206         dataptr += size;
207     } while ((size > 0) && (spaceleft > 0));
208
209     return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size);
210 }
211
212
213 EXTRACT_LIST *
214 first_tape_list(void)
215 {
216     return extract_list;
217 }
218
219 EXTRACT_LIST *
220 next_tape_list(
221     /*@keep@*/ EXTRACT_LIST *list)
222 {
223     if (list == NULL)
224         return NULL;
225     return list->next;
226 }
227
228 static void
229 clear_tape_list(
230     EXTRACT_LIST *      tape_list)
231 {
232     EXTRACT_LIST_ITEM *this, *next;
233     
234
235     this = tape_list->files;
236     while (this != NULL)
237     {
238         next = this->next;
239         amfree(this->path);
240         amfree(this);
241         this = next;
242     }
243     tape_list->files = NULL;
244 }
245
246
247 /* remove a tape list from the extract list, clearing the tape list
248    beforehand if necessary */
249 void
250 delete_tape_list(
251     EXTRACT_LIST *      tape_list)
252 {
253     EXTRACT_LIST *this, *prev;
254
255     if (tape_list == NULL)
256         return;
257
258     /* is it first on the list? */
259     if (tape_list == extract_list)
260     {
261         extract_list = tape_list->next;
262         clear_tape_list(tape_list);
263         amfree(tape_list->date);
264         amfree(tape_list->tape);
265         amfree(tape_list);
266         return;
267     }
268
269     /* so not first on list - find it and delete */
270     prev = extract_list;
271     this = extract_list->next;
272     while (this != NULL)
273     {
274         if (this == tape_list)
275         {
276             prev->next = tape_list->next;
277             clear_tape_list(tape_list);
278             amfree(tape_list->date);
279             amfree(tape_list->tape);
280             amfree(tape_list);
281             return;
282         }
283         prev = this;
284         this = this->next;
285     }
286     /*NOTREACHED*/
287 }
288
289
290 /* return the number of files on a tape's list */
291 int
292 length_of_tape_list(
293     EXTRACT_LIST *      tape_list)
294 {
295     EXTRACT_LIST_ITEM *fn;
296     int n;
297
298     n = 0;
299     for (fn = tape_list->files; fn != NULL; fn = fn->next)
300         n++;
301
302     return n;
303 }
304
305
306 void
307 clear_extract_list(void)
308 {
309     while (extract_list != NULL)
310         delete_tape_list(extract_list);
311 }
312
313
314 void
315 clean_tape_list(
316     EXTRACT_LIST *tape_list)
317 {
318     EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1;
319     EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2;
320     int remove_fn1;
321     int remove_fn2;
322
323     pfn1 = NULL;
324     fn1 = tape_list->files;
325     while (fn1 != NULL) {
326         remove_fn1 = 0;
327
328         pfn2 = fn1;
329         fn2 = fn1->next;
330         while (fn2 != NULL && remove_fn1 == 0) {
331             remove_fn2 = 0;
332             if(strcmp(fn1->path, fn2->path) == 0) {
333                 remove_fn2 = 1;
334             } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 &&
335                        ((strlen(fn2->path) > strlen(fn1->path) &&
336                          fn2->path[strlen(fn1->path)] == '/') ||
337                        (fn1->path[strlen(fn1->path)-1] == '/'))) {
338                 remove_fn2 = 1;
339             } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 &&
340                        ((strlen(fn1->path) > strlen(fn2->path) &&
341                          fn1->path[strlen(fn2->path)] == '/')  ||
342                        (fn2->path[strlen(fn2->path)-1] == '/'))) {
343                 remove_fn1 = 1;
344                 break;
345             }
346
347             if (remove_fn2) {
348                 dbprintf(("removing path %s, it is included in %s\n",
349                           fn2->path, fn1->path));
350                 ofn2 = fn2;
351                 fn2 = fn2->next;
352                 amfree(ofn2->path);
353                 amfree(ofn2);
354                 pfn2->next = fn2;
355             } else if (remove_fn1 == 0) {
356                 pfn2 = fn2;
357                 fn2 = fn2->next;
358             }
359         }
360
361         if(remove_fn1 != 0) {
362             /* fn2->path is always valid */
363             /*@i@*/ dbprintf(("removing path %s, it is included in %s\n",
364             /*@i@*/           fn1->path, fn2->path));
365             ofn1 = fn1;
366             fn1 = fn1->next;
367             amfree(ofn1->path);
368             if(pfn1 == NULL) {
369                 amfree(tape_list->files);
370                 tape_list->files = fn1;
371             } else {
372                 amfree(pfn1->next);
373                 pfn1->next = fn1;
374             }
375         } else {
376             pfn1 = fn1;
377             fn1 = fn1->next;
378         }
379     }
380 }
381
382
383 void
384 clean_extract_list(void)
385 {
386     EXTRACT_LIST *this;
387
388     for (this = extract_list; this != NULL; this = this->next)
389         clean_tape_list(this);
390 }
391
392
393 /* returns -1 if error */
394 /* returns  0 on succes */
395 /* returns  1 if already added */
396 static int
397 add_extract_item(
398     DIR_ITEM *  ditem)
399 {
400     EXTRACT_LIST *this, *this1;
401     EXTRACT_LIST_ITEM *that, *curr;
402     char *ditem_path = NULL;
403
404     ditem_path = stralloc(ditem->path);
405     clean_pathname(ditem_path);
406
407     for (this = extract_list; this != NULL; this = this->next)
408     {
409         /* see if this is the list for the tape */      
410         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
411         {
412             /* yes, so add to list */
413             curr=this->files;
414             while(curr!=NULL)
415             {
416                 if (strcmp(curr->path, ditem_path) == 0) {
417                     amfree(ditem_path);
418                     return 1;
419                 }
420                 curr=curr->next;
421             }
422             that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
423             that->path = stralloc(ditem_path);
424             that->next = this->files;
425             this->files = that;         /* add at front since easiest */
426             amfree(ditem_path);
427             return 0;
428         }
429     }
430
431     /* so this is the first time we have seen this tape */
432     this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
433     this->tape = stralloc(ditem->tape);
434     this->level = ditem->level;
435     this->fileno = ditem->fileno;
436     this->date = stralloc(ditem->date);
437     that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
438     that->path = stralloc(ditem_path);
439     that->next = NULL;
440     this->files = that;
441
442     /* add this in date increasing order          */
443     /* because restore must be done in this order */
444     /* add at begining */
445     if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0) 
446     {
447         this->next = extract_list;
448         extract_list = this;
449         amfree(ditem_path);
450         return 0;
451     }
452     for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
453     {
454         /* add in the middle */
455         if(strcmp(this->date,this1->next->date) < 0)
456         {
457             this->next = this1->next;
458             this1->next = this;
459             amfree(ditem_path);
460             return 0;
461         }
462     }
463     /* add at end */
464     this->next = NULL;
465     this1->next = this;
466     amfree(ditem_path);
467     return 0;
468 }
469
470
471 /* returns -1 if error */
472 /* returns  0 on deletion */
473 /* returns  1 if not there */
474 static int
475 delete_extract_item(
476     DIR_ITEM *  ditem)
477 {
478     EXTRACT_LIST *this;
479     EXTRACT_LIST_ITEM *that, *prev;
480     char *ditem_path = NULL;
481
482     ditem_path = stralloc(ditem->path);
483     clean_pathname(ditem_path);
484
485     for (this = extract_list; this != NULL; this = this->next)
486     {
487         /* see if this is the list for the tape */      
488         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
489         {
490             /* yes, so find file on list */
491             that = this->files;
492             if (strcmp(that->path, ditem_path) == 0)
493             {
494                 /* first on list */
495                 this->files = that->next;
496                 amfree(that->path);
497                 amfree(that);
498                 /* if list empty delete it */
499                 if (this->files == NULL)
500                     delete_tape_list(this);
501                 amfree(ditem_path);
502                 return 0;
503             }
504             prev = that;
505             that = that->next;
506             while (that != NULL)
507             {
508                 if (strcmp(that->path, ditem_path) == 0)
509                 {
510                     prev->next = that->next;
511                     amfree(that->path);
512                     amfree(that);
513                     amfree(ditem_path);
514                     return 0;
515                 }
516                 prev = that;
517                 that = that->next;
518             }
519             amfree(ditem_path);
520             return 1;
521         }
522     }
523
524     amfree(ditem_path);
525     return 1;
526 }
527
528 void
529 add_glob(
530     char *      glob)
531 {
532     char *regex;
533     char *regex_path;
534     char *s;
535     char *uqglob = unquote_string(glob);
536
537     regex = glob_to_regex(uqglob);
538     dbprintf(("add_glob (%s) -> %s\n", uqglob, regex));
539     if ((s = validate_regexp(regex)) != NULL) {
540         printf("%s is not a valid shell wildcard pattern: ", glob);
541         puts(s);
542     } else {
543         /*
544          * glob_to_regex() anchors the beginning of the pattern with ^,
545          * but we will be tacking it onto the end of the current directory
546          * in add_file, so strip that off.  Also, it anchors the end with
547          * $, but we need to match an optional trailing /, so tack that on
548          * the end.
549          */
550         regex_path = stralloc(regex + 1);
551         regex_path[strlen(regex_path) - 1] = '\0';
552         strappend(regex_path, "[/]*$");
553         add_file(uqglob, regex_path);
554         amfree(regex_path);
555     }
556     amfree(regex);
557     amfree(uqglob);
558 }
559
560 void
561 add_regex(
562     char *      regex)
563 {
564     char *s;
565     char *uqregex = unquote_string(regex);
566
567     if ((s = validate_regexp(uqregex)) != NULL) {
568         printf("%s is not a valid regular expression: ", regex);
569         puts(s);
570     } else {
571         add_file(uqregex, regex);
572     }
573     amfree(uqregex);
574 }
575
576 void add_file(
577     char *      path,
578     char *      regex)
579 {
580     DIR_ITEM *ditem, lditem;
581     char *path_on_disk = NULL;
582     char *path_on_disk_slash = NULL;
583     char *cmd = NULL;
584     char *err = NULL;
585     int i;
586     ssize_t j;
587     char *dir, *dir_undo, dir_undo_ch = '\0';
588     char *ditem_path = NULL;
589     char *l = NULL;
590     int  added;
591     char *s, *fp, *quoted;
592     int ch;
593     int found_one;
594     int dir_entries;
595
596     if (disk_path == NULL) {
597         printf("Must select directory before adding files\n");
598         return;
599     }
600     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
601
602     dbprintf(("add_file: Looking for \"%s\"\n", regex));
603
604     if(strcmp(regex, "/[/]*$") == 0) {  /* "/" behave like "." */
605         regex = "\\.[/]*$";
606     }
607     else if(strcmp(regex, "[^/]*[/]*$") == 0) {         /* "*" */
608         regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
609     } else {
610         /* remove "/" at end of path */
611         j = (ssize_t)(strlen(regex) - 1);
612         while(j >= 0 && regex[j] == '/')
613             regex[j--] = '\0';
614     }
615
616     /* convert path (assumed in cwd) to one on disk */
617     if (strcmp(disk_path, "/") == 0) {
618         if (*regex == '/') {
619             /* No mods needed if already starts with '/' */
620             path_on_disk = stralloc(regex);
621         } else {
622             /* Prepend '/' */
623             path_on_disk = stralloc2("/", regex);
624         }
625     } else {
626         char *clean_disk_path = clean_regex(disk_path);
627         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
628         amfree(clean_disk_path);
629     }
630
631     path_on_disk_slash = stralloc2(path_on_disk, "/");
632
633     dbprintf(("add_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
634               regex, path_on_disk));
635
636     found_one = 0;
637     dir_entries = 0;
638     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
639     {
640         dir_entries++;
641         quoted = quote_string(ditem->path);
642         dbprintf(("add_file: Pondering ditem->path=%s\n", quoted));
643         amfree(quoted);
644         if (match(path_on_disk, ditem->path)
645             || match(path_on_disk_slash, ditem->path))
646         {
647             found_one = 1;
648             j = (ssize_t)strlen(ditem->path);
649             if((j > 0 && ditem->path[j-1] == '/')
650                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
651             {   /* It is a directory */
652                 ditem_path = newstralloc(ditem_path, ditem->path);
653                 clean_pathname(ditem_path);
654
655                 cmd = stralloc2("ORLD ", ditem_path);
656                 if(send_command(cmd) == -1) {
657                     amfree(cmd);
658                     amfree(ditem_path);
659                     amfree(path_on_disk);
660                     amfree(path_on_disk_slash);
661                     exit(1);
662                 }
663                 amfree(cmd);
664                 cmd = NULL;
665                 /* skip preamble */
666                 if ((i = get_reply_line()) == -1) {
667                     amfree(ditem_path);
668                     amfree(path_on_disk);
669                     amfree(path_on_disk_slash);
670                     exit(1);
671                 }
672                 if(i==0) {              /* assume something wrong */
673                     amfree(ditem_path);
674                     amfree(path_on_disk);
675                     amfree(path_on_disk_slash);
676                     l = reply_line();
677                     printf("%s\n", l);
678                     return;
679                 }
680                 dir_undo = NULL;
681                 added=0;
682                 lditem.path = newstralloc(lditem.path, ditem->path);
683                 /* skip the last line -- duplicate of the preamble */
684
685                 while ((i = get_reply_line()) != 0) {
686                     if (i == -1) {
687                         amfree(ditem_path);
688                         amfree(path_on_disk);
689                         amfree(path_on_disk_slash);
690                         exit(1);
691                     }
692                     if(err) {
693                         if(cmd == NULL) {
694                             if(dir_undo) *dir_undo = dir_undo_ch;
695                             dir_undo = NULL;
696                             cmd = stralloc(l);  /* save for error report */
697                         }
698                         continue;       /* throw the rest of the lines away */
699                     }
700                     l=reply_line();
701                     if (!server_happy()) {
702                         puts(l);
703                         continue;
704                     }
705
706                     s = l;
707                     if(strncmp_const_skip(l, "201-", s, ch) != 0) {
708                         err = "bad reply: not 201-";
709                         continue;
710                     }
711
712                     ch = *s++;
713                     skip_whitespace(s, ch);
714                     if(ch == '\0') {
715                         err = "bad reply: missing date field";
716                         continue;
717                     }
718                     fp = s-1;
719                     skip_non_whitespace(s, ch);
720                     s[-1] = '\0';
721                     lditem.date = newstralloc(lditem.date, fp);
722                     s[-1] = (char)ch;
723
724                     skip_whitespace(s, ch);
725                     if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
726                         err = "bad reply: cannot parse level field";
727                         continue;
728                     }
729                     skip_integer(s, ch);
730
731                     skip_whitespace(s, ch);
732                     if(ch == '\0') {
733                         err = "bad reply: missing tape field";
734                         continue;
735                     }
736                     fp = s-1;
737                     skip_non_whitespace(s, ch);
738                     s[-1] = '\0';
739                     lditem.tape = newstralloc(lditem.tape, fp);
740                     s[-1] = (char)ch;
741
742                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
743                         OFF_T_FMT_TYPE fileno_ = (OFF_T_FMT_TYPE)0;
744                         skip_whitespace(s, ch);
745                         if(ch == '\0' ||
746                            sscanf(s - 1, OFF_T_FMT, &fileno_) != 1) {
747                             err = "bad reply: cannot parse fileno field";
748                             continue;
749                         }
750                         lditem.fileno = (off_t)fileno_;
751                         skip_integer(s, ch);
752                     }
753
754                     skip_whitespace(s, ch);
755                     if(ch == '\0') {
756                         err = "bad reply: missing directory field";
757                         continue;
758                     }
759                     dir = s - 1;
760                     skip_quoted_string(s, ch);
761                     dir_undo = s - 1;
762                     dir_undo_ch = *dir_undo;
763                     *dir_undo = '\0';
764
765                     switch(add_extract_item(&lditem)) {
766                     case -1:
767                         printf("System error\n");
768                         dbprintf(("add_file: (Failed) System error\n"));
769                         break;
770
771                     case  0:
772                         quoted = quote_string(lditem.path);
773                         printf("Added dir %s at date %s\n",
774                                quoted, lditem.date);
775                         dbprintf(("add_file: (Successful) Added dir %s at date %s\n",
776                                   quoted, lditem.date));
777                         amfree(quoted);
778                         added=1;
779                         break;
780
781                     case  1:
782                         break;
783                     }
784                 }
785                 if(!server_happy()) {
786                     puts(reply_line());
787                 } else if(err) {
788                     if (*err)
789                         puts(err);
790                     if (cmd)
791                         puts(cmd);
792                 } else if(added == 0) {
793                     quoted = quote_string(ditem_path);
794                     printf("dir %s already added\n", quoted);
795                     dbprintf(("add_file: dir %s already added\n", quoted));
796                     amfree(quoted);
797                 }
798             }
799             else /* It is a file */
800             {
801                 switch(add_extract_item(ditem)) {
802                 case -1:
803                     printf("System error\n");
804                     dbprintf(("add_file: (Failed) System error\n"));
805                     break;
806
807                 case  0:
808                     quoted = quote_string(ditem->path);
809                     printf("Added file %s\n", quoted);
810                     dbprintf(("add_file: (Successful) Added %s\n", quoted));
811                     amfree(quoted);
812                     break;
813
814                 case  1:
815                     quoted = quote_string(ditem->path);
816                     printf("File %s already added\n", quoted);
817                     dbprintf(("add_file: file %s already added\n", quoted));
818                     amfree(quoted);
819                     break;
820                 }
821             }
822         }
823     }
824     if (cmd != NULL)
825         amfree(cmd);
826     amfree(ditem_path);
827     amfree(path_on_disk);
828     amfree(path_on_disk_slash);
829
830     if(! found_one) {
831         quoted = quote_string(path);
832         printf("File %s doesn't exist in directory\n", quoted);
833         dbprintf(("add_file: (Failed) File %s doesn't exist in directory\n",
834                   quoted));
835         amfree(quoted);
836     }
837 }
838
839
840 void
841 delete_glob(
842     char *      glob)
843 {
844     char *regex;
845     char *regex_path;
846     char *s;
847     char *uqglob = unquote_string(glob);
848
849     regex = glob_to_regex(uqglob);
850     dbprintf(("delete_glob (%s) -> %s\n", uqglob, regex));
851     if ((s = validate_regexp(regex)) != NULL) {
852         printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
853         puts(s);
854     } else {
855         /*
856          * glob_to_regex() anchors the beginning of the pattern with ^,
857          * but we will be tacking it onto the end of the current directory
858          * in add_file, so strip that off.  Also, it anchors the end with
859          * $, but we need to match an optional trailing /, so tack that on
860          * the end.
861          */
862         regex_path = stralloc(regex + 1);
863         regex_path[strlen(regex_path) - 1] = '\0';
864         strappend(regex_path, "[/]*$");
865         delete_file(uqglob, regex_path);
866         amfree(regex_path);
867     }
868     amfree(regex);
869     amfree(uqglob);
870 }
871
872 void
873 delete_regex(
874     char *      regex)
875 {
876     char *s;
877     char *uqregex = unquote_string(regex);
878
879     if ((s = validate_regexp(regex)) != NULL) {
880         printf("\"%s\" is not a valid regular expression: ", regex);
881         puts(s);
882     } else {
883         delete_file(uqregex, uqregex);
884     }
885     amfree(uqregex);
886 }
887
888 void
889 delete_file(
890     char *      path,
891     char *      regex)
892 {
893     DIR_ITEM *ditem, lditem;
894     char *path_on_disk = NULL;
895     char *path_on_disk_slash = NULL;
896     char *cmd = NULL;
897     char *err = NULL;
898     int i;
899     ssize_t j;
900     char *date;
901     char *tape, *tape_undo, tape_undo_ch = '\0';
902     char *dir_undo, dir_undo_ch = '\0';
903     int  level = 0;
904     off_t fileno;
905     char *ditem_path = NULL;
906     char *l = NULL;
907     int  deleted;
908     char *s;
909     int ch;
910     int found_one;
911     char *quoted;
912
913     if (disk_path == NULL) {
914         printf("Must select directory before deleting files\n");
915         return;
916     }
917     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
918
919     dbprintf(("delete_file: Looking for \"%s\"\n", path));
920
921     if (strcmp(regex, "[^/]*[/]*$") == 0) {
922         /* Looking for * find everything but single . */
923         regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
924     } else {
925         /* remove "/" at end of path */
926         j = (ssize_t)(strlen(regex) - 1);
927         while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
928     }
929
930     /* convert path (assumed in cwd) to one on disk */
931     if (strcmp(disk_path, "/") == 0) {
932         if (*regex == '/') {
933             if (strcmp(regex, "/[/]*$") == 0) {
934                 /* We want "/" to match the directory itself: "/." */
935                 path_on_disk = stralloc("/\\.[/]*$");
936             } else {
937                 /* No mods needed if already starts with '/' */
938                 path_on_disk = stralloc(regex);
939             }
940         } else {
941             /* Prepend '/' */
942             path_on_disk = stralloc2("/", regex);
943         }
944     } else {
945         char *clean_disk_path = clean_regex(disk_path);
946         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
947         amfree(clean_disk_path);
948     }
949
950     path_on_disk_slash = stralloc2(path_on_disk, "/");
951
952     dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
953               regex, path_on_disk));
954     found_one = 0;
955     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
956     {
957         quoted = quote_string(ditem->path);
958         dbprintf(("delete_file: Pondering ditem->path=%s\n", quoted));
959         amfree(quoted);
960         if (match(path_on_disk, ditem->path)
961             || match(path_on_disk_slash, ditem->path))
962         {
963             found_one = 1;
964             j = (ssize_t)strlen(ditem->path);
965             if((j > 0 && ditem->path[j-1] == '/')
966                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
967             {   /* It is a directory */
968                 ditem_path = newstralloc(ditem_path, ditem->path);
969                 clean_pathname(ditem_path);
970
971                 cmd = stralloc2("ORLD ", ditem_path);
972                 if(send_command(cmd) == -1) {
973                     amfree(cmd);
974                     amfree(ditem_path);
975                     amfree(path_on_disk);
976                     amfree(path_on_disk_slash);
977                     exit(1);
978                 }
979                 amfree(cmd);
980                 /* skip preamble */
981                 if ((i = get_reply_line()) == -1) {
982                     amfree(ditem_path);
983                     amfree(path_on_disk);
984                     amfree(path_on_disk_slash);
985                     exit(1);
986                 }
987                 if(i==0)                /* assume something wrong */
988                 {
989                     amfree(ditem_path);
990                     amfree(path_on_disk);
991                     amfree(path_on_disk_slash);
992                     l = reply_line();
993                     printf("%s\n", l);
994                     return;
995                 }
996                 deleted=0;
997                 lditem.path = newstralloc(lditem.path, ditem->path);
998                 amfree(cmd);
999                 tape_undo = dir_undo = NULL;
1000                 /* skip the last line -- duplicate of the preamble */
1001                 while ((i = get_reply_line()) != 0)
1002                 {
1003                     if (i == -1) {
1004                         amfree(ditem_path);
1005                         amfree(path_on_disk);
1006                         amfree(path_on_disk_slash);
1007                         exit(1);
1008                     }
1009                     if(err) {
1010                         if(cmd == NULL) {
1011                             if(tape_undo) *tape_undo = tape_undo_ch;
1012                             if(dir_undo) *dir_undo = dir_undo_ch;
1013                             tape_undo = dir_undo = NULL;
1014                             cmd = stralloc(l);  /* save for the error report */
1015                         }
1016                         continue;       /* throw the rest of the lines away */
1017                     }
1018                     l=reply_line();
1019                     if (!server_happy()) {
1020                         puts(l);
1021                         continue;
1022                     }
1023
1024                     s = l;
1025                     if(strncmp_const_skip(l, "201-", s, ch) != 0) {
1026                         err = "bad reply: not 201-";
1027                         continue;
1028                     }
1029                     ch = *s++;
1030
1031                     skip_whitespace(s, ch);
1032                     if(ch == '\0') {
1033                         err = "bad reply: missing date field";
1034                         continue;
1035                     }
1036                     date = s - 1;
1037                     skip_non_whitespace(s, ch);
1038                     *(s - 1) = '\0';
1039
1040                     skip_whitespace(s, ch);
1041                     if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1042                         err = "bad reply: cannot parse level field";
1043                         continue;
1044                     }
1045                     skip_integer(s, ch);
1046
1047                     skip_whitespace(s, ch);
1048                     if(ch == '\0') {
1049                         err = "bad reply: missing tape field";
1050                         continue;
1051                     }
1052                     tape = s - 1;
1053                     skip_non_whitespace(s, ch);
1054                     tape_undo = s - 1;
1055                     tape_undo_ch = *tape_undo;
1056                     *tape_undo = '\0';
1057
1058                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1059                         OFF_T_FMT_TYPE fileno_ = (OFF_T_FMT_TYPE)0;
1060                         skip_whitespace(s, ch);
1061                         if(ch == '\0' ||
1062                            sscanf(s - 1, OFF_T_FMT, &fileno_) != 1) {
1063                             err = "bad reply: cannot parse fileno field";
1064                             continue;
1065                         }
1066                         fileno = (off_t)fileno_;
1067                         skip_integer(s, ch);
1068                     }
1069
1070                     skip_whitespace(s, ch);
1071                     if(ch == '\0') {
1072                         err = "bad reply: missing directory field";
1073                         continue;
1074                     }
1075                     skip_non_whitespace(s, ch);
1076                     dir_undo = s - 1;
1077                     dir_undo_ch = *dir_undo;
1078                     *dir_undo = '\0';
1079
1080                     lditem.date = newstralloc(lditem.date, date);
1081                     lditem.level=level;
1082                     lditem.tape = newstralloc(lditem.tape, tape);
1083                     switch(delete_extract_item(&lditem)) {
1084                     case -1:
1085                         printf("System error\n");
1086                         dbprintf(("delete_file: (Failed) System error\n"));
1087                         break;
1088                     case  0:
1089                         printf("Deleted dir %s at date %s\n", ditem_path, date);
1090                         dbprintf(("delete_file: (Successful) Deleted dir %s at date %s\n",
1091                                   ditem_path, date));
1092                         deleted=1;
1093                         break;
1094                     case  1:
1095                         break;
1096                     }
1097                 }
1098                 if(!server_happy()) {
1099                     puts(reply_line());
1100                 } else if(err) {
1101                     if (*err)
1102                         puts(err);
1103                     if (cmd)
1104                         puts(cmd);
1105                 } else if(deleted == 0) {
1106                     printf("Warning - dir '%s' not on tape list\n",
1107                            ditem_path);
1108                     dbprintf(("delete_file: dir '%s' not on tape list\n",
1109                               ditem_path));
1110                 }
1111             }
1112             else
1113             {
1114                 switch(delete_extract_item(ditem)) {
1115                 case -1:
1116                     printf("System error\n");
1117                     dbprintf(("delete_file: (Failed) System error\n"));
1118                     break;
1119                 case  0:
1120                     printf("Deleted %s\n", ditem->path);
1121                     dbprintf(("delete_file: (Successful) Deleted %s\n",
1122                               ditem->path));
1123                     break;
1124                 case  1:
1125                     printf("Warning - file '%s' not on tape list\n",
1126                            ditem->path);
1127                     dbprintf(("delete_file: file '%s' not on tape list\n",
1128                               ditem->path));
1129                     break;
1130                 }
1131             }
1132         }
1133     }
1134     amfree(cmd);
1135     amfree(ditem_path);
1136     amfree(path_on_disk);
1137     amfree(path_on_disk_slash);
1138
1139     if(! found_one) {
1140         printf("File %s doesn't exist in directory\n", path);
1141         dbprintf(("delete_file: (Failed) File %s doesn't exist in directory\n",
1142                   path));
1143     }
1144 }
1145
1146
1147 /* print extract list into file. If NULL ptr passed print to screen */
1148 void
1149 display_extract_list(
1150     char *      file)
1151 {
1152     EXTRACT_LIST *this;
1153     EXTRACT_LIST_ITEM *that;
1154     FILE *fp;
1155     char *pager;
1156     char *pager_command;
1157     char *uqfile;
1158
1159     if (file == NULL)
1160     {
1161         if ((pager = getenv("PAGER")) == NULL)
1162         {
1163             pager = "more";
1164         }
1165         /*
1166          * Set up the pager command so if the pager is terminated, we do
1167          * not get a SIGPIPE back.
1168          */
1169         pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1170         if ((fp = popen(pager_command, "w")) == NULL)
1171         {
1172             printf("Warning - can't pipe through %s\n", pager);
1173             fp = stdout;
1174         }
1175         amfree(pager_command);
1176     }
1177     else
1178     {
1179         uqfile = unquote_string(file);
1180         if ((fp = fopen(uqfile, "w")) == NULL)
1181         {
1182             printf("Can't open file %s to print extract list into\n", file);
1183             amfree(uqfile);
1184             return;
1185         }
1186         amfree(uqfile);
1187     }
1188
1189     for (this = extract_list; this != NULL; this = this->next)
1190     {
1191         fprintf(fp, "TAPE %s LEVEL %d DATE %s\n",
1192                 this->tape, this->level, this->date);
1193         for (that = this->files; that != NULL; that = that->next)
1194             fprintf(fp, "\t%s\n", that->path);
1195     }
1196
1197     if (file == NULL) {
1198         apclose(fp);
1199     } else {
1200         printf("Extract list written to file %s\n", file);
1201         afclose(fp);
1202     }
1203 }
1204
1205
1206 /* returns 0 if extract list empty and 1 if it isn't */
1207 int
1208 is_extract_list_nonempty(void)
1209 {
1210     return (extract_list != NULL);
1211 }
1212
1213
1214 /* prints continue prompt and waits for response,
1215    returns 0 if don't, non-0 if do */
1216 static int
1217 okay_to_continue(
1218     int         allow_tape,
1219     int         allow_skip,
1220     int         allow_retry)
1221 {
1222     int ch;
1223     int ret = -1;
1224     char *line = NULL;
1225     char *s;
1226     char *prompt;
1227     int get_tape;
1228
1229     get_tape = 0;
1230     while (ret < 0) {
1231         if (get_tape) {
1232             prompt = "New tape device [?]: ";
1233         } else if (allow_tape && allow_skip) {
1234             prompt = "Continue [?/Y/n/s/t]? ";
1235         } else if (allow_tape && !allow_skip) {
1236             prompt = "Continue [?/Y/n/t]? ";
1237         } else if (allow_retry) {
1238             prompt = "Continue [?/Y/n/r]? ";
1239         } else {
1240             prompt = "Continue [?/Y/n]? ";
1241         }
1242         fputs(prompt, stdout);
1243         fflush(stdout); fflush(stderr);
1244         amfree(line);
1245         if ((line = agets(stdin)) == NULL) {
1246             putchar('\n');
1247             clearerr(stdin);
1248             if (get_tape) {
1249                 get_tape = 0;
1250                 continue;
1251             }
1252             ret = 0;
1253             break;
1254         }
1255         s = line;
1256         while ((ch = *s++) != '\0' && isspace(ch)) {
1257             (void)ch;  /* Quiet empty loop body warning */
1258         }
1259         if (ch == '?') {
1260             if (get_tape) {
1261                 printf("Enter a new device ([host:]device) or \"default\"\n");
1262             } else {
1263                 printf("Enter \"y\"es to continue, \"n\"o to stop");
1264                 if(allow_skip) {
1265                     printf(", \"s\"kip this tape");
1266                 }
1267                 if(allow_retry) {
1268                     printf(" or \"r\"etry this tape");
1269                 }
1270                 if (allow_tape) {
1271                     printf(" or \"t\"ape to change tape drives");
1272                 }
1273                 putchar('\n');
1274             }
1275         } else if (get_tape) {
1276             set_tape(s - 1);
1277             get_tape = 0;
1278         } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1279             ret = 1;
1280         } else if (allow_tape && (ch == 'T' || ch == 't')) {
1281             get_tape = 1;
1282         } else if (ch == 'N' || ch == 'n') {
1283             ret = 0;
1284         } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1285             ret = RETRY_TAPE;
1286         } else if (allow_skip && (ch == 'S' || ch == 's')) {
1287             ret = SKIP_TAPE;
1288         }
1289     }
1290     /*@ignore@*/
1291     amfree(line);
1292     /*@end@*/
1293     return ret;
1294 }
1295
1296 static void
1297 send_to_tape_server(
1298     int         tss,
1299     char *      cmd)
1300 {
1301     char *msg = stralloc2(cmd, "\r\n");
1302
1303     if (fullwrite(tss, msg, strlen(msg)) < 0)
1304     {
1305         error("Error writing to tape server");
1306         /*NOTREACHED*/
1307     }
1308     amfree(msg);
1309 }
1310
1311
1312 /* start up connection to tape server and set commands to initiate
1313    transfer of dump image.
1314    Return tape server socket on success, -1 on error. */
1315 static int
1316 extract_files_setup(
1317     char *      label,
1318     off_t       fsf)
1319 {
1320     struct servent *sp;
1321     in_port_t my_port, my_data_port;
1322     char *disk_regex = NULL;
1323     char *host_regex = NULL;
1324     char *service_name = NULL;
1325     char *line = NULL;
1326     char *clean_datestamp, *ch, *ch1;
1327     char *our_feature_string = NULL;
1328     char *tt = NULL;
1329
1330     service_name = stralloc2("amidxtape", SERVICE_SUFFIX);
1331
1332     /* get tape server details */
1333     if ((sp = getservbyname(service_name, "tcp")) == NULL)
1334     {
1335         printf("%s/tcp unknown protocol - config error?\n", service_name);
1336         amfree(service_name);
1337         return -1;
1338     }
1339     amfree(service_name);
1340     seteuid(0);                                 /* it either works ... */
1341     setegid(0);
1342     tape_control_sock = stream_client_privileged(tape_server_name,
1343                                                   (in_port_t)ntohs((in_port_t)sp->s_port),
1344                                                   0,
1345                                                   STREAM_BUFSIZE,
1346                                                   &my_port,
1347                                                   0);
1348     if (tape_control_sock < 0)
1349     {
1350         printf("cannot connect to %s: %s\n", tape_server_name, strerror(errno));
1351         return -1;
1352     }
1353     if (my_port >= IPPORT_RESERVED) {
1354         aclose(tape_control_sock);
1355         printf("did not get a reserved port: %u\n", (unsigned)my_port);
1356         return -1;
1357     }
1358  
1359     setegid(getgid());
1360     seteuid(getuid());                          /* put it back */
1361
1362     /* do the security thing */
1363     line = get_security();
1364     send_to_tape_server(tape_control_sock, line);
1365     memset(line, '\0', strlen(line));
1366     amfree(line);
1367
1368     disk_regex = alloc(strlen(disk_name) * 2 + 3);
1369
1370     ch = disk_name;
1371     ch1 = disk_regex;
1372
1373     /* we want to force amrestore to only match disk_name exactly */
1374     *(ch1++) = '^';
1375
1376     /* We need to escape some characters first... NT compatibilty crap */
1377     for (; *ch != 0; ch++, ch1++) {
1378         switch (*ch) {     /* done this way in case there are more */
1379         case '$':
1380             *(ch1++) = '\\';
1381             /* no break; we do want to fall through... */
1382         default:
1383             *ch1 = *ch;
1384         }
1385     }
1386
1387     /* we want to force amrestore to only match disk_name exactly */
1388     *(ch1++) = '$';
1389
1390     *ch1 = '\0';
1391
1392     host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1393
1394     ch = dump_hostname;
1395     ch1 = host_regex;
1396
1397     /* we want to force amrestore to only match dump_hostname exactly */
1398     *(ch1++) = '^';
1399
1400     /* We need to escape some characters first... NT compatibilty crap */
1401     for (; *ch != 0; ch++, ch1++) {
1402         switch (*ch) {     /* done this way in case there are more */
1403         case '$':
1404             *(ch1++) = '\\';
1405             /* no break; we do want to fall through... */
1406         default:
1407             *ch1 = *ch;
1408         }
1409     }
1410
1411     /* we want to force amrestore to only match dump_hostname exactly */
1412     *(ch1++) = '$';
1413
1414     *ch1 = '\0';
1415
1416     clean_datestamp = stralloc(dump_datestamp);
1417     for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1418         if(*ch1 != '-') {
1419             *ch = *ch1;
1420             ch++;
1421         }
1422     }
1423     *ch = '\0';
1424
1425     /* push our feature list off to the tape server */
1426     /* XXX assumes that index server and tape server are equivalent, ew */
1427     if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1428         char buffer[32768] = "\0";
1429
1430         our_feature_string = am_feature_to_string(our_features);
1431         tt = newstralloc2(tt, "FEATURES=", our_feature_string);
1432         send_to_tape_server(tape_control_sock, tt);
1433         if (read(tape_control_sock, buffer, sizeof(buffer)) <= 0) {
1434             error("Could not read features from control socket\n");
1435             /*NOTREACHED*/
1436         }
1437         tapesrv_features = am_string_to_feature(buffer);
1438         amfree(our_feature_string);
1439     }
1440
1441
1442     if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1443        am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1444        am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1445        am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1446        am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1447
1448         if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1449             tt = newstralloc2(tt, "CONFIG=", config);
1450             send_to_tape_server(tape_control_sock, tt);
1451         }
1452         if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1453            label && label[0] != '/') {
1454             tt = newstralloc2(tt,"LABEL=",label);
1455             send_to_tape_server(tape_control_sock, tt);
1456         }
1457         if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1458             char v_fsf[100];
1459             snprintf(v_fsf, 99, OFF_T_FMT, (OFF_T_FMT_TYPE)fsf);
1460             tt = newstralloc2(tt, "FSF=",v_fsf);
1461             send_to_tape_server(tape_control_sock, tt);
1462         }
1463         send_to_tape_server(tape_control_sock, "HEADER");
1464         tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1465         send_to_tape_server(tape_control_sock, tt);
1466         tt = newstralloc2(tt, "HOST=", host_regex);
1467         send_to_tape_server(tape_control_sock, tt);
1468         tt = newstralloc2(tt, "DISK=", disk_regex);
1469         send_to_tape_server(tape_control_sock, tt);
1470         tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1471         send_to_tape_server(tape_control_sock, tt);
1472         send_to_tape_server(tape_control_sock, "END");
1473         amfree(tt);
1474     }
1475     else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1476         /* send to the tape server what tape file we want */
1477         /* 6 args:
1478          *   "-h"
1479          *   "-p"
1480          *   "tape device"
1481          *   "hostname"
1482          *   "diskname"
1483          *   "datestamp"
1484          */
1485         send_to_tape_server(tape_control_sock, "6");
1486         send_to_tape_server(tape_control_sock, "-h");
1487         send_to_tape_server(tape_control_sock, "-p");
1488         send_to_tape_server(tape_control_sock, dump_device_name);
1489         send_to_tape_server(tape_control_sock, host_regex);
1490         send_to_tape_server(tape_control_sock, disk_regex);
1491         send_to_tape_server(tape_control_sock, clean_datestamp);
1492
1493         dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n",
1494                   dump_device_name, host_regex, disk_regex, clean_datestamp));
1495     }
1496
1497     /*
1498      * split-restoring amidxtaped versions will expect to set up a data
1499      * connection for dumpfile data, distinct from the socket we're already
1500      * using for control data
1501      */
1502
1503     if(am_has_feature(tapesrv_features, fe_recover_splits)){
1504         char buffer[32768];
1505         in_port_t data_port = (in_port_t)-1;
1506         ssize_t nread;
1507
1508         nread = read(tape_control_sock, buffer, sizeof(buffer));
1509
1510         if (nread <= 0) {
1511             error("Could not read from control socket: %s\n", 
1512                   strerror(errno));
1513             /*NOTREACHED*/
1514         }
1515
1516         buffer[nread] = '\0';
1517         if (sscanf(buffer, "CONNECT %hu\n",
1518                 (unsigned short *)&data_port) != 1) {
1519             error("Recieved invalid port number message from control socket: %s\n",
1520                   buffer);
1521             /*NOTREACHED*/
1522         }       
1523
1524         tape_data_sock = stream_client_privileged(server_name,
1525                                                   data_port,
1526                                                   0,
1527                                                   STREAM_BUFSIZE,
1528                                                   &my_data_port,
1529                                                   0);
1530         if(tape_data_sock == -1){
1531             error("Unable to make data connection to server: %s\n",
1532                       strerror(errno));
1533             /*NOTREACHED*/
1534         }
1535
1536         amfree(our_feature_string);
1537     
1538         line = get_security();
1539
1540         send_to_tape_server(tape_data_sock, line);
1541         memset(line, '\0', strlen(line));
1542         amfree(line);
1543     }
1544
1545     amfree(disk_regex);
1546     amfree(host_regex);
1547     amfree(clean_datestamp);
1548
1549     return tape_control_sock;
1550 }
1551
1552
1553 /*
1554  * Reads the first block of a tape file.
1555  */
1556
1557 void
1558 read_file_header(
1559     char *      buffer,
1560     dumpfile_t *file,
1561     size_t      buflen,
1562     int         tapedev)
1563 {
1564     ssize_t bytes_read;
1565
1566     bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1567     if(bytes_read < 0) {
1568         error("error reading header (%s), check amidxtaped.*.debug on server",
1569               strerror(errno));
1570         /*NOTREACHED*/
1571     }
1572
1573     if((size_t)bytes_read < buflen) {
1574         fprintf(stderr, "%s: short block %d byte%s\n",
1575                 get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s");
1576         print_header(stdout, file);
1577         error("Can't read file header");
1578         /*NOTREACHED*/
1579     }
1580
1581     /* bytes_read == buflen */
1582     parse_file_header(buffer, file, (size_t)bytes_read);
1583 }
1584
1585 enum dumptypes {
1586         IS_UNKNOWN,
1587         IS_DUMP,
1588         IS_GNUTAR,
1589         IS_TAR,
1590         IS_SAMBA,
1591         IS_SAMBA_TAR
1592 };
1593
1594 static void
1595 extract_files_child(
1596     int                 in_fd,
1597     EXTRACT_LIST *      elist)
1598 {
1599     int save_errno;
1600     int extra_params = 0;
1601     int i,j=0;
1602     char **restore_args = NULL;
1603     int files_off_tape;
1604     EXTRACT_LIST_ITEM *fn;
1605     enum dumptypes dumptype = IS_UNKNOWN;
1606     char buffer[DISK_BLOCK_BYTES];
1607     dumpfile_t file;
1608     size_t len_program;
1609     char *cmd = NULL;
1610     int passwd_field = -1;
1611 #ifdef SAMBA_CLIENT
1612     char *domain = NULL, *smbpass = NULL;
1613 #endif
1614
1615     /* code executed by child to do extraction */
1616     /* never returns */
1617
1618     /* make in_fd be our stdin */
1619     if (dup2(in_fd, STDIN_FILENO) == -1)
1620     {
1621         error("dup2 failed in extract_files_child: %s", strerror(errno));
1622         /*NOTREACHED*/
1623     }
1624
1625     /* read the file header */
1626     fh_init(&file);
1627     read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1628
1629     if(file.type != F_DUMPFILE) {
1630         print_header(stdout, &file);
1631         error("bad header");
1632         /*NOTREACHED*/
1633     }
1634
1635     if (file.program != NULL) {
1636 #ifdef GNUTAR
1637         if (strcmp(file.program, GNUTAR) == 0)
1638             dumptype = IS_GNUTAR;
1639 #endif
1640
1641         if (dumptype == IS_UNKNOWN) {
1642             len_program = strlen(file.program);
1643             if(len_program >= 3 &&
1644                strcmp(&file.program[len_program-3],"tar") == 0)
1645                 dumptype = IS_TAR;
1646         }
1647
1648 #ifdef SAMBA_CLIENT
1649         if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1650             if (samba_extract_method == SAMBA_TAR)
1651               dumptype = IS_SAMBA_TAR;
1652             else
1653               dumptype = IS_SAMBA;
1654         }
1655 #endif
1656     }
1657
1658     /* form the arguments to restore */
1659     files_off_tape = length_of_tape_list(elist);
1660     switch (dumptype) {
1661     case IS_SAMBA:
1662 #ifdef SAMBA_CLIENT
1663         extra_params = 10;
1664         break;
1665 #endif
1666     case IS_TAR:
1667     case IS_GNUTAR:
1668         extra_params = 4;
1669         break;
1670     case IS_SAMBA_TAR:
1671         extra_params = 3;
1672         break;
1673     case IS_UNKNOWN:
1674     case IS_DUMP:
1675 #ifdef AIX_BACKUP
1676         extra_params = 2;
1677 #else
1678 #if defined(XFSDUMP)
1679         if (strcmp(file.program, XFSDUMP) == 0) {
1680             extra_params = 4 + files_off_tape;
1681         } else
1682 #endif
1683         {
1684         extra_params = 4;
1685         }
1686 #endif
1687         break;
1688     }
1689
1690     restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
1691                                   * sizeof(char *)));
1692     switch(dumptype) {
1693     case IS_SAMBA:
1694 #ifdef SAMBA_CLIENT
1695         restore_args[j++] = stralloc("smbclient");
1696         smbpass = findpass(file.disk, &domain);
1697         if (smbpass) {
1698             restore_args[j++] = stralloc(file.disk);
1699             passwd_field=j;
1700             restore_args[j++] = stralloc("-U");
1701             restore_args[j++] = smbpass;
1702             if (domain) {
1703                 restore_args[j++] = stralloc("-W");
1704                 restore_args[j++] = stralloc(domain);
1705             } else
1706                 extra_params -= 2;
1707         } else
1708             extra_params -= 6;
1709         restore_args[j++] = stralloc("-d0");
1710         restore_args[j++] = stralloc("-Tx");
1711         restore_args[j++] = stralloc("-");      /* data on stdin */
1712         break;
1713 #endif
1714     case IS_TAR:
1715     case IS_GNUTAR:
1716         restore_args[j++] = stralloc("tar");
1717         restore_args[j++] = stralloc("--numeric-owner");
1718         restore_args[j++] = stralloc("-xpGvf");
1719         restore_args[j++] = stralloc("-");      /* data on stdin */
1720         break;
1721     case IS_SAMBA_TAR:
1722         restore_args[j++] = stralloc("tar");
1723         restore_args[j++] = stralloc("-xpvf");
1724         restore_args[j++] = stralloc("-");      /* data on stdin */
1725         break;
1726     case IS_UNKNOWN:
1727     case IS_DUMP:
1728         restore_args[j++] = stralloc("restore");
1729 #ifdef AIX_BACKUP
1730         restore_args[j++] = stralloc("-xB");
1731 #else
1732 #if defined(XFSDUMP)
1733         if (strcmp(file.program, XFSDUMP) == 0) {
1734             restore_args[j++] = stralloc("-v");
1735             restore_args[j++] = stralloc("silent");
1736         } else
1737 #endif
1738 #if defined(VDUMP)
1739         if (strcmp(file.program, VDUMP) == 0) {
1740             restore_args[j++] = stralloc("xf");
1741             restore_args[j++] = stralloc("-");  /* data on stdin */
1742         } else
1743 #endif
1744         {
1745         restore_args[j++] = stralloc("xbf");
1746         restore_args[j++] = stralloc("2");      /* read in units of 1K */
1747         restore_args[j++] = stralloc("-");      /* data on stdin */
1748         }
1749 #endif
1750     }
1751   
1752     for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1753     {
1754         switch (dumptype) {
1755         case IS_TAR:
1756         case IS_GNUTAR:
1757         case IS_SAMBA_TAR:
1758         case IS_SAMBA:
1759             if (strcmp(fn->path, "/") == 0)
1760                 restore_args[j++] = stralloc(".");
1761             else
1762                 restore_args[j++] = stralloc2(".", fn->path);
1763             break;
1764         case IS_UNKNOWN:
1765         case IS_DUMP:
1766 #if defined(XFSDUMP)
1767             if (strcmp(file.program, XFSDUMP) == 0) {
1768                 /*
1769                  * xfsrestore needs a -s option before each file to be
1770                  * restored, and also wants them to be relative paths.
1771                  */
1772                 restore_args[j++] = stralloc("-s");
1773                 restore_args[j++] = stralloc(fn->path + 1);
1774             } else
1775 #endif
1776             {
1777             restore_args[j++] = stralloc(fn->path);
1778             }
1779         }
1780     }
1781 #if defined(XFSDUMP)
1782     if (strcmp(file.program, XFSDUMP) == 0) {
1783         restore_args[j++] = stralloc("-");
1784         restore_args[j++] = stralloc(".");
1785     }
1786 #endif
1787     restore_args[j] = NULL;
1788
1789     switch (dumptype) {
1790     case IS_SAMBA:
1791 #ifdef SAMBA_CLIENT
1792         cmd = stralloc(SAMBA_CLIENT);
1793         break;
1794 #else
1795         /* fall through to ... */
1796 #endif
1797     case IS_TAR:
1798     case IS_GNUTAR:
1799     case IS_SAMBA_TAR:
1800 #ifndef GNUTAR
1801         fprintf(stderr, "warning: GNUTAR program not available.\n");
1802         cmd = stralloc("tar");
1803 #else
1804         cmd = stralloc(GNUTAR);
1805 #endif
1806         break;
1807     case IS_UNKNOWN:
1808     case IS_DUMP:
1809         cmd = NULL;
1810 #if defined(DUMP)
1811         if (strcmp(file.program, DUMP) == 0) {
1812             cmd = stralloc(RESTORE);
1813         }
1814 #endif
1815 #if defined(VDUMP)
1816         if (strcmp(file.program, VDUMP) == 0) {
1817             cmd = stralloc(VRESTORE);
1818         }
1819 #endif
1820 #if defined(VXDUMP)
1821         if (strcmp(file.program, VXDUMP) == 0) {
1822             cmd = stralloc(VXRESTORE);
1823         }
1824 #endif
1825 #if defined(XFSDUMP)
1826         if (strcmp(file.program, XFSDUMP) == 0) {
1827             cmd = stralloc(XFSRESTORE);
1828         }
1829 #endif
1830         if (cmd == NULL) {
1831             fprintf(stderr, "warning: restore program for %s not available.\n",
1832                     file.program);
1833             cmd = stralloc("restore");
1834         }
1835     }
1836     if (cmd) {
1837         dbprintf(("Exec'ing %s with arguments:\n", cmd));
1838         for (i = 0; i < j; i++) {
1839             if( i == passwd_field)
1840                 dbprintf(("\tXXXXX\n"));
1841             else
1842                 dbprintf(("\t%s\n", restore_args[i]));
1843         }
1844         (void)execv(cmd, restore_args);
1845         /* only get here if exec failed */
1846         save_errno = errno;
1847         for (i = 0; i < j; i++) {
1848             amfree(restore_args[i]);
1849         }
1850         amfree(restore_args);
1851         errno = save_errno;
1852         perror("amrecover couldn't exec");
1853         fprintf(stderr, " problem executing %s\n", cmd);
1854         amfree(cmd);
1855     }
1856     exit(1);
1857     /*NOT REACHED */
1858 }
1859
1860 /*
1861  * Interpose something between the process writing out the dump (writing it to
1862  * some extraction program, really) and the socket from which we're reading, so
1863  * that we can do things like prompt for human interaction for multiple tapes.
1864  */
1865 void
1866 writer_intermediary(
1867     int                 ctl_fd,
1868     int                 data_fd,
1869     EXTRACT_LIST *      elist)
1870 {
1871     int child_pipe[2];
1872     pid_t pid;
1873     char buffer[DISK_BLOCK_BYTES];
1874     ssize_t bytes_read;
1875     amwait_t extractor_status;
1876     int max_fd, nfound;
1877     SELECT_ARG_TYPE readset, selectset;
1878     struct timeval timeout;
1879
1880     /*
1881      * If there's no distinct data channel (such as if we're talking to an
1882      * older server), don't bother doing anything complicated.  Just run the
1883      * extraction.
1884      */
1885     if(data_fd == -1){
1886         extract_files_child(ctl_fd, elist);
1887         /*NOTREACHED*/
1888     }
1889
1890     if(pipe(child_pipe) == -1) {
1891         error("extract_list - error setting up pipe to extractor: %s\n",
1892             strerror(errno));
1893         /*NOTREACHED*/
1894     }
1895
1896     /* okay, ready to extract. fork a child to do the actual work */
1897     if ((pid = fork()) == 0) {
1898         /* this is the child process */
1899         /* never gets out of this clause */
1900         aclose(child_pipe[1]);
1901         extract_files_child(child_pipe[0], elist);
1902         /*NOTREACHED*/
1903     }
1904
1905     /* This is the parent */
1906     if (pid == -1) {
1907         error("writer_intermediary - error forking child");
1908         /*NOTREACHED*/
1909     }
1910
1911     aclose(child_pipe[0]);
1912
1913     if(data_fd > ctl_fd) max_fd = data_fd+1;
1914                     else max_fd = ctl_fd+1;
1915     FD_ZERO(&readset);
1916     FD_SET(data_fd, &readset);
1917     FD_SET(ctl_fd, &readset);
1918
1919     do {
1920         timeout.tv_sec = READ_TIMEOUT;
1921         timeout.tv_usec = 0;
1922         FD_COPY(&readset, &selectset);
1923         
1924         nfound = select(max_fd, &selectset, NULL, NULL,
1925                         &timeout);
1926         if(nfound < 0) {
1927             fprintf(stderr,"select error: %s\n", strerror(errno));
1928             break;
1929         }
1930         
1931         if (nfound == 0) { /* timeout */
1932             fprintf(stderr, "timeout waiting %d seconds for restore\n",
1933                     READ_TIMEOUT);
1934             fprintf(stderr, "increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
1935             break;
1936         }
1937         
1938         if(FD_ISSET(ctl_fd, &selectset)) {
1939             bytes_read = read(ctl_fd, buffer, sizeof(buffer)-1);
1940             switch(bytes_read) {
1941             case -1:
1942                 if ((errno != EINTR) && (errno != EAGAIN)) {
1943                     if (errno != EPIPE) {
1944                         fprintf(stderr,"writer ctl fd read error: %s",
1945                                 strerror(errno));
1946                     }
1947                     FD_CLR(ctl_fd, &readset);
1948                 }
1949                 break;
1950                 
1951             case  0:
1952                 FD_CLR(ctl_fd, &readset);
1953                 break;
1954                 
1955             default: {
1956                 char desired_tape[MAX_TAPE_LABEL_BUF];
1957                 
1958                 buffer[bytes_read] = '\0';
1959                 /* if prompted for a tape, relay said prompt to the user */
1960                 if(sscanf(buffer, "FEEDME %132s\n", desired_tape) == 1) {
1961                     int done = 0;
1962                     while (!done) {
1963                         char *input = NULL;
1964                         printf("Please insert tape %s. Continue? [Y|n]: ",
1965                                desired_tape);
1966                         fflush(stdout);
1967                         
1968                         input = agets(stdin); /* strips \n */
1969                         if (strcasecmp("", input) == 0|| 
1970                             strcasecmp("y", input) == 0|| 
1971                             strcasecmp("yes", input) == 0) {
1972                             send_to_tape_server(tape_control_sock, "OK");
1973                             done = 1;
1974                         } else if (strcasecmp("n", input) == 0|| 
1975                                    strcasecmp("no", input) == 0) {
1976                             send_to_tape_server(tape_control_sock, "ERROR");
1977                             /* Abort!
1978                                We are the middle process, so just die. */
1979                             exit(EXIT_FAILURE);
1980                         }
1981                         amfree(input);
1982                     }
1983                 } else {
1984                     fprintf(stderr, "Strange message from tape server: %s", buffer);
1985                     break;
1986                 }    
1987               }
1988             }
1989         }
1990
1991         /* now read some dump data */
1992         if(FD_ISSET(data_fd, &selectset)) {
1993             bytes_read = read(data_fd, buffer, sizeof(buffer)-1);
1994             switch(bytes_read) {
1995             case -1:
1996                 if ((errno != EINTR) && (errno != EAGAIN)) {
1997                     if (errno != EPIPE) {
1998                         fprintf(stderr,"writer data fd read error: %s",
1999                                 strerror(errno));
2000                     }
2001                     FD_CLR(data_fd, &readset);
2002                 }
2003                 break;
2004                 
2005             case  0:
2006                 FD_CLR(data_fd, &readset);
2007                 break;
2008                 
2009             default:
2010                 /*
2011                  * spit what we got from the server to the child
2012                  *  process handling actual dumpfile extraction
2013                  */
2014                 if(fullwrite(child_pipe[1], buffer, (size_t)bytes_read) < 0) {
2015                     if(errno == EPIPE) {
2016                         error("%s: pipe data reader has quit: %s\n",
2017                               get_pname(), strerror(errno));
2018                         /* NOTREACHED */
2019                     }
2020                     error("Write error to extract child: %s\n",
2021                           strerror(errno));
2022                     /* NOTREACHED */
2023                 }
2024                 break;
2025             }
2026         }
2027     } while(FD_ISSET(ctl_fd, &readset) || FD_ISSET(data_fd, &readset));
2028
2029     aclose(child_pipe[1]);
2030
2031     waitpid(pid, &extractor_status, 0);
2032     if(WEXITSTATUS(extractor_status) != 0){
2033         int ret = WEXITSTATUS(extractor_status);
2034         if(ret == 255) ret = -1;
2035         error("Extractor child exited with status %d\n", ret);
2036         /*NOTREACHED*/
2037     }
2038
2039     exit(0);
2040 }
2041
2042 /* exec restore to do the actual restoration */
2043
2044 /* does the actual extraction of files */
2045 /*
2046  * The original design had the dump image being returned exactly as it
2047  * appears on the tape, and this routine getting from the index server
2048  * whether or not it is compressed, on the assumption that the tape
2049  * server may not know how to uncompress it. But
2050  * - Amrestore can't do that. It returns either compressed or uncompressed
2051  * (always). Amrestore assumes it can uncompress files. It is thus a good
2052  * idea to run the tape server on a machine with gzip.
2053  * - The information about compression in the disklist is really only
2054  * for future dumps. It is possible to change compression on a drive
2055  * so the information in the disklist may not necessarily relate to
2056  * the dump image on the tape.
2057  *   Consequently the design was changed to assuming that amrestore can
2058  * uncompress any dump image and have it return an uncompressed file
2059  * always.
2060  */
2061 void
2062 extract_files(void)
2063 {
2064     EXTRACT_LIST *elist;
2065     pid_t pid;
2066     amwait_t child_stat;
2067     char buf[STR_SIZE];
2068     char *l;
2069     int first;
2070     int otc;
2071     tapelist_t *tlist = NULL;
2072
2073     if (!is_extract_list_nonempty())
2074     {
2075         printf("Extract list empty - No files to extract!\n");
2076         return;
2077     }
2078
2079     clean_extract_list();
2080
2081     /* get tape device name from index server if none specified */
2082     if (tape_server_name == NULL) {
2083         tape_server_name = newstralloc(tape_server_name, server_name);
2084     }
2085     if (tape_device_name == NULL) {
2086         if (send_command("TAPE") == -1)
2087             exit(1);
2088         if (get_reply_line() == -1)
2089             exit(1);
2090         l = reply_line();
2091         if (!server_happy())
2092         {
2093             printf("%s\n", l);
2094             exit(1);
2095         }
2096         /* skip reply number */
2097         tape_device_name = newstralloc(tape_device_name, l+4);
2098     }
2099
2100     if (strcmp(tape_device_name, "/dev/null") == 0)
2101     {
2102         printf("amrecover: warning: using %s as the tape device will not work\n",
2103                tape_device_name);
2104     }
2105
2106     first=1;
2107     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2108         if(elist->tape[0]!='/') {
2109             if(first) {
2110                 printf("\nExtracting files using tape drive %s on host %s.\n",
2111                         tape_device_name, tape_server_name);
2112                 printf("The following tapes are needed:");
2113                 first=0;
2114             }
2115             else
2116                 printf("                               ");
2117             tlist = unmarshal_tapelist_str(elist->tape); 
2118             for( ; tlist != NULL; tlist = tlist->next)
2119                 printf(" %s", tlist->label);
2120             printf("\n");
2121             amfree(tlist);
2122         }
2123     first=1;
2124     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2125     {
2126         if(elist->tape[0]=='/') {
2127             if(first) {
2128                 printf("\nExtracting files from holding disk on host %s.\n",
2129                         tape_server_name);
2130                 printf("The following files are needed:");
2131                 first=0;
2132             }
2133             else
2134                 printf("                               ");
2135             tlist = unmarshal_tapelist_str(elist->tape); 
2136             for( ; tlist != NULL; tlist = tlist->next)
2137                 printf(" %s", tlist->label);
2138             printf("\n");
2139             amfree(tlist);
2140         }
2141     }
2142     printf("\n");
2143
2144     if (getcwd(buf, sizeof(buf)) == NULL) {
2145         perror("extract_list: Current working directory unavailable");
2146         exit(1);
2147     }
2148
2149     printf("Restoring files into directory %s\n", buf);
2150 #ifdef SAMBA_CLIENT
2151     if (samba_extract_method == SAMBA_SMBCLIENT)
2152       printf("(unless it is a Samba backup, that will go through to the SMB server)\n");
2153 #endif
2154     if (!okay_to_continue(0,0,0))
2155         return;
2156     printf("\n");
2157
2158     while ((elist = first_tape_list()) != NULL)
2159     {
2160         if(elist->tape[0]=='/') {
2161             dump_device_name = newstralloc(dump_device_name, elist->tape);
2162             printf("Extracting from file ");
2163             tlist = unmarshal_tapelist_str(dump_device_name); 
2164             for( ; tlist != NULL; tlist = tlist->next)
2165                 printf(" %s", tlist->label);
2166             printf("\n");
2167             amfree(tlist);
2168         }
2169         else {
2170             printf("Extracting files using tape drive %s on host %s.\n",
2171                    tape_device_name, tape_server_name);
2172             tlist = unmarshal_tapelist_str(elist->tape); 
2173             printf("Load tape %s now\n", tlist->label);
2174             amfree(tlist);
2175             otc = okay_to_continue(1,1,0);
2176             if (otc == 0)
2177                 return;
2178             else if (otc == SKIP_TAPE) {
2179                 delete_tape_list(elist); /* skip this tape */
2180                 continue;
2181             }
2182             dump_device_name = newstralloc(dump_device_name, tape_device_name);
2183         }
2184         dump_datestamp = newstralloc(dump_datestamp, elist->date);
2185
2186         /* connect to the tape handler daemon on the tape drive server */
2187         if ((tape_control_sock = extract_files_setup(elist->tape, elist->fileno)) == -1)
2188         {
2189             fprintf(stderr, "amrecover - can't talk to tape server\n");
2190             return;
2191         }
2192
2193         /* okay, ready to extract. fork a child to do the actual work */
2194         if ((pid = fork()) == 0)
2195         {
2196             /* this is the child process */
2197             /* never gets out of this clause */
2198             writer_intermediary(tape_control_sock, tape_data_sock, elist);
2199             /*NOT REACHED*/
2200         }
2201         /* this is the parent */
2202         if (pid == -1)
2203         {
2204             perror("extract_list - error forking child");
2205             aclose(tape_control_sock);
2206             exit(1);
2207         }
2208
2209         /* store the child pid globally so that it can be killed on intr */
2210         extract_restore_child_pid = pid;
2211
2212         /* wait for the child process to finish */
2213         if ((pid = waitpid(-1, &child_stat, 0)) == (pid_t)-1)
2214         {
2215             perror("extract_list - error waiting for child");
2216             exit(1);
2217         }
2218
2219         if(tape_data_sock != -1) {
2220             aclose(tape_data_sock);
2221         }
2222
2223         if (pid == extract_restore_child_pid)
2224         {
2225             extract_restore_child_pid = -1;
2226         }
2227         else
2228         {
2229             fprintf(stderr, "extract list - unknown child terminated?\n");
2230             exit(1);
2231         }
2232         if ((WIFEXITED(child_stat) != 0) && (WEXITSTATUS(child_stat) != 0))
2233         {
2234             fprintf(stderr,
2235                     "extract_list - child returned non-zero status: %d\n",
2236                     WEXITSTATUS(child_stat));
2237             otc = okay_to_continue(0,0,1);
2238             if(otc == 0)
2239                 return;
2240
2241             if(otc == 1) {
2242                 delete_tape_list(elist); /* tape failed so delete from list */
2243             }
2244         }
2245         else {
2246             delete_tape_list(elist);    /* tape done so delete from list */
2247         }
2248     }
2249 }