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