Imported Upstream version 2.5.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: extract_list.c,v 1.6 2006/08/24 01:57:15 paddy_s Exp $
28  *
29  * implements the "extract" command in amrecover
30  */
31
32 #include "amanda.h"
33 #include "version.h"
34 #include "amrecover.h"
35 #include "fileheader.h"
36 #include "dgram.h"
37 #include "stream.h"
38 #include "tapelist.h"
39 #ifdef SAMBA_CLIENT
40 #include "findpass.h"
41 #endif
42 #include "util.h"
43
44 typedef struct EXTRACT_LIST_ITEM {
45     char *path;
46
47     struct EXTRACT_LIST_ITEM *next;
48 }
49 EXTRACT_LIST_ITEM;
50
51 typedef struct EXTRACT_LIST {
52     char *date;                 /* date tape created */
53     int  level;                 /* level of dump */
54     char *tape;                 /* tape label */
55     off_t fileno;               /* fileno on tape */
56     EXTRACT_LIST_ITEM *files;   /* files to get off tape */
57
58     struct EXTRACT_LIST *next;
59 }
60 EXTRACT_LIST;
61
62 #define SKIP_TAPE 2
63 #define RETRY_TAPE 3
64
65 char *dump_device_name = NULL;
66
67 extern char *localhost;
68
69 /* global pid storage for interrupt handler */
70 pid_t extract_restore_child_pid = -1;
71
72 static EXTRACT_LIST *extract_list = NULL;
73 static int tape_control_sock = -1;
74 static int tape_data_sock = -1;
75
76 #ifdef SAMBA_CLIENT
77 unsigned short samba_extract_method = SAMBA_TAR;
78 #endif /* SAMBA_CLIENT */
79
80 #define READ_TIMEOUT    240*60
81
82 EXTRACT_LIST *first_tape_list(void);
83 EXTRACT_LIST *next_tape_list(EXTRACT_LIST *list);
84 int is_extract_list_nonempty(void);
85 int length_of_tape_list(EXTRACT_LIST *tape_list);
86 void add_file(char *path, char *regex);
87 void add_glob(char *glob);
88 void add_regex(char *regex);
89 void clear_extract_list(void);
90 void clean_tape_list(EXTRACT_LIST *tape_list);
91 void clean_extract_list(void);
92 void delete_file(char *path, char *regex);
93 void delete_glob(char *glob);
94 void delete_regex(char *regex);
95 void delete_tape_list(EXTRACT_LIST *tape_list);
96 void display_extract_list(char *file);
97 void extract_files(void);
98 void read_file_header(char *buffer,
99                         dumpfile_t *file,
100                         size_t buflen,
101                         int tapedev);
102 void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist);
103 void writer_intermediary(int ctl_fd, int data_fd, EXTRACT_LIST *elist);
104
105 static int add_extract_item(DIR_ITEM *ditem);
106 static int delete_extract_item(DIR_ITEM *ditem);
107 static int extract_files_setup(char *label, off_t fsf);
108 static int okay_to_continue(int allow_tape,
109                         int allow_skip,
110                         int allow_retry);
111 static int okay_to_continue(int, int,  int);
112 static ssize_t read_buffer(int datafd,
113                         char *buffer,
114                         size_t buflen,
115                         long timeout_s);
116 static void clear_tape_list(EXTRACT_LIST *tape_list);
117 static void extract_files_child(int in_fd, EXTRACT_LIST *elist);
118 static void send_to_tape_server(int tss, char *cmd);
119
120
121 /*
122  * Function:  ssize_t read_buffer(datafd, buffer, buflen, timeout_s)
123  *
124  * Description:
125  *      read data from input file desciptor waiting up to timeout_s
126  *      seconds before returning data.
127  *
128  * Inputs:
129  *      datafd    - File descriptor to read from.
130  *      buffer    - Buffer to read into.
131  *      buflen    - Maximum number of bytes to read into buffer.
132  *      timeout_s - Seconds to wait before returning what was already read.
133  *
134  * Returns:
135  *      >0        - Number of data bytes in buffer.
136  *       0        - EOF
137  *      -1        - errno == ETIMEDOUT if no data available in specified time.
138  *                  errno == ENFILE if datafd is invalid.
139  *                  otherwise errno is set by select or read..
140  */
141
142 static ssize_t
143 read_buffer(
144     int         datafd,
145     char *      buffer,
146     size_t      buflen,
147     long        timeout_s)
148 {
149     ssize_t size = 0;
150     fd_set readset;
151     struct timeval timeout;
152     char *dataptr;
153     ssize_t spaceleft;
154     int nfound;
155
156     if(datafd < 0 || datafd >= (int)FD_SETSIZE) {
157         errno = EMFILE;                                 /* out of range */
158         return -1;
159     }
160
161     dataptr = buffer;
162     spaceleft = (ssize_t)buflen;
163
164     do {
165         FD_ZERO(&readset);
166         FD_SET(datafd, &readset);
167         timeout.tv_sec = timeout_s;
168         timeout.tv_usec = 0;
169         nfound = select(datafd+1, &readset, NULL, NULL, &timeout);
170         if(nfound < 0 ) {
171             /* Select returned an error. */
172             fprintf(stderr,"select error: %s\n", strerror(errno));
173             size = -1;
174             break;
175         }
176
177         if (nfound == 0) {
178             /* Select timed out. */
179             if (timeout_s != 0)  {
180                 /* Not polling: a real read timeout */
181                 fprintf(stderr,"timeout waiting for restore\n");
182                 fprintf(stderr,"increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
183             }
184             errno = ETIMEDOUT;
185             size = -1;
186             break;
187         }
188
189         if(!FD_ISSET(datafd, &readset))
190             continue;
191
192         /* Select says data is available, so read it.  */
193         size = read(datafd, dataptr, (size_t)spaceleft);
194         if (size < 0) {
195             if ((errno == EINTR) || (errno == EAGAIN)) {
196                 continue;
197             }
198             if (errno != EPIPE) {
199                 fprintf(stderr, "read_buffer: read error - %s",
200                     strerror(errno));
201                 break;
202             }
203             size = 0;
204         }
205         spaceleft -= size;
206         dataptr += size;
207     } while ((size > 0) && (spaceleft > 0));
208
209     return ((((ssize_t)buflen-spaceleft) > 0) ? ((ssize_t)buflen-spaceleft) : size);
210 }
211
212
213 EXTRACT_LIST *
214 first_tape_list(void)
215 {
216     return extract_list;
217 }
218
219 EXTRACT_LIST *
220 next_tape_list(
221     /*@keep@*/ EXTRACT_LIST *list)
222 {
223     if (list == NULL)
224         return NULL;
225     return list->next;
226 }
227
228 static void
229 clear_tape_list(
230     EXTRACT_LIST *      tape_list)
231 {
232     EXTRACT_LIST_ITEM *this, *next;
233     
234
235     this = tape_list->files;
236     while (this != NULL)
237     {
238         next = this->next;
239         amfree(this->path);
240         amfree(this);
241         this = next;
242     }
243     tape_list->files = NULL;
244 }
245
246
247 /* remove a tape list from the extract list, clearing the tape list
248    beforehand if necessary */
249 void
250 delete_tape_list(
251     EXTRACT_LIST *      tape_list)
252 {
253     EXTRACT_LIST *this, *prev;
254
255     if (tape_list == NULL)
256         return;
257
258     /* is it first on the list? */
259     if (tape_list == extract_list)
260     {
261         extract_list = tape_list->next;
262         clear_tape_list(tape_list);
263         amfree(tape_list->date);
264         amfree(tape_list->tape);
265         amfree(tape_list);
266         return;
267     }
268
269     /* so not first on list - find it and delete */
270     prev = extract_list;
271     this = extract_list->next;
272     while (this != NULL)
273     {
274         if (this == tape_list)
275         {
276             prev->next = tape_list->next;
277             clear_tape_list(tape_list);
278             amfree(tape_list->date);
279             amfree(tape_list->tape);
280             amfree(tape_list);
281             return;
282         }
283         prev = this;
284         this = this->next;
285     }
286     /*NOTREACHED*/
287 }
288
289
290 /* return the number of files on a tape's list */
291 int
292 length_of_tape_list(
293     EXTRACT_LIST *      tape_list)
294 {
295     EXTRACT_LIST_ITEM *fn;
296     int n;
297
298     n = 0;
299     for (fn = tape_list->files; fn != NULL; fn = fn->next)
300         n++;
301
302     return n;
303 }
304
305
306 void
307 clear_extract_list(void)
308 {
309     while (extract_list != NULL)
310         delete_tape_list(extract_list);
311 }
312
313
314 void
315 clean_tape_list(
316     EXTRACT_LIST *tape_list)
317 {
318     EXTRACT_LIST_ITEM *fn1, *pfn1, *ofn1;
319     EXTRACT_LIST_ITEM *fn2, *pfn2, *ofn2;
320     int remove_fn1;
321     int remove_fn2;
322
323     pfn1 = NULL;
324     fn1 = tape_list->files;
325     while (fn1 != NULL) {
326         remove_fn1 = 0;
327
328         pfn2 = fn1;
329         fn2 = fn1->next;
330         while (fn2 != NULL && remove_fn1 == 0) {
331             remove_fn2 = 0;
332             if(strcmp(fn1->path, fn2->path) == 0) {
333                 remove_fn2 = 1;
334             } else if (strncmp(fn1->path, fn2->path, strlen(fn1->path)) == 0 &&
335                        ((strlen(fn2->path) > strlen(fn1->path) &&
336                          fn2->path[strlen(fn1->path)] == '/') ||
337                        (fn1->path[strlen(fn1->path)-1] == '/'))) {
338                 remove_fn2 = 1;
339             } else if (strncmp(fn2->path, fn1->path, strlen(fn2->path)) == 0 &&
340                        ((strlen(fn1->path) > strlen(fn2->path) &&
341                          fn1->path[strlen(fn2->path)] == '/')  ||
342                        (fn2->path[strlen(fn2->path)-1] == '/'))) {
343                 remove_fn1 = 1;
344                 break;
345             }
346
347             if (remove_fn2) {
348                 dbprintf(("removing path %s, it is included in %s\n",
349                           fn2->path, fn1->path));
350                 ofn2 = fn2;
351                 fn2 = fn2->next;
352                 amfree(ofn2->path);
353                 amfree(ofn2);
354                 pfn2->next = fn2;
355             } else if (remove_fn1 == 0) {
356                 pfn2 = fn2;
357                 fn2 = fn2->next;
358             }
359         }
360
361         if(remove_fn1 != 0) {
362             /* fn2->path is always valid */
363             /*@i@*/ dbprintf(("removing path %s, it is included in %s\n",
364             /*@i@*/           fn1->path, fn2->path));
365             ofn1 = fn1;
366             fn1 = fn1->next;
367             amfree(ofn1->path);
368             if(pfn1 == NULL) {
369                 amfree(tape_list->files);
370                 tape_list->files = fn1;
371             } else {
372                 amfree(pfn1->next);
373                 pfn1->next = fn1;
374             }
375         } else {
376             pfn1 = fn1;
377             fn1 = fn1->next;
378         }
379     }
380 }
381
382
383 void
384 clean_extract_list(void)
385 {
386     EXTRACT_LIST *this;
387
388     for (this = extract_list; this != NULL; this = this->next)
389         clean_tape_list(this);
390 }
391
392
393 /* returns -1 if error */
394 /* returns  0 on succes */
395 /* returns  1 if already added */
396 static int
397 add_extract_item(
398     DIR_ITEM *  ditem)
399 {
400     EXTRACT_LIST *this, *this1;
401     EXTRACT_LIST_ITEM *that, *curr;
402     char *ditem_path = NULL;
403
404     ditem_path = stralloc(ditem->path);
405     clean_pathname(ditem_path);
406
407     for (this = extract_list; this != NULL; this = this->next)
408     {
409         /* see if this is the list for the tape */      
410         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
411         {
412             /* yes, so add to list */
413             curr=this->files;
414             while(curr!=NULL)
415             {
416                 if (strcmp(curr->path, ditem_path) == 0) {
417                     amfree(ditem_path);
418                     return 1;
419                 }
420                 curr=curr->next;
421             }
422             that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
423             that->path = stralloc(ditem_path);
424             that->next = this->files;
425             this->files = that;         /* add at front since easiest */
426             amfree(ditem_path);
427             return 0;
428         }
429     }
430
431     /* so this is the first time we have seen this tape */
432     this = (EXTRACT_LIST *)alloc(sizeof(EXTRACT_LIST));
433     this->tape = stralloc(ditem->tape);
434     this->level = ditem->level;
435     this->fileno = ditem->fileno;
436     this->date = stralloc(ditem->date);
437     that = (EXTRACT_LIST_ITEM *)alloc(sizeof(EXTRACT_LIST_ITEM));
438     that->path = stralloc(ditem_path);
439     that->next = NULL;
440     this->files = that;
441
442     /* add this in date increasing order          */
443     /* because restore must be done in this order */
444     /* add at begining */
445     if(extract_list==NULL || strcmp(this->date,extract_list->date) < 0) 
446     {
447         this->next = extract_list;
448         extract_list = this;
449         amfree(ditem_path);
450         return 0;
451     }
452     for (this1 = extract_list; this1->next != NULL; this1 = this1->next)
453     {
454         /* add in the middle */
455         if(strcmp(this->date,this1->next->date) < 0)
456         {
457             this->next = this1->next;
458             this1->next = this;
459             amfree(ditem_path);
460             return 0;
461         }
462     }
463     /* add at end */
464     this->next = NULL;
465     this1->next = this;
466     amfree(ditem_path);
467     return 0;
468 }
469
470
471 /* returns -1 if error */
472 /* returns  0 on deletion */
473 /* returns  1 if not there */
474 static int
475 delete_extract_item(
476     DIR_ITEM *  ditem)
477 {
478     EXTRACT_LIST *this;
479     EXTRACT_LIST_ITEM *that, *prev;
480     char *ditem_path = NULL;
481
482     ditem_path = stralloc(ditem->path);
483     clean_pathname(ditem_path);
484
485     for (this = extract_list; this != NULL; this = this->next)
486     {
487         /* see if this is the list for the tape */      
488         if (this->level == ditem->level && strcmp(this->tape, ditem->tape) == 0)
489         {
490             /* yes, so find file on list */
491             that = this->files;
492             if (strcmp(that->path, ditem_path) == 0)
493             {
494                 /* first on list */
495                 this->files = that->next;
496                 amfree(that->path);
497                 amfree(that);
498                 /* if list empty delete it */
499                 if (this->files == NULL)
500                     delete_tape_list(this);
501                 amfree(ditem_path);
502                 return 0;
503             }
504             prev = that;
505             that = that->next;
506             while (that != NULL)
507             {
508                 if (strcmp(that->path, ditem_path) == 0)
509                 {
510                     prev->next = that->next;
511                     amfree(that->path);
512                     amfree(that);
513                     amfree(ditem_path);
514                     return 0;
515                 }
516                 prev = that;
517                 that = that->next;
518             }
519             amfree(ditem_path);
520             return 1;
521         }
522     }
523
524     amfree(ditem_path);
525     return 1;
526 }
527
528 void
529 add_glob(
530     char *      glob)
531 {
532     char *regex;
533     char *regex_path;
534     char *s;
535     char *uqglob = unquote_string(glob);
536
537     regex = glob_to_regex(uqglob);
538     dbprintf(("add_glob (%s) -> %s\n", uqglob, regex));
539     if ((s = validate_regexp(regex)) != NULL) {
540         printf("%s is not a valid shell wildcard pattern: ", glob);
541         puts(s);
542     } else {
543         /*
544          * glob_to_regex() anchors the beginning of the pattern with ^,
545          * but we will be tacking it onto the end of the current directory
546          * in add_file, so strip that off.  Also, it anchors the end with
547          * $, but we need to match an optional trailing /, so tack that on
548          * the end.
549          */
550         regex_path = stralloc(regex + 1);
551         regex_path[strlen(regex_path) - 1] = '\0';
552         strappend(regex_path, "[/]*$");
553         add_file(uqglob, regex_path);
554         amfree(regex_path);
555     }
556     amfree(regex);
557     amfree(uqglob);
558 }
559
560 void
561 add_regex(
562     char *      regex)
563 {
564     char *s;
565     char *uqregex = unquote_string(regex);
566
567     if ((s = validate_regexp(uqregex)) != NULL) {
568         printf("%s is not a valid regular expression: ", regex);
569         puts(s);
570     } else {
571         add_file(uqregex, regex);
572     }
573     amfree(uqregex);
574 }
575
576 void add_file(
577     char *      path,
578     char *      regex)
579 {
580     DIR_ITEM *ditem, lditem;
581     char *path_on_disk = NULL;
582     char *path_on_disk_slash = NULL;
583     char *cmd = NULL;
584     char *err = NULL;
585     int i;
586     ssize_t j;
587     char *dir, *dir_undo, dir_undo_ch = '\0';
588     char *ditem_path = NULL;
589     char *l = NULL;
590     int  added;
591     char *s, *fp, *quoted;
592     int ch;
593     int found_one;
594     int dir_entries;
595
596     if (disk_path == NULL) {
597         printf("Must select directory before adding files\n");
598         return;
599     }
600     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
601
602     dbprintf(("add_file: Looking for \"%s\"\n", regex));
603
604     if(strcmp(regex, "/[/]*$") == 0) {  /* "/" behave like "." */
605         regex = "\\.[/]*$";
606     }
607     else if(strcmp(regex, "[^/]*[/]*$") == 0) {         /* "*" */
608         //regex = 
609         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);
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 = stralloc2("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                     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 #define sc "201-"
707                     if(strncmp(l, sc, sizeof(sc)-1) != 0) {
708                         err = "bad reply: not 201-";
709                         continue;
710                     }
711
712                     s = l + sizeof(sc)-1;
713                     ch = *s++;
714 #undef sc
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                         skip_whitespace(s, ch);
746                         if(ch == '\0' || sscanf(s - 1, OFF_T_FMT,
747                                 (OFF_T_FMT_TYPE *)&lditem.fileno) != 1) {
748                             err = "bad reply: cannot parse fileno field";
749                             continue;
750                         }
751                         skip_integer(s, ch);
752                     }
753
754                     skip_whitespace(s, ch);
755                     if(ch == '\0') {
756                         err = "bad reply: missing directory field";
757                         continue;
758                     }
759                     dir = s - 1;
760                     skip_quoted_string(s, ch);
761                     dir_undo = s - 1;
762                     dir_undo_ch = *dir_undo;
763                     *dir_undo = '\0';
764
765                     switch(add_extract_item(&lditem)) {
766                     case -1:
767                         printf("System error\n");
768                         dbprintf(("add_file: (Failed) System error\n"));
769                         break;
770
771                     case  0:
772                         quoted = quote_string(lditem.path);
773                         printf("Added dir %s at date %s\n",
774                                quoted, lditem.date);
775                         dbprintf(("add_file: (Successful) Added dir %s at date %s\n",
776                                   quoted, lditem.date));
777                         amfree(quoted);
778                         added=1;
779                         break;
780
781                     case  1:
782                         break;
783                     }
784                 }
785                 if(!server_happy()) {
786                     puts(reply_line());
787                 } else if(err) {
788                     if (*err)
789                         puts(err);
790                     if (cmd)
791                         puts(cmd);
792                 } else if(added == 0) {
793                     quoted = quote_string(ditem_path);
794                     printf("dir %s already added\n", quoted);
795                     dbprintf(("add_file: dir %s already added\n", quoted));
796                     amfree(quoted);
797                 }
798             }
799             else /* It is a file */
800             {
801                 switch(add_extract_item(ditem)) {
802                 case -1:
803                     printf("System error\n");
804                     dbprintf(("add_file: (Failed) System error\n"));
805                     break;
806
807                 case  0:
808                     quoted = quote_string(ditem->path);
809                     printf("Added file %s\n", quoted);
810                     dbprintf(("add_file: (Successful) Added %s\n", quoted));
811                     amfree(quoted);
812                     break;
813
814                 case  1:
815                     quoted = quote_string(ditem->path);
816                     printf("File %s already added\n", quoted);
817                     dbprintf(("add_file: file %s already added\n", quoted));
818                     amfree(quoted);
819                     break;
820                 }
821             }
822         }
823     }
824     if (cmd != NULL)
825         amfree(cmd);
826     amfree(ditem_path);
827     amfree(path_on_disk);
828     amfree(path_on_disk_slash);
829
830     if(! found_one) {
831         quoted = quote_string(path);
832         printf("File %s doesn't exist in directory\n", quoted);
833         dbprintf(("add_file: (Failed) File %s doesn't exist in directory\n",
834                   quoted));
835         amfree(quoted);
836     }
837 }
838
839
840 void
841 delete_glob(
842     char *      glob)
843 {
844     char *regex;
845     char *regex_path;
846     char *s;
847     char *uqglob = unquote_string(glob);
848
849     regex = glob_to_regex(uqglob);
850     dbprintf(("delete_glob (%s) -> %s\n", uqglob, regex));
851     if ((s = validate_regexp(regex)) != NULL) {
852         printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
853         puts(s);
854     } else {
855         /*
856          * glob_to_regex() anchors the beginning of the pattern with ^,
857          * but we will be tacking it onto the end of the current directory
858          * in add_file, so strip that off.  Also, it anchors the end with
859          * $, but we need to match an optional trailing /, so tack that on
860          * the end.
861          */
862         regex_path = stralloc(regex + 1);
863         regex_path[strlen(regex_path) - 1] = '\0';
864         strappend(regex_path, "[/]*$");
865         delete_file(uqglob, regex_path);
866         amfree(regex_path);
867     }
868     amfree(regex);
869     amfree(uqglob);
870 }
871
872 void
873 delete_regex(
874     char *      regex)
875 {
876     char *s;
877     char *uqregex = unquote_string(regex);
878
879     if ((s = validate_regexp(regex)) != NULL) {
880         printf("\"%s\" is not a valid regular expression: ", regex);
881         puts(s);
882     } else {
883         delete_file(uqregex, uqregex);
884     }
885     amfree(uqregex);
886 }
887
888 void
889 delete_file(
890     char *      path,
891     char *      regex)
892 {
893     DIR_ITEM *ditem, lditem;
894     char *path_on_disk = NULL;
895     char *path_on_disk_slash = NULL;
896     char *cmd = NULL;
897     char *err = NULL;
898     int i;
899     ssize_t j;
900     char *date;
901     char *tape, *tape_undo, tape_undo_ch = '\0';
902     char *dir_undo, dir_undo_ch = '\0';
903     int  level = 0;
904     off_t fileno;
905     char *ditem_path = NULL;
906     char *l = NULL;
907     int  deleted;
908     char *s;
909     int ch;
910     int found_one;
911     char *quoted;
912
913     if (disk_path == NULL) {
914         printf("Must select directory before deleting files\n");
915         return;
916     }
917     memset(&lditem, 0, sizeof(lditem)); /* Prevent use of bogus data... */
918
919     dbprintf(("delete_file: Looking for \"%s\"\n", path));
920
921     if (strcmp(regex, "[^/]*[/]*$") == 0) {
922         /* Looking for * find everything but single . */
923         regex = "([^/.]|\\.[^/]+|[^/.][^/]*)[/]*$";
924     } else {
925         /* remove "/" at end of path */
926         j = (ssize_t)(strlen(regex) - 1);
927         while(j >= 0 && regex[j] == '/') regex[j--] = '\0';
928     }
929
930     /* convert path (assumed in cwd) to one on disk */
931     if (strcmp(disk_path, "/") == 0) {
932         if (*regex == '/') {
933             if (strcmp(regex, "/[/]*$") == 0) {
934                 /* We want "/" to match the directory itself: "/." */
935                 path_on_disk = stralloc("/\\.[/]*$");
936             } else {
937                 /* No mods needed if already starts with '/' */
938                 path_on_disk = stralloc(regex);
939             }
940         } else {
941             /* Prepend '/' */
942             path_on_disk = stralloc2("/", regex);
943         }
944     } else {
945         char *clean_disk_path = clean_regex(disk_path);
946         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
947         amfree(clean_disk_path);
948     }
949
950     path_on_disk_slash = stralloc2(path_on_disk, "/");
951
952     dbprintf(("delete_file: Converted path=\"%s\" to path_on_disk=\"%s\"\n",
953               regex, path_on_disk));
954     found_one = 0;
955     for (ditem=get_dir_list(); ditem!=NULL; ditem=get_next_dir_item(ditem))
956     {
957         quoted = quote_string(ditem->path);
958         dbprintf(("delete_file: Pondering ditem->path=%s\n", quoted));
959         amfree(quoted);
960         if (match(path_on_disk, ditem->path)
961             || match(path_on_disk_slash, ditem->path))
962         {
963             found_one = 1;
964             j = (ssize_t)strlen(ditem->path);
965             if((j > 0 && ditem->path[j-1] == '/')
966                || (j > 1 && ditem->path[j-2] == '/' && ditem->path[j-1] == '.'))
967             {   /* It is a directory */
968                 ditem_path = newstralloc(ditem_path, ditem->path);
969                 clean_pathname(ditem_path);
970
971                 cmd = stralloc2("ORLD ", ditem_path);
972                 if(send_command(cmd) == -1) {
973                     amfree(cmd);
974                     amfree(ditem_path);
975                     amfree(path_on_disk);
976                     amfree(path_on_disk_slash);
977                     exit(1);
978                 }
979                 amfree(cmd);
980                 /* skip preamble */
981                 if ((i = get_reply_line()) == -1) {
982                     amfree(ditem_path);
983                     amfree(path_on_disk);
984                     amfree(path_on_disk_slash);
985                     exit(1);
986                 }
987                 if(i==0)                /* assume something wrong */
988                 {
989                     amfree(ditem_path);
990                     amfree(path_on_disk);
991                     amfree(path_on_disk_slash);
992                     l = reply_line();
993                     printf("%s\n", l);
994                     return;
995                 }
996                 deleted=0;
997                 lditem.path = newstralloc(lditem.path, ditem->path);
998                 amfree(cmd);
999                 tape_undo = dir_undo = NULL;
1000                 /* skip the last line -- duplicate of the preamble */
1001                 while ((i = get_reply_line()) != 0)
1002                 {
1003                     if (i == -1) {
1004                         amfree(ditem_path);
1005                         amfree(path_on_disk);
1006                         amfree(path_on_disk_slash);
1007                         exit(1);
1008                     }
1009                     if(err) {
1010                         if(cmd == NULL) {
1011                             if(tape_undo) *tape_undo = tape_undo_ch;
1012                             if(dir_undo) *dir_undo = dir_undo_ch;
1013                             tape_undo = dir_undo = NULL;
1014                             cmd = stralloc(l);  /* save for the error report */
1015                         }
1016                         continue;       /* throw the rest of the lines away */
1017                     }
1018                     l=reply_line();
1019                     if (!server_happy()) {
1020                         puts(l);
1021                         continue;
1022                     }
1023 #define sc "201-"
1024                     if(strncmp(l, sc, sizeof(sc)-1) != 0) {
1025                         err = "bad reply: not 201-";
1026                         continue;
1027                     }
1028                     s = l + sizeof(sc)-1;
1029                     ch = *s++;
1030 #undef sc
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                         skip_whitespace(s, ch);
1060                         if(ch == '\0' || sscanf(s - 1, OFF_T_FMT,
1061                                 (OFF_T_FMT_TYPE *)&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                         printf("System error\n");
1084                         dbprintf(("delete_file: (Failed) System error\n"));
1085                         break;
1086                     case  0:
1087                         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                     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                     printf("System error\n");
1115                     dbprintf(("delete_file: (Failed) System error\n"));
1116                     break;
1117                 case  0:
1118                     printf("Deleted %s\n", ditem->path);
1119                     dbprintf(("delete_file: (Successful) Deleted %s\n",
1120                               ditem->path));
1121                     break;
1122                 case  1:
1123                     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         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             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             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         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             fprintf(fp, "\t%s\n", that->path);
1193     }
1194
1195     if (file == NULL) {
1196         apclose(fp);
1197     } else {
1198         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' && isspace(ch)) {
1255             (void)ch;  /* Quiet empty loop body warning */
1256         }
1257         if (ch == '?') {
1258             if (get_tape) {
1259                 printf("Enter a new device ([host:]device) or \"default\"\n");
1260             } else {
1261                 printf("Enter \"y\"es to continue, \"n\"o to stop");
1262                 if(allow_skip) {
1263                     printf(", \"s\"kip this tape");
1264                 }
1265                 if(allow_retry) {
1266                     printf(" or \"r\"etry this tape");
1267                 }
1268                 if (allow_tape) {
1269                     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 (fullwrite(tss, msg, strlen(msg)) < 0)
1302     {
1303         error("Error writing to tape server");
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         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         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         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             snprintf(v_fsf, 99, OFF_T_FMT, (OFF_T_FMT_TYPE)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         fprintf(stderr, "%s: short block %d byte%s\n",
1573                 get_pname(), (int)bytes_read, (bytes_read == 1) ? "" : "s");
1574         print_header(stdout, file);
1575         error("Can't read file header");
1576         /*NOTREACHED*/
1577     }
1578
1579     /* bytes_read == buflen */
1580     parse_file_header(buffer, file, (size_t)bytes_read);
1581 }
1582
1583 enum dumptypes {
1584         IS_UNKNOWN,
1585         IS_DUMP,
1586         IS_GNUTAR,
1587         IS_TAR,
1588         IS_SAMBA,
1589         IS_SAMBA_TAR
1590 };
1591
1592 static void
1593 extract_files_child(
1594     int                 in_fd,
1595     EXTRACT_LIST *      elist)
1596 {
1597     int save_errno;
1598     int extra_params = 0;
1599     int i,j=0;
1600     char **restore_args = NULL;
1601     int files_off_tape;
1602     EXTRACT_LIST_ITEM *fn;
1603     enum dumptypes dumptype = IS_UNKNOWN;
1604     char buffer[DISK_BLOCK_BYTES];
1605     dumpfile_t file;
1606     size_t len_program;
1607     char *cmd = NULL;
1608     int passwd_field = -1;
1609 #ifdef SAMBA_CLIENT
1610     char *domain = NULL, *smbpass = NULL;
1611 #endif
1612
1613     /* code executed by child to do extraction */
1614     /* never returns */
1615
1616     /* make in_fd be our stdin */
1617     if (dup2(in_fd, STDIN_FILENO) == -1)
1618     {
1619         error("dup2 failed in extract_files_child: %s", strerror(errno));
1620         /*NOTREACHED*/
1621     }
1622
1623     /* read the file header */
1624     fh_init(&file);
1625     read_file_header(buffer, &file, sizeof(buffer), STDIN_FILENO);
1626
1627     if(file.type != F_DUMPFILE) {
1628         print_header(stdout, &file);
1629         error("bad header");
1630         /*NOTREACHED*/
1631     }
1632
1633     if (file.program != NULL) {
1634 #ifdef GNUTAR
1635         if (strcmp(file.program, GNUTAR) == 0)
1636             dumptype = IS_GNUTAR;
1637 #endif
1638
1639         if (dumptype == IS_UNKNOWN) {
1640             len_program = strlen(file.program);
1641             if(len_program >= 3 &&
1642                strcmp(&file.program[len_program-3],"tar") == 0)
1643                 dumptype = IS_TAR;
1644         }
1645
1646 #ifdef SAMBA_CLIENT
1647         if (dumptype == IS_UNKNOWN && strcmp(file.program, SAMBA_CLIENT) ==0) {
1648             if (samba_extract_method == SAMBA_TAR)
1649               dumptype = IS_SAMBA_TAR;
1650             else
1651               dumptype = IS_SAMBA;
1652         }
1653 #endif
1654     }
1655
1656     /* form the arguments to restore */
1657     files_off_tape = length_of_tape_list(elist);
1658     switch (dumptype) {
1659     case IS_SAMBA:
1660 #ifdef SAMBA_CLIENT
1661         extra_params = 10;
1662         break;
1663 #endif
1664     case IS_TAR:
1665     case IS_GNUTAR:
1666         extra_params = 4;
1667         break;
1668     case IS_SAMBA_TAR:
1669         extra_params = 3;
1670         break;
1671     case IS_UNKNOWN:
1672     case IS_DUMP:
1673 #ifdef AIX_BACKUP
1674         extra_params = 2;
1675 #else
1676 #if defined(XFSDUMP)
1677         if (strcmp(file.program, XFSDUMP) == 0) {
1678             extra_params = 4 + files_off_tape;
1679         } else
1680 #endif
1681         {
1682         extra_params = 4;
1683         }
1684 #endif
1685         break;
1686     }
1687
1688     restore_args = (char **)alloc((size_t)((extra_params + files_off_tape + 1)
1689                                   * sizeof(char *)));
1690     switch(dumptype) {
1691     case IS_SAMBA:
1692 #ifdef SAMBA_CLIENT
1693         restore_args[j++] = stralloc("smbclient");
1694         smbpass = findpass(file.disk, &domain);
1695         if (smbpass) {
1696             restore_args[j++] = stralloc(file.disk);
1697             passwd_field=j;
1698             restore_args[j++] = stralloc("-U");
1699             restore_args[j++] = smbpass;
1700             if (domain) {
1701                 restore_args[j++] = stralloc("-W");
1702                 restore_args[j++] = stralloc(domain);
1703             } else
1704                 extra_params -= 2;
1705         } else
1706             extra_params -= 6;
1707         restore_args[j++] = stralloc("-d0");
1708         restore_args[j++] = stralloc("-Tx");
1709         restore_args[j++] = stralloc("-");      /* data on stdin */
1710         break;
1711 #endif
1712     case IS_TAR:
1713     case IS_GNUTAR:
1714         restore_args[j++] = stralloc("tar");
1715         restore_args[j++] = stralloc("--numeric-owner");
1716         restore_args[j++] = stralloc("-xpGvf");
1717         restore_args[j++] = stralloc("-");      /* data on stdin */
1718         break;
1719     case IS_SAMBA_TAR:
1720         restore_args[j++] = stralloc("tar");
1721         restore_args[j++] = stralloc("-xpvf");
1722         restore_args[j++] = stralloc("-");      /* data on stdin */
1723         break;
1724     case IS_UNKNOWN:
1725     case IS_DUMP:
1726         restore_args[j++] = stralloc("restore");
1727 #ifdef AIX_BACKUP
1728         restore_args[j++] = stralloc("-xB");
1729 #else
1730 #if defined(XFSDUMP)
1731         if (strcmp(file.program, XFSDUMP) == 0) {
1732             restore_args[j++] = stralloc("-v");
1733             restore_args[j++] = stralloc("silent");
1734         } else
1735 #endif
1736 #if defined(VDUMP)
1737         if (strcmp(file.program, VDUMP) == 0) {
1738             restore_args[j++] = stralloc("xf");
1739             restore_args[j++] = stralloc("-");  /* data on stdin */
1740         } else
1741 #endif
1742         {
1743         restore_args[j++] = stralloc("xbf");
1744         restore_args[j++] = stralloc("2");      /* read in units of 1K */
1745         restore_args[j++] = stralloc("-");      /* data on stdin */
1746         }
1747 #endif
1748     }
1749   
1750     for (i = 0, fn = elist->files; i < files_off_tape; i++, fn = fn->next)
1751     {
1752         switch (dumptype) {
1753         case IS_TAR:
1754         case IS_GNUTAR:
1755         case IS_SAMBA_TAR:
1756         case IS_SAMBA:
1757             if (strcmp(fn->path, "/") == 0)
1758                 restore_args[j++] = stralloc(".");
1759             else
1760                 restore_args[j++] = stralloc2(".", fn->path);
1761             break;
1762         case IS_UNKNOWN:
1763         case IS_DUMP:
1764 #if defined(XFSDUMP)
1765             if (strcmp(file.program, XFSDUMP) == 0) {
1766                 /*
1767                  * xfsrestore needs a -s option before each file to be
1768                  * restored, and also wants them to be relative paths.
1769                  */
1770                 restore_args[j++] = stralloc("-s");
1771                 restore_args[j++] = stralloc(fn->path + 1);
1772             } else
1773 #endif
1774             {
1775             restore_args[j++] = stralloc(fn->path);
1776             }
1777         }
1778     }
1779 #if defined(XFSDUMP)
1780     if (strcmp(file.program, XFSDUMP) == 0) {
1781         restore_args[j++] = stralloc("-");
1782         restore_args[j++] = stralloc(".");
1783     }
1784 #endif
1785     restore_args[j] = NULL;
1786
1787     switch (dumptype) {
1788     case IS_SAMBA:
1789 #ifdef SAMBA_CLIENT
1790         cmd = stralloc(SAMBA_CLIENT);
1791         break;
1792 #else
1793         /* fall through to ... */
1794 #endif
1795     case IS_TAR:
1796     case IS_GNUTAR:
1797     case IS_SAMBA_TAR:
1798 #ifndef GNUTAR
1799         fprintf(stderr, "warning: GNUTAR program not available.\n");
1800         cmd = stralloc("tar");
1801 #else
1802         cmd = stralloc(GNUTAR);
1803 #endif
1804         break;
1805     case IS_UNKNOWN:
1806     case IS_DUMP:
1807         cmd = NULL;
1808 #if defined(DUMP)
1809         if (strcmp(file.program, DUMP) == 0) {
1810             cmd = stralloc(RESTORE);
1811         }
1812 #endif
1813 #if defined(VDUMP)
1814         if (strcmp(file.program, VDUMP) == 0) {
1815             cmd = stralloc(VRESTORE);
1816         }
1817 #endif
1818 #if defined(VXDUMP)
1819         if (strcmp(file.program, VXDUMP) == 0) {
1820             cmd = stralloc(VXRESTORE);
1821         }
1822 #endif
1823 #if defined(XFSDUMP)
1824         if (strcmp(file.program, XFSDUMP) == 0) {
1825             cmd = stralloc(XFSRESTORE);
1826         }
1827 #endif
1828         if (cmd == NULL) {
1829             fprintf(stderr, "warning: restore program for %s not available.\n",
1830                     file.program);
1831             cmd = stralloc("restore");
1832         }
1833     }
1834     if (cmd) {
1835         dbprintf(("Exec'ing %s with arguments:\n", cmd));
1836         for (i = 0; i < j; i++) {
1837             if( i == passwd_field)
1838                 dbprintf(("\tXXXXX\n"));
1839             else
1840                 dbprintf(("\t%s\n", restore_args[i]));
1841         }
1842         (void)execv(cmd, restore_args);
1843         /* only get here if exec failed */
1844         save_errno = errno;
1845         for (i = 0; i < j; i++) {
1846             amfree(restore_args[i]);
1847         }
1848         amfree(restore_args);
1849         errno = save_errno;
1850         perror("amrecover couldn't exec");
1851         fprintf(stderr, " problem executing %s\n", cmd);
1852         amfree(cmd);
1853     }
1854     exit(1);
1855     /*NOT REACHED */
1856 }
1857
1858 /*
1859  * Interpose something between the process writing out the dump (writing it to
1860  * some extraction program, really) and the socket from which we're reading, so
1861  * that we can do things like prompt for human interaction for multiple tapes.
1862  */
1863 void
1864 writer_intermediary(
1865     int                 ctl_fd,
1866     int                 data_fd,
1867     EXTRACT_LIST *      elist)
1868 {
1869     int child_pipe[2];
1870     pid_t pid;
1871     char buffer[DISK_BLOCK_BYTES];
1872     ssize_t bytes_read;
1873     amwait_t extractor_status;
1874     int max_fd, nfound;
1875     SELECT_ARG_TYPE readset, selectset;
1876     struct timeval timeout;
1877
1878     /*
1879      * If there's no distinct data channel (such as if we're talking to an
1880      * older server), don't bother doing anything complicated.  Just run the
1881      * extraction.
1882      */
1883     if(data_fd == -1){
1884         extract_files_child(ctl_fd, elist);
1885         /*NOTREACHED*/
1886     }
1887
1888     if(pipe(child_pipe) == -1) {
1889         error("extract_list - error setting up pipe to extractor: %s\n",
1890             strerror(errno));
1891         /*NOTREACHED*/
1892     }
1893
1894     /* okay, ready to extract. fork a child to do the actual work */
1895     if ((pid = fork()) == 0) {
1896         /* this is the child process */
1897         /* never gets out of this clause */
1898         aclose(child_pipe[1]);
1899         extract_files_child(child_pipe[0], elist);
1900         /*NOTREACHED*/
1901     }
1902
1903     /* This is the parent */
1904     if (pid == -1) {
1905         error("writer_intermediary - error forking child");
1906         /*NOTREACHED*/
1907     }
1908
1909     aclose(child_pipe[0]);
1910
1911     if(data_fd > ctl_fd) max_fd = data_fd+1;
1912                     else max_fd = ctl_fd+1;
1913     FD_ZERO(&readset);
1914     FD_SET(data_fd, &readset);
1915     FD_SET(ctl_fd, &readset);
1916
1917     do {
1918         timeout.tv_sec = READ_TIMEOUT;
1919         timeout.tv_usec = 0;
1920         FD_COPY(&readset, &selectset);
1921         
1922         nfound = select(max_fd, &selectset, NULL, NULL,
1923                         &timeout);
1924         if(nfound < 0) {
1925             fprintf(stderr,"select error: %s\n", strerror(errno));
1926             break;
1927         }
1928         
1929         if (nfound == 0) { /* timeout */
1930             fprintf(stderr, "timeout waiting %d seconds for restore\n",
1931                     READ_TIMEOUT);
1932             fprintf(stderr, "increase READ_TIMEOUT in recover-src/extract_list.c if your tape is slow\n");
1933             break;
1934         }
1935         
1936         if(FD_ISSET(ctl_fd, &selectset)) {
1937             bytes_read = read(ctl_fd, buffer, sizeof(buffer)-1);
1938             switch(bytes_read) {
1939             case -1:
1940                 if ((errno != EINTR) && (errno != EAGAIN)) {
1941                     if (errno != EPIPE) {
1942                         fprintf(stderr,"writer ctl fd read error: %s",
1943                                 strerror(errno));
1944                     }
1945                     FD_CLR(ctl_fd, &readset);
1946                 }
1947                 break;
1948                 
1949             case  0:
1950                 FD_CLR(ctl_fd, &readset);
1951                 break;
1952                 
1953             default: {
1954                 char desired_tape[MAX_TAPE_LABEL_BUF];
1955                 
1956                 buffer[bytes_read] = '\0';
1957                 /* if prompted for a tape, relay said prompt to the user */
1958                 if(sscanf(buffer, "FEEDME %132s\n", desired_tape) == 1) {
1959                     int done = 0;
1960                     while (!done) {
1961                         char *input = NULL;
1962                         printf("Please insert tape %s. Continue? [Y|n]: ",
1963                                desired_tape);
1964                         fflush(stdout);
1965                         
1966                         input = agets(stdin); /* strips \n */
1967                         if (strcasecmp("", input) == 0|| 
1968                             strcasecmp("y", input) == 0|| 
1969                             strcasecmp("yes", input) == 0) {
1970                             send_to_tape_server(tape_control_sock, "OK");
1971                             done = 1;
1972                         } else if (strcasecmp("n", input) == 0|| 
1973                                    strcasecmp("no", input) == 0) {
1974                             send_to_tape_server(tape_control_sock, "ERROR");
1975                             /* Abort!
1976                                We are the middle process, so just die. */
1977                             exit(EXIT_FAILURE);
1978                         }
1979                         amfree(input);
1980                     }
1981                 } else {
1982                     fprintf(stderr, "Strange message from tape server: %s", buffer);
1983                     break;
1984                 }    
1985               }
1986             }
1987         }
1988
1989         /* now read some dump data */
1990         if(FD_ISSET(data_fd, &selectset)) {
1991             bytes_read = read(data_fd, buffer, sizeof(buffer)-1);
1992             switch(bytes_read) {
1993             case -1:
1994                 if ((errno != EINTR) && (errno != EAGAIN)) {
1995                     if (errno != EPIPE) {
1996                         fprintf(stderr,"writer data fd read error: %s",
1997                                 strerror(errno));
1998                     }
1999                     FD_CLR(data_fd, &readset);
2000                 }
2001                 break;
2002                 
2003             case  0:
2004                 FD_CLR(data_fd, &readset);
2005                 break;
2006                 
2007             default:
2008                 /*
2009                  * spit what we got from the server to the child
2010                  *  process handling actual dumpfile extraction
2011                  */
2012                 if(fullwrite(child_pipe[1], buffer, (size_t)bytes_read) < 0) {
2013                     if(errno == EPIPE) {
2014                         error("%s: pipe data reader has quit: %s\n",
2015                               get_pname(), strerror(errno));
2016                         /* NOTREACHED */
2017                     }
2018                     error("Write error to extract child: %s\n",
2019                           strerror(errno));
2020                     /* NOTREACHED */
2021                 }
2022                 break;
2023             }
2024         }
2025     } while(FD_ISSET(ctl_fd, &readset) || FD_ISSET(data_fd, &readset));
2026
2027     aclose(child_pipe[1]);
2028
2029     waitpid(pid, &extractor_status, 0);
2030     if(WEXITSTATUS(extractor_status) != 0){
2031         int ret = WEXITSTATUS(extractor_status);
2032         if(ret == 255) ret = -1;
2033         error("Extractor child exited with status %d\n", ret);
2034         /*NOTREACHED*/
2035     }
2036
2037     exit(0);
2038 }
2039
2040 /* exec restore to do the actual restoration */
2041
2042 /* does the actual extraction of files */
2043 /*
2044  * The original design had the dump image being returned exactly as it
2045  * appears on the tape, and this routine getting from the index server
2046  * whether or not it is compressed, on the assumption that the tape
2047  * server may not know how to uncompress it. But
2048  * - Amrestore can't do that. It returns either compressed or uncompressed
2049  * (always). Amrestore assumes it can uncompress files. It is thus a good
2050  * idea to run the tape server on a machine with gzip.
2051  * - The information about compression in the disklist is really only
2052  * for future dumps. It is possible to change compression on a drive
2053  * so the information in the disklist may not necessarily relate to
2054  * the dump image on the tape.
2055  *   Consequently the design was changed to assuming that amrestore can
2056  * uncompress any dump image and have it return an uncompressed file
2057  * always.
2058  */
2059 void
2060 extract_files(void)
2061 {
2062     EXTRACT_LIST *elist;
2063     pid_t pid;
2064     amwait_t child_stat;
2065     char buf[STR_SIZE];
2066     char *l;
2067     int first;
2068     int otc;
2069     tapelist_t *tlist = NULL;
2070
2071     if (!is_extract_list_nonempty())
2072     {
2073         printf("Extract list empty - No files to extract!\n");
2074         return;
2075     }
2076
2077     clean_extract_list();
2078
2079     /* get tape device name from index server if none specified */
2080     if (tape_server_name == NULL) {
2081         tape_server_name = newstralloc(tape_server_name, server_name);
2082     }
2083     if (tape_device_name == NULL) {
2084         if (send_command("TAPE") == -1)
2085             exit(1);
2086         if (get_reply_line() == -1)
2087             exit(1);
2088         l = reply_line();
2089         if (!server_happy())
2090         {
2091             printf("%s\n", l);
2092             exit(1);
2093         }
2094         /* skip reply number */
2095         tape_device_name = newstralloc(tape_device_name, l+4);
2096     }
2097
2098     if (strcmp(tape_device_name, "/dev/null") == 0)
2099     {
2100         printf("amrecover: warning: using %s as the tape device will not work\n",
2101                tape_device_name);
2102     }
2103
2104     first=1;
2105     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2106         if(elist->tape[0]!='/') {
2107             if(first) {
2108                 printf("\nExtracting files using tape drive %s on host %s.\n",
2109                         tape_device_name, tape_server_name);
2110                 printf("The following tapes are needed:");
2111                 first=0;
2112             }
2113             else
2114                 printf("                               ");
2115             tlist = unmarshal_tapelist_str(elist->tape); 
2116             for( ; tlist != NULL; tlist = tlist->next)
2117                 printf(" %s", tlist->label);
2118             printf("\n");
2119             amfree(tlist);
2120         }
2121     first=1;
2122     for (elist = first_tape_list(); elist != NULL; elist = next_tape_list(elist))
2123     {
2124         if(elist->tape[0]=='/') {
2125             if(first) {
2126                 printf("\nExtracting files from holding disk on host %s.\n",
2127                         tape_server_name);
2128                 printf("The following files are needed:");
2129                 first=0;
2130             }
2131             else
2132                 printf("                               ");
2133             tlist = unmarshal_tapelist_str(elist->tape); 
2134             for( ; tlist != NULL; tlist = tlist->next)
2135                 printf(" %s", tlist->label);
2136             printf("\n");
2137             amfree(tlist);
2138         }
2139     }
2140     printf("\n");
2141
2142     if (getcwd(buf, sizeof(buf)) == NULL) {
2143         perror("extract_list: Current working directory unavailable");
2144         exit(1);
2145     }
2146
2147     printf("Restoring files into directory %s\n", buf);
2148 #ifdef SAMBA_CLIENT
2149     if (samba_extract_method == SAMBA_SMBCLIENT)
2150       printf("(unless it is a Samba backup, that will go through to the SMB server)\n");
2151 #endif
2152     if (!okay_to_continue(0,0,0))
2153         return;
2154     printf("\n");
2155
2156     while ((elist = first_tape_list()) != NULL)
2157     {
2158         if(elist->tape[0]=='/') {
2159             dump_device_name = newstralloc(dump_device_name, elist->tape);
2160             printf("Extracting from file ");
2161             tlist = unmarshal_tapelist_str(dump_device_name); 
2162             for( ; tlist != NULL; tlist = tlist->next)
2163                 printf(" %s", tlist->label);
2164             printf("\n");
2165             amfree(tlist);
2166         }
2167         else {
2168             printf("Extracting files using tape drive %s on host %s.\n",
2169                    tape_device_name, tape_server_name);
2170             tlist = unmarshal_tapelist_str(elist->tape); 
2171             printf("Load tape %s now\n", tlist->label);
2172             amfree(tlist);
2173             otc = okay_to_continue(1,1,0);
2174             if (otc == 0)
2175                 return;
2176             else if (otc == SKIP_TAPE) {
2177                 delete_tape_list(elist); /* skip this tape */
2178                 continue;
2179             }
2180             dump_device_name = newstralloc(dump_device_name, tape_device_name);
2181         }
2182         dump_datestamp = newstralloc(dump_datestamp, elist->date);
2183
2184         /* connect to the tape handler daemon on the tape drive server */
2185         if ((tape_control_sock = extract_files_setup(elist->tape, elist->fileno)) == -1)
2186         {
2187             fprintf(stderr, "amrecover - can't talk to tape server\n");
2188             return;
2189         }
2190
2191         /* okay, ready to extract. fork a child to do the actual work */
2192         if ((pid = fork()) == 0)
2193         {
2194             /* this is the child process */
2195             /* never gets out of this clause */
2196             writer_intermediary(tape_control_sock, tape_data_sock, elist);
2197             /*NOT REACHED*/
2198         }
2199         /* this is the parent */
2200         if (pid == -1)
2201         {
2202             perror("extract_list - error forking child");
2203             aclose(tape_control_sock);
2204             exit(1);
2205         }
2206
2207         /* store the child pid globally so that it can be killed on intr */
2208         extract_restore_child_pid = pid;
2209
2210         /* wait for the child process to finish */
2211         if ((pid = waitpid(-1, &child_stat, 0)) == (pid_t)-1)
2212         {
2213             perror("extract_list - error waiting for child");
2214             exit(1);
2215         }
2216
2217         if(tape_data_sock != -1) {
2218             aclose(tape_data_sock);
2219         }
2220
2221         if (pid == extract_restore_child_pid)
2222         {
2223             extract_restore_child_pid = -1;
2224         }
2225         else
2226         {
2227             fprintf(stderr, "extract list - unknown child terminated?\n");
2228             exit(1);
2229         }
2230         if ((WIFEXITED(child_stat) != 0) && (WEXITSTATUS(child_stat) != 0))
2231         {
2232             fprintf(stderr,
2233                     "extract_list - child returned non-zero status: %d\n",
2234                     WEXITSTATUS(child_stat));
2235             otc = okay_to_continue(0,0,1);
2236             if(otc == 0)
2237                 return;
2238
2239             if(otc == 1) {
2240                 delete_tape_list(elist); /* tape failed so delete from list */
2241             }
2242         }
2243         else {
2244             delete_tape_list(elist);    /* tape done so delete from list */
2245         }
2246     }
2247 }