Imported Upstream version 2.5.1p3
[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.2.1 2006/11/08 17:11:39 martinea 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 #define sc "201-"
706                     if(strncmp(l, sc, sizeof(sc)-1) != 0) {
707                         err = "bad reply: not 201-";
708                         continue;
709                     }
710
711                     s = l + sizeof(sc)-1;
712                     ch = *s++;
713 #undef sc
714                     skip_whitespace(s, ch);
715                     if(ch == '\0') {
716                         err = "bad reply: missing date field";
717                         continue;
718                     }
719                     fp = s-1;
720                     skip_non_whitespace(s, ch);
721                     s[-1] = '\0';
722                     lditem.date = newstralloc(lditem.date, fp);
723                     s[-1] = (char)ch;
724
725                     skip_whitespace(s, ch);
726                     if(ch == '\0' || sscanf(s - 1, "%d", &lditem.level) != 1) {
727                         err = "bad reply: cannot parse level field";
728                         continue;
729                     }
730                     skip_integer(s, ch);
731
732                     skip_whitespace(s, ch);
733                     if(ch == '\0') {
734                         err = "bad reply: missing tape field";
735                         continue;
736                     }
737                     fp = s-1;
738                     skip_non_whitespace(s, ch);
739                     s[-1] = '\0';
740                     lditem.tape = newstralloc(lditem.tape, fp);
741                     s[-1] = (char)ch;
742
743                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
744                         skip_whitespace(s, ch);
745                         if(ch == '\0' || sscanf(s - 1, OFF_T_FMT,
746                                 (OFF_T_FMT_TYPE *)&lditem.fileno) != 1) {
747                             err = "bad reply: cannot parse fileno field";
748                             continue;
749                         }
750                         skip_integer(s, ch);
751                     }
752
753                     skip_whitespace(s, ch);
754                     if(ch == '\0') {
755                         err = "bad reply: missing directory field";
756                         continue;
757                     }
758                     dir = s - 1;
759                     skip_quoted_string(s, ch);
760                     dir_undo = s - 1;
761                     dir_undo_ch = *dir_undo;
762                     *dir_undo = '\0';
763
764                     switch(add_extract_item(&lditem)) {
765                     case -1:
766                         printf("System error\n");
767                         dbprintf(("add_file: (Failed) System error\n"));
768                         break;
769
770                     case  0:
771                         quoted = quote_string(lditem.path);
772                         printf("Added dir %s at date %s\n",
773                                quoted, lditem.date);
774                         dbprintf(("add_file: (Successful) Added dir %s at date %s\n",
775                                   quoted, lditem.date));
776                         amfree(quoted);
777                         added=1;
778                         break;
779
780                     case  1:
781                         break;
782                     }
783                 }
784                 if(!server_happy()) {
785                     puts(reply_line());
786                 } else if(err) {
787                     if (*err)
788                         puts(err);
789                     if (cmd)
790                         puts(cmd);
791                 } else if(added == 0) {
792                     quoted = quote_string(ditem_path);
793                     printf("dir %s already added\n", quoted);
794                     dbprintf(("add_file: dir %s already added\n", quoted));
795                     amfree(quoted);
796                 }
797             }
798             else /* It is a file */
799             {
800                 switch(add_extract_item(ditem)) {
801                 case -1:
802                     printf("System error\n");
803                     dbprintf(("add_file: (Failed) System error\n"));
804                     break;
805
806                 case  0:
807                     quoted = quote_string(ditem->path);
808                     printf("Added file %s\n", quoted);
809                     dbprintf(("add_file: (Successful) Added %s\n", quoted));
810                     amfree(quoted);
811                     break;
812
813                 case  1:
814                     quoted = quote_string(ditem->path);
815                     printf("File %s already added\n", quoted);
816                     dbprintf(("add_file: file %s already added\n", quoted));
817                     amfree(quoted);
818                     break;
819                 }
820             }
821         }
822     }
823     if (cmd != NULL)
824         amfree(cmd);
825     amfree(ditem_path);
826     amfree(path_on_disk);
827     amfree(path_on_disk_slash);
828
829     if(! found_one) {
830         quoted = quote_string(path);
831         printf("File %s doesn't exist in directory\n", quoted);
832         dbprintf(("add_file: (Failed) File %s doesn't exist in directory\n",
833                   quoted));
834         amfree(quoted);
835     }
836 }
837
838
839 void
840 delete_glob(
841     char *      glob)
842 {
843     char *regex;
844     char *regex_path;
845     char *s;
846     char *uqglob = unquote_string(glob);
847
848     regex = glob_to_regex(uqglob);
849     dbprintf(("delete_glob (%s) -> %s\n", uqglob, regex));
850     if ((s = validate_regexp(regex)) != NULL) {
851         printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
852         puts(s);
853     } else {
854         /*
855          * glob_to_regex() anchors the beginning of the pattern with ^,
856          * but we will be tacking it onto the end of the current directory
857          * in add_file, so strip that off.  Also, it anchors the end with
858          * $, but we need to match an optional trailing /, so tack that on
859          * the end.
860          */
861         regex_path = stralloc(regex + 1);
862         regex_path[strlen(regex_path) - 1] = '\0';
863         strappend(regex_path, "[/]*$");
864         delete_file(uqglob, regex_path);
865         amfree(regex_path);
866     }
867     amfree(regex);
868     amfree(uqglob);
869 }
870
871 void
872 delete_regex(
873     char *      regex)
874 {
875     char *s;
876     char *uqregex = unquote_string(regex);
877
878     if ((s = validate_regexp(regex)) != NULL) {
879         printf("\"%s\" is not a valid regular expression: ", regex);
880         puts(s);
881     } else {
882         delete_file(uqregex, uqregex);
883     }
884     amfree(uqregex);
885 }
886
887 void
888 delete_file(
889     char *      path,
890     char *      regex)
891 {
892     DIR_ITEM *ditem, lditem;
893     char *path_on_disk = NULL;
894     char *path_on_disk_slash = NULL;
895     char *cmd = NULL;
896     char *err = NULL;
897     int i;
898     ssize_t j;
899     char *date;
900     char *tape, *tape_undo, tape_undo_ch = '\0';
901     char *dir_undo, dir_undo_ch = '\0';
902     int  level = 0;
903     off_t fileno;
904     char *ditem_path = NULL;
905     char *l = NULL;
906     int  deleted;
907     char *s;
908     int ch;
909     int found_one;
910     char *quoted;
911
912     if (disk_path == NULL) {
913         printf("Must select directory before deleting files\n");
914         return;
915     }
916     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
917
918     dbprintf(("delete_file: Looking for \"%s\"\n", path));
919
920     if (strcmp(regex, "[^/]*[/]*$") == 0) {
921         /* Looking for * find everything but single . */
922         regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
923     } else {
924         /* remove "/" at end of path */
925         j = (ssize_t)(strlen(regex) - 1);
926         while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
927     }
928
929     /* convert path (assumed in cwd) to one on disk */
930     if (strcmp(disk_path, "/") == 0) {
931         if (*regex == '/') {
932             if (strcmp(regex, "/[/]*$") == 0) {
933                 /* We want "/" to match the directory itself: "/." */
934                 path_on_disk = stralloc("/\\.[/]*$");
935             } else {
936                 /* No mods needed if already starts with '/' */
937                 path_on_disk = stralloc(regex);
938             }
939         } else {
940             /* Prepend '/' */
941             path_on_disk = stralloc2("/", regex);
942         }
943     } else {
944         char *clean_disk_path = clean_regex(disk_path);
945         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
946         amfree(clean_disk_path);
947     }
948
949     path_on_disk_slash = stralloc2(path_on_disk, "/");
950
951     dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
952               regex, path_on_disk));
953     found_one = 0;
954     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
955     {
956         quoted = quote_string(ditem->path);
957         dbprintf(("delete_file: Pondering ditem->path=%s\n", quoted));
958         amfree(quoted);
959         if (match(path_on_disk, ditem->path)
960             || match(path_on_disk_slash, ditem->path))
961         {
962             found_one = 1;
963             j = (ssize_t)strlen(ditem->path);
964             if((j > 0 && ditem->path[j-1] == '/')
965                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
966             {   /* It is a directory */
967                 ditem_path = newstralloc(ditem_path, ditem->path);
968                 clean_pathname(ditem_path);
969
970                 cmd = stralloc2("ORLD ", ditem_path);
971                 if(send_command(cmd) == -1) {
972                     amfree(cmd);
973                     amfree(ditem_path);
974                     amfree(path_on_disk);
975                     amfree(path_on_disk_slash);
976                     exit(1);
977                 }
978                 amfree(cmd);
979                 /* skip preamble */
980                 if ((i = get_reply_line()) == -1) {
981                     amfree(ditem_path);
982                     amfree(path_on_disk);
983                     amfree(path_on_disk_slash);
984                     exit(1);
985                 }
986                 if(i==0)                /* assume something wrong */
987                 {
988                     amfree(ditem_path);
989                     amfree(path_on_disk);
990                     amfree(path_on_disk_slash);
991                     l = reply_line();
992                     printf("%s\n", l);
993                     return;
994                 }
995                 deleted=0;
996                 lditem.path = newstralloc(lditem.path, ditem->path);
997                 amfree(cmd);
998                 tape_undo = dir_undo = NULL;
999                 /* skip the last line -- duplicate of the preamble */
1000                 while ((i = get_reply_line()) != 0)
1001                 {
1002                     if (i == -1) {
1003                         amfree(ditem_path);
1004                         amfree(path_on_disk);
1005                         amfree(path_on_disk_slash);
1006                         exit(1);
1007                     }
1008                     if(err) {
1009                         if(cmd == NULL) {
1010                             if(tape_undo) *tape_undo = tape_undo_ch;
1011                             if(dir_undo) *dir_undo = dir_undo_ch;
1012                             tape_undo = dir_undo = NULL;
1013                             cmd = stralloc(l);  /* save for the error report */
1014                         }
1015                         continue;       /* throw the rest of the lines away */
1016                     }
1017                     l=reply_line();
1018                     if (!server_happy()) {
1019                         puts(l);
1020                         continue;
1021                     }
1022 #define sc "201-"
1023                     if(strncmp(l, sc, sizeof(sc)-1) != 0) {
1024                         err = "bad reply: not 201-";
1025                         continue;
1026                     }
1027                     s = l + sizeof(sc)-1;
1028                     ch = *s++;
1029 #undef sc
1030                     skip_whitespace(s, ch);
1031                     if(ch == '\0') {
1032                         err = "bad reply: missing date field";
1033                         continue;
1034                     }
1035                     date = s - 1;
1036                     skip_non_whitespace(s, ch);
1037                     *(s - 1) = '\0';
1038
1039                     skip_whitespace(s, ch);
1040                     if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1041                         err = "bad reply: cannot parse level field";
1042                         continue;
1043                     }
1044                     skip_integer(s, ch);
1045
1046                     skip_whitespace(s, ch);
1047                     if(ch == '\0') {
1048                         err = "bad reply: missing tape field";
1049                         continue;
1050                     }
1051                     tape = s - 1;
1052                     skip_non_whitespace(s, ch);
1053                     tape_undo = s - 1;
1054                     tape_undo_ch = *tape_undo;
1055                     *tape_undo = '\0';
1056
1057                     if(am_has_feature(indexsrv_features, fe_amindexd_fileno_in_ORLD)) {
1058                         skip_whitespace(s, ch);
1059                         if(ch == '\0' || sscanf(s - 1, OFF_T_FMT,
1060                                 (OFF_T_FMT_TYPE *)&fileno) != 1) {
1061                             err = "bad reply: cannot parse fileno field";
1062                             continue;
1063                         }
1064                         skip_integer(s, ch);
1065                     }
1066
1067                     skip_whitespace(s, ch);
1068                     if(ch == '\0') {
1069                         err = "bad reply: missing directory field";
1070                         continue;
1071                     }
1072                     skip_non_whitespace(s, ch);
1073                     dir_undo = s - 1;
1074                     dir_undo_ch = *dir_undo;
1075                     *dir_undo = '\0';
1076
1077                     lditem.date = newstralloc(lditem.date, date);
1078                     lditem.level=level;
1079                     lditem.tape = newstralloc(lditem.tape, tape);
1080                     switch(delete_extract_item(&lditem)) {
1081                     case -1:
1082                         printf("System error\n");
1083                         dbprintf(("delete_file: (Failed) System error\n"));
1084                         break;
1085                     case  0:
1086                         printf("Deleted dir %s at date %s\n", ditem_path, date);
1087                         dbprintf(("delete_file: (Successful) Deleted dir %s at date %s\n",
1088                                   ditem_path, date));
1089                         deleted=1;
1090                         break;
1091                     case  1:
1092                         break;
1093                     }
1094                 }
1095                 if(!server_happy()) {
1096                     puts(reply_line());
1097                 } else if(err) {
1098                     if (*err)
1099                         puts(err);
1100                     if (cmd)
1101                         puts(cmd);
1102                 } else if(deleted == 0) {
1103                     printf("Warning - dir '%s' not on tape list\n",
1104                            ditem_path);
1105                     dbprintf(("delete_file: dir '%s' not on tape list\n",
1106                               ditem_path));
1107                 }
1108             }
1109             else
1110             {
1111                 switch(delete_extract_item(ditem)) {
1112                 case -1:
1113                     printf("System error\n");
1114                     dbprintf(("delete_file: (Failed) System error\n"));
1115                     break;
1116                 case  0:
1117                     printf("Deleted %s\n", ditem->path);
1118                     dbprintf(("delete_file: (Successful) Deleted %s\n",
1119                               ditem->path));
1120                     break;
1121                 case  1:
1122                     printf("Warning - file '%s' not on tape list\n",
1123                            ditem->path);
1124                     dbprintf(("delete_file: file '%s' not on tape list\n",
1125                               ditem->path));
1126                     break;
1127                 }
1128             }
1129         }
1130     }
1131     amfree(cmd);
1132     amfree(ditem_path);
1133     amfree(path_on_disk);
1134     amfree(path_on_disk_slash);
1135
1136     if(! found_one) {
1137         printf("File %s doesn't exist in directory\n", path);
1138         dbprintf(("delete_file: (Failed) File %s doesn't exist in directory\n",
1139                   path));
1140     }
1141 }
1142
1143
1144 /* print extract list into file. If NULL ptr passed print to screen */
1145 void
1146 display_extract_list(
1147     char *      file)
1148 {
1149     EXTRACT_LIST *this;
1150     EXTRACT_LIST_ITEM *that;
1151     FILE *fp;
1152     char *pager;
1153     char *pager_command;
1154     char *uqfile;
1155
1156     if (file == NULL)
1157     {
1158         if ((pager = getenv("PAGER")) == NULL)
1159         {
1160             pager = "more";
1161         }
1162         /*
1163          * Set up the pager command so if the pager is terminated, we do
1164          * not get a SIGPIPE back.
1165          */
1166         pager_command = stralloc2(pager, " ; /bin/cat > /dev/null");
1167         if ((fp = popen(pager_command, "w")) == NULL)
1168         {
1169             printf("Warning - can't pipe through %s\n", pager);
1170             fp = stdout;
1171         }
1172         amfree(pager_command);
1173     }
1174     else
1175     {
1176         uqfile = unquote_string(file);
1177         if ((fp = fopen(uqfile, "w")) == NULL)
1178         {
1179             printf("Can't open file %s to print extract list into\n", file);
1180             amfree(uqfile);
1181             return;
1182         }
1183         amfree(uqfile);
1184     }
1185
1186     for (this = extract_list; this != NULL; this = this->next)
1187     {
1188         fprintf(fp, "TAPE %s LEVEL %d DATE %s\n",
1189                 this->tape, this->level, this->date);
1190         for (that = this->files; that != NULL; that = that->next)
1191             fprintf(fp, "\t%s\n", that->path);
1192     }
1193
1194     if (file == NULL) {
1195         apclose(fp);
1196     } else {
1197         printf("Extract list written to file %s\n", file);
1198         afclose(fp);
1199     }
1200 }
1201
1202
1203 /* returns 0 if extract list empty and 1 if it isn't */
1204 int
1205 is_extract_list_nonempty(void)
1206 {
1207     return (extract_list != NULL);
1208 }
1209
1210
1211 /* prints continue prompt and waits for response,
1212    returns 0 if don't, non-0 if do */
1213 static int
1214 okay_to_continue(
1215     int         allow_tape,
1216     int         allow_skip,
1217     int         allow_retry)
1218 {
1219     int ch;
1220     int ret = -1;
1221     char *line = NULL;
1222     char *s;
1223     char *prompt;
1224     int get_tape;
1225
1226     get_tape = 0;
1227     while (ret < 0) {
1228         if (get_tape) {
1229             prompt = "New tape device [?]: ";
1230         } else if (allow_tape && allow_skip) {
1231             prompt = "Continue [?/Y/n/s/t]? ";
1232         } else if (allow_tape && !allow_skip) {
1233             prompt = "Continue [?/Y/n/t]? ";
1234         } else if (allow_retry) {
1235             prompt = "Continue [?/Y/n/r]? ";
1236         } else {
1237             prompt = "Continue [?/Y/n]? ";
1238         }
1239         fputs(prompt, stdout);
1240         fflush(stdout); fflush(stderr);
1241         amfree(line);
1242         if ((line = agets(stdin)) == NULL) {
1243             putchar('\n');
1244             clearerr(stdin);
1245             if (get_tape) {
1246                 get_tape = 0;
1247                 continue;
1248             }
1249             ret = 0;
1250             break;
1251         }
1252         s = line;
1253         while ((ch = *s++) != '\0' && isspace(ch)) {
1254             (void)ch;  /* Quiet empty loop body warning */
1255         }
1256         if (ch == '?') {
1257             if (get_tape) {
1258                 printf("Enter a new device ([host:]device) or \"default\"\n");
1259             } else {
1260                 printf("Enter \"y\"es to continue, \"n\"o to stop");
1261                 if(allow_skip) {
1262                     printf(", \"s\"kip this tape");
1263                 }
1264                 if(allow_retry) {
1265                     printf(" or \"r\"etry this tape");
1266                 }
1267                 if (allow_tape) {
1268                     printf(" or \"t\"ape to change tape drives");
1269                 }
1270                 putchar('\n');
1271             }
1272         } else if (get_tape) {
1273             set_tape(s - 1);
1274             get_tape = 0;
1275         } else if (ch == '\0' || ch == 'Y' || ch == 'y') {
1276             ret = 1;
1277         } else if (allow_tape && (ch == 'T' || ch == 't')) {
1278             get_tape = 1;
1279         } else if (ch == 'N' || ch == 'n') {
1280             ret = 0;
1281         } else if (allow_retry && (ch == 'R' || ch == 'r')) {
1282             ret = RETRY_TAPE;
1283         } else if (allow_skip && (ch == 'S' || ch == 's')) {
1284             ret = SKIP_TAPE;
1285         }
1286     }
1287     /*@ignore@*/
1288     amfree(line);
1289     /*@end@*/
1290     return ret;
1291 }
1292
1293 static void
1294 send_to_tape_server(
1295     int         tss,
1296     char *      cmd)
1297 {
1298     char *msg = stralloc2(cmd, "\r\n");
1299
1300     if (fullwrite(tss, msg, strlen(msg)) < 0)
1301     {
1302         error("Error writing to tape server");
1303         /*NOTREACHED*/
1304     }
1305     amfree(msg);
1306 }
1307
1308
1309 /* start up connection to tape server and set commands to initiate
1310    transfer of dump image.
1311    Return tape server socket on success, -1 on error. */
1312 static int
1313 extract_files_setup(
1314     char *      label,
1315     off_t       fsf)
1316 {
1317     struct servent *sp;
1318     in_port_t my_port, my_data_port;
1319     char *disk_regex = NULL;
1320     char *host_regex = NULL;
1321     char *service_name = NULL;
1322     char *line = NULL;
1323     char *clean_datestamp, *ch, *ch1;
1324     char *our_feature_string = NULL;
1325     char *tt = NULL;
1326
1327     service_name = stralloc2("amidxtape", SERVICE_SUFFIX);
1328
1329     /* get tape server details */
1330     if ((sp = getservbyname(service_name, "tcp")) == NULL)
1331     {
1332         printf("%s/tcp unknown protocol - config error?\n", service_name);
1333         amfree(service_name);
1334         return -1;
1335     }
1336     amfree(service_name);
1337     seteuid(0);                                 /* it either works ... */
1338     setegid(0);
1339     tape_control_sock = stream_client_privileged(tape_server_name,
1340                                                   (in_port_t)ntohs((in_port_t)sp->s_port),
1341                                                   0,
1342                                                   STREAM_BUFSIZE,
1343                                                   &my_port,
1344                                                   0);
1345     if (tape_control_sock < 0)
1346     {
1347         printf("cannot connect to %s: %s\n", tape_server_name, strerror(errno));
1348         return -1;
1349     }
1350     if (my_port >= IPPORT_RESERVED) {
1351         aclose(tape_control_sock);
1352         printf("did not get a reserved port: %u\n", (unsigned)my_port);
1353         return -1;
1354     }
1355  
1356     setegid(getgid());
1357     seteuid(getuid());                          /* put it back */
1358
1359     /* do the security thing */
1360     line = get_security();
1361     send_to_tape_server(tape_control_sock, line);
1362     memset(line, '\0', strlen(line));
1363     amfree(line);
1364
1365     disk_regex = alloc(strlen(disk_name) * 2 + 3);
1366
1367     ch = disk_name;
1368     ch1 = disk_regex;
1369
1370     /* we want to force amrestore to only match disk_name exactly */
1371     *(ch1++) = '^';
1372
1373     /* We need to escape some characters first... NT compatibilty crap */
1374     for (; *ch != 0; ch++, ch1++) {
1375         switch (*ch) {     /* done this way in case there are more */
1376         case '$':
1377             *(ch1++) = '\\';
1378             /* no break; we do want to fall through... */
1379         default:
1380             *ch1 = *ch;
1381         }
1382     }
1383
1384     /* we want to force amrestore to only match disk_name exactly */
1385     *(ch1++) = '$';
1386
1387     *ch1 = '\0';
1388
1389     host_regex = alloc(strlen(dump_hostname) * 2 + 3);
1390
1391     ch = dump_hostname;
1392     ch1 = host_regex;
1393
1394     /* we want to force amrestore to only match dump_hostname exactly */
1395     *(ch1++) = '^';
1396
1397     /* We need to escape some characters first... NT compatibilty crap */
1398     for (; *ch != 0; ch++, ch1++) {
1399         switch (*ch) {     /* done this way in case there are more */
1400         case '$':
1401             *(ch1++) = '\\';
1402             /* no break; we do want to fall through... */
1403         default:
1404             *ch1 = *ch;
1405         }
1406     }
1407
1408     /* we want to force amrestore to only match dump_hostname exactly */
1409     *(ch1++) = '$';
1410
1411     *ch1 = '\0';
1412
1413     clean_datestamp = stralloc(dump_datestamp);
1414     for(ch=ch1=clean_datestamp;*ch1 != '\0';ch1++) {
1415         if(*ch1 != '-') {
1416             *ch = *ch1;
1417             ch++;
1418         }
1419     }
1420     *ch = '\0';
1421
1422     /* push our feature list off to the tape server */
1423     /* XXX assumes that index server and tape server are equivalent, ew */
1424     if(am_has_feature(indexsrv_features, fe_amidxtaped_exchange_features)){
1425         char buffer[32768] = "\0";
1426
1427         our_feature_string = am_feature_to_string(our_features);
1428         tt = newstralloc2(tt, "FEATURES=", our_feature_string);
1429         send_to_tape_server(tape_control_sock, tt);
1430         if (read(tape_control_sock, buffer, sizeof(buffer)) <= 0) {
1431             error("Could not read features from control socket\n");
1432             /*NOTREACHED*/
1433         }
1434         tapesrv_features = am_string_to_feature(buffer);
1435         amfree(our_feature_string);
1436     }
1437
1438
1439     if(am_has_feature(indexsrv_features, fe_amidxtaped_header) &&
1440        am_has_feature(indexsrv_features, fe_amidxtaped_device) &&
1441        am_has_feature(indexsrv_features, fe_amidxtaped_host) &&
1442        am_has_feature(indexsrv_features, fe_amidxtaped_disk) &&
1443        am_has_feature(indexsrv_features, fe_amidxtaped_datestamp)) {
1444
1445         if(am_has_feature(indexsrv_features, fe_amidxtaped_config)) {
1446             tt = newstralloc2(tt, "CONFIG=", config);
1447             send_to_tape_server(tape_control_sock, tt);
1448         }
1449         if(am_has_feature(indexsrv_features, fe_amidxtaped_label) &&
1450            label && label[0] != '/') {
1451             tt = newstralloc2(tt,"LABEL=",label);
1452             send_to_tape_server(tape_control_sock, tt);
1453         }
1454         if(am_has_feature(indexsrv_features, fe_amidxtaped_fsf)) {
1455             char v_fsf[100];
1456             snprintf(v_fsf, 99, OFF_T_FMT, (OFF_T_FMT_TYPE)fsf);
1457             tt = newstralloc2(tt, "FSF=",v_fsf);
1458             send_to_tape_server(tape_control_sock, tt);
1459         }
1460         send_to_tape_server(tape_control_sock, "HEADER");
1461         tt = newstralloc2(tt, "DEVICE=", dump_device_name);
1462         send_to_tape_server(tape_control_sock, tt);
1463         tt = newstralloc2(tt, "HOST=", host_regex);
1464         send_to_tape_server(tape_control_sock, tt);
1465         tt = newstralloc2(tt, "DISK=", disk_regex);
1466         send_to_tape_server(tape_control_sock, tt);
1467         tt = newstralloc2(tt, "DATESTAMP=", clean_datestamp);
1468         send_to_tape_server(tape_control_sock, tt);
1469         send_to_tape_server(tape_control_sock, "END");
1470         amfree(tt);
1471     }
1472     else if(am_has_feature(indexsrv_features, fe_amidxtaped_nargs)) {
1473         /* send to the tape server what tape file we want */
1474         /* 6 args:
1475          *   "-h"
1476          *   "-p"
1477          *   "tape device"
1478          *   "hostname"
1479          *   "diskname"
1480          *   "datestamp"
1481          */
1482         send_to_tape_server(tape_control_sock, "6");
1483         send_to_tape_server(tape_control_sock, "-h");
1484         send_to_tape_server(tape_control_sock, "-p");
1485         send_to_tape_server(tape_control_sock, dump_device_name);
1486         send_to_tape_server(tape_control_sock, host_regex);
1487         send_to_tape_server(tape_control_sock, disk_regex);
1488         send_to_tape_server(tape_control_sock, clean_datestamp);
1489
1490         dbprintf(("Started amidxtaped with arguments \"6 -h -p %s %s %s %s\"\n",
1491                   dump_device_name, host_regex, disk_regex, clean_datestamp));
1492     }
1493
1494     /*
1495      * split-restoring amidxtaped versions will expect to set up a data
1496      * connection for dumpfile data, distinct from the socket we're already
1497      * using for control data
1498      */
1499
1500     if(am_has_feature(tapesrv_features, fe_recover_splits)){
1501         char buffer[32768];
1502         in_port_t data_port = (in_port_t)-1;
1503         ssize_t nread;
1504
1505         nread = read(tape_control_sock, buffer, sizeof(buffer));
1506
1507         if (nread <= 0) {
1508             error("Could not read from control socket: %s\n", 
1509                   strerror(errno));
1510             /*NOTREACHED*/
1511         }
1512
1513         buffer[nread] = '\0';
1514         if (sscanf(buffer, "CONNECT %hu\n",
1515                 (unsigned short *)&data_port) != 1) {
1516             error("Recieved invalid port number message from control socket: %s\n",
1517                   buffer);
1518             /*NOTREACHED*/
1519         }       
1520
1521         tape_data_sock = stream_client_privileged(server_name,
1522                                                   data_port,
1523                                                   0,
1524                                                   STREAM_BUFSIZE,
1525                                                   &my_data_port,
1526                                                   0);
1527         if(tape_data_sock == -1){
1528             error("Unable to make data connection to server: %s\n",
1529                       strerror(errno));
1530             /*NOTREACHED*/
1531         }
1532
1533         amfree(our_feature_string);
1534     
1535         line = get_security();
1536
1537         send_to_tape_server(tape_data_sock, line);
1538         memset(line, '\0', strlen(line));
1539         amfree(line);
1540     }
1541
1542     amfree(disk_regex);
1543     amfree(host_regex);
1544     amfree(clean_datestamp);
1545
1546     return tape_control_sock;
1547 }
1548
1549
1550 /*
1551  * Reads the first block of a tape file.
1552  */
1553
1554 void
1555 read_file_header(
1556     char *      buffer,
1557     dumpfile_t *file,
1558     size_t      buflen,
1559     int         tapedev)
1560 {
1561     ssize_t bytes_read;
1562
1563     bytes_read = read_buffer(tapedev, buffer, buflen, READ_TIMEOUT);
1564     if(bytes_read < 0) {
1565         error("error reading header (%s), check amidxtaped.*.debug on server",
1566               strerror(errno));
1567         /*NOTREACHED*/
1568     }
1569
1570     if((size_t)bytes_read < buflen) {
1571         fprintf(stderr, "%s: short block %d byte%s\n",
1572                 get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s");
1573         print_header(stdout, file);
1574         error("Can't read file header");
1575         /*NOTREACHED*/
1576     }
1577
1578     /* bytes_read == buflen */
1579     parse_file_header(buffer, file, (size_t)bytes_read);
1580 }
1581
1582 enum dumptypes {
1583         IS_UNKNOWN,
1584         IS_DUMP,
1585         IS_GNUTAR,
1586         IS_TAR,
1587         IS_SAMBA,
1588         IS_SAMBA_TAR
1589 };
1590
1591 static void
1592 extract_files_child(
1593     int                 in_fd,
1594     EXTRACT_LIST *      elist)
1595 {
1596     int save_errno;
1597     int extra_params = 0;
1598     int i,j=0;
1599     char **restore_args = NULL;
1600     int files_off_tape;
1601     EXTRACT_LIST_ITEM *fn;
1602     enum dumptypes dumptype = IS_UNKNOWN;
1603     char buffer[DISK_BLOCK_BYTES];
1604     dumpfile_t file;
1605     size_t len_program;
1606     char *cmd = NULL;
1607     int passwd_field = -1;
1608 #ifdef SAMBA_CLIENT
1609     char *domain = NULL, *smbpass = NULL;
1610 #endif
1611
1612     /* code executed by child to do extraction */
1613     /* never returns */
1614
1615     /* make in_fd be our stdin */
1616     if (dup2(in_fd, STDIN_FILENO) == -1)
1617     {
1618         error("dup2 failed in extract_files_child: %s", strerror(errno));
1619         /*NOTREACHED*/
1620     }
1621
1622     /* read the file header */
1623     fh_init(&file);
1624     read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1625
1626     if(file.type != F_DUMPFILE) {
1627         print_header(stdout, &file);
1628         error("bad header");
1629         /*NOTREACHED*/
1630     }
1631
1632     if (file.program != NULL) {
1633 #ifdef GNUTAR
1634         if (strcmp(file.program, GNUTAR) == 0)
1635             dumptype = IS_GNUTAR;
1636 #endif
1637
1638         if (dumptype == IS_UNKNOWN) {
1639             len_program = strlen(file.program);
1640             if(len_program >= 3 &&
1641                strcmp(&file.program[len_program-3],"tar") == 0)
1642                 dumptype = IS_TAR;
1643         }
1644
1645 #ifdef SAMBA_CLIENT
1646         if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1647             if (samba_extract_method == SAMBA_TAR)
1648               dumptype = IS_SAMBA_TAR;
1649             else
1650               dumptype = IS_SAMBA;
1651         }
1652 #endif
1653     }
1654
1655     /* form the arguments to restore */
1656     files_off_tape = length_of_tape_list(elist);
1657     switch (dumptype) {
1658     case IS_SAMBA:
1659 #ifdef SAMBA_CLIENT
1660         extra_params = 10;
1661         break;
1662 #endif
1663     case IS_TAR:
1664     case IS_GNUTAR:
1665         extra_params = 4;
1666         break;
1667     case IS_SAMBA_TAR:
1668         extra_params = 3;
1669         break;
1670     case IS_UNKNOWN:
1671     case IS_DUMP:
1672 #ifdef AIX_BACKUP
1673         extra_params = 2;
1674 #else
1675 #if defined(XFSDUMP)
1676         if (strcmp(file.program, XFSDUMP) == 0) {
1677             extra_params = 4 + files_off_tape;
1678         } else
1679 #endif
1680         {
1681         extra_params = 4;
1682         }
1683 #endif
1684         break;
1685     }
1686
1687     restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
1688                                   * sizeof(char *)));
1689     switch(dumptype) {
1690     case IS_SAMBA:
1691 #ifdef SAMBA_CLIENT
1692         restore_args[j++] = stralloc("smbclient");
1693         smbpass = findpass(file.disk, &domain);
1694         if (smbpass) {
1695             restore_args[j++] = stralloc(file.disk);
1696             passwd_field=j;
1697             restore_args[j++] = stralloc("-U");
1698             restore_args[j++] = smbpass;
1699             if (domain) {
1700                 restore_args[j++] = stralloc("-W");
1701                 restore_args[j++] = stralloc(domain);
1702             } else
1703                 extra_params -= 2;
1704         } else
1705             extra_params -= 6;
1706         restore_args[j++] = stralloc("-d0");
1707         restore_args[j++] = stralloc("-Tx");
1708         restore_args[j++] = stralloc("-");      /* data on stdin */
1709         break;
1710 #endif
1711     case IS_TAR:
1712     case IS_GNUTAR:
1713         restore_args[j++] = stralloc("tar");
1714         restore_args[j++] = stralloc("--numeric-owner");
1715         restore_args[j++] = stralloc("-xpGvf");
1716         restore_args[j++] = stralloc("-");      /* data on stdin */
1717         break;
1718     case IS_SAMBA_TAR:
1719         restore_args[j++] = stralloc("tar");
1720         restore_args[j++] = stralloc("-xpvf");
1721         restore_args[j++] = stralloc("-");      /* data on stdin */
1722         break;
1723     case IS_UNKNOWN:
1724     case IS_DUMP:
1725         restore_args[j++] = stralloc("restore");
1726 #ifdef AIX_BACKUP
1727         restore_args[j++] = stralloc("-xB");
1728 #else
1729 #if defined(XFSDUMP)
1730         if (strcmp(file.program, XFSDUMP) == 0) {
1731             restore_args[j++] = stralloc("-v");
1732             restore_args[j++] = stralloc("silent");
1733         } else
1734 #endif
1735 #if defined(VDUMP)
1736         if (strcmp(file.program, VDUMP) == 0) {
1737             restore_args[j++] = stralloc("xf");
1738             restore_args[j++] = stralloc("-");  /* data on stdin */
1739         } else
1740 #endif
1741         {
1742         restore_args[j++] = stralloc("xbf");
1743         restore_args[j++] = stralloc("2");      /* read in units of 1K */
1744         restore_args[j++] = stralloc("-");      /* data on stdin */
1745         }
1746 #endif
1747     }
1748   
1749     for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1750     {
1751         switch (dumptype) {
1752         case IS_TAR:
1753         case IS_GNUTAR:
1754         case IS_SAMBA_TAR:
1755         case IS_SAMBA:
1756             if (strcmp(fn->path, "/") == 0)
1757                 restore_args[j++] = stralloc(".");
1758             else
1759                 restore_args[j++] = stralloc2(".", fn->path);
1760             break;
1761         case IS_UNKNOWN:
1762         case IS_DUMP:
1763 #if defined(XFSDUMP)
1764             if (strcmp(file.program, XFSDUMP) == 0) {
1765                 /*
1766                  * xfsrestore needs a -s option before each file to be
1767                  * restored, and also wants them to be relative paths.
1768                  */
1769                 restore_args[j++] = stralloc("-s");
1770                 restore_args[j++] = stralloc(fn->path + 1);
1771             } else
1772 #endif
1773             {
1774             restore_args[j++] = stralloc(fn->path);
1775             }
1776         }
1777     }
1778 #if defined(XFSDUMP)
1779     if (strcmp(file.program, XFSDUMP) == 0) {
1780         restore_args[j++] = stralloc("-");
1781         restore_args[j++] = stralloc(".");
1782     }
1783 #endif
1784     restore_args[j] = NULL;
1785
1786     switch (dumptype) {
1787     case IS_SAMBA:
1788 #ifdef SAMBA_CLIENT
1789         cmd = stralloc(SAMBA_CLIENT);
1790         break;
1791 #else
1792         /* fall through to ... */
1793 #endif
1794     case IS_TAR:
1795     case IS_GNUTAR:
1796     case IS_SAMBA_TAR:
1797 #ifndef GNUTAR
1798         fprintf(stderr, "warning: GNUTAR program not available.\n");
1799         cmd = stralloc("tar");
1800 #else
1801         cmd = stralloc(GNUTAR);
1802 #endif
1803         break;
1804     case IS_UNKNOWN:
1805     case IS_DUMP:
1806         cmd = NULL;
1807 #if defined(DUMP)
1808         if (strcmp(file.program, DUMP) == 0) {
1809             cmd = stralloc(RESTORE);
1810         }
1811 #endif
1812 #if defined(VDUMP)
1813         if (strcmp(file.program, VDUMP) == 0) {
1814             cmd = stralloc(VRESTORE);
1815         }
1816 #endif
1817 #if defined(VXDUMP)
1818         if (strcmp(file.program, VXDUMP) == 0) {
1819             cmd = stralloc(VXRESTORE);
1820         }
1821 #endif
1822 #if defined(XFSDUMP)
1823         if (strcmp(file.program, XFSDUMP) == 0) {
1824             cmd = stralloc(XFSRESTORE);
1825         }
1826 #endif
1827         if (cmd == NULL) {
1828             fprintf(stderr, "warning: restore program for %s not available.\n",
1829                     file.program);
1830             cmd = stralloc("restore");
1831         }
1832     }
1833     if (cmd) {
1834         dbprintf(("Exec'ing %s with arguments:\n", cmd));
1835         for (i = 0; i < j; i++) {
1836             if( i == passwd_field)
1837                 dbprintf(("\tXXXXX\n"));
1838             else
1839                 dbprintf(("\t%s\n", restore_args[i]));
1840         }
1841         (void)execv(cmd, restore_args);
1842         /* only get here if exec failed */
1843         save_errno = errno;
1844         for (i = 0; i < j; i++) {
1845             amfree(restore_args[i]);
1846         }
1847         amfree(restore_args);
1848         errno = save_errno;
1849         perror("amrecover couldn't exec");
1850         fprintf(stderr, " problem executing %s\n", cmd);
1851         amfree(cmd);
1852     }
1853     exit(1);
1854     /*NOT REACHED */
1855 }
1856
1857 /*
1858  * Interpose something between the process writing out the dump (writing it to
1859  * some extraction program, really) and the socket from which we're reading, so
1860  * that we can do things like prompt for human interaction for multiple tapes.
1861  */
1862 void
1863 writer_intermediary(
1864     int                 ctl_fd,
1865     int                 data_fd,
1866     EXTRACT_LIST *      elist)
1867 {
1868     int child_pipe[2];
1869     pid_t pid;
1870     char buffer[DISK_BLOCK_BYTES];
1871     ssize_t bytes_read;
1872     amwait_t extractor_status;
1873     int max_fd, nfound;
1874     SELECT_ARG_TYPE readset, selectset;
1875     struct timeval timeout;
1876
1877     /*
1878      * If there's no distinct data channel (such as if we're talking to an
1879      * older server), don't bother doing anything complicated.  Just run the
1880      * extraction.
1881      */
1882     if(data_fd == -1){
1883         extract_files_child(ctl_fd, elist);
1884         /*NOTREACHED*/
1885     }
1886
1887     if(pipe(child_pipe) == -1) {
1888         error("extract_list - error setting up pipe to extractor: %s\n",
1889             strerror(errno));
1890         /*NOTREACHED*/
1891     }
1892
1893     /* okay, ready to extract. fork a child to do the actual work */
1894     if ((pid = fork()) == 0) {
1895         /* this is the child process */
1896         /* never gets out of this clause */
1897         aclose(child_pipe[1]);
1898         extract_files_child(child_pipe[0], elist);
1899         /*NOTREACHED*/
1900     }
1901
1902     /* This is the parent */
1903     if (pid == -1) {
1904         error("writer_intermediary - error forking child");
1905         /*NOTREACHED*/
1906     }
1907
1908     aclose(child_pipe[0]);
1909
1910     if(data_fd > ctl_fd) max_fd = data_fd+1;
1911                     else max_fd = ctl_fd+1;
1912     FD_ZERO(&readset);
1913     FD_SET(data_fd, &readset);
1914     FD_SET(ctl_fd, &readset);
1915
1916     do {
1917         timeout.tv_sec = READ_TIMEOUT;
1918         timeout.tv_usec = 0;
1919         FD_COPY(&readset, &selectset);
1920         
1921         nfound = select(max_fd, &selectset, NULL, NULL,
1922                         &timeout);
1923         if(nfound < 0) {
1924             fprintf(stderr,"select error: %s\n", strerror(errno));
1925             break;
1926         }
1927         
1928         if (nfound == 0) { /* timeout */
1929             fprintf(stderr, "timeout waiting %d seconds for restore\n",
1930                     READ_TIMEOUT);
1931             fprintf(stderr, "increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
1932             break;
1933         }
1934         
1935         if(FD_ISSET(ctl_fd, &selectset)) {
1936             bytes_read = read(ctl_fd, buffer, sizeof(buffer)-1);
1937             switch(bytes_read) {
1938             case -1:
1939                 if ((errno != EINTR) && (errno != EAGAIN)) {
1940                     if (errno != EPIPE) {
1941                         fprintf(stderr,"writer ctl fd read error: %s",
1942                                 strerror(errno));
1943                     }
1944                     FD_CLR(ctl_fd, &readset);
1945                 }
1946                 break;
1947                 
1948             case  0:
1949                 FD_CLR(ctl_fd, &readset);
1950                 break;
1951                 
1952             default: {
1953                 char desired_tape[MAX_TAPE_LABEL_BUF];
1954                 
1955                 buffer[bytes_read] = '\0';
1956                 /* if prompted for a tape, relay said prompt to the user */
1957                 if(sscanf(buffer, "FEEDME %132s\n", desired_tape) == 1) {
1958                     int done = 0;
1959                     while (!done) {
1960                         char *input = NULL;
1961                         printf("Please insert tape %s. Continue? [Y|n]: ",
1962                                desired_tape);
1963                         fflush(stdout);
1964                         
1965                         input = agets(stdin); /* strips \n */
1966                         if (strcasecmp("", input) == 0|| 
1967                             strcasecmp("y", input) == 0|| 
1968                             strcasecmp("yes", input) == 0) {
1969                             send_to_tape_server(tape_control_sock, "OK");
1970                             done = 1;
1971                         } else if (strcasecmp("n", input) == 0|| 
1972                                    strcasecmp("no", input) == 0) {
1973                             send_to_tape_server(tape_control_sock, "ERROR");
1974                             /* Abort!
1975                                We are the middle process, so just die. */
1976                             exit(EXIT_FAILURE);
1977                         }
1978                         amfree(input);
1979                     }
1980                 } else {
1981                     fprintf(stderr, "Strange message from tape server: %s", buffer);
1982                     break;
1983                 }    
1984               }
1985             }
1986         }
1987
1988         /* now read some dump data */
1989         if(FD_ISSET(data_fd, &selectset)) {
1990             bytes_read = read(data_fd, buffer, sizeof(buffer)-1);
1991             switch(bytes_read) {
1992             case -1:
1993                 if ((errno != EINTR) && (errno != EAGAIN)) {
1994                     if (errno != EPIPE) {
1995                         fprintf(stderr,"writer data fd read error: %s",
1996                                 strerror(errno));
1997                     }
1998                     FD_CLR(data_fd, &readset);
1999                 }
2000                 break;
2001                 
2002             case  0:
2003                 FD_CLR(data_fd, &readset);
2004                 break;
2005                 
2006             default:
2007                 /*
2008                  * spit what we got from the server to the child
2009                  *  process handling actual dumpfile extraction
2010                  */
2011                 if(fullwrite(child_pipe[1], buffer, (size_t)bytes_read) < 0) {
2012                     if(errno == EPIPE) {
2013                         error("%s: pipe data reader has quit: %s\n",
2014                               get_pname(), strerror(errno));
2015                         /* NOTREACHED */
2016                     }
2017                     error("Write error to extract child: %s\n",
2018                           strerror(errno));
2019                     /* NOTREACHED */
2020                 }
2021                 break;
2022             }
2023         }
2024     } while(FD_ISSET(ctl_fd, &readset) || FD_ISSET(data_fd, &readset));
2025
2026     aclose(child_pipe[1]);
2027
2028     waitpid(pid, &extractor_status, 0);
2029     if(WEXITSTATUS(extractor_status) != 0){
2030         int ret = WEXITSTATUS(extractor_status);
2031         if(ret == 255) ret = -1;
2032         error("Extractor child exited with status %d\n", ret);
2033         /*NOTREACHED*/
2034     }
2035
2036     exit(0);
2037 }
2038
2039 /* exec restore to do the actual restoration */
2040
2041 /* does the actual extraction of files */
2042 /*
2043  * The original design had the dump image being returned exactly as it
2044  * appears on the tape, and this routine getting from the index server
2045  * whether or not it is compressed, on the assumption that the tape
2046  * server may not know how to uncompress it. But
2047  * - Amrestore can't do that. It returns either compressed or uncompressed
2048  * (always). Amrestore assumes it can uncompress files. It is thus a good
2049  * idea to run the tape server on a machine with gzip.
2050  * - The information about compression in the disklist is really only
2051  * for future dumps. It is possible to change compression on a drive
2052  * so the information in the disklist may not necessarily relate to
2053  * the dump image on the tape.
2054  *   Consequently the design was changed to assuming that amrestore can
2055  * uncompress any dump image and have it return an uncompressed file
2056  * always.
2057  */
2058 void
2059 extract_files(void)
2060 {
2061     EXTRACT_LIST *elist;
2062     pid_t pid;
2063     amwait_t child_stat;
2064     char buf[STR_SIZE];
2065     char *l;
2066     int first;
2067     int otc;
2068     tapelist_t *tlist = NULL;
2069
2070     if (!is_extract_list_nonempty())
2071     {
2072         printf("Extract list empty - No files to extract!\n");
2073         return;
2074     }
2075
2076     clean_extract_list();
2077
2078     /* get tape device name from index server if none specified */
2079     if (tape_server_name == NULL) {
2080         tape_server_name = newstralloc(tape_server_name, server_name);
2081     }
2082     if (tape_device_name == NULL) {
2083         if (send_command("TAPE") == -1)
2084             exit(1);
2085         if (get_reply_line() == -1)
2086             exit(1);
2087         l = reply_line();
2088         if (!server_happy())
2089         {
2090             printf("%s\n", l);
2091             exit(1);
2092         }
2093         /* skip reply number */
2094         tape_device_name = newstralloc(tape_device_name, l+4);
2095     }
2096
2097     if (strcmp(tape_device_name, "/dev/null") == 0)
2098     {
2099         printf("amrecover: warning: using %s as the tape device will not work\n",
2100                tape_device_name);
2101     }
2102
2103     first=1;
2104     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2105         if(elist->tape[0]!='/') {
2106             if(first) {
2107                 printf("\nExtracting files using tape drive %s on host %s.\n",
2108                         tape_device_name, tape_server_name);
2109                 printf("The following tapes are needed:");
2110                 first=0;
2111             }
2112             else
2113                 printf("                               ");
2114             tlist = unmarshal_tapelist_str(elist->tape); 
2115             for( ; tlist != NULL; tlist = tlist->next)
2116                 printf(" %s", tlist->label);
2117             printf("\n");
2118             amfree(tlist);
2119         }
2120     first=1;
2121     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2122     {
2123         if(elist->tape[0]=='/') {
2124             if(first) {
2125                 printf("\nExtracting files from holding disk on host %s.\n",
2126                         tape_server_name);
2127                 printf("The following files are needed:");
2128                 first=0;
2129             }
2130             else
2131                 printf("                               ");
2132             tlist = unmarshal_tapelist_str(elist->tape); 
2133             for( ; tlist != NULL; tlist = tlist->next)
2134                 printf(" %s", tlist->label);
2135             printf("\n");
2136             amfree(tlist);
2137         }
2138     }
2139     printf("\n");
2140
2141     if (getcwd(buf, sizeof(buf)) == NULL) {
2142         perror("extract_list: Current working directory unavailable");
2143         exit(1);
2144     }
2145
2146     printf("Restoring files into directory %s\n", buf);
2147 #ifdef SAMBA_CLIENT
2148     if (samba_extract_method == SAMBA_SMBCLIENT)
2149       printf("(unless it is a Samba backup, that will go through to the SMB server)\n");
2150 #endif
2151     if (!okay_to_continue(0,0,0))
2152         return;
2153     printf("\n");
2154
2155     while ((elist = first_tape_list()) != NULL)
2156     {
2157         if(elist->tape[0]=='/') {
2158             dump_device_name = newstralloc(dump_device_name, elist->tape);
2159             printf("Extracting from file ");
2160             tlist = unmarshal_tapelist_str(dump_device_name); 
2161             for( ; tlist != NULL; tlist = tlist->next)
2162                 printf(" %s", tlist->label);
2163             printf("\n");
2164             amfree(tlist);
2165         }
2166         else {
2167             printf("Extracting files using tape drive %s on host %s.\n",
2168                    tape_device_name, tape_server_name);
2169             tlist = unmarshal_tapelist_str(elist->tape); 
2170             printf("Load tape %s now\n", tlist->label);
2171             amfree(tlist);
2172             otc = okay_to_continue(1,1,0);
2173             if (otc == 0)
2174                 return;
2175             else if (otc == SKIP_TAPE) {
2176                 delete_tape_list(elist); /* skip this tape */
2177                 continue;
2178             }
2179             dump_device_name = newstralloc(dump_device_name, tape_device_name);
2180         }
2181         dump_datestamp = newstralloc(dump_datestamp, elist->date);
2182
2183         /* connect to the tape handler daemon on the tape drive server */
2184         if ((tape_control_sock = extract_files_setup(elist->tape, elist->fileno)) == -1)
2185         {
2186             fprintf(stderr, "amrecover - can't talk to tape server\n");
2187             return;
2188         }
2189
2190         /* okay, ready to extract. fork a child to do the actual work */
2191         if ((pid = fork()) == 0)
2192         {
2193             /* this is the child process */
2194             /* never gets out of this clause */
2195             writer_intermediary(tape_control_sock, tape_data_sock, elist);
2196             /*NOT REACHED*/
2197         }
2198         /* this is the parent */
2199         if (pid == -1)
2200         {
2201             perror("extract_list - error forking child");
2202             aclose(tape_control_sock);
2203             exit(1);
2204         }
2205
2206         /* store the child pid globally so that it can be killed on intr */
2207         extract_restore_child_pid = pid;
2208
2209         /* wait for the child process to finish */
2210         if ((pid = waitpid(-1, &child_stat, 0)) == (pid_t)-1)
2211         {
2212             perror("extract_list - error waiting for child");
2213             exit(1);
2214         }
2215
2216         if(tape_data_sock != -1) {
2217             aclose(tape_data_sock);
2218         }
2219
2220         if (pid == extract_restore_child_pid)
2221         {
2222             extract_restore_child_pid = -1;
2223         }
2224         else
2225         {
2226             fprintf(stderr, "extract list - unknown child terminated?\n");
2227             exit(1);
2228         }
2229         if ((WIFEXITED(child_stat) != 0) && (WEXITSTATUS(child_stat) != 0))
2230         {
2231             fprintf(stderr,
2232                     "extract_list - child returned non-zero status: %d\n",
2233                     WEXITSTATUS(child_stat));
2234             otc = okay_to_continue(0,0,1);
2235             if(otc == 0)
2236                 return;
2237
2238             if(otc == 1) {
2239                 delete_tape_list(elist); /* tape failed so delete from list */
2240             }
2241         }
2242         else {
2243             delete_tape_list(elist);    /* tape done so delete from list */
2244         }
2245     }
2246 }