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