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